355 lines
12 KiB
Markdown
355 lines
12 KiB
Markdown
---
|
|
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}}"
|
|
---
|
|
|
|
<objective>
|
|
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
|
|
</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/02-integration-layer/02-CONTEXT.md
|
|
@.planning/phases/02-integration-layer/02-RESEARCH.md
|
|
|
|
<interfaces>
|
|
<!-- Template pattern from 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.
|
|
</interfaces>
|
|
</context>
|
|
|
|
<tasks>
|
|
|
|
<task type="auto" tdd="true">
|
|
<name>Task 1: Create test scaffold for prompt templates</name>
|
|
<files>tests/test_prompt_templates.py</files>
|
|
<behavior>
|
|
- 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
|
|
</behavior>
|
|
<action>
|
|
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.
|
|
</action>
|
|
<verify>
|
|
<automated>pytest tests/test_prompt_templates.py -v</automated>
|
|
</verify>
|
|
<done>
|
|
- tests/test_prompt_templates.py exists with 7 tests
|
|
- Tests fail initially (no templates yet)
|
|
</done>
|
|
</task>
|
|
|
|
<task type="auto" tdd="true">
|
|
<name>Task 2: Create prompt template files</name>
|
|
<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
|
|
</files>
|
|
<behavior>
|
|
- Test: All 4 tests in TestPromptTemplatesExist pass
|
|
</behavior>
|
|
<action>
|
|
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.
|
|
</action>
|
|
<verify>
|
|
<automated>pytest tests/test_prompt_templates.py::TestPromptTemplatesExist -v</automated>
|
|
</verify>
|
|
<done>
|
|
- All 3 template files exist with {{variable}} placeholders
|
|
- 4 tests in TestPromptTemplatesExist pass
|
|
</done>
|
|
</task>
|
|
|
|
<task type="auto" tdd="true">
|
|
<name>Task 3: Implement PromptLoader class</name>
|
|
<files>src/gsd_openclaw/prompt_loader.py</files>
|
|
<behavior>
|
|
- Test: All 7 tests in test_prompt_templates.py pass
|
|
</behavior>
|
|
<action>
|
|
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.
|
|
</action>
|
|
<verify>
|
|
<automated>pytest tests/test_prompt_templates.py -v</automated>
|
|
</verify>
|
|
<done>
|
|
- PromptLoader class implements get() and render() methods
|
|
- All 7 tests pass
|
|
- Missing variables are left as {{variable}} (safe substitution)
|
|
</done>
|
|
</task>
|
|
|
|
</tasks>
|
|
|
|
<verification>
|
|
- 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
|
|
</verification>
|
|
|
|
<success_criteria>
|
|
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
|
|
</success_criteria>
|
|
|
|
<output>
|
|
After completion, create `.planning/phases/02-integration-layer/02-03-SUMMARY.md`
|
|
</output>
|