@polymorphism-tech/morph-spec 9.0.0 → 9.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (304) hide show
  1. package/CLAUDE.md +8 -9
  2. package/README.md +38 -36
  3. package/bin/morph-spec.js +4 -62
  4. package/bin/validate.js +1 -39
  5. package/claude-plugin.json +1 -1
  6. package/docs/CHEATSHEET.md +1 -1
  7. package/docs/QUICKSTART.md +1 -1
  8. package/framework/CLAUDE.md +1 -1
  9. package/framework/MORPH.md +1 -1
  10. package/framework/agents.json +12 -4
  11. package/framework/commands/morph-apply.md +1 -1
  12. package/framework/commands/morph-status.md +1 -1
  13. package/framework/docs/CLI.md +6 -17
  14. package/framework/hooks/claude-code/core/block-destructive-worktree-removal.js +6 -49
  15. package/framework/hooks/claude-code/core/block-raw-worktree-add.js +2 -35
  16. package/framework/hooks/claude-code/core/chain.js +4 -65
  17. package/framework/hooks/claude-code/core/comment-guard.js +2 -16
  18. package/framework/hooks/claude-code/core/enforce-phase-writes.js +6 -50
  19. package/framework/hooks/claude-code/core/gate-guard.js +7 -67
  20. package/framework/hooks/claude-code/core/graph-refresh-kick.js +3 -24
  21. package/framework/hooks/claude-code/core/heavy-gate.js +3 -21
  22. package/framework/hooks/claude-code/core/heavy-release.js +2 -13
  23. package/framework/hooks/claude-code/core/loop-detect.js +2 -31
  24. package/framework/hooks/claude-code/core/pre-bash-denylist.js +0 -4
  25. package/framework/hooks/claude-code/core/protect-spec-files.js +7 -67
  26. package/framework/hooks/claude-code/core/state-sync.js +11 -67
  27. package/framework/hooks/claude-code/core/telemetry-log.js +12 -93
  28. package/framework/hooks/claude-code/core/validate-completion.js +11 -57
  29. package/framework/hooks/claude-code/core/warn-bash-source-write.js +1 -9
  30. package/framework/hooks/claude-code/core/worktree-context.js +10 -110
  31. package/framework/hooks/claude-code/core/worktree-create.js +6 -70
  32. package/framework/hooks/claude-code/core/worktree-env-inject.js +5 -56
  33. package/framework/hooks/claude-code/core/worktree-remove.js +5 -31
  34. package/framework/hooks/shared/activity-logger.js +9 -89
  35. package/framework/hooks/shared/agent-reap.js +2 -17
  36. package/framework/hooks/shared/build-check.js +6 -75
  37. package/framework/hooks/shared/claim-registry.js +9 -119
  38. package/framework/hooks/shared/command-tokens.js +6 -51
  39. package/framework/hooks/shared/comment-rules.js +5 -20
  40. package/framework/hooks/shared/comment-scanner.js +6 -25
  41. package/framework/hooks/shared/comment-stripper.js +11 -85
  42. package/framework/hooks/shared/e2e-check.js +0 -0
  43. package/framework/hooks/shared/errata.js +2 -15
  44. package/framework/hooks/shared/feature-locator.js +11 -95
  45. package/framework/hooks/shared/frontend-locations.js +1 -3
  46. package/framework/hooks/shared/gate-question.js +1 -7
  47. package/framework/hooks/shared/git-exclude.js +3 -20
  48. package/framework/hooks/shared/git-paths.js +8 -104
  49. package/framework/hooks/shared/graph-freshness.js +22 -166
  50. package/framework/hooks/shared/graph-refresh.js +21 -149
  51. package/framework/hooks/shared/heavy-command.js +4 -12
  52. package/framework/hooks/shared/hook-response.js +8 -83
  53. package/framework/hooks/shared/morph-cli.js +8 -52
  54. package/framework/hooks/shared/payload-utils.js +10 -132
  55. package/framework/hooks/shared/phase-utils.js +1 -42
  56. package/framework/hooks/shared/redact-secrets.js +4 -15
  57. package/framework/hooks/shared/reparse-scan.js +6 -66
  58. package/framework/hooks/shared/slice-progress.js +4 -23
  59. package/framework/hooks/shared/source-files.js +4 -17
  60. package/framework/hooks/shared/state-reader.js +13 -183
  61. package/framework/hooks/shared/state-writer.js +8 -52
  62. package/framework/hooks/shared/stdin-reader.js +1 -11
  63. package/framework/hooks/shared/telemetry-logger.js +7 -67
  64. package/framework/hooks/shared/transcript-usage.js +7 -29
  65. package/framework/hooks/shared/worktree-root.js +7 -29
  66. package/framework/rules/csharp-standards.md +1 -1
  67. package/framework/rules/frontend-standards.md +1 -0
  68. package/framework/rules/hangfire-standards.md +1 -1
  69. package/framework/rules/neon-standards.md +1 -1
  70. package/framework/skills/core/create-umbraco-lp/SKILL.md +1 -1
  71. package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +1 -1
  72. package/framework/skills/core/morph-apply/SKILL.md +2 -2
  73. package/framework/skills/core/morph-hotfix/SKILL.md +1 -1
  74. package/framework/skills/core/morph-machine/SKILL.md +1 -1
  75. package/framework/skills/core/morph-media/SKILL.md +1 -1
  76. package/framework/skills/core/morph-plan/SKILL.md +2 -2
  77. package/framework/skills/core/morph-review/SKILL.md +1 -1
  78. package/framework/skills/core/morph-standards/SKILL.md +1 -1
  79. package/framework/skills/core/morph-uiux/SKILL.md +29 -12
  80. package/framework/standards/STANDARDS.json +34 -12
  81. package/framework/standards/STANDARDS.meta.json +16 -2
  82. package/framework/standards/ai-agents/agent-archetypes.md +2 -2
  83. package/framework/standards/ai-agents/agent-session.md +1 -1
  84. package/framework/standards/ai-agents/conversational-agent-with-phases.md +4 -4
  85. package/framework/standards/ai-agents/durable-workflows-hitl.md +3 -3
  86. package/framework/standards/ai-agents/guardrails.md +1 -1
  87. package/framework/standards/ai-agents/multi-agent-patterns.md +2 -2
  88. package/framework/standards/ai-agents/observability-patterns.md +1 -1
  89. package/framework/standards/ai-agents/production.md +1 -1
  90. package/framework/standards/ai-agents/prompt-sources.md +2 -2
  91. package/framework/standards/ai-agents/providers/model-registry.md +1 -1
  92. package/framework/standards/ai-agents/rag-custom-pgvector.md +2 -2
  93. package/framework/standards/ai-agents/setup.md +2 -2
  94. package/framework/standards/ai-agents/sweet-spot.md +2 -2
  95. package/framework/standards/ai-agents/tool-adapters.md +0 -2
  96. package/framework/standards/ai-agents/vector-data-extensions.md +4 -4
  97. package/framework/standards/ai-agents/workflows.md +1 -1
  98. package/framework/standards/architecture/code-comments.md +1 -1
  99. package/framework/standards/architecture/vertical-slice/vertical-slice.md +1 -1
  100. package/framework/standards/backend/api/validation.md +1 -1
  101. package/framework/standards/backend/dotnet/vsa-handler-patterns.md +5 -5
  102. package/framework/standards/backend/integrations/hangfire/hangfire-jobs.md +1 -1
  103. package/framework/standards/backend/integrations/neon-auth/neon-auth.md +2 -2
  104. package/framework/standards/data/nosql/cache/redis.md +1 -1
  105. package/framework/standards/data/nosql/cosmos-db.md +3 -3
  106. package/framework/standards/frontend/admin-app/forms.md +1 -1
  107. package/framework/standards/frontend/design-system/art-direction.md +88 -0
  108. package/framework/standards/frontend/design-system/premium-finish.md +77 -2
  109. package/framework/standards/infrastructure/neon/neon-pgvector.md +5 -5
  110. package/framework/templates/CLAUDE.md.core.template +3 -3
  111. package/framework/templates/REGISTRY.json +9 -9
  112. package/framework/templates/docs/ui-mockups.md +4 -0
  113. package/package.json +1 -1
  114. package/src/commands/context/context-refresh.js +0 -16
  115. package/src/commands/dashboard/dashboard.js +2 -8
  116. package/src/commands/e2e/e2e.js +17 -104
  117. package/src/commands/graph/affected.js +0 -14
  118. package/src/commands/graph/build.js +11 -50
  119. package/src/commands/graph/explain.js +0 -16
  120. package/src/commands/graph/status.js +1 -12
  121. package/src/commands/mcp/mcp-setup.js +4 -62
  122. package/src/commands/observability/cost.js +5 -57
  123. package/src/commands/observability/telemetry.js +1 -14
  124. package/src/commands/project/branch.js +4 -45
  125. package/src/commands/project/claim.js +4 -48
  126. package/src/commands/project/doctor.js +48 -302
  127. package/src/commands/project/env.js +1 -27
  128. package/src/commands/project/fleet.js +3 -32
  129. package/src/commands/project/init.js +4 -68
  130. package/src/commands/project/install-plugin-cmd.js +0 -1
  131. package/src/commands/project/standards.js +1 -62
  132. package/src/commands/project/status.js +0 -53
  133. package/src/commands/project/update.js +31 -239
  134. package/src/commands/project/worktree.js +108 -882
  135. package/src/commands/scaffold/scaffold.js +0 -25
  136. package/src/commands/state/advance.js +14 -85
  137. package/src/commands/state/approve.js +14 -96
  138. package/src/commands/state/archive.js +12 -64
  139. package/src/commands/state/auto-approval.js +2 -5
  140. package/src/commands/state/create.js +22 -109
  141. package/src/commands/state/delete.js +11 -63
  142. package/src/commands/state/finish.js +98 -475
  143. package/src/commands/state/gate-check.js +22 -132
  144. package/src/commands/state/gate-decision.js +3 -39
  145. package/src/commands/state/handoff.js +3 -30
  146. package/src/commands/state/list.js +14 -84
  147. package/src/commands/state/report-check.js +8 -58
  148. package/src/commands/state/retype.js +4 -26
  149. package/src/commands/validation/index.js +0 -3
  150. package/src/commands/validation/mutate.js +2 -5
  151. package/src/commands/validation/validate-feature.js +5 -94
  152. package/src/commands/validation/verify.js +15 -98
  153. package/src/core/paths/output-schema.js +2 -101
  154. package/src/core/state/feature-identity.js +1 -25
  155. package/src/core/state/gate-order.js +0 -27
  156. package/src/core/state/lifecycle-audit.js +8 -100
  157. package/src/core/state/state-manager.js +22 -304
  158. package/src/core/templates/template-registry.js +2 -97
  159. package/src/core/templates/template-renderer.js +13 -93
  160. package/src/core/templates/template-seed.js +0 -1
  161. package/src/core/work-types.js +1 -36
  162. package/src/dashboard/app/App.jsx +1 -8
  163. package/src/dashboard/app/components/ActivityFeed.jsx +0 -5
  164. package/src/dashboard/app/components/CommandPalette.jsx +0 -7
  165. package/src/dashboard/app/components/DetailPanel.jsx +1 -5
  166. package/src/dashboard/app/components/EntityLink.jsx +0 -12
  167. package/src/dashboard/app/components/ErrorBoundary.jsx +1 -8
  168. package/src/dashboard/app/components/KnowledgeMarkdown.jsx +0 -11
  169. package/src/dashboard/app/components/Markdown.jsx +0 -9
  170. package/src/dashboard/app/components/PhaseRail.jsx +0 -11
  171. package/src/dashboard/app/components/SearchFilter.jsx +0 -4
  172. package/src/dashboard/app/components/Sidebar.jsx +0 -5
  173. package/src/dashboard/app/hooks/useMorphQuery.js +1 -11
  174. package/src/dashboard/app/hooks/usePref.js +1 -5
  175. package/src/dashboard/app/lib/format.js +0 -2
  176. package/src/dashboard/app/lib/phases.js +1 -5
  177. package/src/dashboard/app/lib/project.js +0 -12
  178. package/src/dashboard/app/pages/AgentMap.jsx +1 -8
  179. package/src/dashboard/app/pages/FeatureDetail.jsx +1 -5
  180. package/src/dashboard/app/pages/Features.jsx +1 -8
  181. package/src/dashboard/app/pages/Guide.jsx +0 -1
  182. package/src/dashboard/app/pages/HookInspector.jsx +2 -23
  183. package/src/dashboard/app/pages/PhasePipeline.jsx +0 -3
  184. package/src/dashboard/app/pages/Portfolio.jsx +0 -1
  185. package/src/dashboard/app/pages/RulesMap.jsx +1 -16
  186. package/src/dashboard/app/pages/SkillsLibrary.jsx +1 -16
  187. package/src/dashboard/app/pages/Standards.jsx +2 -17
  188. package/src/dashboard/scanner/activity-scanner.js +10 -69
  189. package/src/dashboard/scanner/agents-scanner.js +1 -23
  190. package/src/dashboard/scanner/docs-scanner.js +0 -6
  191. package/src/dashboard/scanner/feature-status.js +2 -5
  192. package/src/dashboard/scanner/features-scanner.js +10 -66
  193. package/src/dashboard/scanner/hooks-scanner.js +5 -28
  194. package/src/dashboard/scanner/index.js +0 -4
  195. package/src/dashboard/scanner/portfolio.js +5 -29
  196. package/src/dashboard/scanner/project-api.js +8 -54
  197. package/src/dashboard/scanner/rules-scanner.js +1 -2
  198. package/src/dashboard/scanner/skills-scanner.js +2 -12
  199. package/src/dashboard/scanner/standards-scanner.js +2 -6
  200. package/src/dashboard/vite-plugin-morph-data.js +5 -19
  201. package/src/lib/ai/ai-checks.js +7 -52
  202. package/src/lib/ai/ai-project-scan.js +14 -108
  203. package/src/lib/ai/code-audit.js +7 -63
  204. package/src/lib/ai/pin-drift.js +8 -51
  205. package/src/lib/ai/registry-audit.js +11 -90
  206. package/src/lib/ci/ci-verify.js +4 -19
  207. package/src/lib/ci/github-ci.js +9 -37
  208. package/src/lib/ci/workflow-scaffold.js +2 -10
  209. package/src/lib/detectors/claude-config-detector.js +13 -74
  210. package/src/lib/detectors/index.js +4 -50
  211. package/src/lib/e2e/compose-env.js +5 -66
  212. package/src/lib/e2e/stack-teardown.js +6 -40
  213. package/src/lib/generators/architecture-scanner.js +18 -156
  214. package/src/lib/generators/handoff-generator.js +9 -53
  215. package/src/lib/generators/scaffold-generator.js +5 -101
  216. package/src/lib/generators/settings-generator.js +4 -37
  217. package/src/lib/git/branch-classifier.js +11 -84
  218. package/src/lib/git/default-branch.js +14 -169
  219. package/src/lib/git/evidence-staleness.js +1 -0
  220. package/src/lib/git/gh.js +2 -17
  221. package/src/lib/git/pending-prs.js +6 -37
  222. package/src/lib/git/pr-files.js +3 -28
  223. package/src/lib/git/pr-index.js +3 -34
  224. package/src/lib/git/tree-fingerprint.js +3 -15
  225. package/src/lib/git/tree-state.js +6 -33
  226. package/src/lib/graph/god-nodes.js +2 -22
  227. package/src/lib/graph/graph-health.js +3 -24
  228. package/src/lib/graph/graph-loader.js +5 -56
  229. package/src/lib/graph/graphify-runtime.js +7 -37
  230. package/src/lib/graph/graphifyignore.js +8 -74
  231. package/src/lib/graph/sanitize.js +16 -136
  232. package/src/lib/heavy/cleanup.js +1 -1
  233. package/src/lib/installers/effects.js +8 -93
  234. package/src/lib/installers/mcp-installer.js +9 -169
  235. package/src/lib/installers/memory-installer.js +4 -27
  236. package/src/lib/installers/templates-reconcile.js +5 -60
  237. package/src/lib/mutation/dotnet-introspection.js +20 -106
  238. package/src/lib/mutation/subset.js +1 -4
  239. package/src/lib/plan/artifact-vocabulary.js +5 -25
  240. package/src/lib/plan/checks.js +5 -31
  241. package/src/lib/plan/plan-parser.js +1 -6
  242. package/src/lib/projects/registry.js +7 -44
  243. package/src/lib/review-reports.js +5 -23
  244. package/src/lib/risk-detector.js +39 -173
  245. package/src/lib/specs/adr-index.js +6 -34
  246. package/src/lib/specs/frontmatter.js +2 -15
  247. package/src/lib/specs/gfm-table.js +1 -7
  248. package/src/lib/specs/revocations.js +1 -14
  249. package/src/lib/specs/screen-checklist.js +47 -124
  250. package/src/lib/stack/dotnet-build-ignores.js +2 -6
  251. package/src/lib/stack/stack-profile.js +1 -28
  252. package/src/lib/stack/stack-scan.js +1 -8
  253. package/src/lib/stack-filter.js +4 -27
  254. package/src/lib/standards/registry.js +1 -7
  255. package/src/lib/standards/sections.js +2 -13
  256. package/src/lib/standards/verified-against.js +19 -191
  257. package/src/lib/tasks/eval-node.js +21 -169
  258. package/src/lib/tasks/linux-runner.js +18 -142
  259. package/src/lib/tasks/test-nodes.js +43 -240
  260. package/src/lib/tasks/test-runner.js +43 -341
  261. package/src/lib/telemetry/metrics.js +7 -36
  262. package/src/lib/telemetry/telemetry-reader.js +13 -125
  263. package/src/lib/telemetry/verify-event.js +2 -30
  264. package/src/lib/validators/code-budget/code-budget-validator.js +3 -43
  265. package/src/lib/validators/code-budget/index.js +0 -7
  266. package/src/lib/validators/contracts/contract-compliance-validator.js +2 -24
  267. package/src/lib/validators/contracts/index.js +0 -6
  268. package/src/lib/validators/nextjs/index.js +0 -5
  269. package/src/lib/validators/nextjs/next-component-validator.js +4 -81
  270. package/src/lib/validators/packages/index.js +0 -6
  271. package/src/lib/validators/packages/package-validator.js +6 -56
  272. package/src/lib/validators/reuse/index.js +0 -8
  273. package/src/lib/validators/reuse/reuse-validator.js +11 -101
  274. package/src/lib/validators/reuse/symbol-scanner.js +6 -62
  275. package/src/lib/validators/traceability-check.js +5 -63
  276. package/src/lib/validators/validation-runner.js +20 -125
  277. package/src/lib/validators/verify-runner.js +52 -358
  278. package/src/lib/work-type-detector.js +5 -67
  279. package/src/lib/worktree/docker-audit.js +17 -136
  280. package/src/lib/worktree/env-allocator.js +10 -98
  281. package/src/lib/worktree/feature-truth.js +12 -115
  282. package/src/lib/worktree/finish-backup.js +32 -242
  283. package/src/lib/worktree/fleet-scanner.js +13 -95
  284. package/src/lib/worktree/ignored-triage.js +15 -91
  285. package/src/lib/worktree/vscode-settings.js +2 -3
  286. package/src/lib/worktree/worktree-audit.js +21 -213
  287. package/src/lib/worktree/worktree-include.js +3 -5
  288. package/src/scripts/global-install.js +3 -9
  289. package/src/scripts/install-plugin.js +1 -13
  290. package/src/scripts/setup-infra.js +17 -147
  291. package/src/utils/agents-installer.js +28 -275
  292. package/src/utils/banner.js +4 -25
  293. package/src/utils/claude-md-injector.js +1 -37
  294. package/src/utils/claude-settings-manager.js +1 -52
  295. package/src/utils/file-copier.js +11 -95
  296. package/src/utils/hooks-installer.js +21 -298
  297. package/src/utils/skills-installer.js +4 -101
  298. package/src/utils/version-checker.js +4 -83
  299. package/framework/commands/morph-proposal.md +0 -9
  300. package/src/commands/utils/index.js +0 -3
  301. package/src/lib/detectors/design-system-detector.js +0 -188
  302. package/src/lib/generators/design-system-generator.js +0 -508
  303. package/src/lib/specs/old-format.js +0 -68
  304. package/src/utils/agent-id-mapper.js +0 -11
