@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
@@ -2,9 +2,6 @@ import morphData from 'virtual:morph-data';
2
2
  import Markdown from './Markdown.jsx';
3
3
  import EntityLink, { standardKey } from './EntityLink.jsx';
4
4
 
5
- /**
6
- * Names of things that have a page. Built once from the knowledge module.
7
- */
8
5
  const skillIds = new Set((morphData.skills || []).map((s) => s.id));
9
6
  const personaIds = new Set((morphData.agents?.list || []).map((a) => a.id));
10
7
  const standardByKey = new Map();
@@ -13,13 +10,6 @@ for (const s of morphData.standards?.list || []) {
13
10
  standardByKey.set(s.path, s.id);
14
11
  }
15
12
 
16
- /**
17
- * What an inline code span names, if it names something with a page:
18
- * `morph-plan` or `/morph-plan` → skill, `morph-dotnet-senior` → persona,
19
- * a standard id or path → standard.
20
- *
21
- * @returns {{ kind: string, id: string } | null}
22
- */
23
13
  export function resolveMention(text) {
24
14
  const t = String(text).trim();
25
15
  const bare = t.replace(/^\//, '');
@@ -47,7 +37,6 @@ function InlineCode({ className, children, node, ...rest }) {
47
37
  return <code className={className} {...rest}>{children}</code>;
48
38
  }
49
39
 
50
- /** Markdown for framework and feature text: code spans that name a skill, persona or standard become links. */
51
40
  export default function KnowledgeMarkdown({ components, ...props }) {
52
41
  return <Markdown {...props} components={{ code: InlineCode, ...components }} />;
53
42
  }
@@ -6,7 +6,6 @@ import { splitFrontmatter } from '../lib/format.js';
6
6
  const REMARK = [remarkGfm];
7
7
  const REHYPE = [rehypeSlug];
8
8
 
9
- /** Frontmatter keys worth showing, in the order a reader wants them. */
10
9
  const META_ORDER = ['name', 'description', 'when_to_use', 'user-invocable', 'stacks', 'version', 'cliVersion'];
11
10
 
12
11
  function MetaBlock({ meta }) {
@@ -26,14 +25,6 @@ function MetaBlock({ meta }) {
26
25
  );
27
26
  }
28
27
 
29
- /**
30
- * Rendered markdown for every long text in the app: feature artifacts, skills,
31
- * standards, the charter. Frontmatter is lifted into a metadata block instead
32
- * of being printed as text.
33
- *
34
- * @param {{ text: string, components?: object, className?: string, size?: 'md'|'sm' }} props
35
- * `sm` is for side panels, where the reading column is narrower.
36
- */
37
28
  export default function Markdown({ text, components, className = '', size = 'md' }) {
38
29
  const { meta, body } = splitFrontmatter(text || '');
39
30
  return (
@@ -1,16 +1,5 @@
1
1
  import { PHASES, PHASE_INDEX, GATE_LABEL, phaseMeta } from '../lib/phases.js';
2
2
 
3
- /**
4
- * The phase rail: one segment per phase, a notch after each phase that ends in
5
- * an approval gate. The same motif is used in the feature list (sm) and in the
6
- * feature header (lg), so a feature's progress always reads the same way.
7
- *
8
- * Segment state is derived, never stored:
9
- * done — before the current phase, or the whole feature is completed
10
- * current — the derived phase of an active feature
11
- * skipped — an optional phase (uiux) the feature went past without its gate
12
- * todo — after the current phase
13
- */
14
3
  export function segmentStates(feature) {
15
4
  const idx = PHASE_INDEX[feature.phase] ?? 0;
16
5
  const finished = feature.status === 'completed';
@@ -1,10 +1,6 @@
1
1
  import { useState } from 'react';
2
2
  import { MagnifyingGlass } from '@phosphor-icons/react';
3
3
 
4
- /**
5
- * The toolbar of the knowledge pages: a search box and a few selects. The
6
- * `value` of each filter is controlled by the page; the search text is local.
7
- */
8
4
  export default function SearchFilter({ placeholder = 'Buscar', filters = [], onSearchChange, onFilterChange, children }) {
9
5
  const [search, setSearch] = useState('');
10
6
  function handleSearchChange(e) {
@@ -30,10 +30,6 @@ const linkClass = ({ isActive }) => [
30
30
  isActive ? 'bg-white/[0.08] text-[var(--color-fg)] shadow-[inset_0_1px_0_rgb(255_255_255_/_0.06)]' : 'text-[var(--color-dim)] hover:bg-white/[0.04] hover:text-[var(--color-fg)]',
31
31
  ].join(' ');
32
32
 
33
- /**
34
- * Switching project keeps the page: from a feature's detail it lands on the
35
- * other project's feature list, from Activity on the other project's Activity.
36
- */
37
33
  function ProjectSwitcher({ portfolio, current }) {
38
34
  const navigate = useNavigate();
39
35
  const { pathname } = useLocation();
@@ -122,7 +118,6 @@ export default function Sidebar({ portfolio, current, pulse, onSearch }) {
122
118
  );
123
119
  }
124
120
 
125
- /** The same destinations as a compact bar for narrow screens. */
126
121
  export function MobileNav({ current, onSearch }) {
127
122
  const items = [
128
123
  { to: '/', label: 'Portfólio' },
@@ -1,18 +1,8 @@
1
1
  import { useEffect, useRef, useState } from 'react';
2
2
 
3
3
  /**
4
- * Fetch a `/api/morph/*` resource and refetch it whenever the dev server
5
- * reports that the project's `.morph/` changed in the given scope.
6
- *
7
- * `scope` matches the server's invalidation: 'features' (feature.json,
8
- * slice reports, artifacts) or 'activity' (events.jsonl). A refetch keeps the old
9
- * data on screen until the new one arrives — no flash of "loading" while an
10
- * agent is working.
11
- *
4
+ * A refetch keeps the old data on screen until the new one arrives.
12
5
  * @param {string|null} url - null skips the request
13
- * @param {{ scope?: string|string[], project?: string }} [opts] - `scope`: 'features',
14
- * 'activity' or 'projects'; `project`: only changes of this project refetch
15
- * @returns {{ data: any, error: string|null, loading: boolean, refreshedAt: number|null, refetch: () => void }}
16
6
  */
17
7
  export function useMorphQuery(url, { scope = 'features', project } = {}) {
18
8
  const [state, setState] = useState({ data: null, error: null, loading: Boolean(url), refreshedAt: null });
@@ -11,10 +11,7 @@ function read(key, fallback) {
11
11
  }
12
12
  }
13
13
 
14
- /**
15
- * A per-browser preference (view mode, filters). Storage can be unavailable or
16
- * throw (private mode, SSR); the value then simply does not persist.
17
- */
14
+ /** Storage can be unavailable or throw (private mode, SSR); the value then does not persist. */
18
15
  export function usePref(key, fallback) {
19
16
  const [value, setValue] = useState(() => (typeof window === 'undefined' ? fallback : read(key, fallback)));
20
17
  const set = useCallback((next) => {
@@ -27,7 +24,6 @@ export function usePref(key, fallback) {
27
24
  return [value, set];
28
25
  }
29
26
 
30
- /** Re-render every `ms` so relative times ("há 30 s") and "working now" stay true. */
31
27
  export function useNow(ms = 15_000) {
32
28
  const [now, setNow] = useState(() => Date.now());
33
29
  useEffect(() => {
@@ -8,7 +8,6 @@ const UNITS = [
8
8
  ['minute', 60],
9
9
  ];
10
10
 
11
- /** "há 12 minutos" — or an em dash when there is no date. */
12
11
  export function timeAgo(iso, now = Date.now()) {
13
12
  const t = Date.parse(iso || '');
14
13
  if (Number.isNaN(t)) return '—';
@@ -56,7 +55,6 @@ export function duration(seconds) {
56
55
  return `${Math.floor(m / 60)} h ${m % 60} min`;
57
56
  }
58
57
 
59
- /** Strip a leading YAML frontmatter block, returning both parts. */
60
58
  export function splitFrontmatter(text) {
61
59
  const m = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/.exec(text || '');
62
60
  if (!m) return { meta: null, body: text || '' };
@@ -1,8 +1,4 @@
1
- /**
2
- * Display vocabulary for the phase pipeline. The ORDER and the optional flag
3
- * come from framework/phases.json (morphData.phases); this file only adds the
4
- * words and the colour token each phase is shown with.
5
- */
1
+ /** The ORDER and the optional flag come from framework/phases.json; this file only adds words and colours. */
6
2
  import morphData from 'virtual:morph-data';
7
3
 
8
4
  const LABELS = {
@@ -3,10 +3,8 @@ import { useLocation } from 'react-router-dom';
3
3
  import { usePref } from '../hooks/usePref.js';
4
4
  import { useMorphQuery } from '../hooks/useMorphQuery.js';
5
5
 
6
- /** The live API of one project. */
7
6
  export const apiBase = (id) => `/api/morph/p/${encodeURIComponent(id)}`;
8
7
 
9
- /** A page of one project: `/p/{id}/features`, `/p/{id}/features/{name}`, `/p/{id}/activity`. */
10
8
  export const projectHref = (id, sub = 'features') => `/p/${encodeURIComponent(id)}/${sub}`;
11
9
 
12
10
  export const featureHref = (project, name, query = '') =>
@@ -14,10 +12,8 @@ export const featureHref = (project, name, query = '') =>
14
12
 
15
13
  const PROJECTS_CHANGED = 'morph:projects-changed';
16
14
 
17
- /** Tell every reader of the portfolio (page, sidebar, search) to read it again. */
18
15
  export const announceProjectsChanged = () => window.dispatchEvent(new Event(PROJECTS_CHANGED));
19
16
 
20
- /** The portfolio: registered projects, their summaries, and the ones found next to them. */
21
17
  export function useProjects() {
22
18
  const q = useMorphQuery('/api/morph/projects', { scope: 'projects' });
23
19
  const refetch = useRef(q.refetch);
@@ -30,14 +26,6 @@ export function useProjects() {
30
26
  return q;
31
27
  }
32
28
 
33
- /**
34
- * The project the person is looking at: the one in the URL, else the last one
35
- * they opened, else the one the dashboard was launched from. Knowledge pages
36
- * have no project in the URL but the sidebar still needs one to link to.
37
- *
38
- * @param {{ launch?: string|null, projects?: Array<{id: string}> }|null} portfolio
39
- * @returns {{ id: string|null, inUrl: boolean }}
40
- */
41
29
  export function useCurrentProject(portfolio) {
42
30
  const { pathname } = useLocation();
43
31
  const m = /^\/p\/([^/]+)/.exec(pathname);
@@ -1,13 +1,13 @@
1
- import { StrictMode } from 'react';
2
- import { createRoot } from 'react-dom/client';
3
- import { BrowserRouter } from 'react-router-dom';
4
- import App from './App.jsx';
5
- import './styles/index.css';
6
-
7
- createRoot(document.getElementById('root')).render(
8
- <StrictMode>
9
- <BrowserRouter>
10
- <App />
11
- </BrowserRouter>
12
- </StrictMode>
13
- );
1
+ import { StrictMode } from 'react';
2
+ import { createRoot } from 'react-dom/client';
3
+ import { BrowserRouter } from 'react-router-dom';
4
+ import App from './App.jsx';
5
+ import './styles/index.css';
6
+
7
+ createRoot(document.getElementById('root')).render(
8
+ <StrictMode>
9
+ <BrowserRouter>
10
+ <App />
11
+ </BrowserRouter>
12
+ </StrictMode>
13
+ );
@@ -10,11 +10,7 @@ import Markdown from '../components/Markdown.jsx';
10
10
  import EntityLink, { useSelection } from '../components/EntityLink.jsx';
11
11
  import { phaseMeta } from '../lib/phases.js';
12
12
 
13
- /**
14
- * `agents.json → color` is a color NAME (blue, cyan…), the one Claude Code
15
- * paints the sub-agent with. The map reads the same name, so a persona looks
16
- * the same here and in the terminal.
17
- */
13
+ /** Color NAMES, the ones Claude Code paints the sub-agent with. */
18
14
  const COLOR_HEX = {
19
15
  blue: '#60a5fa',
20
16
  cyan: '#22d3ee',
@@ -29,13 +25,11 @@ const COLOR_HEX = {
29
25
  };
30
26
  const FALLBACK_HEX = '#9ca3af';
31
27
 
32
- /** The ONE color lookup: nodes, edges, detail badge and legend all read it. */
33
28
  const personaColor = (agent) => COLOR_HEX[agent?.color] || FALLBACK_HEX;
34
29
 
35
30
  /** `'*'` in a persona's `stack` means "every stack" — it is not a stack of its own. */
36
31
  const UNIVERSAL_STACK = '*';
37
32
 
38
- /** The node of the human + main thread. Never a persona id. */
39
33
  const PRINCIPAL_ID = 'principal';
40
34
 
41
35
  const NODE_W = 220;
@@ -81,7 +75,6 @@ function buildLayout(nodes, edges) {
81
75
  });
82
76
  }
83
77
 
84
- /** Skills whose dispatch lines name this persona, or dispatch any implementer. */
85
78
  function dispatchingSkills(agent) {
86
79
  const implementer = (agent.activePhases || []).includes('implement');
87
80
  return (morphData.skills || []).filter((s) => (s.dispatches || []).some(
@@ -29,13 +29,11 @@ const SLICE_STATUS = {
29
29
  pendente: { label: 'pendente', tone: 'text-[var(--color-faint)]', icon: Circle },
30
30
  };
31
31
 
32
- // An unfinished slice whose dependencies all have a final report can start now.
33
32
  function isReady(sl, byId) {
34
33
  if (sl.status === 'feito') return false;
35
34
  return (sl.depends || []).every((d) => byId[d]?.status === 'feito');
36
35
  }
37
36
 
38
- /** `vscode://file/…` opens the file in the editor from the browser. */
39
37
  export function editorLink(absPath) {
40
38
  return `vscode://file/${encodeURI(String(absPath).replace(/\\/g, '/'))}`;
41
39
  }
@@ -62,7 +60,6 @@ function EditorButton({ path, label = 'Abrir no VS Code' }) {
62
60
  );
63
61
  }
64
62
 
65
- /** What a person should expect next, in one sentence. */
66
63
  export function nextAction(f) {
67
64
  if (f.status === 'completed') return `Concluída${f.archivedAt ? ` ${timeAgo(f.archivedAt)}` : ''}.`;
68
65
  if (f.status === 'abandoned') return 'Abandonada.';
@@ -579,8 +576,7 @@ export default function FeatureDetail() {
579
576
  const tab = TABS.some((t) => t.id === params.get('tab')) ? params.get('tab') : 'overview';
580
577
  const doc = params.get('doc');
581
578
  const { data, error, loading } = useMorphQuery(`${apiBase(project)}/features/${encodeURIComponent(name)}`, { project });
582
- // The cost rides on the activity endpoint (it owns the event stream); one
583
- // line of feed is enough to get it, and it refreshes with the log.
579
+ // The cost rides on the activity endpoint (it owns the event stream).
584
580
  const costQuery = useMorphQuery(`${apiBase(project)}/activity?feature=${encodeURIComponent(name)}&limit=1`, { scope: 'activity', project });
585
581
  const [slicesView, setSlicesView] = usePref('slices.view', 'list');
586
582
  const now = useNow();
@@ -14,7 +14,6 @@ import { isFinished } from '../../scanner/feature-status.js';
14
14
 
15
15
  const COMPLETED_PAGE = 30;
16
16
  const BOARD_DONE_LIMIT = 8;
17
- /** An event this recent means an agent is working on the feature right now. */
18
17
  export const WORKING_MS = 3 * 60 * 1000;
19
18
 
20
19
  const LOCATION_ICON = { worktree: GitBranch, archive: Archive, root: HouseSimple };
@@ -73,7 +72,6 @@ export function stageText(f) {
73
72
  return phaseMeta(f.phase).label;
74
73
  }
75
74
 
76
- /** The second line of a feature: what just happened while it is live, else where it stands. */
77
75
  function subline(f, now) {
78
76
  if (!isFinished(f) && f.lastActivity?.title) {
79
77
  return <>{isWorking(f, now) && <WorkingDot />}<span className="truncate">{f.lastActivity.title}, {timeAgo(f.lastActivity.titleTs, now)}</span></>;
@@ -243,10 +241,6 @@ const chipClass = (on) => [
243
241
  on ? 'bg-white/[0.09] text-[var(--color-fg)] shadow-[inset_0_0_0_1px_var(--color-hair-strong)]' : 'text-[var(--color-faint)] shadow-[inset_0_0_0_1px_var(--color-hair)] hover:text-[var(--color-dim)]',
244
242
  ].join(' ');
245
243
 
246
- /**
247
- * The feature home. All state comes in as props so the view renders the same
248
- * with the live API and with the smoke test's fixture.
249
- */
250
244
  export function FeaturesView({
251
245
  data, query, onQuery, completedShown, onShowMore,
252
246
  view = 'list', onView = () => {}, types = [], onTypes = () => {}, sort = 'updated', onSort = () => {},
@@ -329,7 +323,7 @@ export function FeaturesView({
329
323
  )}
330
324
  {info.initialized && !info.legacyStateOnly && features.length === 0 && (
331
325
  <Empty title="Nenhuma feature ainda">
332
- No Claude Code, comece uma com <code className="font-mono text-[var(--color-fg)]">/morph-proposal</code>, que entrevista e escreve o plano. Ela aparece aqui assim que o <code className="font-mono">feature.json</code> nascer.
326
+ No Claude Code, comece uma com <code className="font-mono text-[var(--color-fg)]">/morph-plan</code>, que entrevista e escreve o plano. Ela aparece aqui assim que o <code className="font-mono">feature.json</code> nascer.
333
327
  </Empty>
334
328
  )}
335
329
  {features.length > 0 && visible.length === 0 && (
@@ -374,7 +368,6 @@ export default function Features() {
374
368
  const now = useNow();
375
369
  const searchRef = useRef(null);
376
370
 
377
- // `/` jumps to the search, like most dev tools.
378
371
  useEffect(() => {
379
372
  const onKey = (e) => {
380
373
  if (e.key !== '/' || e.ctrlKey || e.metaKey || e.altKey) return;
@@ -18,7 +18,6 @@ function personasIn(phaseId) {
18
18
  return (morphData.agents?.list || []).filter((a) => a.activePhases?.includes(phaseId));
19
19
  }
20
20
 
21
- /** The whole flow on one screen: phase, the skill that runs it, who works in it, the gate that closes it. */
22
21
  export function Flow() {
23
22
  return (
24
23
  <section aria-label="O fluxo do morph-spec" className="bezel">
@@ -6,12 +6,7 @@ import FlowGraph from '../components/FlowGraph.jsx';
6
6
  import DetailPanel from '../components/DetailPanel.jsx';
7
7
  import EntityLink, { useSelection } from '../components/EntityLink.jsx';
8
8
 
9
- /**
10
- * Every event the scanner can report. `when` and `power` are what the panel
11
- * explains; `canBlock` is the badge on the graph (the tool-call veto).
12
- * Colors are the dashboard palette as hex, because the MiniMap cannot read
13
- * CSS variables.
14
- */
9
+ /** Colors are hex because the MiniMap cannot read CSS variables. */
15
10
  const EVENTS = {
16
11
  'session-start': {
17
12
  label: 'Início da sessão', color: '#73daca',
@@ -69,12 +64,7 @@ function eventMeta(key) {
69
64
  return EVENTS[key] || { label: key, color: '#8d97a5', when: 'Evento sem descrição no dashboard.', power: '' };
70
65
  }
71
66
 
72
- /**
73
- * The URL key of one registration. A hook file can register more than once
74
- * under the same event with different matchers, so the name alone is not
75
- * unique: `?sel=` holds `event:name:matcher`. A bare hook name also works and
76
- * opens that hook's first registration.
77
- */
67
+ /** A hook can register more than once under one event, so `?sel=` holds `event:name:matcher`. */
78
68
  export function hookKey(hook) {
79
69
  return `${hook.event}:${hook.name}:${hook.matcher || ''}`;
80
70
  }
@@ -89,8 +79,6 @@ function matcherText(hook) {
89
79
  return eventMeta(hook.event).toolEvent ? 'todas as ferramentas' : null;
90
80
  }
91
81
 
92
- // ─── Nodes ────────────────────────────────────────────────────────────────────
93
-
94
82
  const NODE_W = 280;
95
83
  const ROW_H = 26;
96
84
  const HEAD_H = 58;
@@ -161,11 +149,6 @@ function ToolNode() {
161
149
 
162
150
  const nodeTypes = { eventGroupNode: EventGroupNode, toolNode: ToolNode };
163
151
 
164
- // ─── Layout ───────────────────────────────────────────────────────────────────
165
- // Main row: session-start → user-prompt → pre-tool-use → tool → post-tool-use → stop / pre-compact
166
- // Second row: events outside the tool-call flow (worktree pair, teammate-idle,
167
- // unregistered hooks and any event this page does not know).
168
-
169
152
  const COL_GAP = NODE_W + 90;
170
153
  const MAIN_ROW = ['session-start', 'user-prompt', 'pre-tool-use', '__tool__', 'post-tool-use'];
171
154
  const SIDE_ROW = ['worktree-create', 'worktree-remove', 'teammate-idle', 'unregistered'];
@@ -231,8 +214,6 @@ function buildNodesAndEdges(hooksByEvent, onSelectHook, selectedKey) {
231
214
  return { nodes, edges };
232
215
  }
233
216
 
234
- // ─── Panels ───────────────────────────────────────────────────────────────────
235
-
236
217
  function Section({ title, children }) {
237
218
  return (
238
219
  <section>
@@ -369,8 +350,6 @@ function ToolDetail() {
369
350
  );
370
351
  }
371
352
 
372
- // ─── Main Page ────────────────────────────────────────────────────────────────
373
-
374
353
  export default function HookInspector() {
375
354
  const { hooks } = morphData;
376
355
  const [sel, setSel] = useSelection();
@@ -9,7 +9,6 @@ import { phaseMeta, GATE_LABEL } from '../lib/phases.js';
9
9
 
10
10
  const phaseColor = (id) => `var(--color-phase-${id})`;
11
11
 
12
- /** What each approval gate checks, in the words a reader of the pipeline needs. */
13
12
  const GATE_CHECKS = {
14
13
  plan: 'Confere o plano: o problema, o que fica de fora, os cenários com a prova de cada um e as fatias. Depois dele o plan.md congela (só a errata cresce).',
15
14
  uiux: 'Confere o design das telas (mockups e checklist de tela). É opcional: só existe quando a feature passa pela fase de UI/UX, depois do plano.',
@@ -65,7 +64,6 @@ const GATE_W = 150;
65
64
  const GATE_H = 32;
66
65
  const GAP = 48;
67
66
 
68
- /** Left to right: each phase, then its gate (when it has one), then the next phase. */
69
67
  function buildPipelineGraph(phases, sel) {
70
68
  const nodes = [];
71
69
  const edges = [];
@@ -131,7 +129,6 @@ function Chip({ children }) {
131
129
  // Exported for the render smoke test: no page state, only the phase and the personas.
132
130
  export function PhaseDetail({ phase, agents }) {
133
131
  const color = phaseColor(phase.id);
134
- // Personas this phase's skill dispatches (derived by the agents scanner).
135
132
  const activeAgents = agents.filter((a) => a.activePhases?.includes(phase.id));
136
133
 
137
134
  return (
@@ -22,7 +22,6 @@ async function send(method, url, body) {
22
22
  function ProjectBlock({ p, now, onRemove }) {
23
23
  const s = p.summary;
24
24
  const working = s ? s.activeFeatures.filter((f) => isWorking(f, now)).length : 0;
25
- // What waits on you is already listed at the top of the page; here, the rest, latest first.
26
25
  const rest = s ? s.activeFeatures.filter((f) => !f.awaiting) : [];
27
26
  const shown = [...rest].sort((a, b) => (b.lastActivity?.ts || '').localeCompare(a.lastActivity?.ts || '')).slice(0, ACTIVE_SHOWN);
28
27
  return (
@@ -12,13 +12,7 @@ const RULE_COLOR = 'var(--color-phase-plan)';
12
12
  const STD_COLOR = 'var(--color-phase-proposal)';
13
13
  const NODE_W = 260;
14
14
 
15
- // ─── Standard resolution ──────────────────────────────────────────────────────
16
-
17
- /**
18
- * A rule's route → the standard it names in the registry. `id` is what the
19
- * Standards page selects by; with no registry match the path itself is passed,
20
- * so the link still says which file the rule meant.
21
- */
15
+ /** With no registry match the path itself is passed, so the link still names the file. */
22
16
  function resolveStandard(ref, byPath) {
23
17
  const path = standardKey(ref);
24
18
  const std = byPath.get(path);
@@ -30,8 +24,6 @@ function resolveStandard(ref, byPath) {
30
24
  };
31
25
  }
32
26
 
33
- // ─── Node Components ──────────────────────────────────────────────────────────
34
-
35
27
  function RuleNode({ data }) {
36
28
  return (
37
29
  <div
@@ -78,8 +70,6 @@ function StandardRefNode({ data }) {
78
70
 
79
71
  const nodeTypes = { ruleNode: RuleNode, standardRefNode: StandardRefNode };
80
72
 
81
- // ─── Graph Builder ────────────────────────────────────────────────────────────
82
-
83
73
  function matchesFilter(rule, search, stackFilter) {
84
74
  const q = search.toLowerCase();
85
75
  const matchSearch = !q || rule.name.toLowerCase().includes(q) || rule.stacks.some((s) => s.toLowerCase().includes(q));
@@ -153,8 +143,6 @@ function buildGraph(rules, byPath, search, stackFilter, selected) {
153
143
  return { nodes, edges };
154
144
  }
155
145
 
156
- // ─── Detail Content ───────────────────────────────────────────────────────────
157
-
158
146
  function Section({ title, children }) {
159
147
  return (
160
148
  <section>
@@ -221,9 +209,6 @@ function RuleDetail({ rule, byPath }) {
221
209
  );
222
210
  }
223
211
 
224
- // ─── Main Page ────────────────────────────────────────────────────────────────
225
-
226
- /** `?sel=` holds the rule `name` (the file name without `.md`). */
227
212
  export default function RulesMap() {
228
213
  const { rules } = morphData;
229
214
  const [search, setSearch] = useState('');
@@ -22,8 +22,6 @@ const INVOKE_NO = 'só interna';
22
22
 
23
23
  const tint = (color, pct = 14) => `color-mix(in srgb, ${color} ${pct}%, var(--color-core))`;
24
24
 
25
- // ─── Nodes ────────────────────────────────────────────────────────────────────
26
-
27
25
  const HANDLE = { width: 7, height: 7, border: 0 };
28
26
 
29
27
  function PhaseNode({ data }) {
@@ -99,15 +97,11 @@ function GroupNode({ data }) {
99
97
 
100
98
  const nodeTypes = { phaseNode: PhaseNode, skillNode: SkillNode, personaNode: PersonaNode, groupNode: GroupNode };
101
99
 
102
- // ─── Graph model ──────────────────────────────────────────────────────────────
103
-
104
- /** The phase a skill belongs to: its own `phase`, or the phase whose `skill` it is. */
105
100
  function skillPhaseOf(skill, phases) {
106
101
  if (skill.phase) return skill.phase;
107
102
  return phases.find((p) => p.skill === skill.id)?.id || null;
108
103
  }
109
104
 
110
- /** The persona targets of a skill's dispatches: generic ones go to the one "implementadores" node. */
111
105
  function dispatchTargets(skill) {
112
106
  const out = new Set();
113
107
  for (const d of skill.dispatches || []) {
@@ -127,7 +121,7 @@ const SUPPORT_PER_ROW = 5;
127
121
  function buildGraph({ skills, phases, agents, isMatch, hasFilter }) {
128
122
  const nodes = [];
129
123
  const edges = [];
130
- const phaseSkills = new Map(); // phaseId → skill[]
124
+ const phaseSkills = new Map();
131
125
  const support = [];
132
126
  for (const s of skills) {
133
127
  const ph = skillPhaseOf(s, phases);
@@ -137,7 +131,6 @@ function buildGraph({ skills, phases, agents, isMatch, hasFilter }) {
137
131
  } else support.push(s);
138
132
  }
139
133
 
140
- // Row 0 + 1: one column per phase, its skill(s) below it.
141
134
  phases.forEach((p, i) => {
142
135
  const meta = phaseMeta(p.id);
143
136
  const x = i * COL;
@@ -154,7 +147,6 @@ function buildGraph({ skills, phases, agents, isMatch, hasFilter }) {
154
147
  });
155
148
  });
156
149
 
157
- // Row 3+: the support skills, in a labelled group under the personas.
158
150
  const supportCols = Math.min(SUPPORT_PER_ROW, Math.max(1, support.length));
159
151
  const supportRows = Math.ceil(support.length / supportCols);
160
152
  const supportWidth = supportCols * COL;
@@ -173,7 +165,6 @@ function buildGraph({ skills, phases, agents, isMatch, hasFilter }) {
173
165
  nodes.push(skillNode(s, x, y, SUPPORT_COLOR, SUPPORT_LABEL));
174
166
  });
175
167
 
176
- // Row 2: the personas the skills dispatch prompts to.
177
168
  const personaIds = new Set();
178
169
  for (const s of skills) for (const t of dispatchTargets(s)) personaIds.add(t);
179
170
  const agentOrder = agents.map((a) => a.id);
@@ -197,7 +188,6 @@ function buildGraph({ skills, phases, agents, isMatch, hasFilter }) {
197
188
  });
198
189
  });
199
190
 
200
- // skill → persona, from the dispatch prompts.
201
191
  const supportIds = new Set(support.map((s) => s.id));
202
192
  for (const s of skills) {
203
193
  const fromSupport = supportIds.has(s.id);
@@ -226,8 +216,6 @@ function buildGraph({ skills, phases, agents, isMatch, hasFilter }) {
226
216
  }
227
217
  }
228
218
 
229
- // ─── Detail ───────────────────────────────────────────────────────────────────
230
-
231
219
  function Section({ title, children }) {
232
220
  return (
233
221
  <section className="space-y-2">
@@ -252,7 +240,6 @@ function PromptBlock({ text, label = 'Prompt enviado ao sub-agent' }) {
252
240
  );
253
241
  }
254
242
 
255
- /** Plain text of a hast node (the `node` react-markdown hands to overrides). */
256
243
  function hastText(node) {
257
244
  if (!node) return '';
258
245
  if (node.type === 'text') return node.value || '';
@@ -372,8 +359,6 @@ export function SkillDetail({ skill, phases = morphData.phases?.list || [] }) {
372
359
  );
373
360
  }
374
361
 
375
- // ─── Page ─────────────────────────────────────────────────────────────────────
376
-
377
362
  export default function SkillsLibrary() {
378
363
  const skills = morphData.skills || [];
379
364
  const phases = morphData.phases?.list || [];