@polymorphism-tech/morph-spec 4.7.2 → 4.8.4
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/LICENSE +1 -2
- package/README.md +379 -414
- package/bin/morph-spec.js +57 -394
- package/bin/validate.js +2 -26
- package/claude-plugin.json +2 -2
- package/docs/CHEATSHEET.md +203 -221
- package/docs/QUICKSTART.md +2 -8
- package/framework/CLAUDE.md +1 -1
- package/framework/commands/morph-proposal.md +3 -3
- package/framework/hooks/README.md +2 -5
- package/framework/hooks/claude-code/pre-tool-use/protect-readonly-files.js +4 -55
- package/framework/hooks/claude-code/session-start/inject-morph-context.js +20 -5
- package/framework/hooks/claude-code/statusline.py +6 -1
- package/framework/hooks/dev/check-sync-health.js +117 -0
- package/framework/hooks/dev/guard-version-numbers.js +57 -0
- package/framework/hooks/dev/sync-standards-registry.js +60 -0
- package/framework/hooks/dev/sync-template-registry.js +60 -0
- package/framework/hooks/dev/validate-skill-format.js +70 -0
- package/framework/hooks/dev/validate-standard-format.js +73 -0
- package/framework/hooks/shared/payload-utils.js +39 -0
- package/framework/hooks/shared/state-reader.js +25 -1
- package/framework/rules/morph-workflow.md +1 -1
- package/framework/skills/level-0-meta/morph-init/SKILL.md +216 -0
- package/framework/skills/level-0-meta/morph-replicate/SKILL.md +4 -4
- package/framework/skills/level-0-meta/tool-usage-guide/SKILL.md +4 -4
- package/framework/skills/level-0-meta/verification-before-completion/SKILL.md +1 -1
- package/framework/skills/level-1-workflows/phase-clarify/SKILL.md +192 -191
- package/framework/skills/level-1-workflows/phase-codebase-analysis/SKILL.md +181 -180
- package/framework/skills/level-1-workflows/phase-design/SKILL.md +339 -338
- package/framework/skills/level-1-workflows/phase-implement/SKILL.md +254 -253
- package/framework/skills/level-1-workflows/phase-setup/SKILL.md +168 -170
- package/framework/skills/level-1-workflows/phase-tasks/SKILL.md +284 -283
- package/framework/skills/level-1-workflows/phase-uiux/SKILL.md +246 -245
- package/framework/templates/examples/design-system-examples.md +1 -1
- package/framework/templates/ui/FluentDesignTheme.cs +1 -1
- package/framework/templates/ui/MudTheme.cs +1 -1
- package/framework/templates/ui/design-system.css +1 -1
- package/package.json +7 -5
- package/src/commands/agents/index.js +1 -2
- package/src/commands/index.js +13 -16
- package/src/commands/project/doctor.js +100 -14
- package/src/commands/project/index.js +7 -10
- package/src/commands/project/init.js +398 -528
- package/src/commands/project/install-plugin-cmd.js +28 -0
- package/src/commands/project/setup-infra-cmd.js +12 -0
- package/src/commands/project/tutorial.js +115 -0
- package/src/commands/state/approve.js +213 -221
- package/src/commands/state/index.js +0 -1
- package/src/commands/state/state.js +337 -365
- package/src/commands/templates/index.js +0 -4
- package/src/commands/trust/trust.js +1 -93
- package/src/commands/utils/index.js +1 -5
- package/src/commands/validation/index.js +1 -5
- package/src/core/registry/command-registry.js +11 -285
- package/src/core/state/state-manager.js +5 -2
- package/src/lib/detectors/index.js +81 -87
- package/src/lib/detectors/structure-detector.js +275 -273
- package/src/lib/generators/recap-generator.js +232 -225
- package/src/scripts/global-install.js +34 -0
- package/src/scripts/install-plugin.js +126 -0
- package/src/scripts/setup-infra.js +203 -0
- package/src/utils/agents-installer.js +10 -1
- package/src/utils/hooks-installer.js +66 -3
- package/.morph/.morphversion +0 -5
- package/.morph/analytics/threads-log.jsonl +0 -5
- package/.morph/config/config.json +0 -8
- package/.morph/framework/agents.json +0 -1815
- package/.morph/framework/hooks/README.md +0 -205
- package/.morph/framework/hooks/claude-code/notification/approval-reminder.js +0 -54
- package/.morph/framework/hooks/claude-code/post-tool-use/dispatch.js +0 -83
- package/.morph/framework/hooks/claude-code/post-tool-use/handle-tool-failure.js +0 -42
- package/.morph/framework/hooks/claude-code/pre-compact/save-morph-context.js +0 -61
- package/.morph/framework/hooks/claude-code/pre-tool-use/enforce-phase-writes.js +0 -71
- package/.morph/framework/hooks/claude-code/pre-tool-use/protect-readonly-files.js +0 -58
- package/.morph/framework/hooks/claude-code/pre-tool-use/protect-spec-files.js +0 -64
- package/.morph/framework/hooks/claude-code/session-start/inject-morph-context.js +0 -94
- package/.morph/framework/hooks/claude-code/statusline.py +0 -538
- package/.morph/framework/hooks/claude-code/statusline.sh +0 -7
- package/.morph/framework/hooks/claude-code/stop/validate-completion.js +0 -88
- package/.morph/framework/hooks/claude-code/user-prompt/enrich-prompt.js +0 -91
- package/.morph/framework/hooks/git/commit-msg/conventional-commits.sh +0 -33
- package/.morph/framework/hooks/git/pre-commit/agents.sh +0 -25
- package/.morph/framework/hooks/git/pre-commit/orchestrator.sh +0 -64
- package/.morph/framework/hooks/git/pre-commit/specs.sh +0 -50
- package/.morph/framework/hooks/git/pre-push/run-tests.sh +0 -44
- package/.morph/framework/hooks/shared/hook-response.js +0 -45
- package/.morph/framework/hooks/shared/phase-utils.js +0 -129
- package/.morph/framework/hooks/shared/state-reader.js +0 -138
- package/.morph/framework/hooks/shared/stdin-reader.js +0 -26
- package/.morph/framework/standards/STANDARDS.json +0 -933
- package/.morph/framework/standards/ai-agents/blazor-ui.md +0 -364
- package/.morph/framework/standards/ai-agents/production.md +0 -415
- package/.morph/framework/standards/ai-agents/setup.md +0 -418
- package/.morph/framework/standards/ai-agents/team-orchestration.md +0 -479
- package/.morph/framework/standards/ai-agents/workflows.md +0 -354
- package/.morph/framework/standards/architecture/ddd/aggregates.md +0 -120
- package/.morph/framework/standards/architecture/ddd/bounded-contexts.md +0 -105
- package/.morph/framework/standards/architecture/ddd/complexity-levels.md +0 -108
- package/.morph/framework/standards/architecture/ddd/entities.md +0 -99
- package/.morph/framework/standards/architecture/ddd/ubiquitous-language.md +0 -58
- package/.morph/framework/standards/architecture/ddd/value-objects.md +0 -124
- package/.morph/framework/standards/backend/api/minimal-api.md +0 -494
- package/.morph/framework/standards/backend/api/rest.md +0 -492
- package/.morph/framework/standards/backend/api/validation.md +0 -88
- package/.morph/framework/standards/backend/authentication/passkeys.md +0 -428
- package/.morph/framework/standards/backend/database/ef-core.md +0 -199
- package/.morph/framework/standards/backend/database/migrations.md +0 -393
- package/.morph/framework/standards/backend/database/postgresql/database.md +0 -352
- package/.morph/framework/standards/backend/database/repository-patterns.md +0 -528
- package/.morph/framework/standards/backend/database/vector-search-rag.md +0 -541
- package/.morph/framework/standards/backend/dotnet/async.md +0 -366
- package/.morph/framework/standards/backend/dotnet/core.md +0 -117
- package/.morph/framework/standards/backend/dotnet/di.md +0 -439
- package/.morph/framework/standards/backend/dotnet/program-cs-checklist.md +0 -92
- package/.morph/framework/standards/backend/integrations/asaas/asaas-api.md +0 -216
- package/.morph/framework/standards/backend/integrations/clerk/clerk-auth.md +0 -290
- package/.morph/framework/standards/backend/integrations/hangfire/hangfire-jobs.md +0 -350
- package/.morph/framework/standards/backend/integrations/resend/resend-email.md +0 -385
- package/.morph/framework/standards/context/analytics.md +0 -96
- package/.morph/framework/standards/context/bundles.md +0 -110
- package/.morph/framework/standards/context/priming.md +0 -78
- package/.morph/framework/standards/core/architecture.md +0 -185
- package/.morph/framework/standards/core/coding.md +0 -214
- package/.morph/framework/standards/core/git-branching-strategy.md +0 -403
- package/.morph/framework/standards/core/git.md +0 -185
- package/.morph/framework/standards/core/testing.md +0 -295
- package/.morph/framework/standards/data/nosql/blob-storage.md +0 -102
- package/.morph/framework/standards/data/nosql/cache/redis.md +0 -97
- package/.morph/framework/standards/data/nosql/cosmos-db.md +0 -118
- package/.morph/framework/standards/data/vector-search/azure-ai-search.md +0 -121
- package/.morph/framework/standards/data/vector-search/rag-chunking.md +0 -104
- package/.morph/framework/standards/frontend/blazor/design-checklist.md +0 -222
- package/.morph/framework/standards/frontend/blazor/fluent-ui-setup.md +0 -595
- package/.morph/framework/standards/frontend/blazor/fluent-ui.md +0 -137
- package/.morph/framework/standards/frontend/blazor/html-conversion.md +0 -184
- package/.morph/framework/standards/frontend/blazor/lifecycle.md +0 -195
- package/.morph/framework/standards/frontend/blazor/pitfalls.md +0 -198
- package/.morph/framework/standards/frontend/blazor/state.md +0 -191
- package/.morph/framework/standards/frontend/design-system/animations.md +0 -151
- package/.morph/framework/standards/frontend/design-system/naming.md +0 -64
- package/.morph/framework/standards/frontend/nextjs/app-router.md +0 -123
- package/.morph/framework/standards/frontend/nextjs/components.md +0 -132
- package/.morph/framework/standards/frontend/nextjs/data-fetching.md +0 -126
- package/.morph/framework/standards/frontend/nextjs/forms.md +0 -128
- package/.morph/framework/standards/frontend/nextjs/naming-conventions.md +0 -67
- package/.morph/framework/standards/frontend/nextjs/nextjs-patterns.md +0 -215
- package/.morph/framework/standards/frontend/nextjs/project-structure.md +0 -102
- package/.morph/framework/standards/frontend/nextjs/state-management.md +0 -72
- package/.morph/framework/standards/frontend/nextjs/testing.md +0 -111
- package/.morph/framework/standards/infrastructure/azure/azure.md +0 -624
- package/.morph/framework/standards/infrastructure/azure/bicep/bicep-patterns.md +0 -422
- package/.morph/framework/standards/infrastructure/azure/devops/azure-devops-setup.md +0 -516
- package/.morph/framework/standards/infrastructure/azure/devops/local-development.md +0 -520
- package/.morph/framework/standards/infrastructure/azure/services/functions.md +0 -486
- package/.morph/framework/standards/infrastructure/azure/services/service-bus.md +0 -459
- package/.morph/framework/standards/infrastructure/azure/services/storage.md +0 -407
- package/.morph/framework/standards/infrastructure/docker/easypanel-deploy.md +0 -196
- package/.morph/framework/standards/infrastructure/supabase/mcp-setup.md +0 -252
- package/.morph/framework/standards/infrastructure/supabase/supabase-auth.md +0 -176
- package/.morph/framework/standards/infrastructure/supabase/supabase-pgvector.md +0 -169
- package/.morph/framework/standards/infrastructure/supabase/supabase-rls.md +0 -184
- package/.morph/framework/standards/infrastructure/supabase/supabase-storage.md +0 -153
- package/.morph/framework/standards/integration/api/graphql.md +0 -91
- package/.morph/framework/standards/integration/api/grpc.md +0 -114
- package/.morph/framework/standards/integration/api/rest-design.md +0 -95
- package/.morph/framework/standards/integration/event-driven/cqrs.md +0 -101
- package/.morph/framework/standards/integration/event-driven/event-sourcing.md +0 -124
- package/.morph/framework/standards/integration/event-driven/service-bus.md +0 -95
- package/.morph/framework/standards/integration/mcp/mcp-tools.md +0 -384
- package/.morph/framework/standards/observability/logging.md +0 -131
- package/.morph/framework/standards/observability/metrics.md +0 -121
- package/.morph/framework/standards/observability/monitoring.md +0 -114
- package/.morph/framework/standards/observability/tracing.md +0 -132
- package/.morph/framework/standards/workflows/parallel-execution.md +0 -112
- package/.morph/framework/standards/workflows/thread-management.md +0 -113
- package/.morph/framework/templates/.idea/morph-templates.xml +0 -92
- package/.morph/framework/templates/.vscode/morph-templates.code-snippets +0 -186
- package/.morph/framework/templates/IDE-SNIPPETS.md +0 -266
- package/.morph/framework/templates/README.md +0 -814
- package/.morph/framework/templates/REGISTRY.json +0 -1888
- package/.morph/framework/templates/code/dotnet/backend/repository.cs +0 -141
- package/.morph/framework/templates/code/dotnet/backend/service.cs +0 -139
- package/.morph/framework/templates/code/dotnet/contracts/Commands.cs +0 -74
- package/.morph/framework/templates/code/dotnet/contracts/Entities.cs +0 -25
- package/.morph/framework/templates/code/dotnet/contracts/Queries.cs +0 -74
- package/.morph/framework/templates/code/dotnet/contracts/README.md +0 -74
- package/.morph/framework/templates/code/dotnet/contracts/api-contracts.cs +0 -173
- package/.morph/framework/templates/code/dotnet/contracts/contracts-level1.cs +0 -69
- package/.morph/framework/templates/code/dotnet/contracts/contracts-level2.cs +0 -86
- package/.morph/framework/templates/code/dotnet/contracts/contracts-level3.cs +0 -41
- package/.morph/framework/templates/code/dotnet/database/migration.cs +0 -83
- package/.morph/framework/templates/code/dotnet/frontend/component.razor +0 -239
- package/.morph/framework/templates/code/dotnet/jobs/agent.cs +0 -163
- package/.morph/framework/templates/code/dotnet/jobs/job.cs +0 -171
- package/.morph/framework/templates/code/dotnet/test.cs +0 -239
- package/.morph/framework/templates/code/sql/rls-policy.sql +0 -57
- package/.morph/framework/templates/code/sql/supabase-migration.sql +0 -100
- package/.morph/framework/templates/code/sql/supabase-migration.template.sql +0 -113
- package/.morph/framework/templates/code/typescript/contracts.ts +0 -168
- package/.morph/framework/templates/context/CONTEXT-FEATURE.md +0 -276
- package/.morph/framework/templates/context/CONTEXT.md +0 -181
- package/.morph/framework/templates/docs/clarifications.md +0 -253
- package/.morph/framework/templates/docs/onboarding.md +0 -123
- package/.morph/framework/templates/docs/proposal.md +0 -182
- package/.morph/framework/templates/docs/schema-analysis.md +0 -119
- package/.morph/framework/templates/docs/spec.md +0 -198
- package/.morph/framework/templates/docs/ui-components.md +0 -124
- package/.morph/framework/templates/docs/ui-design-system.md +0 -76
- package/.morph/framework/templates/docs/ui-flows.md +0 -167
- package/.morph/framework/templates/docs/ui-mockups.md +0 -98
- package/.morph/framework/templates/docs/user-stories.md +0 -34
- package/.morph/framework/templates/examples/design-system-examples.md +0 -357
- package/.morph/framework/templates/examples/spec-examples.md +0 -90
- package/.morph/framework/templates/feature/decisions.md +0 -187
- package/.morph/framework/templates/feature/recap.md +0 -146
- package/.morph/framework/templates/feature/tasks.md +0 -199
- package/.morph/framework/templates/frontend/nextjs/Dockerfile.nextjs.hbs +0 -43
- package/.morph/framework/templates/frontend/nextjs/client-component.tsx.hbs +0 -26
- package/.morph/framework/templates/frontend/nextjs/env.mjs.hbs +0 -32
- package/.morph/framework/templates/frontend/nextjs/feature-form.tsx.hbs +0 -56
- package/.morph/framework/templates/frontend/nextjs/page.tsx.hbs +0 -22
- package/.morph/framework/templates/frontend/nextjs/tsconfig.json.hbs +0 -26
- package/.morph/framework/templates/frontend/nextjs/use-feature.ts.hbs +0 -54
- package/.morph/framework/templates/infrastructure/azure/Dockerfile.example +0 -82
- package/.morph/framework/templates/infrastructure/azure/README.md +0 -286
- package/.morph/framework/templates/infrastructure/azure/app-insights.bicep +0 -63
- package/.morph/framework/templates/infrastructure/azure/app-service.bicep +0 -164
- package/.morph/framework/templates/infrastructure/azure/container-app-env.bicep +0 -49
- package/.morph/framework/templates/infrastructure/azure/container-app.bicep +0 -156
- package/.morph/framework/templates/infrastructure/azure/deploy-checklist.md +0 -426
- package/.morph/framework/templates/infrastructure/azure/deploy.ps1 +0 -229
- package/.morph/framework/templates/infrastructure/azure/deploy.sh +0 -208
- package/.morph/framework/templates/infrastructure/azure/key-vault.bicep +0 -91
- package/.morph/framework/templates/infrastructure/azure/main.bicep +0 -189
- package/.morph/framework/templates/infrastructure/azure/parameters.dev.json +0 -29
- package/.morph/framework/templates/infrastructure/azure/parameters.prod.json +0 -29
- package/.morph/framework/templates/infrastructure/azure/parameters.staging.json +0 -29
- package/.morph/framework/templates/infrastructure/azure/sql-database.bicep +0 -103
- package/.morph/framework/templates/infrastructure/azure/storage.bicep +0 -106
- package/.morph/framework/templates/infrastructure/docker/Dockerfile.template +0 -58
- package/.morph/framework/templates/infrastructure/docker/docker-compose.template.yml +0 -67
- package/.morph/framework/templates/infrastructure/docker/dockerfile-api.dockerfile +0 -38
- package/.morph/framework/templates/infrastructure/docker/dockerfile-web.dockerfile +0 -48
- package/.morph/framework/templates/infrastructure/docker/easypanel.template.json +0 -54
- package/.morph/framework/templates/infrastructure/github/README.md +0 -593
- package/.morph/framework/templates/infrastructure/github/actions/azure-auth/action.yml.hbs +0 -22
- package/.morph/framework/templates/infrastructure/github/actions/docker-build-push/action.yml.hbs +0 -45
- package/.morph/framework/templates/infrastructure/github/actions/health-check/action.yml.hbs +0 -27
- package/.morph/framework/templates/infrastructure/github/workflows/deploy-azure-app-service.yml.hbs +0 -61
- package/.morph/framework/templates/infrastructure/github/workflows/deploy-easypanel.yml.hbs +0 -31
- package/.morph/framework/templates/infrastructure/github/workflows/docker-build-push.yml.hbs +0 -59
- package/.morph/framework/templates/infrastructure/github/workflows/dotnet-build.yml.hbs +0 -39
- package/.morph/framework/templates/integrations/asaas-client.cs +0 -387
- package/.morph/framework/templates/integrations/asaas-webhook.cs +0 -351
- package/.morph/framework/templates/integrations/azure-identity-config.cs +0 -288
- package/.morph/framework/templates/integrations/clerk-config.cs +0 -258
- package/.morph/framework/templates/meta-prompts/fusion/fusion-agent.md +0 -76
- package/.morph/framework/templates/meta-prompts/fusion/fusion-aggregator.md +0 -100
- package/.morph/framework/templates/meta-prompts/hops/hop-retry.md +0 -78
- package/.morph/framework/templates/meta-prompts/hops/hop-validation.md +0 -97
- package/.morph/framework/templates/meta-prompts/hops/hop-wrapper.md +0 -36
- package/.morph/framework/templates/meta-prompts/parallel-workers/parallel-coordinator.md +0 -113
- package/.morph/framework/templates/meta-prompts/parallel-workers/parallel-worker.md +0 -80
- package/.morph/framework/templates/meta-prompts/squad-leaders/backend-squad.md +0 -90
- package/.morph/framework/templates/meta-prompts/squad-leaders/frontend-squad.md +0 -126
- package/.morph/framework/templates/meta-prompts/squad-leaders/squad-leader.md +0 -43
- package/.morph/framework/templates/meta-prompts/validators/checkpoint-validator.md +0 -107
- package/.morph/framework/templates/meta-prompts/validators/pre-commit-validator.md +0 -95
- package/.morph/framework/templates/project-structure/dotnet-ddd.md +0 -70
- package/.morph/framework/templates/saas/subscription.cs +0 -347
- package/.morph/framework/templates/saas/tenant.cs +0 -338
- package/.morph/framework/templates/state.template.json +0 -17
- package/.morph/framework/templates/ui/FluentDesignTheme.cs +0 -149
- package/.morph/framework/templates/ui/MudTheme.cs +0 -281
- package/.morph/framework/templates/ui/design-system.css +0 -226
- package/.morph/logs/tool-failures.log +0 -17
- package/.morph/memory/pre-compact-2026-02-24T17-43-30-049Z.json +0 -16
- package/.morph/plans/eager-watching-bunny.md +0 -105
- package/.morph/plans/temporal-seeking-nebula.md +0 -45
- package/.morph/state.json +0 -48
- package/CLAUDE.md +0 -77
- package/docs/ARCHITECTURE.md +0 -331
- package/docs/COMMAND-FLOWS.md +0 -368
- package/docs/claude-alignment-report.md +0 -137
- package/docs/examples/order-management/contracts.cs +0 -84
- package/docs/examples/order-management/proposal.md +0 -24
- package/docs/examples/order-management/spec.md +0 -162
- package/docs/plans/2026-02-22-claude-docs-morph-alignment-analysis.md +0 -512
- package/docs/plans/2026-02-22-claude-settings.md +0 -515
- package/docs/plans/2026-02-22-morph-cc-alignment-impl.md +0 -728
- package/docs/plans/2026-02-22-morph-spec-next.md +0 -478
- package/docs/plans/2026-02-22-native-alignment-design.md +0 -199
- package/docs/plans/2026-02-22-native-alignment-impl.md +0 -925
- package/docs/plans/2026-02-22-native-enrichment-design.md +0 -244
- package/docs/plans/2026-02-22-native-enrichment.md +0 -735
- package/docs/plans/2026-02-23-ddd-architecture-refactor.md +0 -1153
- package/docs/plans/2026-02-23-ddd-nextsteps.md +0 -682
- package/docs/plans/2026-02-23-infra-architect-refactor.md +0 -437
- package/docs/plans/2026-02-23-nextjs-code-review-design.md +0 -156
- package/docs/plans/2026-02-23-nextjs-code-review-impl.md +0 -1254
- package/docs/plans/2026-02-23-nextjs-standards-design.md +0 -149
- package/docs/plans/2026-02-23-nextjs-standards-impl.md +0 -1846
- package/scripts/generate-refs.js +0 -336
- package/scripts/generate-standards-registry.js +0 -44
- package/scripts/scan-nextjs.mjs +0 -169
- package/scripts/validate-real.mjs +0 -255
- package/src/commands/feature/create-story.js +0 -362
- package/src/commands/feature/index.js +0 -6
- package/src/commands/feature/shard-spec.js +0 -225
- package/src/commands/feature/sprint-status.js +0 -250
- package/src/commands/generation/generate-onboarding.js +0 -169
- package/src/commands/generation/generate.js +0 -276
- package/src/commands/generation/index.js +0 -5
- package/src/commands/learning/capture-pattern.js +0 -121
- package/src/commands/learning/index.js +0 -5
- package/src/commands/learning/search-patterns.js +0 -126
- package/src/commands/mcp/mcp.js +0 -102
- package/src/commands/project/changes.js +0 -66
- package/src/commands/project/cost.js +0 -179
- package/src/commands/project/diff.js +0 -278
- package/src/commands/project/revert.js +0 -173
- package/src/commands/project/standards.js +0 -80
- package/src/commands/project/sync.js +0 -167
- package/src/commands/project/update-agents.js +0 -23
- package/src/commands/state/rollback-phase.js +0 -185
- package/src/commands/templates/template-customize.js +0 -87
- package/src/commands/templates/template-list.js +0 -114
- package/src/commands/templates/template-show.js +0 -129
- package/src/commands/templates/template-validate.js +0 -91
- package/src/commands/utils/troubleshoot.js +0 -222
- package/src/commands/validation/analyze-blazor-concurrency.js +0 -193
- package/src/commands/validation/lint-fluent.js +0 -352
- package/src/commands/validation/validate-blazor-state.js +0 -210
- package/src/commands/validation/validate-blazor.js +0 -156
- package/src/commands/validation/validate-css.js +0 -84
- package/src/lib/detectors/conversation-analyzer.js +0 -163
- package/src/lib/learning/index.js +0 -7
- package/src/lib/learning/learning-system.js +0 -520
- package/src/lib/troubleshooting/index.js +0 -8
- package/src/lib/troubleshooting/troubleshoot-grep.js +0 -198
- package/src/lib/troubleshooting/troubleshoot-index.js +0 -144
- package/src/llm/environment-detector.js +0 -43
package/scripts/generate-refs.js
DELETED
|
@@ -1,336 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* scripts/generate-refs.js
|
|
4
|
-
*
|
|
5
|
-
* Generates derived files from the single source of truth:
|
|
6
|
-
* src/core/paths/output-schema.js
|
|
7
|
-
*
|
|
8
|
-
* Outputs:
|
|
9
|
-
* framework/hooks/shared/phase-utils.js — constants derived from OUTPUT_SCHEMA
|
|
10
|
-
* framework/skills/level-1-workflows/*.md — morph:outputs blocks
|
|
11
|
-
*
|
|
12
|
-
* Usage:
|
|
13
|
-
* node scripts/generate-refs.js # generate files
|
|
14
|
-
* node scripts/generate-refs.js --check # validate only (CI, exit 1 if drift)
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
import { readFileSync, writeFileSync, existsSync } from 'fs';
|
|
18
|
-
import { join, dirname } from 'path';
|
|
19
|
-
import { fileURLToPath } from 'url';
|
|
20
|
-
import { OUTPUT_SCHEMA } from '../src/core/paths/output-schema.js';
|
|
21
|
-
|
|
22
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
23
|
-
const ROOT = join(__dirname, '..');
|
|
24
|
-
const CHECK_MODE = process.argv.includes('--check');
|
|
25
|
-
|
|
26
|
-
// ============================================================================
|
|
27
|
-
// Configuration
|
|
28
|
-
// ============================================================================
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Phase entries that cannot be derived from OUTPUT_SCHEMA because they share
|
|
32
|
-
* directories with other phases (no dedicated output type of their own).
|
|
33
|
-
*/
|
|
34
|
-
const PHASE_DIRS_EXTRAS = {
|
|
35
|
-
setup: '0-proposal', // setup outputs (proposal.md) go in 0-proposal dir
|
|
36
|
-
sync: '4-implement', // sync updates standards, works in implement space
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
/** Canonical phase ordering for PHASE_ORDER constant. */
|
|
40
|
-
const PHASE_ORDER = ['proposal', 'setup', 'uiux', 'design', 'clarify', 'tasks', 'implement', 'sync'];
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Skill files that contain <!-- morph:outputs:PHASE --> blocks.
|
|
44
|
-
* Keyed by the phase name to inject, value is the file path relative to ROOT.
|
|
45
|
-
*/
|
|
46
|
-
const SKILL_FILES = [
|
|
47
|
-
{ file: 'framework/skills/level-1-workflows/phase-setup.md', phase: 'proposal' },
|
|
48
|
-
{ file: 'framework/skills/level-1-workflows/phase-uiux.md', phase: 'uiux' },
|
|
49
|
-
{ file: 'framework/skills/level-1-workflows/phase-design.md', phase: 'design' },
|
|
50
|
-
{ file: 'framework/skills/level-1-workflows/phase-tasks.md', phase: 'tasks' },
|
|
51
|
-
{ file: 'framework/skills/level-1-workflows/phase-implement.md', phase: 'implement' },
|
|
52
|
-
];
|
|
53
|
-
|
|
54
|
-
// ============================================================================
|
|
55
|
-
// Derivation helpers
|
|
56
|
-
// ============================================================================
|
|
57
|
-
|
|
58
|
-
function derivePhaseDirs() {
|
|
59
|
-
const dirs = {};
|
|
60
|
-
for (const entry of Object.values(OUTPUT_SCHEMA)) {
|
|
61
|
-
dirs[entry.phase] = entry.phaseDir;
|
|
62
|
-
}
|
|
63
|
-
// Add extras in their canonical order (will be merged after schema-derived entries)
|
|
64
|
-
for (const [phase, dir] of Object.entries(PHASE_DIRS_EXTRAS)) {
|
|
65
|
-
dirs[phase] = dir;
|
|
66
|
-
}
|
|
67
|
-
// Re-order to match PHASE_ORDER
|
|
68
|
-
const ordered = {};
|
|
69
|
-
for (const phase of PHASE_ORDER) {
|
|
70
|
-
if (dirs[phase]) ordered[phase] = dirs[phase];
|
|
71
|
-
}
|
|
72
|
-
return ordered;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
function deriveOutputPhaseMap() {
|
|
76
|
-
return Object.fromEntries(
|
|
77
|
-
Object.entries(OUTPUT_SCHEMA).map(([key, entry]) => [key, entry.phase])
|
|
78
|
-
);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
function deriveFilenameToOutput() {
|
|
82
|
-
return Object.fromEntries(
|
|
83
|
-
Object.entries(OUTPUT_SCHEMA).map(([key, entry]) => [entry.filename, key])
|
|
84
|
-
);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
function deriveProtectedSpecFiles() {
|
|
88
|
-
return Object.fromEntries(
|
|
89
|
-
Object.entries(OUTPUT_SCHEMA)
|
|
90
|
-
.filter(([, entry]) => entry.protected && entry.approvalGate)
|
|
91
|
-
.map(([, entry]) => [entry.filename, entry.approvalGate])
|
|
92
|
-
);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// ============================================================================
|
|
96
|
-
// phase-utils.js generator
|
|
97
|
-
// ============================================================================
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* Static utility functions that live in phase-utils.js.
|
|
101
|
-
* Not derived from OUTPUT_SCHEMA — they operate on file paths generically.
|
|
102
|
-
*/
|
|
103
|
-
const UTIL_FUNCTIONS = `
|
|
104
|
-
/**
|
|
105
|
-
* Extract feature name from a .morph/features/{feature}/... path
|
|
106
|
-
* @param {string} filePath - File path to analyze
|
|
107
|
-
* @returns {string|null} Feature name or null
|
|
108
|
-
*/
|
|
109
|
-
export function extractFeatureName(filePath) {
|
|
110
|
-
const normalized = filePath.replace(/\\\\/g, '/');
|
|
111
|
-
const match = normalized.match(/\\.morph\\/features\\/([^/]+)\\//);
|
|
112
|
-
return match ? match[1] : null;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Extract phase subdirectory from a .morph/features/{feature}/{phaseDir}/... path
|
|
117
|
-
* @param {string} filePath - File path to analyze
|
|
118
|
-
* @returns {string|null} Phase directory (e.g., '0-proposal', '1-design') or null
|
|
119
|
-
*/
|
|
120
|
-
export function extractPhaseDir(filePath) {
|
|
121
|
-
const normalized = filePath.replace(/\\\\/g, '/');
|
|
122
|
-
const match = normalized.match(/\\.morph\\/features\\/[^/]+\\/(\\d+-[^/]+)\\//);
|
|
123
|
-
return match ? match[1] : null;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* Check if a file path is inside .morph/features/
|
|
128
|
-
* @param {string} filePath
|
|
129
|
-
* @returns {boolean}
|
|
130
|
-
*/
|
|
131
|
-
export function isFeaturePath(filePath) {
|
|
132
|
-
const normalized = filePath.replace(/\\\\/g, '/');
|
|
133
|
-
return normalized.includes('.morph/features/');
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* Check if a file path is inside .morph/framework/ (readonly)
|
|
138
|
-
* @param {string} filePath
|
|
139
|
-
* @returns {boolean}
|
|
140
|
-
*/
|
|
141
|
-
export function isFrameworkPath(filePath) {
|
|
142
|
-
const normalized = filePath.replace(/\\\\/g, '/');
|
|
143
|
-
return normalized.includes('.morph/framework/');
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* Check if a file path is state.json
|
|
148
|
-
* @param {string} filePath
|
|
149
|
-
* @returns {boolean}
|
|
150
|
-
*/
|
|
151
|
-
export function isStatePath(filePath) {
|
|
152
|
-
const normalized = filePath.replace(/\\\\/g, '/');
|
|
153
|
-
return normalized.endsWith('.morph/state.json');
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
/**
|
|
157
|
-
* Get the basename of a file path
|
|
158
|
-
* @param {string} filePath
|
|
159
|
-
* @returns {string}
|
|
160
|
-
*/
|
|
161
|
-
export function getBasename(filePath) {
|
|
162
|
-
const normalized = filePath.replace(/\\\\/g, '/');
|
|
163
|
-
return normalized.split('/').pop();
|
|
164
|
-
}
|
|
165
|
-
`;
|
|
166
|
-
|
|
167
|
-
function formatJsObject(obj) {
|
|
168
|
-
const needsQuotes = (key) => /[^a-zA-Z0-9_$]/.test(key);
|
|
169
|
-
const entries = Object.entries(obj).map(([k, v]) => {
|
|
170
|
-
const fmtKey = needsQuotes(k) ? `'${k}'` : k;
|
|
171
|
-
return ` ${fmtKey}: '${v}',`;
|
|
172
|
-
});
|
|
173
|
-
return `{\n${entries.join('\n')}\n}`;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
function generatePhaseUtilsContent() {
|
|
177
|
-
const phaseDirs = derivePhaseDirs();
|
|
178
|
-
const outputPhaseMap = deriveOutputPhaseMap();
|
|
179
|
-
const filenameToOut = deriveFilenameToOutput();
|
|
180
|
-
const protectedFiles = deriveProtectedSpecFiles();
|
|
181
|
-
|
|
182
|
-
return `// GENERATED by scripts/generate-refs.js — DO NOT EDIT manually
|
|
183
|
-
// Source of truth: src/core/paths/output-schema.js
|
|
184
|
-
// Regenerate with: node scripts/generate-refs.js
|
|
185
|
-
/**
|
|
186
|
-
* Shared phase utilities for Claude Code hooks.
|
|
187
|
-
*
|
|
188
|
-
* Maps phases to directories and output types.
|
|
189
|
-
*/
|
|
190
|
-
|
|
191
|
-
/** Phase order */
|
|
192
|
-
export const PHASE_ORDER = ${JSON.stringify(PHASE_ORDER)};
|
|
193
|
-
|
|
194
|
-
/** Map phase → allowed output subdirectory */
|
|
195
|
-
export const PHASE_DIRS = ${formatJsObject(phaseDirs)};
|
|
196
|
-
|
|
197
|
-
/** Map output type (camelCase) → phase that produces it */
|
|
198
|
-
export const OUTPUT_PHASE_MAP = ${formatJsObject(outputPhaseMap)};
|
|
199
|
-
|
|
200
|
-
/** Map filename → output type (camelCase) */
|
|
201
|
-
export const FILENAME_TO_OUTPUT = ${formatJsObject(filenameToOut)};
|
|
202
|
-
|
|
203
|
-
/** Protected spec files and the approval gate that locks them */
|
|
204
|
-
export const PROTECTED_SPEC_FILES = ${formatJsObject(protectedFiles)};
|
|
205
|
-
${UTIL_FUNCTIONS}`;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
// ============================================================================
|
|
209
|
-
// Markdown block generator
|
|
210
|
-
// ============================================================================
|
|
211
|
-
|
|
212
|
-
function generateOutputTable(phaseName) {
|
|
213
|
-
const entries = Object.entries(OUTPUT_SCHEMA)
|
|
214
|
-
.filter(([, entry]) => entry.phase === phaseName);
|
|
215
|
-
|
|
216
|
-
if (entries.length === 0) return null;
|
|
217
|
-
|
|
218
|
-
const rows = entries
|
|
219
|
-
.map(([key, entry]) =>
|
|
220
|
-
`| \`${key}\` | \`.morph/features/{feature}/${entry.phaseDir}/${entry.filename}\` |`
|
|
221
|
-
)
|
|
222
|
-
.join('\n');
|
|
223
|
-
|
|
224
|
-
return `| Output | Caminho |\n|--------|---------|
|
|
225
|
-
${rows}`;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
function replaceMarkdownBlocks(content, phaseName) {
|
|
229
|
-
const table = generateOutputTable(phaseName);
|
|
230
|
-
if (!table) return { changed: false, content };
|
|
231
|
-
|
|
232
|
-
const updated = content.replace(
|
|
233
|
-
/<!-- morph:outputs:(\w+) -->([\s\S]*?)<!-- \/morph:outputs -->/g,
|
|
234
|
-
(_match, blockPhase, _inner) => {
|
|
235
|
-
const blockTable = generateOutputTable(blockPhase);
|
|
236
|
-
if (!blockTable) return _match;
|
|
237
|
-
return `<!-- morph:outputs:${blockPhase} -->\n${blockTable}\n<!-- /morph:outputs -->`;
|
|
238
|
-
}
|
|
239
|
-
);
|
|
240
|
-
|
|
241
|
-
return { changed: updated !== content, content: updated };
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
// ============================================================================
|
|
245
|
-
// Write / check helpers
|
|
246
|
-
// ============================================================================
|
|
247
|
-
|
|
248
|
-
let driftFound = false;
|
|
249
|
-
|
|
250
|
-
function writeOrCheck(filePath, generated, label) {
|
|
251
|
-
const current = existsSync(filePath) ? readFileSync(filePath, 'utf8') : null;
|
|
252
|
-
|
|
253
|
-
if (current === generated) {
|
|
254
|
-
console.log(` ✓ ${label} — up to date`);
|
|
255
|
-
return;
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
if (CHECK_MODE) {
|
|
259
|
-
console.error(` ✗ ${label} — DRIFT DETECTED (run: node scripts/generate-refs.js)`);
|
|
260
|
-
if (current === null) {
|
|
261
|
-
console.error(` File does not exist: ${filePath}`);
|
|
262
|
-
} else {
|
|
263
|
-
// Show first difference line
|
|
264
|
-
const curLines = current.split('\n');
|
|
265
|
-
const genLines = generated.split('\n');
|
|
266
|
-
for (let i = 0; i < Math.max(curLines.length, genLines.length); i++) {
|
|
267
|
-
if (curLines[i] !== genLines[i]) {
|
|
268
|
-
console.error(` First diff at line ${i + 1}:`);
|
|
269
|
-
console.error(` current: ${JSON.stringify(curLines[i] ?? '(missing)')}`);
|
|
270
|
-
console.error(` generated: ${JSON.stringify(genLines[i] ?? '(missing)')}`);
|
|
271
|
-
break;
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
driftFound = true;
|
|
276
|
-
} else {
|
|
277
|
-
writeFileSync(filePath, generated, 'utf8');
|
|
278
|
-
console.log(` ✎ ${label} — updated`);
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
// ============================================================================
|
|
283
|
-
// Main
|
|
284
|
-
// ============================================================================
|
|
285
|
-
|
|
286
|
-
console.log(CHECK_MODE ? '\nChecking generated refs...' : '\nGenerating refs from output-schema.js...');
|
|
287
|
-
console.log('');
|
|
288
|
-
|
|
289
|
-
// 1. Generate / check phase-utils.js
|
|
290
|
-
const phaseUtilsPath = join(ROOT, 'framework/hooks/shared/phase-utils.js');
|
|
291
|
-
const phaseUtilsContent = generatePhaseUtilsContent();
|
|
292
|
-
writeOrCheck(phaseUtilsPath, phaseUtilsContent, 'framework/hooks/shared/phase-utils.js');
|
|
293
|
-
|
|
294
|
-
// 2. Update / check morph:outputs blocks in skill markdown files
|
|
295
|
-
console.log('');
|
|
296
|
-
for (const { file, phase } of SKILL_FILES) {
|
|
297
|
-
const filePath = join(ROOT, file);
|
|
298
|
-
if (!existsSync(filePath)) {
|
|
299
|
-
console.log(` - ${file} — not found, skipping`);
|
|
300
|
-
continue;
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
const content = readFileSync(filePath, 'utf8');
|
|
304
|
-
const hasMarker = content.includes(`<!-- morph:outputs:${phase} -->`);
|
|
305
|
-
|
|
306
|
-
if (!hasMarker) {
|
|
307
|
-
console.log(` - ${file} — no morph:outputs:${phase} marker, skipping`);
|
|
308
|
-
continue;
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
const { changed, content: updated } = replaceMarkdownBlocks(content, phase);
|
|
312
|
-
|
|
313
|
-
if (!changed) {
|
|
314
|
-
console.log(` ✓ ${file} — up to date`);
|
|
315
|
-
} else if (CHECK_MODE) {
|
|
316
|
-
console.error(` ✗ ${file} — morph:outputs block DRIFT DETECTED`);
|
|
317
|
-
driftFound = true;
|
|
318
|
-
} else {
|
|
319
|
-
writeFileSync(filePath, updated, 'utf8');
|
|
320
|
-
console.log(` ✎ ${file} — morph:outputs block updated`);
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
console.log('');
|
|
325
|
-
|
|
326
|
-
if (CHECK_MODE) {
|
|
327
|
-
if (driftFound) {
|
|
328
|
-
console.error('FAIL: Generated files are out of sync with output-schema.js');
|
|
329
|
-
console.error(' Run: node scripts/generate-refs.js');
|
|
330
|
-
process.exit(1);
|
|
331
|
-
} else {
|
|
332
|
-
console.log('OK: All generated refs are in sync with output-schema.js');
|
|
333
|
-
}
|
|
334
|
-
} else {
|
|
335
|
-
console.log('Done. Commit any changed files.');
|
|
336
|
-
}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* One-time script to generate framework/standards/STANDARDS.json
|
|
4
|
-
* by scanning all .md files in framework/standards/.
|
|
5
|
-
*
|
|
6
|
-
* Usage: node scripts/generate-standards-registry.js
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { readdirSync, statSync, writeFileSync } from 'fs';
|
|
10
|
-
import { join, relative } from 'path';
|
|
11
|
-
import { fileURLToPath } from 'url';
|
|
12
|
-
|
|
13
|
-
const __dirname = fileURLToPath(new URL('.', import.meta.url));
|
|
14
|
-
const BASE = join(__dirname, '..', 'framework', 'standards');
|
|
15
|
-
|
|
16
|
-
function scanDir(dir) {
|
|
17
|
-
const entries = [];
|
|
18
|
-
for (const f of readdirSync(dir)) {
|
|
19
|
-
const full = join(dir, f);
|
|
20
|
-
const rel = relative(BASE, full).replace(/\\/g, '/');
|
|
21
|
-
if (statSync(full).isDirectory()) {
|
|
22
|
-
entries.push(...scanDir(full));
|
|
23
|
-
} else if (f.endsWith('.md') && f !== 'README.md') {
|
|
24
|
-
const parts = rel.split('/');
|
|
25
|
-
entries.push({
|
|
26
|
-
id: rel.replace(/\//g, '-').replace('.md', ''),
|
|
27
|
-
name: f.replace('.md', '').replace(/-/g, ' '),
|
|
28
|
-
path: rel,
|
|
29
|
-
category: parts[0],
|
|
30
|
-
subcategory: parts.length > 2 ? parts[1] : null,
|
|
31
|
-
tags: parts.slice(0, -1),
|
|
32
|
-
});
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
return entries;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const standards = scanDir(BASE);
|
|
39
|
-
const registry = { version: '1.0.0', standards };
|
|
40
|
-
const outPath = join(BASE, 'STANDARDS.json');
|
|
41
|
-
writeFileSync(outPath, JSON.stringify(registry, null, 2) + '\n');
|
|
42
|
-
console.log(`Generated ${standards.length} standards entries`);
|
|
43
|
-
console.log(`Categories: ${[...new Set(standards.map(s => s.category))].join(', ')}`);
|
|
44
|
-
console.log(`Written to: ${outPath}`);
|
package/scripts/scan-nextjs.mjs
DELETED
|
@@ -1,169 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* scan-nextjs.mjs - CLI scan for Next.js violations.
|
|
4
|
-
* Usage: node scripts/scan-nextjs.mjs [path] [--json]
|
|
5
|
-
* Exit: 0=clean, 1=errors, 2=scan failed
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { readFileSync, existsSync, readdirSync } from 'node:fs';
|
|
9
|
-
import { join, resolve } from 'node:path';
|
|
10
|
-
import { validateNextComponent } from '../src/lib/validators/nextjs/next-component-validator.js';
|
|
11
|
-
|
|
12
|
-
const args = process.argv.slice(2);
|
|
13
|
-
const jsonMode = args.includes('--json');
|
|
14
|
-
const pathArg = args.find(a => !a.startsWith('--'));
|
|
15
|
-
const targetPath = pathArg ? resolve(pathArg) : resolve('src');
|
|
16
|
-
|
|
17
|
-
function checkUseEffectFetch(content, filePath) {
|
|
18
|
-
const issues = [];
|
|
19
|
-
if (/useEffect\s*\(\s*(?:async\s+)?\(\s*\)\s*=>/.test(content) && /\bfetch\s*\(/.test(content)) {
|
|
20
|
-
issues.push({
|
|
21
|
-
type: 'error',
|
|
22
|
-
message: "useEffect used for data fetching — use Server Components or TanStack Query (useQuery) instead",
|
|
23
|
-
suggestion: "Replace with a Server Component fetch or TanStack Query useQuery hook",
|
|
24
|
-
file: filePath,
|
|
25
|
-
});
|
|
26
|
-
}
|
|
27
|
-
return issues;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function checkDefaultExport(content, filePath) {
|
|
31
|
-
const issues = [];
|
|
32
|
-
const fileName = filePath.split('/').pop() ?? '';
|
|
33
|
-
const SPECIAL = ['page.tsx', 'layout.tsx', 'loading.tsx', 'error.tsx', 'not-found.tsx'];
|
|
34
|
-
if (!SPECIAL.includes(fileName) && /^export\s+default\s+/m.test(content) && fileName.endsWith('.tsx')) {
|
|
35
|
-
issues.push({
|
|
36
|
-
type: 'warning',
|
|
37
|
-
message: `Default export in '${fileName}' — prefer named exports for easier refactoring`,
|
|
38
|
-
suggestion: "Change to: export function ComponentName() {}",
|
|
39
|
-
file: filePath,
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
return issues;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function findTsxFiles(dir) {
|
|
46
|
-
const results = [];
|
|
47
|
-
let entries;
|
|
48
|
-
try {
|
|
49
|
-
entries = readdirSync(dir, { withFileTypes: true });
|
|
50
|
-
} catch {
|
|
51
|
-
return results;
|
|
52
|
-
}
|
|
53
|
-
for (const entry of entries) {
|
|
54
|
-
const fullPath = join(dir, entry.name);
|
|
55
|
-
if (['node_modules', '.next', 'dist', '.git'].includes(entry.name)) continue;
|
|
56
|
-
if (entry.isDirectory()) {
|
|
57
|
-
results.push(...findTsxFiles(fullPath));
|
|
58
|
-
} else if (entry.name.endsWith('.tsx') || entry.name.endsWith('.ts')) {
|
|
59
|
-
results.push(fullPath);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
return results;
|
|
63
|
-
}
|
|
64
|
-
function main() {
|
|
65
|
-
if (!existsSync(targetPath)) {
|
|
66
|
-
const msg = `Error: Path does not exist: ${targetPath}`;
|
|
67
|
-
if (jsonMode) {
|
|
68
|
-
console.log(JSON.stringify({ error: msg, exitCode: 2 }));
|
|
69
|
-
} else {
|
|
70
|
-
process.stderr.write(msg + '\n');
|
|
71
|
-
}
|
|
72
|
-
process.exit(2);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
let files;
|
|
76
|
-
try {
|
|
77
|
-
files = findTsxFiles(targetPath);
|
|
78
|
-
} catch (err) {
|
|
79
|
-
const msg = `Error scanning directory: ${err.message}`;
|
|
80
|
-
if (jsonMode) {
|
|
81
|
-
console.log(JSON.stringify({ error: msg, exitCode: 2 }));
|
|
82
|
-
} else {
|
|
83
|
-
process.stderr.write(msg + '\n');
|
|
84
|
-
}
|
|
85
|
-
process.exit(2);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
const normalizedTarget = targetPath.replace(/\\/g, '/');
|
|
89
|
-
|
|
90
|
-
const allIssues = [];
|
|
91
|
-
for (const filePath of files) {
|
|
92
|
-
let content;
|
|
93
|
-
try {
|
|
94
|
-
content = readFileSync(filePath, 'utf8');
|
|
95
|
-
} catch {
|
|
96
|
-
continue;
|
|
97
|
-
}
|
|
98
|
-
const normalizedFile = filePath.replace(/\\/g, '/');
|
|
99
|
-
const relPath = normalizedFile.startsWith(normalizedTarget)
|
|
100
|
-
? normalizedFile.slice(normalizedTarget.length).replace(/^\//, '')
|
|
101
|
-
: normalizedFile;
|
|
102
|
-
|
|
103
|
-
const issues = [
|
|
104
|
-
...validateNextComponent(content, relPath),
|
|
105
|
-
...checkUseEffectFetch(content, relPath),
|
|
106
|
-
...checkDefaultExport(content, relPath),
|
|
107
|
-
];
|
|
108
|
-
allIssues.push(...issues);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
const errors = allIssues.filter(i => i.type === 'error');
|
|
112
|
-
const warnings = allIssues.filter(i => i.type === 'warning');
|
|
113
|
-
|
|
114
|
-
if (jsonMode) {
|
|
115
|
-
const out = JSON.stringify({
|
|
116
|
-
files: files.length,
|
|
117
|
-
issues: allIssues.map(i => ({
|
|
118
|
-
severity: i.type,
|
|
119
|
-
message: i.message,
|
|
120
|
-
file: i.file,
|
|
121
|
-
line: i.line ?? null,
|
|
122
|
-
suggestion: i.suggestion ?? null,
|
|
123
|
-
})),
|
|
124
|
-
errors: errors.length,
|
|
125
|
-
warnings: warnings.length,
|
|
126
|
-
exitCode: errors.length > 0 ? 1 : 0,
|
|
127
|
-
}, null, 2);
|
|
128
|
-
process.stdout.write(out + '\n');
|
|
129
|
-
process.exit(errors.length > 0 ? 1 : 0);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
const displayPath = targetPath.replace(process.cwd().replace(/\\/g, '/'), '.').replace(/\\/g, '/');
|
|
133
|
-
console.log(`🔍 Scanning ${displayPath} (${files.length} file${files.length !== 1 ? "s" : ""})...`);
|
|
134
|
-
console.log("");
|
|
135
|
-
|
|
136
|
-
if (allIssues.length === 0) {
|
|
137
|
-
console.log(`✅ No issues found — ${files.length} file${files.length !== 1 ? "s" : ""} scanned`);
|
|
138
|
-
console.log(`Summary: ${files.length} files scanned | 0 issues (0 critical, 0 high, 0 medium)`);
|
|
139
|
-
process.exit(0);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
if (errors.length > 0) {
|
|
143
|
-
console.log(`CRITICAL (${errors.length})`);
|
|
144
|
-
for (const issue of errors) {
|
|
145
|
-
const loc = issue.line ? `:${issue.line}` : "";
|
|
146
|
-
console.log(` ${issue.file}${loc} — ${issue.message}`);
|
|
147
|
-
if (issue.suggestion) console.log(` → ${issue.suggestion}`);
|
|
148
|
-
}
|
|
149
|
-
console.log('');
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
if (warnings.length > 0) {
|
|
153
|
-
console.log(`HIGH/MEDIUM (${warnings.length})`);
|
|
154
|
-
for (const issue of warnings) {
|
|
155
|
-
console.log(` ${issue.file} — ${issue.message}`);
|
|
156
|
-
}
|
|
157
|
-
console.log('');
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
const cleanFiles = files.length - new Set(allIssues.map(i => i.file)).size;
|
|
161
|
-
if (cleanFiles > 0) {
|
|
162
|
-
console.log(`✅ No issues found in ${cleanFiles} file${cleanFiles !== 1 ? "s" : ""}`);
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
console.log(`Summary: ${files.length} files scanned | ${allIssues.length} issue${allIssues.length !== 1 ? "s" : ""} (${errors.length} critical, ${warnings.length} high/medium)`);
|
|
166
|
-
process.exit(errors.length > 0 ? 1 : 0);
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
main();
|