@polymorphism-tech/morph-spec 3.2.0 → 4.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 +352 -7
- package/README.md +1 -14
- package/bin/detect-agents.js +1 -1
- package/bin/morph-spec.js +122 -34
- package/bin/validate.js +1 -1
- package/docs/getting-started.md +0 -5
- package/docs/v3.0/AGENTS.md +521 -0
- package/docs/v3.0/ANALYSIS.md +555 -0
- package/docs/v3.0/ARCHITECTURE.md +436 -0
- package/docs/v3.0/EXECUTION-FLOW.md +1304 -0
- package/docs/v3.0/FEATURES.md +688 -0
- package/docs/v3.0/README.md +231 -0
- package/docs/v3.0/ROADMAP.md +801 -0
- package/docs/validation-checklist.md +0 -1
- package/package.json +1 -1
- package/src/commands/agents/index.js +4 -0
- package/src/commands/{spawn-team.js → agents/spawn-team.js} +172 -172
- package/src/commands/{create-story.js → feature/create-story.js} +357 -354
- package/src/commands/feature/index.js +6 -0
- package/src/commands/{shard-spec.js → feature/shard-spec.js} +2 -2
- package/src/commands/{sprint-status.js → feature/sprint-status.js} +1 -1
- package/src/commands/{generate-context.js → generation/generate-context.js} +40 -40
- package/src/commands/{generate.js → generation/generate.js} +4 -4
- package/src/commands/generation/index.js +5 -0
- package/src/commands/index.js +16 -0
- package/src/commands/{capture-pattern.js → learning/capture-pattern.js} +121 -121
- package/src/commands/learning/index.js +5 -0
- package/src/commands/{detect-agents.js → project/detect-agents.js} +178 -178
- package/src/commands/project/detect-workflow.js +174 -0
- package/src/commands/{detect.js → project/detect.js} +104 -104
- package/src/commands/{doctor.js → project/doctor.js} +356 -356
- package/src/commands/project/index.js +10 -0
- package/src/commands/{init.js → project/init.js} +305 -295
- package/src/commands/{sync.js → project/sync.js} +167 -167
- package/src/commands/{update.js → project/update.js} +240 -240
- package/src/commands/{advance-phase.js → state/advance-phase.js} +416 -357
- package/src/commands/{approve.js → state/approve.js} +221 -221
- package/src/commands/state/index.js +8 -0
- package/src/commands/{rollback-phase.js → state/rollback-phase.js} +185 -185
- package/src/commands/{state.js → state/state.js} +334 -334
- package/src/commands/{validate-phase.js → state/validate-phase.js} +221 -221
- package/src/commands/tasks/index.js +4 -0
- package/src/commands/{task.js → tasks/task.js} +78 -78
- package/src/commands/templates/index.js +8 -0
- package/src/commands/templates/template-customize.js +101 -0
- package/src/commands/templates/template-list.js +128 -0
- package/src/commands/templates/template-render.js +95 -0
- package/src/commands/templates/template-show.js +131 -0
- package/src/commands/templates/template-validate.js +91 -0
- package/src/commands/utils/index.js +7 -0
- package/src/commands/{session-summary.js → utils/session-summary.js} +291 -291
- package/src/commands/{troubleshoot.js → utils/troubleshoot.js} +222 -222
- package/src/commands/{analyze-blazor-concurrency.js → validation/analyze-blazor-concurrency.js} +193 -193
- package/src/commands/validation/index.js +8 -0
- package/src/commands/{lint-fluent.js → validation/lint-fluent.js} +352 -352
- package/src/commands/{validate-blazor-state.js → validation/validate-blazor-state.js} +210 -210
- package/src/commands/{validate-blazor.js → validation/validate-blazor.js} +156 -156
- package/src/commands/{validate-css.js → validation/validate-css.js} +84 -84
- package/src/core/index.js +10 -0
- package/src/core/registry/command-registry.js +302 -0
- package/src/core/registry/index.js +8 -0
- package/src/core/registry/validator-registry.js +204 -0
- package/src/core/state/index.js +8 -0
- package/src/{lib → core/state}/phase-state-machine.js +214 -214
- package/src/{lib → core/state}/state-manager.js +572 -534
- package/src/core/templates/index.js +9 -0
- package/src/core/templates/template-registry.js +335 -0
- package/src/core/templates/template-renderer.js +477 -0
- package/src/core/templates/template-validator.js +296 -0
- package/src/core/workflows/index.js +7 -0
- package/src/core/workflows/workflow-detector.js +354 -0
- package/src/lib/{complexity-analyzer.js → analysis/complexity-analyzer.js} +441 -441
- package/src/lib/analysis/index.js +7 -0
- package/src/lib/{checkpoint-hooks.js → checkpoints/checkpoint-hooks.js} +258 -258
- package/src/lib/checkpoints/index.js +7 -0
- package/src/lib/detectors/config-detector.js +223 -223
- package/src/lib/detectors/conversation-analyzer.js +163 -163
- package/src/lib/{design-system-detector.js → detectors/design-system-detector.js} +187 -187
- package/src/lib/detectors/index.js +87 -84
- package/src/lib/detectors/standards-generator.js +275 -275
- package/src/lib/detectors/structure-detector.js +245 -245
- package/src/lib/{context-generator.js → generators/context-generator.js} +526 -516
- package/src/lib/generators/index.js +10 -0
- package/src/lib/{metadata-extractor.js → generators/metadata-extractor.js} +387 -380
- package/src/lib/{recap-generator.js → generators/recap-generator.js} +205 -205
- package/src/lib/learning/index.js +7 -0
- package/src/lib/orchestration/index.js +7 -0
- package/src/lib/{team-orchestrator.js → orchestration/team-orchestrator.js} +323 -323
- package/src/lib/stacks/index.js +7 -0
- package/src/lib/{stack-resolver.js → stacks/stack-resolver.js} +180 -148
- package/src/lib/standards/index.js +7 -0
- package/src/lib/{standards-context-injector.js → standards/standards-context-injector.js} +298 -288
- package/src/lib/troubleshooting/index.js +8 -0
- package/src/lib/{troubleshoot-grep.js → troubleshooting/troubleshoot-grep.js} +204 -204
- package/src/lib/{troubleshoot-index.js → troubleshooting/troubleshoot-index.js} +144 -144
- package/src/lib/validators/architecture/architecture-validator.js +387 -0
- package/src/lib/validators/architecture/index.js +7 -0
- package/src/lib/validators/architecture-validator.js +40 -367
- package/src/lib/{blazor-concurrency-analyzer.js → validators/blazor/blazor-concurrency-analyzer.js} +277 -288
- package/src/lib/{blazor-state-validator.js → validators/blazor/blazor-state-validator.js} +279 -291
- package/src/lib/{blazor-validator.js → validators/blazor/blazor-validator.js} +369 -374
- package/src/lib/validators/blazor/index.js +9 -0
- package/src/lib/validators/content/content-validator.js +351 -0
- package/src/lib/validators/content/index.js +7 -0
- package/src/lib/validators/content-validator.js +164 -351
- package/src/lib/validators/{contract-compliance-validator.js → contracts/contract-compliance-validator.js} +273 -273
- package/src/lib/validators/contracts/index.js +7 -0
- package/src/lib/{css-validator.js → validators/css/css-validator.js} +352 -352
- package/src/lib/validators/css/index.js +7 -0
- package/src/lib/validators/{design-system-validator.js → design-system/design-system-validator.js} +231 -231
- package/src/lib/validators/design-system/index.js +7 -0
- package/src/lib/validators/package-validator.js +41 -340
- package/src/lib/validators/packages/index.js +7 -0
- package/src/lib/validators/packages/package-validator.js +360 -0
- package/src/lib/validators/shared/index.js +12 -0
- package/src/lib/validators/shared/issue-counter.js +18 -0
- package/src/lib/validators/shared/result-formatter.js +124 -0
- package/src/lib/{spec-validator.js → validators/spec-validator.js} +258 -258
- package/src/lib/validators/ui/index.js +7 -0
- package/src/lib/validators/ui/ui-contrast-validator.js +422 -0
- package/src/lib/validators/ui-contrast-validator.js +31 -409
- package/src/lib/{validation-runner.js → validators/validation-runner.js} +286 -284
- package/src/ui/wizard-questions.js +0 -2
- package/src/utils/color-utils.js +70 -0
- package/src/utils/file-copier.js +188 -189
- package/src/utils/process-handler.js +97 -0
- package/stacks/blazor-azure/.claude/skills/level-2-domains/ai-agents/ai-system-architect.md +3 -3
- package/stacks/blazor-azure/.claude/skills/level-2-domains/backend/api-designer.md +59 -0
- package/stacks/blazor-azure/.claude/skills/level-2-domains/backend/dotnet-senior.md +45 -255
- package/stacks/blazor-azure/.claude/skills/level-2-domains/backend/ef-modeler.md +33 -88
- package/stacks/blazor-azure/.claude/skills/level-2-domains/backend/ms-agent-expert.md +25 -89
- package/stacks/blazor-azure/.claude/skills/level-2-domains/integrations/hangfire-orchestrator.md +64 -0
- package/stacks/blazor-azure/.morph/config/agents.json +879 -764
- package/stacks/blazor-azure/.morph/hooks/{pre-commit-tests.sh → pre-commit/tests-csharp.sh} +3 -2
- package/stacks/blazor-azure/.morph/templates/infrastructure/github/workflows/cd-prod.yml.hbs +41 -0
- package/stacks/blazor-azure/.morph/templates/infrastructure/github/workflows/cd-staging.yml.hbs +24 -0
- package/stacks/blazor-azure/.morph/templates/infrastructure/github/workflows/ci-build.yml.hbs +23 -0
- package/stacks/nextjs-supabase/.claude/commands/morph-apply.md +221 -0
- package/stacks/nextjs-supabase/.claude/commands/morph-archive.md +79 -0
- package/stacks/nextjs-supabase/.claude/commands/morph-deploy.md +529 -0
- package/stacks/nextjs-supabase/.claude/commands/morph-infra.md +209 -0
- package/stacks/nextjs-supabase/.claude/commands/morph-preflight.md +227 -0
- package/stacks/nextjs-supabase/.claude/commands/morph-proposal.md +122 -0
- package/stacks/nextjs-supabase/.claude/commands/morph-status.md +86 -0
- package/stacks/nextjs-supabase/.claude/commands/morph-troubleshoot.md +122 -0
- package/stacks/nextjs-supabase/.claude/settings.local.json +6 -0
- package/stacks/nextjs-supabase/.claude/skills/level-2-domains/integrations/supabase-expert.md +30 -150
- package/stacks/nextjs-supabase/.morph/config/agents.json +345 -345
- package/stacks/nextjs-supabase/.morph/hooks/pre-commit/tests-typescript.sh +61 -0
- package/stacks/nextjs-supabase/.morph/templates/infrastructure/github/workflows/cd-prod.yml.hbs +22 -0
- package/stacks/nextjs-supabase/.morph/templates/infrastructure/github/workflows/cd-staging.yml.hbs +22 -0
- package/stacks/nextjs-supabase/.morph/templates/infrastructure/github/workflows/ci-build.yml.hbs +35 -0
- package/stacks/nextjs-supabase/README.md +6 -15
- package/bin/render-template.js +0 -349
- package/bin/semantic-detect-agents.js +0 -247
- package/bin/validate-agents-skills.js +0 -257
- package/bin/validate-agents.js +0 -70
- package/bin/validate-phase.js +0 -263
- package/docs/examples.md +0 -328
- package/scripts/reorganize-skills.cjs +0 -175
- package/scripts/validate-agents-structure.cjs +0 -52
- package/scripts/validate-skills.cjs +0 -180
- package/src/commands/deploy.js +0 -780
- package/src/lib/continuous-validator.js +0 -421
- package/src/lib/decision-constraint-loader.js +0 -109
- package/src/lib/design-system-scaffolder.js +0 -299
- package/src/lib/hook-executor.js +0 -257
- package/src/lib/mockup-generator.js +0 -366
- package/src/lib/ui-detector.js +0 -350
- package/src/llm/schema-validator.js +0 -121
- package/src/sanitizer/.gitkeep +0 -0
- package/src/scanner/.gitkeep +0 -0
- package/src/types/index.js +0 -477
- package/src/ui/.gitkeep +0 -0
- package/src/writer/.gitkeep +0 -0
- package/stacks/blazor-azure/.azure/README.md +0 -293
- package/stacks/blazor-azure/.azure/docs/azure-devops-setup.md +0 -454
- package/stacks/blazor-azure/.azure/docs/branch-strategy.md +0 -398
- package/stacks/blazor-azure/.azure/docs/local-development.md +0 -515
- package/stacks/blazor-azure/.azure/pipelines/pipeline-variables.yml +0 -34
- package/stacks/blazor-azure/.azure/pipelines/prod-pipeline.yml +0 -319
- package/stacks/blazor-azure/.azure/pipelines/staging-pipeline.yml +0 -234
- package/stacks/blazor-azure/.azure/pipelines/templates/build-dotnet.yml +0 -75
- package/stacks/blazor-azure/.azure/pipelines/templates/deploy-app-service.yml +0 -94
- package/stacks/blazor-azure/.azure/pipelines/templates/deploy-container-app.yml +0 -120
- package/stacks/blazor-azure/.azure/pipelines/templates/infra-deploy.yml +0 -90
- package/stacks/blazor-azure/.claude/settings.local.json +0 -15
- package/stacks/blazor-azure/.morph/docs/STORY-DRIVEN-DEVELOPMENT.md +0 -392
- package/stacks/blazor-azure/.morph/docs/workflows/design-impl.md +0 -37
- package/stacks/blazor-azure/.morph/docs/workflows/enforcement-pipeline.md +0 -668
- package/stacks/blazor-azure/.morph/docs/workflows/fast-track.md +0 -29
- package/stacks/blazor-azure/.morph/docs/workflows/full-morph.md +0 -76
- package/stacks/blazor-azure/.morph/docs/workflows/standard.md +0 -44
- package/stacks/blazor-azure/.morph/docs/workflows/ui-refresh.md +0 -39
- package/stacks/blazor-azure/.morph/examples/api-nextjs/README.md +0 -241
- package/stacks/blazor-azure/.morph/examples/api-nextjs/contracts.ts +0 -307
- package/stacks/blazor-azure/.morph/examples/api-nextjs/spec.md +0 -399
- package/stacks/blazor-azure/.morph/examples/api-nextjs/tasks.md +0 -168
- package/stacks/blazor-azure/.morph/examples/micro-saas/README.md +0 -125
- package/stacks/blazor-azure/.morph/examples/micro-saas/contracts.cs +0 -358
- package/stacks/blazor-azure/.morph/examples/micro-saas/decisions.md +0 -246
- package/stacks/blazor-azure/.morph/examples/micro-saas/spec.md +0 -236
- package/stacks/blazor-azure/.morph/examples/micro-saas/tasks.md +0 -150
- package/stacks/blazor-azure/.morph/examples/multi-agent/README.md +0 -309
- package/stacks/blazor-azure/.morph/examples/multi-agent/contracts.cs +0 -433
- package/stacks/blazor-azure/.morph/examples/multi-agent/spec.md +0 -479
- package/stacks/blazor-azure/.morph/examples/multi-agent/tasks.md +0 -185
- package/stacks/blazor-azure/.morph/examples/scheduled-reports/decisions.md +0 -158
- package/stacks/blazor-azure/.morph/examples/scheduled-reports/proposal.md +0 -95
- package/stacks/blazor-azure/.morph/examples/scheduled-reports/spec.md +0 -267
- package/stacks/blazor-azure/.morph/examples/state-v3.json +0 -188
- package/stacks/blazor-azure/.morph/hooks/README.md +0 -348
- package/stacks/blazor-azure/.morph/hooks/pre-commit-agents.sh +0 -24
- package/stacks/blazor-azure/.morph/hooks/pre-commit-all.sh +0 -48
- package/stacks/blazor-azure/.morph/hooks/pre-commit-specs.sh +0 -49
- package/stacks/blazor-azure/.morph/hooks/task-completed.js +0 -73
- package/stacks/blazor-azure/.morph/hooks/teammate-idle.js +0 -68
- package/stacks/blazor-azure/.morph/standards/agent-framework-blazor-ui.md +0 -359
- package/stacks/blazor-azure/.morph/standards/agent-framework-production.md +0 -410
- package/stacks/blazor-azure/.morph/standards/agent-framework-setup.md +0 -413
- package/stacks/blazor-azure/.morph/standards/agent-framework-workflows.md +0 -349
- package/stacks/blazor-azure/.morph/standards/agent-teams-workflow.md +0 -474
- package/stacks/blazor-azure/.morph/standards/architecture.md +0 -325
- package/stacks/blazor-azure/.morph/standards/azure.md +0 -605
- package/stacks/blazor-azure/.morph/standards/coding.md +0 -377
- package/stacks/blazor-azure/.morph/standards/dotnet10-migration.md +0 -520
- package/stacks/blazor-azure/.morph/standards/fluent-ui-setup.md +0 -590
- package/stacks/blazor-azure/.morph/standards/migration-guide.md +0 -514
- package/stacks/blazor-azure/.morph/standards/passkeys-auth.md +0 -423
- package/stacks/blazor-azure/.morph/standards/vector-search-rag.md +0 -536
- package/stacks/blazor-azure/.morph/templates/CONTEXT-FEATURE.md +0 -276
- package/stacks/blazor-azure/.morph/templates/CONTEXT.md +0 -170
- package/stacks/blazor-azure/.morph/templates/FluentDesignTheme.cs +0 -149
- package/stacks/blazor-azure/.morph/templates/MudTheme.cs +0 -281
- package/stacks/blazor-azure/.morph/templates/agent.cs +0 -163
- package/stacks/blazor-azure/.morph/templates/clarify-questions.md +0 -159
- package/stacks/blazor-azure/.morph/templates/component.razor +0 -239
- package/stacks/blazor-azure/.morph/templates/contracts/Commands.cs +0 -74
- package/stacks/blazor-azure/.morph/templates/contracts/Entities.cs +0 -25
- package/stacks/blazor-azure/.morph/templates/contracts/Queries.cs +0 -74
- package/stacks/blazor-azure/.morph/templates/contracts/README.md +0 -74
- package/stacks/blazor-azure/.morph/templates/contracts.cs +0 -217
- package/stacks/blazor-azure/.morph/templates/decisions.md +0 -123
- package/stacks/blazor-azure/.morph/templates/design-system.css +0 -226
- package/stacks/blazor-azure/.morph/templates/infra/.dockerignore.example +0 -89
- package/stacks/blazor-azure/.morph/templates/infra/Dockerfile.example +0 -82
- package/stacks/blazor-azure/.morph/templates/infra/README.md +0 -286
- package/stacks/blazor-azure/.morph/templates/infra/app-insights.bicep +0 -63
- package/stacks/blazor-azure/.morph/templates/infra/app-service.bicep +0 -164
- package/stacks/blazor-azure/.morph/templates/infra/azure-pipelines-deploy.yml +0 -480
- package/stacks/blazor-azure/.morph/templates/infra/container-app-env.bicep +0 -49
- package/stacks/blazor-azure/.morph/templates/infra/container-app.bicep +0 -156
- package/stacks/blazor-azure/.morph/templates/infra/deploy-checklist.md +0 -426
- package/stacks/blazor-azure/.morph/templates/infra/deploy.ps1 +0 -229
- package/stacks/blazor-azure/.morph/templates/infra/deploy.sh +0 -208
- package/stacks/blazor-azure/.morph/templates/infra/key-vault.bicep +0 -91
- package/stacks/blazor-azure/.morph/templates/infra/main.bicep +0 -189
- package/stacks/blazor-azure/.morph/templates/infra/parameters.dev.json +0 -29
- package/stacks/blazor-azure/.morph/templates/infra/parameters.prod.json +0 -29
- package/stacks/blazor-azure/.morph/templates/infra/parameters.staging.json +0 -29
- package/stacks/blazor-azure/.morph/templates/infra/sql-database.bicep +0 -103
- package/stacks/blazor-azure/.morph/templates/infra/storage.bicep +0 -106
- package/stacks/blazor-azure/.morph/templates/integrations/asaas-client.cs +0 -387
- package/stacks/blazor-azure/.morph/templates/integrations/asaas-webhook.cs +0 -351
- package/stacks/blazor-azure/.morph/templates/integrations/azure-identity-config.cs +0 -288
- package/stacks/blazor-azure/.morph/templates/integrations/clerk-config.cs +0 -258
- package/stacks/blazor-azure/.morph/templates/job.cs +0 -171
- package/stacks/blazor-azure/.morph/templates/migration.cs +0 -83
- package/stacks/blazor-azure/.morph/templates/proposal.md +0 -141
- package/stacks/blazor-azure/.morph/templates/recap.md +0 -94
- package/stacks/blazor-azure/.morph/templates/repository.cs +0 -141
- package/stacks/blazor-azure/.morph/templates/saas/subscription.cs +0 -347
- package/stacks/blazor-azure/.morph/templates/saas/tenant.cs +0 -338
- package/stacks/blazor-azure/.morph/templates/service.cs +0 -139
- package/stacks/blazor-azure/.morph/templates/simulation.md +0 -353
- package/stacks/blazor-azure/.morph/templates/spec.md +0 -149
- package/stacks/blazor-azure/.morph/templates/sprint-status.yaml +0 -68
- package/stacks/blazor-azure/.morph/templates/state.template.json +0 -222
- package/stacks/blazor-azure/.morph/templates/story.md +0 -143
- package/stacks/blazor-azure/.morph/templates/tasks.md +0 -257
- package/stacks/blazor-azure/.morph/templates/test.cs +0 -239
- package/stacks/blazor-azure/.morph/templates/ui-components.md +0 -362
- package/stacks/blazor-azure/.morph/templates/ui-design-system.md +0 -286
- package/stacks/blazor-azure/.morph/templates/ui-flows.md +0 -336
- package/stacks/blazor-azure/.morph/templates/ui-mockups.md +0 -133
- package/stacks/nextjs-supabase/.morph/docs/easypanel-setup.md +0 -169
- package/stacks/nextjs-supabase/.morph/docs/supabase-mcp-setup.md +0 -247
- package/stacks/nextjs-supabase/.morph/examples/crud-nextjs-supabase/README.md +0 -697
- package/stacks/nextjs-supabase/.morph/examples/crud-nextjs-supabase/spec.md +0 -85
- package/stacks/nextjs-supabase/.morph/examples/crud-nextjs-supabase/tasks.md +0 -86
- package/stacks/nextjs-supabase/.morph/examples/saas-nextjs-supabase/README.md +0 -498
- package/stacks/nextjs-supabase/.morph/examples/saas-nextjs-supabase/decisions.md +0 -121
- package/stacks/nextjs-supabase/.morph/examples/saas-nextjs-supabase/spec.md +0 -138
- package/stacks/nextjs-supabase/.morph/examples/saas-nextjs-supabase/tasks.md +0 -162
- package/stacks/nextjs-supabase/.morph/standards/easypanel-deploy.md +0 -191
- package/stacks/nextjs-supabase/.morph/standards/nextjs-patterns.md +0 -193
- package/stacks/nextjs-supabase/.morph/standards/supabase-auth.md +0 -171
- package/stacks/nextjs-supabase/.morph/standards/supabase-pgvector.md +0 -164
- package/stacks/nextjs-supabase/.morph/standards/supabase-rls.md +0 -179
- package/stacks/nextjs-supabase/.morph/standards/supabase-storage.md +0 -148
- package/stacks/nextjs-supabase/.morph/templates/contracts.cs +0 -173
- package/stacks/nextjs-supabase/.morph/templates/contracts.ts +0 -168
- package/stacks/nextjs-supabase/.morph/templates/decisions.md +0 -115
- package/stacks/nextjs-supabase/.morph/templates/dockerfile-api.dockerfile +0 -38
- package/stacks/nextjs-supabase/.morph/templates/dockerfile-web.dockerfile +0 -48
- package/stacks/nextjs-supabase/.morph/templates/proposal.md +0 -145
- package/stacks/nextjs-supabase/.morph/templates/recap.md +0 -134
- package/stacks/nextjs-supabase/.morph/templates/rls-policy.sql +0 -57
- package/stacks/nextjs-supabase/.morph/templates/spec.md +0 -231
- package/stacks/nextjs-supabase/.morph/templates/supabase-migration.sql +0 -100
- package/stacks/nextjs-supabase/.morph/templates/tasks.md +0 -257
- /package/src/commands/{search-patterns.js → learning/search-patterns.js} +0 -0
- /package/src/commands/{migrate-state.js → utils/migrate-state.js} +0 -0
- /package/src/commands/{upgrade.js → utils/upgrade.js} +0 -0
- /package/src/{lib → core/templates}/template-data-sources.js +0 -0
- /package/src/lib/{design-system-generator.js → generators/design-system-generator.js} +0 -0
- /package/src/lib/{learning-system.js → learning/learning-system.js} +0 -0
- /package/{src/generator → stacks/blazor-azure/.morph/templates}/.gitkeep +0 -0
- /package/{src/llm → stacks/nextjs-supabase/.morph/templates}/.gitkeep +0 -0
|
@@ -1,356 +1,356 @@
|
|
|
1
|
-
import { join } from 'path';
|
|
2
|
-
import { execSync } from 'child_process';
|
|
3
|
-
import { platform } from 'os';
|
|
4
|
-
import fs from 'fs-extra';
|
|
5
|
-
import chalk from 'chalk';
|
|
6
|
-
import { logger } from '
|
|
7
|
-
import { pathExists, readJson } from '
|
|
8
|
-
import {
|
|
9
|
-
checkCLIOutdated,
|
|
10
|
-
checkProjectOutdated,
|
|
11
|
-
getInstalledCLIVersion
|
|
12
|
-
} from '
|
|
13
|
-
|
|
14
|
-
const isWindows = platform() === 'win32';
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Check if morph-spec command is in PATH
|
|
18
|
-
*/
|
|
19
|
-
function isMorphSpecInPath() {
|
|
20
|
-
try {
|
|
21
|
-
const command = isWindows ? 'where morph-spec' : 'which morph-spec';
|
|
22
|
-
execSync(command, { stdio: 'ignore' });
|
|
23
|
-
return true;
|
|
24
|
-
} catch {
|
|
25
|
-
return false;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Detect if nvm-windows is installed
|
|
31
|
-
*/
|
|
32
|
-
function isUsingNvmWindows() {
|
|
33
|
-
if (!isWindows) return false;
|
|
34
|
-
|
|
35
|
-
try {
|
|
36
|
-
const path = process.env.PATH || '';
|
|
37
|
-
return path.includes('nvm4w') || path.includes('\\nvm\\');
|
|
38
|
-
} catch {
|
|
39
|
-
return false;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Get npm global prefix
|
|
45
|
-
*/
|
|
46
|
-
function getNpmGlobalPrefix() {
|
|
47
|
-
try {
|
|
48
|
-
const prefix = execSync('npm config get prefix', { encoding: 'utf8' }).trim();
|
|
49
|
-
return prefix;
|
|
50
|
-
} catch {
|
|
51
|
-
return null;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export async function doctorCommand() {
|
|
56
|
-
const targetPath = process.cwd();
|
|
57
|
-
|
|
58
|
-
logger.header('MORPH-SPEC Health Check');
|
|
59
|
-
|
|
60
|
-
const checks = [];
|
|
61
|
-
let hasErrors = false;
|
|
62
|
-
let hasWarnings = false;
|
|
63
|
-
|
|
64
|
-
// Check versions first
|
|
65
|
-
const cliCheck = await checkCLIOutdated();
|
|
66
|
-
const projectCheck = await checkProjectOutdated(targetPath);
|
|
67
|
-
|
|
68
|
-
// CLI Version
|
|
69
|
-
if (cliCheck.latest) {
|
|
70
|
-
if (cliCheck.isOutdated) {
|
|
71
|
-
checks.push({
|
|
72
|
-
name: `CLI version (${cliCheck.current})`,
|
|
73
|
-
status: 'warn',
|
|
74
|
-
msg: `outdated, ${cliCheck.latest} available`
|
|
75
|
-
});
|
|
76
|
-
hasWarnings = true;
|
|
77
|
-
} else {
|
|
78
|
-
checks.push({
|
|
79
|
-
name: `CLI version (${cliCheck.current})`,
|
|
80
|
-
status: 'ok',
|
|
81
|
-
msg: 'latest'
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
} else {
|
|
85
|
-
checks.push({
|
|
86
|
-
name: `CLI version (${cliCheck.current})`,
|
|
87
|
-
status: 'ok'
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// Project MORPH version
|
|
92
|
-
if (projectCheck.current) {
|
|
93
|
-
if (projectCheck.isOutdated) {
|
|
94
|
-
checks.push({
|
|
95
|
-
name: `Project MORPH version (${projectCheck.current})`,
|
|
96
|
-
status: 'warn',
|
|
97
|
-
msg: `outdated, ${projectCheck.cliVersion} available`
|
|
98
|
-
});
|
|
99
|
-
hasWarnings = true;
|
|
100
|
-
} else {
|
|
101
|
-
checks.push({
|
|
102
|
-
name: `Project MORPH version (${projectCheck.current})`,
|
|
103
|
-
status: 'ok'
|
|
104
|
-
});
|
|
105
|
-
}
|
|
106
|
-
} else {
|
|
107
|
-
checks.push({
|
|
108
|
-
name: 'Project MORPH version',
|
|
109
|
-
status: 'warn',
|
|
110
|
-
msg: 'not found (legacy installation)'
|
|
111
|
-
});
|
|
112
|
-
hasWarnings = true;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// Check if morph-spec is in PATH
|
|
116
|
-
const inPath = isMorphSpecInPath();
|
|
117
|
-
if (inPath) {
|
|
118
|
-
checks.push({
|
|
119
|
-
name: 'morph-spec in PATH',
|
|
120
|
-
status: 'ok'
|
|
121
|
-
});
|
|
122
|
-
} else {
|
|
123
|
-
checks.push({
|
|
124
|
-
name: 'morph-spec in PATH',
|
|
125
|
-
status: 'warn',
|
|
126
|
-
msg: 'command not found'
|
|
127
|
-
});
|
|
128
|
-
hasWarnings = true;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
// Check CLAUDE.md
|
|
132
|
-
const claudeMd = join(targetPath, 'CLAUDE.md');
|
|
133
|
-
if (await pathExists(claudeMd)) {
|
|
134
|
-
checks.push({ name: 'CLAUDE.md', status: 'ok' });
|
|
135
|
-
} else {
|
|
136
|
-
checks.push({ name: 'CLAUDE.md', status: 'missing' });
|
|
137
|
-
hasErrors = true;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
// Check .morph folder
|
|
141
|
-
const morphPath = join(targetPath, '.morph');
|
|
142
|
-
if (await pathExists(morphPath)) {
|
|
143
|
-
checks.push({ name: '.morph/', status: 'ok' });
|
|
144
|
-
} else {
|
|
145
|
-
checks.push({ name: '.morph/', status: 'missing' });
|
|
146
|
-
hasErrors = true;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// Check config.json
|
|
150
|
-
const configPath = join(morphPath, 'config', 'config.json');
|
|
151
|
-
if (await pathExists(configPath)) {
|
|
152
|
-
try {
|
|
153
|
-
const config = await readJson(configPath);
|
|
154
|
-
if (config.project?.name) {
|
|
155
|
-
checks.push({ name: 'config.json', status: 'ok' });
|
|
156
|
-
} else {
|
|
157
|
-
checks.push({ name: 'config.json', status: 'warn', msg: 'project.name not set' });
|
|
158
|
-
}
|
|
159
|
-
} catch {
|
|
160
|
-
checks.push({ name: 'config.json', status: 'error', msg: 'invalid JSON' });
|
|
161
|
-
hasErrors = true;
|
|
162
|
-
}
|
|
163
|
-
} else {
|
|
164
|
-
checks.push({ name: 'config.json', status: 'missing' });
|
|
165
|
-
hasErrors = true;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
// Check agents.json
|
|
169
|
-
const agentsPath = join(morphPath, 'config', 'agents.json');
|
|
170
|
-
if (await pathExists(agentsPath)) {
|
|
171
|
-
checks.push({ name: 'agents.json', status: 'ok' });
|
|
172
|
-
} else {
|
|
173
|
-
checks.push({ name: 'agents.json', status: 'missing' });
|
|
174
|
-
hasErrors = true;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
// Check standards
|
|
178
|
-
const standardsPath = join(morphPath, 'standards');
|
|
179
|
-
if (await pathExists(standardsPath)) {
|
|
180
|
-
const codingStd = join(standardsPath, 'coding.md');
|
|
181
|
-
const archStd = join(standardsPath, 'architecture.md');
|
|
182
|
-
const azureStd = join(standardsPath, 'azure.md');
|
|
183
|
-
|
|
184
|
-
const hasAll = await Promise.all([
|
|
185
|
-
pathExists(codingStd),
|
|
186
|
-
pathExists(archStd),
|
|
187
|
-
pathExists(azureStd)
|
|
188
|
-
]).then(results => results.every(Boolean));
|
|
189
|
-
|
|
190
|
-
if (hasAll) {
|
|
191
|
-
checks.push({ name: 'standards/', status: 'ok' });
|
|
192
|
-
} else {
|
|
193
|
-
checks.push({ name: 'standards/', status: 'warn', msg: 'some files missing' });
|
|
194
|
-
}
|
|
195
|
-
} else {
|
|
196
|
-
checks.push({ name: 'standards/', status: 'missing' });
|
|
197
|
-
hasErrors = true;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
// Check templates
|
|
201
|
-
const templatesPath = join(morphPath, 'templates');
|
|
202
|
-
if (await pathExists(templatesPath)) {
|
|
203
|
-
checks.push({ name: 'templates/', status: 'ok' });
|
|
204
|
-
} else {
|
|
205
|
-
checks.push({ name: 'templates/', status: 'missing' });
|
|
206
|
-
hasErrors = true;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
// Check .claude folder
|
|
210
|
-
const claudePath = join(targetPath, '.claude');
|
|
211
|
-
if (await pathExists(claudePath)) {
|
|
212
|
-
const commandsPath = join(claudePath, 'commands');
|
|
213
|
-
const skillsPath = join(claudePath, 'skills');
|
|
214
|
-
|
|
215
|
-
const hasCommands = await pathExists(commandsPath);
|
|
216
|
-
const hasSkills = await pathExists(skillsPath);
|
|
217
|
-
|
|
218
|
-
if (hasCommands && hasSkills) {
|
|
219
|
-
checks.push({ name: '.claude/', status: 'ok' });
|
|
220
|
-
|
|
221
|
-
// Check skills link status
|
|
222
|
-
const skillEntries = await fs.readdir(skillsPath, { withFileTypes: true });
|
|
223
|
-
const skillDirs = skillEntries.filter(e => e.isDirectory() || e.isSymbolicLink());
|
|
224
|
-
let linkedCount = 0;
|
|
225
|
-
|
|
226
|
-
for (const dir of skillDirs) {
|
|
227
|
-
try {
|
|
228
|
-
const stat = await fs.lstat(join(skillsPath, dir.name));
|
|
229
|
-
if (stat.isSymbolicLink()) linkedCount++;
|
|
230
|
-
} catch { /* ignore */ }
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
if (skillDirs.length > 0) {
|
|
234
|
-
if (linkedCount === skillDirs.length) {
|
|
235
|
-
checks.push({ name: '.claude/skills/ links', status: 'ok', msg: `${linkedCount} categories linked` });
|
|
236
|
-
} else if (linkedCount > 0) {
|
|
237
|
-
checks.push({ name: '.claude/skills/ links', status: 'warn', msg: `${linkedCount}/${skillDirs.length} linked, rest copied` });
|
|
238
|
-
hasWarnings = true;
|
|
239
|
-
} else {
|
|
240
|
-
checks.push({ name: '.claude/skills/ links', status: 'warn', msg: 'all categories copied (no auto-update)' });
|
|
241
|
-
hasWarnings = true;
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
} else {
|
|
245
|
-
checks.push({ name: '.claude/', status: 'warn', msg: 'incomplete structure' });
|
|
246
|
-
}
|
|
247
|
-
} else {
|
|
248
|
-
checks.push({ name: '.claude/', status: 'missing' });
|
|
249
|
-
hasErrors = true;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
// Check state.json
|
|
253
|
-
const statePath = join(targetPath, '.morph', 'state.json');
|
|
254
|
-
if (await pathExists(statePath)) {
|
|
255
|
-
try {
|
|
256
|
-
const stateContent = await readJson(statePath);
|
|
257
|
-
if (stateContent.version && stateContent.features) {
|
|
258
|
-
const featureCount = Object.keys(stateContent.features).length;
|
|
259
|
-
checks.push({ name: 'state.json', status: 'ok', msg: `${featureCount} feature(s)` });
|
|
260
|
-
} else {
|
|
261
|
-
checks.push({ name: 'state.json', status: 'warn', msg: 'invalid schema' });
|
|
262
|
-
hasWarnings = true;
|
|
263
|
-
}
|
|
264
|
-
} catch {
|
|
265
|
-
checks.push({ name: 'state.json', status: 'error', msg: 'invalid JSON' });
|
|
266
|
-
hasErrors = true;
|
|
267
|
-
}
|
|
268
|
-
} else {
|
|
269
|
-
checks.push({ name: 'state.json', status: 'warn', msg: 'not initialized (run: morph-spec state init)' });
|
|
270
|
-
hasWarnings = true;
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
// Check key templates exist
|
|
274
|
-
const keyTemplates = ['proposal.md', 'spec.md', 'tasks.md', 'decisions.md'];
|
|
275
|
-
const missingTemplates = [];
|
|
276
|
-
for (const template of keyTemplates) {
|
|
277
|
-
const templatePath = join(templatesPath, template);
|
|
278
|
-
if (!(await pathExists(templatePath))) {
|
|
279
|
-
missingTemplates.push(template);
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
if (missingTemplates.length === 0) {
|
|
284
|
-
checks.push({ name: 'key templates', status: 'ok' });
|
|
285
|
-
} else if (missingTemplates.length < keyTemplates.length) {
|
|
286
|
-
checks.push({ name: 'key templates', status: 'warn', msg: `missing: ${missingTemplates.join(', ')}` });
|
|
287
|
-
hasWarnings = true;
|
|
288
|
-
} else {
|
|
289
|
-
checks.push({ name: 'key templates', status: 'missing' });
|
|
290
|
-
hasErrors = true;
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
// Check Node.js version
|
|
294
|
-
const nodeVersion = process.version;
|
|
295
|
-
const majorVersion = parseInt(nodeVersion.slice(1).split('.')[0], 10);
|
|
296
|
-
if (majorVersion >= 18) {
|
|
297
|
-
checks.push({ name: `Node.js ${nodeVersion}`, status: 'ok' });
|
|
298
|
-
} else {
|
|
299
|
-
checks.push({ name: `Node.js ${nodeVersion}`, status: 'warn', msg: 'v18+ recommended' });
|
|
300
|
-
hasWarnings = true;
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
// Display results
|
|
304
|
-
for (const check of checks) {
|
|
305
|
-
if (check.status === 'ok') {
|
|
306
|
-
console.log(chalk.green(` ✓ ${check.name}`));
|
|
307
|
-
} else if (check.status === 'warn') {
|
|
308
|
-
console.log(chalk.yellow(` ⚠ ${check.name}`) + chalk.gray(` (${check.msg})`));
|
|
309
|
-
} else if (check.status === 'missing') {
|
|
310
|
-
console.log(chalk.red(` ✗ ${check.name}`) + chalk.gray(' (missing)'));
|
|
311
|
-
} else if (check.status === 'error') {
|
|
312
|
-
console.log(chalk.red(` ✗ ${check.name}`) + chalk.gray(` (${check.msg})`));
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
logger.blank();
|
|
317
|
-
|
|
318
|
-
if (hasErrors) {
|
|
319
|
-
logger.error('Some checks failed. Run "morph-spec init --force" to fix.');
|
|
320
|
-
process.exit(1);
|
|
321
|
-
} else if (hasWarnings) {
|
|
322
|
-
logger.warn('Some checks need attention.');
|
|
323
|
-
logger.blank();
|
|
324
|
-
|
|
325
|
-
if (!inPath) {
|
|
326
|
-
const usingNvm = isUsingNvmWindows();
|
|
327
|
-
const prefix = getNpmGlobalPrefix();
|
|
328
|
-
|
|
329
|
-
if (usingNvm && prefix) {
|
|
330
|
-
logger.info('PATH Issue Detected (nvm-windows):');
|
|
331
|
-
logger.dim(` Add to PATH: ${prefix}`);
|
|
332
|
-
logger.blank();
|
|
333
|
-
logger.dim(' PowerShell (as Administrator):');
|
|
334
|
-
console.log(chalk.gray(` [Environment]::SetEnvironmentVariable("Path", [Environment]::GetEnvironmentVariable("Path", "User") + ";${prefix}", "User")`));
|
|
335
|
-
logger.blank();
|
|
336
|
-
logger.dim(' Or use npx instead:');
|
|
337
|
-
logger.dim(' npx @polymorphism-tech/morph-spec init');
|
|
338
|
-
logger.blank();
|
|
339
|
-
} else if (!inPath) {
|
|
340
|
-
logger.info('morph-spec not in PATH:');
|
|
341
|
-
logger.dim(' Use npx instead:');
|
|
342
|
-
logger.dim(' npx @polymorphism-tech/morph-spec init');
|
|
343
|
-
logger.blank();
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
if (cliCheck.isOutdated || projectCheck.isOutdated) {
|
|
348
|
-
logger.info('To update:');
|
|
349
|
-
logger.dim(' 1. Update the CLI: npm install -g @polymorphism-tech/morph-spec@latest');
|
|
350
|
-
logger.dim(' 2. Update the project: morph-spec update');
|
|
351
|
-
logger.blank();
|
|
352
|
-
}
|
|
353
|
-
} else {
|
|
354
|
-
logger.success('All checks passed!');
|
|
355
|
-
}
|
|
356
|
-
}
|
|
1
|
+
import { join } from 'path';
|
|
2
|
+
import { execSync } from 'child_process';
|
|
3
|
+
import { platform } from 'os';
|
|
4
|
+
import fs from 'fs-extra';
|
|
5
|
+
import chalk from 'chalk';
|
|
6
|
+
import { logger } from '../../utils/logger.js';
|
|
7
|
+
import { pathExists, readJson } from '../../utils/file-copier.js';
|
|
8
|
+
import {
|
|
9
|
+
checkCLIOutdated,
|
|
10
|
+
checkProjectOutdated,
|
|
11
|
+
getInstalledCLIVersion
|
|
12
|
+
} from '../../utils/version-checker.js';
|
|
13
|
+
|
|
14
|
+
const isWindows = platform() === 'win32';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Check if morph-spec command is in PATH
|
|
18
|
+
*/
|
|
19
|
+
function isMorphSpecInPath() {
|
|
20
|
+
try {
|
|
21
|
+
const command = isWindows ? 'where morph-spec' : 'which morph-spec';
|
|
22
|
+
execSync(command, { stdio: 'ignore' });
|
|
23
|
+
return true;
|
|
24
|
+
} catch {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Detect if nvm-windows is installed
|
|
31
|
+
*/
|
|
32
|
+
function isUsingNvmWindows() {
|
|
33
|
+
if (!isWindows) return false;
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
const path = process.env.PATH || '';
|
|
37
|
+
return path.includes('nvm4w') || path.includes('\\nvm\\');
|
|
38
|
+
} catch {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Get npm global prefix
|
|
45
|
+
*/
|
|
46
|
+
function getNpmGlobalPrefix() {
|
|
47
|
+
try {
|
|
48
|
+
const prefix = execSync('npm config get prefix', { encoding: 'utf8' }).trim();
|
|
49
|
+
return prefix;
|
|
50
|
+
} catch {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export async function doctorCommand() {
|
|
56
|
+
const targetPath = process.cwd();
|
|
57
|
+
|
|
58
|
+
logger.header('MORPH-SPEC Health Check');
|
|
59
|
+
|
|
60
|
+
const checks = [];
|
|
61
|
+
let hasErrors = false;
|
|
62
|
+
let hasWarnings = false;
|
|
63
|
+
|
|
64
|
+
// Check versions first
|
|
65
|
+
const cliCheck = await checkCLIOutdated();
|
|
66
|
+
const projectCheck = await checkProjectOutdated(targetPath);
|
|
67
|
+
|
|
68
|
+
// CLI Version
|
|
69
|
+
if (cliCheck.latest) {
|
|
70
|
+
if (cliCheck.isOutdated) {
|
|
71
|
+
checks.push({
|
|
72
|
+
name: `CLI version (${cliCheck.current})`,
|
|
73
|
+
status: 'warn',
|
|
74
|
+
msg: `outdated, ${cliCheck.latest} available`
|
|
75
|
+
});
|
|
76
|
+
hasWarnings = true;
|
|
77
|
+
} else {
|
|
78
|
+
checks.push({
|
|
79
|
+
name: `CLI version (${cliCheck.current})`,
|
|
80
|
+
status: 'ok',
|
|
81
|
+
msg: 'latest'
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
} else {
|
|
85
|
+
checks.push({
|
|
86
|
+
name: `CLI version (${cliCheck.current})`,
|
|
87
|
+
status: 'ok'
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Project MORPH version
|
|
92
|
+
if (projectCheck.current) {
|
|
93
|
+
if (projectCheck.isOutdated) {
|
|
94
|
+
checks.push({
|
|
95
|
+
name: `Project MORPH version (${projectCheck.current})`,
|
|
96
|
+
status: 'warn',
|
|
97
|
+
msg: `outdated, ${projectCheck.cliVersion} available`
|
|
98
|
+
});
|
|
99
|
+
hasWarnings = true;
|
|
100
|
+
} else {
|
|
101
|
+
checks.push({
|
|
102
|
+
name: `Project MORPH version (${projectCheck.current})`,
|
|
103
|
+
status: 'ok'
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
} else {
|
|
107
|
+
checks.push({
|
|
108
|
+
name: 'Project MORPH version',
|
|
109
|
+
status: 'warn',
|
|
110
|
+
msg: 'not found (legacy installation)'
|
|
111
|
+
});
|
|
112
|
+
hasWarnings = true;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Check if morph-spec is in PATH
|
|
116
|
+
const inPath = isMorphSpecInPath();
|
|
117
|
+
if (inPath) {
|
|
118
|
+
checks.push({
|
|
119
|
+
name: 'morph-spec in PATH',
|
|
120
|
+
status: 'ok'
|
|
121
|
+
});
|
|
122
|
+
} else {
|
|
123
|
+
checks.push({
|
|
124
|
+
name: 'morph-spec in PATH',
|
|
125
|
+
status: 'warn',
|
|
126
|
+
msg: 'command not found'
|
|
127
|
+
});
|
|
128
|
+
hasWarnings = true;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Check CLAUDE.md
|
|
132
|
+
const claudeMd = join(targetPath, 'CLAUDE.md');
|
|
133
|
+
if (await pathExists(claudeMd)) {
|
|
134
|
+
checks.push({ name: 'CLAUDE.md', status: 'ok' });
|
|
135
|
+
} else {
|
|
136
|
+
checks.push({ name: 'CLAUDE.md', status: 'missing' });
|
|
137
|
+
hasErrors = true;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Check .morph folder
|
|
141
|
+
const morphPath = join(targetPath, '.morph');
|
|
142
|
+
if (await pathExists(morphPath)) {
|
|
143
|
+
checks.push({ name: '.morph/', status: 'ok' });
|
|
144
|
+
} else {
|
|
145
|
+
checks.push({ name: '.morph/', status: 'missing' });
|
|
146
|
+
hasErrors = true;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Check config.json
|
|
150
|
+
const configPath = join(morphPath, 'config', 'config.json');
|
|
151
|
+
if (await pathExists(configPath)) {
|
|
152
|
+
try {
|
|
153
|
+
const config = await readJson(configPath);
|
|
154
|
+
if (config.project?.name) {
|
|
155
|
+
checks.push({ name: 'config.json', status: 'ok' });
|
|
156
|
+
} else {
|
|
157
|
+
checks.push({ name: 'config.json', status: 'warn', msg: 'project.name not set' });
|
|
158
|
+
}
|
|
159
|
+
} catch {
|
|
160
|
+
checks.push({ name: 'config.json', status: 'error', msg: 'invalid JSON' });
|
|
161
|
+
hasErrors = true;
|
|
162
|
+
}
|
|
163
|
+
} else {
|
|
164
|
+
checks.push({ name: 'config.json', status: 'missing' });
|
|
165
|
+
hasErrors = true;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Check agents.json
|
|
169
|
+
const agentsPath = join(morphPath, 'config', 'agents.json');
|
|
170
|
+
if (await pathExists(agentsPath)) {
|
|
171
|
+
checks.push({ name: 'agents.json', status: 'ok' });
|
|
172
|
+
} else {
|
|
173
|
+
checks.push({ name: 'agents.json', status: 'missing' });
|
|
174
|
+
hasErrors = true;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Check standards
|
|
178
|
+
const standardsPath = join(morphPath, 'standards');
|
|
179
|
+
if (await pathExists(standardsPath)) {
|
|
180
|
+
const codingStd = join(standardsPath, 'coding.md');
|
|
181
|
+
const archStd = join(standardsPath, 'architecture.md');
|
|
182
|
+
const azureStd = join(standardsPath, 'azure.md');
|
|
183
|
+
|
|
184
|
+
const hasAll = await Promise.all([
|
|
185
|
+
pathExists(codingStd),
|
|
186
|
+
pathExists(archStd),
|
|
187
|
+
pathExists(azureStd)
|
|
188
|
+
]).then(results => results.every(Boolean));
|
|
189
|
+
|
|
190
|
+
if (hasAll) {
|
|
191
|
+
checks.push({ name: 'standards/', status: 'ok' });
|
|
192
|
+
} else {
|
|
193
|
+
checks.push({ name: 'standards/', status: 'warn', msg: 'some files missing' });
|
|
194
|
+
}
|
|
195
|
+
} else {
|
|
196
|
+
checks.push({ name: 'standards/', status: 'missing' });
|
|
197
|
+
hasErrors = true;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Check templates
|
|
201
|
+
const templatesPath = join(morphPath, 'templates');
|
|
202
|
+
if (await pathExists(templatesPath)) {
|
|
203
|
+
checks.push({ name: 'templates/', status: 'ok' });
|
|
204
|
+
} else {
|
|
205
|
+
checks.push({ name: 'templates/', status: 'missing' });
|
|
206
|
+
hasErrors = true;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Check .claude folder
|
|
210
|
+
const claudePath = join(targetPath, '.claude');
|
|
211
|
+
if (await pathExists(claudePath)) {
|
|
212
|
+
const commandsPath = join(claudePath, 'commands');
|
|
213
|
+
const skillsPath = join(claudePath, 'skills');
|
|
214
|
+
|
|
215
|
+
const hasCommands = await pathExists(commandsPath);
|
|
216
|
+
const hasSkills = await pathExists(skillsPath);
|
|
217
|
+
|
|
218
|
+
if (hasCommands && hasSkills) {
|
|
219
|
+
checks.push({ name: '.claude/', status: 'ok' });
|
|
220
|
+
|
|
221
|
+
// Check skills link status
|
|
222
|
+
const skillEntries = await fs.readdir(skillsPath, { withFileTypes: true });
|
|
223
|
+
const skillDirs = skillEntries.filter(e => e.isDirectory() || e.isSymbolicLink());
|
|
224
|
+
let linkedCount = 0;
|
|
225
|
+
|
|
226
|
+
for (const dir of skillDirs) {
|
|
227
|
+
try {
|
|
228
|
+
const stat = await fs.lstat(join(skillsPath, dir.name));
|
|
229
|
+
if (stat.isSymbolicLink()) linkedCount++;
|
|
230
|
+
} catch { /* ignore */ }
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (skillDirs.length > 0) {
|
|
234
|
+
if (linkedCount === skillDirs.length) {
|
|
235
|
+
checks.push({ name: '.claude/skills/ links', status: 'ok', msg: `${linkedCount} categories linked` });
|
|
236
|
+
} else if (linkedCount > 0) {
|
|
237
|
+
checks.push({ name: '.claude/skills/ links', status: 'warn', msg: `${linkedCount}/${skillDirs.length} linked, rest copied` });
|
|
238
|
+
hasWarnings = true;
|
|
239
|
+
} else {
|
|
240
|
+
checks.push({ name: '.claude/skills/ links', status: 'warn', msg: 'all categories copied (no auto-update)' });
|
|
241
|
+
hasWarnings = true;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
} else {
|
|
245
|
+
checks.push({ name: '.claude/', status: 'warn', msg: 'incomplete structure' });
|
|
246
|
+
}
|
|
247
|
+
} else {
|
|
248
|
+
checks.push({ name: '.claude/', status: 'missing' });
|
|
249
|
+
hasErrors = true;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// Check state.json
|
|
253
|
+
const statePath = join(targetPath, '.morph', 'state.json');
|
|
254
|
+
if (await pathExists(statePath)) {
|
|
255
|
+
try {
|
|
256
|
+
const stateContent = await readJson(statePath);
|
|
257
|
+
if (stateContent.version && stateContent.features) {
|
|
258
|
+
const featureCount = Object.keys(stateContent.features).length;
|
|
259
|
+
checks.push({ name: 'state.json', status: 'ok', msg: `${featureCount} feature(s)` });
|
|
260
|
+
} else {
|
|
261
|
+
checks.push({ name: 'state.json', status: 'warn', msg: 'invalid schema' });
|
|
262
|
+
hasWarnings = true;
|
|
263
|
+
}
|
|
264
|
+
} catch {
|
|
265
|
+
checks.push({ name: 'state.json', status: 'error', msg: 'invalid JSON' });
|
|
266
|
+
hasErrors = true;
|
|
267
|
+
}
|
|
268
|
+
} else {
|
|
269
|
+
checks.push({ name: 'state.json', status: 'warn', msg: 'not initialized (run: morph-spec state init)' });
|
|
270
|
+
hasWarnings = true;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// Check key templates exist
|
|
274
|
+
const keyTemplates = ['proposal.md', 'spec.md', 'tasks.md', 'decisions.md'];
|
|
275
|
+
const missingTemplates = [];
|
|
276
|
+
for (const template of keyTemplates) {
|
|
277
|
+
const templatePath = join(templatesPath, template);
|
|
278
|
+
if (!(await pathExists(templatePath))) {
|
|
279
|
+
missingTemplates.push(template);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
if (missingTemplates.length === 0) {
|
|
284
|
+
checks.push({ name: 'key templates', status: 'ok' });
|
|
285
|
+
} else if (missingTemplates.length < keyTemplates.length) {
|
|
286
|
+
checks.push({ name: 'key templates', status: 'warn', msg: `missing: ${missingTemplates.join(', ')}` });
|
|
287
|
+
hasWarnings = true;
|
|
288
|
+
} else {
|
|
289
|
+
checks.push({ name: 'key templates', status: 'missing' });
|
|
290
|
+
hasErrors = true;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// Check Node.js version
|
|
294
|
+
const nodeVersion = process.version;
|
|
295
|
+
const majorVersion = parseInt(nodeVersion.slice(1).split('.')[0], 10);
|
|
296
|
+
if (majorVersion >= 18) {
|
|
297
|
+
checks.push({ name: `Node.js ${nodeVersion}`, status: 'ok' });
|
|
298
|
+
} else {
|
|
299
|
+
checks.push({ name: `Node.js ${nodeVersion}`, status: 'warn', msg: 'v18+ recommended' });
|
|
300
|
+
hasWarnings = true;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// Display results
|
|
304
|
+
for (const check of checks) {
|
|
305
|
+
if (check.status === 'ok') {
|
|
306
|
+
console.log(chalk.green(` ✓ ${check.name}`));
|
|
307
|
+
} else if (check.status === 'warn') {
|
|
308
|
+
console.log(chalk.yellow(` ⚠ ${check.name}`) + chalk.gray(` (${check.msg})`));
|
|
309
|
+
} else if (check.status === 'missing') {
|
|
310
|
+
console.log(chalk.red(` ✗ ${check.name}`) + chalk.gray(' (missing)'));
|
|
311
|
+
} else if (check.status === 'error') {
|
|
312
|
+
console.log(chalk.red(` ✗ ${check.name}`) + chalk.gray(` (${check.msg})`));
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
logger.blank();
|
|
317
|
+
|
|
318
|
+
if (hasErrors) {
|
|
319
|
+
logger.error('Some checks failed. Run "morph-spec init --force" to fix.');
|
|
320
|
+
process.exit(1);
|
|
321
|
+
} else if (hasWarnings) {
|
|
322
|
+
logger.warn('Some checks need attention.');
|
|
323
|
+
logger.blank();
|
|
324
|
+
|
|
325
|
+
if (!inPath) {
|
|
326
|
+
const usingNvm = isUsingNvmWindows();
|
|
327
|
+
const prefix = getNpmGlobalPrefix();
|
|
328
|
+
|
|
329
|
+
if (usingNvm && prefix) {
|
|
330
|
+
logger.info('PATH Issue Detected (nvm-windows):');
|
|
331
|
+
logger.dim(` Add to PATH: ${prefix}`);
|
|
332
|
+
logger.blank();
|
|
333
|
+
logger.dim(' PowerShell (as Administrator):');
|
|
334
|
+
console.log(chalk.gray(` [Environment]::SetEnvironmentVariable("Path", [Environment]::GetEnvironmentVariable("Path", "User") + ";${prefix}", "User")`));
|
|
335
|
+
logger.blank();
|
|
336
|
+
logger.dim(' Or use npx instead:');
|
|
337
|
+
logger.dim(' npx @polymorphism-tech/morph-spec init');
|
|
338
|
+
logger.blank();
|
|
339
|
+
} else if (!inPath) {
|
|
340
|
+
logger.info('morph-spec not in PATH:');
|
|
341
|
+
logger.dim(' Use npx instead:');
|
|
342
|
+
logger.dim(' npx @polymorphism-tech/morph-spec init');
|
|
343
|
+
logger.blank();
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
if (cliCheck.isOutdated || projectCheck.isOutdated) {
|
|
348
|
+
logger.info('To update:');
|
|
349
|
+
logger.dim(' 1. Update the CLI: npm install -g @polymorphism-tech/morph-spec@latest');
|
|
350
|
+
logger.dim(' 2. Update the project: morph-spec update');
|
|
351
|
+
logger.blank();
|
|
352
|
+
}
|
|
353
|
+
} else {
|
|
354
|
+
logger.success('All checks passed!');
|
|
355
|
+
}
|
|
356
|
+
}
|