@polymorphism-tech/morph-spec 8.47.0 → 9.1.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 (377) hide show
  1. package/CLAUDE.md +51 -43
  2. package/README.md +39 -38
  3. package/bin/morph-spec.js +41 -113
  4. package/bin/validate.js +1 -147
  5. package/claude-plugin.json +1 -1
  6. package/docs/CHEATSHEET.md +1 -1
  7. package/docs/QUICKSTART.md +1 -1
  8. package/framework/CLAUDE.md +41 -101
  9. package/framework/MORPH.md +95 -252
  10. package/framework/agents.json +86 -21
  11. package/framework/commands/morph-apply.md +5 -28
  12. package/framework/commands/morph-archive.md +1 -1
  13. package/framework/commands/morph-hotfix.md +8 -8
  14. package/framework/commands/morph-infra.md +1 -1
  15. package/framework/commands/morph-preflight.md +2 -3
  16. package/framework/commands/morph-status.md +4 -4
  17. package/framework/docs/CLI.md +574 -286
  18. package/framework/evals/review-checklist.md +82 -41
  19. package/framework/hooks/README.md +10 -11
  20. package/framework/hooks/claude-code/core/block-destructive-worktree-removal.js +6 -49
  21. package/framework/hooks/claude-code/core/block-raw-worktree-add.js +2 -35
  22. package/framework/hooks/claude-code/core/chain.js +4 -65
  23. package/framework/hooks/claude-code/core/comment-guard.js +3 -17
  24. package/framework/hooks/claude-code/core/denylist.json +33 -3
  25. package/framework/hooks/claude-code/core/enforce-phase-writes.js +15 -63
  26. package/framework/hooks/claude-code/core/gate-guard.js +9 -78
  27. package/framework/hooks/claude-code/core/graph-refresh-kick.js +3 -24
  28. package/framework/hooks/claude-code/core/heavy-gate.js +3 -21
  29. package/framework/hooks/claude-code/core/heavy-release.js +2 -13
  30. package/framework/hooks/claude-code/core/loop-detect.js +2 -31
  31. package/framework/hooks/claude-code/core/pre-bash-denylist.js +36 -2
  32. package/framework/hooks/claude-code/core/protect-spec-files.js +25 -297
  33. package/framework/hooks/claude-code/core/state-sync.js +15 -85
  34. package/framework/hooks/claude-code/core/telemetry-log.js +15 -96
  35. package/framework/hooks/claude-code/core/validate-completion.js +37 -147
  36. package/framework/hooks/claude-code/core/warn-bash-source-write.js +23 -0
  37. package/framework/hooks/claude-code/core/worktree-context.js +15 -114
  38. package/framework/hooks/claude-code/core/worktree-create.js +6 -70
  39. package/framework/hooks/claude-code/core/worktree-env-inject.js +5 -56
  40. package/framework/hooks/claude-code/core/worktree-remove.js +5 -31
  41. package/framework/hooks/claude-code/statusline.py +55 -41
  42. package/framework/hooks/package.json +3 -0
  43. package/framework/hooks/shared/activity-logger.js +9 -89
  44. package/framework/hooks/shared/agent-reap.js +2 -17
  45. package/framework/hooks/shared/build-check.js +11 -76
  46. package/framework/hooks/shared/claim-registry.js +9 -119
  47. package/framework/hooks/shared/command-tokens.js +168 -44
  48. package/framework/hooks/shared/comment-rules.js +5 -20
  49. package/framework/hooks/shared/comment-scanner.js +11 -43
  50. package/framework/hooks/shared/comment-stripper.js +11 -85
  51. package/framework/hooks/shared/e2e-check.js +0 -0
  52. package/framework/hooks/shared/errata.js +59 -0
  53. package/framework/hooks/shared/feature-locator.js +11 -95
  54. package/framework/hooks/shared/frontend-locations.js +4 -0
  55. package/framework/hooks/shared/gate-question.js +12 -0
  56. package/framework/hooks/shared/git-exclude.js +3 -20
  57. package/framework/hooks/shared/git-paths.js +8 -104
  58. package/framework/hooks/shared/graph-freshness.js +22 -166
  59. package/framework/hooks/shared/graph-refresh.js +21 -149
  60. package/framework/hooks/shared/heavy-command.js +4 -12
  61. package/framework/hooks/shared/hook-response.js +8 -83
  62. package/framework/hooks/shared/morph-cli.js +8 -52
  63. package/framework/hooks/shared/payload-utils.js +10 -132
  64. package/framework/hooks/shared/phase-utils.js +14 -72
  65. package/framework/hooks/shared/redact-secrets.js +4 -15
  66. package/framework/hooks/shared/reparse-scan.js +6 -66
  67. package/framework/hooks/shared/slice-progress.js +99 -0
  68. package/framework/hooks/shared/source-files.js +4 -17
  69. package/framework/hooks/shared/spawn-command.js +23 -0
  70. package/framework/hooks/shared/state-reader.js +51 -247
  71. package/framework/hooks/shared/state-writer.js +9 -54
  72. package/framework/hooks/shared/stdin-reader.js +1 -11
  73. package/framework/hooks/shared/telemetry-logger.js +7 -67
  74. package/framework/hooks/shared/transcript-usage.js +7 -29
  75. package/framework/hooks/shared/worktree-root.js +7 -29
  76. package/framework/mcp/registry.json +3 -4
  77. package/framework/phases.json +23 -39
  78. package/framework/rules/csharp-standards.md +1 -1
  79. package/framework/rules/frontend-standards.md +1 -0
  80. package/framework/rules/hangfire-standards.md +1 -1
  81. package/framework/rules/neon-standards.md +1 -1
  82. package/framework/schemas/config.schema.json +14 -9
  83. package/framework/schemas/feature.schema.json +15 -50
  84. package/framework/skills/README.md +3 -4
  85. package/framework/skills/core/create-umbraco-lp/SKILL.md +1 -1
  86. package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +1 -1
  87. package/framework/skills/core/morph-apply/SKILL.md +149 -222
  88. package/framework/skills/core/morph-hotfix/SKILL.md +33 -42
  89. package/framework/skills/core/morph-machine/SKILL.md +2 -2
  90. package/framework/skills/core/morph-media/SKILL.md +1 -1
  91. package/framework/skills/core/morph-plan/SKILL.md +137 -299
  92. package/framework/skills/core/morph-review/SKILL.md +111 -136
  93. package/framework/skills/core/morph-standards/SKILL.md +27 -39
  94. package/framework/skills/core/morph-uiux/SKILL.md +94 -152
  95. package/framework/standards/STANDARDS.json +109 -1219
  96. package/framework/standards/STANDARDS.meta.json +1319 -0
  97. package/framework/standards/ai-agents/agent-archetypes.md +2 -2
  98. package/framework/standards/ai-agents/agent-session.md +1 -1
  99. package/framework/standards/ai-agents/conversational-agent-with-phases.md +4 -4
  100. package/framework/standards/ai-agents/durable-workflows-hitl.md +3 -3
  101. package/framework/standards/ai-agents/evals-with-cache.md +1 -2
  102. package/framework/standards/ai-agents/guardrails.md +1 -1
  103. package/framework/standards/ai-agents/multi-agent-patterns.md +2 -2
  104. package/framework/standards/ai-agents/observability-patterns.md +1 -1
  105. package/framework/standards/ai-agents/production.md +1 -1
  106. package/framework/standards/ai-agents/prompt-sources.md +2 -2
  107. package/framework/standards/ai-agents/providers/model-registry.md +1 -1
  108. package/framework/standards/ai-agents/rag-custom-pgvector.md +2 -2
  109. package/framework/standards/ai-agents/setup.md +3 -3
  110. package/framework/standards/ai-agents/sweet-spot.md +2 -2
  111. package/framework/standards/ai-agents/tool-adapters.md +0 -2
  112. package/framework/standards/ai-agents/vector-data-extensions.md +4 -4
  113. package/framework/standards/ai-agents/workflows.md +1 -1
  114. package/framework/standards/architecture/code-comments.md +7 -7
  115. package/framework/standards/architecture/vertical-slice/vertical-slice.md +1 -1
  116. package/framework/standards/backend/api/validation.md +1 -1
  117. package/framework/standards/backend/dotnet/vsa-handler-patterns.md +5 -5
  118. package/framework/standards/backend/integrations/hangfire/hangfire-jobs.md +1 -1
  119. package/framework/standards/backend/integrations/neon-auth/neon-auth.md +2 -2
  120. package/framework/standards/data/nosql/cache/redis.md +1 -1
  121. package/framework/standards/data/nosql/cosmos-db.md +3 -3
  122. package/framework/standards/frontend/admin-app/forms.md +1 -1
  123. package/framework/standards/frontend/design-system/art-direction.md +88 -0
  124. package/framework/standards/frontend/design-system/premium-finish.md +77 -2
  125. package/framework/standards/infrastructure/neon/neon-pgvector.md +5 -5
  126. package/framework/standards/integration/mcp/mcp-tools.md +7 -7
  127. package/framework/templates/CLAUDE.md.core.template +3 -3
  128. package/framework/templates/README.md +2 -3
  129. package/framework/templates/REGISTRY.json +15 -179
  130. package/framework/templates/docs/plan.md +72 -0
  131. package/framework/templates/docs/ui-mockups.md +4 -0
  132. package/framework/templates/dotnet/ai-kit/README.md +1 -9
  133. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Morph.AiKit.Tests.csproj +0 -10
  134. package/framework/templates/meta-prompts/squad-leaders/backend-squad.md +1 -1
  135. package/framework/templates/meta-prompts/squad-leaders/frontend-squad.md +1 -1
  136. package/framework/templates/meta-prompts/squad-leaders/squad-leader.md +1 -1
  137. package/framework/templates/meta-prompts/validators/pre-commit-validator.md +3 -4
  138. package/package.json +1 -1
  139. package/src/commands/context/context-refresh.js +0 -16
  140. package/src/commands/dashboard/dashboard.js +2 -8
  141. package/src/commands/e2e/e2e.js +18 -105
  142. package/src/commands/graph/affected.js +0 -14
  143. package/src/commands/graph/build.js +11 -50
  144. package/src/commands/graph/explain.js +0 -16
  145. package/src/commands/graph/status.js +1 -12
  146. package/src/commands/mcp/mcp-setup.js +4 -62
  147. package/src/commands/observability/cost.js +17 -111
  148. package/src/commands/observability/telemetry.js +1 -14
  149. package/src/commands/project/branch.js +4 -45
  150. package/src/commands/project/claim.js +4 -48
  151. package/src/commands/project/doctor-procs.js +1 -1
  152. package/src/commands/project/doctor.js +53 -307
  153. package/src/commands/project/env.js +1 -27
  154. package/src/commands/project/fleet.js +6 -35
  155. package/src/commands/project/heavy.js +3 -3
  156. package/src/commands/project/init.js +8 -68
  157. package/src/commands/project/install-plugin-cmd.js +0 -1
  158. package/src/commands/project/setup-infra-cmd.js +2 -1
  159. package/src/commands/project/stack-scan.js +58 -0
  160. package/src/commands/project/standards.js +65 -72
  161. package/src/commands/project/status.js +28 -121
  162. package/src/commands/project/update.js +31 -223
  163. package/src/commands/project/worktree.js +109 -883
  164. package/src/commands/scaffold/scaffold.js +0 -25
  165. package/src/commands/state/advance.js +65 -120
  166. package/src/commands/state/approve.js +87 -229
  167. package/src/commands/state/archive.js +23 -139
  168. package/src/commands/state/auto-approval.js +65 -0
  169. package/src/commands/state/create.js +49 -109
  170. package/src/commands/state/delete.js +11 -63
  171. package/src/commands/state/finish.js +105 -494
  172. package/src/commands/state/gate-check.js +74 -183
  173. package/src/commands/state/gate-decision.js +8 -42
  174. package/src/commands/state/handoff.js +4 -31
  175. package/src/commands/state/list.js +21 -91
  176. package/src/commands/state/report-check.js +134 -338
  177. package/src/commands/state/retype.js +12 -28
  178. package/src/commands/validation/index.js +0 -3
  179. package/src/commands/validation/mutate.js +79 -19
  180. package/src/commands/validation/plan-check.js +81 -0
  181. package/src/commands/validation/validate-feature.js +52 -216
  182. package/src/commands/validation/verify.js +45 -160
  183. package/src/core/paths/output-schema.js +9 -155
  184. package/src/core/state/feature-identity.js +1 -25
  185. package/src/core/state/gate-order.js +2 -30
  186. package/src/core/state/lifecycle-audit.js +8 -100
  187. package/src/core/state/state-manager.js +32 -395
  188. package/src/core/templates/template-registry.js +2 -97
  189. package/src/core/templates/template-renderer.js +13 -93
  190. package/src/core/templates/template-seed.js +21 -0
  191. package/src/core/work-types.js +10 -45
  192. package/src/dashboard/app/App.jsx +1 -8
  193. package/src/dashboard/app/components/ActivityFeed.jsx +2 -8
  194. package/src/dashboard/app/components/CommandPalette.jsx +4 -11
  195. package/src/dashboard/app/components/DetailPanel.jsx +1 -5
  196. package/src/dashboard/app/components/EntityLink.jsx +0 -12
  197. package/src/dashboard/app/components/ErrorBoundary.jsx +1 -8
  198. package/src/dashboard/app/components/KnowledgeMarkdown.jsx +0 -11
  199. package/src/dashboard/app/components/Markdown.jsx +0 -9
  200. package/src/dashboard/app/components/PhaseRail.jsx +0 -11
  201. package/src/dashboard/app/components/SearchFilter.jsx +0 -4
  202. package/src/dashboard/app/components/Sidebar.jsx +0 -5
  203. package/src/dashboard/app/hooks/useMorphQuery.js +1 -11
  204. package/src/dashboard/app/hooks/usePref.js +1 -5
  205. package/src/dashboard/app/lib/format.js +0 -2
  206. package/src/dashboard/app/lib/phases.js +3 -9
  207. package/src/dashboard/app/lib/project.js +0 -12
  208. package/src/dashboard/app/pages/Activity.jsx +1 -1
  209. package/src/dashboard/app/pages/AgentMap.jsx +1 -8
  210. package/src/dashboard/app/pages/FeatureDetail.jsx +139 -133
  211. package/src/dashboard/app/pages/Features.jsx +9 -16
  212. package/src/dashboard/app/pages/Guide.jsx +2 -3
  213. package/src/dashboard/app/pages/HookInspector.jsx +2 -23
  214. package/src/dashboard/app/pages/PhasePipeline.jsx +3 -7
  215. package/src/dashboard/app/pages/Portfolio.jsx +0 -1
  216. package/src/dashboard/app/pages/RulesMap.jsx +1 -16
  217. package/src/dashboard/app/pages/SkillsLibrary.jsx +1 -16
  218. package/src/dashboard/app/pages/Standards.jsx +2 -17
  219. package/src/dashboard/scanner/activity-scanner.js +14 -83
  220. package/src/dashboard/scanner/agents-scanner.js +1 -23
  221. package/src/dashboard/scanner/docs-scanner.js +1 -7
  222. package/src/dashboard/scanner/feature-status.js +2 -5
  223. package/src/dashboard/scanner/features-scanner.js +92 -113
  224. package/src/dashboard/scanner/hooks-scanner.js +5 -28
  225. package/src/dashboard/scanner/index.js +0 -4
  226. package/src/dashboard/scanner/portfolio.js +5 -29
  227. package/src/dashboard/scanner/project-api.js +8 -54
  228. package/src/dashboard/scanner/rules-scanner.js +1 -2
  229. package/src/dashboard/scanner/skills-scanner.js +2 -12
  230. package/src/dashboard/scanner/standards-scanner.js +4 -7
  231. package/src/dashboard/vite-plugin-morph-data.js +5 -19
  232. package/src/lib/ai/ai-checks.js +7 -52
  233. package/src/lib/ai/ai-project-scan.js +14 -108
  234. package/src/lib/ai/code-audit.js +7 -63
  235. package/src/lib/ai/pin-drift.js +8 -51
  236. package/src/lib/ai/registry-audit.js +11 -90
  237. package/src/lib/ci/ci-verify.js +14 -13
  238. package/src/lib/ci/github-ci.js +9 -37
  239. package/src/lib/ci/workflow-scaffold.js +2 -10
  240. package/src/lib/detectors/claude-config-detector.js +13 -74
  241. package/src/lib/detectors/index.js +8 -59
  242. package/src/lib/e2e/compose-env.js +5 -66
  243. package/src/lib/e2e/stack-teardown.js +6 -40
  244. package/src/lib/generators/architecture-scanner.js +18 -156
  245. package/src/lib/generators/handoff-generator.js +37 -112
  246. package/src/lib/generators/scaffold-generator.js +5 -101
  247. package/src/lib/generators/settings-generator.js +11 -122
  248. package/src/lib/git/branch-classifier.js +11 -84
  249. package/src/lib/git/default-branch.js +14 -169
  250. package/src/lib/git/evidence-staleness.js +2 -1
  251. package/src/lib/git/gh.js +2 -17
  252. package/src/lib/git/pending-prs.js +6 -37
  253. package/src/lib/git/pr-files.js +3 -28
  254. package/src/lib/git/pr-index.js +3 -34
  255. package/src/lib/git/tree-fingerprint.js +17 -17
  256. package/src/lib/git/tree-state.js +6 -33
  257. package/src/lib/graph/god-nodes.js +2 -22
  258. package/src/lib/graph/graph-health.js +3 -24
  259. package/src/lib/graph/graph-loader.js +5 -56
  260. package/src/lib/graph/graphify-runtime.js +7 -37
  261. package/src/lib/graph/graphifyignore.js +8 -74
  262. package/src/lib/graph/sanitize.js +16 -136
  263. package/src/lib/heavy/cleanup.js +2 -6
  264. package/src/lib/installers/effects.js +8 -93
  265. package/src/lib/installers/mcp-installer.js +9 -169
  266. package/src/lib/installers/memory-installer.js +8 -33
  267. package/src/lib/installers/templates-reconcile.js +5 -60
  268. package/src/lib/mutation/commands.js +3 -2
  269. package/src/lib/mutation/dotnet-introspection.js +281 -0
  270. package/src/lib/mutation/plan.js +14 -6
  271. package/src/lib/mutation/runner.js +1 -1
  272. package/src/lib/mutation/subset.js +47 -13
  273. package/src/lib/plan/artifact-vocabulary.js +40 -0
  274. package/src/lib/plan/checks.js +208 -0
  275. package/src/lib/plan/plan-parser.js +204 -0
  276. package/src/lib/plan/plan-template.js +18 -0
  277. package/src/lib/projects/registry.js +7 -44
  278. package/src/lib/review-reports.js +204 -0
  279. package/src/lib/risk-detector.js +131 -274
  280. package/src/lib/specs/adr-index.js +38 -85
  281. package/src/lib/specs/design-seed.js +18 -0
  282. package/src/lib/specs/frontmatter.js +31 -0
  283. package/src/lib/specs/gfm-table.js +11 -0
  284. package/src/lib/specs/revocations.js +31 -102
  285. package/src/lib/specs/screen-checklist.js +56 -132
  286. package/src/lib/stack/dotnet-build-ignores.js +43 -0
  287. package/src/lib/stack/stack-profile.js +1 -28
  288. package/src/lib/stack/stack-scan.js +260 -0
  289. package/src/lib/stack-filter.js +4 -27
  290. package/src/lib/standards/registry.js +37 -0
  291. package/src/lib/standards/sections.js +47 -0
  292. package/src/lib/standards/verified-against.js +19 -191
  293. package/src/lib/tasks/eval-node.js +23 -179
  294. package/src/lib/tasks/linux-runner.js +18 -142
  295. package/src/lib/tasks/test-nodes.js +43 -313
  296. package/src/lib/tasks/test-runner.js +107 -447
  297. package/src/lib/telemetry/metrics.js +13 -103
  298. package/src/lib/telemetry/telemetry-reader.js +13 -238
  299. package/src/lib/telemetry/verify-event.js +4 -34
  300. package/src/lib/validators/code-budget/code-budget-validator.js +4 -44
  301. package/src/lib/validators/code-budget/index.js +0 -7
  302. package/src/lib/validators/contracts/contract-compliance-validator.js +4 -26
  303. package/src/lib/validators/contracts/index.js +0 -6
  304. package/src/lib/validators/nextjs/index.js +0 -5
  305. package/src/lib/validators/nextjs/next-component-validator.js +4 -81
  306. package/src/lib/validators/packages/index.js +0 -6
  307. package/src/lib/validators/packages/package-validator.js +6 -56
  308. package/src/lib/validators/reuse/index.js +0 -8
  309. package/src/lib/validators/reuse/reuse-validator.js +24 -137
  310. package/src/lib/validators/reuse/symbol-scanner.js +6 -62
  311. package/src/lib/validators/traceability-check.js +54 -156
  312. package/src/lib/validators/validation-runner.js +20 -125
  313. package/src/lib/validators/verify-runner.js +152 -575
  314. package/src/lib/work-type-detector.js +5 -67
  315. package/src/lib/worktree/docker-audit.js +17 -137
  316. package/src/lib/worktree/env-allocator.js +10 -98
  317. package/src/lib/worktree/feature-truth.js +12 -115
  318. package/src/lib/worktree/finish-backup.js +32 -242
  319. package/src/lib/worktree/fleet-scanner.js +31 -126
  320. package/src/lib/worktree/ignored-triage.js +15 -91
  321. package/src/lib/worktree/vscode-settings.js +2 -3
  322. package/src/lib/worktree/worktree-audit.js +21 -213
  323. package/src/lib/worktree/worktree-include.js +3 -5
  324. package/src/scripts/global-install.js +3 -9
  325. package/src/scripts/install-plugin.js +1 -13
  326. package/src/scripts/setup-infra.js +17 -138
  327. package/src/utils/agents-installer.js +92 -272
  328. package/src/utils/banner.js +4 -25
  329. package/src/utils/claude-md-injector.js +1 -37
  330. package/src/utils/claude-settings-manager.js +1 -87
  331. package/src/utils/file-copier.js +32 -90
  332. package/src/utils/hooks-installer.js +26 -296
  333. package/src/utils/skills-installer.js +4 -102
  334. package/src/utils/version-checker.js +4 -83
  335. package/framework/commands/morph-proposal.md +0 -57
  336. package/framework/evals/trace-schema.md +0 -67
  337. package/framework/hooks/claude-code/core/tasks-json-guard.js +0 -83
  338. package/framework/hooks/claude-code/core/trace-autogen.js +0 -277
  339. package/framework/hooks/git/pre-commit/specs.sh +0 -50
  340. package/framework/hooks/shared/tasks-validator.js +0 -111
  341. package/framework/rules/morph-task-tracking.md +0 -54
  342. package/framework/rules/morph-workflow.md +0 -244
  343. package/framework/schemas/tasks.schema.json +0 -79
  344. package/framework/skills/core/morph-brainstorming/SKILL.md +0 -161
  345. package/framework/skills/core/morph-implement/SKILL.md +0 -194
  346. package/framework/skills/core/morph-proposal/SKILL.md +0 -152
  347. package/framework/skills/core/morph-stack-scan/SKILL.md +0 -145
  348. package/framework/templates/docs/proposal.md +0 -192
  349. package/framework/templates/docs/spec.md +0 -57
  350. package/framework/templates/docs/ui-components.md +0 -52
  351. package/framework/templates/docs/ui-design-system.md +0 -76
  352. package/framework/templates/docs/ui-flows.md +0 -167
  353. package/framework/templates/examples/spec-examples.md +0 -91
  354. package/framework/templates/feature/decisions.md +0 -124
  355. package/framework/templates/feature/recap.md +0 -145
  356. package/framework/templates/tasks.template.json +0 -17
  357. package/src/commands/state/usecase.js +0 -143
  358. package/src/commands/tasks/dag.js +0 -181
  359. package/src/commands/utils/index.js +0 -3
  360. package/src/lib/agents/output-collector.js +0 -76
  361. package/src/lib/detectors/design-system-detector.js +0 -188
  362. package/src/lib/evaluator-report.js +0 -104
  363. package/src/lib/generators/design-system-generator.js +0 -508
  364. package/src/lib/specs/acervo-format.js +0 -70
  365. package/src/lib/specs/acervo-lifecycle.js +0 -264
  366. package/src/lib/specs/done-criteria-check.js +0 -200
  367. package/src/lib/specs/usecase-check.js +0 -301
  368. package/src/lib/specs/usecase-promoter.js +0 -301
  369. package/src/lib/specs/usecase-registry.js +0 -237
  370. package/src/lib/tasks/dispatch-planner.js +0 -319
  371. package/src/lib/tasks/dotnet-test-introspection.js +0 -632
  372. package/src/lib/tasks/mermaid-renderer.js +0 -132
  373. package/src/lib/tasks/task-classifier.js +0 -30
  374. package/src/lib/tasks/task-parser.js +0 -209
  375. package/src/lib/tasks/tasks-json-parser.js +0 -296
  376. package/src/lib/tasks/test-scope.js +0 -597
  377. package/src/utils/agent-id-mapper.js +0 -11
