@polymorphism-tech/morph-spec 9.0.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 (304) hide show
  1. package/CLAUDE.md +8 -9
  2. package/README.md +38 -36
  3. package/bin/morph-spec.js +4 -62
  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 +1 -1
  9. package/framework/MORPH.md +1 -1
  10. package/framework/agents.json +12 -4
  11. package/framework/commands/morph-apply.md +1 -1
  12. package/framework/commands/morph-status.md +1 -1
  13. package/framework/docs/CLI.md +6 -17
  14. package/framework/hooks/claude-code/core/block-destructive-worktree-removal.js +6 -49
  15. package/framework/hooks/claude-code/core/block-raw-worktree-add.js +2 -35
  16. package/framework/hooks/claude-code/core/chain.js +4 -65
  17. package/framework/hooks/claude-code/core/comment-guard.js +2 -16
  18. package/framework/hooks/claude-code/core/enforce-phase-writes.js +6 -50
  19. package/framework/hooks/claude-code/core/gate-guard.js +7 -67
  20. package/framework/hooks/claude-code/core/graph-refresh-kick.js +3 -24
  21. package/framework/hooks/claude-code/core/heavy-gate.js +3 -21
  22. package/framework/hooks/claude-code/core/heavy-release.js +2 -13
  23. package/framework/hooks/claude-code/core/loop-detect.js +2 -31
  24. package/framework/hooks/claude-code/core/pre-bash-denylist.js +0 -4
  25. package/framework/hooks/claude-code/core/protect-spec-files.js +7 -67
  26. package/framework/hooks/claude-code/core/state-sync.js +11 -67
  27. package/framework/hooks/claude-code/core/telemetry-log.js +12 -93
  28. package/framework/hooks/claude-code/core/validate-completion.js +11 -57
  29. package/framework/hooks/claude-code/core/warn-bash-source-write.js +1 -9
  30. package/framework/hooks/claude-code/core/worktree-context.js +10 -110
  31. package/framework/hooks/claude-code/core/worktree-create.js +6 -70
  32. package/framework/hooks/claude-code/core/worktree-env-inject.js +5 -56
  33. package/framework/hooks/claude-code/core/worktree-remove.js +5 -31
  34. package/framework/hooks/shared/activity-logger.js +9 -89
  35. package/framework/hooks/shared/agent-reap.js +2 -17
  36. package/framework/hooks/shared/build-check.js +6 -75
  37. package/framework/hooks/shared/claim-registry.js +9 -119
  38. package/framework/hooks/shared/command-tokens.js +6 -51
  39. package/framework/hooks/shared/comment-rules.js +5 -20
  40. package/framework/hooks/shared/comment-scanner.js +6 -25
  41. package/framework/hooks/shared/comment-stripper.js +11 -85
  42. package/framework/hooks/shared/e2e-check.js +0 -0
  43. package/framework/hooks/shared/errata.js +2 -15
  44. package/framework/hooks/shared/feature-locator.js +11 -95
  45. package/framework/hooks/shared/frontend-locations.js +1 -3
  46. package/framework/hooks/shared/gate-question.js +1 -7
  47. package/framework/hooks/shared/git-exclude.js +3 -20
  48. package/framework/hooks/shared/git-paths.js +8 -104
  49. package/framework/hooks/shared/graph-freshness.js +22 -166
  50. package/framework/hooks/shared/graph-refresh.js +21 -149
  51. package/framework/hooks/shared/heavy-command.js +4 -12
  52. package/framework/hooks/shared/hook-response.js +8 -83
  53. package/framework/hooks/shared/morph-cli.js +8 -52
  54. package/framework/hooks/shared/payload-utils.js +10 -132
  55. package/framework/hooks/shared/phase-utils.js +1 -42
  56. package/framework/hooks/shared/redact-secrets.js +4 -15
  57. package/framework/hooks/shared/reparse-scan.js +6 -66
  58. package/framework/hooks/shared/slice-progress.js +4 -23
  59. package/framework/hooks/shared/source-files.js +4 -17
  60. package/framework/hooks/shared/state-reader.js +13 -183
  61. package/framework/hooks/shared/state-writer.js +8 -52
  62. package/framework/hooks/shared/stdin-reader.js +1 -11
  63. package/framework/hooks/shared/telemetry-logger.js +7 -67
  64. package/framework/hooks/shared/transcript-usage.js +7 -29
  65. package/framework/hooks/shared/worktree-root.js +7 -29
  66. package/framework/rules/csharp-standards.md +1 -1
  67. package/framework/rules/frontend-standards.md +1 -0
  68. package/framework/rules/hangfire-standards.md +1 -1
  69. package/framework/rules/neon-standards.md +1 -1
  70. package/framework/skills/core/create-umbraco-lp/SKILL.md +1 -1
  71. package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +1 -1
  72. package/framework/skills/core/morph-apply/SKILL.md +2 -2
  73. package/framework/skills/core/morph-hotfix/SKILL.md +1 -1
  74. package/framework/skills/core/morph-machine/SKILL.md +1 -1
  75. package/framework/skills/core/morph-media/SKILL.md +1 -1
  76. package/framework/skills/core/morph-plan/SKILL.md +2 -2
  77. package/framework/skills/core/morph-review/SKILL.md +1 -1
  78. package/framework/skills/core/morph-standards/SKILL.md +1 -1
  79. package/framework/skills/core/morph-uiux/SKILL.md +29 -12
  80. package/framework/standards/STANDARDS.json +34 -12
  81. package/framework/standards/STANDARDS.meta.json +16 -2
  82. package/framework/standards/ai-agents/agent-archetypes.md +2 -2
  83. package/framework/standards/ai-agents/agent-session.md +1 -1
  84. package/framework/standards/ai-agents/conversational-agent-with-phases.md +4 -4
  85. package/framework/standards/ai-agents/durable-workflows-hitl.md +3 -3
  86. package/framework/standards/ai-agents/guardrails.md +1 -1
  87. package/framework/standards/ai-agents/multi-agent-patterns.md +2 -2
  88. package/framework/standards/ai-agents/observability-patterns.md +1 -1
  89. package/framework/standards/ai-agents/production.md +1 -1
  90. package/framework/standards/ai-agents/prompt-sources.md +2 -2
  91. package/framework/standards/ai-agents/providers/model-registry.md +1 -1
  92. package/framework/standards/ai-agents/rag-custom-pgvector.md +2 -2
  93. package/framework/standards/ai-agents/setup.md +2 -2
  94. package/framework/standards/ai-agents/sweet-spot.md +2 -2
  95. package/framework/standards/ai-agents/tool-adapters.md +0 -2
  96. package/framework/standards/ai-agents/vector-data-extensions.md +4 -4
  97. package/framework/standards/ai-agents/workflows.md +1 -1
  98. package/framework/standards/architecture/code-comments.md +1 -1
  99. package/framework/standards/architecture/vertical-slice/vertical-slice.md +1 -1
  100. package/framework/standards/backend/api/validation.md +1 -1
  101. package/framework/standards/backend/dotnet/vsa-handler-patterns.md +5 -5
  102. package/framework/standards/backend/integrations/hangfire/hangfire-jobs.md +1 -1
  103. package/framework/standards/backend/integrations/neon-auth/neon-auth.md +2 -2
  104. package/framework/standards/data/nosql/cache/redis.md +1 -1
  105. package/framework/standards/data/nosql/cosmos-db.md +3 -3
  106. package/framework/standards/frontend/admin-app/forms.md +1 -1
  107. package/framework/standards/frontend/design-system/art-direction.md +88 -0
  108. package/framework/standards/frontend/design-system/premium-finish.md +77 -2
  109. package/framework/standards/infrastructure/neon/neon-pgvector.md +5 -5
  110. package/framework/templates/CLAUDE.md.core.template +3 -3
  111. package/framework/templates/REGISTRY.json +9 -9
  112. package/framework/templates/docs/ui-mockups.md +4 -0
  113. package/package.json +1 -1
  114. package/src/commands/context/context-refresh.js +0 -16
  115. package/src/commands/dashboard/dashboard.js +2 -8
  116. package/src/commands/e2e/e2e.js +17 -104
  117. package/src/commands/graph/affected.js +0 -14
  118. package/src/commands/graph/build.js +11 -50
  119. package/src/commands/graph/explain.js +0 -16
  120. package/src/commands/graph/status.js +1 -12
  121. package/src/commands/mcp/mcp-setup.js +4 -62
  122. package/src/commands/observability/cost.js +5 -57
  123. package/src/commands/observability/telemetry.js +1 -14
  124. package/src/commands/project/branch.js +4 -45
  125. package/src/commands/project/claim.js +4 -48
  126. package/src/commands/project/doctor.js +48 -302
  127. package/src/commands/project/env.js +1 -27
  128. package/src/commands/project/fleet.js +3 -32
  129. package/src/commands/project/init.js +4 -68
  130. package/src/commands/project/install-plugin-cmd.js +0 -1
  131. package/src/commands/project/standards.js +1 -62
  132. package/src/commands/project/status.js +0 -53
  133. package/src/commands/project/update.js +31 -239
  134. package/src/commands/project/worktree.js +108 -882
  135. package/src/commands/scaffold/scaffold.js +0 -25
  136. package/src/commands/state/advance.js +14 -85
  137. package/src/commands/state/approve.js +14 -96
  138. package/src/commands/state/archive.js +12 -64
  139. package/src/commands/state/auto-approval.js +2 -5
  140. package/src/commands/state/create.js +22 -109
  141. package/src/commands/state/delete.js +11 -63
  142. package/src/commands/state/finish.js +98 -475
  143. package/src/commands/state/gate-check.js +22 -132
  144. package/src/commands/state/gate-decision.js +3 -39
  145. package/src/commands/state/handoff.js +3 -30
  146. package/src/commands/state/list.js +14 -84
  147. package/src/commands/state/report-check.js +8 -58
  148. package/src/commands/state/retype.js +4 -26
  149. package/src/commands/validation/index.js +0 -3
  150. package/src/commands/validation/mutate.js +2 -5
  151. package/src/commands/validation/validate-feature.js +5 -94
  152. package/src/commands/validation/verify.js +15 -98
  153. package/src/core/paths/output-schema.js +2 -101
  154. package/src/core/state/feature-identity.js +1 -25
  155. package/src/core/state/gate-order.js +0 -27
  156. package/src/core/state/lifecycle-audit.js +8 -100
  157. package/src/core/state/state-manager.js +22 -304
  158. package/src/core/templates/template-registry.js +2 -97
  159. package/src/core/templates/template-renderer.js +13 -93
  160. package/src/core/templates/template-seed.js +0 -1
  161. package/src/core/work-types.js +1 -36
  162. package/src/dashboard/app/App.jsx +1 -8
  163. package/src/dashboard/app/components/ActivityFeed.jsx +0 -5
  164. package/src/dashboard/app/components/CommandPalette.jsx +0 -7
  165. package/src/dashboard/app/components/DetailPanel.jsx +1 -5
  166. package/src/dashboard/app/components/EntityLink.jsx +0 -12
  167. package/src/dashboard/app/components/ErrorBoundary.jsx +1 -8
  168. package/src/dashboard/app/components/KnowledgeMarkdown.jsx +0 -11
  169. package/src/dashboard/app/components/Markdown.jsx +0 -9
  170. package/src/dashboard/app/components/PhaseRail.jsx +0 -11
  171. package/src/dashboard/app/components/SearchFilter.jsx +0 -4
  172. package/src/dashboard/app/components/Sidebar.jsx +0 -5
  173. package/src/dashboard/app/hooks/useMorphQuery.js +1 -11
  174. package/src/dashboard/app/hooks/usePref.js +1 -5
  175. package/src/dashboard/app/lib/format.js +0 -2
  176. package/src/dashboard/app/lib/phases.js +1 -5
  177. package/src/dashboard/app/lib/project.js +0 -12
  178. package/src/dashboard/app/pages/AgentMap.jsx +1 -8
  179. package/src/dashboard/app/pages/FeatureDetail.jsx +1 -5
  180. package/src/dashboard/app/pages/Features.jsx +1 -8
  181. package/src/dashboard/app/pages/Guide.jsx +0 -1
  182. package/src/dashboard/app/pages/HookInspector.jsx +2 -23
  183. package/src/dashboard/app/pages/PhasePipeline.jsx +0 -3
  184. package/src/dashboard/app/pages/Portfolio.jsx +0 -1
  185. package/src/dashboard/app/pages/RulesMap.jsx +1 -16
  186. package/src/dashboard/app/pages/SkillsLibrary.jsx +1 -16
  187. package/src/dashboard/app/pages/Standards.jsx +2 -17
  188. package/src/dashboard/scanner/activity-scanner.js +10 -69
  189. package/src/dashboard/scanner/agents-scanner.js +1 -23
  190. package/src/dashboard/scanner/docs-scanner.js +0 -6
  191. package/src/dashboard/scanner/feature-status.js +2 -5
  192. package/src/dashboard/scanner/features-scanner.js +10 -66
  193. package/src/dashboard/scanner/hooks-scanner.js +5 -28
  194. package/src/dashboard/scanner/index.js +0 -4
  195. package/src/dashboard/scanner/portfolio.js +5 -29
  196. package/src/dashboard/scanner/project-api.js +8 -54
  197. package/src/dashboard/scanner/rules-scanner.js +1 -2
  198. package/src/dashboard/scanner/skills-scanner.js +2 -12
  199. package/src/dashboard/scanner/standards-scanner.js +2 -6
  200. package/src/dashboard/vite-plugin-morph-data.js +5 -19
  201. package/src/lib/ai/ai-checks.js +7 -52
  202. package/src/lib/ai/ai-project-scan.js +14 -108
  203. package/src/lib/ai/code-audit.js +7 -63
  204. package/src/lib/ai/pin-drift.js +8 -51
  205. package/src/lib/ai/registry-audit.js +11 -90
  206. package/src/lib/ci/ci-verify.js +4 -19
  207. package/src/lib/ci/github-ci.js +9 -37
  208. package/src/lib/ci/workflow-scaffold.js +2 -10
  209. package/src/lib/detectors/claude-config-detector.js +13 -74
  210. package/src/lib/detectors/index.js +4 -50
  211. package/src/lib/e2e/compose-env.js +5 -66
  212. package/src/lib/e2e/stack-teardown.js +6 -40
  213. package/src/lib/generators/architecture-scanner.js +18 -156
  214. package/src/lib/generators/handoff-generator.js +9 -53
  215. package/src/lib/generators/scaffold-generator.js +5 -101
  216. package/src/lib/generators/settings-generator.js +4 -37
  217. package/src/lib/git/branch-classifier.js +11 -84
  218. package/src/lib/git/default-branch.js +14 -169
  219. package/src/lib/git/evidence-staleness.js +1 -0
  220. package/src/lib/git/gh.js +2 -17
  221. package/src/lib/git/pending-prs.js +6 -37
  222. package/src/lib/git/pr-files.js +3 -28
  223. package/src/lib/git/pr-index.js +3 -34
  224. package/src/lib/git/tree-fingerprint.js +3 -15
  225. package/src/lib/git/tree-state.js +6 -33
  226. package/src/lib/graph/god-nodes.js +2 -22
  227. package/src/lib/graph/graph-health.js +3 -24
  228. package/src/lib/graph/graph-loader.js +5 -56
  229. package/src/lib/graph/graphify-runtime.js +7 -37
  230. package/src/lib/graph/graphifyignore.js +8 -74
  231. package/src/lib/graph/sanitize.js +16 -136
  232. package/src/lib/heavy/cleanup.js +1 -1
  233. package/src/lib/installers/effects.js +8 -93
  234. package/src/lib/installers/mcp-installer.js +9 -169
  235. package/src/lib/installers/memory-installer.js +4 -27
  236. package/src/lib/installers/templates-reconcile.js +5 -60
  237. package/src/lib/mutation/dotnet-introspection.js +20 -106
  238. package/src/lib/mutation/subset.js +1 -4
  239. package/src/lib/plan/artifact-vocabulary.js +5 -25
  240. package/src/lib/plan/checks.js +5 -31
  241. package/src/lib/plan/plan-parser.js +1 -6
  242. package/src/lib/projects/registry.js +7 -44
  243. package/src/lib/review-reports.js +5 -23
  244. package/src/lib/risk-detector.js +39 -173
  245. package/src/lib/specs/adr-index.js +6 -34
  246. package/src/lib/specs/frontmatter.js +2 -15
  247. package/src/lib/specs/gfm-table.js +1 -7
  248. package/src/lib/specs/revocations.js +1 -14
  249. package/src/lib/specs/screen-checklist.js +47 -124
  250. package/src/lib/stack/dotnet-build-ignores.js +2 -6
  251. package/src/lib/stack/stack-profile.js +1 -28
  252. package/src/lib/stack/stack-scan.js +1 -8
  253. package/src/lib/stack-filter.js +4 -27
  254. package/src/lib/standards/registry.js +1 -7
  255. package/src/lib/standards/sections.js +2 -13
  256. package/src/lib/standards/verified-against.js +19 -191
  257. package/src/lib/tasks/eval-node.js +21 -169
  258. package/src/lib/tasks/linux-runner.js +18 -142
  259. package/src/lib/tasks/test-nodes.js +43 -240
  260. package/src/lib/tasks/test-runner.js +43 -341
  261. package/src/lib/telemetry/metrics.js +7 -36
  262. package/src/lib/telemetry/telemetry-reader.js +13 -125
  263. package/src/lib/telemetry/verify-event.js +2 -30
  264. package/src/lib/validators/code-budget/code-budget-validator.js +3 -43
  265. package/src/lib/validators/code-budget/index.js +0 -7
  266. package/src/lib/validators/contracts/contract-compliance-validator.js +2 -24
  267. package/src/lib/validators/contracts/index.js +0 -6
  268. package/src/lib/validators/nextjs/index.js +0 -5
  269. package/src/lib/validators/nextjs/next-component-validator.js +4 -81
  270. package/src/lib/validators/packages/index.js +0 -6
  271. package/src/lib/validators/packages/package-validator.js +6 -56
  272. package/src/lib/validators/reuse/index.js +0 -8
  273. package/src/lib/validators/reuse/reuse-validator.js +11 -101
  274. package/src/lib/validators/reuse/symbol-scanner.js +6 -62
  275. package/src/lib/validators/traceability-check.js +5 -63
  276. package/src/lib/validators/validation-runner.js +20 -125
  277. package/src/lib/validators/verify-runner.js +52 -358
  278. package/src/lib/work-type-detector.js +5 -67
  279. package/src/lib/worktree/docker-audit.js +17 -136
  280. package/src/lib/worktree/env-allocator.js +10 -98
  281. package/src/lib/worktree/feature-truth.js +12 -115
  282. package/src/lib/worktree/finish-backup.js +32 -242
  283. package/src/lib/worktree/fleet-scanner.js +13 -95
  284. package/src/lib/worktree/ignored-triage.js +15 -91
  285. package/src/lib/worktree/vscode-settings.js +2 -3
  286. package/src/lib/worktree/worktree-audit.js +21 -213
  287. package/src/lib/worktree/worktree-include.js +3 -5
  288. package/src/scripts/global-install.js +3 -9
  289. package/src/scripts/install-plugin.js +1 -13
  290. package/src/scripts/setup-infra.js +17 -147
  291. package/src/utils/agents-installer.js +28 -275
  292. package/src/utils/banner.js +4 -25
  293. package/src/utils/claude-md-injector.js +1 -37
  294. package/src/utils/claude-settings-manager.js +1 -52
  295. package/src/utils/file-copier.js +11 -95
  296. package/src/utils/hooks-installer.js +21 -298
  297. package/src/utils/skills-installer.js +4 -101
  298. package/src/utils/version-checker.js +4 -83
  299. package/framework/commands/morph-proposal.md +0 -9
  300. package/src/commands/utils/index.js +0 -3
  301. package/src/lib/detectors/design-system-detector.js +0 -188
  302. package/src/lib/generators/design-system-generator.js +0 -508
  303. package/src/lib/specs/old-format.js +0 -68
  304. package/src/utils/agent-id-mapper.js +0 -11
