diff --git a/gsd-openclaw/.planning/ROADMAP.md b/gsd-openclaw/.planning/ROADMAP.md
index abf83e7..f6ab9c4 100644
--- a/gsd-openclaw/.planning/ROADMAP.md
+++ b/gsd-openclaw/.planning/ROADMAP.md
@@ -45,7 +45,12 @@ Plans:
1. SKILL.md has valid frontmatter (name, description, triggers) and OpenClaw loads the skill successfully
2. Skill triggers on /gsd:* patterns and project management keywords
3. Response formatting adapts to channel capabilities (markdown for rich channels, plain for constrained ones)
-**Plans**: TBD
+**Plans**: 3 plans
+
+Plans:
+- [ ] 02-01-PLAN.md — SKILL.md creation with /gsd:* trigger pattern (OCLW-01, OCLW-02)
+- [ ] 02-02-PLAN.md — FeishuResponseFormatter with Card JSON 2.0 support (OCLW-04)
+- [ ] 02-03-PLAN.md — Prompt templates for context recovery, command mapping, error recovery (OCLW-02)
### Phase 3: Orchestration
**Goal**: Users can send GSD slash commands through chat and receive captured output with proper error handling
@@ -76,10 +81,10 @@ Phases execute in numeric order: 1 -> 2 -> 3 -> 4
| Phase | Plans Complete | Status | Completed |
|-------|----------------|--------|-----------|
| 1. Core Infrastructure | 3/4 | In Progress | - |
-| 2. Integration Layer | 0/TBD | Not started | - |
+| 2. Integration Layer | 0/3 | Ready | - |
| 3. Orchestration | 0/TBD | Not started | - |
| 4. GSD Integration | 0/TBD | Not started | - |
---
*Roadmap created: 2026-03-10*
-*Last updated: 2026-03-11 - Completed 01-03 tmux session lifecycle*
+*Last updated: 2026-03-11 - Planned Phase 2 integration layer*
diff --git a/gsd-openclaw/.planning/phases/02-integration-layer/02-01-PLAN.md b/gsd-openclaw/.planning/phases/02-integration-layer/02-01-PLAN.md
new file mode 100644
index 0000000..95a10ff
--- /dev/null
+++ b/gsd-openclaw/.planning/phases/02-integration-layer/02-01-PLAN.md
@@ -0,0 +1,182 @@
+---
+phase: 02-integration-layer
+plan: 01
+type: execute
+wave: 1
+depends_on: []
+files_modified:
+ - skill/SKILL.md
+ - tests/test_skill_md.py
+autonomous: true
+requirements:
+ - OCLW-01
+ - OCLW-02
+
+must_haves:
+ truths:
+ - "SKILL.md has valid YAML frontmatter with name and description fields"
+ - "OpenClaw can load the skill successfully"
+ - "Skill triggers on /gsd:* command patterns"
+ artifacts:
+ - path: "skill/SKILL.md"
+ provides: "OpenClaw skill definition and trigger patterns"
+ min_lines: 20
+ contains: "---"
+ - path: "tests/test_skill_md.py"
+ provides: "Tests for SKILL.md validation"
+ exports: ["TestSkillMd"]
+ key_links:
+ - from: "skill/SKILL.md"
+ to: "OpenClaw Gateway"
+ via: "skill loading"
+ pattern: "name:.*description:"
+---
+
+
+Create SKILL.md with valid frontmatter that OpenClaw can load and triggers on /gsd:* commands.
+
+Purpose: Define the skill entry point for OpenClaw's skill registry. The SKILL.md file is the "face" of the gsd-openclaw skill that OpenClaw loads to understand when to invoke this skill.
+
+Output:
+- skill/SKILL.md - OpenClaw skill definition
+- tests/test_skill_md.py - Validation tests
+
+
+
+@/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/02-integration-layer/02-CONTEXT.md
+@.planning/phases/02-integration-layer/02-RESEARCH.md
+
+
+
+
+
+
+Example frontmatter format:
+```yaml
+---
+name: gsd-openclaw
+description: GSD project management via /gsd:* commands. Triggers on /gsd:new-project, /gsd:plan, /gsd:status, /gsd:use and other GSD slash commands.
+---
+```
+
+
+
+
+
+
+ Task 1: Create test scaffold for SKILL.md validation
+ tests/test_skill_md.py
+
+ - Test 1: SKILL.md file exists at skill/SKILL.md
+ - Test 2: SKILL.md has valid YAML frontmatter between --- delimiters
+ - Test 3: SKILL.md frontmatter contains `name` field
+ - Test 4: SKILL.md frontmatter contains `description` field
+ - Test 5: description field contains /gsd: pattern for triggering
+
+
+ Create tests/test_skill_md.py with test class TestSkillMd containing:
+
+ 1. test_skill_md_exists():
+ - Assert skill/SKILL.md file exists
+ - Use Path(__file__).parent.parent / "skill" / "SKILL.md"
+
+ 2. test_skill_md_has_valid_frontmatter():
+ - Read file content
+ - Assert content starts with "---\n"
+ - Assert second "---" delimiter exists
+ - Use yaml.safe_load() to parse frontmatter (import yaml)
+
+ 3. test_skill_md_has_name_field():
+ - Parse frontmatter
+ - Assert "name" key exists
+ - Assert name is non-empty string
+
+ 4. test_skill_md_has_description_field():
+ - Parse frontmatter
+ - Assert "description" key exists
+ - Assert description is non-empty string
+
+ 5. test_skill_md_description_contains_gsd_pattern():
+ - Parse frontmatter
+ - Assert "/gsd:" appears in description (for trigger matching)
+
+ Follow test pattern from tests/test_state_manager.py (class-based, clear docstrings).
+ Add yaml to test dependencies if needed (should be available via pytest).
+
+
+ pytest tests/test_skill_md.py -v
+
+
+ - tests/test_skill_md.py exists with 5 passing tests
+ - All tests fail initially (no SKILL.md yet)
+
+
+
+
+ Task 2: Create SKILL.md with valid frontmatter
+ skill/SKILL.md
+
+ - Test: All tests in test_skill_md.py pass
+
+
+ Create skill/SKILL.md with:
+
+ 1. YAML frontmatter (between --- delimiters):
+ - name: gsd-openclaw
+ - description: GSD project management via /gsd:* commands. Triggers on /gsd:new-project, /gsd:plan, /gsd:status, /gsd:use and other GSD slash commands.
+
+ 2. Markdown content body (after frontmatter):
+ - Title: # GSD-OpenClaw Skill
+ - "## When to Use" section explaining trigger patterns
+ - "## Commands" table with supported /gsd:* commands
+ - "## Project Context" section explaining /gsd:use command
+
+ User decision (LOCKED): Only /gsd:* command triggers, no natural language.
+
+ Commands to document in table:
+ | Command | Description |
+ | /gsd:new-project <name> | Create a new GSD project |
+ | /gsd:use <project> | Switch to a project context |
+ | /gsd:status | Show current project status |
+ | /gsd:plan | Run planning phase |
+ | /gsd:ship | Ship the current phase |
+ | /gsd:help | Show available commands |
+
+ Follow dev-workflow/SKILL.md structure as reference.
+
+
+ pytest tests/test_skill_md.py -v
+
+
+ - skill/SKILL.md exists with valid YAML frontmatter
+ - All 5 tests in test_skill_md.py pass
+ - SKILL.md has name, description fields with /gsd: trigger pattern
+
+
+
+
+
+
+- Run pytest tests/test_skill_md.py -v (all tests pass)
+- Verify SKILL.md can be parsed as valid YAML
+- Verify description contains /gsd: pattern for OpenClaw trigger matching
+
+
+
+1. skill/SKILL.md exists with valid YAML frontmatter
+2. Frontmatter contains `name: gsd-openclaw`
+3. Frontmatter contains `description` with /gsd:* pattern
+4. All 5 tests in tests/test_skill_md.py pass
+
+
+
diff --git a/gsd-openclaw/.planning/phases/02-integration-layer/02-02-PLAN.md b/gsd-openclaw/.planning/phases/02-integration-layer/02-02-PLAN.md
new file mode 100644
index 0000000..9eece6b
--- /dev/null
+++ b/gsd-openclaw/.planning/phases/02-integration-layer/02-02-PLAN.md
@@ -0,0 +1,300 @@
+---
+phase: 02-integration-layer
+plan: 02
+type: execute
+wave: 1
+depends_on: []
+files_modified:
+ - src/gsd_openclaw/response_formatter.py
+ - tests/test_response_formatter.py
+autonomous: true
+requirements:
+ - OCLW-04
+
+must_haves:
+ truths:
+ - "Response formatter creates valid Feishu Card JSON 2.0 structure"
+ - "Success cards have green theme"
+ - "Error cards have red theme with guidance buttons"
+ - "Long messages are paginated with [N/M] markers"
+ - "Cards support interactive buttons with callback values"
+ artifacts:
+ - path: "src/gsd_openclaw/response_formatter.py"
+ provides: "Feishu Card message formatting"
+ exports: ["FeishuResponseFormatter", "CardTheme", "CardButton"]
+ - path: "tests/test_response_formatter.py"
+ provides: "Tests for response formatting"
+ exports: ["TestFeishuResponseFormatter"]
+ key_links:
+ - from: "FeishuResponseFormatter"
+ to: "Feishu API"
+ via: "card JSON structure"
+ pattern: '"schema": "2.0"'
+---
+
+
+Create FeishuResponseFormatter class that formats responses as Feishu Card JSON 2.0 messages with three-zone structure (header/content/actions).
+
+Purpose: Provide channel-adaptive response formatting for Feishu. The formatter converts GSD output into structured Feishu Card messages with proper theming, pagination, and interactive elements.
+
+Output:
+- src/gsd_openclaw/response_formatter.py - Response formatter implementation
+- tests/test_response_formatter.py - Formatter tests
+
+
+
+@/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/02-integration-layer/02-CONTEXT.md
+@.planning/phases/02-integration-layer/02-RESEARCH.md
+
+
+
+
+Three-zone layout:
+1. header: Title + status color (blue=info, green=success, red=error)
+2. elements: Main content area (div, markdown, note components)
+3. actions: Interactive buttons (at card bottom)
+
+Card JSON structure:
+```json
+{
+ "schema": "2.0",
+ "config": {"wide_screen_mode": true},
+ "header": {
+ "title": {"tag": "plain_text", "content": "..."},
+ "template": "green"
+ },
+ "elements": [
+ {"tag": "div", "text": {"tag": "lark_md", "content": "..."}}
+ ]
+}
+```
+
+Button structure:
+```json
+{
+ "tag": "action",
+ "actions": [
+ {
+ "tag": "button",
+ "text": {"tag": "plain_text", "content": "Confirm"},
+ "type": "primary",
+ "value": {"action": "approve", "project": "my-auth"}
+ }
+ ]
+}
+```
+
+Use `lark_md` tag for markdown content (Feishu's built-in markdown support).
+
+
+
+
+
+
+ Task 1: Create test scaffold for FeishuResponseFormatter
+ tests/test_response_formatter.py
+
+ - Test 1: format_success returns valid card with green theme
+ - Test 2: format_error returns valid card with red theme
+ - Test 3: format_info returns valid card with blue theme
+ - Test 4: Card with buttons includes action element
+ - Test 5: format_paginated adds [N/M] marker
+ - Test 6: Card has schema 2.0 and wide_screen_mode config
+
+
+ Create tests/test_response_formatter.py with test class TestFeishuResponseFormatter:
+
+ 1. test_format_success_returns_valid_card():
+ - Create FeishuResponseFormatter()
+ - Call format_success("Test Title", "Test content")
+ - Assert card["schema"] == "2.0"
+ - Assert card["header"]["template"] == "green"
+ - Assert card["header"]["title"]["content"] == "Test Title"
+
+ 2. test_format_error_returns_red_theme():
+ - Call format_error("Error", "Something went wrong")
+ - Assert card["header"]["template"] == "red"
+
+ 3. test_format_info_returns_blue_theme():
+ - Call format_info("Info", "Status update")
+ - Assert card["header"]["template"] == "blue"
+
+ 4. test_format_with_buttons_includes_actions():
+ - Create CardButton("OK", {"action": "ok"})
+ - Call format_success("Test", "Content", buttons=[button])
+ - Assert elements contains action with tag="action"
+ - Assert button text is "OK"
+
+ 5. test_format_paginated_adds_marker():
+ - Call format_paginated("Title", "Content", page=1, total_pages=3)
+ - Assert "[1/3]" appears in content
+
+ 6. test_card_has_required_structure():
+ - Call format_success("Test", "Content")
+ - Assert "config" key exists
+ - Assert config["wide_screen_mode"] == True
+ - Assert elements[0]["tag"] == "div"
+
+ Import from gsd_openclaw.response_formatter: FeishuResponseFormatter, CardTheme, CardButton
+
+ Follow test pattern from tests/test_state_manager.py.
+
+
+ pytest tests/test_response_formatter.py -v
+
+
+ - tests/test_response_formatter.py exists with 6 tests
+ - All tests fail initially (no implementation yet)
+
+
+
+
+ Task 2: Implement CardTheme enum and CardButton dataclass
+ src/gsd_openclaw/response_formatter.py
+
+ - Test: CardButton can be created with text, value, button_type
+ - Test: CardTheme enum has SUCCESS, ERROR, INFO, WARNING values
+
+
+ Create src/gsd_openclaw/response_formatter.py with:
+
+ 1. Imports:
+ - from dataclasses import dataclass
+ - from typing import Dict, Any, List, Optional
+ - from enum import Enum
+
+ 2. CardTheme(Enum):
+ - SUCCESS = "green"
+ - ERROR = "red"
+ - INFO = "blue"
+ - WARNING = "orange"
+
+ 3. @dataclass CardButton:
+ - text: str
+ - value: Dict[str, Any]
+ - button_type: str = "primary" # primary, default, danger
+
+ Keep implementation minimal - just these data structures.
+ Tests should now pass for CardTheme and CardButton.
+
+
+ pytest tests/test_response_formatter.py::TestFeishuResponseFormatter::test_format_success_returns_valid_card -v 2>&1 | head -20 || echo "Expected: tests still fail (formatter not implemented)"
+
+
+ - CardTheme enum exists with SUCCESS, ERROR, INFO, WARNING
+ - CardButton dataclass exists with text, value, button_type
+
+
+
+
+ Task 3: Implement FeishuResponseFormatter class
+ src/gsd_openclaw/response_formatter.py
+
+ - Test: All 6 tests in test_response_formatter.py pass
+
+
+ Add FeishuResponseFormatter class to response_formatter.py:
+
+ class FeishuResponseFormatter:
+ """Format responses as Feishu Card messages."""
+
+ def format_success(self, title: str, content: str,
+ buttons: Optional[List[CardButton]] = None) -> dict:
+ """Format a success response card with green theme."""
+ return self._build_card(title, content, CardTheme.SUCCESS, buttons)
+
+ def format_error(self, title: str, content: str,
+ buttons: Optional[List[CardButton]] = None) -> dict:
+ """Format an error response card with red theme."""
+ return self._build_card(title, content, CardTheme.ERROR, buttons)
+
+ def format_info(self, title: str, content: str,
+ buttons: Optional[List[CardButton]] = None) -> dict:
+ """Format an info response card with blue theme."""
+ return self._build_card(title, content, CardTheme.INFO, buttons)
+
+ def format_paginated(self, title: str, content: str,
+ page: int, total_pages: int) -> dict:
+ """Format a paginated response with [page/total] marker."""
+ footer = f"\n\n---\n[{page}/{total_pages}]"
+ return self._build_card(title, content + footer, CardTheme.INFO)
+
+ def _build_card(self, title: str, content: str, theme: CardTheme,
+ buttons: Optional[List[CardButton]] = None) -> dict:
+ """Build complete card JSON structure."""
+ card = {
+ "schema": "2.0",
+ "config": {"wide_screen_mode": True},
+ "header": {
+ "title": {"tag": "plain_text", "content": title},
+ "template": theme.value
+ },
+ "elements": [
+ {"tag": "div", "text": {"tag": "lark_md", "content": content}}
+ ]
+ }
+ if buttons:
+ card["elements"].append(self._build_actions(buttons))
+ return card
+
+ def _build_actions(self, buttons: List[CardButton]) -> dict:
+ """Build actions element from button list."""
+ return {
+ "tag": "action",
+ "actions": [
+ {
+ "tag": "button",
+ "text": {"tag": "plain_text", "content": btn.text},
+ "type": btn.button_type,
+ "value": btn.value
+ }
+ for btn in buttons
+ ]
+ }
+
+ User decision (LOCKED):
+ - Three-zone structure: header + elements + actions
+ - Error cards use red theme
+ - Success cards use green theme
+ - Pagination uses [N/M] format
+ - Interactive buttons support callback values
+
+ All 6 tests should now pass.
+
+
+ pytest tests/test_response_formatter.py -v
+
+
+ - FeishuResponseFormatter class implements all format methods
+ - All 6 tests pass
+ - Card JSON uses schema 2.0 with three-zone structure
+
+
+
+
+
+
+- Run pytest tests/test_response_formatter.py -v (all tests pass)
+- Verify card structure matches Feishu Card JSON 2.0 specification
+- Verify theme colors are correct (green=success, red=error, blue=info)
+
+
+
+1. FeishuResponseFormatter class exists in src/gsd_openclaw/response_formatter.py
+2. format_success, format_error, format_info, format_paginated methods work
+3. Card JSON has schema "2.0" with proper header/elements structure
+4. Buttons are rendered as action elements with callback values
+5. All 6 tests in tests/test_response_formatter.py pass
+
+
+
diff --git a/gsd-openclaw/.planning/phases/02-integration-layer/02-03-PLAN.md b/gsd-openclaw/.planning/phases/02-integration-layer/02-03-PLAN.md
new file mode 100644
index 0000000..3b6beb0
--- /dev/null
+++ b/gsd-openclaw/.planning/phases/02-integration-layer/02-03-PLAN.md
@@ -0,0 +1,354 @@
+---
+phase: 02-integration-layer
+plan: 03
+type: execute
+wave: 1
+depends_on: []
+files_modified:
+ - src/gsd_openclaw/prompts/__init__.py
+ - src/gsd_openclaw/prompts/context_recovery.md
+ - src/gsd_openclaw/prompts/command_mapping.md
+ - src/gsd_openclaw/prompts/error_recovery.md
+ - src/gsd_openclaw/prompt_loader.py
+ - tests/test_prompt_templates.py
+autonomous: true
+requirements:
+ - OCLW-02
+
+must_haves:
+ truths:
+ - "Prompt templates exist as markdown files with {{variable}} placeholders"
+ - "PromptLoader can load and render templates with variable substitution"
+ - "context_recovery.md template helps resume interrupted sessions"
+ - "command_mapping.md template maps user input to GSD commands"
+ - "error_recovery.md template generates user-friendly error messages"
+ artifacts:
+ - path: "src/gsd_openclaw/prompts/context_recovery.md"
+ provides: "Session context recovery prompt"
+ contains: "{{project_name}}"
+ - path: "src/gsd_openclaw/prompts/command_mapping.md"
+ provides: "Command mapping prompt"
+ contains: "{{user_input}}"
+ - path: "src/gsd_openclaw/prompts/error_recovery.md"
+ provides: "Error recovery prompt"
+ contains: "{{error_message}}"
+ - path: "src/gsd_openclaw/prompt_loader.py"
+ provides: "Template loading and rendering"
+ exports: ["PromptLoader"]
+ - path: "tests/test_prompt_templates.py"
+ provides: "Tests for prompt templates"
+ exports: ["TestPromptLoader"]
+ key_links:
+ - from: "PromptLoader"
+ to: "prompts/*.md"
+ via: "string.Template.safe_substitute"
+ pattern: "{{variable}}"
+---
+
+
+Create prompt templates for context recovery, command mapping, and error recovery, plus a PromptLoader class for loading and rendering templates.
+
+Purpose: Provide reusable prompt templates that Phase 3's orchestrator will use to generate context-aware prompts for Claude. Templates use simple {{variable}} syntax with string.Template for substitution.
+
+Output:
+- src/gsd_openclaw/prompts/ directory with 3 template files
+- src/gsd_openclaw/prompt_loader.py - Template loader implementation
+- tests/test_prompt_templates.py - Template tests
+
+
+
+@/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/02-integration-layer/02-CONTEXT.md
+@.planning/phases/02-integration-layer/02-RESEARCH.md
+
+
+
+
+Use string.Template with safe_substitute() for {{variable}} syntax:
+```python
+from string import Template
+from pathlib import Path
+
+class PromptLoader:
+ def __init__(self, template_dir: Path):
+ self.template_dir = template_dir
+ self._cache: Dict[str, Template] = {}
+
+ def render(self, name: str, **variables) -> str:
+ path = self.template_dir / f"{name}.md"
+ template = Template(path.read_text(encoding='utf-8'))
+ return template.safe_substitute(**variables)
+```
+
+Note: string.Template uses $variable syntax by default. For {{variable}} syntax,
+need to override the delimiter pattern or use simple string substitution.
+
+
+
+
+
+
+ Task 1: Create test scaffold for prompt templates
+ tests/test_prompt_templates.py
+
+ - Test 1: context_recovery.md template exists
+ - Test 2: command_mapping.md template exists
+ - Test 3: error_recovery.md template exists
+ - Test 4: PromptLoader.render substitutes variables
+ - Test 5: PromptLoader handles missing variables gracefully (safe_substitute)
+ - Test 6: PromptLoader raises error for missing template file
+
+
+ Create tests/test_prompt_templates.py with test classes:
+
+ class TestPromptTemplatesExist:
+ 1. test_context_recovery_template_exists():
+ - Assert prompts_dir / "context_recovery.md" exists
+
+ 2. test_command_mapping_template_exists():
+ - Assert prompts_dir / "command_mapping.md" exists
+
+ 3. test_error_recovery_template_exists():
+ - Assert prompts_dir / "error_recovery.md" exists
+
+ 4. test_templates_contain_variable_placeholders():
+ - Read each template
+ - Assert {{variable}} pattern exists in each
+
+ class TestPromptLoader:
+ 5. test_render_substitutes_variables():
+ - Create PromptLoader with prompts directory
+ - Call render("context_recovery", project_name="test-project", last_active_at="2026-03-11")
+ - Assert "test-project" appears in output
+ - Assert "{{project_name}}" does NOT appear
+
+ 6. test_render_handles_missing_variables():
+ - Call render with partial variables
+ - Assert output contains literal {{missing_var}} (safe_substitute behavior)
+
+ 7. test_render_raises_for_missing_template():
+ - Call render("nonexistent_template")
+ - Assert FileNotFoundError is raised
+
+ Use Path(__file__).parent.parent / "src" / "gsd_openclaw" / "prompts" for prompts_dir.
+ Follow test pattern from tests/test_state_manager.py.
+
+
+ pytest tests/test_prompt_templates.py -v
+
+
+ - tests/test_prompt_templates.py exists with 7 tests
+ - Tests fail initially (no templates yet)
+
+
+
+
+ Task 2: Create prompt template files
+
+ - src/gsd_openclaw/prompts/__init__.py
+ - src/gsd_openclaw/prompts/context_recovery.md
+ - src/gsd_openclaw/prompts/command_mapping.md
+ - src/gsd_openclaw/prompts/error_recovery.md
+
+
+ - Test: All 4 tests in TestPromptTemplatesExist pass
+
+
+ Create prompts directory and template files:
+
+ 1. src/gsd_openclaw/prompts/__init__.py:
+ - Empty file (marks directory as package)
+
+ 2. src/gsd_openclaw/prompts/context_recovery.md:
+ ```markdown
+ # Context Recovery
+
+ You are resuming a GSD session that was interrupted.
+
+ ## Session Information
+ - **Project**: {{project_name}}
+ - **Last Active**: {{last_active_at}}
+ - **Current Phase**: {{current_phase}}
+ - **Commands Executed**: {{command_count}}
+
+ ## Last Known State
+ {{last_output_snapshot}}
+
+ ## Instructions
+ Continue from where the session left off. Do not repeat completed steps.
+ ```
+
+ 3. src/gsd_openclaw/prompts/command_mapping.md:
+ ```markdown
+ # Command Mapping
+
+ Map the following user input to a GSD slash command.
+
+ ## User Input
+ {{user_input}}
+
+ ## Available Commands
+ - /gsd:new-project <name> - Create a new project
+ - /gsd:use <project> - Switch to a project
+ - /gsd:status - Show current status
+ - /gsd:plan - Run planning phase
+ - /gsd:ship - Ship current phase
+ - /gsd:help - Show help
+
+ ## Current Context
+ - Project: {{current_project}}
+ - Phase: {{current_phase}}
+
+ ## Instructions
+ Determine the most appropriate command based on the user input and context.
+ ```
+
+ 4. src/gsd_openclaw/prompts/error_recovery.md:
+ ```markdown
+ # Error Recovery
+
+ An error occurred while processing a GSD command.
+
+ ## Error Details
+ **Command**: {{command}}
+ **Error**: {{error_message}}
+
+ ## Context
+ - Project: {{project_name}}
+ - Phase: {{current_phase}}
+
+ ## Instructions
+ Generate a user-friendly error message in Chinese that:
+ 1. Explains what went wrong
+ 2. Suggests possible solutions
+ 3. Offers next steps the user can take
+ ```
+
+ User decision (LOCKED): Template format is Markdown with {{variable}} syntax.
+
+
+ pytest tests/test_prompt_templates.py::TestPromptTemplatesExist -v
+
+
+ - All 3 template files exist with {{variable}} placeholders
+ - 4 tests in TestPromptTemplatesExist pass
+
+
+
+
+ Task 3: Implement PromptLoader class
+ src/gsd_openclaw/prompt_loader.py
+
+ - Test: All 7 tests in test_prompt_templates.py pass
+
+
+ Create src/gsd_openclaw/prompt_loader.py:
+
+ ```python
+ """Prompt template loader for GSD-OpenClaw."""
+ from __future__ import annotations
+
+ import re
+ from pathlib import Path
+ from typing import Dict, Any
+
+ class PromptLoader:
+ """Load and render prompt templates with {{variable}} substitution.
+
+ Uses simple regex-based substitution for {{variable}} syntax.
+ Missing variables are left as-is (safe substitution behavior).
+ """
+
+ def __init__(self, template_dir: Path | None = None):
+ """Initialize PromptLoader.
+
+ Args:
+ template_dir: Directory containing template files.
+ Defaults to src/gsd_openclaw/prompts/.
+ """
+ if template_dir is None:
+ template_dir = Path(__file__).parent / "prompts"
+ self.template_dir = Path(template_dir)
+ self._cache: Dict[str, str] = {}
+
+ def get(self, name: str) -> str:
+ """Get raw template content by name (cached).
+
+ Args:
+ name: Template name without .md extension.
+
+ Returns:
+ Raw template content.
+
+ Raises:
+ FileNotFoundError: If template file doesn't exist.
+ """
+ if name not in self._cache:
+ path = self.template_dir / f"{name}.md"
+ if not path.exists():
+ raise FileNotFoundError(f"Template not found: {name}")
+ self._cache[name] = path.read_text(encoding='utf-8')
+ return self._cache[name]
+
+ def render(self, name: str, **variables: Any) -> str:
+ """Render a template with variable substitution.
+
+ Uses {{variable}} syntax. Missing variables are left as literal
+ {{variable}} strings (safe substitution).
+
+ Args:
+ name: Template name without .md extension.
+ **variables: Variable values to substitute.
+
+ Returns:
+ Rendered template content.
+
+ Raises:
+ FileNotFoundError: If template file doesn't exist.
+ """
+ content = self.get(name)
+ for key, value in variables.items():
+ content = content.replace("{{" + key + "}}", str(value))
+ return content
+ ```
+
+ Note: Using simple string.replace() for {{variable}} syntax instead of
+ string.Template which uses $variable syntax. This matches the user's
+ decision for {{variable}} format.
+
+
+ pytest tests/test_prompt_templates.py -v
+
+
+ - PromptLoader class implements get() and render() methods
+ - All 7 tests pass
+ - Missing variables are left as {{variable}} (safe substitution)
+
+
+
+
+
+
+- Run pytest tests/test_prompt_templates.py -v (all tests pass)
+- Verify all 3 template files have {{variable}} placeholders
+- Verify PromptLoader.render substitutes variables correctly
+
+
+
+1. src/gsd_openclaw/prompts/ directory exists with __init__.py
+2. context_recovery.md, command_mapping.md, error_recovery.md templates exist
+3. PromptLoader class can load and render templates
+4. Variable substitution uses {{variable}} syntax
+5. All 7 tests in tests/test_prompt_templates.py pass
+
+
+