@howlil/ez-agents 3.5.0 → 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 (382) hide show
  1. package/README.md +735 -537
  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 -333
  18. package/agents/ez-requirements-agent.md +377 -377
  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/lib/analytics/analytics-collector.cjs +86 -0
  30. package/bin/lib/analytics/analytics-reporter.cjs +130 -0
  31. package/bin/lib/analytics/cohort-analyzer.cjs +138 -0
  32. package/bin/lib/analytics/funnel-analyzer.cjs +147 -0
  33. package/bin/lib/analytics/nps-tracker.cjs +147 -0
  34. package/bin/lib/archetype-detector.cjs +289 -0
  35. package/bin/lib/assistant-adapter.cjs +361 -0
  36. package/bin/lib/audit-exec.cjs +175 -0
  37. package/bin/lib/auth.cjs +176 -0
  38. package/bin/lib/backup-service.cjs +422 -0
  39. package/bin/lib/bdd-validator.cjs +622 -0
  40. package/bin/lib/business-flow-mapper.cjs +429 -0
  41. package/bin/lib/circuit-breaker.cjs +276 -0
  42. package/bin/lib/code-complexity-analyzer.cjs +360 -0
  43. package/bin/lib/codebase-analyzer.cjs +241 -0
  44. package/bin/lib/commands.cjs +691 -0
  45. package/bin/lib/config.cjs +236 -0
  46. package/bin/lib/constraint-extractor.cjs +526 -0
  47. package/bin/lib/content-scanner.cjs +238 -0
  48. package/bin/lib/context-cache.cjs +154 -0
  49. package/bin/lib/context-compressor.cjs +102 -0
  50. package/bin/lib/context-deduplicator.cjs +105 -0
  51. package/bin/lib/context-errors.cjs +78 -0
  52. package/bin/lib/context-manager.cjs +338 -0
  53. package/bin/lib/context-metadata-tracker.cjs +140 -0
  54. package/bin/lib/context-relevance-scorer.cjs +99 -0
  55. package/bin/lib/core.cjs +507 -0
  56. package/bin/lib/cost-alerts.cjs +174 -0
  57. package/bin/lib/cost-tracker.cjs +275 -0
  58. package/bin/lib/crash-recovery.cjs +220 -0
  59. package/bin/lib/dependency-graph.cjs +319 -0
  60. package/bin/lib/deploy/deploy-audit-log.cjs +76 -0
  61. package/bin/lib/deploy/deploy-detector.cjs +69 -0
  62. package/bin/lib/deploy/deploy-env-manager.cjs +109 -0
  63. package/bin/lib/deploy/deploy-health-check.cjs +88 -0
  64. package/bin/lib/deploy/deploy-pre-flight.cjs +57 -0
  65. package/bin/lib/deploy/deploy-rollback.cjs +72 -0
  66. package/bin/lib/deploy/deploy-runner.cjs +97 -0
  67. package/bin/lib/deploy/deploy-status.cjs +74 -0
  68. package/bin/lib/discussion-synthesizer.cjs +439 -0
  69. package/bin/lib/error-cache.cjs +114 -0
  70. package/bin/lib/error-registry.cjs +177 -0
  71. package/bin/lib/file-access.cjs +207 -0
  72. package/bin/lib/file-lock.cjs +236 -0
  73. package/bin/lib/finops/budget-enforcer.cjs +126 -0
  74. package/bin/lib/finops/cost-reporter.cjs +132 -0
  75. package/bin/lib/finops/finops-analyzer.cjs +112 -0
  76. package/bin/lib/finops/spot-manager.cjs +118 -0
  77. package/bin/lib/framework-detector.cjs +396 -0
  78. package/bin/lib/frontmatter.cjs +313 -0
  79. package/bin/lib/fs-utils.cjs +153 -0
  80. package/bin/lib/gate-executor.cjs +272 -0
  81. package/bin/lib/gates/README.md +374 -0
  82. package/bin/lib/gates/gate-01-requirement.cjs +303 -0
  83. package/bin/lib/gates/gate-02-architecture.cjs +555 -0
  84. package/bin/lib/gates/gate-03-code.cjs +635 -0
  85. package/bin/lib/gates/gate-04-security.cjs +829 -0
  86. package/bin/lib/git-errors.cjs +83 -0
  87. package/bin/lib/git-utils.cjs +321 -0
  88. package/bin/lib/git-workflow-engine.cjs +1157 -0
  89. package/bin/lib/health-check.cjs +227 -0
  90. package/bin/lib/index.cjs +279 -0
  91. package/bin/lib/init.cjs +725 -0
  92. package/bin/lib/lock-logger.cjs +194 -0
  93. package/bin/lib/lock-state.cjs +263 -0
  94. package/bin/lib/lockfile-validator.cjs +227 -0
  95. package/bin/lib/log-rotation.cjs +71 -0
  96. package/bin/lib/logger.cjs +125 -0
  97. package/bin/lib/memory-compression.cjs +256 -0
  98. package/bin/lib/milestone.cjs +247 -0
  99. package/bin/lib/model-provider.cjs +241 -0
  100. package/bin/lib/package-manager-detector.cjs +203 -0
  101. package/bin/lib/package-manager-executor.cjs +385 -0
  102. package/bin/lib/package-manager-service.cjs +216 -0
  103. package/bin/lib/perf/api-monitor.cjs +88 -0
  104. package/bin/lib/perf/db-optimizer.cjs +78 -0
  105. package/bin/lib/perf/frontend-performance.cjs +56 -0
  106. package/bin/lib/perf/perf-analyzer.cjs +77 -0
  107. package/bin/lib/perf/perf-baseline.cjs +102 -0
  108. package/bin/lib/perf/perf-reporter.cjs +117 -0
  109. package/bin/lib/perf/regression-detector.cjs +92 -0
  110. package/bin/lib/phase.cjs +963 -0
  111. package/bin/lib/planning-write.cjs +123 -0
  112. package/bin/lib/project-reporter.cjs +565 -0
  113. package/bin/lib/quality-gate.cjs +332 -0
  114. package/bin/lib/quality-metrics.cjs +324 -0
  115. package/bin/lib/recovery-manager.cjs +98 -0
  116. package/bin/lib/release-validator.cjs +617 -0
  117. package/bin/lib/retry.cjs +119 -0
  118. package/bin/lib/roadmap.cjs +309 -0
  119. package/bin/lib/safe-exec.cjs +173 -0
  120. package/bin/lib/safe-path.cjs +130 -0
  121. package/bin/lib/security-errors.cjs +62 -0
  122. package/bin/lib/session-chain.cjs +304 -0
  123. package/bin/lib/session-errors.cjs +81 -0
  124. package/bin/lib/session-export.cjs +251 -0
  125. package/bin/lib/session-import.cjs +262 -0
  126. package/bin/lib/session-manager.cjs +280 -0
  127. package/bin/lib/skill-context.cjs +148 -0
  128. package/bin/lib/skill-matcher.cjs +236 -0
  129. package/bin/lib/skill-registry.cjs +360 -0
  130. package/bin/lib/skill-resolver.cjs +449 -0
  131. package/bin/lib/skill-triggers.cjs +90 -0
  132. package/bin/lib/skill-validator.cjs +270 -0
  133. package/bin/lib/skill-versioning.cjs +355 -0
  134. package/bin/lib/stack-detector.cjs +399 -0
  135. package/bin/lib/state.cjs +736 -0
  136. package/bin/lib/tech-debt-analyzer.cjs +309 -0
  137. package/bin/lib/temp-file.cjs +239 -0
  138. package/bin/lib/template.cjs +223 -0
  139. package/bin/lib/test-file-lock.cjs +112 -0
  140. package/bin/lib/test-graceful.cjs +93 -0
  141. package/bin/lib/test-logger.cjs +60 -0
  142. package/bin/lib/test-safe-exec.cjs +38 -0
  143. package/bin/lib/test-safe-path.cjs +33 -0
  144. package/bin/lib/test-temp-file.cjs +125 -0
  145. package/bin/lib/tier-manager.cjs +428 -0
  146. package/bin/lib/timeout-exec.cjs +63 -0
  147. package/bin/lib/tradeoff-analyzer.cjs +284 -0
  148. package/bin/lib/url-fetch.cjs +170 -0
  149. package/bin/lib/verify.cjs +863 -0
  150. package/bin/update.js +217 -214
  151. package/commands/deploy.cjs +53 -0
  152. package/commands/ez/add-tests.md +41 -41
  153. package/commands/ez/audit-milestone.md +36 -36
  154. package/commands/ez/complete-milestone.md +136 -136
  155. package/commands/ez/discuss-phase.md +90 -90
  156. package/commands/ez/execute-phase.md +52 -52
  157. package/commands/ez/help.md +22 -22
  158. package/commands/ez/map-codebase.md +71 -71
  159. package/commands/ez/new-milestone.md +44 -44
  160. package/commands/ez/new-project.md +51 -42
  161. package/commands/ez/plan-phase.md +53 -53
  162. package/commands/ez/progress.md +36 -36
  163. package/commands/ez/quick.md +45 -45
  164. package/commands/ez/resume-work.md +40 -40
  165. package/commands/ez/run-phase.md +580 -0
  166. package/commands/ez/settings.md +36 -36
  167. package/commands/ez/update.md +37 -37
  168. package/commands/ez/verify-work.md +402 -38
  169. package/commands/health-check.cjs +44 -0
  170. package/commands/rollback.cjs +47 -0
  171. package/ez-agents/bin/ez-tools.cjs +599 -2
  172. package/ez-agents/bin/guards/autonomy-guard.cjs +346 -0
  173. package/ez-agents/bin/guards/context-budget-guard.cjs +247 -0
  174. package/ez-agents/bin/guards/hallucination-guard.cjs +271 -0
  175. package/ez-agents/bin/guards/hidden-state-guard.cjs +182 -0
  176. package/ez-agents/bin/guards/team-overhead-guard.cjs +266 -0
  177. package/ez-agents/bin/guards/tool-sprawl-guard.cjs +271 -0
  178. package/ez-agents/bin/lib/analytics/analytics-collector.cjs +86 -0
  179. package/ez-agents/bin/lib/analytics/analytics-reporter.cjs +130 -0
  180. package/ez-agents/bin/lib/analytics/cohort-analyzer.cjs +138 -0
  181. package/ez-agents/bin/lib/analytics/funnel-analyzer.cjs +147 -0
  182. package/ez-agents/bin/lib/analytics/nps-tracker.cjs +147 -0
  183. package/ez-agents/bin/lib/archetype-detector.cjs +289 -0
  184. package/ez-agents/bin/lib/audit-exec.cjs +166 -167
  185. package/ez-agents/bin/lib/auth.cjs +176 -176
  186. package/ez-agents/bin/lib/backup-service.cjs +422 -0
  187. package/ez-agents/bin/lib/bdd-validator.cjs +622 -622
  188. package/ez-agents/bin/lib/business-flow-mapper.cjs +429 -0
  189. package/ez-agents/bin/lib/code-complexity-analyzer.cjs +360 -0
  190. package/ez-agents/bin/lib/codebase-analyzer.cjs +241 -0
  191. package/ez-agents/bin/lib/commands.cjs +685 -685
  192. package/ez-agents/bin/lib/config.cjs +41 -1
  193. package/ez-agents/bin/lib/constraint-extractor.cjs +526 -0
  194. package/ez-agents/bin/lib/content-scanner.cjs +238 -238
  195. package/ez-agents/bin/lib/context-cache.cjs +154 -154
  196. package/ez-agents/bin/lib/context-errors.cjs +71 -71
  197. package/ez-agents/bin/lib/context-manager.cjs +220 -220
  198. package/ez-agents/bin/lib/core.cjs +507 -512
  199. package/ez-agents/bin/lib/cost-tracker.cjs +243 -0
  200. package/ez-agents/bin/lib/crash-recovery.cjs +172 -0
  201. package/ez-agents/bin/lib/dependency-graph.cjs +319 -0
  202. package/ez-agents/bin/lib/deploy/deploy-audit-log.cjs +76 -0
  203. package/ez-agents/bin/lib/deploy/deploy-detector.cjs +69 -0
  204. package/ez-agents/bin/lib/deploy/deploy-env-manager.cjs +109 -0
  205. package/ez-agents/bin/lib/deploy/deploy-health-check.cjs +88 -0
  206. package/ez-agents/bin/lib/deploy/deploy-pre-flight.cjs +57 -0
  207. package/ez-agents/bin/lib/deploy/deploy-rollback.cjs +72 -0
  208. package/ez-agents/bin/lib/deploy/deploy-runner.cjs +97 -0
  209. package/ez-agents/bin/lib/deploy/deploy-status.cjs +74 -0
  210. package/ez-agents/bin/lib/file-access.cjs +207 -207
  211. package/ez-agents/bin/lib/finops/budget-enforcer.cjs +126 -0
  212. package/ez-agents/bin/lib/finops/cost-reporter.cjs +132 -0
  213. package/ez-agents/bin/lib/finops/finops-analyzer.cjs +112 -0
  214. package/ez-agents/bin/lib/finops/spot-manager.cjs +118 -0
  215. package/ez-agents/bin/lib/framework-detector.cjs +396 -0
  216. package/ez-agents/bin/lib/frontmatter.cjs +3 -1
  217. package/ez-agents/bin/lib/gates/README.md +374 -0
  218. package/ez-agents/bin/lib/gates/gate-01-requirement.cjs +303 -0
  219. package/ez-agents/bin/lib/gates/gate-02-architecture.cjs +555 -0
  220. package/ez-agents/bin/lib/gates/gate-03-code.cjs +635 -0
  221. package/ez-agents/bin/lib/gates/gate-04-security.cjs +829 -0
  222. package/ez-agents/bin/lib/git-errors.cjs +83 -83
  223. package/ez-agents/bin/lib/git-utils.cjs +321 -321
  224. package/ez-agents/bin/lib/git-workflow-engine.cjs +1157 -1157
  225. package/ez-agents/bin/lib/health-check.cjs +162 -162
  226. package/ez-agents/bin/lib/index.cjs +2 -8
  227. package/ez-agents/bin/lib/init.cjs +0 -2
  228. package/ez-agents/bin/lib/lockfile-validator.cjs +227 -227
  229. package/ez-agents/bin/lib/log-rotation.cjs +71 -0
  230. package/ez-agents/bin/lib/logger.cjs +22 -47
  231. package/ez-agents/bin/lib/memory-compression.cjs +256 -256
  232. package/ez-agents/bin/lib/package-manager-detector.cjs +203 -203
  233. package/ez-agents/bin/lib/package-manager-executor.cjs +385 -385
  234. package/ez-agents/bin/lib/package-manager-service.cjs +216 -216
  235. package/ez-agents/bin/lib/perf/api-monitor.cjs +88 -0
  236. package/ez-agents/bin/lib/perf/db-optimizer.cjs +78 -0
  237. package/ez-agents/bin/lib/perf/frontend-performance.cjs +56 -0
  238. package/ez-agents/bin/lib/perf/perf-analyzer.cjs +77 -0
  239. package/ez-agents/bin/lib/perf/perf-baseline.cjs +102 -0
  240. package/ez-agents/bin/lib/perf/perf-reporter.cjs +117 -0
  241. package/ez-agents/bin/lib/perf/regression-detector.cjs +92 -0
  242. package/ez-agents/bin/lib/project-reporter.cjs +502 -0
  243. package/ez-agents/bin/lib/quality-gate.cjs +332 -0
  244. package/ez-agents/bin/lib/recovery-manager.cjs +98 -0
  245. package/ez-agents/bin/lib/release-validator.cjs +617 -614
  246. package/ez-agents/bin/lib/security-errors.cjs +62 -0
  247. package/ez-agents/bin/lib/session-chain.cjs +304 -304
  248. package/ez-agents/bin/lib/session-errors.cjs +81 -81
  249. package/ez-agents/bin/lib/session-export.cjs +251 -251
  250. package/ez-agents/bin/lib/session-import.cjs +262 -262
  251. package/ez-agents/bin/lib/session-manager.cjs +280 -280
  252. package/ez-agents/bin/lib/skill-context.cjs +148 -0
  253. package/ez-agents/bin/lib/skill-matcher.cjs +236 -0
  254. package/ez-agents/bin/lib/skill-registry.cjs +341 -0
  255. package/ez-agents/bin/lib/skill-resolver.cjs +449 -0
  256. package/ez-agents/bin/lib/skill-triggers.cjs +90 -0
  257. package/ez-agents/bin/lib/skill-validator.cjs +270 -0
  258. package/ez-agents/bin/lib/skill-versioning.cjs +355 -0
  259. package/ez-agents/bin/lib/stack-detector.cjs +399 -0
  260. package/ez-agents/bin/lib/tech-debt-analyzer.cjs +309 -0
  261. package/ez-agents/bin/lib/tier-manager.cjs +428 -428
  262. package/ez-agents/bin/lib/tradeoff-analyzer.cjs +284 -0
  263. package/ez-agents/bin/lib/url-fetch.cjs +170 -170
  264. package/ez-agents/bin/lib/verify.cjs +863 -863
  265. package/ez-agents/references/decimal-phase-calculation.md +65 -65
  266. package/ez-agents/references/git-integration.md +248 -248
  267. package/ez-agents/references/git-planning-commit.md +38 -38
  268. package/ez-agents/references/metrics-schema.md +118 -118
  269. package/ez-agents/references/model-profile-resolution.md +34 -34
  270. package/ez-agents/references/model-profiles.md +93 -93
  271. package/ez-agents/references/phase-argument-parsing.md +61 -61
  272. package/ez-agents/references/planning-config.md +340 -340
  273. package/ez-agents/references/tier-strategy.md +103 -103
  274. package/ez-agents/references/ui-brand.md +160 -160
  275. package/ez-agents/references/verification-patterns.md +612 -612
  276. package/ez-agents/templates/DEBUG.md +164 -164
  277. package/ez-agents/templates/UAT.md +247 -247
  278. package/ez-agents/templates/agent-output-format.md +404 -0
  279. package/ez-agents/templates/bdd-feature.md +173 -173
  280. package/ez-agents/templates/codebase/architecture.md +255 -255
  281. package/ez-agents/templates/codebase/structure.md +285 -285
  282. package/ez-agents/templates/copilot-instructions.md +7 -7
  283. package/ez-agents/templates/debug-subagent-prompt.md +91 -91
  284. package/ez-agents/templates/discovery.md +146 -146
  285. package/ez-agents/templates/discussion.md +68 -68
  286. package/ez-agents/templates/handoff-protocol.md +294 -0
  287. package/ez-agents/templates/incident-runbook.md +205 -205
  288. package/ez-agents/templates/mode-workflow-templates.md +301 -0
  289. package/ez-agents/templates/phase-prompt.md +610 -610
  290. package/ez-agents/templates/planner-subagent-prompt.md +117 -117
  291. package/ez-agents/templates/project.md +184 -184
  292. package/ez-agents/templates/release-checklist.md +136 -133
  293. package/ez-agents/templates/research.md +552 -552
  294. package/ez-agents/templates/rollback-plan.md +201 -201
  295. package/ez-agents/templates/security-user-setup.md +244 -0
  296. package/ez-agents/templates/skill-validation-rules.md +476 -0
  297. package/ez-agents/templates/state.md +180 -176
  298. package/ez-agents/templates/summary-complex.md +59 -59
  299. package/ez-agents/tests/gates/gate-01-02.test.cjs +812 -0
  300. package/ez-agents/tests/gates/gate-03-04.test.cjs +762 -0
  301. package/ez-agents/tests/gates/gate-05-validator.test.cjs +145 -0
  302. package/ez-agents/tests/gates/gate-06-docs-validator.test.cjs +244 -0
  303. package/ez-agents/tests/gates/gate-07-release-validator.test.cjs +219 -0
  304. package/ez-agents/tests/guards/context-budget-guard.test.cjs +145 -0
  305. package/ez-agents/tests/guards/edge-case-guards.test.cjs +238 -0
  306. package/ez-agents/tests/guards/hallucination-guard.test.cjs +124 -0
  307. package/ez-agents/workflows/audit-milestone.md +1 -1
  308. package/ez-agents/workflows/autonomous.md +844 -844
  309. package/ez-agents/workflows/complete-milestone.md +1 -1
  310. package/ez-agents/workflows/discuss-phase.md +1 -1
  311. package/ez-agents/workflows/execute-phase.md +124 -3
  312. package/ez-agents/workflows/help.md +42 -181
  313. package/ez-agents/workflows/hotfix.md +291 -291
  314. package/ez-agents/workflows/new-milestone.md +713 -713
  315. package/ez-agents/workflows/new-project.md +1089 -1107
  316. package/ez-agents/workflows/plan-phase.md +0 -40
  317. package/ez-agents/workflows/release.md +253 -253
  318. package/ez-agents/workflows/resume-session.md +215 -215
  319. package/ez-agents/workflows/run-phase.md +531 -0
  320. package/ez-agents/workflows/settings.md +2 -35
  321. package/hooks/dist/ez-check-update.js +81 -81
  322. package/hooks/dist/ez-context-monitor.js +148 -141
  323. package/hooks/dist/ez-statusline.js +115 -115
  324. package/package.json +78 -71
  325. package/scripts/fix-qwen-installation.js +144 -144
  326. package/agents/ez-integration-checker.md +0 -443
  327. package/agents/ez-nyquist-auditor.md +0 -176
  328. package/agents/ez-observer-agent.md +0 -260
  329. package/agents/ez-plan-checker.md +0 -706
  330. package/agents/ez-research-synthesizer.md +0 -247
  331. package/agents/ez-scrum-master-agent.md +0 -242
  332. package/agents/ez-tech-lead-agent.md +0 -267
  333. package/agents/ez-ui-auditor.md +0 -439
  334. package/agents/ez-ui-checker.md +0 -300
  335. package/agents/ez-ui-researcher.md +0 -353
  336. package/commands/ez/add-phase.md +0 -43
  337. package/commands/ez/add-todo.md +0 -47
  338. package/commands/ez/arch-review.md +0 -102
  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/export-session.md +0 -79
  345. package/commands/ez/gather-requirements.md +0 -117
  346. package/commands/ez/git-workflow.md +0 -72
  347. package/commands/ez/health.md +0 -22
  348. package/commands/ez/hotfix.md +0 -120
  349. package/commands/ez/import-session.md +0 -82
  350. package/commands/ez/insert-phase.md +0 -32
  351. package/commands/ez/join-discord.md +0 -18
  352. package/commands/ez/list-phase-assumptions.md +0 -46
  353. package/commands/ez/list-sessions.md +0 -96
  354. package/commands/ez/package-manager.md +0 -316
  355. package/commands/ez/pause-work.md +0 -38
  356. package/commands/ez/plan-milestone-gaps.md +0 -34
  357. package/commands/ez/preflight.md +0 -79
  358. package/commands/ez/reapply-patches.md +0 -124
  359. package/commands/ez/release.md +0 -153
  360. package/commands/ez/remove-phase.md +0 -31
  361. package/commands/ez/research-phase.md +0 -190
  362. package/commands/ez/resume.md +0 -107
  363. package/commands/ez/set-profile.md +0 -34
  364. package/commands/ez/standup.md +0 -85
  365. package/commands/ez/stats.md +0 -18
  366. package/commands/ez/ui-phase.md +0 -34
  367. package/commands/ez/ui-review.md +0 -32
  368. package/commands/ez/validate-phase.md +0 -35
  369. package/ez-agents/bin/lib/metrics-tracker.cjs +0 -406
  370. package/ez-agents/templates/UI-SPEC.md +0 -100
  371. package/ez-agents/templates/VALIDATION.md +0 -76
  372. package/ez-agents/templates/context.md +0 -352
  373. package/ez-agents/templates/verification-report.md +0 -322
  374. package/ez-agents/workflows/arch-review.md +0 -54
  375. package/ez-agents/workflows/export-session.md +0 -255
  376. package/ez-agents/workflows/gather-requirements.md +0 -206
  377. package/ez-agents/workflows/import-session.md +0 -303
  378. package/ez-agents/workflows/research-phase.md +0 -74
  379. package/ez-agents/workflows/standup.md +0 -64
  380. package/ez-agents/workflows/ui-phase.md +0 -290
  381. package/ez-agents/workflows/ui-review.md +0 -157
  382. 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
+ };