aiwcli 0.10.2 → 0.11.0

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 (249) hide show
  1. package/bin/run.js +1 -1
  2. package/dist/commands/clear.d.ts +11 -6
  3. package/dist/commands/clear.js +229 -381
  4. package/dist/commands/init/index.d.ts +1 -17
  5. package/dist/commands/init/index.js +22 -107
  6. package/dist/lib/gitignore-manager.d.ts +32 -0
  7. package/dist/lib/gitignore-manager.js +141 -2
  8. package/dist/lib/template-installer.d.ts +7 -12
  9. package/dist/lib/template-installer.js +69 -193
  10. package/dist/lib/template-settings-reconstructor.d.ts +35 -0
  11. package/dist/lib/template-settings-reconstructor.js +130 -0
  12. package/dist/templates/CLAUDE.md +8 -8
  13. package/dist/templates/_shared/.claude/commands/handoff-resume.md +64 -0
  14. package/dist/templates/_shared/.claude/commands/handoff.md +16 -10
  15. package/dist/templates/_shared/.claude/settings.json +7 -7
  16. package/dist/templates/_shared/hooks-ts/_utils/git-state.ts +2 -0
  17. package/dist/templates/_shared/hooks-ts/archive_plan.ts +159 -0
  18. package/dist/templates/_shared/hooks-ts/context_monitor.ts +147 -0
  19. package/dist/templates/_shared/hooks-ts/file-suggestion.ts +130 -0
  20. package/dist/templates/_shared/hooks-ts/pre_compact.ts +49 -0
  21. package/dist/templates/_shared/hooks-ts/session_end.ts +104 -0
  22. package/dist/templates/_shared/hooks-ts/session_start.ts +144 -0
  23. package/dist/templates/_shared/hooks-ts/task_create_capture.ts +48 -0
  24. package/dist/templates/_shared/hooks-ts/task_update_capture.ts +74 -0
  25. package/dist/templates/_shared/hooks-ts/user_prompt_submit.ts +83 -0
  26. package/dist/templates/_shared/lib-ts/CLAUDE.md +318 -0
  27. package/dist/templates/_shared/lib-ts/base/atomic-write.ts +138 -0
  28. package/dist/templates/_shared/lib-ts/base/constants.ts +306 -0
  29. package/dist/templates/_shared/lib-ts/base/git-state.ts +58 -0
  30. package/dist/templates/_shared/lib-ts/base/hook-utils.ts +439 -0
  31. package/dist/templates/_shared/lib-ts/base/inference.ts +252 -0
  32. package/dist/templates/_shared/lib-ts/base/logger.ts +250 -0
  33. package/dist/templates/_shared/lib-ts/base/state-io.ts +116 -0
  34. package/dist/templates/_shared/lib-ts/base/stop-words.ts +184 -0
  35. package/dist/templates/_shared/lib-ts/base/subprocess-utils.ts +162 -0
  36. package/dist/templates/_shared/lib-ts/base/utils.ts +184 -0
  37. package/dist/templates/_shared/lib-ts/context/context-formatter.ts +438 -0
  38. package/dist/templates/_shared/lib-ts/context/context-selector.ts +515 -0
  39. package/dist/templates/_shared/lib-ts/context/context-store.ts +707 -0
  40. package/dist/templates/_shared/lib-ts/context/plan-manager.ts +316 -0
  41. package/dist/templates/_shared/lib-ts/context/task-tracker.ts +185 -0
  42. package/dist/templates/_shared/lib-ts/handoff/document-generator.ts +216 -0
  43. package/dist/templates/_shared/lib-ts/handoff/handoff-reader.ts +159 -0
  44. package/dist/templates/_shared/lib-ts/package.json +21 -0
  45. package/dist/templates/_shared/lib-ts/templates/formatters.ts +104 -0
  46. package/dist/templates/_shared/{lib/templates/plan_context.py → lib-ts/templates/plan-context.ts} +14 -22
  47. package/dist/templates/_shared/lib-ts/tsconfig.json +13 -0
  48. package/dist/templates/_shared/lib-ts/types.ts +164 -0
  49. package/dist/templates/_shared/scripts/resolve_context.ts +24 -0
  50. package/dist/templates/_shared/scripts/resume_handoff.ts +321 -0
  51. package/dist/templates/_shared/scripts/save_handoff.ts +359 -0
  52. package/dist/templates/_shared/scripts/status_line.ts +733 -0
  53. package/dist/templates/cc-native/.claude/settings.json +175 -185
  54. package/dist/templates/cc-native/TEMPLATE-SCHEMA.md +15 -17
  55. package/dist/templates/cc-native/_cc-native/agents/ARCH-EVOLUTION.md +63 -0
  56. package/dist/templates/cc-native/_cc-native/agents/ARCH-PATTERNS.md +62 -0
  57. package/dist/templates/cc-native/_cc-native/agents/ARCH-STRUCTURE.md +63 -0
  58. package/dist/templates/cc-native/_cc-native/agents/{ASSUMPTION-CHAIN-TRACER.md → ASSUMPTION-TRACER.md} +6 -10
  59. package/dist/templates/cc-native/_cc-native/agents/CLARITY-AUDITOR.md +6 -10
  60. package/dist/templates/cc-native/_cc-native/agents/CLAUDE.md +74 -3
  61. package/dist/templates/cc-native/_cc-native/agents/COMPLETENESS-FEASIBILITY.md +67 -0
  62. package/dist/templates/cc-native/_cc-native/agents/COMPLETENESS-GAPS.md +71 -0
  63. package/dist/templates/cc-native/_cc-native/agents/COMPLETENESS-ORDERING.md +63 -0
  64. package/dist/templates/cc-native/_cc-native/agents/CONSTRAINT-VALIDATOR.md +73 -0
  65. package/dist/templates/cc-native/_cc-native/agents/DESIGN-ADR-VALIDATOR.md +62 -0
  66. package/dist/templates/cc-native/_cc-native/agents/DESIGN-SCALE-MATCHER.md +65 -0
  67. package/dist/templates/cc-native/_cc-native/agents/DEVILS-ADVOCATE.md +6 -9
  68. package/dist/templates/cc-native/_cc-native/agents/DOCUMENTATION-PHILOSOPHY.md +87 -0
  69. package/dist/templates/cc-native/_cc-native/agents/HANDOFF-READINESS.md +5 -9
  70. package/dist/templates/cc-native/_cc-native/agents/{HIDDEN-COMPLEXITY-DETECTOR.md → HIDDEN-COMPLEXITY.md} +6 -10
  71. package/dist/templates/cc-native/_cc-native/agents/INCREMENTAL-DELIVERY.md +67 -0
  72. package/dist/templates/cc-native/_cc-native/agents/PLAN-ORCHESTRATOR.md +91 -18
  73. package/dist/templates/cc-native/_cc-native/agents/RISK-DEPENDENCY.md +63 -0
  74. package/dist/templates/cc-native/_cc-native/agents/RISK-FMEA.md +67 -0
  75. package/dist/templates/cc-native/_cc-native/agents/RISK-PREMORTEM.md +72 -0
  76. package/dist/templates/cc-native/_cc-native/agents/RISK-REVERSIBILITY.md +75 -0
  77. package/dist/templates/cc-native/_cc-native/agents/SCOPE-BOUNDARY.md +78 -0
  78. package/dist/templates/cc-native/_cc-native/agents/SIMPLICITY-GUARDIAN.md +5 -9
  79. package/dist/templates/cc-native/_cc-native/agents/SKEPTIC.md +16 -12
  80. package/dist/templates/cc-native/_cc-native/agents/TESTDRIVEN-BEHAVIOR-AUDITOR.md +62 -0
  81. package/dist/templates/cc-native/_cc-native/agents/TESTDRIVEN-CHARACTERIZATION.md +72 -0
  82. package/dist/templates/cc-native/_cc-native/agents/TESTDRIVEN-FIRST-VALIDATOR.md +62 -0
  83. package/dist/templates/cc-native/_cc-native/agents/TESTDRIVEN-PYRAMID-ANALYZER.md +62 -0
  84. package/dist/templates/cc-native/_cc-native/agents/TRADEOFF-COSTS.md +68 -0
  85. package/dist/templates/cc-native/_cc-native/agents/TRADEOFF-STAKEHOLDERS.md +66 -0
  86. package/dist/templates/cc-native/_cc-native/agents/VERIFY-COVERAGE.md +75 -0
  87. package/dist/templates/cc-native/_cc-native/agents/VERIFY-STRENGTH.md +70 -0
  88. package/dist/templates/cc-native/_cc-native/hooks/CLAUDE.md +109 -135
  89. package/dist/templates/cc-native/_cc-native/hooks/add_plan_context.ts +119 -0
  90. package/dist/templates/cc-native/_cc-native/hooks/cc-native-plan-review.ts +921 -0
  91. package/dist/templates/cc-native/_cc-native/hooks/plan_questions_early.ts +61 -0
  92. package/dist/templates/cc-native/_cc-native/lib-ts/aggregate-agents.ts +157 -0
  93. package/dist/templates/cc-native/_cc-native/lib-ts/artifacts.ts +709 -0
  94. package/dist/templates/cc-native/_cc-native/lib-ts/cc-native-state.ts +199 -0
  95. package/dist/templates/cc-native/_cc-native/lib-ts/cli-output-parser.ts +124 -0
  96. package/dist/templates/cc-native/_cc-native/lib-ts/config.ts +57 -0
  97. package/dist/templates/cc-native/_cc-native/lib-ts/constants.ts +83 -0
  98. package/dist/templates/cc-native/_cc-native/lib-ts/debug.ts +80 -0
  99. package/dist/templates/cc-native/_cc-native/lib-ts/index.ts +119 -0
  100. package/dist/templates/cc-native/_cc-native/lib-ts/json-parser.ts +162 -0
  101. package/dist/templates/cc-native/_cc-native/lib-ts/nul +3 -0
  102. package/dist/templates/cc-native/_cc-native/lib-ts/orchestrator.ts +249 -0
  103. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/agent.ts +155 -0
  104. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/codex.ts +130 -0
  105. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/gemini.ts +106 -0
  106. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/index.ts +10 -0
  107. package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/types.ts +23 -0
  108. package/dist/templates/cc-native/_cc-native/lib-ts/state.ts +243 -0
  109. package/dist/templates/cc-native/_cc-native/lib-ts/tsconfig.json +18 -0
  110. package/dist/templates/cc-native/_cc-native/lib-ts/types.ts +310 -0
  111. package/dist/templates/cc-native/_cc-native/lib-ts/verdict.ts +72 -0
  112. package/dist/templates/cc-native/_cc-native/plan-review.config.json +12 -16
  113. package/oclif.manifest.json +1 -1
  114. package/package.json +1 -1
  115. package/dist/lib/template-merger.d.ts +0 -47
  116. package/dist/lib/template-merger.js +0 -162
  117. package/dist/templates/_shared/hooks/__init__.py +0 -16
  118. package/dist/templates/_shared/hooks/__pycache__/__init__.cpython-313.pyc +0 -0
  119. package/dist/templates/_shared/hooks/__pycache__/archive_plan.cpython-313.pyc +0 -0
  120. package/dist/templates/_shared/hooks/__pycache__/context_enforcer.cpython-313.pyc +0 -0
  121. package/dist/templates/_shared/hooks/__pycache__/context_monitor.cpython-313.pyc +0 -0
  122. package/dist/templates/_shared/hooks/__pycache__/file-suggestion.cpython-313.pyc +0 -0
  123. package/dist/templates/_shared/hooks/__pycache__/pre_compact.cpython-313.pyc +0 -0
  124. package/dist/templates/_shared/hooks/__pycache__/session_end.cpython-313.pyc +0 -0
  125. package/dist/templates/_shared/hooks/__pycache__/session_start.cpython-313.pyc +0 -0
  126. package/dist/templates/_shared/hooks/__pycache__/task_create_atomicity.cpython-313.pyc +0 -0
  127. package/dist/templates/_shared/hooks/__pycache__/task_create_capture.cpython-313.pyc +0 -0
  128. package/dist/templates/_shared/hooks/__pycache__/task_update_capture.cpython-313.pyc +0 -0
  129. package/dist/templates/_shared/hooks/__pycache__/user_prompt_submit.cpython-313.pyc +0 -0
  130. package/dist/templates/_shared/hooks/archive_plan.py +0 -169
  131. package/dist/templates/_shared/hooks/context_monitor.py +0 -270
  132. package/dist/templates/_shared/hooks/file-suggestion.py +0 -215
  133. package/dist/templates/_shared/hooks/pre_compact.py +0 -104
  134. package/dist/templates/_shared/hooks/session_end.py +0 -173
  135. package/dist/templates/_shared/hooks/session_start.py +0 -206
  136. package/dist/templates/_shared/hooks/task_create_capture.py +0 -108
  137. package/dist/templates/_shared/hooks/task_update_capture.py +0 -145
  138. package/dist/templates/_shared/hooks/user_prompt_submit.py +0 -139
  139. package/dist/templates/_shared/lib/__init__.py +0 -1
  140. package/dist/templates/_shared/lib/__pycache__/__init__.cpython-313.pyc +0 -0
  141. package/dist/templates/_shared/lib/base/__init__.py +0 -65
  142. package/dist/templates/_shared/lib/base/__pycache__/__init__.cpython-313.pyc +0 -0
  143. package/dist/templates/_shared/lib/base/__pycache__/atomic_write.cpython-313.pyc +0 -0
  144. package/dist/templates/_shared/lib/base/__pycache__/constants.cpython-313.pyc +0 -0
  145. package/dist/templates/_shared/lib/base/__pycache__/hook_utils.cpython-313.pyc +0 -0
  146. package/dist/templates/_shared/lib/base/__pycache__/inference.cpython-313.pyc +0 -0
  147. package/dist/templates/_shared/lib/base/__pycache__/logger.cpython-313.pyc +0 -0
  148. package/dist/templates/_shared/lib/base/__pycache__/stop_words.cpython-313.pyc +0 -0
  149. package/dist/templates/_shared/lib/base/__pycache__/subprocess_utils.cpython-313.pyc +0 -0
  150. package/dist/templates/_shared/lib/base/__pycache__/utils.cpython-313.pyc +0 -0
  151. package/dist/templates/_shared/lib/base/atomic_write.py +0 -180
  152. package/dist/templates/_shared/lib/base/constants.py +0 -358
  153. package/dist/templates/_shared/lib/base/hook_utils.py +0 -341
  154. package/dist/templates/_shared/lib/base/inference.py +0 -318
  155. package/dist/templates/_shared/lib/base/logger.py +0 -291
  156. package/dist/templates/_shared/lib/base/stop_words.py +0 -213
  157. package/dist/templates/_shared/lib/base/subprocess_utils.py +0 -46
  158. package/dist/templates/_shared/lib/base/utils.py +0 -242
  159. package/dist/templates/_shared/lib/context/__init__.py +0 -102
  160. package/dist/templates/_shared/lib/context/__pycache__/__init__.cpython-313.pyc +0 -0
  161. package/dist/templates/_shared/lib/context/__pycache__/cache.cpython-313.pyc +0 -0
  162. package/dist/templates/_shared/lib/context/__pycache__/context_extractor.cpython-313.pyc +0 -0
  163. package/dist/templates/_shared/lib/context/__pycache__/context_formatter.cpython-313.pyc +0 -0
  164. package/dist/templates/_shared/lib/context/__pycache__/context_manager.cpython-313.pyc +0 -0
  165. package/dist/templates/_shared/lib/context/__pycache__/context_selector.cpython-313.pyc +0 -0
  166. package/dist/templates/_shared/lib/context/__pycache__/context_store.cpython-313.pyc +0 -0
  167. package/dist/templates/_shared/lib/context/__pycache__/discovery.cpython-313.pyc +0 -0
  168. package/dist/templates/_shared/lib/context/__pycache__/event_log.cpython-313.pyc +0 -0
  169. package/dist/templates/_shared/lib/context/__pycache__/plan_archive.cpython-313.pyc +0 -0
  170. package/dist/templates/_shared/lib/context/__pycache__/plan_manager.cpython-313.pyc +0 -0
  171. package/dist/templates/_shared/lib/context/__pycache__/task_sync.cpython-313.pyc +0 -0
  172. package/dist/templates/_shared/lib/context/__pycache__/task_tracker.cpython-313.pyc +0 -0
  173. package/dist/templates/_shared/lib/context/context_formatter.py +0 -317
  174. package/dist/templates/_shared/lib/context/context_selector.py +0 -508
  175. package/dist/templates/_shared/lib/context/context_store.py +0 -653
  176. package/dist/templates/_shared/lib/context/plan_manager.py +0 -204
  177. package/dist/templates/_shared/lib/context/task_tracker.py +0 -188
  178. package/dist/templates/_shared/lib/handoff/__init__.py +0 -22
  179. package/dist/templates/_shared/lib/handoff/__pycache__/__init__.cpython-313.pyc +0 -0
  180. package/dist/templates/_shared/lib/handoff/__pycache__/document_generator.cpython-313.pyc +0 -0
  181. package/dist/templates/_shared/lib/handoff/document_generator.py +0 -278
  182. package/dist/templates/_shared/lib/templates/README.md +0 -206
  183. package/dist/templates/_shared/lib/templates/__init__.py +0 -36
  184. package/dist/templates/_shared/lib/templates/__pycache__/__init__.cpython-313.pyc +0 -0
  185. package/dist/templates/_shared/lib/templates/__pycache__/formatters.cpython-313.pyc +0 -0
  186. package/dist/templates/_shared/lib/templates/__pycache__/persona_questions.cpython-313.pyc +0 -0
  187. package/dist/templates/_shared/lib/templates/__pycache__/plan_context.cpython-313.pyc +0 -0
  188. package/dist/templates/_shared/lib/templates/formatters.py +0 -146
  189. package/dist/templates/_shared/scripts/__pycache__/save_handoff.cpython-313.pyc +0 -0
  190. package/dist/templates/_shared/scripts/__pycache__/status_line.cpython-313.pyc +0 -0
  191. package/dist/templates/_shared/scripts/save_handoff.py +0 -357
  192. package/dist/templates/_shared/scripts/status_line.py +0 -701
  193. package/dist/templates/cc-native/.claude/commands/cc-native/fresh-perspective.md +0 -8
  194. package/dist/templates/cc-native/.windsurf/workflows/cc-native/fresh-perspective.md +0 -8
  195. package/dist/templates/cc-native/MIGRATION.md +0 -86
  196. package/dist/templates/cc-native/_cc-native/agents/ACCESSIBILITY-TESTER.md +0 -79
  197. package/dist/templates/cc-native/_cc-native/agents/ARCHITECT-REVIEWER.md +0 -48
  198. package/dist/templates/cc-native/_cc-native/agents/CODE-REVIEWER.md +0 -70
  199. package/dist/templates/cc-native/_cc-native/agents/COMPLETENESS-CHECKER.md +0 -59
  200. package/dist/templates/cc-native/_cc-native/agents/CONTEXT-EXTRACTOR.md +0 -92
  201. package/dist/templates/cc-native/_cc-native/agents/DOCUMENTATION-REVIEWER.md +0 -51
  202. package/dist/templates/cc-native/_cc-native/agents/FEASIBILITY-ANALYST.md +0 -57
  203. package/dist/templates/cc-native/_cc-native/agents/FRESH-PERSPECTIVE.md +0 -54
  204. package/dist/templates/cc-native/_cc-native/agents/INCENTIVE-MAPPER.md +0 -61
  205. package/dist/templates/cc-native/_cc-native/agents/PENETRATION-TESTER.md +0 -79
  206. package/dist/templates/cc-native/_cc-native/agents/PERFORMANCE-ENGINEER.md +0 -75
  207. package/dist/templates/cc-native/_cc-native/agents/PRECEDENT-FINDER.md +0 -70
  208. package/dist/templates/cc-native/_cc-native/agents/REVERSIBILITY-ANALYST.md +0 -61
  209. package/dist/templates/cc-native/_cc-native/agents/RISK-ASSESSOR.md +0 -58
  210. package/dist/templates/cc-native/_cc-native/agents/SECOND-ORDER-ANALYST.md +0 -61
  211. package/dist/templates/cc-native/_cc-native/agents/STAKEHOLDER-ADVOCATE.md +0 -55
  212. package/dist/templates/cc-native/_cc-native/agents/TRADE-OFF-ILLUMINATOR.md +0 -204
  213. package/dist/templates/cc-native/_cc-native/hooks/__pycache__/add_plan_context.cpython-313.pyc +0 -0
  214. package/dist/templates/cc-native/_cc-native/hooks/__pycache__/cc-native-plan-review.cpython-313.pyc +0 -0
  215. package/dist/templates/cc-native/_cc-native/hooks/__pycache__/mark_questions_asked.cpython-313.pyc +0 -0
  216. package/dist/templates/cc-native/_cc-native/hooks/__pycache__/plan_accepted.cpython-313.pyc +0 -0
  217. package/dist/templates/cc-native/_cc-native/hooks/__pycache__/plan_questions_early.cpython-313.pyc +0 -0
  218. package/dist/templates/cc-native/_cc-native/hooks/__pycache__/suggest-fresh-perspective.cpython-313.pyc +0 -0
  219. package/dist/templates/cc-native/_cc-native/hooks/add_plan_context.py +0 -130
  220. package/dist/templates/cc-native/_cc-native/hooks/cc-native-plan-review.py +0 -869
  221. package/dist/templates/cc-native/_cc-native/hooks/plan_questions_early.py +0 -81
  222. package/dist/templates/cc-native/_cc-native/hooks/suggest-fresh-perspective.py +0 -340
  223. package/dist/templates/cc-native/_cc-native/lib/CLAUDE.md +0 -265
  224. package/dist/templates/cc-native/_cc-native/lib/__init__.py +0 -53
  225. package/dist/templates/cc-native/_cc-native/lib/__pycache__/__init__.cpython-313.pyc +0 -0
  226. package/dist/templates/cc-native/_cc-native/lib/__pycache__/atomic_write.cpython-313.pyc +0 -0
  227. package/dist/templates/cc-native/_cc-native/lib/__pycache__/constants.cpython-313.pyc +0 -0
  228. package/dist/templates/cc-native/_cc-native/lib/__pycache__/debug.cpython-313.pyc +0 -0
  229. package/dist/templates/cc-native/_cc-native/lib/__pycache__/orchestrator.cpython-313.pyc +0 -0
  230. package/dist/templates/cc-native/_cc-native/lib/__pycache__/state.cpython-313.pyc +0 -0
  231. package/dist/templates/cc-native/_cc-native/lib/__pycache__/utils.cpython-313.pyc +0 -0
  232. package/dist/templates/cc-native/_cc-native/lib/constants.py +0 -45
  233. package/dist/templates/cc-native/_cc-native/lib/debug.py +0 -139
  234. package/dist/templates/cc-native/_cc-native/lib/orchestrator.py +0 -362
  235. package/dist/templates/cc-native/_cc-native/lib/reviewers/__init__.py +0 -28
  236. package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/__init__.cpython-313.pyc +0 -0
  237. package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/agent.cpython-313.pyc +0 -0
  238. package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/base.cpython-313.pyc +0 -0
  239. package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/codex.cpython-313.pyc +0 -0
  240. package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/gemini.cpython-313.pyc +0 -0
  241. package/dist/templates/cc-native/_cc-native/lib/reviewers/agent.py +0 -215
  242. package/dist/templates/cc-native/_cc-native/lib/reviewers/base.py +0 -88
  243. package/dist/templates/cc-native/_cc-native/lib/reviewers/codex.py +0 -124
  244. package/dist/templates/cc-native/_cc-native/lib/reviewers/gemini.py +0 -108
  245. package/dist/templates/cc-native/_cc-native/lib/state.py +0 -268
  246. package/dist/templates/cc-native/_cc-native/lib/utils.py +0 -1027
  247. package/dist/templates/cc-native/_cc-native/scripts/__pycache__/aggregate_agents.cpython-313.pyc +0 -0
  248. package/dist/templates/cc-native/_cc-native/scripts/aggregate_agents.py +0 -168
  249. package/dist/templates/cc-native/_cc-native/workflows/fresh-perspective.md +0 -134
