@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,11 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* MCP Installer
|
|
3
|
-
*
|
|
4
|
-
* Core logic for installing and configuring MCP servers during morph-spec init
|
|
5
|
-
* and via `morph-spec mcp setup`. Handles auto-installable (credential-free)
|
|
6
|
-
* MCPs, prerequisite checking, credential collection, and setup guidance.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
1
|
import { join } from 'path';
|
|
10
2
|
import { readFileSync, existsSync, writeFileSync, mkdirSync } from 'fs';
|
|
11
3
|
import { execSync, spawn } from 'child_process';
|
|
@@ -13,10 +5,6 @@ import { homedir } from 'os';
|
|
|
13
5
|
import { installMcpServers } from '../../utils/claude-settings-manager.js';
|
|
14
6
|
import { mcpServerNames } from '../../utils/agents-installer.js';
|
|
15
7
|
|
|
16
|
-
/**
|
|
17
|
-
* Load the enhanced MCP registry (v3.0.0)
|
|
18
|
-
* @returns {Object} Registry with install blocks
|
|
19
|
-
*/
|
|
20
8
|
export function loadMcpRegistry() {
|
|
21
9
|
const registryPath = join(
|
|
22
10
|
import.meta.dirname, '..', '..', '..', 'framework', 'mcp', 'registry.json'
|
|
@@ -25,15 +13,7 @@ export function loadMcpRegistry() {
|
|
|
25
13
|
return JSON.parse(raw);
|
|
26
14
|
}
|
|
27
15
|
|
|
28
|
-
|
|
29
|
-
* Filter MCPs by stack relevance
|
|
30
|
-
* @param {Object} registry - Full MCP registry
|
|
31
|
-
* @param {string} stack - Detected project stack (e.g., 'nextjs-neon', 'blazor')
|
|
32
|
-
* @param {string[]} [integrations] - `project.integrations` (e.g., ['neon']). The
|
|
33
|
-
* detectors put a database or payment provider here, never in the stack string,
|
|
34
|
-
* so an MCP relevant to it (neon, asaas) is matched against this list too.
|
|
35
|
-
* @returns {Object} Filtered map of mcpName → mcpEntry
|
|
36
|
-
*/
|
|
16
|
+
// Detectors put a database or payment provider in `integrations`, never in the stack string.
|
|
37
17
|
export function filterByStack(registry, stack, integrations = []) {
|
|
38
18
|
const result = {};
|
|
39
19
|
const normalizedStack = (stack || '').toLowerCase();
|
|
@@ -53,14 +33,7 @@ export function filterByStack(registry, stack, integrations = []) {
|
|
|
53
33
|
return result;
|
|
54
34
|
}
|
|
55
35
|
|
|
56
|
-
|
|
57
|
-
* Match detected MCP servers against the registry
|
|
58
|
-
* @param {Array} existingServers - From detectClaudeConfig().mcpServers
|
|
59
|
-
* @param {Object} registry - MCP registry
|
|
60
|
-
* @param {string[]} [enabledPlugins] - From detectClaudeConfig().enabledPlugins. A plugin
|
|
61
|
-
* that ships the server already runs it, and writing it to `.mcp.json` too starts it twice.
|
|
62
|
-
* @returns {Object} Map of mcpName → { installed: true, source }
|
|
63
|
-
*/
|
|
36
|
+
// A plugin that ships the server already runs it: writing it to `.mcp.json` too starts it twice.
|
|
64
37
|
export function getInstalledMcps(existingServers, registry, enabledPlugins = []) {
|
|
65
38
|
const installed = {};
|
|
66
39
|
const registryNames = Object.keys(registry.mcps);
|
|
@@ -86,11 +59,6 @@ export function getInstalledMcps(existingServers, registry, enabledPlugins = [])
|
|
|
86
59
|
return installed;
|
|
87
60
|
}
|
|
88
61
|
|
|
89
|
-
/**
|
|
90
|
-
* Check if prerequisites are met for an MCP entry
|
|
91
|
-
* @param {Object} mcpEntry - MCP entry from registry
|
|
92
|
-
* @returns {Array} Array of { name, met, installUrl } for each prerequisite
|
|
93
|
-
*/
|
|
94
62
|
export function checkPrerequisites(mcpEntry) {
|
|
95
63
|
const prereqs = mcpEntry.install?.prerequisites || [];
|
|
96
64
|
const results = [];
|
|
@@ -101,7 +69,6 @@ export function checkPrerequisites(mcpEntry) {
|
|
|
101
69
|
execSync(prereq.checkCommand, { stdio: 'ignore', timeout: 5000 });
|
|
102
70
|
met = true;
|
|
103
71
|
} catch {
|
|
104
|
-
// Command failed or not found
|
|
105
72
|
}
|
|
106
73
|
results.push({
|
|
107
74
|
name: prereq.name,
|
|
@@ -113,25 +80,13 @@ export function checkPrerequisites(mcpEntry) {
|
|
|
113
80
|
return results;
|
|
114
81
|
}
|
|
115
82
|
|
|
116
|
-
/**
|
|
117
|
-
* Check if an MCP config uses remote HTTP transport (no local process).
|
|
118
|
-
* @param {Object} config - MCP server config
|
|
119
|
-
* @returns {boolean}
|
|
120
|
-
*/
|
|
121
83
|
export function isRemoteMcp(config) {
|
|
122
84
|
return config.type === 'http' || config.url != null;
|
|
123
85
|
}
|
|
124
86
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
* On Windows, npx must be invoked via `cmd /c` to avoid ENOENT errors.
|
|
128
|
-
* Registry already stores cmd/c format; this handles legacy configs that still use npx directly.
|
|
129
|
-
* Remote HTTP MCPs are returned as-is (no local process to patch).
|
|
130
|
-
* @param {Object} config - MCP server config { command, args, env? } or { type: "http", url }
|
|
131
|
-
* @returns {Object} Patched config (new object, original unmodified)
|
|
132
|
-
*/
|
|
87
|
+
// On Windows npx must run via `cmd /c` (ENOENT otherwise); the registry already stores that form,
|
|
88
|
+
// this covers legacy configs.
|
|
133
89
|
export function patchConfigForPlatform(config) {
|
|
134
|
-
// Remote MCPs need no platform patching
|
|
135
90
|
if (isRemoteMcp(config)) return config;
|
|
136
91
|
if (config.command !== 'npx') return config;
|
|
137
92
|
return {
|
|
@@ -141,20 +96,12 @@ export function patchConfigForPlatform(config) {
|
|
|
141
96
|
};
|
|
142
97
|
}
|
|
143
98
|
|
|
144
|
-
/**
|
|
145
|
-
* Install auto-installable MCPs (no credentials, no prerequisites)
|
|
146
|
-
* @param {string} targetPath - Project root directory
|
|
147
|
-
* @param {Object} mcpsToInstall - Map of mcpName → mcpEntry
|
|
148
|
-
* @returns {Promise<Object>} Result from installMcpServers
|
|
149
|
-
*/
|
|
150
99
|
export async function installAutoMcps(targetPath, mcpsToInstall) {
|
|
151
100
|
const servers = {};
|
|
152
101
|
|
|
153
102
|
for (const [name, entry] of Object.entries(mcpsToInstall)) {
|
|
154
103
|
let config = { ...entry.install.config };
|
|
155
|
-
// Remote MCPs don't have env to strip
|
|
156
104
|
if (!isRemoteMcp(config)) {
|
|
157
|
-
// Only include env if it has actual values
|
|
158
105
|
if (config.env && Object.values(config.env).every(v => !v)) {
|
|
159
106
|
delete config.env;
|
|
160
107
|
}
|
|
@@ -165,13 +112,6 @@ export async function installAutoMcps(targetPath, mcpsToInstall) {
|
|
|
165
112
|
return installMcpServers(targetPath, servers);
|
|
166
113
|
}
|
|
167
114
|
|
|
168
|
-
/**
|
|
169
|
-
* Install companion skills for MCPs that define them (e.g., Stitch agent skills).
|
|
170
|
-
* Reads the `companionSkills` field from registry entries and runs the install command.
|
|
171
|
-
* @param {Object} mcpsInstalled - Map of mcpName → mcpEntry that were just installed
|
|
172
|
-
* @param {Function} exec - execSync function (for testing)
|
|
173
|
-
* @returns {Array<{name: string, repo: string, success: boolean}>}
|
|
174
|
-
*/
|
|
175
115
|
export function installCompanionSkills(mcpsInstalled, exec = execSync) {
|
|
176
116
|
const results = [];
|
|
177
117
|
|
|
@@ -190,13 +130,6 @@ export function installCompanionSkills(mcpsInstalled, exec = execSync) {
|
|
|
190
130
|
return results;
|
|
191
131
|
}
|
|
192
132
|
|
|
193
|
-
/**
|
|
194
|
-
* Generate the Stitch OAuth proxy template script.
|
|
195
|
-
* Cross-platform (Windows gcloud.cmd + Unix gcloud), fetch()-based,
|
|
196
|
-
* with readline, session tracking, and graceful shutdown.
|
|
197
|
-
* @param {string} gcpProject - GCP project ID for x-goog-user-project header
|
|
198
|
-
* @returns {string} CJS proxy script content
|
|
199
|
-
*/
|
|
200
133
|
export function getStitchProxyTemplate(gcpProject) {
|
|
201
134
|
return `#!/usr/bin/env node
|
|
202
135
|
/**
|
|
@@ -309,12 +242,6 @@ rl.on('close', () => { stdinClosed = true; maybeExit(); });
|
|
|
309
242
|
`;
|
|
310
243
|
}
|
|
311
244
|
|
|
312
|
-
/**
|
|
313
|
-
* Generate and write the Stitch OAuth proxy script to the target project.
|
|
314
|
-
* @param {string} targetPath - Project root directory
|
|
315
|
-
* @param {string} gcpProject - GCP project ID
|
|
316
|
-
* @returns {{ proxyPath: string }} Path to the generated proxy script
|
|
317
|
-
*/
|
|
318
245
|
export function generateStitchProxy(targetPath, gcpProject) {
|
|
319
246
|
const claudeDir = join(targetPath, '.claude');
|
|
320
247
|
if (!existsSync(claudeDir)) {
|
|
@@ -327,20 +254,12 @@ export function generateStitchProxy(targetPath, gcpProject) {
|
|
|
327
254
|
return { proxyPath };
|
|
328
255
|
}
|
|
329
256
|
|
|
330
|
-
/**
|
|
331
|
-
* Resolve the gcloud CLI binary path.
|
|
332
|
-
* Checks: system PATH → bundled at ~/.stitch-mcp/ → null.
|
|
333
|
-
* @param {Function} exec - execSync function (for testing)
|
|
334
|
-
* @returns {{ gcloudPath: string|null, source: string }}
|
|
335
|
-
*/
|
|
336
257
|
export function findGcloud(exec = execSync) {
|
|
337
|
-
// Try system PATH first
|
|
338
258
|
try {
|
|
339
259
|
exec('gcloud --version', { stdio: ['pipe', 'pipe', 'pipe'], timeout: 10000 });
|
|
340
260
|
return { gcloudPath: 'gcloud', source: 'system' };
|
|
341
|
-
} catch {
|
|
261
|
+
} catch {}
|
|
342
262
|
|
|
343
|
-
// Windows: try Cloud SDK default install location
|
|
344
263
|
if (process.platform === 'win32') {
|
|
345
264
|
const winPath = join(process.env.LOCALAPPDATA || '', 'Google', 'Cloud SDK', 'google-cloud-sdk', 'bin', 'gcloud.cmd');
|
|
346
265
|
if (existsSync(winPath)) {
|
|
@@ -348,7 +267,6 @@ export function findGcloud(exec = execSync) {
|
|
|
348
267
|
}
|
|
349
268
|
}
|
|
350
269
|
|
|
351
|
-
// Try bundled at ~/.stitch-mcp/
|
|
352
270
|
const bundledDir = join(homedir(), '.stitch-mcp');
|
|
353
271
|
const bundledBin = process.platform === 'win32'
|
|
354
272
|
? join(bundledDir, 'google-cloud-sdk', 'bin', 'gcloud.cmd')
|
|
@@ -360,29 +278,10 @@ export function findGcloud(exec = execSync) {
|
|
|
360
278
|
return { gcloudPath: null, source: 'not-found' };
|
|
361
279
|
}
|
|
362
280
|
|
|
363
|
-
/**
|
|
364
|
-
* Fully automated Stitch MCP setup orchestrator.
|
|
365
|
-
*
|
|
366
|
-
* Steps:
|
|
367
|
-
* 1. Find gcloud CLI
|
|
368
|
-
* 2. Verify ADC credentials
|
|
369
|
-
* 3. Detect GCP project
|
|
370
|
-
* 4. Enable stitch.googleapis.com API
|
|
371
|
-
* 5. Generate proxy script
|
|
372
|
-
* 6. Install MCP config in .mcp.json
|
|
373
|
-
* 7. Health-check the proxy
|
|
374
|
-
*
|
|
375
|
-
* @param {string} targetPath - Project root directory
|
|
376
|
-
* @param {Object} options
|
|
377
|
-
* @param {string} [options.gcpProject] - GCP project ID (auto-detected if omitted)
|
|
378
|
-
* @param {Function} [options._exec] - execSync override for testing
|
|
379
|
-
* @returns {Promise<Object>} Step-by-step results
|
|
380
|
-
*/
|
|
381
281
|
export async function setupStitch(targetPath, options = {}) {
|
|
382
282
|
const exec = options._exec || execSync;
|
|
383
283
|
const steps = [];
|
|
384
284
|
|
|
385
|
-
// Step 1: Find gcloud
|
|
386
285
|
const { gcloudPath, source: gcloudSource } = findGcloud(exec);
|
|
387
286
|
if (!gcloudPath) {
|
|
388
287
|
steps.push({ step: 'find-gcloud', success: false, error: 'gcloud CLI not found. Install from: https://cloud.google.com/sdk/docs/install' });
|
|
@@ -392,7 +291,6 @@ export async function setupStitch(targetPath, options = {}) {
|
|
|
392
291
|
|
|
393
292
|
const gcloudCmd = gcloudPath.includes(' ') ? `"${gcloudPath}"` : gcloudPath;
|
|
394
293
|
|
|
395
|
-
// Step 2: Verify ADC credentials
|
|
396
294
|
try {
|
|
397
295
|
exec(`${gcloudCmd} auth application-default print-access-token`, {
|
|
398
296
|
encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'], timeout: 15000
|
|
@@ -403,26 +301,23 @@ export async function setupStitch(targetPath, options = {}) {
|
|
|
403
301
|
return { success: false, steps };
|
|
404
302
|
}
|
|
405
303
|
|
|
406
|
-
// Step 3: Detect GCP project
|
|
407
304
|
let gcpProject = options.gcpProject;
|
|
408
305
|
if (!gcpProject) {
|
|
409
|
-
// Try config.json
|
|
410
306
|
const configPath = join(targetPath, '.morph', 'config', 'config.json');
|
|
411
307
|
if (existsSync(configPath)) {
|
|
412
308
|
try {
|
|
413
309
|
const config = JSON.parse(readFileSync(configPath, 'utf-8'));
|
|
414
310
|
gcpProject = config.gcpProject || config.project?.gcpProject;
|
|
415
|
-
} catch {
|
|
311
|
+
} catch {}
|
|
416
312
|
}
|
|
417
313
|
}
|
|
418
314
|
if (!gcpProject) {
|
|
419
|
-
// Try gcloud config
|
|
420
315
|
try {
|
|
421
316
|
gcpProject = exec(`${gcloudCmd} config get-value project`, {
|
|
422
317
|
encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'], timeout: 10000
|
|
423
318
|
}).trim();
|
|
424
319
|
if (!gcpProject || gcpProject === '(unset)') gcpProject = null;
|
|
425
|
-
} catch {
|
|
320
|
+
} catch {}
|
|
426
321
|
}
|
|
427
322
|
if (!gcpProject) {
|
|
428
323
|
steps.push({ step: 'detect-project', success: false, error: 'Could not detect GCP project. Pass it explicitly or set via: gcloud config set project <id>' });
|
|
@@ -430,7 +325,6 @@ export async function setupStitch(targetPath, options = {}) {
|
|
|
430
325
|
}
|
|
431
326
|
steps.push({ step: 'detect-project', success: true, gcpProject });
|
|
432
327
|
|
|
433
|
-
// Step 4: Enable Stitch API
|
|
434
328
|
try {
|
|
435
329
|
exec(`${gcloudCmd} services enable stitch.googleapis.com --project=${gcpProject}`, {
|
|
436
330
|
encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'], timeout: 30000
|
|
@@ -438,20 +332,17 @@ export async function setupStitch(targetPath, options = {}) {
|
|
|
438
332
|
steps.push({ step: 'enable-api', success: true });
|
|
439
333
|
} catch (err) {
|
|
440
334
|
steps.push({ step: 'enable-api', success: false, error: `Failed to enable stitch API: ${err.message}` });
|
|
441
|
-
// Non-fatal
|
|
335
|
+
// Non-fatal: the API may already be enabled and the user may lack permission to re-enable it.
|
|
442
336
|
}
|
|
443
337
|
|
|
444
|
-
// Step 5: Generate proxy
|
|
445
338
|
const { proxyPath } = generateStitchProxy(targetPath, gcpProject);
|
|
446
339
|
steps.push({ step: 'generate-proxy', success: true, proxyPath });
|
|
447
340
|
|
|
448
|
-
// Step 6: Install MCP config in .mcp.json
|
|
449
341
|
const mcpResult = await installMcpServers(targetPath, {
|
|
450
342
|
stitch: { type: 'stdio', command: 'node', args: ['.claude/stitch-proxy.cjs'] }
|
|
451
343
|
});
|
|
452
344
|
steps.push({ step: 'install-mcp', success: true, added: mcpResult.added, skipped: mcpResult.skipped });
|
|
453
345
|
|
|
454
|
-
// Step 7: Health-check
|
|
455
346
|
const healthResult = await healthCheckStitchProxy(proxyPath);
|
|
456
347
|
steps.push({ step: 'health-check', ...healthResult });
|
|
457
348
|
|
|
@@ -462,11 +353,6 @@ export async function setupStitch(targetPath, options = {}) {
|
|
|
462
353
|
return { success: allCriticalPassed, steps, gcpProject };
|
|
463
354
|
}
|
|
464
355
|
|
|
465
|
-
/**
|
|
466
|
-
* Health-check the Stitch proxy by sending an MCP initialize handshake.
|
|
467
|
-
* @param {string} proxyPath - Path to stitch-proxy.cjs
|
|
468
|
-
* @returns {Promise<Object>} { success, protocolVersion?, error? }
|
|
469
|
-
*/
|
|
470
356
|
export function healthCheckStitchProxy(proxyPath) {
|
|
471
357
|
return new Promise((resolve) => {
|
|
472
358
|
const initMsg = JSON.stringify({
|
|
@@ -492,7 +378,6 @@ export function healthCheckStitchProxy(proxyPath) {
|
|
|
492
378
|
|
|
493
379
|
child.stdout.on('data', (data) => {
|
|
494
380
|
output += data.toString();
|
|
495
|
-
// Check if we got a response
|
|
496
381
|
const lines = output.split('\n').filter(l => l.trim());
|
|
497
382
|
for (const line of lines) {
|
|
498
383
|
try {
|
|
@@ -509,7 +394,7 @@ export function healthCheckStitchProxy(proxyPath) {
|
|
|
509
394
|
resolve({ success: false, error: resp.error.message || 'MCP error' });
|
|
510
395
|
return;
|
|
511
396
|
}
|
|
512
|
-
} catch {
|
|
397
|
+
} catch {}
|
|
513
398
|
}
|
|
514
399
|
});
|
|
515
400
|
|
|
@@ -530,38 +415,22 @@ export function healthCheckStitchProxy(proxyPath) {
|
|
|
530
415
|
}
|
|
531
416
|
});
|
|
532
417
|
|
|
533
|
-
// Send the initialize message
|
|
534
418
|
child.stdin.write(initMsg + '\n');
|
|
535
419
|
child.stdin.end();
|
|
536
420
|
});
|
|
537
421
|
}
|
|
538
422
|
|
|
539
|
-
/**
|
|
540
|
-
* Install a single MCP with provided credentials
|
|
541
|
-
* @param {string} targetPath - Project root directory
|
|
542
|
-
* @param {string} name - MCP name
|
|
543
|
-
* @param {Object} mcpEntry - MCP entry from registry
|
|
544
|
-
* @param {Object} credentialValues - Map of envVar → value
|
|
545
|
-
* @returns {Promise<Object>} Result from installMcpServers
|
|
546
|
-
*/
|
|
547
423
|
export async function installMcpWithCredentials(targetPath, name, mcpEntry, credentialValues) {
|
|
548
424
|
const config = patchConfigForPlatform({ ...mcpEntry.install.config });
|
|
549
425
|
config.env = { ...config.env, ...credentialValues };
|
|
550
426
|
return installMcpServers(targetPath, { [name]: config });
|
|
551
427
|
}
|
|
552
428
|
|
|
553
|
-
/**
|
|
554
|
-
* Generate setup instructions for a credential-requiring MCP
|
|
555
|
-
* @param {string} name - MCP name
|
|
556
|
-
* @param {Object} mcpEntry - MCP entry from registry
|
|
557
|
-
* @returns {Object} { configSnippet, credentialUrls, cliCommand }
|
|
558
|
-
*/
|
|
559
429
|
export function generateSetupInstructions(name, mcpEntry) {
|
|
560
430
|
const config = mcpEntry.install.config;
|
|
561
431
|
const credentials = mcpEntry.install.credentials || [];
|
|
562
432
|
const setupGuide = mcpEntry.install.setupGuide;
|
|
563
433
|
|
|
564
|
-
// Remote HTTP MCPs — show claude mcp add command instead of config snippet
|
|
565
434
|
if (isRemoteMcp(config)) {
|
|
566
435
|
const configSnippet = JSON.stringify({ [name]: config }, null, 2);
|
|
567
436
|
const credentialUrls = credentials.map(c => ({
|
|
@@ -577,7 +446,6 @@ export function generateSetupInstructions(name, mcpEntry) {
|
|
|
577
446
|
};
|
|
578
447
|
}
|
|
579
448
|
|
|
580
|
-
// Build the config snippet with placeholder values
|
|
581
449
|
const snippetConfig = { ...config };
|
|
582
450
|
if (credentials.length > 0) {
|
|
583
451
|
snippetConfig.env = {};
|
|
@@ -601,17 +469,6 @@ export function generateSetupInstructions(name, mcpEntry) {
|
|
|
601
469
|
};
|
|
602
470
|
}
|
|
603
471
|
|
|
604
|
-
/**
|
|
605
|
-
* Full orchestrator for MCP setup during init or `mcp setup`
|
|
606
|
-
* @param {string} targetPath - Project root directory
|
|
607
|
-
* @param {string} stack - Detected project stack
|
|
608
|
-
* @param {Array} existingMcps - From detectClaudeConfig().mcpServers
|
|
609
|
-
* @param {Object} options
|
|
610
|
-
* @param {boolean} options.autoOnly - Only handle auto-installable MCPs
|
|
611
|
-
* @param {string[]} [options.integrations] - `project.integrations`, matched like the stack
|
|
612
|
-
* @param {string[]} [options.enabledPlugins] - `detectClaudeConfig().enabledPlugins`
|
|
613
|
-
* @returns {Object} { autoInstallable, alreadyInstalled, needsManualSetup, notRelevant, prerequisitesMissing }
|
|
614
|
-
*/
|
|
615
472
|
export function orchestrateMcpSetup(targetPath, stack, existingMcps, options = {}) {
|
|
616
473
|
const registry = loadMcpRegistry();
|
|
617
474
|
const relevant = filterByStack(registry, stack, options.integrations ?? []);
|
|
@@ -623,23 +480,19 @@ export function orchestrateMcpSetup(targetPath, stack, existingMcps, options = {
|
|
|
623
480
|
const notRelevant = {};
|
|
624
481
|
const prerequisitesMissing = {};
|
|
625
482
|
|
|
626
|
-
// Categorize all MCPs
|
|
627
483
|
for (const [name, entry] of Object.entries(registry.mcps)) {
|
|
628
484
|
if (!entry.install) continue;
|
|
629
485
|
|
|
630
|
-
// Check if already installed
|
|
631
486
|
if (installed[name]) {
|
|
632
487
|
alreadyInstalled[name] = { entry, source: installed[name].source };
|
|
633
488
|
continue;
|
|
634
489
|
}
|
|
635
490
|
|
|
636
|
-
// Check if relevant to stack
|
|
637
491
|
if (!relevant[name]) {
|
|
638
492
|
notRelevant[name] = entry;
|
|
639
493
|
continue;
|
|
640
494
|
}
|
|
641
495
|
|
|
642
|
-
// Check prerequisites
|
|
643
496
|
const prereqResults = checkPrerequisites(entry);
|
|
644
497
|
const unmetPrereqs = prereqResults.filter(p => !p.met);
|
|
645
498
|
if (unmetPrereqs.length > 0) {
|
|
@@ -647,7 +500,6 @@ export function orchestrateMcpSetup(targetPath, stack, existingMcps, options = {
|
|
|
647
500
|
continue;
|
|
648
501
|
}
|
|
649
502
|
|
|
650
|
-
// Categorize by install type
|
|
651
503
|
if (entry.install.autoInstall) {
|
|
652
504
|
autoInstallable[name] = entry;
|
|
653
505
|
} else if (!options.autoOnly) {
|
|
@@ -665,18 +517,11 @@ export function orchestrateMcpSetup(targetPath, stack, existingMcps, options = {
|
|
|
665
517
|
};
|
|
666
518
|
}
|
|
667
519
|
|
|
668
|
-
/**
|
|
669
|
-
* Format MCP status table for display
|
|
670
|
-
* @param {Object} orchestration - Result from orchestrateMcpSetup
|
|
671
|
-
* @param {Object} justInstalled - Map of names just installed during this run
|
|
672
|
-
* @returns {Array<Object>} Array of { name, status, detail }
|
|
673
|
-
*/
|
|
674
520
|
export function formatMcpStatusTable(orchestration, justInstalled = {}) {
|
|
675
521
|
const rows = [];
|
|
676
522
|
|
|
677
523
|
const { autoInstallable, alreadyInstalled, needsManualSetup, notRelevant, prerequisitesMissing, registry } = orchestration;
|
|
678
524
|
|
|
679
|
-
// Installed during this run
|
|
680
525
|
for (const [name, entry] of Object.entries(justInstalled)) {
|
|
681
526
|
const mcpEntry = registry.mcps[name];
|
|
682
527
|
rows.push({
|
|
@@ -686,7 +531,6 @@ export function formatMcpStatusTable(orchestration, justInstalled = {}) {
|
|
|
686
531
|
});
|
|
687
532
|
}
|
|
688
533
|
|
|
689
|
-
// Auto-installable but not yet installed (and not just-installed)
|
|
690
534
|
for (const [name, entry] of Object.entries(autoInstallable)) {
|
|
691
535
|
if (justInstalled[name]) continue;
|
|
692
536
|
rows.push({
|
|
@@ -696,7 +540,6 @@ export function formatMcpStatusTable(orchestration, justInstalled = {}) {
|
|
|
696
540
|
});
|
|
697
541
|
}
|
|
698
542
|
|
|
699
|
-
// Already installed
|
|
700
543
|
for (const [name, { source }] of Object.entries(alreadyInstalled)) {
|
|
701
544
|
rows.push({
|
|
702
545
|
name,
|
|
@@ -705,7 +548,6 @@ export function formatMcpStatusTable(orchestration, justInstalled = {}) {
|
|
|
705
548
|
});
|
|
706
549
|
}
|
|
707
550
|
|
|
708
|
-
// Needs manual setup
|
|
709
551
|
for (const [name, entry] of Object.entries(needsManualSetup)) {
|
|
710
552
|
rows.push({
|
|
711
553
|
name,
|
|
@@ -714,7 +556,6 @@ export function formatMcpStatusTable(orchestration, justInstalled = {}) {
|
|
|
714
556
|
});
|
|
715
557
|
}
|
|
716
558
|
|
|
717
|
-
// Prerequisites missing
|
|
718
559
|
for (const [name, { unmet }] of Object.entries(prerequisitesMissing)) {
|
|
719
560
|
const missing = unmet.map(p => p.name).join(', ');
|
|
720
561
|
rows.push({
|
|
@@ -724,7 +565,6 @@ export function formatMcpStatusTable(orchestration, justInstalled = {}) {
|
|
|
724
565
|
});
|
|
725
566
|
}
|
|
726
567
|
|
|
727
|
-
// Not relevant
|
|
728
568
|
for (const [name] of Object.entries(notRelevant)) {
|
|
729
569
|
rows.push({
|
|
730
570
|
name,
|
|
@@ -1,15 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
*
|
|
4
|
-
* Writes two memory files (project + reference) plus an entry in MEMORY.md
|
|
5
|
-
* so future Claude Code sessions enter pre-contextualized about the
|
|
6
|
-
* morph-spec setup of this project.
|
|
7
|
-
*
|
|
8
|
-
* Memory location: ~/.claude/projects/<encoded-project-path>/memory/
|
|
9
|
-
*
|
|
10
|
-
* The path encoding mirrors Claude Code's own scheme: replace slashes and
|
|
11
|
-
* colons with dashes. Tests can override via MORPH_MEMORY_ROOT env var.
|
|
12
|
-
*/
|
|
1
|
+
// The path encoding mirrors Claude Code's scheme (slashes and colons become dashes);
|
|
2
|
+
// MORPH_MEMORY_ROOT overrides the root in tests.
|
|
13
3
|
|
|
14
4
|
import fs from 'fs-extra';
|
|
15
5
|
import { join } from 'path';
|
|
@@ -41,8 +31,7 @@ metadata:
|
|
|
41
31
|
- \`.morph/framework/MORPH.md\` — NLH charter (read once per session)
|
|
42
32
|
- \`.morph/framework/agents.json\` — 8 agents: 7 domain personas + 1 stage persona (\`scout\`, read-only)
|
|
43
33
|
- \`.morph/framework/standards/STANDARDS.json\` — standards index (load on demand)
|
|
44
|
-
- \`.morph/
|
|
45
|
-
- \`.morph/features/{feature}/\` — feature artifacts: 0-proposal / 1-design / 2-plan / 3-implement / 4-review
|
|
34
|
+
- \`.morph/features/{feature}/\` — feature artifacts: 2-plan / 1-design / 3-implement / 4-review
|
|
46
35
|
- \`.morph/context/\` — long-lived architectural context (design-system, decisions)
|
|
47
36
|
- \`.morph/state.json\` — feature state (managed by state-sync hook; Edit fallback allowed)
|
|
48
37
|
|
|
@@ -50,8 +39,8 @@ metadata:
|
|
|
50
39
|
|
|
51
40
|
| Command | Skill invoked |
|
|
52
41
|
|---------|---------------|
|
|
53
|
-
| \`/morph-
|
|
54
|
-
| \`/morph-apply {feature}\` | \`Skill(morph-apply)\` —
|
|
42
|
+
| \`/morph-plan {feature}\` | \`Skill(morph-plan)\` — entrevista → plan.md → plan-check → gate do plano |
|
|
43
|
+
| \`/morph-apply {feature}\` | \`Skill(morph-apply)\` — fatias do plano → verify → review → Gate 3 |
|
|
55
44
|
| \`/morph-status [feature]\` | inspect state.json |
|
|
56
45
|
| \`/morph-archive {feature}\` | move completed feature to .morph/archive/ |
|
|
57
46
|
| \`/morph-preflight\` | pre-deploy validation |
|
|
@@ -59,13 +48,12 @@ metadata:
|
|
|
59
48
|
|
|
60
49
|
## Core skills
|
|
61
50
|
|
|
62
|
-
\`morph-
|
|
51
|
+
\`morph-plan\`, \`morph-apply\`, \`morph-uiux\`, \`morph-review\`, \`morph-hotfix\`, \`morph-standards\`
|
|
63
52
|
|
|
64
53
|
**Why:** This project is managed by morph-spec — spec-driven autonomous development with trust-aware gates.
|
|
65
54
|
|
|
66
55
|
**How to apply:**
|
|
67
56
|
- When user mentions a feature, check \`.morph/features/\` first.
|
|
68
|
-
- When user types \`/morph-proposal\`, invoke \`Skill(morph-proposal)\` directly.
|
|
69
57
|
- In \`trust: "auto"\`, gates auto-pass; only AskUserQuestion on risk signals.
|
|
70
58
|
`;
|
|
71
59
|
}
|
|
@@ -83,25 +71,12 @@ metadata:
|
|
|
83
71
|
- \`.morph/context/design-system.md\` — design tokens and components (source of truth)
|
|
84
72
|
- \`.morph/framework/MORPH.md\` — NLH charter, Trust Mode rules, gates, skills table
|
|
85
73
|
- \`.morph/framework/standards/STANDARDS.json\` — index; load specific standard via \`path\` field
|
|
86
|
-
- \`.morph/framework/schemas/tasks.schema.json\` — tasks.json schema reference
|
|
87
74
|
- \`.morph/framework/phases.json\` — phase folder mapping and skill associations
|
|
88
75
|
- \`.morph/framework/agents.json\` — domain persona definitions (dotnet-senior, ef-modeler, etc.)
|
|
89
|
-
- \`.morph/framework/evals/review-checklist.md\` — what the Gate 3
|
|
76
|
+
- \`.morph/framework/evals/review-checklist.md\` — what the Gate 3 verifier, reviewer and security check (verdict SHIP/FIX + evidence-typed blockers)
|
|
90
77
|
`;
|
|
91
78
|
|
|
92
|
-
|
|
93
|
-
* Populate Claude Code persistent memory for a morph-spec project.
|
|
94
|
-
*
|
|
95
|
-
* Idempotent: re-running overwrites the memory files with current info
|
|
96
|
-
* but appends MEMORY.md index entries only if missing.
|
|
97
|
-
*
|
|
98
|
-
* @param {string} projectPath - Absolute path of the project root
|
|
99
|
-
* @param {object} info - Project facts to embed in the memory
|
|
100
|
-
* @param {string} info.projectName
|
|
101
|
-
* @param {string} info.frameworkVersion
|
|
102
|
-
* @param {string} [info.stack]
|
|
103
|
-
* @returns {Promise<{ projectMemoryPath: string, referenceMemoryPath: string, indexPath: string }>}
|
|
104
|
-
*/
|
|
79
|
+
// Idempotent: memory files are overwritten, MEMORY.md entries appended only if missing.
|
|
105
80
|
export async function installProjectMemory(projectPath, info) {
|
|
106
81
|
const memoryRoot = getMemoryRoot(projectPath);
|
|
107
82
|
await fs.ensureDir(memoryRoot);
|
|
@@ -1,67 +1,13 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* templates-reconcile.js — reconcile a pre-existing `.morph/framework/templates/`
|
|
3
|
-
* copy against the project's prior recorded morph-spec version.
|
|
4
|
-
*
|
|
5
|
-
* Shared by BOTH install paths that can encounter this directory:
|
|
6
|
-
* - `morph-spec update` (src/commands/project/update.js) — always knows the
|
|
7
|
-
* project's prior version (`checkProjectOutdated().current`, read from
|
|
8
|
-
* `.morph/.morphversion` BEFORE this run overwrites it).
|
|
9
|
-
* - `morph-spec init`/`setup-infra` (src/scripts/setup-infra.js) — the SAME
|
|
10
|
-
* source (`getProjectMorphVersion()`, the function `checkProjectOutdated`
|
|
11
|
-
* itself calls), read at the same point in the run (before its own write
|
|
12
|
-
* of `.morph/.morphversion`), when it's available — e.g. a repair run
|
|
13
|
-
* over an already-initialized project. Falls back to a plain warning when
|
|
14
|
-
* it isn't (a project with no recorded version has nothing here for this
|
|
15
|
-
* function to reconcile against safely).
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
1
|
import { existsSync } from 'fs';
|
|
19
2
|
import { compareVersions } from '../../utils/version-checker.js';
|
|
20
3
|
import { realEffects } from './effects.js';
|
|
21
4
|
|
|
22
|
-
|
|
23
|
-
* Last CLI version whose `update`/`setup-infra` still wrote
|
|
24
|
-
* `.morph/framework/templates/` unconditionally, before Task 2 of the R3
|
|
25
|
-
* perf plan removed that copy. Used as the decision boundary in
|
|
26
|
-
* `reconcileLegacyTemplatesCopy` below — see its doc comment for why a
|
|
27
|
-
* version check is used instead of diffing file content.
|
|
28
|
-
*/
|
|
5
|
+
// Last CLI version whose install still copied `.morph/framework/templates/` unconditionally.
|
|
29
6
|
export const LAST_VERSION_THAT_COPIED_TEMPLATES = '8.17.0';
|
|
30
7
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
* plan — resolution falls back to the npm package's own framework/templates/,
|
|
35
|
-
* see template-registry.js / architecture-scanner.js's layered resolver).
|
|
36
|
-
*
|
|
37
|
-
* Decided from the project's PRIOR recorded CLI version (before THIS run
|
|
38
|
-
* writes a new one), not by diffing content against the package. A content
|
|
39
|
-
* diff can't tell "stale copy from an older CLI version" apart from
|
|
40
|
-
* "deliberate user override" — and staleness is the dominant case for
|
|
41
|
-
* anyone who actually runs `update`/`setup-infra` again, since template
|
|
42
|
-
* content does change between releases. The version boundary is unambiguous
|
|
43
|
-
* instead:
|
|
44
|
-
*
|
|
45
|
-
* - No local copy → nothing to do.
|
|
46
|
-
* - Prior version is unrecorded, OR <= LAST_VERSION_THAT_COPIED_TEMPLATES
|
|
47
|
-
* → the copy could ONLY have been put there by the OLD installer:
|
|
48
|
-
* `.morph/framework/**` is Edit-denied to the agent (MORPH_DENY in
|
|
49
|
-
* hooks-installer.js — "installer-managed, immutable at runtime"),
|
|
50
|
-
* the directory is gitignored, and every install/update before this fix
|
|
51
|
-
* unconditionally wiped + recopied it. There is no path by which a
|
|
52
|
-
* project on an old version could have a genuine override sitting
|
|
53
|
-
* here. Safe to delete unconditionally — it is dead weight, not data.
|
|
54
|
-
* - Prior version > LAST_VERSION_THAT_COPIED_TEMPLATES → this install path
|
|
55
|
-
* has never written this directory for this project. Anything found
|
|
56
|
-
* here was placed deliberately (by the user or other tooling) — this is
|
|
57
|
-
* the forward-looking override use case the layered resolver exists
|
|
58
|
-
* for. Preserve untouched, unconditionally, with no diffing.
|
|
59
|
-
*
|
|
60
|
-
* @param {string} templatesDest - .morph/framework/templates (may not exist)
|
|
61
|
-
* @param {string|null} priorProjectVersion - project's recorded morph-spec
|
|
62
|
-
* version BEFORE this run (e.g. `checkProjectOutdated().current`)
|
|
63
|
-
* @returns {'absent'|'removed-legacy'|'preserved-override'}
|
|
64
|
-
*/
|
|
8
|
+
// Decided by the PRIOR recorded version, not by diffing: a diff can't tell a stale copy from an override.
|
|
9
|
+
// Unrecorded or <= the boundary → only the old installer could have written it, delete.
|
|
10
|
+
// Newer → placed deliberately, preserve.
|
|
65
11
|
export function reconcileLegacyTemplatesCopy(templatesDest, priorProjectVersion, fx = realEffects()) {
|
|
66
12
|
if (!existsSync(templatesDest)) return 'absent';
|
|
67
13
|
|
|
@@ -74,8 +20,7 @@ export function reconcileLegacyTemplatesCopy(templatesDest, priorProjectVersion,
|
|
|
74
20
|
fx.remove(templatesDest);
|
|
75
21
|
return 'removed-legacy';
|
|
76
22
|
} catch {
|
|
77
|
-
// Couldn't remove
|
|
78
|
-
// the layered resolver's override-first check pick it up as before.
|
|
23
|
+
// Couldn't remove: leave it; the resolver's override-first check picks it up.
|
|
79
24
|
return 'preserved-override';
|
|
80
25
|
}
|
|
81
26
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { resolve } from 'node:path';
|
|
2
|
+
import { discoverDotnetBuildTargets, dotnetBuildCommandFor } from '../tasks/test-runner.js';
|
|
2
3
|
|
|
3
4
|
const FILTER = /--filter(?:[ =:])(?:"([^"]*)"|'([^']*)'|(\S+))/;
|
|
4
5
|
const quote = (s) => `"${String(s).replace(/"/g, '\\"')}"`;
|
|
@@ -32,7 +33,7 @@ const VERBOSITY = /\s(?:-v|--verbosity)[ =:]?\S+/g;
|
|
|
32
33
|
export function dotnetBuildCommands(node, projectPath) {
|
|
33
34
|
const configuration = CONFIGURATION.exec(node.command ?? '');
|
|
34
35
|
const flag = configuration ? ` -c ${configuration[1] ?? configuration[2]}` : '';
|
|
35
|
-
return
|
|
36
|
+
return discoverDotnetBuildTargets(node, resolve(projectPath || '.', node.cwd || '.'))
|
|
36
37
|
.map((target) => dotnetBuildCommandFor(target, projectPath))
|
|
37
38
|
.filter(Boolean)
|
|
38
39
|
.map((command) => `${command.replace(VERBOSITY, '')} -v m${flag}`);
|