anvil-dev-framework 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +719 -0
- package/VERSION +1 -0
- package/docs/ANVIL-REPO-IMPLEMENTATION-PLAN.md +441 -0
- package/docs/FIRST-SKILL-TUTORIAL.md +408 -0
- package/docs/INSTALLATION-RETRO-NOTES.md +458 -0
- package/docs/INSTALLATION.md +984 -0
- package/docs/anvil-hud.md +469 -0
- package/docs/anvil-init.md +255 -0
- package/docs/anvil-state.md +210 -0
- package/docs/boris-cherny-ralph-wiggum-insights.md +608 -0
- package/docs/command-reference.md +2022 -0
- package/docs/hooks-tts.md +368 -0
- package/docs/implementation-guide.md +810 -0
- package/docs/linear-github-integration.md +247 -0
- package/docs/local-issues.md +677 -0
- package/docs/patterns/README.md +419 -0
- package/docs/planning-responsibilities.md +139 -0
- package/docs/session-workflow.md +573 -0
- package/docs/simplification-plan-template.md +297 -0
- package/docs/simplification-principles.md +129 -0
- package/docs/specifications/CCS-RALPH-INTEGRATION-DESIGN.md +633 -0
- package/docs/specifications/CCS-RESEARCH-REPORT.md +169 -0
- package/docs/specifications/PLAN-ANV-verification-ralph-wiggum.md +403 -0
- package/docs/specifications/PLAN-parallel-tracks-anvil-memory-ccs.md +494 -0
- package/docs/specifications/SPEC-ANV-VRW/component-01-verify.md +208 -0
- package/docs/specifications/SPEC-ANV-VRW/component-02-stop-gate.md +226 -0
- package/docs/specifications/SPEC-ANV-VRW/component-03-posttooluse.md +209 -0
- package/docs/specifications/SPEC-ANV-VRW/component-04-ralph-wiggum.md +604 -0
- package/docs/specifications/SPEC-ANV-VRW/component-05-atomic-actions.md +311 -0
- package/docs/specifications/SPEC-ANV-VRW/component-06-verify-subagent.md +264 -0
- package/docs/specifications/SPEC-ANV-VRW/component-07-claude-md.md +363 -0
- package/docs/specifications/SPEC-ANV-VRW/index.md +182 -0
- package/docs/specifications/SPEC-ANV-anvil-memory.md +573 -0
- package/docs/specifications/SPEC-ANV-context-checkpoints.md +781 -0
- package/docs/specifications/SPEC-ANV-verification-ralph-wiggum.md +789 -0
- package/docs/sync.md +122 -0
- package/global/CLAUDE.md +140 -0
- package/global/agents/verify-app.md +164 -0
- package/global/commands/anvil-settings.md +527 -0
- package/global/commands/anvil-sync.md +121 -0
- package/global/commands/change.md +197 -0
- package/global/commands/clarify.md +252 -0
- package/global/commands/cleanup.md +292 -0
- package/global/commands/commit-push-pr.md +207 -0
- package/global/commands/decay-review.md +127 -0
- package/global/commands/discover.md +158 -0
- package/global/commands/doc-coverage.md +122 -0
- package/global/commands/evidence.md +307 -0
- package/global/commands/explore.md +121 -0
- package/global/commands/force-exit.md +135 -0
- package/global/commands/handoff.md +191 -0
- package/global/commands/healthcheck.md +302 -0
- package/global/commands/hud.md +84 -0
- package/global/commands/insights.md +319 -0
- package/global/commands/linear-setup.md +184 -0
- package/global/commands/lint-fix.md +198 -0
- package/global/commands/orient.md +510 -0
- package/global/commands/plan.md +228 -0
- package/global/commands/ralph.md +346 -0
- package/global/commands/ready.md +182 -0
- package/global/commands/release.md +305 -0
- package/global/commands/retro.md +96 -0
- package/global/commands/shard.md +166 -0
- package/global/commands/spec.md +227 -0
- package/global/commands/sprint.md +184 -0
- package/global/commands/tasks.md +228 -0
- package/global/commands/test-and-commit.md +151 -0
- package/global/commands/validate.md +132 -0
- package/global/commands/verify.md +251 -0
- package/global/commands/weekly-review.md +156 -0
- package/global/hooks/__pycache__/ralph_context_monitor.cpython-314.pyc +0 -0
- package/global/hooks/__pycache__/statusline_agent_sync.cpython-314.pyc +0 -0
- package/global/hooks/anvil_memory_observe.ts +322 -0
- package/global/hooks/anvil_memory_session.ts +166 -0
- package/global/hooks/anvil_memory_stop.ts +187 -0
- package/global/hooks/parse_transcript.py +116 -0
- package/global/hooks/post_merge_cleanup.sh +132 -0
- package/global/hooks/post_tool_format.sh +215 -0
- package/global/hooks/ralph_context_monitor.py +240 -0
- package/global/hooks/ralph_stop.sh +502 -0
- package/global/hooks/statusline.sh +1110 -0
- package/global/hooks/statusline_agent_sync.py +224 -0
- package/global/hooks/stop_gate.sh +250 -0
- package/global/lib/.claude/anvil-state.json +21 -0
- package/global/lib/__pycache__/agent_registry.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/claim_service.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/coderabbit_service.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/config_service.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/coordination_service.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/doc_coverage_service.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/gate_logger.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/github_service.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/hygiene_service.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/issue_models.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/issue_provider.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/linear_data_service.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/linear_provider.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/local_provider.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/quality_service.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/ralph_state.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/state_manager.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/transcript_parser.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/verification_runner.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/verify_iteration.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/verify_subagent.cpython-314.pyc +0 -0
- package/global/lib/agent_registry.py +995 -0
- package/global/lib/anvil-state.sh +435 -0
- package/global/lib/claim_service.py +515 -0
- package/global/lib/coderabbit_service.py +314 -0
- package/global/lib/config_service.py +423 -0
- package/global/lib/coordination_service.py +331 -0
- package/global/lib/doc_coverage_service.py +1305 -0
- package/global/lib/gate_logger.py +316 -0
- package/global/lib/github_service.py +310 -0
- package/global/lib/handoff_generator.py +775 -0
- package/global/lib/hygiene_service.py +712 -0
- package/global/lib/issue_models.py +257 -0
- package/global/lib/issue_provider.py +339 -0
- package/global/lib/linear_data_service.py +210 -0
- package/global/lib/linear_provider.py +987 -0
- package/global/lib/linear_provider.py.backup +671 -0
- package/global/lib/local_provider.py +486 -0
- package/global/lib/orient_fast.py +457 -0
- package/global/lib/quality_service.py +470 -0
- package/global/lib/ralph_prompt_generator.py +563 -0
- package/global/lib/ralph_state.py +1202 -0
- package/global/lib/state_manager.py +417 -0
- package/global/lib/transcript_parser.py +597 -0
- package/global/lib/verification_runner.py +557 -0
- package/global/lib/verify_iteration.py +490 -0
- package/global/lib/verify_subagent.py +250 -0
- package/global/skills/README.md +155 -0
- package/global/skills/quality-gates/SKILL.md +252 -0
- package/global/skills/skill-template/SKILL.md +109 -0
- package/global/skills/testing-strategies/SKILL.md +337 -0
- package/global/templates/CHANGE-template.md +105 -0
- package/global/templates/HANDOFF-template.md +63 -0
- package/global/templates/PLAN-template.md +111 -0
- package/global/templates/SPEC-template.md +93 -0
- package/global/templates/ralph/PROMPT.md.template +89 -0
- package/global/templates/ralph/fix_plan.md.template +31 -0
- package/global/templates/ralph/progress.txt.template +23 -0
- package/global/tests/__pycache__/test_doc_coverage.cpython-314.pyc +0 -0
- package/global/tests/test_doc_coverage.py +520 -0
- package/global/tests/test_issue_models.py +299 -0
- package/global/tests/test_local_provider.py +323 -0
- package/global/tools/README.md +178 -0
- package/global/tools/__pycache__/anvil-hud.cpython-314.pyc +0 -0
- package/global/tools/anvil-hud.py +3622 -0
- package/global/tools/anvil-hud.py.bak +3318 -0
- package/global/tools/anvil-issue.py +432 -0
- package/global/tools/anvil-memory/CLAUDE.md +49 -0
- package/global/tools/anvil-memory/README.md +42 -0
- package/global/tools/anvil-memory/bun.lock +25 -0
- package/global/tools/anvil-memory/bunfig.toml +9 -0
- package/global/tools/anvil-memory/package.json +23 -0
- package/global/tools/anvil-memory/src/__tests__/ccs/context-monitor.test.ts +535 -0
- package/global/tools/anvil-memory/src/__tests__/ccs/edge-cases.test.ts +645 -0
- package/global/tools/anvil-memory/src/__tests__/ccs/fixtures.ts +363 -0
- package/global/tools/anvil-memory/src/__tests__/ccs/index.ts +8 -0
- package/global/tools/anvil-memory/src/__tests__/ccs/integration.test.ts +417 -0
- package/global/tools/anvil-memory/src/__tests__/ccs/prompt-generator.test.ts +571 -0
- package/global/tools/anvil-memory/src/__tests__/ccs/ralph-stop.test.ts +440 -0
- package/global/tools/anvil-memory/src/__tests__/ccs/test-utils.ts +252 -0
- package/global/tools/anvil-memory/src/__tests__/commands.test.ts +657 -0
- package/global/tools/anvil-memory/src/__tests__/db.test.ts +641 -0
- package/global/tools/anvil-memory/src/__tests__/hooks.test.ts +272 -0
- package/global/tools/anvil-memory/src/__tests__/performance.test.ts +427 -0
- package/global/tools/anvil-memory/src/__tests__/test-utils.ts +113 -0
- package/global/tools/anvil-memory/src/commands/checkpoint.ts +197 -0
- package/global/tools/anvil-memory/src/commands/get.ts +115 -0
- package/global/tools/anvil-memory/src/commands/init.ts +94 -0
- package/global/tools/anvil-memory/src/commands/observe.ts +163 -0
- package/global/tools/anvil-memory/src/commands/search.ts +112 -0
- package/global/tools/anvil-memory/src/db.ts +638 -0
- package/global/tools/anvil-memory/src/index.ts +205 -0
- package/global/tools/anvil-memory/src/types.ts +122 -0
- package/global/tools/anvil-memory/tsconfig.json +29 -0
- package/global/tools/ralph-loop.sh +359 -0
- package/package.json +45 -0
- package/scripts/anvil +822 -0
- package/scripts/extract_patterns.py +222 -0
- package/scripts/init-project.sh +541 -0
- package/scripts/install.sh +229 -0
- package/scripts/postinstall.js +41 -0
- package/scripts/rollback.sh +188 -0
- package/scripts/sync.sh +623 -0
- package/scripts/test-statusline.sh +248 -0
- package/scripts/update_claude_md.py +224 -0
- package/scripts/verify.sh +255 -0
|
@@ -0,0 +1,563 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
ralph_prompt_generator.py - PROMPT.md Resume Template Generator (ANV-201)
|
|
4
|
+
|
|
5
|
+
Generates resume PROMPT.md templates for checkpoint continuation, supporting
|
|
6
|
+
both Ralph autonomous mode and manual session resumption.
|
|
7
|
+
|
|
8
|
+
Usage:
|
|
9
|
+
from global.lib.ralph_prompt_generator import generate_resume_prompt
|
|
10
|
+
|
|
11
|
+
# Generate resume prompt after checkpoint
|
|
12
|
+
content = generate_resume_prompt(
|
|
13
|
+
checkpoint=context_checkpoint,
|
|
14
|
+
state=ralph_state,
|
|
15
|
+
mode="ralph" # or "manual"
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
# CLI usage
|
|
19
|
+
python ralph_prompt_generator.py --mode ralph --output PROMPT.md
|
|
20
|
+
python ralph_prompt_generator.py --mode manual --handoff .claude/handoffs/2026-01-11-1145.md
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
import json
|
|
24
|
+
import subprocess
|
|
25
|
+
from dataclasses import dataclass
|
|
26
|
+
from datetime import datetime, timezone
|
|
27
|
+
from pathlib import Path
|
|
28
|
+
from typing import Any, Dict, List, Optional
|
|
29
|
+
|
|
30
|
+
# =============================================================================
|
|
31
|
+
# Configuration
|
|
32
|
+
# =============================================================================
|
|
33
|
+
|
|
34
|
+
DEFAULT_STATE_FILE = ".claude/ralph-state.json"
|
|
35
|
+
DEFAULT_PROMPT_FILE = "PROMPT.md"
|
|
36
|
+
DEFAULT_HANDOFFS_DIR = ".claude/handoffs"
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
# =============================================================================
|
|
40
|
+
# Template Sections
|
|
41
|
+
# =============================================================================
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def get_checkpoint_header(
|
|
45
|
+
level: str,
|
|
46
|
+
percent: int,
|
|
47
|
+
timestamp: str,
|
|
48
|
+
mode: str = "ralph",
|
|
49
|
+
) -> str:
|
|
50
|
+
"""Generate checkpoint resume header section."""
|
|
51
|
+
mode_label = "Ralph Autonomous" if mode == "ralph" else "Manual"
|
|
52
|
+
return f"""# {mode_label} Session Resume
|
|
53
|
+
|
|
54
|
+
> **Context Checkpoint Recovery**
|
|
55
|
+
> Generated at {timestamp}
|
|
56
|
+
> Previous session hit {level} threshold ({percent}% context)
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def get_session_summary(
|
|
63
|
+
task_name: str,
|
|
64
|
+
objective: str,
|
|
65
|
+
iteration: int,
|
|
66
|
+
completed_items: List[str],
|
|
67
|
+
total_items: int,
|
|
68
|
+
) -> str:
|
|
69
|
+
"""Generate previous session summary section."""
|
|
70
|
+
completed_count = len(completed_items)
|
|
71
|
+
progress_pct = int((completed_count / total_items) * 100) if total_items > 0 else 0
|
|
72
|
+
|
|
73
|
+
completed_list = ""
|
|
74
|
+
if completed_items:
|
|
75
|
+
completed_list = "\n".join(f" - [x] {item}" for item in completed_items[-5:])
|
|
76
|
+
if len(completed_items) > 5:
|
|
77
|
+
completed_list = f" (...{len(completed_items) - 5} earlier items)\n" + completed_list
|
|
78
|
+
else:
|
|
79
|
+
completed_list = " (None completed yet)"
|
|
80
|
+
|
|
81
|
+
return f"""## Previous Session Summary
|
|
82
|
+
|
|
83
|
+
**Task**: {task_name}
|
|
84
|
+
**Objective**: {objective}
|
|
85
|
+
**Iteration**: {iteration}
|
|
86
|
+
**Progress**: {completed_count}/{total_items} items ({progress_pct}%)
|
|
87
|
+
|
|
88
|
+
### Completed Work
|
|
89
|
+
{completed_list}
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
"""
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def get_handoff_reference(handoff_file: str) -> str:
|
|
96
|
+
"""Generate handoff document reference section."""
|
|
97
|
+
if not handoff_file:
|
|
98
|
+
return ""
|
|
99
|
+
|
|
100
|
+
return f"""## Handoff Document
|
|
101
|
+
|
|
102
|
+
The previous session created a detailed handoff document with full context:
|
|
103
|
+
|
|
104
|
+
```
|
|
105
|
+
{handoff_file}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
**IMPORTANT**: Read this file first to understand the full context of where work left off.
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
"""
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def get_memory_query_instructions(include_anvil_memory: bool = True) -> str:
|
|
115
|
+
"""Generate Anvil Memory query instructions section."""
|
|
116
|
+
if not include_anvil_memory:
|
|
117
|
+
return ""
|
|
118
|
+
|
|
119
|
+
return """## Memory Query (Optional)
|
|
120
|
+
|
|
121
|
+
If Anvil Memory is configured, query for additional context:
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
# Get recent checkpoint context
|
|
125
|
+
anvil-memory context --include-checkpoints --limit 5
|
|
126
|
+
|
|
127
|
+
# Get session history for this task
|
|
128
|
+
anvil-memory search --type checkpoint --project $(basename $(pwd))
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
This provides cross-session context that may help with continuation.
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
"""
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def get_current_task_context(
|
|
138
|
+
current_todo_item: str,
|
|
139
|
+
progress_on_item: str,
|
|
140
|
+
files_in_progress: List[Dict[str, Any]],
|
|
141
|
+
remaining_items: List[str],
|
|
142
|
+
) -> str:
|
|
143
|
+
"""Generate current task context section."""
|
|
144
|
+
|
|
145
|
+
# Current item being worked on
|
|
146
|
+
current_section = ""
|
|
147
|
+
if current_todo_item:
|
|
148
|
+
current_section = f"""### Current Item (In Progress)
|
|
149
|
+
|
|
150
|
+
**Task**: {current_todo_item}
|
|
151
|
+
**Progress**: {progress_on_item or "Started but not completed"}
|
|
152
|
+
"""
|
|
153
|
+
|
|
154
|
+
# Files that were being modified
|
|
155
|
+
files_section = ""
|
|
156
|
+
if files_in_progress:
|
|
157
|
+
files_list = "\n".join(
|
|
158
|
+
f" - `{f.get('path', 'unknown')}` (lines {f.get('start_line', '?')}-{f.get('end_line', '?')})"
|
|
159
|
+
for f in files_in_progress[:5]
|
|
160
|
+
)
|
|
161
|
+
files_section = f"""
|
|
162
|
+
### Files In Progress
|
|
163
|
+
|
|
164
|
+
{files_list}
|
|
165
|
+
"""
|
|
166
|
+
|
|
167
|
+
# Remaining todo items
|
|
168
|
+
remaining_section = ""
|
|
169
|
+
if remaining_items:
|
|
170
|
+
remaining_list = "\n".join(f" - [ ] {item}" for item in remaining_items[:10])
|
|
171
|
+
if len(remaining_items) > 10:
|
|
172
|
+
remaining_list += f"\n - ... and {len(remaining_items) - 10} more items"
|
|
173
|
+
remaining_section = f"""
|
|
174
|
+
### Remaining Work
|
|
175
|
+
|
|
176
|
+
{remaining_list}
|
|
177
|
+
"""
|
|
178
|
+
|
|
179
|
+
return f"""## Current Task Context
|
|
180
|
+
|
|
181
|
+
{current_section}{files_section}{remaining_section}
|
|
182
|
+
---
|
|
183
|
+
"""
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def get_resume_instructions(mode: str = "ralph") -> str:
|
|
187
|
+
"""Generate resume instructions section."""
|
|
188
|
+
|
|
189
|
+
if mode == "ralph":
|
|
190
|
+
return """## Resume Instructions
|
|
191
|
+
|
|
192
|
+
**CRITICAL**: This is a continuation session. Follow these rules:
|
|
193
|
+
|
|
194
|
+
1. **DO NOT restart from the beginning** - Work has already been done
|
|
195
|
+
2. **Read the handoff document first** - Contains full context
|
|
196
|
+
3. **Continue from the current item** - Pick up exactly where left off
|
|
197
|
+
4. **Complete current task before moving on** - Don't skip items
|
|
198
|
+
5. **Signal completion with `<promise>COMPLETE</promise>`** when done
|
|
199
|
+
|
|
200
|
+
### Ralph Mode Guidelines
|
|
201
|
+
|
|
202
|
+
- The loop will continue until completion or safety limits
|
|
203
|
+
- Create git checkpoints at each iteration
|
|
204
|
+
- If stuck, write to `fix_plan.md` for next iteration
|
|
205
|
+
- Report progress to `progress.txt`
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
"""
|
|
209
|
+
else:
|
|
210
|
+
return """## Resume Instructions
|
|
211
|
+
|
|
212
|
+
**CRITICAL**: This is a continuation of a previous session. Follow these steps:
|
|
213
|
+
|
|
214
|
+
1. **Read the handoff document** - Contains full context from previous session
|
|
215
|
+
2. **Check git status** - See what files were modified
|
|
216
|
+
3. **Review the remaining todo items** - Understand what's left
|
|
217
|
+
4. **Continue from where left off** - Don't restart completed work
|
|
218
|
+
|
|
219
|
+
### Manual Resume Checklist
|
|
220
|
+
|
|
221
|
+
- [ ] Read handoff document
|
|
222
|
+
- [ ] Run `git status` and `git diff` to see current state
|
|
223
|
+
- [ ] Review any uncommitted changes
|
|
224
|
+
- [ ] Understand the current task context
|
|
225
|
+
- [ ] Continue implementation from the stopping point
|
|
226
|
+
|
|
227
|
+
### If Unsure
|
|
228
|
+
|
|
229
|
+
If the context is unclear, ask clarifying questions before proceeding.
|
|
230
|
+
It's better to confirm understanding than to redo completed work.
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
"""
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
def get_next_steps(
|
|
237
|
+
current_todo_item: str,
|
|
238
|
+
remaining_items: List[str],
|
|
239
|
+
) -> str:
|
|
240
|
+
"""Generate next steps section."""
|
|
241
|
+
steps = []
|
|
242
|
+
|
|
243
|
+
if current_todo_item:
|
|
244
|
+
steps.append(f"1. Complete current item: **{current_todo_item}**")
|
|
245
|
+
|
|
246
|
+
if remaining_items:
|
|
247
|
+
next_item = remaining_items[0] if not current_todo_item else (
|
|
248
|
+
remaining_items[1] if len(remaining_items) > 1 else None
|
|
249
|
+
)
|
|
250
|
+
if next_item:
|
|
251
|
+
steps.append(f"2. Move to next item: {next_item}")
|
|
252
|
+
|
|
253
|
+
if not steps:
|
|
254
|
+
steps.append("1. Review handoff document for context")
|
|
255
|
+
steps.append("2. Determine what work remains")
|
|
256
|
+
|
|
257
|
+
steps.append(f"{len(steps) + 1}. Continue until task is complete")
|
|
258
|
+
|
|
259
|
+
return f"""## Next Steps
|
|
260
|
+
|
|
261
|
+
{chr(10).join(steps)}
|
|
262
|
+
"""
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
def get_footer(mode: str = "ralph") -> str:
|
|
266
|
+
"""Generate footer section."""
|
|
267
|
+
timestamp = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
268
|
+
|
|
269
|
+
if mode == "ralph":
|
|
270
|
+
return f"""
|
|
271
|
+
---
|
|
272
|
+
|
|
273
|
+
*This prompt was auto-generated by ralph_prompt_generator.py (ANV-201)*
|
|
274
|
+
*Generated at: {timestamp}*
|
|
275
|
+
*Mode: Ralph Autonomous Execution*
|
|
276
|
+
"""
|
|
277
|
+
else:
|
|
278
|
+
return f"""
|
|
279
|
+
---
|
|
280
|
+
|
|
281
|
+
*This prompt was auto-generated for manual session resumption (ANV-201)*
|
|
282
|
+
*Generated at: {timestamp}*
|
|
283
|
+
*Mode: Manual Resume*
|
|
284
|
+
"""
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
# =============================================================================
|
|
288
|
+
# Main Generator Functions
|
|
289
|
+
# =============================================================================
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
def generate_resume_prompt(
|
|
293
|
+
level: str = "L2",
|
|
294
|
+
percent: int = 85,
|
|
295
|
+
timestamp: Optional[str] = None,
|
|
296
|
+
task_name: str = "",
|
|
297
|
+
objective: str = "",
|
|
298
|
+
iteration: int = 0,
|
|
299
|
+
handoff_file: str = "",
|
|
300
|
+
current_todo_item: str = "",
|
|
301
|
+
progress_on_item: str = "",
|
|
302
|
+
files_in_progress: Optional[List[Dict[str, Any]]] = None,
|
|
303
|
+
completed_items: Optional[List[str]] = None,
|
|
304
|
+
remaining_items: Optional[List[str]] = None,
|
|
305
|
+
mode: str = "ralph",
|
|
306
|
+
include_anvil_memory: bool = True,
|
|
307
|
+
) -> str:
|
|
308
|
+
"""Generate a complete resume PROMPT.md.
|
|
309
|
+
|
|
310
|
+
Args:
|
|
311
|
+
level: CCS level that triggered checkpoint (L1, L2, L3)
|
|
312
|
+
percent: Context percentage at checkpoint
|
|
313
|
+
timestamp: Checkpoint timestamp (auto-generated if not provided)
|
|
314
|
+
task_name: Name of the task being worked on
|
|
315
|
+
objective: Task objective description
|
|
316
|
+
iteration: Ralph iteration number
|
|
317
|
+
handoff_file: Path to handoff document
|
|
318
|
+
current_todo_item: Item currently being worked on
|
|
319
|
+
progress_on_item: Progress description for current item
|
|
320
|
+
files_in_progress: List of files being modified
|
|
321
|
+
completed_items: List of completed todo items
|
|
322
|
+
remaining_items: List of remaining todo items
|
|
323
|
+
mode: "ralph" or "manual" - affects instructions
|
|
324
|
+
include_anvil_memory: Whether to include memory query instructions
|
|
325
|
+
|
|
326
|
+
Returns:
|
|
327
|
+
Complete PROMPT.md content as string
|
|
328
|
+
"""
|
|
329
|
+
if timestamp is None:
|
|
330
|
+
timestamp = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
331
|
+
|
|
332
|
+
files_in_progress = files_in_progress or []
|
|
333
|
+
completed_items = completed_items or []
|
|
334
|
+
remaining_items = remaining_items or []
|
|
335
|
+
total_items = len(completed_items) + len(remaining_items)
|
|
336
|
+
if current_todo_item and current_todo_item not in remaining_items:
|
|
337
|
+
total_items += 1
|
|
338
|
+
|
|
339
|
+
sections = [
|
|
340
|
+
get_checkpoint_header(level, percent, timestamp, mode),
|
|
341
|
+
get_session_summary(task_name, objective, iteration, completed_items, total_items),
|
|
342
|
+
get_handoff_reference(handoff_file),
|
|
343
|
+
get_memory_query_instructions(include_anvil_memory),
|
|
344
|
+
get_current_task_context(
|
|
345
|
+
current_todo_item, progress_on_item, files_in_progress, remaining_items
|
|
346
|
+
),
|
|
347
|
+
get_resume_instructions(mode),
|
|
348
|
+
get_next_steps(current_todo_item, remaining_items),
|
|
349
|
+
get_footer(mode),
|
|
350
|
+
]
|
|
351
|
+
|
|
352
|
+
return "".join(sections)
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
def generate_from_state(
|
|
356
|
+
state_file: str = DEFAULT_STATE_FILE,
|
|
357
|
+
mode: str = "ralph",
|
|
358
|
+
include_anvil_memory: bool = True,
|
|
359
|
+
) -> str:
|
|
360
|
+
"""Generate resume prompt from Ralph state file.
|
|
361
|
+
|
|
362
|
+
Args:
|
|
363
|
+
state_file: Path to ralph-state.json
|
|
364
|
+
mode: "ralph" or "manual"
|
|
365
|
+
include_anvil_memory: Whether to include memory query instructions
|
|
366
|
+
|
|
367
|
+
Returns:
|
|
368
|
+
Complete PROMPT.md content as string
|
|
369
|
+
"""
|
|
370
|
+
state_path = Path(state_file)
|
|
371
|
+
if not state_path.exists():
|
|
372
|
+
raise FileNotFoundError(f"State file not found: {state_file}")
|
|
373
|
+
|
|
374
|
+
with open(state_path) as f:
|
|
375
|
+
state = json.load(f)
|
|
376
|
+
|
|
377
|
+
# Extract checkpoint info
|
|
378
|
+
checkpoint = state.get("context_checkpoint", {})
|
|
379
|
+
|
|
380
|
+
return generate_resume_prompt(
|
|
381
|
+
level=checkpoint.get("level", "L2"),
|
|
382
|
+
percent=checkpoint.get("percent_at_checkpoint", 85),
|
|
383
|
+
timestamp=checkpoint.get("timestamp"),
|
|
384
|
+
task_name=state.get("task_name", "Unknown Task"),
|
|
385
|
+
objective=state.get("objective", ""),
|
|
386
|
+
iteration=state.get("iteration", 0),
|
|
387
|
+
handoff_file=checkpoint.get("handoff_file", ""),
|
|
388
|
+
current_todo_item=checkpoint.get("current_todo_item", ""),
|
|
389
|
+
progress_on_item=checkpoint.get("progress_on_item", ""),
|
|
390
|
+
files_in_progress=checkpoint.get("files_in_progress", []),
|
|
391
|
+
completed_items=state.get("completed_items", []),
|
|
392
|
+
remaining_items=state.get("todo_items", []),
|
|
393
|
+
mode=mode,
|
|
394
|
+
include_anvil_memory=include_anvil_memory,
|
|
395
|
+
)
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
def generate_from_handoff(
|
|
399
|
+
handoff_file: str,
|
|
400
|
+
mode: str = "manual",
|
|
401
|
+
include_anvil_memory: bool = True,
|
|
402
|
+
) -> str:
|
|
403
|
+
"""Generate resume prompt from a handoff document.
|
|
404
|
+
|
|
405
|
+
Args:
|
|
406
|
+
handoff_file: Path to handoff markdown file
|
|
407
|
+
mode: "ralph" or "manual"
|
|
408
|
+
include_anvil_memory: Whether to include memory query instructions
|
|
409
|
+
|
|
410
|
+
Returns:
|
|
411
|
+
Complete PROMPT.md content as string
|
|
412
|
+
"""
|
|
413
|
+
handoff_path = Path(handoff_file)
|
|
414
|
+
if not handoff_path.exists():
|
|
415
|
+
raise FileNotFoundError(f"Handoff file not found: {handoff_file}")
|
|
416
|
+
|
|
417
|
+
content = handoff_path.read_text()
|
|
418
|
+
|
|
419
|
+
# Parse YAML frontmatter if present
|
|
420
|
+
task_name = ""
|
|
421
|
+
level = "L2"
|
|
422
|
+
percent = 85
|
|
423
|
+
|
|
424
|
+
if content.startswith("---"):
|
|
425
|
+
parts = content.split("---", 2)
|
|
426
|
+
if len(parts) >= 3:
|
|
427
|
+
import yaml
|
|
428
|
+
try:
|
|
429
|
+
meta = yaml.safe_load(parts[1])
|
|
430
|
+
task_name = meta.get("linear_issues", "")
|
|
431
|
+
# Try to extract checkpoint info from frontmatter
|
|
432
|
+
if "checkpoint_trigger" in meta:
|
|
433
|
+
trigger = meta["checkpoint_trigger"]
|
|
434
|
+
if "L3" in trigger:
|
|
435
|
+
level = "L3"
|
|
436
|
+
elif "L2" in trigger:
|
|
437
|
+
level = "L2"
|
|
438
|
+
elif "L1" in trigger:
|
|
439
|
+
level = "L1"
|
|
440
|
+
if "context_at_checkpoint" in meta:
|
|
441
|
+
percent = meta["context_at_checkpoint"]
|
|
442
|
+
except Exception:
|
|
443
|
+
pass
|
|
444
|
+
|
|
445
|
+
# Extract task name from first heading if not in frontmatter
|
|
446
|
+
if not task_name:
|
|
447
|
+
for line in content.split("\n"):
|
|
448
|
+
if line.startswith("# "):
|
|
449
|
+
task_name = line[2:].strip()
|
|
450
|
+
break
|
|
451
|
+
|
|
452
|
+
return generate_resume_prompt(
|
|
453
|
+
level=level,
|
|
454
|
+
percent=percent,
|
|
455
|
+
task_name=task_name,
|
|
456
|
+
objective="Resume from handoff document",
|
|
457
|
+
handoff_file=handoff_file,
|
|
458
|
+
mode=mode,
|
|
459
|
+
include_anvil_memory=include_anvil_memory,
|
|
460
|
+
)
|
|
461
|
+
|
|
462
|
+
|
|
463
|
+
def write_resume_prompt(
|
|
464
|
+
content: str,
|
|
465
|
+
output_file: str = DEFAULT_PROMPT_FILE,
|
|
466
|
+
) -> str:
|
|
467
|
+
"""Write resume prompt to file.
|
|
468
|
+
|
|
469
|
+
Args:
|
|
470
|
+
content: PROMPT.md content
|
|
471
|
+
output_file: Output file path
|
|
472
|
+
|
|
473
|
+
Returns:
|
|
474
|
+
Path to written file
|
|
475
|
+
"""
|
|
476
|
+
output_path = Path(output_file)
|
|
477
|
+
output_path.write_text(content)
|
|
478
|
+
return str(output_path)
|
|
479
|
+
|
|
480
|
+
|
|
481
|
+
# =============================================================================
|
|
482
|
+
# CLI Entry Point
|
|
483
|
+
# =============================================================================
|
|
484
|
+
|
|
485
|
+
|
|
486
|
+
def main():
|
|
487
|
+
"""CLI entry point for ralph_prompt_generator."""
|
|
488
|
+
import argparse
|
|
489
|
+
|
|
490
|
+
parser = argparse.ArgumentParser(
|
|
491
|
+
description="Generate PROMPT.md resume templates for checkpoint continuation"
|
|
492
|
+
)
|
|
493
|
+
|
|
494
|
+
parser.add_argument(
|
|
495
|
+
"--mode",
|
|
496
|
+
choices=["ralph", "manual"],
|
|
497
|
+
default="ralph",
|
|
498
|
+
help="Resume mode: ralph (autonomous) or manual",
|
|
499
|
+
)
|
|
500
|
+
|
|
501
|
+
parser.add_argument(
|
|
502
|
+
"--state",
|
|
503
|
+
default=DEFAULT_STATE_FILE,
|
|
504
|
+
help=f"Path to ralph-state.json (default: {DEFAULT_STATE_FILE})",
|
|
505
|
+
)
|
|
506
|
+
|
|
507
|
+
parser.add_argument(
|
|
508
|
+
"--handoff",
|
|
509
|
+
help="Path to handoff document (for manual mode)",
|
|
510
|
+
)
|
|
511
|
+
|
|
512
|
+
parser.add_argument(
|
|
513
|
+
"--output",
|
|
514
|
+
default=DEFAULT_PROMPT_FILE,
|
|
515
|
+
help=f"Output file path (default: {DEFAULT_PROMPT_FILE})",
|
|
516
|
+
)
|
|
517
|
+
|
|
518
|
+
parser.add_argument(
|
|
519
|
+
"--no-memory",
|
|
520
|
+
action="store_true",
|
|
521
|
+
help="Exclude Anvil Memory query instructions",
|
|
522
|
+
)
|
|
523
|
+
|
|
524
|
+
parser.add_argument(
|
|
525
|
+
"--dry-run",
|
|
526
|
+
action="store_true",
|
|
527
|
+
help="Print to stdout instead of writing file",
|
|
528
|
+
)
|
|
529
|
+
|
|
530
|
+
args = parser.parse_args()
|
|
531
|
+
|
|
532
|
+
include_memory = not args.no_memory
|
|
533
|
+
|
|
534
|
+
try:
|
|
535
|
+
if args.handoff:
|
|
536
|
+
content = generate_from_handoff(
|
|
537
|
+
args.handoff,
|
|
538
|
+
mode=args.mode,
|
|
539
|
+
include_anvil_memory=include_memory,
|
|
540
|
+
)
|
|
541
|
+
else:
|
|
542
|
+
content = generate_from_state(
|
|
543
|
+
args.state,
|
|
544
|
+
mode=args.mode,
|
|
545
|
+
include_anvil_memory=include_memory,
|
|
546
|
+
)
|
|
547
|
+
|
|
548
|
+
if args.dry_run:
|
|
549
|
+
print(content)
|
|
550
|
+
else:
|
|
551
|
+
output_path = write_resume_prompt(content, args.output)
|
|
552
|
+
print(f"Generated resume prompt: {output_path}")
|
|
553
|
+
|
|
554
|
+
except FileNotFoundError as e:
|
|
555
|
+
print(f"Error: {e}")
|
|
556
|
+
exit(1)
|
|
557
|
+
except Exception as e:
|
|
558
|
+
print(f"Error generating resume prompt: {e}")
|
|
559
|
+
exit(1)
|
|
560
|
+
|
|
561
|
+
|
|
562
|
+
if __name__ == "__main__":
|
|
563
|
+
main()
|