199 lines
8.2 KiB
Markdown
199 lines
8.2 KiB
Markdown
---
|
||
phase: 03-orchestration
|
||
plan: 01
|
||
type: tdd
|
||
wave: 1
|
||
depends_on: []
|
||
files_modified:
|
||
- src/gsd_openclaw/command_executor.py
|
||
- tests/test_command_executor.py
|
||
autonomous: true
|
||
requirements: [BRDG-02, BRDG-03]
|
||
|
||
must_haves:
|
||
truths:
|
||
- "Command output is captured from tmux pane and returned as string"
|
||
- "Command completion is detected when prompt reappears or output stabilizes"
|
||
- "Error keywords in output are detected and flagged"
|
||
- "Timeout is enforced during output capture"
|
||
artifacts:
|
||
- path: "src/gsd_openclaw/command_executor.py"
|
||
provides: "Command execution and output capture"
|
||
exports: ["CommandExecutor", "CommandResult", "OutputCapture"]
|
||
- path: "tests/test_command_executor.py"
|
||
provides: "Test coverage for command execution"
|
||
min_lines: 150
|
||
key_links:
|
||
- from: "CommandExecutor"
|
||
to: "TmuxSessionManager"
|
||
via: "session.tmux_session.active_pane"
|
||
pattern: "active_pane\\.send_keys|capture_pane"
|
||
---
|
||
|
||
<objective>
|
||
Implement command execution layer that sends commands to tmux sessions and captures output with completion detection.
|
||
|
||
Purpose: Provide the foundational I/O layer for all GSD command bridging - send commands to tmux, capture output, detect completion, identify errors.
|
||
Output: CommandExecutor class with send_command(), capture_output(), and error detection.
|
||
</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/03-orchestration/03-CONTEXT.md
|
||
@.planning/phases/03-orchestration/03-RESEARCH.md
|
||
|
||
<interfaces>
|
||
<!-- Key types and contracts from existing codebase -->
|
||
|
||
From src/gsd_openclaw/tmux_session.py:
|
||
```python
|
||
@dataclass
|
||
class TmuxSession:
|
||
session_id: str
|
||
project_name: str
|
||
tmux_session_name: str
|
||
tmux_session: Optional[libtmux.Session] = None
|
||
|
||
@property
|
||
def exists(self) -> bool: ...
|
||
|
||
class TmuxSessionManager:
|
||
SESSION_PREFIX = "gsd-"
|
||
|
||
def get_session(self, project_name: str) -> Optional[TmuxSession]: ...
|
||
def create_session(self, project_name: str, ...) -> TmuxSession: ...
|
||
def resume_session(self, project_name: str) -> TmuxSession: ...
|
||
```
|
||
|
||
From src/gsd_openclaw/response_formatter.py:
|
||
```python
|
||
class CardTheme(Enum):
|
||
SUCCESS = "green"
|
||
ERROR = "red"
|
||
INFO = "blue"
|
||
|
||
@dataclass
|
||
class CardButton:
|
||
text: str
|
||
value: Dict[str, Any]
|
||
button_type: str = "primary"
|
||
|
||
class FeishuResponseFormatter:
|
||
def format_error(self, title: str, content: str, buttons=None) -> dict: ...
|
||
def format_success(self, title: str, content: str, buttons=None) -> dict: ...
|
||
```
|
||
</interfaces>
|
||
</context>
|
||
|
||
<feature>
|
||
<name>Command Execution with Output Capture</name>
|
||
<files>src/gsd_openclaw/command_executor.py, tests/test_command_executor.py</files>
|
||
<behavior>
|
||
- Test 1: send_command sends text to tmux pane via send_keys with enter=True
|
||
- Test 2: capture_output returns pane content until prompt appears or timeout
|
||
- Test 3: Output stabilizes (no changes for 3 polls) is detected as completion
|
||
- Test 4: Error keywords (error, failed, exception, traceback, 错误, 失败) are detected
|
||
- Test 5: Timeout (default 60s) is enforced during capture
|
||
- Test 6: CommandResult dataclass contains output, completed, has_error, error_type
|
||
</behavior>
|
||
<implementation>
|
||
1. Create CommandResult dataclass with: output (str), completed (bool), has_error (bool), error_type (Optional[str])
|
||
2. Create OutputCapture class with:
|
||
- PROMPT_PATTERNS: list of regex patterns for shell prompts (^\$\s*$, ^❯\s*$, ^➜\s*$)
|
||
- capture_until_complete(pane, timeout, poll_interval) -> tuple[str, bool]
|
||
- _is_prompt(line) -> bool
|
||
- Use stable output detection (no changes for 3 consecutive polls)
|
||
3. Create CommandExecutor class with:
|
||
- ERROR_KEYWORDS: ['error', 'failed', 'exception', 'traceback', '错误', '失败']
|
||
- DEFAULT_TIMEOUT: 60.0 (from CONTEXT.md user decision)
|
||
- send_command(pane, command) -> None (calls pane.send_keys)
|
||
- execute(session, command, timeout) -> CommandResult
|
||
- _detect_errors(output) -> tuple[bool, Optional[str]]
|
||
4. All classes use type hints and docstrings following Phase 1 patterns
|
||
5. Mock-based testing - no real tmux server required (follow Phase 1 test patterns)
|
||
</implementation>
|
||
</feature>
|
||
|
||
<tasks>
|
||
|
||
<task type="auto" tdd="true">
|
||
<name>Task 1: Create test scaffold for command executor</name>
|
||
<files>tests/test_command_executor.py</files>
|
||
<behavior>
|
||
- Test 1: OutputCapture._is_prompt detects common prompt patterns
|
||
- Test 2: OutputCapture.capture_until_complete returns output and completion flag
|
||
- Test 3: CommandExecutor._detect_errors identifies error keywords
|
||
- Test 4: CommandExecutor.execute returns CommandResult with all fields
|
||
</behavior>
|
||
<action>Create test file with pytest test functions for CommandExecutor and OutputCapture. Use mock objects for tmux pane (no real tmux). Follow test patterns from tests/test_tmux_session.py.</action>
|
||
<verify>
|
||
<automated>pytest tests/test_command_executor.py -v --collect-only 2>&1 | grep -c "test_" | xargs -I{} bash -c 'test {} -ge 4'</automated>
|
||
</verify>
|
||
<done>Test file exists with at least 4 test functions, all tests are discoverable by pytest</done>
|
||
</task>
|
||
|
||
<task type="auto" tdd="true">
|
||
<name>Task 2: Implement OutputCapture class</name>
|
||
<files>src/gsd_openclaw/command_executor.py, tests/test_command_executor.py</files>
|
||
<behavior>
|
||
- Test 1: _is_prompt returns True for "$ ", "❯ ", "➜ " prompts
|
||
- Test 2: _is_prompt returns False for non-prompt lines
|
||
- Test 3: capture_until_complete returns (output, True) when prompt detected
|
||
- Test 4: capture_until_complete returns (output, True) when output stabilizes
|
||
- Test 5: capture_until_complete returns (output, False) on timeout
|
||
</behavior>
|
||
<action>Implement OutputCapture class with PROMPT_PATTERNS constant, _is_prompt() method, and capture_until_complete() method. Use re module for pattern matching. Implement stable output detection (3 consecutive polls with same line count). Mock pane.capture_pane() in tests.</action>
|
||
<verify>
|
||
<automated>pytest tests/test_command_executor.py::test_is_prompt -v && pytest tests/test_command_executor.py::test_capture_until_complete -v</automated>
|
||
</verify>
|
||
<done>OutputCapture class implemented, all related tests pass</done>
|
||
</task>
|
||
|
||
<task type="auto" tdd="true">
|
||
<name>Task 3: Implement CommandExecutor class</name>
|
||
<files>src/gsd_openclaw/command_executor.py, tests/test_command_executor.py</files>
|
||
<behavior>
|
||
- Test 1: send_command calls pane.send_keys with enter=True
|
||
- Test 2: _detect_errors returns (True, "error") for "Error: something"
|
||
- Test 3: _detect_errors returns (True, "failed") for "Process failed"
|
||
- Test 4: _detect_errors returns (False, None) for normal output
|
||
- Test 5: execute returns CommandResult with captured output
|
||
- Test 6: execute sets has_error=True when error keywords detected
|
||
- Test 7: execute respects timeout parameter (default 60s)
|
||
</behavior>
|
||
<action>Implement CommandResult dataclass and CommandExecutor class with ERROR_KEYWORDS constant, DEFAULT_TIMEOUT=60.0, send_command(), _detect_errors(), and execute() methods. Use OutputCapture for output capture. Integrate with TmuxSession (session.tmux_session.active_pane).</action>
|
||
<verify>
|
||
<automated>pytest tests/test_command_executor.py -v</automated>
|
||
</verify>
|
||
<done>CommandExecutor class implemented with all methods, all tests pass (7+ tests)</done>
|
||
</task>
|
||
|
||
</tasks>
|
||
|
||
<verification>
|
||
Run full test suite for command executor module:
|
||
```bash
|
||
pytest tests/test_command_executor.py -v --cov=src/gsd_openclaw/command_executor
|
||
```
|
||
All tests must pass with >90% coverage.
|
||
</verification>
|
||
|
||
<success_criteria>
|
||
- OutputCapture class correctly detects prompt patterns and output stabilization
|
||
- CommandExecutor sends commands and captures output with timeout enforcement
|
||
- Error detection identifies error keywords in output
|
||
- All tests pass with mock-based testing (no real tmux required)
|
||
- Code follows Phase 1 patterns (dataclasses, type hints, docstrings)
|
||
</success_criteria>
|
||
|
||
<output>
|
||
After completion, create `.planning/phases/03-orchestration/03-01-SUMMARY.md`
|
||
</output>
|