@@ -1,70 +1,12 @@
1
- /**
2
- * effects.js — o coletor de efeitos ÚNICO do caminho de instalação/atualização.
3
- *
4
- * A propriedade que faz o `--dry-run` ser honesto está numa frase:
5
- *
6
- * > **o caminho real também emite.**
7
- *
8
- * Em modo normal cada verbo EXECUTA e registra; em `dryRun`, só registra. O
9
- * resumo impresso ao fim de uma execução real sai de `list()`, não de
10
- * contadores paralelos. A consequência é o ponto: um ponto de emissão
11
- * esquecido some das DUAS saídas ao mesmo tempo, e o resumo de uma execução
12
- * real fica visivelmente incompleto — o defeito aparece sem sequer usar o
13
- * `--dry-run`.
14
- *
15
- * **Não há segunda travessia.** Nenhum filtro, walker ou lista de payload é
16
- * reimplementado aqui. Os filtros que decidem o que instalar
17
- * (`shouldInstallSkill`, `agentEligible`, `shouldInstall`) já são puros e
18
- * continuam sendo os únicos. O anti-exemplar tem nome e mora neste
19
- * repositório: `checkAgentsDrift` funciona como preditor porque re-deriva o
20
- * conjunto elegível por conta própria e porque os dois lados chamam o mesmo
21
- * renderer puro. Estender essa forma para 9 payloads + hooks + merge de
22
- * settings + gitignore + índice do git é reimplementar o instalador inteiro e
23
- * depender de disciplina humana para mantê-los sincronizados.
24
- *
25
- * **Os verbos são SÍNCRONOS de propósito.** Metade dos instaladores que eles
26
- * atravessam (`installSkills`, `installAgents`, `installRulesForStack`) é
27
- * síncrona; um coletor assíncrono obrigaria a tornar `installRulesForStack`
28
- * async e a mudar a assinatura que `setup-infra`, `init` e os testes já usam.
29
- *
30
- * **O registro é a IDENTIDADE do efeito, nunca o conteúdo.** `writeFile` grava
31
- * `{ verb, target }`, não os bytes: `installClaudeHooks` carimba um
32
- * `generated_at` a cada execução, e comparar conteúdo faria o canário de
33
- * igualdade dry × real reprovar por causa do relógio.
34
- */
1
+ // O caminho real também emite: o resumo de uma execução real sai de `list()`, então um ponto de
2
+ // emissão esquecido some das duas saídas. Registra-se a identidade do efeito, nunca o conteúdo:
3
+ // `installClaudeHooks` carimba `generated_at`, e comparar bytes reprovaria a igualdade dry × real.
35
4
 
