aiwcli 0.10.3 → 0.11.1

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 (191) hide show
  1. package/bin/run.js +1 -1
  2. package/dist/commands/clear.js +28 -131
  3. package/dist/commands/init/index.js +3 -3
  4. package/dist/lib/gitignore-manager.d.ts +32 -0
  5. package/dist/lib/gitignore-manager.js +141 -2
  6. package/dist/templates/CLAUDE.md +8 -8
  7. package/dist/templates/_shared/.claude/commands/handoff-resume.md +64 -0
  8. package/dist/templates/_shared/.claude/commands/handoff.md +16 -10
  9. package/dist/templates/_shared/.claude/settings.json +7 -7
  10. package/dist/templates/_shared/hooks-ts/_utils/git-state.ts +2 -0
  11. package/dist/templates/_shared/hooks-ts/archive_plan.ts +159 -0
  12. package/dist/templates/_shared/hooks-ts/context_monitor.ts +147 -0
  13. package/dist/templates/_shared/hooks-ts/file-suggestion.ts +130 -0
  14. package/dist/templates/_shared/hooks-ts/pre_compact.ts +49 -0
  15. package/dist/templates/_shared/hooks-ts/session_end.ts +107 -0
  16. package/dist/templates/_shared/hooks-ts/session_start.ts +144 -0
  17. package/dist/templates/_shared/hooks-ts/task_create_capture.ts +48 -0
  18. package/dist/templates/_shared/hooks-ts/task_update_capture.ts +74 -0
  19. package/dist/templates/_shared/hooks-ts/user_prompt_submit.ts +83 -0
  20. package/dist/templates/_shared/lib-ts/CLAUDE.md +318 -0
  21. package/dist/templates/_shared/lib-ts/base/atomic-write.ts +12 -12
  22. package/dist/templates/_shared/lib-ts/base/constants.ts +22 -15
  23. package/dist/templates/_shared/lib-ts/base/git-state.ts +1 -1
  24. package/dist/templates/_shared/lib-ts/base/hook-utils.ts +129 -50
  25. package/dist/templates/_shared/lib-ts/base/inference.ts +28 -21
  26. package/dist/templates/_shared/lib-ts/base/logger.ts +15 -2
  27. package/dist/templates/_shared/lib-ts/base/state-io.ts +9 -7
  28. package/dist/templates/_shared/lib-ts/base/stop-words.ts +131 -131
  29. package/dist/templates/_shared/lib-ts/base/subprocess-utils.ts +142 -0
  30. package/dist/templates/_shared/lib-ts/base/utils.ts +69 -69
  31. package/dist/templates/_shared/lib-ts/context/context-formatter.ts +30 -24
  32. package/dist/templates/_shared/lib-ts/context/context-selector.ts +50 -32
  33. package/dist/templates/_shared/lib-ts/context/context-store.ts +76 -48
  34. package/dist/templates/_shared/lib-ts/context/plan-manager.ts +43 -23
  35. package/dist/templates/_shared/lib-ts/context/task-tracker.ts +10 -6
  36. package/dist/templates/_shared/lib-ts/handoff/document-generator.ts +11 -10
  37. package/dist/templates/_shared/lib-ts/handoff/handoff-reader.ts +158 -0
  38. package/dist/templates/_shared/lib-ts/templates/formatters.ts +6 -4
  39. package/dist/templates/_shared/lib-ts/types.ts +68 -55
  40. package/dist/templates/_shared/scripts/resolve_context.ts +24 -0
  41. package/dist/templates/_shared/scripts/resume_handoff.ts +345 -0
  42. package/dist/templates/_shared/scripts/save_handoff.ts +3 -3
  43. package/dist/templates/_shared/scripts/status_line.ts +687 -0
  44. package/dist/templates/cc-native/.claude/settings.json +175 -185
  45. package/dist/templates/cc-native/TEMPLATE-SCHEMA.md +15 -17
  46. package/dist/templates/cc-native/_cc-native/agents/CLAUDE.md +0 -2
  47. package/dist/templates/cc-native/_cc-native/hooks/CLAUDE.md +109 -135
  48. package/dist/templates/cc-native/_cc-native/hooks/add_plan_context.ts +119 -0
  49. package/dist/templates/cc-native/_cc-native/hooks/cc-native-plan-review.ts +1027 -0
  50. package/dist/templates/cc-native/_cc-native/hooks/plan_questions_early.ts +61 -0
  51. package/dist/templates/cc-native/_cc-native/lib-ts/aggregate-agents.ts +156 -0
  52. package/dist/templates/cc-native/_cc-native/lib-ts/artifacts.ts +792 -0
  53. package/dist/templates/cc-native/_cc-native/lib-ts/cc-native-state.ts +199 -0
  54. package/dist/templates/cc-native/_cc-native/lib-ts/cli-output-parser.ts +144 -0
  55. package/dist/templates/cc-native/_cc-native/lib-ts/config.ts +57 -0
  56. package/dist/templates/cc-native/_cc-native/lib-ts/constants.ts +83 -0
  57. package/dist/templates/cc-native/_cc-native/lib-ts/corroboration.ts +115 -0
  58. package/dist/templates/cc-native/_cc-native/lib-ts/debug.ts +80 -0
  59. package/dist/templates/cc-native/_cc-native/lib-ts/index.ts +120 -0
  60. package/dist/templates/cc-native/_cc-native/lib-ts/json-parser.ts +168 -0
  61. package/dist/templates/cc-native/_cc-native/lib-ts/nul +3 -0
  62. package/dist/templates/cc-native/_cc-native/lib-ts/orchestrator.ts +250 -0
  63. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/agent.ts +275 -0
  64. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/codex.ts +130 -0
  65. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/gemini.ts +107 -0
  66. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/index.ts +10 -0
  67. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/types.ts +23 -0
  68. package/dist/templates/cc-native/_cc-native/lib-ts/state.ts +240 -0
  69. package/dist/templates/cc-native/_cc-native/lib-ts/tsconfig.json +18 -0
  70. package/dist/templates/cc-native/_cc-native/lib-ts/types.ts +385 -0
  71. package/dist/templates/cc-native/_cc-native/lib-ts/verdict.ts +72 -0
  72. package/dist/templates/cc-native/_cc-native/plan-review.config.json +14 -1
  73. package/oclif.manifest.json +1 -1
  74. package/package.json +2 -2
  75. package/dist/templates/_shared/hooks/__init__.py +0 -16
  76. package/dist/templates/_shared/hooks/__pycache__/__init__.cpython-313.pyc +0 -0
  77. package/dist/templates/_shared/hooks/__pycache__/archive_plan.cpython-313.pyc +0 -0
  78. package/dist/templates/_shared/hooks/__pycache__/context_enforcer.cpython-313.pyc +0 -0
  79. package/dist/templates/_shared/hooks/__pycache__/context_monitor.cpython-313.pyc +0 -0
  80. package/dist/templates/_shared/hooks/__pycache__/file-suggestion.cpython-313.pyc +0 -0
  81. package/dist/templates/_shared/hooks/__pycache__/pre_compact.cpython-313.pyc +0 -0
  82. package/dist/templates/_shared/hooks/__pycache__/session_end.cpython-313.pyc +0 -0
  83. package/dist/templates/_shared/hooks/__pycache__/session_start.cpython-313.pyc +0 -0
  84. package/dist/templates/_shared/hooks/__pycache__/task_create_atomicity.cpython-313.pyc +0 -0
  85. package/dist/templates/_shared/hooks/__pycache__/task_create_capture.cpython-313.pyc +0 -0
  86. package/dist/templates/_shared/hooks/__pycache__/task_update_capture.cpython-313.pyc +0 -0
  87. package/dist/templates/_shared/hooks/__pycache__/user_prompt_submit.cpython-313.pyc +0 -0
  88. package/dist/templates/_shared/hooks/archive_plan.py +0 -177
  89. package/dist/templates/_shared/hooks/context_monitor.py +0 -270
  90. package/dist/templates/_shared/hooks/file-suggestion.py +0 -215
  91. package/dist/templates/_shared/hooks/pre_compact.py +0 -104
  92. package/dist/templates/_shared/hooks/session_end.py +0 -173
  93. package/dist/templates/_shared/hooks/session_start.py +0 -206
  94. package/dist/templates/_shared/hooks/task_create_capture.py +0 -108
  95. package/dist/templates/_shared/hooks/task_update_capture.py +0 -145
  96. package/dist/templates/_shared/hooks/user_prompt_submit.py +0 -139
  97. package/dist/templates/_shared/lib/__init__.py +0 -1
  98. package/dist/templates/_shared/lib/__pycache__/__init__.cpython-313.pyc +0 -0
  99. package/dist/templates/_shared/lib/base/__init__.py +0 -65
  100. package/dist/templates/_shared/lib/base/__pycache__/__init__.cpython-313.pyc +0 -0
  101. package/dist/templates/_shared/lib/base/__pycache__/atomic_write.cpython-313.pyc +0 -0
  102. package/dist/templates/_shared/lib/base/__pycache__/constants.cpython-313.pyc +0 -0
  103. package/dist/templates/_shared/lib/base/__pycache__/hook_utils.cpython-313.pyc +0 -0
  104. package/dist/templates/_shared/lib/base/__pycache__/inference.cpython-313.pyc +0 -0
  105. package/dist/templates/_shared/lib/base/__pycache__/logger.cpython-313.pyc +0 -0
  106. package/dist/templates/_shared/lib/base/__pycache__/stop_words.cpython-313.pyc +0 -0
  107. package/dist/templates/_shared/lib/base/__pycache__/subprocess_utils.cpython-313.pyc +0 -0
  108. package/dist/templates/_shared/lib/base/__pycache__/utils.cpython-313.pyc +0 -0
  109. package/dist/templates/_shared/lib/base/atomic_write.py +0 -180
  110. package/dist/templates/_shared/lib/base/constants.py +0 -358
  111. package/dist/templates/_shared/lib/base/hook_utils.py +0 -339
  112. package/dist/templates/_shared/lib/base/inference.py +0 -307
  113. package/dist/templates/_shared/lib/base/logger.py +0 -305
  114. package/dist/templates/_shared/lib/base/stop_words.py +0 -221
  115. package/dist/templates/_shared/lib/base/subprocess_utils.py +0 -46
  116. package/dist/templates/_shared/lib/base/utils.py +0 -263
  117. package/dist/templates/_shared/lib/context/__init__.py +0 -102
  118. package/dist/templates/_shared/lib/context/__pycache__/__init__.cpython-313.pyc +0 -0
  119. package/dist/templates/_shared/lib/context/__pycache__/cache.cpython-313.pyc +0 -0
  120. package/dist/templates/_shared/lib/context/__pycache__/context_extractor.cpython-313.pyc +0 -0
  121. package/dist/templates/_shared/lib/context/__pycache__/context_formatter.cpython-313.pyc +0 -0
  122. package/dist/templates/_shared/lib/context/__pycache__/context_manager.cpython-313.pyc +0 -0
  123. package/dist/templates/_shared/lib/context/__pycache__/context_selector.cpython-313.pyc +0 -0
  124. package/dist/templates/_shared/lib/context/__pycache__/context_store.cpython-313.pyc +0 -0
  125. package/dist/templates/_shared/lib/context/__pycache__/discovery.cpython-313.pyc +0 -0
  126. package/dist/templates/_shared/lib/context/__pycache__/event_log.cpython-313.pyc +0 -0
  127. package/dist/templates/_shared/lib/context/__pycache__/plan_archive.cpython-313.pyc +0 -0
  128. package/dist/templates/_shared/lib/context/__pycache__/plan_manager.cpython-313.pyc +0 -0
  129. package/dist/templates/_shared/lib/context/__pycache__/task_sync.cpython-313.pyc +0 -0
  130. package/dist/templates/_shared/lib/context/__pycache__/task_tracker.cpython-313.pyc +0 -0
  131. package/dist/templates/_shared/lib/context/context_formatter.py +0 -317
  132. package/dist/templates/_shared/lib/context/context_selector.py +0 -508
  133. package/dist/templates/_shared/lib/context/context_store.py +0 -653
  134. package/dist/templates/_shared/lib/context/plan_manager.py +0 -303
  135. package/dist/templates/_shared/lib/context/task_tracker.py +0 -188
  136. package/dist/templates/_shared/lib/handoff/__init__.py +0 -22
  137. package/dist/templates/_shared/lib/handoff/__pycache__/__init__.cpython-313.pyc +0 -0
  138. package/dist/templates/_shared/lib/handoff/__pycache__/document_generator.cpython-313.pyc +0 -0
  139. package/dist/templates/_shared/lib/handoff/document_generator.py +0 -278
  140. package/dist/templates/_shared/lib/templates/README.md +0 -206
  141. package/dist/templates/_shared/lib/templates/__init__.py +0 -36
  142. package/dist/templates/_shared/lib/templates/__pycache__/__init__.cpython-313.pyc +0 -0
  143. package/dist/templates/_shared/lib/templates/__pycache__/formatters.cpython-313.pyc +0 -0
  144. package/dist/templates/_shared/lib/templates/__pycache__/persona_questions.cpython-313.pyc +0 -0
  145. package/dist/templates/_shared/lib/templates/__pycache__/plan_context.cpython-313.pyc +0 -0
  146. package/dist/templates/_shared/lib/templates/formatters.py +0 -146
  147. package/dist/templates/_shared/lib/templates/plan_context.py +0 -73
  148. package/dist/templates/_shared/scripts/__pycache__/save_handoff.cpython-313.pyc +0 -0
  149. package/dist/templates/_shared/scripts/__pycache__/status_line.cpython-313.pyc +0 -0
  150. package/dist/templates/_shared/scripts/save_handoff.py +0 -357
  151. package/dist/templates/_shared/scripts/status_line.py +0 -716
  152. package/dist/templates/cc-native/.claude/commands/cc-native/fresh-perspective.md +0 -8
  153. package/dist/templates/cc-native/.windsurf/workflows/cc-native/fresh-perspective.md +0 -8
  154. package/dist/templates/cc-native/MIGRATION.md +0 -86
  155. package/dist/templates/cc-native/_cc-native/hooks/__pycache__/add_plan_context.cpython-313.pyc +0 -0
  156. package/dist/templates/cc-native/_cc-native/hooks/__pycache__/cc-native-plan-review.cpython-313.pyc +0 -0
  157. package/dist/templates/cc-native/_cc-native/hooks/__pycache__/mark_questions_asked.cpython-313.pyc +0 -0
  158. package/dist/templates/cc-native/_cc-native/hooks/__pycache__/plan_accepted.cpython-313.pyc +0 -0
  159. package/dist/templates/cc-native/_cc-native/hooks/__pycache__/plan_questions_early.cpython-313.pyc +0 -0
  160. package/dist/templates/cc-native/_cc-native/hooks/__pycache__/suggest-fresh-perspective.cpython-313.pyc +0 -0
  161. package/dist/templates/cc-native/_cc-native/hooks/add_plan_context.py +0 -130
  162. package/dist/templates/cc-native/_cc-native/hooks/cc-native-plan-review.py +0 -954
  163. package/dist/templates/cc-native/_cc-native/hooks/plan_questions_early.py +0 -81
  164. package/dist/templates/cc-native/_cc-native/hooks/suggest-fresh-perspective.py +0 -340
  165. package/dist/templates/cc-native/_cc-native/lib/CLAUDE.md +0 -265
  166. package/dist/templates/cc-native/_cc-native/lib/__init__.py +0 -53
  167. package/dist/templates/cc-native/_cc-native/lib/__pycache__/__init__.cpython-313.pyc +0 -0
  168. package/dist/templates/cc-native/_cc-native/lib/__pycache__/atomic_write.cpython-313.pyc +0 -0
  169. package/dist/templates/cc-native/_cc-native/lib/__pycache__/constants.cpython-313.pyc +0 -0
  170. package/dist/templates/cc-native/_cc-native/lib/__pycache__/debug.cpython-313.pyc +0 -0
  171. package/dist/templates/cc-native/_cc-native/lib/__pycache__/orchestrator.cpython-313.pyc +0 -0
  172. package/dist/templates/cc-native/_cc-native/lib/__pycache__/state.cpython-313.pyc +0 -0
  173. package/dist/templates/cc-native/_cc-native/lib/__pycache__/utils.cpython-313.pyc +0 -0
  174. package/dist/templates/cc-native/_cc-native/lib/constants.py +0 -45
  175. package/dist/templates/cc-native/_cc-native/lib/debug.py +0 -139
  176. package/dist/templates/cc-native/_cc-native/lib/orchestrator.py +0 -362
  177. package/dist/templates/cc-native/_cc-native/lib/reviewers/__init__.py +0 -28
  178. package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/__init__.cpython-313.pyc +0 -0
  179. package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/agent.cpython-313.pyc +0 -0
  180. package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/base.cpython-313.pyc +0 -0
  181. package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/codex.cpython-313.pyc +0 -0
  182. package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/gemini.cpython-313.pyc +0 -0
  183. package/dist/templates/cc-native/_cc-native/lib/reviewers/agent.py +0 -215
  184. package/dist/templates/cc-native/_cc-native/lib/reviewers/base.py +0 -88
  185. package/dist/templates/cc-native/_cc-native/lib/reviewers/codex.py +0 -124
  186. package/dist/templates/cc-native/_cc-native/lib/reviewers/gemini.py +0 -108
  187. package/dist/templates/cc-native/_cc-native/lib/state.py +0 -268
  188. package/dist/templates/cc-native/_cc-native/lib/utils.py +0 -1071
  189. package/dist/templates/cc-native/_cc-native/scripts/__pycache__/aggregate_agents.cpython-313.pyc +0 -0
  190. package/dist/templates/cc-native/_cc-native/scripts/aggregate_agents.py +0 -168
  191. package/dist/templates/cc-native/_cc-native/workflows/fresh-perspective.md +0 -134
