@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,11 +1,3 @@
1
- /**
2
- * MCP Installer
3
- *
4
- * Core logic for installing and configuring MCP servers during morph-spec init
5
- * and via `morph-spec mcp setup`. Handles auto-installable (credential-free)
6
- * MCPs, prerequisite checking, credential collection, and setup guidance.
7
- */
8
-
9
1
  import { join } from 'path';
10
2
  import { readFileSync, existsSync, writeFileSync, mkdirSync } from 'fs';
11
3
  import { execSync, spawn } from 'child_process';
@@ -13,10 +5,6 @@ import { homedir } from 'os';
13
5
  import { installMcpServers } from '../../utils/claude-settings-manager.js';
14
6
  import { mcpServerNames } from '../../utils/agents-installer.js';
15
7
 
16
- /**
17
- * Load the enhanced MCP registry (v3.0.0)
18
- * @returns {Object} Registry with install blocks
19
- */
20
8
  export function loadMcpRegistry() {
21
9
  const registryPath = join(
22
10
  import.meta.dirname, '..', '..', '..', 'framework', 'mcp', 'registry.json'
@@ -25,15 +13,7 @@ export function loadMcpRegistry() {
25
13
  return JSON.parse(raw);
26
14
  }
27
15
 
28
- /**
29
- * Filter MCPs by stack relevance
30
- * @param {Object} registry - Full MCP registry
31
- * @param {string} stack - Detected project stack (e.g., 'nextjs-neon', 'blazor')
32
- * @param {string[]} [integrations] - `project.integrations` (e.g., ['neon']). The
33
- * detectors put a database or payment provider here, never in the stack string,
34
- * so an MCP relevant to it (neon, asaas) is matched against this list too.
35
- * @returns {Object} Filtered map of mcpName → mcpEntry
36
- */
16
+ // Detectors put a database or payment provider in `integrations`, never in the stack string.
37
17
  export function filterByStack(registry, stack, integrations = []) {
38
18
  const result = {};
39
19
  const normalizedStack = (stack || '').toLowerCase();
@@ -53,14 +33,7 @@ export function filterByStack(registry, stack, integrations = []) {
53
33
  return result;
54
34
  }
55
35
 
56
- /**
57
- * Match detected MCP servers against the registry
58
- * @param {Array} existingServers - From detectClaudeConfig().mcpServers
59
- * @param {Object} registry - MCP registry
60
- * @param {string[]} [enabledPlugins] - From detectClaudeConfig().enabledPlugins. A plugin
61
- * that ships the server already runs it, and writing it to `.mcp.json` too starts it twice.
62
- * @returns {Object} Map of mcpName → { installed: true, source }
63
- */
36
+ // A plugin that ships the server already runs it: writing it to `.mcp.json` too starts it twice.
64
37
  export function getInstalledMcps(existingServers, registry, enabledPlugins = []) {
65
38
  const installed = {};
66
39
  const registryNames = Object.keys(registry.mcps);
@@ -86,11 +59,6 @@ export function getInstalledMcps(existingServers, registry, enabledPlugins = [])
86
59
  return installed;
87
60
  }
88
61
 
89
- /**
90
- * Check if prerequisites are met for an MCP entry
91
- * @param {Object} mcpEntry - MCP entry from registry
92
- * @returns {Array} Array of { name, met, installUrl } for each prerequisite
93
- */
94
62
  export function checkPrerequisites(mcpEntry) {
95
63
  const prereqs = mcpEntry.install?.prerequisites || [];
96
64
  const results = [];
@@ -101,7 +69,6 @@ export function checkPrerequisites(mcpEntry) {
101
69
  execSync(prereq.checkCommand, { stdio: 'ignore', timeout: 5000 });
102
70
  met = true;
103
71
  } catch {
104
- // Command failed or not found
105
72
  }
106
73
  results.push({
107
74
  name: prereq.name,
@@ -113,25 +80,13 @@ export function checkPrerequisites(mcpEntry) {
113
80
  return results;
114
81
  }
115
82
 
116
- /**
117
- * Check if an MCP config uses remote HTTP transport (no local process).
118
- * @param {Object} config - MCP server config
119
- * @returns {boolean}
120
- */
121
83
  export function isRemoteMcp(config) {
122
84
  return config.type === 'http' || config.url != null;
123
85
  }
124
86
 
125
- /**
126
- * Patch MCP config for the current platform.
127
- * On Windows, npx must be invoked via `cmd /c` to avoid ENOENT errors.
128
- * Registry already stores cmd/c format; this handles legacy configs that still use npx directly.
129
- * Remote HTTP MCPs are returned as-is (no local process to patch).
130
- * @param {Object} config - MCP server config { command, args, env? } or { type: "http", url }
131
- * @returns {Object} Patched config (new object, original unmodified)
132
- */
87
+ // On Windows npx must run via `cmd /c` (ENOENT otherwise); the registry already stores that form,
88
+ // this covers legacy configs.
133
89
  export function patchConfigForPlatform(config) {
134
- // Remote MCPs need no platform patching
135
90
  if (isRemoteMcp(config)) return config;
136
91
  if (config.command !== 'npx') return config;
137
92
  return {
@@ -141,20 +96,12 @@ export function patchConfigForPlatform(config) {
141
96
  };
142
97
  }
143
98
 
144
- /**
145
- * Install auto-installable MCPs (no credentials, no prerequisites)
146
- * @param {string} targetPath - Project root directory
147
- * @param {Object} mcpsToInstall - Map of mcpName → mcpEntry
148
- * @returns {Promise<Object>} Result from installMcpServers
149
- */
150
99
  export async function installAutoMcps(targetPath, mcpsToInstall) {
151
100
  const servers = {};
152
101
 
153
102
  for (const [name, entry] of Object.entries(mcpsToInstall)) {
154
103
  let config = { ...entry.install.config };
155
- // Remote MCPs don't have env to strip
156
104
  if (!isRemoteMcp(config)) {
157
- // Only include env if it has actual values
158
105
  if (config.env && Object.values(config.env).every(v => !v)) {
159
106
  delete config.env;
160
107
  }
@@ -165,13 +112,6 @@ export async function installAutoMcps(targetPath, mcpsToInstall) {
165
112
  return installMcpServers(targetPath, servers);
166
113
  }
167
114
 
168
- /**
169
- * Install companion skills for MCPs that define them (e.g., Stitch agent skills).
170
- * Reads the `companionSkills` field from registry entries and runs the install command.
171
- * @param {Object} mcpsInstalled - Map of mcpName → mcpEntry that were just installed
172
- * @param {Function} exec - execSync function (for testing)
173
- * @returns {Array<{name: string, repo: string, success: boolean}>}
174
- */
175
115
  export function installCompanionSkills(mcpsInstalled, exec = execSync) {
176
116
  const results = [];
177
117
 
@@ -190,13 +130,6 @@ export function installCompanionSkills(mcpsInstalled, exec = execSync) {
190
130
  return results;
191
131
  }
192
132
 
193
- /**
194
- * Generate the Stitch OAuth proxy template script.
195
- * Cross-platform (Windows gcloud.cmd + Unix gcloud), fetch()-based,
196
- * with readline, session tracking, and graceful shutdown.
197
- * @param {string} gcpProject - GCP project ID for x-goog-user-project header
198
- * @returns {string} CJS proxy script content
199
- */
200
133
  export function getStitchProxyTemplate(gcpProject) {
201
134
  return `#!/usr/bin/env node
202
135
  /**
@@ -309,12 +242,6 @@ rl.on('close', () => { stdinClosed = true; maybeExit(); });
309
242
  `;
310
243
  }
311
244
 
312
- /**
313
- * Generate and write the Stitch OAuth proxy script to the target project.
314
- * @param {string} targetPath - Project root directory
315
- * @param {string} gcpProject - GCP project ID
316
- * @returns {{ proxyPath: string }} Path to the generated proxy script
317
- */
318
245
  export function generateStitchProxy(targetPath, gcpProject) {
319
246
  const claudeDir = join(targetPath, '.claude');
320
247
  if (!existsSync(claudeDir)) {
@@ -327,20 +254,12 @@ export function generateStitchProxy(targetPath, gcpProject) {
327
254
  return { proxyPath };
328
255
  }
329
256
 
330
- /**
331
- * Resolve the gcloud CLI binary path.
332
- * Checks: system PATH → bundled at ~/.stitch-mcp/ → null.
333
- * @param {Function} exec - execSync function (for testing)
334
- * @returns {{ gcloudPath: string|null, source: string }}
335
- */
336
257
  export function findGcloud(exec = execSync) {
337
- // Try system PATH first
338
258
  try {
339
259
  exec('gcloud --version', { stdio: ['pipe', 'pipe', 'pipe'], timeout: 10000 });
340
260
  return { gcloudPath: 'gcloud', source: 'system' };
341
- } catch { /* not on PATH */ }
261
+ } catch {}
342
262
 
343
- // Windows: try Cloud SDK default install location
344
263
  if (process.platform === 'win32') {
345
264
  const winPath = join(process.env.LOCALAPPDATA || '', 'Google', 'Cloud SDK', 'google-cloud-sdk', 'bin', 'gcloud.cmd');
346
265
  if (existsSync(winPath)) {
@@ -348,7 +267,6 @@ export function findGcloud(exec = execSync) {
348
267
  }
349
268
  }
350
269
 
351
- // Try bundled at ~/.stitch-mcp/
352
270
  const bundledDir = join(homedir(), '.stitch-mcp');
353
271
  const bundledBin = process.platform === 'win32'
354
272
  ? join(bundledDir, 'google-cloud-sdk', 'bin', 'gcloud.cmd')
@@ -360,29 +278,10 @@ export function findGcloud(exec = execSync) {
360
278
  return { gcloudPath: null, source: 'not-found' };
361
279
  }
362
280
 
363
- /**
364
- * Fully automated Stitch MCP setup orchestrator.
365
- *
366
- * Steps:
367
- * 1. Find gcloud CLI
368
- * 2. Verify ADC credentials
369
- * 3. Detect GCP project
370
- * 4. Enable stitch.googleapis.com API
371
- * 5. Generate proxy script
372
- * 6. Install MCP config in .mcp.json
373
- * 7. Health-check the proxy
374
- *
375
- * @param {string} targetPath - Project root directory
376
- * @param {Object} options
377
- * @param {string} [options.gcpProject] - GCP project ID (auto-detected if omitted)
378
- * @param {Function} [options._exec] - execSync override for testing
379
- * @returns {Promise<Object>} Step-by-step results
380
- */
381
281
  export async function setupStitch(targetPath, options = {}) {
382
282
  const exec = options._exec || execSync;
383
283
  const steps = [];
384
284
 
385
- // Step 1: Find gcloud
386
285
  const { gcloudPath, source: gcloudSource } = findGcloud(exec);
387
286
  if (!gcloudPath) {
388
287
  steps.push({ step: 'find-gcloud', success: false, error: 'gcloud CLI not found. Install from: https://cloud.google.com/sdk/docs/install' });
@@ -392,7 +291,6 @@ export async function setupStitch(targetPath, options = {}) {
392
291
 
393
292
  const gcloudCmd = gcloudPath.includes(' ') ? `"${gcloudPath}"` : gcloudPath;
394
293
 
395
- // Step 2: Verify ADC credentials
396
294
  try {
397
295
  exec(`${gcloudCmd} auth application-default print-access-token`, {
398
296
  encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'], timeout: 15000
@@ -403,26 +301,23 @@ export async function setupStitch(targetPath, options = {}) {
403
301
  return { success: false, steps };
404
302
  }
405
303
 
406
- // Step 3: Detect GCP project
407
304
  let gcpProject = options.gcpProject;
408
305
  if (!gcpProject) {
409
- // Try config.json
410
306
  const configPath = join(targetPath, '.morph', 'config', 'config.json');
411
307
  if (existsSync(configPath)) {
412
308
  try {
413
309
  const config = JSON.parse(readFileSync(configPath, 'utf-8'));
414
310
  gcpProject = config.gcpProject || config.project?.gcpProject;
415
- } catch { /* invalid config */ }
311
+ } catch {}
416
312
  }
417
313
  }
418
314
  if (!gcpProject) {
419
- // Try gcloud config
420
315
  try {
421
316
  gcpProject = exec(`${gcloudCmd} config get-value project`, {
422
317
  encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'], timeout: 10000
423
318
  }).trim();
424
319
  if (!gcpProject || gcpProject === '(unset)') gcpProject = null;
425
- } catch { /* gcloud config failed */ }
320
+ } catch {}
426
321
  }
427
322
  if (!gcpProject) {
428
323
  steps.push({ step: 'detect-project', success: false, error: 'Could not detect GCP project. Pass it explicitly or set via: gcloud config set project <id>' });
@@ -430,7 +325,6 @@ export async function setupStitch(targetPath, options = {}) {
430
325
  }
431
326
  steps.push({ step: 'detect-project', success: true, gcpProject });
432
327
 
433
- // Step 4: Enable Stitch API
434
328
  try {
435
329
  exec(`${gcloudCmd} services enable stitch.googleapis.com --project=${gcpProject}`, {
436
330
  encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'], timeout: 30000
@@ -438,20 +332,17 @@ export async function setupStitch(targetPath, options = {}) {
438
332
  steps.push({ step: 'enable-api', success: true });
439
333
  } catch (err) {
440
334
  steps.push({ step: 'enable-api', success: false, error: `Failed to enable stitch API: ${err.message}` });
441
- // Non-fatal — API might already be enabled and user may lack permissions to re-enable
335
+ // Non-fatal: the API may already be enabled and the user may lack permission to re-enable it.
442
336
  }
443
337
 
444
- // Step 5: Generate proxy
445
338
  const { proxyPath } = generateStitchProxy(targetPath, gcpProject);
446
339
  steps.push({ step: 'generate-proxy', success: true, proxyPath });
447
340
 
448
- // Step 6: Install MCP config in .mcp.json
449
341
  const mcpResult = await installMcpServers(targetPath, {
450
342
  stitch: { type: 'stdio', command: 'node', args: ['.claude/stitch-proxy.cjs'] }
451
343
  });
452
344
  steps.push({ step: 'install-mcp', success: true, added: mcpResult.added, skipped: mcpResult.skipped });
453
345
 
454
- // Step 7: Health-check
455
346
  const healthResult = await healthCheckStitchProxy(proxyPath);
456
347
  steps.push({ step: 'health-check', ...healthResult });
457
348
 
@@ -462,11 +353,6 @@ export async function setupStitch(targetPath, options = {}) {
462
353
  return { success: allCriticalPassed, steps, gcpProject };
463
354
  }
464
355
 
465
- /**
466
- * Health-check the Stitch proxy by sending an MCP initialize handshake.
467
- * @param {string} proxyPath - Path to stitch-proxy.cjs
468
- * @returns {Promise<Object>} { success, protocolVersion?, error? }
469
- */
470
356
  export function healthCheckStitchProxy(proxyPath) {
471
357
  return new Promise((resolve) => {
472
358
  const initMsg = JSON.stringify({
@@ -492,7 +378,6 @@ export function healthCheckStitchProxy(proxyPath) {
492
378
 
493
379
  child.stdout.on('data', (data) => {
494
380
  output += data.toString();
495
- // Check if we got a response
496
381
  const lines = output.split('\n').filter(l => l.trim());
497
382
  for (const line of lines) {
498
383
  try {
@@ -509,7 +394,7 @@ export function healthCheckStitchProxy(proxyPath) {
509
394
  resolve({ success: false, error: resp.error.message || 'MCP error' });
510
395
  return;
511
396
  }
512
- } catch { /* not valid JSON yet */ }
397
+ } catch {}
513
398
  }
514
399
  });
515
400
 
@@ -530,38 +415,22 @@ export function healthCheckStitchProxy(proxyPath) {
530
415
  }
531
416
  });
532
417
 
533
- // Send the initialize message
534
418
  child.stdin.write(initMsg + '\n');
535
419
  child.stdin.end();
536
420
  });
537
421
  }
538
422
 
539
- /**
540
- * Install a single MCP with provided credentials
541
- * @param {string} targetPath - Project root directory
542
- * @param {string} name - MCP name
543
- * @param {Object} mcpEntry - MCP entry from registry
544
- * @param {Object} credentialValues - Map of envVar → value
545
- * @returns {Promise<Object>} Result from installMcpServers
546
- */
547
423
  export async function installMcpWithCredentials(targetPath, name, mcpEntry, credentialValues) {
548
424
  const config = patchConfigForPlatform({ ...mcpEntry.install.config });
549
425
  config.env = { ...config.env, ...credentialValues };
550
426
  return installMcpServers(targetPath, { [name]: config });
551
427
  }
552
428
 
553
- /**
554
- * Generate setup instructions for a credential-requiring MCP
555
- * @param {string} name - MCP name
556
- * @param {Object} mcpEntry - MCP entry from registry
557
- * @returns {Object} { configSnippet, credentialUrls, cliCommand }
558
- */
559
429
  export function generateSetupInstructions(name, mcpEntry) {
560
430
  const config = mcpEntry.install.config;
561
431
  const credentials = mcpEntry.install.credentials || [];
562
432
  const setupGuide = mcpEntry.install.setupGuide;
563
433
 
564
- // Remote HTTP MCPs — show claude mcp add command instead of config snippet
565
434
  if (isRemoteMcp(config)) {
566
435
  const configSnippet = JSON.stringify({ [name]: config }, null, 2);
567
436
  const credentialUrls = credentials.map(c => ({
@@ -577,7 +446,6 @@ export function generateSetupInstructions(name, mcpEntry) {
577
446
  };
578
447
  }
579
448
 
580
- // Build the config snippet with placeholder values
581
449
  const snippetConfig = { ...config };
582
450
  if (credentials.length > 0) {
583
451
  snippetConfig.env = {};
@@ -601,17 +469,6 @@ export function generateSetupInstructions(name, mcpEntry) {
601
469
  };
602
470
  }
603
471
 
604
- /**
605
- * Full orchestrator for MCP setup during init or `mcp setup`
606
- * @param {string} targetPath - Project root directory
607
- * @param {string} stack - Detected project stack
608
- * @param {Array} existingMcps - From detectClaudeConfig().mcpServers
609
- * @param {Object} options
610
- * @param {boolean} options.autoOnly - Only handle auto-installable MCPs
611
- * @param {string[]} [options.integrations] - `project.integrations`, matched like the stack
612
- * @param {string[]} [options.enabledPlugins] - `detectClaudeConfig().enabledPlugins`
613
- * @returns {Object} { autoInstallable, alreadyInstalled, needsManualSetup, notRelevant, prerequisitesMissing }
614
- */
615
472
  export function orchestrateMcpSetup(targetPath, stack, existingMcps, options = {}) {
616
473
  const registry = loadMcpRegistry();
617
474
  const relevant = filterByStack(registry, stack, options.integrations ?? []);
@@ -623,23 +480,19 @@ export function orchestrateMcpSetup(targetPath, stack, existingMcps, options = {
623
480
  const notRelevant = {};
624
481
  const prerequisitesMissing = {};
625
482
 
626
- // Categorize all MCPs
627
483
  for (const [name, entry] of Object.entries(registry.mcps)) {
628
484
  if (!entry.install) continue;
629
485
 
630
- // Check if already installed
631
486
  if (installed[name]) {
632
487
  alreadyInstalled[name] = { entry, source: installed[name].source };
633
488
  continue;
634
489
  }
635
490
 
636
- // Check if relevant to stack
637
491
  if (!relevant[name]) {
638
492
  notRelevant[name] = entry;
639
493
  continue;
640
494
  }
641
495
 
642
- // Check prerequisites
643
496
  const prereqResults = checkPrerequisites(entry);
644
497
  const unmetPrereqs = prereqResults.filter(p => !p.met);
645
498
  if (unmetPrereqs.length > 0) {
@@ -647,7 +500,6 @@ export function orchestrateMcpSetup(targetPath, stack, existingMcps, options = {
647
500
  continue;
648
501
  }
649
502
 
650
- // Categorize by install type
651
503
  if (entry.install.autoInstall) {
652
504
  autoInstallable[name] = entry;
653
505
  } else if (!options.autoOnly) {
@@ -665,18 +517,11 @@ export function orchestrateMcpSetup(targetPath, stack, existingMcps, options = {
665
517
  };
666
518
  }
667
519
 
668
- /**
669
- * Format MCP status table for display
670
- * @param {Object} orchestration - Result from orchestrateMcpSetup
671
- * @param {Object} justInstalled - Map of names just installed during this run
672
- * @returns {Array<Object>} Array of { name, status, detail }
673
- */
674
520
  export function formatMcpStatusTable(orchestration, justInstalled = {}) {
675
521
  const rows = [];
676
522
 
677
523
  const { autoInstallable, alreadyInstalled, needsManualSetup, notRelevant, prerequisitesMissing, registry } = orchestration;
678
524
 
679
- // Installed during this run
680
525
  for (const [name, entry] of Object.entries(justInstalled)) {
681
526
  const mcpEntry = registry.mcps[name];
682
527
  rows.push({
@@ -686,7 +531,6 @@ export function formatMcpStatusTable(orchestration, justInstalled = {}) {
686
531
  });
687
532
  }
688
533
 
689
- // Auto-installable but not yet installed (and not just-installed)
690
534
  for (const [name, entry] of Object.entries(autoInstallable)) {
691
535
  if (justInstalled[name]) continue;
692
536
  rows.push({
@@ -696,7 +540,6 @@ export function formatMcpStatusTable(orchestration, justInstalled = {}) {
696
540
  });
697
541
  }
698
542
 
699
- // Already installed
700
543
  for (const [name, { source }] of Object.entries(alreadyInstalled)) {
701
544
  rows.push({
702
545
  name,
@@ -705,7 +548,6 @@ export function formatMcpStatusTable(orchestration, justInstalled = {}) {
705
548
  });
706
549
  }
707
550
 
708
- // Needs manual setup
709
551
  for (const [name, entry] of Object.entries(needsManualSetup)) {
710
552
  rows.push({
711
553
  name,
@@ -714,7 +556,6 @@ export function formatMcpStatusTable(orchestration, justInstalled = {}) {
714
556
  });
715
557
  }
716
558
 
717
- // Prerequisites missing
718
559
  for (const [name, { unmet }] of Object.entries(prerequisitesMissing)) {
719
560
  const missing = unmet.map(p => p.name).join(', ');
720
561
  rows.push({
@@ -724,7 +565,6 @@ export function formatMcpStatusTable(orchestration, justInstalled = {}) {
724
565
  });
725
566
  }
726
567
 
727
- // Not relevant
728
568
  for (const [name] of Object.entries(notRelevant)) {
729
569
  rows.push({
730
570
  name,
@@ -1,15 +1,5 @@
1
- /**
2
- * Memory Installer — populates Claude Code persistent memory during `morph-spec init`.
3
- *
4
- * Writes two memory files (project + reference) plus an entry in MEMORY.md
5
- * so future Claude Code sessions enter pre-contextualized about the
6
- * morph-spec setup of this project.
7
- *
8
- * Memory location: ~/.claude/projects/<encoded-project-path>/memory/
9
- *
10
- * The path encoding mirrors Claude Code's own scheme: replace slashes and
11
- * colons with dashes. Tests can override via MORPH_MEMORY_ROOT env var.
12
- */
1
+ // The path encoding mirrors Claude Code's scheme (slashes and colons become dashes);
2
+ // MORPH_MEMORY_ROOT overrides the root in tests.
13
3
 
14
4
  import fs from 'fs-extra';
15
5
  import { join } from 'path';
@@ -41,8 +31,7 @@ metadata:
41
31
  - \`.morph/framework/MORPH.md\` — NLH charter (read once per session)
42
32
  - \`.morph/framework/agents.json\` — 8 agents: 7 domain personas + 1 stage persona (\`scout\`, read-only)
43
33
  - \`.morph/framework/standards/STANDARDS.json\` — standards index (load on demand)
44
- - \`.morph/framework/schemas/tasks.schema.json\` — JSON Schema for tasks.json
45
- - \`.morph/features/{feature}/\` — feature artifacts: 0-proposal / 1-design / 2-plan / 3-implement / 4-review
34
+ - \`.morph/features/{feature}/\` — feature artifacts: 2-plan / 1-design / 3-implement / 4-review
46
35
  - \`.morph/context/\` — long-lived architectural context (design-system, decisions)
47
36
  - \`.morph/state.json\` — feature state (managed by state-sync hook; Edit fallback allowed)
48
37
 
@@ -50,8 +39,8 @@ metadata:
50
39
 
51
40
  | Command | Skill invoked |
52
41
  |---------|---------------|
53
- | \`/morph-proposal {feature}\` | \`Skill(morph-proposal)\` — brainstorming → Gate 1 → plan → Gate 2 |
54
- | \`/morph-apply {feature}\` | \`Skill(morph-apply)\` — DAG-adaptive implement loop → review → Gate 3 |
42
+ | \`/morph-plan {feature}\` | \`Skill(morph-plan)\` — entrevista → plan.md → plan-check → gate do plano |
43
+ | \`/morph-apply {feature}\` | \`Skill(morph-apply)\` — fatias do plano → verify → review → Gate 3 |
55
44
  | \`/morph-status [feature]\` | inspect state.json |
56
45
  | \`/morph-archive {feature}\` | move completed feature to .morph/archive/ |
57
46
  | \`/morph-preflight\` | pre-deploy validation |
@@ -59,13 +48,12 @@ metadata:
59
48
 
60
49
  ## Core skills
61
50
 
62
- \`morph-proposal\`, \`morph-apply\`, \`morph-brainstorming\`, \`morph-plan\`, \`morph-implement\`, \`morph-review\`, \`morph-stack-scan\`, \`morph-standards\`
51
+ \`morph-plan\`, \`morph-apply\`, \`morph-uiux\`, \`morph-review\`, \`morph-hotfix\`, \`morph-standards\`
63
52
 
64
53
  **Why:** This project is managed by morph-spec — spec-driven autonomous development with trust-aware gates.
65
54
 
66
55
  **How to apply:**
67
56
  - When user mentions a feature, check \`.morph/features/\` first.
68
- - When user types \`/morph-proposal\`, invoke \`Skill(morph-proposal)\` directly.
69
57
  - In \`trust: "auto"\`, gates auto-pass; only AskUserQuestion on risk signals.
70
58
  `;
71
59
  }
@@ -83,25 +71,12 @@ metadata:
83
71
  - \`.morph/context/design-system.md\` — design tokens and components (source of truth)
84
72
  - \`.morph/framework/MORPH.md\` — NLH charter, Trust Mode rules, gates, skills table
85
73
  - \`.morph/framework/standards/STANDARDS.json\` — index; load specific standard via \`path\` field
86
- - \`.morph/framework/schemas/tasks.schema.json\` — tasks.json schema reference
87
74
  - \`.morph/framework/phases.json\` — phase folder mapping and skill associations
88
75
  - \`.morph/framework/agents.json\` — domain persona definitions (dotnet-senior, ef-modeler, etc.)
89
- - \`.morph/framework/evals/review-checklist.md\` — what the Gate 3 evaluator checks (verdict SHIP/FIX + evidence-typed blockers)
76
+ - \`.morph/framework/evals/review-checklist.md\` — what the Gate 3 verifier, reviewer and security check (verdict SHIP/FIX + evidence-typed blockers)
90
77
  `;
91
78
 
92
- /**
93
- * Populate Claude Code persistent memory for a morph-spec project.
94
- *
95
- * Idempotent: re-running overwrites the memory files with current info
96
- * but appends MEMORY.md index entries only if missing.
97
- *
98
- * @param {string} projectPath - Absolute path of the project root
99
- * @param {object} info - Project facts to embed in the memory
100
- * @param {string} info.projectName
101
- * @param {string} info.frameworkVersion
102
- * @param {string} [info.stack]
103
- * @returns {Promise<{ projectMemoryPath: string, referenceMemoryPath: string, indexPath: string }>}
104
- */
79
+ // Idempotent: memory files are overwritten, MEMORY.md entries appended only if missing.
105
80
  export async function installProjectMemory(projectPath, info) {
106
81
  const memoryRoot = getMemoryRoot(projectPath);
107
82
  await fs.ensureDir(memoryRoot);
@@ -1,67 +1,13 @@
1
- /**
2
- * templates-reconcile.js — reconcile a pre-existing `.morph/framework/templates/`
3
- * copy against the project's prior recorded morph-spec version.
4
- *
5
- * Shared by BOTH install paths that can encounter this directory:
6
- * - `morph-spec update` (src/commands/project/update.js) — always knows the
7
- * project's prior version (`checkProjectOutdated().current`, read from
8
- * `.morph/.morphversion` BEFORE this run overwrites it).
9
- * - `morph-spec init`/`setup-infra` (src/scripts/setup-infra.js) — the SAME
10
- * source (`getProjectMorphVersion()`, the function `checkProjectOutdated`
11
- * itself calls), read at the same point in the run (before its own write
12
- * of `.morph/.morphversion`), when it's available — e.g. a repair run
13
- * over an already-initialized project. Falls back to a plain warning when
14
- * it isn't (a project with no recorded version has nothing here for this
15
- * function to reconcile against safely).
16
- */
17
-
18
1
  import { existsSync } from 'fs';
19
2
  import { compareVersions } from '../../utils/version-checker.js';
20
3
  import { realEffects } from './effects.js';
21
4
 
22
- /**
23
- * Last CLI version whose `update`/`setup-infra` still wrote
24
- * `.morph/framework/templates/` unconditionally, before Task 2 of the R3
25
- * perf plan removed that copy. Used as the decision boundary in
26
- * `reconcileLegacyTemplatesCopy` below — see its doc comment for why a
27
- * version check is used instead of diffing file content.
28
- */
5
+ // Last CLI version whose install still copied `.morph/framework/templates/` unconditionally.
29
6
  export const LAST_VERSION_THAT_COPIED_TEMPLATES = '8.17.0';
30
7
 
31
- /**
32
- * Reconcile a pre-existing `.morph/framework/templates/` copy now that
33
- * neither install path unconditionally writes it anymore (Task 2, R3 perf
34
- * plan — resolution falls back to the npm package's own framework/templates/,
35
- * see template-registry.js / architecture-scanner.js's layered resolver).
36
- *
37
- * Decided from the project's PRIOR recorded CLI version (before THIS run
38
- * writes a new one), not by diffing content against the package. A content
39
- * diff can't tell "stale copy from an older CLI version" apart from
40
- * "deliberate user override" — and staleness is the dominant case for
41
- * anyone who actually runs `update`/`setup-infra` again, since template
42
- * content does change between releases. The version boundary is unambiguous
43
- * instead:
44
- *
45
- * - No local copy → nothing to do.
46
- * - Prior version is unrecorded, OR <= LAST_VERSION_THAT_COPIED_TEMPLATES
47
- * → the copy could ONLY have been put there by the OLD installer:
48
- * `.morph/framework/**` is Edit-denied to the agent (MORPH_DENY in
49
- * hooks-installer.js — "installer-managed, immutable at runtime"),
50
- * the directory is gitignored, and every install/update before this fix
51
- * unconditionally wiped + recopied it. There is no path by which a
52
- * project on an old version could have a genuine override sitting
53
- * here. Safe to delete unconditionally — it is dead weight, not data.
54
- * - Prior version > LAST_VERSION_THAT_COPIED_TEMPLATES → this install path
55
- * has never written this directory for this project. Anything found
56
- * here was placed deliberately (by the user or other tooling) — this is
57
- * the forward-looking override use case the layered resolver exists
58
- * for. Preserve untouched, unconditionally, with no diffing.
59
- *
60
- * @param {string} templatesDest - .morph/framework/templates (may not exist)
61
- * @param {string|null} priorProjectVersion - project's recorded morph-spec
62
- * version BEFORE this run (e.g. `checkProjectOutdated().current`)
63
- * @returns {'absent'|'removed-legacy'|'preserved-override'}
64
- */
8
+ // Decided by the PRIOR recorded version, not by diffing: a diff can't tell a stale copy from an override.
9
+ // Unrecorded or <= the boundary → only the old installer could have written it, delete.
10
+ // Newer → placed deliberately, preserve.
65
11
  export function reconcileLegacyTemplatesCopy(templatesDest, priorProjectVersion, fx = realEffects()) {
66
12
  if (!existsSync(templatesDest)) return 'absent';
67
13
 
@@ -74,8 +20,7 @@ export function reconcileLegacyTemplatesCopy(templatesDest, priorProjectVersion,
74
20
  fx.remove(templatesDest);
75
21
  return 'removed-legacy';
76
22
  } catch {
77
- // Couldn't remove — don't risk a half-broken state; leave it and let
78
- // the layered resolver's override-first check pick it up as before.
23
+ // Couldn't remove: leave it; the resolver's override-first check picks it up.
79
24
  return 'preserved-override';
80
25
  }
81
26
  }
@@ -1,4 +1,5 @@
1
- import { dotnetBuildCommandFor, resolveTestBuildTargets } from '../tasks/test-runner.js';
1
+ import { resolve } from 'node:path';
2
+ import { discoverDotnetBuildTargets, dotnetBuildCommandFor } from '../tasks/test-runner.js';
2
3
 
3
4
  const FILTER = /--filter(?:[ =:])(?:"([^"]*)"|'([^']*)'|(\S+))/;
4
5
  const quote = (s) => `"${String(s).replace(/"/g, '\\"')}"`;
@@ -32,7 +33,7 @@ const VERBOSITY = /\s(?:-v|--verbosity)[ =:]?\S+/g;
32
33
  export function dotnetBuildCommands(node, projectPath) {
33
34
  const configuration = CONFIGURATION.exec(node.command ?? '');
34
35
  const flag = configuration ? ` -c ${configuration[1] ?? configuration[2]}` : '';
35
- return resolveTestBuildTargets(node)
36
+ return discoverDotnetBuildTargets(node, resolve(projectPath || '.', node.cwd || '.'))
36
37
  .map((target) => dotnetBuildCommandFor(target, projectPath))
37
38
  .filter(Boolean)
38
39
  .map((command) => `${command.replace(VERBOSITY, '')} -v m${flag}`);