docs(04-gsd-integration): create phase 4 plans for GSD command bridging
This commit is contained in:
@@ -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*
|
||||
|
||||
@@ -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>
|
||||
Reference in New Issue
Block a user