package/bin/morph-spec.js CHANGED
@@ -1,14 +1,5 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- /**
4
- * morph-spec CLI v5
5
- *
6
- * In v5 the CLI is the *installer* — Claude executes everything else
7
- * autonomously based on MORPH.md and the core skills. Commands here are
8
- * limited to actions that humans take or operations Claude needs help with
9
- * (scaffolding, validation, context refresh).
10
- */
11
-
12
3
  import { program } from 'commander';
13
4
  import chalk from 'chalk';
14
5
  import { fileURLToPath } from 'url';
@@ -16,14 +7,9 @@ import { dirname, join } from 'path';
16
7
  import { readFileSync } from 'fs';
17
8
  import { renderLogo, maybePrintFirstRunBanner } from '../src/utils/banner.js';
18
9
 
19
- // Command modules load on demand. Importing all ~45 of them eagerly cost
20
- // ~700 ms on EVERY invocation (init.js alone pulls inquirer: ~550 ms), paid
21
- // by each hook that shells out to the CLI and by each CLI spawn in the suite.
22
- // test/bin/command-modules-import.test.js imports every path below, so a module
23
- // that breaks at import time still fails the suite, not only its own command.
10
+ // Lazy: eager import of every command cost ~700 ms per invocation, paid by each hook that shells out.
24
11
  const lazy = (path, name) => async (...args) => (await import(path))[name](...args);
