@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
@@ -1,20 +1,3 @@
1
- /**
2
- * `morph-spec env [feature] [--json|--posix|--powershell]` — print the
3
- * dedicated environment of a worktree (port block + compose project) in a
4
- * shell-loadable form.
5
- *
6
- * Inside Claude Code the `worktree-env-inject` hook applies this block to
7
- * every dev-server / compose launcher automatically. This command is the same
8
- * contract for everything OUTSIDE the harness: a human terminal
9
- * (`eval "$(morph-spec env)"`), a CI step, a sub-agent running without hooks.
10
- * It derives from `buildComposeEnv` — the function the `verify` e2e node and
11
- * `e2e up` already use — so a shell and the stack can never disagree.
12
- *
13
- * Feature resolution: explicit argument, else the `morph/{f}` (or
14
- * `morph-task/{n}`) branch of the cwd. Read-only: allocation is idempotent and
15
- * never moves an existing block.
16
- */
17
-
18
1
  import { execFileSync } from 'child_process';
19
2
  import { readFileSync } from 'fs';
20
3
  import { join, resolve } from 'path';
@@ -49,14 +32,9 @@ function gitCommonDir(cwd) {
49
32
  }
50
33
  }
51
34
 
52
- /** POSIX single-quote. */
53
35
  const shq = (v) => `'${String(v).replace(/'/g, "'\\''")}'`;
54
36
 
