@polymorphism-tech/morph-spec 3.2.0 → 4.3.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/README.md +1 -14
- package/bin/detect-agents.js +1 -1
- package/bin/morph-spec.js +403 -40
- package/bin/validate.js +5 -5
- package/docs/getting-started.md +0 -5
- package/docs/next-generation/AGENTS.md +521 -0
- package/docs/next-generation/ANALYSIS.md +555 -0
- package/docs/next-generation/ARCHITECTURE.md +436 -0
- package/docs/next-generation/CONTEXT-OPTIMIZATION.md +267 -0
- package/docs/next-generation/EXECUTION-FLOW.md +274 -0
- package/docs/next-generation/FEATURES.md +688 -0
- package/docs/next-generation/META-PROMPTS.md +235 -0
- package/docs/next-generation/MIGRATION-GUIDE.md +253 -0
- package/docs/next-generation/README.md +231 -0
- package/docs/next-generation/ROADMAP.md +801 -0
- package/docs/next-generation/THREAD-MANAGEMENT.md +240 -0
- package/docs/validation-checklist.md +0 -1
- package/package.json +5 -5
- package/src/commands/agents/agents-fuse.js +96 -0
- package/src/commands/agents/index.js +4 -0
- package/src/commands/agents/micro-agent.js +112 -0
- package/src/commands/{spawn-team.js → agents/spawn-team.js} +237 -172
- package/src/commands/agents/squad-template.js +146 -0
- package/src/commands/analytics/analytics.js +176 -0
- package/src/commands/context/context-prime.js +63 -0
- package/src/commands/context/core-four.js +54 -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} +4 -4
- package/src/commands/generation/index.js +5 -0
- package/src/commands/index.js +16 -0
- package/src/commands/{capture-pattern.js → learning/capture-pattern.js} +121 -121
- package/src/commands/learning/index.js +5 -0
- package/src/commands/mcp/mcp.js +102 -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} +221 -4
- package/src/commands/project/index.js +10 -0
- package/src/commands/{init.js → project/init.js} +305 -295
- package/src/commands/{sync.js → project/sync.js} +167 -167
- package/src/commands/{update.js → project/update.js} +240 -240
- package/src/commands/{advance-phase.js → state/advance-phase.js} +101 -25
- package/src/commands/{approve.js → state/approve.js} +221 -221
- package/src/commands/state/index.js +8 -0
- package/src/commands/{rollback-phase.js → state/rollback-phase.js} +185 -185
- package/src/commands/{state.js → state/state.js} +334 -334
- package/src/commands/{validate-phase.js → state/validate-phase.js} +221 -221
- package/src/commands/tasks/index.js +4 -0
- package/src/commands/{task.js → tasks/task.js} +78 -78
- package/src/commands/templates/index.js +8 -0
- package/src/commands/templates/template-customize.js +101 -0
- package/src/commands/templates/template-list.js +128 -0
- package/src/commands/templates/template-render.js +174 -0
- package/src/commands/templates/template-show.js +131 -0
- package/src/commands/templates/template-validate.js +91 -0
- package/src/commands/threads/thread-template.js +103 -0
- package/src/commands/threads/threads.js +261 -0
- package/src/commands/trust/trust.js +205 -0
- package/src/commands/utils/index.js +7 -0
- package/src/commands/{session-summary.js → utils/session-summary.js} +291 -291
- package/src/commands/{troubleshoot.js → utils/troubleshoot.js} +222 -222
- package/src/commands/{analyze-blazor-concurrency.js → validation/analyze-blazor-concurrency.js} +193 -193
- package/src/commands/validation/index.js +8 -0
- package/src/commands/{lint-fluent.js → validation/lint-fluent.js} +352 -352
- package/src/commands/{validate-blazor-state.js → validation/validate-blazor-state.js} +210 -210
- package/src/commands/{validate-blazor.js → validation/validate-blazor.js} +156 -156
- package/src/commands/{validate-css.js → validation/validate-css.js} +84 -84
- package/src/core/index.js +10 -0
- package/src/{orchestrator.js → core/orchestrator.js} +8 -8
- package/src/core/registry/command-registry.js +302 -0
- package/src/core/registry/index.js +8 -0
- package/src/core/registry/validator-registry.js +204 -0
- package/src/core/state/index.js +8 -0
- package/src/{lib → core/state}/phase-state-machine.js +214 -214
- package/src/{lib → core/state}/state-manager.js +588 -534
- package/src/core/templates/index.js +9 -0
- package/src/core/templates/template-registry.js +335 -0
- package/src/core/templates/template-renderer.js +477 -0
- package/src/core/templates/template-validator.js +296 -0
- package/src/core/workflows/index.js +7 -0
- package/src/core/workflows/workflow-detector.js +452 -0
- package/src/lib/agents/micro-agent-factory.js +161 -0
- package/src/lib/{complexity-analyzer.js → analysis/complexity-analyzer.js} +441 -441
- package/src/lib/analysis/index.js +7 -0
- package/src/lib/analytics/analytics-engine.js +345 -0
- package/src/lib/{checkpoint-hooks.js → checkpoints/checkpoint-hooks.js} +35 -0
- package/src/lib/checkpoints/index.js +7 -0
- package/src/lib/context/context-bundler.js +240 -0
- package/src/lib/context/context-optimizer.js +212 -0
- package/src/lib/context/context-tracker.js +273 -0
- package/src/lib/context/core-four-tracker.js +201 -0
- package/src/lib/context/mcp-optimizer.js +200 -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/execution/fusion-executor.js +304 -0
- package/src/lib/execution/parallel-executor.js +270 -0
- package/src/lib/{context-generator.js → generators/context-generator.js} +526 -516
- package/src/lib/generators/index.js +10 -0
- package/src/lib/{metadata-extractor.js → generators/metadata-extractor.js} +387 -380
- package/src/lib/{recap-generator.js → generators/recap-generator.js} +205 -205
- package/src/lib/hooks/hook-executor.js +169 -0
- package/src/lib/hooks/stop-hook-executor.js +286 -0
- package/src/lib/hops/hop-composer.js +221 -0
- 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/threads/thread-coordinator.js +238 -0
- package/src/lib/threads/thread-manager.js +317 -0
- package/src/lib/tracking/artifact-trail.js +202 -0
- 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/trust/trust-manager.js +269 -0
- package/src/lib/validators/{architecture-validator.js → architecture/architecture-validator.js} +8 -8
- package/src/lib/validators/architecture/index.js +7 -0
- 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-validator.js → content/content-validator.js} +351 -351
- package/src/lib/validators/content/index.js +7 -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/packages/index.js +7 -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/{validation-runner.js → validators/validation-runner.js} +286 -284
- package/src/ui/wizard-questions.js +0 -2
- package/src/utils/color-utils.js +70 -0
- package/src/utils/file-copier.js +188 -189
- package/src/utils/process-handler.js +97 -0
- package/stacks/blazor-azure/.morph/config/agents.json +948 -764
- package/stacks/blazor-azure/.morph/hooks/{pre-commit-tests.sh → pre-commit/tests-csharp.sh} +3 -2
- package/stacks/blazor-azure/.morph/templates/infrastructure/github/workflows/cd-prod.yml.hbs +41 -0
- package/stacks/blazor-azure/.morph/templates/infrastructure/github/workflows/cd-staging.yml.hbs +24 -0
- package/stacks/blazor-azure/.morph/templates/infrastructure/github/workflows/ci-build.yml.hbs +23 -0
- package/stacks/nextjs-supabase/.morph/config/agents.json +345 -345
- package/stacks/nextjs-supabase/.morph/hooks/pre-commit/tests-typescript.sh +61 -0
- package/stacks/nextjs-supabase/.morph/templates/infrastructure/github/workflows/cd-prod.yml.hbs +22 -0
- package/stacks/nextjs-supabase/.morph/templates/infrastructure/github/workflows/cd-staging.yml.hbs +22 -0
- package/stacks/nextjs-supabase/.morph/templates/infrastructure/github/workflows/ci-build.yml.hbs +35 -0
- package/stacks/nextjs-supabase/README.md +6 -15
- package/CLAUDE.md +0 -648
- package/bin/render-template.js +0 -349
- package/bin/semantic-detect-agents.js +0 -247
- package/bin/validate-agents-skills.js +0 -257
- package/bin/validate-agents.js +0 -70
- package/bin/validate-phase.js +0 -263
- package/docs/examples.md +0 -328
- package/docs/llm-interaction-config.md +0 -735
- 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/commands/migrate-state.js +0 -158
- package/src/commands/upgrade.js +0 -346
- package/src/lib/continuous-validator.js +0 -421
- package/src/lib/decision-constraint-loader.js +0 -109
- package/src/lib/design-system-scaffolder.js +0 -299
- package/src/lib/hook-executor.js +0 -257
- package/src/lib/mockup-generator.js +0 -366
- package/src/lib/ui-detector.js +0 -350
- package/src/llm/schema-validator.js +0 -121
- package/src/sanitizer/.gitkeep +0 -0
- package/src/scanner/.gitkeep +0 -0
- package/src/types/index.js +0 -477
- package/src/ui/.gitkeep +0 -0
- package/src/writer/.gitkeep +0 -0
- package/stacks/blazor-azure/.azure/README.md +0 -293
- package/stacks/blazor-azure/.azure/docs/azure-devops-setup.md +0 -454
- package/stacks/blazor-azure/.azure/docs/branch-strategy.md +0 -398
- package/stacks/blazor-azure/.azure/docs/local-development.md +0 -515
- package/stacks/blazor-azure/.azure/pipelines/pipeline-variables.yml +0 -34
- package/stacks/blazor-azure/.azure/pipelines/prod-pipeline.yml +0 -319
- package/stacks/blazor-azure/.azure/pipelines/staging-pipeline.yml +0 -234
- package/stacks/blazor-azure/.azure/pipelines/templates/build-dotnet.yml +0 -75
- package/stacks/blazor-azure/.azure/pipelines/templates/deploy-app-service.yml +0 -94
- package/stacks/blazor-azure/.azure/pipelines/templates/deploy-container-app.yml +0 -120
- package/stacks/blazor-azure/.azure/pipelines/templates/infra-deploy.yml +0 -90
- package/stacks/blazor-azure/.claude/commands/morph-apply.md +0 -221
- package/stacks/blazor-azure/.claude/commands/morph-archive.md +0 -79
- package/stacks/blazor-azure/.claude/commands/morph-deploy.md +0 -529
- package/stacks/blazor-azure/.claude/commands/morph-infra.md +0 -209
- package/stacks/blazor-azure/.claude/commands/morph-preflight.md +0 -227
- package/stacks/blazor-azure/.claude/commands/morph-proposal.md +0 -122
- package/stacks/blazor-azure/.claude/commands/morph-status.md +0 -86
- package/stacks/blazor-azure/.claude/commands/morph-troubleshoot.md +0 -122
- package/stacks/blazor-azure/.claude/settings.local.json +0 -15
- package/stacks/blazor-azure/.claude/skills/level-0-meta/README.md +0 -7
- package/stacks/blazor-azure/.claude/skills/level-0-meta/code-review.md +0 -226
- package/stacks/blazor-azure/.claude/skills/level-0-meta/morph-checklist.md +0 -117
- package/stacks/blazor-azure/.claude/skills/level-0-meta/simulation-checklist.md +0 -77
- package/stacks/blazor-azure/.claude/skills/level-1-workflows/README.md +0 -7
- package/stacks/blazor-azure/.claude/skills/level-1-workflows/morph-replicate.md +0 -213
- package/stacks/blazor-azure/.claude/skills/level-1-workflows/phase-clarify.md +0 -131
- package/stacks/blazor-azure/.claude/skills/level-1-workflows/phase-design.md +0 -213
- package/stacks/blazor-azure/.claude/skills/level-1-workflows/phase-setup.md +0 -106
- package/stacks/blazor-azure/.claude/skills/level-1-workflows/phase-tasks.md +0 -164
- package/stacks/blazor-azure/.claude/skills/level-1-workflows/phase-uiux.md +0 -169
- package/stacks/blazor-azure/.claude/skills/level-2-domains/README.md +0 -14
- package/stacks/blazor-azure/.claude/skills/level-2-domains/ai-agents/ai-system-architect.md +0 -192
- package/stacks/blazor-azure/.claude/skills/level-2-domains/architecture/po-pm-advisor.md +0 -197
- package/stacks/blazor-azure/.claude/skills/level-2-domains/architecture/prompt-engineer.md +0 -189
- package/stacks/blazor-azure/.claude/skills/level-2-domains/architecture/seo-growth-hacker.md +0 -320
- package/stacks/blazor-azure/.claude/skills/level-2-domains/architecture/standards-architect.md +0 -156
- package/stacks/blazor-azure/.claude/skills/level-2-domains/backend/dotnet-senior.md +0 -287
- package/stacks/blazor-azure/.claude/skills/level-2-domains/backend/ef-modeler.md +0 -113
- package/stacks/blazor-azure/.claude/skills/level-2-domains/backend/hangfire-orchestrator.md +0 -126
- package/stacks/blazor-azure/.claude/skills/level-2-domains/backend/ms-agent-expert.md +0 -109
- package/stacks/blazor-azure/.claude/skills/level-2-domains/frontend/blazor-builder.md +0 -210
- package/stacks/blazor-azure/.claude/skills/level-2-domains/frontend/nextjs-expert.md +0 -154
- package/stacks/blazor-azure/.claude/skills/level-2-domains/frontend/ui-ux-designer.md +0 -191
- package/stacks/blazor-azure/.claude/skills/level-2-domains/infrastructure/azure-architect.md +0 -142
- package/stacks/blazor-azure/.claude/skills/level-2-domains/infrastructure/azure-deploy-specialist.md +0 -699
- package/stacks/blazor-azure/.claude/skills/level-2-domains/infrastructure/bicep-architect.md +0 -126
- package/stacks/blazor-azure/.claude/skills/level-2-domains/infrastructure/container-specialist.md +0 -131
- package/stacks/blazor-azure/.claude/skills/level-2-domains/infrastructure/devops-engineer.md +0 -119
- package/stacks/blazor-azure/.claude/skills/level-2-domains/integrations/asaas-financial.md +0 -130
- package/stacks/blazor-azure/.claude/skills/level-2-domains/integrations/azure-identity.md +0 -142
- package/stacks/blazor-azure/.claude/skills/level-2-domains/integrations/clerk-auth.md +0 -108
- package/stacks/blazor-azure/.claude/skills/level-2-domains/integrations/resend-email.md +0 -119
- package/stacks/blazor-azure/.claude/skills/level-2-domains/quality/code-analyzer.md +0 -235
- package/stacks/blazor-azure/.claude/skills/level-2-domains/quality/testing-specialist.md +0 -126
- package/stacks/blazor-azure/.claude/skills/level-3-technologies/README.md +0 -7
- package/stacks/blazor-azure/.claude/skills/level-4-patterns/README.md +0 -7
- package/stacks/blazor-azure/.morph/archive/.gitkeep +0 -25
- 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/features/.gitkeep +0 -25
- 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/schemas/agent.schema.json +0 -296
- package/stacks/blazor-azure/.morph/schemas/tasks.schema.json +0 -220
- package/stacks/blazor-azure/.morph/specs/.gitkeep +0 -20
- 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/blazor-azure/.morph/test-infra/example.bicep +0 -59
- package/stacks/nextjs-supabase/.claude/skills/level-2-domains/backend/dotnet-supabase.md +0 -244
- package/stacks/nextjs-supabase/.claude/skills/level-2-domains/frontend/nextjs-supabase.md +0 -335
- package/stacks/nextjs-supabase/.claude/skills/level-2-domains/infrastructure/easypanel-deployer.md +0 -189
- package/stacks/nextjs-supabase/.claude/skills/level-2-domains/integrations/supabase-expert.md +0 -170
- package/stacks/nextjs-supabase/.morph/docs/easypanel-setup.md +0 -169
- package/stacks/nextjs-supabase/.morph/docs/supabase-mcp-setup.md +0 -247
- package/stacks/nextjs-supabase/.morph/examples/crud-nextjs-supabase/README.md +0 -697
- package/stacks/nextjs-supabase/.morph/examples/crud-nextjs-supabase/spec.md +0 -85
- package/stacks/nextjs-supabase/.morph/examples/crud-nextjs-supabase/tasks.md +0 -86
- package/stacks/nextjs-supabase/.morph/examples/saas-nextjs-supabase/README.md +0 -498
- package/stacks/nextjs-supabase/.morph/examples/saas-nextjs-supabase/decisions.md +0 -121
- package/stacks/nextjs-supabase/.morph/examples/saas-nextjs-supabase/spec.md +0 -138
- package/stacks/nextjs-supabase/.morph/examples/saas-nextjs-supabase/tasks.md +0 -162
- package/stacks/nextjs-supabase/.morph/standards/easypanel-deploy.md +0 -191
- package/stacks/nextjs-supabase/.morph/standards/nextjs-patterns.md +0 -193
- package/stacks/nextjs-supabase/.morph/standards/supabase-auth.md +0 -171
- package/stacks/nextjs-supabase/.morph/standards/supabase-pgvector.md +0 -164
- package/stacks/nextjs-supabase/.morph/standards/supabase-rls.md +0 -179
- package/stacks/nextjs-supabase/.morph/standards/supabase-storage.md +0 -148
- package/stacks/nextjs-supabase/.morph/templates/contracts.cs +0 -173
- package/stacks/nextjs-supabase/.morph/templates/contracts.ts +0 -168
- package/stacks/nextjs-supabase/.morph/templates/decisions.md +0 -115
- package/stacks/nextjs-supabase/.morph/templates/dockerfile-api.dockerfile +0 -38
- package/stacks/nextjs-supabase/.morph/templates/dockerfile-web.dockerfile +0 -48
- package/stacks/nextjs-supabase/.morph/templates/proposal.md +0 -145
- package/stacks/nextjs-supabase/.morph/templates/recap.md +0 -134
- package/stacks/nextjs-supabase/.morph/templates/rls-policy.sql +0 -57
- package/stacks/nextjs-supabase/.morph/templates/spec.md +0 -231
- package/stacks/nextjs-supabase/.morph/templates/supabase-migration.sql +0 -100
- package/stacks/nextjs-supabase/.morph/templates/tasks.md +0 -257
- /package/src/commands/{search-patterns.js → learning/search-patterns.js} +0 -0
- /package/src/{lib → core/templates}/template-data-sources.js +0 -0
- /package/src/lib/{design-system-generator.js → generators/design-system-generator.js} +0 -0
- /package/src/lib/{learning-system.js → learning/learning-system.js} +0 -0
- /package/src/lib/validators/{package-validator.js → packages/package-validator.js} +0 -0
- /package/src/lib/validators/{ui-contrast-validator.js → ui/ui-contrast-validator.js} +0 -0
- /package/{src/generator → stacks/blazor-azure/.morph/templates}/.gitkeep +0 -0
- /package/{src/llm → stacks/nextjs-supabase/.morph/templates}/.gitkeep +0 -0
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Analytics Engine — Metrics collection and JSONL storage
|
|
3
|
+
*
|
|
4
|
+
* Records events to append-only JSONL files:
|
|
5
|
+
* - .morph/analytics/threads-log.jsonl
|
|
6
|
+
* - .morph/analytics/context-log.jsonl
|
|
7
|
+
* - .morph/analytics/trust-log.jsonl
|
|
8
|
+
*
|
|
9
|
+
* Provides aggregation, ASCII chart generation, and 30-day dashboards.
|
|
10
|
+
* Auto-prunes entries older than 90 days.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { readFileSync, writeFileSync, appendFileSync, existsSync, mkdirSync } from 'fs';
|
|
14
|
+
import { join } from 'path';
|
|
15
|
+
|
|
16
|
+
const ANALYTICS_DIR = join(process.cwd(), '.morph/analytics');
|
|
17
|
+
const THREADS_LOG = join(ANALYTICS_DIR, 'threads-log.jsonl');
|
|
18
|
+
const CONTEXT_LOG = join(ANALYTICS_DIR, 'context-log.jsonl');
|
|
19
|
+
const TRUST_LOG = join(ANALYTICS_DIR, 'trust-log.jsonl');
|
|
20
|
+
const PRUNE_DAYS = 90;
|
|
21
|
+
const DASHBOARD_DAYS = 30;
|
|
22
|
+
|
|
23
|
+
// ============================================================================
|
|
24
|
+
// Internal Helpers
|
|
25
|
+
// ============================================================================
|
|
26
|
+
|
|
27
|
+
function ensureAnalyticsDir() {
|
|
28
|
+
if (!existsSync(ANALYTICS_DIR)) {
|
|
29
|
+
mkdirSync(ANALYTICS_DIR, { recursive: true });
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function appendJSONL(filePath, record) {
|
|
34
|
+
ensureAnalyticsDir();
|
|
35
|
+
appendFileSync(filePath, JSON.stringify(record) + '\n', 'utf8');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function readJSONL(filePath) {
|
|
39
|
+
if (!existsSync(filePath)) return [];
|
|
40
|
+
|
|
41
|
+
const lines = readFileSync(filePath, 'utf8').trim().split('\n').filter(Boolean);
|
|
42
|
+
const records = [];
|
|
43
|
+
|
|
44
|
+
for (const line of lines) {
|
|
45
|
+
try {
|
|
46
|
+
records.push(JSON.parse(line));
|
|
47
|
+
} catch {
|
|
48
|
+
// Skip malformed lines
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return records;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function pruneJSONL(filePath) {
|
|
55
|
+
if (!existsSync(filePath)) return;
|
|
56
|
+
|
|
57
|
+
const cutoff = new Date();
|
|
58
|
+
cutoff.setDate(cutoff.getDate() - PRUNE_DAYS);
|
|
59
|
+
|
|
60
|
+
const records = readJSONL(filePath);
|
|
61
|
+
const fresh = records.filter(r => {
|
|
62
|
+
const ts = new Date(r.timestamp || r.createdAt || 0);
|
|
63
|
+
return ts > cutoff;
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
if (fresh.length < records.length) {
|
|
67
|
+
writeFileSync(filePath, fresh.map(r => JSON.stringify(r)).join('\n') + '\n', 'utf8');
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function recentRecords(records, days = DASHBOARD_DAYS) {
|
|
72
|
+
const cutoff = new Date();
|
|
73
|
+
cutoff.setDate(cutoff.getDate() - days);
|
|
74
|
+
return records.filter(r => {
|
|
75
|
+
const ts = new Date(r.timestamp || r.createdAt || 0);
|
|
76
|
+
return ts > cutoff;
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// ============================================================================
|
|
81
|
+
// Event Recording
|
|
82
|
+
// ============================================================================
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Record a thread analytics event
|
|
86
|
+
* @param {Object} event
|
|
87
|
+
* @param {string} event.type - Event type (thread_created|thread_completed|thread_failed|checkpoint_passed|etc.)
|
|
88
|
+
* @param {string} event.feature - Feature name
|
|
89
|
+
* @param {string} [event.threadId] - Thread ID
|
|
90
|
+
* @param {string} [event.agent] - Agent name
|
|
91
|
+
* @param {Object} [event.data] - Additional event data
|
|
92
|
+
*/
|
|
93
|
+
export function recordEvent(event) {
|
|
94
|
+
const record = {
|
|
95
|
+
...event,
|
|
96
|
+
timestamp: new Date().toISOString()
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
// Route to appropriate log
|
|
100
|
+
if (event.type?.startsWith('context_') || event.type === 'token_usage') {
|
|
101
|
+
appendJSONL(CONTEXT_LOG, record);
|
|
102
|
+
} else if (event.type?.startsWith('trust_')) {
|
|
103
|
+
appendJSONL(TRUST_LOG, record);
|
|
104
|
+
} else {
|
|
105
|
+
appendJSONL(THREADS_LOG, record);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Record a context event (token usage, optimization, etc.)
|
|
111
|
+
* @param {Object} event
|
|
112
|
+
*/
|
|
113
|
+
export function recordContextEvent(event) {
|
|
114
|
+
recordEvent({ ...event, type: event.type || 'context_usage' });
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Record a trust level change event
|
|
119
|
+
* @param {string} feature
|
|
120
|
+
* @param {string} level - new trust level
|
|
121
|
+
* @param {string} reason
|
|
122
|
+
*/
|
|
123
|
+
export function recordTrustEvent(feature, level, reason = '') {
|
|
124
|
+
appendJSONL(TRUST_LOG, {
|
|
125
|
+
timestamp: new Date().toISOString(),
|
|
126
|
+
type: 'trust_level_changed',
|
|
127
|
+
feature,
|
|
128
|
+
level,
|
|
129
|
+
reason
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Record auto-approval event (gate approved by trust)
|
|
135
|
+
* @param {string} feature
|
|
136
|
+
* @param {string} gate - 'design' | 'tasks' | 'proposal'
|
|
137
|
+
* @param {string} trustLevel
|
|
138
|
+
* @param {number} passRate
|
|
139
|
+
*/
|
|
140
|
+
export function recordAutoApproval(feature, gate, trustLevel, passRate) {
|
|
141
|
+
appendJSONL(TRUST_LOG, {
|
|
142
|
+
timestamp: new Date().toISOString(),
|
|
143
|
+
type: 'trust_auto_approved',
|
|
144
|
+
feature,
|
|
145
|
+
gate,
|
|
146
|
+
trustLevel,
|
|
147
|
+
passRate,
|
|
148
|
+
timeSavedMinutes: 5 // estimated minutes saved per auto-approval
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Get trust progression for a feature
|
|
154
|
+
* @param {string} featureName
|
|
155
|
+
* @returns {Array} Trust level history
|
|
156
|
+
*/
|
|
157
|
+
export function getTrustProgression(featureName) {
|
|
158
|
+
const events = readJSONL(TRUST_LOG)
|
|
159
|
+
.filter(e => e.feature === featureName && e.type === 'trust_level_changed')
|
|
160
|
+
.sort((a, b) => new Date(a.timestamp) - new Date(b.timestamp));
|
|
161
|
+
return events;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// ============================================================================
|
|
165
|
+
// Feature Analytics
|
|
166
|
+
// ============================================================================
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Get analytics for a specific feature
|
|
170
|
+
* @param {string} feature - Feature name
|
|
171
|
+
* @returns {Object} Feature analytics
|
|
172
|
+
*/
|
|
173
|
+
export function getFeatureAnalytics(feature) {
|
|
174
|
+
const allThreadEvents = readJSONL(THREADS_LOG).filter(r => r.feature === feature);
|
|
175
|
+
const allContextEvents = readJSONL(CONTEXT_LOG).filter(r => r.feature === feature);
|
|
176
|
+
|
|
177
|
+
const threadsByStatus = allThreadEvents
|
|
178
|
+
.filter(e => e.type === 'thread_completed' || e.type === 'thread_failed')
|
|
179
|
+
.reduce((acc, e) => {
|
|
180
|
+
const status = e.type === 'thread_completed' ? 'completed' : 'failed';
|
|
181
|
+
acc[status] = (acc[status] || 0) + 1;
|
|
182
|
+
return acc;
|
|
183
|
+
}, {});
|
|
184
|
+
|
|
185
|
+
const checkpoints = allThreadEvents.filter(e =>
|
|
186
|
+
e.type === 'checkpoint_passed' || e.type === 'checkpoint_failed'
|
|
187
|
+
);
|
|
188
|
+
const checkpointPassRate = checkpoints.length > 0
|
|
189
|
+
? Math.round(checkpoints.filter(e => e.type === 'checkpoint_passed').length / checkpoints.length * 100)
|
|
190
|
+
: 100;
|
|
191
|
+
|
|
192
|
+
const totalDuration = allThreadEvents
|
|
193
|
+
.filter(e => e.type === 'thread_completed' && e.data?.duration)
|
|
194
|
+
.reduce((sum, e) => sum + e.data.duration, 0);
|
|
195
|
+
|
|
196
|
+
const toolCallEvents = allThreadEvents.filter(e => e.type === 'tool_call');
|
|
197
|
+
const tokenEvents = allContextEvents.filter(e => e.data?.tokensUsed);
|
|
198
|
+
const totalTokens = tokenEvents.reduce((sum, e) => sum + (e.data.tokensUsed || 0), 0);
|
|
199
|
+
|
|
200
|
+
return {
|
|
201
|
+
feature,
|
|
202
|
+
threads: threadsByStatus,
|
|
203
|
+
checkpointPassRate,
|
|
204
|
+
totalDuration,
|
|
205
|
+
totalToolCalls: toolCallEvents.length,
|
|
206
|
+
totalTokensUsed: totalTokens,
|
|
207
|
+
optimizationEvents: allContextEvents.filter(e => e.type === 'context_optimized').length
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Get project-wide analytics dashboard (30-day summary)
|
|
213
|
+
* @returns {Object} Project analytics
|
|
214
|
+
*/
|
|
215
|
+
export function getProjectAnalytics() {
|
|
216
|
+
const allThreadEvents = recentRecords(readJSONL(THREADS_LOG));
|
|
217
|
+
const allContextEvents = recentRecords(readJSONL(CONTEXT_LOG));
|
|
218
|
+
const allTrustEvents = recentRecords(readJSONL(TRUST_LOG));
|
|
219
|
+
|
|
220
|
+
const features = [...new Set(allThreadEvents.map(e => e.feature).filter(Boolean))];
|
|
221
|
+
|
|
222
|
+
const completedThreads = allThreadEvents.filter(e => e.type === 'thread_completed');
|
|
223
|
+
const failedThreads = allThreadEvents.filter(e => e.type === 'thread_failed');
|
|
224
|
+
const checkpoints = allThreadEvents.filter(e =>
|
|
225
|
+
e.type === 'checkpoint_passed' || e.type === 'checkpoint_failed'
|
|
226
|
+
);
|
|
227
|
+
|
|
228
|
+
const parallelEvents = allThreadEvents.filter(e => e.data?.type === 'parallel');
|
|
229
|
+
const avgParallel = parallelEvents.length > 0
|
|
230
|
+
? parallelEvents.reduce((sum, e) => sum + (e.data?.concurrency || 1), 0) / parallelEvents.length
|
|
231
|
+
: 1;
|
|
232
|
+
|
|
233
|
+
const autoApprovalEvents = allTrustEvents.filter(e => e.type === 'trust_auto_approved');
|
|
234
|
+
const avgTokens = allContextEvents.filter(e => e.data?.tokensUsed).length > 0
|
|
235
|
+
? Math.round(allContextEvents.reduce((sum, e) => sum + (e.data?.tokensUsed || 0), 0) / allContextEvents.filter(e => e.data?.tokensUsed).length)
|
|
236
|
+
: 0;
|
|
237
|
+
|
|
238
|
+
return {
|
|
239
|
+
period: `${DASHBOARD_DAYS} days`,
|
|
240
|
+
features: features.length,
|
|
241
|
+
threads: {
|
|
242
|
+
total: completedThreads.length + failedThreads.length,
|
|
243
|
+
completed: completedThreads.length,
|
|
244
|
+
failed: failedThreads.length
|
|
245
|
+
},
|
|
246
|
+
checkpoints: {
|
|
247
|
+
total: checkpoints.length,
|
|
248
|
+
passed: checkpoints.filter(e => e.type === 'checkpoint_passed').length,
|
|
249
|
+
passRate: checkpoints.length > 0
|
|
250
|
+
? Math.round(checkpoints.filter(e => e.type === 'checkpoint_passed').length / checkpoints.length * 100)
|
|
251
|
+
: 100
|
|
252
|
+
},
|
|
253
|
+
parallelization: {
|
|
254
|
+
avgConcurrency: Math.round(avgParallel * 10) / 10,
|
|
255
|
+
parallelThreads: parallelEvents.length
|
|
256
|
+
},
|
|
257
|
+
trust: {
|
|
258
|
+
autoApprovals: autoApprovalEvents.length,
|
|
259
|
+
trustChanges: allTrustEvents.filter(e => e.type === 'trust_level_changed').length
|
|
260
|
+
},
|
|
261
|
+
context: {
|
|
262
|
+
avgTokensPerSession: avgTokens,
|
|
263
|
+
optimizations: allContextEvents.filter(e => e.type === 'context_optimized').length
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// ============================================================================
|
|
269
|
+
// ASCII Charts
|
|
270
|
+
// ============================================================================
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Generate an ASCII bar chart
|
|
274
|
+
* @param {Object} data - { label: value }
|
|
275
|
+
* @param {Object} [opts]
|
|
276
|
+
* @param {number} [opts.width=40] - Chart width in chars
|
|
277
|
+
* @param {string} [opts.title] - Chart title
|
|
278
|
+
* @returns {string} ASCII chart string
|
|
279
|
+
*/
|
|
280
|
+
export function generateAsciiChart(data, { width = 40, title = '' } = {}) {
|
|
281
|
+
const entries = Object.entries(data);
|
|
282
|
+
if (entries.length === 0) return ' (no data)';
|
|
283
|
+
|
|
284
|
+
const maxValue = Math.max(...entries.map(([, v]) => v), 1);
|
|
285
|
+
const maxLabelLen = Math.max(...entries.map(([k]) => k.length));
|
|
286
|
+
const lines = [];
|
|
287
|
+
|
|
288
|
+
if (title) {
|
|
289
|
+
lines.push(` ${title}`);
|
|
290
|
+
lines.push(' ' + '─'.repeat(width + maxLabelLen + 5));
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
for (const [label, value] of entries) {
|
|
294
|
+
const barLen = Math.round((value / maxValue) * width);
|
|
295
|
+
const bar = '█'.repeat(barLen);
|
|
296
|
+
const paddedLabel = label.padStart(maxLabelLen);
|
|
297
|
+
lines.push(` ${paddedLabel} │${bar} ${value}`);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
return lines.join('\n');
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Generate thread timeline ASCII chart
|
|
305
|
+
* @param {Array} events - Array of thread events with timestamps
|
|
306
|
+
* @param {string} label - Chart label
|
|
307
|
+
* @returns {string} ASCII timeline
|
|
308
|
+
*/
|
|
309
|
+
export function generateTimelineChart(events, label = 'Timeline') {
|
|
310
|
+
if (events.length === 0) return ' (no data)';
|
|
311
|
+
|
|
312
|
+
const now = new Date();
|
|
313
|
+
const days = Array.from({ length: 7 }, (_, i) => {
|
|
314
|
+
const d = new Date(now);
|
|
315
|
+
d.setDate(d.getDate() - (6 - i));
|
|
316
|
+
return d.toLocaleDateString('en-US', { weekday: 'short' });
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
const counts = new Array(7).fill(0);
|
|
320
|
+
for (const event of events) {
|
|
321
|
+
const ts = new Date(event.timestamp);
|
|
322
|
+
const daysAgo = Math.floor((now - ts) / (1000 * 60 * 60 * 24));
|
|
323
|
+
if (daysAgo < 7) {
|
|
324
|
+
counts[6 - daysAgo]++;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
const data = {};
|
|
329
|
+
days.forEach((d, i) => { data[d] = counts[i]; });
|
|
330
|
+
|
|
331
|
+
return generateAsciiChart(data, { title: label });
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// ============================================================================
|
|
335
|
+
// Maintenance
|
|
336
|
+
// ============================================================================
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Prune all analytics logs (remove entries older than 90 days)
|
|
340
|
+
*/
|
|
341
|
+
export function pruneAnalytics() {
|
|
342
|
+
pruneJSONL(THREADS_LOG);
|
|
343
|
+
pruneJSONL(CONTEXT_LOG);
|
|
344
|
+
pruneJSONL(TRUST_LOG);
|
|
345
|
+
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { readFileSync, existsSync } from 'fs';
|
|
2
2
|
import { join } from 'path';
|
|
3
3
|
import { execSync } from 'child_process';
|
|
4
|
+
import { executeStopHooks, isMaxRetriesExceeded, incrementRetryCount } from '../hooks/stop-hook-executor.js';
|
|
5
|
+
import { recordEvent } from '../analytics/analytics-engine.js';
|
|
4
6
|
|
|
5
7
|
/**
|
|
6
8
|
* Checkpoint Hooks - Automated validation orchestration
|
|
@@ -224,11 +226,44 @@ export async function runCheckpointHooks(featureName, checkpointNum) {
|
|
|
224
226
|
|
|
225
227
|
console.log('\n' + '━'.repeat(60) + '\n');
|
|
226
228
|
|
|
229
|
+
// Run stop hooks for L-Thread support
|
|
230
|
+
let stopHookResult = null;
|
|
231
|
+
if (featureName) {
|
|
232
|
+
try {
|
|
233
|
+
// Use feature name as pseudo thread ID for stop hook feedback
|
|
234
|
+
const pseudoThreadId = `checkpoint-${featureName}-${checkpointNum}`;
|
|
235
|
+
stopHookResult = await executeStopHooks(pseudoThreadId, 'on-stop', { feature: featureName });
|
|
236
|
+
|
|
237
|
+
if (!stopHookResult.passed) {
|
|
238
|
+
// Check retry count
|
|
239
|
+
if (isMaxRetriesExceeded(pseudoThreadId)) {
|
|
240
|
+
console.log(chalk.red('\n❌ Stop hooks failed 5+ times. Escalating to user.'));
|
|
241
|
+
} else {
|
|
242
|
+
const retryCount = incrementRetryCount(pseudoThreadId);
|
|
243
|
+
console.log(chalk.yellow(`\n⚠ Stop hooks failed (attempt ${retryCount}/5). Feedback saved.`));
|
|
244
|
+
if (stopHookResult.feedback?.suggestions?.length > 0) {
|
|
245
|
+
stopHookResult.feedback.suggestions.forEach(s => console.log(` → ${s}`));
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Record to analytics
|
|
250
|
+
recordEvent({
|
|
251
|
+
type: 'stop_hook_failed_at_checkpoint',
|
|
252
|
+
feature: featureName,
|
|
253
|
+
data: { checkpointNum, retryCount: getRetryCount ? undefined : undefined }
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
} catch {
|
|
257
|
+
// Stop hooks are non-critical — don't block checkpoint if they error
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
227
261
|
return {
|
|
228
262
|
passed,
|
|
229
263
|
checkpointNum,
|
|
230
264
|
timestamp: new Date().toISOString(),
|
|
231
265
|
results,
|
|
266
|
+
stopHookResult,
|
|
232
267
|
summary: {
|
|
233
268
|
errors: errorCount,
|
|
234
269
|
warnings: warningCount,
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context Bundler — Checkpoint compression (180K → 15K)
|
|
3
|
+
*
|
|
4
|
+
* Compresses checkpoint state into a compact bundle for session resumption.
|
|
5
|
+
* Saves bundles to .morph/bundles/{bundleId}.json
|
|
6
|
+
*
|
|
7
|
+
* A bundle contains:
|
|
8
|
+
* - decisions[]: key architectural decisions made
|
|
9
|
+
* - artifacts[]: paths to important generated files
|
|
10
|
+
* - nextSteps[]: what to implement next
|
|
11
|
+
* - metadata: original vs bundled token counts
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync, readdirSync } from 'fs';
|
|
15
|
+
import { join } from 'path';
|
|
16
|
+
import { randomUUID } from 'crypto';
|
|
17
|
+
|
|
18
|
+
const BUNDLES_DIR = join(process.cwd(), '.morph/bundles');
|
|
19
|
+
|
|
20
|
+
// ============================================================================
|
|
21
|
+
// Bundle Creation
|
|
22
|
+
// ============================================================================
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Create a context bundle from checkpoint data
|
|
26
|
+
* @param {Object} opts
|
|
27
|
+
* @param {string} opts.feature - Feature name
|
|
28
|
+
* @param {number} opts.checkpointNum - Checkpoint number this bundle captures
|
|
29
|
+
* @param {Array} opts.decisions - Key decisions made: [{ id, title, summary }]
|
|
30
|
+
* @param {Array} opts.artifacts - Important file paths: [{ path, description }]
|
|
31
|
+
* @param {Array} opts.nextSteps - Next tasks to implement: [string]
|
|
32
|
+
* @param {number} [opts.originalTokens=180000] - Estimated original context size
|
|
33
|
+
* @param {Object} [opts.meta] - Additional metadata
|
|
34
|
+
* @returns {Object} Bundle object with ID
|
|
35
|
+
*/
|
|
36
|
+
export function createBundle({
|
|
37
|
+
feature,
|
|
38
|
+
checkpointNum,
|
|
39
|
+
decisions = [],
|
|
40
|
+
artifacts = [],
|
|
41
|
+
nextSteps = [],
|
|
42
|
+
originalTokens = 180000,
|
|
43
|
+
meta = {}
|
|
44
|
+
}) {
|
|
45
|
+
const bundleId = `${feature}-cp${checkpointNum}-${randomUUID().substring(0, 8)}`;
|
|
46
|
+
const now = new Date().toISOString();
|
|
47
|
+
|
|
48
|
+
// Estimate bundle size (decisions + artifacts + nextSteps summary)
|
|
49
|
+
const bundledContent = JSON.stringify({ decisions, artifacts, nextSteps, meta });
|
|
50
|
+
const bundledTokens = Math.round(bundledContent.length / 4); // ~4 chars/token
|
|
51
|
+
|
|
52
|
+
const bundle = {
|
|
53
|
+
bundleId,
|
|
54
|
+
feature,
|
|
55
|
+
checkpointNum,
|
|
56
|
+
createdAt: now,
|
|
57
|
+
decisions,
|
|
58
|
+
artifacts,
|
|
59
|
+
nextSteps,
|
|
60
|
+
meta,
|
|
61
|
+
tokenMetrics: {
|
|
62
|
+
originalTokens,
|
|
63
|
+
bundledTokens,
|
|
64
|
+
compressionRatio: Math.round(originalTokens / Math.max(bundledTokens, 1)),
|
|
65
|
+
savingsTokens: originalTokens - bundledTokens
|
|
66
|
+
},
|
|
67
|
+
resumeInstructions: buildResumeInstructions(feature, checkpointNum, decisions, nextSteps)
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
return bundle;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Save a bundle to disk
|
|
75
|
+
* @param {Object} bundle - Bundle object (from createBundle)
|
|
76
|
+
* @returns {string} Path to saved bundle file
|
|
77
|
+
*/
|
|
78
|
+
export function saveBundle(bundle) {
|
|
79
|
+
if (!existsSync(BUNDLES_DIR)) {
|
|
80
|
+
mkdirSync(BUNDLES_DIR, { recursive: true });
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const filePath = join(BUNDLES_DIR, `${bundle.bundleId}.json`);
|
|
84
|
+
writeFileSync(filePath, JSON.stringify(bundle, null, 2), 'utf8');
|
|
85
|
+
|
|
86
|
+
// Also update feature state with bundle reference
|
|
87
|
+
try {
|
|
88
|
+
const statePath = join(process.cwd(), '.morph/state.json');
|
|
89
|
+
if (existsSync(statePath)) {
|
|
90
|
+
const state = JSON.parse(readFileSync(statePath, 'utf8'));
|
|
91
|
+
if (state.features?.[bundle.feature]) {
|
|
92
|
+
state.features[bundle.feature].contextBundles = state.features[bundle.feature].contextBundles || [];
|
|
93
|
+
state.features[bundle.feature].contextBundles.push({
|
|
94
|
+
bundleId: bundle.bundleId,
|
|
95
|
+
checkpointNum: bundle.checkpointNum,
|
|
96
|
+
createdAt: bundle.createdAt,
|
|
97
|
+
originalTokens: bundle.tokenMetrics.originalTokens,
|
|
98
|
+
bundledTokens: bundle.tokenMetrics.bundledTokens,
|
|
99
|
+
path: filePath
|
|
100
|
+
});
|
|
101
|
+
state.metadata = state.metadata || {};
|
|
102
|
+
state.metadata.lastUpdated = new Date().toISOString();
|
|
103
|
+
writeFileSync(statePath, JSON.stringify(state, null, 2), 'utf8');
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
} catch {
|
|
107
|
+
// Non-fatal if state update fails
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return filePath;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Load a bundle from disk
|
|
115
|
+
* @param {string} bundleId - Bundle ID
|
|
116
|
+
* @returns {Object|null} Bundle object or null if not found
|
|
117
|
+
*/
|
|
118
|
+
export function loadBundle(bundleId) {
|
|
119
|
+
const filePath = join(BUNDLES_DIR, `${bundleId}.json`);
|
|
120
|
+
|
|
121
|
+
if (!existsSync(filePath)) {
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
try {
|
|
126
|
+
return JSON.parse(readFileSync(filePath, 'utf8'));
|
|
127
|
+
} catch {
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* List all bundles, optionally filtered by feature
|
|
134
|
+
* @param {string} [feature] - Filter by feature name
|
|
135
|
+
* @returns {Array} Array of bundle summaries
|
|
136
|
+
*/
|
|
137
|
+
export function listBundles(feature = null) {
|
|
138
|
+
if (!existsSync(BUNDLES_DIR)) return [];
|
|
139
|
+
|
|
140
|
+
const files = readdirSync(BUNDLES_DIR).filter(f => f.endsWith('.json'));
|
|
141
|
+
const bundles = [];
|
|
142
|
+
|
|
143
|
+
for (const file of files) {
|
|
144
|
+
try {
|
|
145
|
+
const bundle = JSON.parse(readFileSync(join(BUNDLES_DIR, file), 'utf8'));
|
|
146
|
+
if (!feature || bundle.feature === feature) {
|
|
147
|
+
bundles.push({
|
|
148
|
+
bundleId: bundle.bundleId,
|
|
149
|
+
feature: bundle.feature,
|
|
150
|
+
checkpointNum: bundle.checkpointNum,
|
|
151
|
+
createdAt: bundle.createdAt,
|
|
152
|
+
originalTokens: bundle.tokenMetrics?.originalTokens,
|
|
153
|
+
bundledTokens: bundle.tokenMetrics?.bundledTokens,
|
|
154
|
+
compressionRatio: bundle.tokenMetrics?.compressionRatio,
|
|
155
|
+
nextStepsCount: bundle.nextSteps?.length || 0
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
} catch {
|
|
159
|
+
// Skip malformed files
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return bundles.sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt));
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Get the latest bundle for a feature at or before a checkpoint number
|
|
168
|
+
* @param {string} feature - Feature name
|
|
169
|
+
* @param {number} [checkpointNum] - Target checkpoint (latest if not specified)
|
|
170
|
+
* @returns {Object|null} Bundle or null
|
|
171
|
+
*/
|
|
172
|
+
export function getLatestBundle(feature, checkpointNum = Infinity) {
|
|
173
|
+
const bundles = listBundles(feature)
|
|
174
|
+
.filter(b => b.checkpointNum <= checkpointNum)
|
|
175
|
+
.sort((a, b) => b.checkpointNum - a.checkpointNum);
|
|
176
|
+
|
|
177
|
+
if (bundles.length === 0) return null;
|
|
178
|
+
return loadBundle(bundles[0].bundleId);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// ============================================================================
|
|
182
|
+
// Session Replay
|
|
183
|
+
// ============================================================================
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Generate minimal context for session replay from a bundle
|
|
187
|
+
* @param {Object} bundle - Bundle object
|
|
188
|
+
* @returns {string} Compact context string (~15K tokens) for session resumption
|
|
189
|
+
*/
|
|
190
|
+
export function generateResumeContext(bundle) {
|
|
191
|
+
const lines = [];
|
|
192
|
+
lines.push(`# Session Resume — ${bundle.feature} @ Checkpoint ${bundle.checkpointNum}`);
|
|
193
|
+
lines.push(`> Bundle ID: ${bundle.bundleId} | Created: ${bundle.createdAt}`);
|
|
194
|
+
lines.push(`> Context compression: ${bundle.tokenMetrics?.originalTokens?.toLocaleString()} → ${bundle.tokenMetrics?.bundledTokens?.toLocaleString()} tokens`);
|
|
195
|
+
lines.push('');
|
|
196
|
+
|
|
197
|
+
if (bundle.decisions?.length > 0) {
|
|
198
|
+
lines.push('## Key Decisions Made');
|
|
199
|
+
for (const d of bundle.decisions) {
|
|
200
|
+
lines.push(`- **${d.id || ''}${d.id ? ':' : ''} ${d.title}** — ${d.summary}`);
|
|
201
|
+
if (d.fullPath) lines.push(` *(Full: ${d.fullPath})*`);
|
|
202
|
+
}
|
|
203
|
+
lines.push('');
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (bundle.artifacts?.length > 0) {
|
|
207
|
+
lines.push('## Created Artifacts');
|
|
208
|
+
for (const a of bundle.artifacts) {
|
|
209
|
+
lines.push(`- \`${a.path}\` — ${a.description || ''}`);
|
|
210
|
+
}
|
|
211
|
+
lines.push('');
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (bundle.nextSteps?.length > 0) {
|
|
215
|
+
lines.push('## Next Steps (continue from here)');
|
|
216
|
+
bundle.nextSteps.forEach((step, i) => {
|
|
217
|
+
lines.push(`${i + 1}. ${step}`);
|
|
218
|
+
});
|
|
219
|
+
lines.push('');
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
lines.push(bundle.resumeInstructions || '');
|
|
223
|
+
|
|
224
|
+
return lines.join('\n');
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// ============================================================================
|
|
228
|
+
// Helpers
|
|
229
|
+
// ============================================================================
|
|
230
|
+
|
|
231
|
+
function buildResumeInstructions(feature, checkpointNum, decisions, nextSteps) {
|
|
232
|
+
return `## Resume Instructions
|
|
233
|
+
|
|
234
|
+
1. Load minimal standards: run \`morph-spec prime feature\`
|
|
235
|
+
2. Read spec: \`.morph/project/outputs/${feature}/spec.md\` (key sections only)
|
|
236
|
+
3. Check tasks: \`morph-spec state get ${feature}\`
|
|
237
|
+
4. Continue from: ${nextSteps[0] || `task after checkpoint ${checkpointNum}`}
|
|
238
|
+
|
|
239
|
+
> This bundle was created at checkpoint ${checkpointNum}. All decisions above are final.`;
|
|
240
|
+
}
|