@@ -1,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,32 +1,5 @@
1
- /**
2
- * `morph-spec cost [feature] [--by 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 many context bytes were re-ingested (grouped by kind: rubric/standard/skill/agents/plan-artifact/
6
- * code), and how many sub-agent dispatches happened (with total prompt
7
- * chars). When the stream has `SessionUsage` events (real, hook-observed
8
- * token counts), those are summed and shown too — see the sessionUsage
9
- * caveat below.
10
- *
11
- * IMPORTANT: bytes re-ingested is a DETERMINISTIC PROXY for cost, not a
12
- * token count. Hooks only see what tools read off disk — never the model's
13
- * actual token usage — so this measures re-ingestion pressure, not spend.
14
- * `sessionUsage` (when present) is the exception: real tokens, observed
15
- * directly, not a proxy — see aggregateCost's docstring for the contract.
16
- *
17
- * `--all-trees` widens the source from "just the current tree" to every git
18
- * worktree that has its own events.jsonl plus the logs `finish` saved before
19
- * removing a worktree, deduplicated by eventId.
20
- *
21
- * `--metrics` measures the loop instead: turns, tool calls by orchestrator and
22
- * sub-agent, time per command class, identical reruns and why, rereads, how
23
- * many verifies each feature ran, git calls and whether
24
- * the reuse validator ran in root and worktree (see lib/telemetry/metrics.js).
25
- *
26
- * Read-only, always exits 0 on a normal (even empty) run — fail-open, an
27
- * empty stream is an answer, not an error. Only `--by` with an unknown value
28
- * is a usage error (exit 1).
29
- */
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.
30
3
 
