@polymorphism-tech/morph-spec 9.0.0 → 9.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +8 -9
- package/README.md +38 -36
- package/bin/morph-spec.js +11 -63
- package/bin/validate.js +1 -39
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +1 -1
- package/docs/QUICKSTART.md +1 -1
- package/framework/CLAUDE.md +2 -1
- package/framework/MORPH.md +3 -3
- package/framework/agents.json +19 -11
- package/framework/commands/morph-apply.md +1 -1
- package/framework/commands/morph-status.md +1 -1
- package/framework/docs/CLI.md +22 -31
- package/framework/evals/review-checklist.md +12 -2
- package/framework/hooks/README.md +8 -44
- 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 +2 -16
- package/framework/hooks/claude-code/core/enforce-phase-writes.js +6 -50
- package/framework/hooks/claude-code/core/gate-guard.js +7 -67
- 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 +0 -4
- package/framework/hooks/claude-code/core/protect-spec-files.js +7 -67
- package/framework/hooks/claude-code/core/state-sync.js +12 -68
- package/framework/hooks/claude-code/core/telemetry-log.js +12 -93
- package/framework/hooks/claude-code/core/validate-completion.js +11 -57
- package/framework/hooks/claude-code/core/warn-bash-source-write.js +1 -9
- package/framework/hooks/claude-code/core/worktree-context.js +10 -110
- 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.sh +11 -11
- 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 +6 -75
- package/framework/hooks/shared/claim-registry.js +9 -119
- package/framework/hooks/shared/command-tokens.js +6 -51
- package/framework/hooks/shared/comment-rules.js +5 -20
- package/framework/hooks/shared/comment-scanner.js +6 -25
- package/framework/hooks/shared/comment-stripper.js +11 -85
- package/framework/hooks/shared/e2e-check.js +0 -0
- package/framework/hooks/shared/errata.js +2 -15
- package/framework/hooks/shared/feature-locator.js +11 -95
- package/framework/hooks/shared/frontend-locations.js +1 -3
- package/framework/hooks/shared/gate-question.js +1 -7
- 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 +1 -42
- 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 +4 -23
- package/framework/hooks/shared/source-files.js +4 -17
- package/framework/hooks/shared/state-reader.js +13 -183
- package/framework/hooks/shared/state-writer.js +8 -52
- 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/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/rules/nextjs-standards.md +1 -1
- 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 +4 -4
- package/framework/skills/core/morph-hotfix/SKILL.md +3 -3
- package/framework/skills/core/morph-machine/SKILL.md +1 -1
- package/framework/skills/core/morph-media/SKILL.md +1 -1
- package/framework/skills/core/morph-plan/SKILL.md +9 -7
- package/framework/skills/core/morph-review/SKILL.md +9 -6
- package/framework/skills/core/morph-standards/SKILL.md +1 -1
- package/framework/skills/core/morph-uiux/SKILL.md +29 -12
- package/framework/standards/STANDARDS.json +36 -14
- package/framework/standards/STANDARDS.meta.json +16 -2
- 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/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 +2 -2
- 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 +1 -1
- 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/testing.md +10 -4
- 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/frontend/nextjs/testing.md +10 -6
- package/framework/standards/infrastructure/neon/neon-pgvector.md +5 -5
- package/framework/templates/.idea/morph-templates.xml +92 -0
- package/framework/templates/.vscode/morph-templates.code-snippets +186 -0
- package/framework/templates/CLAUDE.md.core.template +3 -3
- package/framework/templates/IDE-SNIPPETS.md +262 -262
- package/framework/templates/REGISTRY.json +9 -9
- package/framework/templates/code/dotnet/abstractions/api-endpoint.cs +7 -7
- package/framework/templates/code/dotnet/feature/request.cs.hbs +4 -4
- package/framework/templates/code/dotnet/feature/response.cs.hbs +4 -4
- package/framework/templates/code/sql/ai-agents/pgvector-embeddings-migration.sql.template +91 -91
- package/framework/templates/docs/plan.md +3 -1
- package/framework/templates/docs/ui-mockups.md +4 -0
- package/framework/templates/frontend/nextjs/client-component.tsx.hbs +26 -26
- package/framework/templates/frontend/nextjs/feature-form.tsx.hbs +56 -56
- package/framework/templates/frontend/nextjs/tsconfig.json.hbs +26 -26
- package/framework/templates/infrastructure/docker/Dockerfile.template +58 -58
- package/framework/templates/infrastructure/docker/dockerfile-api.dockerfile +38 -38
- package/framework/templates/infrastructure/docker/dockerfile-web.dockerfile +48 -48
- package/framework/templates/infrastructure/github/actions/docker-build-push/action.yml.hbs +45 -45
- package/framework/templates/infrastructure/github/actions/health-check/action.yml.hbs +27 -27
- package/framework/templates/infrastructure/github/workflows/docker-build-push.yml.hbs +59 -59
- package/framework/templates/infrastructure/github/workflows/dotnet-build.yml.hbs +39 -39
- package/framework/templates/meta-prompts/fusion/fusion-agent.md +76 -76
- package/framework/templates/meta-prompts/fusion/fusion-aggregator.md +100 -100
- package/framework/templates/state.template.json +17 -17
- package/framework/templates/ui/design-system.css +226 -226
- 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 +17 -104
- 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 +5 -57
- 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.js +48 -302
- package/src/commands/project/env.js +1 -27
- package/src/commands/project/fleet.js +3 -32
- package/src/commands/project/init.js +4 -68
- package/src/commands/project/install-plugin-cmd.js +0 -1
- package/src/commands/project/standards.js +1 -62
- package/src/commands/project/status.js +0 -53
- package/src/commands/project/update.js +31 -239
- package/src/commands/project/worktree.js +108 -882
- package/src/commands/scaffold/scaffold.js +0 -25
- package/src/commands/state/advance.js +14 -85
- package/src/commands/state/approve.js +15 -97
- package/src/commands/state/archive.js +12 -64
- package/src/commands/state/auto-approval.js +2 -5
- package/src/commands/state/create.js +22 -109
- package/src/commands/state/delete.js +11 -63
- package/src/commands/state/finish.js +98 -475
- package/src/commands/state/gate-check.js +22 -132
- package/src/commands/state/gate-decision.js +3 -39
- package/src/commands/state/handoff.js +3 -30
- package/src/commands/state/list.js +14 -84
- package/src/commands/state/report-check.js +8 -58
- package/src/commands/state/retype.js +4 -26
- package/src/commands/validation/index.js +0 -3
- package/src/commands/validation/mutate.js +11 -11
- package/src/commands/validation/plan-check.js +4 -2
- package/src/commands/validation/tests-audit.js +7 -0
- package/src/commands/validation/validate-feature.js +10 -97
- package/src/commands/validation/verify.js +30 -113
- package/src/core/paths/output-schema.js +2 -101
- package/src/core/state/feature-identity.js +1 -25
- package/src/core/state/gate-order.js +0 -27
- package/src/core/state/lifecycle-audit.js +8 -100
- package/src/core/state/state-manager.js +22 -304
- 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 +0 -1
- package/src/core/work-types.js +1 -36
- package/src/dashboard/app/App.jsx +1 -8
- package/src/dashboard/app/components/ActivityFeed.jsx +0 -5
- package/src/dashboard/app/components/CommandPalette.jsx +0 -7
- 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 +1 -5
- package/src/dashboard/app/lib/project.js +0 -12
- package/src/dashboard/app/main.jsx +13 -13
- package/src/dashboard/app/pages/AgentMap.jsx +1 -8
- package/src/dashboard/app/pages/FeatureDetail.jsx +1 -5
- package/src/dashboard/app/pages/Features.jsx +1 -8
- package/src/dashboard/app/pages/Guide.jsx +0 -1
- package/src/dashboard/app/pages/HookInspector.jsx +2 -23
- package/src/dashboard/app/pages/PhasePipeline.jsx +0 -3
- 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 +10 -69
- package/src/dashboard/scanner/agents-scanner.js +1 -23
- package/src/dashboard/scanner/docs-scanner.js +0 -6
- package/src/dashboard/scanner/feature-status.js +2 -5
- package/src/dashboard/scanner/features-scanner.js +10 -66
- 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 +2 -6
- package/src/dashboard/vite-plugin-morph-data.js +5 -19
- package/src/dashboard/vite.config.js +24 -24
- 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 +4 -19
- package/src/lib/ci/github-ci.js +9 -37
- package/src/lib/ci/workflow-scaffold.js +14 -10
- package/src/lib/detectors/claude-config-detector.js +13 -74
- package/src/lib/detectors/index.js +4 -50
- package/src/lib/e2e/applicability.js +37 -0
- 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 +9 -53
- package/src/lib/generators/scaffold-generator.js +5 -101
- package/src/lib/generators/settings-generator.js +4 -37
- 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 +1 -0
- 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 +3 -15
- 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 +1 -1
- package/src/lib/installers/effects.js +8 -93
- package/src/lib/installers/mcp-installer.js +9 -169
- package/src/lib/installers/memory-installer.js +4 -27
- package/src/lib/installers/templates-reconcile.js +5 -60
- package/src/lib/mutation/adapters.js +6 -17
- package/src/lib/mutation/dotnet-introspection.js +20 -106
- package/src/lib/mutation/runner.js +23 -75
- package/src/lib/mutation/subset.js +1 -4
- package/src/lib/mutation/trx.js +0 -1
- package/src/lib/plan/artifact-vocabulary.js +5 -25
- package/src/lib/plan/checks.js +34 -33
- package/src/lib/plan/plan-parser.js +7 -8
- package/src/lib/projects/registry.js +7 -44
- package/src/lib/review-reports.js +11 -26
- package/src/lib/risk-detector.js +41 -173
- package/src/lib/specs/adr-index.js +6 -34
- package/src/lib/specs/frontmatter.js +2 -15
- package/src/lib/specs/gfm-table.js +1 -7
- package/src/lib/specs/revocations.js +1 -14
- package/src/lib/specs/screen-checklist.js +47 -124
- package/src/lib/stack/dotnet-build-ignores.js +2 -6
- package/src/lib/stack/stack-profile.js +1 -28
- package/src/lib/stack/stack-scan.js +1 -8
- package/src/lib/stack-filter.js +4 -27
- package/src/lib/standards/registry.js +1 -7
- package/src/lib/standards/sections.js +2 -13
- package/src/lib/standards/verified-against.js +19 -191
- package/src/lib/tasks/eval-node.js +21 -169
- package/src/lib/tasks/linux-runner.js +18 -142
- package/src/lib/tasks/test-nodes.js +43 -240
- package/src/lib/tasks/test-runner.js +43 -341
- package/src/lib/telemetry/metrics.js +7 -36
- package/src/lib/telemetry/telemetry-reader.js +13 -125
- package/src/lib/telemetry/verify-event.js +2 -30
- package/src/lib/tests-audit/audit.js +96 -0
- 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 +2 -24
- 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 +11 -101
- package/src/lib/validators/reuse/symbol-scanner.js +6 -62
- package/src/lib/validators/traceability-check.js +5 -63
- package/src/lib/validators/validation-runner.js +21 -158
- package/src/lib/validators/verify-runner.js +85 -376
- package/src/lib/work-type-detector.js +5 -67
- package/src/lib/worktree/docker-audit.js +17 -136
- 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 +13 -95
- 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 -147
- package/src/utils/agents-installer.js +29 -276
- package/src/utils/banner.js +4 -25
- package/src/utils/claude-md-injector.js +1 -37
- package/src/utils/claude-settings-manager.js +1 -52
- package/src/utils/file-copier.js +11 -95
- package/src/utils/hooks-installer.js +21 -298
- package/src/utils/skills-installer.js +4 -101
- package/src/utils/version-checker.js +4 -83
- package/framework/commands/morph-proposal.md +0 -9
- package/framework/hooks/git/commit-msg/conventional-commits.sh +0 -33
- package/framework/hooks/git/pre-commit/agents.sh +0 -25
- package/framework/hooks/git/pre-commit/orchestrator.sh +0 -64
- package/framework/hooks/git/pre-push/run-tests.sh +0 -44
- package/src/commands/utils/index.js +0 -3
- package/src/lib/detectors/design-system-detector.js +0 -188
- package/src/lib/generators/design-system-generator.js +0 -508
- package/src/lib/specs/old-format.js +0 -68
- package/src/utils/agent-id-mapper.js +0 -11
package/src/commands/e2e/e2e.js
CHANGED
|
@@ -1,22 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
* The stack is deliberately NOT torn down between verifies: cold-starting a
|
|
5
|
-
* .NET container per task is the difference between a loop people run and one
|
|
6
|
-
* they disable. `up` brings it (or reuses it), `status` says whether what is
|
|
7
|
-
* running still matches the working tree, and `down` releases it — which
|
|
8
|
-
* `morph-spec finish` also does as part of teardown.
|
|
9
|
-
*
|
|
10
|
-
* Every subcommand acts in the TREE THAT OWNS THE FEATURE: run from the
|
|
11
|
-
* primary root for a feature that lives in a worktree, the compose file, the
|
|
12
|
-
* bind mount and the stamp are all the worktree's — the root's copy is a
|
|
13
|
-
* different checkout of different code.
|
|
14
|
-
*
|
|
15
|
-
* All the real logic lives in framework/hooks/shared/e2e-check.js, shared with
|
|
16
|
-
* the `verify` e2e node so the two can never drift.
|
|
17
|
-
*
|
|
18
|
-
* @module commands/e2e
|
|
19
|
-
*/
|
|
1
|
+
// The stack is deliberately NOT torn down between verifies: a cold .NET start per task is the
|
|
2
|
+
// difference between a loop people run and one they disable. Every subcommand acts in the tree
|
|
3
|
+
// that owns the feature.
|
|
20
4
|
|
|
21
5
|
import chalk from 'chalk';
|
|
22
6
|
import { execFileSync } from 'child_process';
|
|
@@ -46,22 +30,10 @@ import { createHeavyGate, HeavyBusyError } from '../../lib/heavy/gate.js';
|
|
|
46
30
|
import { afterHeavyRun } from '../../lib/heavy/cleanup.js';
|
|
47
31
|
import { readHeavyConfig } from '../../../framework/hooks/shared/heavy-config.js';
|
|
48
32
|
|
|
49
|
-
/** Exit code for a usage error, matching `verify`'s convention. */
|
|
50
33
|
const EXIT_USAGE = 2;
|
|
51
34
|
const EXIT_BUSY = 3;
|
|
52
35
|
|
|
53
|
-
//
|
|
54
|
-
// Shared context
|
|
55
|
-
// ============================================================================
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Everything the three subcommands need, resolved once — against the tree
|
|
59
|
-
* that owns the feature (its live worktree when it has one, the root
|
|
60
|
-
* otherwise).
|
|
61
|
-
*
|
|
62
|
-
* Returns null (after printing) when that tree has no compose file — a usage
|
|
63
|
-
* error here, unlike in the verify node where it is a graded skip.
|
|
64
|
-
*/
|
|
36
|
+
// No compose file is a usage error here, unlike in the verify node where it is a graded skip.
|
|
65
37
|
function resolveContext(featureName, { json = false } = {}) {
|
|
66
38
|
const root = resolvePrimaryRoot(process.cwd());
|
|
67
39
|
const found = findWorktreeForFeature(featureName, root);
|
|
@@ -95,49 +67,24 @@ function resolveContext(featureName, { json = false } = {}) {
|
|
|
95
67
|
};
|
|
96
68
|
}
|
|
97
69
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
* from the gitignored auth env file.
|
|
101
|
-
*
|
|
102
|
-
* The port block comes from `buildComposeEnv` rather than being assembled here
|
|
103
|
-
* so this command and the `e2e` node inside `verify` agree by construction —
|
|
104
|
-
* isolation that exists only in the command nobody runs is no isolation at all.
|
|
105
|
-
*/
|
|
70
|
+
// The port block comes from `buildComposeEnv`, so this command and the verify node agree by
|
|
71
|
+
// construction.
|
|
106
72
|
function buildEnv({ cwd, config, featureName, projectName, gitCommonDir }) {
|
|
107
|
-
//
|
|
108
|
-
// as the fail-open floor for a repo with no git common dir (no block → no
|
|
109
|
-
// layout), where the project name is still what isolates the containers.
|
|
73
|
+
// The explicit key is the floor for a repo with no git common dir (no port layout).
|
|
110
74
|
return {
|
|
111
75
|
COMPOSE_PROJECT_NAME: projectName,
|
|
112
76
|
...buildComposeEnv({ cwd, config, featureName, gitCommonDir }),
|
|
113
77
|
};
|
|
114
78
|
}
|
|
115
79
|
|
|
116
|
-
//
|
|
117
|
-
//
|
|
118
|
-
// ============================================================================
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* `morph-spec e2e up <feature> [--fresh]`
|
|
122
|
-
*
|
|
123
|
-
* `--fresh` is destructive on purpose: it tears the previous stack down WITH
|
|
124
|
-
* its named volumes and forces the auth seed to run again. A rebuild that
|
|
125
|
-
* preserved the database would hand the next suite the previous run's rows,
|
|
126
|
-
* and a storageState captured against that database would keep authenticating
|
|
127
|
-
* a user that no longer exists.
|
|
128
|
-
*
|
|
129
|
-
* Exit codes: 0 = stack healthy · 1 = failed to come up (or auth broke, or the
|
|
130
|
-
* compose cannot be isolated inside a worktree) · 2 = usage (no compose file /
|
|
131
|
-
* no docker).
|
|
132
|
-
*/
|
|
80
|
+
// `--fresh` drops the named volumes and reruns the auth seed: a preserved database would hand the
|
|
81
|
+
// next suite stale rows and a storageState for a user that no longer exists.
|
|
133
82
|
export async function e2eUpCommand(featureName, options = {}) {
|
|
134
83
|
const ctx = resolveContext(featureName, options);
|
|
135
84
|
if (!ctx) process.exit(EXIT_USAGE);
|
|
136
85
|
|
|
137
|
-
// Isolation BEFORE the docker check:
|
|
138
|
-
//
|
|
139
|
-
// names its containers) collides with the sibling worktree no matter what we
|
|
140
|
-
// inject. Same rule as the verify node — see decideIsolation().
|
|
86
|
+
// Isolation BEFORE the docker check: a compose that hardcodes host ports or container names
|
|
87
|
+
// collides with a sibling worktree whatever we inject.
|
|
141
88
|
const lint = lintComposeIsolation(readComposeText(ctx.cwd, ctx.composeFile));
|
|
142
89
|
const isolation = decideIsolation({ findings: lint.findings, insideWorktree: ctx.insideWorktree });
|
|
143
90
|
if (isolation.status === 'fail') {
|
|
@@ -189,8 +136,7 @@ export async function e2eUpCommand(featureName, options = {}) {
|
|
|
189
136
|
? prepareAuth(ctx.cwd, { config: ctx.config, env: ctx.env, forceSeed: Boolean(options.fresh) })
|
|
190
137
|
: { status: 'skip', reason: null };
|
|
191
138
|
|
|
192
|
-
//
|
|
193
|
-
// runs because `mode` switches it off is reported, never swallowed.
|
|
139
|
+
// A seedCommand that `mode` switches off is reported, never swallowed.
|
|
194
140
|
const warnings = [];
|
|
195
141
|
if (isolation.status === 'warn') warnings.push(isolation.reason);
|
|
196
142
|
if (auth.warning) warnings.push(auth.warning);
|
|
@@ -225,32 +171,13 @@ export async function e2eUpCommand(featureName, options = {}) {
|
|
|
225
171
|
process.exit(failed ? 1 : 0);
|
|
226
172
|
}
|
|
227
173
|
|
|
228
|
-
//
|
|
229
|
-
//
|
|
230
|
-
// ============================================================================
|
|
231
|
-
|
|
232
|
-
/**
|
|
233
|
-
* Where the app actually landed.
|
|
234
|
-
*
|
|
235
|
-
* The orchestrator hands this to the verifier instead of letting it guess
|
|
236
|
-
* `localhost:3000` — each feature owns a port block, so the default port is the
|
|
237
|
-
* one address most likely to be wrong. Mirrors what `playwright.config.ts` is
|
|
238
|
-
* asked to do, deliberately: one rule, two readers.
|
|
239
|
-
*
|
|
240
|
-
* @param {Record<string,string>} env
|
|
241
|
-
* @returns {string}
|
|
242
|
-
*/
|
|
174
|
+
// Handed to the verifier so it never guesses `localhost:3000`: each feature owns a port block.
|
|
175
|
+
// Mirrors what `playwright.config.ts` does: one rule, two readers.
|
|
243
176
|
export function resolveBaseUrl(env = {}) {
|
|
244
177
|
return env.PLAYWRIGHT_BASE_URL ?? `http://localhost:${env.PORT ?? 3000}`;
|
|
245
178
|
}
|
|
246
179
|
|
|
247
|
-
|
|
248
|
-
* `morph-spec e2e status <feature> [--json]`
|
|
249
|
-
*
|
|
250
|
-
* Answers the question that matters before trusting a green run: is what is
|
|
251
|
-
* running still built from the code on disk? Exit 0 when up AND fresh, 1
|
|
252
|
-
* otherwise — so it can gate a script.
|
|
253
|
-
*/
|
|
180
|
+
// Exit 0 only when up AND built from the code on disk, so it can gate a script.
|
|
254
181
|
export async function e2eStatusCommand(featureName, options = {}) {
|
|
255
182
|
const ctx = resolveContext(featureName, options);
|
|
256
183
|
if (!ctx) process.exit(EXIT_USAGE);
|
|
@@ -309,17 +236,8 @@ export async function e2eStatusCommand(featureName, options = {}) {
|
|
|
309
236
|
process.exit(status.up && status.healthy && fresh ? 0 : 1);
|
|
310
237
|
}
|
|
311
238
|
|
|
312
|
-
//
|
|
313
|
-
//
|
|
314
|
-
// ============================================================================
|
|
315
|
-
|
|
316
|
-
/**
|
|
317
|
-
* `morph-spec e2e down <feature> [--volumes]`
|
|
318
|
-
*
|
|
319
|
-
* Teardown is best-effort by design — a stack that will not come down must
|
|
320
|
-
* never block `finish`. The freshness stamp goes regardless: a stamp that
|
|
321
|
-
* outlives its containers would let the next `up` reuse a stack that is gone.
|
|
322
|
-
*/
|
|
239
|
+
// Best-effort: a stack that will not come down must never block `finish`. The freshness stamp
|
|
240
|
+
// goes regardless, or the next `up` would reuse a stack that is gone.
|
|
323
241
|
export async function e2eDownCommand(featureName, options = {}) {
|
|
324
242
|
const ctx = resolveContext(featureName, options);
|
|
325
243
|
if (!ctx) process.exit(EXIT_USAGE);
|
|
@@ -348,10 +266,6 @@ export async function e2eDownCommand(featureName, options = {}) {
|
|
|
348
266
|
process.exit(0);
|
|
349
267
|
}
|
|
350
268
|
|
|
351
|
-
// ============================================================================
|
|
352
|
-
// Shared helpers
|
|
353
|
-
// ============================================================================
|
|
354
|
-
|
|
355
269
|
function readConfig(cwd) {
|
|
356
270
|
try {
|
|
357
271
|
const path = join(cwd, '.morph', 'config', 'config.json');
|
|
@@ -370,7 +284,6 @@ function readComposeText(cwd, composeFile) {
|
|
|
370
284
|
}
|
|
371
285
|
}
|
|
372
286
|
|
|
373
|
-
/** Same resolution (and same fail-open) as verify-runner's. */
|
|
374
287
|
function resolveGitCommonDir(cwd) {
|
|
375
288
|
try {
|
|
376
289
|
const common = execFileSync('git', ['rev-parse', '--git-common-dir'], {
|
|
@@ -1,23 +1,9 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `morph-spec graph affected <símbolo>` — blast radius saneado.
|
|
3
|
-
*
|
|
4
|
-
* O valor medido não é recall: contra o grep, o grafo achou ZERO arquivos a
|
|
5
|
-
* mais. É precisão — 58% menos ruído em 12 símbolos do ProspectPRO (1.092
|
|
6
|
-
* arquivos no grep, 266 usos reais) e a classificação da relação. `IHandler`
|
|
7
|
-
* aparece em 193 arquivos no grep e tem 2 usos reais: é implementada por 98.
|
|
8
|
-
*
|
|
9
|
-
* @module graph-affected
|
|
10
|
-
*/
|
|
11
1
|
import chalk from 'chalk';
|
|
12
2
|
import { loadGraph, getFreshness, describeStale } from '../../lib/graph/graph-loader.js';
|
|
13
3
|
import { buildIndex, resolveLabel, affected } from '../../lib/graph/sanitize.js';
|
|
14
4
|
|
|
15
5
|
const FALLBACK_HINT = 'Sem resposta do grafo, caia para grep/Glob — o grafo é um subconjunto estrito do grep, então cair para grep não perde recall.';
|
|
16
6
|
|
|
17
|
-
/**
|
|
18
|
-
* @param {string} symbol
|
|
19
|
-
* @param {{path?: string, json?: boolean, prod?: boolean, usesOnly?: boolean}} [options]
|
|
20
|
-
*/
|
|
21
7
|
export async function graphAffectedCommand(symbol, options = {}) {
|
|
22
8
|
const projectPath = options.path || process.cwd();
|
|
23
9
|
const loaded = loadGraph(projectPath);
|
|
@@ -1,11 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `morph-spec graph build` (extração completa) e `graph refresh` (incremental).
|
|
3
|
-
*
|
|
4
|
-
* Fail-open: sem graphify no PATH, explica o que fazer e sai 1 — sem stack
|
|
5
|
-
* trace. Um projeto sem grafo continua funcionando por grep.
|
|
6
|
-
*
|
|
7
|
-
* @module graph-build
|
|
8
|
-
*/
|
|
9
1
|
import chalk from 'chalk';
|
|
10
2
|
import { readFileSync, existsSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
11
3
|
import { join } from 'node:path';
|
|
@@ -33,26 +25,9 @@ function readProjectTags(projectPath) {
|
|
|
33
25
|
}
|
|
34
26
|
}
|
|
35
27
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
* Na raiz: escrito ou migrado (writeGraphifyignore). Numa worktree o arquivo é
|
|
40
|
-
* do checkout e nunca é reescrito — seria uma mudança na branch da feature.
|
|
41
|
-
* Ausente ali (a raiz ainda não o versionou), as regras — as da raiz
|
|
42
|
-
* (`ignoreTextOf`), ou as geradas — entram só durante a rodada e saem depois
|
|
43
|
-
* (`cleanup`): um `?? .graphifyignore` deixado na worktree faz o `finish` e
|
|
44
|
-
* o `worktree remove --if-clean` recusarem com worktree-dirty (medido), e pôr
|
|
45
|
-
* o arquivo no exclude do common dir esconderia também o da raiz. O arquivo leva
|
|
46
|
-
* `TEMP_IGNORE_MARKER` na 1ª linha: se este processo morrer antes do
|
|
47
|
-
* `cleanup`, a sobra é reconhecida pelo conteúdo — a próxima rodada a reusa e o
|
|
48
|
-
* `finish` a apaga (`dropLeftoverIgnore`). O graphify não herda o da raiz: o
|
|
49
|
-
* ignore dele para na raiz de VCS mais próxima, que numa worktree é ela mesma
|
|
50
|
-
* (detect.py `_load_graphifyignore`).
|
|
51
|
-
*
|
|
52
|
-
* @param {string} projectPath
|
|
53
|
-
* @param {string[]} tags
|
|
54
|
-
* @returns {{written: boolean, migrated?: boolean, outdated?: object, temporary?: boolean, path: string, cleanup: () => void}}
|
|
55
|
-
*/
|
|
28
|
+
// Numa worktree o arquivo do checkout nunca é reescrito; ausente, as regras entram só durante
|
|
29
|
+
// a rodada (uma sobra suja a worktree e o `finish` recusa). `TEMP_IGNORE_MARKER` na 1ª linha
|
|
30
|
+
// deixa a próxima rodada e o `finish` reconhecerem a sobra de um processo morto.
|
|
56
31
|
function prepareIgnore(projectPath, tags) {
|
|
57
32
|
const none = () => {};
|
|
58
33
|
if (!isSecondaryWorktree(projectPath)) return { ...writeGraphifyignore(projectPath, tags), cleanup: none };
|
|
@@ -71,9 +46,6 @@ function prepareIgnore(projectPath, tags) {
|
|
|
71
46
|
};
|
|
72
47
|
}
|
|
73
48
|
|
|
74
|
-
/**
|
|
75
|
-
* @param {{path?: string, json?: boolean, refresh?: boolean}} [options]
|
|
76
|
-
*/
|
|
77
49
|
export async function graphBuildCommand(options = {}) {
|
|
78
50
|
const projectPath = options.path || process.cwd();
|
|
79
51
|
const refresh = options.refresh === true;
|
|
@@ -93,19 +65,14 @@ export async function graphBuildCommand(options = {}) {
|
|
|
93
65
|
process.exit(1);
|
|
94
66
|
}
|
|
95
67
|
|
|
96
|
-
// O ignore precisa existir ANTES da extração, senão o grafo nasce com o
|
|
97
|
-
// ruído que a ADR-004 existe para tirar.
|
|
68
|
+
// O ignore precisa existir ANTES da extração, senão o grafo nasce com o ruído.
|
|
98
69
|
const ignore = prepareIgnore(projectPath, readProjectTags(projectPath));
|
|
99
70
|
if (ignore.outdated && !options.json) {
|
|
100
71
|
console.error(chalk.yellow(`\n⚠ ${describeOutdatedIgnore(ignore.outdated)}`));
|
|
101
72
|
}
|
|
102
73
|
|
|
103
|
-
// Na
|
|
104
|
-
//
|
|
105
|
-
// disputam o arquivo e todos os núcleos. Numa worktree ninguém mais escreve
|
|
106
|
-
// o graph.json dela — o background só toca a raiz —, e esperar o lock da
|
|
107
|
-
// raiz ali prometeria "o grafo chega ao HEAD" sobre um refresh que nunca a
|
|
108
|
-
// alcança. Sem git não há lock, e o comando segue sozinho.
|
|
74
|
+
// Na raiz o refresh em background escreve o mesmo graph.json: dois graphify disputam o arquivo
|
|
75
|
+
// e os núcleos. Numa worktree só este comando escreve o grafo dela, e sem git não há lock.
|
|
109
76
|
const primary = isPrimaryTree(projectPath);
|
|
110
77
|
const lock = primary ? acquireRefreshLock(projectPath) : null;
|
|
111
78
|
const running = primary && !lock ? readRefreshLock(projectPath) : null;
|
|
@@ -127,24 +94,18 @@ export async function graphBuildCommand(options = {}) {
|
|
|
127
94
|
? ['update', projectPath]
|
|
128
95
|
: ['extract', projectPath, '--code-only'];
|
|
129
96
|
|
|
130
|
-
//
|
|
131
|
-
//
|
|
132
|
-
// código para indexar, e essas duas coisas não podem virar a mesma mensagem.
|
|
133
|
-
// HEAD e regras lidos ANTES: são o que o graphify leu.
|
|
97
|
+
// Output capturado: o graphify sai 1 tanto ao quebrar quanto sem código para indexar, e as duas
|
|
98
|
+
// coisas não podem virar a mesma mensagem. HEAD e regras lidos ANTES: são o que o graphify leu.
|
|
134
99
|
const head = readHead(projectPath);
|
|
135
100
|
const ignoreHash = ignoreRulesHash(projectPath);
|
|
136
101
|
let run;
|
|
137
102
|
let empty = false;
|
|
138
103
|
try {
|
|
139
104
|
run = runGraphify(projectPath, args, { timeout: null });
|
|
140
|
-
// Projeto sem código
|
|
141
|
-
// falha: o graphify diz "found 0 code" / "graph is empty", sai 1 e não
|
|
142
|
-
// escreve graph.json. Tratar isso como erro faria todo init greenfield
|
|
143
|
-
// terminar em vermelho.
|
|
105
|
+
// Projeto sem código (logo depois de um `init`) não é falha: o graphify sai 1 sem graph.json.
|
|
144
106
|
empty = run.status !== 0 && (/found 0 code/i.test(run.output) || /graph is empty/i.test(run.output));
|
|
145
|
-
//
|
|
146
|
-
//
|
|
147
|
-
// velho para sempre (framework/hooks/shared/graph-freshness.js).
|
|
107
|
+
// Sem o registro, um refresh que não mudou a topologia deixa o carimbo no commit antigo
|
|
108
|
+
// e o grafo parece velho para sempre.
|
|
148
109
|
if (!empty) {
|
|
149
110
|
recordRefreshRun(projectPath, {
|
|
150
111
|
head, exit: run.status ?? null, ignoreHash, unavailable: Boolean(run.unavailable),
|
|
@@ -1,25 +1,9 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `morph-spec graph explain <símbolo>` — vizinhança com citação arquivo:linha.
|
|
3
|
-
*
|
|
4
|
-
* É o comando preferido nas personas: no eval, `explain` foi consistentemente
|
|
5
|
-
* mais preciso que `query`, que trunca por budget e traz ruído.
|
|
6
|
-
*
|
|
7
|
-
* Cada aresta carrega a procedência (`via`), então dá para ver o que o
|
|
8
|
-
* saneamento recuperou — `coalesced` são exatamente as arestas que o graphify
|
|
9
|
-
* cru perde.
|
|
10
|
-
*
|
|
11
|
-
* @module graph-explain
|
|
12
|
-
*/
|
|
13
1
|
import chalk from 'chalk';
|
|
14
2
|
import { loadGraph, getFreshness, describeStale } from '../../lib/graph/graph-loader.js';
|
|
15
3
|
import { buildIndex, resolveLabel, neighborhood, isTestPath } from '../../lib/graph/sanitize.js';
|
|
16
4
|
|
|
17
5
|
const FALLBACK_HINT = 'Sem resposta do grafo, caia para grep/Glob.';
|
|
18
6
|
|
|
19
|
-
/**
|
|
20
|
-
* @param {string} symbol
|
|
21
|
-
* @param {{path?: string, json?: boolean}} [options]
|
|
22
|
-
*/
|
|
23
7
|
export async function graphExplainCommand(symbol, options = {}) {
|
|
24
8
|
const projectPath = options.path || process.cwd();
|
|
25
9
|
const loaded = loadGraph(projectPath);
|
|
@@ -1,12 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `morph-spec graph status` — existência, tamanho, frescor e saúde do
|
|
3
|
-
* saneamento.
|
|
4
|
-
*
|
|
5
|
-
* Ausência de grafo NÃO é erro (ADR-002): exit 0 com `available: false`. Um
|
|
6
|
-
* projeto sem grafo é um estado válido, e o framework segue por grep.
|
|
7
|
-
*
|
|
8
|
-
* @module graph-status
|
|
9
|
-
*/
|
|
10
1
|
import chalk from 'chalk';
|
|
11
2
|
import {
|
|
12
3
|
loadGraph, getFreshness, describeStale, refreshState, staleCause,
|
|
@@ -24,9 +15,7 @@ function summarizeSanitize(index) {
|
|
|
24
15
|
return counts;
|
|
25
16
|
}
|
|
26
17
|
|
|
27
|
-
|
|
28
|
-
* @param {{path?: string, json?: boolean}} [options]
|
|
29
|
-
*/
|
|
18
|
+
// Ausência de grafo não é erro: exit 0 com `available: false`, e o framework segue por grep.
|
|
30
19
|
export async function graphStatusCommand(options = {}) {
|
|
31
20
|
const projectPath = options.path || process.cwd();
|
|
32
21
|
const installed = detectGraphifyVersion();
|
|
@@ -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 {
|