--- 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 After completion, create `.planning/phases/02-integration-layer/02-03-SUMMARY.md`