31
4
  import chalk from 'chalk';
32
5
  import { loadTelemetry, loadTelemetryAllTrees, aggregateCost, dedupeEvents } from '../../lib/telemetry/telemetry-reader.js';
@@ -79,10 +52,6 @@ function tokenLine(u) {
79
52
  + `cacheRead=${u.cacheReadTokens} cacheCreation=${u.cacheCreationTokens}`;
80
53
  }
81
54
 
82
- /**
83
- * Real (hook-observed) token counts — the non-proxy sibling of the bytes
84
- * above: the main sessions and, separately, the sub-agents.
85
- */
86
55
  function printSessionUsage(summary) {
87
56
  const { sessionUsage, agentUsage, sessionUsageLegacySamples } = summary;
88
57
  if (sessionUsage) {
@@ -178,10 +147,6 @@ function printMetricsBody(m) {
178
147
  for (const [reason, n] of Object.entries(m.reuse.skipReasons)) console.log(chalk.gray(` pulou ${n}×: ${reason}`));
179
148
  }
180
149
 
181
- /**
182
- * `--metrics`: the loop measurements over the current tree (and, with
183
- * `--all-trees`, every tree plus the finish backups, deduplicated).
184
- */
185
150
  function printMetrics(feature, options, cwd) {
186
151
  const events = options.allTrees
187
152
  ? dedupeEvents(loadTelemetryAllTrees(cwd).flatMap((t) => t.events))
@@ -202,16 +167,6 @@ function printMetrics(feature, options, cwd) {
202
167
  printMetricsBody(metrics);
203
168
  }
204
169
 
205
- /**
206
- * `--all-trees` rendering: a short line per tree (so a fleet operator can
207
- * spot which tree is expensive) followed by the COMBINED aggregate — full
208
- * detail (the phase breakdown honors `--by` here too, same as the
209
- * single-tree path; a typeable flag combination is never a silent no-op).
210
- *
211
- * @param {string} [feature]
212
- * @param {{ json?: boolean, by?: 'phase' }} options
213
- * @param {string} cwd
214
- */
215
170
  function printAllTrees(feature, options, cwd) {
216
171
  const treesData = loadTelemetryAllTrees(cwd);
217
172
  const perTree = treesData.map(({ tree, path, events }) => ({
@@ -254,10 +209,6 @@ function printAllTrees(feature, options, cwd) {
254
209
  }
255
210
  }
256
211
 
257
- /**
258
- * @param {string} [feature] - narrow to a single feature
259
- * @param {{ json?: boolean, by?: 'phase', allTrees?: boolean, cwd?: string }} [options]
260
- */
261
212
  export function costCommand(feature, options = {}) {
262
213
  if (options.by && !BY_MODES.includes(options.by)) {
263
214
  console.error(chalk.red(`\n❌ Invalid --by: "${options.by}"`));
@@ -274,8 +225,7 @@ export function costCommand(feature, options = {}) {
274
225
  }
275
226
 
276
227
  if (options.allTrees) {
277
- // Same fail-open + Windows-flush discipline as the single-tree path
278
- // below — no forced process.exit(), --json included.
228
+ // No process.exit() here either, --json included.
279
229
  printAllTrees(feature, options, cwd);
280
230
  return;
281
231
  }
@@ -284,10 +234,8 @@ export function costCommand(feature, options = {}) {
284
234
  const summary = aggregateCost(events, { feature });
285
235
 
286
236
  if (options.json) {
287
- // No forced process.exit() here: on Windows a piped stdout write is
288
- // async, and exit()ing right after a large console.log can race the
289
- // flush and truncate output. Letting the event loop drain naturally
290
- // (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.
291
239
  console.log(JSON.stringify(summary));
292
240
  return;
293
241
  }
@@ -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
 
@@ -1,24 +1,3 @@
1
- /**
2
- * `morph-spec claim [feature] [--release] [--list]` — the manual half of
3
- * advisory feature ownership (B1).
4
- *
5
- * Claims are acquired and heartbeaten AUTOMATICALLY (SessionStart hook +
6
- * telemetry hook), so the operator never has to remember anything. This command
7
- * exists for the two cases automation cannot cover:
8
- * - a session that died without releasing, leaving a claim that keeps
9
- * warning everyone until its TTL expires (`--release`);
10
- * - an operator switching this SAME session/tree to a different feature
11
- * mid-task. `morph-spec claim <feature>` (no flags) ACQUIRES it, moving
12
- * the tree's previous claim in the same step — a manual release-then-
13
- * reclaim was observed to just not happen mid-task, and the silent no-op
14
- * this used to be (any unclaimed feature name printed "nothing here" and
15
- * changed nothing) was worse: it looked like a query, not a skipped write.
16
- *
17
- * `--list` forces the read-only view even when a feature is named. `--release`
18
- * is the only other mutation. Always exits 0 except on a genuine misuse — the
19
- * claim system is advisory and must never become a source of hard failures.
20
- */
21
-
22
1
  import chalk from 'chalk';
23
2
  import { execSync } from 'child_process';
24
3
  import { resolve } from 'path';
@@ -31,7 +10,6 @@ import {
31
10
  claimAge,
32
11
  } from '../../../framework/hooks/shared/claim-registry.js';
33
12
 
34
- /** Resolve the repo's shared git common dir — where claims live. */
35
13
  function gitCommonDirOf(cwd) {
36
14
  const root = resolvePrimaryRoot(cwd);
37
15
  const common = execSync('git rev-parse --git-common-dir', { cwd: root, stdio: 'pipe' }).toString().trim();
@@ -41,15 +19,8 @@ function gitCommonDirOf(cwd) {
41
19
  const norm = (p) => String(p ?? '').replace(/\\/g, '/');
42
20
 
43
21
  /**
44
- * Acquire `feature`'s claim, moving it in the same step if this tree already
45
- * holds a live claim on a different feature.
46
- *
47
- * "This tree's own claim" is identified by PATH, not session id: the CLI has
48
- * no access to the Claude Code session id SessionStart stamps (that only
49
- * reaches hooks, via stdin JSON) — path is the one honest signal a manual
50
- * invocation shares with the automatic claim it may be replacing. Re-using
51
- * that claim's session id when re-acquiring keeps the move from reading as a
52
- * takeover of a stranger.
22
+ * Moves this tree's live claim, identified by PATH: the CLI never sees the session id
23
+ * SessionStart stamps. Reusing that claim's session id keeps the move from reading as a takeover.
53
24
  */
54
25
  function acquireOrMove(feature, { cwd, gitCommonDir, json }) {
55
26
  const here = norm(cwd);
@@ -84,10 +55,6 @@ function acquireOrMove(feature, { cwd, gitCommonDir, json }) {
84
55
  process.exit(0);
85
56
  }
86
57
 
87
- /**
88
- * @param {string|undefined} feature
89
- * @param {{ release?: boolean, list?: boolean, json?: boolean, cwd?: string }} [options]
90
- */
91
58
  export function claimCommand(feature, options = {}) {
92
59
  const cwd = options.cwd || process.cwd();
93
60
  if (!isGitRepo(cwd)) {
@@ -120,27 +87,16 @@ export function claimCommand(feature, options = {}) {
120
87
  process.exit(0);
121
88
  }
122
89
 
123
- // `morph-spec claim <feature>` (no --release, no --list) is the manual
124
- // acquire/move half of B1 — automation (SessionStart + heartbeat) covers the
125
- // common case; this covers switching this session to a different feature
126
- // mid-task without a release-then-reclaim dance. `--list` forces the
127
- // read-only view below even when a feature is named.
128
90
  if (feature && !options.list) {
129
91
  acquireOrMove(feature, { cwd, gitCommonDir, json: options.json });
130
92
  return;
131
93
  }
132
94
 
133
- // No feature (or explicit --list): the read-only board, whole or filtered.
134
95
  const all = listClaims(gitCommonDir);
135
96
  const scoped = feature ? all.filter((c) => c.feature === feature) : all;
136
97
 
137
- // Um claim morto NÃO é limpo do registro — ele só para de avisar (o TTL de
138
- // 30 min o mata, `live` é computado na leitura). O efeito colateral é que o
139
- // board acumula: features encerradas há 20h e 73h apareciam no `--list` como
140
- // se fossem informação. Um quadro de avisos onde quase nada é aviso ensina a
141
- // não olhar o quadro. Por padrão mostramos só o que ainda avisa; `--all`
142
- // devolve o registro inteiro, e o rodapé conta o que ficou de fora para a
143
- // omissão nunca ser silenciosa.
98
+ // Claim morto segue no registro (`live` é computado na leitura): por padrão só o que ainda
99
+ // avisa; `--all` devolve tudo e o rodapé conta o que ficou de fora.
144
100
  const rows = options.all ? scoped : scoped.filter((c) => c.live);
145
101
  const hidden = scoped.length - rows.length;
146
102