@polymorphism-tech/morph-spec 9.0.0 → 9.2.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 (345) hide show
  1. package/CLAUDE.md +8 -9
  2. package/README.md +38 -36
  3. package/bin/morph-spec.js +11 -63
  4. package/bin/validate.js +1 -39
  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 +2 -1
  9. package/framework/MORPH.md +3 -3
  10. package/framework/agents.json +19 -11
  11. package/framework/commands/morph-apply.md +1 -1
  12. package/framework/commands/morph-status.md +1 -1
  13. package/framework/docs/CLI.md +22 -31
  14. package/framework/evals/review-checklist.md +12 -2
  15. package/framework/hooks/README.md +8 -44
  16. package/framework/hooks/claude-code/core/block-destructive-worktree-removal.js +6 -49
  17. package/framework/hooks/claude-code/core/block-raw-worktree-add.js +2 -35
  18. package/framework/hooks/claude-code/core/chain.js +4 -65
  19. package/framework/hooks/claude-code/core/comment-guard.js +2 -16
  20. package/framework/hooks/claude-code/core/enforce-phase-writes.js +6 -50
  21. package/framework/hooks/claude-code/core/gate-guard.js +7 -67
  22. package/framework/hooks/claude-code/core/graph-refresh-kick.js +3 -24
  23. package/framework/hooks/claude-code/core/heavy-gate.js +3 -21
  24. package/framework/hooks/claude-code/core/heavy-release.js +2 -13
  25. package/framework/hooks/claude-code/core/loop-detect.js +2 -31
  26. package/framework/hooks/claude-code/core/pre-bash-denylist.js +0 -4
  27. package/framework/hooks/claude-code/core/protect-spec-files.js +7 -67
  28. package/framework/hooks/claude-code/core/state-sync.js +12 -68
  29. package/framework/hooks/claude-code/core/telemetry-log.js +12 -93
  30. package/framework/hooks/claude-code/core/validate-completion.js +11 -57
  31. package/framework/hooks/claude-code/core/warn-bash-source-write.js +1 -9
  32. package/framework/hooks/claude-code/core/worktree-context.js +10 -110
  33. package/framework/hooks/claude-code/core/worktree-create.js +6 -70
  34. package/framework/hooks/claude-code/core/worktree-env-inject.js +5 -56
  35. package/framework/hooks/claude-code/core/worktree-remove.js +5 -31
  36. package/framework/hooks/claude-code/statusline.sh +11 -11
  37. package/framework/hooks/shared/activity-logger.js +9 -89
  38. package/framework/hooks/shared/agent-reap.js +2 -17
  39. package/framework/hooks/shared/build-check.js +6 -75
  40. package/framework/hooks/shared/claim-registry.js +9 -119
  41. package/framework/hooks/shared/command-tokens.js +6 -51
  42. package/framework/hooks/shared/comment-rules.js +5 -20
  43. package/framework/hooks/shared/comment-scanner.js +6 -25
  44. package/framework/hooks/shared/comment-stripper.js +11 -85
  45. package/framework/hooks/shared/e2e-check.js +0 -0
  46. package/framework/hooks/shared/errata.js +2 -15
  47. package/framework/hooks/shared/feature-locator.js +11 -95
  48. package/framework/hooks/shared/frontend-locations.js +1 -3
  49. package/framework/hooks/shared/gate-question.js +1 -7
  50. package/framework/hooks/shared/git-exclude.js +3 -20
  51. package/framework/hooks/shared/git-paths.js +8 -104
  52. package/framework/hooks/shared/graph-freshness.js +22 -166
  53. package/framework/hooks/shared/graph-refresh.js +21 -149
  54. package/framework/hooks/shared/heavy-command.js +4 -12
  55. package/framework/hooks/shared/hook-response.js +8 -83
  56. package/framework/hooks/shared/morph-cli.js +8 -52
  57. package/framework/hooks/shared/payload-utils.js +10 -132
  58. package/framework/hooks/shared/phase-utils.js +1 -42
  59. package/framework/hooks/shared/redact-secrets.js +4 -15
  60. package/framework/hooks/shared/reparse-scan.js +6 -66
  61. package/framework/hooks/shared/slice-progress.js +4 -23
  62. package/framework/hooks/shared/source-files.js +4 -17
  63. package/framework/hooks/shared/state-reader.js +13 -183
  64. package/framework/hooks/shared/state-writer.js +8 -52
  65. package/framework/hooks/shared/stdin-reader.js +1 -11
  66. package/framework/hooks/shared/telemetry-logger.js +7 -67
  67. package/framework/hooks/shared/transcript-usage.js +7 -29
  68. package/framework/hooks/shared/worktree-root.js +7 -29
  69. package/framework/rules/csharp-standards.md +1 -1
  70. package/framework/rules/frontend-standards.md +1 -0
  71. package/framework/rules/hangfire-standards.md +1 -1
  72. package/framework/rules/neon-standards.md +1 -1
  73. package/framework/rules/nextjs-standards.md +1 -1
  74. package/framework/skills/core/create-umbraco-lp/SKILL.md +1 -1
  75. package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +1 -1
  76. package/framework/skills/core/morph-apply/SKILL.md +4 -4
  77. package/framework/skills/core/morph-hotfix/SKILL.md +3 -3
  78. package/framework/skills/core/morph-machine/SKILL.md +1 -1
  79. package/framework/skills/core/morph-media/SKILL.md +1 -1
  80. package/framework/skills/core/morph-plan/SKILL.md +9 -7
  81. package/framework/skills/core/morph-review/SKILL.md +9 -6
  82. package/framework/skills/core/morph-standards/SKILL.md +1 -1
  83. package/framework/skills/core/morph-uiux/SKILL.md +29 -12
  84. package/framework/standards/STANDARDS.json +36 -14
  85. package/framework/standards/STANDARDS.meta.json +16 -2
  86. package/framework/standards/ai-agents/agent-archetypes.md +2 -2
  87. package/framework/standards/ai-agents/agent-session.md +1 -1
  88. package/framework/standards/ai-agents/conversational-agent-with-phases.md +4 -4
  89. package/framework/standards/ai-agents/durable-workflows-hitl.md +3 -3
  90. package/framework/standards/ai-agents/guardrails.md +1 -1
  91. package/framework/standards/ai-agents/multi-agent-patterns.md +2 -2
  92. package/framework/standards/ai-agents/observability-patterns.md +1 -1
  93. package/framework/standards/ai-agents/production.md +1 -1
  94. package/framework/standards/ai-agents/prompt-sources.md +2 -2
  95. package/framework/standards/ai-agents/providers/model-registry.md +1 -1
  96. package/framework/standards/ai-agents/rag-custom-pgvector.md +2 -2
  97. package/framework/standards/ai-agents/setup.md +2 -2
  98. package/framework/standards/ai-agents/sweet-spot.md +2 -2
  99. package/framework/standards/ai-agents/tool-adapters.md +0 -2
  100. package/framework/standards/ai-agents/vector-data-extensions.md +4 -4
  101. package/framework/standards/ai-agents/workflows.md +1 -1
  102. package/framework/standards/architecture/code-comments.md +1 -1
  103. package/framework/standards/architecture/vertical-slice/vertical-slice.md +1 -1
  104. package/framework/standards/backend/api/validation.md +1 -1
  105. package/framework/standards/backend/dotnet/testing.md +10 -4
  106. package/framework/standards/backend/dotnet/vsa-handler-patterns.md +5 -5
  107. package/framework/standards/backend/integrations/hangfire/hangfire-jobs.md +1 -1
  108. package/framework/standards/backend/integrations/neon-auth/neon-auth.md +2 -2
  109. package/framework/standards/data/nosql/cache/redis.md +1 -1
  110. package/framework/standards/data/nosql/cosmos-db.md +3 -3
  111. package/framework/standards/frontend/admin-app/forms.md +1 -1
  112. package/framework/standards/frontend/design-system/art-direction.md +88 -0
  113. package/framework/standards/frontend/design-system/premium-finish.md +77 -2
  114. package/framework/standards/frontend/nextjs/testing.md +10 -6
  115. package/framework/standards/infrastructure/neon/neon-pgvector.md +5 -5
  116. package/framework/templates/.idea/morph-templates.xml +92 -0
  117. package/framework/templates/.vscode/morph-templates.code-snippets +186 -0
  118. package/framework/templates/CLAUDE.md.core.template +3 -3
  119. package/framework/templates/IDE-SNIPPETS.md +262 -262
  120. package/framework/templates/REGISTRY.json +9 -9
  121. package/framework/templates/code/dotnet/abstractions/api-endpoint.cs +7 -7
  122. package/framework/templates/code/dotnet/feature/request.cs.hbs +4 -4
  123. package/framework/templates/code/dotnet/feature/response.cs.hbs +4 -4
  124. package/framework/templates/code/sql/ai-agents/pgvector-embeddings-migration.sql.template +91 -91
  125. package/framework/templates/docs/plan.md +3 -1
  126. package/framework/templates/docs/ui-mockups.md +4 -0
  127. package/framework/templates/frontend/nextjs/client-component.tsx.hbs +26 -26
  128. package/framework/templates/frontend/nextjs/feature-form.tsx.hbs +56 -56
  129. package/framework/templates/frontend/nextjs/tsconfig.json.hbs +26 -26
  130. package/framework/templates/infrastructure/docker/Dockerfile.template +58 -58
  131. package/framework/templates/infrastructure/docker/dockerfile-api.dockerfile +38 -38
  132. package/framework/templates/infrastructure/docker/dockerfile-web.dockerfile +48 -48
  133. package/framework/templates/infrastructure/github/actions/docker-build-push/action.yml.hbs +45 -45
  134. package/framework/templates/infrastructure/github/actions/health-check/action.yml.hbs +27 -27
  135. package/framework/templates/infrastructure/github/workflows/docker-build-push.yml.hbs +59 -59
  136. package/framework/templates/infrastructure/github/workflows/dotnet-build.yml.hbs +39 -39
  137. package/framework/templates/meta-prompts/fusion/fusion-agent.md +76 -76
  138. package/framework/templates/meta-prompts/fusion/fusion-aggregator.md +100 -100
  139. package/framework/templates/state.template.json +17 -17
  140. package/framework/templates/ui/design-system.css +226 -226
  141. package/package.json +1 -1
  142. package/src/commands/context/context-refresh.js +0 -16
  143. package/src/commands/dashboard/dashboard.js +2 -8
  144. package/src/commands/e2e/e2e.js +17 -104
  145. package/src/commands/graph/affected.js +0 -14
  146. package/src/commands/graph/build.js +11 -50
  147. package/src/commands/graph/explain.js +0 -16
  148. package/src/commands/graph/status.js +1 -12
  149. package/src/commands/mcp/mcp-setup.js +4 -62
  150. package/src/commands/observability/cost.js +5 -57
  151. package/src/commands/observability/telemetry.js +1 -14
  152. package/src/commands/project/branch.js +4 -45
  153. package/src/commands/project/claim.js +4 -48
  154. package/src/commands/project/doctor.js +48 -302
  155. package/src/commands/project/env.js +1 -27
  156. package/src/commands/project/fleet.js +3 -32
  157. package/src/commands/project/init.js +4 -68
  158. package/src/commands/project/install-plugin-cmd.js +0 -1
  159. package/src/commands/project/standards.js +1 -62
  160. package/src/commands/project/status.js +0 -53
  161. package/src/commands/project/update.js +31 -239
  162. package/src/commands/project/worktree.js +108 -882
  163. package/src/commands/scaffold/scaffold.js +0 -25
  164. package/src/commands/state/advance.js +14 -85
  165. package/src/commands/state/approve.js +15 -97
  166. package/src/commands/state/archive.js +12 -64
  167. package/src/commands/state/auto-approval.js +2 -5
  168. package/src/commands/state/create.js +22 -109
  169. package/src/commands/state/delete.js +11 -63
  170. package/src/commands/state/finish.js +98 -475
  171. package/src/commands/state/gate-check.js +22 -132
  172. package/src/commands/state/gate-decision.js +3 -39
  173. package/src/commands/state/handoff.js +3 -30
  174. package/src/commands/state/list.js +14 -84
  175. package/src/commands/state/report-check.js +8 -58
  176. package/src/commands/state/retype.js +4 -26
  177. package/src/commands/validation/index.js +0 -3
  178. package/src/commands/validation/mutate.js +11 -11
  179. package/src/commands/validation/plan-check.js +4 -2
  180. package/src/commands/validation/tests-audit.js +7 -0
  181. package/src/commands/validation/validate-feature.js +10 -97
  182. package/src/commands/validation/verify.js +30 -113
  183. package/src/core/paths/output-schema.js +2 -101
  184. package/src/core/state/feature-identity.js +1 -25
  185. package/src/core/state/gate-order.js +0 -27
  186. package/src/core/state/lifecycle-audit.js +8 -100
  187. package/src/core/state/state-manager.js +22 -304
  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 +0 -1
  191. package/src/core/work-types.js +1 -36
  192. package/src/dashboard/app/App.jsx +1 -8
  193. package/src/dashboard/app/components/ActivityFeed.jsx +0 -5
  194. package/src/dashboard/app/components/CommandPalette.jsx +0 -7
  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 +1 -5
  207. package/src/dashboard/app/lib/project.js +0 -12
  208. package/src/dashboard/app/main.jsx +13 -13
  209. package/src/dashboard/app/pages/AgentMap.jsx +1 -8
  210. package/src/dashboard/app/pages/FeatureDetail.jsx +1 -5
  211. package/src/dashboard/app/pages/Features.jsx +1 -8
  212. package/src/dashboard/app/pages/Guide.jsx +0 -1
  213. package/src/dashboard/app/pages/HookInspector.jsx +2 -23
  214. package/src/dashboard/app/pages/PhasePipeline.jsx +0 -3
  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 +10 -69
  220. package/src/dashboard/scanner/agents-scanner.js +1 -23
  221. package/src/dashboard/scanner/docs-scanner.js +0 -6
  222. package/src/dashboard/scanner/feature-status.js +2 -5
  223. package/src/dashboard/scanner/features-scanner.js +10 -66
  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 +2 -6
  231. package/src/dashboard/vite-plugin-morph-data.js +5 -19
  232. package/src/dashboard/vite.config.js +24 -24
  233. package/src/lib/ai/ai-checks.js +7 -52
  234. package/src/lib/ai/ai-project-scan.js +14 -108
  235. package/src/lib/ai/code-audit.js +7 -63
  236. package/src/lib/ai/pin-drift.js +8 -51
  237. package/src/lib/ai/registry-audit.js +11 -90
  238. package/src/lib/ci/ci-verify.js +4 -19
  239. package/src/lib/ci/github-ci.js +9 -37
  240. package/src/lib/ci/workflow-scaffold.js +14 -10
  241. package/src/lib/detectors/claude-config-detector.js +13 -74
  242. package/src/lib/detectors/index.js +4 -50
  243. package/src/lib/e2e/applicability.js +37 -0
  244. package/src/lib/e2e/compose-env.js +5 -66
  245. package/src/lib/e2e/stack-teardown.js +6 -40
  246. package/src/lib/generators/architecture-scanner.js +18 -156
  247. package/src/lib/generators/handoff-generator.js +9 -53
  248. package/src/lib/generators/scaffold-generator.js +5 -101
  249. package/src/lib/generators/settings-generator.js +4 -37
  250. package/src/lib/git/branch-classifier.js +11 -84
  251. package/src/lib/git/default-branch.js +14 -169
  252. package/src/lib/git/evidence-staleness.js +1 -0
  253. package/src/lib/git/gh.js +2 -17
  254. package/src/lib/git/pending-prs.js +6 -37
  255. package/src/lib/git/pr-files.js +3 -28
  256. package/src/lib/git/pr-index.js +3 -34
  257. package/src/lib/git/tree-fingerprint.js +3 -15
  258. package/src/lib/git/tree-state.js +6 -33
  259. package/src/lib/graph/god-nodes.js +2 -22
  260. package/src/lib/graph/graph-health.js +3 -24
  261. package/src/lib/graph/graph-loader.js +5 -56
  262. package/src/lib/graph/graphify-runtime.js +7 -37
  263. package/src/lib/graph/graphifyignore.js +8 -74
  264. package/src/lib/graph/sanitize.js +16 -136
  265. package/src/lib/heavy/cleanup.js +1 -1
  266. package/src/lib/installers/effects.js +8 -93
  267. package/src/lib/installers/mcp-installer.js +9 -169
  268. package/src/lib/installers/memory-installer.js +4 -27
  269. package/src/lib/installers/templates-reconcile.js +5 -60
  270. package/src/lib/mutation/adapters.js +6 -17
  271. package/src/lib/mutation/dotnet-introspection.js +20 -106
  272. package/src/lib/mutation/runner.js +23 -75
  273. package/src/lib/mutation/subset.js +1 -4
  274. package/src/lib/mutation/trx.js +0 -1
  275. package/src/lib/plan/artifact-vocabulary.js +5 -25
  276. package/src/lib/plan/checks.js +34 -33
  277. package/src/lib/plan/plan-parser.js +7 -8
  278. package/src/lib/projects/registry.js +7 -44
  279. package/src/lib/review-reports.js +11 -26
  280. package/src/lib/risk-detector.js +41 -173
  281. package/src/lib/specs/adr-index.js +6 -34
  282. package/src/lib/specs/frontmatter.js +2 -15
  283. package/src/lib/specs/gfm-table.js +1 -7
  284. package/src/lib/specs/revocations.js +1 -14
  285. package/src/lib/specs/screen-checklist.js +47 -124
  286. package/src/lib/stack/dotnet-build-ignores.js +2 -6
  287. package/src/lib/stack/stack-profile.js +1 -28
  288. package/src/lib/stack/stack-scan.js +1 -8
  289. package/src/lib/stack-filter.js +4 -27
  290. package/src/lib/standards/registry.js +1 -7
  291. package/src/lib/standards/sections.js +2 -13
  292. package/src/lib/standards/verified-against.js +19 -191
  293. package/src/lib/tasks/eval-node.js +21 -169
  294. package/src/lib/tasks/linux-runner.js +18 -142
  295. package/src/lib/tasks/test-nodes.js +43 -240
  296. package/src/lib/tasks/test-runner.js +43 -341
  297. package/src/lib/telemetry/metrics.js +7 -36
  298. package/src/lib/telemetry/telemetry-reader.js +13 -125
  299. package/src/lib/telemetry/verify-event.js +2 -30
  300. package/src/lib/tests-audit/audit.js +96 -0
  301. package/src/lib/validators/code-budget/code-budget-validator.js +4 -44
  302. package/src/lib/validators/code-budget/index.js +0 -7
  303. package/src/lib/validators/contracts/contract-compliance-validator.js +2 -24
  304. package/src/lib/validators/contracts/index.js +0 -6
  305. package/src/lib/validators/nextjs/index.js +0 -5
  306. package/src/lib/validators/nextjs/next-component-validator.js +4 -81
  307. package/src/lib/validators/packages/index.js +0 -6
  308. package/src/lib/validators/packages/package-validator.js +6 -56
  309. package/src/lib/validators/reuse/index.js +0 -8
  310. package/src/lib/validators/reuse/reuse-validator.js +11 -101
  311. package/src/lib/validators/reuse/symbol-scanner.js +6 -62
  312. package/src/lib/validators/traceability-check.js +5 -63
  313. package/src/lib/validators/validation-runner.js +21 -158
  314. package/src/lib/validators/verify-runner.js +85 -376
  315. package/src/lib/work-type-detector.js +5 -67
  316. package/src/lib/worktree/docker-audit.js +17 -136
  317. package/src/lib/worktree/env-allocator.js +10 -98
  318. package/src/lib/worktree/feature-truth.js +12 -115
  319. package/src/lib/worktree/finish-backup.js +32 -242
  320. package/src/lib/worktree/fleet-scanner.js +13 -95
  321. package/src/lib/worktree/ignored-triage.js +15 -91
  322. package/src/lib/worktree/vscode-settings.js +2 -3
  323. package/src/lib/worktree/worktree-audit.js +21 -213
  324. package/src/lib/worktree/worktree-include.js +3 -5
  325. package/src/scripts/global-install.js +3 -9
  326. package/src/scripts/install-plugin.js +1 -13
  327. package/src/scripts/setup-infra.js +17 -147
  328. package/src/utils/agents-installer.js +29 -276
  329. package/src/utils/banner.js +4 -25
  330. package/src/utils/claude-md-injector.js +1 -37
  331. package/src/utils/claude-settings-manager.js +1 -52
  332. package/src/utils/file-copier.js +11 -95
  333. package/src/utils/hooks-installer.js +21 -298
  334. package/src/utils/skills-installer.js +4 -101
  335. package/src/utils/version-checker.js +4 -83
  336. package/framework/commands/morph-proposal.md +0 -9
  337. package/framework/hooks/git/commit-msg/conventional-commits.sh +0 -33
  338. package/framework/hooks/git/pre-commit/agents.sh +0 -25
  339. package/framework/hooks/git/pre-commit/orchestrator.sh +0 -64
  340. package/framework/hooks/git/pre-push/run-tests.sh +0 -44
  341. package/src/commands/utils/index.js +0 -3
  342. package/src/lib/detectors/design-system-detector.js +0 -188
  343. package/src/lib/generators/design-system-generator.js +0 -508
  344. package/src/lib/specs/old-format.js +0 -68
  345. package/src/utils/agent-id-mapper.js +0 -11
