@polymorphism-tech/morph-spec 8.34.2 → 8.35.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +22 -5
- package/README.md +2 -2
- package/bin/morph-spec.js +18 -4
- package/bin/validate.js +379 -379
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +5 -4
- package/docs/QUICKSTART.md +1 -1
- package/framework/MORPH.md +16 -17
- package/framework/agents.json +197 -44
- package/framework/ai-pin.json +22 -0
- package/framework/commands/morph-media.md +45 -0
- package/framework/docs/CLI.md +236 -11
- package/framework/evals/proof-rubrics/README.md +76 -0
- package/framework/evals/proof-rubrics/eval-de-modelo.md +64 -0
- package/framework/evals/proof-rubrics/fiacao.md +54 -0
- package/framework/evals/proof-rubrics/guard-cego.md +52 -0
- package/framework/evals/proof-rubrics/media-claims.md +109 -0
- package/framework/evals/proof-rubrics/mutation-proof.md +77 -0
- package/framework/evals/proof-rubrics/observabilidade-genai.md +119 -0
- package/framework/evals/proof-rubrics/verde-nao-e-coberto.md +48 -0
- package/framework/evals/rubrics/architecture.md +30 -0
- package/framework/evals/rubrics/code-quality.md +26 -0
- package/framework/evals/rubrics/test-coverage.md +20 -0
- package/framework/hooks/claude-code/core/protect-spec-files.js +41 -4
- package/framework/hooks/claude-code/core/worktree-env-inject.js +43 -0
- package/framework/hooks/claude-code/statusline.sh +11 -11
- package/framework/hooks/git/commit-msg/conventional-commits.sh +33 -33
- package/framework/hooks/git/pre-commit/agents.sh +25 -25
- package/framework/hooks/git/pre-commit/orchestrator.sh +64 -64
- package/framework/hooks/git/pre-commit/specs.sh +50 -50
- package/framework/hooks/git/pre-push/run-tests.sh +44 -44
- package/framework/hooks/shared/e2e-check.js +135 -22
- package/framework/hooks/shared/payload-utils.js +15 -2
- package/framework/hooks/shared/stdin-reader.js +26 -26
- package/framework/rules/ai-agents-standards.md +14 -2
- package/framework/schemas/ai-pin.schema.json +97 -0
- package/framework/schemas/config.schema.json +107 -0
- package/framework/schemas/feature.schema.json +23 -4
- package/framework/skills/core/create-umbraco-lp/SKILL.md +1 -1
- package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +1 -1
- package/framework/skills/core/morph-apply/SKILL.md +26 -5
- package/framework/skills/core/morph-brainstorming/SKILL.md +1 -1
- package/framework/skills/core/morph-eval/SKILL.md +1 -1
- package/framework/skills/core/morph-hotfix/SKILL.md +2 -2
- package/framework/skills/core/morph-implement/SKILL.md +18 -2
- package/framework/skills/core/morph-media/SKILL.md +215 -0
- package/framework/skills/core/morph-plan/SKILL.md +33 -2
- package/framework/skills/core/morph-proposal/SKILL.md +1 -1
- package/framework/skills/core/morph-review/SKILL.md +166 -9
- package/framework/skills/core/morph-stack-scan/SKILL.md +1 -1
- package/framework/skills/core/morph-standards/SKILL.md +1 -1
- package/framework/skills/core/morph-uiux/SKILL.md +1 -1
- package/framework/standards/PROVENANCE.json +81 -3
- package/framework/standards/STANDARDS.json +1483 -839
- package/framework/standards/ai-agents/agent-archetypes.md +160 -0
- package/framework/standards/ai-agents/agent-session.md +164 -68
- package/framework/standards/ai-agents/agent-spec.md +196 -0
- package/framework/standards/ai-agents/batching.md +61 -37
- package/framework/standards/ai-agents/context-providers.md +97 -24
- package/framework/standards/ai-agents/conversational-agent-with-phases.md +239 -0
- package/framework/standards/ai-agents/cost-and-budget.md +256 -0
- package/framework/standards/ai-agents/durable-workflows-hitl.md +98 -5
- package/framework/standards/ai-agents/evals-with-cache.md +289 -0
- package/framework/standards/ai-agents/guardrails.md +222 -0
- package/framework/standards/ai-agents/llm-runtime-defaults.md +226 -0
- package/framework/standards/ai-agents/mcp-server.md +119 -18
- package/framework/standards/ai-agents/mcp-tools.md +85 -32
- package/framework/standards/ai-agents/media-pipeline.md +352 -0
- package/framework/standards/ai-agents/media-video.md +267 -0
- package/framework/standards/ai-agents/middleware-patterns.md +39 -5
- package/framework/standards/ai-agents/modalities-image-gen.md +354 -105
- package/framework/standards/ai-agents/modalities-speech-to-text.md +8 -2
- package/framework/standards/ai-agents/modalities-text-to-speech.md +10 -2
- package/framework/standards/ai-agents/modalities-vision.md +6 -2
- package/framework/standards/ai-agents/multi-agent-patterns.md +61 -5
- package/framework/standards/ai-agents/observability-patterns.md +227 -17
- package/framework/standards/ai-agents/production.md +38 -9
- package/framework/standards/ai-agents/prompt-sources.md +389 -0
- package/framework/standards/ai-agents/providers/model-registry.md +174 -39
- package/framework/standards/ai-agents/rag-custom-pgvector.md +69 -4
- package/framework/standards/ai-agents/service-tier-flex.md +27 -19
- package/framework/standards/ai-agents/setup.md +166 -50
- package/framework/standards/ai-agents/structured-output.md +34 -3
- package/framework/standards/ai-agents/sweet-spot.md +23 -3
- package/framework/standards/ai-agents/testing-ai.md +236 -0
- package/framework/standards/ai-agents/vector-data-extensions.md +50 -20
- package/framework/standards/ai-agents/workflows.md +27 -7
- package/framework/standards/frontend/design-system/ai-image-generation.md +12 -0
- package/framework/standards/frontend/design-system/ai-video-generation.md +13 -0
- package/framework/standards/infrastructure/docker/local-compose-isolation.md +3 -1
- package/framework/templates/.idea/morph-templates.xml +92 -0
- package/framework/templates/.vscode/morph-templates.code-snippets +186 -0
- package/framework/templates/IDE-SNIPPETS.md +262 -262
- package/framework/templates/REGISTRY.json +13 -0
- package/framework/templates/code/dotnet/abstractions/api-endpoint.cs +7 -7
- package/framework/templates/code/dotnet/ai-agents/AgentBootstrap.cs.template +39 -39
- package/framework/templates/code/dotnet/ai-agents/EmbeddingService.cs.template +68 -68
- package/framework/templates/code/dotnet/ai-agents/ModelRegistry.cs.template +117 -117
- package/framework/templates/code/dotnet/ai-agents/PromptRepository.cs.template +1 -1
- package/framework/templates/code/dotnet/ai-agents/ResilientToolMiddleware.cs.template +61 -61
- package/framework/templates/code/dotnet/ai-agents/VectorSearchTool.cs.template +107 -107
- package/framework/templates/code/dotnet/ai-agents/model-registry.json.template +58 -58
- package/framework/templates/code/dotnet/contracts/contracts-vsa.cs +281 -281
- package/framework/templates/code/dotnet/database/migration.cs +83 -83
- package/framework/templates/code/dotnet/feature/endpoint.cs.hbs +21 -21
- package/framework/templates/code/dotnet/feature/request.cs.hbs +4 -4
- package/framework/templates/code/dotnet/feature/response.cs.hbs +4 -4
- package/framework/templates/code/dotnet/feature/validator.cs.hbs +15 -15
- package/framework/templates/code/sql/ai-agents/agent-prompts-migration.sql.template +1 -1
- package/framework/templates/code/sql/ai-agents/pgvector-embeddings-migration.sql.template +91 -91
- package/framework/templates/code/typescript/contracts.ts +167 -167
- package/framework/templates/docs/clarifications.md +253 -253
- package/framework/templates/docs/schema-analysis.md +119 -119
- package/framework/templates/docs/ui-components.md +52 -52
- package/framework/templates/docs/ui-design-system.md +76 -76
- package/framework/templates/docs/ui-flows.md +167 -167
- package/framework/templates/docs/ui-mockups.md +98 -98
- package/framework/templates/dotnet/ai-kit/Directory.Build.props +25 -0
- package/framework/templates/dotnet/ai-kit/Directory.Packages.props +26 -0
- package/framework/templates/dotnet/ai-kit/Morph.AiKit.sln +56 -0
- package/framework/templates/dotnet/ai-kit/NuGet.config +12 -0
- package/framework/templates/dotnet/ai-kit/README.md +128 -0
- package/framework/templates/dotnet/ai-kit/global.json +9 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentApi.cs +65 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentFactory.cs +289 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentFactoryOptions.cs +91 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentSpec.cs +91 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentSpecOptions.cs +181 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/AiKitAssemblyMarker.cs +14 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Conversation/ConversationStore.cs +174 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Conversation/IConversationStore.cs +54 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Conversation/InMemoryConversationStore.cs +75 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/CountingChatClient.cs +71 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalAssertion.cs +246 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalCacheMissException.cs +57 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalCacheOptions.cs +78 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalCacheStamp.cs +225 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalFixture.cs +463 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalMode.cs +32 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalQualityEvaluators.cs +49 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalScenario.cs +214 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalSummary.cs +168 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/OfflineChatClient.cs +142 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/ShortPathResponseCache.cs +349 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/IMediaGenerator.cs +42 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/IMediaProvider.cs +77 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaArtifact.cs +28 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaDisclosure.cs +107 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaGenerator.cs +253 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaIdempotencyKey.cs +103 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaJob.cs +259 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaJobId.cs +21 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaJobState.cs +47 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaPricing.cs +102 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaProvenance.cs +44 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaRequest.cs +192 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaResult.cs +94 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaReview.cs +99 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaSeams.cs +97 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaStore.cs +133 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MorphMedia.cs +93 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/GoogleImageProvider.cs +112 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/OpenAIImageProvider.cs +288 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/VeoVideoProvider.cs +158 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Middleware/IUsageCostSink.cs +29 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Middleware/UsageCostMiddleware.cs +208 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Middleware/UsageCostRecord.cs +95 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Morph.AiKit.csproj +38 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Observability/MorphOpenTelemetryExtensions.cs +119 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Observability/MorphTelemetryOptions.cs +82 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/IProviderClientFactory.cs +33 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/ModelAlias.cs +267 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/ModelRegistry.cs +474 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/model-registry.v2.json +87 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Rag/DeltaIngest.cs +169 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Rag/DeltaPlan.cs +49 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/AgentFactoryTests.cs +495 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/AgentSpecTests.cs +260 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/ConversationStoreTests.cs +405 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/DeltaIngestTests.cs +379 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalAssertionTests.cs +197 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalCacheMissTests.cs +139 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalCacheStampTests.cs +157 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalFailuresTests.cs +178 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalFixtureTests.cs +153 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalRefreshTests.cs +194 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalWorkspace.cs +86 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/KitEvals.cs +213 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/OfflineChatClientTests.cs +95 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeChatClient.cs +225 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeChatClientTests.cs +215 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeClock.cs +33 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeMediaProvider.cs +89 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeProviderClientFactory.cs +55 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeUsageCostSink.cs +60 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/InMemoryMediaStore.cs +131 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaAccessibilityTests.cs +133 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaAliasIsNotAnAgentTests.cs +127 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaDisclosureTests.cs +216 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaGeneratorTests.cs +264 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaIdempotencyKeyTests.cs +148 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaJobStateTests.cs +184 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaReviewTests.cs +89 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaStoreTests.cs +98 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/ModelRegistryTests.cs +451 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Morph.AiKit.Tests.csproj +76 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/SkeletonSmokeTests.cs +33 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/TelemetryTests.cs +261 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/UsageCostMiddlewareTests.cs +374 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/.morph-eval-cache.json +12 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/orcamento.acme.a-resposta-estruturada/1/CF70A7CE27BBD66A/contents.data +13 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/orcamento.acme.a-resposta-estruturada/1/CF70A7CE27BBD66A/entry.json +7 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/triagem.acme.a-saudacao/1/19F631228C6E00BA/contents.data +13 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/triagem.acme.a-saudacao/1/19F631228C6E00BA/entry.json +7 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/triagem.acme.b-escalonamento/1/A24A7C9BDD8239D8/contents.data +13 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/triagem.acme.b-escalonamento/1/A24A7C9BDD8239D8/entry.json +7 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/triagem.globex.a-saudacao/1/E38CD37DF003EDB7/contents.data +13 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/triagem.globex.a-saudacao/1/E38CD37DF003EDB7/entry.json +7 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-datasets/orcamento/acme/a-resposta-estruturada.json +24 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-datasets/triagem/acme/a-saudacao.json +23 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-datasets/triagem/acme/b-escalonamento.json +24 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-datasets/triagem/globex/a-saudacao.json +23 -0
- package/framework/templates/feature/decisions.md +122 -122
- package/framework/templates/frontend/nextjs/Dockerfile.nextjs.hbs +43 -43
- package/framework/templates/frontend/nextjs/client-component.tsx.hbs +26 -26
- package/framework/templates/frontend/nextjs/env.mjs.hbs +32 -32
- package/framework/templates/frontend/nextjs/feature-form.tsx.hbs +56 -56
- package/framework/templates/frontend/nextjs/page.tsx.hbs +22 -22
- package/framework/templates/frontend/nextjs/tsconfig.json.hbs +26 -26
- package/framework/templates/frontend/nextjs/use-feature.ts.hbs +54 -54
- package/framework/templates/infrastructure/docker/Dockerfile.template +58 -58
- package/framework/templates/infrastructure/docker/dockerfile-api.dockerfile +38 -38
- package/framework/templates/infrastructure/docker/dockerfile-web.dockerfile +48 -48
- package/framework/templates/infrastructure/github/README.md +56 -56
- package/framework/templates/infrastructure/github/actions/docker-build-push/action.yml.hbs +45 -45
- package/framework/templates/infrastructure/github/actions/health-check/action.yml.hbs +27 -27
- package/framework/templates/infrastructure/github/workflows/docker-build-push.yml.hbs +59 -59
- package/framework/templates/infrastructure/github/workflows/dotnet-build.yml.hbs +39 -39
- package/framework/templates/integrations/asaas-client.cs +387 -387
- package/framework/templates/integrations/asaas-webhook.cs +351 -351
- package/framework/templates/meta-prompts/fusion/fusion-agent.md +76 -76
- package/framework/templates/meta-prompts/fusion/fusion-aggregator.md +100 -100
- package/framework/templates/meta-prompts/validators/pre-commit-validator.md +95 -95
- package/framework/templates/project/validate.js +120 -120
- package/framework/templates/state.template.json +17 -17
- package/framework/templates/ui/design-system.css +226 -226
- package/package.json +3 -3
- package/src/commands/dashboard/dashboard.js +53 -53
- package/src/commands/e2e/e2e.js +15 -2
- package/src/commands/index.js +13 -13
- package/src/commands/project/checkpoint.js +209 -209
- package/src/commands/project/doctor.js +148 -6
- package/src/commands/project/update.js +469 -153
- package/src/commands/project/worktree.js +76 -4
- package/src/commands/state/advance.js +24 -2
- package/src/commands/state/approve.js +33 -1
- package/src/commands/state/create.js +72 -11
- package/src/commands/state/finish.js +379 -24
- package/src/commands/state/gate-check.js +190 -6
- package/src/commands/state/score.js +36 -1
- package/src/commands/validation/verify.js +134 -25
- package/src/core/state/phase-state-machine.js +197 -197
- package/src/core/state/state-manager.js +9 -1
- package/src/core/templates/template-renderer.js +477 -477
- package/src/dashboard/app/components/DetailPanel.jsx +11 -11
- package/src/dashboard/app/components/FlowGraph.jsx +17 -17
- package/src/dashboard/app/components/SearchFilter.jsx +22 -22
- package/src/dashboard/app/hooks/useDetailPanel.js +15 -15
- package/src/dashboard/app/main.jsx +13 -13
- package/src/dashboard/app/pages/AgentMap.jsx +370 -370
- package/src/dashboard/app/pages/PhasePipeline.jsx +408 -408
- package/src/dashboard/app/pages/RulesMap.jsx +273 -273
- package/src/dashboard/app/pages/SkillsLibrary.jsx +366 -366
- package/src/dashboard/app/pages/Standards.jsx +332 -332
- package/src/dashboard/app/styles/index.css +47 -47
- package/src/dashboard/index.html +12 -12
- package/src/dashboard/package.json +24 -24
- package/src/dashboard/scanner/phases-scanner.js +15 -15
- package/src/dashboard/scanner/rubrics-scanner.js +12 -12
- package/src/dashboard/scanner/rules-scanner.js +37 -37
- package/src/dashboard/scanner/skills-scanner.js +43 -43
- package/src/dashboard/scanner/squads-scanner.js +13 -13
- package/src/dashboard/vite-plugin-morph-data.js +17 -17
- package/src/dashboard/vite.config.js +24 -24
- package/src/lib/ai/ai-checks.js +182 -0
- package/src/lib/ai/ai-project-scan.js +347 -0
- package/src/lib/ai/code-audit.js +181 -0
- package/src/lib/ai/comment-stripper.js +184 -0
- package/src/lib/ai/pin-drift.js +138 -0
- package/src/lib/ai/registry-audit.js +285 -0
- package/src/lib/detectors/design-system-detector.js +188 -188
- package/src/lib/detectors/index.js +217 -217
- package/src/lib/e2e/compose-env.js +50 -4
- package/src/lib/evaluator-report.js +269 -15
- package/src/lib/generators/design-system-generator.js +508 -508
- package/src/lib/generators/settings-generator.js +209 -209
- package/src/lib/git/default-branch.js +92 -4
- package/src/lib/git/pr-files.js +56 -0
- package/src/lib/hooks/hook-executor.js +167 -167
- package/src/lib/installers/effects.js +193 -0
- package/src/lib/installers/mcp-installer.js +719 -719
- package/src/lib/installers/templates-reconcile.js +3 -3
- package/src/lib/risk-detector.js +284 -0
- package/src/lib/standards/verified-against.js +276 -0
- package/src/lib/tasks/eval-node.js +485 -0
- package/src/lib/tasks/linux-runner.js +297 -0
- package/src/lib/tasks/task-classifier.js +30 -30
- package/src/lib/tasks/task-parser.js +209 -209
- package/src/lib/tasks/test-nodes.js +486 -0
- package/src/lib/tasks/test-runner.js +410 -33
- package/src/lib/validators/checkpoint-persistence.js +39 -39
- package/src/lib/validators/css/css-validator.js +340 -340
- package/src/lib/validators/shared/emit-validator-dispatch.js +14 -14
- package/src/lib/validators/shared/issue-counter.js +14 -14
- package/src/lib/validators/verify-runner.js +583 -28
- package/src/lib/worktree/docker-audit.js +287 -0
- package/src/lib/worktree/env-allocator.js +24 -0
- package/src/lib/worktree/worktree-audit.js +30 -1
- package/src/scripts/setup-infra.js +24 -11
- package/src/utils/agent-id-mapper.js +11 -11
- package/src/utils/agents-installer.js +18 -8
- package/src/utils/claude-md-injector.js +10 -5
- package/src/utils/claude-settings-manager.js +300 -300
- package/src/utils/file-copier.js +11 -4
- package/src/utils/hooks-installer.js +29 -26
- package/src/utils/skills-installer.js +36 -29
- package/src/utils/version-checker.js +68 -3
- package/framework/standards/ai-agents/prompts-in-db.md +0 -214
|
@@ -1,167 +1,167 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Hook Executor — Agent Teams hook validation
|
|
3
|
-
*
|
|
4
|
-
* Executes validation hooks triggered by Claude Code agent-teams events:
|
|
5
|
-
* TeammateIdle, TaskCompleted, PhaseAdvanced
|
|
6
|
-
*
|
|
7
|
-
* Reads agents.json to find validators with matching hook_triggers,
|
|
8
|
-
* then runs each validator's checks against the current project state.
|
|
9
|
-
*
|
|
10
|
-
* @module hook-executor
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
import { readFileSync, existsSync } from 'fs';
|
|
14
|
-
import { join } from 'path';
|
|
15
|
-
|
|
16
|
-
// ============================================================================
|
|
17
|
-
// Loaders
|
|
18
|
-
// ============================================================================
|
|
19
|
-
|
|
20
|
-
function loadAgents(projectPath) {
|
|
21
|
-
const agentPaths = [
|
|
22
|
-
join(projectPath, '.morph/framework/agents.json'),
|
|
23
|
-
];
|
|
24
|
-
|
|
25
|
-
const allAgents = {};
|
|
26
|
-
for (const agentPath of agentPaths) {
|
|
27
|
-
if (existsSync(agentPath)) {
|
|
28
|
-
try {
|
|
29
|
-
const data = JSON.parse(readFileSync(agentPath, 'utf8'));
|
|
30
|
-
Object.assign(allAgents, data.agents || {});
|
|
31
|
-
} catch (e) { /* skip malformed files */ }
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
return allAgents;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function loadStateFeature(projectPath, featureName) {
|
|
38
|
-
const statePath = join(projectPath, '.morph/state.json');
|
|
39
|
-
if (!existsSync(statePath)) return null;
|
|
40
|
-
|
|
41
|
-
const state = JSON.parse(readFileSync(statePath, 'utf8'));
|
|
42
|
-
const features = state.features;
|
|
43
|
-
|
|
44
|
-
// Support both object format { featureName: {...} } and array format [{name, ...}]
|
|
45
|
-
if (Array.isArray(features)) {
|
|
46
|
-
return features.find(f => f.name === featureName) || null;
|
|
47
|
-
}
|
|
48
|
-
return features[featureName] || null;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// ============================================================================
|
|
52
|
-
// Main Executor
|
|
53
|
-
// ============================================================================
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Execute agent-teams hook validation
|
|
57
|
-
* @param {string} projectPath - Absolute path to project root
|
|
58
|
-
* @param {string} featureName - Feature to validate
|
|
59
|
-
* @param {string} hookEvent - Event name (TeammateIdle, TaskCompleted, PhaseAdvanced)
|
|
60
|
-
* @param {Object} [opts]
|
|
61
|
-
* @param {boolean} [opts.dryRun=false] - Identify validators without running them
|
|
62
|
-
* @param {boolean} [opts.verbose=true] - Print results to console
|
|
63
|
-
* @returns {Promise<Object>} { passed, blocked, errors, warnings }
|
|
64
|
-
*/
|
|
65
|
-
export async function executeHook(projectPath, featureName, hookEvent, opts = {}) {
|
|
66
|
-
const { dryRun = false } = opts;
|
|
67
|
-
|
|
68
|
-
// Load feature state
|
|
69
|
-
const feature = loadStateFeature(projectPath, featureName);
|
|
70
|
-
if (!feature) {
|
|
71
|
-
return {
|
|
72
|
-
passed: false,
|
|
73
|
-
blocked: true,
|
|
74
|
-
errors: [`Feature not found: ${featureName}`],
|
|
75
|
-
warnings: []
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// Load all available agents
|
|
80
|
-
const agents = loadAgents(projectPath);
|
|
81
|
-
|
|
82
|
-
// Filter: agents that run in hooks AND trigger on this event
|
|
83
|
-
const hookValidators = Object.entries(agents)
|
|
84
|
-
.filter(([, agent]) =>
|
|
85
|
-
agent.relationships?.runs_in === 'hooks' &&
|
|
86
|
-
Array.isArray(agent.relationships?.hook_triggers) &&
|
|
87
|
-
agent.relationships.hook_triggers.includes(hookEvent)
|
|
88
|
-
)
|
|
89
|
-
.map(([agentId, agent]) => ({ agentId, ...agent }));
|
|
90
|
-
|
|
91
|
-
// No validators configured for this event, or dry run — pass immediately
|
|
92
|
-
if (hookValidators.length === 0 || dryRun) {
|
|
93
|
-
return { passed: true, blocked: false, errors: [], warnings: [] };
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// Run validators
|
|
97
|
-
// In production, each validator would execute its actual checks.
|
|
98
|
-
// This implementation returns pass since real validation requires external tooling.
|
|
99
|
-
const errors = [];
|
|
100
|
-
const warnings = [];
|
|
101
|
-
|
|
102
|
-
const passed = errors.length === 0;
|
|
103
|
-
const blocked = errors.some(e => typeof e === 'object' && e.blocksOnFail !== false);
|
|
104
|
-
|
|
105
|
-
return { passed, blocked, errors, warnings };
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// ============================================================================
|
|
109
|
-
// Formatter
|
|
110
|
-
// ============================================================================
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* Format hook execution results as a human-readable string
|
|
114
|
-
* @param {Object} results - Result from executeHook
|
|
115
|
-
* @param {string} hookEvent - Event name
|
|
116
|
-
* @returns {string} Formatted output
|
|
117
|
-
*/
|
|
118
|
-
export function formatHookResults(results, hookEvent) {
|
|
119
|
-
const { passed, blocked, errors = [], warnings = [] } = results;
|
|
120
|
-
const lines = [];
|
|
121
|
-
|
|
122
|
-
if (!passed && errors.length > 0) {
|
|
123
|
-
lines.push(`❌ Hook: ${hookEvent}`);
|
|
124
|
-
lines.push('FAILED');
|
|
125
|
-
lines.push('ERRORS (blocking):');
|
|
126
|
-
|
|
127
|
-
for (const err of errors) {
|
|
128
|
-
if (err.issues && Array.isArray(err.issues)) {
|
|
129
|
-
for (const issue of err.issues) {
|
|
130
|
-
lines.push(` ${issue.message}`);
|
|
131
|
-
if (issue.file && issue.line !== undefined) {
|
|
132
|
-
lines.push(` ${issue.file}:${issue.line}`);
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
} else if (err.error) {
|
|
136
|
-
lines.push(` Error: ${err.error}`);
|
|
137
|
-
} else if (typeof err === 'string') {
|
|
138
|
-
lines.push(` ${err}`);
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
if (blocked) {
|
|
143
|
-
lines.push('BLOCKED');
|
|
144
|
-
}
|
|
145
|
-
} else if (warnings.length === 0) {
|
|
146
|
-
lines.push(`✅ Hook: ${hookEvent}`);
|
|
147
|
-
lines.push('All validators passed');
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
if (warnings.length > 0) {
|
|
151
|
-
lines.push(`⚠️ Hook: ${hookEvent}`);
|
|
152
|
-
lines.push('WARNINGS (non-blocking):');
|
|
153
|
-
|
|
154
|
-
for (const warn of warnings) {
|
|
155
|
-
if (warn.issues && Array.isArray(warn.issues)) {
|
|
156
|
-
for (const issue of warn.issues) {
|
|
157
|
-
lines.push(` ${issue.message}`);
|
|
158
|
-
if (issue.file && issue.line !== undefined) {
|
|
159
|
-
lines.push(` ${issue.file}:${issue.line}`);
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
return lines.join('\n');
|
|
167
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Hook Executor — Agent Teams hook validation
|
|
3
|
+
*
|
|
4
|
+
* Executes validation hooks triggered by Claude Code agent-teams events:
|
|
5
|
+
* TeammateIdle, TaskCompleted, PhaseAdvanced
|
|
6
|
+
*
|
|
7
|
+
* Reads agents.json to find validators with matching hook_triggers,
|
|
8
|
+
* then runs each validator's checks against the current project state.
|
|
9
|
+
*
|
|
10
|
+
* @module hook-executor
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { readFileSync, existsSync } from 'fs';
|
|
14
|
+
import { join } from 'path';
|
|
15
|
+
|
|
16
|
+
// ============================================================================
|
|
17
|
+
// Loaders
|
|
18
|
+
// ============================================================================
|
|
19
|
+
|
|
20
|
+
function loadAgents(projectPath) {
|
|
21
|
+
const agentPaths = [
|
|
22
|
+
join(projectPath, '.morph/framework/agents.json'),
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
const allAgents = {};
|
|
26
|
+
for (const agentPath of agentPaths) {
|
|
27
|
+
if (existsSync(agentPath)) {
|
|
28
|
+
try {
|
|
29
|
+
const data = JSON.parse(readFileSync(agentPath, 'utf8'));
|
|
30
|
+
Object.assign(allAgents, data.agents || {});
|
|
31
|
+
} catch (e) { /* skip malformed files */ }
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return allAgents;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function loadStateFeature(projectPath, featureName) {
|
|
38
|
+
const statePath = join(projectPath, '.morph/state.json');
|
|
39
|
+
if (!existsSync(statePath)) return null;
|
|
40
|
+
|
|
41
|
+
const state = JSON.parse(readFileSync(statePath, 'utf8'));
|
|
42
|
+
const features = state.features;
|
|
43
|
+
|
|
44
|
+
// Support both object format { featureName: {...} } and array format [{name, ...}]
|
|
45
|
+
if (Array.isArray(features)) {
|
|
46
|
+
return features.find(f => f.name === featureName) || null;
|
|
47
|
+
}
|
|
48
|
+
return features[featureName] || null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// ============================================================================
|
|
52
|
+
// Main Executor
|
|
53
|
+
// ============================================================================
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Execute agent-teams hook validation
|
|
57
|
+
* @param {string} projectPath - Absolute path to project root
|
|
58
|
+
* @param {string} featureName - Feature to validate
|
|
59
|
+
* @param {string} hookEvent - Event name (TeammateIdle, TaskCompleted, PhaseAdvanced)
|
|
60
|
+
* @param {Object} [opts]
|
|
61
|
+
* @param {boolean} [opts.dryRun=false] - Identify validators without running them
|
|
62
|
+
* @param {boolean} [opts.verbose=true] - Print results to console
|
|
63
|
+
* @returns {Promise<Object>} { passed, blocked, errors, warnings }
|
|
64
|
+
*/
|
|
65
|
+
export async function executeHook(projectPath, featureName, hookEvent, opts = {}) {
|
|
66
|
+
const { dryRun = false } = opts;
|
|
67
|
+
|
|
68
|
+
// Load feature state
|
|
69
|
+
const feature = loadStateFeature(projectPath, featureName);
|
|
70
|
+
if (!feature) {
|
|
71
|
+
return {
|
|
72
|
+
passed: false,
|
|
73
|
+
blocked: true,
|
|
74
|
+
errors: [`Feature not found: ${featureName}`],
|
|
75
|
+
warnings: []
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Load all available agents
|
|
80
|
+
const agents = loadAgents(projectPath);
|
|
81
|
+
|
|
82
|
+
// Filter: agents that run in hooks AND trigger on this event
|
|
83
|
+
const hookValidators = Object.entries(agents)
|
|
84
|
+
.filter(([, agent]) =>
|
|
85
|
+
agent.relationships?.runs_in === 'hooks' &&
|
|
86
|
+
Array.isArray(agent.relationships?.hook_triggers) &&
|
|
87
|
+
agent.relationships.hook_triggers.includes(hookEvent)
|
|
88
|
+
)
|
|
89
|
+
.map(([agentId, agent]) => ({ agentId, ...agent }));
|
|
90
|
+
|
|
91
|
+
// No validators configured for this event, or dry run — pass immediately
|
|
92
|
+
if (hookValidators.length === 0 || dryRun) {
|
|
93
|
+
return { passed: true, blocked: false, errors: [], warnings: [] };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Run validators
|
|
97
|
+
// In production, each validator would execute its actual checks.
|
|
98
|
+
// This implementation returns pass since real validation requires external tooling.
|
|
99
|
+
const errors = [];
|
|
100
|
+
const warnings = [];
|
|
101
|
+
|
|
102
|
+
const passed = errors.length === 0;
|
|
103
|
+
const blocked = errors.some(e => typeof e === 'object' && e.blocksOnFail !== false);
|
|
104
|
+
|
|
105
|
+
return { passed, blocked, errors, warnings };
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// ============================================================================
|
|
109
|
+
// Formatter
|
|
110
|
+
// ============================================================================
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Format hook execution results as a human-readable string
|
|
114
|
+
* @param {Object} results - Result from executeHook
|
|
115
|
+
* @param {string} hookEvent - Event name
|
|
116
|
+
* @returns {string} Formatted output
|
|
117
|
+
*/
|
|
118
|
+
export function formatHookResults(results, hookEvent) {
|
|
119
|
+
const { passed, blocked, errors = [], warnings = [] } = results;
|
|
120
|
+
const lines = [];
|
|
121
|
+
|
|
122
|
+
if (!passed && errors.length > 0) {
|
|
123
|
+
lines.push(`❌ Hook: ${hookEvent}`);
|
|
124
|
+
lines.push('FAILED');
|
|
125
|
+
lines.push('ERRORS (blocking):');
|
|
126
|
+
|
|
127
|
+
for (const err of errors) {
|
|
128
|
+
if (err.issues && Array.isArray(err.issues)) {
|
|
129
|
+
for (const issue of err.issues) {
|
|
130
|
+
lines.push(` ${issue.message}`);
|
|
131
|
+
if (issue.file && issue.line !== undefined) {
|
|
132
|
+
lines.push(` ${issue.file}:${issue.line}`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
} else if (err.error) {
|
|
136
|
+
lines.push(` Error: ${err.error}`);
|
|
137
|
+
} else if (typeof err === 'string') {
|
|
138
|
+
lines.push(` ${err}`);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (blocked) {
|
|
143
|
+
lines.push('BLOCKED');
|
|
144
|
+
}
|
|
145
|
+
} else if (warnings.length === 0) {
|
|
146
|
+
lines.push(`✅ Hook: ${hookEvent}`);
|
|
147
|
+
lines.push('All validators passed');
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (warnings.length > 0) {
|
|
151
|
+
lines.push(`⚠️ Hook: ${hookEvent}`);
|
|
152
|
+
lines.push('WARNINGS (non-blocking):');
|
|
153
|
+
|
|
154
|
+
for (const warn of warnings) {
|
|
155
|
+
if (warn.issues && Array.isArray(warn.issues)) {
|
|
156
|
+
for (const issue of warn.issues) {
|
|
157
|
+
lines.push(` ${issue.message}`);
|
|
158
|
+
if (issue.file && issue.line !== undefined) {
|
|
159
|
+
lines.push(` ${issue.file}:${issue.line}`);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return lines.join('\n');
|
|
167
|
+
}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* effects.js — o coletor de efeitos ÚNICO do caminho de instalação/atualização.
|
|
3
|
+
*
|
|
4
|
+
* A propriedade que faz o `--dry-run` ser honesto está numa frase:
|
|
5
|
+
*
|
|
6
|
+
* > **o caminho real também emite.**
|
|
7
|
+
*
|
|
8
|
+
* Em modo normal cada verbo EXECUTA e registra; em `dryRun`, só registra. O
|
|
9
|
+
* resumo impresso ao fim de uma execução real sai de `list()`, não de
|
|
10
|
+
* contadores paralelos. A consequência é o ponto: um ponto de emissão
|
|
11
|
+
* esquecido some das DUAS saídas ao mesmo tempo, e o resumo de uma execução
|
|
12
|
+
* real fica visivelmente incompleto — o defeito aparece sem sequer usar o
|
|
13
|
+
* `--dry-run`.
|
|
14
|
+
*
|
|
15
|
+
* **Não há segunda travessia.** Nenhum filtro, walker ou lista de payload é
|
|
16
|
+
* reimplementado aqui. Os filtros que decidem o que instalar
|
|
17
|
+
* (`shouldInstallSkill`, `agentEligible`, `shouldInstall`) já são puros e
|
|
18
|
+
* continuam sendo os únicos. O anti-exemplar tem nome e mora neste
|
|
19
|
+
* repositório: `checkAgentsDrift` funciona como preditor porque re-deriva o
|
|
20
|
+
* conjunto elegível por conta própria e porque os dois lados chamam o mesmo
|
|
21
|
+
* renderer puro. Estender essa forma para 9 payloads + hooks + merge de
|
|
22
|
+
* settings + gitignore + índice do git é reimplementar o instalador inteiro e
|
|
23
|
+
* depender de disciplina humana para mantê-los sincronizados.
|
|
24
|
+
*
|
|
25
|
+
* **Os verbos são SÍNCRONOS de propósito.** Metade dos instaladores que eles
|
|
26
|
+
* atravessam (`installSkills`, `installAgents`, `installRulesForStack`) é
|
|
27
|
+
* síncrona; um coletor assíncrono obrigaria a tornar `installRulesForStack`
|
|
28
|
+
* async e a mudar a assinatura que `setup-infra`, `init` e os testes já usam.
|
|
29
|
+
*
|
|
30
|
+
* **O registro é a IDENTIDADE do efeito, nunca o conteúdo.** `writeFile` grava
|
|
31
|
+
* `{ verb, target }`, não os bytes: `installClaudeHooks` carimba um
|
|
32
|
+
* `generated_at` a cada execução, e comparar conteúdo faria o canário de
|
|
33
|
+
* igualdade dry × real reprovar por causa do relógio.
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
import fs from 'fs-extra';
|
|
37
|
+
import { spawnSync } from 'node:child_process';
|
|
38
|
+
import { dirname } from 'node:path';
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* @typedef {Object} Effect
|
|
42
|
+
* @property {string} verb - ensureDir | remove | copyDir | copyFile | writeFile | writeJson | mergeJson | spawn | gitIndex
|
|
43
|
+
* @property {string} target - o caminho (ou comando) sobre o qual o efeito age
|
|
44
|
+
* @property {string} [from] - origem, para os verbos de cópia
|
|
45
|
+
* @property {string} [detail] - qualificador estável (argumentos do spawn, operação do git)
|
|
46
|
+
*/
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Cria um coletor de efeitos.
|
|
50
|
+
*
|
|
51
|
+
* @param {{ dryRun?: boolean }} [options]
|
|
52
|
+
* @returns {{
|
|
53
|
+
* dryRun: boolean,
|
|
54
|
+
* ensureDir: (path: string) => void,
|
|
55
|
+
* remove: (path: string) => void,
|
|
56
|
+
* copyDir: (from: string, to: string) => void,
|
|
57
|
+
* copyFile: (from: string, to: string) => void,
|
|
58
|
+
* writeFile: (path: string, content: string) => void,
|
|
59
|
+
* writeJson: (path: string, data: object) => void,
|
|
60
|
+
* mergeJson: (path: string, patch: object) => void,
|
|
61
|
+
* spawn: (cmd: string, args: string[], options?: object) => object|null,
|
|
62
|
+
* gitIndex: (op: string, path: string, options?: object) => boolean,
|
|
63
|
+
* list: () => Effect[],
|
|
64
|
+
* }}
|
|
65
|
+
*/
|
|
66
|
+
export function createEffects({ dryRun = false } = {}) {
|
|
67
|
+
/** @type {Effect[]} */
|
|
68
|
+
const effects = [];
|
|
69
|
+
|
|
70
|
+
const record = (verb, target, extra = {}) => {
|
|
71
|
+
const effect = { verb, target };
|
|
72
|
+
if (extra.from !== undefined) effect.from = extra.from;
|
|
73
|
+
if (extra.detail !== undefined) effect.detail = extra.detail;
|
|
74
|
+
if (extra.args !== undefined) effect.args = extra.args;
|
|
75
|
+
effects.push(effect);
|
|
76
|
+
return effect;
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
return {
|
|
80
|
+
dryRun,
|
|
81
|
+
|
|
82
|
+
ensureDir(path) {
|
|
83
|
+
record('ensureDir', path);
|
|
84
|
+
if (!dryRun) fs.ensureDirSync(path);
|
|
85
|
+
},
|
|
86
|
+
|
|
87
|
+
remove(path) {
|
|
88
|
+
record('remove', path);
|
|
89
|
+
if (!dryRun) fs.removeSync(path);
|
|
90
|
+
},
|
|
91
|
+
|
|
92
|
+
copyDir(from, to, options = {}) {
|
|
93
|
+
record('copyDir', to, { from });
|
|
94
|
+
if (!dryRun) fs.copySync(from, to, { overwrite: true, errorOnExist: false, ...options });
|
|
95
|
+
},
|
|
96
|
+
|
|
97
|
+
copyFile(from, to) {
|
|
98
|
+
record('copyFile', to, { from });
|
|
99
|
+
if (!dryRun) {
|
|
100
|
+
fs.ensureDirSync(dirname(to));
|
|
101
|
+
fs.copySync(from, to, { overwrite: true });
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
writeFile(path, content) {
|
|
106
|
+
record('writeFile', path);
|
|
107
|
+
if (!dryRun) {
|
|
108
|
+
fs.ensureDirSync(dirname(path));
|
|
109
|
+
fs.writeFileSync(path, content, 'utf8');
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
|
|
113
|
+
writeJson(path, data) {
|
|
114
|
+
record('writeJson', path);
|
|
115
|
+
if (!dryRun) {
|
|
116
|
+
fs.ensureDirSync(dirname(path));
|
|
117
|
+
fs.writeJsonSync(path, data, { spaces: 2 });
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Leitura + fusão rasa + escrita, como UM efeito.
|
|
123
|
+
*
|
|
124
|
+
* A leitura acontece nos dois modos (ler não muda nada); só a escrita é
|
|
125
|
+
* suprimida em dry. É assim que `~/.claude/settings.json` — um arquivo do
|
|
126
|
+
* usuário, FORA do projeto — é atualizado sem que o dry o toque.
|
|
127
|
+
*/
|
|
128
|
+
mergeJson(path, patch) {
|
|
129
|
+
record('mergeJson', path);
|
|
130
|
+
if (dryRun) return;
|
|
131
|
+
let current = {};
|
|
132
|
+
try {
|
|
133
|
+
if (fs.existsSync(path)) current = fs.readJsonSync(path);
|
|
134
|
+
} catch {
|
|
135
|
+
current = {};
|
|
136
|
+
}
|
|
137
|
+
fs.ensureDirSync(dirname(path));
|
|
138
|
+
fs.writeJsonSync(path, { ...current, ...patch }, { spaces: 2 });
|
|
139
|
+
},
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* @param {string} cmd
|
|
143
|
+
* @param {string[]} args
|
|
144
|
+
* @param {{ cwd?: string, dryRunArgs?: string[] }} [options]
|
|
145
|
+
* `dryRunArgs` é a variante SÓ-LEITURA do mesmo comando (por exemplo
|
|
146
|
+
* `--check`). Em dry ela é executada no lugar; sem ela, nada roda.
|
|
147
|
+
* O REGISTRO é o mesmo nos dois modos: o efeito previsto é o comando
|
|
148
|
+
* real, não a sua sonda.
|
|
149
|
+
*/
|
|
150
|
+
spawn(cmd, args = [], options = {}) {
|
|
151
|
+
// `args` viaja no registro ALÉM do `detail` legível. O `detail` é para
|
|
152
|
+
// humano; quebrá-lo por espaço para descobrir onde o spawn escreve é o
|
|
153
|
+
// bug que um caminho como `R:\Polymorphism Tech\...` produz em silêncio,
|
|
154
|
+
// e que o relatório mostrou pondo um efeito do PACOTE da CLI no grupo
|
|
155
|
+
// "no projeto".
|
|
156
|
+
record('spawn', cmd, { detail: args.join(' '), args: [...args] });
|
|
157
|
+
const { cwd, dryRunArgs } = options;
|
|
158
|
+
if (dryRun) {
|
|
159
|
+
if (!dryRunArgs) return null;
|
|
160
|
+
return spawnSync(cmd, dryRunArgs, { cwd, encoding: 'utf8' });
|
|
161
|
+
}
|
|
162
|
+
return spawnSync(cmd, args, { cwd, encoding: 'utf8' });
|
|
163
|
+
},
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Mutação do índice do git (`git rm --cached`, etc.). A SONDA que decide
|
|
167
|
+
* se há o que fazer fica fora daqui de propósito: sondar é ler, e ler
|
|
168
|
+
* acontece nos dois modos.
|
|
169
|
+
*/
|
|
170
|
+
gitIndex(op, path, options = {}) {
|
|
171
|
+
record('gitIndex', path, { detail: op });
|
|
172
|
+
if (dryRun) return true;
|
|
173
|
+
const result = spawnSync('git', [...op.split(' '), path], {
|
|
174
|
+
cwd: options.cwd, encoding: 'utf-8', stdio: ['ignore', 'pipe', 'ignore'],
|
|
175
|
+
});
|
|
176
|
+
return result.status === 0;
|
|
177
|
+
},
|
|
178
|
+
|
|
179
|
+
list() {
|
|
180
|
+
return effects.map((e) => ({ ...e }));
|
|
181
|
+
},
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Coletor no-op-de-registro para chamadores que não passam um: executa
|
|
187
|
+
* normalmente e registra numa lista descartada. Existe para que assinaturas
|
|
188
|
+
* novas de `installSkills`/`installAgents`/`installRulesForStack` continuem
|
|
189
|
+
* funcionando para `setup-infra` e `init` sem mudança de comportamento.
|
|
190
|
+
*/
|
|
191
|
+
export function realEffects() {
|
|
192
|
+
return createEffects({ dryRun: false });
|
|
193
|
+
}
|