@polymorphism-tech/morph-spec 8.47.0 → 9.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (377) hide show
  1. package/CLAUDE.md +51 -43
  2. package/README.md +39 -38
  3. package/bin/morph-spec.js +41 -113
  4. package/bin/validate.js +1 -147
  5. package/claude-plugin.json +1 -1
  6. package/docs/CHEATSHEET.md +1 -1
  7. package/docs/QUICKSTART.md +1 -1
  8. package/framework/CLAUDE.md +41 -101
  9. package/framework/MORPH.md +95 -252
  10. package/framework/agents.json +86 -21
  11. package/framework/commands/morph-apply.md +5 -28
  12. package/framework/commands/morph-archive.md +1 -1
  13. package/framework/commands/morph-hotfix.md +8 -8
  14. package/framework/commands/morph-infra.md +1 -1
  15. package/framework/commands/morph-preflight.md +2 -3
  16. package/framework/commands/morph-status.md +4 -4
  17. package/framework/docs/CLI.md +574 -286
  18. package/framework/evals/review-checklist.md +82 -41
  19. package/framework/hooks/README.md +10 -11
  20. package/framework/hooks/claude-code/core/block-destructive-worktree-removal.js +6 -49
  21. package/framework/hooks/claude-code/core/block-raw-worktree-add.js +2 -35
  22. package/framework/hooks/claude-code/core/chain.js +4 -65
  23. package/framework/hooks/claude-code/core/comment-guard.js +3 -17
  24. package/framework/hooks/claude-code/core/denylist.json +33 -3
  25. package/framework/hooks/claude-code/core/enforce-phase-writes.js +15 -63
  26. package/framework/hooks/claude-code/core/gate-guard.js +9 -78
  27. package/framework/hooks/claude-code/core/graph-refresh-kick.js +3 -24
  28. package/framework/hooks/claude-code/core/heavy-gate.js +3 -21
  29. package/framework/hooks/claude-code/core/heavy-release.js +2 -13
  30. package/framework/hooks/claude-code/core/loop-detect.js +2 -31
  31. package/framework/hooks/claude-code/core/pre-bash-denylist.js +36 -2
  32. package/framework/hooks/claude-code/core/protect-spec-files.js +25 -297
  33. package/framework/hooks/claude-code/core/state-sync.js +15 -85
  34. package/framework/hooks/claude-code/core/telemetry-log.js +15 -96
  35. package/framework/hooks/claude-code/core/validate-completion.js +37 -147
  36. package/framework/hooks/claude-code/core/warn-bash-source-write.js +23 -0
  37. package/framework/hooks/claude-code/core/worktree-context.js +15 -114
  38. package/framework/hooks/claude-code/core/worktree-create.js +6 -70
  39. package/framework/hooks/claude-code/core/worktree-env-inject.js +5 -56
  40. package/framework/hooks/claude-code/core/worktree-remove.js +5 -31
  41. package/framework/hooks/claude-code/statusline.py +55 -41
  42. package/framework/hooks/package.json +3 -0
  43. package/framework/hooks/shared/activity-logger.js +9 -89
  44. package/framework/hooks/shared/agent-reap.js +2 -17
  45. package/framework/hooks/shared/build-check.js +11 -76
  46. package/framework/hooks/shared/claim-registry.js +9 -119
  47. package/framework/hooks/shared/command-tokens.js +168 -44
  48. package/framework/hooks/shared/comment-rules.js +5 -20
  49. package/framework/hooks/shared/comment-scanner.js +11 -43
  50. package/framework/hooks/shared/comment-stripper.js +11 -85
  51. package/framework/hooks/shared/e2e-check.js +0 -0
  52. package/framework/hooks/shared/errata.js +59 -0
  53. package/framework/hooks/shared/feature-locator.js +11 -95
  54. package/framework/hooks/shared/frontend-locations.js +4 -0
  55. package/framework/hooks/shared/gate-question.js +12 -0
  56. package/framework/hooks/shared/git-exclude.js +3 -20
  57. package/framework/hooks/shared/git-paths.js +8 -104
  58. package/framework/hooks/shared/graph-freshness.js +22 -166
  59. package/framework/hooks/shared/graph-refresh.js +21 -149
  60. package/framework/hooks/shared/heavy-command.js +4 -12
  61. package/framework/hooks/shared/hook-response.js +8 -83
  62. package/framework/hooks/shared/morph-cli.js +8 -52
  63. package/framework/hooks/shared/payload-utils.js +10 -132
  64. package/framework/hooks/shared/phase-utils.js +14 -72
  65. package/framework/hooks/shared/redact-secrets.js +4 -15
  66. package/framework/hooks/shared/reparse-scan.js +6 -66
  67. package/framework/hooks/shared/slice-progress.js +99 -0
  68. package/framework/hooks/shared/source-files.js +4 -17
  69. package/framework/hooks/shared/spawn-command.js +23 -0
  70. package/framework/hooks/shared/state-reader.js +51 -247
  71. package/framework/hooks/shared/state-writer.js +9 -54
  72. package/framework/hooks/shared/stdin-reader.js +1 -11
  73. package/framework/hooks/shared/telemetry-logger.js +7 -67
  74. package/framework/hooks/shared/transcript-usage.js +7 -29
  75. package/framework/hooks/shared/worktree-root.js +7 -29
  76. package/framework/mcp/registry.json +3 -4
  77. package/framework/phases.json +23 -39
  78. package/framework/rules/csharp-standards.md +1 -1
  79. package/framework/rules/frontend-standards.md +1 -0
  80. package/framework/rules/hangfire-standards.md +1 -1
  81. package/framework/rules/neon-standards.md +1 -1
  82. package/framework/schemas/config.schema.json +14 -9
  83. package/framework/schemas/feature.schema.json +15 -50
  84. package/framework/skills/README.md +3 -4
  85. package/framework/skills/core/create-umbraco-lp/SKILL.md +1 -1
  86. package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +1 -1
  87. package/framework/skills/core/morph-apply/SKILL.md +149 -222
  88. package/framework/skills/core/morph-hotfix/SKILL.md +33 -42
  89. package/framework/skills/core/morph-machine/SKILL.md +2 -2
  90. package/framework/skills/core/morph-media/SKILL.md +1 -1
  91. package/framework/skills/core/morph-plan/SKILL.md +137 -299
  92. package/framework/skills/core/morph-review/SKILL.md +111 -136
  93. package/framework/skills/core/morph-standards/SKILL.md +27 -39
  94. package/framework/skills/core/morph-uiux/SKILL.md +94 -152
  95. package/framework/standards/STANDARDS.json +109 -1219
  96. package/framework/standards/STANDARDS.meta.json +1319 -0
  97. package/framework/standards/ai-agents/agent-archetypes.md +2 -2
  98. package/framework/standards/ai-agents/agent-session.md +1 -1
  99. package/framework/standards/ai-agents/conversational-agent-with-phases.md +4 -4
  100. package/framework/standards/ai-agents/durable-workflows-hitl.md +3 -3
  101. package/framework/standards/ai-agents/evals-with-cache.md +1 -2
  102. package/framework/standards/ai-agents/guardrails.md +1 -1
  103. package/framework/standards/ai-agents/multi-agent-patterns.md +2 -2
  104. package/framework/standards/ai-agents/observability-patterns.md +1 -1
  105. package/framework/standards/ai-agents/production.md +1 -1
  106. package/framework/standards/ai-agents/prompt-sources.md +2 -2
  107. package/framework/standards/ai-agents/providers/model-registry.md +1 -1
  108. package/framework/standards/ai-agents/rag-custom-pgvector.md +2 -2
  109. package/framework/standards/ai-agents/setup.md +3 -3
  110. package/framework/standards/ai-agents/sweet-spot.md +2 -2
  111. package/framework/standards/ai-agents/tool-adapters.md +0 -2
  112. package/framework/standards/ai-agents/vector-data-extensions.md +4 -4
  113. package/framework/standards/ai-agents/workflows.md +1 -1
  114. package/framework/standards/architecture/code-comments.md +7 -7
  115. package/framework/standards/architecture/vertical-slice/vertical-slice.md +1 -1
  116. package/framework/standards/backend/api/validation.md +1 -1
  117. package/framework/standards/backend/dotnet/vsa-handler-patterns.md +5 -5
  118. package/framework/standards/backend/integrations/hangfire/hangfire-jobs.md +1 -1
  119. package/framework/standards/backend/integrations/neon-auth/neon-auth.md +2 -2
  120. package/framework/standards/data/nosql/cache/redis.md +1 -1
  121. package/framework/standards/data/nosql/cosmos-db.md +3 -3
  122. package/framework/standards/frontend/admin-app/forms.md +1 -1
  123. package/framework/standards/frontend/design-system/art-direction.md +88 -0
  124. package/framework/standards/frontend/design-system/premium-finish.md +77 -2
  125. package/framework/standards/infrastructure/neon/neon-pgvector.md +5 -5
  126. package/framework/standards/integration/mcp/mcp-tools.md +7 -7
  127. package/framework/templates/CLAUDE.md.core.template +3 -3
  128. package/framework/templates/README.md +2 -3
  129. package/framework/templates/REGISTRY.json +15 -179
  130. package/framework/templates/docs/plan.md +72 -0
  131. package/framework/templates/docs/ui-mockups.md +4 -0
  132. package/framework/templates/dotnet/ai-kit/README.md +1 -9
  133. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Morph.AiKit.Tests.csproj +0 -10
  134. package/framework/templates/meta-prompts/squad-leaders/backend-squad.md +1 -1
  135. package/framework/templates/meta-prompts/squad-leaders/frontend-squad.md +1 -1
  136. package/framework/templates/meta-prompts/squad-leaders/squad-leader.md +1 -1
  137. package/framework/templates/meta-prompts/validators/pre-commit-validator.md +3 -4
  138. package/package.json +1 -1
  139. package/src/commands/context/context-refresh.js +0 -16
  140. package/src/commands/dashboard/dashboard.js +2 -8
  141. package/src/commands/e2e/e2e.js +18 -105
  142. package/src/commands/graph/affected.js +0 -14
  143. package/src/commands/graph/build.js +11 -50
  144. package/src/commands/graph/explain.js +0 -16
  145. package/src/commands/graph/status.js +1 -12
  146. package/src/commands/mcp/mcp-setup.js +4 -62
  147. package/src/commands/observability/cost.js +17 -111
  148. package/src/commands/observability/telemetry.js +1 -14
  149. package/src/commands/project/branch.js +4 -45
  150. package/src/commands/project/claim.js +4 -48
  151. package/src/commands/project/doctor-procs.js +1 -1
  152. package/src/commands/project/doctor.js +53 -307
  153. package/src/commands/project/env.js +1 -27
  154. package/src/commands/project/fleet.js +6 -35
  155. package/src/commands/project/heavy.js +3 -3
  156. package/src/commands/project/init.js +8 -68
  157. package/src/commands/project/install-plugin-cmd.js +0 -1
  158. package/src/commands/project/setup-infra-cmd.js +2 -1
  159. package/src/commands/project/stack-scan.js +58 -0
  160. package/src/commands/project/standards.js +65 -72
  161. package/src/commands/project/status.js +28 -121
  162. package/src/commands/project/update.js +31 -223
  163. package/src/commands/project/worktree.js +109 -883
  164. package/src/commands/scaffold/scaffold.js +0 -25
  165. package/src/commands/state/advance.js +65 -120
  166. package/src/commands/state/approve.js +87 -229
  167. package/src/commands/state/archive.js +23 -139
  168. package/src/commands/state/auto-approval.js +65 -0
  169. package/src/commands/state/create.js +49 -109
  170. package/src/commands/state/delete.js +11 -63
  171. package/src/commands/state/finish.js +105 -494
  172. package/src/commands/state/gate-check.js +74 -183
  173. package/src/commands/state/gate-decision.js +8 -42
  174. package/src/commands/state/handoff.js +4 -31
  175. package/src/commands/state/list.js +21 -91
  176. package/src/commands/state/report-check.js +134 -338
  177. package/src/commands/state/retype.js +12 -28
  178. package/src/commands/validation/index.js +0 -3
  179. package/src/commands/validation/mutate.js +79 -19
  180. package/src/commands/validation/plan-check.js +81 -0
  181. package/src/commands/validation/validate-feature.js +52 -216
  182. package/src/commands/validation/verify.js +45 -160
  183. package/src/core/paths/output-schema.js +9 -155
  184. package/src/core/state/feature-identity.js +1 -25
  185. package/src/core/state/gate-order.js +2 -30
  186. package/src/core/state/lifecycle-audit.js +8 -100
  187. package/src/core/state/state-manager.js +32 -395
  188. package/src/core/templates/template-registry.js +2 -97
  189. package/src/core/templates/template-renderer.js +13 -93
  190. package/src/core/templates/template-seed.js +21 -0
  191. package/src/core/work-types.js +10 -45
  192. package/src/dashboard/app/App.jsx +1 -8
  193. package/src/dashboard/app/components/ActivityFeed.jsx +2 -8
  194. package/src/dashboard/app/components/CommandPalette.jsx +4 -11
  195. package/src/dashboard/app/components/DetailPanel.jsx +1 -5
  196. package/src/dashboard/app/components/EntityLink.jsx +0 -12
  197. package/src/dashboard/app/components/ErrorBoundary.jsx +1 -8
  198. package/src/dashboard/app/components/KnowledgeMarkdown.jsx +0 -11
  199. package/src/dashboard/app/components/Markdown.jsx +0 -9
  200. package/src/dashboard/app/components/PhaseRail.jsx +0 -11
  201. package/src/dashboard/app/components/SearchFilter.jsx +0 -4
  202. package/src/dashboard/app/components/Sidebar.jsx +0 -5
  203. package/src/dashboard/app/hooks/useMorphQuery.js +1 -11
  204. package/src/dashboard/app/hooks/usePref.js +1 -5
  205. package/src/dashboard/app/lib/format.js +0 -2
  206. package/src/dashboard/app/lib/phases.js +3 -9
  207. package/src/dashboard/app/lib/project.js +0 -12
  208. package/src/dashboard/app/pages/Activity.jsx +1 -1
  209. package/src/dashboard/app/pages/AgentMap.jsx +1 -8
  210. package/src/dashboard/app/pages/FeatureDetail.jsx +139 -133
  211. package/src/dashboard/app/pages/Features.jsx +9 -16
  212. package/src/dashboard/app/pages/Guide.jsx +2 -3
  213. package/src/dashboard/app/pages/HookInspector.jsx +2 -23
  214. package/src/dashboard/app/pages/PhasePipeline.jsx +3 -7
  215. package/src/dashboard/app/pages/Portfolio.jsx +0 -1
  216. package/src/dashboard/app/pages/RulesMap.jsx +1 -16
  217. package/src/dashboard/app/pages/SkillsLibrary.jsx +1 -16
  218. package/src/dashboard/app/pages/Standards.jsx +2 -17
  219. package/src/dashboard/scanner/activity-scanner.js +14 -83
  220. package/src/dashboard/scanner/agents-scanner.js +1 -23
  221. package/src/dashboard/scanner/docs-scanner.js +1 -7
  222. package/src/dashboard/scanner/feature-status.js +2 -5
  223. package/src/dashboard/scanner/features-scanner.js +92 -113
  224. package/src/dashboard/scanner/hooks-scanner.js +5 -28
  225. package/src/dashboard/scanner/index.js +0 -4
  226. package/src/dashboard/scanner/portfolio.js +5 -29
  227. package/src/dashboard/scanner/project-api.js +8 -54
  228. package/src/dashboard/scanner/rules-scanner.js +1 -2
  229. package/src/dashboard/scanner/skills-scanner.js +2 -12
  230. package/src/dashboard/scanner/standards-scanner.js +4 -7
  231. package/src/dashboard/vite-plugin-morph-data.js +5 -19
  232. package/src/lib/ai/ai-checks.js +7 -52
  233. package/src/lib/ai/ai-project-scan.js +14 -108
  234. package/src/lib/ai/code-audit.js +7 -63
  235. package/src/lib/ai/pin-drift.js +8 -51
  236. package/src/lib/ai/registry-audit.js +11 -90
  237. package/src/lib/ci/ci-verify.js +14 -13
  238. package/src/lib/ci/github-ci.js +9 -37
  239. package/src/lib/ci/workflow-scaffold.js +2 -10
  240. package/src/lib/detectors/claude-config-detector.js +13 -74
  241. package/src/lib/detectors/index.js +8 -59
  242. package/src/lib/e2e/compose-env.js +5 -66
  243. package/src/lib/e2e/stack-teardown.js +6 -40
  244. package/src/lib/generators/architecture-scanner.js +18 -156
  245. package/src/lib/generators/handoff-generator.js +37 -112
  246. package/src/lib/generators/scaffold-generator.js +5 -101
  247. package/src/lib/generators/settings-generator.js +11 -122
  248. package/src/lib/git/branch-classifier.js +11 -84
  249. package/src/lib/git/default-branch.js +14 -169
  250. package/src/lib/git/evidence-staleness.js +2 -1
  251. package/src/lib/git/gh.js +2 -17
  252. package/src/lib/git/pending-prs.js +6 -37
  253. package/src/lib/git/pr-files.js +3 -28
  254. package/src/lib/git/pr-index.js +3 -34
  255. package/src/lib/git/tree-fingerprint.js +17 -17
  256. package/src/lib/git/tree-state.js +6 -33
  257. package/src/lib/graph/god-nodes.js +2 -22
  258. package/src/lib/graph/graph-health.js +3 -24
  259. package/src/lib/graph/graph-loader.js +5 -56
  260. package/src/lib/graph/graphify-runtime.js +7 -37
  261. package/src/lib/graph/graphifyignore.js +8 -74
  262. package/src/lib/graph/sanitize.js +16 -136
  263. package/src/lib/heavy/cleanup.js +2 -6
  264. package/src/lib/installers/effects.js +8 -93
  265. package/src/lib/installers/mcp-installer.js +9 -169
  266. package/src/lib/installers/memory-installer.js +8 -33
  267. package/src/lib/installers/templates-reconcile.js +5 -60
  268. package/src/lib/mutation/commands.js +3 -2
  269. package/src/lib/mutation/dotnet-introspection.js +281 -0
  270. package/src/lib/mutation/plan.js +14 -6
  271. package/src/lib/mutation/runner.js +1 -1
  272. package/src/lib/mutation/subset.js +47 -13
  273. package/src/lib/plan/artifact-vocabulary.js +40 -0
  274. package/src/lib/plan/checks.js +208 -0
  275. package/src/lib/plan/plan-parser.js +204 -0
  276. package/src/lib/plan/plan-template.js +18 -0
  277. package/src/lib/projects/registry.js +7 -44
  278. package/src/lib/review-reports.js +204 -0
  279. package/src/lib/risk-detector.js +131 -274
  280. package/src/lib/specs/adr-index.js +38 -85
  281. package/src/lib/specs/design-seed.js +18 -0
  282. package/src/lib/specs/frontmatter.js +31 -0
  283. package/src/lib/specs/gfm-table.js +11 -0
  284. package/src/lib/specs/revocations.js +31 -102
  285. package/src/lib/specs/screen-checklist.js +56 -132
  286. package/src/lib/stack/dotnet-build-ignores.js +43 -0
  287. package/src/lib/stack/stack-profile.js +1 -28
  288. package/src/lib/stack/stack-scan.js +260 -0
  289. package/src/lib/stack-filter.js +4 -27
  290. package/src/lib/standards/registry.js +37 -0
  291. package/src/lib/standards/sections.js +47 -0
  292. package/src/lib/standards/verified-against.js +19 -191
  293. package/src/lib/tasks/eval-node.js +23 -179
  294. package/src/lib/tasks/linux-runner.js +18 -142
  295. package/src/lib/tasks/test-nodes.js +43 -313
  296. package/src/lib/tasks/test-runner.js +107 -447
  297. package/src/lib/telemetry/metrics.js +13 -103
  298. package/src/lib/telemetry/telemetry-reader.js +13 -238
  299. package/src/lib/telemetry/verify-event.js +4 -34
  300. package/src/lib/validators/code-budget/code-budget-validator.js +4 -44
  301. package/src/lib/validators/code-budget/index.js +0 -7
  302. package/src/lib/validators/contracts/contract-compliance-validator.js +4 -26
  303. package/src/lib/validators/contracts/index.js +0 -6
  304. package/src/lib/validators/nextjs/index.js +0 -5
  305. package/src/lib/validators/nextjs/next-component-validator.js +4 -81
  306. package/src/lib/validators/packages/index.js +0 -6
  307. package/src/lib/validators/packages/package-validator.js +6 -56
  308. package/src/lib/validators/reuse/index.js +0 -8
  309. package/src/lib/validators/reuse/reuse-validator.js +24 -137
  310. package/src/lib/validators/reuse/symbol-scanner.js +6 -62
  311. package/src/lib/validators/traceability-check.js +54 -156
  312. package/src/lib/validators/validation-runner.js +20 -125
  313. package/src/lib/validators/verify-runner.js +152 -575
  314. package/src/lib/work-type-detector.js +5 -67
  315. package/src/lib/worktree/docker-audit.js +17 -137
  316. package/src/lib/worktree/env-allocator.js +10 -98
  317. package/src/lib/worktree/feature-truth.js +12 -115
  318. package/src/lib/worktree/finish-backup.js +32 -242
  319. package/src/lib/worktree/fleet-scanner.js +31 -126
  320. package/src/lib/worktree/ignored-triage.js +15 -91
  321. package/src/lib/worktree/vscode-settings.js +2 -3
  322. package/src/lib/worktree/worktree-audit.js +21 -213
  323. package/src/lib/worktree/worktree-include.js +3 -5
  324. package/src/scripts/global-install.js +3 -9
  325. package/src/scripts/install-plugin.js +1 -13
  326. package/src/scripts/setup-infra.js +17 -138
  327. package/src/utils/agents-installer.js +92 -272
  328. package/src/utils/banner.js +4 -25
  329. package/src/utils/claude-md-injector.js +1 -37
  330. package/src/utils/claude-settings-manager.js +1 -87
  331. package/src/utils/file-copier.js +32 -90
  332. package/src/utils/hooks-installer.js +26 -296
  333. package/src/utils/skills-installer.js +4 -102
  334. package/src/utils/version-checker.js +4 -83
  335. package/framework/commands/morph-proposal.md +0 -57
  336. package/framework/evals/trace-schema.md +0 -67
  337. package/framework/hooks/claude-code/core/tasks-json-guard.js +0 -83
  338. package/framework/hooks/claude-code/core/trace-autogen.js +0 -277
  339. package/framework/hooks/git/pre-commit/specs.sh +0 -50
  340. package/framework/hooks/shared/tasks-validator.js +0 -111
  341. package/framework/rules/morph-task-tracking.md +0 -54
  342. package/framework/rules/morph-workflow.md +0 -244
  343. package/framework/schemas/tasks.schema.json +0 -79
  344. package/framework/skills/core/morph-brainstorming/SKILL.md +0 -161
  345. package/framework/skills/core/morph-implement/SKILL.md +0 -194
  346. package/framework/skills/core/morph-proposal/SKILL.md +0 -152
  347. package/framework/skills/core/morph-stack-scan/SKILL.md +0 -145
  348. package/framework/templates/docs/proposal.md +0 -192
  349. package/framework/templates/docs/spec.md +0 -57
  350. package/framework/templates/docs/ui-components.md +0 -52
  351. package/framework/templates/docs/ui-design-system.md +0 -76
  352. package/framework/templates/docs/ui-flows.md +0 -167
  353. package/framework/templates/examples/spec-examples.md +0 -91
  354. package/framework/templates/feature/decisions.md +0 -124
  355. package/framework/templates/feature/recap.md +0 -145
  356. package/framework/templates/tasks.template.json +0 -17
  357. package/src/commands/state/usecase.js +0 -143
  358. package/src/commands/tasks/dag.js +0 -181
  359. package/src/commands/utils/index.js +0 -3
  360. package/src/lib/agents/output-collector.js +0 -76
  361. package/src/lib/detectors/design-system-detector.js +0 -188
  362. package/src/lib/evaluator-report.js +0 -104
  363. package/src/lib/generators/design-system-generator.js +0 -508
  364. package/src/lib/specs/acervo-format.js +0 -70
  365. package/src/lib/specs/acervo-lifecycle.js +0 -264
  366. package/src/lib/specs/done-criteria-check.js +0 -200
  367. package/src/lib/specs/usecase-check.js +0 -301
  368. package/src/lib/specs/usecase-promoter.js +0 -301
  369. package/src/lib/specs/usecase-registry.js +0 -237
  370. package/src/lib/tasks/dispatch-planner.js +0 -319
  371. package/src/lib/tasks/dotnet-test-introspection.js +0 -632
  372. package/src/lib/tasks/mermaid-renderer.js +0 -132
  373. package/src/lib/tasks/task-classifier.js +0 -30
  374. package/src/lib/tasks/task-parser.js +0 -209
  375. package/src/lib/tasks/tasks-json-parser.js +0 -296
  376. package/src/lib/tasks/test-scope.js +0 -597
  377. package/src/utils/agent-id-mapper.js +0 -11