@@ -1,22 +1,6 @@
1
- /**
2
- * `morph-spec e2e up|status|down` — lifecycle of the verification stack.
3
- *
4
- * The stack is deliberately NOT torn down between verifies: cold-starting a
5
- * .NET container per task is the difference between a loop people run and one
6
- * they disable. `up` brings it (or reuses it), `status` says whether what is
7
- * running still matches the working tree, and `down` releases it — which
8
- * `morph-spec finish` also does as part of teardown.
9
- *
10
- * Every subcommand acts in the TREE THAT OWNS THE FEATURE: run from the
11
- * primary root for a feature that lives in a worktree, the compose file, the
12
- * bind mount and the stamp are all the worktree's — the root's copy is a
13
- * different checkout of different code.
14
- *
15
- * All the real logic lives in framework/hooks/shared/e2e-check.js, shared with
16
- * the `verify` e2e node so the two can never drift.
17
- *
18
- * @module commands/e2e
19
- */
1
+ // The stack is deliberately NOT torn down between verifies: a cold .NET start per task is the
2
+ // difference between a loop people run and one they disable. Every subcommand acts in the tree
3
+ // that owns the feature.
20
4
 
21
5
  import chalk from 'chalk';
22
6
  import { execFileSync } from 'child_process';
@@ -46,22 +30,10 @@ import { createHeavyGate, HeavyBusyError } from '../../lib/heavy/gate.js';
46
30
  import { afterHeavyRun } from '../../lib/heavy/cleanup.js';
