@polymorphism-tech/morph-spec 3.1.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 +882 -3
- package/README.md +79 -18
- package/bin/detect-agents.js +1 -1
- package/bin/morph-spec.js +163 -26
- package/bin/task-manager.cjs +101 -7
- package/bin/validate.js +1 -1
- package/docs/cli-auto-detection.md +219 -0
- package/docs/getting-started.md +0 -5
- package/docs/llm-interaction-config.md +735 -0
- package/docs/troubleshooting.md +269 -0
- 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 +5 -1
- package/src/commands/agents/index.js +4 -0
- package/src/commands/agents/spawn-team.js +172 -0
- 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} +130 -3
- package/src/commands/generation/index.js +5 -0
- package/src/commands/index.js +16 -0
- package/src/commands/learning/capture-pattern.js +121 -0
- package/src/commands/learning/index.js +5 -0
- package/src/commands/learning/search-patterns.js +126 -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 -258
- package/src/commands/{sync.js → project/sync.js} +167 -167
- package/src/commands/{update.js → project/update.js} +240 -204
- package/src/commands/{advance-phase.js → state/advance-phase.js} +416 -266
- package/src/commands/state/approve.js +221 -0
- 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/utils/migrate-state.js +158 -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/utils/upgrade.js +346 -0
- 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/core/state/phase-state-machine.js +214 -0
- package/src/{lib → core/state}/state-manager.js +572 -414
- package/src/core/templates/index.js +9 -0
- package/src/core/templates/template-data-sources.js +325 -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/generator/config-generator.js +206 -0
- package/src/generator/templates/config.json.template +40 -0
- package/src/generator/templates/project.md.template +67 -0
- package/src/lib/{complexity-analyzer.js → analysis/complexity-analyzer.js} +441 -441
- package/src/lib/analysis/index.js +7 -0
- package/src/lib/checkpoints/checkpoint-hooks.js +258 -0
- 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/generators/metadata-extractor.js +387 -0
- 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 -0
- 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/llm/analyzer.js +215 -0
- package/src/llm/environment-detector.js +43 -0
- package/src/llm/few-shot-examples.js +216 -0
- package/src/llm/project-config-schema.json +188 -0
- package/src/llm/prompt-builder.js +96 -0
- package/src/orchestrator.js +206 -0
- package/src/sanitizer/context-sanitizer.js +221 -0
- package/src/sanitizer/patterns.js +163 -0
- package/src/scanner/project-scanner.js +242 -0
- package/src/ui/diff-display.js +91 -0
- package/src/ui/interactive-wizard.js +96 -0
- package/src/ui/user-review.js +211 -0
- package/src/ui/wizard-questions.js +188 -0
- 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/src/writer/file-writer.js +86 -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/.gitkeep +0 -0
- 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/.gitkeep +0 -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 -303
- 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/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/lib/{design-system-generator.js → generators/design-system-generator.js} +0 -0
- /package/src/lib/{learning-system.js → learning/learning-system.js} +0 -0
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Template Validator - Validates Handlebars templates
|
|
3
|
+
* Checks for:
|
|
4
|
+
* - Required placeholders are present
|
|
5
|
+
* - No deprecated pre-computed variables ({FEATURE_NAME_PASCAL})
|
|
6
|
+
* - Valid Handlebars syntax
|
|
7
|
+
* - Proper helper usage
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { readFileSync } from 'fs';
|
|
11
|
+
import { getTemplateById, getAllTemplates } from './template-registry.js';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Deprecated placeholder patterns (v1.0 pre-computed variables)
|
|
15
|
+
*/
|
|
16
|
+
const DEPRECATED_PATTERNS = [
|
|
17
|
+
{ pattern: /\{\{FEATURE_NAME_PASCAL\}\}/g, replacement: '{{pascalCase FEATURE_NAME}}' },
|
|
18
|
+
{ pattern: /\{\{FEATURE_NAME_CAMEL\}\}/g, replacement: '{{camelCase FEATURE_NAME}}' },
|
|
19
|
+
{ pattern: /\{\{FEATURE_NAME_SNAKE\}\}/g, replacement: '{{snakeCase FEATURE_NAME}}' },
|
|
20
|
+
{ pattern: /\{\{FEATURE_NAME_UPPER_SNAKE\}\}/g, replacement: '{{upperSnakeCase FEATURE_NAME}}' },
|
|
21
|
+
{ pattern: /\{\{FEATURE_NAME_TITLE\}\}/g, replacement: '{{titleCase FEATURE_NAME}}' },
|
|
22
|
+
{ pattern: /\{\{FEATURE_NAME_KEBAB\}\}/g, replacement: '{{kebabCase FEATURE_NAME}}' },
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Old template syntax (non-Handlebars)
|
|
27
|
+
*/
|
|
28
|
+
const OLD_SYNTAX_PATTERNS = [
|
|
29
|
+
{ pattern: /\{Feature\}/g, description: 'Old {Feature} syntax (use {{pascalCase FEATURE_NAME}})' },
|
|
30
|
+
{ pattern: /\{feature\}/g, description: 'Old {feature} syntax (use {{kebabCase FEATURE_NAME}})' },
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Validation result
|
|
35
|
+
*/
|
|
36
|
+
class ValidationResult {
|
|
37
|
+
constructor(templateId, templatePath) {
|
|
38
|
+
this.templateId = templateId;
|
|
39
|
+
this.templatePath = templatePath;
|
|
40
|
+
this.valid = true;
|
|
41
|
+
this.errors = [];
|
|
42
|
+
this.warnings = [];
|
|
43
|
+
this.info = [];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
addError(message) {
|
|
47
|
+
this.errors.push(message);
|
|
48
|
+
this.valid = false;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
addWarning(message) {
|
|
52
|
+
this.warnings.push(message);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
addInfo(message) {
|
|
56
|
+
this.info.push(message);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
get hasIssues() {
|
|
60
|
+
return this.errors.length > 0 || this.warnings.length > 0;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Validates a single template
|
|
66
|
+
* @param {string} templateId - Template ID
|
|
67
|
+
* @param {string} projectPath - Project root path
|
|
68
|
+
* @returns {ValidationResult}
|
|
69
|
+
*/
|
|
70
|
+
export function validateTemplate(templateId, projectPath = process.cwd()) {
|
|
71
|
+
const template = getTemplateById(templateId, projectPath);
|
|
72
|
+
|
|
73
|
+
if (!template) {
|
|
74
|
+
const result = new ValidationResult(templateId, null);
|
|
75
|
+
result.addError(`Template not found: ${templateId}`);
|
|
76
|
+
return result;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const result = new ValidationResult(templateId, template.path);
|
|
80
|
+
|
|
81
|
+
// Read template content
|
|
82
|
+
let content;
|
|
83
|
+
try {
|
|
84
|
+
const fullPath = `${projectPath}/framework/templates/${template.path}`;
|
|
85
|
+
content = readFileSync(fullPath, 'utf-8');
|
|
86
|
+
} catch (error) {
|
|
87
|
+
result.addError(`Failed to read template: ${error.message}`);
|
|
88
|
+
return result;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Check for deprecated pre-computed variables
|
|
92
|
+
for (const { pattern, replacement } of DEPRECATED_PATTERNS) {
|
|
93
|
+
const matches = content.match(pattern);
|
|
94
|
+
if (matches) {
|
|
95
|
+
result.addError(
|
|
96
|
+
`Deprecated placeholder found: ${matches[0]} (use ${replacement} instead)`
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Check for old non-Handlebars syntax
|
|
102
|
+
for (const { pattern, description } of OLD_SYNTAX_PATTERNS) {
|
|
103
|
+
const matches = content.match(pattern);
|
|
104
|
+
if (matches) {
|
|
105
|
+
const count = matches.length;
|
|
106
|
+
result.addWarning(
|
|
107
|
+
`${count} occurrence(s) of old syntax: ${description}`
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Check for required placeholders if specified
|
|
113
|
+
if (template.placeholders && template.placeholders.length > 0) {
|
|
114
|
+
const missingPlaceholders = [];
|
|
115
|
+
|
|
116
|
+
for (const placeholder of template.placeholders) {
|
|
117
|
+
// Check for exact placeholder or helper usage
|
|
118
|
+
const exactMatch = new RegExp(`\\{\\{${placeholder}\\}\\}`, 'g');
|
|
119
|
+
const helperMatch = new RegExp(`\\{\\{\\w+\\s+${placeholder}\\}\\}`, 'g');
|
|
120
|
+
|
|
121
|
+
if (!exactMatch.test(content) && !helperMatch.test(content)) {
|
|
122
|
+
missingPlaceholders.push(placeholder);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (missingPlaceholders.length > 0) {
|
|
127
|
+
result.addWarning(
|
|
128
|
+
`Template declares placeholders but they're not used: ${missingPlaceholders.join(', ')}`
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Check for balanced Handlebars blocks
|
|
134
|
+
const openBlocks = (content.match(/\{\{#\w+/g) || []).length;
|
|
135
|
+
const closeBlocks = (content.match(/\{\{\/\w+/g) || []).length;
|
|
136
|
+
|
|
137
|
+
if (openBlocks !== closeBlocks) {
|
|
138
|
+
result.addError(
|
|
139
|
+
`Unbalanced Handlebars blocks: ${openBlocks} opening, ${closeBlocks} closing`
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Info: Count Handlebars placeholders
|
|
144
|
+
const placeholderMatches = content.match(/\{\{[^}]+\}\}/g) || [];
|
|
145
|
+
const uniquePlaceholders = [...new Set(placeholderMatches)];
|
|
146
|
+
result.addInfo(`Found ${uniquePlaceholders.length} unique Handlebars expressions`);
|
|
147
|
+
|
|
148
|
+
// Info: Check if template uses helpers
|
|
149
|
+
const helperMatches = content.match(/\{\{(pascalCase|camelCase|snakeCase|titleCase|kebabCase|upperSnakeCase|pluralize|singularize|formatDate|uppercase|lowercase|capitalize|trim|replace|concat|substr|startsWith|endsWith|contains|length|add|subtract|multiply|divide|mod|round|join|first|last|slice|now|year|default|json)/g) || [];
|
|
150
|
+
if (helperMatches.length > 0) {
|
|
151
|
+
const uniqueHelpers = [...new Set(helperMatches.map(m => m.replace('{{', '')))];
|
|
152
|
+
result.addInfo(`Uses helpers: ${uniqueHelpers.join(', ')}`);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return result;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Validates all templates
|
|
160
|
+
* @param {string} projectPath - Project root path
|
|
161
|
+
* @param {Object} options - Validation options
|
|
162
|
+
* @returns {Array<ValidationResult>}
|
|
163
|
+
*/
|
|
164
|
+
export function validateAllTemplates(projectPath = process.cwd(), options = {}) {
|
|
165
|
+
const {
|
|
166
|
+
skipDeprecated = true,
|
|
167
|
+
category = null,
|
|
168
|
+
technology = null,
|
|
169
|
+
} = options;
|
|
170
|
+
|
|
171
|
+
const templates = getAllTemplates(projectPath);
|
|
172
|
+
const results = [];
|
|
173
|
+
|
|
174
|
+
for (const template of templates) {
|
|
175
|
+
// Skip deprecated if requested
|
|
176
|
+
if (skipDeprecated && template.deprecated) {
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Filter by category if specified
|
|
181
|
+
if (category && template.category !== category) {
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Filter by technology if specified
|
|
186
|
+
if (technology && template.technology !== technology) {
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const result = validateTemplate(template.id, projectPath);
|
|
191
|
+
results.push(result);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return results;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Generates a validation summary
|
|
199
|
+
* @param {Array<ValidationResult>} results - Validation results
|
|
200
|
+
* @returns {Object}
|
|
201
|
+
*/
|
|
202
|
+
export function summarizeValidation(results) {
|
|
203
|
+
const summary = {
|
|
204
|
+
total: results.length,
|
|
205
|
+
valid: 0,
|
|
206
|
+
hasErrors: 0,
|
|
207
|
+
hasWarnings: 0,
|
|
208
|
+
errors: [],
|
|
209
|
+
warnings: [],
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
for (const result of results) {
|
|
213
|
+
if (result.valid && result.warnings.length === 0) {
|
|
214
|
+
summary.valid++;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (result.errors.length > 0) {
|
|
218
|
+
summary.hasErrors++;
|
|
219
|
+
summary.errors.push({
|
|
220
|
+
templateId: result.templateId,
|
|
221
|
+
errors: result.errors,
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
if (result.warnings.length > 0) {
|
|
226
|
+
summary.hasWarnings++;
|
|
227
|
+
summary.warnings.push({
|
|
228
|
+
templateId: result.templateId,
|
|
229
|
+
warnings: result.warnings,
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
return summary;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Formats validation results for display
|
|
239
|
+
* @param {Array<ValidationResult>} results - Validation results
|
|
240
|
+
* @param {Object} options - Display options
|
|
241
|
+
* @returns {string}
|
|
242
|
+
*/
|
|
243
|
+
export function formatValidationResults(results, options = {}) {
|
|
244
|
+
const { showInfo = false, showValid = false } = options;
|
|
245
|
+
|
|
246
|
+
let output = '';
|
|
247
|
+
let validCount = 0;
|
|
248
|
+
let errorCount = 0;
|
|
249
|
+
let warningCount = 0;
|
|
250
|
+
|
|
251
|
+
for (const result of results) {
|
|
252
|
+
if (result.valid && result.warnings.length === 0) {
|
|
253
|
+
validCount++;
|
|
254
|
+
if (showValid) {
|
|
255
|
+
output += `✅ ${result.templateId}\n`;
|
|
256
|
+
}
|
|
257
|
+
continue;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
if (result.errors.length > 0) {
|
|
261
|
+
errorCount++;
|
|
262
|
+
output += `\n❌ ${result.templateId}\n`;
|
|
263
|
+
output += ` Path: ${result.templatePath}\n`;
|
|
264
|
+
for (const error of result.errors) {
|
|
265
|
+
output += ` ERROR: ${error}\n`;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
if (result.warnings.length > 0) {
|
|
270
|
+
warningCount++;
|
|
271
|
+
if (result.errors.length === 0) {
|
|
272
|
+
output += `\n⚠️ ${result.templateId}\n`;
|
|
273
|
+
output += ` Path: ${result.templatePath}\n`;
|
|
274
|
+
}
|
|
275
|
+
for (const warning of result.warnings) {
|
|
276
|
+
output += ` WARNING: ${warning}\n`;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
if (showInfo && result.info.length > 0) {
|
|
281
|
+
for (const info of result.info) {
|
|
282
|
+
output += ` INFO: ${info}\n`;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// Summary
|
|
288
|
+
output += `\n${'='.repeat(60)}\n`;
|
|
289
|
+
output += `SUMMARY:\n`;
|
|
290
|
+
output += ` Total templates: ${results.length}\n`;
|
|
291
|
+
output += ` ✅ Valid: ${validCount}\n`;
|
|
292
|
+
output += ` ❌ Errors: ${errorCount}\n`;
|
|
293
|
+
output += ` ⚠️ Warnings: ${warningCount}\n`;
|
|
294
|
+
|
|
295
|
+
return output;
|
|
296
|
+
}
|
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Workflow Detection Engine
|
|
3
|
+
*
|
|
4
|
+
* Analyzes user requests to automatically select the appropriate workflow based on:
|
|
5
|
+
* - Keyword matching
|
|
6
|
+
* - Complexity estimation
|
|
7
|
+
* - Workflow criteria
|
|
8
|
+
*
|
|
9
|
+
* @module workflow-detector
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { readFileSync, existsSync, readdirSync } from 'fs';
|
|
13
|
+
import { join, dirname } from 'path';
|
|
14
|
+
import { fileURLToPath } from 'url';
|
|
15
|
+
|
|
16
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Get framework root directory
|
|
20
|
+
* @param {string} projectPath - Project path
|
|
21
|
+
* @returns {string} Framework root path
|
|
22
|
+
*/
|
|
23
|
+
function getFrameworkRoot(projectPath) {
|
|
24
|
+
// Try npm-installed package first
|
|
25
|
+
const npmPath = join(projectPath, 'node_modules/@polymorphism-tech/morph-spec');
|
|
26
|
+
if (existsSync(npmPath)) {
|
|
27
|
+
return npmPath;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Fallback to local development path (from src/lib/ → root)
|
|
31
|
+
return join(__dirname, '..', '..');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Load all workflow configurations
|
|
36
|
+
* @param {string} projectPath - Project path
|
|
37
|
+
* @returns {Array<Object>} Array of workflow configs
|
|
38
|
+
*/
|
|
39
|
+
function loadWorkflowConfigs(projectPath) {
|
|
40
|
+
const frameworkRoot = getFrameworkRoot(projectPath);
|
|
41
|
+
const configsDir = join(frameworkRoot, 'framework/workflows/configs');
|
|
42
|
+
|
|
43
|
+
if (!existsSync(configsDir)) {
|
|
44
|
+
return [];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const configFiles = readdirSync(configsDir).filter(f => f.endsWith('.json'));
|
|
48
|
+
const configs = [];
|
|
49
|
+
|
|
50
|
+
for (const file of configFiles) {
|
|
51
|
+
try {
|
|
52
|
+
const configPath = join(configsDir, file);
|
|
53
|
+
const content = readFileSync(configPath, 'utf8');
|
|
54
|
+
configs.push(JSON.parse(content));
|
|
55
|
+
} catch (err) {
|
|
56
|
+
// Skip invalid configs
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return configs;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Match keywords in user request
|
|
66
|
+
* @param {string} userRequest - User request text
|
|
67
|
+
* @param {Array<string>} keywords - Workflow keywords
|
|
68
|
+
* @returns {{matched: Array<string>, score: number}}
|
|
69
|
+
*/
|
|
70
|
+
function matchKeywords(userRequest, keywords) {
|
|
71
|
+
const requestLower = userRequest.toLowerCase();
|
|
72
|
+
const matched = [];
|
|
73
|
+
|
|
74
|
+
for (const keyword of keywords) {
|
|
75
|
+
const keywordLower = keyword.toLowerCase();
|
|
76
|
+
|
|
77
|
+
// Check for partial matches (e.g., "paginação" matches "pagination")
|
|
78
|
+
if (requestLower.includes(keywordLower)) {
|
|
79
|
+
matched.push(keyword);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Score: percentage of keywords matched
|
|
84
|
+
const score = matched.length > 0 ? matched.length / keywords.length : 0;
|
|
85
|
+
|
|
86
|
+
return { matched, score };
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Estimate complexity from user request
|
|
91
|
+
* @param {string} userRequest - User request text
|
|
92
|
+
* @returns {{files: number, lines: number, components: number, hasInfra: boolean}}
|
|
93
|
+
*/
|
|
94
|
+
function estimateComplexity(userRequest) {
|
|
95
|
+
const requestLower = userRequest.toLowerCase();
|
|
96
|
+
|
|
97
|
+
// Scope indicators
|
|
98
|
+
const scopeIndicators = {
|
|
99
|
+
trivial: ['button', 'cor', 'color', 'css', 'fix', 'typo', 'edge case', 'one'],
|
|
100
|
+
small: ['add', 'create', 'single', 'simple', 'quick'],
|
|
101
|
+
medium: ['feature', 'pagination', 'filter', 'export', 'refactor', 'optimize'],
|
|
102
|
+
large: ['system', 'complete', 'all', 'multiple', 'integration', 'architecture'],
|
|
103
|
+
huge: ['platform', 'infrastructure', 'subsystem', 'migrate', 'rebuild']
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
// Infrastructure indicators
|
|
107
|
+
const infraKeywords = ['bicep', 'azure', 'deploy', 'docker', 'kubernetes', 'infrastructure', 'terraform'];
|
|
108
|
+
const hasInfra = infraKeywords.some(kw => requestLower.includes(kw));
|
|
109
|
+
|
|
110
|
+
// Component indicators
|
|
111
|
+
const componentKeywords = ['component', 'page', 'screen', 'form', 'table', 'chart', 'modal'];
|
|
112
|
+
const componentMatches = componentKeywords.filter(kw => requestLower.includes(kw)).length;
|
|
113
|
+
|
|
114
|
+
// Entity indicators
|
|
115
|
+
const entityKeywords = ['user', 'order', 'product', 'customer', 'invoice', 'report'];
|
|
116
|
+
const entityMatches = entityKeywords.filter(kw => requestLower.includes(kw)).length;
|
|
117
|
+
|
|
118
|
+
// Determine scope
|
|
119
|
+
let scope = 'medium';
|
|
120
|
+
for (const [level, indicators] of Object.entries(scopeIndicators)) {
|
|
121
|
+
if (indicators.some(indicator => requestLower.includes(indicator))) {
|
|
122
|
+
scope = level;
|
|
123
|
+
break;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Estimate based on scope
|
|
128
|
+
const estimates = {
|
|
129
|
+
trivial: { files: 1, lines: 30, components: 0 },
|
|
130
|
+
small: { files: 2, lines: 100, components: 1 },
|
|
131
|
+
medium: { files: 5, lines: 200, components: 1 },
|
|
132
|
+
large: { files: 12, lines: 800, components: 3 },
|
|
133
|
+
huge: { files: 25, lines: 2000, components: 5 }
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
const base = estimates[scope] || estimates.medium;
|
|
137
|
+
|
|
138
|
+
// Adjust based on entities and components mentioned
|
|
139
|
+
const files = base.files + entityMatches + componentMatches;
|
|
140
|
+
const lines = base.lines + (entityMatches * 50) + (componentMatches * 100);
|
|
141
|
+
const components = base.components + componentMatches;
|
|
142
|
+
|
|
143
|
+
return {
|
|
144
|
+
files,
|
|
145
|
+
lines,
|
|
146
|
+
components,
|
|
147
|
+
hasInfra
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Check if complexity matches workflow criteria
|
|
153
|
+
* @param {Object} complexity - Estimated complexity
|
|
154
|
+
* @param {Object} criteria - Workflow criteria
|
|
155
|
+
* @returns {{matches: boolean, score: number}}
|
|
156
|
+
*/
|
|
157
|
+
function matchComplexity(complexity, criteria) {
|
|
158
|
+
let matches = 0;
|
|
159
|
+
let total = 0;
|
|
160
|
+
|
|
161
|
+
// File count
|
|
162
|
+
if (criteria.maxFiles !== undefined) {
|
|
163
|
+
total++;
|
|
164
|
+
if (complexity.files <= criteria.maxFiles) matches++;
|
|
165
|
+
}
|
|
166
|
+
if (criteria.minFiles !== undefined) {
|
|
167
|
+
total++;
|
|
168
|
+
if (complexity.files >= criteria.minFiles) matches++;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Line count
|
|
172
|
+
if (criteria.maxLines !== undefined) {
|
|
173
|
+
total++;
|
|
174
|
+
if (complexity.lines <= criteria.maxLines) matches++;
|
|
175
|
+
}
|
|
176
|
+
if (criteria.minLines !== undefined) {
|
|
177
|
+
total++;
|
|
178
|
+
if (complexity.lines >= criteria.minLines) matches++;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Component count
|
|
182
|
+
if (criteria.maxComponents !== undefined) {
|
|
183
|
+
total++;
|
|
184
|
+
if (complexity.components <= criteria.maxComponents) matches++;
|
|
185
|
+
}
|
|
186
|
+
if (criteria.minComponents !== undefined) {
|
|
187
|
+
total++;
|
|
188
|
+
if (complexity.components >= criteria.minComponents) matches++;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Infrastructure
|
|
192
|
+
if (criteria.noInfrastructure !== undefined) {
|
|
193
|
+
total++;
|
|
194
|
+
if (criteria.noInfrastructure === !complexity.hasInfra) matches++;
|
|
195
|
+
}
|
|
196
|
+
if (criteria.infrastructure !== undefined) {
|
|
197
|
+
total++;
|
|
198
|
+
if (criteria.infrastructure === complexity.hasInfra) matches++;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const score = total > 0 ? matches / total : 0.5;
|
|
202
|
+
const meetsAll = total > 0 && matches === total;
|
|
203
|
+
|
|
204
|
+
return { matches: meetsAll, score };
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Detect workflow for a feature request
|
|
209
|
+
* @param {Object} options - Detection options
|
|
210
|
+
* @param {string} options.userRequest - User's feature request
|
|
211
|
+
* @param {string} [options.projectPath='.'] - Project path
|
|
212
|
+
* @param {string} [options.featureName] - Feature name (optional)
|
|
213
|
+
* @returns {Object} Detection result
|
|
214
|
+
*/
|
|
215
|
+
export async function detectWorkflow(options) {
|
|
216
|
+
const { userRequest, projectPath = '.', featureName } = options;
|
|
217
|
+
|
|
218
|
+
if (!userRequest || userRequest.trim().length === 0) {
|
|
219
|
+
throw new Error('userRequest is required');
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Load all workflow configs
|
|
223
|
+
const workflows = loadWorkflowConfigs(projectPath);
|
|
224
|
+
|
|
225
|
+
if (workflows.length === 0) {
|
|
226
|
+
// Fallback if no configs found
|
|
227
|
+
return {
|
|
228
|
+
workflowId: 'full-morph',
|
|
229
|
+
confidence: 0.5,
|
|
230
|
+
matchedKeywords: [],
|
|
231
|
+
estimatedComplexity: { files: 0, lines: 0, components: 0, hasInfra: false },
|
|
232
|
+
reasoning: 'No workflow configs found - using full-morph as fallback',
|
|
233
|
+
alternativeWorkflows: []
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Estimate complexity
|
|
238
|
+
const complexity = estimateComplexity(userRequest);
|
|
239
|
+
|
|
240
|
+
// Score each workflow
|
|
241
|
+
const scores = [];
|
|
242
|
+
|
|
243
|
+
for (const workflow of workflows) {
|
|
244
|
+
const keywordMatch = matchKeywords(userRequest, workflow.keywords || []);
|
|
245
|
+
const complexityMatch = matchComplexity(complexity, workflow.criteria || {});
|
|
246
|
+
|
|
247
|
+
// Weighted scoring
|
|
248
|
+
const keywordWeight = 0.4;
|
|
249
|
+
const complexityWeight = 0.3;
|
|
250
|
+
const priorityWeight = 0.2;
|
|
251
|
+
const contextWeight = 0.1;
|
|
252
|
+
|
|
253
|
+
// Priority score (lower number = higher priority = higher score)
|
|
254
|
+
const priorityScore = workflow.priority ? (4 - workflow.priority) / 3 : 0.5;
|
|
255
|
+
|
|
256
|
+
// Context score (checks for special conditions)
|
|
257
|
+
let contextScore = 0.5;
|
|
258
|
+
if (workflow.criteria?.hasPrototype && (userRequest.toLowerCase().includes('prototype') || userRequest.toLowerCase().includes('mockup'))) {
|
|
259
|
+
contextScore = 1.0;
|
|
260
|
+
}
|
|
261
|
+
if (workflow.criteria?.hasExistingPages && userRequest.toLowerCase().includes('existing')) {
|
|
262
|
+
contextScore = 1.0;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// Calculate total score
|
|
266
|
+
let totalScore =
|
|
267
|
+
(keywordMatch.score * keywordWeight) +
|
|
268
|
+
(complexityMatch.score * complexityWeight) +
|
|
269
|
+
(priorityScore * priorityWeight) +
|
|
270
|
+
(contextScore * contextWeight);
|
|
271
|
+
|
|
272
|
+
// Penalty for workflows with no keyword matches (unless it's full-morph fallback)
|
|
273
|
+
if (keywordMatch.matched.length === 0 && workflow.id !== 'full-morph') {
|
|
274
|
+
totalScore *= 0.5; // 50% penalty for no keyword match
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// Boost for strong keyword matches (3+ keywords matched)
|
|
278
|
+
if (keywordMatch.matched.length >= 3) {
|
|
279
|
+
totalScore *= 1.2; // 20% boost
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
scores.push({
|
|
283
|
+
workflowId: workflow.id,
|
|
284
|
+
workflowName: workflow.name,
|
|
285
|
+
score: totalScore,
|
|
286
|
+
keywordMatch: keywordMatch.matched,
|
|
287
|
+
complexityMatch: complexityMatch.matches,
|
|
288
|
+
reasoning: []
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Sort by score (highest first)
|
|
293
|
+
scores.sort((a, b) => b.score - a.score);
|
|
294
|
+
|
|
295
|
+
// Get top result
|
|
296
|
+
const topResult = scores[0];
|
|
297
|
+
const confidence = topResult.score;
|
|
298
|
+
|
|
299
|
+
// Build reasoning
|
|
300
|
+
const reasoning = [];
|
|
301
|
+
if (topResult.keywordMatch.length > 0) {
|
|
302
|
+
reasoning.push(`Matched keyword${topResult.keywordMatch.length > 1 ? 's' : ''}: ${topResult.keywordMatch.join(', ')}`);
|
|
303
|
+
}
|
|
304
|
+
reasoning.push(`Estimated ${complexity.files} files, ~${complexity.lines} lines`);
|
|
305
|
+
if (complexity.hasInfra) {
|
|
306
|
+
reasoning.push('Infrastructure keywords detected');
|
|
307
|
+
}
|
|
308
|
+
if (topResult.complexityMatch) {
|
|
309
|
+
reasoning.push('Meets all complexity criteria');
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// Get alternatives (next 2 workflows with score > 0.2)
|
|
313
|
+
const alternatives = scores.slice(1, 3)
|
|
314
|
+
.filter(w => w.score > 0.2)
|
|
315
|
+
.map(w => ({
|
|
316
|
+
workflowId: w.workflowId,
|
|
317
|
+
confidence: w.score,
|
|
318
|
+
reason: `${w.workflowName} (${Math.round(w.score * 100)}% match)`
|
|
319
|
+
}));
|
|
320
|
+
|
|
321
|
+
return {
|
|
322
|
+
workflowId: topResult.workflowId,
|
|
323
|
+
confidence,
|
|
324
|
+
matchedKeywords: topResult.keywordMatch,
|
|
325
|
+
estimatedComplexity: complexity,
|
|
326
|
+
reasoning: reasoning.join(' + '),
|
|
327
|
+
alternativeWorkflows: alternatives
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Get workflow configuration by ID
|
|
333
|
+
* @param {string} workflowId - Workflow ID
|
|
334
|
+
* @param {string} [projectPath='.'] - Project path
|
|
335
|
+
* @returns {Object|null} Workflow config or null
|
|
336
|
+
*/
|
|
337
|
+
export function getWorkflowConfig(workflowId, projectPath = '.') {
|
|
338
|
+
const workflows = loadWorkflowConfigs(projectPath);
|
|
339
|
+
return workflows.find(w => w.id === workflowId) || null;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* List all available workflows
|
|
344
|
+
* @param {string} [projectPath='.'] - Project path
|
|
345
|
+
* @returns {Array<{id: string, name: string, description: string}>}
|
|
346
|
+
*/
|
|
347
|
+
export function listWorkflows(projectPath = '.') {
|
|
348
|
+
const workflows = loadWorkflowConfigs(projectPath);
|
|
349
|
+
return workflows.map(w => ({
|
|
350
|
+
id: w.id,
|
|
351
|
+
name: w.name,
|
|
352
|
+
description: w.description
|
|
353
|
+
}));
|
|
354
|
+
}
|