25
12
 
26
- // === Project / install ===
27
13
  const initCommand = lazy('../src/commands/project/init.js', 'initCommand');
28
14
  const setupInfraCommand = lazy('../src/commands/project/setup-infra-cmd.js', 'setupInfraCommand');
29
15
  const installPluginCommand = lazy('../src/commands/project/install-plugin-cmd.js', 'installPluginCommand');
@@ -45,8 +31,9 @@ const claimCommand = lazy('../src/commands/project/claim.js', 'claimCommand');
45
31
  const standardsCommand = lazy('../src/commands/project/standards.js', 'standardsCommand');
46
32
  const reportCheckCommand = lazy('../src/commands/state/report-check.js', 'reportCheckCommand');
47
33
  const scoutCheckCommand = lazy('../src/commands/validation/scout-check.js', 'scoutCheckCommand');
34
+ const planCheckCommand = lazy('../src/commands/validation/plan-check.js', 'planCheckCommand');
35
+ const stackScanCommand = lazy('../src/commands/project/stack-scan.js', 'stackScanCommand');
48
36
 
49
- // === Approval gates (human-only) ===
50
37
  const approveCommand = lazy('../src/commands/state/approve.js', 'approveCommand');
51
38
  const approvalStatusCommand = lazy('../src/commands/state/approve.js', 'approvalStatusCommand');