47
31
  import { readHeavyConfig } from '../../../framework/hooks/shared/heavy-config.js';
48
32
 
49
- /** Exit code for a usage error, matching `verify`'s convention. */
50
33
  const EXIT_USAGE = 2;
51
34
  const EXIT_BUSY = 3;
52
35
 
53
- // ============================================================================
54
- // Shared context
55
- // ============================================================================
56
-
57
- /**
58
- * Everything the three subcommands need, resolved once — against the tree
59
- * that owns the feature (its live worktree when it has one, the root
60
- * otherwise).
61
- *
62
- * Returns null (after printing) when that tree has no compose file — a usage
63
- * error here, unlike in the verify node where it is a graded skip.
64
- */
36
+ // No compose file is a usage error here, unlike in the verify node where it is a graded skip.
65
37
  function resolveContext(featureName, { json = false } = {}) {
66
38
  const root = resolvePrimaryRoot(process.cwd());
67
39
  const found = findWorktreeForFeature(featureName, root);
@@ -95,49 +67,24 @@ function resolveContext(featureName, { json = false } = {}) {
95
67
  };
96
68
  }
97
69
 
98
- /**
99
- * Compose environment: the port block this feature owns, plus any credentials
100
- * from the gitignored auth env file.
101
- *
102
- * The port block comes from `buildComposeEnv` rather than being assembled here
103
- * so this command and the `e2e` node inside `verify` agree by construction —
104
- * isolation that exists only in the command nobody runs is no isolation at all.
105
- */
70
+ // The port block comes from `buildComposeEnv`, so this command and the verify node agree by
71
+ // construction.
106
72
  function buildEnv({ cwd, config, featureName, projectName, gitCommonDir }) {
107
- // COMPOSE_PROJECT_NAME is part of the port layout now; the explicit key stays
108
- // as the fail-open floor for a repo with no git common dir (no block → no
109
- // layout), where the project name is still what isolates the containers.
73
+ // The explicit key is the floor for a repo with no git common dir (no port layout).
110
74
  return {
111
75
  COMPOSE_PROJECT_NAME: projectName,
112
76
  ...buildComposeEnv({ cwd, config, featureName, gitCommonDir }),
113
77
  };
114
78
  }
