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.
Files changed (190) hide show
  1. package/README.md +719 -0
  2. package/VERSION +1 -0
  3. package/docs/ANVIL-REPO-IMPLEMENTATION-PLAN.md +441 -0
  4. package/docs/FIRST-SKILL-TUTORIAL.md +408 -0
  5. package/docs/INSTALLATION-RETRO-NOTES.md +458 -0
  6. package/docs/INSTALLATION.md +984 -0
  7. package/docs/anvil-hud.md +469 -0
  8. package/docs/anvil-init.md +255 -0
  9. package/docs/anvil-state.md +210 -0
  10. package/docs/boris-cherny-ralph-wiggum-insights.md +608 -0
  11. package/docs/command-reference.md +2022 -0
  12. package/docs/hooks-tts.md +368 -0
  13. package/docs/implementation-guide.md +810 -0
  14. package/docs/linear-github-integration.md +247 -0
  15. package/docs/local-issues.md +677 -0
  16. package/docs/patterns/README.md +419 -0
  17. package/docs/planning-responsibilities.md +139 -0
  18. package/docs/session-workflow.md +573 -0
  19. package/docs/simplification-plan-template.md +297 -0
  20. package/docs/simplification-principles.md +129 -0
  21. package/docs/specifications/CCS-RALPH-INTEGRATION-DESIGN.md +633 -0
  22. package/docs/specifications/CCS-RESEARCH-REPORT.md +169 -0
  23. package/docs/specifications/PLAN-ANV-verification-ralph-wiggum.md +403 -0
  24. package/docs/specifications/PLAN-parallel-tracks-anvil-memory-ccs.md +494 -0
  25. package/docs/specifications/SPEC-ANV-VRW/component-01-verify.md +208 -0
  26. package/docs/specifications/SPEC-ANV-VRW/component-02-stop-gate.md +226 -0
  27. package/docs/specifications/SPEC-ANV-VRW/component-03-posttooluse.md +209 -0
  28. package/docs/specifications/SPEC-ANV-VRW/component-04-ralph-wiggum.md +604 -0
  29. package/docs/specifications/SPEC-ANV-VRW/component-05-atomic-actions.md +311 -0
  30. package/docs/specifications/SPEC-ANV-VRW/component-06-verify-subagent.md +264 -0
  31. package/docs/specifications/SPEC-ANV-VRW/component-07-claude-md.md +363 -0
  32. package/docs/specifications/SPEC-ANV-VRW/index.md +182 -0
  33. package/docs/specifications/SPEC-ANV-anvil-memory.md +573 -0
  34. package/docs/specifications/SPEC-ANV-context-checkpoints.md +781 -0
  35. package/docs/specifications/SPEC-ANV-verification-ralph-wiggum.md +789 -0
  36. package/docs/sync.md +122 -0
  37. package/global/CLAUDE.md +140 -0
  38. package/global/agents/verify-app.md +164 -0
  39. package/global/commands/anvil-settings.md +527 -0
  40. package/global/commands/anvil-sync.md +121 -0
  41. package/global/commands/change.md +197 -0
  42. package/global/commands/clarify.md +252 -0
  43. package/global/commands/cleanup.md +292 -0
  44. package/global/commands/commit-push-pr.md +207 -0
  45. package/global/commands/decay-review.md +127 -0
  46. package/global/commands/discover.md +158 -0
  47. package/global/commands/doc-coverage.md +122 -0
  48. package/global/commands/evidence.md +307 -0
  49. package/global/commands/explore.md +121 -0
  50. package/global/commands/force-exit.md +135 -0
  51. package/global/commands/handoff.md +191 -0
  52. package/global/commands/healthcheck.md +302 -0
  53. package/global/commands/hud.md +84 -0
  54. package/global/commands/insights.md +319 -0
  55. package/global/commands/linear-setup.md +184 -0
  56. package/global/commands/lint-fix.md +198 -0
  57. package/global/commands/orient.md +510 -0
  58. package/global/commands/plan.md +228 -0
  59. package/global/commands/ralph.md +346 -0
  60. package/global/commands/ready.md +182 -0
  61. package/global/commands/release.md +305 -0
  62. package/global/commands/retro.md +96 -0
  63. package/global/commands/shard.md +166 -0
  64. package/global/commands/spec.md +227 -0
  65. package/global/commands/sprint.md +184 -0
  66. package/global/commands/tasks.md +228 -0
  67. package/global/commands/test-and-commit.md +151 -0
  68. package/global/commands/validate.md +132 -0
  69. package/global/commands/verify.md +251 -0
  70. package/global/commands/weekly-review.md +156 -0
  71. package/global/hooks/__pycache__/ralph_context_monitor.cpython-314.pyc +0 -0
  72. package/global/hooks/__pycache__/statusline_agent_sync.cpython-314.pyc +0 -0
  73. package/global/hooks/anvil_memory_observe.ts +322 -0
  74. package/global/hooks/anvil_memory_session.ts +166 -0
  75. package/global/hooks/anvil_memory_stop.ts +187 -0
  76. package/global/hooks/parse_transcript.py +116 -0
  77. package/global/hooks/post_merge_cleanup.sh +132 -0
  78. package/global/hooks/post_tool_format.sh +215 -0
  79. package/global/hooks/ralph_context_monitor.py +240 -0
  80. package/global/hooks/ralph_stop.sh +502 -0
  81. package/global/hooks/statusline.sh +1110 -0
  82. package/global/hooks/statusline_agent_sync.py +224 -0
  83. package/global/hooks/stop_gate.sh +250 -0
  84. package/global/lib/.claude/anvil-state.json +21 -0
  85. package/global/lib/__pycache__/agent_registry.cpython-314.pyc +0 -0
  86. package/global/lib/__pycache__/claim_service.cpython-314.pyc +0 -0
  87. package/global/lib/__pycache__/coderabbit_service.cpython-314.pyc +0 -0
  88. package/global/lib/__pycache__/config_service.cpython-314.pyc +0 -0
  89. package/global/lib/__pycache__/coordination_service.cpython-314.pyc +0 -0
  90. package/global/lib/__pycache__/doc_coverage_service.cpython-314.pyc +0 -0
  91. package/global/lib/__pycache__/gate_logger.cpython-314.pyc +0 -0
  92. package/global/lib/__pycache__/github_service.cpython-314.pyc +0 -0
  93. package/global/lib/__pycache__/hygiene_service.cpython-314.pyc +0 -0
  94. package/global/lib/__pycache__/issue_models.cpython-314.pyc +0 -0
  95. package/global/lib/__pycache__/issue_provider.cpython-314.pyc +0 -0
  96. package/global/lib/__pycache__/linear_data_service.cpython-314.pyc +0 -0
  97. package/global/lib/__pycache__/linear_provider.cpython-314.pyc +0 -0
  98. package/global/lib/__pycache__/local_provider.cpython-314.pyc +0 -0
  99. package/global/lib/__pycache__/quality_service.cpython-314.pyc +0 -0
  100. package/global/lib/__pycache__/ralph_state.cpython-314.pyc +0 -0
  101. package/global/lib/__pycache__/state_manager.cpython-314.pyc +0 -0
  102. package/global/lib/__pycache__/transcript_parser.cpython-314.pyc +0 -0
  103. package/global/lib/__pycache__/verification_runner.cpython-314.pyc +0 -0
  104. package/global/lib/__pycache__/verify_iteration.cpython-314.pyc +0 -0
  105. package/global/lib/__pycache__/verify_subagent.cpython-314.pyc +0 -0
  106. package/global/lib/agent_registry.py +995 -0
  107. package/global/lib/anvil-state.sh +435 -0
  108. package/global/lib/claim_service.py +515 -0
  109. package/global/lib/coderabbit_service.py +314 -0
  110. package/global/lib/config_service.py +423 -0
  111. package/global/lib/coordination_service.py +331 -0
  112. package/global/lib/doc_coverage_service.py +1305 -0
  113. package/global/lib/gate_logger.py +316 -0
  114. package/global/lib/github_service.py +310 -0
  115. package/global/lib/handoff_generator.py +775 -0
  116. package/global/lib/hygiene_service.py +712 -0
  117. package/global/lib/issue_models.py +257 -0
  118. package/global/lib/issue_provider.py +339 -0
  119. package/global/lib/linear_data_service.py +210 -0
  120. package/global/lib/linear_provider.py +987 -0
  121. package/global/lib/linear_provider.py.backup +671 -0
  122. package/global/lib/local_provider.py +486 -0
  123. package/global/lib/orient_fast.py +457 -0
  124. package/global/lib/quality_service.py +470 -0
  125. package/global/lib/ralph_prompt_generator.py +563 -0
  126. package/global/lib/ralph_state.py +1202 -0
  127. package/global/lib/state_manager.py +417 -0
  128. package/global/lib/transcript_parser.py +597 -0
  129. package/global/lib/verification_runner.py +557 -0
  130. package/global/lib/verify_iteration.py +490 -0
  131. package/global/lib/verify_subagent.py +250 -0
  132. package/global/skills/README.md +155 -0
  133. package/global/skills/quality-gates/SKILL.md +252 -0
  134. package/global/skills/skill-template/SKILL.md +109 -0
  135. package/global/skills/testing-strategies/SKILL.md +337 -0
  136. package/global/templates/CHANGE-template.md +105 -0
  137. package/global/templates/HANDOFF-template.md +63 -0
  138. package/global/templates/PLAN-template.md +111 -0
  139. package/global/templates/SPEC-template.md +93 -0
  140. package/global/templates/ralph/PROMPT.md.template +89 -0
  141. package/global/templates/ralph/fix_plan.md.template +31 -0
  142. package/global/templates/ralph/progress.txt.template +23 -0
  143. package/global/tests/__pycache__/test_doc_coverage.cpython-314.pyc +0 -0
  144. package/global/tests/test_doc_coverage.py +520 -0
  145. package/global/tests/test_issue_models.py +299 -0
  146. package/global/tests/test_local_provider.py +323 -0
  147. package/global/tools/README.md +178 -0
  148. package/global/tools/__pycache__/anvil-hud.cpython-314.pyc +0 -0
  149. package/global/tools/anvil-hud.py +3622 -0
  150. package/global/tools/anvil-hud.py.bak +3318 -0
  151. package/global/tools/anvil-issue.py +432 -0
  152. package/global/tools/anvil-memory/CLAUDE.md +49 -0
  153. package/global/tools/anvil-memory/README.md +42 -0
  154. package/global/tools/anvil-memory/bun.lock +25 -0
  155. package/global/tools/anvil-memory/bunfig.toml +9 -0
  156. package/global/tools/anvil-memory/package.json +23 -0
  157. package/global/tools/anvil-memory/src/__tests__/ccs/context-monitor.test.ts +535 -0
  158. package/global/tools/anvil-memory/src/__tests__/ccs/edge-cases.test.ts +645 -0
  159. package/global/tools/anvil-memory/src/__tests__/ccs/fixtures.ts +363 -0
  160. package/global/tools/anvil-memory/src/__tests__/ccs/index.ts +8 -0
  161. package/global/tools/anvil-memory/src/__tests__/ccs/integration.test.ts +417 -0
  162. package/global/tools/anvil-memory/src/__tests__/ccs/prompt-generator.test.ts +571 -0
  163. package/global/tools/anvil-memory/src/__tests__/ccs/ralph-stop.test.ts +440 -0
  164. package/global/tools/anvil-memory/src/__tests__/ccs/test-utils.ts +252 -0
  165. package/global/tools/anvil-memory/src/__tests__/commands.test.ts +657 -0
  166. package/global/tools/anvil-memory/src/__tests__/db.test.ts +641 -0
  167. package/global/tools/anvil-memory/src/__tests__/hooks.test.ts +272 -0
  168. package/global/tools/anvil-memory/src/__tests__/performance.test.ts +427 -0
  169. package/global/tools/anvil-memory/src/__tests__/test-utils.ts +113 -0
  170. package/global/tools/anvil-memory/src/commands/checkpoint.ts +197 -0
  171. package/global/tools/anvil-memory/src/commands/get.ts +115 -0
  172. package/global/tools/anvil-memory/src/commands/init.ts +94 -0
  173. package/global/tools/anvil-memory/src/commands/observe.ts +163 -0
  174. package/global/tools/anvil-memory/src/commands/search.ts +112 -0
  175. package/global/tools/anvil-memory/src/db.ts +638 -0
  176. package/global/tools/anvil-memory/src/index.ts +205 -0
  177. package/global/tools/anvil-memory/src/types.ts +122 -0
  178. package/global/tools/anvil-memory/tsconfig.json +29 -0
  179. package/global/tools/ralph-loop.sh +359 -0
  180. package/package.json +45 -0
  181. package/scripts/anvil +822 -0
  182. package/scripts/extract_patterns.py +222 -0
  183. package/scripts/init-project.sh +541 -0
  184. package/scripts/install.sh +229 -0
  185. package/scripts/postinstall.js +41 -0
  186. package/scripts/rollback.sh +188 -0
  187. package/scripts/sync.sh +623 -0
  188. package/scripts/test-statusline.sh +248 -0
  189. package/scripts/update_claude_md.py +224 -0
  190. 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()