52
39
  const unapproveCommand = lazy('../src/commands/state/approve.js', 'unapproveCommand');
@@ -60,19 +47,12 @@ const retypeCommand = lazy('../src/commands/state/retype.js', 'retypeCommand');
60
47
  const handoffCommand = lazy('../src/commands/state/handoff.js', 'handoffCommand');
61
48
  const listCommand = lazy('../src/commands/state/list.js', 'listCommand');
62
49
  const deleteCommand = lazy('../src/commands/state/delete.js', 'deleteCommand');
63
- const usecaseReserveCommand = lazy('../src/commands/state/usecase.js', 'usecaseReserveCommand');
64
- const usecaseCheckCommand = lazy('../src/commands/state/usecase.js', 'usecaseCheckCommand');
65
-
66
- // === Tasks ===
67
- const dagCommand = lazy('../src/commands/tasks/dag.js', 'dagCommand');
68
50
 
69
- // === Codebase graph ===
70
51
  const graphStatusCommand = lazy('../src/commands/graph/status.js', 'graphStatusCommand');
71
52
  const graphBuildCommand = lazy('../src/commands/graph/build.js', 'graphBuildCommand');
72
53
  const graphAffectedCommand = lazy('../src/commands/graph/affected.js', 'graphAffectedCommand');
73
54
  const graphExplainCommand = lazy('../src/commands/graph/explain.js', 'graphExplainCommand');
74
55
 
75
- // === Validation ===
76
56
  const validateCommand = lazy('./validate.js', 'validateCommand');
77
57
  const validateFeatureCommand = lazy('../src/commands/validation/validate-feature.js', 'validateFeatureCommand');
78
58
  const verifyCommand = lazy('../src/commands/validation/verify.js', 'verifyCommand');
@@ -81,20 +61,15 @@ const e2eUpCommand = lazy('../src/commands/e2e/e2e.js', 'e2eUpCommand');
81
61
  const e2eStatusCommand = lazy('../src/commands/e2e/e2e.js', 'e2eStatusCommand');
82
62
  const e2eDownCommand = lazy('../src/commands/e2e/e2e.js', 'e2eDownCommand');
83
63
 
84
- // === Scaffolding ===
85
64
  const scaffoldDotnetCommand = lazy('../src/commands/scaffold/scaffold.js', 'scaffoldDotnetCommand');
86
65
  const scaffoldNextjsCommand = lazy('../src/commands/scaffold/scaffold.js', 'scaffoldNextjsCommand');
87
66
 
88
- // === MCP setup ===
89
67
  const mcpSetupCommand = lazy('../src/commands/mcp/mcp-setup.js', 'mcpSetupCommand');
90
68
 
91
- // === Context refresh ===
92
69
  const contextRefreshCommand = lazy('../src/commands/context/context-refresh.js', 'contextRefreshCommand');
93
70
 
94
- // === Dashboard ===
95
71
  const dashboardCommand = lazy('../src/commands/dashboard/dashboard.js', 'dashboardCommand');
96
72
 
97
- // === Observability ===
98
73
  const telemetryCommand = lazy('../src/commands/observability/telemetry.js', 'telemetryCommand');
99
74
  const costCommand = lazy('../src/commands/observability/cost.js', 'costCommand');
100
75
 
@@ -105,9 +80,7 @@ const banner = `${renderLogo(pkg.version)}
105
80
  ${chalk.dim('Spec-driven development harness for Claude Code — by Polymorphism Tech')}
106
81
  `;
107
82
 
108
- // First run after an install/upgrade: show the install banner (npm ≥7 hides
109
- // postinstall output). Skipped for --version/--help (parsed/already-bannered)
110
- // and for non-TTY output — see maybePrintFirstRunBanner.
83
+ // npm >=7 hides postinstall output, so the first run after an install shows the banner.
111
84
  const argvRest = process.argv.slice(2);