55
- /**
56
- * Pure: the env map for a key, or `{ error }`.
57
- * @param {string|null} featureArg
58
- * @param {string} cwd
59
- */
37
+ /** The env map for a key, or `{ error }`. */
60
38
  export function resolveWorktreeEnv(featureArg, cwd = process.cwd()) {
61
39
  const key = featureArg || envKeyFromBranch(currentBranch(cwd));
62
40
  if (!key) {
@@ -78,10 +56,6 @@ export function render(env, format) {
78
56
  return entries.map(([k, v]) => `export ${k}=${shq(v)}`).join('\n');
79
57
  }
80
58
 
81
- /**
82
- * @param {string|undefined} featureArg
83
- * @param {{ json?: boolean, posix?: boolean, powershell?: boolean }} options
84
- */
85
59
  export function envCommand(featureArg, options = {}) {
86
60
  const result = resolveWorktreeEnv(featureArg || null, process.cwd());
87
61
  if (result.error) {
@@ -1,22 +1,7 @@
1
- /**
2
- * `morph-spec fleet [--json]` — the fleet view.
3
- *
4
- * One line per active feature across the primary root and every morph/*
5
- * worktree: phase, pending gates, task progress, port block, handoff
6
- * freshness, last commit and dirtiness. This is the terminal surface the
7
- * operator scans between sessions ("what is each instance doing, what is
8
- * waiting on me") — `worktree list` remains the lean per-worktree view.
9
- *
10
- * Read-only, always exits 0 (fail-open — an empty fleet is an answer).
11
- */
12
-
13
1
  import chalk from 'chalk';
14
2
  import { scanFleet } from '../../lib/worktree/fleet-scanner.js';
15
3
  import { claimAge } from '../../../framework/hooks/shared/claim-registry.js';
16
4
 
17
- /**
18
- * @param {{ json?: boolean, cwd?: string }} [options]
19
- */
20
5
  export function fleetCommand(options = {}) {
21
6
  const fleet = scanFleet(options.cwd || process.cwd());
22
7
 
@@ -32,22 +17,13 @@ export function fleetCommand(options = {}) {
32
17
 
33
18
  const col = (s, w) => String(s ?? '-').padEnd(w);
34
19
 
35
- /**
36
- * The REMOTO cell. `-` means "unknown" (no origin, or the probe failed) —
37
- * deliberately the same glyph as "nothing there", because both mean "do not
38
- * trust the local view alone". `local` is the confident negative.
39
- */
20
+ /** `-` (unknown) shares the "nothing there" glyph on purpose: both mean "do not trust the local view alone". */
40
21
  const remoteCell = (r) => {
41
22
  if (!r) return '-';
42
23
  if (r.pr) return `PR #${r.pr.number}${r.pr.state && r.pr.state !== 'OPEN' ? ` (${r.pr.state.toLowerCase()})` : ''}`;
43
24
  return r.pushed ? 'pushed' : 'local';
44
25
  };
45
26
 
46
- /**
47
- * The CLAIM cell — who is on this feature right now (B1). A stale claim is
48
- * shown as stale, not hidden: "nobody here" and "somebody died here" are
49
- * different facts for the operator.
50
- */
51
27
  const claimCell = (c) => {
52
28
  if (!c) return '-';
53
29
  const age = claimAge(c);
@@ -55,20 +31,15 @@ export function fleetCommand(options = {}) {
55
31
  };
56
32
 
57
33
  console.log(chalk.white.bold(
58
- // PHASE at 18: `aguardando-merge` (the pr-pending synthesized phase, see
59
- // fleet-scanner.js) is 16 chars — 1 wider than the col width `list.js`
60
- // already uses (17) for the identical string. A narrower column here
61
- // would misalign every column after it just for that one row.
62
- col('FEATURE', 20) + col('TYPE', 9) + col('PHASE', 18) + col('TASKS', 8)
34
+ // PHASE at 18 fits `aguardando-merge`; narrower misaligns every column after it.
35
+ col('FEATURE', 20) + col('TYPE', 9) + col('PHASE', 18) + col('FATIAS', 8)
63
36
  + col('GATES PEND.', 20) + col('WHERE', 24) + col('REMOTO', 16) + col('CLAIM', 16)
64
37
  + col('PORTS', 12) + col('HANDOFF', 9) + 'ATIVIDADE',
65
38
  ));
66
39
  for (const e of fleet.entries) {
67
- const tasks = e.tasks ? `${e.tasks.done}/${e.tasks.total}` : '-';
40
+ const slices = e.slices ? `${e.slices.done}/${e.slices.total}` : '-';
68
41
  const gates = e.pendingGates.length ? e.pendingGates.join(',') : '-';
69
- // pr-pending has no worktree and isn't root-resident either — its branch
70
- // is the only place it exists, so show that instead of the misleading
71
- // 'raiz' a location-blind check would print.
42
+ // pr-pending exists only on its branch, never in a worktree or the root.
72
43
  const where = (e.location === 'worktree' || e.location === 'pr-pending') ? e.branch : 'raiz';
73
44
  const ports = e.env ? `${e.env.range[0]}-${e.env.range[1]}` : '-';
74
45
  const handoff = e.handoff?.exists ? 'sim' : '-';
@@ -80,7 +51,7 @@ export function fleetCommand(options = {}) {
80
51
  // length and append the padding by hand.
81
52
  const claimPlain = e.claim ? `${e.claim.live ? 'sessão' : 'morta'} (${claimAge(e.claim)})` : '-';
82
53
  console.log(
83
- col(e.feature, 20) + col(e.workType || '-', 9) + col(e.phase, 18) + col(tasks, 8)
54
+ col(e.feature, 20) + col(e.workType || '-', 9) + col(e.phase, 18) + col(slices, 8)
84
55
  + col(gates, 20) + col(where, 24) + col(remoteCell(e.remote), 16)
85
56
  + claimCell(e.claim) + ' '.repeat(Math.max(1, 16 - claimPlain.length))
86
57
  + col(ports, 12) + col(handoff, 9) + activity,
@@ -4,9 +4,9 @@ import { logger } from '../../utils/logger.js';
4
4
  import { readHeavyConfig } from '../../../framework/hooks/shared/heavy-config.js';
5
5
  import { createHeavyGate, HeavyBusyError } from '../../lib/heavy/gate.js';
6
6
  import { describeHolder, heavyDir, heavyStatus } from '../../../framework/hooks/shared/heavy-slots.js';
7
+ import { resolveSpawn } from '../../../framework/hooks/shared/spawn-command.js';
7
8
 
8
9
  const EXIT_BUSY = 3;
9
- const NEEDS_CMD_SHIM = /^(npm|npx|pnpm|yarn)$/;
10
10
 
11
11
  export function heavyStatusCommand(options = {}) {
12
12
  const config = readHeavyConfig(process.cwd());
@@ -49,8 +49,8 @@ export function heavyRunCommand(command, options = {}) {
49
49
  process.exit(EXIT_BUSY);
50
50
  }
51
51
 
52
- const exe = process.platform === 'win32' && NEEDS_CMD_SHIM.test(file) ? `${file}.cmd` : file;
53
- const res = spawnSync(exe, args, { stdio: 'inherit', shell: exe.endsWith('.cmd') });
52
+ const spawn = resolveSpawn(file, args);
53
+ const res = spawnSync(spawn.file, spawn.args, { stdio: 'inherit', shell: spawn.shell });
54
54
  gate.release();
55
55
 
56
56
  if (res.error) {
@@ -37,25 +37,8 @@ import { resolvePrimaryRoot } from './worktree.js';
37
37
  import { registerProject } from '../../lib/projects/registry.js';
38
38
 
39
39
  /**
40
- * Guard against running a full `init` install from inside a secondary
41
- * (linked) git worktree.
42
- *
43
- * A worktree's `.claude/` and `.morph/framework/` are junctions back to the
44
- * primary root (see `worktree.js`'s `setupInfraLinks`), but `.morph/config/`,
45
- * `.morph/context/`, `.morph/checkpoints/`, `.morph/memory/`, `.morph/archive/`
46
- * and `.morph/state.json` are NOT linked — a full `init` there would scaffold
47
- * a second, disconnected install (config, state) inside the worktree, orphaned
48
- * from the primary root's real install.
49
- *
50
- * Fail-SAFE (not fail-open): blocks whenever `resolvePrimaryRoot(targetPath)`
51
- * resolves to a path OTHER than `targetPath` itself. `resolvePrimaryRoot`
52
- * itself fails open to `targetPath` outside a git repo (or on any git error),
53
- * so non-git projects and the primary root itself are never blocked.
54
- * `--force` is the explicit override, mirroring init's own `--force` flag.
55
- *
56
- * @param {string} targetPath - Effective install target (init's targetPath)
57
- * @param {{ force?: boolean }} [options]
58
- * @returns {{ blocked: boolean, root?: string, target?: string }}
40
+ * A linked worktree junctions `.claude/` and `.morph/framework/` but not `.morph/config`/state:
41
+ * a full install there orphans a second, disconnected one. `--force` overrides.
59
42
  */
60
43
  export function checkSecondaryWorktreeGuard(targetPath, options = {}) {
61
44
  if (options.force) return { blocked: false };
@@ -72,9 +55,6 @@ export async function initCommand(options) {
72
55
  logger.dim(`Target: ${targetPath}`);
73
56
  logger.blank();
74
57
 
75
- // Guard: refuse a full install from inside a secondary worktree — it would
76
- // corrupt the shared infra junctioned back to the primary root and scaffold
77
- // a second, disconnected .morph/config + state.json. --force overrides.
78
58
  const worktreeGuard = checkSecondaryWorktreeGuard(targetPath, options);
79
59
  if (worktreeGuard.blocked) {
80
60
  logger.warn(`Você está num worktree secundário (${worktreeGuard.target}).`);
@@ -83,11 +63,9 @@ export async function initCommand(options) {
83
63
  process.exit(0);
84
64
  }
85
65
 
86
- // Check if already initialized
87
66
  const morphPath = join(targetPath, '.morph');
88
67
  if (await pathExists(morphPath)) {
89
68
  if (!options.force) {
90
- // Check if project version is outdated — auto-update instead of blocking
91
69
  const projectCheck = await checkProjectOutdated(targetPath);
92
70
  if (projectCheck.isOutdated) {
93
71
  logger.info(`MORPH already initialized (v${projectCheck.current}). Framework update available (v${projectCheck.cliVersion}).`);
@@ -105,31 +83,27 @@ export async function initCommand(options) {
105
83
 
106
84
  try {
107
85
 
108
- // Steps 1-10: Infrastructure (headless, no prompts)
109
86
  spinner.stop();
110
- // force: true — the secondary-worktree guard already ran above (either we
111
- // ARE the primary root, or the caller explicitly passed --force), so it
112
- // must not be redundantly re-applied inside setupInfra().
87
+ // The secondary-worktree guard already ran above; setupInfra must not re-apply it.
113
88
  const infraResult = await setupInfra(targetPath, { force: true });
89
+ if (infraResult?.refused) {
90
+ process.exitCode = 1;
91
+ return infraResult;
92
+ }
114
93
  spinner.start('Continuing...');
115
94
 
116
- // Derive display variables from what setupInfra installed
117
95
  const cliVersion = getInstalledCLIVersion();
118
96
  const commandsCopied = await pathExists(join(targetPath, '.claude', 'commands'));
119
97
  const rulesCopied = await pathExists(join(targetPath, '.claude', 'rules'));
120
- // Re-run installClaudeHooks to get the result object (idempotent — hooks already installed)
121
98
  const hooksResult = await installClaudeHooks(targetPath);
122
99
 
123
- // Re-derive config path for stack detection updates
124
100
  const configDir = join(morphPath, 'config');
125
101
  const configPath = join(configDir, 'config.json');
126
102
  const contextDir = join(morphPath, 'context');
127
103
 
128
- // Read config written by setupInfra so stack detection can update it
129
104
  let config = (await pathExists(configPath)) ? await readJson(configPath) : { project: {} };
130
105
  if (!config.project) config.project = {};
131
106
 
132
- // 11b. Detect Claude Code environment (plugins, MCPs, skills)
133
107
  spinner.text = 'Detecting Claude Code environment...';
134
108
  let claudeConfigDetected = null;
135
109
  let integrationsCreated = false;
@@ -148,14 +122,6 @@ export async function initCommand(options) {
148
122
  logger.info(summary);
149
123
  }
150
124
 
151
- // Show MCP-to-phase mapping.
152
- //
153
- // This called mapMcpsToPhases with one argument where it takes three,
154
- // so `phaseMatrix[currentPhase]` threw a TypeError on every project
155
- // that had any MCP configured. The outer catch reported it as
156
- // "Claude config detection skipped" — which reads like "nothing to
157
- // detect" — and everything below it never ran: integrations.json was
158
- // never written and the optimized settings were never generated.
159
125
  if (claudeConfigDetected.mcpServers.length > 0) {
160
126
  const byPhase = mcpAvailabilityByPhase(
161
127
  claudeConfigDetected.mcpServers,
@@ -191,11 +157,9 @@ export async function initCommand(options) {
191
157
  }
192
158
  }
193
159
  } catch (error) {
194
- // Non-fatal: continue without integrations
195
160
  logger.dim(` Claude config detection skipped: ${error.message}`);
196
161
  }
197
162
 
198
- // 11c. Stack detection (moved before MCP setup so we know the stack)
199
163
  spinner.text = 'Detecting project stack...';
200
164
  let detectedStack = 'unknown';
201
165
  const hasProjectFiles = await pathExists(join(targetPath, 'package.json'))
@@ -210,7 +174,6 @@ export async function initCommand(options) {
210
174
  if (structure?.stack && structure.stack !== 'unknown') {
211
175
  detectedStack = structure.stack;
212
176
 
213
- // Persist detected stack to config.json
214
177
  if (structure?.stack && structure.stack !== 'unknown') {
215
178
  config.project.stack = structure.stack;
216
179
  }
@@ -252,23 +215,19 @@ export async function initCommand(options) {
252
215
  logger.dim('Review and edit .morph/context/standards.md as needed.');
253
216
  }
254
217
 
255
- // Re-install stack-filtered assets now that we know the stack
256
218
  const stackTags = getProjectTags(config);
257
219
  if (stackTags.length > 0) {
258
220
  spinner.start('Re-installing stack-filtered assets...');
259
221
  const frameworkDir = join(targetPath, '.morph', 'framework');
260
222
  const fwSrcDir = join(dirname(fileURLToPath(import.meta.url)), '..', '..', '..', 'framework');
261
223
 
262
- // Rules
263
224
  const rulesSrc = join(fwSrcDir, 'rules');
264
225
  const rulesDest = join(targetPath, '.claude', 'rules');
265
226
  installRulesForStack(rulesSrc, rulesDest, stackTags);
266
227
  installCommandsForStack(join(fwSrcDir, 'commands'), join(targetPath, '.claude', 'commands'), stackTags);
267
228
 
268
- // Skills
269
229
  await installSkills(targetPath, { projectTags: stackTags });
270
230
 
271
- // Agents (domain personas from agents.json)
272
231
  await installAgents(targetPath, fwSrcDir, { projectTags: stackTags });
273
232
  await installDomainAgents(targetPath, fwSrcDir, { projectTags: stackTags });
274
233
 
@@ -282,7 +241,6 @@ export async function initCommand(options) {
282
241
  }
283
242
  }
284
243
 
285
- // 11d. MCP Auto-Setup
286
244
  let mcpSetupResult = null;
287
245
  if (!options.skipMcp) {
288
246
  try {
@@ -300,7 +258,6 @@ export async function initCommand(options) {
300
258
  logger.blank();
301
259
  logger.header('MCP Server Setup');
302
260
 
303
- // Auto-install prompt
304
261
  let autoInstalled = {};
305
262
  if (hasAutoMcps) {
306
263
  const { installAuto } = await inquirer.prompt([{
@@ -318,7 +275,6 @@ export async function initCommand(options) {
318
275
  autoInstalled[name] = orchestration.autoInstallable[name];
319
276
  }
320
277
 
321
- // Install companion skills for MCPs that define them (e.g., Stitch agent skills)
322
278
  const skillResults = installCompanionSkills(orchestration.autoInstallable);
323
279
  for (const r of skillResults) {
324
280
  if (r.success) {
@@ -330,13 +286,11 @@ export async function initCommand(options) {
330
286
  }
331
287
  }
332
288
 
333
- // Credential MCPs — inline setup or instructions
334
289
  const manualInstalled = {};
335
290
  if (hasManualMcps) {
336
291
  for (const [name, entry] of Object.entries(orchestration.needsManualSetup)) {
337
292
  logger.blank();
338
293
 
339
- // Show warnings
340
294
  for (const warning of entry.install.warnings || []) {
341
295
  logger.warn(` ${warning}`);
342
296
  }
@@ -350,7 +304,6 @@ export async function initCommand(options) {
350
304
  }]);
351
305
 
352
306
  if (setupNow) {
353
- // Stitch — automated setup via setupStitch()
354
307
  if (name === 'stitch' && !entry.install.autoInstall) {
355
308
  spinner.start('Setting up Stitch MCP...');
356
309
  const stitchResult = await setupStitch(targetPath);
@@ -370,7 +323,6 @@ export async function initCommand(options) {
370
323
  continue;
371
324
  }
372
325
 
373
- // Remote MCPs — launch OAuth via claude mcp add
374
326
  if (isRemoteMcp(entry.install.config)) {
375
327
  const url = entry.install.config.url;
376
328
  logger.info(` Launching ${capitalizedName} OAuth authentication...`);
@@ -387,7 +339,6 @@ export async function initCommand(options) {
387
339
  continue;
388
340
  }
389
341
 
390
- // Collect credentials
391
342
  const credentialValues = {};
392
343
  for (const cred of entry.install.credentials) {
393
344
  const { value } = await inquirer.prompt([{
@@ -404,7 +355,6 @@ export async function initCommand(options) {
404
355
  spinner.succeed(`${capitalizedName} MCP configured`);
405
356
  manualInstalled[name] = entry;
406
357
  } else {
407
- // Show setup instructions
408
358
  const instructions = generateSetupInstructions(name, entry);
409
359
  logger.blank();
410
360
  logger.dim(' Add to .mcp.json → mcpServers:');
@@ -422,7 +372,6 @@ export async function initCommand(options) {
422
372
  }
423
373
  }
424
374
 
425
- // Summary table
426
375
  const justInstalled = { ...autoInstalled, ...manualInstalled };
427
376
  const statusRows = formatMcpStatusTable(orchestration, justInstalled);
428
377
 
@@ -456,7 +405,6 @@ export async function initCommand(options) {
456
405
  logger.dim('\nSkipped MCP setup (--skip-mcp flag)');
457
406
  }
458
407
 
459
- // Auto-generate architecture context (non-blocking on failure)
460
408
  spinner.text = 'Generating architecture context...';
461
409
  try {
462
410
  const { scanArchitecture, renderArchitectureContext } = await import('../../lib/generators/architecture-scanner.js');
@@ -464,15 +412,12 @@ export async function initCommand(options) {
464
412
  await renderArchitectureContext(targetPath, scanResult);
465
413
  spinner.succeed('Architecture context generated');
466
414
  } catch {
467
- // Non-fatal: placeholder README.md remains
468
415
  spinner.warn('Architecture context generation skipped');
469
416
  }
470
417
 
471
418
  spinner.succeed('MORPH-SPEC installed successfully!');
472
- // The dashboard's portfolio learns about the project here (fail-open).
473
419
  registerProject(targetPath, 'init');
474
420
 
475
- // Show next steps
476
421
  logger.blank();
477
422
  logger.success(`MORPH-SPEC v${cliVersion} installed successfully!`);
478
423
  logger.blank();
@@ -513,7 +458,6 @@ export async function initCommand(options) {
513
458
 
514
459
  logger.dim(' ✓ .claude/skills/ (installed as skill directories)');
515
460
 
516
- // Show agent team breakdown from setup-infra result
517
461
  const agents = infraResult?.agents;
518
462
  if (agents) {
519
463
  const total = (agents.tier1 || 0) + (agents.tier2 || 0) + (agents.specialists || 0) + (agents.personas || 0);
@@ -524,7 +468,6 @@ export async function initCommand(options) {
524
468
 
525
469
  logger.dim(' ✓ ~/.claude/statusline.sh (global statusline installed)');
526
470
 
527
- // Check CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS in ~/.claude/settings.local.json
528
471
  logger.blank();
529
472
  const globalSettingsPath = join(homedir(), '.claude', 'settings.local.json');
530
473
  let agentTeamsEnabled = false;
@@ -532,7 +475,6 @@ export async function initCommand(options) {
532
475
  const globalSettings = await readJson(globalSettingsPath);
533
476
  agentTeamsEnabled = globalSettings?.env?.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS === '1';
534
477
  } catch {
535
- // File missing or unreadable — treat as not enabled
536
478
  }
537
479
 
538
480
  if (agentTeamsEnabled) {
@@ -544,7 +486,6 @@ export async function initCommand(options) {
544
486
 
545
487
  logger.blank();
546
488
 
547
- // Populate Claude Code persistent memory so future sessions enter contextualized
548
489
  try {
549
490
  const { installProjectMemory } = await import('../../lib/installers/memory-installer.js');
550
491
  const memResult = await installProjectMemory(targetPath, {
@@ -557,10 +498,9 @@ export async function initCommand(options) {
557
498
  logger.dim(` ⚠ Memory install skipped: ${err.message}`);
558
499
  }
559
500
 
560
- // Suggest tutorial for first-time users
561
501
  if (integrationsCreated) {
562
502
  logger.blank();
563
- logger.info('First time using morph-spec? Run `/morph-proposal <feature>` to start a new feature.');
503
+ logger.info('First time using morph-spec? Run `/morph-plan <feature>` to start a new feature.');
564
504
  }
565
505
 
566
506
  logger.blank();
@@ -1,4 +1,3 @@
1
- // src/commands/project/install-plugin-cmd.js
2
1
  import { installPlugin } from '../../scripts/install-plugin.js';
3
2
  import { logger } from '../../utils/logger.js';
4
3
 
@@ -4,7 +4,8 @@ import { logger } from '../../utils/logger.js';
4
4
  export async function setupInfraCommand(options) {
5
5
  const targetPath = options.path || process.cwd();
6
6
  try {
7
- await setupInfra(targetPath, { reinstall: options.reinstall ?? false });
7
+ const result = await setupInfra(targetPath, { reinstall: options.reinstall ?? false });
8
+ if (result?.refused) process.exitCode = 1;
8
9
  } catch (err) {
9
10
  logger.error(`setup-infra failed: ${err.message}`);
10
11
  process.exit(1);
@@ -0,0 +1,58 @@
1
+ import chalk from 'chalk';
2
+ import { existsSync, mkdirSync, writeFileSync } from 'fs';
3
+ import { join } from 'path';
4
+ import { getFeatureDir } from '../../core/paths/output-schema.js';
5
+ import { readMorphConfig } from '../../lib/tasks/test-runner.js';
6
+ import { notAuditedReason, scanStack, renderStackScan } from '../../lib/stack/stack-scan.js';
7
+ import { assertOwnerTree, ownerTreeRefusalLines } from '../../lib/worktree/feature-truth.js';
8
+
9
+ export const STACK_SCAN_FILE = 'stack-scan.md';
10
+
11
+ /** Scan the tree at `cwd`; with a feature, write `2-plan/stack-scan.md`. */
12
+ export function runStackScan(featureName, cwd = process.cwd()) {
13
+ const result = scanStack(cwd, readMorphConfig(cwd));
14
+ const markdown = renderStackScan(result, featureName ?? 'projeto');
15
+ if (!featureName) return { ...result, markdown, written: null, exit: 0 };
16
+
17
+ const featureDir = join(cwd, getFeatureDir(featureName));
18
+ if (!existsSync(featureDir)) return { ...result, markdown, written: null, exit: 2, error: 'feature-not-found' };
19
+ const planDir = join(featureDir, '2-plan');
20
+ mkdirSync(planDir, { recursive: true });
21
+ const written = join(planDir, STACK_SCAN_FILE);
22
+ writeFileSync(written, markdown, 'utf8');
23
+ return { ...result, markdown, written, exit: 0 };
24
+ }
25
+
26
+ export function stackScanCommand(featureName, options = {}) {
27
+ if (featureName) {
28
+ const owner = assertOwnerTree(featureName, process.cwd());
29
+ if (owner.blocked) {
30
+ for (const { tone, text } of ownerTreeRefusalLines(featureName, owner.tree, owner.branch, `morph-spec stack-scan ${featureName}`)) {
31
+ console.error(chalk[tone](text));
32
+ }
33
+ process.exit(1);
34
+ }
35
+ }
36
+
37
+ const result = runStackScan(featureName);
38
+ if (options.json) {
39
+ const { markdown, ...rest } = result;
40
+ console.log(JSON.stringify(rest, null, 2));
41
+ process.exit(result.exit);
42
+ }
43
+ if (result.error === 'feature-not-found') {
44
+ console.error(chalk.red(`stack-scan: a feature "${featureName}" não existe — rode morph-spec create ${featureName} antes`));
45
+ process.exit(2);
46
+ }
47
+ if (result.written) {
48
+ const high = result.deviations.filter((d) => d.severity === 'Bloqueante' || d.severity === 'Alto').length;
49
+ console.log(chalk.green(`✓ stack-scan: ${join(getFeatureDir(featureName), '2-plan', STACK_SCAN_FILE)} gravado`));
50
+ const summary = result.greenfield ? 'greenfield'
51
+ : !result.audited ? notAuditedReason(result)
52
+ : `${result.deviations.length} desvio(s), ${high} Bloqueante/Alto`;
53
+ console.log(chalk.gray(` ${summary}`));
54
+ } else {
55
+ process.stdout.write(result.markdown);
56
+ }
57
+ process.exit(result.exit);
58
+ }