aiwcli 0.15.4 → 0.15.7

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 (299) hide show
  1. package/README.md +6 -3
  2. package/dist/capabilities/branch/adapters.d.ts +2 -0
  3. package/dist/capabilities/branch/adapters.js +21 -0
  4. package/dist/capabilities/branch/contracts.d.ts +57 -0
  5. package/dist/capabilities/branch/contracts.js +1 -0
  6. package/dist/capabilities/branch/control-plane.d.ts +2 -0
  7. package/dist/capabilities/branch/control-plane.js +343 -0
  8. package/dist/capabilities/branch/runtime-core.d.ts +5 -0
  9. package/dist/capabilities/branch/runtime-core.js +36 -0
  10. package/dist/capabilities/installation/control-plane/clean-command.d.ts +41 -0
  11. package/dist/capabilities/installation/control-plane/clean-command.js +196 -0
  12. package/dist/capabilities/installation/control-plane/clear-command.d.ts +160 -0
  13. package/dist/capabilities/installation/control-plane/clear-command.js +1220 -0
  14. package/dist/capabilities/installation/control-plane/init-command.d.ts +81 -0
  15. package/dist/capabilities/installation/control-plane/init-command.js +449 -0
  16. package/dist/capabilities/launch/contracts.d.ts +51 -0
  17. package/dist/capabilities/launch/contracts.js +1 -0
  18. package/dist/capabilities/launch/control-plane/execute-launch.d.ts +2 -0
  19. package/dist/capabilities/launch/control-plane/execute-launch.js +222 -0
  20. package/dist/capabilities/launch/runtime-core/launch-options.d.ts +14 -0
  21. package/dist/capabilities/launch/runtime-core/launch-options.js +69 -0
  22. package/dist/cli/base-command.d.ts +18 -0
  23. package/dist/cli/base-command.js +55 -0
  24. package/dist/commands/branch.d.ts +0 -20
  25. package/dist/commands/branch.js +24 -416
  26. package/dist/commands/clean.d.ts +1 -41
  27. package/dist/commands/clean.js +1 -196
  28. package/dist/commands/clear.d.ts +1 -161
  29. package/dist/commands/clear.js +1 -1121
  30. package/dist/commands/init/index.d.ts +1 -98
  31. package/dist/commands/init/index.js +4 -478
  32. package/dist/commands/launch.d.ts +36 -11
  33. package/dist/commands/launch.js +135 -159
  34. package/dist/lib/base-command.d.ts +1 -114
  35. package/dist/lib/base-command.js +1 -153
  36. package/dist/lib/claude-settings-types.d.ts +31 -19
  37. package/dist/lib/context/context-formatter.d.ts +74 -0
  38. package/dist/lib/context/context-formatter.js +493 -0
  39. package/dist/lib/context/context-selector.d.ts +42 -0
  40. package/dist/lib/context/context-selector.js +451 -0
  41. package/dist/lib/context/context-store.d.ts +100 -0
  42. package/dist/lib/context/context-store.js +618 -0
  43. package/dist/lib/context/plan-manager.d.ts +54 -0
  44. package/dist/lib/context/plan-manager.js +282 -0
  45. package/dist/lib/context/task-tracker.d.ts +44 -0
  46. package/dist/lib/context/task-tracker.js +146 -0
  47. package/dist/lib/core-ide-base.d.ts +4 -0
  48. package/dist/lib/core-ide-base.js +77 -0
  49. package/dist/lib/core-installer.d.ts +5 -0
  50. package/dist/lib/core-installer.js +54 -0
  51. package/dist/lib/git-exclude-manager.d.ts +2 -2
  52. package/dist/lib/git-exclude-manager.js +3 -3
  53. package/dist/lib/hooks/hook-utils.d.ts +143 -0
  54. package/dist/lib/hooks/hook-utils.js +609 -0
  55. package/dist/lib/hooks/session-end-logic.d.ts +5 -0
  56. package/dist/lib/hooks/session-end-logic.js +63 -0
  57. package/dist/lib/hooks-merger.js +25 -19
  58. package/dist/lib/ide-path-resolver.d.ts +19 -7
  59. package/dist/lib/ide-path-resolver.js +25 -9
  60. package/dist/lib/install-state.d.ts +34 -0
  61. package/dist/lib/install-state.js +161 -0
  62. package/dist/lib/launch-options.d.ts +1 -0
  63. package/dist/lib/launch-options.js +1 -0
  64. package/dist/lib/lsp-patch.d.ts +12 -0
  65. package/dist/lib/lsp-patch.js +156 -0
  66. package/dist/lib/multiplexer.d.ts +57 -0
  67. package/dist/lib/multiplexer.js +19 -0
  68. package/dist/lib/multiplexers/psmux.d.ts +75 -0
  69. package/dist/lib/multiplexers/psmux.js +384 -0
  70. package/dist/lib/multiplexers/tmux.d.ts +44 -0
  71. package/dist/lib/multiplexers/tmux.js +262 -0
  72. package/dist/lib/mux-utils.d.ts +5 -0
  73. package/dist/lib/mux-utils.js +42 -0
  74. package/dist/lib/paths.d.ts +2 -2
  75. package/dist/lib/paths.js +2 -2
  76. package/dist/lib/platform-commands.d.ts +27 -0
  77. package/dist/lib/platform-commands.js +49 -0
  78. package/dist/lib/runtime/aiw-cli.d.ts +37 -0
  79. package/dist/lib/runtime/aiw-cli.js +74 -0
  80. package/dist/lib/runtime/atomic-write.d.ts +19 -0
  81. package/dist/lib/runtime/atomic-write.js +121 -0
  82. package/dist/lib/runtime/cli-args.d.ts +55 -0
  83. package/dist/lib/runtime/cli-args.js +185 -0
  84. package/dist/lib/runtime/constants.d.ts +56 -0
  85. package/dist/lib/runtime/constants.js +230 -0
  86. package/dist/lib/runtime/executable-policy.d.ts +16 -0
  87. package/dist/lib/runtime/executable-policy.js +57 -0
  88. package/dist/lib/runtime/git-state.d.ts +9 -0
  89. package/dist/lib/runtime/git-state.js +59 -0
  90. package/dist/lib/runtime/inference.d.ts +37 -0
  91. package/dist/lib/runtime/inference.js +262 -0
  92. package/dist/lib/runtime/lint-dispatch.d.ts +40 -0
  93. package/dist/lib/runtime/lint-dispatch.js +285 -0
  94. package/dist/lib/runtime/logger.d.ts +66 -0
  95. package/dist/lib/runtime/logger.js +201 -0
  96. package/dist/lib/runtime/models.d.ts +14 -0
  97. package/dist/lib/runtime/models.js +14 -0
  98. package/dist/lib/runtime/platform-adapter.d.ts +7 -0
  99. package/dist/lib/runtime/platform-adapter.js +21 -0
  100. package/dist/lib/runtime/preflight.d.ts +24 -0
  101. package/dist/lib/runtime/preflight.js +65 -0
  102. package/dist/lib/runtime/sentinel-ipc.d.ts +14 -0
  103. package/dist/lib/runtime/sentinel-ipc.js +67 -0
  104. package/dist/lib/runtime/state-io.d.ts +30 -0
  105. package/dist/lib/runtime/state-io.js +174 -0
  106. package/dist/lib/runtime/stop-words.d.ts +20 -0
  107. package/dist/lib/runtime/stop-words.js +150 -0
  108. package/dist/lib/runtime/subprocess-utils.d.ts +29 -0
  109. package/dist/lib/runtime/subprocess-utils.js +96 -0
  110. package/dist/lib/runtime/tmux-preflight.d.ts +13 -0
  111. package/dist/lib/runtime/tmux-preflight.js +78 -0
  112. package/dist/lib/runtime/utils.d.ts +54 -0
  113. package/dist/lib/runtime/utils.js +162 -0
  114. package/dist/lib/sentinel-wrapper.d.ts +9 -0
  115. package/dist/lib/sentinel-wrapper.js +20 -0
  116. package/dist/lib/shell-quoting.d.ts +5 -0
  117. package/dist/lib/shell-quoting.js +17 -0
  118. package/dist/lib/spawn-errors.d.ts +6 -0
  119. package/dist/lib/spawn-errors.js +15 -0
  120. package/dist/lib/spawn.js +5 -11
  121. package/dist/lib/template-installer.d.ts +4 -5
  122. package/dist/lib/template-installer.js +36 -34
  123. package/dist/lib/template-resolver.d.ts +6 -7
  124. package/dist/lib/template-resolver.js +26 -21
  125. package/dist/lib/template-settings-reconstructor.d.ts +7 -2
  126. package/dist/lib/template-settings-reconstructor.js +76 -45
  127. package/dist/lib/terminal-strategy.d.ts +11 -0
  128. package/dist/lib/terminal-strategy.js +49 -0
  129. package/dist/lib/terminal.d.ts +28 -0
  130. package/dist/lib/terminal.js +162 -112
  131. package/dist/lib/tmux-pane-placement.d.ts +17 -0
  132. package/dist/lib/tmux-pane-placement.js +58 -0
  133. package/dist/lib/tmux-primitives.d.ts +5 -0
  134. package/dist/lib/tmux-primitives.js +15 -0
  135. package/dist/lib/tmux-session.d.ts +32 -0
  136. package/dist/lib/tmux-session.js +86 -0
  137. package/dist/lib/tty-detection.js +1 -1
  138. package/dist/lib/types.d.ts +168 -0
  139. package/dist/lib/types.js +6 -0
  140. package/dist/lib/version.d.ts +1 -1
  141. package/dist/lib/version.js +1 -1
  142. package/dist/platform/launch.d.ts +10 -0
  143. package/dist/platform/launch.js +10 -0
  144. package/dist/templates/CLAUDE.md +31 -40
  145. package/dist/templates/cc-native/.claude/settings.json +27 -27
  146. package/dist/templates/cc-native/CC-NATIVE-README.md +1 -1
  147. package/dist/templates/cc-native/TEMPLATE-SCHEMA.md +10 -9
  148. package/dist/templates/cc-native/_cc-native/CLAUDE.md +18 -18
  149. package/dist/templates/cc-native/_cc-native/artifacts/CLAUDE.md +3 -3
  150. package/dist/templates/cc-native/_cc-native/artifacts/lib/format.ts +14 -14
  151. package/dist/templates/cc-native/_cc-native/artifacts/lib/tracker.ts +1 -1
  152. package/dist/templates/cc-native/_cc-native/artifacts/lib/write.ts +3 -3
  153. package/dist/templates/cc-native/_cc-native/cc-native.config.json +3 -3
  154. package/dist/templates/cc-native/_cc-native/hooks/CLAUDE.md +16 -15
  155. package/dist/templates/cc-native/_cc-native/hooks/cc-native-plan-review.ts +3 -3
  156. package/dist/templates/cc-native/_cc-native/hooks/enhance_plan_post_subagent.ts +2 -2
  157. package/dist/templates/cc-native/_cc-native/hooks/enhance_plan_post_write.ts +2 -2
  158. package/dist/templates/cc-native/_cc-native/hooks/mark_questions_asked.ts +3 -3
  159. package/dist/templates/cc-native/_cc-native/hooks/plan_questions_early.ts +2 -2
  160. package/dist/templates/cc-native/_cc-native/hooks/validate_task_prompt.ts +3 -3
  161. package/dist/templates/cc-native/_cc-native/lib-ts/CLAUDE.md +8 -8
  162. package/dist/templates/cc-native/_cc-native/lib-ts/aggregate-agents.ts +1 -1
  163. package/dist/templates/cc-native/_cc-native/lib-ts/cc-native-state.ts +4 -4
  164. package/dist/templates/cc-native/_cc-native/lib-ts/cli-output-parser.ts +1 -1
  165. package/dist/templates/cc-native/_cc-native/lib-ts/config.ts +1 -1
  166. package/dist/templates/cc-native/_cc-native/lib-ts/debug.ts +1 -1
  167. package/dist/templates/cc-native/_cc-native/lib-ts/json-parser.ts +1 -1
  168. package/dist/templates/cc-native/_cc-native/lib-ts/plan-discovery.ts +2 -2
  169. package/dist/templates/cc-native/_cc-native/lib-ts/rlm/logger.ts +1 -1
  170. package/dist/templates/cc-native/_cc-native/lib-ts/rlm/retrieval-pipeline.ts +2 -2
  171. package/dist/templates/cc-native/_cc-native/lib-ts/rlm/types.ts +1 -1
  172. package/dist/templates/cc-native/_cc-native/lib-ts/settings.ts +8 -8
  173. package/dist/templates/cc-native/_cc-native/lib-ts/state.ts +3 -3
  174. package/dist/templates/cc-native/_cc-native/lib-ts/tsconfig.json +2 -2
  175. package/dist/templates/cc-native/_cc-native/lib-ts/types.ts +3 -3
  176. package/dist/templates/cc-native/_cc-native/plan-review/CLAUDE.md +3 -1
  177. package/dist/templates/cc-native/_cc-native/plan-review/lib/__tests__/agent-selection.test.ts +345 -0
  178. package/dist/templates/cc-native/_cc-native/plan-review/lib/__tests__/preflight.test.ts +344 -0
  179. package/dist/templates/cc-native/_cc-native/plan-review/lib/agent-selection.ts +37 -15
  180. package/dist/templates/cc-native/_cc-native/plan-review/lib/corroboration.ts +16 -69
  181. package/dist/templates/cc-native/_cc-native/plan-review/lib/orchestrator.ts +1 -1
  182. package/dist/templates/cc-native/_cc-native/plan-review/lib/output-builder.ts +1 -1
  183. package/dist/templates/cc-native/_cc-native/plan-review/lib/plan-questions.ts +2 -2
  184. package/dist/templates/cc-native/_cc-native/plan-review/lib/preflight.ts +56 -26
  185. package/dist/templates/cc-native/_cc-native/plan-review/lib/review-pipeline.ts +7 -7
  186. package/dist/templates/cc-native/_cc-native/plan-review/lib/reviewers/agent.ts +4 -4
  187. package/dist/templates/cc-native/_cc-native/plan-review/lib/reviewers/base/base-agent.ts +3 -3
  188. package/dist/templates/cc-native/_cc-native/plan-review/lib/reviewers/index.ts +1 -1
  189. package/dist/templates/cc-native/_cc-native/plan-review/lib/reviewers/providers/claude-agent.ts +2 -2
  190. package/dist/templates/cc-native/_cc-native/plan-review/lib/reviewers/providers/codex-agent.ts +4 -4
  191. package/dist/templates/cc-native/_cc-native/plan-review/lib/reviewers/providers/gemini-agent.ts +1 -1
  192. package/dist/templates/cc-native/_cc-native/plan-review/lib/reviewers/providers/orchestrator-claude-agent.ts +5 -6
  193. package/dist/templates/core/.codex/workflows/codex.md +17 -0
  194. package/dist/templates/core/.codex/workflows/handoff.md +5 -0
  195. package/dist/templates/core/.codex/workflows/meta-plan.md +7 -0
  196. package/dist/templates/core/.cognition/AGENTS.md +5 -0
  197. package/dist/templates/core/.cognition/config.json +12 -0
  198. package/dist/templates/{_shared → core}/.windsurf/workflows/handoff.md +1 -1
  199. package/dist/templates/{_shared → core}/.windsurf/workflows/meta-plan.md +1 -1
  200. package/dist/templates/core/hooks-ts/_utils/git-state.ts +2 -0
  201. package/dist/templates/{_shared → core}/hooks-ts/archive_plan.ts +14 -23
  202. package/dist/templates/core/hooks-ts/codex_explorer.ts +160 -0
  203. package/dist/templates/{_shared → core}/hooks-ts/context_monitor.ts +23 -55
  204. package/dist/templates/{_shared → core}/hooks-ts/file-suggestion.ts +4 -3
  205. package/dist/templates/{_shared → core}/hooks-ts/lint_after_edit.ts +7 -9
  206. package/dist/templates/{_shared → core}/hooks-ts/pre_compact.ts +5 -5
  207. package/dist/templates/{_shared → core}/hooks-ts/session_end.ts +38 -78
  208. package/dist/templates/{_shared → core}/hooks-ts/session_start.ts +5 -5
  209. package/dist/templates/core/hooks-ts/task_create_capture.ts +32 -0
  210. package/dist/templates/{_shared → core}/hooks-ts/task_update_capture.ts +9 -24
  211. package/dist/templates/core/hooks-ts/user_prompt_submit.ts +46 -0
  212. package/dist/templates/{_shared → core}/lib-ts/CLAUDE.md +27 -16
  213. package/dist/templates/{_shared → core}/lib-ts/agent-exec/backends/headless.ts +3 -2
  214. package/dist/templates/{_shared → core}/lib-ts/agent-exec/backends/tmux.ts +44 -15
  215. package/dist/templates/{_shared → core}/lib-ts/agent-exec/base-agent.ts +6 -4
  216. package/dist/templates/{_shared → core}/lib-ts/agent-exec/execution-backend.ts +1 -1
  217. package/dist/templates/{_shared → core}/lib-ts/agent-exec/index.ts +2 -2
  218. package/dist/templates/{_shared → core}/lib-ts/agent-exec/structured-output.ts +4 -5
  219. package/dist/templates/{_shared → core}/lib-ts/context/CLAUDE.md +9 -6
  220. package/dist/templates/{_shared → core}/lib-ts/context/context-formatter.ts +16 -21
  221. package/dist/templates/{_shared → core}/lib-ts/context/context-selector.ts +8 -6
  222. package/dist/templates/{_shared → core}/lib-ts/context/context-store.ts +32 -20
  223. package/dist/templates/{_shared → core}/lib-ts/context/plan-manager.ts +19 -15
  224. package/dist/templates/{_shared → core}/lib-ts/context/task-tracker.ts +3 -3
  225. package/dist/templates/core/lib-ts/hooks/context-monitor-logic.ts +32 -0
  226. package/dist/templates/{_shared/lib-ts/base → core/lib-ts/hooks}/hook-utils.ts +168 -41
  227. package/dist/templates/core/lib-ts/hooks/prompt-binding-logic.ts +80 -0
  228. package/dist/templates/core/lib-ts/hooks/session-end-logic.ts +93 -0
  229. package/dist/templates/core/lib-ts/package.json +19 -0
  230. package/dist/templates/core/lib-ts/runtime/agent-launcher.ts +295 -0
  231. package/dist/templates/core/lib-ts/runtime/aiw-cli.ts +106 -0
  232. package/dist/templates/{_shared/lib-ts/base → core/lib-ts/runtime}/atomic-write.ts +12 -7
  233. package/dist/templates/{_shared/lib-ts/base → core/lib-ts/runtime}/cli-args.ts +8 -6
  234. package/dist/templates/{_shared/lib-ts/base → core/lib-ts/runtime}/constants.ts +326 -324
  235. package/dist/templates/core/lib-ts/runtime/executable-policy.ts +89 -0
  236. package/dist/templates/{_shared/lib-ts/base → core/lib-ts/runtime}/git-state.ts +6 -4
  237. package/dist/templates/{_shared/lib-ts/base → core/lib-ts/runtime}/inference.ts +59 -10
  238. package/dist/templates/{_shared/lib-ts/base → core/lib-ts/runtime}/lint-dispatch.ts +25 -23
  239. package/dist/templates/{_shared/lib-ts/base → core/lib-ts/runtime}/logger.ts +32 -29
  240. package/dist/templates/{_shared/lib-ts/base → core/lib-ts/runtime}/models.ts +2 -2
  241. package/dist/templates/core/lib-ts/runtime/platform-adapter.ts +33 -0
  242. package/dist/templates/{_shared/lib-ts/base → core/lib-ts/runtime}/preflight.ts +4 -3
  243. package/dist/templates/core/lib-ts/runtime/sentinel-ipc.ts +91 -0
  244. package/dist/templates/{_shared/lib-ts/base → core/lib-ts/runtime}/state-io.ts +11 -7
  245. package/dist/templates/core/lib-ts/runtime/stop-words.ts +185 -0
  246. package/dist/templates/core/lib-ts/runtime/subprocess-utils.ts +147 -0
  247. package/dist/templates/core/lib-ts/runtime/tmux-preflight.ts +93 -0
  248. package/dist/templates/{_shared/lib-ts/base → core/lib-ts/runtime}/utils.ts +4 -3
  249. package/dist/templates/{_shared → core}/lib-ts/templates/formatters.ts +7 -5
  250. package/dist/templates/{_shared → core}/lib-ts/templates/plan-context.ts +2 -1
  251. package/dist/templates/{_shared → core}/lib-ts/tsconfig.json +3 -1
  252. package/dist/templates/{_shared → core}/lib-ts/types.ts +78 -77
  253. package/dist/templates/core/scripts/resolve-run.ts +61 -0
  254. package/dist/templates/{_shared → core}/scripts/resolve_context.ts +3 -3
  255. package/dist/templates/{_shared → core}/scripts/status_line.ts +25 -20
  256. package/dist/templates/core/skills/codex/CLAUDE.md +78 -0
  257. package/dist/templates/{_shared → core}/skills/codex/SKILL.md +21 -18
  258. package/dist/templates/{_shared → core}/skills/codex/lib/codex-watcher.ts +76 -103
  259. package/dist/templates/{_shared → core}/skills/codex/scripts/launch-codex.ts +119 -133
  260. package/dist/templates/{_shared → core}/skills/codex/scripts/watch-codex.ts +6 -4
  261. package/dist/templates/core/skills/devin/CLAUDE.md +65 -0
  262. package/dist/templates/core/skills/devin/SKILL.md +73 -0
  263. package/dist/templates/core/skills/devin/lib/devin-watcher.ts +280 -0
  264. package/dist/templates/core/skills/devin/scripts/launch-devin.ts +257 -0
  265. package/dist/templates/{_shared → core}/skills/handoff-system/CLAUDE.md +436 -433
  266. package/dist/templates/{_shared → core}/skills/handoff-system/lib/document-generator.ts +9 -7
  267. package/dist/templates/{_shared → core}/skills/handoff-system/lib/handoff-reader.ts +6 -4
  268. package/dist/templates/{_shared → core}/skills/handoff-system/scripts/resume_handoff.ts +10 -8
  269. package/dist/templates/{_shared → core}/skills/handoff-system/scripts/save_handoff.ts +12 -10
  270. package/dist/templates/{_shared → core}/skills/handoff-system/workflows/handoff-resume.md +2 -2
  271. package/dist/templates/{_shared → core}/skills/handoff-system/workflows/handoff.md +6 -5
  272. package/dist/templates/{_shared → core}/skills/meta-plan/CLAUDE.md +2 -1
  273. package/dist/templates/{_shared → core}/skills/meta-plan/workflows/meta-plan.md +8 -7
  274. package/oclif.manifest.json +89 -13
  275. package/package.json +13 -12
  276. package/dist/templates/_shared/.claude/settings.json +0 -120
  277. package/dist/templates/_shared/.claude/skills/codex/SKILL.md +0 -35
  278. package/dist/templates/_shared/.claude/skills/handoff/SKILL.md +0 -13
  279. package/dist/templates/_shared/.claude/skills/handoff-resume/SKILL.md +0 -13
  280. package/dist/templates/_shared/.claude/skills/meta-plan/SKILL.md +0 -43
  281. package/dist/templates/_shared/.codex/workflows/codex.md +0 -11
  282. package/dist/templates/_shared/.codex/workflows/handoff.md +0 -226
  283. package/dist/templates/_shared/.codex/workflows/meta-plan.md +0 -347
  284. package/dist/templates/_shared/hooks-ts/_utils/git-state.ts +0 -2
  285. package/dist/templates/_shared/hooks-ts/task_create_capture.ts +0 -48
  286. package/dist/templates/_shared/hooks-ts/user_prompt_submit.ts +0 -93
  287. package/dist/templates/_shared/lib-ts/base/launchers/tmux-launcher.ts +0 -173
  288. package/dist/templates/_shared/lib-ts/base/launchers/window-launcher.ts +0 -93
  289. package/dist/templates/_shared/lib-ts/base/launchers/wt-launcher.ts +0 -64
  290. package/dist/templates/_shared/lib-ts/base/pane-launcher.ts +0 -55
  291. package/dist/templates/_shared/lib-ts/base/sentinel-ipc.ts +0 -87
  292. package/dist/templates/_shared/lib-ts/base/stop-words.ts +0 -184
  293. package/dist/templates/_shared/lib-ts/base/subprocess-utils.ts +0 -249
  294. package/dist/templates/_shared/lib-ts/base/tmux-driver.ts +0 -341
  295. package/dist/templates/_shared/lib-ts/base/tmux-pane-placement.ts +0 -78
  296. package/dist/templates/_shared/lib-ts/package.json +0 -20
  297. package/dist/templates/_shared/scripts/resolve-run.ts +0 -62
  298. package/dist/templates/_shared/skills/codex/CLAUDE.md +0 -70
  299. /package/dist/templates/{_shared → core}/lib-ts/agent-exec/backends/index.ts +0 -0
