@polymorphism-tech/morph-spec 9.0.0 → 9.2.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 +8 -9
- package/README.md +38 -36
- package/bin/morph-spec.js +11 -63
- package/bin/validate.js +1 -39
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +1 -1
- package/docs/QUICKSTART.md +1 -1
- package/framework/CLAUDE.md +2 -1
- package/framework/MORPH.md +3 -3
- package/framework/agents.json +19 -11
- package/framework/commands/morph-apply.md +1 -1
- package/framework/commands/morph-status.md +1 -1
- package/framework/docs/CLI.md +22 -31
- package/framework/evals/review-checklist.md +12 -2
- package/framework/hooks/README.md +8 -44
- package/framework/hooks/claude-code/core/block-destructive-worktree-removal.js +6 -49
- package/framework/hooks/claude-code/core/block-raw-worktree-add.js +2 -35
- package/framework/hooks/claude-code/core/chain.js +4 -65
- package/framework/hooks/claude-code/core/comment-guard.js +2 -16
- package/framework/hooks/claude-code/core/enforce-phase-writes.js +6 -50
- package/framework/hooks/claude-code/core/gate-guard.js +7 -67
- package/framework/hooks/claude-code/core/graph-refresh-kick.js +3 -24
- package/framework/hooks/claude-code/core/heavy-gate.js +3 -21
- package/framework/hooks/claude-code/core/heavy-release.js +2 -13
- package/framework/hooks/claude-code/core/loop-detect.js +2 -31
- package/framework/hooks/claude-code/core/pre-bash-denylist.js +0 -4
- package/framework/hooks/claude-code/core/protect-spec-files.js +7 -67
- package/framework/hooks/claude-code/core/state-sync.js +12 -68
- package/framework/hooks/claude-code/core/telemetry-log.js +12 -93
- package/framework/hooks/claude-code/core/validate-completion.js +11 -57
- package/framework/hooks/claude-code/core/warn-bash-source-write.js +1 -9
- package/framework/hooks/claude-code/core/worktree-context.js +10 -110
- package/framework/hooks/claude-code/core/worktree-create.js +6 -70
- package/framework/hooks/claude-code/core/worktree-env-inject.js +5 -56
- package/framework/hooks/claude-code/core/worktree-remove.js +5 -31
- package/framework/hooks/claude-code/statusline.sh +11 -11
- package/framework/hooks/shared/activity-logger.js +9 -89
- package/framework/hooks/shared/agent-reap.js +2 -17
- package/framework/hooks/shared/build-check.js +6 -75
- package/framework/hooks/shared/claim-registry.js +9 -119
- package/framework/hooks/shared/command-tokens.js +6 -51
- package/framework/hooks/shared/comment-rules.js +5 -20
- package/framework/hooks/shared/comment-scanner.js +6 -25
- package/framework/hooks/shared/comment-stripper.js +11 -85
- package/framework/hooks/shared/e2e-check.js +0 -0
- package/framework/hooks/shared/errata.js +2 -15
- package/framework/hooks/shared/feature-locator.js +11 -95
- package/framework/hooks/shared/frontend-locations.js +1 -3
- package/framework/hooks/shared/gate-question.js +1 -7
- package/framework/hooks/shared/git-exclude.js +3 -20
- package/framework/hooks/shared/git-paths.js +8 -104
- package/framework/hooks/shared/graph-freshness.js +22 -166
- package/framework/hooks/shared/graph-refresh.js +21 -149
- package/framework/hooks/shared/heavy-command.js +4 -12
- package/framework/hooks/shared/hook-response.js +8 -83
- package/framework/hooks/shared/morph-cli.js +8 -52
- package/framework/hooks/shared/payload-utils.js +10 -132
- package/framework/hooks/shared/phase-utils.js +1 -42
- package/framework/hooks/shared/redact-secrets.js +4 -15
- package/framework/hooks/shared/reparse-scan.js +6 -66
- package/framework/hooks/shared/slice-progress.js +4 -23
- package/framework/hooks/shared/source-files.js +4 -17
- package/framework/hooks/shared/state-reader.js +13 -183
- package/framework/hooks/shared/state-writer.js +8 -52
- package/framework/hooks/shared/stdin-reader.js +1 -11
- package/framework/hooks/shared/telemetry-logger.js +7 -67
- package/framework/hooks/shared/transcript-usage.js +7 -29
- package/framework/hooks/shared/worktree-root.js +7 -29
- package/framework/rules/csharp-standards.md +1 -1
- package/framework/rules/frontend-standards.md +1 -0
- package/framework/rules/hangfire-standards.md +1 -1
- package/framework/rules/neon-standards.md +1 -1
- package/framework/rules/nextjs-standards.md +1 -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 +4 -4
- package/framework/skills/core/morph-hotfix/SKILL.md +3 -3
- package/framework/skills/core/morph-machine/SKILL.md +1 -1
- package/framework/skills/core/morph-media/SKILL.md +1 -1
- package/framework/skills/core/morph-plan/SKILL.md +9 -7
- package/framework/skills/core/morph-review/SKILL.md +9 -6
- package/framework/skills/core/morph-standards/SKILL.md +1 -1
- package/framework/skills/core/morph-uiux/SKILL.md +29 -12
- package/framework/standards/STANDARDS.json +36 -14
- package/framework/standards/STANDARDS.meta.json +16 -2
- package/framework/standards/ai-agents/agent-archetypes.md +2 -2
- package/framework/standards/ai-agents/agent-session.md +1 -1
- package/framework/standards/ai-agents/conversational-agent-with-phases.md +4 -4
- package/framework/standards/ai-agents/durable-workflows-hitl.md +3 -3
- package/framework/standards/ai-agents/guardrails.md +1 -1
- package/framework/standards/ai-agents/multi-agent-patterns.md +2 -2
- package/framework/standards/ai-agents/observability-patterns.md +1 -1
- package/framework/standards/ai-agents/production.md +1 -1
- package/framework/standards/ai-agents/prompt-sources.md +2 -2
- package/framework/standards/ai-agents/providers/model-registry.md +1 -1
- package/framework/standards/ai-agents/rag-custom-pgvector.md +2 -2
- package/framework/standards/ai-agents/setup.md +2 -2
- package/framework/standards/ai-agents/sweet-spot.md +2 -2
- package/framework/standards/ai-agents/tool-adapters.md +0 -2
- package/framework/standards/ai-agents/vector-data-extensions.md +4 -4
- package/framework/standards/ai-agents/workflows.md +1 -1
- package/framework/standards/architecture/code-comments.md +1 -1
- package/framework/standards/architecture/vertical-slice/vertical-slice.md +1 -1
- package/framework/standards/backend/api/validation.md +1 -1
- package/framework/standards/backend/dotnet/testing.md +10 -4
- package/framework/standards/backend/dotnet/vsa-handler-patterns.md +5 -5
- package/framework/standards/backend/integrations/hangfire/hangfire-jobs.md +1 -1
- package/framework/standards/backend/integrations/neon-auth/neon-auth.md +2 -2
- package/framework/standards/data/nosql/cache/redis.md +1 -1
- package/framework/standards/data/nosql/cosmos-db.md +3 -3
- package/framework/standards/frontend/admin-app/forms.md +1 -1
- package/framework/standards/frontend/design-system/art-direction.md +88 -0
- package/framework/standards/frontend/design-system/premium-finish.md +77 -2
- package/framework/standards/frontend/nextjs/testing.md +10 -6
- package/framework/standards/infrastructure/neon/neon-pgvector.md +5 -5
- package/framework/templates/.idea/morph-templates.xml +92 -0
- package/framework/templates/.vscode/morph-templates.code-snippets +186 -0
- package/framework/templates/CLAUDE.md.core.template +3 -3
- package/framework/templates/IDE-SNIPPETS.md +262 -262
- package/framework/templates/REGISTRY.json +9 -9
- package/framework/templates/code/dotnet/abstractions/api-endpoint.cs +7 -7
- 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/sql/ai-agents/pgvector-embeddings-migration.sql.template +91 -91
- package/framework/templates/docs/plan.md +3 -1
- package/framework/templates/docs/ui-mockups.md +4 -0
- package/framework/templates/frontend/nextjs/client-component.tsx.hbs +26 -26
- package/framework/templates/frontend/nextjs/feature-form.tsx.hbs +56 -56
- package/framework/templates/frontend/nextjs/tsconfig.json.hbs +26 -26
- 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/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/meta-prompts/fusion/fusion-agent.md +76 -76
- package/framework/templates/meta-prompts/fusion/fusion-aggregator.md +100 -100
- package/framework/templates/state.template.json +17 -17
- package/framework/templates/ui/design-system.css +226 -226
- package/package.json +1 -1
- package/src/commands/context/context-refresh.js +0 -16
- package/src/commands/dashboard/dashboard.js +2 -8
- package/src/commands/e2e/e2e.js +17 -104
- package/src/commands/graph/affected.js +0 -14
- package/src/commands/graph/build.js +11 -50
- package/src/commands/graph/explain.js +0 -16
- package/src/commands/graph/status.js +1 -12
- package/src/commands/mcp/mcp-setup.js +4 -62
- package/src/commands/observability/cost.js +5 -57
- package/src/commands/observability/telemetry.js +1 -14
- package/src/commands/project/branch.js +4 -45
- package/src/commands/project/claim.js +4 -48
- package/src/commands/project/doctor.js +48 -302
- package/src/commands/project/env.js +1 -27
- package/src/commands/project/fleet.js +3 -32
- package/src/commands/project/init.js +4 -68
- package/src/commands/project/install-plugin-cmd.js +0 -1
- package/src/commands/project/standards.js +1 -62
- package/src/commands/project/status.js +0 -53
- package/src/commands/project/update.js +31 -239
- package/src/commands/project/worktree.js +108 -882
- package/src/commands/scaffold/scaffold.js +0 -25
- package/src/commands/state/advance.js +14 -85
- package/src/commands/state/approve.js +15 -97
- package/src/commands/state/archive.js +12 -64
- package/src/commands/state/auto-approval.js +2 -5
- package/src/commands/state/create.js +22 -109
- package/src/commands/state/delete.js +11 -63
- package/src/commands/state/finish.js +98 -475
- package/src/commands/state/gate-check.js +22 -132
- package/src/commands/state/gate-decision.js +3 -39
- package/src/commands/state/handoff.js +3 -30
- package/src/commands/state/list.js +14 -84
- package/src/commands/state/report-check.js +8 -58
- package/src/commands/state/retype.js +4 -26
- package/src/commands/validation/index.js +0 -3
- package/src/commands/validation/mutate.js +11 -11
- package/src/commands/validation/plan-check.js +4 -2
- package/src/commands/validation/tests-audit.js +7 -0
- package/src/commands/validation/validate-feature.js +10 -97
- package/src/commands/validation/verify.js +30 -113
- package/src/core/paths/output-schema.js +2 -101
- package/src/core/state/feature-identity.js +1 -25
- package/src/core/state/gate-order.js +0 -27
- package/src/core/state/lifecycle-audit.js +8 -100
- package/src/core/state/state-manager.js +22 -304
- package/src/core/templates/template-registry.js +2 -97
- package/src/core/templates/template-renderer.js +13 -93
- package/src/core/templates/template-seed.js +0 -1
- package/src/core/work-types.js +1 -36
- package/src/dashboard/app/App.jsx +1 -8
- package/src/dashboard/app/components/ActivityFeed.jsx +0 -5
- package/src/dashboard/app/components/CommandPalette.jsx +0 -7
- package/src/dashboard/app/components/DetailPanel.jsx +1 -5
- package/src/dashboard/app/components/EntityLink.jsx +0 -12
- package/src/dashboard/app/components/ErrorBoundary.jsx +1 -8
- package/src/dashboard/app/components/KnowledgeMarkdown.jsx +0 -11
- package/src/dashboard/app/components/Markdown.jsx +0 -9
- package/src/dashboard/app/components/PhaseRail.jsx +0 -11
- package/src/dashboard/app/components/SearchFilter.jsx +0 -4
- package/src/dashboard/app/components/Sidebar.jsx +0 -5
- package/src/dashboard/app/hooks/useMorphQuery.js +1 -11
- package/src/dashboard/app/hooks/usePref.js +1 -5
- package/src/dashboard/app/lib/format.js +0 -2
- package/src/dashboard/app/lib/phases.js +1 -5
- package/src/dashboard/app/lib/project.js +0 -12
- package/src/dashboard/app/main.jsx +13 -13
- package/src/dashboard/app/pages/AgentMap.jsx +1 -8
- package/src/dashboard/app/pages/FeatureDetail.jsx +1 -5
- package/src/dashboard/app/pages/Features.jsx +1 -8
- package/src/dashboard/app/pages/Guide.jsx +0 -1
- package/src/dashboard/app/pages/HookInspector.jsx +2 -23
- package/src/dashboard/app/pages/PhasePipeline.jsx +0 -3
- package/src/dashboard/app/pages/Portfolio.jsx +0 -1
- package/src/dashboard/app/pages/RulesMap.jsx +1 -16
- package/src/dashboard/app/pages/SkillsLibrary.jsx +1 -16
- package/src/dashboard/app/pages/Standards.jsx +2 -17
- package/src/dashboard/scanner/activity-scanner.js +10 -69
- package/src/dashboard/scanner/agents-scanner.js +1 -23
- package/src/dashboard/scanner/docs-scanner.js +0 -6
- package/src/dashboard/scanner/feature-status.js +2 -5
- package/src/dashboard/scanner/features-scanner.js +10 -66
- package/src/dashboard/scanner/hooks-scanner.js +5 -28
- package/src/dashboard/scanner/index.js +0 -4
- package/src/dashboard/scanner/portfolio.js +5 -29
- package/src/dashboard/scanner/project-api.js +8 -54
- package/src/dashboard/scanner/rules-scanner.js +1 -2
- package/src/dashboard/scanner/skills-scanner.js +2 -12
- package/src/dashboard/scanner/standards-scanner.js +2 -6
- package/src/dashboard/vite-plugin-morph-data.js +5 -19
- package/src/dashboard/vite.config.js +24 -24
- package/src/lib/ai/ai-checks.js +7 -52
- package/src/lib/ai/ai-project-scan.js +14 -108
- package/src/lib/ai/code-audit.js +7 -63
- package/src/lib/ai/pin-drift.js +8 -51
- package/src/lib/ai/registry-audit.js +11 -90
- package/src/lib/ci/ci-verify.js +4 -19
- package/src/lib/ci/github-ci.js +9 -37
- package/src/lib/ci/workflow-scaffold.js +14 -10
- package/src/lib/detectors/claude-config-detector.js +13 -74
- package/src/lib/detectors/index.js +4 -50
- package/src/lib/e2e/applicability.js +37 -0
- package/src/lib/e2e/compose-env.js +5 -66
- package/src/lib/e2e/stack-teardown.js +6 -40
- package/src/lib/generators/architecture-scanner.js +18 -156
- package/src/lib/generators/handoff-generator.js +9 -53
- package/src/lib/generators/scaffold-generator.js +5 -101
- package/src/lib/generators/settings-generator.js +4 -37
- package/src/lib/git/branch-classifier.js +11 -84
- package/src/lib/git/default-branch.js +14 -169
- package/src/lib/git/evidence-staleness.js +1 -0
- package/src/lib/git/gh.js +2 -17
- package/src/lib/git/pending-prs.js +6 -37
- package/src/lib/git/pr-files.js +3 -28
- package/src/lib/git/pr-index.js +3 -34
- package/src/lib/git/tree-fingerprint.js +3 -15
- package/src/lib/git/tree-state.js +6 -33
- package/src/lib/graph/god-nodes.js +2 -22
- package/src/lib/graph/graph-health.js +3 -24
- package/src/lib/graph/graph-loader.js +5 -56
- package/src/lib/graph/graphify-runtime.js +7 -37
- package/src/lib/graph/graphifyignore.js +8 -74
- package/src/lib/graph/sanitize.js +16 -136
- package/src/lib/heavy/cleanup.js +1 -1
- package/src/lib/installers/effects.js +8 -93
- package/src/lib/installers/mcp-installer.js +9 -169
- package/src/lib/installers/memory-installer.js +4 -27
- package/src/lib/installers/templates-reconcile.js +5 -60
- package/src/lib/mutation/adapters.js +6 -17
- package/src/lib/mutation/dotnet-introspection.js +20 -106
- package/src/lib/mutation/runner.js +23 -75
- package/src/lib/mutation/subset.js +1 -4
- package/src/lib/mutation/trx.js +0 -1
- package/src/lib/plan/artifact-vocabulary.js +5 -25
- package/src/lib/plan/checks.js +34 -33
- package/src/lib/plan/plan-parser.js +7 -8
- package/src/lib/projects/registry.js +7 -44
- package/src/lib/review-reports.js +11 -26
- package/src/lib/risk-detector.js +41 -173
- package/src/lib/specs/adr-index.js +6 -34
- package/src/lib/specs/frontmatter.js +2 -15
- package/src/lib/specs/gfm-table.js +1 -7
- package/src/lib/specs/revocations.js +1 -14
- package/src/lib/specs/screen-checklist.js +47 -124
- package/src/lib/stack/dotnet-build-ignores.js +2 -6
- package/src/lib/stack/stack-profile.js +1 -28
- package/src/lib/stack/stack-scan.js +1 -8
- package/src/lib/stack-filter.js +4 -27
- package/src/lib/standards/registry.js +1 -7
- package/src/lib/standards/sections.js +2 -13
- package/src/lib/standards/verified-against.js +19 -191
- package/src/lib/tasks/eval-node.js +21 -169
- package/src/lib/tasks/linux-runner.js +18 -142
- package/src/lib/tasks/test-nodes.js +43 -240
- package/src/lib/tasks/test-runner.js +43 -341
- package/src/lib/telemetry/metrics.js +7 -36
- package/src/lib/telemetry/telemetry-reader.js +13 -125
- package/src/lib/telemetry/verify-event.js +2 -30
- package/src/lib/tests-audit/audit.js +96 -0
- package/src/lib/validators/code-budget/code-budget-validator.js +4 -44
- package/src/lib/validators/code-budget/index.js +0 -7
- package/src/lib/validators/contracts/contract-compliance-validator.js +2 -24
- package/src/lib/validators/contracts/index.js +0 -6
- package/src/lib/validators/nextjs/index.js +0 -5
- package/src/lib/validators/nextjs/next-component-validator.js +4 -81
- package/src/lib/validators/packages/index.js +0 -6
- package/src/lib/validators/packages/package-validator.js +6 -56
- package/src/lib/validators/reuse/index.js +0 -8
- package/src/lib/validators/reuse/reuse-validator.js +11 -101
- package/src/lib/validators/reuse/symbol-scanner.js +6 -62
- package/src/lib/validators/traceability-check.js +5 -63
- package/src/lib/validators/validation-runner.js +21 -158
- package/src/lib/validators/verify-runner.js +85 -376
- package/src/lib/work-type-detector.js +5 -67
- package/src/lib/worktree/docker-audit.js +17 -136
- package/src/lib/worktree/env-allocator.js +10 -98
- package/src/lib/worktree/feature-truth.js +12 -115
- package/src/lib/worktree/finish-backup.js +32 -242
- package/src/lib/worktree/fleet-scanner.js +13 -95
- package/src/lib/worktree/ignored-triage.js +15 -91
- package/src/lib/worktree/vscode-settings.js +2 -3
- package/src/lib/worktree/worktree-audit.js +21 -213
- package/src/lib/worktree/worktree-include.js +3 -5
- package/src/scripts/global-install.js +3 -9
- package/src/scripts/install-plugin.js +1 -13
- package/src/scripts/setup-infra.js +17 -147
- package/src/utils/agents-installer.js +29 -276
- package/src/utils/banner.js +4 -25
- package/src/utils/claude-md-injector.js +1 -37
- package/src/utils/claude-settings-manager.js +1 -52
- package/src/utils/file-copier.js +11 -95
- package/src/utils/hooks-installer.js +21 -298
- package/src/utils/skills-installer.js +4 -101
- package/src/utils/version-checker.js +4 -83
- package/framework/commands/morph-proposal.md +0 -9
- package/framework/hooks/git/commit-msg/conventional-commits.sh +0 -33
- package/framework/hooks/git/pre-commit/agents.sh +0 -25
- package/framework/hooks/git/pre-commit/orchestrator.sh +0 -64
- package/framework/hooks/git/pre-push/run-tests.sh +0 -44
- package/src/commands/utils/index.js +0 -3
- package/src/lib/detectors/design-system-detector.js +0 -188
- package/src/lib/generators/design-system-generator.js +0 -508
- package/src/lib/specs/old-format.js +0 -68
- package/src/utils/agent-id-mapper.js +0 -11
|
@@ -2,9 +2,6 @@ import morphData from 'virtual:morph-data';
|
|
|
2
2
|
import Markdown from './Markdown.jsx';
|
|
3
3
|
import EntityLink, { standardKey } from './EntityLink.jsx';
|
|
4
4
|
|
|
5
|
-
/**
|
|
6
|
-
* Names of things that have a page. Built once from the knowledge module.
|
|
7
|
-
*/
|
|
8
5
|
const skillIds = new Set((morphData.skills || []).map((s) => s.id));
|
|
9
6
|
const personaIds = new Set((morphData.agents?.list || []).map((a) => a.id));
|
|
10
7
|
const standardByKey = new Map();
|
|
@@ -13,13 +10,6 @@ for (const s of morphData.standards?.list || []) {
|
|
|
13
10
|
standardByKey.set(s.path, s.id);
|
|
14
11
|
}
|
|
15
12
|
|
|
16
|
-
/**
|
|
17
|
-
* What an inline code span names, if it names something with a page:
|
|
18
|
-
* `morph-plan` or `/morph-plan` → skill, `morph-dotnet-senior` → persona,
|
|
19
|
-
* a standard id or path → standard.
|
|
20
|
-
*
|
|
21
|
-
* @returns {{ kind: string, id: string } | null}
|
|
22
|
-
*/
|
|
23
13
|
export function resolveMention(text) {
|
|
24
14
|
const t = String(text).trim();
|
|
25
15
|
const bare = t.replace(/^\//, '');
|
|
@@ -47,7 +37,6 @@ function InlineCode({ className, children, node, ...rest }) {
|
|
|
47
37
|
return <code className={className} {...rest}>{children}</code>;
|
|
48
38
|
}
|
|
49
39
|
|
|
50
|
-
/** Markdown for framework and feature text: code spans that name a skill, persona or standard become links. */
|
|
51
40
|
export default function KnowledgeMarkdown({ components, ...props }) {
|
|
52
41
|
return <Markdown {...props} components={{ code: InlineCode, ...components }} />;
|
|
53
42
|
}
|
|
@@ -6,7 +6,6 @@ import { splitFrontmatter } from '../lib/format.js';
|
|
|
6
6
|
const REMARK = [remarkGfm];
|
|
7
7
|
const REHYPE = [rehypeSlug];
|
|
8
8
|
|
|
9
|
-
/** Frontmatter keys worth showing, in the order a reader wants them. */
|
|
10
9
|
const META_ORDER = ['name', 'description', 'when_to_use', 'user-invocable', 'stacks', 'version', 'cliVersion'];
|
|
11
10
|
|
|
12
11
|
function MetaBlock({ meta }) {
|
|
@@ -26,14 +25,6 @@ function MetaBlock({ meta }) {
|
|
|
26
25
|
);
|
|
27
26
|
}
|
|
28
27
|
|
|
29
|
-
/**
|
|
30
|
-
* Rendered markdown for every long text in the app: feature artifacts, skills,
|
|
31
|
-
* standards, the charter. Frontmatter is lifted into a metadata block instead
|
|
32
|
-
* of being printed as text.
|
|
33
|
-
*
|
|
34
|
-
* @param {{ text: string, components?: object, className?: string, size?: 'md'|'sm' }} props
|
|
35
|
-
* `sm` is for side panels, where the reading column is narrower.
|
|
36
|
-
*/
|
|
37
28
|
export default function Markdown({ text, components, className = '', size = 'md' }) {
|
|
38
29
|
const { meta, body } = splitFrontmatter(text || '');
|
|
39
30
|
return (
|
|
@@ -1,16 +1,5 @@
|
|
|
1
1
|
import { PHASES, PHASE_INDEX, GATE_LABEL, phaseMeta } from '../lib/phases.js';
|
|
2
2
|
|
|
3
|
-
/**
|
|
4
|
-
* The phase rail: one segment per phase, a notch after each phase that ends in
|
|
5
|
-
* an approval gate. The same motif is used in the feature list (sm) and in the
|
|
6
|
-
* feature header (lg), so a feature's progress always reads the same way.
|
|
7
|
-
*
|
|
8
|
-
* Segment state is derived, never stored:
|
|
9
|
-
* done — before the current phase, or the whole feature is completed
|
|
10
|
-
* current — the derived phase of an active feature
|
|
11
|
-
* skipped — an optional phase (uiux) the feature went past without its gate
|
|
12
|
-
* todo — after the current phase
|
|
13
|
-
*/
|
|
14
3
|
export function segmentStates(feature) {
|
|
15
4
|
const idx = PHASE_INDEX[feature.phase] ?? 0;
|
|
16
5
|
const finished = feature.status === 'completed';
|
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
import { useState } from 'react';
|
|
2
2
|
import { MagnifyingGlass } from '@phosphor-icons/react';
|
|
3
3
|
|
|
4
|
-
/**
|
|
5
|
-
* The toolbar of the knowledge pages: a search box and a few selects. The
|
|
6
|
-
* `value` of each filter is controlled by the page; the search text is local.
|
|
7
|
-
*/
|
|
8
4
|
export default function SearchFilter({ placeholder = 'Buscar', filters = [], onSearchChange, onFilterChange, children }) {
|
|
9
5
|
const [search, setSearch] = useState('');
|
|
10
6
|
function handleSearchChange(e) {
|
|
@@ -30,10 +30,6 @@ const linkClass = ({ isActive }) => [
|
|
|
30
30
|
isActive ? 'bg-white/[0.08] text-[var(--color-fg)] shadow-[inset_0_1px_0_rgb(255_255_255_/_0.06)]' : 'text-[var(--color-dim)] hover:bg-white/[0.04] hover:text-[var(--color-fg)]',
|
|
31
31
|
].join(' ');
|
|
32
32
|
|
|
33
|
-
/**
|
|
34
|
-
* Switching project keeps the page: from a feature's detail it lands on the
|
|
35
|
-
* other project's feature list, from Activity on the other project's Activity.
|
|
36
|
-
*/
|
|
37
33
|
function ProjectSwitcher({ portfolio, current }) {
|
|
38
34
|
const navigate = useNavigate();
|
|
39
35
|
const { pathname } = useLocation();
|
|
@@ -122,7 +118,6 @@ export default function Sidebar({ portfolio, current, pulse, onSearch }) {
|
|
|
122
118
|
);
|
|
123
119
|
}
|
|
124
120
|
|
|
125
|
-
/** The same destinations as a compact bar for narrow screens. */
|
|
126
121
|
export function MobileNav({ current, onSearch }) {
|
|
127
122
|
const items = [
|
|
128
123
|
{ to: '/', label: 'Portfólio' },
|
|
@@ -1,18 +1,8 @@
|
|
|
1
1
|
import { useEffect, useRef, useState } from 'react';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
* reports that the project's `.morph/` changed in the given scope.
|
|
6
|
-
*
|
|
7
|
-
* `scope` matches the server's invalidation: 'features' (feature.json,
|
|
8
|
-
* slice reports, 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
|
-
*
|
|
4
|
+
* A refetch keeps the old data on screen until the new one arrives.
|
|
12
5
|
* @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
6
|
*/
|
|
17
7
|
export function useMorphQuery(url, { scope = 'features', project } = {}) {
|
|
18
8
|
const [state, setState] = useState({ data: null, error: null, loading: Boolean(url), refreshedAt: null });
|
|
@@ -11,10 +11,7 @@ function read(key, fallback) {
|
|
|
11
11
|
}
|
|
12
12
|
}
|
|
13
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
|
-
*/
|
|
14
|
+
/** Storage can be unavailable or throw (private mode, SSR); the value then does not persist. */
|
|
18
15
|
export function usePref(key, fallback) {
|
|
19
16
|
const [value, setValue] = useState(() => (typeof window === 'undefined' ? fallback : read(key, fallback)));
|
|
20
17
|
const set = useCallback((next) => {
|
|
@@ -27,7 +24,6 @@ export function usePref(key, fallback) {
|
|
|
27
24
|
return [value, set];
|
|
28
25
|
}
|
|
29
26
|
|
|
30
|
-
/** Re-render every `ms` so relative times ("há 30 s") and "working now" stay true. */
|
|
31
27
|
export function useNow(ms = 15_000) {
|
|
32
28
|
const [now, setNow] = useState(() => Date.now());
|
|
33
29
|
useEffect(() => {
|
|
@@ -8,7 +8,6 @@ const UNITS = [
|
|
|
8
8
|
['minute', 60],
|
|
9
9
|
];
|
|
10
10
|
|
|
11
|
-
/** "há 12 minutos" — or an em dash when there is no date. */
|
|
12
11
|
export function timeAgo(iso, now = Date.now()) {
|
|
13
12
|
const t = Date.parse(iso || '');
|
|
14
13
|
if (Number.isNaN(t)) return '—';
|
|
@@ -56,7 +55,6 @@ export function duration(seconds) {
|
|
|
56
55
|
return `${Math.floor(m / 60)} h ${m % 60} min`;
|
|
57
56
|
}
|
|
58
57
|
|
|
59
|
-
/** Strip a leading YAML frontmatter block, returning both parts. */
|
|
60
58
|
export function splitFrontmatter(text) {
|
|
61
59
|
const m = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/.exec(text || '');
|
|
62
60
|
if (!m) return { meta: null, body: text || '' };
|
|
@@ -1,8 +1,4 @@
|
|
|
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
|
-
*/
|
|
1
|
+
/** The ORDER and the optional flag come from framework/phases.json; this file only adds words and colours. */
|
|
6
2
|
import morphData from 'virtual:morph-data';
|
|
7
3
|
|
|
8
4
|
const LABELS = {
|
|
@@ -3,10 +3,8 @@ import { useLocation } from 'react-router-dom';
|
|
|
3
3
|
import { usePref } from '../hooks/usePref.js';
|
|
4
4
|
import { useMorphQuery } from '../hooks/useMorphQuery.js';
|
|
5
5
|
|
|
6
|
-
/** The live API of one project. */
|
|
7
6
|
export const apiBase = (id) => `/api/morph/p/${encodeURIComponent(id)}`;
|
|
8
7
|
|
|
9
|
-
/** A page of one project: `/p/{id}/features`, `/p/{id}/features/{name}`, `/p/{id}/activity`. */
|
|
10
8
|
export const projectHref = (id, sub = 'features') => `/p/${encodeURIComponent(id)}/${sub}`;
|
|
11
9
|
|
|
12
10
|
export const featureHref = (project, name, query = '') =>
|
|
@@ -14,10 +12,8 @@ export const featureHref = (project, name, query = '') =>
|
|
|
14
12
|
|
|
15
13
|
const PROJECTS_CHANGED = 'morph:projects-changed';
|
|
16
14
|
|
|
17
|
-
/** Tell every reader of the portfolio (page, sidebar, search) to read it again. */
|
|
18
15
|
export const announceProjectsChanged = () => window.dispatchEvent(new Event(PROJECTS_CHANGED));
|
|
19
16
|
|
|
20
|
-
/** The portfolio: registered projects, their summaries, and the ones found next to them. */
|
|
21
17
|
export function useProjects() {
|
|
22
18
|
const q = useMorphQuery('/api/morph/projects', { scope: 'projects' });
|
|
23
19
|
const refetch = useRef(q.refetch);
|
|
@@ -30,14 +26,6 @@ export function useProjects() {
|
|
|
30
26
|
return q;
|
|
31
27
|
}
|
|
32
28
|
|
|
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
29
|
export function useCurrentProject(portfolio) {
|
|
42
30
|
const { pathname } = useLocation();
|
|
43
31
|
const m = /^\/p\/([^/]+)/.exec(pathname);
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { StrictMode } from 'react';
|
|
2
|
-
import { createRoot } from 'react-dom/client';
|
|
3
|
-
import { BrowserRouter } from 'react-router-dom';
|
|
4
|
-
import App from './App.jsx';
|
|
5
|
-
import './styles/index.css';
|
|
6
|
-
|
|
7
|
-
createRoot(document.getElementById('root')).render(
|
|
8
|
-
<StrictMode>
|
|
9
|
-
<BrowserRouter>
|
|
10
|
-
<App />
|
|
11
|
-
</BrowserRouter>
|
|
12
|
-
</StrictMode>
|
|
13
|
-
);
|
|
1
|
+
import { StrictMode } from 'react';
|
|
2
|
+
import { createRoot } from 'react-dom/client';
|
|
3
|
+
import { BrowserRouter } from 'react-router-dom';
|
|
4
|
+
import App from './App.jsx';
|
|
5
|
+
import './styles/index.css';
|
|
6
|
+
|
|
7
|
+
createRoot(document.getElementById('root')).render(
|
|
8
|
+
<StrictMode>
|
|
9
|
+
<BrowserRouter>
|
|
10
|
+
<App />
|
|
11
|
+
</BrowserRouter>
|
|
12
|
+
</StrictMode>
|
|
13
|
+
);
|
|
@@ -10,11 +10,7 @@ import Markdown from '../components/Markdown.jsx';
|
|
|
10
10
|
import EntityLink, { useSelection } from '../components/EntityLink.jsx';
|
|
11
11
|
import { phaseMeta } from '../lib/phases.js';
|
|
12
12
|
|
|
13
|
-
/**
|
|
14
|
-
* `agents.json → color` is a color NAME (blue, cyan…), the one Claude Code
|
|
15
|
-
* paints the sub-agent with. The map reads the same name, so a persona looks
|
|
16
|
-
* the same here and in the terminal.
|
|
17
|
-
*/
|
|
13
|
+
/** Color NAMES, the ones Claude Code paints the sub-agent with. */
|
|
18
14
|
const COLOR_HEX = {
|
|
19
15
|
blue: '#60a5fa',
|
|
20
16
|
cyan: '#22d3ee',
|
|
@@ -29,13 +25,11 @@ const COLOR_HEX = {
|
|
|
29
25
|
};
|
|
30
26
|
const FALLBACK_HEX = '#9ca3af';
|
|
31
27
|
|
|
32
|
-
/** The ONE color lookup: nodes, edges, detail badge and legend all read it. */
|
|
33
28
|
const personaColor = (agent) => COLOR_HEX[agent?.color] || FALLBACK_HEX;
|
|
34
29
|
|
|
35
30
|
/** `'*'` in a persona's `stack` means "every stack" — it is not a stack of its own. */
|
|
36
31
|
const UNIVERSAL_STACK = '*';
|
|
37
32
|
|
|
38
|
-
/** The node of the human + main thread. Never a persona id. */
|
|
39
33
|
const PRINCIPAL_ID = 'principal';
|
|
40
34
|
|
|
41
35
|
const NODE_W = 220;
|
|
@@ -81,7 +75,6 @@ function buildLayout(nodes, edges) {
|
|
|
81
75
|
});
|
|
82
76
|
}
|
|
83
77
|
|
|
84
|
-
/** Skills whose dispatch lines name this persona, or dispatch any implementer. */
|
|
85
78
|
function dispatchingSkills(agent) {
|
|
86
79
|
const implementer = (agent.activePhases || []).includes('implement');
|
|
87
80
|
return (morphData.skills || []).filter((s) => (s.dispatches || []).some(
|
|
@@ -29,13 +29,11 @@ const SLICE_STATUS = {
|
|
|
29
29
|
pendente: { label: 'pendente', tone: 'text-[var(--color-faint)]', icon: Circle },
|
|
30
30
|
};
|
|
31
31
|
|
|
32
|
-
// An unfinished slice whose dependencies all have a final report can start now.
|
|
33
32
|
function isReady(sl, byId) {
|
|
34
33
|
if (sl.status === 'feito') return false;
|
|
35
34
|
return (sl.depends || []).every((d) => byId[d]?.status === 'feito');
|
|
36
35
|
}
|
|
37
36
|
|
|
38
|
-
/** `vscode://file/…` opens the file in the editor from the browser. */
|
|
39
37
|
export function editorLink(absPath) {
|
|
40
38
|
return `vscode://file/${encodeURI(String(absPath).replace(/\\/g, '/'))}`;
|
|
41
39
|
}
|
|
@@ -62,7 +60,6 @@ function EditorButton({ path, label = 'Abrir no VS Code' }) {
|
|
|
62
60
|
);
|
|
63
61
|
}
|
|
64
62
|
|
|
65
|
-
/** What a person should expect next, in one sentence. */
|
|
66
63
|
export function nextAction(f) {
|
|
67
64
|
if (f.status === 'completed') return `Concluída${f.archivedAt ? ` ${timeAgo(f.archivedAt)}` : ''}.`;
|
|
68
65
|
if (f.status === 'abandoned') return 'Abandonada.';
|
|
@@ -579,8 +576,7 @@ export default function FeatureDetail() {
|
|
|
579
576
|
const tab = TABS.some((t) => t.id === params.get('tab')) ? params.get('tab') : 'overview';
|
|
580
577
|
const doc = params.get('doc');
|
|
581
578
|
const { data, error, loading } = useMorphQuery(`${apiBase(project)}/features/${encodeURIComponent(name)}`, { project });
|
|
582
|
-
// The cost rides on the activity endpoint (it owns the event stream)
|
|
583
|
-
// line of feed is enough to get it, and it refreshes with the log.
|
|
579
|
+
// The cost rides on the activity endpoint (it owns the event stream).
|
|
584
580
|
const costQuery = useMorphQuery(`${apiBase(project)}/activity?feature=${encodeURIComponent(name)}&limit=1`, { scope: 'activity', project });
|
|
585
581
|
const [slicesView, setSlicesView] = usePref('slices.view', 'list');
|
|
586
582
|
const now = useNow();
|
|
@@ -14,7 +14,6 @@ import { isFinished } from '../../scanner/feature-status.js';
|
|
|
14
14
|
|
|
15
15
|
const COMPLETED_PAGE = 30;
|
|
16
16
|
const BOARD_DONE_LIMIT = 8;
|
|
17
|
-
/** An event this recent means an agent is working on the feature right now. */
|
|
18
17
|
export const WORKING_MS = 3 * 60 * 1000;
|
|
19
18
|
|
|
20
19
|
const LOCATION_ICON = { worktree: GitBranch, archive: Archive, root: HouseSimple };
|
|
@@ -73,7 +72,6 @@ export function stageText(f) {
|
|
|
73
72
|
return phaseMeta(f.phase).label;
|
|
74
73
|
}
|
|
75
74
|
|
|
76
|
-
/** The second line of a feature: what just happened while it is live, else where it stands. */
|
|
77
75
|
function subline(f, now) {
|
|
78
76
|
if (!isFinished(f) && f.lastActivity?.title) {
|
|
79
77
|
return <>{isWorking(f, now) && <WorkingDot />}<span className="truncate">{f.lastActivity.title}, {timeAgo(f.lastActivity.titleTs, now)}</span></>;
|
|
@@ -243,10 +241,6 @@ const chipClass = (on) => [
|
|
|
243
241
|
on ? 'bg-white/[0.09] text-[var(--color-fg)] shadow-[inset_0_0_0_1px_var(--color-hair-strong)]' : 'text-[var(--color-faint)] shadow-[inset_0_0_0_1px_var(--color-hair)] hover:text-[var(--color-dim)]',
|
|
244
242
|
].join(' ');
|
|
245
243
|
|
|
246
|
-
/**
|
|
247
|
-
* The feature home. All state comes in as props so the view renders the same
|
|
248
|
-
* with the live API and with the smoke test's fixture.
|
|
249
|
-
*/
|
|
250
244
|
export function FeaturesView({
|
|
251
245
|
data, query, onQuery, completedShown, onShowMore,
|
|
252
246
|
view = 'list', onView = () => {}, types = [], onTypes = () => {}, sort = 'updated', onSort = () => {},
|
|
@@ -329,7 +323,7 @@ export function FeaturesView({
|
|
|
329
323
|
)}
|
|
330
324
|
{info.initialized && !info.legacyStateOnly && features.length === 0 && (
|
|
331
325
|
<Empty title="Nenhuma feature ainda">
|
|
332
|
-
No Claude Code, comece uma com <code className="font-mono text-[var(--color-fg)]">/morph-
|
|
326
|
+
No Claude Code, comece uma com <code className="font-mono text-[var(--color-fg)]">/morph-plan</code>, que entrevista e escreve o plano. Ela aparece aqui assim que o <code className="font-mono">feature.json</code> nascer.
|
|
333
327
|
</Empty>
|
|
334
328
|
)}
|
|
335
329
|
{features.length > 0 && visible.length === 0 && (
|
|
@@ -374,7 +368,6 @@ export default function Features() {
|
|
|
374
368
|
const now = useNow();
|
|
375
369
|
const searchRef = useRef(null);
|
|
376
370
|
|
|
377
|
-
// `/` jumps to the search, like most dev tools.
|
|
378
371
|
useEffect(() => {
|
|
379
372
|
const onKey = (e) => {
|
|
380
373
|
if (e.key !== '/' || e.ctrlKey || e.metaKey || e.altKey) return;
|
|
@@ -18,7 +18,6 @@ function personasIn(phaseId) {
|
|
|
18
18
|
return (morphData.agents?.list || []).filter((a) => a.activePhases?.includes(phaseId));
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
/** The whole flow on one screen: phase, the skill that runs it, who works in it, the gate that closes it. */
|
|
22
21
|
export function Flow() {
|
|
23
22
|
return (
|
|
24
23
|
<section aria-label="O fluxo do morph-spec" className="bezel">
|
|
@@ -6,12 +6,7 @@ import FlowGraph from '../components/FlowGraph.jsx';
|
|
|
6
6
|
import DetailPanel from '../components/DetailPanel.jsx';
|
|
7
7
|
import EntityLink, { useSelection } from '../components/EntityLink.jsx';
|
|
8
8
|
|
|
9
|
-
/**
|
|
10
|
-
* Every event the scanner can report. `when` and `power` are what the panel
|
|
11
|
-
* explains; `canBlock` is the badge on the graph (the tool-call veto).
|
|
12
|
-
* Colors are the dashboard palette as hex, because the MiniMap cannot read
|
|
13
|
-
* CSS variables.
|
|
14
|
-
*/
|
|
9
|
+
/** Colors are hex because the MiniMap cannot read CSS variables. */
|
|
15
10
|
const EVENTS = {
|
|
16
11
|
'session-start': {
|
|
17
12
|
label: 'Início da sessão', color: '#73daca',
|
|
@@ -69,12 +64,7 @@ function eventMeta(key) {
|
|
|
69
64
|
return EVENTS[key] || { label: key, color: '#8d97a5', when: 'Evento sem descrição no dashboard.', power: '' };
|
|
70
65
|
}
|
|
71
66
|
|
|
72
|
-
/**
|
|
73
|
-
* The URL key of one registration. A hook file can register more than once
|
|
74
|
-
* under the same event with different matchers, so the name alone is not
|
|
75
|
-
* unique: `?sel=` holds `event:name:matcher`. A bare hook name also works and
|
|
76
|
-
* opens that hook's first registration.
|
|
77
|
-
*/
|
|
67
|
+
/** A hook can register more than once under one event, so `?sel=` holds `event:name:matcher`. */
|
|
78
68
|
export function hookKey(hook) {
|
|
79
69
|
return `${hook.event}:${hook.name}:${hook.matcher || ''}`;
|
|
80
70
|
}
|
|
@@ -89,8 +79,6 @@ function matcherText(hook) {
|
|
|
89
79
|
return eventMeta(hook.event).toolEvent ? 'todas as ferramentas' : null;
|
|
90
80
|
}
|
|
91
81
|
|
|
92
|
-
// ─── Nodes ────────────────────────────────────────────────────────────────────
|
|
93
|
-
|
|
94
82
|
const NODE_W = 280;
|
|
95
83
|
const ROW_H = 26;
|
|
96
84
|
const HEAD_H = 58;
|
|
@@ -161,11 +149,6 @@ function ToolNode() {
|
|
|
161
149
|
|
|
162
150
|
const nodeTypes = { eventGroupNode: EventGroupNode, toolNode: ToolNode };
|
|
163
151
|
|
|
164
|
-
// ─── Layout ───────────────────────────────────────────────────────────────────
|
|
165
|
-
// Main row: session-start → user-prompt → pre-tool-use → tool → post-tool-use → stop / pre-compact
|
|
166
|
-
// Second row: events outside the tool-call flow (worktree pair, teammate-idle,
|
|
167
|
-
// unregistered hooks and any event this page does not know).
|
|
168
|
-
|
|
169
152
|
const COL_GAP = NODE_W + 90;
|
|
170
153
|
const MAIN_ROW = ['session-start', 'user-prompt', 'pre-tool-use', '__tool__', 'post-tool-use'];
|
|
171
154
|
const SIDE_ROW = ['worktree-create', 'worktree-remove', 'teammate-idle', 'unregistered'];
|
|
@@ -231,8 +214,6 @@ function buildNodesAndEdges(hooksByEvent, onSelectHook, selectedKey) {
|
|
|
231
214
|
return { nodes, edges };
|
|
232
215
|
}
|
|
233
216
|
|
|
234
|
-
// ─── Panels ───────────────────────────────────────────────────────────────────
|
|
235
|
-
|
|
236
217
|
function Section({ title, children }) {
|
|
237
218
|
return (
|
|
238
219
|
<section>
|
|
@@ -369,8 +350,6 @@ function ToolDetail() {
|
|
|
369
350
|
);
|
|
370
351
|
}
|
|
371
352
|
|
|
372
|
-
// ─── Main Page ────────────────────────────────────────────────────────────────
|
|
373
|
-
|
|
374
353
|
export default function HookInspector() {
|
|
375
354
|
const { hooks } = morphData;
|
|
376
355
|
const [sel, setSel] = useSelection();
|
|
@@ -9,7 +9,6 @@ import { phaseMeta, GATE_LABEL } from '../lib/phases.js';
|
|
|
9
9
|
|
|
10
10
|
const phaseColor = (id) => `var(--color-phase-${id})`;
|
|
11
11
|
|
|
12
|
-
/** What each approval gate checks, in the words a reader of the pipeline needs. */
|
|
13
12
|
const GATE_CHECKS = {
|
|
14
13
|
plan: 'Confere o plano: o problema, o que fica de fora, os cenários com a prova de cada um e as fatias. Depois dele o plan.md congela (só a errata cresce).',
|
|
15
14
|
uiux: 'Confere o design das telas (mockups e checklist de tela). É opcional: só existe quando a feature passa pela fase de UI/UX, depois do plano.',
|
|
@@ -65,7 +64,6 @@ const GATE_W = 150;
|
|
|
65
64
|
const GATE_H = 32;
|
|
66
65
|
const GAP = 48;
|
|
67
66
|
|
|
68
|
-
/** Left to right: each phase, then its gate (when it has one), then the next phase. */
|
|
69
67
|
function buildPipelineGraph(phases, sel) {
|
|
70
68
|
const nodes = [];
|
|
71
69
|
const edges = [];
|
|
@@ -131,7 +129,6 @@ function Chip({ children }) {
|
|
|
131
129
|
// Exported for the render smoke test: no page state, only the phase and the personas.
|
|
132
130
|
export function PhaseDetail({ phase, agents }) {
|
|
133
131
|
const color = phaseColor(phase.id);
|
|
134
|
-
// Personas this phase's skill dispatches (derived by the agents scanner).
|
|
135
132
|
const activeAgents = agents.filter((a) => a.activePhases?.includes(phase.id));
|
|
136
133
|
|
|
137
134
|
return (
|
|
@@ -22,7 +22,6 @@ async function send(method, url, body) {
|
|
|
22
22
|
function ProjectBlock({ p, now, onRemove }) {
|
|
23
23
|
const s = p.summary;
|
|
24
24
|
const working = s ? s.activeFeatures.filter((f) => isWorking(f, now)).length : 0;
|
|
25
|
-
// What waits on you is already listed at the top of the page; here, the rest, latest first.
|
|
26
25
|
const rest = s ? s.activeFeatures.filter((f) => !f.awaiting) : [];
|
|
27
26
|
const shown = [...rest].sort((a, b) => (b.lastActivity?.ts || '').localeCompare(a.lastActivity?.ts || '')).slice(0, ACTIVE_SHOWN);
|
|
28
27
|
return (
|
|
@@ -12,13 +12,7 @@ const RULE_COLOR = 'var(--color-phase-plan)';
|
|
|
12
12
|
const STD_COLOR = 'var(--color-phase-proposal)';
|
|
13
13
|
const NODE_W = 260;
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* A rule's route → the standard it names in the registry. `id` is what the
|
|
19
|
-
* Standards page selects by; with no registry match the path itself is passed,
|
|
20
|
-
* so the link still says which file the rule meant.
|
|
21
|
-
*/
|
|
15
|
+
/** With no registry match the path itself is passed, so the link still names the file. */
|
|
22
16
|
function resolveStandard(ref, byPath) {
|
|
23
17
|
const path = standardKey(ref);
|
|
24
18
|
const std = byPath.get(path);
|
|
@@ -30,8 +24,6 @@ function resolveStandard(ref, byPath) {
|
|
|
30
24
|
};
|
|
31
25
|
}
|
|
32
26
|
|
|
33
|
-
// ─── Node Components ──────────────────────────────────────────────────────────
|
|
34
|
-
|
|
35
27
|
function RuleNode({ data }) {
|
|
36
28
|
return (
|
|
37
29
|
<div
|
|
@@ -78,8 +70,6 @@ function StandardRefNode({ data }) {
|
|
|
78
70
|
|
|
79
71
|
const nodeTypes = { ruleNode: RuleNode, standardRefNode: StandardRefNode };
|
|
80
72
|
|
|
81
|
-
// ─── Graph Builder ────────────────────────────────────────────────────────────
|
|
82
|
-
|
|
83
73
|
function matchesFilter(rule, search, stackFilter) {
|
|
84
74
|
const q = search.toLowerCase();
|
|
85
75
|
const matchSearch = !q || rule.name.toLowerCase().includes(q) || rule.stacks.some((s) => s.toLowerCase().includes(q));
|
|
@@ -153,8 +143,6 @@ function buildGraph(rules, byPath, search, stackFilter, selected) {
|
|
|
153
143
|
return { nodes, edges };
|
|
154
144
|
}
|
|
155
145
|
|
|
156
|
-
// ─── Detail Content ───────────────────────────────────────────────────────────
|
|
157
|
-
|
|
158
146
|
function Section({ title, children }) {
|
|
159
147
|
return (
|
|
160
148
|
<section>
|
|
@@ -221,9 +209,6 @@ function RuleDetail({ rule, byPath }) {
|
|
|
221
209
|
);
|
|
222
210
|
}
|
|
223
211
|
|
|
224
|
-
// ─── Main Page ────────────────────────────────────────────────────────────────
|
|
225
|
-
|
|
226
|
-
/** `?sel=` holds the rule `name` (the file name without `.md`). */
|
|
227
212
|
export default function RulesMap() {
|
|
228
213
|
const { rules } = morphData;
|
|
229
214
|
const [search, setSearch] = useState('');
|
|
@@ -22,8 +22,6 @@ const INVOKE_NO = 'só interna';
|
|
|
22
22
|
|
|
23
23
|
const tint = (color, pct = 14) => `color-mix(in srgb, ${color} ${pct}%, var(--color-core))`;
|
|
24
24
|
|
|
25
|
-
// ─── Nodes ────────────────────────────────────────────────────────────────────
|
|
26
|
-
|
|
27
25
|
const HANDLE = { width: 7, height: 7, border: 0 };
|
|
28
26
|
|
|
29
27
|
function PhaseNode({ data }) {
|
|
@@ -99,15 +97,11 @@ function GroupNode({ data }) {
|
|
|
99
97
|
|
|
100
98
|
const nodeTypes = { phaseNode: PhaseNode, skillNode: SkillNode, personaNode: PersonaNode, groupNode: GroupNode };
|
|
101
99
|
|
|
102
|
-
// ─── Graph model ──────────────────────────────────────────────────────────────
|
|
103
|
-
|
|
104
|
-
/** The phase a skill belongs to: its own `phase`, or the phase whose `skill` it is. */
|
|
105
100
|
function skillPhaseOf(skill, phases) {
|
|
106
101
|
if (skill.phase) return skill.phase;
|
|
107
102
|
return phases.find((p) => p.skill === skill.id)?.id || null;
|
|
108
103
|
}
|
|
109
104
|
|
|
110
|
-
/** The persona targets of a skill's dispatches: generic ones go to the one "implementadores" node. */
|
|
111
105
|
function dispatchTargets(skill) {
|
|
112
106
|
const out = new Set();
|
|
113
107
|
for (const d of skill.dispatches || []) {
|
|
@@ -127,7 +121,7 @@ const SUPPORT_PER_ROW = 5;
|
|
|
127
121
|
function buildGraph({ skills, phases, agents, isMatch, hasFilter }) {
|
|
128
122
|
const nodes = [];
|
|
129
123
|
const edges = [];
|
|
130
|
-
const phaseSkills = new Map();
|
|
124
|
+
const phaseSkills = new Map();
|
|
131
125
|
const support = [];
|
|
132
126
|
for (const s of skills) {
|
|
133
127
|
const ph = skillPhaseOf(s, phases);
|
|
@@ -137,7 +131,6 @@ function buildGraph({ skills, phases, agents, isMatch, hasFilter }) {
|
|
|
137
131
|
} else support.push(s);
|
|
138
132
|
}
|
|
139
133
|
|
|
140
|
-
// Row 0 + 1: one column per phase, its skill(s) below it.
|
|
141
134
|
phases.forEach((p, i) => {
|
|
142
135
|
const meta = phaseMeta(p.id);
|
|
143
136
|
const x = i * COL;
|
|
@@ -154,7 +147,6 @@ function buildGraph({ skills, phases, agents, isMatch, hasFilter }) {
|
|
|
154
147
|
});
|
|
155
148
|
});
|
|
156
149
|
|
|
157
|
-
// Row 3+: the support skills, in a labelled group under the personas.
|
|
158
150
|
const supportCols = Math.min(SUPPORT_PER_ROW, Math.max(1, support.length));
|
|
159
151
|
const supportRows = Math.ceil(support.length / supportCols);
|
|
160
152
|
const supportWidth = supportCols * COL;
|
|
@@ -173,7 +165,6 @@ function buildGraph({ skills, phases, agents, isMatch, hasFilter }) {
|
|
|
173
165
|
nodes.push(skillNode(s, x, y, SUPPORT_COLOR, SUPPORT_LABEL));
|
|
174
166
|
});
|
|
175
167
|
|
|
176
|
-
// Row 2: the personas the skills dispatch prompts to.
|
|
177
168
|
const personaIds = new Set();
|
|
178
169
|
for (const s of skills) for (const t of dispatchTargets(s)) personaIds.add(t);
|
|
179
170
|
const agentOrder = agents.map((a) => a.id);
|
|
@@ -197,7 +188,6 @@ function buildGraph({ skills, phases, agents, isMatch, hasFilter }) {
|
|
|
197
188
|
});
|
|
198
189
|
});
|
|
199
190
|
|
|
200
|
-
// skill → persona, from the dispatch prompts.
|
|
201
191
|
const supportIds = new Set(support.map((s) => s.id));
|
|
202
192
|
for (const s of skills) {
|
|
203
193
|
const fromSupport = supportIds.has(s.id);
|
|
@@ -226,8 +216,6 @@ function buildGraph({ skills, phases, agents, isMatch, hasFilter }) {
|
|
|
226
216
|
}
|
|
227
217
|
}
|
|
228
218
|
|
|
229
|
-
// ─── Detail ───────────────────────────────────────────────────────────────────
|
|
230
|
-
|
|
231
219
|
function Section({ title, children }) {
|
|
232
220
|
return (
|
|
233
221
|
<section className="space-y-2">
|
|
@@ -252,7 +240,6 @@ function PromptBlock({ text, label = 'Prompt enviado ao sub-agent' }) {
|
|
|
252
240
|
);
|
|
253
241
|
}
|
|
254
242
|
|
|
255
|
-
/** Plain text of a hast node (the `node` react-markdown hands to overrides). */
|
|
256
243
|
function hastText(node) {
|
|
257
244
|
if (!node) return '';
|
|
258
245
|
if (node.type === 'text') return node.value || '';
|
|
@@ -372,8 +359,6 @@ export function SkillDetail({ skill, phases = morphData.phases?.list || [] }) {
|
|
|
372
359
|
);
|
|
373
360
|
}
|
|
374
361
|
|
|
375
|
-
// ─── Page ─────────────────────────────────────────────────────────────────────
|
|
376
|
-
|
|
377
362
|
export default function SkillsLibrary() {
|
|
378
363
|
const skills = morphData.skills || [];
|
|
379
364
|
const phases = morphData.phases?.list || [];
|