@@ -1,22 +1,5 @@
1
- /**
2
- * registry-audit.js — o registry de modelos do projeto: tarifa por alias e
3
- * protocolo (`api`) por alias.
4
- *
5
- * As duas perguntas saem do standard `ai-agents/providers/model-registry.md`,
6
- * não da memória de quem implementa:
7
- *
8
- * - **tarifa por alias** — um alias sem tarifa é um custo que ninguém
9
- * consegue prever antes de a chamada ser paga. O caso medido no acervo é o
10
- * GHLBrain, cujo mapa `Pricing.Models` é literalmente `{}`.
11
- * - **`api` por alias** — o standard mede, sobre o pin, que
12
- * `AgentApi.Responses = 0`, ou seja `default(AgentApi)` é Responses, e que
13
- * `ModelRegistry.ReadApi` devolve `AgentApi.Responses` quando o campo está
14
- * ausente. A ausência do campo, portanto, NÃO é defeito: é uma decisão
15
- * tomada por omissão. O achado NOMEIA o default assumido e pede
16
- * confirmação — nunca afirma defeito.
17
- *
18
- * Severidade: `warn` nas duas. Só `pin-drift` produz `error`.
19
- */
1
+ // Ausência de `api` não é defeito: o default medido sobre o pin é Responses, então o achado nomeia o
2
+ // default e pede confirmação. Severidade `warn`; só `pin-drift` produz `error`.
20
3
 
