@polymorphism-tech/morph-spec 9.0.0 → 9.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (345) hide show
  1. package/CLAUDE.md +8 -9
  2. package/README.md +38 -36
  3. package/bin/morph-spec.js +11 -63
  4. package/bin/validate.js +1 -39
  5. package/claude-plugin.json +1 -1
  6. package/docs/CHEATSHEET.md +1 -1
  7. package/docs/QUICKSTART.md +1 -1
  8. package/framework/CLAUDE.md +2 -1
  9. package/framework/MORPH.md +3 -3
  10. package/framework/agents.json +19 -11
  11. package/framework/commands/morph-apply.md +1 -1
  12. package/framework/commands/morph-status.md +1 -1
  13. package/framework/docs/CLI.md +22 -31
  14. package/framework/evals/review-checklist.md +12 -2
  15. package/framework/hooks/README.md +8 -44
  16. package/framework/hooks/claude-code/core/block-destructive-worktree-removal.js +6 -49
  17. package/framework/hooks/claude-code/core/block-raw-worktree-add.js +2 -35
  18. package/framework/hooks/claude-code/core/chain.js +4 -65
  19. package/framework/hooks/claude-code/core/comment-guard.js +2 -16
  20. package/framework/hooks/claude-code/core/enforce-phase-writes.js +6 -50
  21. package/framework/hooks/claude-code/core/gate-guard.js +7 -67
  22. package/framework/hooks/claude-code/core/graph-refresh-kick.js +3 -24
  23. package/framework/hooks/claude-code/core/heavy-gate.js +3 -21
  24. package/framework/hooks/claude-code/core/heavy-release.js +2 -13
  25. package/framework/hooks/claude-code/core/loop-detect.js +2 -31
  26. package/framework/hooks/claude-code/core/pre-bash-denylist.js +0 -4
  27. package/framework/hooks/claude-code/core/protect-spec-files.js +7 -67
  28. package/framework/hooks/claude-code/core/state-sync.js +12 -68
  29. package/framework/hooks/claude-code/core/telemetry-log.js +12 -93
  30. package/framework/hooks/claude-code/core/validate-completion.js +11 -57
  31. package/framework/hooks/claude-code/core/warn-bash-source-write.js +1 -9
  32. package/framework/hooks/claude-code/core/worktree-context.js +10 -110
  33. package/framework/hooks/claude-code/core/worktree-create.js +6 -70
  34. package/framework/hooks/claude-code/core/worktree-env-inject.js +5 -56
  35. package/framework/hooks/claude-code/core/worktree-remove.js +5 -31
  36. package/framework/hooks/claude-code/statusline.sh +11 -11
  37. package/framework/hooks/shared/activity-logger.js +9 -89
  38. package/framework/hooks/shared/agent-reap.js +2 -17
  39. package/framework/hooks/shared/build-check.js +6 -75
  40. package/framework/hooks/shared/claim-registry.js +9 -119
  41. package/framework/hooks/shared/command-tokens.js +6 -51
  42. package/framework/hooks/shared/comment-rules.js +5 -20
  43. package/framework/hooks/shared/comment-scanner.js +6 -25
  44. package/framework/hooks/shared/comment-stripper.js +11 -85
  45. package/framework/hooks/shared/e2e-check.js +0 -0
  46. package/framework/hooks/shared/errata.js +2 -15
  47. package/framework/hooks/shared/feature-locator.js +11 -95
  48. package/framework/hooks/shared/frontend-locations.js +1 -3
  49. package/framework/hooks/shared/gate-question.js +1 -7
  50. package/framework/hooks/shared/git-exclude.js +3 -20
  51. package/framework/hooks/shared/git-paths.js +8 -104
  52. package/framework/hooks/shared/graph-freshness.js +22 -166
  53. package/framework/hooks/shared/graph-refresh.js +21 -149
  54. package/framework/hooks/shared/heavy-command.js +4 -12
  55. package/framework/hooks/shared/hook-response.js +8 -83
  56. package/framework/hooks/shared/morph-cli.js +8 -52
  57. package/framework/hooks/shared/payload-utils.js +10 -132
  58. package/framework/hooks/shared/phase-utils.js +1 -42
  59. package/framework/hooks/shared/redact-secrets.js +4 -15
  60. package/framework/hooks/shared/reparse-scan.js +6 -66
  61. package/framework/hooks/shared/slice-progress.js +4 -23
  62. package/framework/hooks/shared/source-files.js +4 -17
  63. package/framework/hooks/shared/state-reader.js +13 -183
  64. package/framework/hooks/shared/state-writer.js +8 -52
  65. package/framework/hooks/shared/stdin-reader.js +1 -11
  66. package/framework/hooks/shared/telemetry-logger.js +7 -67
  67. package/framework/hooks/shared/transcript-usage.js +7 -29
  68. package/framework/hooks/shared/worktree-root.js +7 -29
  69. package/framework/rules/csharp-standards.md +1 -1
  70. package/framework/rules/frontend-standards.md +1 -0
  71. package/framework/rules/hangfire-standards.md +1 -1
  72. package/framework/rules/neon-standards.md +1 -1
  73. package/framework/rules/nextjs-standards.md +1 -1
  74. package/framework/skills/core/create-umbraco-lp/SKILL.md +1 -1
  75. package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +1 -1
  76. package/framework/skills/core/morph-apply/SKILL.md +4 -4
  77. package/framework/skills/core/morph-hotfix/SKILL.md +3 -3
  78. package/framework/skills/core/morph-machine/SKILL.md +1 -1
  79. package/framework/skills/core/morph-media/SKILL.md +1 -1
  80. package/framework/skills/core/morph-plan/SKILL.md +9 -7
  81. package/framework/skills/core/morph-review/SKILL.md +9 -6
  82. package/framework/skills/core/morph-standards/SKILL.md +1 -1
  83. package/framework/skills/core/morph-uiux/SKILL.md +29 -12
  84. package/framework/standards/STANDARDS.json +36 -14
  85. package/framework/standards/STANDARDS.meta.json +16 -2
  86. package/framework/standards/ai-agents/agent-archetypes.md +2 -2
  87. package/framework/standards/ai-agents/agent-session.md +1 -1
  88. package/framework/standards/ai-agents/conversational-agent-with-phases.md +4 -4
  89. package/framework/standards/ai-agents/durable-workflows-hitl.md +3 -3
  90. package/framework/standards/ai-agents/guardrails.md +1 -1
  91. package/framework/standards/ai-agents/multi-agent-patterns.md +2 -2
  92. package/framework/standards/ai-agents/observability-patterns.md +1 -1
  93. package/framework/standards/ai-agents/production.md +1 -1
  94. package/framework/standards/ai-agents/prompt-sources.md +2 -2
  95. package/framework/standards/ai-agents/providers/model-registry.md +1 -1
  96. package/framework/standards/ai-agents/rag-custom-pgvector.md +2 -2
  97. package/framework/standards/ai-agents/setup.md +2 -2
  98. package/framework/standards/ai-agents/sweet-spot.md +2 -2
  99. package/framework/standards/ai-agents/tool-adapters.md +0 -2
  100. package/framework/standards/ai-agents/vector-data-extensions.md +4 -4
  101. package/framework/standards/ai-agents/workflows.md +1 -1
  102. package/framework/standards/architecture/code-comments.md +1 -1
  103. package/framework/standards/architecture/vertical-slice/vertical-slice.md +1 -1
  104. package/framework/standards/backend/api/validation.md +1 -1
  105. package/framework/standards/backend/dotnet/testing.md +10 -4
  106. package/framework/standards/backend/dotnet/vsa-handler-patterns.md +5 -5
  107. package/framework/standards/backend/integrations/hangfire/hangfire-jobs.md +1 -1
  108. package/framework/standards/backend/integrations/neon-auth/neon-auth.md +2 -2
  109. package/framework/standards/data/nosql/cache/redis.md +1 -1
  110. package/framework/standards/data/nosql/cosmos-db.md +3 -3
  111. package/framework/standards/frontend/admin-app/forms.md +1 -1
  112. package/framework/standards/frontend/design-system/art-direction.md +88 -0
  113. package/framework/standards/frontend/design-system/premium-finish.md +77 -2
  114. package/framework/standards/frontend/nextjs/testing.md +10 -6
  115. package/framework/standards/infrastructure/neon/neon-pgvector.md +5 -5
  116. package/framework/templates/.idea/morph-templates.xml +92 -0
  117. package/framework/templates/.vscode/morph-templates.code-snippets +186 -0
  118. package/framework/templates/CLAUDE.md.core.template +3 -3
  119. package/framework/templates/IDE-SNIPPETS.md +262 -262
  120. package/framework/templates/REGISTRY.json +9 -9
  121. package/framework/templates/code/dotnet/abstractions/api-endpoint.cs +7 -7
  122. package/framework/templates/code/dotnet/feature/request.cs.hbs +4 -4
  123. package/framework/templates/code/dotnet/feature/response.cs.hbs +4 -4
  124. package/framework/templates/code/sql/ai-agents/pgvector-embeddings-migration.sql.template +91 -91
  125. package/framework/templates/docs/plan.md +3 -1
  126. package/framework/templates/docs/ui-mockups.md +4 -0
  127. package/framework/templates/frontend/nextjs/client-component.tsx.hbs +26 -26
  128. package/framework/templates/frontend/nextjs/feature-form.tsx.hbs +56 -56
  129. package/framework/templates/frontend/nextjs/tsconfig.json.hbs +26 -26
  130. package/framework/templates/infrastructure/docker/Dockerfile.template +58 -58
  131. package/framework/templates/infrastructure/docker/dockerfile-api.dockerfile +38 -38
  132. package/framework/templates/infrastructure/docker/dockerfile-web.dockerfile +48 -48
  133. package/framework/templates/infrastructure/github/actions/docker-build-push/action.yml.hbs +45 -45
  134. package/framework/templates/infrastructure/github/actions/health-check/action.yml.hbs +27 -27
  135. package/framework/templates/infrastructure/github/workflows/docker-build-push.yml.hbs +59 -59
  136. package/framework/templates/infrastructure/github/workflows/dotnet-build.yml.hbs +39 -39
  137. package/framework/templates/meta-prompts/fusion/fusion-agent.md +76 -76
  138. package/framework/templates/meta-prompts/fusion/fusion-aggregator.md +100 -100
  139. package/framework/templates/state.template.json +17 -17
  140. package/framework/templates/ui/design-system.css +226 -226
  141. package/package.json +1 -1
  142. package/src/commands/context/context-refresh.js +0 -16
  143. package/src/commands/dashboard/dashboard.js +2 -8
  144. package/src/commands/e2e/e2e.js +17 -104
  145. package/src/commands/graph/affected.js +0 -14
  146. package/src/commands/graph/build.js +11 -50
  147. package/src/commands/graph/explain.js +0 -16
  148. package/src/commands/graph/status.js +1 -12
  149. package/src/commands/mcp/mcp-setup.js +4 -62
  150. package/src/commands/observability/cost.js +5 -57
  151. package/src/commands/observability/telemetry.js +1 -14
  152. package/src/commands/project/branch.js +4 -45
  153. package/src/commands/project/claim.js +4 -48
  154. package/src/commands/project/doctor.js +48 -302
  155. package/src/commands/project/env.js +1 -27
  156. package/src/commands/project/fleet.js +3 -32
  157. package/src/commands/project/init.js +4 -68
  158. package/src/commands/project/install-plugin-cmd.js +0 -1
  159. package/src/commands/project/standards.js +1 -62
  160. package/src/commands/project/status.js +0 -53
  161. package/src/commands/project/update.js +31 -239
  162. package/src/commands/project/worktree.js +108 -882
  163. package/src/commands/scaffold/scaffold.js +0 -25
  164. package/src/commands/state/advance.js +14 -85
  165. package/src/commands/state/approve.js +15 -97
  166. package/src/commands/state/archive.js +12 -64
  167. package/src/commands/state/auto-approval.js +2 -5
  168. package/src/commands/state/create.js +22 -109
  169. package/src/commands/state/delete.js +11 -63
  170. package/src/commands/state/finish.js +98 -475
  171. package/src/commands/state/gate-check.js +22 -132
  172. package/src/commands/state/gate-decision.js +3 -39
  173. package/src/commands/state/handoff.js +3 -30
  174. package/src/commands/state/list.js +14 -84
  175. package/src/commands/state/report-check.js +8 -58
  176. package/src/commands/state/retype.js +4 -26
  177. package/src/commands/validation/index.js +0 -3
  178. package/src/commands/validation/mutate.js +11 -11
  179. package/src/commands/validation/plan-check.js +4 -2
  180. package/src/commands/validation/tests-audit.js +7 -0
  181. package/src/commands/validation/validate-feature.js +10 -97
  182. package/src/commands/validation/verify.js +30 -113
  183. package/src/core/paths/output-schema.js +2 -101
  184. package/src/core/state/feature-identity.js +1 -25
  185. package/src/core/state/gate-order.js +0 -27
  186. package/src/core/state/lifecycle-audit.js +8 -100
  187. package/src/core/state/state-manager.js +22 -304
  188. package/src/core/templates/template-registry.js +2 -97
  189. package/src/core/templates/template-renderer.js +13 -93
  190. package/src/core/templates/template-seed.js +0 -1
  191. package/src/core/work-types.js +1 -36
  192. package/src/dashboard/app/App.jsx +1 -8
  193. package/src/dashboard/app/components/ActivityFeed.jsx +0 -5
  194. package/src/dashboard/app/components/CommandPalette.jsx +0 -7
  195. package/src/dashboard/app/components/DetailPanel.jsx +1 -5
  196. package/src/dashboard/app/components/EntityLink.jsx +0 -12
  197. package/src/dashboard/app/components/ErrorBoundary.jsx +1 -8
  198. package/src/dashboard/app/components/KnowledgeMarkdown.jsx +0 -11
  199. package/src/dashboard/app/components/Markdown.jsx +0 -9
  200. package/src/dashboard/app/components/PhaseRail.jsx +0 -11
  201. package/src/dashboard/app/components/SearchFilter.jsx +0 -4
  202. package/src/dashboard/app/components/Sidebar.jsx +0 -5
  203. package/src/dashboard/app/hooks/useMorphQuery.js +1 -11
  204. package/src/dashboard/app/hooks/usePref.js +1 -5
  205. package/src/dashboard/app/lib/format.js +0 -2
  206. package/src/dashboard/app/lib/phases.js +1 -5
  207. package/src/dashboard/app/lib/project.js +0 -12
  208. package/src/dashboard/app/main.jsx +13 -13
  209. package/src/dashboard/app/pages/AgentMap.jsx +1 -8
  210. package/src/dashboard/app/pages/FeatureDetail.jsx +1 -5
  211. package/src/dashboard/app/pages/Features.jsx +1 -8
  212. package/src/dashboard/app/pages/Guide.jsx +0 -1
  213. package/src/dashboard/app/pages/HookInspector.jsx +2 -23
  214. package/src/dashboard/app/pages/PhasePipeline.jsx +0 -3
  215. package/src/dashboard/app/pages/Portfolio.jsx +0 -1
  216. package/src/dashboard/app/pages/RulesMap.jsx +1 -16
  217. package/src/dashboard/app/pages/SkillsLibrary.jsx +1 -16
  218. package/src/dashboard/app/pages/Standards.jsx +2 -17
  219. package/src/dashboard/scanner/activity-scanner.js +10 -69
  220. package/src/dashboard/scanner/agents-scanner.js +1 -23
  221. package/src/dashboard/scanner/docs-scanner.js +0 -6
  222. package/src/dashboard/scanner/feature-status.js +2 -5
  223. package/src/dashboard/scanner/features-scanner.js +10 -66
  224. package/src/dashboard/scanner/hooks-scanner.js +5 -28
  225. package/src/dashboard/scanner/index.js +0 -4
  226. package/src/dashboard/scanner/portfolio.js +5 -29
  227. package/src/dashboard/scanner/project-api.js +8 -54
  228. package/src/dashboard/scanner/rules-scanner.js +1 -2
  229. package/src/dashboard/scanner/skills-scanner.js +2 -12
  230. package/src/dashboard/scanner/standards-scanner.js +2 -6
  231. package/src/dashboard/vite-plugin-morph-data.js +5 -19
  232. package/src/dashboard/vite.config.js +24 -24
  233. package/src/lib/ai/ai-checks.js +7 -52
  234. package/src/lib/ai/ai-project-scan.js +14 -108
  235. package/src/lib/ai/code-audit.js +7 -63
  236. package/src/lib/ai/pin-drift.js +8 -51
  237. package/src/lib/ai/registry-audit.js +11 -90
  238. package/src/lib/ci/ci-verify.js +4 -19
  239. package/src/lib/ci/github-ci.js +9 -37
  240. package/src/lib/ci/workflow-scaffold.js +14 -10
  241. package/src/lib/detectors/claude-config-detector.js +13 -74
  242. package/src/lib/detectors/index.js +4 -50
  243. package/src/lib/e2e/applicability.js +37 -0
  244. package/src/lib/e2e/compose-env.js +5 -66
  245. package/src/lib/e2e/stack-teardown.js +6 -40
  246. package/src/lib/generators/architecture-scanner.js +18 -156
  247. package/src/lib/generators/handoff-generator.js +9 -53
  248. package/src/lib/generators/scaffold-generator.js +5 -101
  249. package/src/lib/generators/settings-generator.js +4 -37
  250. package/src/lib/git/branch-classifier.js +11 -84
  251. package/src/lib/git/default-branch.js +14 -169
  252. package/src/lib/git/evidence-staleness.js +1 -0
  253. package/src/lib/git/gh.js +2 -17
  254. package/src/lib/git/pending-prs.js +6 -37
  255. package/src/lib/git/pr-files.js +3 -28
  256. package/src/lib/git/pr-index.js +3 -34
  257. package/src/lib/git/tree-fingerprint.js +3 -15
  258. package/src/lib/git/tree-state.js +6 -33
  259. package/src/lib/graph/god-nodes.js +2 -22
  260. package/src/lib/graph/graph-health.js +3 -24
  261. package/src/lib/graph/graph-loader.js +5 -56
  262. package/src/lib/graph/graphify-runtime.js +7 -37
  263. package/src/lib/graph/graphifyignore.js +8 -74
  264. package/src/lib/graph/sanitize.js +16 -136
  265. package/src/lib/heavy/cleanup.js +1 -1
  266. package/src/lib/installers/effects.js +8 -93
  267. package/src/lib/installers/mcp-installer.js +9 -169
  268. package/src/lib/installers/memory-installer.js +4 -27
  269. package/src/lib/installers/templates-reconcile.js +5 -60
  270. package/src/lib/mutation/adapters.js +6 -17
  271. package/src/lib/mutation/dotnet-introspection.js +20 -106
  272. package/src/lib/mutation/runner.js +23 -75
  273. package/src/lib/mutation/subset.js +1 -4
  274. package/src/lib/mutation/trx.js +0 -1
  275. package/src/lib/plan/artifact-vocabulary.js +5 -25
  276. package/src/lib/plan/checks.js +34 -33
  277. package/src/lib/plan/plan-parser.js +7 -8
  278. package/src/lib/projects/registry.js +7 -44
  279. package/src/lib/review-reports.js +11 -26
  280. package/src/lib/risk-detector.js +41 -173
  281. package/src/lib/specs/adr-index.js +6 -34
  282. package/src/lib/specs/frontmatter.js +2 -15
  283. package/src/lib/specs/gfm-table.js +1 -7
  284. package/src/lib/specs/revocations.js +1 -14
  285. package/src/lib/specs/screen-checklist.js +47 -124
  286. package/src/lib/stack/dotnet-build-ignores.js +2 -6
  287. package/src/lib/stack/stack-profile.js +1 -28
  288. package/src/lib/stack/stack-scan.js +1 -8
  289. package/src/lib/stack-filter.js +4 -27
  290. package/src/lib/standards/registry.js +1 -7
  291. package/src/lib/standards/sections.js +2 -13
  292. package/src/lib/standards/verified-against.js +19 -191
  293. package/src/lib/tasks/eval-node.js +21 -169
  294. package/src/lib/tasks/linux-runner.js +18 -142
  295. package/src/lib/tasks/test-nodes.js +43 -240
  296. package/src/lib/tasks/test-runner.js +43 -341
  297. package/src/lib/telemetry/metrics.js +7 -36
  298. package/src/lib/telemetry/telemetry-reader.js +13 -125
  299. package/src/lib/telemetry/verify-event.js +2 -30
  300. package/src/lib/tests-audit/audit.js +96 -0
  301. package/src/lib/validators/code-budget/code-budget-validator.js +4 -44
  302. package/src/lib/validators/code-budget/index.js +0 -7
  303. package/src/lib/validators/contracts/contract-compliance-validator.js +2 -24
  304. package/src/lib/validators/contracts/index.js +0 -6
  305. package/src/lib/validators/nextjs/index.js +0 -5
  306. package/src/lib/validators/nextjs/next-component-validator.js +4 -81
  307. package/src/lib/validators/packages/index.js +0 -6
  308. package/src/lib/validators/packages/package-validator.js +6 -56
  309. package/src/lib/validators/reuse/index.js +0 -8
  310. package/src/lib/validators/reuse/reuse-validator.js +11 -101
  311. package/src/lib/validators/reuse/symbol-scanner.js +6 -62
  312. package/src/lib/validators/traceability-check.js +5 -63
  313. package/src/lib/validators/validation-runner.js +21 -158
  314. package/src/lib/validators/verify-runner.js +85 -376
  315. package/src/lib/work-type-detector.js +5 -67
  316. package/src/lib/worktree/docker-audit.js +17 -136
  317. package/src/lib/worktree/env-allocator.js +10 -98
  318. package/src/lib/worktree/feature-truth.js +12 -115
  319. package/src/lib/worktree/finish-backup.js +32 -242
  320. package/src/lib/worktree/fleet-scanner.js +13 -95
  321. package/src/lib/worktree/ignored-triage.js +15 -91
  322. package/src/lib/worktree/vscode-settings.js +2 -3
  323. package/src/lib/worktree/worktree-audit.js +21 -213
  324. package/src/lib/worktree/worktree-include.js +3 -5
  325. package/src/scripts/global-install.js +3 -9
  326. package/src/scripts/install-plugin.js +1 -13
  327. package/src/scripts/setup-infra.js +17 -147
  328. package/src/utils/agents-installer.js +29 -276
  329. package/src/utils/banner.js +4 -25
  330. package/src/utils/claude-md-injector.js +1 -37
  331. package/src/utils/claude-settings-manager.js +1 -52
  332. package/src/utils/file-copier.js +11 -95
  333. package/src/utils/hooks-installer.js +21 -298
  334. package/src/utils/skills-installer.js +4 -101
  335. package/src/utils/version-checker.js +4 -83
  336. package/framework/commands/morph-proposal.md +0 -9
  337. package/framework/hooks/git/commit-msg/conventional-commits.sh +0 -33
  338. package/framework/hooks/git/pre-commit/agents.sh +0 -25
  339. package/framework/hooks/git/pre-commit/orchestrator.sh +0 -64
  340. package/framework/hooks/git/pre-push/run-tests.sh +0 -44
  341. package/src/commands/utils/index.js +0 -3
  342. package/src/lib/detectors/design-system-detector.js +0 -188
  343. package/src/lib/generators/design-system-generator.js +0 -508
  344. package/src/lib/specs/old-format.js +0 -68
  345. package/src/utils/agent-id-mapper.js +0 -11
