@polymorphism-tech/morph-spec 8.37.1 → 8.40.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 +37 -13
- package/README.md +2 -2
- package/bin/morph-spec.js +75 -49
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +1 -1
- package/docs/QUICKSTART.md +1 -1
- package/framework/CLAUDE.md +6 -5
- package/framework/MORPH.md +25 -22
- package/framework/agents.json +96 -36
- package/framework/docs/CLI.md +54 -17
- package/framework/evals/rubrics/code-quality.md +5 -0
- package/framework/evals/rubrics/condensed.md +6 -2
- package/framework/evals/rubrics/contracts.md +20 -0
- package/framework/evals/rubrics/test-coverage.md +17 -6
- package/framework/hooks/README.md +9 -3
- package/framework/hooks/claude-code/core/graph-refresh-kick.js +52 -0
- package/framework/hooks/claude-code/core/telemetry-log.js +172 -87
- package/framework/hooks/claude-code/core/validate-completion.js +16 -90
- package/framework/hooks/claude-code/core/worktree-context.js +10 -0
- package/framework/hooks/shared/command-tokens.js +96 -0
- package/framework/hooks/shared/git-exclude.js +44 -0
- package/framework/hooks/shared/git-paths.js +52 -0
- package/framework/hooks/shared/graph-freshness.js +380 -0
- package/framework/hooks/shared/graph-refresh.js +416 -0
- package/framework/hooks/shared/morph-cli.js +17 -5
- package/framework/hooks/shared/redact-secrets.js +55 -0
- package/framework/hooks/shared/telemetry-logger.js +25 -2
- package/framework/hooks/shared/transcript-usage.js +80 -0
- package/framework/mcp/registry.json +33 -2
- package/framework/rubrics/backend.json +2 -2
- package/framework/rubrics/default.json +2 -2
- package/framework/rubrics/frontend.json +2 -2
- package/framework/rules/csharp-standards.md +1 -0
- package/framework/rules/infrastructure-standards.md +0 -1
- package/framework/rules/morph-workflow.md +10 -4
- package/framework/rules/nextjs-standards.md +1 -0
- package/framework/schemas/config.schema.json +41 -1
- package/framework/schemas/feature.schema.json +5 -1
- 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 +40 -11
- package/framework/skills/core/morph-brainstorming/SKILL.md +16 -7
- package/framework/skills/core/morph-eval/SKILL.md +1 -1
- package/framework/skills/core/morph-hotfix/SKILL.md +1 -1
- package/framework/skills/core/morph-implement/SKILL.md +44 -10
- package/framework/skills/core/morph-media/SKILL.md +1 -1
- package/framework/skills/core/morph-plan/SKILL.md +73 -15
- package/framework/skills/core/morph-proposal/SKILL.md +1 -1
- package/framework/skills/core/morph-review/SKILL.md +51 -13
- 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 +39 -13
- package/framework/standards/STANDARDS.json +189 -14
- package/framework/standards/ai-agents/context-providers.md +16 -26
- package/framework/standards/ai-agents/durable-workflows-hitl.md +15 -21
- package/framework/standards/ai-agents/modalities-image-gen.md +21 -25
- package/framework/standards/ai-agents/modalities-speech-to-text.md +4 -15
- package/framework/standards/ai-agents/modalities-text-to-speech.md +2 -11
- package/framework/standards/ai-agents/modalities-vision.md +1 -18
- package/framework/standards/ai-agents/prompt-sources.md +0 -4
- package/framework/standards/ai-agents/testing-ai.md +2 -1
- package/framework/standards/architecture/code-comments.md +100 -0
- package/framework/standards/backend/dotnet/testing.md +22 -0
- package/framework/standards/backend/integrations/neon-auth/neon-auth.md +63 -50
- package/framework/standards/frontend/admin-app/data-table.md +562 -0
- package/framework/standards/frontend/admin-app/feedback.md +236 -0
- package/framework/standards/frontend/admin-app/forms.md +232 -0
- package/framework/standards/frontend/admin-app/page-structure.md +203 -0
- package/framework/standards/frontend/design-system/ai-image-generation.md +5 -5
- package/framework/standards/frontend/nextjs/components.md +37 -58
- package/framework/standards/frontend/nextjs/data-fetching.md +1 -0
- package/framework/standards/frontend/nextjs/forms.md +31 -30
- package/framework/standards/frontend/nextjs/state-management.md +2 -1
- package/framework/standards/frontend/nextjs/testing.md +18 -0
- package/framework/standards/integration/mcp/mcp-tools.md +48 -117
- package/framework/templates/code/dotnet/abstractions/result.cs +0 -2
- package/framework/templates/code/dotnet/ai-agents/AgentBootstrap.cs.template +26 -39
- package/framework/templates/code/dotnet/ai-agents/EmbeddingService.cs.template +28 -68
- package/framework/templates/code/dotnet/ai-agents/ModelRegistry.cs.template +99 -117
- package/framework/templates/code/dotnet/ai-agents/PromptRepository.cs.template +2 -44
- package/framework/templates/code/dotnet/ai-agents/ResilientToolMiddleware.cs.template +40 -61
- package/framework/templates/code/dotnet/ai-agents/VectorSearchTool.cs.template +54 -107
- package/framework/templates/code/dotnet/ai-agents/model-registry.json.template +53 -58
- package/framework/templates/code/dotnet/contracts/contracts-vsa.cs +192 -281
- package/framework/templates/code/dotnet/database/migration.cs +49 -83
- package/framework/templates/code/dotnet/database/vector-entity.cs +2 -46
- package/framework/templates/code/dotnet/extensions/result-extensions.cs +0 -2
- package/framework/templates/code/dotnet/feature/validator.cs.hbs +11 -15
- package/framework/templates/code/dotnet/jobs/agent.cs +3 -51
- package/framework/templates/code/dotnet/jobs/job.cs +2 -97
- package/framework/templates/code/dotnet/test.cs +2 -87
- package/framework/templates/code/typescript/contracts.ts +140 -167
- package/framework/templates/context/design-system.md +3 -3
- package/framework/templates/docs/proposal.md +11 -0
- package/framework/templates/docs/spec.md +15 -0
- package/framework/templates/docs/ui-mockups.md +115 -98
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentApi.cs +1 -48
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentFactory.cs +3 -79
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentFactoryOptions.cs +1 -70
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentSpec.cs +1 -47
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentSpecOptions.cs +2 -99
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/AiKitAssemblyMarker.cs +0 -6
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Conversation/ConversationStore.cs +3 -88
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Conversation/IConversationStore.cs +1 -41
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Conversation/InMemoryConversationStore.cs +0 -21
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/CountingChatClient.cs +1 -25
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalAssertion.cs +2 -63
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalCacheMissException.cs +0 -30
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalCacheOptions.cs +1 -52
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalCacheStamp.cs +3 -78
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalFixture.cs +4 -109
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalMode.cs +0 -22
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalQualityEvaluators.cs +1 -36
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalScenario.cs +2 -57
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalSummary.cs +0 -75
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/OfflineChatClient.cs +2 -59
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/ShortPathResponseCache.cs +4 -111
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/IMediaGenerator.cs +0 -34
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/IMediaProvider.cs +1 -57
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaArtifact.cs +0 -19
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaDisclosure.cs +2 -74
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaGenerator.cs +2 -33
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaIdempotencyKey.cs +1 -43
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaJob.cs +1 -96
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaJobId.cs +0 -13
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaJobState.cs +0 -27
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaPricing.cs +0 -33
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaProvenance.cs +0 -31
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaRequest.cs +3 -75
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaResult.cs +0 -42
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaReview.cs +0 -39
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaSeams.cs +1 -55
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaStore.cs +3 -59
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MorphMedia.cs +0 -46
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/GoogleImageProvider.cs +3 -28
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/OpenAIImageProvider.cs +7 -61
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/VeoVideoProvider.cs +2 -37
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Middleware/IUsageCostSink.cs +1 -23
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Middleware/UsageCostMiddleware.cs +2 -82
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Middleware/UsageCostRecord.cs +1 -68
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Observability/MorphOpenTelemetryExtensions.cs +1 -62
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Observability/MorphTelemetryOptions.cs +2 -62
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/IProviderClientFactory.cs +1 -25
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/ModelAlias.cs +0 -124
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/ModelRegistry.cs +1 -73
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Rag/DeltaIngest.cs +1 -66
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Rag/DeltaPlan.cs +0 -41
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/AgentFactoryTests.cs +2 -112
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/AgentSpecTests.cs +2 -43
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/ConversationStoreTests.cs +5 -110
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/DeltaIngestTests.cs +7 -103
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalAssertionTests.cs +1 -42
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalCacheMissTests.cs +1 -30
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalCacheStampTests.cs +1 -19
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalFailuresTests.cs +0 -48
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalFixtureTests.cs +1 -29
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalRefreshTests.cs +2 -38
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalWorkspace.cs +1 -14
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/KitEvals.cs +4 -69
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/OfflineChatClientTests.cs +1 -10
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeChatClient.cs +1 -57
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeChatClientTests.cs +0 -26
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeClock.cs +1 -19
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeMediaProvider.cs +0 -32
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeProviderClientFactory.cs +0 -14
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeUsageCostSink.cs +0 -24
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/InMemoryMediaStore.cs +0 -32
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaAccessibilityTests.cs +0 -37
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaAliasIsNotAnAgentTests.cs +0 -24
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaDisclosureTests.cs +0 -78
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaGeneratorTests.cs +0 -53
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaIdempotencyKeyTests.cs +0 -44
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaJobStateTests.cs +0 -44
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaReviewTests.cs +0 -19
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaStoreTests.cs +0 -24
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/ModelRegistryTests.cs +6 -48
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/SkeletonSmokeTests.cs +1 -13
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/TelemetryTests.cs +2 -71
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/UsageCostMiddlewareTests.cs +5 -100
- package/framework/templates/feature/decisions.md +124 -122
- package/framework/templates/frontend/nextjs/Dockerfile.nextjs.hbs +39 -43
- package/framework/templates/frontend/nextjs/env.mjs.hbs +29 -32
- package/framework/templates/frontend/nextjs/page.tsx.hbs +21 -22
- package/framework/templates/frontend/nextjs/use-feature.ts.hbs +42 -54
- package/framework/templates/integrations/asaas-client.cs +332 -387
- package/framework/templates/integrations/asaas-webhook.cs +290 -351
- package/framework/templates/integrations/neon-auth-config.cs +9 -105
- package/framework/templates/project/validate.js +97 -120
- package/package.json +2 -2
- package/src/commands/dashboard/dashboard.js +80 -53
- package/src/commands/graph/affected.js +4 -3
- package/src/commands/graph/build.js +109 -18
- package/src/commands/graph/explain.js +8 -5
- package/src/commands/graph/status.js +14 -8
- package/src/commands/mcp/mcp-setup.js +37 -9
- package/src/commands/observability/cost.js +148 -18
- package/src/commands/project/doctor.js +85 -15
- package/src/commands/project/init.js +6 -3
- package/src/commands/project/install-plugin-cmd.js +1 -1
- package/src/commands/project/update.js +30 -0
- package/src/commands/project/worktree.js +73 -90
- package/src/commands/state/advance.js +18 -0
- package/src/commands/state/approve.js +15 -0
- package/src/commands/state/archive.js +62 -9
- package/src/commands/state/finish.js +62 -14
- package/src/commands/state/gate-check.js +8 -1
- package/src/commands/state/gate-decision.js +8 -4
- package/src/commands/state/report-check.js +90 -9
- package/src/commands/state/usecase.js +66 -0
- package/src/commands/tasks/dag.js +17 -2
- package/src/commands/validation/validate-feature.js +6 -1
- package/src/commands/validation/verify.js +20 -1
- package/src/dashboard/app/App.jsx +80 -16
- package/src/dashboard/app/components/ActivityFeed.jsx +172 -0
- package/src/dashboard/app/components/CommandPalette.jsx +155 -0
- package/src/dashboard/app/components/DetailPanel.jsx +47 -11
- package/src/dashboard/app/components/EntityLink.jsx +55 -0
- package/src/dashboard/app/components/ErrorBoundary.jsx +54 -0
- package/src/dashboard/app/components/FlowGraph.jsx +19 -17
- package/src/dashboard/app/components/KnowledgeMarkdown.jsx +53 -0
- package/src/dashboard/app/components/Markdown.jsx +47 -0
- package/src/dashboard/app/components/PhaseRail.jsx +111 -0
- package/src/dashboard/app/components/SearchFilter.jsx +40 -22
- package/src/dashboard/app/components/Sidebar.jsx +146 -37
- package/src/dashboard/app/hooks/useMorphQuery.js +54 -0
- package/src/dashboard/app/hooks/usePref.js +38 -0
- package/src/dashboard/app/lib/format.js +69 -0
- package/src/dashboard/app/lib/phases.js +56 -0
- package/src/dashboard/app/lib/project.js +50 -0
- package/src/dashboard/app/pages/Activity.jsx +24 -0
- package/src/dashboard/app/pages/AgentMap.jsx +371 -370
- package/src/dashboard/app/pages/FeatureDetail.jsx +604 -0
- package/src/dashboard/app/pages/Features.jsx +400 -0
- package/src/dashboard/app/pages/Guide.jsx +146 -0
- package/src/dashboard/app/pages/HookInspector.jsx +357 -322
- package/src/dashboard/app/pages/PhasePipeline.jsx +277 -408
- package/src/dashboard/app/pages/Portfolio.jsx +156 -0
- package/src/dashboard/app/pages/Rubrics.jsx +101 -0
- package/src/dashboard/app/pages/RulesMap.jsx +300 -273
- package/src/dashboard/app/pages/SkillsLibrary.jsx +483 -366
- package/src/dashboard/app/pages/Standards.jsx +407 -332
- package/src/dashboard/app/styles/index.css +187 -47
- package/src/dashboard/index.html +14 -12
- package/src/dashboard/package.json +32 -24
- package/src/dashboard/scanner/activity-scanner.js +351 -0
- package/src/dashboard/scanner/agents-scanner.js +63 -1
- package/src/dashboard/scanner/docs-scanner.js +59 -0
- package/src/dashboard/scanner/feature-status.js +10 -0
- package/src/dashboard/scanner/features-scanner.js +360 -0
- package/src/dashboard/scanner/hooks-scanner.js +8 -1
- package/src/dashboard/scanner/index.js +14 -7
- package/src/dashboard/scanner/phases-scanner.js +14 -15
- package/src/dashboard/scanner/portfolio.js +153 -0
- package/src/dashboard/scanner/project-api.js +195 -0
- package/src/dashboard/scanner/rules-scanner.js +42 -37
- package/src/dashboard/scanner/skills-scanner.js +105 -43
- package/src/dashboard/scanner/standards-scanner.js +4 -1
- package/src/dashboard/vite-plugin-morph-data.js +134 -17
- package/src/lib/ai/comment-stripper.js +101 -4
- package/src/lib/detectors/claude-config-detector.js +2 -7
- package/src/lib/detectors/index.js +5 -1
- package/src/lib/evaluator-report.js +47 -0
- package/src/lib/generators/handoff-generator.js +4 -18
- package/src/lib/generators/settings-generator.js +208 -209
- package/src/lib/git/default-branch.js +52 -0
- package/src/lib/git/tree-fingerprint.js +58 -0
- package/src/lib/git/tree-state.js +167 -0
- package/src/lib/graph/god-nodes.js +12 -3
- package/src/lib/graph/graph-health.js +48 -2
- package/src/lib/graph/graph-loader.js +99 -25
- package/src/lib/graph/graphifyignore.js +145 -28
- package/src/lib/graph/sanitize.js +117 -11
- package/src/lib/installers/mcp-installer.js +724 -719
- package/src/lib/installers/memory-installer.js +1 -2
- package/src/lib/projects/registry.js +154 -0
- package/src/lib/risk-detector.js +90 -0
- package/src/lib/specs/acervo-format.js +70 -0
- package/src/lib/specs/acervo-lifecycle.js +264 -0
- package/src/lib/specs/adr-index.js +169 -0
- package/src/lib/specs/revocations.js +156 -0
- package/src/lib/specs/screen-checklist.js +357 -0
- package/src/lib/specs/usecase-check.js +20 -16
- package/src/lib/specs/usecase-promoter.js +103 -38
- package/src/lib/specs/usecase-registry.js +7 -7
- package/src/lib/tasks/tasks-json-parser.js +2 -3
- package/src/lib/telemetry/metrics.js +386 -0
- package/src/lib/telemetry/telemetry-reader.js +161 -51
- package/src/lib/telemetry/verify-event.js +79 -0
- package/src/lib/validators/code-budget/code-budget-validator.js +200 -0
- package/src/lib/validators/code-budget/comment-scanner.js +75 -0
- package/src/lib/validators/code-budget/index.js +9 -0
- package/src/lib/validators/reuse/reuse-validator.js +4 -2
- package/src/lib/validators/reuse/symbol-scanner.js +27 -22
- package/src/lib/validators/validation-runner.js +21 -5
- package/src/lib/validators/verify-runner.js +11 -0
- package/src/lib/worktree/finish-backup.js +739 -0
- package/src/lib/worktree/ignored-triage.js +15 -8
- package/src/lib/worktree/worktree-audit.js +5 -0
- package/src/scripts/install-plugin.js +1 -1
- package/src/scripts/setup-infra.js +31 -22
- package/src/utils/agents-installer.js +338 -24
- package/src/utils/banner.js +1 -1
- package/src/utils/hooks-installer.js +75 -15
- package/src/commands/index.js +0 -13
- package/src/commands/project/checkpoint.js +0 -209
- package/src/core/state/phase-state-machine.js +0 -197
- package/src/dashboard/app/hooks/useDetailPanel.js +0 -15
- package/src/dashboard/app/pages/LiveState.jsx +0 -206
- package/src/dashboard/scanner/squads-scanner.js +0 -13
- package/src/dashboard/scanner/state-scanner.js +0 -85
- package/src/lib/generators/index.js +0 -10
- package/src/lib/hooks/hook-executor.js +0 -167
- package/src/lib/validators/checkpoint-persistence.js +0 -39
- package/src/lib/validators/shared/emit-validator-dispatch.js +0 -14
- package/src/lib/validators/shared/index.js +0 -12
- package/src/lib/validators/shared/issue-counter.js +0 -14
- package/src/lib/validators/shared/result-formatter.js +0 -124
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { useEffect, useRef, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Fetch a `/api/morph/*` resource and refetch it whenever the dev server
|
|
5
|
+
* reports that the project's `.morph/` changed in the given scope.
|
|
6
|
+
*
|
|
7
|
+
* `scope` matches the server's invalidation: 'features' (feature.json,
|
|
8
|
+
* tasks.json, artifacts) or 'activity' (events.jsonl). A refetch keeps the old
|
|
9
|
+
* data on screen until the new one arrives — no flash of "loading" while an
|
|
10
|
+
* agent is working.
|
|
11
|
+
*
|
|
12
|
+
* @param {string|null} url - null skips the request
|
|
13
|
+
* @param {{ scope?: string|string[], project?: string }} [opts] - `scope`: 'features',
|
|
14
|
+
* 'activity' or 'projects'; `project`: only changes of this project refetch
|
|
15
|
+
* @returns {{ data: any, error: string|null, loading: boolean, refreshedAt: number|null, refetch: () => void }}
|
|
16
|
+
*/
|
|
17
|
+
export function useMorphQuery(url, { scope = 'features', project } = {}) {
|
|
18
|
+
const [state, setState] = useState({ data: null, error: null, loading: Boolean(url), refreshedAt: null });
|
|
19
|
+
const [tick, setTick] = useState(0);
|
|
20
|
+
const urlRef = useRef(url);
|
|
21
|
+
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
if (urlRef.current !== url) {
|
|
24
|
+
urlRef.current = url;
|
|
25
|
+
setState({ data: null, error: null, loading: Boolean(url), refreshedAt: null });
|
|
26
|
+
}
|
|
27
|
+
if (!url) return undefined;
|
|
28
|
+
let cancelled = false;
|
|
29
|
+
fetch(url)
|
|
30
|
+
.then(async (res) => {
|
|
31
|
+
const body = await res.json().catch(() => ({}));
|
|
32
|
+
if (!res.ok) throw new Error(body.error || `HTTP ${res.status}`);
|
|
33
|
+
return body;
|
|
34
|
+
})
|
|
35
|
+
.then((data) => { if (!cancelled) setState({ data, error: null, loading: false, refreshedAt: Date.now() }); })
|
|
36
|
+
.catch((err) => { if (!cancelled) setState((s) => ({ ...s, error: err.message, loading: false })); });
|
|
37
|
+
return () => { cancelled = true; };
|
|
38
|
+
}, [url, tick]);
|
|
39
|
+
|
|
40
|
+
const scopes = Array.isArray(scope) ? scope.join(',') : scope;
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
const hot = import.meta.hot;
|
|
43
|
+
if (!hot) return undefined;
|
|
44
|
+
const wanted = scopes.split(',');
|
|
45
|
+
const onChange = (payload) => {
|
|
46
|
+
if (payload?.project && project && payload.project !== project) return;
|
|
47
|
+
if (!payload?.scope || wanted.includes(payload.scope)) setTick((t) => t + 1);
|
|
48
|
+
};
|
|
49
|
+
hot.on('morph:changed', onChange);
|
|
50
|
+
return () => hot.off?.('morph:changed', onChange);
|
|
51
|
+
}, [scopes, project]);
|
|
52
|
+
|
|
53
|
+
return { ...state, refetch: () => setTick((t) => t + 1) };
|
|
54
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { useCallback, useEffect, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
const PREFIX = 'morph-dashboard:';
|
|
4
|
+
|
|
5
|
+
function read(key, fallback) {
|
|
6
|
+
try {
|
|
7
|
+
const raw = window.localStorage.getItem(PREFIX + key);
|
|
8
|
+
return raw === null ? fallback : JSON.parse(raw);
|
|
9
|
+
} catch {
|
|
10
|
+
return fallback;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* A per-browser preference (view mode, filters). Storage can be unavailable or
|
|
16
|
+
* throw (private mode, SSR); the value then simply does not persist.
|
|
17
|
+
*/
|
|
18
|
+
export function usePref(key, fallback) {
|
|
19
|
+
const [value, setValue] = useState(() => (typeof window === 'undefined' ? fallback : read(key, fallback)));
|
|
20
|
+
const set = useCallback((next) => {
|
|
21
|
+
setValue((prev) => {
|
|
22
|
+
const v = typeof next === 'function' ? next(prev) : next;
|
|
23
|
+
try { window.localStorage.setItem(PREFIX + key, JSON.stringify(v)); } catch { /* not persisted */ }
|
|
24
|
+
return v;
|
|
25
|
+
});
|
|
26
|
+
}, [key]);
|
|
27
|
+
return [value, set];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Re-render every `ms` so relative times ("há 30 s") and "working now" stay true. */
|
|
31
|
+
export function useNow(ms = 15_000) {
|
|
32
|
+
const [now, setNow] = useState(() => Date.now());
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
const id = setInterval(() => setNow(Date.now()), ms);
|
|
35
|
+
return () => clearInterval(id);
|
|
36
|
+
}, [ms]);
|
|
37
|
+
return now;
|
|
38
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
const rtf = new Intl.RelativeTimeFormat('pt-BR', { numeric: 'auto' });
|
|
2
|
+
const UNITS = [
|
|
3
|
+
['year', 365 * 24 * 3600],
|
|
4
|
+
['month', 30 * 24 * 3600],
|
|
5
|
+
['week', 7 * 24 * 3600],
|
|
6
|
+
['day', 24 * 3600],
|
|
7
|
+
['hour', 3600],
|
|
8
|
+
['minute', 60],
|
|
9
|
+
];
|
|
10
|
+
|
|
11
|
+
/** "há 12 minutos" — or an em dash when there is no date. */
|
|
12
|
+
export function timeAgo(iso, now = Date.now()) {
|
|
13
|
+
const t = Date.parse(iso || '');
|
|
14
|
+
if (Number.isNaN(t)) return '—';
|
|
15
|
+
const diff = (t - now) / 1000;
|
|
16
|
+
for (const [unit, secs] of UNITS) {
|
|
17
|
+
if (Math.abs(diff) >= secs) return rtf.format(Math.round(diff / secs), unit);
|
|
18
|
+
}
|
|
19
|
+
return 'agora';
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function dateTime(iso) {
|
|
23
|
+
const t = Date.parse(iso || '');
|
|
24
|
+
if (Number.isNaN(t)) return '—';
|
|
25
|
+
return new Date(t).toLocaleString('pt-BR', { dateStyle: 'short', timeStyle: 'short' });
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function clock(iso) {
|
|
29
|
+
const t = Date.parse(iso || '');
|
|
30
|
+
if (Number.isNaN(t)) return '—';
|
|
31
|
+
return new Date(t).toLocaleTimeString('pt-BR', { hour: '2-digit', minute: '2-digit' });
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function dayLabel(iso) {
|
|
35
|
+
const t = Date.parse(iso || '');
|
|
36
|
+
if (Number.isNaN(t)) return '—';
|
|
37
|
+
return new Date(t).toLocaleDateString('pt-BR', { weekday: 'long', day: 'numeric', month: 'long' });
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const compact = new Intl.NumberFormat('pt-BR', { notation: 'compact', maximumFractionDigits: 1 });
|
|
41
|
+
export function num(n) {
|
|
42
|
+
return typeof n === 'number' && Number.isFinite(n) ? compact.format(n) : '—';
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function bytes(n) {
|
|
46
|
+
if (typeof n !== 'number' || !Number.isFinite(n)) return '—';
|
|
47
|
+
if (n < 1024) return `${n} B`;
|
|
48
|
+
if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)} KB`;
|
|
49
|
+
return `${(n / 1024 / 1024).toFixed(1)} MB`;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function duration(seconds) {
|
|
53
|
+
if (typeof seconds !== 'number' || !Number.isFinite(seconds) || seconds <= 0) return '—';
|
|
54
|
+
const m = Math.round(seconds / 60);
|
|
55
|
+
if (m < 60) return `${m} min`;
|
|
56
|
+
return `${Math.floor(m / 60)} h ${m % 60} min`;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Strip a leading YAML frontmatter block, returning both parts. */
|
|
60
|
+
export function splitFrontmatter(text) {
|
|
61
|
+
const m = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/.exec(text || '');
|
|
62
|
+
if (!m) return { meta: null, body: text || '' };
|
|
63
|
+
const meta = {};
|
|
64
|
+
for (const line of m[1].split(/\r?\n/)) {
|
|
65
|
+
const kv = /^([\w-]+):\s*(.*)$/.exec(line);
|
|
66
|
+
if (kv) meta[kv[1]] = kv[2].replace(/^["']|["']$/g, '');
|
|
67
|
+
}
|
|
68
|
+
return { meta, body: text.slice(m[0].length) };
|
|
69
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Display vocabulary for the phase pipeline. The ORDER and the optional flag
|
|
3
|
+
* come from framework/phases.json (morphData.phases); this file only adds the
|
|
4
|
+
* words and the colour token each phase is shown with.
|
|
5
|
+
*/
|
|
6
|
+
import morphData from 'virtual:morph-data';
|
|
7
|
+
|
|
8
|
+
const LABELS = {
|
|
9
|
+
proposal: 'Proposta',
|
|
10
|
+
uiux: 'UI/UX',
|
|
11
|
+
plan: 'Plano',
|
|
12
|
+
implement: 'Implementação',
|
|
13
|
+
review: 'Revisão',
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export const PHASES = (morphData.phases?.list || []).map((p) => ({
|
|
17
|
+
id: p.id,
|
|
18
|
+
label: LABELS[p.id] || p.displayName || p.id,
|
|
19
|
+
optional: Boolean(p.optional),
|
|
20
|
+
folder: p.folder || null,
|
|
21
|
+
skill: p.skill || null,
|
|
22
|
+
color: `var(--color-phase-${p.id})`,
|
|
23
|
+
}));
|
|
24
|
+
|
|
25
|
+
export const PHASE_INDEX = Object.fromEntries(PHASES.map((p, i) => [p.id, i]));
|
|
26
|
+
|
|
27
|
+
export function phaseMeta(id) {
|
|
28
|
+
return PHASES.find((p) => p.id === id) || { id, label: LABELS[id] || id || '—', color: 'var(--color-faint)', optional: false };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export const GATE_LABEL = {
|
|
32
|
+
proposal: 'Gate 1 (proposta)',
|
|
33
|
+
uiux: 'Gate UI/UX',
|
|
34
|
+
plan: 'Gate 2 (plano)',
|
|
35
|
+
review: 'Gate 3 (revisão)',
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export const STATUS_LABEL = {
|
|
39
|
+
draft: 'Rascunho',
|
|
40
|
+
in_progress: 'Em andamento',
|
|
41
|
+
completed: 'Concluída',
|
|
42
|
+
abandoned: 'Abandonada',
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export const WORKTYPE_LABEL = {
|
|
46
|
+
feature: 'feature',
|
|
47
|
+
bug: 'bug',
|
|
48
|
+
chore: 'chore',
|
|
49
|
+
hotfix: 'hotfix',
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export const LOCATION_LABEL = {
|
|
53
|
+
root: 'raiz',
|
|
54
|
+
worktree: 'worktree',
|
|
55
|
+
archive: 'arquivo',
|
|
56
|
+
};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { useEffect, useRef } from 'react';
|
|
2
|
+
import { useLocation } from 'react-router-dom';
|
|
3
|
+
import { usePref } from '../hooks/usePref.js';
|
|
4
|
+
import { useMorphQuery } from '../hooks/useMorphQuery.js';
|
|
5
|
+
|
|
6
|
+
/** The live API of one project. */
|
|
7
|
+
export const apiBase = (id) => `/api/morph/p/${encodeURIComponent(id)}`;
|
|
8
|
+
|
|
9
|
+
/** A page of one project: `/p/{id}/features`, `/p/{id}/features/{name}`, `/p/{id}/activity`. */
|
|
10
|
+
export const projectHref = (id, sub = 'features') => `/p/${encodeURIComponent(id)}/${sub}`;
|
|
11
|
+
|
|
12
|
+
export const featureHref = (project, name, query = '') =>
|
|
13
|
+
projectHref(project, `features/${encodeURIComponent(name)}`) + query;
|
|
14
|
+
|
|
15
|
+
const PROJECTS_CHANGED = 'morph:projects-changed';
|
|
16
|
+
|
|
17
|
+
/** Tell every reader of the portfolio (page, sidebar, search) to read it again. */
|
|
18
|
+
export const announceProjectsChanged = () => window.dispatchEvent(new Event(PROJECTS_CHANGED));
|
|
19
|
+
|
|
20
|
+
/** The portfolio: registered projects, their summaries, and the ones found next to them. */
|
|
21
|
+
export function useProjects() {
|
|
22
|
+
const q = useMorphQuery('/api/morph/projects', { scope: 'projects' });
|
|
23
|
+
const refetch = useRef(q.refetch);
|
|
24
|
+
refetch.current = q.refetch;
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
const on = () => refetch.current();
|
|
27
|
+
window.addEventListener(PROJECTS_CHANGED, on);
|
|
28
|
+
return () => window.removeEventListener(PROJECTS_CHANGED, on);
|
|
29
|
+
}, []);
|
|
30
|
+
return q;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* The project the person is looking at: the one in the URL, else the last one
|
|
35
|
+
* they opened, else the one the dashboard was launched from. Knowledge pages
|
|
36
|
+
* have no project in the URL but the sidebar still needs one to link to.
|
|
37
|
+
*
|
|
38
|
+
* @param {{ launch?: string|null, projects?: Array<{id: string}> }|null} portfolio
|
|
39
|
+
* @returns {{ id: string|null, inUrl: boolean }}
|
|
40
|
+
*/
|
|
41
|
+
export function useCurrentProject(portfolio) {
|
|
42
|
+
const { pathname } = useLocation();
|
|
43
|
+
const m = /^\/p\/([^/]+)/.exec(pathname);
|
|
44
|
+
const fromUrl = m ? decodeURIComponent(m[1]) : null;
|
|
45
|
+
const [last, setLast] = usePref('project.last', null);
|
|
46
|
+
useEffect(() => { if (fromUrl && fromUrl !== last) setLast(fromUrl); }, [fromUrl, last, setLast]);
|
|
47
|
+
const known = (id) => id && (!portfolio?.projects || portfolio.projects.some((p) => p.id === id));
|
|
48
|
+
const id = fromUrl || (known(last) ? last : null) || portfolio?.launch || null;
|
|
49
|
+
return { id, inUrl: Boolean(fromUrl) };
|
|
50
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { useParams } from 'react-router-dom';
|
|
2
|
+
import ActivityFeed from '../components/ActivityFeed.jsx';
|
|
3
|
+
import { useProjects } from '../lib/project.js';
|
|
4
|
+
|
|
5
|
+
export default function Activity() {
|
|
6
|
+
const { project } = useParams();
|
|
7
|
+
const { data } = useProjects();
|
|
8
|
+
const name = data?.projects?.find((p) => p.id === project)?.name;
|
|
9
|
+
return (
|
|
10
|
+
<div className="mx-auto max-w-4xl px-4 pb-24 pt-10 md:px-10 md:pt-16">
|
|
11
|
+
<header className="rise mb-8">
|
|
12
|
+
{name && <p className="text-sm text-[var(--color-dim)]">{name}</p>}
|
|
13
|
+
<h1 className="mt-1 text-4xl font-semibold tracking-tight">Atividade</h1>
|
|
14
|
+
<p className="mt-3 max-w-2xl text-sm leading-relaxed text-[var(--color-dim)]">
|
|
15
|
+
O que o Claude fez no projeto, contado a partir da telemetria dos hooks: suas mensagens, as skills carregadas,
|
|
16
|
+
os sub-agents despachados, as tasks, os verifies e os gates. Atualiza sozinho enquanto ele trabalha.
|
|
17
|
+
</p>
|
|
18
|
+
</header>
|
|
19
|
+
<div className="rise-2 bezel">
|
|
20
|
+
<div className="bezel-core px-5 py-5"><ActivityFeed project={project} showFeature /></div>
|
|
21
|
+
</div>
|
|
22
|
+
</div>
|
|
23
|
+
);
|
|
24
|
+
}
|