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