@@ -0,0 +1,201 @@
1
+ /**
2
+ * Unified logging for all hooks and libraries.
3
+ *
4
+ * Log format: JSONL (one JSON object per line)
5
+ * Log location: _output/hook-log.jsonl (global, all sessions)
6
+ * Filter by session using the "sid" field.
7
+ *
8
+ * stderr is OPT-IN: convenience functions (logDebug, logInfo, logWarn, logError)
9
+ * write to file only by default. To also write to stderr (visible to Claude Code
10
+ * as "hook error"), pass { stderr: true } or use logBlocking().
11
+ * logHookError() always writes to stderr (unhandled errors must be visible).
12
+ *
13
+ * Environment variables:
14
+ * - HOOK_LOG_DISABLE=1: Disable all file logging
15
+ * - HOOK_LOG_LEVEL=warn: Minimum level to log (default: debug)
16
+ * - HOOK_ERROR_LOG_DISABLE=1: Legacy alias for HOOK_LOG_DISABLE
17
+ *
18
+ * Never throws. No buffering. Stdlib only.
19
+ * See SPEC.md §3
20
+ */
21
+ import * as fs from "node:fs";
22
+ import path from "node:path";
23
+ const LEVELS = {
24
+ debug: 0,
25
+ info: 1,
26
+ warn: 2,
27
+ error: 3,
28
+ };
29
+ const MAX_LOG_BYTES = 2_000_000; // 2MB threshold before pruning
30
+ let _prunedThisProcess = false;
31
+ // Module-level session ID cache
32
+ let _cachedSessionId = null;
33
+ // Module-level context path cache (kept for external callers)
34
+ let _cachedContextPath = null;
35
+ let _contextResolved = false;
36
+ /**
37
+ * Set the session ID for this process. All subsequent log calls include it.
38
+ */
39
+ export function setSessionId(sessionId) {
40
+ _cachedSessionId = sessionId;
41
+ }
42
+ /**
43
+ * Set the context path for this process. Kept for external callers.
44
+ */
45
+ export function setContextPath(contextPath) {
46
+ _cachedContextPath = contextPath;
47
+ _contextResolved = true;
48
+ }
49
+ export function getContextPath() {
50
+ if (!_contextResolved) {
51
+ _contextResolved = true; // Don't retry
52
+ }
53
+ return _cachedContextPath;
54
+ }
55
+ function getMinLevel() {
56
+ const env = (process.env.HOOK_LOG_LEVEL ?? "debug").toLowerCase();
57
+ return LEVELS[env] ?? 0;
58
+ }
59
+ function isDisabled() {
60
+ return (process.env.HOOK_LOG_DISABLE === "1" ||
61
+ process.env.HOOK_ERROR_LOG_DISABLE === "1" ||
62
+ process.env.CCNATIVE_DEBUG_DISABLE === "1");
63
+ }
64
+ function getProjectRoot() {
65
+ return process.env.CLAUDE_PROJECT_DIR || process.cwd();
66
+ }
67
+ /**
68
+ * Write a structured log entry to the global hook log.
69
+ *
70
+ * All entries go to _output/hook-log.jsonl. Use the "sid" field
71
+ * (set via setSessionId) to filter by session.
72
+ */
73
+ export function hookLog(level, hookName, message, opts) {
74
+ try {
75
+ const levelLower = level.toLowerCase();
76
+ const levelNum = LEVELS[levelLower] ?? 0;
77
+ const component = opts?.component ?? "";
78
+ const tracebackStr = opts?.traceback_str ?? "";
79
+ const stderrEnabled = opts?.stderr === true;
80
+ // Write to stderr
81
+ if (stderrEnabled) {
82
+ const prefix = component
83
+ ? `[${hookName}:${component}]`
84
+ : `[${hookName}]`;
85
+ process.stderr.write(`${prefix} ${message}\n`);
86
+ if (tracebackStr) {
87
+ process.stderr.write(tracebackStr + "\n");
88
+ }
89
+ }
90
+ // Check if file logging is enabled
91
+ if (isDisabled())
92
+ return;
93
+ // Check minimum level
94
+ if (levelNum < getMinLevel())
95
+ return;
96
+ // Build JSONL entry
97
+ const now = new Date();
98
+ const ts = now.toISOString().replace("Z", "").slice(0, 23);
99
+ const entry = {
100
+ ts,
101
+ level: levelLower,
102
+ hook: hookName,
103
+ msg: message,
104
+ };
105
+ if (_cachedSessionId)
106
+ entry.sid = _cachedSessionId;
107
+ if (component)
108
+ entry.component = component;
109
+ if (opts?.data !== undefined && opts.data !== null) {
110
+ try {
111
+ JSON.stringify(opts.data);
112
+ entry.data = opts.data;
113
+ }
114
+ catch {
115
+ entry.data = String(opts.data);
116
+ }
117
+ }
118
+ if (tracebackStr)
119
+ entry.tb = tracebackStr.trimEnd();
120
+ const line = JSON.stringify(entry) + "\n";
121
+ // Always write to global log
122
+ const logPath = path.join(getProjectRoot(), "_output", "hook-log.jsonl");
123
+ // Ensure directory exists
124
+ const dir = path.dirname(logPath);
125
+ fs.mkdirSync(dir, { recursive: true });
126
+ // Size-based prune: runs at most once per bun process
127
+ if (!_prunedThisProcess) {
128
+ _prunedThisProcess = true;
129
+ try {
130
+ const stat = fs.statSync(logPath);
131
+ if (stat.size > MAX_LOG_BYTES) {
132
+ const content = fs.readFileSync(logPath, "utf8");
133
+ const pruneTarget = Math.floor(content.length * 0.1);
134
+ const keepFrom = content.indexOf("\n", pruneTarget);
135
+ if (keepFrom > 0) {
136
+ fs.writeFileSync(logPath, content.slice(keepFrom + 1), "utf8");
137
+ }
138
+ }
139
+ }
140
+ catch { /* file doesn't exist yet — fine */ }
141
+ }
142
+ fs.appendFileSync(logPath, line, "utf8");
143
+ }
144
+ catch {
145
+ // Never crash
146
+ }
147
+ }
148
+ export function logDebug(hookName, message, opts) {
149
+ hookLog("debug", hookName, message, opts);
150
+ }
151
+ export function logInfo(hookName, message, opts) {
152
+ hookLog("info", hookName, message, opts);
153
+ }
154
+ export function logWarn(hookName, message, opts) {
155
+ hookLog("warn", hookName, message, opts);
156
+ }
157
+ export function logError(hookName, message, opts) {
158
+ hookLog("error", hookName, message, opts);
159
+ }
160
+ /**
161
+ * Log an error that SHOULD be visible to user/model via stderr.
162
+ * Use for real problems needing attention, not routine diagnostics.
163
+ */
164
+ export function logBlocking(hookName, message, opts) {
165
+ hookLog("error", hookName, message, { ...opts, stderr: true });
166
+ }
167
+ /**
168
+ * Log a structured diagnostic entry at a hook decision point.
169
+ * See SPEC.md §3.8
170
+ */
171
+ export function logDiagnostic(hookName, phase, summary, opts) {
172
+ const diagData = { phase };
173
+ if (opts?.inputs !== undefined)
174
+ diagData.inputs = opts.inputs;
175
+ if (opts?.decision !== undefined)
176
+ diagData.decision = opts.decision;
177
+ if (opts?.reasoning !== undefined)
178
+ diagData.reasoning = opts.reasoning;
179
+ if (opts?.data && typeof opts.data === "object") {
180
+ Object.assign(diagData, opts.data);
181
+ }
182
+ hookLog("debug", hookName, `[DIAG:${phase}] ${summary}`, {
183
+ component: opts?.component ?? "diag",
184
+ data: diagData,
185
+ });
186
+ }
187
+ /**
188
+ * Backward-compatible wrapper matching old hook_utils.log_hook_error signature.
189
+ * See SPEC.md §3.6
190
+ */
191
+ export function logHookError(hookName, error, hookEvent = "unknown", tracebackStr = "") {
192
+ const errStr = typeof error === "string" ? error : String(error);
193
+ const msg = errStr.replaceAll(/[\n\r]/g, " ").slice(0, 200);
194
+ const errType = typeof error === "object" && error !== null
195
+ ? error.constructor.name
196
+ : "Error";
197
+ hookLog("error", hookName, `[${hookEvent}] ${errType}: ${msg}`, {
198
+ traceback_str: tracebackStr,
199
+ stderr: true,
200
+ });
201
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Canonical model ID constants — single source of truth.
3
+ * All model IDs used across the system should reference these constants.
4
+ */
5
+ export declare const CLAUDE_MODELS: {
6
+ readonly haiku: "claude-haiku-4-5-20251001";
7
+ readonly sonnet: "claude-sonnet-4-6";
8
+ readonly opus: "claude-opus-4-6";
9
+ };
10
+ export declare const CODEX_MODELS: {
11
+ readonly spark: "gpt-5.3-codex-spark";
12
+ readonly codex: "gpt-5.4";
13
+ readonly gpt: "gpt-5.4";
14
+ };
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Canonical model ID constants — single source of truth.
3
+ * All model IDs used across the system should reference these constants.
4
+ */
5
+ export const CLAUDE_MODELS = {
6
+ haiku: "claude-haiku-4-5-20251001",
7
+ sonnet: "claude-sonnet-4-6",
8
+ opus: "claude-opus-4-6",
9
+ };
10
+ export const CODEX_MODELS = {
11
+ spark: "gpt-5.3-codex-spark",
12
+ codex: "gpt-5.4",
13
+ gpt: "gpt-5.4",
14
+ };
@@ -0,0 +1,7 @@
1
+ export type TmuxColorMode = 'c256' | 'truecolor';
2
+ export declare function isWindowsPlatform(platform?: NodeJS.Platform): boolean;
3
+ export declare function isNonWindowsPlatform(platform?: NodeJS.Platform): boolean;
4
+ export declare function commandLookupBinary(platform?: NodeJS.Platform): 'where.exe' | 'which';
5
+ export declare function shouldUseShell(platform?: NodeJS.Platform): boolean;
6
+ export declare function resolveTmuxColorModeForPlatform(_platform?: NodeJS.Platform): TmuxColorMode;
7
+ export declare function applyTmuxLaunchEnv(envVars: Record<string, string>, _platform?: NodeJS.Platform): Record<string, string>;
@@ -0,0 +1,21 @@
1
+ export function isWindowsPlatform(platform = process.platform) {
2
+ return platform === 'win32';
3
+ }
4
+ export function isNonWindowsPlatform(platform = process.platform) {
5
+ return !isWindowsPlatform(platform);
6
+ }
7
+ export function commandLookupBinary(platform = process.platform) {
8
+ return isWindowsPlatform(platform) ? 'where.exe' : 'which';
9
+ }
10
+ export function shouldUseShell(platform = process.platform) {
11
+ return isWindowsPlatform(platform);
12
+ }
13
+ export function resolveTmuxColorModeForPlatform(_platform) {
14
+ // Always truecolor — Windows now uses psmux (native ConPTY) which supports truecolor natively.
15
+ // The c256 degradation was only needed for tmux-via-MSYS2-bash.
16
+ return 'truecolor';
17
+ }
18
+ export function applyTmuxLaunchEnv(envVars, _platform) {
19
+ // Always inject truecolor — psmux on Windows and tmux on Unix both support it.
20
+ return { COLORTERM: 'truecolor', ...envVars };
21
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Shared preflight health check for provider+model availability.
3
+ * Extracted from plan-review/lib/preflight.ts for reuse by unknown hook.
4
+ *
5
+ * Validates that a CLI tool + model combo works by running a minimal "ping" request.
6
+ */
7
+ export interface PreflightCommandConfig {
8
+ cliName: string;
9
+ buildArgs: (model: string) => string[];
10
+ input: string;
11
+ }
12
+ export interface PreflightCheckResult {
13
+ provider: string;
14
+ model: string;
15
+ available: boolean;
16
+ latencyMs: number;
17
+ error?: string;
18
+ }
19
+ export declare function classifyError(stderr: string, exitCode: number | null, killed: boolean, signal: string | null): string;
20
+ /**
21
+ * Check if a single provider:model combo is available.
22
+ * Takes a PreflightCommandConfig so callers define their own CLI args.
23
+ */
24
+ export declare function checkProviderModel(provider: string, model: string, config: PreflightCommandConfig, timeoutMs: number, hook?: string): Promise<PreflightCheckResult>;
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Shared preflight health check for provider+model availability.
3
+ * Extracted from plan-review/lib/preflight.ts for reuse by unknown hook.
4
+ *
5
+ * Validates that a CLI tool + model combo works by running a minimal "ping" request.
6
+ */
7
+ import { logDebug, logWarn } from "./logger.js";
8
+ import { findExecutable, execFileAsync, getInternalSubprocessEnv } from "./subprocess-utils.js";
9
+ // ---------------------------------------------------------------------------
10
+ // Error Classification
11
+ // ---------------------------------------------------------------------------
12
+ export function classifyError(stderr, exitCode, killed, signal) {
13
+ if (killed || signal === "SIGTERM")
14
+ return "Preflight timed out";
15
+ if (/model.*not found|not available/i.test(stderr))
16
+ return "Model not available for this account";
17
+ if (/rate limit|429/i.test(stderr))
18
+ return "Rate limited";
19
+ if (/auth|api key|401/i.test(stderr))
20
+ return "Authentication failed";
21
+ if (/quota|billing/i.test(stderr))
22
+ return "Quota/billing issue";
23
+ return `Exit code ${exitCode}`;
24
+ }
25
+ // ---------------------------------------------------------------------------
26
+ // Single Provider+Model Check
27
+ // ---------------------------------------------------------------------------
28
+ /**
29
+ * Check if a single provider:model combo is available.
30
+ * Takes a PreflightCommandConfig so callers define their own CLI args.
31
+ */
32
+ export async function checkProviderModel(provider, model, config, timeoutMs, hook = "preflight") {
33
+ const cliPath = findExecutable(config.cliName);
34
+ if (!cliPath) {
35
+ return { provider, model, available: false, latencyMs: 0, error: `CLI '${config.cliName}' not found on PATH` };
36
+ }
37
+ const start = Date.now();
38
+ try {
39
+ const env = getInternalSubprocessEnv();
40
+ const result = await execFileAsync(cliPath, config.buildArgs(model), {
41
+ input: config.input,
42
+ timeout: timeoutMs,
43
+ env: env,
44
+ maxBuffer: 1 * 1024 * 1024,
45
+ shell: process.platform === "win32",
46
+ });
47
+ const latencyMs = Date.now() - start;
48
+ if (result.killed || result.signal === "SIGTERM") {
49
+ return { provider, model, available: false, latencyMs, error: "Preflight timed out" };
50
+ }
51
+ if (result.exitCode !== 0) {
52
+ const error = classifyError(result.stderr, result.exitCode, result.killed, result.signal);
53
+ logWarn(hook, `${provider}:${model} failed: ${error} (stderr: ${result.stderr.slice(-200)})`);
54
+ return { provider, model, available: false, latencyMs, error };
55
+ }
56
+ logDebug(hook, `${provider}:${model} passed (${latencyMs}ms)`);
57
+ return { provider, model, available: true, latencyMs };
58
+ }
59
+ catch (error_) {
60
+ const latencyMs = Date.now() - start;
61
+ const error = error_ instanceof Error ? error_.message : String(error_);
62
+ logWarn(hook, `${provider}:${model} exception: ${error}`);
63
+ return { provider, model, available: false, latencyMs, error };
64
+ }
65
+ }
@@ -0,0 +1,14 @@
1
+ export interface SentinelIpcPaths {
2
+ tmpDir: string;
3
+ inputPath: string;
4
+ stdoutPath: string;
5
+ stderrPath: string;
6
+ sentinelPath: string;
7
+ }
8
+ export declare function createSentinelIpcPaths(prefix: string): SentinelIpcPaths;
9
+ export declare function buildShellCaptureScript(command: string, paths: Pick<SentinelIpcPaths, 'inputPath' | 'stdoutPath' | 'stderrPath' | 'sentinelPath'>, quote: (input: string) => string): string;
10
+ export declare function waitForSentinelFile(sentinelPath: string, timeoutMs: number, pollIntervalMs?: number): Promise<boolean>;
11
+ export declare function readSentinelExitCode(sentinelPath: string, fallback?: number): number;
12
+ export declare function readTextIfExists(filePath: string): string;
13
+ export declare function cleanupSentinelIpc(paths: Pick<SentinelIpcPaths, 'tmpDir'>): void;
14
+ export declare function cleanupSentinelPath(sentinelPath: string | undefined): void;
@@ -0,0 +1,67 @@
1
+ import * as fs from 'node:fs';
2
+ import * as os from 'node:os';
3
+ import path from 'node:path';
4
+ function sanitizePrefix(prefix) {
5
+ return prefix.replaceAll(/[^a-zA-Z0-9_-]/g, '-');
6
+ }
7
+ export function createSentinelIpcPaths(prefix) {
8
+ const safePrefix = sanitizePrefix(prefix);
9
+ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), `${safePrefix}-`));
10
+ return {
11
+ tmpDir,
12
+ inputPath: path.join(tmpDir, 'input.txt'),
13
+ stdoutPath: path.join(tmpDir, 'stdout.txt'),
14
+ stderrPath: path.join(tmpDir, 'stderr.txt'),
15
+ sentinelPath: path.join(tmpDir, 'sentinel.txt'),
16
+ };
17
+ }
18
+ export function buildShellCaptureScript(command, paths, quote) {
19
+ return [
20
+ command,
21
+ `< ${quote(paths.inputPath)}`,
22
+ `> ${quote(paths.stdoutPath)}`,
23
+ `2> ${quote(paths.stderrPath)}`,
24
+ `; echo $? > ${quote(paths.sentinelPath)}`,
25
+ ].join(' ');
26
+ }
27
+ export async function waitForSentinelFile(sentinelPath, timeoutMs, pollIntervalMs = 250) {
28
+ const deadline = Date.now() + timeoutMs;
29
+ while (Date.now() < deadline) {
30
+ if (fs.existsSync(sentinelPath))
31
+ return true;
32
+ await new Promise((resolve) => {
33
+ setTimeout(resolve, pollIntervalMs);
34
+ });
35
+ }
36
+ return fs.existsSync(sentinelPath);
37
+ }
38
+ export function readSentinelExitCode(sentinelPath, fallback = 1) {
39
+ if (!fs.existsSync(sentinelPath))
40
+ return fallback;
41
+ const raw = fs.readFileSync(sentinelPath, 'utf8').trim();
42
+ const parsed = Number.parseInt(raw, 10);
43
+ return Number.isFinite(parsed) ? parsed : fallback;
44
+ }
45
+ export function readTextIfExists(filePath) {
46
+ if (!filePath || !fs.existsSync(filePath))
47
+ return '';
48
+ return fs.readFileSync(filePath, 'utf8');
49
+ }
50
+ export function cleanupSentinelIpc(paths) {
51
+ try {
52
+ fs.rmSync(paths.tmpDir, { recursive: true, force: true });
53
+ }
54
+ catch {
55
+ // Best-effort cleanup.
56
+ }
57
+ }
58
+ export function cleanupSentinelPath(sentinelPath) {
59
+ if (!sentinelPath)
60
+ return;
61
+ try {
62
+ fs.rmSync(path.dirname(sentinelPath), { recursive: true, force: true });
63
+ }
64
+ catch {
65
+ // Best-effort cleanup.
66
+ }
67
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Shared I/O for state.json — read, write, serialize.
3
+ * Extracted to avoid circular deps between context-store and task-tracker.
4
+ * See SPEC.md §7.1
5
+ */
6
+ import type { ContextState } from "../types.js";
7
+ /**
8
+ * Serialize a ContextState for JSON output.
9
+ * Omits null/undefined keys but keeps false, 0, empty string, and empty arrays.
10
+ */
11
+ export declare function toDict(state: ContextState): Record<string, unknown>;
12
+ /**
13
+ * Get path to state.json for a context.
14
+ */
15
+ export declare function statePath(contextId: string, projectRoot?: string): string;
16
+ /**
17
+ * Read and parse state.json for a context.
18
+ * Applies legacy mode migration. Returns null if file doesn't exist or is corrupt.
19
+ */
20
+ export declare function readStateJson(contextId: string, projectRoot?: string): ContextState | null;
21
+ /**
22
+ * Atomically write state.json for a context.
23
+ * Returns [success, error].
24
+ */
25
+ export declare function writeStateJson(contextId: string, state: ContextState, projectRoot?: string): [boolean, string | null];
26
+ /**
27
+ * Construct a ContextState from a dict, migrating old mode names.
28
+ * Only includes fields that are present in the source data (preserves null-stripping).
29
+ */
30
+ export declare function dictToState(data: Record<string, unknown>): ContextState;
@@ -0,0 +1,174 @@
1
+ /**
2
+ * Shared I/O for state.json — read, write, serialize.
3
+ * Extracted to avoid circular deps between context-store and task-tracker.
4
+ * See SPEC.md §7.1
5
+ */
6
+ import * as fs from "node:fs";
7
+ import path from "node:path";
8
+ import { atomicWrite } from "./atomic-write.js";
9
+ import { getContextDir } from "./constants.js";
10
+ import { logWarn } from "./logger.js";
11
+ /** Mode migration from legacy context_manager values. */
12
+ const MODE_MIGRATION = {
13
+ none: "idle",
14
+ planning: "idle",
15
+ pending_implementation: "has_staged_work",
16
+ implementing: "active",
17
+ };
18
+ /**
19
+ * Serialize a ContextState for JSON output.
20
+ * Omits null/undefined keys but keeps false, 0, empty string, and empty arrays.
21
+ */
22
+ export function toDict(state) {
23
+ const result = {};
24
+ for (const [key, value] of Object.entries(state)) {
25
+ if (value !== null && value !== undefined) {
26
+ result[key] = value;
27
+ }
28
+ }
29
+ return result;
30
+ }
31
+ /**
32
+ * Migrate old consumed flags to unified work_consumed.
33
+ * Runs on every state.json read for transparent backward compatibility.
34
+ * Idempotent: safe to run multiple times.
35
+ */
36
+ function migrateConsumedFlags(data) {
37
+ const legacy = data;
38
+ // Skip if already migrated (check both fields and mode)
39
+ const alreadyMigrated = typeof legacy.work_consumed === "boolean" &&
40
+ legacy.mode !== "has_plan" &&
41
+ legacy.mode !== "has_handoff";
42
+ if (alreadyMigrated)
43
+ return;
44
+ const hasPlan = Boolean(legacy.plan_path && legacy.plan_hash);
45
+ const hasHandoff = Boolean(legacy.handoff_path);
46
+ // Migrate consumed flag (plan takes precedence if both exist)
47
+ if (hasPlan && typeof legacy.plan_consumed === "boolean") {
48
+ data.work_consumed = legacy.plan_consumed;
49
+ }
50
+ else if (hasHandoff && typeof legacy.handoff_consumed === "boolean") {
51
+ data.work_consumed = legacy.handoff_consumed;
52
+ }
53
+ else {
54
+ data.work_consumed = false;
55
+ }
56
+ // Migrate mode: has_plan/has_handoff → has_staged_work
57
+ if (legacy.mode === "has_plan" || legacy.mode === "has_handoff") {
58
+ const artifactType = legacy.mode === "has_handoff" ? "handoff" : "plan";
59
+ data.mode = "has_staged_work";
60
+ data.next_artifact_type = artifactType;
61
+ }
62
+ // Set next_artifact_type based on which artifact exists
63
+ if (!legacy.next_artifact_type) {
64
+ if (hasPlan && hasHandoff) {
65
+ // Both exist - conflict resolution: plan priority during migration
66
+ // (Cannot determine "latest" without timestamps - plan takes precedence)
67
+ data.next_artifact_type = "plan";
68
+ data.handoff_path = null;
69
+ }
70
+ else if (hasPlan) {
71
+ data.next_artifact_type = "plan";
72
+ }
73
+ else if (hasHandoff) {
74
+ data.next_artifact_type = "handoff";
75
+ }
76
+ }
77
+ // Delete old flags (clean cut migration)
78
+ delete data.plan_consumed;
79
+ delete data.handoff_consumed;
80
+ }
81
+ /**
82
+ * Get path to state.json for a context.
83
+ */
84
+ export function statePath(contextId, projectRoot) {
85
+ return path.join(getContextDir(contextId, projectRoot), "state.json");
86
+ }
87
+ /**
88
+ * Read and parse state.json for a context.
89
+ * Applies legacy mode migration. Returns null if file doesn't exist or is corrupt.
90
+ */
91
+ export function readStateJson(contextId, projectRoot) {
92
+ const sp = statePath(contextId, projectRoot);
93
+ if (!fs.existsSync(sp))
94
+ return null;
95
+ try {
96
+ const raw = fs.readFileSync(sp, "utf8");
97
+ const data = JSON.parse(raw);
98
+ migrateConsumedFlags(data); // Migrate before dictToState
99
+ return dictToState(data);
100
+ }
101
+ catch (error) {
102
+ logWarn("state_io", `Failed to read state.json for '${contextId}': ${error}`);
103
+ return null;
104
+ }
105
+ }
106
+ /**
107
+ * Atomically write state.json for a context.
108
+ * Returns [success, error].
109
+ */
110
+ export function writeStateJson(contextId, state, projectRoot) {
111
+ const sp = statePath(contextId, projectRoot);
112
+ const dir = path.dirname(sp);
113
+ fs.mkdirSync(dir, { recursive: true });
114
+ const content = JSON.stringify(toDict(state), null, 2);
115
+ // fsync: false — state.json is reconstructable from context folder contents
116
+ return atomicWrite(sp, content, 2, [500, 1000], false);
117
+ }
118
+ /**
119
+ * Construct a ContextState from a dict, migrating old mode names.
120
+ * Only includes fields that are present in the source data (preserves null-stripping).
121
+ */
122
+ export function dictToState(data) {
123
+ // Validate required fields
124
+ if (typeof data.id !== "string" || !data.id) {
125
+ throw new Error("dictToState: missing or invalid required field 'id'");
126
+ }
127
+ const rawMode = typeof data.mode === "string" ? data.mode : "idle";
128
+ const mode = (MODE_MIGRATION[rawMode] ?? rawMode);
129
+ const state = {
130
+ id: data.id,
131
+ status: data.status ?? "active",
132
+ summary: data.summary ?? "",
133
+ method: data.method ?? "",
134
+ tags: data.tags ?? [],
135
+ created_at: data.created_at ?? "",
136
+ last_active: data.last_active ?? "",
137
+ mode,
138
+ plan_anchors: data.plan_anchors ?? [],
139
+ work_consumed: data.work_consumed ?? false,
140
+ session_ids: data.session_ids ?? [],
141
+ tasks: data.tasks ?? [],
142
+ };
143
+ // Only set nullable fields if they exist in the source data
144
+ if ("plan_path" in data)
145
+ state.plan_path = data.plan_path;
146
+ if ("plan_hash" in data)
147
+ state.plan_hash = data.plan_hash;
148
+ if ("plan_signature" in data)
149
+ state.plan_signature = data.plan_signature;
150
+ if ("plan_id" in data)
151
+ state.plan_id = data.plan_id;
152
+ if ("handoff_path" in data)
153
+ state.handoff_path = data.handoff_path;
154
+ if ("next_artifact_type" in data)
155
+ state.next_artifact_type = data.next_artifact_type;
156
+ if ("last_session" in data)
157
+ state.last_session = data.last_session;
158
+ // Migration: plan_hash_consumed (added in multi-plan context fix)
159
+ if ("plan_hash_consumed" in data) {
160
+ state.plan_hash_consumed = data.plan_hash_consumed;
161
+ }
162
+ else {
163
+ state.plan_hash_consumed = null; // Default for old contexts
164
+ }
165
+ // Preserve method-specific extension data (e.g., cc_native) that isn't
166
+ // part of the core ContextState interface. Without this, round-trip
167
+ // read→write cycles silently drop extension fields.
168
+ for (const key of Object.keys(data)) {
169
+ if (!(key in state)) {
170
+ state[key] = data[key];
171
+ }
172
+ }
173
+ return state;
174
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Stop words for context ID generation.
3
+ *
4
+ * Generated from analysis of 1,424 prompts, context summaries, and plan files.
5
+ * Words that appear frequently but don't help identify the specific task.
6
+ *
7
+ * ACTION VERBS ARE INTENTIONALLY EXCLUDED:
8
+ * add, fix, update, create, implement, refactor, migrate, debug, remove, change,
9
+ * move, rename, delete, build, test, deploy, verify, analyze, modify, write, run,
10
+ * check, replace, save, sync, load, extract, install, clean, merge, etc.
11
+ *
12
+ * See SPEC.md §14.1
13
+ */
14
+ export declare const STOP_WORDS: ReadonlySet<string>;
15
+ /**
16
+ * Filter stop words from text.
17
+ * Splits on whitespace, removes words in STOP_WORDS set and single-char words.
18
+ * See SPEC.md §6.4
19
+ */
20
+ export declare function filterStopWords(text: string): string;