112
85
  const skipFirstRun = argvRest.length === 0 ||
113
86
  argvRest.some(a => ['-V', '--version', '-h', '--help'].includes(a));
@@ -121,7 +94,6 @@ program
121
94
  .version(pkg.version)
122
95
  .addHelpText('beforeAll', banner);
123
96
 
124
- // ─── Install ──────────────────────────────────────────────────────────────
125
97
  program
126
98
  .command('init')
127
99
  .description('Initialize MORPH-SPEC in current directory')
@@ -164,7 +136,6 @@ program
164
136
  .option('--reset', 'Remove morph-managed entries from .claude/settings.local.json')
165
137
  .action(doctorCommand);
166
138
 
167
- // ─── Status / inspection ──────────────────────────────────────────────────
168
139
  program
169
140
  .command('status <feature>')
170
141
  .description('Show feature status (phase, gate state, task counts)')
@@ -174,22 +145,21 @@ program
174
145
 
175
146
  program
176
147
  .command('list')
177
- .description('List every feature × status × gates (G1/G2/G3) × workType — the audit board')
148
+ .description('List every feature × status × gates (G1 plan / Gd uiux / G3 review) × workType — the audit board')
178
149
  .option('--all', 'Include archived features (.morph/archive)')
179
150
  .option('--archived', 'Include archived features (alias of --all)')
180
151
  .option('--no-branch', 'Skip reading morph/{feature} for truth — report only what is on disk in this tree')
181
152
  .option('--json', 'Output as JSON')
182
153
  .action((options) => listCommand(options));
183
154
 
184
- // ─── Approval gates (dev-only) ────────────────────────────────────────────
185
155
  program
186
156
  .command('approve <feature> <gate>')
187
- .description('Approve a phase gate (proposal, plan, review). Enforces gate order (N requires N-1)')
157
+ .description('Approve a phase gate (plan, uiux, review). Enforces gate order (uiux and review require plan)')
188
158
  .option('--approver <name>', 'Name of approver (default: current user when interactive, "auto" otherwise)')
189
159
  .option('--mode <mode>', 'Approval mode: "user" or "auto" (default: detected from TTY)')
190
160
  .option('--notes <notes>', 'Approval notes')
191
161
  .option('--force', 'Approve out of order (bypass the gate-monotonicity check)')
192
- .option('--self-assessed', 'Gate 3 only: approve without 4-review/evaluator-report.md, recording the degradation in feature.json')
162
+ .option('--self-assessed', 'Gate 3 only: approve without the review reports (verifier.md, reviewer.md, security.md), recording the degradation in feature.json')
193
163
  .action((feature, gate, options) => approveCommand(feature, gate, options));
194
164
 
195
165
  program
@@ -197,7 +167,7 @@ program
197
167
  .description('Approve the current phase gate and scaffold the next phase folder (one step)')
198
168
  .option('--approver <name>', 'Name of approver (default: current user when interactive, "auto" otherwise)')
199
169
  .option('--mode <mode>', 'Approval mode: "user" or "auto" (default: detected from TTY)')
200
- .option('--design', 'From proposal, advance into the optional design (uiux) phase instead of plan')
170
+ .option('--design', 'From plan, advance into the optional design (uiux) phase instead of implement')
201
171
  .option('--notes <notes>', 'Approval notes')
202
172
  .option('--force', 'Advance even if a prerequisite gate is unapproved (out-of-order recovery)')
203
173
  .action((feature, options) => advanceCommand(feature, options));
@@ -251,7 +221,7 @@ program
251
221
  program
252
222
  .command('gate-check <feature> <gate>')
253
223
  .description('Decide + record a gate\'s trust decision from disk-derived signals (shouldPauseGate) — the deterministic default; see gate-decision for the manual escape hatch')
254
- .option('--signals <json>', 'Override ONLY standardsViolations/designSystemDivergence (not derivable from disk) — everything else is read from feature.json/tasks.json/git and cannot be overridden here')
224
+ .option('--signals <json>', 'Override ONLY standardsViolations/designSystemDivergence (not derivable from disk) — everything else is read from feature.json/checks.json/git and cannot be overridden here')
255
225
  .option('--trust <mode>', 'Trust mode to decide with (default: config.json → trust, or "auto")')
256
226
  .option('--json', 'Output as JSON')
257
227
  .action((feature, gateArg, options) => gateCheckCommand(feature, gateArg, options));
@@ -294,34 +264,6 @@ program
294
264
  .option('--here', 'Force building/writing in the current directory, even if another tree owns this feature')
295
265
  .action((feature, options) => handoffCommand(feature, options));
296
266
 
297
- // ─── Use cases ────────────────────────────────────────────────────────────
298
- const usecaseCommand = program
299
- .command('usecase')
300
- .description('Project-wide use cases: UC-{nn} numbering (acervo + every worktree of this repo) and the acervo lifecycle check');
301
-
302
- usecaseCommand
303
- .command('reserve')
304
- .description('Atomically reserve the next N UC-{nn} numbers, shared across every worktree of this repo — replaces the old max+1 disk scan, which collides across worktrees')
305
- .option('--count <n>', 'How many numbers to reserve', '1')
306
- .option('--feature <name>', 'Feature to record as the reservation owner (defaults to the sole active feature, if unambiguous) — also what the promotion collision guard compares against')
307
- .option('--json', 'Output as JSON')
308
- .action((options) => usecaseReserveCommand(options));
309
-
310
- usecaseCommand
311
- .command('check <feature>')
312
- .description('Check a feature against the acervo lifecycle: tasks citing a superseded UC, malformed supersession frontmatter, spec.md revocation rows the finish could not apply (plus the task/use-case plumbing dag checks, when tasks.json exists)')
313
- .option('--json', 'Output as JSON')
314
- .action((feature, options) => usecaseCheckCommand(feature, options));
315
-
316
- // ─── Tasks ────────────────────────────────────────────────────────────────
317
- program
318
- .command('dag <feature>')
319
- .description('Analyze the tasks.json DAG and print the sequential vs parallel dispatch decision (effort-weighted)')
320
- .option('--json', 'Output as JSON')
321
- .option('--mermaid', 'Emit the tasks.json DAG as a Mermaid flowchart (flowchart TD)')
322
- .action((feature, options) => dagCommand(feature, options));
323
-
324
- // ─── Codebase graph ───────────────────────────────────────────────────────
325
267
  const graphCommand = program
326
268
  .command('graph')
327
269
  .description('Grafo de codebase (graphify) com saneamento determinístico — fail-open: sem grafo, tudo segue por grep');
@@ -363,7 +305,6 @@ graphCommand
363
305
  .option('--json', 'Output as JSON')
364
306
  .action((symbol, options) => graphExplainCommand(symbol, options));
365
307
 
366
- // ─── Validation ───────────────────────────────────────────────────────────
367
308
  program
368
309
  .command('validate [validator]')
369
310
  .description('Run project validations (all | packages | next-components | spec-tasks)')
@@ -388,24 +329,23 @@ program
388
329
  .action(validateFeatureCommand);
389
330
 
390
331
  program
391
- .command('verify <feature> [task]')
332
+ .command('verify <feature>')
392
333
  .description('Run the deterministic verification chain (build + tests + validators + e2e) — code before the LLM judge')
393
334
  .option('--json', 'Output as JSON')
394
335
  .option('--timeout <ms>', 'Timeout in ms for the build AND tests nodes (build default 120000; tests default 120000 per test project, or project.testTimeoutMs)', (v) => parseInt(v, 10))
395
336
  .option('--filter <ids>', 'Run only the test nodes whose id matches (comma-separated; exact or substring). Unmatched nodes are reported as skip; matching nothing is a usage error (exit 2)')
396
337
  .option('--linux', 'Run the test nodes inside a Linux container, on a COPY of the tree (the host tree is mounted read-only and copied in — a bind mount is case-insensitive and cannot reproduce case defects). No Docker = usage error, never a skip')
