@polymorphism-tech/morph-spec 8.33.0 → 8.35.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +22 -2
- package/README.md +2 -2
- package/bin/morph-spec.js +43 -5
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +5 -4
- package/docs/QUICKSTART.md +1 -1
- package/framework/CLAUDE.md +2 -2
- package/framework/MORPH.md +20 -22
- package/framework/agents.json +198 -44
- package/framework/ai-pin.json +22 -0
- package/framework/commands/morph-media.md +45 -0
- package/framework/docs/CLI.md +238 -10
- 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/README.md +11 -1
- package/framework/hooks/claude-code/core/block-destructive-worktree-removal.js +5 -8
- package/framework/hooks/claude-code/core/block-raw-worktree-add.js +79 -0
- package/framework/hooks/claude-code/core/chain.js +27 -7
- package/framework/hooks/claude-code/core/protect-spec-files.js +41 -4
- package/framework/hooks/claude-code/core/worktree-context.js +83 -14
- package/framework/hooks/claude-code/core/worktree-create.js +206 -0
- package/framework/hooks/claude-code/core/worktree-env-inject.js +87 -0
- package/framework/hooks/claude-code/core/worktree-remove.js +81 -0
- package/framework/hooks/shared/command-tokens.js +23 -0
- package/framework/hooks/shared/e2e-check.js +0 -0
- package/framework/hooks/shared/git-paths.js +25 -0
- package/framework/hooks/shared/hook-response.js +17 -0
- package/framework/hooks/shared/morph-cli.js +134 -0
- package/framework/rules/ai-agents-standards.md +14 -2
- package/framework/rules/infrastructure-standards.md +3 -1
- 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 +28 -7
- 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 +20 -2
- package/framework/skills/core/morph-media/SKILL.md +215 -0
- package/framework/skills/core/morph-plan/SKILL.md +38 -4
- 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 +1504 -830
- package/framework/standards/ai-agents/agent-archetypes.md +160 -0
- package/framework/standards/ai-agents/agent-session.md +256 -160
- package/framework/standards/ai-agents/agent-spec.md +196 -0
- package/framework/standards/ai-agents/batching.md +217 -193
- package/framework/standards/ai-agents/context-providers.md +261 -188
- 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 +351 -250
- package/framework/standards/ai-agents/mcp-tools.md +250 -197
- 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 +241 -207
- package/framework/standards/ai-agents/modalities-image-gen.md +422 -173
- package/framework/standards/ai-agents/modalities-speech-to-text.md +177 -171
- package/framework/standards/ai-agents/modalities-text-to-speech.md +189 -181
- package/framework/standards/ai-agents/modalities-vision.md +205 -201
- package/framework/standards/ai-agents/multi-agent-patterns.md +211 -155
- package/framework/standards/ai-agents/observability-patterns.md +227 -17
- package/framework/standards/ai-agents/production.md +260 -231
- package/framework/standards/ai-agents/prompt-sources.md +389 -0
- package/framework/standards/ai-agents/providers/model-registry.md +293 -158
- package/framework/standards/ai-agents/rag-custom-pgvector.md +69 -4
- package/framework/standards/ai-agents/service-tier-flex.md +143 -135
- package/framework/standards/ai-agents/setup.md +166 -50
- package/framework/standards/ai-agents/structured-output.md +280 -249
- package/framework/standards/ai-agents/sweet-spot.md +129 -109
- 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 +90 -0
- package/framework/templates/REGISTRY.json +14 -1
- package/framework/templates/code/dotnet/ai-agents/PromptRepository.cs.template +95 -95
- package/framework/templates/code/sql/ai-agents/agent-prompts-migration.sql.template +58 -58
- 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/infrastructure/docker/docker-compose.template.yml +24 -14
- package/package.json +3 -3
- package/src/commands/e2e/e2e.js +91 -40
- package/src/commands/project/doctor.js +143 -6
- package/src/commands/project/env.js +95 -0
- package/src/commands/project/update.js +469 -153
- package/src/commands/project/worktree.js +274 -53
- 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/delete.js +8 -0
- package/src/commands/state/finish.js +360 -53
- 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/state-manager.js +9 -1
- package/src/dashboard/app/pages/HookInspector.jsx +430 -418
- package/src/dashboard/scanner/hooks-scanner.js +5 -1
- 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/e2e/compose-env.js +55 -14
- package/src/lib/e2e/stack-teardown.js +115 -0
- package/src/lib/evaluator-report.js +269 -15
- package/src/lib/generators/architecture-scanner.js +2 -1
- package/src/lib/git/default-branch.js +92 -4
- package/src/lib/graph/graphifyignore.js +2 -1
- package/src/lib/installers/effects.js +193 -0
- 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/test-nodes.js +486 -0
- package/src/lib/tasks/test-runner.js +431 -37
- package/src/lib/validators/verify-runner.js +590 -28
- package/src/lib/worktree/dirs.js +32 -0
- package/src/lib/worktree/docker-audit.js +287 -0
- package/src/lib/worktree/env-allocator.js +69 -11
- package/src/lib/worktree/worktree-audit.js +70 -3
- package/src/scripts/setup-infra.js +24 -11
- package/src/utils/agents-installer.js +18 -8
- package/src/utils/claude-md-injector.js +10 -5
- package/src/utils/file-copier.js +28 -16
- package/src/utils/hooks-installer.js +75 -29
- package/src/utils/skills-installer.js +194 -187
- package/src/utils/version-checker.js +68 -3
- package/framework/standards/ai-agents/prompts-in-db.md +0 -214
|
@@ -19,6 +19,7 @@ import { checkMissingAgents, checkAgentsDrift } from '../../utils/agents-install
|
|
|
19
19
|
import { getProjectTags } from '../../lib/stack-filter.js';
|
|
20
20
|
import { auditGraph } from '../../lib/graph/graph-health.js';
|
|
21
21
|
import { auditFeatureLifecycle } from '../../core/state/lifecycle-audit.js';
|
|
22
|
+
import { validateTestNodes, morphConfigPath } from '../../lib/tasks/test-nodes.js';
|
|
22
23
|
|
|
23
24
|
const isWindows = platform() === 'win32';
|
|
24
25
|
/**
|
|
@@ -367,6 +368,43 @@ async function doctorFullCommand(frameworkRoot) {
|
|
|
367
368
|
check('state.json', false, true, 'not found (run: morph-spec init)');
|
|
368
369
|
}
|
|
369
370
|
|
|
371
|
+
// ── 8. project.tests[] — o contrato do config, camada 3 ─────────────────
|
|
372
|
+
//
|
|
373
|
+
// As outras duas camadas são o `config.schema.json` (declarativa) e
|
|
374
|
+
// `validateTestNodes()` chamada pelo `verify` (fail-closed, em uso). Esta
|
|
375
|
+
// aqui existe para o defeito chegar ANTES do gate: um `id` com typo em
|
|
376
|
+
// `tests[]` só se manifesta quando alguém roda `morph-spec verify` e lê um
|
|
377
|
+
// nó vermelho — o doctor o mostra na checagem de saúde, que é onde se olha
|
|
378
|
+
// primeiro. Reusa `validateTestNodes` (a MESMA autoridade que o verify
|
|
379
|
+
// consulta): uma segunda leitura do mesmo campo aqui seria uma segunda
|
|
380
|
+
// opinião sobre o que é um nó válido, e as duas divergiriam.
|
|
381
|
+
//
|
|
382
|
+
// PULA de verdade — sem check e sem cabeçalho — quando não há `config.json`
|
|
383
|
+
// ou quando `project.tests` não foi declarado. Um projeto sem declaração usa
|
|
384
|
+
// o fallback de quatro passos e não tem contrato a violar; este repositório
|
|
385
|
+
// é exatamente esse caso, e um check que não pode passar aqui é ruído, não
|
|
386
|
+
// achado (`test/commands/doctor.test.js`: `doctor --full` sem nenhum `✗`).
|
|
387
|
+
// WARN e nunca ERROR: a barreira fail-closed é o `verify`; o doctor informa.
|
|
388
|
+
const testsConfigPath = morphConfigPath(frameworkRoot);
|
|
389
|
+
if (await pathExists(testsConfigPath)) {
|
|
390
|
+
let testsConfig = null;
|
|
391
|
+
try {
|
|
392
|
+
testsConfig = JSON.parse((await fs.readFile(testsConfigPath, 'utf8')).replace(/^\uFEFF/, ''));
|
|
393
|
+
} catch {
|
|
394
|
+
testsConfig = null; // config ilegível já é reportado pelo doctor base
|
|
395
|
+
}
|
|
396
|
+
if (testsConfig?.project?.tests !== undefined) {
|
|
397
|
+
console.log(chalk.cyan('\n .morph/config/config.json — project.tests[]'));
|
|
398
|
+
const declared = validateTestNodes(testsConfig, frameworkRoot);
|
|
399
|
+
const broken = declared.filter((n) => n.status === 'fail');
|
|
400
|
+
check(`config tests[] (${declared.length - broken.length}/${declared.length})`,
|
|
401
|
+
broken.length === 0, true,
|
|
402
|
+
broken.length > 0
|
|
403
|
+
? broken.map((n) => n.reason ?? `invalid-test-node: ${n.id}`).join('; ')
|
|
404
|
+
: '');
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
370
408
|
// ── Display ──────────────────────────────────────────────────────────────
|
|
371
409
|
console.log('\n' + '─'.repeat(60));
|
|
372
410
|
console.log(chalk.bold('\nResults:\n'));
|
|
@@ -586,11 +624,14 @@ export async function doctorWorktreesCommand(targetPath, options = {}) {
|
|
|
586
624
|
}
|
|
587
625
|
|
|
588
626
|
const issues = auditWorktrees(targetPath);
|
|
589
|
-
// `severity: 'info'` entries (a worktree carrying unintegrated work)
|
|
590
|
-
//
|
|
591
|
-
//
|
|
592
|
-
//
|
|
593
|
-
|
|
627
|
+
// `severity: 'info'` entries (a worktree carrying unintegrated work) and
|
|
628
|
+
// `severity: 'warn'` entries (a worktree created outside morph's container,
|
|
629
|
+
// and the docker findings from `docker-audit.js`: an orphan stack, a port
|
|
630
|
+
// held by another compose project) are REPORTED but are not failures — a
|
|
631
|
+
// `doctor --worktrees || ...` in CI must not go red because somebody has an
|
|
632
|
+
// open PR, a hand-made tree, or a container still up. The human path below
|
|
633
|
+
// splits the same way; both surfaces must agree on what counts as broken.
|
|
634
|
+
const isProblem = (i) => !['info', 'warn'].includes(i.severity);
|
|
594
635
|
|
|
595
636
|
if (options.json) {
|
|
596
637
|
console.log(JSON.stringify({ issues }, null, 2));
|
|
@@ -617,7 +658,7 @@ export async function doctorWorktreesCommand(targetPath, options = {}) {
|
|
|
617
658
|
console.log(` → ${i.fix}`);
|
|
618
659
|
}
|
|
619
660
|
for (const i of informational) {
|
|
620
|
-
console.log(` ℹ ${i.type}`);
|
|
661
|
+
console.log(i.severity === 'warn' ? chalk.yellow(` ⚠ ${i.type}`) : ` ℹ ${i.type}`);
|
|
621
662
|
console.log(` ${i.detail}`);
|
|
622
663
|
console.log(` → ${i.fix}`);
|
|
623
664
|
}
|
|
@@ -687,6 +728,86 @@ function auditPullRequests(root) {
|
|
|
687
728
|
return issues;
|
|
688
729
|
}
|
|
689
730
|
|
|
731
|
+
/**
|
|
732
|
+
* `morph-spec doctor --ai` — as cinco checagens de projeto .NET com agentes.
|
|
733
|
+
*
|
|
734
|
+
* **Superfície própria, no molde do `--mcp`, e é decisão, não acidente.** O
|
|
735
|
+
* `--ai` NÃO entra no `--full` nem no `doctor` default:
|
|
736
|
+
*
|
|
737
|
+
* - o renderer do `--full` é `ok → ✓`, `warn → ⚠`, **`else → ✗`** — não tem o
|
|
738
|
+
* ramo `skip → ○`. Uma checagem de IA lá seria VERMELHA neste repositório
|
|
739
|
+
* mesmo dizendo "não verificado", e `test/commands/doctor.test.js` exige
|
|
740
|
+
* `doesNotMatch(out, /✗/)` para `doctor --full` aqui, com a justificativa
|
|
741
|
+
* escrita no próprio teste: *"every check must be satisfiable here — a
|
|
742
|
+
* permanently red one teaches nothing"*;
|
|
743
|
+
* - o `doctor` default é o comando mais rodado do dia, e cinco linhas `○`
|
|
744
|
+
* permanentes em todo projeto sem .NET é ruído com custo e sem informação.
|
|
745
|
+
*
|
|
746
|
+
* Exit code: 1 se houver ao menos um `error`; 0 caso contrário. `warn` e `skip`
|
|
747
|
+
* nunca mudam o código de saída.
|
|
748
|
+
*
|
|
749
|
+
* @param {string} targetPath
|
|
750
|
+
* @param {object} [options] - repassado a `collectAiChecks` (`config`, `deps`)
|
|
751
|
+
* @returns {Promise<Array>} as checagens, para o canário assere-las sem parsear texto
|
|
752
|
+
*/
|
|
753
|
+
export async function doctorAiCommand(targetPath, options = {}) {
|
|
754
|
+
const { collectAiChecks, aiExitCode, allSkipped } = await import('../../lib/ai/ai-checks.js');
|
|
755
|
+
const { readMorphConfig } = await import('../../lib/tasks/test-runner.js');
|
|
756
|
+
|
|
757
|
+
logger.header('AI Health Check (.NET + agentes)');
|
|
758
|
+
logger.blank();
|
|
759
|
+
|
|
760
|
+
const config = options.config !== undefined ? options.config : readMorphConfig(targetPath);
|
|
761
|
+
const checks = collectAiChecks(targetPath, { config, deps: options.deps ?? {} });
|
|
762
|
+
|
|
763
|
+
if (allSkipped(checks)) {
|
|
764
|
+
// Condição 4 do silêncio: nenhuma checagem teve insumo. Uma linha, não
|
|
765
|
+
// cinco `○` vazios — listar cinco vezes "não verificado" é o ruído que
|
|
766
|
+
// esta superfície existe para não produzir.
|
|
767
|
+
const reasons = [...new Set(checks.map((c) => c.reason))];
|
|
768
|
+
for (const reason of reasons) {
|
|
769
|
+
console.log(chalk.gray(` ○ Nenhuma das ${checks.length} checagens teve insumo — ${reason}.`));
|
|
770
|
+
}
|
|
771
|
+
logger.blank();
|
|
772
|
+
process.exitCode = 0;
|
|
773
|
+
return checks;
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
for (const c of checks) {
|
|
777
|
+
if (c.status === 'ok') {
|
|
778
|
+
console.log(chalk.green(` ✓ ${c.name}`));
|
|
779
|
+
} else if (c.status === 'skip') {
|
|
780
|
+
console.log(chalk.gray(` ○ ${c.name}`) + chalk.gray(` — não verificado: ${c.reason}`));
|
|
781
|
+
} else if (c.status === 'warn') {
|
|
782
|
+
console.log(chalk.yellow(` ⚠ ${c.name}`));
|
|
783
|
+
} else {
|
|
784
|
+
console.log(chalk.red(` ✗ ${c.name}`));
|
|
785
|
+
}
|
|
786
|
+
for (const f of c.findings) {
|
|
787
|
+
const mark = f.severity === 'error' ? chalk.red(' ✗') : chalk.yellow(' →');
|
|
788
|
+
console.log(`${mark} ${chalk.gray(f.message)}`);
|
|
789
|
+
}
|
|
790
|
+
for (const file of c.unverified ?? []) {
|
|
791
|
+
console.log(chalk.gray(` ○ não verificado: ${relative(targetPath, file) || file} (ilegível)`));
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
logger.blank();
|
|
796
|
+
const errors = checks.filter((c) => c.status === 'error').length;
|
|
797
|
+
const warns = checks.filter((c) => c.status === 'warn').length;
|
|
798
|
+
const skips = checks.filter((c) => c.status === 'skip').length;
|
|
799
|
+
const oks = checks.filter((c) => c.status === 'ok').length;
|
|
800
|
+
|
|
801
|
+
const summary = `${oks} ok · ${warns} aviso(s) · ${errors} erro(s) · ${skips} não verificada(s)`;
|
|
802
|
+
if (errors > 0) console.log(chalk.red(` ❌ ${summary}`));
|
|
803
|
+
else if (warns > 0) console.log(chalk.yellow(` ⚠️ ${summary}`));
|
|
804
|
+
else console.log(chalk.green(` ✅ ${summary}`));
|
|
805
|
+
logger.blank();
|
|
806
|
+
|
|
807
|
+
process.exitCode = aiExitCode(checks);
|
|
808
|
+
return checks;
|
|
809
|
+
}
|
|
810
|
+
|
|
690
811
|
export async function doctorCommand(options = {}) {
|
|
691
812
|
// `--json` and `--prune` only mean something under `--worktrees`. Falling
|
|
692
813
|
// through to the human health check would hand a script a banner and a table
|
|
@@ -701,6 +822,22 @@ export async function doctorCommand(options = {}) {
|
|
|
701
822
|
return false;
|
|
702
823
|
}
|
|
703
824
|
|
|
825
|
+
// `--ai` é superfície própria (D1). Combiná-la com outra superfície pediria
|
|
826
|
+
// dois relatórios com dois códigos de saída no mesmo processo — o segundo
|
|
827
|
+
// apagaria o primeiro em silêncio. Mesmo molde da guarda acima: recusa em
|
|
828
|
+
// vez de fingir.
|
|
829
|
+
if (options.ai) {
|
|
830
|
+
const conflicts = ['full', 'mcp', 'worktrees'].filter((f) => options[f]);
|
|
831
|
+
if (conflicts.length) {
|
|
832
|
+
const flags = conflicts.map((f) => `--${f}`).join(' e ');
|
|
833
|
+
logger.error(`--ai não se combina com ${flags} — cada superfície tem o seu relatório e o seu código de saída.`);
|
|
834
|
+
logger.dim(' Rode: morph-spec doctor --ai');
|
|
835
|
+
process.exitCode = 1;
|
|
836
|
+
return false;
|
|
837
|
+
}
|
|
838
|
+
return doctorAiCommand(process.cwd(), options);
|
|
839
|
+
}
|
|
840
|
+
|
|
704
841
|
// Reset mode: remove morph-managed settings entries
|
|
705
842
|
if (options.reset) {
|
|
706
843
|
const targetPath = process.cwd();
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `morph-spec env [feature] [--json|--posix|--powershell]` — print the
|
|
3
|
+
* dedicated environment of a worktree (port block + compose project) in a
|
|
4
|
+
* shell-loadable form.
|
|
5
|
+
*
|
|
6
|
+
* Inside Claude Code the `worktree-env-inject` hook applies this block to
|
|
7
|
+
* every dev-server / compose launcher automatically. This command is the same
|
|
8
|
+
* contract for everything OUTSIDE the harness: a human terminal
|
|
9
|
+
* (`eval "$(morph-spec env)"`), a CI step, a sub-agent running without hooks.
|
|
10
|
+
* It derives from `buildComposeEnv` — the function the `verify` e2e node and
|
|
11
|
+
* `e2e up` already use — so a shell and the stack can never disagree.
|
|
12
|
+
*
|
|
13
|
+
* Feature resolution: explicit argument, else the `morph/{f}` (or
|
|
14
|
+
* `morph-task/{n}`) branch of the cwd. Read-only: allocation is idempotent and
|
|
15
|
+
* never moves an existing block.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { execFileSync } from 'child_process';
|
|
19
|
+
import { readFileSync } from 'fs';
|
|
20
|
+
import { join, resolve } from 'path';
|
|
21
|
+
import chalk from 'chalk';
|
|
22
|
+
import { buildComposeEnv } from '../../lib/e2e/compose-env.js';
|
|
23
|
+
import { resolvePrimaryRoot } from './worktree.js';
|
|
24
|
+
import { currentBranch } from '../../lib/worktree/feature-truth.js';
|
|
25
|
+
|
|
26
|
+
/** `morph/<f>` → `<f>`; `morph-task/<n>` → `task/<n>`; anything else → null. */
|
|
27
|
+
export function envKeyFromBranch(branch) {
|
|
28
|
+
if (!branch) return null;
|
|
29
|
+
const feature = branch.match(/^morph\/(.+)$/)?.[1];
|
|
30
|
+
if (feature) return feature;
|
|
31
|
+
const task = branch.match(/^morph-task\/(.+)$/)?.[1];
|
|
32
|
+
return task ? `task/${task}` : null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function readConfig(root) {
|
|
36
|
+
try {
|
|
37
|
+
return JSON.parse(readFileSync(join(root, '.morph', 'config', 'config.json'), 'utf8').replace(/^/, ''));
|
|
38
|
+
} catch {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function gitCommonDir(cwd) {
|
|
44
|
+
try {
|
|
45
|
+
const out = execFileSync('git', ['rev-parse', '--git-common-dir'], { cwd, stdio: 'pipe' }).toString().trim();
|
|
46
|
+
return out ? resolve(cwd, out) : null;
|
|
47
|
+
} catch {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** POSIX single-quote. */
|
|
53
|
+
const shq = (v) => `'${String(v).replace(/'/g, "'\\''")}'`;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Pure: the env map for a key, or `{ error }`.
|
|
57
|
+
* @param {string|null} featureArg
|
|
58
|
+
* @param {string} cwd
|
|
59
|
+
*/
|
|
60
|
+
export function resolveWorktreeEnv(featureArg, cwd = process.cwd()) {
|
|
61
|
+
const key = featureArg || envKeyFromBranch(currentBranch(cwd));
|
|
62
|
+
if (!key) {
|
|
63
|
+
return { error: 'no-feature', message: 'Nenhuma feature: passe o nome ou rode de uma branch morph/{f} / morph-task/{n}.' };
|
|
64
|
+
}
|
|
65
|
+
const common = gitCommonDir(cwd);
|
|
66
|
+
if (!common) {
|
|
67
|
+
return { error: 'not-a-git-repo', message: 'Fora de um repositório git não há bloco de portas.' };
|
|
68
|
+
}
|
|
69
|
+
const root = resolvePrimaryRoot(cwd);
|
|
70
|
+
const env = buildComposeEnv({ cwd: root, config: readConfig(root), featureName: key, gitCommonDir: common });
|
|
71
|
+
return { key, env };
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function render(env, format) {
|
|
75
|
+
const entries = Object.entries(env);
|
|
76
|
+
if (format === 'json') return JSON.stringify(env, null, 2);
|
|
77
|
+
if (format === 'powershell') return entries.map(([k, v]) => `$env:${k}="${String(v).replace(/"/g, '`"')}"`).join('\n');
|
|
78
|
+
return entries.map(([k, v]) => `export ${k}=${shq(v)}`).join('\n');
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* @param {string|undefined} featureArg
|
|
83
|
+
* @param {{ json?: boolean, posix?: boolean, powershell?: boolean }} options
|
|
84
|
+
*/
|
|
85
|
+
export function envCommand(featureArg, options = {}) {
|
|
86
|
+
const result = resolveWorktreeEnv(featureArg || null, process.cwd());
|
|
87
|
+
if (result.error) {
|
|
88
|
+
if (options.json) console.log(JSON.stringify({ error: result.error, message: result.message }));
|
|
89
|
+
else console.error(chalk.red(`✗ ${result.message}`));
|
|
90
|
+
process.exit(1);
|
|
91
|
+
}
|
|
92
|
+
const format = options.json ? 'json' : options.powershell ? 'powershell' : 'posix';
|
|
93
|
+
console.log(render(result.env, format));
|
|
94
|
+
process.exit(0);
|
|
95
|
+
}
|