@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,15 +1,3 @@
1
- /**
2
- * MCP Setup Command
3
- *
4
- * Install and configure MCP servers for morph-spec projects.
5
- *
6
- * Usage:
7
- * morph-spec mcp setup — Interactive wizard showing all MCPs
8
- * morph-spec mcp setup <name> — Setup a specific MCP
9
- * morph-spec mcp setup --list — Show MCP install status
10
- * morph-spec mcp setup --auto — Non-interactive auto-install credential-free MCPs
11
- */
12
-
13
1
  import chalk from 'chalk';
14
2
  import inquirer from 'inquirer';
15
3
  import ora from 'ora';
@@ -31,17 +19,9 @@ import {
31
19
  import { detectClaudeConfig } from '../../lib/detectors/claude-config-detector.js';
32
20
  import { detectProject } from '../../lib/detectors/index.js';
33
21
 
34
- /**
35
- * Detect the current project's stack.
36
- * Prefers .morph/config/config.json (set during morph-spec init) over filesystem re-detection.
37
- *
38
- * Exported for testing: the stack decides which MCPs are relevant, and it is
39
- * the one branch here worth pinning — `.morph/config/config.json` is the only
40
- * place a compound stack (`dotnet-nextjs`) survives, since filesystem
41
- * re-detection collapses it to whichever half it notices first.
42
- */
22
+ // config.json is the only place a compound stack (`dotnet-nextjs`) survives: filesystem
23
+ // re-detection collapses it to whichever half it notices first.
43
24
  export async function detectStack(targetPath) {
44
- // 1. Prefer the authoritative config (handles compound stacks like nextjs+dotnet)
45
25
  try {
46
26
  const configPath = join(targetPath, '.morph', 'config', 'config.json');
47
27
  if (existsSync(configPath)) {
@@ -55,7 +35,6 @@ export async function detectStack(targetPath) {
55
35
  // Fall through to filesystem detection
56
36
  }
57
37
 
58
- // 2. Fallback: detect from filesystem
59
38
  try {
60
39
  const results = await detectProject(targetPath, { conversation: false, generateStandards: false });
61
40
  return results.structure?.stack || 'unknown';
@@ -64,14 +43,8 @@ export async function detectStack(targetPath) {
64
43
  }
65
44
  }
66
45
 
67
- /**
68
- * Detect the project's integrations (`neon`, `stripe`...), same sources and
69
- * order as detectStack. The detectors never fold them into the stack string, so
70
- * without this an MCP scoped to an integration (neon, asaas) is never offered.
71
- *
72
- * @param {string} targetPath
73
- * @returns {Promise<string[]>}
74
- */
46
+ // The detectors never fold integrations into the stack string: without this, an MCP scoped
47
+ // to an integration (neon, asaas) is never offered.
75
48
  export async function detectIntegrations(targetPath) {
76
49
  try {
77
50
  const configPath = join(targetPath, '.morph', 'config', 'config.json');
@@ -95,7 +68,6 @@ export async function mcpSetupCommand(name, options) {
95
68
  const targetPath = process.cwd();
96
69
 
97
70
  try {
98
- // Detect current environment
99
71
  const stack = await detectStack(targetPath);
100
72
  const integrations = await detectIntegrations(targetPath);
101
73
  const claudeConfig = await detectClaudeConfig(targetPath);
@@ -114,7 +86,6 @@ export async function mcpSetupCommand(name, options) {
114
86
  return setupSpecificMcp(targetPath, name, stack, existingMcps);
115
87
  }
116
88
 
117
- // Interactive wizard
118
89
  return interactiveWizard(targetPath, stack, existingMcps, integrations, enabledPlugins);
119
90
  } catch (err) {
120
91
  console.error(chalk.red(`Error: ${err.message}`));
@@ -122,9 +93,6 @@ export async function mcpSetupCommand(name, options) {
122
93
  }
123
94
  }
124
95
 
125
- /**
126
- * Show MCP status list (--list)
127
- */
128
96
  async function showStatusList(targetPath, stack, existingMcps, integrations, enabledPlugins) {
129
97
  const orchestration = orchestrateMcpSetup(targetPath, stack, existingMcps, { integrations, enabledPlugins });
130
98
  const statusRows = formatMcpStatusTable(orchestration, {});
@@ -156,9 +124,6 @@ async function showStatusList(targetPath, stack, existingMcps, integrations, ena
156
124
  console.log('');
157
125
  }
158
126
 
159
- /**
160
- * Auto-install credential-free MCPs (--auto)
161
- */
162
127
  async function autoInstall(targetPath, stack, existingMcps, integrations, enabledPlugins) {
163
128
  const orchestration = orchestrateMcpSetup(targetPath, stack, existingMcps, { autoOnly: true, integrations, enabledPlugins });
164
129
  const autoNames = Object.keys(orchestration.autoInstallable);
@@ -176,7 +141,6 @@ async function autoInstall(targetPath, stack, existingMcps, integrations, enable
176
141
  console.log(chalk.gray(` Skipped (already configured): ${result.skipped.join(', ')}`));
177
142
  }
178
143
 
179
- // Install companion skills for MCPs that define them
180
144
  const skillResults = installCompanionSkills(orchestration.autoInstallable);
181
145
  for (const r of skillResults) {
182
146
  if (r.success) {
@@ -189,9 +153,6 @@ async function autoInstall(targetPath, stack, existingMcps, integrations, enable
189
153
  console.log('');
190
154
  }
191
155
 
192
- /**
193
- * Setup a specific MCP by name
194
- */
195
156
  async function setupSpecificMcp(targetPath, name, stack, existingMcps) {
196
157
  const registry = loadMcpRegistry();
197
158
  const mcpEntry = registry.mcps[name];
@@ -208,7 +169,6 @@ async function setupSpecificMcp(targetPath, name, stack, existingMcps) {
208
169
  return;
209
170
  }
210
171
 
211
- // Check if already installed
212
172
  const isInstalled = existingMcps.some(s => s.name.toLowerCase().includes(name));
213
173
  if (isInstalled) {
214
174
  const { reconfigure } = await inquirer.prompt([{
@@ -220,7 +180,6 @@ async function setupSpecificMcp(targetPath, name, stack, existingMcps) {
220
180
  if (!reconfigure) return;
221
181
  }
222
182
 
223
- // Check prerequisites
224
183
  const prereqs = mcpEntry.install.prerequisites || [];
225
184
  for (const prereq of prereqs) {
226
185
  try {
@@ -234,20 +193,17 @@ async function setupSpecificMcp(targetPath, name, stack, existingMcps) {
234
193
  }
235
194
  }
236
195
 
237
- // Show warnings
238
196
  for (const warning of mcpEntry.install.warnings || []) {
239
197
  console.log(chalk.yellow(`\n ⚠ ${warning}`));
240
198
  }
241
199
 
242
200
  const credentials = mcpEntry.install.credentials || [];
243
201
 
244
- // Stitch MCP — automated setup via setupStitch()
245
202
  if (name === 'stitch' && !mcpEntry.install.autoInstall) {
246
203
  console.log(chalk.cyan(`\n Setting up Stitch MCP (automated)\n`));
247
204
  console.log(chalk.gray(` Usage: ${mcpEntry.usage}`));
248
205
  console.log('');
249
206
 
250
- // Allow user to provide GCP project if auto-detection might fail
251
207
  let gcpProject;
252
208
  const gcpCred = credentials.find(c => c.envVar === 'STITCH_GCP_PROJECT');
253
209
  const { gcpProjectInput } = await inquirer.prompt([{
@@ -260,7 +216,6 @@ async function setupSpecificMcp(targetPath, name, stack, existingMcps) {
260
216
  const spinner = ora('Setting up Stitch MCP...').start();
261
217
  const result = await setupStitch(targetPath, { gcpProject });
262
218
 
263
- // Report step-by-step results
264
219
  for (const step of result.steps) {
265
220
  if (step.success) {
266
221
  switch (step.step) {
@@ -288,7 +243,6 @@ async function setupSpecificMcp(targetPath, name, stack, existingMcps) {
288
243
  if (failedStep?.error) {
289
244
  console.log(chalk.red(` ${failedStep.error}`));
290
245
  }
291
- // Show health-check warning if it failed but setup otherwise succeeded
292
246
  const healthStep = result.steps.find(s => s.step === 'health-check');
293
247
  if (healthStep && !healthStep.success) {
294
248
  console.log(chalk.yellow(` ⚠ Health-check: ${healthStep.error}`));
@@ -318,7 +272,6 @@ async function setupSpecificMcp(targetPath, name, stack, existingMcps) {
318
272
  return;
319
273
  }
320
274
 
321
- // Auto-installable — just install
322
275
  if (mcpEntry.install.autoInstall) {
323
276
  const spinner = ora(`Installing ${name}...`).start();
324
277
  const result = await installAutoMcps(targetPath, { [name]: mcpEntry });
@@ -331,7 +284,6 @@ async function setupSpecificMcp(targetPath, name, stack, existingMcps) {
331
284
  return;
332
285
  }
333
286
 
334
- // Remote HTTP MCP — show setup command and optionally auto-configure
335
287
  if (isRemoteMcp(mcpEntry.install.config)) {
336
288
  console.log(chalk.cyan(`\n Setting up ${name} MCP (remote)\n`));
337
289
  console.log(chalk.gray(` Usage: ${mcpEntry.usage}`));
@@ -372,7 +324,6 @@ async function setupSpecificMcp(targetPath, name, stack, existingMcps) {
372
324
  return;
373
325
  }
374
326
 
375
- // Needs credentials (stdio MCPs)
376
327
  console.log(chalk.cyan(`\n Setting up ${name} MCP\n`));
377
328
  console.log(chalk.gray(` Usage: ${mcpEntry.usage}`));
378
329
  console.log('');
@@ -400,7 +351,6 @@ async function setupSpecificMcp(targetPath, name, stack, existingMcps) {
400
351
  await installMcpWithCredentials(targetPath, name, mcpEntry, credentialValues);
401
352
  spinner.succeed(`${name} MCP configured`);
402
353
  } else {
403
- // Show config snippet
404
354
  const instructions = generateSetupInstructions(name, mcpEntry);
405
355
  console.log(chalk.gray('\n Add to .mcp.json → mcpServers:'));
406
356
  console.log(chalk.gray(' ' + '─'.repeat(55)));
@@ -417,9 +367,6 @@ async function setupSpecificMcp(targetPath, name, stack, existingMcps) {
417
367
  console.log('');
418
368
  }
419
369
 
420
- /**
421
- * Interactive wizard showing all MCPs
422
- */
423
370
  async function interactiveWizard(targetPath, stack, existingMcps, integrations, enabledPlugins) {
424
371
  const orchestration = orchestrateMcpSetup(targetPath, stack, existingMcps, { integrations, enabledPlugins });
425
372
 
@@ -435,7 +382,6 @@ async function interactiveWizard(targetPath, stack, existingMcps, integrations,
435
382
  console.log(chalk.green(` Already configured: ${alreadyNames.join(', ')}`));
436
383
  }
437
384
 
438
- // Auto-install
439
385
  if (autoNames.length > 0) {
440
386
  const { installAuto } = await inquirer.prompt([{
441
387
  type: 'confirm',
@@ -449,7 +395,6 @@ async function interactiveWizard(targetPath, stack, existingMcps, integrations,
449
395
  const result = await installAutoMcps(targetPath, orchestration.autoInstallable);
450
396
  spinner.succeed(`Installed: ${result.added.join(', ')}`);
451
397
 
452
- // Install companion skills for MCPs that define them
453
398
  const skillResults = installCompanionSkills(orchestration.autoInstallable);
454
399
  for (const r of skillResults) {
455
400
  if (r.success) {
@@ -461,7 +406,6 @@ async function interactiveWizard(targetPath, stack, existingMcps, integrations,
461
406
  }
462
407
  }
463
408
 
464
- // Credential MCPs
465
409
  if (manualNames.length > 0) {
466
410
  console.log(chalk.cyan('\n Credential-requiring MCPs:'));
467
411
 
@@ -481,7 +425,6 @@ async function interactiveWizard(targetPath, stack, existingMcps, integrations,
481
425
  }]);
482
426
 
483
427
  if (setupNow) {
484
- // Stitch — automated setup via setupStitch()
485
428
  if (name === 'stitch' && !entry.install.autoInstall) {
486
429
  const gcpCred = entry.install.credentials?.find(c => c.envVar === 'STITCH_GCP_PROJECT');
487
430
  const { gcpProject } = await inquirer.prompt([{
@@ -507,7 +450,6 @@ async function interactiveWizard(targetPath, stack, existingMcps, integrations,
507
450
  continue;
508
451
  }
509
452
 
510
- // Remote MCPs — launch OAuth via claude mcp add
511
453
  if (isRemoteMcp(entry.install.config)) {
512
454
  console.log(chalk.cyan(` Launching ${capitalizedName} OAuth...`));
513
455
  try {
@@ -1,34 +1,5 @@
1
- /**
2
- * `morph-spec cost [feature] [--by task|phase] [--all-trees] [--metrics [--top n]] [--json]`
3
- *
4
- * A cost-facing view over the same harness event stream `telemetry` reads
5
- * (.morph/logs/events.jsonl): how much wall-clock time was spent inside
6
- * measured tasks (TaskStart→TaskEnd), how many context bytes were
7
- * re-ingested (grouped by kind: rubric/standard/skill/agents/plan-artifact/
8
- * code), and how many sub-agent dispatches happened (with total prompt
9
- * chars). When the stream has `SessionUsage` events (real, hook-observed
10
- * token counts), those are summed and shown too — see the sessionUsage
11
- * caveat below.
12
- *
13
- * IMPORTANT: bytes re-ingested is a DETERMINISTIC PROXY for cost, not a
14
- * token count. Hooks only see what tools read off disk — never the model's
15
- * actual token usage — so this measures re-ingestion pressure, not spend.
16
- * `sessionUsage` (when present) is the exception: real tokens, observed
17
- * directly, not a proxy — see aggregateCost's docstring for the contract.
18
- *
19
- * `--all-trees` widens the source from "just the current tree" to every git
20
- * worktree that has its own events.jsonl plus the logs `finish` saved before
21
- * removing a worktree, deduplicated by eventId.
22
- *
23
- * `--metrics` measures the loop instead: turns, tool calls by orchestrator and
24
- * sub-agent, time per command class, identical reruns and why, rereads, how
25
- * many feature-scope verifies each feature ran, git calls per task and whether
26
- * the reuse validator ran in root and worktree (see lib/telemetry/metrics.js).
27
- *
28
- * Read-only, always exits 0 on a normal (even empty) run — fail-open, an
29
- * empty stream is an answer, not an error. Only `--by` with an unknown value
30
- * is a usage error (exit 1).
31
- */
1
+ // Bytes re-ingested is a deterministic proxy for cost, not a token count: hooks only see what
2
+ // tools read off disk. `sessionUsage` is the exception, real hook-observed tokens.
32
3
 
33
4
  import chalk from 'chalk';
34
5
  import { loadTelemetry, loadTelemetryAllTrees, aggregateCost, dedupeEvents } from '../../lib/telemetry/telemetry-reader.js';
@@ -37,7 +8,7 @@ import { computeMetrics } from '../../lib/telemetry/metrics.js';
37
8
  const BYTES_CAVEAT =
38
9
  'Bytes re-ingeridos são um PROXY determinístico de custo (o que as ferramentas leram do disco) — NÃO são tokens reais. Hooks não enxergam o uso real de tokens do modelo.';
39
10
 
40
- const BY_MODES = ['task', 'phase'];
11
+ const BY_MODES = ['phase'];
41
12
 
42
13
  function fmtBytes(n) {
43
14
  if (!n) return '0 B';
@@ -65,21 +36,6 @@ function printBytesByKind(bytesByKind) {
65
36
  }
66
37
  }
67
38
 
68
- function printTasks(tasks) {
69
- if (!tasks.length) {
70
- console.log(chalk.gray('\nNenhuma task com TaskStart/TaskEnd capturada.'));
71
- return;
72
- }
73
- for (const t of tasks) {
74
- const status = t.open ? chalk.yellow('[em aberto]') : (t.status || '-');
75
- console.log('\n' + chalk.white.bold(`Task ${t.task}`) + chalk.gray(` · ${t.feature ?? '(sem feature)'}`) + ` · ${status}`);
76
- console.log(` duração: ${fmtDuration(t.durationSeconds)}`);
77
- console.log(` dispatches: ${t.dispatches.count} (${t.dispatches.promptChars} chars de prompt)`);
78
- console.log(' bytes re-ingeridos por kind:');
79
- printBytesByKind(t.bytesByKind);
80
- }
81
- }
82
-
83
39
  function printPhases(phases) {
84
40
  if (!phases.length) {
85
41
  console.log(chalk.gray('\nNenhum byte de Read com phase capturado.'));
@@ -96,10 +52,6 @@ function tokenLine(u) {
96
52
  + `cacheRead=${u.cacheReadTokens} cacheCreation=${u.cacheCreationTokens}`;
97
53
  }
98
54
 
99
- /**
100
- * Real (hook-observed) token counts — the non-proxy sibling of the bytes
101
- * above: the main sessions and, separately, the sub-agents.
102
- */
103
55
  function printSessionUsage(summary) {
104
56
  const { sessionUsage, agentUsage, sessionUsageLegacySamples } = summary;
105
57
  if (sessionUsage) {
@@ -118,8 +70,7 @@ function printSessionUsage(summary) {
118
70
  }
119
71
 
120
72
  function printSummary(summary) {
121
- console.log(`\n duração dentro de tasks medidas: ${fmtDuration(summary.taskDurationSeconds)}`);
122
- console.log(` dispatches: ${summary.dispatches.count} (${summary.dispatches.promptChars} chars de prompt)`);
73
+ console.log(`\n dispatches: ${summary.dispatches.count} (${summary.dispatches.promptChars} chars de prompt)`);
123
74
 
124
75
  console.log('\n' + chalk.white.bold('Bytes re-ingeridos por kind') + ` (${fmtBytes(summary.bytesTotal)} total)`);
125
76
  printBytesByKind(summary.bytesByKind);
@@ -140,7 +91,7 @@ function split(counts) {
140
91
  return `${counts.total} (orq ${counts.orchestrator} · sub ${counts.subagent}${counts.unknown ? ` · sem agente ${counts.unknown}` : ''})`;
141
92
  }
142
93
 
143
- function printMetricsBody(m, { by }) {
94
+ function printMetricsBody(m) {
144
95
  const legacyShare = m.window.events ? m.coverage.legacy / m.window.events : 0;
145
96
  console.log(chalk.gray(`janela: ${m.window.from ?? '-'} → ${m.window.to ?? '-'} · ${m.window.events} eventos`
146
97
  + ` · ${pct(1 - legacyShare)} no esquema v2`));
@@ -149,16 +100,8 @@ function printMetricsBody(m, { by }) {
149
100
  }
150
101
 
151
102
  console.log('\n' + chalk.white.bold('Turnos e chamadas'));
152
- console.log(` turnos: ${m.turns ? split(m.turns) : 'desconhecido (nenhum PostToolBatch na janela)'}`
153
- + (m.turnsPerTaskMedian !== null ? chalk.gray(` · mediana por task ${m.turnsPerTaskMedian}`) : ''));
103
+ console.log(` turnos: ${m.turns ? split(m.turns) : 'desconhecido (nenhum PostToolBatch na janela)'}`);
154
104
  console.log(` chamadas de ferramenta: ${split(m.toolCalls)}`);
155
- const perTask = m.perTaskByFeature;
156
- if (perTask.toolCalls !== null) {
157
- console.log(` por task (total da feature ÷ tasks dela, mediana): ${perTask.toolCalls} chamadas`
158
- + (perTask.turns !== null ? ` · ${perTask.turns} turnos` : '') + ` · ${perTask.git} git status+diff`
159
- + chalk.gray(` · as janelas TaskStart→TaskEnd têm ${pct(m.taskWindowCoverage)} das chamadas`));
160
- }
161
-
162
105
  console.log('\n' + chalk.white.bold('Tempo por classe de comando'));
163
106
  for (const [klass, t] of Object.entries(m.timeByClass).sort((a, b) => b[1].measuredMs - a[1].measuredMs)) {
164
107
  const unmeasured = t.unmeasured ? chalk.gray(` (${t.unmeasured} sem duração)`) : '';
@@ -183,9 +126,9 @@ function printMetricsBody(m, { by }) {
183
126
  }
184
127
 
185
128
  console.log('\n' + chalk.white.bold('verify, git e reuso'));
186
- const perFeature = Object.entries(m.verify.byFeature).sort((a, b) => b[1].feature - a[1].feature);
187
- console.log(` verify de feature por feature: mediana ${m.verify.featureScopeMedian ?? '-'}`
188
- + (perFeature.length ? chalk.gray(` (${perFeature.slice(0, 5).map(([f, v]) => `${f} ${v.feature}`).join(', ')})`) : ''));
129
+ const perFeature = Object.entries(m.verify.byFeature).sort((a, b) => b[1] - a[1]);
130
+ console.log(` verify por feature: mediana ${m.verify.median ?? '-'}`
131
+ + (perFeature.length ? chalk.gray(` (${perFeature.slice(0, 5).map(([f, n]) => `${f} ${n}`).join(', ')})`) : ''));
189
132
  const repeats = Object.entries(m.verify.unchangedRepeats);
190
133
  if (repeats.length) {
191
134
  console.log(` verify de feature sobre a mesma árvore do anterior: ${repeats.reduce((n, [, v]) => n + v, 0)}`
@@ -196,30 +139,14 @@ function printMetricsBody(m, { by }) {
196
139
  console.log(` suíte .NET inteira que recompila (sem --no-build): ${m.dotnetFullSuite.withBuild}/${m.dotnetFullSuite.runs}`
197
140
  + chalk.gray(` (${phases})`));
198
141
  }
199
- console.log(` git status / diff por task: mediana ${m.gitPerTaskMedian.status ?? '-'} / ${m.gitPerTaskMedian.diff ?? '-'}`
200
- + chalk.gray(` · total ${m.git.status} / ${m.git.diff}`));
142
+ console.log(` git status / diff: ${m.git.status} / ${m.git.diff}`);
201
143
  const trees = Object.entries(m.reuse.byTree);
202
144
  console.log(` reuse-validator: ${trees.length
203
145
  ? trees.map(([tree, s]) => `${tree} rodou ${s.ran}, pulou ${s.skipped}`).join(' · ')
204
146
  : 'nenhum evento Verify na janela'}`);
205
147
  for (const [reason, n] of Object.entries(m.reuse.skipReasons)) console.log(chalk.gray(` pulou ${n}×: ${reason}`));
206
-
207
- if (by === 'task') {
208
- for (const t of m.tasks) {
209
- const status = t.open ? chalk.yellow('[em aberto]') : (t.status || '-');
210
- console.log('\n' + chalk.white.bold(`Task ${t.task}`) + chalk.gray(` · ${t.feature ?? '(sem feature)'}`) + ` · ${status}`);
211
- console.log(` turnos ${t.turns ? split(t.turns) : '-'} · chamadas ${split(t.toolCalls)}`);
212
- const time = Object.entries(t.timeByClass).map(([k, v]) => `${k} ${fmtDuration(v.measuredMs / 1000)}`).join(' · ');
213
- if (time) console.log(` tempo: ${time}`);
214
- console.log(` rerun ${pct(t.reruns.rate)} (${t.reruns.reruns}/${t.reruns.runs}) · releitura ${pct(t.rereads.rate)} · git status/diff ${t.git.status}/${t.git.diff}`);
215
- }
216
- }
217
148
  }
218
149
 
219
- /**
220
- * `--metrics`: the loop measurements over the current tree (and, with
221
- * `--all-trees`, every tree plus the finish backups, deduplicated).
222
- */
223
150
  function printMetrics(feature, options, cwd) {
224
151
  const events = options.allTrees
225
152
  ? dedupeEvents(loadTelemetryAllTrees(cwd).flatMap((t) => t.events))
@@ -237,19 +164,9 @@ function printMetrics(feature, options, cwd) {
237
164
  }
238
165
  const scope = feature ? chalk.cyan(` · feature ${feature}`) : '';
239
166
  console.log(chalk.white.bold(`Medições do loop${options.allTrees ? ' — todas as árvores + finish-backup' : ''}`) + scope);
240
- printMetricsBody(metrics, options);
167
+ printMetricsBody(metrics);
241
168
  }
242
169
 
243
- /**
244
- * `--all-trees` rendering: a short line per tree (so a fleet operator can
245
- * spot which tree is expensive) followed by the COMBINED aggregate — full
246
- * detail (task/phase breakdown honors `--by` here too, same as the
247
- * single-tree path; a typeable flag combination is never a silent no-op).
248
- *
249
- * @param {string} [feature]
250
- * @param {{ json?: boolean, by?: 'task'|'phase' }} options
251
- * @param {string} cwd
252
- */
253
170
  function printAllTrees(feature, options, cwd) {
254
171
  const treesData = loadTelemetryAllTrees(cwd);
255
172
  const perTree = treesData.map(({ tree, path, events }) => ({
@@ -277,7 +194,7 @@ function printAllTrees(feature, options, cwd) {
277
194
 
278
195
  for (const t of perTree) {
279
196
  console.log('\n' + chalk.cyan.bold(`── Árvore: ${t.tree}`) + chalk.gray(` — ${t.path}`));
280
- console.log(` ${t.total} eventos · duração em tasks: ${fmtDuration(t.taskDurationSeconds)} · `
197
+ console.log(` ${t.total} eventos · `
281
198
  + `bytes: ${fmtBytes(t.bytesTotal)} · dispatches: ${t.dispatches.count}`);
282
199
  }
283
200
 
@@ -285,19 +202,13 @@ function printAllTrees(feature, options, cwd) {
285
202
  if (combined.features.length) {
286
203
  console.log(chalk.gray(`Features: ${combined.features.join(', ')}`));
287
204
  }
288
- if (options.by === 'task') {
289
- printTasks(combined.tasks);
290
- } else if (options.by === 'phase') {
205
+ if (options.by === 'phase') {
291
206
  printPhases(combined.phases);
292
207
  } else {
293
208
  printSummary(combined);
294
209
  }
295
210
  }
296
211
 
297
- /**
298
- * @param {string} [feature] - narrow to a single feature
299
- * @param {{ json?: boolean, by?: 'task'|'phase', allTrees?: boolean, cwd?: string }} [options]
300
- */
301
212
  export function costCommand(feature, options = {}) {
302
213
  if (options.by && !BY_MODES.includes(options.by)) {
303
214
  console.error(chalk.red(`\n❌ Invalid --by: "${options.by}"`));
@@ -314,8 +225,7 @@ export function costCommand(feature, options = {}) {
314
225
  }
315
226
 
316
227
  if (options.allTrees) {
317
- // Same fail-open + Windows-flush discipline as the single-tree path
318
- // below — no forced process.exit(), --json included.
228
+ // No process.exit() here either, --json included.
319
229
  printAllTrees(feature, options, cwd);
320
230
  return;
321
231
  }
@@ -324,10 +234,8 @@ export function costCommand(feature, options = {}) {
324
234
  const summary = aggregateCost(events, { feature });
325
235
 
326
236
  if (options.json) {
327
- // No forced process.exit() here: on Windows a piped stdout write is
328
- // async, and exit()ing right after a large console.log can race the
329
- // flush and truncate output. Letting the event loop drain naturally
330
- // (nothing else keeps it alive in this command) exits 0 without risk.
237
+ // No process.exit(): on Windows a piped stdout write is async, and exiting right after a
238
+ // large console.log can truncate the output.
331
239
  console.log(JSON.stringify(summary));
332
240
  return;
333
241
  }
@@ -345,9 +253,7 @@ export function costCommand(feature, options = {}) {
345
253
  console.log(chalk.gray(`Features: ${summary.features.join(', ')}`));
346
254
  }
347
255
 
348
- if (options.by === 'task') {
349
- printTasks(summary.tasks);
350
- } else if (options.by === 'phase') {
256
+ if (options.by === 'phase') {
351
257
  printPhases(summary.phases);
352
258
  } else {
353
259
  printSummary(summary);
@@ -1,14 +1,3 @@
1
- /**
2
- * `morph-spec telemetry [--json] [--feature <name>] [--limit <n>]`
3
- *
4
- * The reader surface over the harness event stream (.morph/logs/events.jsonl,
5
- * written by the telemetry-log hook). Reconstructs the whole execution path of
6
- * an NLH session: top skills, personas dispatched, standards (knowledge base)
7
- * read, the tool mix, and the recent prompts (user + sub-agent).
8
- *
9
- * Read-only, always exits 0 (fail-open — an empty stream is an answer).
10
- */
11
-
12
1
  import chalk from 'chalk';
13
2
  import { loadTelemetry, aggregateTelemetry } from '../../lib/telemetry/telemetry-reader.js';
14
3
 
@@ -23,9 +12,7 @@ function section(title, rows, fmt) {
23
12
  for (const r of rows) console.log(' ' + fmt(r));
24
13
  }
25
14
 
26
- /**
27
- * @param {{ json?: boolean, feature?: string, limit?: number, cwd?: string }} [options]
28
- */
15
+ // Read-only and always exits 0: an empty stream is an answer.
29
16
  export function telemetryCommand(options = {}) {
30
17
  const events = loadTelemetry(options.cwd || process.cwd());
31
18
  const summary = aggregateTelemetry(events, { feature: options.feature });
@@ -1,23 +1,3 @@
1
- /**
2
- * branch.js — `morph-spec branch prune`.
3
- *
4
- * The gap this closes: after a few features close by squash-merge, the local
5
- * repo accumulates branches git considers unmerged. `git branch --merged` lists
6
- * none of them, `git branch -d` refuses each one, and the only command left is
7
- * `git branch -D` — which is EXACTLY the command for throwing away real work.
8
- * Once a user has typed `-D` on five safe branches, the sixth (an actual
9
- * unintegrated feature) gets the same keystroke.
10
- *
11
- * So this command never asks the user to make that distinction: it makes it
12
- * itself, prints the evidence for each branch, and deletes only what it PROVED
13
- * is already integrated (see branch-classifier.js for the five classes and
14
- * their order). Everything else is listed with the reason it survives.
15
- *
16
- * The remote is a separate decision: `--remote` is required to touch
17
- * `origin/<branch>`, because a local branch is a local artifact while the remote
18
- * one may be what someone else, or an open review, is looking at.
19
- */
20
-
21
1
  import { execFileSync } from 'child_process';
22
2
  import chalk from 'chalk';
23
3
  import { isGitRepo, resolvePrimaryRoot } from './worktree.js';
@@ -25,15 +5,8 @@ import { detectDefaultBranch } from '../../lib/git/default-branch.js';
25
5
  import { buildPrIndex } from '../../lib/git/pr-index.js';
26
6
  import { classifyBranches } from '../../lib/git/branch-classifier.js';
27
7
 
28
- /**
29
- * Delete one local branch.
30
- *
31
- * `-D`, not `-d`, and that is the whole point of the classifier: `-d` refuses
32
- * every squash-merged branch, so a command that used it would be unable to do
33
- * its one job. The safety does not come from git's own check (which cannot see
34
- * a squash-merge) — it comes from only ever reaching this function with a
35
- * branch whose integration was positively proven.
36
- */
8
+ // `-D`, not `-d`: `-d` refuses every squash-merged branch. Safety comes from only
9
+ // reaching here with integration positively proven.
37
10
  function deleteLocal(name, cwd) {
38
11
  try {
39
12
  execFileSync('git', ['branch', '-D', name], { cwd, stdio: 'pipe' });
@@ -52,7 +25,6 @@ function deleteRemote(name, cwd) {
52
25
  }
53
26
  }
54
27
 
55
- /** Does `origin/<name>` exist locally as a remote-tracking ref? */
56
28
  function remoteRefExists(name, cwd) {
57
29
  try {
58
30
  execFileSync('git', ['show-ref', '--verify', '--quiet', `refs/remotes/origin/${name}`], { cwd, stdio: 'pipe' });
@@ -62,15 +34,6 @@ function remoteRefExists(name, cwd) {
62
34
  }
63
35
  }
64
36
 
65
- /**
66
- * Command handler for `morph-spec branch prune [--remote] [--dry-run] [--json]`.
67
- *
68
- * Fail-open outside a git repo (exit 0, empty result) — the same discipline the
69
- * rest of the worktree suite follows: a housekeeping command must never be the
70
- * reason a script dies.
71
- *
72
- * @param {{ remote?: boolean, dryRun?: boolean, json?: boolean, cwd?: string }} [options]
73
- */
74
37
  export function branchPruneCommand(options = {}) {
75
38
  const cwd = options.cwd || process.cwd();
76
39
  const emit = (payload, code = 0) => {
@@ -85,9 +48,7 @@ export function branchPruneCommand(options = {}) {
85
48
  }
86
49
  const root = resolvePrimaryRoot(cwd);
87
50
  const defaultBranch = detectDefaultBranch(root);
88
- // null = UNKNOWN, never "no PR" — the classifier treats a branch with no
89
- // positive evidence as unmerged, so a missing `gh` costs conservatism, not
90
- // safety. Reported so the user can tell "no PR" from "could not ask".
51
+ // null = UNKNOWN, never "no PR": a missing `gh` costs conservatism, not safety.
91
52
  const prIndex = buildPrIndex(root);
92
53
 
93
54
  const classified = classifyBranches({ cwd: root, defaultBranch, prIndex });
@@ -139,9 +100,7 @@ function printReport(payload, options) {
139
100
  const verb = payload.dryRun ? 'seriam apagadas' : 'apagadas';
140
101
  console.log(chalk.bold(`\nbranch prune — default: ${payload.defaultBranch || chalk.red('não resolvida')}`));
141
102
  if (!payload.prIndexAvailable) {
142
- // Load-bearing caveat, not noise: without `gh` a squash-merge is only
143
- // detectable by the tree comparison, so branches that WERE merged may
144
- // legitimately show up as unmerged here.
103
+ // Without `gh`, squash-merged branches may legitimately show as unmerged.
145
104
  console.log(chalk.gray(' (sem `gh`/GitHub: squash-merge só é detectado por árvore idêntica)'));
146
105
  }
147
106