@@ -8,9 +8,6 @@ import SearchFilter from '../components/SearchFilter.jsx';
8
8
  import Markdown from '../components/Markdown.jsx';
9
9
  import EntityLink, { useSelection, standardKey } from '../components/EntityLink.jsx';
10
10
 
11
- // ─── Colors ───────────────────────────────────────────────────────────────────
12
-
13
- /** Token palette the categories draw from, in order; the categories come from the data. */
14
11
  const PALETTE = [
15
12
  'var(--color-phase-implement)',
16
13
  'var(--color-phase-uiux)',
@@ -24,20 +21,17 @@ const PALETTE = [
24
21
 
25
22
  const tint = (color, pct = 14) => `color-mix(in srgb, ${color} ${pct}%, var(--color-core))`;
26
23
 
27
- /** category → color, stable by category name so a color never jumps between renders. */
24
+ /** Stable by category name, so a color never jumps between renders. */
28
25
  function categoryColors(standards) {
29
26
  const cats = [...new Set(standards.map((s) => s.category || 'outros'))].sort();
30
27
  return Object.fromEntries(cats.map((c, i) => [c, PALETTE[i % PALETTE.length]]));
31
28
  }
32
29
 
33
- // Resolve `?sel=` to a standard: by id first, then by the path a plan cites.
34
30
  export function findStandard(list, sel) {
35
31
  if (!sel) return null;
36
32
  return list.find((s) => s.id === sel) || list.find((s) => s.path === standardKey(sel)) || null;
37
33
  }
38
34
 
39
- // ─── Nodes ────────────────────────────────────────────────────────────────────
40
-
41
35
  const HANDLE = { width: 7, height: 7, border: 0 };
42
36
  const NODE_W = 230;
43
37
  const NODE_H = 52;
@@ -97,8 +91,6 @@ function CategoryNode({ data }) {
97
91
 
98
92
  const nodeTypes = { standardNode: StandardNode, agentNode: AgentNode, categoryNode: CategoryNode };
99
93
 
100
- // ─── Layout ───────────────────────────────────────────────────────────────────
101
-
102
94
  const CELL_W = NODE_W + 14;
103
95
  const CELL_H = NODE_H + 12;
104
96
  const PAD = 16;
@@ -106,7 +98,6 @@ const HEADER = 40;
106
98
  const BLOCK_GAP = 36;
107
99
  const MAX_ROW_W = 1700;
108
100
 
109
- /** How many columns a category block gets: tall categories spread sideways. */
110
101
  function colsFor(n) {
111
102
  if (n > 12) return 3;
112
103
  if (n > 5) return 2;
@@ -127,7 +118,6 @@ function buildGraph({ standards, agents, colors, isMatch, hasFilter, persona, se
127
118
  .map(([cat, list]) => [cat, [...list].sort((a, b) => (a.subcategory || '').localeCompare(b.subcategory || '') || a.name.localeCompare(b.name))])
128
119
  .sort((a, b) => b[1].length - a[1].length || a[0].localeCompare(b[0]));
129
120
 
130
- // Pack the category blocks into rows no wider than MAX_ROW_W.
131
121
  const blocks = cats.map(([cat, list]) => {
132
122
  const cols = colsFor(list.length);
133
123
  const rows = Math.ceil(list.length / cols);
@@ -141,7 +131,7 @@ function buildGraph({ standards, agents, colors, isMatch, hasFilter, persona, se
141
131
  }
142
132
  const totalW = Math.max(...rows.map((r) => r.w), 0);
143
133
 
144
- const std = new Set(); // node ids present
134
+ const std = new Set();
145
135
  let y = 180;
146
136
  for (const row of rows) {
147
137
  let x = (totalW - row.w) / 2;
@@ -170,7 +160,6 @@ function buildGraph({ standards, agents, colors, isMatch, hasFilter, persona, se
170
160
  y += row.h + BLOCK_GAP;
171
161
  }
172
162
 
173
- // The personas on top, each with an edge to every standard it consults.
174
163
  const withStandards = agents.filter((a) => (a.standards || []).some((id) => std.has(id)));
175
164
  const agentX0 = (totalW - withStandards.length * (NODE_W + 24)) / 2;
176
165
  const byId = new Map(standards.map((s) => [s.id, s]));
@@ -194,8 +183,6 @@ function buildGraph({ standards, agents, colors, isMatch, hasFilter, persona, se
194
183
  return { nodes, edges, categories: cats.map(([c, l]) => [c, l.length]) };
195
184
  }
196
185
 
197
- // ─── Detail ───────────────────────────────────────────────────────────────────
198
-
199
186
  function Section({ title, children }) {
200
187
  return (
201
188
  <section className="space-y-2">
@@ -307,8 +294,6 @@ export function StandardDetail({ std, color = 'var(--color-dim)' }) {
307
294
  );
308
295
  }
309
296
 
310
- // ─── Page ─────────────────────────────────────────────────────────────────────
311
-
312
297
  export default function Standards() {
313
298
  const standards = morphData.standards?.list || [];
314
299
  const version = morphData.standards?.version;
@@ -1,12 +1,6 @@
1
1
  /**
2
- * The harness event stream (`.morph/logs/events.jsonl`, schema v2 of
3
- * framework/hooks/shared/telemetry-logger.js) told as a feed a person can read.
4
- *
5
- * Reading and summing are NOT done here: `loadTelemetryAllTrees` gathers every
6
- * tree's log plus the finish backups, and `aggregateCost` is the one place
7
- * tokens are added up (only `delta` can be summed; a v1 stream yields `null`,
8
- * which the UI shows as unknown — never 0). This module only decides what each
9
- * event means in words, and which ones are worth a line.
2
+ * `loadTelemetryAllTrees` reads and `aggregateCost` is the one place tokens are
3
+ * summed: a v1 stream yields `null`, shown as unknown, never 0.
10
4
  */
11
5
 
12
6
  import { statSync, openSync, readSync, closeSync } from 'fs';
@@ -14,7 +8,6 @@ import { join, resolve } from 'path';
14
8
  import { loadTelemetryAllTrees, aggregateCost, dedupeEvents } from '../../lib/telemetry/telemetry-reader.js';
15
9
  import { isFailedToolCall } from '../../../framework/hooks/shared/telemetry-logger.js';
16
10
 
17
- /** Kinds a person follows by default; the rest are one click away. */
18
11
  export const DEFAULT_KINDS = ['prompt', 'question', 'skill', 'dispatch', 'handback', 'verify', 'gate', 'failure'];
19
12
  export const ALL_KINDS = [...DEFAULT_KINDS, 'write', 'read', 'command', 'tool'];
20
13
 
@@ -44,16 +37,6 @@ function nodesSummary(nodes) {
44
37
  return Object.entries(nodes).map(([name, n]) => ({ name, status: typeof n === 'string' ? n : n?.status ?? null }));
45
38
  }
46
39
 
47
- /**
48
- * One event → one feed line, or null when the event is bookkeeping.
49
- *
50
- * @returns {null | {
51
- * id: string, ts: string, feature: string|null, phase: string|null,
52
- * kind: string, title: string, detail: string|null, status: 'ok'|'fail'|null,
53
- * refs: { persona?: string, skill?: string, path?: string },
54
- * nodes?: Array<{name: string, status: string|null}>
55
- * }}
56
- */
57
40
  export function describeEvent(e) {
58
41
  if (!e || typeof e !== 'object' || SILENT_EVENTS.has(e.event)) return null;
59
42
  const base = {
@@ -69,9 +52,8 @@ export function describeEvent(e) {
69
52
  const line = (kind, title, extra = {}) => ({ ...base, kind, title, ...extra, refs: { ...base.refs, ...(extra.refs || {}) } });
70
53
 
71
54
  if (e.event === 'UserPromptSubmit') {
72
- // Hand-backs, task notifications and reminders arrive through the same door
73
- // as a person's prompt, wrapped in a tag. A slash command is the one tagged
74
- // prompt a person typed.
55
+ // Hand-backs, notifications and reminders arrive as a prompt wrapped in a tag;
56
+ // a slash command is the one tagged prompt a person typed.
75
57
  const text = typeof e.prompt === 'string' ? e.prompt.trim() : '';
76
58
  const command = /^<command-name>\/?([^<]+)<\/command-name>/.exec(text);
77
59
  if (command) return line('prompt', `Você rodou /${command[1].trim()}`);
@@ -119,8 +101,7 @@ export function describeEvent(e) {
119
101
  return line('tool', tool || e.event);
120
102
  }
121
103
 
122
- // The same event array is fed again on every request until the log changes:
123
- // describing ~100k events once, not per page, is the difference that matters.
104
+ // Memoized per events array: describing ~100k events once, not per page.
124
105
  const described = new WeakMap();
125
106
 
126
107
  function describeAll(events) {
@@ -137,12 +118,6 @@ function describeAll(events) {
137
118
  return described.get(events);
138
119
  }
139
120
 
140
- /**
141
- * The feed, newest first, filtered and paginated by a `before` timestamp cursor.
142
- *
143
- * @param {Object[]} events - raw events, any order
144
- * @param {{ feature?: string, kinds?: string[], before?: string, limit?: number }} [opts]
145
- */
146
121
  export function buildFeed(events, { feature, kinds = DEFAULT_KINDS, before, limit = 100 } = {}) {
147
122
  const wanted = new Set(kinds);
148
123
  const lines = describeAll(events).filter((l) => wanted.has(l.kind) && (!feature || l.feature === feature));
@@ -152,15 +127,6 @@ export function buildFeed(events, { feature, kinds = DEFAULT_KINDS, before, limi
152
127
  return { items: page, next: page.length === limit && last ? last.ts : null, total: lines.length };
153
128
  }
154
129
 
155
- /**
156
- * What the feature list shows as "the last thing that happened" in each
157
- * feature: the newest line a person follows by default (a dispatch, a verify, a
158
- * task — not a Read or a shell command), plus `ts`, the newest event of ANY kind,
159
- * which is what says an agent is working right now.
160
- *
161
- * @param {Object[]} events
162
- * @returns {Record<string, {ts: string, title: string|null, kind: string|null, status: string|null, titleTs: string|null}>}
163
- */
164
130
  const lastByLines = new WeakMap();
165
131
 
166
132
  export function lastActivityByFeature(events) {
@@ -179,7 +145,6 @@ export function lastActivityByFeature(events) {
179
145
  return out;
180
146
  }
181
147
 
182
- /** Every event of the project across its trees and finish backups. */
183
148
  export function loadProjectEvents(projectDir) {
184
149
  return dedupeEvents(loadTelemetryAllTrees(projectDir).flatMap((t) => t.events));
185
150
  }
@@ -195,27 +160,14 @@ function fileSize(path) {
195
160
  }
196
161
 
197
162
  /**
198
- * The project's events kept in memory and followed as they are appended.
199
- *
200
- * A running agent appends one line to its tree's `events.jsonl` per tool call.
201
- * Re-reading every log (≈100k events, seconds) on each append would block the
202
- * dev server for the whole run, so the store remembers how far it read each
203
- * live log and parses only the bytes after that. Anything it cannot follow
204
- * that way — a log that shrank (SessionStart rotated it), a log it has never
205
- * seen (a new worktree), an archive or backup — falls back to a full reload
206
- * through `loadTelemetryAllTrees`, the same reader as always. Copies of one
207
- * event are still dropped by `eventId`.
208
- *
209
- * Every update yields a NEW events array, so everything memoized per array
210
- * (described lines, per-feature cost) follows the change.
211
- *
212
- * @param {string} projectDir
213
- * @param {{ loadTrees?: Function }} [deps] - test seam, defaults to loadTelemetryAllTrees
163
+ * Tails each live log from the last byte read; a shrunk (rotated) or unseen log,
164
+ * an archive or a backup falls back to a full reload. Every update yields a NEW
165
+ * events array, so everything memoized per array follows the change.
214
166
  */
215
167
  export function createEventStore(projectDir, { loadTrees = loadTelemetryAllTrees } = {}) {
216
168
  let events = null;
217
169
  let seen = new Set();
218
- let offsets = new Map(); // live log path -> bytes consumed (up to the last newline)
170
+ let offsets = new Map();
219
171
  let pending = new Set();
220
172
  let fullReload = true;
221
173
  const costs = new Map();
@@ -238,7 +190,6 @@ export function createEventStore(projectDir, { loadTrees = loadTelemetryAllTrees
238
190
  costs.clear();
239
191
  }
240
192
 
241
- /** Parse the bytes appended to `path` since the last read; false when it cannot follow. */
242
193
  function tail(path) {
243
194
  const from = offsets.get(path);
244
195
  const size = fileSize(path);
@@ -273,7 +224,6 @@ export function createEventStore(projectDir, { loadTrees = loadTelemetryAllTrees
273
224
  const prevLines = described.get(events);
274
225
  const merged = events.concat(fresh);
275
226
  events = fresh.every((e) => (e.ts || '') >= last) ? merged : dedupeEvents(merged);
276
- // Carry the described lines over instead of describing 100k events again.
277
227
  if (prevLines) {
278
228
  const byTsDesc = (a, b) => (b.ts || '').localeCompare(a.ts || '');
279
229
  const added = fresh.map(describeEvent).filter(Boolean).sort(byTsDesc);
@@ -302,19 +252,13 @@ export function createEventStore(projectDir, { loadTrees = loadTelemetryAllTrees
302
252
 
303
253
  return {
304
254
  events: current,
305
- /**
306
- * Something under `.morph/logs` changed. A known live log is tailed on the
307
- * next read; anything else (rotation archive, new tree, backup) reloads.
308
- */
309
255
  changed(path) {
310
256
  const known = path && [...offsets.keys()].find((p) => resolve(p) === resolve(path));
311
257
  if (known) pending.add(known);
312
258
  else fullReload = true;
313
259
  },
314
260
  reset() { fullReload = true; },
315
- /** Loaded and not waiting for a full reload: reading it now is cheap. */
316
261
  ready() { return events !== null && !fullReload; },
317
- /** `featureCost`, memoized until the events change. */
318
262
  cost(feature) {
319
263
  const all = current();
320
264
  if (!costs.has(feature)) costs.set(feature, featureCost(all, feature));
@@ -323,10 +267,7 @@ export function createEventStore(projectDir, { loadTrees = loadTelemetryAllTrees
323
267
  };
324
268
  }
325
269
 
326
- /**
327
- * What the feature cost, straight from `aggregateCost`. `sessionUsage` and
328
- * `agentUsage` stay `null` when the stream has no summable sample.
329
- */
270
+ /** `sessionUsage` and `agentUsage` stay `null` when the stream has no summable sample. */
330
271
  export function featureCost(events, feature) {
331
272
  const c = aggregateCost(events, { feature });
332
273
  return {
@@ -2,27 +2,13 @@ import { readFileSync, existsSync } from 'fs';
2
2
  import { join } from 'path';
3
3
  import { personaStandardIds, isImplementer, renderAgentMd, loadKnownMcpIds } from '../../utils/agents-installer.js';
4
4
 
5
- /** A line of a phase skill that dispatches a sub-agent. */
6
5
  const DISPATCH_LINE = /despach|dispatch|subagent_type/i;
7
6
 
8
- /** The generic dispatch token: "the implementer of the task's domain". */
9
7
  const GENERIC_DISPATCH = 'morph-{id}';
10
8
 
11
9
  /**
12
- * Which phases each persona works in, read from the phase skills themselves.
13
- *
14
- * v5 has no phase list on the persona: a phase dispatches a persona when its
15
- * skill (`phases.json → skill`) names `morph-{personaId}` on a dispatch line
16
- * (morph-uiux → ui-designer, morph-plan → scout, morph-review → verifier, reviewer, security).
17
- * A skill that dispatches the generic `morph-{id}` runs every implementer —
18
- * the same `isImplementer` the validator and the installer use.
19
- *
20
10
  * Only dispatch lines count: morph-plan cites `morph-dotnet-senior` as a
21
11
  * spelling example of the `agent` field, which dispatches nobody.
22
- *
23
- * @param {string} frameworkDir
24
- * @param {Record<string, object>} agents - agents.json `agents` map
25
- * @returns {Map<string, string[]>} personaId → phase ids, in phase order
26
12
  */
27
13
  export function personaPhases(frameworkDir, agents) {
28
14
  const result = new Map(Object.keys(agents).map((id) => [id, []]));
@@ -46,10 +32,6 @@ export function personaPhases(frameworkDir, agents) {
46
32
  return result;
47
33
  }
48
34
 
49
- /**
50
- * Scan agents.json (v5) — flat domain personas, no tiers.
51
- * Returns the list shaped for the dashboard inspector.
52
- */
53
35
  export function scanAgents(frameworkDir) {
54
36
  const agentsJsonPath = join(frameworkDir, 'agents.json');
55
37
  if (!existsSync(agentsJsonPath)) {
@@ -58,9 +40,7 @@ export function scanAgents(frameworkDir) {
58
40
  const agentsJson = JSON.parse(readFileSync(agentsJsonPath, 'utf8'));
59
41
  const phasesOf = personaPhases(frameworkDir, agentsJson.agents);
60
42
  const knownMcpIds = loadKnownMcpIds(frameworkDir);
61
- // The exact `.claude/agents/morph-{id}.md` the installer writes: the system
62
- // prompt the sub-agent actually runs with. A persona whose data the renderer
63
- // refuses shows the refusal instead of hiding the persona.
43
+ // A persona whose data the renderer refuses shows the refusal instead of hiding the persona.
64
44
  const promptOf = (id, agent) => {
65
45
  try { return renderAgentMd({ id, ...agent }, knownMcpIds); } catch (err) { return `<!-- não renderizou: ${err.message} -->`; }
66
46
  };
@@ -71,8 +51,6 @@ export function scanAgents(frameworkDir) {
71
51
  color: agent.color || null,
72
52
  domains: agent.domains || [],
73
53
  keywords: agent.keywords || [],
74
- // Every standard the persona can cite, surfaces included — the same
75
- // reading the installer and the validator use.
76
54
  standards: personaStandardIds(agent),
77
55
  surfaces: agent.surfaces || null,
78
56
  stack: agent.stack || ['*'],
@@ -1,8 +1,3 @@
1
- /**
2
- * The reading material of the framework — the charter, the runtime
3
- * instructions, the CLI reference and the Gate 3 review checklist — for the
4
- * dashboard's Guide page.
5
- */
6
1
  import { readFileSync } from 'fs';
7
2
  import { join } from 'path';
8
3
 
@@ -10,7 +5,6 @@ function read(path) {
10
5
  try { return readFileSync(path, 'utf8'); } catch { return null; }
11
6
  }
12
7
 
13
- /** `## Heading` lines, the table of contents a long document is read by. */
14
8
  export function headingsOf(content) {
15
9
  const out = [];
16
10
  let inFence = false;
@@ -1,9 +1,6 @@
1
1
  /**
2
- * When a feature counts as finished — the one rule the portfolio (server) and
3
- * the feature list (client) share. Pure, no node imports: the client bundles it.
4
- *
5
- * A feature in `.morph/archive/` is closed whatever its status says: legacy
6
- * archives keep `draft` or `in_progress` from before `finish` set the status.
2
+ * Pure, no node imports: the client bundles it. An archived feature is closed
3
+ * whatever its legacy status says.
7
4
  */
8
5
  export function isFinished(f) {
9
6
  return f?.status === 'completed' || f?.status === 'abandoned' || f?.location === 'archive';
@@ -1,22 +1,6 @@
1
1
  /**
2
- * The dashboard's view of the target project's features — the ONLY scanner
3
- * that reads the project rather than the framework package.
4
- *
5
- * Every rule here is borrowed, never re-encoded:
6
- * - which tree owns a feature → `resolveFeatureTree` (feature-truth.js)
7
- * - the current phase → `derivePhaseFrom` (state-reader.js)
8
- * - gate order → `GATE_PREREQ` (gate-order.js)
9
- * - the review verdict → `reviewReportsFromDir` (review-reports.js)
10
- * - path confinement → `canonicalPath`/`isInside` (test-runner.js)
11
- * - slice progress → `sliceProgress` (slice-progress.js)
12
- *
13
- * Three places a feature can live: the root `.morph/features/{f}`, a worktree
14
- * `{worktrees}/{wt}/.morph/features/{f}`, and `.morph/archive/{f}` once
15
- * `finish` ran. The root copy of a feature built in a worktree is its
16
- * birth-instant skeleton and never changes again, so for an active feature the
17
- * owner tree wins. Only active features pay for that question — it spawns git.
18
- *
19
- * Read-only and fail-open: a corrupt file degrades one feature, never the scan.
2
+ * The root copy of a feature built in a worktree is its birth skeleton, so for an
3
+ * active feature the owner tree wins. Only active features ask: it spawns git.
20
4
  */
21
5
 
22
6
  import { readFileSync, existsSync, readdirSync, statSync } from 'fs';
@@ -48,10 +32,7 @@ function subdirs(dir) {
48
32
  } catch { return []; }
49
33
  }
50
34
 
51
- /**
52
- * Old feature.json files stamped a gate as a bare `true` (MORPH_OS). Lifting it
53
- * to `{approved: true}` is what lets the shared rules read them unchanged.
54
- */
35
+ /** Old feature.json files stamped a gate as a bare `true`. */
55
36
  export function normalizeGates(gates) {
56
37
  const out = {};
57
38
  for (const [name, value] of Object.entries(gates || {})) {
@@ -65,10 +46,6 @@ function normalizeFeature(fj) {
65
46
  return { ...f, approvalGates: normalizeGates(f.approvalGates || f.approvals) };
66
47
  }
67
48
 
68
- /**
69
- * Every place a feature lives, before deciding which one tells the truth.
70
- * @returns {Map<string, {root?: string, worktrees: string[], archive?: string}>}
71
- */
72
49
  function collectLocations(projectDir) {
73
50
  const found = new Map();
74
51
  const slot = (name) => {
@@ -93,17 +70,10 @@ function collectLocations(projectDir) {
93
70
  return found;
94
71
  }
95
72
 
96
- /**
97
- * Decide which directory is this feature's truth.
98
- *
99
- * @returns {{ location: 'root'|'worktree'|'archive', dir: string, feature: object,
100
- * ownerTree: string|null, rootStale: boolean }}
101
- */
102
73
  function resolveLocation(projectDir, name, loc, resolveTree) {
103
74
  const rootFj = loc.root ? readJson(join(loc.root, 'feature.json')) : null;
104
75
 
105
- // Archived: `finish` moved the artifacts. An old root copy may linger (MORPH_OS
106
- // keeps a feature.json per finished feature) — the archive holds the files.
76
+ // An old root copy may linger; the archive holds the files.
107
77
  const rootFinished = rootFj && (FINISHED.has(rootFj.status) || rootFj.archivedAt);
108
78
  if (loc.archive && (!rootFj || rootFinished)) {
109
79
  const archFj = readJson(join(loc.archive, 'feature.json'));
@@ -123,8 +93,7 @@ function resolveLocation(projectDir, name, loc, resolveTree) {
123
93
  const wtDir = join(tree, '.morph', 'features', name);
124
94
  const wtFj = readJson(join(wtDir, 'feature.json'));
125
95
  if (wtFj) return { location: 'worktree', dir: wtDir, feature: wtFj, ownerTree: tree, rootStale: false };
126
- // The owner tree exists but carries no copy of the feature: all we have is
127
- // the root skeleton, and the UI has to say so.
96
+ // The owner tree has no copy of the feature: only the root skeleton is left.
128
97
  return { location: 'root', dir: loc.root, feature: rootFj, ownerTree: tree, rootStale: true };
129
98
  }
130
99
  }
@@ -141,10 +110,6 @@ function resolveLocation(projectDir, name, loc, resolveTree) {
141
110
  };
142
111
  }
143
112
 
144
- /**
145
- * The plan's slices with the status their reports give, plus the fields the
146
- * extraction carries for the card. `null` when the plan has not been checked yet.
147
- */
148
113
  export function readSlices(dir) {
149
114
  const progress = sliceProgress(dir);
150
115
  if (!progress) return null;
@@ -189,10 +154,6 @@ function reviewVerdict(dir, workType, reports = reviewReportsFromDir(dir)) {
189
154
  return { verdict: combinedVerdict(reports, required), blockers: allBlockers(reports) };
190
155
  }
191
156
 
192
- /**
193
- * One row per review report the feature asks for or already has: status,
194
- * verdict, blockers and the SHA it reviewed.
195
- */
196
157
  export function reviewRows(reports, workType) {
197
158
  const required = requiredReviewRoles({ workType, securityRequired: reports.security.status !== 'missing' });
198
159
  return Object.keys(REVIEW_ROLES)
@@ -210,10 +171,7 @@ export function reviewRows(reports, workType) {
210
171
  });
211
172
  }
212
173
 
213
- /**
214
- * The plan's scenarios, each with the verifier's row for it. `coverage` is
215
- * `null` until a verifier report exists; only PROVADO covers.
216
- */
174
+ /** `coverage` is `null` until a verifier report exists; only PROVADO covers. */
217
175
  export function readChecks(dir, verifier) {
218
176
  const checks = readJson(checksPath(dir))?.checks;
219
177
  if (!Array.isArray(checks)) return null;
@@ -258,8 +216,7 @@ function summarize(name, resolved) {
258
216
  gates,
259
217
  awaiting: !finished && gates.some((g) => g.paused),
260
218
  slices: slices?.counts ?? NO_SLICES,
261
- // Titles of an active feature's slices, for the global search. Finished
262
- // features leave them out: 170 archived plans would bloat every list reply.
219
+ // Finished features leave slice titles out: archived plans would bloat every list reply.
263
220
  sliceIndex: finished || !slices ? [] : slices.list.map((sl) => ({ id: sl.id, title: sl.title, status: sl.status })),
264
221
  verification: feature.verification
265
222
  ? { status: feature.verification.status ?? null, ts: feature.verification.ts ?? null }
@@ -272,10 +229,6 @@ function summarize(name, resolved) {
272
229
  };
273
230
  }
274
231
 
275
- /**
276
- * Files of a feature, grouped by the phase their top folder belongs to.
277
- * @returns {Array<{path: string, phase: string|null, size: number, mtime: string}>}
278
- */
279
232
  export function listDocuments(dir) {
280
233
  const out = [];
281
234
  const walk = (abs, rel, depth) => {
@@ -305,9 +258,8 @@ export function listDocuments(dir) {
305
258
  }
306
259
 
307
260
  /**
308
- * Resolve one feature. `known` is where a previous scan already found it: the
309
- * list scan paid for the git calls, and opening a feature or one of its files
310
- * must not pay again. It is only trusted while its feature.json still reads.
261
+ * `known` is where a previous scan found the feature, so opening it does not pay
262
+ * for git again. It is only trusted while its feature.json still reads.
311
263
  */
312
264
  function resolveOne(projectDir, name, resolveTree, known) {
313
265
  if (known?.dir) {
@@ -319,13 +271,6 @@ function resolveOne(projectDir, name, resolveTree, known) {
319
271
  return resolveLocation(projectDir, name, loc, resolveTree);
320
272
  }
321
273
 
322
- /**
323
- * The feature list of a project.
324
- *
325
- * @param {string} projectDir
326
- * @param {{ resolveTree?: Function }} [opts] - seam for tests; defaults to
327
- * `resolveFeatureTree`
328
- */
329
274
  export function scanFeatures(projectDir, { resolveTree = resolveFeatureTree } = {}) {
330
275
  const config = readMorphConfig(projectDir);
331
276
  const features = [];
@@ -344,8 +289,7 @@ export function scanFeatures(projectDir, { resolveTree = resolveFeatureTree } =
344
289
  trust: config?.trust || null,
345
290
  dir: projectDir,
346
291
  initialized: existsSync(join(projectDir, '.morph')),
347
- // A project that has state.json but no feature.json anywhere predates the
348
- // per-feature state model — the dashboard says so instead of showing "empty".
292
+ // state.json with no feature.json anywhere predates the per-feature state model.
349
293
  legacyStateOnly: hasState && features.length === 0,
350
294
  },
351
295
  features,
@@ -26,13 +26,7 @@ function extractImports(source) {
26
26
  return imports;
27
27
  }
28
28
 
29
- /**
30
- * Claude Code event name → the kebab-case key the Hook Inspector groups by.
31
- * `pre-compact` and `teammate-idle` have no MORPH hooks — those nodes render
32
- * empty on purpose. `worktree-create`/`worktree-remove` are the worktree
33
- * lifecycle events (v3.8.0) — outside the tool-call flow, drawn as their own
34
- * pair in HookInspector.jsx.
35
- */
29
+ /** `pre-compact` and `teammate-idle` have no MORPH hooks: those nodes render empty on purpose. */
36
30
  const EVENT_KEYS = {
37
31
  SessionStart: 'session-start',
38
32
  UserPromptSubmit: 'user-prompt',
@@ -47,20 +41,7 @@ const EVENT_KEYS = {
47
41
  WorktreeRemove: 'worktree-remove',
48
42
  };
49
43
 
50
- /**
51
- * Build filename → [{ event, matcher }] from the installer's MORPH_HOOKS.
52
- *
53
- * The event is NOT derivable from the path. Before the v4→v5 migration the
54
- * hooks sat in per-event folders, so `basename(dirname(file))` produced the
55
- * event key; the migration flattened everything into `core/`, and from then on
56
- * every hook reported `event: 'core'`. Nothing crashed — the Inspector groups
57
- * by event, found no key it knew, and rendered every node empty. Twelve hooks,
58
- * zero shown.
59
- *
60
- * `getHookDefinitions()` is the single source of truth (the same array the
61
- * installer writes into settings.local.json), so the registration and the
62
- * drawing can no longer disagree.
63
- */
44
+ /** The event is not derivable from the path: every hook sits in `core/`. */
64
45
  export function hookRegistrations(definitions = getHookDefinitions()) {
65
46
  const byFile = new Map();
66
47
  for (const def of definitions) {
@@ -69,10 +50,8 @@ export function hookRegistrations(definitions = getHookDefinitions()) {
69
50
  for (const h of def.hooks || []) {
70
51
  const file = basename(String(h.command || '').trim());
71
52
  if (!file.endsWith('.js')) continue;
72
- // Grupos consolidados apontam para chain.js com os hooks reais em
73
- // `args` (base names, na ordem de execução) — registre cada elo, não o
74
- // runner: é o elo que o Inspector precisa mostrar. chain.js em si não é
75
- // um hook de domínio e ficaria como nó fantasma em todo evento.
53
+ // Grupos consolidados apontam para chain.js com os elos em `args`: registre
54
+ // cada elo, senão chain.js vira nó fantasma em todo evento.
76
55
  const files = file === 'chain.js' && Array.isArray(h.args) && h.args.length
77
56
  ? h.args.map(a => `${a}.js`)
78
57
  : [file];
@@ -80,8 +59,7 @@ export function hookRegistrations(definitions = getHookDefinitions()) {
80
59
  if (!byFile.has(f)) byFile.set(f, []);
81
60
  const entries = byFile.get(f);
82
61
  const matcher = def.matcher || null;
83
- // Several raw events share one Inspector column (PostToolUse and its
84
- // Failure/Batch siblings): one node per column, not one per event.
62
+ // PostToolUse and its Failure/Batch siblings share one Inspector column.
85
63
  if (entries.some((e) => e.event === key && e.matcher === matcher)) continue;
86
64
  entries.push({ event: key, rawEvent: def.event, matcher });
87
65
  }
@@ -111,7 +89,6 @@ export function scanHooks(frameworkDir) {
111
89
  // PostToolUse and UserPromptSubmit, and must show under both.
112
90
  const regs = registrations.get(filename);
113
91
  if (!regs || regs.length === 0) {
114
- // On disk but never registered — real dead weight, and worth seeing.
115
92
  return [{ ...base, event: 'unregistered', matcher: null }];
116
93
  }
117
94
  return regs.map(r => ({ ...base, event: r.event, rawEvent: r.rawEvent, matcher: r.matcher }));
@@ -6,10 +6,6 @@ import { scanStandards } from './standards-scanner.js';
6
6
  import { scanRules } from './rules-scanner.js';
7
7
  import { scanDocs } from './docs-scanner.js';
8
8
 
9
- /**
10
- * The framework's knowledge, for the static virtual module. The project's
11
- * state (features, activity) is served live by project-api.js instead.
12
- */
13
9
  export function scanAll(frameworkDir) {
14
10
  const agents = scanAgents(frameworkDir);
15
11
  const phases = scanPhases(frameworkDir);