@howlil/ez-agents 3.4.2 → 4.0.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 (365) hide show
  1. package/README.md +735 -462
  2. package/agents/ez-architect-agent.md +267 -0
  3. package/agents/ez-backend-agent.md +303 -0
  4. package/agents/ez-chief-strategist.md +271 -0
  5. package/agents/ez-codebase-mapper.md +770 -770
  6. package/agents/ez-context-manager.md +319 -0
  7. package/agents/ez-debugger.md +1255 -1255
  8. package/agents/ez-design-expert.md +347 -0
  9. package/agents/ez-devops-agent.md +331 -0
  10. package/agents/ez-executor.md +487 -487
  11. package/agents/ez-frontend-agent.md +322 -0
  12. package/agents/ez-phase-researcher.md +553 -553
  13. package/agents/ez-planner.md +1307 -1307
  14. package/agents/ez-product-engineer.md +435 -0
  15. package/agents/ez-project-researcher.md +629 -629
  16. package/agents/ez-qa-agent.md +320 -0
  17. package/agents/ez-release-agent.md +333 -0
  18. package/agents/ez-requirements-agent.md +377 -0
  19. package/agents/ez-roadmapper.md +650 -650
  20. package/agents/ez-technical-writer.md +551 -0
  21. package/agents/ez-ux-expert.md +393 -0
  22. package/agents/ez-verifier.md +579 -579
  23. package/bin/guards/autonomy-guard.cjs +346 -0
  24. package/bin/guards/context-budget-guard.cjs +278 -0
  25. package/bin/guards/hallucination-guard.cjs +380 -0
  26. package/bin/guards/hidden-state-guard.cjs +182 -0
  27. package/bin/guards/team-overhead-guard.cjs +266 -0
  28. package/bin/guards/tool-sprawl-guard.cjs +271 -0
  29. package/bin/install.js +3221 -3272
  30. package/bin/lib/analytics/analytics-collector.cjs +86 -0
  31. package/bin/lib/analytics/analytics-reporter.cjs +130 -0
  32. package/bin/lib/analytics/cohort-analyzer.cjs +138 -0
  33. package/bin/lib/analytics/funnel-analyzer.cjs +147 -0
  34. package/bin/lib/analytics/nps-tracker.cjs +147 -0
  35. package/bin/lib/archetype-detector.cjs +289 -0
  36. package/bin/lib/assistant-adapter.cjs +361 -0
  37. package/bin/lib/audit-exec.cjs +175 -0
  38. package/bin/lib/auth.cjs +176 -0
  39. package/bin/lib/backup-service.cjs +422 -0
  40. package/bin/lib/bdd-validator.cjs +622 -0
  41. package/bin/lib/business-flow-mapper.cjs +429 -0
  42. package/bin/lib/circuit-breaker.cjs +276 -0
  43. package/bin/lib/code-complexity-analyzer.cjs +360 -0
  44. package/bin/lib/codebase-analyzer.cjs +241 -0
  45. package/bin/lib/commands.cjs +691 -0
  46. package/bin/lib/config.cjs +236 -0
  47. package/bin/lib/constraint-extractor.cjs +526 -0
  48. package/bin/lib/content-scanner.cjs +238 -0
  49. package/bin/lib/context-cache.cjs +154 -0
  50. package/bin/lib/context-compressor.cjs +102 -0
  51. package/bin/lib/context-deduplicator.cjs +105 -0
  52. package/bin/lib/context-errors.cjs +78 -0
  53. package/bin/lib/context-manager.cjs +338 -0
  54. package/bin/lib/context-metadata-tracker.cjs +140 -0
  55. package/bin/lib/context-relevance-scorer.cjs +99 -0
  56. package/bin/lib/core.cjs +507 -0
  57. package/bin/lib/cost-alerts.cjs +174 -0
  58. package/bin/lib/cost-tracker.cjs +275 -0
  59. package/bin/lib/crash-recovery.cjs +220 -0
  60. package/bin/lib/dependency-graph.cjs +319 -0
  61. package/bin/lib/deploy/deploy-audit-log.cjs +76 -0
  62. package/bin/lib/deploy/deploy-detector.cjs +69 -0
  63. package/bin/lib/deploy/deploy-env-manager.cjs +109 -0
  64. package/bin/lib/deploy/deploy-health-check.cjs +88 -0
  65. package/bin/lib/deploy/deploy-pre-flight.cjs +57 -0
  66. package/bin/lib/deploy/deploy-rollback.cjs +72 -0
  67. package/bin/lib/deploy/deploy-runner.cjs +97 -0
  68. package/bin/lib/deploy/deploy-status.cjs +74 -0
  69. package/bin/lib/discussion-synthesizer.cjs +439 -0
  70. package/bin/lib/error-cache.cjs +114 -0
  71. package/bin/lib/error-registry.cjs +177 -0
  72. package/bin/lib/file-access.cjs +207 -0
  73. package/bin/lib/file-lock.cjs +236 -0
  74. package/bin/lib/finops/budget-enforcer.cjs +126 -0
  75. package/bin/lib/finops/cost-reporter.cjs +132 -0
  76. package/bin/lib/finops/finops-analyzer.cjs +112 -0
  77. package/bin/lib/finops/spot-manager.cjs +118 -0
  78. package/bin/lib/framework-detector.cjs +396 -0
  79. package/bin/lib/frontmatter.cjs +313 -0
  80. package/bin/lib/fs-utils.cjs +153 -0
  81. package/bin/lib/gate-executor.cjs +272 -0
  82. package/bin/lib/gates/README.md +374 -0
  83. package/bin/lib/gates/gate-01-requirement.cjs +303 -0
  84. package/bin/lib/gates/gate-02-architecture.cjs +555 -0
  85. package/bin/lib/gates/gate-03-code.cjs +635 -0
  86. package/bin/lib/gates/gate-04-security.cjs +829 -0
  87. package/bin/lib/git-errors.cjs +83 -0
  88. package/bin/lib/git-utils.cjs +321 -0
  89. package/bin/lib/git-workflow-engine.cjs +1157 -0
  90. package/bin/lib/health-check.cjs +227 -0
  91. package/bin/lib/index.cjs +279 -0
  92. package/bin/lib/init.cjs +725 -0
  93. package/bin/lib/lock-logger.cjs +194 -0
  94. package/bin/lib/lock-state.cjs +263 -0
  95. package/bin/lib/lockfile-validator.cjs +227 -0
  96. package/bin/lib/log-rotation.cjs +71 -0
  97. package/bin/lib/logger.cjs +125 -0
  98. package/bin/lib/memory-compression.cjs +256 -0
  99. package/bin/lib/milestone.cjs +247 -0
  100. package/bin/lib/model-provider.cjs +241 -0
  101. package/bin/lib/package-manager-detector.cjs +203 -0
  102. package/bin/lib/package-manager-executor.cjs +385 -0
  103. package/bin/lib/package-manager-service.cjs +216 -0
  104. package/bin/lib/perf/api-monitor.cjs +88 -0
  105. package/bin/lib/perf/db-optimizer.cjs +78 -0
  106. package/bin/lib/perf/frontend-performance.cjs +56 -0
  107. package/bin/lib/perf/perf-analyzer.cjs +77 -0
  108. package/bin/lib/perf/perf-baseline.cjs +102 -0
  109. package/bin/lib/perf/perf-reporter.cjs +117 -0
  110. package/bin/lib/perf/regression-detector.cjs +92 -0
  111. package/bin/lib/phase.cjs +963 -0
  112. package/bin/lib/planning-write.cjs +123 -0
  113. package/bin/lib/project-reporter.cjs +565 -0
  114. package/bin/lib/quality-gate.cjs +332 -0
  115. package/bin/lib/quality-metrics.cjs +324 -0
  116. package/bin/lib/recovery-manager.cjs +98 -0
  117. package/bin/lib/release-validator.cjs +617 -0
  118. package/bin/lib/retry.cjs +119 -0
  119. package/bin/lib/roadmap.cjs +309 -0
  120. package/bin/lib/safe-exec.cjs +173 -0
  121. package/bin/lib/safe-path.cjs +130 -0
  122. package/bin/lib/security-errors.cjs +62 -0
  123. package/bin/lib/session-chain.cjs +304 -0
  124. package/bin/lib/session-errors.cjs +81 -0
  125. package/bin/lib/session-export.cjs +251 -0
  126. package/bin/lib/session-import.cjs +262 -0
  127. package/bin/lib/session-manager.cjs +280 -0
  128. package/bin/lib/skill-context.cjs +148 -0
  129. package/bin/lib/skill-matcher.cjs +236 -0
  130. package/bin/lib/skill-registry.cjs +360 -0
  131. package/bin/lib/skill-resolver.cjs +449 -0
  132. package/bin/lib/skill-triggers.cjs +90 -0
  133. package/bin/lib/skill-validator.cjs +270 -0
  134. package/bin/lib/skill-versioning.cjs +355 -0
  135. package/bin/lib/stack-detector.cjs +399 -0
  136. package/bin/lib/state.cjs +736 -0
  137. package/bin/lib/tech-debt-analyzer.cjs +309 -0
  138. package/bin/lib/temp-file.cjs +239 -0
  139. package/bin/lib/template.cjs +223 -0
  140. package/bin/lib/test-file-lock.cjs +112 -0
  141. package/bin/lib/test-graceful.cjs +93 -0
  142. package/bin/lib/test-logger.cjs +60 -0
  143. package/bin/lib/test-safe-exec.cjs +38 -0
  144. package/bin/lib/test-safe-path.cjs +33 -0
  145. package/bin/lib/test-temp-file.cjs +125 -0
  146. package/bin/lib/tier-manager.cjs +428 -0
  147. package/bin/lib/timeout-exec.cjs +63 -0
  148. package/bin/lib/tradeoff-analyzer.cjs +284 -0
  149. package/bin/lib/url-fetch.cjs +170 -0
  150. package/bin/lib/verify.cjs +863 -0
  151. package/bin/update.js +217 -214
  152. package/commands/deploy.cjs +53 -0
  153. package/commands/ez/add-tests.md +41 -41
  154. package/commands/ez/audit-milestone.md +36 -36
  155. package/commands/ez/complete-milestone.md +136 -136
  156. package/commands/ez/discuss-phase.md +90 -90
  157. package/commands/ez/execute-phase.md +52 -41
  158. package/commands/ez/help.md +22 -22
  159. package/commands/ez/map-codebase.md +71 -71
  160. package/commands/ez/new-milestone.md +44 -44
  161. package/commands/ez/new-project.md +51 -42
  162. package/commands/ez/plan-phase.md +53 -45
  163. package/commands/ez/progress.md +36 -24
  164. package/commands/ez/quick.md +45 -45
  165. package/commands/ez/resume-work.md +40 -40
  166. package/commands/ez/run-phase.md +580 -0
  167. package/commands/ez/settings.md +36 -36
  168. package/commands/ez/update.md +37 -37
  169. package/commands/ez/verify-work.md +402 -38
  170. package/commands/health-check.cjs +44 -0
  171. package/commands/rollback.cjs +47 -0
  172. package/ez-agents/bin/ez-tools.cjs +1692 -716
  173. package/ez-agents/bin/guards/autonomy-guard.cjs +346 -0
  174. package/ez-agents/bin/guards/context-budget-guard.cjs +247 -0
  175. package/ez-agents/bin/guards/hallucination-guard.cjs +271 -0
  176. package/ez-agents/bin/guards/hidden-state-guard.cjs +182 -0
  177. package/ez-agents/bin/guards/team-overhead-guard.cjs +266 -0
  178. package/ez-agents/bin/guards/tool-sprawl-guard.cjs +271 -0
  179. package/ez-agents/bin/lib/analytics/analytics-collector.cjs +86 -0
  180. package/ez-agents/bin/lib/analytics/analytics-reporter.cjs +130 -0
  181. package/ez-agents/bin/lib/analytics/cohort-analyzer.cjs +138 -0
  182. package/ez-agents/bin/lib/analytics/funnel-analyzer.cjs +147 -0
  183. package/ez-agents/bin/lib/analytics/nps-tracker.cjs +147 -0
  184. package/ez-agents/bin/lib/archetype-detector.cjs +289 -0
  185. package/ez-agents/bin/lib/audit-exec.cjs +166 -167
  186. package/ez-agents/bin/lib/auth.cjs +176 -176
  187. package/ez-agents/bin/lib/backup-service.cjs +422 -0
  188. package/ez-agents/bin/lib/bdd-validator.cjs +622 -0
  189. package/ez-agents/bin/lib/business-flow-mapper.cjs +429 -0
  190. package/ez-agents/bin/lib/code-complexity-analyzer.cjs +360 -0
  191. package/ez-agents/bin/lib/codebase-analyzer.cjs +241 -0
  192. package/ez-agents/bin/lib/commands.cjs +685 -685
  193. package/ez-agents/bin/lib/config.cjs +41 -1
  194. package/ez-agents/bin/lib/constraint-extractor.cjs +526 -0
  195. package/ez-agents/bin/lib/content-scanner.cjs +238 -0
  196. package/ez-agents/bin/lib/context-cache.cjs +154 -0
  197. package/ez-agents/bin/lib/context-errors.cjs +71 -0
  198. package/ez-agents/bin/lib/context-manager.cjs +220 -0
  199. package/ez-agents/bin/lib/core.cjs +507 -512
  200. package/ez-agents/bin/lib/cost-tracker.cjs +243 -0
  201. package/ez-agents/bin/lib/crash-recovery.cjs +172 -0
  202. package/ez-agents/bin/lib/dependency-graph.cjs +319 -0
  203. package/ez-agents/bin/lib/deploy/deploy-audit-log.cjs +76 -0
  204. package/ez-agents/bin/lib/deploy/deploy-detector.cjs +69 -0
  205. package/ez-agents/bin/lib/deploy/deploy-env-manager.cjs +109 -0
  206. package/ez-agents/bin/lib/deploy/deploy-health-check.cjs +88 -0
  207. package/ez-agents/bin/lib/deploy/deploy-pre-flight.cjs +57 -0
  208. package/ez-agents/bin/lib/deploy/deploy-rollback.cjs +72 -0
  209. package/ez-agents/bin/lib/deploy/deploy-runner.cjs +97 -0
  210. package/ez-agents/bin/lib/deploy/deploy-status.cjs +74 -0
  211. package/ez-agents/bin/lib/discussion-synthesizer.cjs +458 -0
  212. package/ez-agents/bin/lib/file-access.cjs +207 -0
  213. package/ez-agents/bin/lib/finops/budget-enforcer.cjs +126 -0
  214. package/ez-agents/bin/lib/finops/cost-reporter.cjs +132 -0
  215. package/ez-agents/bin/lib/finops/finops-analyzer.cjs +112 -0
  216. package/ez-agents/bin/lib/finops/spot-manager.cjs +118 -0
  217. package/ez-agents/bin/lib/framework-detector.cjs +396 -0
  218. package/ez-agents/bin/lib/frontmatter.cjs +3 -1
  219. package/ez-agents/bin/lib/gates/README.md +374 -0
  220. package/ez-agents/bin/lib/gates/gate-01-requirement.cjs +303 -0
  221. package/ez-agents/bin/lib/gates/gate-02-architecture.cjs +555 -0
  222. package/ez-agents/bin/lib/gates/gate-03-code.cjs +635 -0
  223. package/ez-agents/bin/lib/gates/gate-04-security.cjs +829 -0
  224. package/ez-agents/bin/lib/git-errors.cjs +83 -0
  225. package/ez-agents/bin/lib/git-utils.cjs +118 -0
  226. package/ez-agents/bin/lib/git-workflow-engine.cjs +1157 -0
  227. package/ez-agents/bin/lib/health-check.cjs +162 -162
  228. package/ez-agents/bin/lib/index.cjs +40 -2
  229. package/ez-agents/bin/lib/init.cjs +0 -2
  230. package/ez-agents/bin/lib/lockfile-validator.cjs +227 -0
  231. package/ez-agents/bin/lib/log-rotation.cjs +71 -0
  232. package/ez-agents/bin/lib/logger.cjs +99 -154
  233. package/ez-agents/bin/lib/memory-compression.cjs +256 -0
  234. package/ez-agents/bin/lib/package-manager-detector.cjs +203 -0
  235. package/ez-agents/bin/lib/package-manager-executor.cjs +385 -0
  236. package/ez-agents/bin/lib/package-manager-service.cjs +216 -0
  237. package/ez-agents/bin/lib/perf/api-monitor.cjs +88 -0
  238. package/ez-agents/bin/lib/perf/db-optimizer.cjs +78 -0
  239. package/ez-agents/bin/lib/perf/frontend-performance.cjs +56 -0
  240. package/ez-agents/bin/lib/perf/perf-analyzer.cjs +77 -0
  241. package/ez-agents/bin/lib/perf/perf-baseline.cjs +102 -0
  242. package/ez-agents/bin/lib/perf/perf-reporter.cjs +117 -0
  243. package/ez-agents/bin/lib/perf/regression-detector.cjs +92 -0
  244. package/ez-agents/bin/lib/project-reporter.cjs +502 -0
  245. package/ez-agents/bin/lib/quality-gate.cjs +332 -0
  246. package/ez-agents/bin/lib/recovery-manager.cjs +98 -0
  247. package/ez-agents/bin/lib/release-validator.cjs +617 -0
  248. package/ez-agents/bin/lib/safe-exec.cjs +128 -214
  249. package/ez-agents/bin/lib/security-errors.cjs +62 -0
  250. package/ez-agents/bin/lib/session-chain.cjs +304 -0
  251. package/ez-agents/bin/lib/session-errors.cjs +81 -0
  252. package/ez-agents/bin/lib/session-export.cjs +251 -0
  253. package/ez-agents/bin/lib/session-import.cjs +262 -0
  254. package/ez-agents/bin/lib/session-manager.cjs +280 -0
  255. package/ez-agents/bin/lib/skill-context.cjs +148 -0
  256. package/ez-agents/bin/lib/skill-matcher.cjs +236 -0
  257. package/ez-agents/bin/lib/skill-registry.cjs +341 -0
  258. package/ez-agents/bin/lib/skill-resolver.cjs +449 -0
  259. package/ez-agents/bin/lib/skill-triggers.cjs +90 -0
  260. package/ez-agents/bin/lib/skill-validator.cjs +270 -0
  261. package/ez-agents/bin/lib/skill-versioning.cjs +355 -0
  262. package/ez-agents/bin/lib/stack-detector.cjs +399 -0
  263. package/ez-agents/bin/lib/tech-debt-analyzer.cjs +309 -0
  264. package/ez-agents/bin/lib/tier-manager.cjs +428 -0
  265. package/ez-agents/bin/lib/tradeoff-analyzer.cjs +284 -0
  266. package/ez-agents/bin/lib/url-fetch.cjs +170 -0
  267. package/ez-agents/bin/lib/verify.cjs +863 -863
  268. package/ez-agents/references/decimal-phase-calculation.md +65 -65
  269. package/ez-agents/references/git-integration.md +248 -248
  270. package/ez-agents/references/git-planning-commit.md +38 -38
  271. package/ez-agents/references/metrics-schema.md +118 -0
  272. package/ez-agents/references/model-profile-resolution.md +34 -34
  273. package/ez-agents/references/model-profiles.md +93 -93
  274. package/ez-agents/references/phase-argument-parsing.md +61 -61
  275. package/ez-agents/references/planning-config.md +340 -200
  276. package/ez-agents/references/tier-strategy.md +103 -0
  277. package/ez-agents/references/ui-brand.md +160 -160
  278. package/ez-agents/references/verification-patterns.md +612 -612
  279. package/ez-agents/templates/DEBUG.md +164 -164
  280. package/ez-agents/templates/UAT.md +247 -247
  281. package/ez-agents/templates/agent-output-format.md +404 -0
  282. package/ez-agents/templates/bdd-feature.md +173 -0
  283. package/ez-agents/templates/codebase/architecture.md +255 -255
  284. package/ez-agents/templates/codebase/structure.md +285 -285
  285. package/ez-agents/templates/copilot-instructions.md +7 -7
  286. package/ez-agents/templates/debug-subagent-prompt.md +91 -91
  287. package/ez-agents/templates/discovery.md +146 -146
  288. package/ez-agents/templates/discussion.md +68 -0
  289. package/ez-agents/templates/handoff-protocol.md +294 -0
  290. package/ez-agents/templates/incident-runbook.md +205 -0
  291. package/ez-agents/templates/mode-workflow-templates.md +301 -0
  292. package/ez-agents/templates/phase-prompt.md +610 -610
  293. package/ez-agents/templates/planner-subagent-prompt.md +117 -117
  294. package/ez-agents/templates/project.md +184 -184
  295. package/ez-agents/templates/release-checklist.md +136 -0
  296. package/ez-agents/templates/research.md +552 -552
  297. package/ez-agents/templates/rollback-plan.md +201 -0
  298. package/ez-agents/templates/security-user-setup.md +244 -0
  299. package/ez-agents/templates/skill-validation-rules.md +476 -0
  300. package/ez-agents/templates/state.md +180 -176
  301. package/ez-agents/templates/summary-complex.md +59 -59
  302. package/ez-agents/tests/gates/gate-01-02.test.cjs +812 -0
  303. package/ez-agents/tests/gates/gate-03-04.test.cjs +762 -0
  304. package/ez-agents/tests/gates/gate-05-validator.test.cjs +145 -0
  305. package/ez-agents/tests/gates/gate-06-docs-validator.test.cjs +244 -0
  306. package/ez-agents/tests/gates/gate-07-release-validator.test.cjs +219 -0
  307. package/ez-agents/tests/guards/context-budget-guard.test.cjs +145 -0
  308. package/ez-agents/tests/guards/edge-case-guards.test.cjs +238 -0
  309. package/ez-agents/tests/guards/hallucination-guard.test.cjs +124 -0
  310. package/ez-agents/workflows/audit-milestone.md +1 -1
  311. package/ez-agents/workflows/autonomous.md +131 -30
  312. package/ez-agents/workflows/complete-milestone.md +1 -1
  313. package/ez-agents/workflows/discuss-phase.md +1 -1
  314. package/ez-agents/workflows/execute-phase.md +169 -3
  315. package/ez-agents/workflows/help.md +86 -133
  316. package/ez-agents/workflows/hotfix.md +291 -0
  317. package/ez-agents/workflows/new-milestone.md +340 -11
  318. package/ez-agents/workflows/new-project.md +294 -318
  319. package/ez-agents/workflows/plan-phase.md +22 -40
  320. package/ez-agents/workflows/progress.md +15 -25
  321. package/ez-agents/workflows/release.md +253 -0
  322. package/ez-agents/workflows/resume-session.md +215 -0
  323. package/ez-agents/workflows/run-phase.md +531 -0
  324. package/ez-agents/workflows/settings.md +2 -35
  325. package/hooks/dist/ez-check-update.js +81 -81
  326. package/hooks/dist/ez-context-monitor.js +148 -141
  327. package/hooks/dist/ez-statusline.js +115 -115
  328. package/package.json +78 -64
  329. package/scripts/fix-qwen-installation.js +144 -144
  330. package/agents/ez-integration-checker.md +0 -443
  331. package/agents/ez-nyquist-auditor.md +0 -176
  332. package/agents/ez-plan-checker.md +0 -706
  333. package/agents/ez-research-synthesizer.md +0 -247
  334. package/agents/ez-ui-auditor.md +0 -439
  335. package/agents/ez-ui-checker.md +0 -300
  336. package/agents/ez-ui-researcher.md +0 -353
  337. package/commands/ez/add-phase.md +0 -43
  338. package/commands/ez/add-todo.md +0 -47
  339. package/commands/ez/auth.md +0 -87
  340. package/commands/ez/autonomous.md +0 -41
  341. package/commands/ez/check-todos.md +0 -45
  342. package/commands/ez/cleanup.md +0 -18
  343. package/commands/ez/debug.md +0 -168
  344. package/commands/ez/health.md +0 -22
  345. package/commands/ez/insert-phase.md +0 -32
  346. package/commands/ez/join-discord.md +0 -18
  347. package/commands/ez/list-phase-assumptions.md +0 -46
  348. package/commands/ez/pause-work.md +0 -38
  349. package/commands/ez/plan-milestone-gaps.md +0 -34
  350. package/commands/ez/reapply-patches.md +0 -124
  351. package/commands/ez/remove-phase.md +0 -31
  352. package/commands/ez/research-phase.md +0 -190
  353. package/commands/ez/set-profile.md +0 -34
  354. package/commands/ez/stats.md +0 -18
  355. package/commands/ez/ui-phase.md +0 -34
  356. package/commands/ez/ui-review.md +0 -32
  357. package/commands/ez/validate-phase.md +0 -35
  358. package/ez-agents/templates/UI-SPEC.md +0 -100
  359. package/ez-agents/templates/VALIDATION.md +0 -76
  360. package/ez-agents/templates/context.md +0 -352
  361. package/ez-agents/templates/verification-report.md +0 -322
  362. package/ez-agents/workflows/research-phase.md +0 -74
  363. package/ez-agents/workflows/ui-phase.md +0 -290
  364. package/ez-agents/workflows/ui-review.md +0 -157
  365. package/ez-agents/workflows/validate-phase.md +0 -167