397
338
  .option('--linux-image <img>', 'Image used by --linux (default: mcr.microsoft.com/dotnet/sdk:10.0)')
398
- .option('--full', 'No escopo de task, desliga o recorte automático e roda todos os nós inteiros (o group da task não escolhe nós e os outputs não estreitam comandos). No escopo de feature não muda nada: ali nada é recortado')
399
- .option('--record-baseline', 'Measure every test node and write expectedCount back into .morph/config/config.json (only from an all-green run at FEATURE scope; with a task id it is a usage error; never creates config.json)')
339
+ .option('--record-baseline', 'Measure every test node and write expectedCount back into .morph/config/config.json (only from an all-green run; never creates config.json)')
400
340
  .option('--refresh-cache', 'Rewrite the response cache of every `runner: "evals"` node against the real provider. NEVER implicit: a cache rewrite is a paid call nobody asked for. Requires the declared envFile; no eval node declared, --skip-tests, or a --filter that excludes them all are usage errors (exit 2), refused BEFORE the cache is touched')
401
- .option('--skip-tests', 'Skip the test node. At feature scope the run is partial, so the verification stamp is NOT written (reason "skip-tests")')
402
- .option('--skip-build', 'Skip the build node. At feature scope the run is partial, so the verification stamp is NOT written (reason "skip-build")')
341
+ .option('--skip-tests', 'Skip the test node. The run is partial, so the verification stamp is NOT written (reason "skip-tests")')
342
+ .option('--skip-build', 'Skip the build node. The run is partial, so the verification stamp is NOT written (reason "skip-build")')
403
343
  .option('--skip-e2e', 'Skip the e2e node (Docker bring-up + Playwright)')
404
344
  .option('--fresh-e2e', 'Recreate the e2e stack from scratch: down -v (named volumes included), drop the freshness stamp, rebuild, re-seed auth')
405
345
  .option('--no-persist', 'Do not persist the result to feature.json')
