diff --git a/gsd-openclaw/.planning/ROADMAP.md b/gsd-openclaw/.planning/ROADMAP.md
index c9227ab..1f6f199 100644
--- a/gsd-openclaw/.planning/ROADMAP.md
+++ b/gsd-openclaw/.planning/ROADMAP.md
@@ -14,7 +14,7 @@ Decimal phases appear between their surrounding integers in numeric order.
- [x] **Phase 1: Core Infrastructure** - State persistence and tmux session lifecycle management
- [x] **Phase 2: Integration Layer** - SKILL.md definition, response formatting, and prompt templates
-- [ ] **Phase 3: Orchestration** - Command routing, user authentication, and bidirectional bridging
+- [x] **Phase 3: Orchestration** - Command routing, user authentication, and bidirectional bridging
- [ ] **Phase 4: GSD Integration** - Full slash command bridging, approval flows, and progress reporting
## Phase Details
@@ -65,9 +65,9 @@ Plans:
**Plans**: 3 plans
Plans:
-- [ ] 03-01-PLAN.md — Command executor with output capture and error detection (BRDG-02, BRDG-03)
-- [ ] 03-02-PLAN.md — Approval handler and progress monitor (BRDG-04, BRDG-05)
-- [ ] 03-03-PLAN.md — Orchestrator integration with command routing (BRDG-01, OCLW-03, OCLW-05, OCLW-06)
+- [x] 03-01-PLAN.md — Command executor with output capture and error detection (BRDG-02, BRDG-03)
+- [x] 03-02-PLAN.md — Approval handler and progress monitor (BRDG-04, BRDG-05)
+- [x] 03-03-PLAN.md — Orchestrator integration with command routing (BRDG-01, OCLW-03, OCLW-05, OCLW-06)
### Phase 4: GSD Integration
**Goal**: All GSD slash commands are fully bridged with complete workflow support
@@ -76,7 +76,12 @@ Plans:
**Success Criteria** (what must be TRUE):
1. All GSD slash commands work through the bridge (no command left behind)
2. Complete GSD workflow is accessible from any supported chat channel
-**Plans**: TBD
+**Plans**: 3 plans
+
+Plans:
+- [ ] 04-01-PLAN.md — UserContextManager for per-user project context tracking (BRDG-06)
+- [ ] 04-02-PLAN.md — Orchestrator special command handling for /gsd:new-project and /gsd:use (BRDG-06)
+- [ ] 04-03-PLAN.md — Long command support and button callback for Create New Project (BRDG-06)
## Progress
@@ -87,9 +92,9 @@ Phases execute in numeric order: 1 -> 2 -> 3 -> 4
|-------|----------------|--------|-----------|
| 1. Core Infrastructure | 4/4 | Complete | 2026-03-11 |
| 2. Integration Layer | 3/3 | Complete | 2026-03-11 |
-| 3. Orchestration | 0/3 | Not started | - |
-| 4. GSD Integration | 0/TBD | Not started | - |
+| 3. Orchestration | 3/3 | Complete | 2026-03-11 |
+| 4. GSD Integration | 0/3 | Not started | - |
---
*Roadmap created: 2026-03-10*
-*Last updated: 2026-03-11 - Planned Phase 3 orchestration*
+*Last updated: 2026-03-11 - Planned Phase 4 GSD Integration*
diff --git a/gsd-openclaw/.planning/phases/04-gsd-integration/04-01-PLAN.md b/gsd-openclaw/.planning/phases/04-gsd-integration/04-01-PLAN.md
new file mode 100644
index 0000000..c5c6cbc
--- /dev/null
+++ b/gsd-openclaw/.planning/phases/04-gsd-integration/04-01-PLAN.md
@@ -0,0 +1,235 @@
+---
+phase: 04-gsd-integration
+plan: 01
+type: execute
+wave: 1
+depends_on: []
+files_modified:
+ - src/gsd_openclaw/user_context.py
+ - src/gsd_openclaw/__init__.py
+ - tests/test_user_context.py
+ - tests/conftest.py
+autonomous: true
+requirements:
+ - BRDG-06
+user_setup: []
+must_haves:
+ truths:
+ - "User's project context persists across chat sessions"
+ - "User context is stored in isolated per-user files"
+ - "User context writes are atomic and crash-safe"
+ - "Each user has independent project tracking"
+ artifacts:
+ - path: "src/gsd_openclaw/user_context.py"
+ provides: "UserContextManager class for per-user project tracking"
+ min_lines: 100
+ - path: "tests/test_user_context.py"
+ provides: "Tests for UserContextManager"
+ exports: ["TestUserContextManager", "TestAtomicWrites", "TestMultiUser"]
+ key_links:
+ - from: "src/gsd_openclaw/user_context.py"
+ to: "~/gsd-workspaces/.users/{user_id}/context.json"
+ via: "atomic write pattern (tempfile + os.replace)"
+ pattern: "tempfile\\.mkstemp|os\\.replace"
+---
+
+
+Create UserContextManager class for per-user project context tracking with atomic persistence.
+
+Purpose: Enable users to have persistent project context across chat sessions, supporting the `/gsd:use` command requirement.
+
+Output: UserContextManager class with load/save operations, atomic writes, and multi-user isolation.
+
+
+
+@/home/starmen1265/.claude/get-shit-done/workflows/execute-plan.md
+@/home/starmen1265/.claude/get-shit-done/templates/summary.md
+
+
+
+@.planning/PROJECT.md
+@.planning/ROADMAP.md
+@.planning/STATE.md
+@.planning/phases/04-gsd-integration/04-CONTEXT.md
+@.planning/phases/04-gsd-integration/04-RESEARCH.md
+
+
+
+
+From src/gsd_openclaw/state_manager.py (atomic write pattern to follow):
+```python
+def save_state(state: SessionState, state_path: Path) -> None:
+ """Save session state to file atomically with backup rotation."""
+ # 1. Create parent directories if missing
+ state_path.parent.mkdir(parents=True, exist_ok=True)
+ # 2. Rotate backup
+ # 3. Write to temp file in same directory
+ fd, temp_path = tempfile.mkstemp(dir=state_path.parent, suffix='.tmp')
+ # 4. Atomic replace
+ os.replace(temp_path, state_path)
+```
+
+From src/gsd_openclaw/response_formatter.py:
+```python
+@dataclass
+class CardButton:
+ """Interactive button for Feishu Card actions."""
+ text: str
+ value: Dict[str, Any]
+ button_type: str = "primary" # primary, default, danger
+```
+
+User context storage location (from CONTEXT.md):
+- Location: `~/gsd-workspaces/.users/{user_id}/context.json`
+- Format: JSON, atomic writes (same as Phase 1 state.json)
+
+User context data structure (from CONTEXT.md):
+- current_project: str - User's current project
+- recent_projects: List[str] - Recent 5 projects
+- last_active_at: str - ISO timestamp
+- preferences: dict - Optional settings
+
+
+
+
+
+
+ Task 1: Write UserContext dataclass and UserContextManager interface
+ src/gsd_openclaw/user_context.py, tests/test_user_context.py
+
+ - Test 1: UserContext can be created with all fields (user_id, current_project, recent_projects, last_active_at, preferences)
+ - Test 2: UserContext.to_dict() returns complete dict with all fields
+ - Test 3: UserContext.from_dict() reconstructs from dict, ignoring unknown fields
+ - Test 4: UserContextManager.__init__ sets users_root and creates context_path correctly
+ - Test 5: UserContextManager.load() returns None for new users (no existing context)
+ - Test 6: UserContextManager.load() returns UserContext for existing context file
+
+
+Create UserContext dataclass and UserContextManager class skeleton in src/gsd_openclaw/user_context.py:
+
+1. Create UserContext dataclass with fields:
+ - schema_version: str = "1.0"
+ - user_id: str = ""
+ - current_project: str = ""
+ - recent_projects: List[str] = field(default_factory=list)
+ - last_active_at: str = ""
+ - preferences: dict = field(default_factory=dict)
+
+2. Add to_dict() and from_dict() methods (follow SessionState pattern exactly)
+
+3. Create UserContextManager class:
+ - USERS_ROOT = Path.home() / "gsd-workspaces" / ".users"
+ - __init__(self, user_id: str, users_root: Optional[Path] = None)
+ - context_path property: users_root / user_id / "context.json"
+ - load() -> Optional[UserContext] method
+ - save(context: UserContext) -> None method (stub for next task)
+
+4. Create tests/test_user_context.py with TestUserContextManager class
+
+5. Follow state_manager.py patterns exactly for consistency
+
+
+ pytest tests/test_user_context.py -x -v
+
+ UserContext dataclass and UserContextManager skeleton exist with passing tests
+
+
+
+ Task 2: Implement atomic save with backup rotation
+ src/gsd_openclaw/user_context.py, tests/test_user_context.py
+
+ - Test 1: save() creates file atomically (no temp files remain after save)
+ - Test 2: save() creates parent directories if missing
+ - Test 3: save() rotates backup (context.json -> context.json.bak)
+ - Test 4: save() overwrites old backup on subsequent saves
+ - Test 5: save/load roundtrip preserves all data
+ - Test 6: load() falls back to backup when main file is corrupted
+
+
+Implement UserContextManager.save() with atomic write pattern:
+
+1. Follow state_manager.py save_state() pattern exactly:
+ - Create parent directories: context_path.parent.mkdir(parents=True, exist_ok=True)
+ - Rotate backup: if context_path exists, rename to context.json.bak
+ - Write to temp file: tempfile.mkstemp(dir=context_path.parent, suffix='.tmp')
+ - Atomic replace: os.replace(temp_path, context_path)
+ - Exception handling: clean up temp file on failure
+
+2. Add TestAtomicWrites class to tests/test_user_context.py with tests for:
+ - Atomic file creation
+ - Parent directory creation
+ - Backup rotation
+ - Cleanup on failure
+
+3. Ensure load() has backup fallback (same as load_state in state_manager.py)
+
+4. Add imports: tempfile, os, json
+
+
+ pytest tests/test_user_context.py::TestAtomicWrites -x -v
+
+ UserContextManager.save() implements atomic writes with backup rotation, matching state_manager.py pattern
+
+
+
+ Task 3: Implement multi-user isolation and convenience methods
+ src/gsd_openclaw/user_context.py, tests/test_user_context.py, tests/conftest.py, src/gsd_openclaw/__init__.py
+
+ - Test 1: Two users have completely isolated context files
+ - Test 2: get_current_project() returns current_project or None
+ - Test 3: set_current_project() updates current_project and adds to recent_projects
+ - Test 4: set_current_project() maintains only last 5 recent projects
+ - Test 5: get_recent_projects() returns list of recent projects
+ - Test 6: New user gets default context created on first access
+
+
+Add convenience methods and multi-user support:
+
+1. Add to UserContextManager:
+ - get_current_project() -> Optional[str]: Returns current_project or None
+ - set_current_project(project: str) -> None:
+ * Creates default context if none exists
+ * Updates current_project
+ * Adds to recent_projects (max 5, remove oldest if needed)
+ * Updates last_active_at to now
+ * Calls save()
+ - get_recent_projects() -> List[str]: Returns recent_projects list
+ - create_default_context() -> UserContext: Creates new context with defaults
+
+2. Add TestMultiUser class to tests/test_user_context.py:
+ - Test isolated context paths for different users
+ - Test changes to one user don't affect another
+
+3. Add fixtures to tests/conftest.py:
+ - tmp_users_root(tmp_path): Temp directory for user contexts
+ - user_context_manager(tmp_users_root): UserContextManager with temp root
+
+4. Update src/gsd_openclaw/__init__.py:
+ - Add: from gsd_openclaw.user_context import UserContext, UserContextManager
+ - Add to __all__: "UserContext", "UserContextManager"
+
+
+ pytest tests/test_user_context.py::TestMultiUser tests/test_user_context.py::TestUserContextManager -x -v
+
+ UserContextManager has convenience methods, multi-user isolation works, exports added to __init__.py
+
+
+
+
+
+- All tests in tests/test_user_context.py pass
+- UserContext follows SessionState pattern for consistency
+- Atomic write pattern matches state_manager.py exactly
+- Multi-user isolation verified through tests
+
+
+
+- UserContextManager class exists with load/save/get_current_project/set_current_project methods
+- Atomic writes with backup rotation work correctly
+- Multi-user isolation verified by tests
+- Exports added to __init__.py
+
+
+
diff --git a/gsd-openclaw/.planning/phases/04-gsd-integration/04-02-PLAN.md b/gsd-openclaw/.planning/phases/04-gsd-integration/04-02-PLAN.md
new file mode 100644
index 0000000..3c8ecdd
--- /dev/null
+++ b/gsd-openclaw/.planning/phases/04-gsd-integration/04-02-PLAN.md
@@ -0,0 +1,250 @@
+---
+phase: 04-gsd-integration
+plan: 02
+type: execute
+wave: 1
+depends_on: []
+files_modified:
+ - src/gsd_openclaw/orchestrator.py
+ - tests/test_orchestrator.py
+autonomous: true
+requirements:
+ - BRDG-06
+user_setup: []
+must_haves:
+ truths:
+ - "User can send /gsd:new-project through chat and get a pre-created session"
+ - "User can send /gsd:use to switch their current project context"
+ - "User receives clear error when /gsd:use references non-existent project"
+ - "Generic GSD commands pass through correctly"
+ artifacts:
+ - path: "src/gsd_openclaw/orchestrator.py"
+ provides: "Extended Orchestrator with special command handling"
+ exports: ["Orchestrator._handle_new_project", "Orchestrator._handle_use_project"]
+ - path: "tests/test_orchestrator.py"
+ provides: "Tests for special command handling"
+ exports: ["TestSpecialCommands"]
+ key_links:
+ - from: "src/gsd_openclaw/orchestrator.py"
+ to: "src/gsd_openclaw/user_context.py"
+ via: "UserContextManager for context tracking"
+ pattern: "UserContextManager"
+ - from: "src/gsd_openclaw/orchestrator.py"
+ to: "src/gsd_openclaw/tmux_session.py"
+ via: "TmuxSessionManager.create_session for new-project"
+ pattern: "create_session"
+---
+
+
+Extend Orchestrator with special command handling for /gsd:new-project and /gsd:use.
+
+Purpose: Enable key GSD commands that require orchestrator-level logic: pre-creating sessions for new-project and tracking user context for use.
+
+Output: Orchestrator with _handle_new_project and _handle_use_project methods integrated into handle_message routing.
+
+
+
+@/home/starmen1265/.claude/get-shit-done/workflows/execute-plan.md
+@/home/starmen1265/.claude/get-shit-done/templates/summary.md
+
+
+
+@.planning/PROJECT.md
+@.planning/ROADMAP.md
+@.planning/STATE.md
+@.planning/phases/04-gsd-integration/04-CONTEXT.md
+@.planning/phases/04-gsd-integration/04-RESEARCH.md
+
+
+
+
+From src/gsd_openclaw/orchestrator.py (current implementation):
+```python
+class Orchestrator:
+ COMMAND_PATTERN = re.compile(r'^/gsd:(\S+)\s*(.*)$')
+ MAX_OUTPUT_LENGTH = 2000
+
+ def parse_command(self, message: str) -> Optional[Tuple[str, str]]:
+ """Parse a message for /gsd:* command pattern."""
+ # Returns (command, args) or None
+
+ def handle_message(self, user_id: str, message: str, channel: str, context: Dict) -> Dict:
+ """Main entry point - currently auto-creates session for all commands."""
+```
+
+From src/gsd_openclaw/response_formatter.py:
+```python
+@dataclass
+class CardButton:
+ """Interactive button for Feishu Card actions."""
+ text: str
+ value: Dict[str, Any]
+ button_type: str = "primary" # primary, default, danger
+```
+
+From src/gsd_openclaw/tmux_session.py:
+```python
+class TmuxSessionManager:
+ def create_session(self, project_name: str, ...) -> TmuxSession:
+ """Create a new tmux session for a project."""
+
+ def get_session(self, project_name: str) -> Optional[TmuxSession]:
+ """Get existing session without creating new one."""
+```
+
+Special command handling requirements (from CONTEXT.md):
+- /gsd:new-project: Pre-create session, then pass command to tmux
+- /gsd:use: Track user context, show error + "Create New Project" button if not exists
+
+
+
+
+
+
+ Task 1: Add UserContextManager to Orchestrator and implement /gsd:new-project handling
+ src/gsd_openclaw/orchestrator.py, tests/test_orchestrator.py
+
+ - Test 1: Orchestrator initializes with UserContextManager instance
+ - Test 2: _handle_new_project creates tmux session before executing command
+ - Test 3: _handle_new_project updates user context with new project
+ - Test 4: _handle_new_project executes the command in the new session
+ - Test 5: _handle_new_project returns success card with output
+ - Test 6: handle_message routes 'new-project' command to _handle_new_project
+
+
+Extend Orchestrator with UserContextManager and new-project handling:
+
+1. Add import: from gsd_openclaw.user_context import UserContextManager
+
+2. Update Orchestrator.__init__:
+ - Add: self.user_context_managers: Dict[str, UserContextManager] = {}
+ - Add helper: _get_user_context_manager(user_id) -> UserContextManager
+
+3. Add LONG_COMMANDS class attribute:
+ ```python
+ LONG_COMMANDS = {'execute-phase', 'plan-phase', 'discuss-phase', 'new-project'}
+ ```
+
+4. Implement _handle_new_project(user_id: str, project_name: str) -> Dict:
+ - If project_name is empty, return format_error with "Project name required"
+ - Create session via tmux_manager.create_session(project_name)
+ - Execute "/gsd:new-project {project_name}" in the session with timeout=None
+ - Update user context: user_context.set_current_project(project_name)
+ - Return formatted result (success or error based on command result)
+
+5. Update handle_message to route 'new-project' to _handle_new_project
+
+6. Add TestSpecialCommands class to tests/test_orchestrator.py with tests for new-project
+
+
+ pytest tests/test_orchestrator.py::TestSpecialCommands::test_new_project -x -v
+
+ Orchestrator has _handle_new_project method that pre-creates session and updates user context
+
+
+
+ Task 2: Implement /gsd:use command handling with context tracking
+ src/gsd_openclaw/orchestrator.py, tests/test_orchestrator.py
+
+ - Test 1: _handle_use_project updates user context to new project
+ - Test 2: _handle_use_project returns success card when project exists
+ - Test 3: _handle_use_project returns error card with "Create New Project" button when project doesn't exist
+ - Test 4: handle_message routes 'use' command to _handle_use_project
+ - Test 5: _handle_use_project with no args returns error card
+ - Test 6: Subsequent commands use the project from user context
+
+
+Implement _handle_use_project method:
+
+1. Implement _handle_use_project(user_id: str, project_name: str) -> Dict:
+ - If project_name is empty, return format_error with "Project name required"
+ - Check if project session exists: tmux_manager.get_session(project_name)
+ - If session exists:
+ * Update user context: user_context.set_current_project(project_name)
+ * Return format_success with "Switched to project '{project_name}'"
+ - If session doesn't exist:
+ * Return format_error with:
+ - title: "Project not found"
+ - content: "Project '{project_name}' does not exist"
+ - buttons: [CardButton(text="Create New Project", value={"action": "new-project", "name": project_name})]
+
+2. Update handle_message to route 'use' command to _handle_use_project
+
+3. Add tests for _handle_use_project in TestSpecialCommands class
+
+4. Ensure CardButton import is available from response_formatter
+
+
+ pytest tests/test_orchestrator.py::TestSpecialCommands::test_use_project tests/test_orchestrator.py::TestSpecialCommands::test_use_nonexistent -x -v
+
+ Orchestrator has _handle_use_project method with context tracking and error handling
+
+
+
+ Task 3: Update handle_message routing to use user context for project selection
+ src/gsd_openclaw/orchestrator.py, tests/test_orchestrator.py
+
+ - Test 1: Generic commands use project from user context when not in context param
+ - Test 2: Generic commands fall back to "default" project if no context
+ - Test 3: Special commands (new-project, use) are routed correctly
+ - Test 4: Generic pass-through works for all other commands
+
+
+Update handle_message routing logic:
+
+1. Modify handle_message to get project from user context:
+ ```python
+ def handle_message(self, user_id, message, channel, context):
+ # Parse command
+ parsed = self.parse_command(message)
+ if not parsed:
+ return format_error(...)
+
+ command, args = parsed
+
+ # Special commands
+ if command == 'new-project':
+ return self._handle_new_project(user_id, args)
+ if command == 'use':
+ return self._handle_use_project(user_id, args)
+
+ # Generic commands - get project from user context
+ user_ctx = self._get_user_context_manager(user_id)
+ project_name = user_ctx.get_current_project()
+ if not project_name:
+ project_name = context.get("project", "default")
+
+ # Rest of existing logic...
+ ```
+
+2. Ensure existing tests still pass (backward compatibility)
+
+3. Add integration tests in TestIntegrationFlows for:
+ - Command after /gsd:use goes to correct project
+ - Context persists across multiple commands
+
+
+ pytest tests/test_orchestrator.py -x -v
+
+ handle_message routes special commands and uses user context for project selection
+
+
+
+
+
+- All tests in tests/test_orchestrator.py pass
+- /gsd:new-project pre-creates session and updates context
+- /gsd:use tracks context and shows error for non-existent projects
+- Generic commands use user context for project selection
+
+
+
+- Orchestrator has _handle_new_project and _handle_use_project methods
+- Special commands are routed correctly in handle_message
+- User context is used for project selection in generic commands
+- Error cards include "Create New Project" button for missing projects
+
+
+
diff --git a/gsd-openclaw/.planning/phases/04-gsd-integration/04-03-PLAN.md b/gsd-openclaw/.planning/phases/04-gsd-integration/04-03-PLAN.md
new file mode 100644
index 0000000..5d2eabf
--- /dev/null
+++ b/gsd-openclaw/.planning/phases/04-gsd-integration/04-03-PLAN.md
@@ -0,0 +1,267 @@
+---
+phase: 04-gsd-integration
+plan: 03
+type: execute
+wave: 2
+depends_on:
+ - 04-01
+ - 04-02
+files_modified:
+ - src/gsd_openclaw/command_executor.py
+ - src/gsd_openclaw/orchestrator.py
+ - tests/test_command_executor.py
+ - tests/test_orchestrator.py
+autonomous: true
+requirements:
+ - BRDG-06
+user_setup: []
+must_haves:
+ truths:
+ - "Long commands (execute-phase, plan-phase, discuss-phase, new-project) don't timeout"
+ - "Regular commands still use 60 second default timeout"
+ - "Long command detection is based on command name matching"
+ - "Create New Project button callback works from error card"
+ artifacts:
+ - path: "src/gsd_openclaw/command_executor.py"
+ provides: "Extended CommandExecutor with long command support"
+ exports: ["CommandExecutor.LONG_COMMANDS", "CommandExecutor._is_long_command"]
+ - path: "src/gsd_openclaw/orchestrator.py"
+ provides: "Orchestrator with adaptive timeout and button callback"
+ exports: ["Orchestrator._is_long_command"]
+ - path: "tests/test_command_executor.py"
+ provides: "Tests for long command detection"
+ exports: ["TestLongCommands"]
+ key_links:
+ - from: "src/gsd_openclaw/orchestrator.py"
+ to: "src/gsd_openclaw/command_executor.py"
+ via: "execute() with timeout=None for long commands"
+ pattern: "_is_long_command|LONG_COMMANDS"
+---
+
+
+Extend CommandExecutor with long command support and update Orchestrator for adaptive timeout and button callback.
+
+Purpose: Ensure long-running GSD commands don't timeout prematurely, and the "Create New Project" button from error cards works correctly.
+
+Output: CommandExecutor with LONG_COMMANDS detection, Orchestrator with adaptive timeout handling, and button callback support.
+
+
+
+@/home/starmen1265/.claude/get-shit-done/workflows/execute-plan.md
+@/home/starmen1265/.claude/get-shit-done/templates/summary.md
+
+
+
+@.planning/PROJECT.md
+@.planning/ROADMAP.md
+@.planning/STATE.md
+@.planning/phases/04-gsd-integration/04-CONTEXT.md
+@.planning/phases/04-gsd-integration/04-RESEARCH.md
+
+
+
+
+From src/gsd_openclaw/command_executor.py:
+```python
+class CommandExecutor:
+ ERROR_KEYWORDS: List[str] = [...]
+ DEFAULT_TIMEOUT: float = 60.0
+
+ def execute(self, session, command: str, timeout: Optional[float] = None) -> CommandResult:
+ """Execute command with optional timeout override."""
+ timeout = timeout if timeout is not None else self.DEFAULT_TIMEOUT
+ # ...
+```
+
+From src/gsd_openclaw/orchestrator.py:
+```python
+class Orchestrator:
+ COMMAND_PATTERN = re.compile(r'^/gsd:(\S+)\s*(.*)$')
+ MAX_OUTPUT_LENGTH = 2000
+
+ def handle_callback(self, callback_data: Dict[str, Any]) -> Dict[str, Any]:
+ """Handle button callback - currently only supports approve/reject."""
+```
+
+Long commands from CONTEXT.md:
+- execute-phase, plan-phase, discuss-phase, new-project have no timeout
+- Detection via command name matching
+
+
+
+
+
+
+ Task 1: Add LONG_COMMANDS detection to CommandExecutor
+ src/gsd_openclaw/command_executor.py, tests/test_command_executor.py
+
+ - Test 1: LONG_COMMANDS contains 'execute-phase', 'plan-phase', 'discuss-phase', 'new-project'
+ - Test 2: _is_long_command returns True for commands in LONG_COMMANDS
+ - Test 3: _is_long_command returns False for commands not in LONG_COMMANDS
+ - Test 4: _is_long_command handles commands with arguments correctly
+
+
+Add long command detection to CommandExecutor:
+
+1. Add LONG_COMMANDS class attribute:
+ ```python
+ class CommandExecutor:
+ ERROR_KEYWORDS: List[str] = [...]
+ DEFAULT_TIMEOUT: float = 60.0
+ LONG_COMMANDS: Set[str] = {'execute-phase', 'plan-phase', 'discuss-phase', 'new-project'}
+ ```
+
+2. Add _is_long_command method:
+ ```python
+ def _is_long_command(self, command: str) -> bool:
+ """Check if command requires no timeout.
+
+ Args:
+ command: The full command string (e.g., '/gsd:execute-phase').
+
+ Returns:
+ True if command is in LONG_COMMANDS, False otherwise.
+ """
+ # Extract command name from /gsd:command format
+ match = re.match(r'^/gsd:(\S+)', command)
+ if match:
+ command_name = match.group(1)
+ return command_name in self.LONG_COMMANDS
+ return False
+ ```
+
+3. Add TestLongCommands class to tests/test_command_executor.py:
+ - Test LONG_COMMANDS set contents
+ - Test _is_long_command for each long command
+ - Test _is_long_command returns False for regular commands
+ - Test _is_long_command handles commands with args
+
+
+ pytest tests/test_command_executor.py::TestLongCommands -x -v
+
+ CommandExecutor has LONG_COMMANDS set and _is_long_command method with tests
+
+
+
+ Task 2: Update Orchestrator to use adaptive timeout
+ src/gsd_openclaw/orchestrator.py, tests/test_orchestrator.py
+
+ - Test 1: _is_long_command in Orchestrator returns True for long commands
+ - Test 2: execute() is called with timeout=None for long commands
+ - Test 3: execute() is called with default timeout for regular commands
+ - Test 4: handle_message passes correct timeout based on command type
+
+
+Add adaptive timeout to Orchestrator:
+
+1. Add LONG_COMMANDS and _is_long_command to Orchestrator (mirror CommandExecutor):
+ ```python
+ class Orchestrator:
+ LONG_COMMANDS = {'execute-phase', 'plan-phase', 'discuss-phase', 'new-project'}
+
+ def _is_long_command(self, command: str) -> bool:
+ """Check if command requires no timeout."""
+ # Extract command name from parsed command
+ if command in self.LONG_COMMANDS:
+ return True
+ return False
+ ```
+
+2. Update handle_message to use adaptive timeout:
+ ```python
+ # In handle_message, before execute call:
+ timeout = None if self._is_long_command(command) else self.executor.DEFAULT_TIMEOUT
+ result = self.executor.execute(session, full_command, timeout=timeout)
+ ```
+
+3. Update _handle_new_project to use timeout=None (already a long command):
+ ```python
+ result = self.executor.execute(session, f"/gsd:new-project {project_name}", timeout=None)
+ ```
+
+4. Add tests in TestSpecialCommands for adaptive timeout:
+ - Test that long commands get timeout=None
+ - Test that regular commands get DEFAULT_TIMEOUT
+
+
+ pytest tests/test_orchestrator.py::TestSpecialCommands -x -v
+
+ Orchestrator uses adaptive timeout based on command type
+
+
+
+ Task 3: Update handle_callback to support Create New Project button
+ src/gsd_openclaw/orchestrator.py, tests/test_orchestrator.py
+
+ - Test 1: Callback with action="new-project" creates new session
+ - Test 2: Callback with action="new-project" executes /gsd:new-project command
+ - Test 3: Callback with action="new-project" returns success card
+ - Test 4: Callback with action="new-project" and missing name returns error
+
+
+Update handle_callback to support new-project action:
+
+1. Add new-project action handling to handle_callback:
+ ```python
+ def handle_callback(self, callback_data: Dict[str, Any]) -> Dict[str, Any]:
+ action = callback_data.get("action", "")
+
+ if action == "new-project":
+ project_name = callback_data.get("name", "")
+ user_id = callback_data.get("user_id", "")
+
+ if not project_name:
+ return self.formatter.format_error(
+ title="Invalid Request",
+ content="Project name is required"
+ )
+
+ return self._handle_new_project(user_id, project_name)
+
+ # Existing approval handling...
+ if action == "approve":
+ ...
+ ```
+
+2. Update _handle_use_project to include user_id in button value:
+ ```python
+ CardButton(
+ text="Create New Project",
+ value={
+ "action": "new-project",
+ "name": project_name,
+ "user_id": user_id
+ }
+ )
+ ```
+
+3. Add tests in TestHandleCallback for new-project action
+
+4. Ensure backward compatibility with existing approval callbacks
+
+
+ pytest tests/test_orchestrator.py::TestHandleCallback -x -v
+
+ handle_callback supports "new-project" action from error card button
+
+
+
+
+
+- All tests in tests/test_command_executor.py pass
+- All tests in tests/test_orchestrator.py pass
+- Long commands execute with no timeout
+- Regular commands still use 60 second timeout
+- "Create New Project" button callback works correctly
+
+
+
+- CommandExecutor has LONG_COMMANDS detection
+- Orchestrator uses adaptive timeout based on command type
+- handle_callback supports "new-project" action
+- All existing tests still pass
+
+
+