@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,622 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * BDD Validator — INVEST criteria checker and MoSCoW tagging utilities
5
+ *
6
+ * Validates Gherkin .feature files against:
7
+ * - INVEST criteria (Independent, Negotiable, Valuable, Estimable, Small, Testable)
8
+ * - MoSCoW priority tagging (@must/@should/@could/@wont)
9
+ * - Tier tagging (@mvp/@medium/@enterprise)
10
+ * - Structural correctness (Given/When/Then format)
11
+ */
12
+
13
+ 'use strict';
14
+
15
+ const fs = require('fs');
16
+ const path = require('path');
17
+
18
+ // ─────────────────────────────────────────────
19
+ // Traceability
20
+ // ─────────────────────────────────────────────
21
+
22
+ /**
23
+ * Generate a deterministic scenario ID using FNV-1a hash
24
+ * @param {string} featureName
25
+ * @param {string} scenarioName
26
+ * @returns {string} ID like "SC-A1B2C3"
27
+ */
28
+ function generateScenarioId(featureName, scenarioName) {
29
+ const input = `${featureName}::${scenarioName}`.toLowerCase().replace(/\s+/g, '-');
30
+ // Simple non-crypto hash (FNV-1a)
31
+ let hash = 0x811c9dc5;
32
+ for (const char of input) {
33
+ hash ^= char.charCodeAt(0);
34
+ hash = (hash * 0x01000193) >>> 0;
35
+ }
36
+ return `SC-${hash.toString(16).toUpperCase().slice(0, 6)}`;
37
+ }
38
+
39
+ // ─────────────────────────────────────────────
40
+ // Parser
41
+ // ─────────────────────────────────────────────
42
+
43
+ /**
44
+ * Parse a .feature file into structured object
45
+ * @param {string} content - Raw file content
46
+ * @returns {{ feature: object, scenarios: object[], errors: string[] }}
47
+ */
48
+ function parseFeatureFile(content) {
49
+ const lines = content.split('\n');
50
+ const result = { feature: null, scenarios: [], errors: [] };
51
+
52
+ let currentScenario = null;
53
+ let currentStep = null;
54
+ let pendingTags = [];
55
+
56
+ for (let i = 0; i < lines.length; i++) {
57
+ const line = lines[i].trim();
58
+
59
+ // Skip blank lines and comments
60
+ if (!line || line.startsWith('#')) continue;
61
+
62
+ // Tags
63
+ if (line.startsWith('@')) {
64
+ const tags = line.split(/\s+/).filter(t => t.startsWith('@'));
65
+ pendingTags.push(...tags);
66
+ continue;
67
+ }
68
+
69
+ // Feature declaration
70
+ if (line.startsWith('Feature:')) {
71
+ result.feature = {
72
+ name: line.replace('Feature:', '').trim(),
73
+ tags: pendingTags.slice(),
74
+ lineNumber: i + 1
75
+ };
76
+ pendingTags = [];
77
+ continue;
78
+ }
79
+
80
+ // Background
81
+ if (line.startsWith('Background:')) {
82
+ currentScenario = { type: 'background', steps: [] };
83
+ pendingTags = [];
84
+ continue;
85
+ }
86
+
87
+ // Scenario
88
+ if (line.startsWith('Scenario:') || line.startsWith('Scenario Outline:')) {
89
+ if (currentScenario && currentScenario.type !== 'background') {
90
+ result.scenarios.push(currentScenario);
91
+ }
92
+ const scenarioName = line.replace(/^Scenario(?: Outline)?:/, '').trim();
93
+ const featureName = result.feature ? result.feature.name : 'unknown';
94
+ currentScenario = {
95
+ type: line.startsWith('Scenario Outline:') ? 'outline' : 'scenario',
96
+ name: scenarioName,
97
+ id: generateScenarioId(featureName, scenarioName),
98
+ tags: pendingTags.slice(),
99
+ steps: [],
100
+ lineNumber: i + 1
101
+ };
102
+ pendingTags = [];
103
+ currentStep = null;
104
+ continue;
105
+ }
106
+
107
+ // Steps
108
+ const stepMatch = line.match(/^(Given|When|Then|And|But)\s+(.+)$/);
109
+ if (stepMatch && currentScenario) {
110
+ const stepType = stepMatch[1];
111
+ // Resolve And/But to actual type based on previous step
112
+ let resolvedType = stepType;
113
+ if ((stepType === 'And' || stepType === 'But') && currentStep) {
114
+ resolvedType = currentStep.resolvedType;
115
+ } else if (stepType === 'And' || stepType === 'But') {
116
+ resolvedType = 'Given'; // fallback
117
+ }
118
+ currentStep = {
119
+ keyword: stepType,
120
+ resolvedType,
121
+ text: stepMatch[2],
122
+ lineNumber: i + 1
123
+ };
124
+ currentScenario.steps.push(currentStep);
125
+ continue;
126
+ }
127
+ }
128
+
129
+ // Push last scenario
130
+ if (currentScenario && currentScenario.type !== 'background') {
131
+ result.scenarios.push(currentScenario);
132
+ }
133
+
134
+ if (!result.feature) {
135
+ result.errors.push('No Feature: declaration found');
136
+ }
137
+
138
+ return result;
139
+ }
140
+
141
+ // ─────────────────────────────────────────────
142
+ // MoSCoW Validation
143
+ // ─────────────────────────────────────────────
144
+
145
+ const MOSCOW_TAGS = ['@must', '@should', '@could', '@wont'];
146
+ const TIER_TAGS = ['@mvp', '@medium', '@enterprise'];
147
+
148
+ /**
149
+ * Validate MoSCoW tags on a scenario
150
+ * @param {object} scenario
151
+ * @returns {{ valid: boolean, moscow: string|null, tier: string|null, issues: string[] }}
152
+ */
153
+ function validateMosCowTags(scenario) {
154
+ const issues = [];
155
+ const tags = scenario.tags || [];
156
+
157
+ const moscowTag = tags.find(t => MOSCOW_TAGS.includes(t));
158
+ const tierTag = tags.find(t => TIER_TAGS.includes(t));
159
+
160
+ if (!moscowTag) {
161
+ issues.push(`Scenario "${scenario.name}" missing MoSCoW tag (@must/@should/@could/@wont)`);
162
+ }
163
+
164
+ if (!tierTag && moscowTag !== '@wont') {
165
+ issues.push(`Scenario "${scenario.name}" missing tier tag (@mvp/@medium/@enterprise)`);
166
+ }
167
+
168
+ // Check consistency
169
+ if (moscowTag === '@must' && tierTag && tierTag !== '@mvp') {
170
+ issues.push(`Scenario "${scenario.name}": @must scenarios should be tagged @mvp (found ${tierTag})`);
171
+ }
172
+ if (moscowTag === '@could' && tierTag === '@mvp') {
173
+ issues.push(`Scenario "${scenario.name}": @could scenarios should not be tagged @mvp`);
174
+ }
175
+
176
+ return {
177
+ valid: issues.length === 0,
178
+ moscow: moscowTag || null,
179
+ tier: tierTag || null,
180
+ issues
181
+ };
182
+ }
183
+
184
+ /**
185
+ * Count scenarios by MoSCoW priority
186
+ * @param {object[]} scenarios
187
+ * @returns {{ must: number, should: number, could: number, wont: number, untagged: number }}
188
+ */
189
+ function countByMosCow(scenarios) {
190
+ const counts = { must: 0, should: 0, could: 0, wont: 0, untagged: 0 };
191
+ for (const s of scenarios) {
192
+ const tag = (s.tags || []).find(t => MOSCOW_TAGS.includes(t));
193
+ if (!tag) counts.untagged++;
194
+ else counts[tag.replace('@', '')]++;
195
+ }
196
+ return counts;
197
+ }
198
+
199
+ // ─────────────────────────────────────────────
200
+ // INVEST Validation
201
+ // ─────────────────────────────────────────────
202
+
203
+ /**
204
+ * Validate INVEST criteria for a Feature + its scenarios
205
+ * @param {object} parsed - Result from parseFeatureFile
206
+ * @returns {{ score: number, max: number, dimensions: object[], passed: boolean }}
207
+ */
208
+ function validateINVEST(parsed) {
209
+ const dimensions = [];
210
+
211
+ // Independent — check for explicit dependency language
212
+ const dependencyWords = ['requires', 'depends on', 'after', 'before completing'];
213
+ const featureName = parsed.feature ? parsed.feature.name.toLowerCase() : '';
214
+ const hasDependencyLanguage = dependencyWords.some(w => featureName.includes(w));
215
+ dimensions.push({
216
+ dimension: 'Independent',
217
+ letter: 'I',
218
+ passed: !hasDependencyLanguage,
219
+ note: hasDependencyLanguage
220
+ ? 'Feature name suggests hard dependency — split or remove dependency language'
221
+ : 'No hard dependency language detected in Feature name'
222
+ });
223
+
224
+ // Negotiable — check that Then clauses don't over-specify implementation
225
+ const implementationWords = ['using react', 'via postgres', 'with redis', 'using jwt', 'via sendgrid'];
226
+ let thenClauses = [];
227
+ for (const s of parsed.scenarios) {
228
+ thenClauses.push(...s.steps.filter(st => st.resolvedType === 'Then').map(st => st.text.toLowerCase()));
229
+ }
230
+ const overSpecified = thenClauses.some(t => implementationWords.some(w => t.includes(w)));
231
+ dimensions.push({
232
+ dimension: 'Negotiable',
233
+ letter: 'N',
234
+ passed: !overSpecified,
235
+ note: overSpecified
236
+ ? 'Then clauses reference specific implementation technology — keep outcomes technology-agnostic'
237
+ : 'Then clauses describe outcomes, not implementation'
238
+ });
239
+
240
+ // Valuable — check Feature has "As a... I want... So that..." structure
241
+ const hasAsA = parsed.feature && /as a/i.test(parsed.feature.name);
242
+ dimensions.push({
243
+ dimension: 'Valuable',
244
+ letter: 'V',
245
+ passed: !!parsed.feature, // Feature declaration exists
246
+ note: parsed.feature
247
+ ? (hasAsA ? 'Feature has user-value statement' : 'Feature exists but consider adding "As a... I want... So that..."')
248
+ : 'No Feature declaration found'
249
+ });
250
+
251
+ // Estimable — check sufficient detail in steps
252
+ const avgStepsPerScenario = parsed.scenarios.length > 0
253
+ ? parsed.scenarios.reduce((sum, s) => sum + s.steps.length, 0) / parsed.scenarios.length
254
+ : 0;
255
+ const estimable = avgStepsPerScenario >= 2 && avgStepsPerScenario <= 10;
256
+ dimensions.push({
257
+ dimension: 'Estimable',
258
+ letter: 'E',
259
+ passed: estimable,
260
+ note: avgStepsPerScenario < 2
261
+ ? 'Scenarios have too few steps — add more detail for estimability'
262
+ : avgStepsPerScenario > 10
263
+ ? 'Scenarios are overly complex — split into smaller scenarios'
264
+ : `Average ${avgStepsPerScenario.toFixed(1)} steps per scenario — good estimability`
265
+ });
266
+
267
+ // Small — count @must scenarios (should be <= 8 for one phase)
268
+ const mustCount = parsed.scenarios.filter(s => (s.tags || []).includes('@must')).length;
269
+ const small = mustCount <= 8;
270
+ dimensions.push({
271
+ dimension: 'Small',
272
+ letter: 'S',
273
+ passed: small,
274
+ note: small
275
+ ? `${mustCount} @must scenarios — fits in one phase`
276
+ : `${mustCount} @must scenarios — consider splitting Feature across phases (max 8 recommended)`
277
+ });
278
+
279
+ // Testable — check all Then clauses have specific assertions
280
+ const vagueWords = ['should work', 'is correct', 'looks good', 'is happy', 'functions properly'];
281
+ const vagueThens = thenClauses.filter(t => vagueWords.some(w => t.includes(w)));
282
+ dimensions.push({
283
+ dimension: 'Testable',
284
+ letter: 'T',
285
+ passed: vagueThens.length === 0,
286
+ note: vagueThens.length === 0
287
+ ? 'All Then clauses have specific, testable assertions'
288
+ : `${vagueThens.length} vague Then clause(s) found — replace with specific assertions`
289
+ });
290
+
291
+ const score = dimensions.filter(d => d.passed).length;
292
+ return {
293
+ score,
294
+ max: dimensions.length,
295
+ dimensions,
296
+ passed: score === dimensions.length
297
+ };
298
+ }
299
+
300
+ // ─────────────────────────────────────────────
301
+ // Structural Validation
302
+ // ─────────────────────────────────────────────
303
+
304
+ /**
305
+ * Validate Given/When/Then structure of scenarios
306
+ * @param {object[]} scenarios
307
+ * @returns {{ valid: boolean, issues: string[] }}
308
+ */
309
+ function validateStructure(scenarios) {
310
+ const issues = [];
311
+
312
+ for (const scenario of scenarios) {
313
+ if (scenario.type === 'background') continue;
314
+
315
+ const steps = scenario.steps;
316
+ if (steps.length === 0) {
317
+ issues.push(`Scenario "${scenario.name}" has no steps`);
318
+ continue;
319
+ }
320
+
321
+ const hasGiven = steps.some(s => s.resolvedType === 'Given');
322
+ const hasWhen = steps.some(s => s.resolvedType === 'When');
323
+ const hasThen = steps.some(s => s.resolvedType === 'Then');
324
+
325
+ if (!hasWhen) {
326
+ issues.push(`Scenario "${scenario.name}": missing When step (the action being tested)`);
327
+ }
328
+ if (!hasThen) {
329
+ issues.push(`Scenario "${scenario.name}": missing Then step (the expected outcome)`);
330
+ }
331
+
332
+ // Check order: Given before When, When before Then
333
+ let givenIndex = steps.findLastIndex(s => s.resolvedType === 'Given');
334
+ let whenIndex = steps.findIndex(s => s.resolvedType === 'When');
335
+ let thenIndex = steps.findIndex(s => s.resolvedType === 'Then');
336
+
337
+ if (hasWhen && hasThen && whenIndex > thenIndex) {
338
+ issues.push(`Scenario "${scenario.name}": When step appears after Then step`);
339
+ }
340
+ if (hasGiven && hasWhen && givenIndex > whenIndex) {
341
+ // Only warn if Given appears much later
342
+ }
343
+ }
344
+
345
+ return { valid: issues.length === 0, issues };
346
+ }
347
+
348
+ // ─────────────────────────────────────────────
349
+ // Main Validation Entry Point
350
+ // ─────────────────────────────────────────────
351
+
352
+ /**
353
+ * Validate a single .feature file
354
+ * @param {string} filePath - Path to .feature file
355
+ * @returns {{ file: string, valid: boolean, invest: object, moscow: object, structure: object, summary: string }}
356
+ */
357
+ function validateFeatureFile(filePath) {
358
+ if (!fs.existsSync(filePath)) {
359
+ return {
360
+ file: filePath,
361
+ valid: false,
362
+ error: `File not found: ${filePath}`,
363
+ invest: null,
364
+ moscow: null,
365
+ structure: null,
366
+ summary: 'FILE_NOT_FOUND'
367
+ };
368
+ }
369
+
370
+ const content = fs.readFileSync(filePath, 'utf8');
371
+ const parsed = parseFeatureFile(content);
372
+
373
+ // Structural validation
374
+ const structure = validateStructure(parsed.scenarios);
375
+
376
+ // INVEST validation
377
+ const invest = validateINVEST(parsed);
378
+
379
+ // MoSCoW validation (per scenario)
380
+ const moscowResults = parsed.scenarios.map(s => validateMosCowTags(s));
381
+ const moscowIssues = moscowResults.flatMap(r => r.issues);
382
+ const moscow = {
383
+ valid: moscowIssues.length === 0,
384
+ issues: moscowIssues,
385
+ counts: countByMosCow(parsed.scenarios),
386
+ scenarios: moscowResults
387
+ };
388
+
389
+ const allIssues = [
390
+ ...parsed.errors,
391
+ ...structure.issues,
392
+ ...moscowIssues
393
+ ];
394
+
395
+ const valid = allIssues.length === 0 && invest.score >= 5; // Must pass at least 5/6 INVEST
396
+
397
+ return {
398
+ file: filePath,
399
+ valid,
400
+ invest,
401
+ moscow,
402
+ structure,
403
+ parseErrors: parsed.errors,
404
+ scenarioCount: parsed.scenarios.length,
405
+ summary: valid ? 'PASS' : `FAIL (${allIssues.length} issues, INVEST ${invest.score}/${invest.max})`
406
+ };
407
+ }
408
+
409
+ /**
410
+ * Validate all .feature files in a directory
411
+ * @param {string} dirPath - Directory to scan
412
+ * @param {object} options - { outputTraceability: boolean, cwd: string }
413
+ * @returns {{ valid: boolean, files: object[], totalScenarios: number, moscowCounts: object }}
414
+ */
415
+ function validateFeatureDirectory(dirPath, options = {}) {
416
+ const featureFiles = findFeatureFiles(dirPath);
417
+
418
+ if (featureFiles.length === 0) {
419
+ return {
420
+ valid: false,
421
+ files: [],
422
+ totalScenarios: 0,
423
+ moscowCounts: { must: 0, should: 0, could: 0, wont: 0, untagged: 0 },
424
+ error: `No .feature files found in ${dirPath}`
425
+ };
426
+ }
427
+
428
+ const results = featureFiles.map(f => validateFeatureFile(f));
429
+ const totalMoscow = { must: 0, should: 0, could: 0, wont: 0, untagged: 0 };
430
+
431
+ let totalScenarios = 0;
432
+ const allScenarios = []; // for traceability matrix
433
+
434
+ for (const r of results) {
435
+ totalScenarios += r.scenarioCount || 0;
436
+ if (r.moscow && r.moscow.counts) {
437
+ for (const [key, val] of Object.entries(r.moscow.counts)) {
438
+ totalMoscow[key] = (totalMoscow[key] || 0) + val;
439
+ }
440
+ }
441
+ // Collect scenario data for traceability
442
+ if (r.moscow && r.moscow.scenarios) {
443
+ const featureBase = path.basename(r.file);
444
+ r.moscow.scenarios.forEach((s, idx) => {
445
+ allScenarios.push({
446
+ id: s.id || `SC-${idx}`,
447
+ name: s.scenario ? s.scenario.name : `Scenario ${idx + 1}`,
448
+ moscow: s.moscow || 'untagged',
449
+ file: featureBase,
450
+ status: 'not-run'
451
+ });
452
+ });
453
+ }
454
+ }
455
+
456
+ // Write traceability matrix if requested or by default when .planning/ exists
457
+ const cwd = options.cwd || process.cwd();
458
+ const planningDir = path.join(cwd, '.planning');
459
+ if (allScenarios.length > 0 && fs.existsSync(planningDir)) {
460
+ try {
461
+ const matrixLines = [
462
+ '# BDD Traceability Matrix',
463
+ '',
464
+ '| ID | Scenario | MoSCoW | Feature File | Status |',
465
+ '|-----|----------|--------|-------------|--------|'
466
+ ];
467
+ for (const s of allScenarios) {
468
+ matrixLines.push(`| ${s.id} | ${s.name} | ${s.moscow} | ${s.file} | ⬜ ${s.status} |`);
469
+ }
470
+ matrixLines.push('');
471
+ matrixLines.push(`*Generated: ${new Date().toISOString()}*`);
472
+ fs.writeFileSync(
473
+ path.join(planningDir, 'bdd-traceability.md'),
474
+ matrixLines.join('\n'),
475
+ 'utf8'
476
+ );
477
+ } catch {
478
+ // Non-fatal — traceability matrix is best-effort
479
+ }
480
+ }
481
+
482
+ return {
483
+ valid: results.every(r => r.valid),
484
+ files: results,
485
+ totalScenarios,
486
+ moscowCounts: totalMoscow
487
+ };
488
+ }
489
+
490
+ /**
491
+ * Recursively find all .feature files
492
+ * @param {string} dirPath
493
+ * @returns {string[]}
494
+ */
495
+ function findFeatureFiles(dirPath) {
496
+ if (!fs.existsSync(dirPath)) return [];
497
+
498
+ const entries = fs.readdirSync(dirPath, { withFileTypes: true });
499
+ const files = [];
500
+
501
+ for (const entry of entries) {
502
+ const fullPath = path.join(dirPath, entry.name);
503
+ if (entry.isDirectory()) {
504
+ files.push(...findFeatureFiles(fullPath));
505
+ } else if (entry.name.endsWith('.feature')) {
506
+ files.push(fullPath);
507
+ }
508
+ }
509
+
510
+ return files;
511
+ }
512
+
513
+ /**
514
+ * Format validation report as human-readable markdown
515
+ * @param {object} result - From validateFeatureDirectory or validateFeatureFile
516
+ * @returns {string}
517
+ */
518
+ function formatReport(result) {
519
+ const lines = [];
520
+
521
+ if (result.files) {
522
+ // Directory report
523
+ lines.push(`## BDD Validation Report`);
524
+ lines.push(`**Status:** ${result.valid ? '✓ PASS' : '✗ FAIL'}`);
525
+ lines.push(`**Files:** ${result.files.length} | **Scenarios:** ${result.totalScenarios}`);
526
+ lines.push('');
527
+ lines.push('### MoSCoW Distribution');
528
+ lines.push(`| Priority | Count |`);
529
+ lines.push(`|----------|-------|`);
530
+ for (const [key, val] of Object.entries(result.moscowCounts)) {
531
+ lines.push(`| @${key} | ${val} |`);
532
+ }
533
+ lines.push('');
534
+ lines.push('### File Results');
535
+ for (const f of result.files) {
536
+ const icon = f.valid ? '✓' : '✗';
537
+ lines.push(`- ${icon} \`${path.basename(f.file)}\` — ${f.summary}`);
538
+ if (!f.valid && f.parseErrors) {
539
+ for (const e of f.parseErrors) lines.push(` - **Parse Error:** ${e}`);
540
+ }
541
+ if (f.structure && f.structure.issues.length > 0) {
542
+ for (const e of f.structure.issues) lines.push(` - **Structure:** ${e}`);
543
+ }
544
+ if (f.moscow && f.moscow.issues.length > 0) {
545
+ for (const e of f.moscow.issues.slice(0, 3)) lines.push(` - **MoSCoW:** ${e}`);
546
+ }
547
+ }
548
+ } else {
549
+ // Single file report
550
+ lines.push(`## BDD Validation: ${path.basename(result.file)}`);
551
+ lines.push(`**Status:** ${result.valid ? '✓ PASS' : '✗ FAIL'}`);
552
+ lines.push(`**Scenarios:** ${result.scenarioCount}`);
553
+
554
+ if (result.invest) {
555
+ lines.push('');
556
+ lines.push('### INVEST Score');
557
+ lines.push(`**${result.invest.score}/${result.invest.max}** dimensions pass`);
558
+ for (const d of result.invest.dimensions) {
559
+ lines.push(`- ${d.passed ? '✓' : '✗'} **${d.letter}** — ${d.dimension}: ${d.note}`);
560
+ }
561
+ }
562
+ }
563
+
564
+ return lines.join('\n');
565
+ }
566
+
567
+ // ─────────────────────────────────────────────
568
+ // CLI Interface
569
+ // ─────────────────────────────────────────────
570
+
571
+ if (require.main === module) {
572
+ const args = process.argv.slice(2);
573
+ const cmd = args[0];
574
+ const target = args[1];
575
+
576
+ if (!cmd || !target) {
577
+ console.error('Usage: bdd-validator.cjs <validate-file|validate-dir|count-moscow> <path>');
578
+ process.exit(1);
579
+ }
580
+
581
+ try {
582
+ if (cmd === 'validate-file') {
583
+ const result = validateFeatureFile(target);
584
+ if (args.includes('--json')) {
585
+ console.log(JSON.stringify(result, null, 2));
586
+ } else {
587
+ console.log(formatReport(result));
588
+ process.exit(result.valid ? 0 : 1);
589
+ }
590
+ } else if (cmd === 'validate-dir') {
591
+ const result = validateFeatureDirectory(target);
592
+ if (args.includes('--json')) {
593
+ console.log(JSON.stringify(result, null, 2));
594
+ } else {
595
+ console.log(formatReport(result));
596
+ process.exit(result.valid ? 0 : 1);
597
+ }
598
+ } else if (cmd === 'count-moscow') {
599
+ const result = validateFeatureDirectory(target);
600
+ console.log(JSON.stringify(result.moscowCounts, null, 2));
601
+ } else {
602
+ console.error(`Unknown command: ${cmd}`);
603
+ process.exit(1);
604
+ }
605
+ } catch (err) {
606
+ console.error(`Error: ${err.message}`);
607
+ process.exit(1);
608
+ }
609
+ }
610
+
611
+ module.exports = {
612
+ parseFeatureFile,
613
+ validateFeatureFile,
614
+ validateFeatureDirectory,
615
+ validateMosCowTags,
616
+ validateINVEST,
617
+ validateStructure,
618
+ countByMosCow,
619
+ findFeatureFiles,
620
+ formatReport,
621
+ generateScenarioId
622
+ };