406
- .option('--ci', 'Feature scope only: push morph/<feature> and take the proof from the project CI (creates .github/workflows/morph-verify.yml when no workflow runs verify). Same stamp, source "ci". No GitHub/gh/workflow = local verify with the reason; uncommitted code = exit 2; CI still running after 50 min = exit 3, no stamp')
407
- .action((feature, task, options) =>
408
- verifyCommand(feature, task, { ...options, noPersist: options.persist === false })
346
+ .option('--ci', 'Push morph/<feature> and take the proof from the project CI (creates .github/workflows/morph-verify.yml when no workflow runs verify). Same stamp, source "ci". No GitHub/gh/workflow = local verify with the reason; uncommitted code = exit 2; CI still running after 50 min = exit 3, no stamp')
347
+ .action((feature, options) =>
348
+ verifyCommand(feature, { ...options, noPersist: options.persist === false })
409
349
  );
410
350
 
411
351
  program
@@ -421,7 +361,6 @@ program
421
361
  .option('--json', 'Output as JSON')
422
362
  .action((feature, options) => mutateCommand(feature, options));
423
363
 
424
- // ─── Scaffolding ──────────────────────────────────────────────────────────
425
364
  const scaffoldCmd = program
426
365
  .command('scaffold')
427
366
  .description('Generate feature files from templates');
@@ -443,7 +382,6 @@ scaffoldCmd
443
382
  .option('-v, --verbose', 'Show detailed output')
444
383
  .action(scaffoldNextjsCommand);
445
384
 
446
- // ─── MCP setup ────────────────────────────────────────────────────────────
447
385
  const mcpCommand = program
448
386
  .command('mcp')
449
387
  .description('MCP server setup and configuration');
@@ -455,7 +393,6 @@ mcpCommand
455
393
  .option('--auto', 'Auto-install credential-free MCPs without prompts')
456
394
  .action((name, options) => mcpSetupCommand(name, options));
457
395
 
458
- // ─── Context refresh ──────────────────────────────────────────────────────
459
396
  const contextCommand = program
460
397
  .command('context')
461
398
  .description('Project context management (refresh)');
@@ -468,7 +405,6 @@ contextCommand
468
405
  .option('--json', 'Output scan result as JSON')
469
406
  .action((options) => contextRefreshCommand(options));
470
407
 
471
- // ─── E2E stack (Docker bring-up for runtime verification) ─────────────────
472
408
  const e2eCommand = program
473
409
  .command('e2e')
474
410
  .description("Lifecycle of the feature's verification stack (docker compose)");
@@ -493,7 +429,6 @@ e2eCommand
493
429
  .option('--json', 'Output as JSON')
494
430
  .action((feature, options) => e2eDownCommand(feature, options));
495
431
 
496
- // ─── Heavy nodes (build, tests, e2e) ─────────────────────────────────────
497
432
  const heavyCommand = program
498
433
  .command('heavy')
499
434
  .description('Slots por usuário para os nós pesados (build, testes, e2e), compartilhados entre as sessões');
@@ -510,7 +445,6 @@ heavyCommand
510
445
  .option('--label <label>', 'Nome do que roda, mostrado a quem espera pelo slot')
511
446
  .action((command, options) => heavyRunCommand(command, options));
512
447
 
513
- // ─── Worktree (optional, isolation) ───────────────────────────────────────
514
448
  const worktreeCommand = program
515
449
  .command('worktree')
516
450
  .description('Manage git worktrees for feature isolation');
@@ -522,10 +456,6 @@ worktreeCommand
522
456
  .option('--json', 'Output as JSON (always JSON — accepted so the WorktreeCreate hook can call setup and provision uniformly)')
523
457
  .action((feature, options) => worktreeSetupCommand(feature, options));
524
458
 
525
- // Task isolation — the tree `claude -w`, EnterWorktree and Agent
526
- // isolation:"worktree" ask for when the name is NOT a feature. Same infra as
527
- // setup (junctions, port block, git-invisible manifest, primary-root landing),
528
- // no feature identity. The WorktreeCreate hook is its main caller.
529
459
  worktreeCommand
530
460
  .command('provision <name>')
531
461
  .description('Create a task-isolation worktree at worktrees/{name}/ on branch morph-task/{name} — full morph infra, no feature identity')
@@ -540,10 +470,8 @@ worktreeCommand
540
470
  .option('--all', 'Link all shared dirs, not just node_modules')
541
471
  .action((options) => worktreeLinkCommand(options));
542
472
 
543
- // The SAFE removal — it drops the infra junctions before letting git near the
544
- // directory. Raw `git worktree remove` follows them out and empties the primary
545
- // root's .claude/ and .morph/framework/, so nothing in this repo (message, doc,
546
- // or skill) may point at it; they all point here.
473
+ // Raw `git worktree remove` follows the infra junctions and empties the primary root's .claude/
474
+ // and .morph/framework/: nothing in this repo may point at it, only at this command.
547
475
  worktreeCommand
548
476
  .command('remove [feature]')
549
477
  .description('Safely tear down a worktree (drops infra junctions first, PROVE-or-DECLINE) — does NOT close the feature; that is `finish`')
@@ -554,9 +482,6 @@ worktreeCommand
554
482
  .option('--json', 'Output as JSON (always JSON)')
555
483
  .action((feature, options) => worktreeRemoveCommand(feature, options));
556
484
 
557
- // Sub-agent trees (`agent-<hex>` on `morph-task/agent-<hex>`) outlive the agent: nothing
558
- // tells the harness the agent is done with its worktree. The reaper removes only what it can prove
559
- // is finished and lost-proof; the rest is listed with the reason.
560
485
  worktreeCommand
561
486
  .command('reap')
562
487
  .description('Remove the worktrees and branches of sub-agents that stopped clean (nothing uncommitted, no commit only they hold); everything else is listed with the reason and left alone')
@@ -571,11 +496,6 @@ worktreeCommand
571
496
  .option('--json', 'Output as JSON')
572
497
  .action((options) => worktreeListCommand(options));
573
498
 
574
- // ─── Branch housekeeping ──────────────────────────────────────────────────
575
- // `git branch --merged` cannot see a squash-merge, so the only command left for
576
- // a squash-merged branch is `git branch -D` — indistinguishable from discarding
577
- // real work. `prune` makes the distinction itself and deletes only what it
578
- // proved is already integrated.
579
499
  const branchCommand = program
580
500
  .command('branch')
581
501
  .description('Local branch housekeeping');
@@ -588,7 +508,6 @@ branchCommand
588
508
  .option('--json', 'Output as JSON')
589
509
  .action((options) => branchPruneCommand(options));
590
510
 
591
- // ─── Worktree env (shell-loadable port block) ─────────────────────────────
592
511
  program
593
512
  .command('env [feature]')
594
513
  .description('Print the worktree\'s dedicated environment (port block + COMPOSE_PROJECT_NAME), shell-loadable: eval "$(morph-spec env)" outside Claude Code; feature defaults to the morph/{f} or morph-task/{n} branch of cwd')
@@ -596,14 +515,12 @@ program
596
515
  .option('--powershell', 'Emit $env:K="V" lines instead of POSIX exports')
597
516
  .action((feature, options) => envCommand(feature, options));
598
517
 
599
- // ─── Fleet ────────────────────────────────────────────────────────────────
600
518
  program
601
519
  .command('fleet')
602
520
  .description('Fleet view — every active feature across primary root and worktrees (phase, gates, tasks, ports, handoff, activity)')
603
521
  .option('--json', 'Output as JSON')
604
522
  .action((options) => fleetCommand(options));
605
523
 
606
- // ─── Claims (advisory feature ownership) ──────────────────────────────────
607
524
  program
608
525
  .command('claim [feature]')
609
526
  .description('claim [feature] acquires it (moves this tree\'s previous claim in the same step); no args shows the board (claims are advisory warnings, never locks)')
@@ -613,23 +530,22 @@ program
613
530
  .option('--json', 'Output as JSON')
614
531
  .action((feature, options) => claimCommand(feature, options));
615
532
 
616
- // ─── Standards search (server-side, R2 token cut) ─────────────────────────
617
533
  program
618
534
  .command('standards')
619
- .description('Search STANDARDS.json server-side (top 3, full digest) instead of reading the whole registry. No --find: list categories + counts')
535
+ .description('Search STANDARDS.json server-side (top 3, digest + anchors) or print one section, instead of reading the whole registry. Bare: categories + counts')
620
536
  .option('--find <query>', 'Search query — matches name/id, tags/aliases, category, digest')
537
+ .option('--show <target>', '<id>: digest + anchors; <id>#<anchor>: only that section of the standard .md')
621
538
  .option('--category <category>', 'Restrict --find to a single category')
622
539
  .option('--tags <tags>', 'Restrict --find to standards carrying any of these comma-separated tags')
623
540
  .option('--json', 'Output as JSON')
624
541
  .action((options) => standardsCommand(options));
625
542
 
626
- // ─── Sub-agent report protocol ────────────────────────────────────────────
627
543
  program
628
- .command('report-check <feature> [tasks...]')
629
- .description('Audit sub-agent report files against the work that actually landed. Answers "did the WORK land even though the REPORT did not" — exits 0 only when everything is intact. Several task ids audit a parallel batch in one call')
630
- .option('--before-eval', 'Save a snapshot of the tree before dispatching the evaluator; later runs report what changed since')
544
+ .command('report-check <feature> [slices...]')
545
+ .description('Audit slice report files (3-implement/reports/{slice}.md) against the work the dispatch actually wrote (telemetry, keyed by the agent that wrote the report). Exits 0 only when every audited slice is intact; naming slices audits only those')
546
+ .option('--before-eval', 'Save a snapshot of the tree before dispatching the review agents and print their models; later runs report what changed since')
631
547
  .option('--json', 'Output as JSON')
632
- .action((feature, tasks, options) => reportCheckCommand(feature, tasks.length > 1 ? tasks : tasks[0], options));
548
+ .action((feature, slices, options) => reportCheckCommand(feature, slices, options));
633
549
 
634
550
  program
635
551
  .command('scout-check <map>')
@@ -638,7 +554,20 @@ program
638
554
  .option('--json', 'Output as JSON')
639
555
  .action((map, options) => scoutCheckCommand(map, options));
640
556
 
641
- // ─── Dashboard ────────────────────────────────────────────────────────────
557
+ program
558
+ .command('plan-check <feature>')
559
+ .description('Parse 2-plan/plan.md (scenarios, slices, decisions, reuse) and, with no error, write 2-plan/checks.json — the only writer of checks.json. Refuses to rewrite it once the plan gate is approved and the plan changed outside ## Errata. Exits 1 on errors, 2 when plan.md is missing')
560
+ .option('--json', 'Output as JSON')
561
+ .addHelpText('after', () => '\nGramática (framework/templates/docs/plan.md):\n\n' +
562
+ readFileSync(join(__dirname, '..', 'framework', 'templates', 'docs', 'plan.md'), 'utf8').replace(/\{\{FEATURE_NAME\}\}/g, '<feature>'))
563
+ .action((feature, options) => planCheckCommand(feature, options));
564
+
565
+ program
566
+ .command('stack-scan [feature]')
567
+ .description('Audit the code against config.project.stack, judging only the configured stacks (runtime, ORM, database, Guid.NewGuid, generic repository, service layer, raw SQL, sealed, router, UI library) by severity. With a feature, write .morph/features/<f>/2-plan/stack-scan.md; without, print the report')
568
+ .option('--json', 'Output as JSON')
569
+ .action((feature, options) => stackScanCommand(feature, options));
570
+
642
571
  program
643
572
  .command('dashboard')
644
573
  .description('Open the morph-spec inspector dashboard')
@@ -646,7 +575,6 @@ program
646
575
  .option('--no-open', 'Don\'t auto-open browser')
647
576
  .action(dashboardCommand);
648
577
 
649
- // ─── Observability ────────────────────────────────────────────────────────
650
578
  program
651
579
  .command('telemetry')
652
580
  .description('Summarize the harness execution path from .morph/logs/events.jsonl (skills, personas, standards read, tool mix, prompts)')
@@ -657,10 +585,10 @@ program
657
585
 
658
586
  program
659
587
  .command('cost [feature]')
660
- .description('Cost-facing view over .morph/logs/events.jsonl: task duration, bytes re-ingested by kind (proxy, not tokens), dispatch counts')
661
- .option('--by <mode>', 'Group by: task|phase (default: feature-wide summary; with --all-trees, applies to the combined aggregate)')
588
+ .description('Cost-facing view over .morph/logs/events.jsonl: bytes re-ingested by kind (proxy, not tokens), dispatch counts, real tokens when observed')
589
+ .option('--by <mode>', 'Group by: phase (default: feature-wide summary; with --all-trees, applies to the combined aggregate)')
662
590
  .option('--all-trees', 'Aggregate across every git worktree with a log plus the logs finish saved in .git/morph-spec/finish-backup — per-tree breakdown + combined total')
663
- .option('--metrics', 'Loop measurements: turns, tool calls (orchestrator x sub-agent), time per command class, identical reruns, rereads, feature verify count, git per task, reuse validator per tree')
591
+ .option('--metrics', 'Loop measurements: turns, tool calls (orchestrator x sub-agent), time per command class, identical reruns, rereads, verify count per feature, git calls, reuse validator per tree')
664
592
  .option('--top <n>', 'With --metrics: how many reread files to list (default 10)', (v) => Number.parseInt(v, 10))
665
593
  .option('--json', 'Output the aggregate as JSON')
666
594
  .action((feature, options) => costCommand(feature, options));
package/bin/validate.js CHANGED
@@ -1,32 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- /**
4
- * Validation CLI
5
- *
6
- * Runs validators for the current project stack (detected from .morph/config/config.json).
7
- *
8
- * .NET stacks: packages, spec-tasks
9
- * Next.js stack: next-components, spec-tasks
10
- * Other stacks: spec-tasks
11
- *
12
- * Usage:
13
- * morph-spec validate [options]
14
- * morph-spec validate all
15
- * morph-spec validate next-components (Next.js projects)
16
- * morph-spec validate packages (.NET projects)
17
- *
18
- * MORPH-SPEC 3.0
19
- */
20
-
21
3
  import chalk from 'chalk';
22
4
  import { getStackProfile } from '../src/lib/stack/stack-profile.js';
23
5
 
24
- /**
25
- * Build the VALIDATORS registry for the current stack.
26
- * .NET stacks → packages, spec-tasks
27
- * nextjs stack → next-components, spec-tasks
28
- * other stacks → spec-tasks only
29
- */
30
6
  async function buildValidators(stack) {
31
7
  const validators = {};
32
8
 
@@ -52,114 +28,9 @@ async function buildValidators(stack) {
52
28
  };
53
29
  }
54
30
 
55
- validators['spec-tasks'] = {
56
- name: 'Spec vs Tasks Coverage',
57
- description: 'Validates that tasks.json covers all requirements from spec.md',
58
- run: validateSpecVsTasks
59
- };
60
-
61
31
  return validators;
62
32
  }