@@ -0,0 +1,71 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Log Rotation Utility
5
+ *
6
+ * Automatically deletes EZ Agents logs older than 7 days to prevent git spam.
7
+ * Run this weekly or add to CI/CD cleanup job.
8
+ *
9
+ * Usage: node ez-agents/bin/lib/log-rotation.cjs [--dry-run]
10
+ */
11
+
12
+ const fs = require('fs');
13
+ const path = require('path');
14
+
15
+ const LOGS_DIR = path.join(process.cwd(), '.planning', 'logs');
16
+ const MAX_AGE_DAYS = 7;
17
+ const DRY_RUN = process.argv.includes('--dry-run');
18
+
19
+ function rotateLogs() {
20
+ if (!fs.existsSync(LOGS_DIR)) {
21
+ console.log('Logs directory not found:', LOGS_DIR);
22
+ return;
23
+ }
24
+
25
+ const cutoff = new Date();
26
+ cutoff.setDate(cutoff.getDate() - MAX_AGE_DAYS);
27
+
28
+ const files = fs.readdirSync(LOGS_DIR);
29
+ const logFiles = files.filter(f => f.endsWith('.log'));
30
+
31
+ let deleted = 0;
32
+ let kept = 0;
33
+ let totalSize = 0;
34
+
35
+ logFiles.forEach(file => {
36
+ const filePath = path.join(LOGS_DIR, file);
37
+ const stats = fs.statSync(filePath);
38
+
39
+ if (stats.mtime < cutoff) {
40
+ if (DRY_RUN) {
41
+ console.log(`[DRY-RUN] Would delete: ${file} (${formatBytes(stats.size)})`);
42
+ } else {
43
+ fs.unlinkSync(filePath);
44
+ console.log(`Deleted: ${file}`);
45
+ }
46
+ deleted++;
47
+ totalSize += stats.size;
48
+ } else {
49
+ kept++;
50
+ }
51
+ });
52
+
53
+ console.log(`\n${DRY_RUN ? '[DRY-RUN] ' : ''}Log Rotation Complete`);
54
+ console.log(` Deleted: ${deleted} files (${formatBytes(totalSize)})`);
55
+ console.log(` Kept: ${kept} files (last ${MAX_AGE_DAYS} days)`);
56
+ }
57
+
58
+ function formatBytes(bytes) {
59
+ if (bytes === 0) return '0 Bytes';
60
+ const k = 1024;
61
+ const sizes = ['Bytes', 'KB', 'MB', 'GB'];
62
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
63
+ return Math.round(bytes / Math.pow(k, i) * 100) / 100 + ' ' + sizes[i];
64
+ }
65
+
66
+ // Run if called directly
67
+ if (require.main === module) {
68
+ rotateLogs();
69
+ }
70
+
71
+ module.exports = { rotateLogs };
@@ -0,0 +1,125 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * EZ Logger — Centralized logging module for EZ workflow
5
+ *
6
+ * Provides structured logging with levels (ERROR, WARN, INFO, DEBUG)
7
+ * Logs to console only (no file logging)
8
+ * Replaces silent catch {} blocks with proper error logging
9
+ * Integrated with ErrorCache for recurring error detection
10
+ *
11
+ * Usage:
12
+ * const Logger = require('./logger.cjs');
13
+ * const logger = new Logger();
14
+ * logger.error('Something failed', { context: 'details' });
15
+ */
16
+
17
+ // Lazy-load ErrorCache to avoid circular dependencies
18
+ let ErrorCacheInstance = null;
19
+
20
+ /**
21
+ * Get or create ErrorCache singleton
22
+ * @returns {Object} ErrorCache instance
23
+ */
24
+ function getErrorCache() {
25
+ if (!ErrorCacheInstance) {
26
+ try {
27
+ const { ErrorCache } = require('./error-cache.cjs');
28
+ ErrorCacheInstance = new ErrorCache();
29
+ } catch (err) {
30
+ // ErrorCache not available - continue without it
31
+ ErrorCacheInstance = null;
32
+ }
33
+ }
34
+ return ErrorCacheInstance;
35
+ }
36
+
37
+ class Logger {
38
+ /**
39
+ * Create a Logger instance
40
+ */
41
+ constructor() {
42
+ // No file logging - console only
43
+ }
44
+
45
+ /**
46
+ * Write a log entry to console
47
+ * @param {string} level - Log level (ERROR, WARN, INFO, DEBUG)
48
+ * @param {string} message - Log message
49
+ * @param {Object} context - Additional context data
50
+ */
51
+ log(level, message, context = {}) {
52
+ const entry = {
53
+ timestamp: new Date().toISOString(),
54
+ level,
55
+ message,
56
+ context,
57
+ pid: process.pid
58
+ };
59
+
60
+ // Always output to console
61
+ if (level === 'ERROR') {
62
+ console.error(`[EZ ${level}] ${message}`);
63
+ } else if (level === 'WARN') {
64
+ console.warn(`[EZ ${level}] ${message}`);
65
+ } else if (process.env.DEBUG === 'ez-agents') {
66
+ // Only output INFO/DEBUG in debug mode
67
+ console.log(`[EZ ${level}] ${message}`);
68
+ }
69
+ }
70
+
71
+ /**
72
+ * Log an ERROR level message
73
+ * Records error to cache for recurring detection
74
+ * @param {string} msg - Error message
75
+ * @param {Object} ctx - Additional context (can include error object)
76
+ */
77
+ error(msg, ctx) {
78
+ // Record to error cache if error object provided
79
+ if (ctx && ctx.error instanceof Error) {
80
+ const cache = getErrorCache();
81
+ if (cache) {
82
+ const fingerprint = cache.record(ctx.error, ctx);
83
+ if (cache.isRecurring(fingerprint)) {
84
+ const entry = cache.get(fingerprint);
85
+ console.warn(`[EZ RECURRING] (${entry.count}x): ${msg}`);
86
+ }
87
+ }
88
+ }
89
+
90
+ this.log('ERROR', msg, ctx);
91
+ }
92
+
93
+ /**
94
+ * Log a WARN level message
95
+ * @param {string} msg - Warning message
96
+ * @param {Object} ctx - Additional context
97
+ */
98
+ warn(msg, ctx) {
99
+ this.log('WARN', msg, ctx);
100
+ }
101
+
102
+ /**
103
+ * Log an INFO level message
104
+ * @param {string} msg - Info message
105
+ * @param {Object} ctx - Additional context
106
+ */
107
+ info(msg, ctx) {
108
+ this.log('INFO', msg, ctx);
109
+ }
110
+
111
+ /**
112
+ * Log a DEBUG level message
113
+ * @param {string} msg - Debug message
114
+ * @param {Object} ctx - Additional context
115
+ */
116
+ debug(msg, ctx) {
117
+ this.log('DEBUG', msg, ctx);
118
+ }
119
+ }
120
+
121
+ // Singleton instance for default usage
122
+ const defaultLogger = new Logger();
123
+
124
+ module.exports = Logger;
125
+ module.exports.defaultLogger = defaultLogger;
@@ -0,0 +1,256 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Memory Compression — Compress long session transcripts
5
+ *
6
+ * Reduces session size by keeping first N and last M messages
7
+ */
8
+
9
+ const { defaultLogger: logger } = require('./logger.cjs');
10
+
11
+ class MemoryCompression {
12
+ /**
13
+ * Create a MemoryCompression instance
14
+ * @param {Object} sessionManager - SessionManager instance
15
+ */
16
+ constructor(sessionManager) {
17
+ this.sessionManager = sessionManager;
18
+ }
19
+
20
+ /**
21
+ * Compress a session transcript
22
+ * @param {string} sessionId - Session ID
23
+ * @param {Object} options - Compression options
24
+ * @param {number} [options.threshold=50] - Minimum messages before compression
25
+ * @param {number} [options.keepFirst=5] - Messages to keep at start
26
+ * @param {number} [options.keepLast=10] - Messages to keep at end
27
+ * @returns {Object} Compression result
28
+ */
29
+ compress(sessionId, options = {}) {
30
+ const session = this.sessionManager.loadSession(sessionId);
31
+ if (!session) {
32
+ return { compressed: false, reason: 'Session not found' };
33
+ }
34
+
35
+ const {
36
+ threshold = 50,
37
+ keepFirst = 5,
38
+ keepLast = 10
39
+ } = options;
40
+
41
+ const transcript = session.context?.transcript || '';
42
+
43
+ // Handle string transcript (split by newlines or messages)
44
+ let messages = [];
45
+ if (typeof transcript === 'string') {
46
+ // Try to parse as JSON array first
47
+ try {
48
+ messages = JSON.parse(transcript);
49
+ } catch {
50
+ // Split by newlines as fallback
51
+ messages = transcript.split('\n').filter(line => line.trim());
52
+ }
53
+ } else if (Array.isArray(transcript)) {
54
+ messages = transcript;
55
+ }
56
+
57
+ if (messages.length <= threshold) {
58
+ logger.info('Session below compression threshold', { sessionId, messageCount: messages.length });
59
+ return {
60
+ compressed: false,
61
+ reason: 'Below threshold',
62
+ messageCount: messages.length,
63
+ threshold
64
+ };
65
+ }
66
+
67
+ // Create compressed transcript
68
+ const firstMessages = messages.slice(0, keepFirst);
69
+ const lastMessages = messages.slice(-keepLast);
70
+ const compressedCount = messages.length - keepFirst - keepLast;
71
+
72
+ const placeholder = {
73
+ role: 'system',
74
+ content: `... ${compressedCount} messages compressed ...`,
75
+ timestamp: new Date().toISOString(),
76
+ compressed: true
77
+ };
78
+
79
+ const compressedMessages = [
80
+ ...firstMessages,
81
+ placeholder,
82
+ ...lastMessages
83
+ ];
84
+
85
+ // Update session
86
+ const updates = {
87
+ context: {
88
+ transcript: compressedMessages
89
+ },
90
+ metadata: {
91
+ compressed: true,
92
+ compressed_at: new Date().toISOString(),
93
+ compression_stats: {
94
+ original_count: messages.length,
95
+ compressed_count: compressedMessages.length,
96
+ removed_count: compressedCount
97
+ }
98
+ }
99
+ };
100
+
101
+ this.sessionManager.updateSession(sessionId, updates);
102
+
103
+ const reduction = Math.round((1 - compressedMessages.length / messages.length) * 100);
104
+
105
+ logger.info('Session compressed', {
106
+ sessionId,
107
+ originalLength: messages.length,
108
+ newLength: compressedMessages.length,
109
+ reduction
110
+ });
111
+
112
+ return {
113
+ compressed: true,
114
+ originalLength: messages.length,
115
+ newLength: compressedMessages.length,
116
+ reduction
117
+ };
118
+ }
119
+
120
+ /**
121
+ * Get compression stats for a session
122
+ * @param {string} sessionId - Session ID
123
+ * @returns {Object} Compression statistics
124
+ */
125
+ getCompressionStats(sessionId) {
126
+ const session = this.sessionManager.loadSession(sessionId);
127
+ if (!session) {
128
+ return { compressed: false, reason: 'Session not found' };
129
+ }
130
+
131
+ if (!session.metadata?.compressed) {
132
+ return { compressed: false };
133
+ }
134
+
135
+ const originalSize = session.metadata.compression_stats?.original_count || 0;
136
+ const compressedSize = session.metadata.compression_stats?.compressed_count || 0;
137
+ const reduction = session.metadata.compression_stats?.removed_count || 0;
138
+ const reductionPercent = originalSize > 0
139
+ ? Math.round((reduction / originalSize) * 100)
140
+ : 0;
141
+
142
+ return {
143
+ compressed: true,
144
+ original_size: originalSize,
145
+ compressed_size: compressedSize,
146
+ reduction_percent: reductionPercent,
147
+ compressed_at: session.metadata.compressed_at
148
+ };
149
+ }
150
+
151
+ /**
152
+ * Check if session should be compressed
153
+ * @param {string} sessionId - Session ID
154
+ * @param {number} threshold - Message threshold
155
+ * @returns {boolean} True if compression recommended
156
+ */
157
+ shouldCompress(sessionId, threshold = 50) {
158
+ const session = this.sessionManager.loadSession(sessionId);
159
+ if (!session) {
160
+ return false;
161
+ }
162
+
163
+ const transcript = session.context?.transcript || '';
164
+ let messageCount = 0;
165
+
166
+ if (typeof transcript === 'string') {
167
+ try {
168
+ const messages = JSON.parse(transcript);
169
+ messageCount = Array.isArray(messages) ? messages.length : 0;
170
+ } catch {
171
+ messageCount = transcript.split('\n').filter(line => line.trim()).length;
172
+ }
173
+ } else if (Array.isArray(transcript)) {
174
+ messageCount = transcript.length;
175
+ }
176
+
177
+ return messageCount > threshold;
178
+ }
179
+
180
+ /**
181
+ * Compress all sessions exceeding threshold
182
+ * @param {Object} options - Compression options
183
+ * @returns {Object} Batch compression result
184
+ */
185
+ compressAll(options = {}) {
186
+ const sessions = this.sessionManager.listSessions();
187
+ const results = {
188
+ total: sessions.length,
189
+ compressed: 0,
190
+ skipped: 0,
191
+ details: []
192
+ };
193
+
194
+ for (const sessionMeta of sessions) {
195
+ const shouldCompress = this.shouldCompress(sessionMeta.session_id, options.threshold);
196
+
197
+ if (shouldCompress) {
198
+ const result = this.compress(sessionMeta.session_id, options);
199
+ if (result.compressed) {
200
+ results.compressed++;
201
+ results.details.push({
202
+ sessionId: sessionMeta.session_id,
203
+ ...result
204
+ });
205
+ } else {
206
+ results.skipped++;
207
+ }
208
+ } else {
209
+ results.skipped++;
210
+ }
211
+ }
212
+
213
+ logger.info('Batch compression complete', {
214
+ total: results.total,
215
+ compressed: results.compressed,
216
+ skipped: results.skipped
217
+ });
218
+
219
+ return results;
220
+ }
221
+
222
+ /**
223
+ * Decompress a session (restore placeholder info)
224
+ * Note: Cannot restore original messages, only marks as decompressed
225
+ * @param {string} sessionId - Session ID
226
+ * @returns {Object} Decompression result
227
+ */
228
+ decompress(sessionId) {
229
+ const session = this.sessionManager.loadSession(sessionId);
230
+ if (!session) {
231
+ return { decompressed: false, reason: 'Session not found' };
232
+ }
233
+
234
+ if (!session.metadata?.compressed) {
235
+ return { decompressed: false, reason: 'Not compressed' };
236
+ }
237
+
238
+ // Remove compression metadata
239
+ this.sessionManager.updateSession(sessionId, {
240
+ metadata: {
241
+ compressed: false,
242
+ compressed_at: null,
243
+ compression_stats: null
244
+ }
245
+ });
246
+
247
+ logger.info('Session marked as decompressed', { sessionId });
248
+
249
+ return {
250
+ decompressed: true,
251
+ note: 'Original messages cannot be restored'
252
+ };
253
+ }
254
+ }
255
+
256
+ module.exports = MemoryCompression;
@@ -0,0 +1,247 @@
1
+ /**
2
+ * Milestone — Milestone and requirements lifecycle operations
3
+ */
4
+
5
+ const fs = require('fs');
6
+ const path = require('path');
7
+ const { escapeRegex, getMilestonePhaseFilter, output, error } = require('./core.cjs');
8
+ const { extractFrontmatter } = require('./frontmatter.cjs');
9
+ const { writeStateMd } = require('./state.cjs');
10
+
11
+ function cmdRequirementsMarkComplete(cwd, reqIdsRaw, raw) {
12
+ if (!reqIdsRaw || reqIdsRaw.length === 0) {
13
+ error('requirement IDs required. Usage: requirements mark-complete REQ-01,REQ-02 or REQ-01 REQ-02');
14
+ }
15
+
16
+ // Accept comma-separated, space-separated, or bracket-wrapped: [REQ-01, REQ-02]
17
+ const reqIds = reqIdsRaw
18
+ .join(' ')
19
+ .replace(/[\[\]]/g, '')
20
+ .split(/[,\s]+/)
21
+ .map(r => r.trim())
22
+ .filter(Boolean);
23
+
24
+ if (reqIds.length === 0) {
25
+ error('no valid requirement IDs found');
26
+ }
27
+
28
+ const reqPath = path.join(cwd, '.planning', 'REQUIREMENTS.md');
29
+ if (!fs.existsSync(reqPath)) {
30
+ output({ updated: false, reason: 'REQUIREMENTS.md not found', ids: reqIds }, raw, 'no requirements file');
31
+ return;
32
+ }
33
+
34
+ let reqContent = fs.readFileSync(reqPath, 'utf-8');
35
+ const updated = [];
36
+ const notFound = [];
37
+
38
+ for (const reqId of reqIds) {
39
+ let found = false;
40
+ const reqEscaped = escapeRegex(reqId);
41
+
42
+ // Update checkbox: - [ ] **REQ-ID** → - [x] **REQ-ID**
43
+ const checkboxPattern = new RegExp(`(-\\s*\\[)[ ](\\]\\s*\\*\\*${reqEscaped}\\*\\*)`, 'gi');
44
+ if (checkboxPattern.test(reqContent)) {
45
+ reqContent = reqContent.replace(checkboxPattern, '$1x$2');
46
+ found = true;
47
+ }
48
+
49
+ // Update traceability table: | REQ-ID | Phase N | Pending | → | REQ-ID | Phase N | Complete |
50
+ const tablePattern = new RegExp(`(\\|\\s*${reqEscaped}\\s*\\|[^|]+\\|)\\s*Pending\\s*(\\|)`, 'gi');
51
+ if (tablePattern.test(reqContent)) {
52
+ // Re-read since test() advances lastIndex for global regex
53
+ reqContent = reqContent.replace(
54
+ new RegExp(`(\\|\\s*${reqEscaped}\\s*\\|[^|]+\\|)\\s*Pending\\s*(\\|)`, 'gi'),
55
+ '$1 Complete $2'
56
+ );
57
+ found = true;
58
+ }
59
+
60
+ if (found) {
61
+ updated.push(reqId);
62
+ } else {
63
+ notFound.push(reqId);
64
+ }
65
+ }
66
+
67
+ if (updated.length > 0) {
68
+ fs.writeFileSync(reqPath, reqContent, 'utf-8');
69
+ }
70
+
71
+ output({
72
+ updated: updated.length > 0,
73
+ marked_complete: updated,
74
+ not_found: notFound,
75
+ total: reqIds.length,
76
+ }, raw, `${updated.length}/${reqIds.length} requirements marked complete`);
77
+ }
78
+
79
+ function cmdMilestoneComplete(cwd, version, options, raw) {
80
+ if (!version) {
81
+ error('version required for milestone complete (e.g., v1.0)');
82
+ }
83
+
84
+ const roadmapPath = path.join(cwd, '.planning', 'ROADMAP.md');
85
+ const reqPath = path.join(cwd, '.planning', 'REQUIREMENTS.md');
86
+ const statePath = path.join(cwd, '.planning', 'STATE.md');
87
+ const milestonesPath = path.join(cwd, '.planning', 'MILESTONES.md');
88
+ const archiveDir = path.join(cwd, '.planning', 'milestones');
89
+ const phasesDir = path.join(cwd, '.planning', 'phases');
90
+ const today = new Date().toISOString().split('T')[0];
91
+ const milestoneName = options.name || version;
92
+
93
+ // Ensure archive directory exists
94
+ fs.mkdirSync(archiveDir, { recursive: true });
95
+
96
+ // Scope stats and accomplishments to only the phases belonging to the
97
+ // current milestone's ROADMAP. Uses the shared filter from core.cjs
98
+ // (same logic used by cmdPhasesList and other callers).
99
+ const isDirInMilestone = getMilestonePhaseFilter(cwd);
100
+
101
+ // Gather stats from phases (scoped to current milestone only)
102
+ let phaseCount = 0;
103
+ let totalPlans = 0;
104
+ let totalTasks = 0;
105
+ const accomplishments = [];
106
+
107
+ try {
108
+ const entries = fs.readdirSync(phasesDir, { withFileTypes: true });
109
+ const dirs = entries.filter(e => e.isDirectory()).map(e => e.name).sort();
110
+
111
+ for (const dir of dirs) {
112
+ if (!isDirInMilestone(dir)) continue;
113
+
114
+ phaseCount++;
115
+ const phaseFiles = fs.readdirSync(path.join(phasesDir, dir));
116
+ const plans = phaseFiles.filter(f => f.endsWith('-PLAN.md') || f === 'PLAN.md');
117
+ const summaries = phaseFiles.filter(f => f.endsWith('-SUMMARY.md') || f === 'SUMMARY.md');
118
+ totalPlans += plans.length;
119
+
120
+ // Extract one-liners from summaries
121
+ for (const s of summaries) {
122
+ try {
123
+ const content = fs.readFileSync(path.join(phasesDir, dir, s), 'utf-8');
124
+ const fm = extractFrontmatter(content);
125
+ if (fm['one-liner']) {
126
+ accomplishments.push(fm['one-liner']);
127
+ }
128
+ // Count tasks
129
+ const taskMatches = content.match(/##\s*Task\s*\d+/gi) || [];
130
+ totalTasks += taskMatches.length;
131
+ } catch (err) {
132
+ const { defaultLogger: logger } = require('./logger.cjs');
133
+ logger.warn('Failed to read phase summary', { phase: dir, summary: s, error: err.message });
134
+ }
135
+ }
136
+ }
137
+ } catch (err) {
138
+ const { defaultLogger: logger } = require('./logger.cjs');
139
+ logger.warn('Failed to scan phase summaries', { phasesDir, error: err.message });
140
+ }
141
+
142
+ // Archive ROADMAP.md
143
+ if (fs.existsSync(roadmapPath)) {
144
+ const roadmapContent = fs.readFileSync(roadmapPath, 'utf-8');
145
+ fs.writeFileSync(path.join(archiveDir, `${version}-ROADMAP.md`), roadmapContent, 'utf-8');
146
+ }
147
+
148
+ // Archive REQUIREMENTS.md
149
+ if (fs.existsSync(reqPath)) {
150
+ const reqContent = fs.readFileSync(reqPath, 'utf-8');
151
+ const archiveHeader = `# Requirements Archive: ${version} ${milestoneName}\n\n**Archived:** ${today}\n**Status:** SHIPPED\n\nFor current requirements, see \`.planning/REQUIREMENTS.md\`.\n\n---\n\n`;
152
+ fs.writeFileSync(path.join(archiveDir, `${version}-REQUIREMENTS.md`), archiveHeader + reqContent, 'utf-8');
153
+ }
154
+
155
+ // Archive audit file if exists
156
+ const auditFile = path.join(cwd, '.planning', `${version}-MILESTONE-AUDIT.md`);
157
+ if (fs.existsSync(auditFile)) {
158
+ fs.renameSync(auditFile, path.join(archiveDir, `${version}-MILESTONE-AUDIT.md`));
159
+ }
160
+
161
+ // Create/append MILESTONES.md entry
162
+ const accomplishmentsList = accomplishments.map(a => `- ${a}`).join('\n');
163
+ const milestoneEntry = `## ${version} ${milestoneName} (Shipped: ${today})\n\n**Phases completed:** ${phaseCount} phases, ${totalPlans} plans, ${totalTasks} tasks\n\n**Key accomplishments:**\n${accomplishmentsList || '- (none recorded)'}\n\n---\n\n`;
164
+
165
+ if (fs.existsSync(milestonesPath)) {
166
+ const existing = fs.readFileSync(milestonesPath, 'utf-8');
167
+ if (!existing.trim()) {
168
+ // Empty file — treat like new
169
+ fs.writeFileSync(milestonesPath, `# Milestones\n\n${milestoneEntry}`, 'utf-8');
170
+ } else {
171
+ // Insert after the header line(s) for reverse chronological order (newest first)
172
+ const headerMatch = existing.match(/^(#{1,3}\s+[^\n]*\n\n?)/);
173
+ if (headerMatch) {
174
+ const header = headerMatch[1];
175
+ const rest = existing.slice(header.length);
176
+ fs.writeFileSync(milestonesPath, header + milestoneEntry + rest, 'utf-8');
177
+ } else {
178
+ // No recognizable header — prepend the entry
179
+ fs.writeFileSync(milestonesPath, milestoneEntry + existing, 'utf-8');
180
+ }
181
+ }
182
+ } else {
183
+ fs.writeFileSync(milestonesPath, `# Milestones\n\n${milestoneEntry}`, 'utf-8');
184
+ }
185
+
186
+ // Update STATE.md
187
+ if (fs.existsSync(statePath)) {
188
+ let stateContent = fs.readFileSync(statePath, 'utf-8');
189
+ stateContent = stateContent.replace(
190
+ /(\*\*Status:\*\*\s*).*/,
191
+ `$1${version} milestone complete`
192
+ );
193
+ stateContent = stateContent.replace(
194
+ /(\*\*Last Activity:\*\*\s*).*/,
195
+ `$1${today}`
196
+ );
197
+ stateContent = stateContent.replace(
198
+ /(\*\*Last Activity Description:\*\*\s*).*/,
199
+ `$1${version} milestone completed and archived`
200
+ );
201
+ writeStateMd(statePath, stateContent, cwd);
202
+ }
203
+
204
+ // Archive phase directories if requested
205
+ let phasesArchived = false;
206
+ if (options.archivePhases) {
207
+ try {
208
+ const phaseArchiveDir = path.join(archiveDir, `${version}-phases`);
209
+ fs.mkdirSync(phaseArchiveDir, { recursive: true });
210
+
211
+ const phaseEntries = fs.readdirSync(phasesDir, { withFileTypes: true });
212
+ const phaseDirNames = phaseEntries.filter(e => e.isDirectory()).map(e => e.name);
213
+ let archivedCount = 0;
214
+ for (const dir of phaseDirNames) {
215
+ if (!isDirInMilestone(dir)) continue;
216
+ fs.renameSync(path.join(phasesDir, dir), path.join(phaseArchiveDir, dir));
217
+ archivedCount++;
218
+ }
219
+ phasesArchived = archivedCount > 0;
220
+ } catch {}
221
+ }
222
+
223
+ const result = {
224
+ version,
225
+ name: milestoneName,
226
+ date: today,
227
+ phases: phaseCount,
228
+ plans: totalPlans,
229
+ tasks: totalTasks,
230
+ accomplishments,
231
+ archived: {
232
+ roadmap: fs.existsSync(path.join(archiveDir, `${version}-ROADMAP.md`)),
233
+ requirements: fs.existsSync(path.join(archiveDir, `${version}-REQUIREMENTS.md`)),
234
+ audit: fs.existsSync(path.join(archiveDir, `${version}-MILESTONE-AUDIT.md`)),
235
+ phases: phasesArchived,
236
+ },
237
+ milestones_updated: true,
238
+ state_updated: fs.existsSync(statePath),
239
+ };
240
+
241
+ output(result, raw);
242
+ }
243
+
244
+ module.exports = {
245
+ cmdRequirementsMarkComplete,
246
+ cmdMilestoneComplete,
247
+ };