@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,617 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Release Validator — Automated release readiness validation
5
+ *
6
+ * Runs security gates, tier checklist validation, and produces
7
+ * a Production Readiness Score (0-100) for /ez:release.
8
+ */
9
+
10
+ 'use strict';
11
+
12
+ const fs = require('fs');
13
+ const path = require('path');
14
+ const { execSync } = require('child_process');
15
+ const TierManager = require('./tier-manager.cjs');
16
+
17
+ // ─────────────────────────────────────────────
18
+ // Security Helpers
19
+ // ─────────────────────────────────────────────
20
+
21
+ /**
22
+ * Calculate Shannon entropy for a string — used to detect high-entropy secrets
23
+ * @param {string} str
24
+ * @param {number} threshold
25
+ * @returns {boolean}
26
+ */
27
+ function hasHighEntropy(str, threshold = 4.5) {
28
+ const freq = {};
29
+ for (const c of str) freq[c] = (freq[c] || 0) + 1;
30
+ const len = str.length;
31
+ let entropy = 0;
32
+ for (const c in freq) {
33
+ const p = freq[c] / len;
34
+ entropy -= p * Math.log2(p);
35
+ }
36
+ return entropy > threshold;
37
+ }
38
+
39
+ // ─────────────────────────────────────────────
40
+ // Security Gates
41
+ // ─────────────────────────────────────────────
42
+
43
+ /**
44
+ * Run all security gates
45
+ * @param {string} cwd - Working directory
46
+ * @returns {{ passed: boolean, gates: object[] }}
47
+ */
48
+ function runSecurityGates(cwd = process.cwd()) {
49
+ const gates = [];
50
+
51
+ // Gate 1: Multi-pattern secret detection
52
+ try {
53
+ const secretPatterns = [
54
+ // Original + variants
55
+ '(api[_-]?key|api[_-]?k[e3]y|password|passw[0o]rd|s[e3]cr[e3]t|auth[_-]?token)',
56
+ // High-value secret types
57
+ '(bearer|private[_-]?key|access[_-]?token|refresh[_-]?token|client[_-]?secret)',
58
+ // Known formats: AWS
59
+ 'AKIA[0-9A-Z]{16}',
60
+ // GitHub PAT
61
+ 'ghp_[a-zA-Z0-9]{36}',
62
+ // JWT pattern
63
+ 'eyJ[a-zA-Z0-9-_=]+\\.[a-zA-Z0-9-_=]+\\.'
64
+ ];
65
+ const secretPattern = secretPatterns.join('|');
66
+ const result = execSync(
67
+ `git grep -i -E "${secretPattern}" HEAD 2>/dev/null | grep -v "example\\|placeholder\\|your-key\\|process\\.env\\|env\\.\\|config\\." | wc -l`,
68
+ { cwd, encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] }
69
+ ).trim();
70
+ const count = parseInt(result) || 0;
71
+ gates.push({
72
+ name: 'no_secrets',
73
+ label: 'No secrets in committed files',
74
+ passed: count === 0,
75
+ blocking: true,
76
+ detail: count === 0 ? 'Clean' : `${count} potential secret(s) found`
77
+ });
78
+ } catch {
79
+ gates.push({ name: 'no_secrets', label: 'No secrets in committed files', passed: true, blocking: true, detail: 'Check skipped (git not available)' });
80
+ }
81
+
82
+ // Gate 2: npm audit
83
+ try {
84
+ execSync('npm audit --audit-level=critical 2>/dev/null', { cwd, stdio: 'pipe' });
85
+ gates.push({ name: 'npm_audit', label: 'npm audit — no critical vulnerabilities', passed: true, blocking: true, detail: 'Clean' });
86
+ } catch (err) {
87
+ const output = err.stdout ? err.stdout.toString() : '';
88
+ const criticals = (output.match(/critical/gi) || []).length;
89
+ gates.push({
90
+ name: 'npm_audit',
91
+ label: 'npm audit — no critical vulnerabilities',
92
+ passed: false,
93
+ blocking: true,
94
+ detail: `${criticals} critical vulnerability issue(s). Run: npm audit fix`
95
+ });
96
+ }
97
+
98
+ // Gate 3: No production TODOs
99
+ try {
100
+ const srcDirs = ['src', 'lib', 'app', 'server'].filter(d => fs.existsSync(path.join(cwd, d)));
101
+ if (srcDirs.length > 0) {
102
+ const searchDirs = srcDirs.join(' ');
103
+ const result = execSync(
104
+ `grep -rn "TODO\\|FIXME\\|HACK\\|XXX" ${searchDirs} --include="*.ts" --include="*.js" --include="*.py" 2>/dev/null | grep -v "test\\|spec\\|\\.test\\." | wc -l`,
105
+ { cwd, encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] }
106
+ ).trim();
107
+ const count = parseInt(result) || 0;
108
+ gates.push({
109
+ name: 'no_prod_todos',
110
+ label: 'No production TODO/FIXME in src/',
111
+ passed: count === 0,
112
+ blocking: false, // advisory
113
+ detail: count === 0 ? 'Clean' : `${count} TODO/FIXME found in production code`
114
+ });
115
+ } else {
116
+ gates.push({ name: 'no_prod_todos', label: 'No production TODO/FIXME in src/', passed: true, blocking: false, detail: 'No src/ directory found — skipped' });
117
+ }
118
+ } catch {
119
+ gates.push({ name: 'no_prod_todos', label: 'No production TODO/FIXME in src/', passed: true, blocking: false, detail: 'Check skipped' });
120
+ }
121
+
122
+ // Gate 4: .env in .gitignore
123
+ try {
124
+ const gitignore = fs.readFileSync(path.join(cwd, '.gitignore'), 'utf8');
125
+ const protected_ = gitignore.match(/^\.env/m) !== null;
126
+ gates.push({
127
+ name: 'env_protected',
128
+ label: '.env files in .gitignore',
129
+ passed: protected_,
130
+ blocking: true,
131
+ detail: protected_ ? 'Protected' : '.env not found in .gitignore — add it before releasing'
132
+ });
133
+ } catch {
134
+ gates.push({ name: 'env_protected', label: '.env files in .gitignore', passed: false, blocking: true, detail: '.gitignore not found or .env not listed' });
135
+ }
136
+
137
+ // Gate 5: MoSCoW coverage advisory (non-blocking)
138
+ try {
139
+ const featuresDir = path.join(cwd, 'features');
140
+ const testDir = path.join(cwd, 'test');
141
+ const specDir = path.join(cwd, 'spec');
142
+ const bddDir = [featuresDir, testDir, specDir].find(d => fs.existsSync(d));
143
+ if (bddDir) {
144
+ const featureFiles = fs.readdirSync(bddDir).filter(f => f.endsWith('.feature'));
145
+ if (featureFiles.length > 0) {
146
+ let totalScenarios = 0;
147
+ let taggedScenarios = 0;
148
+ for (const file of featureFiles) {
149
+ const content = fs.readFileSync(path.join(bddDir, file), 'utf8');
150
+ const scenarioMatches = content.match(/^\s*Scenario/gm) || [];
151
+ const mustMatches = content.match(/@must|@should|@could|@wont/g) || [];
152
+ totalScenarios += scenarioMatches.length;
153
+ taggedScenarios += mustMatches.length;
154
+ }
155
+ const untaggedPct = totalScenarios > 0 ? ((totalScenarios - taggedScenarios) / totalScenarios) * 100 : 0;
156
+ gates.push({
157
+ name: 'moscow_coverage',
158
+ label: 'MoSCoW tag coverage in BDD scenarios',
159
+ passed: untaggedPct <= 20,
160
+ blocking: false, // advisory
161
+ detail: untaggedPct <= 20
162
+ ? `${Math.round(100 - untaggedPct)}% scenarios tagged`
163
+ : `${Math.round(untaggedPct)}% scenarios missing @must/@should tags`
164
+ });
165
+ } else {
166
+ gates.push({ name: 'moscow_coverage', label: 'MoSCoW tag coverage in BDD scenarios', passed: true, blocking: false, detail: 'No .feature files found — skipped' });
167
+ }
168
+ } else {
169
+ gates.push({ name: 'moscow_coverage', label: 'MoSCoW tag coverage in BDD scenarios', passed: true, blocking: false, detail: 'No BDD directory found — skipped' });
170
+ }
171
+ } catch {
172
+ gates.push({ name: 'moscow_coverage', label: 'MoSCoW tag coverage in BDD scenarios', passed: true, blocking: false, detail: 'Check skipped' });
173
+ }
174
+
175
+ const passed = gates.filter(g => g.passed && g.blocking).length;
176
+ const total = gates.filter(g => g.blocking).length;
177
+ const allPassed = gates.filter(g => g.blocking).every(g => g.passed);
178
+
179
+ return { passed: allPassed, gates, score: `${passed}/${total}` };
180
+ }
181
+
182
+ // ─────────────────────────────────────────────
183
+ // Tier Checklist
184
+ // ─────────────────────────────────────────────
185
+
186
+ const MVP_CHECKLIST = [
187
+ { id: 'bdd_must', label: 'All @must BDD scenarios passing', auto: true },
188
+ { id: 'npm_audit', label: 'npm audit — no critical vulnerabilities', auto: true },
189
+ { id: 'health_endpoint', label: 'Health endpoint returns 200', auto: true },
190
+ { id: 'no_secrets', label: 'No secrets in committed files', auto: true },
191
+ { id: 'app_starts', label: 'Application starts without errors', auto: true },
192
+ { id: 'rollback_documented', label: 'Rollback procedure documented', auto: true },
193
+ { id: 'security_scan', label: 'Baseline security scan completed', auto: true },
194
+ { id: 'audit_logging', label: 'Audit logging enabled for security-sensitive actions', auto: true },
195
+ { id: 'compliance_evidence', label: 'Required compliance checklist/evidence files present', auto: false }
196
+ ];
197
+
198
+ const MEDIUM_EXTRA = [
199
+ { id: 'bdd_should', label: 'All @should BDD scenarios passing', auto: true },
200
+ { id: 'coverage_80', label: 'Test coverage ≥ 80%', auto: true },
201
+ { id: 'staging_parity', label: 'Staging environment parity verified', auto: false },
202
+ { id: 'monitoring', label: 'Monitoring/alerts configured', auto: false },
203
+ { id: 'structured_logging', label: 'Structured logging (no console.log in prod)', auto: true },
204
+ { id: 'perf_baseline', label: 'Performance baseline documented', auto: false },
205
+ { id: 'error_tracking', label: 'Error tracking configured', auto: false },
206
+ { id: 'db_migrations', label: 'Database migrations tested on staging', auto: false },
207
+ { id: 'api_docs', label: 'API documentation current', auto: false },
208
+ { id: 'env_example', label: '.env.example up to date', auto: true },
209
+ { id: 'graceful_shutdown', label: 'Graceful shutdown handled', auto: true },
210
+ { id: 'rate_limiting', label: 'Rate limiting on public API endpoints', auto: true }
211
+ ];
212
+
213
+ const ENTERPRISE_EXTRA = [
214
+ { id: 'bdd_could', label: 'All @could BDD scenarios passing', auto: true },
215
+ { id: 'coverage_95', label: 'Test coverage ≥ 95%', auto: true },
216
+ { id: 'security_audit', label: 'Security audit completed', auto: false },
217
+ { id: 'compliance_docs', label: 'Compliance documentation updated', auto: false },
218
+ { id: 'load_test', label: 'Load test results documented', auto: false },
219
+ { id: 'dr_tested', label: 'Disaster recovery tested', auto: false },
220
+ { id: 'data_retention', label: 'Data retention policy configured', auto: false },
221
+ { id: 'audit_logging', label: 'Audit logging enabled', auto: true },
222
+ { id: 'pentest', label: 'Penetration test completed or scheduled', auto: false },
223
+ { id: 'soc2_gdpr', label: 'SOC2/GDPR controls validated', auto: false },
224
+ { id: 'change_ticket', label: 'Change management ticket filed', auto: false },
225
+ { id: 'incident_runbook', label: 'Incident runbook up to date', auto: false }
226
+ ];
227
+
228
+ // ─────────────────────────────────────────────
229
+ // Rollback Validation
230
+ // ─────────────────────────────────────────────
231
+
232
+ /**
233
+ * Validate rollback plan content — checks for unfilled placeholders
234
+ * @param {string} cwd
235
+ * @returns {{ status: string, detail: string }}
236
+ */
237
+ function validateRollbackContent(cwd) {
238
+ const releasesDir = path.join(cwd, '.planning', 'releases');
239
+ if (!fs.existsSync(releasesDir)) {
240
+ return { status: 'fail', detail: 'No .planning/releases/ directory found' };
241
+ }
242
+
243
+ const rollbackFiles = fs.readdirSync(releasesDir)
244
+ .filter(f => f.includes('ROLLBACK') && f.endsWith('.md'));
245
+
246
+ if (rollbackFiles.length === 0) {
247
+ return { status: 'fail', detail: 'No rollback plan found' };
248
+ }
249
+
250
+ const latest = rollbackFiles.sort().pop();
251
+ const content = fs.readFileSync(path.join(releasesDir, latest), 'utf8');
252
+
253
+ // Detect unfilled placeholders like {name}, {migration_name}, {your-domain}
254
+ const placeholders = content.match(/\{[a-z_-]+\}/gi) || [];
255
+ if (placeholders.length > 0) {
256
+ return {
257
+ status: 'fail',
258
+ detail: `Rollback plan has ${placeholders.length} unfilled placeholder(s): ${placeholders.slice(0, 3).join(', ')}`
259
+ };
260
+ }
261
+
262
+ return { status: 'pass', detail: `Rollback plan validated: ${latest}` };
263
+ }
264
+
265
+ // ─────────────────────────────────────────────
266
+ // Manual Checklist State Persistence (Fix 11)
267
+ // ─────────────────────────────────────────────
268
+
269
+ /**
270
+ * Load persisted manual checklist state
271
+ * @param {string} cwd
272
+ * @returns {object}
273
+ */
274
+ function loadChecklistState(cwd) {
275
+ const statePath = path.join(cwd, '.planning', 'releases', 'checklist-state.json');
276
+ if (!fs.existsSync(statePath)) return {};
277
+ try { return JSON.parse(fs.readFileSync(statePath, 'utf8')); }
278
+ catch { return {}; }
279
+ }
280
+
281
+ /**
282
+ * Mark a manual checklist item as complete with approver and timestamp
283
+ * @param {string} itemId
284
+ * @param {string} approver
285
+ * @param {string} cwd
286
+ */
287
+ function markManualItemComplete(itemId, approver, cwd = process.cwd()) {
288
+ const state = loadChecklistState(cwd);
289
+ state[itemId] = {
290
+ approved: true,
291
+ approver: approver || 'unknown',
292
+ timestamp: new Date().toISOString(),
293
+ expires: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString() // 30 days
294
+ };
295
+ const statePath = path.join(cwd, '.planning', 'releases', 'checklist-state.json');
296
+ fs.mkdirSync(path.dirname(statePath), { recursive: true });
297
+ fs.writeFileSync(statePath, JSON.stringify(state, null, 2));
298
+ }
299
+
300
+ /**
301
+ * Get full checklist for a tier
302
+ * @param {string} tier
303
+ * @returns {object[]}
304
+ */
305
+ function getChecklist(tier) {
306
+ const t = tier.toLowerCase();
307
+ if (t === 'mvp') return [...MVP_CHECKLIST];
308
+ if (t === 'medium') return [...MVP_CHECKLIST, ...MEDIUM_EXTRA];
309
+ if (t === 'enterprise') return [...MVP_CHECKLIST, ...MEDIUM_EXTRA, ...ENTERPRISE_EXTRA];
310
+ throw new Error(`Unknown tier: ${tier}`);
311
+ }
312
+
313
+ /**
314
+ * Run automated checklist items
315
+ * @param {string} tier
316
+ * @param {string} cwd
317
+ * @param {object} context - additional context (coverage, bddResults, etc.)
318
+ * @returns {{ items: object[], passed: number, total: number, score: number }}
319
+ */
320
+ function runChecklist(tier, cwd = process.cwd(), context = {}) {
321
+ const items = getChecklist(tier);
322
+ const results = [];
323
+
324
+ for (const item of items) {
325
+ let result;
326
+ if (!item.auto) {
327
+ // Fix 11: Check persisted state for manual items
328
+ const state = loadChecklistState(cwd);
329
+ const saved = state[item.id];
330
+ if (saved && saved.approved) {
331
+ const age = Date.now() - new Date(saved.timestamp).getTime();
332
+ const ageDays = Math.floor(age / (1000 * 60 * 60 * 24));
333
+ if (ageDays > 30) {
334
+ result = { ...item, status: 'fail', detail: `Manual check expired ${ageDays}d ago — re-verify required` };
335
+ } else {
336
+ result = { ...item, status: 'pass', detail: `Verified by ${saved.approver} on ${saved.timestamp.split('T')[0]}` };
337
+ }
338
+ } else {
339
+ result = { ...item, status: 'manual', detail: 'Requires manual verification (run: ez checklist mark <id> <approver>)' };
340
+ }
341
+ } else {
342
+ result = runChecklistItem(item, cwd, context);
343
+ }
344
+ results.push(result);
345
+ }
346
+
347
+ const autoItems = results.filter(r => r.auto);
348
+ const passed = autoItems.filter(r => r.status === 'pass').length;
349
+ const total = autoItems.length;
350
+
351
+ // Compute readiness score: blocking failures cost 10, advisory failures cost 2
352
+ let score = 100;
353
+ for (const r of results) {
354
+ if (r.status === 'fail') {
355
+ score -= r.blocking !== false ? 10 : 2;
356
+ }
357
+ }
358
+ score = Math.max(0, score);
359
+
360
+ return { items: results, passed, total, score };
361
+ }
362
+
363
+ function runChecklistItem(item, cwd, context) {
364
+ try {
365
+ switch (item.id) {
366
+ case 'npm_audit':
367
+ case 'no_secrets':
368
+ // Already handled in security gates — check from context
369
+ return { ...item, status: 'pass', detail: 'Verified in security gates' };
370
+
371
+ case 'coverage_80': {
372
+ const cov = context.coverage;
373
+ if (cov === undefined) return { ...item, status: 'skip', detail: 'No coverage data available' };
374
+ return { ...item, status: cov >= 80 ? 'pass' : 'fail', detail: `Coverage: ${cov}%` };
375
+ }
376
+
377
+ case 'coverage_95': {
378
+ const cov = context.coverage;
379
+ if (cov === undefined) return { ...item, status: 'skip', detail: 'No coverage data available' };
380
+ return { ...item, status: cov >= 95 ? 'pass' : 'fail', detail: `Coverage: ${cov}%` };
381
+ }
382
+
383
+ case 'bdd_must': {
384
+ const { bddPassed, moscowTagged, totalScenarios } = context;
385
+ // Hard gate: fail if there are too many untagged scenarios
386
+ if (moscowTagged !== undefined && totalScenarios > 0) {
387
+ const untaggedPct = ((totalScenarios - moscowTagged) / totalScenarios) * 100;
388
+ if (untaggedPct > 20) { // > 20% untagged = blocking
389
+ return { ...item, status: 'fail', detail: `${Math.round(untaggedPct)}% scenarios missing @must/@should tags — BDD coverage unverifiable` };
390
+ }
391
+ }
392
+ if (bddPassed === undefined) return { ...item, status: 'skip', detail: 'No BDD results — run test suite first' };
393
+ return { ...item, status: bddPassed ? 'pass' : 'fail', detail: bddPassed ? 'All scenarios passing' : 'Some scenarios failing' };
394
+ }
395
+
396
+ case 'bdd_should':
397
+ case 'bdd_could': {
398
+ const bddPassed = context.bddPassed;
399
+ if (bddPassed === undefined) return { ...item, status: 'skip', detail: 'No BDD test results available' };
400
+ return { ...item, status: bddPassed ? 'pass' : 'fail', detail: bddPassed ? 'All scenarios passing' : 'Some scenarios failing' };
401
+ }
402
+
403
+ case 'rollback_documented': {
404
+ const rollbackResult = validateRollbackContent(cwd);
405
+ return { ...item, status: rollbackResult.status, detail: rollbackResult.detail };
406
+ }
407
+
408
+ case 'env_example': {
409
+ const hasExample = fs.existsSync(path.join(cwd, '.env.example'));
410
+ return { ...item, status: hasExample ? 'pass' : 'fail', detail: hasExample ? '.env.example found' : '.env.example missing' };
411
+ }
412
+
413
+ case 'structured_logging': {
414
+ try {
415
+ const srcDirs = ['src', 'lib', 'app'].filter(d => fs.existsSync(path.join(cwd, d)));
416
+ if (srcDirs.length === 0) return { ...item, status: 'skip', detail: 'No src/ found' };
417
+ const result = execSync(
418
+ `grep -rn "console\\.log" ${srcDirs.join(' ')} --include="*.ts" --include="*.js" 2>/dev/null | grep -v "test\\|spec" | wc -l`,
419
+ { cwd, encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] }
420
+ ).trim();
421
+ const count = parseInt(result) || 0;
422
+ return { ...item, status: count === 0 ? 'pass' : 'fail', detail: count === 0 ? 'No console.log in prod' : `${count} console.log found` };
423
+ } catch {
424
+ return { ...item, status: 'skip', detail: 'Check failed' };
425
+ }
426
+ }
427
+
428
+ case 'health_endpoint': {
429
+ // Try to detect health endpoint in source
430
+ try {
431
+ const srcDirs = ['src', 'app', 'server', 'pages/api'].filter(d => fs.existsSync(path.join(cwd, d)));
432
+ if (srcDirs.length === 0) return { ...item, status: 'skip', detail: 'No src/ found' };
433
+ const result = execSync(
434
+ `grep -rn "health\\|/ping\\|/status" ${srcDirs.join(' ')} --include="*.ts" --include="*.js" 2>/dev/null | wc -l`,
435
+ { cwd, encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] }
436
+ ).trim();
437
+ const found = parseInt(result) > 0;
438
+ return { ...item, status: found ? 'pass' : 'skip', detail: found ? 'Health endpoint found in source' : 'No health endpoint found (optional for MVP)' };
439
+ } catch {
440
+ return { ...item, status: 'skip', detail: 'Check failed' };
441
+ }
442
+ }
443
+
444
+ default:
445
+ return { ...item, status: 'skip', detail: 'Automated check not implemented' };
446
+ }
447
+ } catch (err) {
448
+ return { ...item, status: 'error', detail: err.message };
449
+ }
450
+ }
451
+
452
+ // ─────────────────────────────────────────────
453
+ // Full Validation
454
+ // ─────────────────────────────────────────────
455
+
456
+ /**
457
+ * Run full release validation
458
+ * @param {string} tier
459
+ * @param {string} version
460
+ * @param {string} cwd
461
+ * @param {object} context
462
+ * @returns {{ valid: boolean, blockers: string[], warnings: string[], score: number, securityGates: object, checklist: object }}
463
+ */
464
+ function validateRelease(tier, version, cwd = process.cwd(), context = {}) {
465
+ const securityGates = runSecurityGates(cwd);
466
+ const checklist = runChecklist(tier, cwd, context);
467
+
468
+ const blockers = [];
469
+ const warnings = [];
470
+
471
+ // Security gate failures
472
+ for (const gate of securityGates.gates) {
473
+ if (!gate.passed && gate.blocking) blockers.push(`Security: ${gate.label} — ${gate.detail}`);
474
+ if (!gate.passed && !gate.blocking) warnings.push(`Security: ${gate.label} — ${gate.detail}`);
475
+ }
476
+
477
+ // Checklist failures
478
+ for (const item of checklist.items) {
479
+ if (item.status === 'fail') {
480
+ if (item.blocking !== false) warnings.push(`Checklist: ${item.label}`);
481
+ }
482
+ }
483
+
484
+ const valid = blockers.length === 0;
485
+ const readinessScore = Math.min(checklist.score, securityGates.passed ? 100 : 50);
486
+
487
+ const readinessStatus = readinessScore >= 90 ? 'READY'
488
+ : readinessScore >= 70 ? 'CONDITIONAL'
489
+ : 'NOT READY';
490
+
491
+ return {
492
+ valid,
493
+ tier,
494
+ version,
495
+ blockers,
496
+ warnings,
497
+ score: readinessScore,
498
+ readinessStatus,
499
+ securityGates,
500
+ checklist
501
+ };
502
+ }
503
+
504
+ /**
505
+ * Format validation result as markdown
506
+ */
507
+ function formatValidation(result) {
508
+ const lines = [];
509
+ const icon = result.valid ? '✓' : '✗';
510
+
511
+ lines.push(`## Release Validation: v${result.version} (${result.tier})`);
512
+ lines.push(`**Status:** ${icon} ${result.valid ? 'READY' : 'BLOCKED'}`);
513
+ lines.push(`**Production Readiness Score:** ${result.score}/100 — ${result.readinessStatus}`);
514
+ lines.push('');
515
+
516
+ lines.push('### Security Gates');
517
+ for (const g of result.securityGates.gates) {
518
+ lines.push(`- ${g.passed ? '✓' : '✗'} ${g.label}: ${g.detail}`);
519
+ }
520
+ lines.push('');
521
+
522
+ lines.push(`### Checklist (${result.tier})`);
523
+ for (const item of result.checklist.items) {
524
+ const icon2 = item.status === 'pass' ? '✓' : item.status === 'skip' ? '○' : item.status === 'manual' ? '?' : '✗';
525
+ lines.push(`- ${icon2} ${item.label}${item.detail ? ` — ${item.detail}` : ''}`);
526
+ }
527
+ lines.push('');
528
+
529
+ if (result.blockers.length > 0) {
530
+ lines.push('### Blockers (must fix)');
531
+ result.blockers.forEach(b => lines.push(`- 🛑 ${b}`));
532
+ lines.push('');
533
+ }
534
+
535
+ if (result.warnings.length > 0) {
536
+ lines.push('### Warnings (advisory)');
537
+ result.warnings.forEach(w => lines.push(`- ⚠️ ${w}`));
538
+ }
539
+
540
+ return lines.join('\n');
541
+ }
542
+
543
+ // ─────────────────────────────────────────────
544
+ // CLI Interface
545
+ // ─────────────────────────────────────────────
546
+
547
+ if (require.main === module) {
548
+ const args = process.argv.slice(2);
549
+ const cmd = args[0];
550
+
551
+ try {
552
+ if (cmd === 'security-gates') {
553
+ const result = runSecurityGates(process.cwd());
554
+ if (args.includes('--json')) {
555
+ console.log(JSON.stringify(result, null, 2));
556
+ } else {
557
+ for (const g of result.gates) {
558
+ console.log(`${g.passed ? '✓' : '✗'} ${g.label}: ${g.detail}`);
559
+ }
560
+ process.exit(result.passed ? 0 : 1);
561
+ }
562
+ } else if (cmd === 'checklist') {
563
+ const tier = args[1];
564
+ if (!tier) { console.error('Usage: release-validator.cjs checklist <tier>'); process.exit(1); }
565
+ const result = runChecklist(tier, process.cwd());
566
+ if (args.includes('--json')) {
567
+ console.log(JSON.stringify(result, null, 2));
568
+ } else {
569
+ for (const item of result.items) {
570
+ const icon = item.status === 'pass' ? '✓' : item.status === 'skip' ? '○' : '✗';
571
+ console.log(`${icon} ${item.label}`);
572
+ }
573
+ console.log(`\nScore: ${result.score}/100`);
574
+ }
575
+ } else if (cmd === 'validate') {
576
+ const tier = args[1];
577
+ const version = args[2] || '0.0.0';
578
+ if (!tier) { console.error('Usage: release-validator.cjs validate <tier> [version]'); process.exit(1); }
579
+ const result = validateRelease(tier, version, process.cwd());
580
+ if (args.includes('--json')) {
581
+ console.log(JSON.stringify(result, null, 2));
582
+ } else {
583
+ console.log(formatValidation(result));
584
+ process.exit(result.valid ? 0 : 1);
585
+ }
586
+ } else if (cmd === 'checklist-mark') {
587
+ // Fix 11: ez checklist mark <id> <approver>
588
+ const itemId = args[1];
589
+ const approver = args[2];
590
+ if (!itemId || !approver) {
591
+ console.error('Usage: release-validator.cjs checklist-mark <item-id> <approver>');
592
+ process.exit(1);
593
+ }
594
+ markManualItemComplete(itemId, approver, process.cwd());
595
+ console.log(JSON.stringify({ marked: true, item: itemId, approver, timestamp: new Date().toISOString() }));
596
+ } else {
597
+ console.error(`Unknown command: ${cmd}`);
598
+ console.error('Commands: security-gates, checklist, validate, checklist-mark');
599
+ process.exit(1);
600
+ }
601
+ } catch (err) {
602
+ console.error(`Error: ${err.message}`);
603
+ process.exit(1);
604
+ }
605
+ }
606
+
607
+ module.exports = {
608
+ runSecurityGates,
609
+ getChecklist,
610
+ runChecklist,
611
+ validateRelease,
612
+ formatValidation,
613
+ validateRollbackContent,
614
+ loadChecklistState,
615
+ markManualItemComplete,
616
+ hasHighEntropy
617
+ };