63
33
 
64
- /**
65
- * Validate spec.md requirements are covered by tasks
66
- */
67
- async function validateSpecVsTasks(projectPath, options = {}) {
68
- const path = await import('path');
69
- const fs = await import('fs');
70
- const { loadState } = await import('../src/core/state/state-manager.js');
71
-
72
- const results = [];
73
- let errors = 0;
74
- let warnings = 0;
75
-
76
- try {
77
- const state = loadState(false);
78
- if (!state || !state.features) {
79
- return { status: 'ok', errors: 0, warnings: 0, message: 'No features to validate' };
80
- }
81
-
82
- for (const [featureName, feature] of Object.entries(state.features)) {
83
- const outputsPath = path.join(projectPath, '.morph/features', featureName);
84
- const specPath = path.join(outputsPath, 'spec.md');
85
-
86
- if (!fs.existsSync(specPath)) {
87
- continue; // No spec to validate
88
- }
89
-
90
- // Read spec.md and extract requirements
91
- const specContent = fs.readFileSync(specPath, 'utf-8');
92
-
93
- // Extract requirement patterns (## Requisitos, - [ ], numbered lists, etc.)
94
- const requirementPatterns = [
95
- /^#+\s*Requisitos?\s*$/gim,
96
- /^-\s*\[.\]\s*(.+)$/gm,
97
- /^\d+\.\s+(.+)$/gm
98
- ];
99
-
100
- const requirements = [];
101
- for (const pattern of requirementPatterns) {
102
- const matches = specContent.matchAll(pattern);
103
- for (const match of matches) {
104
- if (match[1]) {
105
- requirements.push(match[1].trim());
106
- }
107
- }
108
- }
109
-
110
- // Check if feature has tasks
111
- if (!feature.tasks || (Array.isArray(feature.tasks) && feature.tasks.length === 0)) {
112
- if (requirements.length > 0) {
113
- warnings++;
114
- results.push({
115
- feature: featureName,
116
- level: 'warning',
117
- message: `Spec has ${requirements.length} requirements but no tasks defined`
118
- });
119
- }
120
- continue;
121
- }
122
-
123
- // Get task titles
124
- const taskTitles = Array.isArray(feature.tasks)
125
- ? feature.tasks.map(t => t.title?.toLowerCase() || '')
126
- : [];
127
-
128
- // Check each requirement has a matching task (fuzzy match)
129
- for (const req of requirements.slice(0, 20)) { // Limit to first 20
130
- const reqLower = req.toLowerCase();
131
- const hasMatch = taskTitles.some(title =>
132
- title.includes(reqLower.slice(0, 20)) ||
133
- reqLower.includes(title.slice(0, 20))
134
- );
135
-
136
- if (!hasMatch && options.verbose) {
137
- console.log(chalk.gray(` ? Requirement may not be covered: "${req.slice(0, 50)}..."`));
138
- }
139
- }
140
-
141
- if (options.verbose) {
142
- console.log(chalk.green(` ✓ ${featureName}: ${requirements.length} requirements, ${taskTitles.length} tasks`));
143
- }
144
- }
145
-
146
- } catch (error) {
147
- if (options.verbose) {
148
- console.log(chalk.yellow(` ⚠ Could not validate: ${error.message}`));
149
- }
150
- }
151
-
152
- return {
153
- status: errors > 0 ? 'error' : warnings > 0 ? 'warning' : 'ok',
154
- errors,
155
- warnings,
156
- results
157
- };
158
- }
159
-
160
- /**
161
- * Main validation command
162
- */
163
34
  export async function validateCommand(args = []) {
164
35
  const validatorName = args[0] || 'all';
165
36
  const options = parseOptions(args);
@@ -187,9 +58,6 @@ export async function validateCommand(args = []) {
187
58
  }
188
59
  }
189
60
 
190
- /**
191
- * Run all validators
192
- */
193
61
  async function runAllValidators(VALIDATORS, options) {
194
62
  const results = {};
195
63
  let totalErrors = 0;
@@ -209,12 +77,10 @@ async function runAllValidators(VALIDATORS, options) {
209
77
 
210
78
  results[name] = result;
211
79
 
212
- // Count errors/warnings
213
80
  if (result.errors !== undefined) {
214
81
  totalErrors += result.errors;
215
82
  totalWarnings += result.warnings || 0;
216
83
  } else if (result.results) {
217
- // Count issues in results array
218
84
  const issues = result.results.flatMap(r => r.issues || []);
219
85
  totalErrors += issues.filter(i => i.level === 'error').length;
220
86
  totalWarnings += issues.filter(i => i.level === 'warning').length;
@@ -226,7 +92,6 @@ async function runAllValidators(VALIDATORS, options) {
226
92
  }
227
93
  }
228
94
 
229
- // Summary
230
95
  console.log(chalk.bold.cyan('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'));
231
96
  console.log(chalk.bold('📊 Validation Summary\n'));
232
97
 
@@ -243,15 +108,11 @@ async function runAllValidators(VALIDATORS, options) {
243
108
 
244
109
  console.log('');
245
110
 
246
- // Exit with error code if errors found
247
111
  if (totalErrors > 0 && !options.noFail) {
248
112
  process.exit(1);
249
113
  }
250
114
  }
251
115
 
252
- /**
253
- * Run single validator
254
- */
255
116
  async function runValidator(VALIDATORS, name, options) {
256
117
  const validator = VALIDATORS[name];
257
118
 
@@ -260,7 +121,7 @@ async function runValidator(VALIDATORS, name, options) {
260
121
 
261
122
  try {
262
123
  const result = await validator.run('.', {
263
- verbose: true, // Always verbose for single validator
124
+ verbose: true,
264
125
  autoFix: options.autoFix
265
126
  });
266
127
 
@@ -282,9 +143,6 @@ async function runValidator(VALIDATORS, name, options) {
282
143
  }
283
144
  }
284
145
 
285
- /**
286
- * Parse command-line options
287
- */
288
146
  function parseOptions(args) {
289
147
  const options = {
290
148
  verbose: false,
@@ -305,9 +163,6 @@ function parseOptions(args) {
305
163
  return options;
306
164
  }
307
165
 
308
- /**
309
- * Show help
310
- */
311
166
  export function showValidateHelp() {
312
167
  console.log(`
313
168
  ${chalk.bold.cyan('morph-spec validate')} - Run project validations
@@ -342,7 +197,6 @@ ${chalk.bold('INTEGRATION')}
342
197
  `);
343
198
  }
344
199
 
345
- // Run if called directly
346
200
  if (import.meta.url === `file://${process.argv[1]}`) {
347
201
  const args = process.argv.slice(2);
348
202
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "morph-spec",
3
- "version": "8.47.0",
3
+ "version": "9.1.0",
4
4
  "displayName": "MORPH-SPEC Framework",
5
5
  "description": "Spec-driven development with 38 agents and 8-phase workflow for .NET/Blazor/Next.js/Azure",
6
6
  "publisher": "polymorphism-tech",