@polymorphism-tech/morph-spec 8.47.0 → 9.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +51 -43
- package/README.md +39 -38
- package/bin/morph-spec.js +41 -113
- package/bin/validate.js +1 -147
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +1 -1
- package/docs/QUICKSTART.md +1 -1
- package/framework/CLAUDE.md +41 -101
- package/framework/MORPH.md +95 -252
- package/framework/agents.json +86 -21
- package/framework/commands/morph-apply.md +5 -28
- package/framework/commands/morph-archive.md +1 -1
- package/framework/commands/morph-hotfix.md +8 -8
- package/framework/commands/morph-infra.md +1 -1
- package/framework/commands/morph-preflight.md +2 -3
- package/framework/commands/morph-status.md +4 -4
- package/framework/docs/CLI.md +574 -286
- package/framework/evals/review-checklist.md +82 -41
- package/framework/hooks/README.md +10 -11
- package/framework/hooks/claude-code/core/block-destructive-worktree-removal.js +6 -49
- package/framework/hooks/claude-code/core/block-raw-worktree-add.js +2 -35
- package/framework/hooks/claude-code/core/chain.js +4 -65
- package/framework/hooks/claude-code/core/comment-guard.js +3 -17
- package/framework/hooks/claude-code/core/denylist.json +33 -3
- package/framework/hooks/claude-code/core/enforce-phase-writes.js +15 -63
- package/framework/hooks/claude-code/core/gate-guard.js +9 -78
- package/framework/hooks/claude-code/core/graph-refresh-kick.js +3 -24
- package/framework/hooks/claude-code/core/heavy-gate.js +3 -21
- package/framework/hooks/claude-code/core/heavy-release.js +2 -13
- package/framework/hooks/claude-code/core/loop-detect.js +2 -31
- package/framework/hooks/claude-code/core/pre-bash-denylist.js +36 -2
- package/framework/hooks/claude-code/core/protect-spec-files.js +25 -297
- package/framework/hooks/claude-code/core/state-sync.js +15 -85
- package/framework/hooks/claude-code/core/telemetry-log.js +15 -96
- package/framework/hooks/claude-code/core/validate-completion.js +37 -147
- package/framework/hooks/claude-code/core/warn-bash-source-write.js +23 -0
- package/framework/hooks/claude-code/core/worktree-context.js +15 -114
- package/framework/hooks/claude-code/core/worktree-create.js +6 -70
- package/framework/hooks/claude-code/core/worktree-env-inject.js +5 -56
- package/framework/hooks/claude-code/core/worktree-remove.js +5 -31
- package/framework/hooks/claude-code/statusline.py +55 -41
- package/framework/hooks/package.json +3 -0
- package/framework/hooks/shared/activity-logger.js +9 -89
- package/framework/hooks/shared/agent-reap.js +2 -17
- package/framework/hooks/shared/build-check.js +11 -76
- package/framework/hooks/shared/claim-registry.js +9 -119
- package/framework/hooks/shared/command-tokens.js +168 -44
- package/framework/hooks/shared/comment-rules.js +5 -20
- package/framework/hooks/shared/comment-scanner.js +11 -43
- package/framework/hooks/shared/comment-stripper.js +11 -85
- package/framework/hooks/shared/e2e-check.js +0 -0
- package/framework/hooks/shared/errata.js +59 -0
- package/framework/hooks/shared/feature-locator.js +11 -95
- package/framework/hooks/shared/frontend-locations.js +4 -0
- package/framework/hooks/shared/gate-question.js +12 -0
- package/framework/hooks/shared/git-exclude.js +3 -20
- package/framework/hooks/shared/git-paths.js +8 -104
- package/framework/hooks/shared/graph-freshness.js +22 -166
- package/framework/hooks/shared/graph-refresh.js +21 -149
- package/framework/hooks/shared/heavy-command.js +4 -12
- package/framework/hooks/shared/hook-response.js +8 -83
- package/framework/hooks/shared/morph-cli.js +8 -52
- package/framework/hooks/shared/payload-utils.js +10 -132
- package/framework/hooks/shared/phase-utils.js +14 -72
- package/framework/hooks/shared/redact-secrets.js +4 -15
- package/framework/hooks/shared/reparse-scan.js +6 -66
- package/framework/hooks/shared/slice-progress.js +99 -0
- package/framework/hooks/shared/source-files.js +4 -17
- package/framework/hooks/shared/spawn-command.js +23 -0
- package/framework/hooks/shared/state-reader.js +51 -247
- package/framework/hooks/shared/state-writer.js +9 -54
- package/framework/hooks/shared/stdin-reader.js +1 -11
- package/framework/hooks/shared/telemetry-logger.js +7 -67
- package/framework/hooks/shared/transcript-usage.js +7 -29
- package/framework/hooks/shared/worktree-root.js +7 -29
- package/framework/mcp/registry.json +3 -4
- package/framework/phases.json +23 -39
- package/framework/rules/csharp-standards.md +1 -1
- package/framework/rules/frontend-standards.md +1 -0
- package/framework/rules/hangfire-standards.md +1 -1
- package/framework/rules/neon-standards.md +1 -1
- package/framework/schemas/config.schema.json +14 -9
- package/framework/schemas/feature.schema.json +15 -50
- package/framework/skills/README.md +3 -4
- package/framework/skills/core/create-umbraco-lp/SKILL.md +1 -1
- package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +1 -1
- package/framework/skills/core/morph-apply/SKILL.md +149 -222
- package/framework/skills/core/morph-hotfix/SKILL.md +33 -42
- package/framework/skills/core/morph-machine/SKILL.md +2 -2
- package/framework/skills/core/morph-media/SKILL.md +1 -1
- package/framework/skills/core/morph-plan/SKILL.md +137 -299
- package/framework/skills/core/morph-review/SKILL.md +111 -136
- package/framework/skills/core/morph-standards/SKILL.md +27 -39
- package/framework/skills/core/morph-uiux/SKILL.md +94 -152
- package/framework/standards/STANDARDS.json +109 -1219
- package/framework/standards/STANDARDS.meta.json +1319 -0
- package/framework/standards/ai-agents/agent-archetypes.md +2 -2
- package/framework/standards/ai-agents/agent-session.md +1 -1
- package/framework/standards/ai-agents/conversational-agent-with-phases.md +4 -4
- package/framework/standards/ai-agents/durable-workflows-hitl.md +3 -3
- package/framework/standards/ai-agents/evals-with-cache.md +1 -2
- package/framework/standards/ai-agents/guardrails.md +1 -1
- package/framework/standards/ai-agents/multi-agent-patterns.md +2 -2
- package/framework/standards/ai-agents/observability-patterns.md +1 -1
- package/framework/standards/ai-agents/production.md +1 -1
- package/framework/standards/ai-agents/prompt-sources.md +2 -2
- package/framework/standards/ai-agents/providers/model-registry.md +1 -1
- package/framework/standards/ai-agents/rag-custom-pgvector.md +2 -2
- package/framework/standards/ai-agents/setup.md +3 -3
- package/framework/standards/ai-agents/sweet-spot.md +2 -2
- package/framework/standards/ai-agents/tool-adapters.md +0 -2
- package/framework/standards/ai-agents/vector-data-extensions.md +4 -4
- package/framework/standards/ai-agents/workflows.md +1 -1
- package/framework/standards/architecture/code-comments.md +7 -7
- package/framework/standards/architecture/vertical-slice/vertical-slice.md +1 -1
- package/framework/standards/backend/api/validation.md +1 -1
- package/framework/standards/backend/dotnet/vsa-handler-patterns.md +5 -5
- package/framework/standards/backend/integrations/hangfire/hangfire-jobs.md +1 -1
- package/framework/standards/backend/integrations/neon-auth/neon-auth.md +2 -2
- package/framework/standards/data/nosql/cache/redis.md +1 -1
- package/framework/standards/data/nosql/cosmos-db.md +3 -3
- package/framework/standards/frontend/admin-app/forms.md +1 -1
- package/framework/standards/frontend/design-system/art-direction.md +88 -0
- package/framework/standards/frontend/design-system/premium-finish.md +77 -2
- package/framework/standards/infrastructure/neon/neon-pgvector.md +5 -5
- package/framework/standards/integration/mcp/mcp-tools.md +7 -7
- package/framework/templates/CLAUDE.md.core.template +3 -3
- package/framework/templates/README.md +2 -3
- package/framework/templates/REGISTRY.json +15 -179
- package/framework/templates/docs/plan.md +72 -0
- package/framework/templates/docs/ui-mockups.md +4 -0
- package/framework/templates/dotnet/ai-kit/README.md +1 -9
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Morph.AiKit.Tests.csproj +0 -10
- package/framework/templates/meta-prompts/squad-leaders/backend-squad.md +1 -1
- package/framework/templates/meta-prompts/squad-leaders/frontend-squad.md +1 -1
- package/framework/templates/meta-prompts/squad-leaders/squad-leader.md +1 -1
- package/framework/templates/meta-prompts/validators/pre-commit-validator.md +3 -4
- package/package.json +1 -1
- package/src/commands/context/context-refresh.js +0 -16
- package/src/commands/dashboard/dashboard.js +2 -8
- package/src/commands/e2e/e2e.js +18 -105
- package/src/commands/graph/affected.js +0 -14
- package/src/commands/graph/build.js +11 -50
- package/src/commands/graph/explain.js +0 -16
- package/src/commands/graph/status.js +1 -12
- package/src/commands/mcp/mcp-setup.js +4 -62
- package/src/commands/observability/cost.js +17 -111
- package/src/commands/observability/telemetry.js +1 -14
- package/src/commands/project/branch.js +4 -45
- package/src/commands/project/claim.js +4 -48
- package/src/commands/project/doctor-procs.js +1 -1
- package/src/commands/project/doctor.js +53 -307
- package/src/commands/project/env.js +1 -27
- package/src/commands/project/fleet.js +6 -35
- package/src/commands/project/heavy.js +3 -3
- package/src/commands/project/init.js +8 -68
- package/src/commands/project/install-plugin-cmd.js +0 -1
- package/src/commands/project/setup-infra-cmd.js +2 -1
- package/src/commands/project/stack-scan.js +58 -0
- package/src/commands/project/standards.js +65 -72
- package/src/commands/project/status.js +28 -121
- package/src/commands/project/update.js +31 -223
- package/src/commands/project/worktree.js +109 -883
- package/src/commands/scaffold/scaffold.js +0 -25
- package/src/commands/state/advance.js +65 -120
- package/src/commands/state/approve.js +87 -229
- package/src/commands/state/archive.js +23 -139
- package/src/commands/state/auto-approval.js +65 -0
- package/src/commands/state/create.js +49 -109
- package/src/commands/state/delete.js +11 -63
- package/src/commands/state/finish.js +105 -494
- package/src/commands/state/gate-check.js +74 -183
- package/src/commands/state/gate-decision.js +8 -42
- package/src/commands/state/handoff.js +4 -31
- package/src/commands/state/list.js +21 -91
- package/src/commands/state/report-check.js +134 -338
- package/src/commands/state/retype.js +12 -28
- package/src/commands/validation/index.js +0 -3
- package/src/commands/validation/mutate.js +79 -19
- package/src/commands/validation/plan-check.js +81 -0
- package/src/commands/validation/validate-feature.js +52 -216
- package/src/commands/validation/verify.js +45 -160
- package/src/core/paths/output-schema.js +9 -155
- package/src/core/state/feature-identity.js +1 -25
- package/src/core/state/gate-order.js +2 -30
- package/src/core/state/lifecycle-audit.js +8 -100
- package/src/core/state/state-manager.js +32 -395
- package/src/core/templates/template-registry.js +2 -97
- package/src/core/templates/template-renderer.js +13 -93
- package/src/core/templates/template-seed.js +21 -0
- package/src/core/work-types.js +10 -45
- package/src/dashboard/app/App.jsx +1 -8
- package/src/dashboard/app/components/ActivityFeed.jsx +2 -8
- package/src/dashboard/app/components/CommandPalette.jsx +4 -11
- package/src/dashboard/app/components/DetailPanel.jsx +1 -5
- package/src/dashboard/app/components/EntityLink.jsx +0 -12
- package/src/dashboard/app/components/ErrorBoundary.jsx +1 -8
- package/src/dashboard/app/components/KnowledgeMarkdown.jsx +0 -11
- package/src/dashboard/app/components/Markdown.jsx +0 -9
- package/src/dashboard/app/components/PhaseRail.jsx +0 -11
- package/src/dashboard/app/components/SearchFilter.jsx +0 -4
- package/src/dashboard/app/components/Sidebar.jsx +0 -5
- package/src/dashboard/app/hooks/useMorphQuery.js +1 -11
- package/src/dashboard/app/hooks/usePref.js +1 -5
- package/src/dashboard/app/lib/format.js +0 -2
- package/src/dashboard/app/lib/phases.js +3 -9
- package/src/dashboard/app/lib/project.js +0 -12
- package/src/dashboard/app/pages/Activity.jsx +1 -1
- package/src/dashboard/app/pages/AgentMap.jsx +1 -8
- package/src/dashboard/app/pages/FeatureDetail.jsx +139 -133
- package/src/dashboard/app/pages/Features.jsx +9 -16
- package/src/dashboard/app/pages/Guide.jsx +2 -3
- package/src/dashboard/app/pages/HookInspector.jsx +2 -23
- package/src/dashboard/app/pages/PhasePipeline.jsx +3 -7
- package/src/dashboard/app/pages/Portfolio.jsx +0 -1
- package/src/dashboard/app/pages/RulesMap.jsx +1 -16
- package/src/dashboard/app/pages/SkillsLibrary.jsx +1 -16
- package/src/dashboard/app/pages/Standards.jsx +2 -17
- package/src/dashboard/scanner/activity-scanner.js +14 -83
- package/src/dashboard/scanner/agents-scanner.js +1 -23
- package/src/dashboard/scanner/docs-scanner.js +1 -7
- package/src/dashboard/scanner/feature-status.js +2 -5
- package/src/dashboard/scanner/features-scanner.js +92 -113
- package/src/dashboard/scanner/hooks-scanner.js +5 -28
- package/src/dashboard/scanner/index.js +0 -4
- package/src/dashboard/scanner/portfolio.js +5 -29
- package/src/dashboard/scanner/project-api.js +8 -54
- package/src/dashboard/scanner/rules-scanner.js +1 -2
- package/src/dashboard/scanner/skills-scanner.js +2 -12
- package/src/dashboard/scanner/standards-scanner.js +4 -7
- package/src/dashboard/vite-plugin-morph-data.js +5 -19
- package/src/lib/ai/ai-checks.js +7 -52
- package/src/lib/ai/ai-project-scan.js +14 -108
- package/src/lib/ai/code-audit.js +7 -63
- package/src/lib/ai/pin-drift.js +8 -51
- package/src/lib/ai/registry-audit.js +11 -90
- package/src/lib/ci/ci-verify.js +14 -13
- package/src/lib/ci/github-ci.js +9 -37
- package/src/lib/ci/workflow-scaffold.js +2 -10
- package/src/lib/detectors/claude-config-detector.js +13 -74
- package/src/lib/detectors/index.js +8 -59
- package/src/lib/e2e/compose-env.js +5 -66
- package/src/lib/e2e/stack-teardown.js +6 -40
- package/src/lib/generators/architecture-scanner.js +18 -156
- package/src/lib/generators/handoff-generator.js +37 -112
- package/src/lib/generators/scaffold-generator.js +5 -101
- package/src/lib/generators/settings-generator.js +11 -122
- package/src/lib/git/branch-classifier.js +11 -84
- package/src/lib/git/default-branch.js +14 -169
- package/src/lib/git/evidence-staleness.js +2 -1
- package/src/lib/git/gh.js +2 -17
- package/src/lib/git/pending-prs.js +6 -37
- package/src/lib/git/pr-files.js +3 -28
- package/src/lib/git/pr-index.js +3 -34
- package/src/lib/git/tree-fingerprint.js +17 -17
- package/src/lib/git/tree-state.js +6 -33
- package/src/lib/graph/god-nodes.js +2 -22
- package/src/lib/graph/graph-health.js +3 -24
- package/src/lib/graph/graph-loader.js +5 -56
- package/src/lib/graph/graphify-runtime.js +7 -37
- package/src/lib/graph/graphifyignore.js +8 -74
- package/src/lib/graph/sanitize.js +16 -136
- package/src/lib/heavy/cleanup.js +2 -6
- package/src/lib/installers/effects.js +8 -93
- package/src/lib/installers/mcp-installer.js +9 -169
- package/src/lib/installers/memory-installer.js +8 -33
- package/src/lib/installers/templates-reconcile.js +5 -60
- package/src/lib/mutation/commands.js +3 -2
- package/src/lib/mutation/dotnet-introspection.js +281 -0
- package/src/lib/mutation/plan.js +14 -6
- package/src/lib/mutation/runner.js +1 -1
- package/src/lib/mutation/subset.js +47 -13
- package/src/lib/plan/artifact-vocabulary.js +40 -0
- package/src/lib/plan/checks.js +208 -0
- package/src/lib/plan/plan-parser.js +204 -0
- package/src/lib/plan/plan-template.js +18 -0
- package/src/lib/projects/registry.js +7 -44
- package/src/lib/review-reports.js +204 -0
- package/src/lib/risk-detector.js +131 -274
- package/src/lib/specs/adr-index.js +38 -85
- package/src/lib/specs/design-seed.js +18 -0
- package/src/lib/specs/frontmatter.js +31 -0
- package/src/lib/specs/gfm-table.js +11 -0
- package/src/lib/specs/revocations.js +31 -102
- package/src/lib/specs/screen-checklist.js +56 -132
- package/src/lib/stack/dotnet-build-ignores.js +43 -0
- package/src/lib/stack/stack-profile.js +1 -28
- package/src/lib/stack/stack-scan.js +260 -0
- package/src/lib/stack-filter.js +4 -27
- package/src/lib/standards/registry.js +37 -0
- package/src/lib/standards/sections.js +47 -0
- package/src/lib/standards/verified-against.js +19 -191
- package/src/lib/tasks/eval-node.js +23 -179
- package/src/lib/tasks/linux-runner.js +18 -142
- package/src/lib/tasks/test-nodes.js +43 -313
- package/src/lib/tasks/test-runner.js +107 -447
- package/src/lib/telemetry/metrics.js +13 -103
- package/src/lib/telemetry/telemetry-reader.js +13 -238
- package/src/lib/telemetry/verify-event.js +4 -34
- package/src/lib/validators/code-budget/code-budget-validator.js +4 -44
- package/src/lib/validators/code-budget/index.js +0 -7
- package/src/lib/validators/contracts/contract-compliance-validator.js +4 -26
- package/src/lib/validators/contracts/index.js +0 -6
- package/src/lib/validators/nextjs/index.js +0 -5
- package/src/lib/validators/nextjs/next-component-validator.js +4 -81
- package/src/lib/validators/packages/index.js +0 -6
- package/src/lib/validators/packages/package-validator.js +6 -56
- package/src/lib/validators/reuse/index.js +0 -8
- package/src/lib/validators/reuse/reuse-validator.js +24 -137
- package/src/lib/validators/reuse/symbol-scanner.js +6 -62
- package/src/lib/validators/traceability-check.js +54 -156
- package/src/lib/validators/validation-runner.js +20 -125
- package/src/lib/validators/verify-runner.js +152 -575
- package/src/lib/work-type-detector.js +5 -67
- package/src/lib/worktree/docker-audit.js +17 -137
- package/src/lib/worktree/env-allocator.js +10 -98
- package/src/lib/worktree/feature-truth.js +12 -115
- package/src/lib/worktree/finish-backup.js +32 -242
- package/src/lib/worktree/fleet-scanner.js +31 -126
- package/src/lib/worktree/ignored-triage.js +15 -91
- package/src/lib/worktree/vscode-settings.js +2 -3
- package/src/lib/worktree/worktree-audit.js +21 -213
- package/src/lib/worktree/worktree-include.js +3 -5
- package/src/scripts/global-install.js +3 -9
- package/src/scripts/install-plugin.js +1 -13
- package/src/scripts/setup-infra.js +17 -138
- package/src/utils/agents-installer.js +92 -272
- package/src/utils/banner.js +4 -25
- package/src/utils/claude-md-injector.js +1 -37
- package/src/utils/claude-settings-manager.js +1 -87
- package/src/utils/file-copier.js +32 -90
- package/src/utils/hooks-installer.js +26 -296
- package/src/utils/skills-installer.js +4 -102
- package/src/utils/version-checker.js +4 -83
- package/framework/commands/morph-proposal.md +0 -57
- package/framework/evals/trace-schema.md +0 -67
- package/framework/hooks/claude-code/core/tasks-json-guard.js +0 -83
- package/framework/hooks/claude-code/core/trace-autogen.js +0 -277
- package/framework/hooks/git/pre-commit/specs.sh +0 -50
- package/framework/hooks/shared/tasks-validator.js +0 -111
- package/framework/rules/morph-task-tracking.md +0 -54
- package/framework/rules/morph-workflow.md +0 -244
- package/framework/schemas/tasks.schema.json +0 -79
- package/framework/skills/core/morph-brainstorming/SKILL.md +0 -161
- package/framework/skills/core/morph-implement/SKILL.md +0 -194
- package/framework/skills/core/morph-proposal/SKILL.md +0 -152
- package/framework/skills/core/morph-stack-scan/SKILL.md +0 -145
- package/framework/templates/docs/proposal.md +0 -192
- package/framework/templates/docs/spec.md +0 -57
- package/framework/templates/docs/ui-components.md +0 -52
- package/framework/templates/docs/ui-design-system.md +0 -76
- package/framework/templates/docs/ui-flows.md +0 -167
- package/framework/templates/examples/spec-examples.md +0 -91
- package/framework/templates/feature/decisions.md +0 -124
- package/framework/templates/feature/recap.md +0 -145
- package/framework/templates/tasks.template.json +0 -17
- package/src/commands/state/usecase.js +0 -143
- package/src/commands/tasks/dag.js +0 -181
- package/src/commands/utils/index.js +0 -3
- package/src/lib/agents/output-collector.js +0 -76
- package/src/lib/detectors/design-system-detector.js +0 -188
- package/src/lib/evaluator-report.js +0 -104
- package/src/lib/generators/design-system-generator.js +0 -508
- package/src/lib/specs/acervo-format.js +0 -70
- package/src/lib/specs/acervo-lifecycle.js +0 -264
- package/src/lib/specs/done-criteria-check.js +0 -200
- package/src/lib/specs/usecase-check.js +0 -301
- package/src/lib/specs/usecase-promoter.js +0 -301
- package/src/lib/specs/usecase-registry.js +0 -237
- package/src/lib/tasks/dispatch-planner.js +0 -319
- package/src/lib/tasks/dotnet-test-introspection.js +0 -632
- package/src/lib/tasks/mermaid-renderer.js +0 -132
- package/src/lib/tasks/task-classifier.js +0 -30
- package/src/lib/tasks/task-parser.js +0 -209
- package/src/lib/tasks/tasks-json-parser.js +0 -296
- package/src/lib/tasks/test-scope.js +0 -597
- package/src/utils/agent-id-mapper.js +0 -11
|
@@ -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,12 +9,10 @@ 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
|
-
|
|
15
|
-
uiux: 'Confere o design das telas (mockups e checklist de tela). É opcional: só existe quando a feature passa pela fase de UI/UX.',
|
|
16
|
-
|
|
17
|
-
review: 'Confere a avaliação independente: um avaliador em contexto novo dá o veredito (SHIP ou FIX) e só bloqueia com evidência, com o verify verde.',
|
|
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).',
|
|
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.',
|
|
15
|
+
review: 'Confere a avaliação independente em contexto novo: verificador e revisor (e segurança, quando o diff toca auth, segredo ou dado pessoal), cada um com veredito SHIP ou FIX sobre o mesmo commit. Só bloqueia com evidência, com o verify verde e todo cenário provado.',
|
|
18
16
|
};
|
|
19
17
|
|
|
20
18
|
const GATE_PREFIX = 'gate-';
|
|
@@ -66,7 +64,6 @@ const GATE_W = 150;
|
|
|
66
64
|
const GATE_H = 32;
|
|
67
65
|
const GAP = 48;
|
|
68
66
|
|
|
69
|
-
/** Left to right: each phase, then its gate (when it has one), then the next phase. */
|
|
70
67
|
function buildPipelineGraph(phases, sel) {
|
|
71
68
|
const nodes = [];
|
|
72
69
|
const edges = [];
|
|
@@ -132,7 +129,6 @@ function Chip({ children }) {
|
|
|
132
129
|
// Exported for the render smoke test: no page state, only the phase and the personas.
|
|
133
130
|
export function PhaseDetail({ phase, agents }) {
|
|
134
131
|
const color = phaseColor(phase.id);
|
|
135
|
-
// Personas this phase's skill dispatches (derived by the agents scanner).
|
|
136
132
|
const activeAgents = agents.filter((a) => a.activePhases?.includes(phase.id));
|
|
137
133
|
|
|
138
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
|
-
|
|
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();
|
|
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 || [];
|
|
@@ -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
|
-
/**
|
|
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 task 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();
|
|
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
|
-
*
|
|
3
|
-
*
|
|
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,8 +8,7 @@ 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
|
-
|
|
18
|
-
export const DEFAULT_KINDS = ['prompt', 'question', 'skill', 'dispatch', 'handback', 'task', 'verify', 'gate', 'failure'];
|
|
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
|
|
|
21
14
|
const GATE_COMMAND = /\bmorph-spec\s+(approve|gate-check|gate-decision|advance|finish)\b/;
|
|
@@ -44,20 +37,10 @@ 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, task?: 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 = {
|
|
60
|
-
id: e.eventId || `${e.ts}:${e.event}:${e.tool || ''}
|
|
43
|
+
id: e.eventId || `${e.ts}:${e.event}:${e.tool || ''}`,
|
|
61
44
|
ts: e.ts || null,
|
|
62
45
|
feature: e.feature || null,
|
|
63
46
|
phase: e.phase || null,
|
|
@@ -69,27 +52,18 @@ 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,
|
|
73
|
-
//
|
|
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()}`);
|
|
78
60
|
if (text.startsWith('<')) return null;
|
|
79
61
|
return line('prompt', 'Você escreveu', { detail: clip(text) });
|
|
80
62
|
}
|
|
81
|
-
if (e.event === 'TaskStart') return line('task', `${e.task} começou`, { refs: { task: e.task } });
|
|
82
|
-
if (e.event === 'TaskEnd') {
|
|
83
|
-
const done = e.status === 'done';
|
|
84
|
-
return line('task', done ? `${e.task} concluída` : `${e.task} terminou (${e.status ?? 'sem status'})`, {
|
|
85
|
-
status: done ? 'ok' : null, refs: { task: e.task },
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
63
|
if (e.event === 'Verify') {
|
|
89
64
|
const ok = e.status === 'pass';
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
status: ok ? 'ok' : 'fail', nodes: nodesSummary(e.nodes), refs: e.task ? { task: e.task } : {},
|
|
65
|
+
return line('verify', `verify ${ok ? 'passou' : 'falhou'}`, {
|
|
66
|
+
status: ok ? 'ok' : 'fail', nodes: nodesSummary(e.nodes),
|
|
93
67
|
detail: e.persisted === false && e.reason ? `não carimbou: ${e.reason}` : null,
|
|
94
68
|
});
|
|
95
69
|
}
|
|
@@ -127,8 +101,7 @@ export function describeEvent(e) {
|
|
|
127
101
|
return line('tool', tool || e.event);
|
|
128
102
|
}
|
|
129
103
|
|
|
130
|
-
//
|
|
131
|
-
// describing ~100k events once, not per page, is the difference that matters.
|
|
104
|
+
// Memoized per events array: describing ~100k events once, not per page.
|
|
132
105
|
const described = new WeakMap();
|
|
133
106
|
|
|
134
107
|
function describeAll(events) {
|
|
@@ -145,12 +118,6 @@ function describeAll(events) {
|
|
|
145
118
|
return described.get(events);
|
|
146
119
|
}
|
|
147
120
|
|
|
148
|
-
/**
|
|
149
|
-
* The feed, newest first, filtered and paginated by a `before` timestamp cursor.
|
|
150
|
-
*
|
|
151
|
-
* @param {Object[]} events - raw events, any order
|
|
152
|
-
* @param {{ feature?: string, kinds?: string[], before?: string, limit?: number }} [opts]
|
|
153
|
-
*/
|
|
154
121
|
export function buildFeed(events, { feature, kinds = DEFAULT_KINDS, before, limit = 100 } = {}) {
|
|
155
122
|
const wanted = new Set(kinds);
|
|
156
123
|
const lines = describeAll(events).filter((l) => wanted.has(l.kind) && (!feature || l.feature === feature));
|
|
@@ -160,15 +127,6 @@ export function buildFeed(events, { feature, kinds = DEFAULT_KINDS, before, limi
|
|
|
160
127
|
return { items: page, next: page.length === limit && last ? last.ts : null, total: lines.length };
|
|
161
128
|
}
|
|
162
129
|
|
|
163
|
-
/**
|
|
164
|
-
* What the feature list shows as "the last thing that happened" in each
|
|
165
|
-
* feature: the newest line a person follows by default (a dispatch, a verify, a
|
|
166
|
-
* task — not a Read or a shell command), plus `ts`, the newest event of ANY kind,
|
|
167
|
-
* which is what says an agent is working right now.
|
|
168
|
-
*
|
|
169
|
-
* @param {Object[]} events
|
|
170
|
-
* @returns {Record<string, {ts: string, title: string|null, kind: string|null, status: string|null, titleTs: string|null}>}
|
|
171
|
-
*/
|
|
172
130
|
const lastByLines = new WeakMap();
|
|
173
131
|
|
|
174
132
|
export function lastActivityByFeature(events) {
|
|
@@ -187,7 +145,6 @@ export function lastActivityByFeature(events) {
|
|
|
187
145
|
return out;
|
|
188
146
|
}
|
|
189
147
|
|
|
190
|
-
/** Every event of the project across its trees and finish backups. */
|
|
191
148
|
export function loadProjectEvents(projectDir) {
|
|
192
149
|
return dedupeEvents(loadTelemetryAllTrees(projectDir).flatMap((t) => t.events));
|
|
193
150
|
}
|
|
@@ -203,27 +160,14 @@ function fileSize(path) {
|
|
|
203
160
|
}
|
|
204
161
|
|
|
205
162
|
/**
|
|
206
|
-
*
|
|
207
|
-
*
|
|
208
|
-
*
|
|
209
|
-
* Re-reading every log (≈100k events, seconds) on each append would block the
|
|
210
|
-
* dev server for the whole run, so the store remembers how far it read each
|
|
211
|
-
* live log and parses only the bytes after that. Anything it cannot follow
|
|
212
|
-
* that way — a log that shrank (SessionStart rotated it), a log it has never
|
|
213
|
-
* seen (a new worktree), an archive or backup — falls back to a full reload
|
|
214
|
-
* through `loadTelemetryAllTrees`, the same reader as always. Copies of one
|
|
215
|
-
* event are still dropped by `eventId`.
|
|
216
|
-
*
|
|
217
|
-
* Every update yields a NEW events array, so everything memoized per array
|
|
218
|
-
* (described lines, per-feature cost) follows the change.
|
|
219
|
-
*
|
|
220
|
-
* @param {string} projectDir
|
|
221
|
-
* @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.
|
|
222
166
|
*/
|
|
223
167
|
export function createEventStore(projectDir, { loadTrees = loadTelemetryAllTrees } = {}) {
|
|
224
168
|
let events = null;
|
|
225
169
|
let seen = new Set();
|
|
226
|
-
let offsets = new Map();
|
|
170
|
+
let offsets = new Map();
|
|
227
171
|
let pending = new Set();
|
|
228
172
|
let fullReload = true;
|
|
229
173
|
const costs = new Map();
|
|
@@ -246,7 +190,6 @@ export function createEventStore(projectDir, { loadTrees = loadTelemetryAllTrees
|
|
|
246
190
|
costs.clear();
|
|
247
191
|
}
|
|
248
192
|
|
|
249
|
-
/** Parse the bytes appended to `path` since the last read; false when it cannot follow. */
|
|
250
193
|
function tail(path) {
|
|
251
194
|
const from = offsets.get(path);
|
|
252
195
|
const size = fileSize(path);
|
|
@@ -281,7 +224,6 @@ export function createEventStore(projectDir, { loadTrees = loadTelemetryAllTrees
|
|
|
281
224
|
const prevLines = described.get(events);
|
|
282
225
|
const merged = events.concat(fresh);
|
|
283
226
|
events = fresh.every((e) => (e.ts || '') >= last) ? merged : dedupeEvents(merged);
|
|
284
|
-
// Carry the described lines over instead of describing 100k events again.
|
|
285
227
|
if (prevLines) {
|
|
286
228
|
const byTsDesc = (a, b) => (b.ts || '').localeCompare(a.ts || '');
|
|
287
229
|
const added = fresh.map(describeEvent).filter(Boolean).sort(byTsDesc);
|
|
@@ -310,19 +252,13 @@ export function createEventStore(projectDir, { loadTrees = loadTelemetryAllTrees
|
|
|
310
252
|
|
|
311
253
|
return {
|
|
312
254
|
events: current,
|
|
313
|
-
/**
|
|
314
|
-
* Something under `.morph/logs` changed. A known live log is tailed on the
|
|
315
|
-
* next read; anything else (rotation archive, new tree, backup) reloads.
|
|
316
|
-
*/
|
|
317
255
|
changed(path) {
|
|
318
256
|
const known = path && [...offsets.keys()].find((p) => resolve(p) === resolve(path));
|
|
319
257
|
if (known) pending.add(known);
|
|
320
258
|
else fullReload = true;
|
|
321
259
|
},
|
|
322
260
|
reset() { fullReload = true; },
|
|
323
|
-
/** Loaded and not waiting for a full reload: reading it now is cheap. */
|
|
324
261
|
ready() { return events !== null && !fullReload; },
|
|
325
|
-
/** `featureCost`, memoized until the events change. */
|
|
326
262
|
cost(feature) {
|
|
327
263
|
const all = current();
|
|
328
264
|
if (!costs.has(feature)) costs.set(feature, featureCost(all, feature));
|
|
@@ -331,10 +267,7 @@ export function createEventStore(projectDir, { loadTrees = loadTelemetryAllTrees
|
|
|
331
267
|
};
|
|
332
268
|
}
|
|
333
269
|
|
|
334
|
-
/**
|
|
335
|
-
* What the feature cost, straight from `aggregateCost`. `sessionUsage` and
|
|
336
|
-
* `agentUsage` stay `null` when the stream has no summable sample.
|
|
337
|
-
*/
|
|
270
|
+
/** `sessionUsage` and `agentUsage` stay `null` when the stream has no summable sample. */
|
|
338
271
|
export function featureCost(events, feature) {
|
|
339
272
|
const c = aggregateCost(events, { feature });
|
|
340
273
|
return {
|
|
@@ -344,8 +277,6 @@ export function featureCost(events, feature) {
|
|
|
344
277
|
dispatches: c.dispatches,
|
|
345
278
|
bytesTotal: c.bytesTotal,
|
|
346
279
|
bytesByKind: c.bytesByKind,
|
|
347
|
-
taskDurationSeconds: c.taskDurationSeconds,
|
|
348
|
-
tasks: c.tasks,
|
|
349
280
|
events: c.total,
|
|
350
281
|
};
|
|
351
282
|
}
|
|
@@ -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 → evaluator).
|
|
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
|
-
//
|
|
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 evaluator 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;
|
|
@@ -27,7 +21,7 @@ const GUIDE = [
|
|
|
27
21
|
{ id: 'morph', path: 'MORPH.md', title: 'Carta do harness (MORPH.md)', summary: 'Como o Claude trabalha: papéis, fases, gates, sub-agents. É a fonte única do comportamento.' },
|
|
28
22
|
{ id: 'runtime', path: 'CLAUDE.md', title: 'Instruções de runtime (CLAUDE.md)', summary: 'O que é injetado no CLAUDE.md do projeto: como começar cada sessão, regras críticas, comandos.' },
|
|
29
23
|
{ id: 'cli', path: 'docs/CLI.md', title: 'Referência do CLI', summary: 'Todos os comandos morph-spec: gates, verify, worktrees, grafo, telemetria.' },
|
|
30
|
-
{ id: 'review-checklist', path: 'evals/review-checklist.md', title: 'Checklist do
|
|
24
|
+
{ id: 'review-checklist', path: 'evals/review-checklist.md', title: 'Checklist do Gate 3', summary: 'O que verificador, revisor e segurança procuram antes do ship: veredito SHIP/FIX e bloqueador com evidência.' },
|
|
31
25
|
];
|
|
32
26
|
|
|
33
27
|
export function scanDocs(frameworkDir) {
|
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
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';
|