@polymorphism-tech/morph-spec 8.47.0 → 9.1.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 +51 -43
- package/README.md +39 -38
- package/bin/morph-spec.js +41 -113
- package/bin/validate.js +1 -147
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +1 -1
- package/docs/QUICKSTART.md +1 -1
- package/framework/CLAUDE.md +41 -101
- package/framework/MORPH.md +95 -252
- package/framework/agents.json +86 -21
- package/framework/commands/morph-apply.md +5 -28
- package/framework/commands/morph-archive.md +1 -1
- package/framework/commands/morph-hotfix.md +8 -8
- package/framework/commands/morph-infra.md +1 -1
- package/framework/commands/morph-preflight.md +2 -3
- package/framework/commands/morph-status.md +4 -4
- package/framework/docs/CLI.md +574 -286
- package/framework/evals/review-checklist.md +82 -41
- package/framework/hooks/README.md +10 -11
- 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 +3 -17
- package/framework/hooks/claude-code/core/denylist.json +33 -3
- package/framework/hooks/claude-code/core/enforce-phase-writes.js +15 -63
- package/framework/hooks/claude-code/core/gate-guard.js +9 -78
- 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 +36 -2
- package/framework/hooks/claude-code/core/protect-spec-files.js +25 -297
- package/framework/hooks/claude-code/core/state-sync.js +15 -85
- package/framework/hooks/claude-code/core/telemetry-log.js +15 -96
- package/framework/hooks/claude-code/core/validate-completion.js +37 -147
- package/framework/hooks/claude-code/core/warn-bash-source-write.js +23 -0
- package/framework/hooks/claude-code/core/worktree-context.js +15 -114
- 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.py +55 -41
- package/framework/hooks/package.json +3 -0
- 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 +11 -76
- package/framework/hooks/shared/claim-registry.js +9 -119
- package/framework/hooks/shared/command-tokens.js +168 -44
- package/framework/hooks/shared/comment-rules.js +5 -20
- package/framework/hooks/shared/comment-scanner.js +11 -43
- package/framework/hooks/shared/comment-stripper.js +11 -85
- package/framework/hooks/shared/e2e-check.js +0 -0
- package/framework/hooks/shared/errata.js +59 -0
- package/framework/hooks/shared/feature-locator.js +11 -95
- package/framework/hooks/shared/frontend-locations.js +4 -0
- package/framework/hooks/shared/gate-question.js +12 -0
- 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 +14 -72
- 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 +99 -0
- package/framework/hooks/shared/source-files.js +4 -17
- package/framework/hooks/shared/spawn-command.js +23 -0
- package/framework/hooks/shared/state-reader.js +51 -247
- package/framework/hooks/shared/state-writer.js +9 -54
- 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/mcp/registry.json +3 -4
- package/framework/phases.json +23 -39
- 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/schemas/config.schema.json +14 -9
- package/framework/schemas/feature.schema.json +15 -50
- package/framework/skills/README.md +3 -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 +149 -222
- package/framework/skills/core/morph-hotfix/SKILL.md +33 -42
- package/framework/skills/core/morph-machine/SKILL.md +2 -2
- package/framework/skills/core/morph-media/SKILL.md +1 -1
- package/framework/skills/core/morph-plan/SKILL.md +137 -299
- package/framework/skills/core/morph-review/SKILL.md +111 -136
- package/framework/skills/core/morph-standards/SKILL.md +27 -39
- package/framework/skills/core/morph-uiux/SKILL.md +94 -152
- package/framework/standards/STANDARDS.json +109 -1219
- package/framework/standards/STANDARDS.meta.json +1319 -0
- 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/evals-with-cache.md +1 -2
- 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 +3 -3
- 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 +7 -7
- 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/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/infrastructure/neon/neon-pgvector.md +5 -5
- package/framework/standards/integration/mcp/mcp-tools.md +7 -7
- package/framework/templates/CLAUDE.md.core.template +3 -3
- package/framework/templates/README.md +2 -3
- package/framework/templates/REGISTRY.json +15 -179
- package/framework/templates/docs/plan.md +72 -0
- package/framework/templates/docs/ui-mockups.md +4 -0
- package/framework/templates/dotnet/ai-kit/README.md +1 -9
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Morph.AiKit.Tests.csproj +0 -10
- package/framework/templates/meta-prompts/squad-leaders/backend-squad.md +1 -1
- package/framework/templates/meta-prompts/squad-leaders/frontend-squad.md +1 -1
- package/framework/templates/meta-prompts/squad-leaders/squad-leader.md +1 -1
- package/framework/templates/meta-prompts/validators/pre-commit-validator.md +3 -4
- 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 +18 -105
- 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 +17 -111
- 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-procs.js +1 -1
- package/src/commands/project/doctor.js +53 -307
- package/src/commands/project/env.js +1 -27
- package/src/commands/project/fleet.js +6 -35
- package/src/commands/project/heavy.js +3 -3
- package/src/commands/project/init.js +8 -68
- package/src/commands/project/install-plugin-cmd.js +0 -1
- package/src/commands/project/setup-infra-cmd.js +2 -1
- package/src/commands/project/stack-scan.js +58 -0
- package/src/commands/project/standards.js +65 -72
- package/src/commands/project/status.js +28 -121
- package/src/commands/project/update.js +31 -223
- package/src/commands/project/worktree.js +109 -883
- package/src/commands/scaffold/scaffold.js +0 -25
- package/src/commands/state/advance.js +65 -120
- package/src/commands/state/approve.js +87 -229
- package/src/commands/state/archive.js +23 -139
- package/src/commands/state/auto-approval.js +65 -0
- package/src/commands/state/create.js +49 -109
- package/src/commands/state/delete.js +11 -63
- package/src/commands/state/finish.js +105 -494
- package/src/commands/state/gate-check.js +74 -183
- package/src/commands/state/gate-decision.js +8 -42
- package/src/commands/state/handoff.js +4 -31
- package/src/commands/state/list.js +21 -91
- package/src/commands/state/report-check.js +134 -338
- package/src/commands/state/retype.js +12 -28
- package/src/commands/validation/index.js +0 -3
- package/src/commands/validation/mutate.js +79 -19
- package/src/commands/validation/plan-check.js +81 -0
- package/src/commands/validation/validate-feature.js +52 -216
- package/src/commands/validation/verify.js +45 -160
- package/src/core/paths/output-schema.js +9 -155
- package/src/core/state/feature-identity.js +1 -25
- package/src/core/state/gate-order.js +2 -30
- package/src/core/state/lifecycle-audit.js +8 -100
- package/src/core/state/state-manager.js +32 -395
- 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 +21 -0
- package/src/core/work-types.js +10 -45
- package/src/dashboard/app/App.jsx +1 -8
- package/src/dashboard/app/components/ActivityFeed.jsx +2 -8
- package/src/dashboard/app/components/CommandPalette.jsx +4 -11
- 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 +3 -9
- package/src/dashboard/app/lib/project.js +0 -12
- package/src/dashboard/app/pages/Activity.jsx +1 -1
- package/src/dashboard/app/pages/AgentMap.jsx +1 -8
- package/src/dashboard/app/pages/FeatureDetail.jsx +139 -133
- package/src/dashboard/app/pages/Features.jsx +9 -16
- package/src/dashboard/app/pages/Guide.jsx +2 -3
- package/src/dashboard/app/pages/HookInspector.jsx +2 -23
- package/src/dashboard/app/pages/PhasePipeline.jsx +3 -7
- 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 +14 -83
- package/src/dashboard/scanner/agents-scanner.js +1 -23
- package/src/dashboard/scanner/docs-scanner.js +1 -7
- package/src/dashboard/scanner/feature-status.js +2 -5
- package/src/dashboard/scanner/features-scanner.js +92 -113
- 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 +4 -7
- package/src/dashboard/vite-plugin-morph-data.js +5 -19
- 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 +14 -13
- package/src/lib/ci/github-ci.js +9 -37
- package/src/lib/ci/workflow-scaffold.js +2 -10
- package/src/lib/detectors/claude-config-detector.js +13 -74
- package/src/lib/detectors/index.js +8 -59
- 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 +37 -112
- package/src/lib/generators/scaffold-generator.js +5 -101
- package/src/lib/generators/settings-generator.js +11 -122
- 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 +2 -1
- 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 +17 -17
- 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 +2 -6
- package/src/lib/installers/effects.js +8 -93
- package/src/lib/installers/mcp-installer.js +9 -169
- package/src/lib/installers/memory-installer.js +8 -33
- package/src/lib/installers/templates-reconcile.js +5 -60
- package/src/lib/mutation/commands.js +3 -2
- package/src/lib/mutation/dotnet-introspection.js +281 -0
- package/src/lib/mutation/plan.js +14 -6
- package/src/lib/mutation/runner.js +1 -1
- package/src/lib/mutation/subset.js +47 -13
- package/src/lib/plan/artifact-vocabulary.js +40 -0
- package/src/lib/plan/checks.js +208 -0
- package/src/lib/plan/plan-parser.js +204 -0
- package/src/lib/plan/plan-template.js +18 -0
- package/src/lib/projects/registry.js +7 -44
- package/src/lib/review-reports.js +204 -0
- package/src/lib/risk-detector.js +131 -274
- package/src/lib/specs/adr-index.js +38 -85
- package/src/lib/specs/design-seed.js +18 -0
- package/src/lib/specs/frontmatter.js +31 -0
- package/src/lib/specs/gfm-table.js +11 -0
- package/src/lib/specs/revocations.js +31 -102
- package/src/lib/specs/screen-checklist.js +56 -132
- package/src/lib/stack/dotnet-build-ignores.js +43 -0
- package/src/lib/stack/stack-profile.js +1 -28
- package/src/lib/stack/stack-scan.js +260 -0
- package/src/lib/stack-filter.js +4 -27
- package/src/lib/standards/registry.js +37 -0
- package/src/lib/standards/sections.js +47 -0
- package/src/lib/standards/verified-against.js +19 -191
- package/src/lib/tasks/eval-node.js +23 -179
- package/src/lib/tasks/linux-runner.js +18 -142
- package/src/lib/tasks/test-nodes.js +43 -313
- package/src/lib/tasks/test-runner.js +107 -447
- package/src/lib/telemetry/metrics.js +13 -103
- package/src/lib/telemetry/telemetry-reader.js +13 -238
- package/src/lib/telemetry/verify-event.js +4 -34
- 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 +4 -26
- 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 +24 -137
- package/src/lib/validators/reuse/symbol-scanner.js +6 -62
- package/src/lib/validators/traceability-check.js +54 -156
- package/src/lib/validators/validation-runner.js +20 -125
- package/src/lib/validators/verify-runner.js +152 -575
- package/src/lib/work-type-detector.js +5 -67
- package/src/lib/worktree/docker-audit.js +17 -137
- 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 +31 -126
- 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 -138
- package/src/utils/agents-installer.js +92 -272
- package/src/utils/banner.js +4 -25
- package/src/utils/claude-md-injector.js +1 -37
- package/src/utils/claude-settings-manager.js +1 -87
- package/src/utils/file-copier.js +32 -90
- package/src/utils/hooks-installer.js +26 -296
- package/src/utils/skills-installer.js +4 -102
- package/src/utils/version-checker.js +4 -83
- package/framework/commands/morph-proposal.md +0 -57
- package/framework/evals/trace-schema.md +0 -67
- package/framework/hooks/claude-code/core/tasks-json-guard.js +0 -83
- package/framework/hooks/claude-code/core/trace-autogen.js +0 -277
- package/framework/hooks/git/pre-commit/specs.sh +0 -50
- package/framework/hooks/shared/tasks-validator.js +0 -111
- package/framework/rules/morph-task-tracking.md +0 -54
- package/framework/rules/morph-workflow.md +0 -244
- package/framework/schemas/tasks.schema.json +0 -79
- package/framework/skills/core/morph-brainstorming/SKILL.md +0 -161
- package/framework/skills/core/morph-implement/SKILL.md +0 -194
- package/framework/skills/core/morph-proposal/SKILL.md +0 -152
- package/framework/skills/core/morph-stack-scan/SKILL.md +0 -145
- package/framework/templates/docs/proposal.md +0 -192
- package/framework/templates/docs/spec.md +0 -57
- package/framework/templates/docs/ui-components.md +0 -52
- package/framework/templates/docs/ui-design-system.md +0 -76
- package/framework/templates/docs/ui-flows.md +0 -167
- package/framework/templates/examples/spec-examples.md +0 -91
- package/framework/templates/feature/decisions.md +0 -124
- package/framework/templates/feature/recap.md +0 -145
- package/framework/templates/tasks.template.json +0 -17
- package/src/commands/state/usecase.js +0 -143
- package/src/commands/tasks/dag.js +0 -181
- package/src/commands/utils/index.js +0 -3
- package/src/lib/agents/output-collector.js +0 -76
- package/src/lib/detectors/design-system-detector.js +0 -188
- package/src/lib/evaluator-report.js +0 -104
- package/src/lib/generators/design-system-generator.js +0 -508
- package/src/lib/specs/acervo-format.js +0 -70
- package/src/lib/specs/acervo-lifecycle.js +0 -264
- package/src/lib/specs/done-criteria-check.js +0 -200
- package/src/lib/specs/usecase-check.js +0 -301
- package/src/lib/specs/usecase-promoter.js +0 -301
- package/src/lib/specs/usecase-registry.js +0 -237
- package/src/lib/tasks/dispatch-planner.js +0 -319
- package/src/lib/tasks/dotnet-test-introspection.js +0 -632
- package/src/lib/tasks/mermaid-renderer.js +0 -132
- package/src/lib/tasks/task-classifier.js +0 -30
- package/src/lib/tasks/task-parser.js +0 -209
- package/src/lib/tasks/tasks-json-parser.js +0 -296
- package/src/lib/tasks/test-scope.js +0 -597
- package/src/utils/agent-id-mapper.js +0 -11
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { readFileSync, readdirSync, existsSync } from 'fs';
|
|
2
2
|
import { join } from 'path';
|
|
3
3
|
|
|
4
|
-
/**
|
|
4
|
+
/** CRLF-safe: a checkout with autocrlf is still read. */
|
|
5
5
|
export function parseFrontmatter(content) {
|
|
6
6
|
const match = /^---\r?\n([\s\S]*?)\r?\n---/.exec(content);
|
|
7
7
|
if (!match) return {};
|
|
@@ -21,19 +21,9 @@ export function parseFrontmatter(content) {
|
|
|
21
21
|
const FENCE = /^(\s*)```/;
|
|
22
22
|
const DISPATCH_HINT = /dispatch|despach|subagent_type|Template do prompt/i;
|
|
23
23
|
const PERSONA_REF = /morph-(\{id\}|\{task\.agent\}|[a-z][a-z-]*[a-z])/g;
|
|
24
|
-
/** Words after `morph-` that are skills or CLI verbs, never personas. */
|
|
25
|
-
/** Claude Code's own sub-agents: dispatched by skills, but not personas of agents.json. */
|
|
26
24
|
const BUILTIN_AGENT = /`(Explore|general-purpose|Plan)`/g;
|
|
27
25
|
const NOT_PERSONA = /^(spec|proposal|brainstorming|stack-scan|uiux|plan|implement|apply|review|eval|hotfix|standards|media|task)$/;
|
|
28
26
|
|
|
29
|
-
/**
|
|
30
|
-
* The prompts a skill tells the orchestrator to send to a sub-agent: fenced
|
|
31
|
-
* blocks that are either written as a dispatch (`dispatch Agent (…)`) or sit
|
|
32
|
-
* right under a line about dispatching. Each carries the personas it names —
|
|
33
|
-
* `generic` when the template is `morph-{id}` (any implementer).
|
|
34
|
-
*
|
|
35
|
-
* @returns {Array<{ index: number, personas: string[], generic: boolean, builtin: string[], context: string, text: string }>}
|
|
36
|
-
*/
|
|
37
27
|
export function extractDispatchPrompts(content) {
|
|
38
28
|
const lines = content.split(/\r?\n/);
|
|
39
29
|
const out = [];
|
|
@@ -45,7 +35,7 @@ export function extractDispatchPrompts(content) {
|
|
|
45
35
|
const body = lines.slice(i + 1, j);
|
|
46
36
|
const text = body.map((l) => l.slice(Math.min(open[1].length, l.length - l.trimStart().length))).join('\n');
|
|
47
37
|
// Only the sentence that introduces the block counts: a paragraph further up
|
|
48
|
-
// that mentions a dispatch
|
|
38
|
+
// that mentions a dispatch is not this block's.
|
|
49
39
|
let k = i - 1;
|
|
50
40
|
while (k >= 0 && lines[k].trim() === '') k--;
|
|
51
41
|
const before = k >= 0 && i - k <= 2 ? lines[k] : '';
|
|
@@ -1,20 +1,17 @@
|
|
|
1
1
|
import { readFileSync, existsSync } from 'fs';
|
|
2
2
|
import { join } from 'path';
|
|
3
3
|
import { personaStandardIds } from '../../utils/agents-installer.js';
|
|
4
|
+
import { loadStandardsRegistry } from '../../lib/standards/registry.js';
|
|
4
5
|
|
|
5
6
|
export function scanStandards(frameworkDir, agents) {
|
|
6
7
|
const standardsJsonPath = join(frameworkDir, 'standards', 'STANDARDS.json');
|
|
7
8
|
if (!existsSync(standardsJsonPath)) return { version: 'unknown', list: [] };
|
|
8
|
-
const sJson =
|
|
9
|
+
const sJson = loadStandardsRegistry(standardsJsonPath);
|
|
9
10
|
const refMap = {};
|
|
10
11
|
for (const agent of agents) {
|
|
11
12
|
for (const std of personaStandardIds(agent)) {
|
|
12
|
-
// agents.json stores standards as
|
|
13
|
-
//
|
|
14
|
-
// [undefined] and the lookup below missed all 60 — the Standards graph
|
|
15
|
-
// drew standards with no agent edge at all. The object form is the v4
|
|
16
|
-
// shape (`{ id, scope, priority }`); still accepted so a mixed or
|
|
17
|
-
// restored agents.json does not silently lose edges again.
|
|
13
|
+
// agents.json stores standards as id strings; the `{ id }` object form is
|
|
14
|
+
// still accepted so a mixed agents.json does not lose edges.
|
|
18
15
|
const id = typeof std === 'string' ? std : std?.id;
|
|
19
16
|
if (!id) continue;
|
|
20
17
|
if (!refMap[id]) refMap[id] = [];
|
|
@@ -21,14 +21,6 @@ function readBody(req) {
|
|
|
21
21
|
});
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
/**
|
|
25
|
-
* Two data paths:
|
|
26
|
-
* - `virtual:morph-data` — the framework's knowledge (agents, phases, skills,
|
|
27
|
-
* standards, rules, hooks, docs). Static for the life of the server.
|
|
28
|
-
* - `/api/morph/*` — the portfolio (`/projects`) and each project's features
|
|
29
|
-
* and activity (`/p/:id/…`), read on demand and pushed as `morph:changed`
|
|
30
|
-
* over the HMR socket when a project's `.morph/` changes.
|
|
31
|
-
*/
|
|
32
24
|
export default function morphDataPlugin(frameworkDir, projectDir) {
|
|
33
25
|
return {
|
|
34
26
|
name: 'vite-plugin-morph-data',
|
|
@@ -43,11 +35,8 @@ export default function morphDataPlugin(frameworkDir, projectDir) {
|
|
|
43
35
|
const portfolio = createPortfolio({ launchDir: projectDir });
|
|
44
36
|
const push = (data) => server.ws.send({ type: 'custom', event: 'morph:changed', data });
|
|
45
37
|
|
|
46
|
-
// Vite reloads
|
|
47
|
-
//
|
|
48
|
-
// reload). A project's .morph/ carries .html design sources, and an agent
|
|
49
|
-
// writing one reloaded the dashboard under the person reading it. The
|
|
50
|
-
// dashboard's own code never lives under .morph/.
|
|
38
|
+
// Vite 6 full-reloads any changed .html it watches even when handleHotUpdate
|
|
39
|
+
// returns [], and a project's .morph/ carries .html design sources.
|
|
51
40
|
const clientHot = server.environments?.client?.hot;
|
|
52
41
|
if (clientHot?.send) {
|
|
53
42
|
const send = clientHot.send.bind(clientHot);
|
|
@@ -64,10 +53,8 @@ export default function morphDataPlugin(frameworkDir, projectDir) {
|
|
|
64
53
|
if (fresh.length) server.watcher.add(fresh);
|
|
65
54
|
}
|
|
66
55
|
|
|
67
|
-
//
|
|
68
|
-
//
|
|
69
|
-
// event loop for seconds, so each step waits WARM_GAP_MS first: the reads
|
|
70
|
-
// a push (or an add) just caused are served before the next block.
|
|
56
|
+
// One project at a time, WARM_GAP_MS apart: a read blocks the event loop for
|
|
57
|
+
// seconds, and the reads a push just caused are served first.
|
|
71
58
|
let warming = false;
|
|
72
59
|
function warmSoon() {
|
|
73
60
|
if (warming) return;
|
|
@@ -88,7 +75,6 @@ export default function morphDataPlugin(frameworkDir, projectDir) {
|
|
|
88
75
|
res.setHeader('Content-Type', 'application/json; charset=utf-8');
|
|
89
76
|
res.setHeader('Cache-Control', 'no-store');
|
|
90
77
|
res.end(JSON.stringify(out));
|
|
91
|
-
// The portfolio's list changed: watch and read any project just added.
|
|
92
78
|
if (req.method !== 'GET' && status === 200) { watchNew(); push({ scope: 'projects' }); warmSoon(); }
|
|
93
79
|
});
|
|
94
80
|
|
|
@@ -98,7 +84,7 @@ export default function morphDataPlugin(frameworkDir, projectDir) {
|
|
|
98
84
|
rescan.unref?.();
|
|
99
85
|
|
|
100
86
|
const timers = new Map();
|
|
101
|
-
const changed = new Map();
|
|
87
|
+
const changed = new Map();
|
|
102
88
|
const onFsEvent = (event, path) => {
|
|
103
89
|
const scope = classifyChange(path);
|
|
104
90
|
if (!scope) return;
|
package/src/lib/ai/ai-checks.js
CHANGED
|
@@ -1,26 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
*
|
|
4
|
-
* Contrato de uma checagem:
|
|
5
|
-
*
|
|
6
|
-
* { id, name, status: 'ok'|'warn'|'error'|'skip', reason?, findings: [], unverified?: [] }
|
|
7
|
-
*
|
|
8
|
-
* Duas regras que este módulo existe para tornar mecânicas:
|
|
9
|
-
*
|
|
10
|
-
* 1. **`status: 'skip'` sai com `reason` obrigatória.** "Não verificado" sem
|
|
11
|
-
* motivo é ruído — quem lê não consegue decidir se falta insumo ou se falta
|
|
12
|
-
* checagem. Um `skip` sem razão é bug deste arquivo, e há canário.
|
|
13
|
-
* 2. **`null` de uma função de checagem é DESCONHECIDO, nunca "não achei
|
|
14
|
-
* nada".** `[]` é "perguntei e não achei" e vira `ok`. É a doutrina de
|
|
15
|
-
* `pr-index.js`, já viva no `doctor`.
|
|
16
|
-
*
|
|
17
|
-
* E uma proibição: **nada alcançável daqui escreve.** Os candidatos óbvios ao
|
|
18
|
-
* erro — `scripts/generate-ai-pin-props.js` e
|
|
19
|
-
* `scripts/generate-standards-registry.js` — escrevem por padrão e NÃO entram
|
|
20
|
-
* no grafo de imports deste módulo. É a lição do T10 da #90, onde a auditoria
|
|
21
|
-
* ia ALOCAR um bloco de porta ao auditar, inventando o estado que reportava.
|
|
22
|
-
* O canário assere a lista de chamadas feitas, não só o retorno.
|
|
23
|
-
*/
|
|
1
|
+
// `skip` sai com `reason` obrigatória, e `null` de uma checagem é DESCONHECIDO, nunca "não achei".
|
|
2
|
+
// Nada alcançável daqui escreve: os geradores de `scripts/` ficam fora do grafo de imports.
|
|
24
3
|
|
|
25
4
|
import { scanAiPackageRefs } from './ai-project-scan.js';
|
|
26
5
|
import { resolveAiPin, auditPinDrift } from './pin-drift.js';
|
|
@@ -30,7 +9,6 @@ import { auditRenamedApi, auditPromptSource, renamedApiGate } from './code-audit
|
|
|
30
9
|
/** Os cinco ids, na ordem em que o relatório os imprime. */
|
|
31
10
|
export const AI_CHECK_IDS = ['pin-drift', 'removed-api', 'pricing-missing', 'api-per-alias', 'prompt-source'];
|
|
32
11
|
|
|
33
|
-
/** Nome legível de cada checagem. */
|
|
34
12
|
export const AI_CHECK_NAMES = {
|
|
35
13
|
'pin-drift': 'pin × projeto (versões de pacote de IA)',
|
|
36
14
|
'removed-api': 'API renomeada antes do 1.0 GA',
|
|
@@ -42,7 +20,6 @@ export const AI_CHECK_NAMES = {
|
|
|
42
20
|
/** O pacote cuja versão gateia a checagem de API renomeada. */
|
|
43
21
|
export const MAF_PACKAGE = 'Microsoft.Agents.AI';
|
|
44
22
|
|
|
45
|
-
/** As dependências reais. Substituíveis inteiras ou em parte, por canário. */
|
|
46
23
|
export function defaultAiDeps() {
|
|
47
24
|
return {
|
|
48
25
|
scanAiPackageRefs,
|
|
@@ -75,19 +52,8 @@ function skipAll(reason) {
|
|
|
75
52
|
return AI_CHECK_IDS.map((id) => skip(id, reason));
|
|
76
53
|
}
|
|
77
54
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
*
|
|
81
|
-
* As três condições de silêncio total acontecem ANTES de qualquer leitura do
|
|
82
|
-
* pin — e é essa ordem, não o retorno, que o canário assere: sem `.csproj`
|
|
83
|
-
* alcançável, `resolveAiPin` não é sequer chamada.
|
|
84
|
-
*
|
|
85
|
-
* @param {string} targetPath - raiz do projeto
|
|
86
|
-
* @param {object} [options]
|
|
87
|
-
* @param {object|null} [options.config] - `.morph/config/config.json` já lido
|
|
88
|
-
* @param {object} [options.deps] - substituição parcial de `defaultAiDeps()`
|
|
89
|
-
* @returns {Array<{id: string, name: string, status: string, reason?: string, findings: Array}>}
|
|
90
|
-
*/
|
|
55
|
+
// As condições de silêncio total acontecem ANTES de ler o pin: sem `.csproj` alcançável,
|
|
56
|
+
// `resolveAiPin` nem é chamada.
|
|
91
57
|
export function collectAiChecks(targetPath, { config = null, deps = {} } = {}) {
|
|
92
58
|
const d = { ...defaultAiDeps(), ...deps };
|
|
93
59
|
|
|
@@ -114,8 +80,7 @@ export function collectAiChecks(targetPath, { config = null, deps = {} } = {}) {
|
|
|
114
80
|
const unverified = scan.unreadable.slice();
|
|
115
81
|
const checks = [];
|
|
116
82
|
|
|
117
|
-
//
|
|
118
|
-
// Condição 2: sem pin resolvível, a checagem que depende dele sai skip.
|
|
83
|
+
// Sem pin resolvível, a checagem que depende dele sai skip.
|
|
119
84
|
const pin = d.resolveAiPin(targetPath);
|
|
120
85
|
if (!pin) {
|
|
121
86
|
checks.push(skip('pin-drift', 'nenhum `ai-pin.json` resolvível (nem em `.morph/framework/`, nem '
|
|
@@ -127,7 +92,6 @@ export function collectAiChecks(targetPath, { config = null, deps = {} } = {}) {
|
|
|
127
92
|
: resolved('pin-drift', findings, { unverified }));
|
|
128
93
|
}
|
|
129
94
|
|
|
130
|
-
// ── removed-api ──────────────────────────────────────────────────────────
|
|
131
95
|
const maf = scan.packages.find((p) => p.name === MAF_PACKAGE) ?? null;
|
|
132
96
|
const gate = d.renamedApiGate(maf);
|
|
133
97
|
if (gate !== null) {
|
|
@@ -139,7 +103,6 @@ export function collectAiChecks(targetPath, { config = null, deps = {} } = {}) {
|
|
|
139
103
|
: resolved('removed-api', findings));
|
|
140
104
|
}
|
|
141
105
|
|
|
142
|
-
// ── pricing-missing e api-per-alias ──────────────────────────────────────
|
|
143
106
|
const registry = d.locateAiRegistry(targetPath, config);
|
|
144
107
|
const noRegistry = 'nenhum registry de modelos encontrado — nem `project.ai.modelRegistry` no '
|
|
145
108
|
+ 'config, nem um `.json` com `defaultAlias` + mapa de aliases, nem uma seção `Pricing` num '
|
|
@@ -155,7 +118,6 @@ export function collectAiChecks(targetPath, { config = null, deps = {} } = {}) {
|
|
|
155
118
|
: noRegistry)
|
|
156
119
|
: resolved('api-per-alias', perAlias));
|
|
157
120
|
|
|
158
|
-
// ── prompt-source ────────────────────────────────────────────────────────
|
|
159
121
|
const prompts = d.auditPromptSource(targetPath);
|
|
160
122
|
checks.push(prompts === null
|
|
161
123
|
? skip('prompt-source', 'nenhum `.cs` alcançável a partir desta raiz')
|
|
@@ -164,19 +126,12 @@ export function collectAiChecks(targetPath, { config = null, deps = {} } = {}) {
|
|
|
164
126
|
return checks;
|
|
165
127
|
}
|
|
166
128
|
|
|
167
|
-
/**
|
|
168
|
-
* Código de saída do `doctor --ai`: 1 quando há ao menos um `error`, 0 caso
|
|
169
|
-
* contrário. `warn` e `skip` NUNCA mudam o código de saída — só `pin-drift`
|
|
170
|
-
* produz `error`, e só na faixa flutuante, a única classe com prejuízo medido.
|
|
171
|
-
*
|
|
172
|
-
* @param {Array<{status: string}>} checks
|
|
173
|
-
* @returns {0|1}
|
|
174
|
-
*/
|
|
129
|
+
/** `warn` e `skip` nunca mudam o código de saída: só `pin-drift` produz `error`, na faixa flutuante. */
|
|
175
130
|
export function aiExitCode(checks) {
|
|
176
131
|
return checks.some((c) => c.status === 'error') ? 1 : 0;
|
|
177
132
|
}
|
|
178
133
|
|
|
179
|
-
/** true quando
|
|
134
|
+
/** true quando nenhuma checagem teve insumo: o relatório sai em uma linha. */
|
|
180
135
|
export function allSkipped(checks) {
|
|
181
136
|
return checks.length > 0 && checks.every((c) => c.status === 'skip');
|
|
182
137
|
}
|
|
@@ -1,28 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
* de um projeto .NET, para responder "o que este projeto declara sobre pacotes de IA?".
|
|
4
|
-
*
|
|
5
|
-
* Três invariantes deste módulo, todas cobradas por canário:
|
|
6
|
-
*
|
|
7
|
-
* 1. **Nada aqui escreve.** Nenhum import de `fs` que mute, nenhum `spawn`.
|
|
8
|
-
* A auditoria não pode inventar o estado que reporta — é a lição do T10 da
|
|
9
|
-
* #90, onde a auditoria de portas ia ALOCAR um bloco ao auditar.
|
|
10
|
-
* 2. **`null` é DESCONHECIDO, `[]` é "perguntei e não achei".** Colapsar os
|
|
11
|
-
* dois é a doutrina que `src/lib/git/pr-index.js` já pinou neste repositório.
|
|
12
|
-
* Aqui: `scanAiPackageRefs` devolve `null` quando havia arquivos candidatos
|
|
13
|
-
* e NENHUM foi legível; devolve um resultado com `projectFiles: []` quando
|
|
14
|
-
* varreu e não achou `.csproj` nenhum.
|
|
15
|
-
* 3. **Worktree é o checkout de OUTRA feature.** `worktrees/` e `.worktrees/`
|
|
16
|
-
* ficam fora da varredura pela mesma razão pinada em `test-runner.js`:
|
|
17
|
-
* varrê-los reporta sobre código fora da árvore avaliada. Os nomes vêm de
|
|
18
|
-
* `src/lib/worktree/dirs.js`, nunca duplicados aqui.
|
|
19
|
-
*/
|
|
1
|
+
// Nada aqui escreve: a auditoria não pode inventar o estado que reporta. `null` é DESCONHECIDO,
|
|
2
|
+
// `[]` é "perguntei e não achei". Worktrees ficam fora: são o checkout de outra feature.
|
|
20
3
|
|
|
21
4
|
import { readdirSync, readFileSync, statSync, existsSync } from 'node:fs';
|
|
22
5
|
import { join, basename } from 'node:path';
|
|
23
6
|
import { WORKTREE_DIR_NAMES } from '../worktree/dirs.js';
|
|
24
7
|
|
|
25
|
-
/** Diretórios que a varredura nunca desce. */
|
|
26
8
|
export const SCAN_IGNORED_DIRS = new Set([
|
|
27
9
|
'node_modules',
|
|
28
10
|
'bin',
|
|
@@ -31,14 +13,8 @@ export const SCAN_IGNORED_DIRS = new Set([
|
|
|
31
13
|
...WORKTREE_DIR_NAMES,
|
|
32
14
|
]);
|
|
33
15
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
*
|
|
37
|
-
* Deliberadamente curto: a lista existe para decidir se há assunto (condição 3
|
|
38
|
-
* do silêncio do `doctor --ai`), não para catalogar o ecossistema. Um pacote de
|
|
39
|
-
* IA fora desta lista aparece na varredura como pacote comum e, se estiver no
|
|
40
|
-
* pin, `pin-drift` o pega mesmo assim — o pin é a outra metade do predicado.
|
|
41
|
-
*/
|
|
16
|
+
// Deliberadamente curto: decide se há assunto, não cataloga o ecossistema; um pacote de IA fora daqui
|
|
17
|
+
// que esteja no pin ainda é pego por `pin-drift`.
|
|
42
18
|
export const AI_PACKAGE_PREFIXES = [
|
|
43
19
|
'Microsoft.Agents.AI',
|
|
44
20
|
'Microsoft.Extensions.AI',
|
|
@@ -50,10 +26,6 @@ export const AI_PACKAGE_PREFIXES = [
|
|
|
50
26
|
'Anthropic.',
|
|
51
27
|
];
|
|
52
28
|
|
|
53
|
-
/**
|
|
54
|
-
* @param {string} name - nome do pacote NuGet
|
|
55
|
-
* @returns {boolean} true quando o nome casa com um dos prefixos de IA
|
|
56
|
-
*/
|
|
57
29
|
export function isAiPackage(name) {
|
|
58
30
|
if (typeof name !== 'string' || name.length === 0) return false;
|
|
59
31
|
return AI_PACKAGE_PREFIXES.some(
|
|
@@ -61,22 +33,11 @@ export function isAiPackage(name) {
|
|
|
61
33
|
);
|
|
62
34
|
}
|
|
63
35
|
|
|
64
|
-
/** Remove o BOM UTF-8 do início do texto (mesmo cuidado de `doctor.js` e `env.js`). */
|
|
65
36
|
function stripBom(text) {
|
|
66
37
|
return text.charCodeAt(0) === 0xfeff ? text.slice(1) : text;
|
|
67
38
|
}
|
|
68
39
|
|
|
69
|
-
/**
|
|
70
|
-
* Checagem de boa-formação de XML suficiente para MSBuild, sem dependência nova.
|
|
71
|
-
*
|
|
72
|
-
* Não valida schema: valida que as tags fecham na ordem certa e que todo `<`
|
|
73
|
-
* tem o seu `>`. É o predicado que separa "li o arquivo" de "não pude
|
|
74
|
-
* perguntar" — e é ele que faz um `.csproj` truncado virar `unreadable` em vez
|
|
75
|
-
* de virar "sem drift".
|
|
76
|
-
*
|
|
77
|
-
* @param {string} xml
|
|
78
|
-
* @returns {boolean}
|
|
79
|
-
*/
|
|
40
|
+
/** Tags fecham na ordem e todo `<` tem `>`: um `.csproj` truncado vira `unreadable`, não "sem drift". */
|
|
80
41
|
export function isWellFormedXml(xml) {
|
|
81
42
|
const stack = [];
|
|
82
43
|
let i = 0;
|
|
@@ -121,7 +82,6 @@ export function isWellFormedXml(xml) {
|
|
|
121
82
|
return stack.length === 0;
|
|
122
83
|
}
|
|
123
84
|
|
|
124
|
-
/** Todas as tags de um nome, com os atributos crus. */
|
|
125
85
|
function* elements(xml, tagName) {
|
|
126
86
|
const re = new RegExp(`<${tagName}\\b([^>]*?)(/?)>`, 'g');
|
|
127
87
|
let m;
|
|
@@ -130,17 +90,13 @@ function* elements(xml, tagName) {
|
|
|
130
90
|
}
|
|
131
91
|
}
|
|
132
92
|
|
|
133
|
-
/** Valor de um atributo dentro do texto cru de atributos de uma tag. */
|
|
134
93
|
function attr(attrs, name) {
|
|
135
94
|
const m = new RegExp(`\\b${name}\\s*=\\s*"([^"]*)"`, 'i').exec(attrs)
|
|
136
95
|
|| new RegExp(`\\b${name}\\s*=\\s*'([^']*)'`, 'i').exec(attrs);
|
|
137
96
|
return m ? m[1].trim() : null;
|
|
138
97
|
}
|
|
139
98
|
|
|
140
|
-
|
|
141
|
-
* Elemento filho `<Version>x</Version>` de um `<PackageReference>` não
|
|
142
|
-
* auto-fechado. Recebe o XML e o índice logo após a tag de abertura.
|
|
143
|
-
*/
|
|
99
|
+
// Recebe o índice logo após a tag de abertura de um `<PackageReference>` não auto-fechado.
|
|
144
100
|
function childVersion(xml, startIndex, tagName) {
|
|
145
101
|
const close = xml.indexOf(`</${tagName}>`, startIndex);
|
|
146
102
|
if (close === -1) return null;
|
|
@@ -149,18 +105,7 @@ function childVersion(xml, startIndex, tagName) {
|
|
|
149
105
|
return m ? m[1].trim() : null;
|
|
150
106
|
}
|
|
151
107
|
|
|
152
|
-
/**
|
|
153
|
-
* Classifica a string de versão declarada no projeto.
|
|
154
|
-
*
|
|
155
|
-
* - `fixed` — `1.20.0` ou a forma de igualdade exata `[1.20.0]`
|
|
156
|
-
* - `floating` — contém `*`, ou é intervalo (`[1.0,)`, `[1.0,2.0)`, `(,2.0]`)
|
|
157
|
-
* - `unresolved` — referência a propriedade MSBuild (`$(MafVersion)`): a
|
|
158
|
-
* varredura NÃO resolve propriedades, e chutar seria inventar o dado
|
|
159
|
-
* - `absent` — sem versão declarada e sem entrada de CPM
|
|
160
|
-
*
|
|
161
|
-
* @param {string|null} raw
|
|
162
|
-
* @returns {{ kind: 'fixed'|'floating'|'unresolved'|'absent', value: string|null }}
|
|
163
|
-
*/
|
|
108
|
+
/** `unresolved` é referência a propriedade MSBuild (`$(X)`): a varredura não resolve, e chutar inventaria o dado. */
|
|
164
109
|
export function classifyVersion(raw) {
|
|
165
110
|
if (raw === null || raw === undefined || String(raw).trim() === '') {
|
|
166
111
|
return { kind: 'absent', value: null };
|
|
@@ -175,18 +120,7 @@ export function classifyVersion(raw) {
|
|
|
175
120
|
return { kind: 'fixed', value };
|
|
176
121
|
}
|
|
177
122
|
|
|
178
|
-
/**
|
|
179
|
-
* Coleta recursiva de arquivos que casam com um predicado, respeitando as
|
|
180
|
-
* exclusões de `SCAN_IGNORED_DIRS`.
|
|
181
|
-
*
|
|
182
|
-
* Exportada porque `registry-audit.js` e `code-audit.js` precisam da MESMA
|
|
183
|
-
* travessia com as MESMAS exclusões. Uma segunda cópia da lista de diretórios
|
|
184
|
-
* ignorados é como um dos três volta a varrer `worktrees/` sozinho.
|
|
185
|
-
*
|
|
186
|
-
* @param {string} root
|
|
187
|
-
* @param {(name: string, fullPath: string) => boolean} matches
|
|
188
|
-
* @returns {string[]}
|
|
189
|
-
*/
|
|
123
|
+
/** Exportada para que registry-audit e code-audit usem a mesma travessia com as mesmas exclusões. */
|
|
190
124
|
export function collectProjectFiles(root, matches) {
|
|
191
125
|
const out = [];
|
|
192
126
|
if (!root || !existsSync(root)) return out;
|
|
@@ -194,7 +128,6 @@ export function collectProjectFiles(root, matches) {
|
|
|
194
128
|
return out;
|
|
195
129
|
}
|
|
196
130
|
|
|
197
|
-
/** Coleta recursiva de arquivos que casam com um predicado, respeitando as exclusões. */
|
|
198
131
|
function collectFiles(root, matches, out, depth = 0) {
|
|
199
132
|
if (depth > 24) return;
|
|
200
133
|
let entries;
|
|
@@ -208,8 +141,7 @@ function collectFiles(root, matches, out, depth = 0) {
|
|
|
208
141
|
const full = join(root, name);
|
|
209
142
|
let isDir = entry.isDirectory();
|
|
210
143
|
if (entry.isSymbolicLink()) {
|
|
211
|
-
// Junction/symlink
|
|
212
|
-
// worktree do Windows enxerga `.claude/`). Não descemos.
|
|
144
|
+
// Junction/symlink pode apontar para fora da árvore (assim um worktree do Windows enxerga `.claude/`).
|
|
213
145
|
continue;
|
|
214
146
|
}
|
|
215
147
|
if (isDir) {
|
|
@@ -221,23 +153,8 @@ function collectFiles(root, matches, out, depth = 0) {
|
|
|
221
153
|
}
|
|
222
154
|
}
|
|
223
155
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
*
|
|
227
|
-
* Sem limite de 10 arquivos (o de `scanStackDependencies`, que é do relatório
|
|
228
|
-
* de arquitetura e tem outro consumidor): uma solução .NET real tem dezenas de
|
|
229
|
-
* `.csproj`, e cortar em 10 é reportar sobre uma amostra chamando-a de todo.
|
|
230
|
-
*
|
|
231
|
-
* @param {string} root - raiz da varredura (o projeto)
|
|
232
|
-
* @returns {null | {
|
|
233
|
-
* projectFiles: string[],
|
|
234
|
-
* cpmFiles: string[],
|
|
235
|
-
* unreadable: string[],
|
|
236
|
-
* packages: Array<{ name: string, file: string, ai: boolean,
|
|
237
|
-
* version: string|null, versionKind: string,
|
|
238
|
-
* declared: string|null, fromCpm: boolean }>
|
|
239
|
-
* }} `null` = não pude perguntar (havia candidatos e nenhum era legível).
|
|
240
|
-
*/
|
|
156
|
+
// Sem limite de arquivos: cortar uma solução real é reportar sobre uma amostra chamando-a de todo.
|
|
157
|
+
// `null` = havia candidatos e nenhum era legível.
|
|
241
158
|
export function scanAiPackageRefs(root) {
|
|
242
159
|
if (!root || !existsSync(root)) return null;
|
|
243
160
|
try {
|
|
@@ -280,7 +197,7 @@ export function scanAiPackageRefs(root) {
|
|
|
280
197
|
}
|
|
281
198
|
|
|
282
199
|
// Central Package Management primeiro: um PackageReference sem Version num
|
|
283
|
-
// projeto com CPM não é achado de versão ausente
|
|
200
|
+
// projeto com CPM não é achado de versão ausente.
|
|
284
201
|
const cpm = new Map();
|
|
285
202
|
for (const file of cpmFiles) {
|
|
286
203
|
const xml = readable.get(file);
|
|
@@ -319,28 +236,17 @@ export function scanAiPackageRefs(root) {
|
|
|
319
236
|
}
|
|
320
237
|
}
|
|
321
238
|
|
|
322
|
-
//
|
|
323
|
-
// .csproj que o referencie ainda declara a intenção — mas não é uso, e este
|
|
324
|
-
// módulo reporta declaração de uso. Fica de fora, de propósito.
|
|
239
|
+
// PackageVersion de IA sem .csproj que o referencie declara intenção, não uso: fica de fora.
|
|
325
240
|
|
|
326
241
|
return { projectFiles, cpmFiles, unreadable, packages };
|
|
327
242
|
}
|
|
328
243
|
|
|
329
|
-
/**
|
|
330
|
-
* Versão declarada de um pacote específico, ou `null` quando o projeto não a
|
|
331
|
-
* declara de forma resolvível. Usado pelo gate do `removed-api` (só roda com
|
|
332
|
-
* `Microsoft.Agents.AI` >= 1.0) e pelo relatório.
|
|
333
|
-
*
|
|
334
|
-
* @param {ReturnType<typeof scanAiPackageRefs>} scan
|
|
335
|
-
* @param {string} name
|
|
336
|
-
* @returns {{ name: string, version: string|null, versionKind: string, declared: string|null, file: string } | null}
|
|
337
|
-
*/
|
|
244
|
+
/** `null` quando o projeto não declara o pacote de forma resolvível. */
|
|
338
245
|
export function findPackage(scan, name) {
|
|
339
246
|
if (!scan || !Array.isArray(scan.packages)) return null;
|
|
340
247
|
return scan.packages.find((p) => p.name === name) ?? null;
|
|
341
248
|
}
|
|
342
249
|
|
|
343
|
-
/** Nomes de arquivo (sem diretório) dos projetos varridos — para mensagens. */
|
|
344
250
|
export function projectFileNames(scan) {
|
|
345
251
|
if (!scan) return [];
|
|
346
252
|
return scan.projectFiles.map((f) => basename(f));
|
package/src/lib/ai/code-audit.js
CHANGED
|
@@ -1,26 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
* (a) `removed-api` — nomes renomeados ANTES do 1.0 GA do `Microsoft.Agents.AI`
|
|
5
|
-
* (b) `prompt-source` — `Instructions` recebendo literal de string no ponto de
|
|
6
|
-
* construção do agente
|
|
7
|
-
*
|
|
8
|
-
* Nenhuma das duas é grep cego, e as duas são `warn`:
|
|
9
|
-
*
|
|
10
|
-
* - A #93 apurou, e o standard `ai-agents/agent-session.md` registra, que
|
|
11
|
-
* `AgentThread` → `AgentSession` e `GetNewThread()` → `CreateSessionAsync()`
|
|
12
|
-
* aconteceram no ciclo PRÉ-GA. Dentro do `Microsoft.Agents.AI` >= 1.0 os
|
|
13
|
-
* nomes antigos são nomes mortos — mas o MESMO identificador pertence a
|
|
14
|
-
* outros SDKs de agente na mesma solução, e é por isso que o achado é aviso
|
|
15
|
-
* e a mensagem manda CONFERIR a qual cliente a chamada pertence, em vez de
|
|
16
|
-
* afirmar defeito. Sem o pacote declarado em >= 1.0 a checagem nem roda.
|
|
17
|
-
*
|
|
18
|
-
* - O `prompt-source` é deliberadamente conservador: acusa só o literal no
|
|
19
|
-
* ponto de construção. Leitura de arquivo, banco, configuração ou constante
|
|
20
|
-
* nomeada não é acusada. Falso negativo é aceitável; falso positivo não —
|
|
21
|
-
* uma checagem que grita em código correto é desligada pelo usuário na
|
|
22
|
-
* primeira semana.
|
|
23
|
-
*/
|
|
1
|
+
// As duas checagens são `warn`: o nome renomeado no ciclo pré-GA é o mesmo identificador de outros SDKs
|
|
2
|
+
// de agente, então a mensagem manda conferir. `prompt-source` acusa só o literal no ponto de construção:
|
|
3
|
+
// falso negativo é aceitável, falso positivo faz o usuário desligar a checagem.
|
|
24
4
|
|
|
25
5
|
import { readFileSync } from 'node:fs';
|
|
26
6
|
import { relative } from 'node:path';
|
|
@@ -40,22 +20,11 @@ function lineOf(text, index) {
|
|
|
40
20
|
return line;
|
|
41
21
|
}
|
|
42
22
|
|
|
43
|
-
/** Todos os `.cs` do projeto, com as mesmas exclusões da varredura de `.csproj`. */
|
|
44
23
|
export function collectCsharpFiles(root) {
|
|
45
24
|
return collectProjectFiles(root, (name) => name.toLowerCase().endsWith('.cs'));
|
|
46
25
|
}
|
|
47
26
|
|
|
48
|
-
/**
|
|
49
|
-
* Menor major que a versão declarada pode assumir, ou `null` quando a
|
|
50
|
-
* declaração não permite decidir.
|
|
51
|
-
*
|
|
52
|
-
* `1.0.*` e `[1.0,)` têm limite inferior 1 — sobem, nunca descem. `$(Prop)` e
|
|
53
|
-
* versão ausente NÃO permitem decidir, e decidir mesmo assim seria inventar o
|
|
54
|
-
* dado que a checagem reporta.
|
|
55
|
-
*
|
|
56
|
-
* @param {{ declared: string|null, versionKind: string }|null} pkg
|
|
57
|
-
* @returns {number|null}
|
|
58
|
-
*/
|
|
27
|
+
/** `null` quando a declaração não permite decidir (`$(Prop)`, ausente): decidir inventaria o dado. */
|
|
59
28
|
export function lowerBoundMajor(pkg) {
|
|
60
29
|
if (!pkg) return null;
|
|
61
30
|
if (pkg.versionKind === 'unresolved' || pkg.versionKind === 'absent') return null;
|
|
@@ -66,12 +35,7 @@ export function lowerBoundMajor(pkg) {
|
|
|
66
35
|
return m ? Number(m[1]) : null;
|
|
67
36
|
}
|
|
68
37
|
|
|
69
|
-
/**
|
|
70
|
-
* Por que o `removed-api` NÃO roda, quando não roda.
|
|
71
|
-
*
|
|
72
|
-
* @param {{ declared: string|null, versionKind: string }|null} mafPackage
|
|
73
|
-
* @returns {string|null} razão do "não verificado", ou `null` quando pode rodar
|
|
74
|
-
*/
|
|
38
|
+
/** A razão do "não verificado", ou `null` quando pode rodar. */
|
|
75
39
|
export function renamedApiGate(mafPackage) {
|
|
76
40
|
if (!mafPackage) {
|
|
77
41
|
return 'o projeto não declara `Microsoft.Agents.AI` — os nomes antigos não pertencem a este código';
|
|
@@ -88,14 +52,7 @@ export function renamedApiGate(mafPackage) {
|
|
|
88
52
|
return null;
|
|
89
53
|
}
|
|
90
54
|
|
|
91
|
-
/**
|
|
92
|
-
* (a) Uso de API renomeada antes do 1.0 GA.
|
|
93
|
-
*
|
|
94
|
-
* @param {string} root
|
|
95
|
-
* @param {{ mafPackage?: object|null }} [options]
|
|
96
|
-
* @returns {null | Array<{ id: string, severity: 'warn', symbol: string, file: string, line: number, message: string }>}
|
|
97
|
-
* `null` = não pude perguntar (gate fechado ou raiz ilegível).
|
|
98
|
-
*/
|
|
55
|
+
/** `null` = não pude perguntar (gate fechado ou raiz ilegível). */
|
|
99
56
|
export function auditRenamedApi(root, { mafPackage = null } = {}) {
|
|
100
57
|
if (renamedApiGate(mafPackage) !== null) return null;
|
|
101
58
|
|
|
@@ -131,22 +88,9 @@ export function auditRenamedApi(root, { mafPackage = null } = {}) {
|
|
|
131
88
|
return findings;
|
|
132
89
|
}
|
|
133
90
|
|
|
134
|
-
|
|
135
|
-
* `Instructions` recebendo literal de string no ponto de construção.
|
|
136
|
-
*
|
|
137
|
-
* Casa `Instructions = "…"`, `Instructions = @"…"`, `Instructions = $"…"`,
|
|
138
|
-
* `Instructions = """…"""` e o argumento nomeado `instructions: "…"`.
|
|
139
|
-
* NÃO casa atribuição de variável, chamada, `await`, constante nomeada nem
|
|
140
|
-
* literal dentro de comentário.
|
|
141
|
-
*/
|
|
91
|
+
// NÃO casa atribuição de variável, chamada, `await`, constante nomeada nem literal dentro de comentário.
|
|
142
92
|
const INSTRUCTIONS_LITERAL_RE = /(?<![\w."])(?:Instructions\s*=(?!=)|instructions\s*:)\s*(?:[$@]){0,2}"/g;
|
|
143
93
|
|
|
144
|
-
/**
|
|
145
|
-
* (b) Prompt sem fonte declarada.
|
|
146
|
-
*
|
|
147
|
-
* @param {string} root
|
|
148
|
-
* @returns {null | Array<{ id: string, severity: 'warn', file: string, line: number, message: string }>}
|
|
149
|
-
*/
|
|
150
94
|
export function auditPromptSource(root) {
|
|
151
95
|
const files = collectCsharpFiles(root);
|
|
152
96
|
if (files.length === 0) return null;
|
package/src/lib/ai/pin-drift.js
CHANGED
|
@@ -1,70 +1,27 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
* o `.csproj` do projeto declara.
|
|
4
|
-
*
|
|
5
|
-
* É a ÚNICA das cinco checagens do `doctor --ai` que produz `error`, e só na
|
|
6
|
-
* faixa flutuante. O motivo é prejuízo medido, não estética: uma faixa `2.*`
|
|
7
|
-
* sobe sozinha para uma versão com assinatura mudada e o `MissingMethodException`
|
|
8
|
-
* chega DEPOIS de a chamada ao modelo ter sido paga. As outras quatro checagens
|
|
9
|
-
* informam — uma checagem que grita em código correto é desligada pelo usuário
|
|
10
|
-
* na primeira semana.
|
|
11
|
-
*
|
|
12
|
-
* O pin é lido por `readAiPin` (#92), nunca por um segundo leitor: duas cópias
|
|
13
|
-
* do mesmo fato divergem, e é para isso que a Dependency Matrix existe.
|
|
14
|
-
*/
|
|
1
|
+
// Única checagem que produz `error`, e só na faixa flutuante: `2.*` sobe sozinha para uma assinatura mudada
|
|
2
|
+
// e o `MissingMethodException` chega depois de a chamada ao modelo ter sido paga.
|
|
15
3
|
|
|
16
4
|
import { join } from 'node:path';
|
|
17
5
|
import { readAiPin } from '../standards/verified-against.js';
|
|
18
6
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
*
|
|
22
|
-
* Ordem: (1) `<target>/.morph/framework/ai-pin.json` — a régua com que aquele
|
|
23
|
-
* projeto foi instalado; (2) o pin do pacote da CLI. Nenhum dos dois resolve →
|
|
24
|
-
* `null`, que o relatório traduz em "não verificado". Falta de régua é ausência
|
|
25
|
-
* de dado, nunca defeito do projeto: é literalmente o estado de todo projeto
|
|
26
|
-
* cujo framework é anterior ao pin.
|
|
27
|
-
*
|
|
28
|
-
* @param {string} targetPath - raiz do projeto
|
|
29
|
-
* @returns {{ pinnedAt: string, packages: Record<string, {version: string}> } | null}
|
|
30
|
-
*/
|
|
7
|
+
// O pin instalado no projeto, senão o do pacote da CLI; nenhum resolve → `null` ("não verificado"):
|
|
8
|
+
// falta de régua é ausência de dado, nunca defeito do projeto.
|
|
31
9
|
export function resolveAiPin(targetPath) {
|
|
32
10
|
const roots = [];
|
|
33
11
|
if (targetPath) roots.push(join(targetPath, '.morph'));
|
|
34
|
-
roots.push(undefined);
|
|
12
|
+
roots.push(undefined);
|
|
35
13
|
|
|
36
14
|
for (const root of roots) {
|
|
37
15
|
try {
|
|
38
16
|
const pin = root === undefined ? readAiPin() : readAiPin(root);
|
|
39
17
|
if (pin && pin.packages && Object.keys(pin.packages).length > 0) return pin;
|
|
40
|
-
} catch {
|
|
41
|
-
// Ausente ou inválido nesta raiz — tenta a próxima.
|
|
42
|
-
}
|
|
18
|
+
} catch {}
|
|
43
19
|
}
|
|
44
20
|
return null;
|
|
45
21
|
}
|
|
46
22
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
*
|
|
50
|
-
* Três classes de achado, e a diferença entre elas é de quem é o defeito:
|
|
51
|
-
*
|
|
52
|
-
* | classe | severidade | de quem é |
|
|
53
|
-
* |---|---|---|
|
|
54
|
-
* | pacote do pin declarado com faixa flutuante | `error` | do projeto |
|
|
55
|
-
* | pacote do pin declarado com versão fixa ≠ pin | `warn` | do projeto |
|
|
56
|
-
* | pacote de IA presente no projeto e ausente do pin | `warn` | **do pin** (lacuna) |
|
|
57
|
-
*
|
|
58
|
-
* A terceira classe é descrita como lacuna do PIN de propósito: medido,
|
|
59
|
-
* `Microsoft.Agents.AI.OpenAI` aparece em três repositórios do acervo e não
|
|
60
|
-
* está no pin. Acusar o projeto ali seria acusar o certo pelo errado.
|
|
61
|
-
*
|
|
62
|
-
* @param {object} params
|
|
63
|
-
* @param {ReturnType<import('./ai-project-scan.js').scanAiPackageRefs>} params.scan
|
|
64
|
-
* @param {ReturnType<typeof resolveAiPin>} params.pin
|
|
65
|
-
* @returns {null | Array<{ id: string, severity: 'error'|'warn', package: string, message: string }>}
|
|
66
|
-
* `null` = não pude perguntar (sem varredura ou sem pin).
|
|
67
|
-
*/
|
|
23
|
+
// Pacote de IA presente no projeto e ausente do pin é lacuna do PIN, não do projeto.
|
|
24
|
+
// `null` = não pude perguntar (sem varredura ou sem pin).
|
|
68
25
|
export function auditPinDrift({ scan, pin } = {}) {
|
|
69
26
|
if (!scan || !pin || !pin.packages) return null;
|
|
70
27
|
|