@@ -1,73 +0,0 @@
1
- """Plan context templates for add_plan_context hook.
2
-
3
- Provides standardized templates for:
4
- - Evaluation context reminder (injected on plan writes)
5
- """
6
-
7
-
8
- def get_evaluation_context_reminder() -> str:
9
- """Get the plan evaluation context reminder template.
10
-
11
- Returns:
12
- Formatted markdown reminder about adding evaluation context
13
- """
14
- return """
15
- ## CRITICAL: Write This Plan for a Different Agent
16
-
17
- The agent executing this plan has ZERO context from this conversation — no chat history, no memory of files you explored or research you did.
18
-
19
- **Write as if YOU are that agent. What would you need?**
20
-
21
- ### Required Structure
22
-
23
- ```
24
- # Plan: <descriptive title>
25
-
26
- ## Background
27
- Why this change is needed (2-3 sentences)
28
-
29
- ## Task
30
- What exactly to build/change
31
-
32
- ## Files
33
- **Modify:**
34
- - `exact/path/to/file.py` - What changes (reference line numbers or patterns)
35
-
36
- **Reference:**
37
- - `exact/path/to/reference.py` - Why relevant (e.g., "pattern to follow at lines 12-30")
38
-
39
- ## Steps
40
- 1. [Specific steps with function names, patterns, or code snippets]
41
- 2. [Enough detail for someone who never saw this conversation]
42
-
43
- ## Constraints
44
- - Technical requirements, preferences, or limitations
45
-
46
- ## Documentation
47
- Decisions not written down are lost when this session ends. Update the nearest CLAUDE.md and MEMORY.md so the next session inherits what you learned.
48
-
49
- **CLAUDE.md** (nearest to changed code — cascades to subdirectories):
50
- - `exact/path/to/CLAUDE.md` — What to document
51
-
52
- **What to write:**
53
- - Architectural choices and why alternatives were rejected
54
- - Non-obvious constraints (what breaks if this changes)
55
- - Workarounds with context on the underlying issue
56
- - Patterns that prevent future mistakes
57
-
58
- **Format:** `## Topic` / `**Decision:** ...` / `**Rationale:** ...`
59
-
60
- **MEMORY.md** (cross-session learning for the AI agent):
61
- - Insight that would prevent a future mistake (e.g., "hook X silently drops field Y")
62
-
63
- **Include when:** Architectural decisions, non-obvious constraints, workarounds, or patterns discovered during implementation.
64
- **Omit entries for:** Routine changes with no decisions (rename, formatting, dependency bump).
65
- When in doubt, write it — a lean entry is better than a lost decision.
66
- ```
67
-
68
- ### Self-Check
69
- - [ ] Could I execute this if I forgot our entire conversation?
70
- - [ ] Are file paths exact (not "the auth file")?
71
- - [ ] Are implementation details specific (not "use the approach we discussed")?
72
- - [ ] Do documentation entries capture decisions the next session would otherwise lose?
73
- """.strip()
@@ -1,357 +0,0 @@
1
- #!/usr/bin/env python3
2
- """Save a handoff document with folder-based sharding.
3
-
4
- Usage:
5
- python .aiwcli/_shared/scripts/save_handoff.py <context_id> <<'EOF'
6
- # Your handoff markdown content here (with <!-- SECTION: name --> markers)
7
- EOF
8
-
9
- Or with a file:
10
- python .aiwcli/_shared/scripts/save_handoff.py <context_id> < handoff.md
11
-
12
- This script:
13
- 1. Parses sections from incoming markdown using <!-- SECTION: name --> markers
14
- 2. Creates a timestamped folder at _output/contexts/{context_id}/handoffs/{YYYY-MM-DD-HHMM}/
15
- 3. Writes sharded files:
16
- - index.md (main entry point with navigation)
17
- - completed-work.md, dead-ends.md, decisions.md, pending.md, context.md
18
- - plan.md (copy of original plan if it exists)
19
- 4. Records the event in events.jsonl (informational only)
20
- """
21
- import json
22
- import re
23
- import subprocess
24
- import sys
25
- from datetime import datetime
26
- from pathlib import Path
27
- from typing import Dict, Optional
28
-
29
- # Add parent directories to path for imports
30
- SCRIPT_DIR = Path(__file__).resolve().parent
31
- SHARED_ROOT = SCRIPT_DIR.parent
32
- sys.path.insert(0, str(SHARED_ROOT))
33
-
34
- from lib.context.context_store import get_context, save_state
35
- from lib.base.logger import log_info, log_warn, log_error
36
- from lib.base.atomic_write import atomic_write
37
- from lib.base.constants import get_handoff_folder_path
38
-
39
-
40
- def parse_frontmatter(content: str) -> tuple[Dict[str, str], str]:
41
- """Parse YAML frontmatter from markdown content.
42
-
43
- Returns:
44
- Tuple of (frontmatter dict, remaining content)
45
- """
46
- frontmatter = {}
47
- remaining = content
48
-
49
- if content.startswith('---'):
50
- parts = content.split('---', 2)
51
- if len(parts) >= 3:
52
- fm_lines = parts[1].strip().split('\n')
53
- for line in fm_lines:
54
- if ':' in line:
55
- key, value = line.split(':', 1)
56
- frontmatter[key.strip()] = value.strip()
57
- remaining = parts[2].strip()
58
-
59
- return frontmatter, remaining
60
-
61
-
62
- def parse_handoff_sections(content: str) -> Dict[str, str]:
63
- """Parse markdown content by section markers.
64
-
65
- Looks for <!-- SECTION: name --> markers and extracts content between them.
66
-
67
- Returns:
68
- Dict mapping section names to their content
69
- """
70
- sections = {}
71
- current_section = None
72
- current_content = []
73
-
74
- for line in content.split('\n'):
75
- if line.strip().startswith('<!-- SECTION:'):
76
- # Save previous section
77
- if current_section:
78
- sections[current_section] = '\n'.join(current_content).strip()
79
- # Extract new section name
80
- match = re.search(r'<!-- SECTION:\s*(\S+)\s*-->', line)
81
- if match:
82
- current_section = match.group(1)
83
- current_content = []
84
- elif current_section:
85
- current_content.append(line)
86
-
87
- # Save final section
88
- if current_section:
89
- sections[current_section] = '\n'.join(current_content).strip()
90
-
91
- return sections
92
-
93
-
94
- def get_git_status() -> str:
95
- """Get current git status."""
96
- try:
97
- result = subprocess.run(
98
- ['git', 'status', '--short'],
99
- capture_output=True,
100
- text=True,
101
- timeout=5
102
- )
103
- return result.stdout.strip() or "(no changes)"
104
- except Exception:
105
- return "(git status unavailable)"
106
-
107
-
108
- def get_plan_path_from_context(context_id: str, project_root: Path) -> Optional[Path]:
109
- """Get the plan path from state.json if available."""
110
- context = get_context(context_id, project_root)
111
- if not context or not context.plan_path:
112
- return None
113
-
114
- plan_path = Path(context.plan_path)
115
- if plan_path.exists():
116
- return plan_path
117
-
118
- return None
119
-
120
-
121
- def generate_index(
122
- frontmatter: Dict[str, str],
123
- sections: Dict[str, str],
124
- git_status: str,
125
- has_plan: bool,
126
- ) -> str:
127
- """Generate the index.md file with summary and navigation."""
128
- now = datetime.now()
129
-
130
- lines = [
131
- "---",
132
- "type: handoff",
133
- f"context_id: {frontmatter.get('context_id', 'unknown')}",
134
- f"created_at: {now.isoformat()}",
135
- f"session_id: {frontmatter.get('session_id', 'unknown')}",
136
- f"project: {frontmatter.get('project', 'unknown')}",
137
- f"plan_path: {frontmatter.get('plan_document', 'none')}",
138
- "---",
139
- "",
140
- f"# Session Handoff - {now.strftime('%Y-%m-%d %H:%M')}",
141
- "",
142
- ]
143
-
144
- # Summary section
145
- summary = sections.get('summary', '').strip()
146
- if summary:
147
- # Extract just the content (skip the ## Summary header if present)
148
- summary_lines = summary.split('\n')
149
- summary_text = '\n'.join(
150
- line for line in summary_lines
151
- if not line.strip().startswith('##')
152
- ).strip()
153
- lines.extend([
154
- "## Summary",
155
- summary_text,
156
- "",
157
- ])
158
-
159
- # Navigation table
160
- lines.extend([
161
- "## Quick Navigation",
162
- "",
163
- "| Document | Purpose | Priority |",
164
- "|----------|---------|----------|",
165
- "| [Dead Ends](./dead-ends.md) | Failed approaches - DO NOT RETRY | Read First |",
166
- "| [Pending](./pending.md) | Next steps and blockers | Action Items |",
167
- "| [Completed Work](./completed-work.md) | Tasks finished this session | Reference |",
168
- "| [Decisions](./decisions.md) | Technical choices and rationale | Reference |",
169
- ])
170
-
171
- if has_plan:
172
- lines.append("| [Plan](./plan.md) | Original plan being implemented | Reference |")
173
-
174
- lines.extend([
175
- "| [Context](./context.md) | External requirements and notes | Reference |",
176
- "",
177
- "## Continuation Instructions",
178
- "",
179
- "To continue this work in a new session:",
180
- "1. This index document provides the overview",
181
- "2. **Read [Dead Ends](./dead-ends.md) first** to avoid repeating failed approaches",
182
- "3. Check [Pending](./pending.md) for immediate next steps",
183
- "4. Reference other documents as needed",
184
- "",
185
- "## Git Status at Handoff",
186
- "```",
187
- git_status,
188
- "```",
189
- "",
190
- ])
191
-
192
- return '\n'.join(lines)
193
-
194
-
195
- def write_section_file(folder: Path, filename: str, title: str, content: str) -> bool:
196
- """Write a section file with header."""
197
- lines = [
198
- f"# {title}",
199
- "",
200
- content if content else "(No content for this section)",
201
- "",
202
- ]
203
-
204
- file_path = folder / filename
205
- success, error = atomic_write(file_path, '\n'.join(lines))
206
- if not success:
207
- log_warn("save_handoff", f"Failed to write {filename}: {error}")
208
- return False
209
- return True
210
-
211
-
212
- def main():
213
- if len(sys.argv) < 2:
214
- print("Usage: python save_handoff.py <context_id> < content.md", file=sys.stderr)
215
- print(" python save_handoff.py <context_id> <<'EOF'", file=sys.stderr)
216
- print(" ... markdown content with <!-- SECTION: name --> markers ...", file=sys.stderr)
217
- print(" EOF", file=sys.stderr)
218
- sys.exit(1)
219
-
220
- context_id = sys.argv[1]
221
-
222
- # Read content from stdin
223
- content = sys.stdin.read()
224
- if not content.strip():
225
- log_error("save_handoff", "No content provided via stdin")
226
- sys.exit(1)
227
-
228
- # Project root is the parent of .aiwcli
229
- project_root = SHARED_ROOT.parent.parent
230
-
231
- # Verify context exists
232
- context = get_context(context_id, project_root)
233
- if not context:
234
- log_error("save_handoff", f"Context not found: {context_id}")
235
- sys.exit(1)
236
-
237
- # Parse frontmatter and sections
238
- frontmatter, body = parse_frontmatter(content)
239
- sections = parse_handoff_sections(body)
240
-
241
- log_info("save_handoff", f"Parsed {len(sections)} sections: {list(sections.keys())}")
242
-
243
- # Create handoff folder
244
- handoff_folder = get_handoff_folder_path(context_id, project_root)
245
- handoff_folder.mkdir(parents=True, exist_ok=True)
246
- log_info("save_handoff", f"Created folder: {handoff_folder}")
247
-
248
- # Get git status
249
- git_status = get_git_status()
250
-
251
- # Check for plan
252
- plan_path = get_plan_path_from_context(context_id, project_root)
253
- has_plan = plan_path is not None
254
-
255
- # Copy plan if exists
256
- if plan_path:
257
- try:
258
- plan_content = plan_path.read_text(encoding='utf-8')
259
- plan_dest = handoff_folder / "plan.md"
260
- success, error = atomic_write(plan_dest, plan_content)
261
- if success:
262
- log_info("save_handoff", f"Copied plan from {plan_path}")
263
- else:
264
- log_warn("save_handoff", f"Failed to copy plan: {error}")
265
- except Exception as e:
266
- log_warn("save_handoff", f"Failed to read plan: {e}")
267
-
268
- # Write index.md
269
- index_content = generate_index(frontmatter, sections, git_status, has_plan)
270
- index_path = handoff_folder / "index.md"
271
- success, error = atomic_write(index_path, index_content)
272
- if not success:
273
- log_error("save_handoff", f"Failed to write index.md: {error}")
274
- sys.exit(1)
275
-
276
- # Write section files
277
- section_mapping = {
278
- 'completed': ('completed-work.md', 'Work Completed'),
279
- 'dead-ends': ('dead-ends.md', 'Dead Ends - Do Not Retry'),
280
- 'decisions': ('decisions.md', 'Key Decisions'),
281
- 'pending': ('pending.md', 'Pending Issues'),
282
- 'next-steps': ('pending.md', None), # Append to pending.md
283
- 'files': ('completed-work.md', None), # Append to completed-work.md
284
- 'context': ('context.md', 'Context for Future Sessions'),
285
- }
286
-
287
- # Track which files we've written with their content
288
- file_contents: Dict[str, list] = {}
289
-
290
- for section_name, (filename, title) in section_mapping.items():
291
- section_content = sections.get(section_name, '')
292
- if not section_content:
293
- continue
294
-
295
- if title is None:
296
- # Append mode - add to existing content
297
- if filename not in file_contents:
298
- file_contents[filename] = []
299
- file_contents[filename].append(section_content)
300
- else:
301
- # Write mode - set as main content with title
302
- if filename not in file_contents:
303
- file_contents[filename] = [f"# {title}", "", section_content]
304
- else:
305
- # Insert title at beginning if not present
306
- file_contents[filename] = [f"# {title}", ""] + file_contents[filename] + ["", section_content]
307
-
308
- # Write all accumulated content
309
- for filename, content_parts in file_contents.items():
310
- file_path = handoff_folder / filename
311
- full_content = '\n'.join(content_parts) + '\n'
312
- success, error = atomic_write(file_path, full_content)
313
- if not success:
314
- log_warn("save_handoff", f"Failed to write {filename}: {error}")
315
-
316
- # Ensure all expected files exist (even if empty)
317
- expected_files = ['completed-work.md', 'dead-ends.md', 'decisions.md', 'pending.md', 'context.md']
318
- titles = {
319
- 'completed-work.md': 'Work Completed',
320
- 'dead-ends.md': 'Dead Ends - Do Not Retry',
321
- 'decisions.md': 'Key Decisions',
322
- 'pending.md': 'Pending Issues & Next Steps',
323
- 'context.md': 'Context for Future Sessions',
324
- }
325
-
326
- for filename in expected_files:
327
- file_path = handoff_folder / filename
328
- if not file_path.exists():
329
- write_section_file(handoff_folder, filename, titles[filename], "")
330
-
331
- # Set handoff_path so next session auto-detects it
332
- try:
333
- index_path_str = str(handoff_folder / "index.md")
334
- state = get_context(context_id, project_root)
335
- if state:
336
- state.handoff_path = index_path_str
337
- state.handoff_consumed = False
338
- save_state(state, project_root)
339
- log_info("save_handoff", f"Set handoff_path: {index_path_str}")
340
- else:
341
- log_warn("save_handoff", f"Could not load context state for {context_id}")
342
- except Exception as e:
343
- log_warn("save_handoff", f"Handoff saved but auto-resume won't work (context update failed): {e}")
344
-
345
- # Output success message (ASCII-safe for Windows)
346
- print(f"[OK] Created handoff folder: {handoff_folder}")
347
- print(f" - index.md (entry point with navigation)")
348
-
349
- files_created = [f.name for f in handoff_folder.iterdir() if f.is_file() and f.name != 'index.md']
350
- print(f" - {', '.join(sorted(files_created))}")
351
-
352
- print()
353
- print("Handoff document saved. Use this folder for context in the next session.")
354
-
355
-
356
- if __name__ == "__main__":
357
- main()