115
79
 
116
- // ============================================================================
117
- // up
118
- // ============================================================================
119
-
120
- /**
121
- * `morph-spec e2e up <feature> [--fresh]`
122
- *
123
- * `--fresh` is destructive on purpose: it tears the previous stack down WITH
124
- * its named volumes and forces the auth seed to run again. A rebuild that
125
- * preserved the database would hand the next suite the previous run's rows,
126
- * and a storageState captured against that database would keep authenticating
127
- * a user that no longer exists.
128
- *
129
- * Exit codes: 0 = stack healthy · 1 = failed to come up (or auth broke, or the
130
- * compose cannot be isolated inside a worktree) · 2 = usage (no compose file /
131
- * no docker).
132
- */
80
+ // `--fresh` drops the named volumes and reruns the auth seed: a preserved database would hand the
81
+ // next suite stale rows and a storageState for a user that no longer exists.
133
82
  export async function e2eUpCommand(featureName, options = {}) {
134
83
  const ctx = resolveContext(featureName, options);
135
84
  if (!ctx) process.exit(EXIT_USAGE);
136
85
 
137
- // Isolation BEFORE the docker check: `COMPOSE_PROJECT_NAME` isolates
138
- // container names but not host ports, and a compose that hardcodes them (or
139
- // names its containers) collides with the sibling worktree no matter what we
140
- // inject. Same rule as the verify node — see decideIsolation().
86
+ // Isolation BEFORE the docker check: a compose that hardcodes host ports or container names
87
+ // collides with a sibling worktree whatever we inject.
141
88
  const lint = lintComposeIsolation(readComposeText(ctx.cwd, ctx.composeFile));
142
89
  const isolation = decideIsolation({ findings: lint.findings, insideWorktree: ctx.insideWorktree });
143
90
  if (isolation.status === 'fail') {
@@ -189,8 +136,7 @@ export async function e2eUpCommand(featureName, options = {}) {
189
136
  ? prepareAuth(ctx.cwd, { config: ctx.config, env: ctx.env, forceSeed: Boolean(options.fresh) })
190
137
  : { status: 'skip', reason: null };
191
138
 
192
- // Same warnings[] contract as the verify e2e node: a seedCommand that never
193
- // runs because `mode` switches it off is reported, never swallowed.
139
+ // A seedCommand that `mode` switches off is reported, never swallowed.
194
140
  const warnings = [];
195
141
  if (isolation.status === 'warn') warnings.push(isolation.reason);
196
142
  if (auth.warning) warnings.push(auth.warning);
@@ -225,32 +171,13 @@ export async function e2eUpCommand(featureName, options = {}) {
225
171
  process.exit(failed ? 1 : 0);
226
172
  }
227
173
 
228
- // ============================================================================
229
- // status
230
- // ============================================================================
231
-
232
- /**
233
- * Where the app actually landed.
234
- *
235
- * The orchestrator hands this to the verifier instead of letting it guess
236
- * `localhost:3000` — each feature owns a port block, so the default port is the
237
- * one address most likely to be wrong. Mirrors what `playwright.config.ts` is
238
- * asked to do, deliberately: one rule, two readers.
239
- *
240
- * @param {Record<string,string>} env
241
- * @returns {string}
242
- */
174
+ // Handed to the verifier so it never guesses `localhost:3000`: each feature owns a port block.
175
+ // Mirrors what `playwright.config.ts` does: one rule, two readers.
243
176
  export function resolveBaseUrl(env = {}) {
244
177
  return env.PLAYWRIGHT_BASE_URL ?? `http://localhost:${env.PORT ?? 3000}`;
245
178
  }
246
179
 
247
- /**
248
- * `morph-spec e2e status <feature> [--json]`
249
- *
250
- * Answers the question that matters before trusting a green run: is what is
251
- * running still built from the code on disk? Exit 0 when up AND fresh, 1
252
- * otherwise — so it can gate a script.
253
- */
180
+ // Exit 0 only when up AND built from the code on disk, so it can gate a script.
254
181
  export async function e2eStatusCommand(featureName, options = {}) {
255
182
  const ctx = resolveContext(featureName, options);
256
183
  if (!ctx) process.exit(EXIT_USAGE);
@@ -309,17 +236,8 @@ export async function e2eStatusCommand(featureName, options = {}) {
309
236
  process.exit(status.up && status.healthy && fresh ? 0 : 1);
310
237
  }
311
238
 
312
- // ============================================================================
313
- // down
314
- // ============================================================================
315
-
316
- /**
317
- * `morph-spec e2e down <feature> [--volumes]`
318
- *
319
- * Teardown is best-effort by design — a stack that will not come down must
320
- * never block `finish`. The freshness stamp goes regardless: a stamp that
321
- * outlives its containers would let the next `up` reuse a stack that is gone.
322
- */
239
+ // Best-effort: a stack that will not come down must never block `finish`. The freshness stamp
240
+ // goes regardless, or the next `up` would reuse a stack that is gone.
323
241
  export async function e2eDownCommand(featureName, options = {}) {
324
242
  const ctx = resolveContext(featureName, options);
325
243
  if (!ctx) process.exit(EXIT_USAGE);
@@ -348,10 +266,6 @@ export async function e2eDownCommand(featureName, options = {}) {
348
266
  process.exit(0);
349
267
  }
350
268
 
351
- // ============================================================================
352
- // Shared helpers
353
- // ============================================================================
354
-
355
269
  function readConfig(cwd) {
356
270
  try {
357
271
  const path = join(cwd, '.morph', 'config', 'config.json');
@@ -370,7 +284,6 @@ function readComposeText(cwd, composeFile) {
370
284
  }
371
285
  }
372
286
 
373
- /** Same resolution (and same fail-open) as verify-runner's. */
374
287
  function resolveGitCommonDir(cwd) {
375
288
  try {
376
289
  const common = execFileSync('git', ['rev-parse', '--git-common-dir'], {
@@ -1,23 +1,9 @@
1
- /**
2
- * `morph-spec graph affected <símbolo>` — blast radius saneado.
3
- *
4
- * O valor medido não é recall: contra o grep, o grafo achou ZERO arquivos a
5
- * mais. É precisão — 58% menos ruído em 12 símbolos do ProspectPRO (1.092
6
- * arquivos no grep, 266 usos reais) e a classificação da relação. `IHandler`
7
- * aparece em 193 arquivos no grep e tem 2 usos reais: é implementada por 98.
8
- *
9
- * @module graph-affected
10
- */
11
1
  import chalk from 'chalk';
12
2
  import { loadGraph, getFreshness, describeStale } from '../../lib/graph/graph-loader.js';
13
3
  import { buildIndex, resolveLabel, affected } from '../../lib/graph/sanitize.js';
14
4
 
15
5
  const FALLBACK_HINT = 'Sem resposta do grafo, caia para grep/Glob — o grafo é um subconjunto estrito do grep, então cair para grep não perde recall.';
16
6
 
17
- /**
18
- * @param {string} symbol
19
- * @param {{path?: string, json?: boolean, prod?: boolean, usesOnly?: boolean}} [options]
20
- */
21
7
  export async function graphAffectedCommand(symbol, options = {}) {
22
8
  const projectPath = options.path || process.cwd();
23
9
  const loaded = loadGraph(projectPath);
@@ -1,11 +1,3 @@
1
- /**
2
- * `morph-spec graph build` (extração completa) e `graph refresh` (incremental).
3
- *
4
- * Fail-open: sem graphify no PATH, explica o que fazer e sai 1 — sem stack
5
- * trace. Um projeto sem grafo continua funcionando por grep.
6
- *
7
- * @module graph-build
8
- */
9
1
  import chalk from 'chalk';
10
2
  import { readFileSync, existsSync, unlinkSync, writeFileSync } from 'node:fs';
11
3
  import { join } from 'node:path';
@@ -33,26 +25,9 @@ function readProjectTags(projectPath) {
33
25
  }
34
26
  }
35
27
 
36
- /**
37
- * O `.graphifyignore` com que o graphify vai rodar.
38
- *
39
- * Na raiz: escrito ou migrado (writeGraphifyignore). Numa worktree o arquivo é
40
- * do checkout e nunca é reescrito — seria uma mudança na branch da feature.
41
- * Ausente ali (a raiz ainda não o versionou), as regras — as da raiz
42
- * (`ignoreTextOf`), ou as geradas — entram só durante a rodada e saem depois
43
- * (`cleanup`): um `?? .graphifyignore` deixado na worktree faz o `finish` e
44
- * o `worktree remove --if-clean` recusarem com worktree-dirty (medido), e pôr
45
- * o arquivo no exclude do common dir esconderia também o da raiz. O arquivo leva
46
- * `TEMP_IGNORE_MARKER` na 1ª linha: se este processo morrer antes do
47
- * `cleanup`, a sobra é reconhecida pelo conteúdo — a próxima rodada a reusa e o
48
- * `finish` a apaga (`dropLeftoverIgnore`). O graphify não herda o da raiz: o
49
- * ignore dele para na raiz de VCS mais próxima, que numa worktree é ela mesma
50
- * (detect.py `_load_graphifyignore`).
51
- *
52
- * @param {string} projectPath
53
- * @param {string[]} tags
54
- * @returns {{written: boolean, migrated?: boolean, outdated?: object, temporary?: boolean, path: string, cleanup: () => void}}
55
- */
28
+ // Numa worktree o arquivo do checkout nunca é reescrito; ausente, as regras entram só durante
29
+ // a rodada (uma sobra suja a worktree e o `finish` recusa). `TEMP_IGNORE_MARKER` na 1ª linha
30
+ // deixa a próxima rodada e o `finish` reconhecerem a sobra de um processo morto.
56
31
  function prepareIgnore(projectPath, tags) {
57
32
  const none = () => {};
58
33
  if (!isSecondaryWorktree(projectPath)) return { ...writeGraphifyignore(projectPath, tags), cleanup: none };
@@ -71,9 +46,6 @@ function prepareIgnore(projectPath, tags) {
71
46
  };
72
47
  }
73
48
 
74
- /**
75
- * @param {{path?: string, json?: boolean, refresh?: boolean}} [options]
76
- */
77
49
  export async function graphBuildCommand(options = {}) {
78
50
  const projectPath = options.path || process.cwd();
79
51
  const refresh = options.refresh === true;
@@ -93,19 +65,14 @@ export async function graphBuildCommand(options = {}) {
93
65
  process.exit(1);
94
66
  }
95
67
 
96
- // O ignore precisa existir ANTES da extração, senão o grafo nasce com o
97
- // ruído que a ADR-004 existe para tirar.
68
+ // O ignore precisa existir ANTES da extração, senão o grafo nasce com o ruído.
98
69
  const ignore = prepareIgnore(projectPath, readProjectTags(projectPath));
99
70
  if (ignore.outdated && !options.json) {
100
71
  console.error(chalk.yellow(`\n⚠ ${describeOutdatedIgnore(ignore.outdated)}`));
101
72
  }
102
73
 
103
- // Na árvore principal o refresh em background (framework/hooks/shared/
104
- // graph-refresh.js) escreve o mesmo graph.json: dois graphify ao mesmo tempo
105
- // disputam o arquivo e todos os núcleos. Numa worktree ninguém mais escreve
106
- // o graph.json dela — o background só toca a raiz —, e esperar o lock da
107
- // raiz ali prometeria "o grafo chega ao HEAD" sobre um refresh que nunca a
108
- // alcança. Sem git não há lock, e o comando segue sozinho.
74
+ // Na raiz o refresh em background escreve o mesmo graph.json: dois graphify disputam o arquivo
75
+ // e os núcleos. Numa worktree só este comando escreve o grafo dela, e sem git não há lock.
109
76
  const primary = isPrimaryTree(projectPath);
110
77
  const lock = primary ? acquireRefreshLock(projectPath) : null;
111
78
  const running = primary && !lock ? readRefreshLock(projectPath) : null;
@@ -127,24 +94,18 @@ export async function graphBuildCommand(options = {}) {
127
94
  ? ['update', projectPath]
128
95
  : ['extract', projectPath, '--code-only'];
129
96
 
130
- // Capturamos o output em vez de herdar o terminal porque precisamos
131
- // inspecioná-lo: o graphify sai 1 tanto quando quebra quanto quando não há
132
- // código para indexar, e essas duas coisas não podem virar a mesma mensagem.
133
- // HEAD e regras lidos ANTES: são o que o graphify leu.
97
+ // Output capturado: o graphify sai 1 tanto ao quebrar quanto sem código para indexar, e as duas
98
+ // coisas não podem virar a mesma mensagem. HEAD e regras lidos ANTES: são o que o graphify leu.
134
99
  const head = readHead(projectPath);
135
100
  const ignoreHash = ignoreRulesHash(projectPath);
136
101
  let run;
137
102
  let empty = false;
138
103
  try {
139
104
  run = runGraphify(projectPath, args, { timeout: null });
140
- // Projeto sem código é o estado normal logo depois de um `init`, não uma
141
- // falha: o graphify diz "found 0 code" / "graph is empty", sai 1 e não
142
- // escreve graph.json. Tratar isso como erro faria todo init greenfield
143
- // terminar em vermelho.
105
+ // Projeto sem código (logo depois de um `init`) não é falha: o graphify sai 1 sem graph.json.
144
106
  empty = run.status !== 0 && (/found 0 code/i.test(run.output) || /graph is empty/i.test(run.output));
145
- // O registro é o que torna "untouched" em dia: sem ele, um refresh que não
146
- // mudou a topologia deixa o carimbo no commit antigo e o grafo parece
147
- // velho para sempre (framework/hooks/shared/graph-freshness.js).
107
+ // Sem o registro, um refresh que não mudou a topologia deixa o carimbo no commit antigo
108
+ // e o grafo parece velho para sempre.
148
109
  if (!empty) {
149
110
  recordRefreshRun(projectPath, {
150
111
  head, exit: run.status ?? null, ignoreHash, unavailable: Boolean(run.unavailable),
@@ -1,25 +1,9 @@
1
- /**
2
- * `morph-spec graph explain <símbolo>` — vizinhança com citação arquivo:linha.
3
- *
4
- * É o comando preferido nas personas: no eval, `explain` foi consistentemente
5
- * mais preciso que `query`, que trunca por budget e traz ruído.
6
- *
7
- * Cada aresta carrega a procedência (`via`), então dá para ver o que o
8
- * saneamento recuperou — `coalesced` são exatamente as arestas que o graphify
9
- * cru perde.
10
- *
11
- * @module graph-explain
12
- */
13
1
  import chalk from 'chalk';
14
2
  import { loadGraph, getFreshness, describeStale } from '../../lib/graph/graph-loader.js';
15
3
  import { buildIndex, resolveLabel, neighborhood, isTestPath } from '../../lib/graph/sanitize.js';
16
4
 
17
5
  const FALLBACK_HINT = 'Sem resposta do grafo, caia para grep/Glob.';
18
6
 
19
- /**
20
- * @param {string} symbol
21
- * @param {{path?: string, json?: boolean}} [options]
22
- */
23
7
  export async function graphExplainCommand(symbol, options = {}) {
24
8
  const projectPath = options.path || process.cwd();
25
9
  const loaded = loadGraph(projectPath);
@@ -1,12 +1,3 @@
1
- /**
2
- * `morph-spec graph status` — existência, tamanho, frescor e saúde do
3
- * saneamento.
4
- *
5
- * Ausência de grafo NÃO é erro (ADR-002): exit 0 com `available: false`. Um
6
- * projeto sem grafo é um estado válido, e o framework segue por grep.
7
- *
8
- * @module graph-status
9
- */
10
1
  import chalk from 'chalk';
11
2
  import {
12
3
  loadGraph, getFreshness, describeStale, refreshState, staleCause,
@@ -24,9 +15,7 @@ function summarizeSanitize(index) {
24
15
  return counts;
25
16
  }
26
17
 
27
- /**
28
- * @param {{path?: string, json?: boolean}} [options]
29
- */
18
+ // Ausência de grafo não é erro: exit 0 com `available: false`, e o framework segue por grep.
30
19
  export async function graphStatusCommand(options = {}) {
31
20
  const projectPath = options.path || process.cwd();
32
21
  const installed = detectGraphifyVersion();
@@ -1,15 +1,3 @@
1
- /**
2
- * MCP Setup Command
3
- *
4
- * Install and configure MCP servers for morph-spec projects.
5
- *
6
- * Usage:
7
- * morph-spec mcp setup — Interactive wizard showing all MCPs
8
- * morph-spec mcp setup <name> — Setup a specific MCP
9
- * morph-spec mcp setup --list — Show MCP install status
10
- * morph-spec mcp setup --auto — Non-interactive auto-install credential-free MCPs
11
- */
12
-
13
1
  import chalk from 'chalk';
14
2
  import inquirer from 'inquirer';
15
3
  import ora from 'ora';
@@ -31,17 +19,9 @@ import {
31
19
  import { detectClaudeConfig } from '../../lib/detectors/claude-config-detector.js';
32
20
  import { detectProject } from '../../lib/detectors/index.js';
33
21
 
34
- /**
35
- * Detect the current project's stack.
36
- * Prefers .morph/config/config.json (set during morph-spec init) over filesystem re-detection.
37
- *
38
- * Exported for testing: the stack decides which MCPs are relevant, and it is
39
- * the one branch here worth pinning — `.morph/config/config.json` is the only
40
- * place a compound stack (`dotnet-nextjs`) survives, since filesystem
41
- * re-detection collapses it to whichever half it notices first.
42
- */
22
+ // config.json is the only place a compound stack (`dotnet-nextjs`) survives: filesystem
23
+ // re-detection collapses it to whichever half it notices first.
43
24
  export async function detectStack(targetPath) {
44
- // 1. Prefer the authoritative config (handles compound stacks like nextjs+dotnet)
45
25
  try {
46
26
  const configPath = join(targetPath, '.morph', 'config', 'config.json');
47
27
  if (existsSync(configPath)) {
@@ -55,7 +35,6 @@ export async function detectStack(targetPath) {
55
35
  // Fall through to filesystem detection
56
36
  }
57
37
 
58
- // 2. Fallback: detect from filesystem
59
38
  try {
60
39
  const results = await detectProject(targetPath, { conversation: false, generateStandards: false });
61
40
  return results.structure?.stack || 'unknown';
@@ -64,14 +43,8 @@ export async function detectStack(targetPath) {
64
43
  }
65
44
  }
66
45
 
67
- /**
68
- * Detect the project's integrations (`neon`, `stripe`...), same sources and
69
- * order as detectStack. The detectors never fold them into the stack string, so
70
- * without this an MCP scoped to an integration (neon, asaas) is never offered.
71
- *
72
- * @param {string} targetPath
73
- * @returns {Promise<string[]>}
74
- */
46
+ // The detectors never fold integrations into the stack string: without this, an MCP scoped
47
+ // to an integration (neon, asaas) is never offered.
75
48
  export async function detectIntegrations(targetPath) {
76
49
  try {
77
50
  const configPath = join(targetPath, '.morph', 'config', 'config.json');
@@ -95,7 +68,6 @@ export async function mcpSetupCommand(name, options) {
95
68
  const targetPath = process.cwd();
96
69
 
97
70
  try {
98
- // Detect current environment
99
71
  const stack = await detectStack(targetPath);
100
72
  const integrations = await detectIntegrations(targetPath);
101
73
  const claudeConfig = await detectClaudeConfig(targetPath);
@@ -114,7 +86,6 @@ export async function mcpSetupCommand(name, options) {
114
86
  return setupSpecificMcp(targetPath, name, stack, existingMcps);
115
87
  }
116
88
 
117
- // Interactive wizard
118
89
  return interactiveWizard(targetPath, stack, existingMcps, integrations, enabledPlugins);
119
90
  } catch (err) {
120
91
  console.error(chalk.red(`Error: ${err.message}`));
@@ -122,9 +93,6 @@ export async function mcpSetupCommand(name, options) {
122
93
  }
123
94
  }
124
95
 
125
- /**
126
- * Show MCP status list (--list)
127
- */
128
96
  async function showStatusList(targetPath, stack, existingMcps, integrations, enabledPlugins) {
129
97
  const orchestration = orchestrateMcpSetup(targetPath, stack, existingMcps, { integrations, enabledPlugins });
130
98
  const statusRows = formatMcpStatusTable(orchestration, {});
@@ -156,9 +124,6 @@ async function showStatusList(targetPath, stack, existingMcps, integrations, ena
156
124
  console.log('');
157
125
  }
158
126
 
159
- /**
160
- * Auto-install credential-free MCPs (--auto)
161
- */
162
127
  async function autoInstall(targetPath, stack, existingMcps, integrations, enabledPlugins) {
163
128
  const orchestration = orchestrateMcpSetup(targetPath, stack, existingMcps, { autoOnly: true, integrations, enabledPlugins });
164
129
  const autoNames = Object.keys(orchestration.autoInstallable);
@@ -176,7 +141,6 @@ async function autoInstall(targetPath, stack, existingMcps, integrations, enable
176
141
  console.log(chalk.gray(` Skipped (already configured): ${result.skipped.join(', ')}`));
177
142
  }
178
143
 
179
- // Install companion skills for MCPs that define them
180
144
  const skillResults = installCompanionSkills(orchestration.autoInstallable);
181
145
  for (const r of skillResults) {
182
146
  if (r.success) {
@@ -189,9 +153,6 @@ async function autoInstall(targetPath, stack, existingMcps, integrations, enable
189
153
  console.log('');
190
154
  }
191
155
 
192
- /**
193
- * Setup a specific MCP by name
194
- */
195
156
  async function setupSpecificMcp(targetPath, name, stack, existingMcps) {
196
157
  const registry = loadMcpRegistry();
197
158
  const mcpEntry = registry.mcps[name];
@@ -208,7 +169,6 @@ async function setupSpecificMcp(targetPath, name, stack, existingMcps) {
208
169
  return;
209
170
  }
210
171
 
211
- // Check if already installed
212
172
  const isInstalled = existingMcps.some(s => s.name.toLowerCase().includes(name));
213
173
  if (isInstalled) {
214
174
  const { reconfigure } = await inquirer.prompt([{
@@ -220,7 +180,6 @@ async function setupSpecificMcp(targetPath, name, stack, existingMcps) {
220
180
  if (!reconfigure) return;
221
181
  }
222
182
 
223
- // Check prerequisites
224
183
  const prereqs = mcpEntry.install.prerequisites || [];
225
184
  for (const prereq of prereqs) {
226
185
  try {
@@ -234,20 +193,17 @@ async function setupSpecificMcp(targetPath, name, stack, existingMcps) {
234
193
  }
235
194
  }
236
195
 
237
- // Show warnings
238
196
  for (const warning of mcpEntry.install.warnings || []) {
239
197
  console.log(chalk.yellow(`\n ⚠ ${warning}`));
240
198
  }
241
199
 
242
200
  const credentials = mcpEntry.install.credentials || [];
243
201
 
244
- // Stitch MCP — automated setup via setupStitch()
245
202
  if (name === 'stitch' && !mcpEntry.install.autoInstall) {
246
203
  console.log(chalk.cyan(`\n Setting up Stitch MCP (automated)\n`));
247
204
  console.log(chalk.gray(` Usage: ${mcpEntry.usage}`));
248
205
  console.log('');
249
206
 
250
- // Allow user to provide GCP project if auto-detection might fail
251
207
  let gcpProject;
252
208
  const gcpCred = credentials.find(c => c.envVar === 'STITCH_GCP_PROJECT');
253
209
  const { gcpProjectInput } = await inquirer.prompt([{
@@ -260,7 +216,6 @@ async function setupSpecificMcp(targetPath, name, stack, existingMcps) {
260
216
  const spinner = ora('Setting up Stitch MCP...').start();
261
217
  const result = await setupStitch(targetPath, { gcpProject });
262
218
 
263
- // Report step-by-step results
264
219
  for (const step of result.steps) {
265
220
  if (step.success) {
266
221
  switch (step.step) {
@@ -288,7 +243,6 @@ async function setupSpecificMcp(targetPath, name, stack, existingMcps) {
288
243
  if (failedStep?.error) {
289
244
  console.log(chalk.red(` ${failedStep.error}`));
290
245
  }
291
- // Show health-check warning if it failed but setup otherwise succeeded
292
246
  const healthStep = result.steps.find(s => s.step === 'health-check');
293
247
  if (healthStep && !healthStep.success) {
294
248
  console.log(chalk.yellow(` ⚠ Health-check: ${healthStep.error}`));
@@ -318,7 +272,6 @@ async function setupSpecificMcp(targetPath, name, stack, existingMcps) {
318
272
  return;
319
273
  }
320
274
 
321
- // Auto-installable — just install
322
275
  if (mcpEntry.install.autoInstall) {
323
276
  const spinner = ora(`Installing ${name}...`).start();
324
277
  const result = await installAutoMcps(targetPath, { [name]: mcpEntry });
@@ -331,7 +284,6 @@ async function setupSpecificMcp(targetPath, name, stack, existingMcps) {
331
284
  return;
332
285
  }
333
286
 
334
- // Remote HTTP MCP — show setup command and optionally auto-configure
335
287
  if (isRemoteMcp(mcpEntry.install.config)) {
336
288
  console.log(chalk.cyan(`\n Setting up ${name} MCP (remote)\n`));
337
289
  console.log(chalk.gray(` Usage: ${mcpEntry.usage}`));
@@ -372,7 +324,6 @@ async function setupSpecificMcp(targetPath, name, stack, existingMcps) {
372
324
  return;
373
325
  }
374
326
 
375
- // Needs credentials (stdio MCPs)
376
327
  console.log(chalk.cyan(`\n Setting up ${name} MCP\n`));
377
328
  console.log(chalk.gray(` Usage: ${mcpEntry.usage}`));
378
329
  console.log('');
@@ -400,7 +351,6 @@ async function setupSpecificMcp(targetPath, name, stack, existingMcps) {
400
351
  await installMcpWithCredentials(targetPath, name, mcpEntry, credentialValues);
401
352
  spinner.succeed(`${name} MCP configured`);
402
353
  } else {
403
- // Show config snippet
404
354
  const instructions = generateSetupInstructions(name, mcpEntry);
405
355
  console.log(chalk.gray('\n Add to .mcp.json → mcpServers:'));
406
356
  console.log(chalk.gray(' ' + '─'.repeat(55)));
@@ -417,9 +367,6 @@ async function setupSpecificMcp(targetPath, name, stack, existingMcps) {
417
367
  console.log('');
418
368
  }
419
369
 
420
- /**
421
- * Interactive wizard showing all MCPs
422
- */
423
370
  async function interactiveWizard(targetPath, stack, existingMcps, integrations, enabledPlugins) {
424
371
  const orchestration = orchestrateMcpSetup(targetPath, stack, existingMcps, { integrations, enabledPlugins });
425
372
 
@@ -435,7 +382,6 @@ async function interactiveWizard(targetPath, stack, existingMcps, integrations,
435
382
  console.log(chalk.green(` Already configured: ${alreadyNames.join(', ')}`));
436
383
  }
437
384
 
438
- // Auto-install
439
385
  if (autoNames.length > 0) {
440
386
  const { installAuto } = await inquirer.prompt([{
441
387
  type: 'confirm',
@@ -449,7 +395,6 @@ async function interactiveWizard(targetPath, stack, existingMcps, integrations,
449
395
  const result = await installAutoMcps(targetPath, orchestration.autoInstallable);
450
396
  spinner.succeed(`Installed: ${result.added.join(', ')}`);
451
397
 
452
- // Install companion skills for MCPs that define them
453
398
  const skillResults = installCompanionSkills(orchestration.autoInstallable);
454
399
  for (const r of skillResults) {
455
400
  if (r.success) {
@@ -461,7 +406,6 @@ async function interactiveWizard(targetPath, stack, existingMcps, integrations,
461
406
  }
462
407
  }
463
408
 
464
- // Credential MCPs
465
409
  if (manualNames.length > 0) {
466
410
  console.log(chalk.cyan('\n Credential-requiring MCPs:'));
467
411
 
@@ -481,7 +425,6 @@ async function interactiveWizard(targetPath, stack, existingMcps, integrations,
481
425
  }]);
482
426
 
483
427
  if (setupNow) {
484
- // Stitch — automated setup via setupStitch()
485
428
  if (name === 'stitch' && !entry.install.autoInstall) {
486
429
  const gcpCred = entry.install.credentials?.find(c => c.envVar === 'STITCH_GCP_PROJECT');
487
430
  const { gcpProject } = await inquirer.prompt([{
@@ -507,7 +450,6 @@ async function interactiveWizard(targetPath, stack, existingMcps, integrations,
507
450
  continue;
508
451
  }
509
452
 
510
- // Remote MCPs — launch OAuth via claude mcp add
511
453
  if (isRemoteMcp(entry.install.config)) {
512
454
  console.log(chalk.cyan(` Launching ${capitalizedName} OAuth...`));
513
455
  try {