@polymorphism-tech/morph-spec 8.47.0 → 9.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +51 -43
- package/README.md +39 -38
- package/bin/morph-spec.js +41 -113
- package/bin/validate.js +1 -147
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +1 -1
- package/docs/QUICKSTART.md +1 -1
- package/framework/CLAUDE.md +41 -101
- package/framework/MORPH.md +95 -252
- package/framework/agents.json +86 -21
- package/framework/commands/morph-apply.md +5 -28
- package/framework/commands/morph-archive.md +1 -1
- package/framework/commands/morph-hotfix.md +8 -8
- package/framework/commands/morph-infra.md +1 -1
- package/framework/commands/morph-preflight.md +2 -3
- package/framework/commands/morph-status.md +4 -4
- package/framework/docs/CLI.md +574 -286
- package/framework/evals/review-checklist.md +82 -41
- package/framework/hooks/README.md +10 -11
- package/framework/hooks/claude-code/core/block-destructive-worktree-removal.js +6 -49
- package/framework/hooks/claude-code/core/block-raw-worktree-add.js +2 -35
- package/framework/hooks/claude-code/core/chain.js +4 -65
- package/framework/hooks/claude-code/core/comment-guard.js +3 -17
- package/framework/hooks/claude-code/core/denylist.json +33 -3
- package/framework/hooks/claude-code/core/enforce-phase-writes.js +15 -63
- package/framework/hooks/claude-code/core/gate-guard.js +9 -78
- package/framework/hooks/claude-code/core/graph-refresh-kick.js +3 -24
- package/framework/hooks/claude-code/core/heavy-gate.js +3 -21
- package/framework/hooks/claude-code/core/heavy-release.js +2 -13
- package/framework/hooks/claude-code/core/loop-detect.js +2 -31
- package/framework/hooks/claude-code/core/pre-bash-denylist.js +36 -2
- package/framework/hooks/claude-code/core/protect-spec-files.js +25 -297
- package/framework/hooks/claude-code/core/state-sync.js +15 -85
- package/framework/hooks/claude-code/core/telemetry-log.js +15 -96
- package/framework/hooks/claude-code/core/validate-completion.js +37 -147
- package/framework/hooks/claude-code/core/warn-bash-source-write.js +23 -0
- package/framework/hooks/claude-code/core/worktree-context.js +15 -114
- package/framework/hooks/claude-code/core/worktree-create.js +6 -70
- package/framework/hooks/claude-code/core/worktree-env-inject.js +5 -56
- package/framework/hooks/claude-code/core/worktree-remove.js +5 -31
- package/framework/hooks/claude-code/statusline.py +55 -41
- package/framework/hooks/package.json +3 -0
- package/framework/hooks/shared/activity-logger.js +9 -89
- package/framework/hooks/shared/agent-reap.js +2 -17
- package/framework/hooks/shared/build-check.js +11 -76
- package/framework/hooks/shared/claim-registry.js +9 -119
- package/framework/hooks/shared/command-tokens.js +168 -44
- package/framework/hooks/shared/comment-rules.js +5 -20
- package/framework/hooks/shared/comment-scanner.js +11 -43
- package/framework/hooks/shared/comment-stripper.js +11 -85
- package/framework/hooks/shared/e2e-check.js +0 -0
- package/framework/hooks/shared/errata.js +59 -0
- package/framework/hooks/shared/feature-locator.js +11 -95
- package/framework/hooks/shared/frontend-locations.js +4 -0
- package/framework/hooks/shared/gate-question.js +12 -0
- package/framework/hooks/shared/git-exclude.js +3 -20
- package/framework/hooks/shared/git-paths.js +8 -104
- package/framework/hooks/shared/graph-freshness.js +22 -166
- package/framework/hooks/shared/graph-refresh.js +21 -149
- package/framework/hooks/shared/heavy-command.js +4 -12
- package/framework/hooks/shared/hook-response.js +8 -83
- package/framework/hooks/shared/morph-cli.js +8 -52
- package/framework/hooks/shared/payload-utils.js +10 -132
- package/framework/hooks/shared/phase-utils.js +14 -72
- package/framework/hooks/shared/redact-secrets.js +4 -15
- package/framework/hooks/shared/reparse-scan.js +6 -66
- package/framework/hooks/shared/slice-progress.js +99 -0
- package/framework/hooks/shared/source-files.js +4 -17
- package/framework/hooks/shared/spawn-command.js +23 -0
- package/framework/hooks/shared/state-reader.js +51 -247
- package/framework/hooks/shared/state-writer.js +9 -54
- package/framework/hooks/shared/stdin-reader.js +1 -11
- package/framework/hooks/shared/telemetry-logger.js +7 -67
- package/framework/hooks/shared/transcript-usage.js +7 -29
- package/framework/hooks/shared/worktree-root.js +7 -29
- package/framework/mcp/registry.json +3 -4
- package/framework/phases.json +23 -39
- package/framework/rules/csharp-standards.md +1 -1
- package/framework/rules/frontend-standards.md +1 -0
- package/framework/rules/hangfire-standards.md +1 -1
- package/framework/rules/neon-standards.md +1 -1
- package/framework/schemas/config.schema.json +14 -9
- package/framework/schemas/feature.schema.json +15 -50
- package/framework/skills/README.md +3 -4
- package/framework/skills/core/create-umbraco-lp/SKILL.md +1 -1
- package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +1 -1
- package/framework/skills/core/morph-apply/SKILL.md +149 -222
- package/framework/skills/core/morph-hotfix/SKILL.md +33 -42
- package/framework/skills/core/morph-machine/SKILL.md +2 -2
- package/framework/skills/core/morph-media/SKILL.md +1 -1
- package/framework/skills/core/morph-plan/SKILL.md +137 -299
- package/framework/skills/core/morph-review/SKILL.md +111 -136
- package/framework/skills/core/morph-standards/SKILL.md +27 -39
- package/framework/skills/core/morph-uiux/SKILL.md +94 -152
- package/framework/standards/STANDARDS.json +109 -1219
- package/framework/standards/STANDARDS.meta.json +1319 -0
- package/framework/standards/ai-agents/agent-archetypes.md +2 -2
- package/framework/standards/ai-agents/agent-session.md +1 -1
- package/framework/standards/ai-agents/conversational-agent-with-phases.md +4 -4
- package/framework/standards/ai-agents/durable-workflows-hitl.md +3 -3
- package/framework/standards/ai-agents/evals-with-cache.md +1 -2
- package/framework/standards/ai-agents/guardrails.md +1 -1
- package/framework/standards/ai-agents/multi-agent-patterns.md +2 -2
- package/framework/standards/ai-agents/observability-patterns.md +1 -1
- package/framework/standards/ai-agents/production.md +1 -1
- package/framework/standards/ai-agents/prompt-sources.md +2 -2
- package/framework/standards/ai-agents/providers/model-registry.md +1 -1
- package/framework/standards/ai-agents/rag-custom-pgvector.md +2 -2
- package/framework/standards/ai-agents/setup.md +3 -3
- package/framework/standards/ai-agents/sweet-spot.md +2 -2
- package/framework/standards/ai-agents/tool-adapters.md +0 -2
- package/framework/standards/ai-agents/vector-data-extensions.md +4 -4
- package/framework/standards/ai-agents/workflows.md +1 -1
- package/framework/standards/architecture/code-comments.md +7 -7
- package/framework/standards/architecture/vertical-slice/vertical-slice.md +1 -1
- package/framework/standards/backend/api/validation.md +1 -1
- package/framework/standards/backend/dotnet/vsa-handler-patterns.md +5 -5
- package/framework/standards/backend/integrations/hangfire/hangfire-jobs.md +1 -1
- package/framework/standards/backend/integrations/neon-auth/neon-auth.md +2 -2
- package/framework/standards/data/nosql/cache/redis.md +1 -1
- package/framework/standards/data/nosql/cosmos-db.md +3 -3
- package/framework/standards/frontend/admin-app/forms.md +1 -1
- package/framework/standards/frontend/design-system/art-direction.md +88 -0
- package/framework/standards/frontend/design-system/premium-finish.md +77 -2
- package/framework/standards/infrastructure/neon/neon-pgvector.md +5 -5
- package/framework/standards/integration/mcp/mcp-tools.md +7 -7
- package/framework/templates/CLAUDE.md.core.template +3 -3
- package/framework/templates/README.md +2 -3
- package/framework/templates/REGISTRY.json +15 -179
- package/framework/templates/docs/plan.md +72 -0
- package/framework/templates/docs/ui-mockups.md +4 -0
- package/framework/templates/dotnet/ai-kit/README.md +1 -9
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Morph.AiKit.Tests.csproj +0 -10
- package/framework/templates/meta-prompts/squad-leaders/backend-squad.md +1 -1
- package/framework/templates/meta-prompts/squad-leaders/frontend-squad.md +1 -1
- package/framework/templates/meta-prompts/squad-leaders/squad-leader.md +1 -1
- package/framework/templates/meta-prompts/validators/pre-commit-validator.md +3 -4
- package/package.json +1 -1
- package/src/commands/context/context-refresh.js +0 -16
- package/src/commands/dashboard/dashboard.js +2 -8
- package/src/commands/e2e/e2e.js +18 -105
- package/src/commands/graph/affected.js +0 -14
- package/src/commands/graph/build.js +11 -50
- package/src/commands/graph/explain.js +0 -16
- package/src/commands/graph/status.js +1 -12
- package/src/commands/mcp/mcp-setup.js +4 -62
- package/src/commands/observability/cost.js +17 -111
- package/src/commands/observability/telemetry.js +1 -14
- package/src/commands/project/branch.js +4 -45
- package/src/commands/project/claim.js +4 -48
- package/src/commands/project/doctor-procs.js +1 -1
- package/src/commands/project/doctor.js +53 -307
- package/src/commands/project/env.js +1 -27
- package/src/commands/project/fleet.js +6 -35
- package/src/commands/project/heavy.js +3 -3
- package/src/commands/project/init.js +8 -68
- package/src/commands/project/install-plugin-cmd.js +0 -1
- package/src/commands/project/setup-infra-cmd.js +2 -1
- package/src/commands/project/stack-scan.js +58 -0
- package/src/commands/project/standards.js +65 -72
- package/src/commands/project/status.js +28 -121
- package/src/commands/project/update.js +31 -223
- package/src/commands/project/worktree.js +109 -883
- package/src/commands/scaffold/scaffold.js +0 -25
- package/src/commands/state/advance.js +65 -120
- package/src/commands/state/approve.js +87 -229
- package/src/commands/state/archive.js +23 -139
- package/src/commands/state/auto-approval.js +65 -0
- package/src/commands/state/create.js +49 -109
- package/src/commands/state/delete.js +11 -63
- package/src/commands/state/finish.js +105 -494
- package/src/commands/state/gate-check.js +74 -183
- package/src/commands/state/gate-decision.js +8 -42
- package/src/commands/state/handoff.js +4 -31
- package/src/commands/state/list.js +21 -91
- package/src/commands/state/report-check.js +134 -338
- package/src/commands/state/retype.js +12 -28
- package/src/commands/validation/index.js +0 -3
- package/src/commands/validation/mutate.js +79 -19
- package/src/commands/validation/plan-check.js +81 -0
- package/src/commands/validation/validate-feature.js +52 -216
- package/src/commands/validation/verify.js +45 -160
- package/src/core/paths/output-schema.js +9 -155
- package/src/core/state/feature-identity.js +1 -25
- package/src/core/state/gate-order.js +2 -30
- package/src/core/state/lifecycle-audit.js +8 -100
- package/src/core/state/state-manager.js +32 -395
- package/src/core/templates/template-registry.js +2 -97
- package/src/core/templates/template-renderer.js +13 -93
- package/src/core/templates/template-seed.js +21 -0
- package/src/core/work-types.js +10 -45
- package/src/dashboard/app/App.jsx +1 -8
- package/src/dashboard/app/components/ActivityFeed.jsx +2 -8
- package/src/dashboard/app/components/CommandPalette.jsx +4 -11
- package/src/dashboard/app/components/DetailPanel.jsx +1 -5
- package/src/dashboard/app/components/EntityLink.jsx +0 -12
- package/src/dashboard/app/components/ErrorBoundary.jsx +1 -8
- package/src/dashboard/app/components/KnowledgeMarkdown.jsx +0 -11
- package/src/dashboard/app/components/Markdown.jsx +0 -9
- package/src/dashboard/app/components/PhaseRail.jsx +0 -11
- package/src/dashboard/app/components/SearchFilter.jsx +0 -4
- package/src/dashboard/app/components/Sidebar.jsx +0 -5
- package/src/dashboard/app/hooks/useMorphQuery.js +1 -11
- package/src/dashboard/app/hooks/usePref.js +1 -5
- package/src/dashboard/app/lib/format.js +0 -2
- package/src/dashboard/app/lib/phases.js +3 -9
- package/src/dashboard/app/lib/project.js +0 -12
- package/src/dashboard/app/pages/Activity.jsx +1 -1
- package/src/dashboard/app/pages/AgentMap.jsx +1 -8
- package/src/dashboard/app/pages/FeatureDetail.jsx +139 -133
- package/src/dashboard/app/pages/Features.jsx +9 -16
- package/src/dashboard/app/pages/Guide.jsx +2 -3
- package/src/dashboard/app/pages/HookInspector.jsx +2 -23
- package/src/dashboard/app/pages/PhasePipeline.jsx +3 -7
- package/src/dashboard/app/pages/Portfolio.jsx +0 -1
- package/src/dashboard/app/pages/RulesMap.jsx +1 -16
- package/src/dashboard/app/pages/SkillsLibrary.jsx +1 -16
- package/src/dashboard/app/pages/Standards.jsx +2 -17
- package/src/dashboard/scanner/activity-scanner.js +14 -83
- package/src/dashboard/scanner/agents-scanner.js +1 -23
- package/src/dashboard/scanner/docs-scanner.js +1 -7
- package/src/dashboard/scanner/feature-status.js +2 -5
- package/src/dashboard/scanner/features-scanner.js +92 -113
- package/src/dashboard/scanner/hooks-scanner.js +5 -28
- package/src/dashboard/scanner/index.js +0 -4
- package/src/dashboard/scanner/portfolio.js +5 -29
- package/src/dashboard/scanner/project-api.js +8 -54
- package/src/dashboard/scanner/rules-scanner.js +1 -2
- package/src/dashboard/scanner/skills-scanner.js +2 -12
- package/src/dashboard/scanner/standards-scanner.js +4 -7
- package/src/dashboard/vite-plugin-morph-data.js +5 -19
- package/src/lib/ai/ai-checks.js +7 -52
- package/src/lib/ai/ai-project-scan.js +14 -108
- package/src/lib/ai/code-audit.js +7 -63
- package/src/lib/ai/pin-drift.js +8 -51
- package/src/lib/ai/registry-audit.js +11 -90
- package/src/lib/ci/ci-verify.js +14 -13
- package/src/lib/ci/github-ci.js +9 -37
- package/src/lib/ci/workflow-scaffold.js +2 -10
- package/src/lib/detectors/claude-config-detector.js +13 -74
- package/src/lib/detectors/index.js +8 -59
- package/src/lib/e2e/compose-env.js +5 -66
- package/src/lib/e2e/stack-teardown.js +6 -40
- package/src/lib/generators/architecture-scanner.js +18 -156
- package/src/lib/generators/handoff-generator.js +37 -112
- package/src/lib/generators/scaffold-generator.js +5 -101
- package/src/lib/generators/settings-generator.js +11 -122
- package/src/lib/git/branch-classifier.js +11 -84
- package/src/lib/git/default-branch.js +14 -169
- package/src/lib/git/evidence-staleness.js +2 -1
- package/src/lib/git/gh.js +2 -17
- package/src/lib/git/pending-prs.js +6 -37
- package/src/lib/git/pr-files.js +3 -28
- package/src/lib/git/pr-index.js +3 -34
- package/src/lib/git/tree-fingerprint.js +17 -17
- package/src/lib/git/tree-state.js +6 -33
- package/src/lib/graph/god-nodes.js +2 -22
- package/src/lib/graph/graph-health.js +3 -24
- package/src/lib/graph/graph-loader.js +5 -56
- package/src/lib/graph/graphify-runtime.js +7 -37
- package/src/lib/graph/graphifyignore.js +8 -74
- package/src/lib/graph/sanitize.js +16 -136
- package/src/lib/heavy/cleanup.js +2 -6
- package/src/lib/installers/effects.js +8 -93
- package/src/lib/installers/mcp-installer.js +9 -169
- package/src/lib/installers/memory-installer.js +8 -33
- package/src/lib/installers/templates-reconcile.js +5 -60
- package/src/lib/mutation/commands.js +3 -2
- package/src/lib/mutation/dotnet-introspection.js +281 -0
- package/src/lib/mutation/plan.js +14 -6
- package/src/lib/mutation/runner.js +1 -1
- package/src/lib/mutation/subset.js +47 -13
- package/src/lib/plan/artifact-vocabulary.js +40 -0
- package/src/lib/plan/checks.js +208 -0
- package/src/lib/plan/plan-parser.js +204 -0
- package/src/lib/plan/plan-template.js +18 -0
- package/src/lib/projects/registry.js +7 -44
- package/src/lib/review-reports.js +204 -0
- package/src/lib/risk-detector.js +131 -274
- package/src/lib/specs/adr-index.js +38 -85
- package/src/lib/specs/design-seed.js +18 -0
- package/src/lib/specs/frontmatter.js +31 -0
- package/src/lib/specs/gfm-table.js +11 -0
- package/src/lib/specs/revocations.js +31 -102
- package/src/lib/specs/screen-checklist.js +56 -132
- package/src/lib/stack/dotnet-build-ignores.js +43 -0
- package/src/lib/stack/stack-profile.js +1 -28
- package/src/lib/stack/stack-scan.js +260 -0
- package/src/lib/stack-filter.js +4 -27
- package/src/lib/standards/registry.js +37 -0
- package/src/lib/standards/sections.js +47 -0
- package/src/lib/standards/verified-against.js +19 -191
- package/src/lib/tasks/eval-node.js +23 -179
- package/src/lib/tasks/linux-runner.js +18 -142
- package/src/lib/tasks/test-nodes.js +43 -313
- package/src/lib/tasks/test-runner.js +107 -447
- package/src/lib/telemetry/metrics.js +13 -103
- package/src/lib/telemetry/telemetry-reader.js +13 -238
- package/src/lib/telemetry/verify-event.js +4 -34
- package/src/lib/validators/code-budget/code-budget-validator.js +4 -44
- package/src/lib/validators/code-budget/index.js +0 -7
- package/src/lib/validators/contracts/contract-compliance-validator.js +4 -26
- package/src/lib/validators/contracts/index.js +0 -6
- package/src/lib/validators/nextjs/index.js +0 -5
- package/src/lib/validators/nextjs/next-component-validator.js +4 -81
- package/src/lib/validators/packages/index.js +0 -6
- package/src/lib/validators/packages/package-validator.js +6 -56
- package/src/lib/validators/reuse/index.js +0 -8
- package/src/lib/validators/reuse/reuse-validator.js +24 -137
- package/src/lib/validators/reuse/symbol-scanner.js +6 -62
- package/src/lib/validators/traceability-check.js +54 -156
- package/src/lib/validators/validation-runner.js +20 -125
- package/src/lib/validators/verify-runner.js +152 -575
- package/src/lib/work-type-detector.js +5 -67
- package/src/lib/worktree/docker-audit.js +17 -137
- package/src/lib/worktree/env-allocator.js +10 -98
- package/src/lib/worktree/feature-truth.js +12 -115
- package/src/lib/worktree/finish-backup.js +32 -242
- package/src/lib/worktree/fleet-scanner.js +31 -126
- package/src/lib/worktree/ignored-triage.js +15 -91
- package/src/lib/worktree/vscode-settings.js +2 -3
- package/src/lib/worktree/worktree-audit.js +21 -213
- package/src/lib/worktree/worktree-include.js +3 -5
- package/src/scripts/global-install.js +3 -9
- package/src/scripts/install-plugin.js +1 -13
- package/src/scripts/setup-infra.js +17 -138
- package/src/utils/agents-installer.js +92 -272
- package/src/utils/banner.js +4 -25
- package/src/utils/claude-md-injector.js +1 -37
- package/src/utils/claude-settings-manager.js +1 -87
- package/src/utils/file-copier.js +32 -90
- package/src/utils/hooks-installer.js +26 -296
- package/src/utils/skills-installer.js +4 -102
- package/src/utils/version-checker.js +4 -83
- package/framework/commands/morph-proposal.md +0 -57
- package/framework/evals/trace-schema.md +0 -67
- package/framework/hooks/claude-code/core/tasks-json-guard.js +0 -83
- package/framework/hooks/claude-code/core/trace-autogen.js +0 -277
- package/framework/hooks/git/pre-commit/specs.sh +0 -50
- package/framework/hooks/shared/tasks-validator.js +0 -111
- package/framework/rules/morph-task-tracking.md +0 -54
- package/framework/rules/morph-workflow.md +0 -244
- package/framework/schemas/tasks.schema.json +0 -79
- package/framework/skills/core/morph-brainstorming/SKILL.md +0 -161
- package/framework/skills/core/morph-implement/SKILL.md +0 -194
- package/framework/skills/core/morph-proposal/SKILL.md +0 -152
- package/framework/skills/core/morph-stack-scan/SKILL.md +0 -145
- package/framework/templates/docs/proposal.md +0 -192
- package/framework/templates/docs/spec.md +0 -57
- package/framework/templates/docs/ui-components.md +0 -52
- package/framework/templates/docs/ui-design-system.md +0 -76
- package/framework/templates/docs/ui-flows.md +0 -167
- package/framework/templates/examples/spec-examples.md +0 -91
- package/framework/templates/feature/decisions.md +0 -124
- package/framework/templates/feature/recap.md +0 -145
- package/framework/templates/tasks.template.json +0 -17
- package/src/commands/state/usecase.js +0 -143
- package/src/commands/tasks/dag.js +0 -181
- package/src/commands/utils/index.js +0 -3
- package/src/lib/agents/output-collector.js +0 -76
- package/src/lib/detectors/design-system-detector.js +0 -188
- package/src/lib/evaluator-report.js +0 -104
- package/src/lib/generators/design-system-generator.js +0 -508
- package/src/lib/specs/acervo-format.js +0 -70
- package/src/lib/specs/acervo-lifecycle.js +0 -264
- package/src/lib/specs/done-criteria-check.js +0 -200
- package/src/lib/specs/usecase-check.js +0 -301
- package/src/lib/specs/usecase-promoter.js +0 -301
- package/src/lib/specs/usecase-registry.js +0 -237
- package/src/lib/tasks/dispatch-planner.js +0 -319
- package/src/lib/tasks/dotnet-test-introspection.js +0 -632
- package/src/lib/tasks/mermaid-renderer.js +0 -132
- package/src/lib/tasks/task-classifier.js +0 -30
- package/src/lib/tasks/task-parser.js +0 -209
- package/src/lib/tasks/tasks-json-parser.js +0 -296
- package/src/lib/tasks/test-scope.js +0 -597
- package/src/utils/agent-id-mapper.js +0 -11
|
@@ -1,19 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Architecture Scanner - Generates rich architectural context from project filesystem.
|
|
3
|
-
*
|
|
4
|
-
* Performs fast (<5s) glob-based scanning to produce a snapshot of:
|
|
5
|
-
* - .NET entities (sealed class/record)
|
|
6
|
-
* - API endpoints (IApiEndpoint implementations)
|
|
7
|
-
* - Next.js pages and components
|
|
8
|
-
* - Integrations (from appsettings.json)
|
|
9
|
-
* - Database schema (from EF migrations)
|
|
10
|
-
* - Stack dependencies (.csproj + package.json)
|
|
11
|
-
*
|
|
12
|
-
* All scanning is regex-based (no AST parsing) for speed.
|
|
13
|
-
*
|
|
14
|
-
* @module architecture-scanner
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
1
|
import fs from 'fs/promises';
|
|
18
2
|
import path from 'path';
|
|
19
3
|
import { fileURLToPath } from 'url';
|
|
@@ -25,16 +9,6 @@ import { WORKTREE_DIR_NAMES } from '../worktree/dirs.js';
|
|
|
25
9
|
export const SKIP_DIRS = new Set(['node_modules', 'bin', 'obj', '.git', '.morph', '.next', 'dist', 'out', ...WORKTREE_DIR_NAMES]);
|
|
26
10
|
const MAX_FILES_PER_SCANNER = 200;
|
|
27
11
|
|
|
28
|
-
/**
|
|
29
|
-
* Recursively walk a directory, returning files matching a filter.
|
|
30
|
-
* Skips known heavy directories for performance.
|
|
31
|
-
*
|
|
32
|
-
* @param {string} dir - Directory to walk
|
|
33
|
-
* @param {function} filter - (relativePath) => boolean
|
|
34
|
-
* @param {string} [base] - Base path for relative calculation
|
|
35
|
-
* @param {number} [limit] - Max files to return
|
|
36
|
-
* @returns {Promise<Array<{abs: string, rel: string}>>}
|
|
37
|
-
*/
|
|
38
12
|
async function walkDir(dir, filter, base = dir, limit = MAX_FILES_PER_SCANNER) {
|
|
39
13
|
const results = [];
|
|
40
14
|
|
|
@@ -68,12 +42,6 @@ async function walkDir(dir, filter, base = dir, limit = MAX_FILES_PER_SCANNER) {
|
|
|
68
42
|
return results;
|
|
69
43
|
}
|
|
70
44
|
|
|
71
|
-
/**
|
|
72
|
-
* Scan .NET entities from Entities/ or Features/ directories.
|
|
73
|
-
*
|
|
74
|
-
* @param {string} projectPath
|
|
75
|
-
* @returns {Promise<Array<{name: string, type: string, path: string}>>}
|
|
76
|
-
*/
|
|
77
45
|
export async function scanEntities(projectPath) {
|
|
78
46
|
try {
|
|
79
47
|
const files = await walkDir(
|
|
@@ -91,7 +59,6 @@ export async function scanEntities(projectPath) {
|
|
|
91
59
|
entityRegex.lastIndex = 0;
|
|
92
60
|
while ((match = entityRegex.exec(content)) !== null) {
|
|
93
61
|
const line = content.slice(Math.max(0, match.index - 80), match.index + match[0].length);
|
|
94
|
-
// Only capture sealed/record types or classes in entity-like directories
|
|
95
62
|
if (/sealed|record/.test(line) || file.rel.includes('Entities/')) {
|
|
96
63
|
entities.push({
|
|
97
64
|
name: match[2],
|
|
@@ -100,7 +67,7 @@ export async function scanEntities(projectPath) {
|
|
|
100
67
|
});
|
|
101
68
|
}
|
|
102
69
|
}
|
|
103
|
-
} catch {
|
|
70
|
+
} catch {}
|
|
104
71
|
}
|
|
105
72
|
|
|
106
73
|
return entities;
|
|
@@ -109,12 +76,6 @@ export async function scanEntities(projectPath) {
|
|
|
109
76
|
}
|
|
110
77
|
}
|
|
111
78
|
|
|
112
|
-
/**
|
|
113
|
-
* Scan API endpoints implementing IApiEndpoint.
|
|
114
|
-
*
|
|
115
|
-
* @param {string} projectPath
|
|
116
|
-
* @returns {Promise<Array<{name: string, route: string, method: string, path: string}>>}
|
|
117
|
-
*/
|
|
118
79
|
export async function scanEndpoints(projectPath) {
|
|
119
80
|
try {
|
|
120
81
|
const files = await walkDir(
|
|
@@ -142,7 +103,7 @@ export async function scanEndpoints(projectPath) {
|
|
|
142
103
|
method: methodMatch ? (methodMatch[1] || methodMatch[2] || '').toUpperCase() : '',
|
|
143
104
|
path: file.rel
|
|
144
105
|
});
|
|
145
|
-
} catch {
|
|
106
|
+
} catch {}
|
|
146
107
|
}
|
|
147
108
|
|
|
148
109
|
return endpoints;
|
|
@@ -151,17 +112,10 @@ export async function scanEndpoints(projectPath) {
|
|
|
151
112
|
}
|
|
152
113
|
}
|
|
153
114
|
|
|
154
|
-
/**
|
|
155
|
-
* Scan Next.js pages and components.
|
|
156
|
-
*
|
|
157
|
-
* @param {string} projectPath
|
|
158
|
-
* @returns {Promise<{pages: Array<{route: string, path: string}>, layouts: Array<{path: string}>, components: Array<{name: string, path: string}>}>}
|
|
159
|
-
*/
|
|
160
115
|
export async function scanNextjsComponents(projectPath) {
|
|
161
116
|
try {
|
|
162
117
|
const result = { pages: [], layouts: [], components: [] };
|
|
163
118
|
|
|
164
|
-
// Scan pages and layouts from app directory
|
|
165
119
|
const appFiles = await walkDir(
|
|
166
120
|
projectPath,
|
|
167
121
|
(rel, name) =>
|
|
@@ -181,7 +135,6 @@ export async function scanNextjsComponents(projectPath) {
|
|
|
181
135
|
}
|
|
182
136
|
}
|
|
183
137
|
|
|
184
|
-
// Scan components
|
|
185
138
|
const componentFiles = await walkDir(
|
|
186
139
|
projectPath,
|
|
187
140
|
(rel, name) =>
|
|
@@ -191,7 +144,6 @@ export async function scanNextjsComponents(projectPath) {
|
|
|
191
144
|
|
|
192
145
|
for (const file of componentFiles) {
|
|
193
146
|
const name = path.basename(file.rel, path.extname(file.rel));
|
|
194
|
-
// Skip index files and test files
|
|
195
147
|
if (name === 'index' || name.includes('.test') || name.includes('.spec')) continue;
|
|
196
148
|
result.components.push({ name, path: file.rel });
|
|
197
149
|
}
|
|
@@ -202,12 +154,6 @@ export async function scanNextjsComponents(projectPath) {
|
|
|
202
154
|
}
|
|
203
155
|
}
|
|
204
156
|
|
|
205
|
-
/**
|
|
206
|
-
* Scan integrations from appsettings*.json files.
|
|
207
|
-
*
|
|
208
|
-
* @param {string} projectPath
|
|
209
|
-
* @returns {Promise<Array<{name: string, type: string, configured: boolean}>>}
|
|
210
|
-
*/
|
|
211
157
|
export async function scanIntegrations(projectPath) {
|
|
212
158
|
try {
|
|
213
159
|
const files = await walkDir(
|
|
@@ -217,7 +163,6 @@ export async function scanIntegrations(projectPath) {
|
|
|
217
163
|
20
|
|
218
164
|
);
|
|
219
165
|
|
|
220
|
-
// Also check .env files for common integrations
|
|
221
166
|
const envFiles = await walkDir(
|
|
222
167
|
projectPath,
|
|
223
168
|
(rel, name) => name === '.env' || name === '.env.local' || name === '.env.production',
|
|
@@ -242,7 +187,6 @@ export async function scanIntegrations(projectPath) {
|
|
|
242
187
|
|
|
243
188
|
const detected = new Map();
|
|
244
189
|
|
|
245
|
-
// Scan appsettings files
|
|
246
190
|
for (const file of files) {
|
|
247
191
|
try {
|
|
248
192
|
const content = JSON.parse(await fs.readFile(file.abs, 'utf8'));
|
|
@@ -256,7 +200,6 @@ export async function scanIntegrations(projectPath) {
|
|
|
256
200
|
}
|
|
257
201
|
}
|
|
258
202
|
}
|
|
259
|
-
// Check ConnectionStrings specifically
|
|
260
203
|
if (content.ConnectionStrings) {
|
|
261
204
|
for (const [key, val] of Object.entries(content.ConnectionStrings)) {
|
|
262
205
|
if (val && !String(val).startsWith('{{')) {
|
|
@@ -264,10 +207,9 @@ export async function scanIntegrations(projectPath) {
|
|
|
264
207
|
}
|
|
265
208
|
}
|
|
266
209
|
}
|
|
267
|
-
} catch {
|
|
210
|
+
} catch {}
|
|
268
211
|
}
|
|
269
212
|
|
|
270
|
-
// Scan .env files for known prefixes
|
|
271
213
|
const envPrefixes = {
|
|
272
214
|
'NEON_AUTH_': 'NeonAuth',
|
|
273
215
|
'DATABASE_URL': 'Neon',
|
|
@@ -289,7 +231,7 @@ export async function scanIntegrations(projectPath) {
|
|
|
289
231
|
}
|
|
290
232
|
}
|
|
291
233
|
}
|
|
292
|
-
} catch {
|
|
234
|
+
} catch {}
|
|
293
235
|
}
|
|
294
236
|
|
|
295
237
|
return Array.from(detected.values());
|
|
@@ -298,12 +240,6 @@ export async function scanIntegrations(projectPath) {
|
|
|
298
240
|
}
|
|
299
241
|
}
|
|
300
242
|
|
|
301
|
-
/**
|
|
302
|
-
* Scan database schema from EF Core migrations.
|
|
303
|
-
*
|
|
304
|
-
* @param {string} projectPath
|
|
305
|
-
* @returns {Promise<Array<{table: string, migration: string}>>}
|
|
306
|
-
*/
|
|
307
243
|
export async function scanDatabaseSchema(projectPath) {
|
|
308
244
|
try {
|
|
309
245
|
const files = await walkDir(
|
|
@@ -331,10 +267,9 @@ export async function scanDatabaseSchema(projectPath) {
|
|
|
331
267
|
}
|
|
332
268
|
}
|
|
333
269
|
}
|
|
334
|
-
} catch {
|
|
270
|
+
} catch {}
|
|
335
271
|
}
|
|
336
272
|
|
|
337
|
-
// Also scan database migrations (SQL)
|
|
338
273
|
const sqlFiles = await walkDir(
|
|
339
274
|
projectPath,
|
|
340
275
|
(rel, name) => name.endsWith('.sql') && rel.includes('migrations/'),
|
|
@@ -355,7 +290,7 @@ export async function scanDatabaseSchema(projectPath) {
|
|
|
355
290
|
tables.set(match[1], { table: match[1], migration: migrationName });
|
|
356
291
|
}
|
|
357
292
|
}
|
|
358
|
-
} catch {
|
|
293
|
+
} catch {}
|
|
359
294
|
}
|
|
360
295
|
|
|
361
296
|
return Array.from(tables.values());
|
|
@@ -364,17 +299,10 @@ export async function scanDatabaseSchema(projectPath) {
|
|
|
364
299
|
}
|
|
365
300
|
}
|
|
366
301
|
|
|
367
|
-
/**
|
|
368
|
-
* Detect stack from .csproj PackageReference and package.json dependencies.
|
|
369
|
-
*
|
|
370
|
-
* @param {string} projectPath
|
|
371
|
-
* @returns {Promise<{dotnet: Array<{name: string, version: string}>, npm: Array<{name: string, version: string}>}>}
|
|
372
|
-
*/
|
|
373
302
|
export async function scanStackDependencies(projectPath) {
|
|
374
303
|
try {
|
|
375
304
|
const result = { dotnet: [], npm: [] };
|
|
376
305
|
|
|
377
|
-
// Scan .csproj files
|
|
378
306
|
const csprojFiles = await walkDir(
|
|
379
307
|
projectPath,
|
|
380
308
|
(rel, name) => name.endsWith('.csproj'),
|
|
@@ -392,10 +320,9 @@ export async function scanStackDependencies(projectPath) {
|
|
|
392
320
|
while ((match = packageRefRegex.exec(content)) !== null) {
|
|
393
321
|
result.dotnet.push({ name: match[1], version: match[2] });
|
|
394
322
|
}
|
|
395
|
-
} catch {
|
|
323
|
+
} catch {}
|
|
396
324
|
}
|
|
397
325
|
|
|
398
|
-
// Scan package.json (root only)
|
|
399
326
|
const pkgPath = path.join(projectPath, 'package.json');
|
|
400
327
|
if (existsSync(pkgPath)) {
|
|
401
328
|
try {
|
|
@@ -404,10 +331,9 @@ export async function scanStackDependencies(projectPath) {
|
|
|
404
331
|
for (const [name, version] of Object.entries(allDeps)) {
|
|
405
332
|
result.npm.push({ name, version: String(version) });
|
|
406
333
|
}
|
|
407
|
-
} catch {
|
|
334
|
+
} catch {}
|
|
408
335
|
}
|
|
409
336
|
|
|
410
|
-
// Also check web/ or frontend/ subdirectory package.json
|
|
411
337
|
for (const subdir of ['web', 'frontend', 'client', 'src']) {
|
|
412
338
|
const subPkgPath = path.join(projectPath, subdir, 'package.json');
|
|
413
339
|
if (existsSync(subPkgPath)) {
|
|
@@ -415,12 +341,11 @@ export async function scanStackDependencies(projectPath) {
|
|
|
415
341
|
const pkg = JSON.parse(await fs.readFile(subPkgPath, 'utf8'));
|
|
416
342
|
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
417
343
|
for (const [name, version] of Object.entries(allDeps)) {
|
|
418
|
-
// Avoid duplicates
|
|
419
344
|
if (!result.npm.some(d => d.name === name)) {
|
|
420
345
|
result.npm.push({ name, version: String(version) });
|
|
421
346
|
}
|
|
422
347
|
}
|
|
423
|
-
} catch {
|
|
348
|
+
} catch {}
|
|
424
349
|
}
|
|
425
350
|
}
|
|
426
351
|
|
|
@@ -430,12 +355,6 @@ export async function scanStackDependencies(projectPath) {
|
|
|
430
355
|
}
|
|
431
356
|
}
|
|
432
357
|
|
|
433
|
-
/**
|
|
434
|
-
* Master scan — runs all scanners in parallel.
|
|
435
|
-
*
|
|
436
|
-
* @param {string} projectPath
|
|
437
|
-
* @returns {Promise<Object>} ArchitectureScanResult
|
|
438
|
-
*/
|
|
439
358
|
export async function scanArchitecture(projectPath) {
|
|
440
359
|
const [entities, endpoints, nextjs, integrations, database, stack, graph] = await Promise.all([
|
|
441
360
|
scanEntities(projectPath),
|
|
@@ -444,9 +363,7 @@ export async function scanArchitecture(projectPath) {
|
|
|
444
363
|
scanIntegrations(projectPath),
|
|
445
364
|
scanDatabaseSchema(projectPath),
|
|
446
365
|
scanStackDependencies(projectPath),
|
|
447
|
-
// Aditivo: sem grafo devolve listas vazias
|
|
448
|
-
// inventário acima continua sendo a espinha do arquivo — ele tem
|
|
449
|
-
// exaustividade que o grafo não tem.
|
|
366
|
+
// Aditivo: sem grafo devolve listas vazias; o inventário acima tem a exaustividade que o grafo não tem.
|
|
450
367
|
scanGraph(projectPath)
|
|
451
368
|
]);
|
|
452
369
|
|
|
@@ -462,20 +379,11 @@ export async function scanArchitecture(projectPath) {
|
|
|
462
379
|
};
|
|
463
380
|
}
|
|
464
381
|
|
|
465
|
-
|
|
466
|
-
* Resolve a template path: check .morph/framework/templates/ first, then framework/templates/,
|
|
467
|
-
* then fall back to the CLI package's own framework/templates/.
|
|
468
|
-
*
|
|
469
|
-
* @param {string} projectPath
|
|
470
|
-
* @param {string} templateRelPath
|
|
471
|
-
* @returns {string|null}
|
|
472
|
-
*/
|
|
382
|
+
// Project-local `.morph/framework/templates/` override first, then the CLI package's own.
|
|
473
383
|
function resolveTemplatePath(projectPath, templateRelPath) {
|
|
474
|
-
// 1. Project-local override
|
|
475
384
|
const projectLocal = path.join(projectPath, '.morph/framework/templates', templateRelPath);
|
|
476
385
|
if (existsSync(projectLocal)) return projectLocal;
|
|
477
386
|
|
|
478
|
-
// 2. CLI package framework/ (for running from source or during init)
|
|
479
387
|
const cliFramework = path.join(
|
|
480
388
|
cliRootDir(), 'framework', 'templates', templateRelPath
|
|
481
389
|
);
|
|
@@ -484,103 +392,64 @@ function resolveTemplatePath(projectPath, templateRelPath) {
|
|
|
484
392
|
return null;
|
|
485
393
|
}
|
|
486
394
|
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
*
|
|
490
|
-
* fileURLToPath, not `new URL(...).pathname`: the latter leaves the path
|
|
491
|
-
* percent-encoded, so an install under a directory with a space resolved to
|
|
492
|
-
* ".../Program%20Files/..." — a path that never exists. `resolveTemplatePath`
|
|
493
|
-
* then returned null and the caller fell through to its plain-text fallback
|
|
494
|
-
* WITHOUT any warning. The first lines of that fallback are identical to the
|
|
495
|
-
* template's, which is why nobody noticed the tables were gone. On Windows a
|
|
496
|
-
* global npm install lives under a spaced path by default, so this was the
|
|
497
|
-
* common case, not the edge one.
|
|
498
|
-
*/
|
|
395
|
+
// fileURLToPath, not `new URL(...).pathname`: the latter stays percent-encoded, so a path with a space
|
|
396
|
+
// (the default global npm install on Windows) never resolves and the template is silently dropped.
|
|
499
397
|
function cliRootDir() {
|
|
500
398
|
return path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..', '..');
|
|
501
399
|
}
|
|
502
400
|
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
*
|
|
506
|
-
* QUEM LÊ ESTE ARQUIVO, E QUANDO: o passo 1c da skill `morph-plan` (mapa de
|
|
507
|
-
* reuso), antes de decidir REUSAR/ESTENDER/CRIAR cada conceito da feature. É a
|
|
508
|
-
* granularidade de CONCEITO — entidades, endpoints com rota, componentes com
|
|
509
|
-
* path — que o grafo de codebase, que casa por símbolo, não dá; e é o único
|
|
510
|
-
* inventário legível num projeto sem grafo (sem Python), antes do grep.
|
|
511
|
-
*
|
|
512
|
-
* Até 2026-08-27 este arquivo era gerado e NUNCA lido por skill nenhuma
|
|
513
|
-
* (docs/specs/code-reuse-gap/analysis.md §2.5). Se um dia o consumidor acima
|
|
514
|
-
* desaparecer do `morph-plan`, este gerador volta a queimar tempo por nada —
|
|
515
|
-
* a decisão então é remover os dois, não deixar o arquivo órfão de novo.
|
|
516
|
-
*
|
|
517
|
-
* @param {string} projectPath
|
|
518
|
-
* @param {Object} scanResult - Result from scanArchitecture()
|
|
519
|
-
* @param {Object} [options]
|
|
520
|
-
* @param {string} [options.projectName] - Override project name
|
|
521
|
-
* @returns {Promise<string>} Path to written file
|
|
522
|
-
*/
|
|
401
|
+
// Read by `morph-plan` for the concept-level inventory the graph doesn't give. If that reader goes away,
|
|
402
|
+
// remove this generator too.
|
|
523
403
|
export async function renderArchitectureContext(projectPath, scanResult, options = {}) {
|
|
524
404
|
const templatePath = resolveTemplatePath(projectPath, 'context/ARCHITECTURE.md');
|
|
525
405
|
|
|
526
|
-
// Determine project name
|
|
527
406
|
let projectName = options.projectName;
|
|
528
407
|
if (!projectName) {
|
|
529
|
-
// Try config.json
|
|
530
408
|
const configPath = path.join(projectPath, '.morph/config/config.json');
|
|
531
409
|
if (existsSync(configPath)) {
|
|
532
410
|
try {
|
|
533
411
|
const config = JSON.parse(readFileSync(configPath, 'utf8'));
|
|
534
412
|
projectName = config.project?.name;
|
|
535
|
-
} catch {
|
|
413
|
+
} catch {}
|
|
536
414
|
}
|
|
537
|
-
// Fallback to directory name
|
|
538
415
|
if (!projectName) {
|
|
539
416
|
projectName = path.basename(projectPath);
|
|
540
417
|
}
|
|
541
418
|
}
|
|
542
419
|
|
|
543
|
-
// Get morph-spec version
|
|
544
420
|
let morphVersion = 'unknown';
|
|
545
421
|
try {
|
|
546
422
|
const pkgPath = path.join(cliRootDir(), 'package.json');
|
|
547
423
|
if (existsSync(pkgPath)) {
|
|
548
424
|
morphVersion = JSON.parse(readFileSync(pkgPath, 'utf8')).version;
|
|
549
425
|
}
|
|
550
|
-
} catch {
|
|
426
|
+
} catch {}
|
|
551
427
|
|
|
552
428
|
const variables = {
|
|
553
429
|
projectName,
|
|
554
430
|
timestamp: new Date().toISOString().replace('T', ' ').split('.')[0],
|
|
555
431
|
morphVersion,
|
|
556
|
-
// Entities
|
|
557
432
|
hasDotnet: scanResult.entities.length > 0 || scanResult.endpoints.length > 0,
|
|
558
433
|
entities: scanResult.entities,
|
|
559
434
|
entityCount: scanResult.entities.length,
|
|
560
435
|
endpoints: scanResult.endpoints,
|
|
561
436
|
endpointCount: scanResult.endpoints.length,
|
|
562
|
-
// Next.js
|
|
563
437
|
hasNextjs: scanResult.nextjs.pages.length > 0 || scanResult.nextjs.components.length > 0,
|
|
564
438
|
pages: scanResult.nextjs.pages,
|
|
565
439
|
pageCount: scanResult.nextjs.pages.length,
|
|
566
440
|
layouts: scanResult.nextjs.layouts,
|
|
567
441
|
components: scanResult.nextjs.components,
|
|
568
442
|
componentCount: scanResult.nextjs.components.length,
|
|
569
|
-
// Integrations
|
|
570
443
|
hasIntegrations: scanResult.integrations.length > 0,
|
|
571
444
|
integrations: scanResult.integrations,
|
|
572
|
-
// Database
|
|
573
445
|
hasTables: scanResult.database.length > 0,
|
|
574
446
|
tables: scanResult.database,
|
|
575
447
|
tableCount: scanResult.database.length,
|
|
576
|
-
// Stack
|
|
577
448
|
hasDotnetPackages: scanResult.stack.dotnet.length > 0,
|
|
578
449
|
dotnetPackages: scanResult.stack.dotnet,
|
|
579
450
|
hasNpmPackages: scanResult.stack.npm.length > 0,
|
|
580
451
|
npmPackages: scanResult.stack.npm,
|
|
581
|
-
//
|
|
582
|
-
// Comunidade ainda com rótulo Community_N fica de fora: sem
|
|
583
|
-
// `graphify label` ela é ruído puro (441 páginas assim no eval).
|
|
452
|
+
// Comunidade ainda rotulada Community_N fica de fora: sem `graphify label` é ruído puro.
|
|
584
453
|
hasGraph: scanResult.graph?.available === true && (scanResult.graph.godNodes?.length ?? 0) > 0,
|
|
585
454
|
godNodes: scanResult.graph?.godNodes ?? [],
|
|
586
455
|
communities: (scanResult.graph?.communities ?? []).filter((c) => c.labeled),
|
|
@@ -591,11 +460,9 @@ export async function renderArchitectureContext(projectPath, scanResult, options
|
|
|
591
460
|
if (templatePath) {
|
|
592
461
|
content = renderTemplate(templatePath, variables);
|
|
593
462
|
} else {
|
|
594
|
-
// Fallback: generate minimal markdown without template
|
|
595
463
|
content = generateFallbackMarkdown(variables);
|
|
596
464
|
}
|
|
597
465
|
|
|
598
|
-
// Write to .morph/context/README.md
|
|
599
466
|
const contextDir = path.join(projectPath, '.morph/context');
|
|
600
467
|
await fs.mkdir(contextDir, { recursive: true });
|
|
601
468
|
const outputPath = path.join(contextDir, 'README.md');
|
|
@@ -604,11 +471,6 @@ export async function renderArchitectureContext(projectPath, scanResult, options
|
|
|
604
471
|
return outputPath;
|
|
605
472
|
}
|
|
606
473
|
|
|
607
|
-
/**
|
|
608
|
-
* Generate minimal markdown when template is not available.
|
|
609
|
-
* @param {Object} vars
|
|
610
|
-
* @returns {string}
|
|
611
|
-
*/
|
|
612
474
|
function generateFallbackMarkdown(vars) {
|
|
613
475
|
const lines = [`# ${vars.projectName} — Architecture Context\n`];
|
|
614
476
|
lines.push(`> Auto-generated by morph-spec context refresh`);
|