@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
|
@@ -1,20 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `morph-spec env [feature] [--json|--posix|--powershell]` — print the
|
|
3
|
-
* dedicated environment of a worktree (port block + compose project) in a
|
|
4
|
-
* shell-loadable form.
|
|
5
|
-
*
|
|
6
|
-
* Inside Claude Code the `worktree-env-inject` hook applies this block to
|
|
7
|
-
* every dev-server / compose launcher automatically. This command is the same
|
|
8
|
-
* contract for everything OUTSIDE the harness: a human terminal
|
|
9
|
-
* (`eval "$(morph-spec env)"`), a CI step, a sub-agent running without hooks.
|
|
10
|
-
* It derives from `buildComposeEnv` — the function the `verify` e2e node and
|
|
11
|
-
* `e2e up` already use — so a shell and the stack can never disagree.
|
|
12
|
-
*
|
|
13
|
-
* Feature resolution: explicit argument, else the `morph/{f}` (or
|
|
14
|
-
* `morph-task/{n}`) branch of the cwd. Read-only: allocation is idempotent and
|
|
15
|
-
* never moves an existing block.
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
1
|
import { execFileSync } from 'child_process';
|
|
19
2
|
import { readFileSync } from 'fs';
|
|
20
3
|
import { join, resolve } from 'path';
|
|
@@ -49,14 +32,9 @@ function gitCommonDir(cwd) {
|
|
|
49
32
|
}
|
|
50
33
|
}
|
|
51
34
|
|
|
52
|
-
/** POSIX single-quote. */
|
|
53
35
|
const shq = (v) => `'${String(v).replace(/'/g, "'\\''")}'`;
|
|
54
36
|
|
|
55
|
-
/**
|
|
56
|
-
* Pure: the env map for a key, or `{ error }`.
|
|
57
|
-
* @param {string|null} featureArg
|
|
58
|
-
* @param {string} cwd
|
|
59
|
-
*/
|
|
37
|
+
/** The env map for a key, or `{ error }`. */
|
|
60
38
|
export function resolveWorktreeEnv(featureArg, cwd = process.cwd()) {
|
|
61
39
|
const key = featureArg || envKeyFromBranch(currentBranch(cwd));
|
|
62
40
|
if (!key) {
|
|
@@ -78,10 +56,6 @@ export function render(env, format) {
|
|
|
78
56
|
return entries.map(([k, v]) => `export ${k}=${shq(v)}`).join('\n');
|
|
79
57
|
}
|
|
80
58
|
|
|
81
|
-
/**
|
|
82
|
-
* @param {string|undefined} featureArg
|
|
83
|
-
* @param {{ json?: boolean, posix?: boolean, powershell?: boolean }} options
|
|
84
|
-
*/
|
|
85
59
|
export function envCommand(featureArg, options = {}) {
|
|
86
60
|
const result = resolveWorktreeEnv(featureArg || null, process.cwd());
|
|
87
61
|
if (result.error) {
|
|
@@ -1,22 +1,7 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `morph-spec fleet [--json]` — the fleet view.
|
|
3
|
-
*
|
|
4
|
-
* One line per active feature across the primary root and every morph/*
|
|
5
|
-
* worktree: phase, pending gates, slice progress, port block, handoff
|
|
6
|
-
* freshness, last commit and dirtiness. This is the terminal surface the
|
|
7
|
-
* operator scans between sessions ("what is each instance doing, what is
|
|
8
|
-
* waiting on me") — `worktree list` remains the lean per-worktree view.
|
|
9
|
-
*
|
|
10
|
-
* Read-only, always exits 0 (fail-open — an empty fleet is an answer).
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
1
|
import chalk from 'chalk';
|
|
14
2
|
import { scanFleet } from '../../lib/worktree/fleet-scanner.js';
|
|
15
3
|
import { claimAge } from '../../../framework/hooks/shared/claim-registry.js';
|
|
16
4
|
|
|
17
|
-
/**
|
|
18
|
-
* @param {{ json?: boolean, cwd?: string }} [options]
|
|
19
|
-
*/
|
|
20
5
|
export function fleetCommand(options = {}) {
|
|
21
6
|
const fleet = scanFleet(options.cwd || process.cwd());
|
|
22
7
|
|
|
@@ -32,22 +17,13 @@ export function fleetCommand(options = {}) {
|
|
|
32
17
|
|
|
33
18
|
const col = (s, w) => String(s ?? '-').padEnd(w);
|
|
34
19
|
|
|
35
|
-
/**
|
|
36
|
-
* The REMOTO cell. `-` means "unknown" (no origin, or the probe failed) —
|
|
37
|
-
* deliberately the same glyph as "nothing there", because both mean "do not
|
|
38
|
-
* trust the local view alone". `local` is the confident negative.
|
|
39
|
-
*/
|
|
20
|
+
/** `-` (unknown) shares the "nothing there" glyph on purpose: both mean "do not trust the local view alone". */
|
|
40
21
|
const remoteCell = (r) => {
|
|
41
22
|
if (!r) return '-';
|
|
42
23
|
if (r.pr) return `PR #${r.pr.number}${r.pr.state && r.pr.state !== 'OPEN' ? ` (${r.pr.state.toLowerCase()})` : ''}`;
|
|
43
24
|
return r.pushed ? 'pushed' : 'local';
|
|
44
25
|
};
|
|
45
26
|
|
|
46
|
-
/**
|
|
47
|
-
* The CLAIM cell — who is on this feature right now (B1). A stale claim is
|
|
48
|
-
* shown as stale, not hidden: "nobody here" and "somebody died here" are
|
|
49
|
-
* different facts for the operator.
|
|
50
|
-
*/
|
|
51
27
|
const claimCell = (c) => {
|
|
52
28
|
if (!c) return '-';
|
|
53
29
|
const age = claimAge(c);
|
|
@@ -55,10 +31,7 @@ export function fleetCommand(options = {}) {
|
|
|
55
31
|
};
|
|
56
32
|
|
|
57
33
|
console.log(chalk.white.bold(
|
|
58
|
-
// PHASE at 18
|
|
59
|
-
// fleet-scanner.js) is 16 chars — 1 wider than the col width `list.js`
|
|
60
|
-
// already uses (17) for the identical string. A narrower column here
|
|
61
|
-
// would misalign every column after it just for that one row.
|
|
34
|
+
// PHASE at 18 fits `aguardando-merge`; narrower misaligns every column after it.
|
|
62
35
|
col('FEATURE', 20) + col('TYPE', 9) + col('PHASE', 18) + col('FATIAS', 8)
|
|
63
36
|
+ col('GATES PEND.', 20) + col('WHERE', 24) + col('REMOTO', 16) + col('CLAIM', 16)
|
|
64
37
|
+ col('PORTS', 12) + col('HANDOFF', 9) + 'ATIVIDADE',
|
|
@@ -66,9 +39,7 @@ export function fleetCommand(options = {}) {
|
|
|
66
39
|
for (const e of fleet.entries) {
|
|
67
40
|
const slices = e.slices ? `${e.slices.done}/${e.slices.total}` : '-';
|
|
68
41
|
const gates = e.pendingGates.length ? e.pendingGates.join(',') : '-';
|
|
69
|
-
// pr-pending
|
|
70
|
-
// is the only place it exists, so show that instead of the misleading
|
|
71
|
-
// 'raiz' a location-blind check would print.
|
|
42
|
+
// pr-pending exists only on its branch, never in a worktree or the root.
|
|
72
43
|
const where = (e.location === 'worktree' || e.location === 'pr-pending') ? e.branch : 'raiz';
|
|
73
44
|
const ports = e.env ? `${e.env.range[0]}-${e.env.range[1]}` : '-';
|
|
74
45
|
const handoff = e.handoff?.exists ? 'sim' : '-';
|
|
@@ -37,25 +37,8 @@ import { resolvePrimaryRoot } from './worktree.js';
|
|
|
37
37
|
import { registerProject } from '../../lib/projects/registry.js';
|
|
38
38
|
|
|
39
39
|
/**
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
* A worktree's `.claude/` and `.morph/framework/` are junctions back to the
|
|
44
|
-
* primary root (see `worktree.js`'s `setupInfraLinks`), but `.morph/config/`,
|
|
45
|
-
* `.morph/context/`, `.morph/checkpoints/`, `.morph/memory/`, `.morph/archive/`
|
|
46
|
-
* and `.morph/state.json` are NOT linked — a full `init` there would scaffold
|
|
47
|
-
* a second, disconnected install (config, state) inside the worktree, orphaned
|
|
48
|
-
* from the primary root's real install.
|
|
49
|
-
*
|
|
50
|
-
* Fail-SAFE (not fail-open): blocks whenever `resolvePrimaryRoot(targetPath)`
|
|
51
|
-
* resolves to a path OTHER than `targetPath` itself. `resolvePrimaryRoot`
|
|
52
|
-
* itself fails open to `targetPath` outside a git repo (or on any git error),
|
|
53
|
-
* so non-git projects and the primary root itself are never blocked.
|
|
54
|
-
* `--force` is the explicit override, mirroring init's own `--force` flag.
|
|
55
|
-
*
|
|
56
|
-
* @param {string} targetPath - Effective install target (init's targetPath)
|
|
57
|
-
* @param {{ force?: boolean }} [options]
|
|
58
|
-
* @returns {{ blocked: boolean, root?: string, target?: string }}
|
|
40
|
+
* A linked worktree junctions `.claude/` and `.morph/framework/` but not `.morph/config`/state:
|
|
41
|
+
* a full install there orphans a second, disconnected one. `--force` overrides.
|
|
59
42
|
*/
|
|
60
43
|
export function checkSecondaryWorktreeGuard(targetPath, options = {}) {
|
|
61
44
|
if (options.force) return { blocked: false };
|
|
@@ -72,9 +55,6 @@ export async function initCommand(options) {
|
|
|
72
55
|
logger.dim(`Target: ${targetPath}`);
|
|
73
56
|
logger.blank();
|
|
74
57
|
|
|
75
|
-
// Guard: refuse a full install from inside a secondary worktree — it would
|
|
76
|
-
// corrupt the shared infra junctioned back to the primary root and scaffold
|
|
77
|
-
// a second, disconnected .morph/config + state.json. --force overrides.
|
|
78
58
|
const worktreeGuard = checkSecondaryWorktreeGuard(targetPath, options);
|
|
79
59
|
if (worktreeGuard.blocked) {
|
|
80
60
|
logger.warn(`Você está num worktree secundário (${worktreeGuard.target}).`);
|
|
@@ -83,11 +63,9 @@ export async function initCommand(options) {
|
|
|
83
63
|
process.exit(0);
|
|
84
64
|
}
|
|
85
65
|
|
|
86
|
-
// Check if already initialized
|
|
87
66
|
const morphPath = join(targetPath, '.morph');
|
|
88
67
|
if (await pathExists(morphPath)) {
|
|
89
68
|
if (!options.force) {
|
|
90
|
-
// Check if project version is outdated — auto-update instead of blocking
|
|
91
69
|
const projectCheck = await checkProjectOutdated(targetPath);
|
|
92
70
|
if (projectCheck.isOutdated) {
|
|
93
71
|
logger.info(`MORPH already initialized (v${projectCheck.current}). Framework update available (v${projectCheck.cliVersion}).`);
|
|
@@ -105,11 +83,8 @@ export async function initCommand(options) {
|
|
|
105
83
|
|
|
106
84
|
try {
|
|
107
85
|
|
|
108
|
-
// Steps 1-10: Infrastructure (headless, no prompts)
|
|
109
86
|
spinner.stop();
|
|
110
|
-
//
|
|
111
|
-
// ARE the primary root, or the caller explicitly passed --force), so it
|
|
112
|
-
// must not be redundantly re-applied inside setupInfra().
|
|
87
|
+
// The secondary-worktree guard already ran above; setupInfra must not re-apply it.
|
|
113
88
|
const infraResult = await setupInfra(targetPath, { force: true });
|
|
114
89
|
if (infraResult?.refused) {
|
|
115
90
|
process.exitCode = 1;
|
|
@@ -117,23 +92,18 @@ export async function initCommand(options) {
|
|
|
117
92
|
}
|
|
118
93
|
spinner.start('Continuing...');
|
|
119
94
|
|
|
120
|
-
// Derive display variables from what setupInfra installed
|
|
121
95
|
const cliVersion = getInstalledCLIVersion();
|
|
122
96
|
const commandsCopied = await pathExists(join(targetPath, '.claude', 'commands'));
|
|
123
97
|
const rulesCopied = await pathExists(join(targetPath, '.claude', 'rules'));
|
|
124
|
-
// Re-run installClaudeHooks to get the result object (idempotent — hooks already installed)
|
|
125
98
|
const hooksResult = await installClaudeHooks(targetPath);
|
|
126
99
|
|
|
127
|
-
// Re-derive config path for stack detection updates
|
|
128
100
|
const configDir = join(morphPath, 'config');
|
|
129
101
|
const configPath = join(configDir, 'config.json');
|
|
130
102
|
const contextDir = join(morphPath, 'context');
|
|
131
103
|
|
|
132
|
-
// Read config written by setupInfra so stack detection can update it
|
|
133
104
|
let config = (await pathExists(configPath)) ? await readJson(configPath) : { project: {} };
|
|
134
105
|
if (!config.project) config.project = {};
|
|
135
106
|
|
|
136
|
-
// 11b. Detect Claude Code environment (plugins, MCPs, skills)
|
|
137
107
|
spinner.text = 'Detecting Claude Code environment...';
|
|
138
108
|
let claudeConfigDetected = null;
|
|
139
109
|
let integrationsCreated = false;
|
|
@@ -152,14 +122,6 @@ export async function initCommand(options) {
|
|
|
152
122
|
logger.info(summary);
|
|
153
123
|
}
|
|
154
124
|
|
|
155
|
-
// Show MCP-to-phase mapping.
|
|
156
|
-
//
|
|
157
|
-
// This called mapMcpsToPhases with one argument where it takes three,
|
|
158
|
-
// so `phaseMatrix[currentPhase]` threw a TypeError on every project
|
|
159
|
-
// that had any MCP configured. The outer catch reported it as
|
|
160
|
-
// "Claude config detection skipped" — which reads like "nothing to
|
|
161
|
-
// detect" — and everything below it never ran: integrations.json was
|
|
162
|
-
// never written and the optimized settings were never generated.
|
|
163
125
|
if (claudeConfigDetected.mcpServers.length > 0) {
|
|
164
126
|
const byPhase = mcpAvailabilityByPhase(
|
|
165
127
|
claudeConfigDetected.mcpServers,
|
|
@@ -195,11 +157,9 @@ export async function initCommand(options) {
|
|
|
195
157
|
}
|
|
196
158
|
}
|
|
197
159
|
} catch (error) {
|
|
198
|
-
// Non-fatal: continue without integrations
|
|
199
160
|
logger.dim(` Claude config detection skipped: ${error.message}`);
|
|
200
161
|
}
|
|
201
162
|
|
|
202
|
-
// 11c. Stack detection (moved before MCP setup so we know the stack)
|
|
203
163
|
spinner.text = 'Detecting project stack...';
|
|
204
164
|
let detectedStack = 'unknown';
|
|
205
165
|
const hasProjectFiles = await pathExists(join(targetPath, 'package.json'))
|
|
@@ -214,7 +174,6 @@ export async function initCommand(options) {
|
|
|
214
174
|
if (structure?.stack && structure.stack !== 'unknown') {
|
|
215
175
|
detectedStack = structure.stack;
|
|
216
176
|
|
|
217
|
-
// Persist detected stack to config.json
|
|
218
177
|
if (structure?.stack && structure.stack !== 'unknown') {
|
|
219
178
|
config.project.stack = structure.stack;
|
|
220
179
|
}
|
|
@@ -256,23 +215,19 @@ export async function initCommand(options) {
|
|
|
256
215
|
logger.dim('Review and edit .morph/context/standards.md as needed.');
|
|
257
216
|
}
|
|
258
217
|
|
|
259
|
-
// Re-install stack-filtered assets now that we know the stack
|
|
260
218
|
const stackTags = getProjectTags(config);
|
|
261
219
|
if (stackTags.length > 0) {
|
|
262
220
|
spinner.start('Re-installing stack-filtered assets...');
|
|
263
221
|
const frameworkDir = join(targetPath, '.morph', 'framework');
|
|
264
222
|
const fwSrcDir = join(dirname(fileURLToPath(import.meta.url)), '..', '..', '..', 'framework');
|
|
265
223
|
|
|
266
|
-
// Rules
|
|
267
224
|
const rulesSrc = join(fwSrcDir, 'rules');
|
|
268
225
|
const rulesDest = join(targetPath, '.claude', 'rules');
|
|
269
226
|
installRulesForStack(rulesSrc, rulesDest, stackTags);
|
|
270
227
|
installCommandsForStack(join(fwSrcDir, 'commands'), join(targetPath, '.claude', 'commands'), stackTags);
|
|
271
228
|
|
|
272
|
-
// Skills
|
|
273
229
|
await installSkills(targetPath, { projectTags: stackTags });
|
|
274
230
|
|
|
275
|
-
// Agents (domain personas from agents.json)
|
|
276
231
|
await installAgents(targetPath, fwSrcDir, { projectTags: stackTags });
|
|
277
232
|
await installDomainAgents(targetPath, fwSrcDir, { projectTags: stackTags });
|
|
278
233
|
|
|
@@ -286,7 +241,6 @@ export async function initCommand(options) {
|
|
|
286
241
|
}
|
|
287
242
|
}
|
|
288
243
|
|
|
289
|
-
// 11d. MCP Auto-Setup
|
|
290
244
|
let mcpSetupResult = null;
|
|
291
245
|
if (!options.skipMcp) {
|
|
292
246
|
try {
|
|
@@ -304,7 +258,6 @@ export async function initCommand(options) {
|
|
|
304
258
|
logger.blank();
|
|
305
259
|
logger.header('MCP Server Setup');
|
|
306
260
|
|
|
307
|
-
// Auto-install prompt
|
|
308
261
|
let autoInstalled = {};
|
|
309
262
|
if (hasAutoMcps) {
|
|
310
263
|
const { installAuto } = await inquirer.prompt([{
|
|
@@ -322,7 +275,6 @@ export async function initCommand(options) {
|
|
|
322
275
|
autoInstalled[name] = orchestration.autoInstallable[name];
|
|
323
276
|
}
|
|
324
277
|
|
|
325
|
-
// Install companion skills for MCPs that define them (e.g., Stitch agent skills)
|
|
326
278
|
const skillResults = installCompanionSkills(orchestration.autoInstallable);
|
|
327
279
|
for (const r of skillResults) {
|
|
328
280
|
if (r.success) {
|
|
@@ -334,13 +286,11 @@ export async function initCommand(options) {
|
|
|
334
286
|
}
|
|
335
287
|
}
|
|
336
288
|
|
|
337
|
-
// Credential MCPs — inline setup or instructions
|
|
338
289
|
const manualInstalled = {};
|
|
339
290
|
if (hasManualMcps) {
|
|
340
291
|
for (const [name, entry] of Object.entries(orchestration.needsManualSetup)) {
|
|
341
292
|
logger.blank();
|
|
342
293
|
|
|
343
|
-
// Show warnings
|
|
344
294
|
for (const warning of entry.install.warnings || []) {
|
|
345
295
|
logger.warn(` ${warning}`);
|
|
346
296
|
}
|
|
@@ -354,7 +304,6 @@ export async function initCommand(options) {
|
|
|
354
304
|
}]);
|
|
355
305
|
|
|
356
306
|
if (setupNow) {
|
|
357
|
-
// Stitch — automated setup via setupStitch()
|
|
358
307
|
if (name === 'stitch' && !entry.install.autoInstall) {
|
|
359
308
|
spinner.start('Setting up Stitch MCP...');
|
|
360
309
|
const stitchResult = await setupStitch(targetPath);
|
|
@@ -374,7 +323,6 @@ export async function initCommand(options) {
|
|
|
374
323
|
continue;
|
|
375
324
|
}
|
|
376
325
|
|
|
377
|
-
// Remote MCPs — launch OAuth via claude mcp add
|
|
378
326
|
if (isRemoteMcp(entry.install.config)) {
|
|
379
327
|
const url = entry.install.config.url;
|
|
380
328
|
logger.info(` Launching ${capitalizedName} OAuth authentication...`);
|
|
@@ -391,7 +339,6 @@ export async function initCommand(options) {
|
|
|
391
339
|
continue;
|
|
392
340
|
}
|
|
393
341
|
|
|
394
|
-
// Collect credentials
|
|
395
342
|
const credentialValues = {};
|
|
396
343
|
for (const cred of entry.install.credentials) {
|
|
397
344
|
const { value } = await inquirer.prompt([{
|
|
@@ -408,7 +355,6 @@ export async function initCommand(options) {
|
|
|
408
355
|
spinner.succeed(`${capitalizedName} MCP configured`);
|
|
409
356
|
manualInstalled[name] = entry;
|
|
410
357
|
} else {
|
|
411
|
-
// Show setup instructions
|
|
412
358
|
const instructions = generateSetupInstructions(name, entry);
|
|
413
359
|
logger.blank();
|
|
414
360
|
logger.dim(' Add to .mcp.json → mcpServers:');
|
|
@@ -426,7 +372,6 @@ export async function initCommand(options) {
|
|
|
426
372
|
}
|
|
427
373
|
}
|
|
428
374
|
|
|
429
|
-
// Summary table
|
|
430
375
|
const justInstalled = { ...autoInstalled, ...manualInstalled };
|
|
431
376
|
const statusRows = formatMcpStatusTable(orchestration, justInstalled);
|
|
432
377
|
|
|
@@ -460,7 +405,6 @@ export async function initCommand(options) {
|
|
|
460
405
|
logger.dim('\nSkipped MCP setup (--skip-mcp flag)');
|
|
461
406
|
}
|
|
462
407
|
|
|
463
|
-
// Auto-generate architecture context (non-blocking on failure)
|
|
464
408
|
spinner.text = 'Generating architecture context...';
|
|
465
409
|
try {
|
|
466
410
|
const { scanArchitecture, renderArchitectureContext } = await import('../../lib/generators/architecture-scanner.js');
|
|
@@ -468,15 +412,12 @@ export async function initCommand(options) {
|
|
|
468
412
|
await renderArchitectureContext(targetPath, scanResult);
|
|
469
413
|
spinner.succeed('Architecture context generated');
|
|
470
414
|
} catch {
|
|
471
|
-
// Non-fatal: placeholder README.md remains
|
|
472
415
|
spinner.warn('Architecture context generation skipped');
|
|
473
416
|
}
|
|
474
417
|
|
|
475
418
|
spinner.succeed('MORPH-SPEC installed successfully!');
|
|
476
|
-
// The dashboard's portfolio learns about the project here (fail-open).
|
|
477
419
|
registerProject(targetPath, 'init');
|
|
478
420
|
|
|
479
|
-
// Show next steps
|
|
480
421
|
logger.blank();
|
|
481
422
|
logger.success(`MORPH-SPEC v${cliVersion} installed successfully!`);
|
|
482
423
|
logger.blank();
|
|
@@ -517,7 +458,6 @@ export async function initCommand(options) {
|
|
|
517
458
|
|
|
518
459
|
logger.dim(' ✓ .claude/skills/ (installed as skill directories)');
|
|
519
460
|
|
|
520
|
-
// Show agent team breakdown from setup-infra result
|
|
521
461
|
const agents = infraResult?.agents;
|
|
522
462
|
if (agents) {
|
|
523
463
|
const total = (agents.tier1 || 0) + (agents.tier2 || 0) + (agents.specialists || 0) + (agents.personas || 0);
|
|
@@ -528,7 +468,6 @@ export async function initCommand(options) {
|
|
|
528
468
|
|
|
529
469
|
logger.dim(' ✓ ~/.claude/statusline.sh (global statusline installed)');
|
|
530
470
|
|
|
531
|
-
// Check CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS in ~/.claude/settings.local.json
|
|
532
471
|
logger.blank();
|
|
533
472
|
const globalSettingsPath = join(homedir(), '.claude', 'settings.local.json');
|
|
534
473
|
let agentTeamsEnabled = false;
|
|
@@ -536,7 +475,6 @@ export async function initCommand(options) {
|
|
|
536
475
|
const globalSettings = await readJson(globalSettingsPath);
|
|
537
476
|
agentTeamsEnabled = globalSettings?.env?.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS === '1';
|
|
538
477
|
} catch {
|
|
539
|
-
// File missing or unreadable — treat as not enabled
|
|
540
478
|
}
|
|
541
479
|
|
|
542
480
|
if (agentTeamsEnabled) {
|
|
@@ -548,7 +486,6 @@ export async function initCommand(options) {
|
|
|
548
486
|
|
|
549
487
|
logger.blank();
|
|
550
488
|
|
|
551
|
-
// Populate Claude Code persistent memory so future sessions enter contextualized
|
|
552
489
|
try {
|
|
553
490
|
const { installProjectMemory } = await import('../../lib/installers/memory-installer.js');
|
|
554
491
|
const memResult = await installProjectMemory(targetPath, {
|
|
@@ -561,10 +498,9 @@ export async function initCommand(options) {
|
|
|
561
498
|
logger.dim(` ⚠ Memory install skipped: ${err.message}`);
|
|
562
499
|
}
|
|
563
500
|
|
|
564
|
-
// Suggest tutorial for first-time users
|
|
565
501
|
if (integrationsCreated) {
|
|
566
502
|
logger.blank();
|
|
567
|
-
logger.info('First time using morph-spec? Run `/morph-
|
|
503
|
+
logger.info('First time using morph-spec? Run `/morph-plan <feature>` to start a new feature.');
|
|
568
504
|
}
|
|
569
505
|
|
|
570
506
|
logger.blank();
|
|
@@ -1,30 +1,10 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `morph-spec standards [--find "<query>"] [--show <id>[#<anchor>]] [--category <c>] [--tags <a,b>] [--json]`
|
|
3
|
-
*
|
|
4
|
-
* `--find` scores the registry server-side and returns the TOP 3 hits with
|
|
5
|
-
* digest and anchor keys, so nobody reads the whole registry to pick a
|
|
6
|
-
* standard. `--show <id>#<anchor>` prints only that section of the .md;
|
|
7
|
-
* `--show <id>` prints the digest and the anchors. Bare call: categories + counts.
|
|
8
|
-
*
|
|
9
|
-
* A query with no hit exits 0 (advisory, not a gate); under `--json` the
|
|
10
|
-
* shape stays `{ query, results: [], suggestion }`.
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
1
|
import { existsSync, readFileSync } from 'fs';
|
|
14
2
|
import { dirname, join } from 'path';
|
|
15
3
|
import chalk from 'chalk';
|
|
16
4
|
import { loadStandardsRegistry } from '../../lib/standards/registry.js';
|
|
17
5
|
import { markdownHeadings, sectionBySlug } from '../../lib/standards/sections.js';
|
|
18
6
|
|
|
19
|
-
/**
|
|
20
|
-
* Resolve STANDARDS.json the same way doctor.js resolves the standards root:
|
|
21
|
-
* an installed project first (`.morph/framework/standards/`), falling back
|
|
22
|
-
* to this package's own bundled copy (`framework/standards/`) — the framework
|
|
23
|
-
* repo in dev, or the npm package's own copy before `setup-infra` has run.
|
|
24
|
-
*
|
|
25
|
-
* @param {string} targetPath - project root to look for `.morph/` under
|
|
26
|
-
* @returns {string|null} absolute path to STANDARDS.json, or null if neither exists
|
|
27
|
-
*/
|
|
7
|
+
/** Installed project first, else this package's bundled copy (dev repo, or before `setup-infra` ran). */
|
|
28
8
|
export function resolveStandardsJsonPath(targetPath) {
|
|
29
9
|
const installed = join(targetPath, '.morph', 'framework', 'standards', 'STANDARDS.json');
|
|
30
10
|
if (existsSync(installed)) return installed;
|
|
@@ -35,13 +15,6 @@ export function resolveStandardsJsonPath(targetPath) {
|
|
|
35
15
|
return null;
|
|
36
16
|
}
|
|
37
17
|
|
|
38
|
-
/**
|
|
39
|
-
* The one section a `--show` target names. `anchor` is an anchor key of the
|
|
40
|
-
* registry entry (`regra`) or any heading slug of the .md (`#regra-principal`).
|
|
41
|
-
* @returns {{ status: 'ok', section: string, slug: string }
|
|
42
|
-
* | { status: 'unknown-anchor', anchors: Record<string, string>, headings: string[] }
|
|
43
|
-
* | { status: 'unreadable' }}
|
|
44
|
-
*/
|
|
45
18
|
export function resolveStandardSection(standard, mdText, anchor) {
|
|
46
19
|
if (mdText === null) return { status: 'unreadable' };
|
|
47
20
|
const anchors = standard.anchors || {};
|
|
@@ -98,11 +71,6 @@ function showStandard(standards, standardsJsonPath, target, json) {
|
|
|
98
71
|
process.exit(1);
|
|
99
72
|
}
|
|
100
73
|
|
|
101
|
-
/**
|
|
102
|
-
* category → count, sorted by count desc then name asc.
|
|
103
|
-
* @param {object[]} standards
|
|
104
|
-
* @returns {{ category: string, count: number }[]}
|
|
105
|
-
*/
|
|
106
74
|
export function categoryCounts(standards) {
|
|
107
75
|
const counts = {};
|
|
108
76
|
for (const std of standards) {
|
|
@@ -114,16 +82,6 @@ export function categoryCounts(standards) {
|
|
|
114
82
|
.sort((a, b) => b.count - a.count || a.category.localeCompare(b.category));
|
|
115
83
|
}
|
|
116
84
|
|
|
117
|
-
/**
|
|
118
|
-
* Simple weighted relevance score for one standard against a query.
|
|
119
|
-
* Tiers, as specified: name/id > tags(+aliases) > digest. category/subcategory
|
|
120
|
-
* ride alongside the tags tier (lighter). A token-level fallback keeps
|
|
121
|
-
* multi-word queries findable even without a verbatim phrase match anywhere.
|
|
122
|
-
*
|
|
123
|
-
* @param {object} std - one STANDARDS.json entry
|
|
124
|
-
* @param {string} query - raw user query (already trimmed, non-empty)
|
|
125
|
-
* @returns {number} score, 0 = no match
|
|
126
|
-
*/
|
|
127
85
|
export function scoreStandard(std, query) {
|
|
128
86
|
const q = query.toLowerCase();
|
|
129
87
|
const tokens = q.split(/\s+/).filter((t) => t.length >= 3);
|
|
@@ -138,19 +96,15 @@ export function scoreStandard(std, query) {
|
|
|
138
96
|
|
|
139
97
|
let score = 0;
|
|
140
98
|
|
|
141
|
-
// Tier 1: name / id — the strongest signal.
|
|
142
99
|
if (id === q || name === q) score += 20;
|
|
143
100
|
else if (id.includes(q) || name.includes(q)) score += 12;
|
|
144
101
|
|
|
145
|
-
// Tier 2: tags / aliases (curated search terms) + category/subcategory.
|
|
146
102
|
if (tags.includes(q) || aliases.includes(q)) score += 8;
|
|
147
103
|
else if (tags.some((t) => t.includes(q)) || aliases.some((a) => a.includes(q))) score += 5;
|
|
148
104
|
if (category === q || subcategory === q) score += 4;
|
|
149
105
|
|
|
150
|
-
// Tier 3: digest — weakest signal, full-phrase substring only.
|
|
151
106
|
if (digest.includes(q)) score += 2;
|
|
152
107
|
|
|
153
|
-
// Token-level fallback for multi-word queries with no verbatim match above.
|
|
154
108
|
for (const tok of tokens) {
|
|
155
109
|
if (name.includes(tok) || id.includes(tok)) score += 2;
|
|
156
110
|
if (tags.some((t) => t.includes(tok)) || aliases.some((a) => a.includes(tok))) score += 1;
|
|
@@ -160,15 +114,6 @@ export function scoreStandard(std, query) {
|
|
|
160
114
|
return score;
|
|
161
115
|
}
|
|
162
116
|
|
|
163
|
-
/**
|
|
164
|
-
* Rank standards against a query, applying optional category/tags filters
|
|
165
|
-
* first. Returns the TOP 3 with score > 0, sorted desc (ties broken by id).
|
|
166
|
-
*
|
|
167
|
-
* @param {object[]} standards
|
|
168
|
-
* @param {string} query
|
|
169
|
-
* @param {{ category?: string, tags?: string[] }} [filters]
|
|
170
|
-
* @returns {Array<object & { score: number }>}
|
|
171
|
-
*/
|
|
172
117
|
export function findStandards(standards, query, filters = {}) {
|
|
173
118
|
const category = filters.category?.toLowerCase();
|
|
174
119
|
const tags = (filters.tags || []).map((t) => t.toLowerCase());
|
|
@@ -189,15 +134,11 @@ export function findStandards(standards, query, filters = {}) {
|
|
|
189
134
|
.slice(0, 3);
|
|
190
135
|
}
|
|
191
136
|
|
|
192
|
-
/** Parse `--tags a,b,c` into a trimmed, non-empty array. */
|
|
193
137
|
function parseTagsOption(raw) {
|
|
194
138
|
if (!raw) return [];
|
|
195
139
|
return String(raw).split(',').map((t) => t.trim()).filter(Boolean);
|
|
196
140
|
}
|
|
197
141
|
|
|
198
|
-
/**
|
|
199
|
-
* @param {{ find?: string, show?: string, category?: string, tags?: string, json?: boolean, cwd?: string }} [options]
|
|
200
|
-
*/
|
|
201
142
|
export function standardsCommand(options = {}) {
|
|
202
143
|
const cwd = options.cwd || process.cwd();
|
|
203
144
|
const standardsJsonPath = resolveStandardsJsonPath(cwd);
|
|
@@ -220,7 +161,6 @@ export function standardsCommand(options = {}) {
|
|
|
220
161
|
if (options.show) showStandard(standards, standardsJsonPath, options.show, options.json);
|
|
221
162
|
const query = (options.find || '').trim();
|
|
222
163
|
|
|
223
|
-
// ── Bare call (no --find): categories + counts, cheap orientation ────────
|
|
224
164
|
if (!query) {
|
|
225
165
|
const counts = categoryCounts(standards);
|
|
226
166
|
if (options.json) {
|
|
@@ -236,7 +176,6 @@ export function standardsCommand(options = {}) {
|
|
|
236
176
|
process.exit(0);
|
|
237
177
|
}
|
|
238
178
|
|
|
239
|
-
// ── --find: score + filter ────────────────────────────────────────────
|
|
240
179
|
const filters = { category: options.category, tags: parseTagsOption(options.tags) };
|
|
241
180
|
const results = findStandards(standards, query, filters);
|
|
242
181
|
|