@@ -0,0 +1,707 @@
1
+ /**
2
+ * Context store — 2-layer CRUD for context state management.
3
+ * See SPEC.md §7
4
+ *
5
+ * Replaces context_manager's 3-layer approach with a simpler 2-layer model:
6
+ * state.json (per context folder — SOURCE OF TRUTH)
7
+ * index.json (at _output/ root — fast session→context lookup)
8
+ */
9
+
10
+ import * as fs from "node:fs";
11
+ import * as path from "node:path";
12
+
13
+ import { atomicWrite } from "../base/atomic-write.js";
14
+ import {
15
+ getArchiveContextDir,
16
+ getArchiveDir,
17
+ getArchiveIndexPath,
18
+ getContextDir,
19
+ getContextsDir,
20
+ getIndexPath,
21
+ validateContextId,
22
+ } from "../base/constants.js";
23
+ import { logDebug as _logDebug, logError, logInfo, logWarn, setContextPath } from "../base/logger.js";
24
+ import { dictToState as _dictToState, readStateJson, toDict as _toDict, writeStateJson } from "../base/state-io.js";
25
+ import { generateContextId, nowIso } from "../base/utils.js";
26
+ import type { ContextState, IndexEntry, IndexFile, Mode } from "../types.js";
27
+
28
+ const INDEX_VERSION = "3.0";
29
+
30
+ // ---------------------------------------------------------------------------
31
+ // Internal helpers
32
+ // ---------------------------------------------------------------------------
33
+
34
+ function loadIndex(projectRoot?: string): IndexFile {
35
+ const indexPath = getIndexPath(projectRoot);
36
+ if (fs.existsSync(indexPath)) {
37
+ try {
38
+ const raw = fs.readFileSync(indexPath, "utf8");
39
+ return JSON.parse(raw) as IndexFile;
40
+ } catch (error: any) {
41
+ logWarn("context_store", `Failed to read index, recreating: ${error}`);
42
+ }
43
+ }
44
+
45
+ return { version: INDEX_VERSION, updated_at: nowIso(), sessions: {}, contexts: {} };
46
+ }
47
+
48
+ function saveIndex(index: IndexFile, projectRoot?: string): boolean {
49
+ index.updated_at = nowIso();
50
+ const content = JSON.stringify(index, null, 2);
51
+ const [success, error] = atomicWrite(getIndexPath(projectRoot), content);
52
+ if (!success) {
53
+ logWarn("context_store", `Failed to write index: ${error}`);
54
+ }
55
+
56
+ return success;
57
+ }
58
+
59
+ function toIndexEntry(state: ContextState): IndexEntry {
60
+ return {
61
+ summary: state.summary,
62
+ mode: state.mode,
63
+ last_active: state.last_active,
64
+ };
65
+ }
66
+
67
+ /**
68
+ * Backward compat: read legacy context.json and convert to ContextState.
69
+ */
70
+ function migrateContextJson(contextId: string, projectRoot?: string): ContextState | null {
71
+ const legacyPath = path.join(getContextDir(contextId, projectRoot), "context.json");
72
+ if (!fs.existsSync(legacyPath)) return null;
73
+
74
+ try {
75
+ const data = JSON.parse(fs.readFileSync(legacyPath, "utf8"));
76
+ const inFlight = data.in_flight ?? {};
77
+ const oldMode = inFlight.mode ?? "none";
78
+ const MODE_MIGRATION: Record<string, string> = {
79
+ none: "idle",
80
+ planning: "idle",
81
+ pending_implementation: "has_plan",
82
+ implementing: "active",
83
+ };
84
+ const mode = (MODE_MIGRATION[oldMode] ?? "idle") as Mode;
85
+
86
+ const sessionIds: string[] = inFlight.session_ids ??
87
+ (inFlight.session_id ? [inFlight.session_id] : []);
88
+
89
+ return {
90
+ id: data.id ?? contextId,
91
+ status: data.status ?? "active",
92
+ summary: data.summary ?? "",
93
+ method: data.method ?? "",
94
+ tags: data.tags ?? [],
95
+ created_at: data.created_at ?? "",
96
+ last_active: data.last_active ?? "",
97
+ mode,
98
+ plan_path: inFlight.artifact_path ?? null,
99
+ plan_hash: inFlight.artifact_hash ?? null,
100
+ plan_signature: null,
101
+ plan_id: null,
102
+ plan_anchors: [],
103
+ plan_consumed: false,
104
+ handoff_path: inFlight.handoff_path ?? null,
105
+ handoff_consumed: false,
106
+ session_ids: sessionIds,
107
+ last_session: null,
108
+ tasks: [],
109
+ };
110
+ } catch (error: any) {
111
+ logWarn("context_store", `Failed to migrate context.json for '${contextId}': ${error}`);
112
+ return null;
113
+ }
114
+ }
115
+
116
+ // ---------------------------------------------------------------------------
117
+ // Core CRUD
118
+ // ---------------------------------------------------------------------------
119
+
120
+ /**
121
+ * Read state.json for a context. Falls back to context.json for migration.
122
+ * See SPEC.md §7.2
123
+ */
124
+ export function loadState(contextId: string, projectRoot?: string): ContextState | null {
125
+ const state = readStateJson(contextId, projectRoot);
126
+ if (state) return state;
127
+
128
+ // Backward compat: migrate from legacy context.json
129
+ return migrateContextJson(contextId, projectRoot);
130
+ }
131
+
132
+ /**
133
+ * Atomically write state.json AND update index.json.
134
+ * See SPEC.md §7.3
135
+ */
136
+ export function saveState(
137
+ contextId: string,
138
+ state: ContextState,
139
+ projectRoot?: string,
140
+ ): [boolean, null | string] {
141
+ // Ensure the state ID matches
142
+ state.id = contextId;
143
+
144
+ const [success, error] = writeStateJson(contextId, state, projectRoot);
145
+ if (!success) {
146
+ logWarn("context_store", `Failed to write state.json for '${contextId}': ${error}`);
147
+ return [false, error];
148
+ }
149
+
150
+ // Update index.json
151
+ const index = loadIndex(projectRoot);
152
+ index.contexts[contextId] = toIndexEntry(state);
153
+ // Keep session mappings in sync
154
+ for (const sid of state.session_ids) {
155
+ if (!index.sessions) index.sessions = {} as Record<string, string>;
156
+ index.sessions[sid] = contextId;
157
+ }
158
+
159
+ const indexOk = saveIndex(index, projectRoot);
160
+ if (!indexOk) {
161
+ return [true, "state.json saved but index.json update failed"];
162
+ }
163
+
164
+ return [true, null];
165
+ }
166
+
167
+ /**
168
+ * Create a new context folder + state.json + index entry.
169
+ * Throws ValueError-equivalent if context already exists.
170
+ * See SPEC.md §7.4
171
+ */
172
+ export function createContext(
173
+ contextId: null | string,
174
+ summary: string,
175
+ method = "",
176
+ projectRoot?: string,
177
+ tags?: string[],
178
+ ): ContextState {
179
+ // Generate ID if needed
180
+ if (!contextId) {
181
+ const existingIds = new Set<string>();
182
+ const contextsDir = getContextsDir(projectRoot);
183
+ if (fs.existsSync(contextsDir)) {
184
+ for (const entry of fs.readdirSync(contextsDir)) {
185
+ const fullPath = path.join(contextsDir, entry);
186
+ try {
187
+ if (fs.statSync(fullPath).isDirectory()) {
188
+ existingIds.add(entry);
189
+ }
190
+ } catch { /* ignore */ }
191
+ }
192
+ }
193
+
194
+ contextId = generateContextId(summary, existingIds);
195
+ }
196
+
197
+ contextId = validateContextId(contextId);
198
+ const contextDir = getContextDir(contextId, projectRoot);
199
+
200
+ if (fs.existsSync(contextDir)) {
201
+ throw new Error(`Context '${contextId}' already exists`);
202
+ }
203
+
204
+ fs.mkdirSync(contextDir, { recursive: true });
205
+
206
+ const now = nowIso();
207
+ const state: ContextState = {
208
+ id: contextId,
209
+ status: "active",
210
+ summary,
211
+ method,
212
+ tags: tags ?? [],
213
+ created_at: now,
214
+ last_active: now,
215
+ mode: "idle",
216
+ plan_path: null,
217
+ plan_hash: null,
218
+ plan_signature: null,
219
+ plan_id: null,
220
+ plan_anchors: [],
221
+ plan_consumed: false,
222
+ handoff_path: null,
223
+ handoff_consumed: false,
224
+ session_ids: [],
225
+ last_session: null,
226
+ tasks: [],
227
+ };
228
+
229
+ saveState(contextId, state, projectRoot);
230
+ logInfo("context_store", `Created context: ${contextId}`);
231
+ return state;
232
+ }
233
+
234
+ /**
235
+ * Load a single context by ID.
236
+ * See SPEC.md §7.5
237
+ */
238
+ export function getContext(contextId: string, projectRoot?: string): ContextState | null {
239
+ try {
240
+ contextId = validateContextId(contextId);
241
+ } catch {
242
+ return null;
243
+ }
244
+
245
+ return loadState(contextId, projectRoot);
246
+ }
247
+
248
+ /**
249
+ * List contexts from index.json, loading each state.json.
250
+ * Falls back to scanning context folders if index is missing.
251
+ * Results sorted by last_active descending.
252
+ * See SPEC.md §7.6
253
+ */
254
+ export function getAllContexts(
255
+ status?: string,
256
+ projectRoot?: string,
257
+ ): ContextState[] {
258
+ const results: ContextState[] = [];
259
+ const contextsDir = getContextsDir(projectRoot);
260
+ if (!fs.existsSync(contextsDir)) return [];
261
+
262
+ // Try index-driven path first
263
+ const index = loadIndex(projectRoot);
264
+ const ctxMap = index.contexts;
265
+
266
+ if (ctxMap && typeof ctxMap === "object" && Object.keys(ctxMap).length > 0) {
267
+ for (const cid of Object.keys(ctxMap)) {
268
+ const state = loadState(cid, projectRoot);
269
+ if (state && (!status || state.status === status)) {
270
+ results.push(state);
271
+ }
272
+ }
273
+ } else {
274
+ // Fallback: scan folders
275
+ try {
276
+ for (const entry of fs.readdirSync(contextsDir)) {
277
+ if (entry.startsWith("_")) continue;
278
+ const fullPath = path.join(contextsDir, entry);
279
+ try {
280
+ if (!fs.statSync(fullPath).isDirectory()) continue;
281
+ } catch { continue; }
282
+
283
+ const state = loadState(entry, projectRoot);
284
+ if (state && (!status || state.status === status)) {
285
+ results.push(state);
286
+ }
287
+ }
288
+ } catch { /* empty dir */ }
289
+ }
290
+
291
+ results.sort((a, b) => (b.last_active || "").localeCompare(a.last_active || ""));
292
+ return results;
293
+ }
294
+
295
+ /**
296
+ * Update allowed metadata fields (summary, tags, method) on a context.
297
+ * See SPEC.md §7.7
298
+ */
299
+ export function updateContext(
300
+ contextId: string,
301
+ updates: Partial<Pick<ContextState, "method" | "summary" | "tags">>,
302
+ projectRoot?: string,
303
+ ): ContextState | null {
304
+ const state = getContext(contextId, projectRoot);
305
+ if (!state) return null;
306
+
307
+ let changed = false;
308
+ if (updates.summary !== undefined) { state.summary = updates.summary; changed = true; }
309
+ if (updates.tags !== undefined) { state.tags = updates.tags; changed = true; }
310
+ if (updates.method !== undefined) { state.method = updates.method; changed = true; }
311
+
312
+ if (!changed) return state;
313
+
314
+ state.last_active = nowIso();
315
+ saveState(contextId, state, projectRoot);
316
+ return state;
317
+ }
318
+
319
+ // ---------------------------------------------------------------------------
320
+ // Session binding & mode updates
321
+ // ---------------------------------------------------------------------------
322
+
323
+ /**
324
+ * O(1) lookup: check index.json sessions map first.
325
+ * Side effect: sets logger context path for per-context log routing.
326
+ * See SPEC.md §7.8
327
+ */
328
+ export function getContextBySessionId(
329
+ sessionId: string,
330
+ projectRoot?: string,
331
+ ): ContextState | null {
332
+ if (!sessionId || sessionId === "unknown") return null;
333
+
334
+ const index = loadIndex(projectRoot);
335
+ const cid = index.sessions?.[sessionId];
336
+ if (cid) {
337
+ const state = loadState(cid, projectRoot);
338
+ if (state) {
339
+ setLoggerContext(state.id, projectRoot);
340
+ return state;
341
+ }
342
+ }
343
+
344
+ // Fallback: scan all contexts
345
+ for (const state of getAllContexts("active", projectRoot)) {
346
+ if (state.session_ids.includes(sessionId)) {
347
+ setLoggerContext(state.id, projectRoot);
348
+ return state;
349
+ }
350
+ }
351
+
352
+ return null;
353
+ }
354
+
355
+ function setLoggerContext(contextId: string, projectRoot?: string): void {
356
+ try {
357
+ const ctxDir = getContextDir(contextId, projectRoot);
358
+ if (fs.existsSync(ctxDir)) {
359
+ setContextPath(ctxDir);
360
+ }
361
+ } catch {
362
+ // Never crash on logging setup
363
+ }
364
+ }
365
+
366
+ /**
367
+ * Add session_id to both index.json sessions map and state.json session_ids.
368
+ * See SPEC.md §7.9
369
+ */
370
+ export function bindSession(
371
+ contextId: string,
372
+ sessionId: string,
373
+ projectRoot?: string,
374
+ ): boolean {
375
+ if (!sessionId || sessionId === "unknown") return false;
376
+
377
+ const state = getContext(contextId, projectRoot);
378
+ if (!state) return false;
379
+
380
+ if (!state.session_ids.includes(sessionId)) {
381
+ state.session_ids.push(sessionId);
382
+ }
383
+
384
+ state.last_active = nowIso();
385
+
386
+ const [success] = saveState(contextId, state, projectRoot);
387
+ return success;
388
+ }
389
+
390
+ /**
391
+ * Change the mode field, optionally setting plan/handoff fields.
392
+ * See SPEC.md §7.10
393
+ */
394
+ export function updateMode(
395
+ contextId: string,
396
+ mode: Mode,
397
+ projectRoot?: string,
398
+ opts?: {
399
+ handoff_consumed?: boolean;
400
+ plan_anchors?: string[];
401
+ plan_consumed?: boolean;
402
+ plan_hash?: string;
403
+ plan_id?: string;
404
+ plan_path?: string;
405
+ plan_signature?: string;
406
+ },
407
+ ): ContextState | null {
408
+ const state = getContext(contextId, projectRoot);
409
+ if (!state) return null;
410
+
411
+ state.mode = mode;
412
+ state.last_active = nowIso();
413
+
414
+ if (opts) {
415
+ if (opts.plan_path !== undefined) state.plan_path = opts.plan_path;
416
+ if (opts.plan_hash !== undefined) state.plan_hash = opts.plan_hash;
417
+ if (opts.plan_signature !== undefined) state.plan_signature = opts.plan_signature;
418
+ if (opts.plan_id !== undefined) state.plan_id = opts.plan_id;
419
+ if (opts.plan_anchors !== undefined) state.plan_anchors = opts.plan_anchors;
420
+ if (opts.plan_consumed !== undefined) state.plan_consumed = opts.plan_consumed;
421
+ if (opts.handoff_consumed !== undefined) state.handoff_consumed = opts.handoff_consumed;
422
+ }
423
+
424
+ // Clear plan/handoff fields when returning to idle
425
+ if (mode === "idle") {
426
+ state.plan_path = null;
427
+ state.plan_hash = null;
428
+ state.plan_signature = null;
429
+ state.plan_id = null;
430
+ state.plan_anchors = [];
431
+ state.plan_consumed = false;
432
+ state.handoff_consumed = false;
433
+ }
434
+
435
+ saveState(contextId, state, projectRoot);
436
+ return state;
437
+ }
438
+
439
+ /**
440
+ * Transition idle/has_plan/has_handoff → active, unless in plan mode.
441
+ * See SPEC.md §7.11
442
+ */
443
+ export function maybeActivate(
444
+ contextId: string,
445
+ permissionMode: string,
446
+ projectRoot?: string,
447
+ caller = "",
448
+ ): boolean {
449
+ if (permissionMode === "plan") return false;
450
+
451
+ const state = getContext(contextId, projectRoot);
452
+ if (!state) return false;
453
+
454
+ if (state.mode === "idle" || state.mode === "has_plan" || state.mode === "has_handoff") {
455
+ const oldMode = state.mode;
456
+ const opts: Record<string, any> = {};
457
+ if (oldMode === "has_plan") opts.plan_consumed = true;
458
+ else if (oldMode === "has_handoff") opts.handoff_consumed = true;
459
+ updateMode(contextId, "active", projectRoot, opts);
460
+ logInfo("context_store", `maybe_activate (${caller}): ${contextId} ${oldMode} -> active`);
461
+ return true;
462
+ }
463
+
464
+ return false;
465
+ }
466
+
467
+ // ---------------------------------------------------------------------------
468
+ // Lifecycle
469
+ // ---------------------------------------------------------------------------
470
+
471
+ /**
472
+ * Mark context completed and archive it.
473
+ * See SPEC.md §7.12
474
+ */
475
+ export function completeContext(contextId: string, projectRoot?: string): ContextState | null {
476
+ const state = getContext(contextId, projectRoot);
477
+ if (!state) return null;
478
+
479
+ if (state.status === "completed") {
480
+ logInfo("context_store", `Context '${contextId}' already completed`);
481
+ return state;
482
+ }
483
+
484
+ state.status = "completed";
485
+ state.last_active = nowIso();
486
+ saveState(contextId, state, projectRoot);
487
+ logInfo("context_store", `Completed context: ${contextId}`);
488
+
489
+ const archived = archiveContext(contextId, projectRoot);
490
+ return archived ?? state;
491
+ }
492
+
493
+ /**
494
+ * Move completed context folder to _archive/, update indices.
495
+ * See SPEC.md §7.13
496
+ */
497
+ export function archiveContext(contextId: string, projectRoot?: string): ContextState | null {
498
+ const state = getContext(contextId, projectRoot);
499
+ if (!state) {
500
+ logWarn("context_store", `Cannot archive: context '${contextId}' not found`);
501
+ return null;
502
+ }
503
+
504
+ if (state.status !== "completed") {
505
+ logWarn("context_store", `Cannot archive: context '${contextId}' not completed`);
506
+ return null;
507
+ }
508
+
509
+ const sourceDir = getContextDir(contextId, projectRoot);
510
+ const archiveDest = getArchiveContextDir(contextId, projectRoot);
511
+
512
+ if (fs.existsSync(archiveDest)) {
513
+ logWarn("context_store", `Cannot archive: archive folder already exists for '${contextId}'`);
514
+ return null;
515
+ }
516
+
517
+ const archiveParent = path.dirname(archiveDest);
518
+ fs.mkdirSync(archiveParent, { recursive: true });
519
+
520
+ try {
521
+ fs.renameSync(sourceDir, archiveDest);
522
+ } catch (error: any) {
523
+ logError("context_store", `Failed to move context to archive: ${error}`);
524
+ return null;
525
+ }
526
+
527
+ // Remove from main index
528
+ const index = loadIndex(projectRoot);
529
+ delete index.contexts[contextId];
530
+ const sessions = index.sessions ?? {};
531
+ for (const [sid, cid] of Object.entries(sessions)) {
532
+ if (cid === contextId) delete sessions[sid];
533
+ }
534
+
535
+ saveIndex(index, projectRoot);
536
+
537
+ // Add to archive index
538
+ updateArchiveIndex(state, projectRoot);
539
+
540
+ logInfo("context_store", `Archived context: ${contextId}`);
541
+ return state;
542
+ }
543
+
544
+ /**
545
+ * Reopen a completed/archived context.
546
+ * See SPEC.md §7.14
547
+ */
548
+ export function reopenContext(contextId: string, projectRoot?: string): ContextState | null {
549
+ let state = getContext(contextId, projectRoot);
550
+
551
+ if (!state) {
552
+ state = restoreFromArchive(contextId, projectRoot);
553
+ }
554
+
555
+ if (!state) return null;
556
+
557
+ if (state.status === "active") {
558
+ logInfo("context_store", `Context '${contextId}' already active`);
559
+ return state;
560
+ }
561
+
562
+ state.status = "active";
563
+ state.last_active = nowIso();
564
+ saveState(contextId, state, projectRoot);
565
+ logInfo("context_store", `Reopened context: ${contextId}`);
566
+ return state;
567
+ }
568
+
569
+ // ---------------------------------------------------------------------------
570
+ // Auto-creation from prompt
571
+ // ---------------------------------------------------------------------------
572
+
573
+ /**
574
+ * Auto-create a context from the user's prompt.
575
+ * See SPEC.md §7.15
576
+ */
577
+ export function createContextFromPrompt(
578
+ userPrompt: string,
579
+ projectRoot?: string,
580
+ ): ContextState {
581
+ let summary = userPrompt.trim().slice(0, 2000);
582
+ if (userPrompt.trim().length > 2000) {
583
+ summary += "...";
584
+ }
585
+
586
+ return createContext(
587
+ null,
588
+ summary,
589
+ "auto-created",
590
+ projectRoot,
591
+ ["auto-created"],
592
+ );
593
+ }
594
+
595
+ /**
596
+ * Find the active context ID programmatically.
597
+ * Checks CONTEXT_ID env var first, then searches for the single active context.
598
+ * Returns null if no active context or multiple active contexts found.
599
+ */
600
+ export function findActiveContextId(projectRoot?: string): null | string {
601
+ // Env var takes priority
602
+ const envId = process.env.CONTEXT_ID;
603
+ if (envId) {
604
+ const ctx = getContext(envId, projectRoot);
605
+ if (ctx) return ctx.id;
606
+ }
607
+
608
+ // Search for active contexts
609
+ const active = getAllContexts("active", projectRoot)
610
+ .filter(c => c.mode === "active" || c.mode === "has_plan" || c.mode === "has_handoff");
611
+
612
+ if (active.length === 1) return active[0]!.id;
613
+ if (active.length > 1) {
614
+ // Multiple active — try to find the most recently active
615
+ const sorted = active.sort((a, b) =>
616
+ (b.last_active ?? "").localeCompare(a.last_active ?? ""),
617
+ );
618
+ return sorted[0]!.id;
619
+ }
620
+
621
+ return null;
622
+ }
623
+
624
+ // ---------------------------------------------------------------------------
625
+ // Archive helpers
626
+ // ---------------------------------------------------------------------------
627
+
628
+ function updateArchiveIndex(state: ContextState, projectRoot?: string): boolean {
629
+ const archiveDir = getArchiveDir(projectRoot);
630
+ const archiveIndexPath = getArchiveIndexPath(projectRoot);
631
+ fs.mkdirSync(archiveDir, { recursive: true });
632
+
633
+ let archiveIndex: IndexFile = {
634
+ version: INDEX_VERSION,
635
+ updated_at: nowIso(),
636
+ sessions: {},
637
+ contexts: {},
638
+ };
639
+
640
+ if (fs.existsSync(archiveIndexPath)) {
641
+ try {
642
+ archiveIndex = JSON.parse(fs.readFileSync(archiveIndexPath, "utf8"));
643
+ } catch (error_: any) {
644
+ logWarn("context_store", `Failed to read archive index, recreating: ${error_}`);
645
+ }
646
+ }
647
+
648
+ archiveIndex.contexts[state.id] = toIndexEntry(state);
649
+ archiveIndex.updated_at = nowIso();
650
+
651
+ const content = JSON.stringify(archiveIndex, null, 2);
652
+ const [success, error] = atomicWrite(archiveIndexPath, content);
653
+ if (!success) {
654
+ logWarn("context_store", `Failed to write archive index: ${error}`);
655
+ }
656
+
657
+ return success;
658
+ }
659
+
660
+ function restoreFromArchive(contextId: string, projectRoot?: string): ContextState | null {
661
+ const archiveDir = getArchiveContextDir(contextId, projectRoot);
662
+ const activeDir = getContextDir(contextId, projectRoot);
663
+
664
+ if (!fs.existsSync(archiveDir)) return null;
665
+ if (fs.existsSync(activeDir)) {
666
+ logWarn("context_store", `Cannot restore: active folder already exists for '${contextId}'`);
667
+ return null;
668
+ }
669
+
670
+ try {
671
+ fs.renameSync(archiveDir, activeDir);
672
+ } catch (error: any) {
673
+ logError("context_store", `Failed to restore context from archive: ${error}`);
674
+ return null;
675
+ }
676
+
677
+ // Remove from archive index
678
+ removeFromArchiveIndex(contextId, projectRoot);
679
+
680
+ const state = loadState(contextId, projectRoot);
681
+ logInfo("context_store", `Restored context from archive: ${contextId}`);
682
+ return state;
683
+ }
684
+
685
+ function removeFromArchiveIndex(contextId: string, projectRoot?: string): boolean {
686
+ const archiveIndexPath = getArchiveIndexPath(projectRoot);
687
+ if (!fs.existsSync(archiveIndexPath)) return true;
688
+
689
+ try {
690
+ const archiveIndex = JSON.parse(fs.readFileSync(archiveIndexPath, "utf8")) as IndexFile;
691
+ if (archiveIndex.contexts[contextId]) {
692
+ delete archiveIndex.contexts[contextId];
693
+ archiveIndex.updated_at = nowIso();
694
+ const content = JSON.stringify(archiveIndex, null, 2);
695
+ const [success, error] = atomicWrite(archiveIndexPath, content);
696
+ if (!success) {
697
+ logWarn("context_store", `Failed to write archive index: ${error}`);
698
+ return false;
699
+ }
700
+ }
701
+
702
+ return true;
703
+ } catch (error: any) {
704
+ logWarn("context_store", `Failed to read archive index: ${error}`);
705
+ return false;
706
+ }
707
+ }