36
5
  import fs from 'fs-extra';
37
6
  import { spawnSync } from 'node:child_process';
38
7
  import { dirname } from 'node:path';
39
8
 
40
- /**
41
- * @typedef {Object} Effect
42
- * @property {string} verb - ensureDir | remove | copyDir | copyFile | writeFile | writeJson | mergeJson | spawn | gitIndex
43
- * @property {string} target - o caminho (ou comando) sobre o qual o efeito age
44
- * @property {string} [from] - origem, para os verbos de cópia
45
- * @property {string} [detail] - qualificador estável (argumentos do spawn, operação do git)
46
- */
47
-
48
- /**
49
- * Cria um coletor de efeitos.
50
- *
51
- * @param {{ dryRun?: boolean }} [options]
52
- * @returns {{
53
- * dryRun: boolean,
54
- * ensureDir: (path: string) => void,
55
- * remove: (path: string) => void,
56
- * copyDir: (from: string, to: string) => void,
57
- * copyFile: (from: string, to: string) => void,
58
- * writeFile: (path: string, content: string) => void,
59
- * writeJson: (path: string, data: object) => void,
60
- * mergeJson: (path: string, patch: object) => void,
61
- * spawn: (cmd: string, args: string[], options?: object) => object|null,
62
- * gitIndex: (op: string, path: string, options?: object) => boolean,
63
- * list: () => Effect[],
64
- * }}
65
- */
66
9
  export function createEffects({ dryRun = false } = {}) {
67
- /** @type {Effect[]} */
68
10
  const effects = [];
69
11
 
70
12
  const record = (verb, target, extra = {}) => {
@@ -118,13 +60,7 @@ export function createEffects({ dryRun = false } = {}) {
118
60
  }
119
61
  },
120
62
 
121
- /**
122
- * Leitura + fusão rasa + escrita, como UM efeito.
123
- *
124
- * A leitura acontece nos dois modos (ler não muda nada); só a escrita é
125
- * suprimida em dry. É assim que `~/.claude/settings.json` — um arquivo do
126
- * usuário, FORA do projeto — é atualizado sem que o dry o toque.
127
- */
63
+ // A leitura acontece nos dois modos; só a escrita é suprimida em dry.
128
64
  mergeJson(path, patch) {
129
65
  record('mergeJson', path);
130
66
  if (dryRun) return;
@@ -138,21 +74,9 @@ export function createEffects({ dryRun = false } = {}) {
138
74
  fs.writeJsonSync(path, { ...current, ...patch }, { spaces: 2 });
139
75
  },
140
76
 
141
- /**
142
- * @param {string} cmd
143
- * @param {string[]} args
144
- * @param {{ cwd?: string, dryRunArgs?: string[] }} [options]
145
- * `dryRunArgs` é a variante SÓ-LEITURA do mesmo comando (por exemplo
146
- * `--check`). Em dry ela é executada no lugar; sem ela, nada roda.
147
- * O REGISTRO é o mesmo nos dois modos: o efeito previsto é o comando
148
- * real, não a sua sonda.
149
- */
77
+ // `dryRunArgs`: variante só-leitura do comando, executada no lugar em dry; o registro é o comando real.
150
78
  spawn(cmd, args = [], options = {}) {
151
- // `args` viaja no registro ALÉM do `detail` legível. O `detail` é para
152
- // humano; quebrá-lo por espaço para descobrir onde o spawn escreve é o
153
- // bug que um caminho como `R:\Polymorphism Tech\...` produz em silêncio,
154
- // e que o relatório mostrou pondo um efeito do PACOTE da CLI no grupo
155
- // "no projeto".
79
+ // `args` viaja além do `detail`: quebrar o `detail` por espaço erra caminhos com espaço.
156
80
  record('spawn', cmd, { detail: args.join(' '), args: [...args] });
157
81
  const { cwd, dryRunArgs } = options;
158
82
  if (dryRun) {
@@ -162,11 +86,7 @@ export function createEffects({ dryRun = false } = {}) {
162
86
  return spawnSync(cmd, args, { cwd, encoding: 'utf8' });
163
87
  },
164
88
 
165
- /**
166
- * Mutação do índice do git (`git rm --cached`, etc.). A SONDA que decide
167
- * se há o que fazer fica fora daqui de propósito: sondar é ler, e ler
168
- * acontece nos dois modos.
169
- */
89
+ // A sonda que decide se há o que fazer fica fora daqui: sondar é ler, e ler acontece nos dois modos.
170
90
  gitIndex(op, path, options = {}) {
171
91
  record('gitIndex', path, { detail: op });
172
92
  if (dryRun) return true;
@@ -182,12 +102,7 @@ export function createEffects({ dryRun = false } = {}) {
182
102
  };
183
103
  }
184
104
 
185
- /**
186
- * Coletor no-op-de-registro para chamadores que não passam um: executa
187
- * normalmente e registra numa lista descartada. Existe para que assinaturas
188
- * novas de `installSkills`/`installAgents`/`installRulesForStack` continuem
189
- * funcionando para `setup-infra` e `init` sem mudança de comportamento.
190
- */
105
+ // Executa e registra numa lista descartada, para chamadores que não passam um coletor.
191
106
  export function realEffects() {
192
107
  return createEffects({ dryRun: false });
193
108
  }
@@ -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';
@@ -49,7 +39,7 @@ metadata:
49
39
 
50
40
  | Command | Skill invoked |
51
41
  |---------|---------------|
52
- | \`/morph-proposal {feature}\` | \`Skill(morph-plan)\` — entrevista → plan.md → plan-check → gate do plano |
42
+ | \`/morph-plan {feature}\` | \`Skill(morph-plan)\` — entrevista → plan.md → plan-check → gate do plano |
53
43
  | \`/morph-apply {feature}\` | \`Skill(morph-apply)\` — fatias do plano → verify → review → Gate 3 |
54
44
  | \`/morph-status [feature]\` | inspect state.json |
55
45
  | \`/morph-archive {feature}\` | move completed feature to .morph/archive/ |
@@ -64,7 +54,6 @@ metadata:
64
54
 
65
55
  **How to apply:**
66
56
  - When user mentions a feature, check \`.morph/features/\` first.
67
- - When user types \`/morph-proposal\`, invoke \`Skill(morph-plan)\` directly.
68
57
  - In \`trust: "auto"\`, gates auto-pass; only AskUserQuestion on risk signals.
69
58
  `;
70
59
  }
@@ -87,19 +76,7 @@ metadata:
87
76
  - \`.morph/framework/evals/review-checklist.md\` — what the Gate 3 verifier, reviewer and security check (verdict SHIP/FIX + evidence-typed blockers)
88
77
  `;
89
78
 
90
- /**
91
- * Populate Claude Code persistent memory for a morph-spec project.
92
- *
93
- * Idempotent: re-running overwrites the memory files with current info
94
- * but appends MEMORY.md index entries only if missing.
95
- *
96
- * @param {string} projectPath - Absolute path of the project root
97
- * @param {object} info - Project facts to embed in the memory
98
- * @param {string} info.projectName
99
- * @param {string} info.frameworkVersion
100
- * @param {string} [info.stack]
101
- * @returns {Promise<{ projectMemoryPath: string, referenceMemoryPath: string, indexPath: string }>}
102
- */
79
+ // Idempotent: memory files are overwritten, MEMORY.md entries appended only if missing.
103
80
  export async function installProjectMemory(projectPath, info) {
104
81
  const memoryRoot = getMemoryRoot(projectPath);
105
82
  await fs.ensureDir(memoryRoot);