docs(04-gsd-integration): create phase 4 plans for GSD command bridging

This commit is contained in:
2026-03-11 18:37:30 +08:00
parent caf205819f
commit 25ab3315da
4 changed files with 765 additions and 8 deletions
+13 -8
View File
@@ -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 1: Core Infrastructure** - State persistence and tmux session lifecycle management
- [x] **Phase 2: Integration Layer** - SKILL.md definition, response formatting, and prompt templates - [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 4: GSD Integration** - Full slash command bridging, approval flows, and progress reporting
## Phase Details ## Phase Details
@@ -65,9 +65,9 @@ Plans:
**Plans**: 3 plans **Plans**: 3 plans
Plans: Plans:
- [ ] 03-01-PLAN.md — Command executor with output capture and error detection (BRDG-02, BRDG-03) - [x] 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) - [x] 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-03-PLAN.md — Orchestrator integration with command routing (BRDG-01, OCLW-03, OCLW-05, OCLW-06)
### Phase 4: GSD Integration ### Phase 4: GSD Integration
**Goal**: All GSD slash commands are fully bridged with complete workflow support **Goal**: All GSD slash commands are fully bridged with complete workflow support
@@ -76,7 +76,12 @@ Plans:
**Success Criteria** (what must be TRUE): **Success Criteria** (what must be TRUE):
1. All GSD slash commands work through the bridge (no command left behind) 1. All GSD slash commands work through the bridge (no command left behind)
2. Complete GSD workflow is accessible from any supported chat channel 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 ## Progress
@@ -87,9 +92,9 @@ Phases execute in numeric order: 1 -> 2 -> 3 -> 4
|-------|----------------|--------|-----------| |-------|----------------|--------|-----------|
| 1. Core Infrastructure | 4/4 | Complete | 2026-03-11 | | 1. Core Infrastructure | 4/4 | Complete | 2026-03-11 |
| 2. Integration Layer | 3/3 | Complete | 2026-03-11 | | 2. Integration Layer | 3/3 | Complete | 2026-03-11 |
| 3. Orchestration | 0/3 | Not started | - | | 3. Orchestration | 3/3 | Complete | 2026-03-11 |
| 4. GSD Integration | 0/TBD | Not started | - | | 4. GSD Integration | 0/3 | Not started | - |
--- ---
*Roadmap created: 2026-03-10* *Roadmap created: 2026-03-10*
*Last updated: 2026-03-11 - Planned Phase 3 orchestration* *Last updated: 2026-03-11 - Planned Phase 4 GSD Integration*
@@ -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"
---
<objective>
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.
</objective>
<execution_context>
@/home/starmen1265/.claude/get-shit-done/workflows/execute-plan.md
@/home/starmen1265/.claude/get-shit-done/templates/summary.md
</execution_context>
<context>
@.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
<interfaces>
<!-- Key types and contracts from existing codebase -->
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
</interfaces>
</context>
<tasks>
<task type="auto" tdd="true">
<name>Task 1: Write UserContext dataclass and UserContextManager interface</name>
<files>src/gsd_openclaw/user_context.py, tests/test_user_context.py</files>
<behavior>
- 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
</behavior>
<action>
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
</action>
<verify>
<automated>pytest tests/test_user_context.py -x -v</automated>
</verify>
<done>UserContext dataclass and UserContextManager skeleton exist with passing tests</done>
</task>
<task type="auto" tdd="true">
<name>Task 2: Implement atomic save with backup rotation</name>
<files>src/gsd_openclaw/user_context.py, tests/test_user_context.py</files>
<behavior>
- 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
</behavior>
<action>
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
</action>
<verify>
<automated>pytest tests/test_user_context.py::TestAtomicWrites -x -v</automated>
</verify>
<done>UserContextManager.save() implements atomic writes with backup rotation, matching state_manager.py pattern</done>
</task>
<task type="auto" tdd="true">
<name>Task 3: Implement multi-user isolation and convenience methods</name>
<files>src/gsd_openclaw/user_context.py, tests/test_user_context.py, tests/conftest.py, src/gsd_openclaw/__init__.py</files>
<behavior>
- 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
</behavior>
<action>
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"
</action>
<verify>
<automated>pytest tests/test_user_context.py::TestMultiUser tests/test_user_context.py::TestUserContextManager -x -v</automated>
</verify>
<done>UserContextManager has convenience methods, multi-user isolation works, exports added to __init__.py</done>
</task>
</tasks>
<verification>
- 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
</verification>
<success_criteria>
- 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
</success_criteria>
<output>
After completion, create `.planning/phases/04-gsd-integration/04-01-SUMMARY.md`
</output>
@@ -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 <project> 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"
---
<objective>
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.
</objective>
<execution_context>
@/home/starmen1265/.claude/get-shit-done/workflows/execute-plan.md
@/home/starmen1265/.claude/get-shit-done/templates/summary.md
</execution_context>
<context>
@.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
<interfaces>
<!-- Key types and contracts from existing codebase -->
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
</interfaces>
</context>
<tasks>
<task type="auto" tdd="true">
<name>Task 1: Add UserContextManager to Orchestrator and implement /gsd:new-project handling</name>
<files>src/gsd_openclaw/orchestrator.py, tests/test_orchestrator.py</files>
<behavior>
- 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
</behavior>
<action>
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
</action>
<verify>
<automated>pytest tests/test_orchestrator.py::TestSpecialCommands::test_new_project -x -v</automated>
</verify>
<done>Orchestrator has _handle_new_project method that pre-creates session and updates user context</done>
</task>
<task type="auto" tdd="true">
<name>Task 2: Implement /gsd:use command handling with context tracking</name>
<files>src/gsd_openclaw/orchestrator.py, tests/test_orchestrator.py</files>
<behavior>
- 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
</behavior>
<action>
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
</action>
<verify>
<automated>pytest tests/test_orchestrator.py::TestSpecialCommands::test_use_project tests/test_orchestrator.py::TestSpecialCommands::test_use_nonexistent -x -v</automated>
</verify>
<done>Orchestrator has _handle_use_project method with context tracking and error handling</done>
</task>
<task type="auto" tdd="true">
<name>Task 3: Update handle_message routing to use user context for project selection</name>
<files>src/gsd_openclaw/orchestrator.py, tests/test_orchestrator.py</files>
<behavior>
- 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
</behavior>
<action>
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
</action>
<verify>
<automated>pytest tests/test_orchestrator.py -x -v</automated>
</verify>
<done>handle_message routes special commands and uses user context for project selection</done>
</task>
</tasks>
<verification>
- 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
</verification>
<success_criteria>
- 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
</success_criteria>
<output>
After completion, create `.planning/phases/04-gsd-integration/04-02-SUMMARY.md`
</output>
@@ -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"
---
<objective>
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.
</objective>
<execution_context>
@/home/starmen1265/.claude/get-shit-done/workflows/execute-plan.md
@/home/starmen1265/.claude/get-shit-done/templates/summary.md
</execution_context>
<context>
@.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
<interfaces>
<!-- Key types and contracts from existing codebase -->
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
</interfaces>
</context>
<tasks>
<task type="auto" tdd="true">
<name>Task 1: Add LONG_COMMANDS detection to CommandExecutor</name>
<files>src/gsd_openclaw/command_executor.py, tests/test_command_executor.py</files>
<behavior>
- 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
</behavior>
<action>
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
</action>
<verify>
<automated>pytest tests/test_command_executor.py::TestLongCommands -x -v</automated>
</verify>
<done>CommandExecutor has LONG_COMMANDS set and _is_long_command method with tests</done>
</task>
<task type="auto" tdd="true">
<name>Task 2: Update Orchestrator to use adaptive timeout</name>
<files>src/gsd_openclaw/orchestrator.py, tests/test_orchestrator.py</files>
<behavior>
- 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
</behavior>
<action>
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
</action>
<verify>
<automated>pytest tests/test_orchestrator.py::TestSpecialCommands -x -v</automated>
</verify>
<done>Orchestrator uses adaptive timeout based on command type</done>
</task>
<task type="auto" tdd="true">
<name>Task 3: Update handle_callback to support Create New Project button</name>
<files>src/gsd_openclaw/orchestrator.py, tests/test_orchestrator.py</files>
<behavior>
- 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
</behavior>
<action>
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
</action>
<verify>
<automated>pytest tests/test_orchestrator.py::TestHandleCallback -x -v</automated>
</verify>
<done>handle_callback supports "new-project" action from error card button</done>
</task>
</tasks>
<verification>
- 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
</verification>
<success_criteria>
- CommandExecutor has LONG_COMMANDS detection
- Orchestrator uses adaptive timeout based on command type
- handle_callback supports "new-project" action
- All existing tests still pass
</success_criteria>
<output>
After completion, create `.planning/phases/04-gsd-integration/04-03-SUMMARY.md`
</output>