21
4
  import { readFileSync } from 'node:fs';
22
5
  import { existsSync } from 'node:fs';
@@ -24,22 +7,10 @@ import { isAbsolute, join, basename } from 'node:path';
24
7
  import { collectProjectFiles } from './ai-project-scan.js';
25
8
  import { jsoncToJson } from '../../../framework/hooks/shared/comment-stripper.js';
26
9
 
27
- /** Limite de tamanho para um JSON candidato (bytes de texto) — evita ler dumps. */
28
10
  const MAX_JSON_CHARS = 512 * 1024;
29
11
 
30
- /**
31
- * Lê um candidato a registry aceitando **JSONC**.
32
- *
33
- * Não é indulgência: é paridade com o leitor de produção. O exemplar canônico
34
- * `framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/model-registry.v2.json`
35
- * — que diz na primeira linha "copie para `config/model-registry.json` no
36
- * projeto" — tem 20 linhas de `//`, e `ModelRegistry.FromJson` do kit declara
37
- * `CommentHandling = JsonCommentHandling.Skip` + `AllowTrailingCommas = true`.
38
- * Com `JSON.parse` cru, as duas checagens de registry saíam `skip` sobre a
39
- * ÚNICA forma que existem para auditar — e `skip` não muda o código de saída,
40
- * então o silêncio era total. A auditoria tem de enxergar a mesma forma que o
41
- * leitor de produção enxerga.
42
- */
12
+ // JSONC por paridade com o leitor de produção (`ModelRegistry.FromJson` aceita comentário e vírgula
13
+ // sobrando): com `JSON.parse` cru o exemplar canônico saía `skip`, e `skip` não muda o código de saída.
43
14
  function readJsonSafe(file) {
44
15
  try {
45
16
  const raw = readFileSync(file, 'utf8');
@@ -67,7 +38,7 @@ function asAliasSource(file, data) {
67
38
  return { file, aliases: data.aliases, defaultAlias: data.defaultAlias, data };
68
39
  }
69
40
 
70
- /** A forma medida no GHLBrain: a seção `Pricing` de um `appsettings*.json`. */
41
+ /** A seção `Pricing` de um `appsettings*.json`. */
71
42
  function asPricingSource(file, data) {
72
43
  if (!isPlainObject(data)) return null;
73
44
  const pricing = data.Pricing ?? data.pricing;
@@ -77,19 +48,8 @@ function asPricingSource(file, data) {
77
48
  return { file, models };
78
49
  }
79
50
 
80
- /**
81
- * Localiza o registry de modelos do projeto.
82
- *
83
- * Ordem, primeira ocorrência ganha (spec §3.5):
84
- * 1. path declarado em `.morph/config/config.json → project.ai.modelRegistry`;
85
- * 2. um `*.json` cujo objeto raiz tenha `defaultAlias` **e** um mapa `aliases`;
86
- * 3. a seção `Pricing` de um `appsettings*.json`.
87
- *
88
- * @param {string} root - raiz do projeto
89
- * @param {object|null} config - conteúdo de `.morph/config/config.json`, se houver
90
- * @returns {null | { aliasSource: object|null, pricingSource: object|null }}
91
- * `null` quando NENHUMA das três formas foi encontrada.
92
- */
51
+ // Primeira forma ganha: o path de `project.ai.modelRegistry`, um `*.json` com `defaultAlias` e `aliases`,
52
+ // ou a seção `Pricing` de um `appsettings*.json`. `null` sem nenhuma.
93
53
  export function locateAiRegistry(root, config = null) {
94
54
  let aliasSource = null;
95
55
  let pricingSource = null;
@@ -132,7 +92,6 @@ export function locateAiRegistry(root, config = null) {
132
92
  return { aliasSource, pricingSource };
133
93
  }
134
94
 
135
- /** Mapa de tarifas embutido no próprio registry de modelos, quando existir. */
136
95
  function extractPricingFromRegistry(file, data) {
137
96
  if (!isPlainObject(data)) return null;
138
97
  const pricing = data.pricing ?? data.Pricing;
@@ -153,40 +112,14 @@ function aliasHasWaiver(def) {
153
112
  return isPlainObject(def) && typeof def.pricingWaiver === 'string' && def.pricingWaiver.trim() !== '';
154
113
  }
155
114
 
156
- /**
157
- * O documento usa a forma **por alias** (`pricing` / `pricingWaiver` dentro de
158
- * cada alias) em vez da forma de mapa no topo?
159
- *
160
- * Basta UM alias declarar qualquer uma das duas: um documento meio-preenchido
161
- * ainda é um documento na forma v2, e os aliases que faltam é que são o achado.
162
- */
115
+ // Basta um alias declarar `pricing` ou `pricingWaiver`: documento meio-preenchido ainda é forma por alias.
163
116
  export function usesPerAliasPricing(aliasSource) {
164
117
  if (!aliasSource) return false;
165
118
  return Object.values(aliasSource.aliases).some((d) => aliasHasPricing(d) || aliasHasWaiver(d));
166
119
  }
167
120
 
168
- /**
169
- * Todo alias do registry tem uma DECISÃO de tarifa?
170
- *
171
- * Duas formas, e a checagem cobra a que o documento usa:
172
- *
173
- * 1. **Por alias** (`model-registry.v2.json`, o exemplar que o framework manda
174
- * copiar) — a REGRA DURA escrita no próprio exemplar e imposta por
175
- * `ModelRegistry.ReadPricing`: *"todo alias declara `pricing` OU
176
- * `pricingWaiver` — nunca os dois, nunca nenhum"*. Um `pricingWaiver` é uma
177
- * tarifa **decidida**: a ausência está escrita, não esquecida, e acusá-la
178
- * seria gritar em código correto.
179
- * 2. **Mapa no topo** (`pricing` na raiz, ou a seção `Pricing.Models` de um
180
- * `appsettings*.json` — a forma medida no GHLBrain, cujo mapa é
181
- * literalmente `{}`).
182
- *
183
- * Os achados de MAPA (`pricing-map-missing` / `pricing-map-empty`) só existem
184
- * na forma 2: num documento v2 não há mapa a faltar, e emitir o achado ali era
185
- * o primeiro dos seis falsos positivos sobre o exemplar canônico.
186
- *
187
- * @param {ReturnType<typeof locateAiRegistry>} registry
188
- * @returns {null | Array<{ id: string, severity: 'warn', alias?: string, message: string }>}
189
- */
121
+ // Por alias, todo alias declara `pricing` OU `pricingWaiver`, nunca os dois, nunca nenhum (um waiver é
122
+ // tarifa decidida). Os achados de mapa só existem na forma de mapa no topo.
190
123
  export function auditModelPricing(registry) {
191
124
  if (!registry) return null;
192
125
  const { aliasSource, pricingSource } = registry;
@@ -253,19 +186,7 @@ export function auditModelPricing(registry) {
253
186
  return findings;
254
187
  }
255
188
 
256
- /**
257
- * Todo alias declara `api`?
258
- *
259
- * O default do standard é **`responses`**: medido sobre o pin, `AgentApi.Responses = 0`
260
- * e `ModelRegistry.ReadApi` devolve Responses quando o campo está ausente. A mensagem
261
- * nomeia esse default e PEDE CONFIRMAÇÃO — a ausência do campo é uma decisão por
262
- * omissão, não um defeito. É a pergunta que o `ModelRegistry.cs` do ProspectPRO
263
- * documenta ter custado um bug.
264
- *
265
- * @param {ReturnType<typeof locateAiRegistry>} registry
266
- * @returns {null | Array<{ id: string, severity: 'warn', alias: string, message: string }>}
267
- * `null` quando não há aliases para perguntar.
268
- */
189
+ /** `null` quando não há aliases para perguntar. */
269
190
  export function auditApiPerAlias(registry) {
270
191
  if (!registry || !registry.aliasSource) return null;
271
192
  const findings = [];
@@ -18,7 +18,7 @@ export class CiUsageError extends Error {}
18
18
  const NODE_KEYS = ['build', 'tests', 'validators', 'e2e'];
19
19
 
20
20
  function isVerifyResult(result) {
21
- return Boolean(result) && ['pass', 'fail'].includes(result.status) && result.scope === 'feature'
21
+ return Boolean(result) && ['pass', 'fail'].includes(result.status)
22
22
  && NODE_KEYS.every((k) => typeof result.nodes?.[k]?.status === 'string');
23
23
  }
24
24
 
@@ -47,18 +47,19 @@ function dispatchWorkflow(root, workflow, branch) {
47
47
  }
48
48
  }
49
49
 
50
- /**
51
- * `verify {f} --ci`: the feature-scope proof, run by the project's CI over the pushed commit.
52
- * Every way CI cannot answer is `mode: 'local'` with the reason, and the caller runs the local
53
- * verify: no CI is never a pause. Only the uploaded `verify --json` becomes a result, so the
54
- * gate judges a CI run by the same stamp and the same rules as a local one.
55
- *
56
- * @returns {Promise<
57
- * { mode: 'local', reason: string } |
58
- * { mode: 'pending', reason: string, run: object|null } |
59
- * { mode: 'ci', result: object, run: object, created: string|null }>}
60
- * @throws {CiUsageError} code CI would not see (uncommitted work)
61
- */
50
+ // The last green stamp still proves the tree if nothing moved since it and nothing is uncommitted
51
+ // (treeMovedSince sees only commits). A null sha or HEAD would read as "not moved", so both are required.
52
+ export function reusableLocalStamp(root, stamp) {
53
+ if (stamp?.status !== 'pass' || !stamp.sha) return null;
54
+ const head = getHeadSha(root);
55
+ if (!head) return null;
56
+ const dirty = uncommittedCode(root);
57
+ if (dirty === null || dirty.length > 0) return null;
58
+ return treeMovedSince(root, stamp.sha, head) ? null : stamp;
59
+ }
60
+
61
+ // Every way CI cannot answer is `mode: 'local'` with the reason: no CI is never a pause. Only the
62
+ // uploaded `verify --json` becomes a result, judged by the same stamp and rules as a local run.
62
63
  export async function runCiVerify(root, featureName, config, { log = () => {}, waitMs = CI_WAIT_MS, wait = waitForRun } = {}) {
63
64
  const avail = ciAvailability(root, featureName, config);
64
65
  if (!avail.ok) return { mode: 'local', reason: avail.reason };
@@ -21,14 +21,8 @@ function gh(cwd, args, timeout = 15000) {
21
21
  return execFileSync(bin, [...prefix, ...args], { cwd, stdio: 'pipe', timeout }).toString();
22
22
  }
23
23
 
24
- /**
25
- * The workflow that can stand in for `verify {feature}`: it must call `morph-spec verify` AND
26
- * publish its JSON, because only that artifact becomes a stamp. A green run of any other
27
- * workflow (lint, deploy) proves nothing about the suite.
28
- *
29
- * @param {string} root
30
- * @returns {string|null} workflow file name, as `gh run list --workflow` takes it
31
- */
24
+ // Must call `morph-spec verify` AND publish its JSON: only that artifact becomes a stamp, and a green
25
+ // lint or deploy run proves nothing about the suite.
32
26
  export function findCiWorkflow(root) {
33
27
  const dir = join(root, CI_WORKFLOW_DIR);
34
28
  if (!existsSync(dir)) return null;
@@ -69,15 +63,7 @@ function ghAuthText(root) {
69
63
  return `${res.stdout ?? ''}${res.stderr ?? ''}`;
70
64
  }
71
65
 
72
- /**
73
- * Whether CI can answer for this feature, and why not. Never throws: every "no" is a reason the
74
- * caller prints before falling back to the local verify.
75
- *
76
- * @param {string} root - the feature's tree
77
- * @param {string} featureName
78
- * @param {object|null} config - `.morph/config/config.json`
79
- * @returns {{ ok: true, branch: string, https: boolean, scopes: string|null } | { ok: false, reason: string }}
80
- */
66
+ /** Never throws: every "no" is a reason the caller prints before falling back to the local verify. */
81
67
  export function ciAvailability(root, featureName, config) {
82
68
  if (config?.project?.ci === false) return { ok: false, reason: 'project.ci = false no config.json' };
83
69
  const url = originUrl(root);
@@ -93,21 +79,14 @@ export function ciAvailability(root, featureName, config) {
93
79
  return { ok: true, branch, https: /^https?:/i.test(url), scopes };
94
80
  }
95
81
 
96
- /**
97
- * Creating a workflow file over HTTPS needs the token's `workflow` scope; without it GitHub
98
- * rejects the whole push. Unknown scopes (fine-grained token, SSH remote) are not a refusal.
99
- */
82
+ // Creating a workflow file over HTTPS needs the token's `workflow` scope, or GitHub rejects the whole push.
83
+ // Unknown scopes (fine-grained token, SSH remote) are not a refusal.
100
84
  export function workflowPushProblem({ https, scopes }) {
101
85
  if (!https || scopes === null) return null;
102
86
  return /\bworkflow\b/.test(scopes) ? null : 'o token do gh não tem o escopo "workflow" (gh auth refresh -s workflow)';
103
87
  }
104
88
 
105
- /**
106
- * Paths CI would not see: uncommitted or untracked files outside `.morph/`, plus the config,
107
- * which names the test commands.
108
- *
109
- * @returns {string[]|null} null when git did not answer
110
- */
89
+ /** Uncommitted or untracked files outside `.morph/`, plus the config; null when git did not answer. */
111
90
  export function uncommittedCode(root) {
112
91
  let out;
113
92
  try {
@@ -138,10 +117,8 @@ export function listRuns(root, branch, workflow) {
138
117
  }
139
118
  }
140
119
 
141
- /**
142
- * The newest run over the same code as HEAD. `isCurrent` is a tree comparison, not SHA equality:
143
- * the evaluator and the archive commit only `.morph/`, and the workflow does not re-run for them.
144
- */
120
+ // `isCurrent` compares trees, not SHAs: the review agents and the archive commit only `.morph/`,
121
+ // and the workflow does not re-run for them.
145
122
  export function pickRun(runs, isCurrent) {
146
123
  return (runs || []).find((run) => !(run.status === 'completed' && NO_VERDICT.has(run.conclusion))
147
124
  && run.headSha && isCurrent(run.headSha)) ?? null;
@@ -160,12 +137,7 @@ export function downloadResult(root, runId) {
160
137
  }
161
138
  }
162
139
 
163
- /**
164
- * Polls until the run over the current tree completes. Never decides: the caller turns
165
- * `timeout`/`no-run`/`unknown` into "no CI proof", never into red.
166
- *
167
- * @returns {Promise<{ state: 'completed'|'timeout'|'no-run'|'unknown', run?: object }>}
168
- */
140
+ /** Never decides: the caller turns `timeout`/`no-run`/`unknown` into "no CI proof", never into red. */
169
141
  export async function waitForRun(root, { branch, workflow, isCurrent, timeoutMs, appearMs, pollMs = 15000,
170
142
  sleep = (ms) => new Promise((r) => setTimeout(r, ms)), now = Date.now, onPending = () => {}, list = listRuns }) {
171
143
  const start = now();
@@ -56,16 +56,8 @@ const step = (name, dir, run) => [
56
56
  ` run: ${run}`,
57
57
  ];
58
58
 
59
- /**
60
- * The workflow `verify {f} --ci` relies on. Same judge as the local run: it calls the project's
61
- * pinned morph-spec, so the suite is whatever `project.tests` says, not a second definition here.
62
- * `.morph/` is out of the trigger (bookkeeping commits would cancel the run over the code) except
63
- * the config, which names the commands.
64
- *
65
- * @param {string} root
66
- * @param {object|null} config
67
- * @returns {string}
68
- */
59
+ // Calls the project's pinned morph-spec, so the suite is whatever `project.tests` says. `.morph/` is out of
60
+ // the trigger (bookkeeping commits would cancel the run over the code) except the config.
69
61
  export function renderCiWorkflow(root, config) {
70
62
  const packages = packageDirs(root, config);
71
63
  const lines = [
@@ -1,24 +1,8 @@
1
- /**
2
- * @fileoverview Claude Config Detector
3
- *
4
- * Scans .claude/ settings and plugins for MCP servers, hooks, skills, and commands.
5
- * Used by mcp-setup and session-start hooks.
6
- *
7
- * @module detectors/claude-config-detector
8
- */
9
-
10
1
  import { existsSync, readFileSync, readdirSync } from 'fs';
11
2
  import { join } from 'path';
12
3
  import { homedir } from 'os';
13
4
 
14
- /**
15
- * Plugin ids (`name@marketplace`) that are switched ON. User settings first, then
16
- * the project's; a later file overrides an earlier one, and `false` turns one off.
17
- *
18
- * @param {string} targetPath - Root path of the project
19
- * @param {string} [home] - Home directory holding `.claude/settings.json`
20
- * @returns {string[]}
21
- */
5
+ // User settings first, then the project's: a later file overrides, and `false` turns a plugin off.
22
6
  export function readEnabledPlugins(targetPath, home = homedir()) {
23
7
  const merged = new Map();
24
8
  const files = [
@@ -32,17 +16,11 @@ export function readEnabledPlugins(targetPath, home = homedir()) {
32
16
  const map = JSON.parse(readFileSync(file, 'utf8').replace(/^/, '')).enabledPlugins;
33
17
  if (!map || typeof map !== 'object') continue;
34
18
  for (const [id, on] of Object.entries(map)) merged.set(id, on === true);
35
- } catch { /* invalid JSON */ }
19
+ } catch {}
36
20
  }
37
21
  return [...merged].filter(([, on]) => on).map(([id]) => id);
38
22
  }
39
23
 
40
- /**
41
- * Detect Claude Code configuration for a project.
42
- *
43
- * @param {string} targetPath - Root path of the project
44
- * @returns {{ plugins: string[], enabledPlugins: string[], mcpServers: Array<{name: string, config: Object}>, customSkills: string[], customCommands: string[], hooks: string[] }}
45
- */
46
24
  export function detectClaudeConfig(targetPath) {
47
25
  const result = {
48
26
  plugins: [],
@@ -58,7 +36,6 @@ export function detectClaudeConfig(targetPath) {
58
36
  const mcpJsonNames = new Set();
59
37
 
60
38
  try {
61
- // ── MCP Servers from .mcp.json (primary — Claude Code reads this) ───────
62
39
  const mcpJsonPath = join(targetPath, '.mcp.json');
63
40
  if (existsSync(mcpJsonPath)) {
64
41
  try {
@@ -68,10 +45,9 @@ export function detectClaudeConfig(targetPath) {
68
45
  result.mcpServers.push({ name, config });
69
46
  mcpJsonNames.add(name);
70
47
  }
71
- } catch { /* invalid JSON */ }
48
+ } catch {}
72
49
  }
73
50
 
74
- // ── MCP Servers + hooks from settings (legacy + hooks/permissions) ───────
75
51
  const settingsPaths = [
76
52
  join(targetPath, '.claude', 'settings.json'),
77
53
  join(targetPath, '.claude', 'settings.local.json'),
@@ -81,19 +57,18 @@ export function detectClaudeConfig(targetPath) {
81
57
  if (existsSync(sp)) {
82
58
  try {
83
59
  const settings = JSON.parse(readFileSync(sp, 'utf8'));
84
- // Legacy MCP entries — skip if already in .mcp.json, but allow later settings files to override earlier ones
60
+ // Legacy entries: .mcp.json wins; among settings files, the later one wins.
85
61
  const mcps = settings.mcpServers || {};
86
62
  for (const [name, config] of Object.entries(mcps)) {
87
- if (mcpJsonNames.has(name)) continue; // .mcp.json takes priority
63
+ if (mcpJsonNames.has(name)) continue;
88
64
  const idx = result.mcpServers.findIndex(m => m.name === name);
89
65
  if (idx >= 0) {
90
- result.mcpServers[idx] = { name, config }; // later file wins
66
+ result.mcpServers[idx] = { name, config };
91
67
  } else {
92
68
  result.mcpServers.push({ name, config });
93
69
  }
94
70
  }
95
71
 
96
- // Collect hooks
97
72
  if (settings.hooks) {
98
73
  for (const [event, eventHooks] of Object.entries(settings.hooks)) {
99
74
  for (const group of (Array.isArray(eventHooks) ? eventHooks : [])) {
@@ -103,60 +78,43 @@ export function detectClaudeConfig(targetPath) {
103
78
  }
104
79
  }
105
80
  }
106
- } catch { /* invalid JSON */ }
81
+ } catch {}
107
82
  }
108
83
  }
109
84
 
110
- // ── Plugins from global config ──────────────────────────────────────────
111
85
  const pluginsPath = join(homedir(), '.claude', 'plugins', 'installed_plugins.json');
112
86
  if (existsSync(pluginsPath)) {
113
87
  try {
114
88
  const pluginsData = JSON.parse(readFileSync(pluginsPath, 'utf8'));
115
89
  const plugins = pluginsData.plugins || {};
116
90
  result.plugins = Object.keys(plugins);
117
- } catch { /* invalid JSON */ }
91
+ } catch {}
118
92
  }
119
93
 
120
- // ── Custom skills ───────────────────────────────────────────────────────
121
94
  const skillsPath = join(targetPath, '.claude', 'skills');
122
95
  if (existsSync(skillsPath)) {
123
96
  try {
124
97
  result.customSkills = readdirSync(skillsPath).filter(f => !f.startsWith('.'));
125
- } catch { /* ignore */ }
98
+ } catch {}
126
99
  }
127
100
 
128
- // ── Custom commands ─────────────────────────────────────────────────────
129
101
  const commandsPath = join(targetPath, '.claude', 'commands');
130
102
  if (existsSync(commandsPath)) {
131
103
  try {
132
104
  result.customCommands = readdirSync(commandsPath).filter(f => f.endsWith('.md'));
133
- } catch { /* ignore */ }
105
+ } catch {}
134
106
  }
135
107
  } catch {
136
- // Fail-safe
137
108
  }
138
109
 
139
110
  return result;
140
111
  }
141
112
 
142
- /**
143
- * Cross-reference configured MCPs with the MCP registry's phase matrix.
144
- *
145
- * `phaseMatrix` and `currentPhase` default rather than being required: the
146
- * `init` caller passed only `mcpServers`, so `phaseMatrix[currentPhase]` threw
147
- * a TypeError that `init`'s outer catch swallowed as
148
- * "Claude config detection skipped" — taking `saveIntegrations` and
149
- * `generateSettings` down with it, since both run after this line.
150
- *
151
- * @param {Array<{name: string}>} mcpServers - Detected MCP server list
152
- * @param {Object} [phaseMatrix] - From framework/mcp/registry.json → phaseMatrix
153
- * @param {string} [currentPhase] - Phase to look up; omit for every phase
154
- * @returns {{ available: string[], recommended: string[], missing: string[] }}
155
- */
113
+ // `phaseMatrix`/`currentPhase` default: `init` passes only `mcpServers`, and a throw here was
114
+ // swallowed by its outer catch, skipping the steps after it.
156
115
  export function mapMcpsToPhases(mcpServers, phaseMatrix = {}, currentPhase = null) {
157
116
  const configuredNames = new Set((mcpServers || []).map(m => m.name.toLowerCase()));
158
- // No phase asked for → the union across every phase, deduped. That is what a
159
- // caller showing "which of my MCPs this framework uses" wants.
117
+ // No phase asked for → the union across every phase, deduped.
160
118
  const recommended = currentPhase
161
119
  ? (phaseMatrix[currentPhase] || [])
162
120
  : [...new Set(Object.values(phaseMatrix).flat())];
@@ -168,19 +126,6 @@ export function mapMcpsToPhases(mcpServers, phaseMatrix = {}, currentPhase = nul
168
126
  };
169
127
  }
170
128
 
171
- /**
172
- * The MCP-by-phase table `init` prints: every phase that recommends at least
173
- * one of the MCPs this machine actually has configured.
174
- *
175
- * Separate from `mapMcpsToPhases` because the caller wants a per-phase
176
- * breakdown, not one phase's slice — reaching for the wrong one is what
177
- * produced `Object.entries()` over `{available, recommended, missing}` and a
178
- * `.map(m => m.name)` over plain strings.
179
- *
180
- * @param {Array<{name: string}>} mcpServers
181
- * @param {Object} phaseMatrix
182
- * @returns {Array<{phase: string, available: string[]}>} phases with ≥1 match
183
- */
184
129
  export function mcpAvailabilityByPhase(mcpServers, phaseMatrix = {}) {
185
130
  return Object.keys(phaseMatrix)
186
131
  .map(phase => ({
@@ -190,12 +135,6 @@ export function mcpAvailabilityByPhase(mcpServers, phaseMatrix = {}) {
190
135
  .filter(({ available }) => available.length > 0);
191
136
  }
192
137
 
193
- /**
194
- * Format a human-readable one-liner summary of detected config.
195
- *
196
- * @param {{ plugins: string[], mcpServers: Array<{name: string}> }} config
197
- * @returns {string}
198
- */
199
138
  export function formatConfigSummary(config) {
200
139
  const parts = [];
201
140
  if (config.mcpServers.length > 0) {