@mmerterden/multi-agent-pipeline 12.5.0 → 12.7.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/CHANGELOG.md +213 -0
- package/README.md +19 -19
- package/docs/FIGMA_PIPELINE.md +34 -34
- package/docs/adr/0001-three-model-triage.md +12 -12
- package/docs/adr/0002-instruction-driven-flag.md +5 -5
- package/docs/adr/0003-unified-shared-skills.md +5 -5
- package/docs/adr/0004-zero-dependency-philosophy.md +5 -5
- package/docs/adr/0005-lazy-phase-docs.md +2 -2
- package/docs/adr/0006-skills-core-external-split.md +6 -6
- package/docs/adr/0007-multi-tool-adapter-framework.md +19 -19
- package/docs/adr/0008-installer-modularization-and-secret-leak-defense.md +19 -19
- package/docs/adr/README.md +1 -1
- package/docs/best-practices.md +3 -3
- package/docs/features.md +48 -28
- package/docs/performance.md +16 -16
- package/docs/recovery-guide.md +39 -39
- package/index.js +11 -5
- package/install/_common.mjs +5 -11
- package/install/_copilot-instructions.mjs +2 -2
- package/install/_dev-only-files.mjs +2 -1
- package/install/_platform-filter.mjs +1 -1
- package/install/_telemetry.mjs +1 -1
- package/install/claude.mjs +10 -9
- package/install/copilot.mjs +10 -19
- package/install/index.mjs +7 -15
- package/install/templates/copilot-instructions.md +54 -54
- package/install.js +1 -1
- package/package.json +15 -11
- package/pipeline/agents/security-auditor.md +1 -1
- package/pipeline/commands/archive-guard.md +5 -5
- package/pipeline/commands/multi-agent/SKILL.md +3 -1
- package/pipeline/commands/multi-agent/analysis/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/analysis-resolve/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/autopilot/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/build-optimize/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/create-jira/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/design-check/SKILL.md +287 -0
- package/pipeline/commands/multi-agent/dev/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/dev-autopilot/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/dev-local/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/dev-local-autopilot/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/diff-explain/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/finish/SKILL.md +6 -6
- package/pipeline/commands/multi-agent/forget/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/garbage-collect/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/help/SKILL.md +46 -6
- package/pipeline/commands/multi-agent/issue/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/jira/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/kill/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/language/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/local/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/local-autopilot/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/log/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/manual-test/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/prune-logs/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/purge/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/refactor/SKILL.md +107 -19
- package/pipeline/commands/multi-agent/resume/SKILL.md +2 -2
- package/pipeline/commands/multi-agent/review/SKILL.md +2 -2
- package/pipeline/commands/multi-agent/review-issue/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/review-jira/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/routines/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/save/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/scan/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/search/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/setup/SKILL.md +2 -2
- package/pipeline/commands/multi-agent/stack/SKILL.md +3 -3
- package/pipeline/commands/multi-agent/status/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/sync/SKILL.md +123 -16
- package/pipeline/commands/multi-agent/test/SKILL.md +2 -2
- package/pipeline/commands/multi-agent/uninstall/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/update/SKILL.md +3 -3
- package/pipeline/commands/sim-test.md +5 -5
- package/pipeline/eval/run-metrics-fixture.json +60 -13
- package/pipeline/lib/account-resolver.sh +1 -1
- package/pipeline/lib/channels-multi-repo.sh +1 -1
- package/pipeline/lib/context-link-extractor.sh +1 -1
- package/pipeline/lib/credential-store.sh +33 -1
- package/pipeline/lib/fetch-confluence.sh +1 -1
- package/pipeline/lib/fetch-crashlytics.sh +1 -1
- package/pipeline/lib/fetch-fortify.sh +1 -1
- package/pipeline/lib/fetch-graylog.sh +1 -1
- package/pipeline/lib/fetch-swagger.sh +1 -1
- package/pipeline/lib/issue-fetcher.sh +1 -1
- package/pipeline/lib/multi-repo-pipeline.sh +1 -1
- package/pipeline/lib/repo-cache.sh +1 -1
- package/pipeline/lib/submodule-detector.sh +1 -1
- package/pipeline/multi-agent-refs/component-dispatch.md +1 -1
- package/pipeline/multi-agent-refs/component-generation.md +121 -0
- package/pipeline/multi-agent-refs/cross-cli-contract.md +4 -4
- package/pipeline/multi-agent-refs/features/model-fallback.md +2 -2
- package/pipeline/multi-agent-refs/phases/phase-3-dev.md +2 -2
- package/pipeline/multi-agent-refs/phases/phase-4-review.md +1 -1
- package/pipeline/multi-agent-refs/phases/phase-5-test.md +4 -4
- package/pipeline/preferences-template.json +23 -12
- package/pipeline/schemas/agent-state.schema.json +125 -5
- package/pipeline/schemas/analysis-output.schema.json +18 -4
- package/pipeline/schemas/analysis-spec.schema.json +120 -32
- package/pipeline/schemas/clarify-output.schema.json +15 -5
- package/pipeline/schemas/design-check-config.schema.json +183 -0
- package/pipeline/schemas/dev-critic-output.schema.json +20 -5
- package/pipeline/schemas/figma-project-config.schema.json +42 -10
- package/pipeline/schemas/learnings-ledger.schema.json +10 -2
- package/pipeline/schemas/migrations/figma-config-1.0.0-to-2.0.0.mjs +1 -4
- package/pipeline/schemas/migrations/prefs-2.0.0-to-2.1.0.mjs +24 -7
- package/pipeline/schemas/migrations/state-2.0.0-to-2.1.0.mjs +30 -12
- package/pipeline/schemas/plan-todos.schema.json +6 -3
- package/pipeline/schemas/planning-output.schema.json +5 -1
- package/pipeline/schemas/prefs.schema.json +215 -197
- package/pipeline/schemas/test-gap.schema.json +5 -5
- package/pipeline/schemas/token-budget.json +8 -8
- package/pipeline/schemas/triage-corpus.schema.json +1 -1
- package/pipeline/scripts/README.md +7 -5
- package/pipeline/scripts/aggregate-metrics.mjs +18 -6
- package/pipeline/scripts/build-skills-index.mjs +6 -2
- package/pipeline/scripts/build-stack-plugins.mjs +142 -39
- package/pipeline/scripts/check-derived-drift.mjs +196 -0
- package/pipeline/scripts/check-md-links.mjs +6 -2
- package/pipeline/scripts/classify-plan-safety.mjs +26 -8
- package/pipeline/scripts/cost-budget-check.mjs +11 -6
- package/pipeline/scripts/cost-table.json +1 -1
- package/pipeline/scripts/diff-explain.mjs +7 -3
- package/pipeline/scripts/diff-risk-score.mjs +13 -3
- package/pipeline/scripts/eval-golden-tasks-live.mjs +8 -3
- package/pipeline/scripts/eval-golden-tasks.mjs +21 -9
- package/pipeline/scripts/eval-intent.mjs +8 -4
- package/pipeline/scripts/eval-mine-corpus.mjs +14 -4
- package/pipeline/scripts/evidence-gate.mjs +7 -2
- package/pipeline/scripts/fixtures/install-layout.tsv +7 -7
- package/pipeline/scripts/gen-mode-dispatch.mjs +38 -21
- package/pipeline/scripts/gen-skills-index.mjs +18 -3
- package/pipeline/scripts/learning-curve.mjs +13 -3
- package/pipeline/scripts/learnings-ledger.mjs +119 -34
- package/pipeline/scripts/lint-mcp-refs.mjs +218 -0
- package/pipeline/scripts/lint-skills.mjs +20 -9
- package/pipeline/scripts/localize-commands.mjs +6 -1
- package/pipeline/scripts/match-skills.mjs +15 -4
- package/pipeline/scripts/memory-load.sh +5 -1
- package/pipeline/scripts/migrate-prefs.mjs +33 -16
- package/pipeline/scripts/phase-tracker.sh +3 -1
- package/pipeline/scripts/render-work-summary.sh +4 -1
- package/pipeline/scripts/repo-map.mjs +110 -64
- package/pipeline/scripts/review-scope.mjs +7 -1
- package/pipeline/scripts/routine-registry.mjs +4 -9
- package/pipeline/scripts/run-aggregator.mjs +11 -5
- package/pipeline/scripts/run-metrics.mjs +13 -8
- package/pipeline/scripts/run-smokes.mjs +57 -3
- package/pipeline/scripts/scorecard.mjs +258 -0
- package/pipeline/scripts/smoke-command-inventory.sh +81 -0
- package/pipeline/scripts/smoke-commands-skills-parity.sh +1 -1
- package/pipeline/scripts/smoke-compliance-skills.sh +4 -4
- package/pipeline/scripts/smoke-context-budget.sh +72 -0
- package/pipeline/scripts/smoke-cross-cli-behavior.sh +12 -2
- package/pipeline/scripts/smoke-generate-issue.sh +6 -5
- package/pipeline/scripts/smoke-model-fallback.sh +1 -1
- package/pipeline/scripts/smoke-no-mcp-in-dev-phases.sh +86 -7
- package/pipeline/scripts/smoke-own-punctuation.sh +103 -0
- package/pipeline/scripts/smoke-per-repo-memory.sh +2 -2
- package/pipeline/scripts/smoke-review-readiness.sh +3 -2
- package/pipeline/scripts/smoke-schema-validation.sh +19 -5
- package/pipeline/scripts/smoke-shadow-git.sh +4 -2
- package/pipeline/scripts/smoke-workflow-audit.sh +43 -11
- package/pipeline/scripts/smoke-write-state.sh +49 -5
- package/pipeline/scripts/test-gap-rules/android.json +11 -11
- package/pipeline/scripts/test-gap-rules/ios.json +16 -11
- package/pipeline/scripts/test-gap-rules/node.json +19 -7
- package/pipeline/scripts/test-gap-rules/python.json +10 -4
- package/pipeline/scripts/test-gap-scan.mjs +44 -12
- package/pipeline/scripts/test-integrity-gate.mjs +5 -1
- package/pipeline/scripts/token-budget-report.mjs +44 -21
- package/pipeline/scripts/triage-memory.mjs +142 -32
- package/pipeline/scripts/uninstall.mjs +75 -31
- package/pipeline/scripts/validate-analysis-doc.mjs +15 -5
- package/pipeline/scripts/validate-diff-risk.mjs +32 -18
- package/pipeline/scripts/validate-test-gap.mjs +17 -7
- package/pipeline/scripts/validate-triage.mjs +17 -5
- package/pipeline/scripts/write-state.mjs +32 -9
- package/pipeline/skills/.skill-manifest.json +24 -8
- package/pipeline/skills/.skills-index.json +125 -89
- package/pipeline/skills/shared/README.md +65 -61
- package/pipeline/skills/shared/core/apple-archive-compliance/SKILL.md +10 -8
- package/pipeline/skills/shared/core/google-play-compliance/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent/SKILL.md +26 -279
- package/pipeline/skills/shared/core/multi-agent-analysis/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-analysis-resolve/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-autopilot/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-build-optimize/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-create-jira/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-design-check/SKILL.md +248 -0
- package/pipeline/skills/shared/core/multi-agent-dev/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-dev-autopilot/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-dev-local/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-dev-local-autopilot/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-diff-explain/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-finish/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-forget/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-garbage-collect/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-help/SKILL.md +23 -1
- package/pipeline/skills/shared/core/multi-agent-issue/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-jira/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-kill/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-language/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-local/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-local-autopilot/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-log/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-manual-test/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-prune-logs/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-purge/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-refactor/SKILL.md +82 -18
- package/pipeline/skills/shared/core/multi-agent-resume/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-review/SKILL.md +2 -2
- package/pipeline/skills/shared/core/multi-agent-review-issue/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-review-jira/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-routines/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-save/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-scan/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-search/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-setup/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-stack/SKILL.md +3 -3
- package/pipeline/skills/shared/core/multi-agent-status/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-sync/SKILL.md +51 -9
- package/pipeline/skills/shared/core/multi-agent-test/SKILL.md +2 -2
- package/pipeline/skills/shared/core/multi-agent-uninstall/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-update/SKILL.md +1 -1
- package/pipeline/skills/shared/external/accessibility-compliance-accessibility-audit/SKILL.md +1 -1
- package/pipeline/skills/shared/external/agent-introspection-debugging/SKILL.md +4 -4
- package/pipeline/skills/shared/external/agentflow/SKILL.md +1 -1
- package/pipeline/skills/shared/external/android-jetpack-compose-expert/SKILL.md +1 -1
- package/pipeline/skills/shared/external/android_ui_verification/SKILL.md +1 -1
- package/pipeline/skills/shared/external/api-patterns/SKILL.md +1 -1
- package/pipeline/skills/shared/external/api-security-best-practices/SKILL.md +1 -1
- package/pipeline/skills/shared/external/app-store-changelog/SKILL.md +1 -1
- package/pipeline/skills/shared/external/backlog/BACKLOG.md +1 -1
- package/pipeline/skills/shared/external/backlog/SKILL.md +12 -12
- package/pipeline/skills/shared/external/ci-cd-pipelines/SKILL.md +1 -1
- package/pipeline/skills/shared/external/context-compression/SKILL.md +1 -1
- package/pipeline/skills/shared/external/council/SKILL.md +3 -3
- package/pipeline/skills/shared/external/css-modern/SKILL.md +1 -1
- package/pipeline/skills/shared/external/database-patterns/SKILL.md +1 -1
- package/pipeline/skills/shared/external/debugging-strategies/SKILL.md +1 -1
- package/pipeline/skills/shared/external/docker-expert/SKILL.md +1 -1
- package/pipeline/skills/shared/external/fastapi-pro/SKILL.md +1 -1
- package/pipeline/skills/shared/external/firebase/SKILL.md +1 -1
- package/pipeline/skills/shared/external/github-actions-templates/SKILL.md +1 -1
- package/pipeline/skills/shared/external/help-skills/SKILL.md +1 -1
- package/pipeline/skills/shared/external/hig-components-content/SKILL.md +1 -1
- package/pipeline/skills/shared/external/hig-components-layout/SKILL.md +1 -1
- package/pipeline/skills/shared/external/hig-components-status/SKILL.md +1 -1
- package/pipeline/skills/shared/external/hig-components-system/SKILL.md +1 -1
- package/pipeline/skills/shared/external/hig-foundations/SKILL.md +1 -1
- package/pipeline/skills/shared/external/hig-inputs/SKILL.md +1 -1
- package/pipeline/skills/shared/external/hig-patterns/SKILL.md +1 -1
- package/pipeline/skills/shared/external/hig-platforms/SKILL.md +1 -1
- package/pipeline/skills/shared/external/hig-technologies/SKILL.md +1 -1
- package/pipeline/skills/shared/external/html-semantic/SKILL.md +1 -1
- package/pipeline/skills/shared/external/humanizer/SKILL.md +1 -1
- package/pipeline/skills/shared/external/ios-debugger-agent/SKILL.md +1 -1
- package/pipeline/skills/shared/external/ios-developer/SKILL.md +1 -1
- package/pipeline/skills/shared/external/kotlin-coroutines-expert/SKILL.md +1 -1
- package/pipeline/skills/shared/external/macos-menubar-tuist-app/SKILL.md +1 -1
- package/pipeline/skills/shared/external/macos-spm-app-packaging/SKILL.md +1 -1
- package/pipeline/skills/shared/external/monorepo-architect/SKILL.md +1 -1
- package/pipeline/skills/shared/external/nextjs-app-router/SKILL.md +1 -1
- package/pipeline/skills/shared/external/nodejs-backend-patterns/SKILL.md +1 -1
- package/pipeline/skills/shared/external/observability-engineer/SKILL.md +1 -1
- package/pipeline/skills/shared/external/python-patterns/SKILL.md +1 -1
- package/pipeline/skills/shared/external/react-best-practices/SKILL.md +1 -1
- package/pipeline/skills/shared/external/rest-api-design/SKILL.md +1 -1
- package/pipeline/skills/shared/external/search-first/SKILL.md +2 -2
- package/pipeline/skills/shared/external/skill-creator/SKILL.md +12 -12
- package/pipeline/skills/shared/external/skill-creator/audit.md +21 -21
- package/pipeline/skills/shared/external/skill-creator/checklist.md +3 -3
- package/pipeline/skills/shared/external/skill-creator/examples.md +10 -10
- package/pipeline/skills/shared/external/skill-creator/label-check.md +17 -17
- package/pipeline/skills/shared/external/skill-creator/scripts/audit-panel.js +86 -50
- package/pipeline/skills/shared/external/skill-creator/template.md +9 -9
- package/pipeline/skills/shared/external/swift-concurrency-expert/SKILL.md +1 -1
- package/pipeline/skills/shared/external/swiftui-performance-audit/SKILL.md +1 -1
- package/pipeline/skills/shared/external/swiftui-ui-patterns/SKILL.md +1 -1
- package/pipeline/skills/shared/external/swiftui-view-refactor/SKILL.md +1 -1
- package/pipeline/skills/shared/external/tailwind-css/SKILL.md +1 -1
- package/pipeline/skills/shared/external/testing-backend/SKILL.md +1 -1
- package/pipeline/skills/shared/external/typescript-patterns/SKILL.md +1 -1
- package/pipeline/skills/shared/external/vue-composition/SKILL.md +1 -1
- package/pipeline/skills/shared/external/web-accessibility/SKILL.md +1 -1
- package/pipeline/skills/shared/external/web-performance/SKILL.md +1 -1
- package/pipeline/skills/shared/external/web-testing/SKILL.md +1 -1
- package/pipeline/skills/shared/external/xcode-build-benchmark/schemas/build-benchmark.schema.json +9 -49
- package/pipeline/skills/skills-index.md +63 -59
- package/pipeline/scripts/smoke-plugin-validate.sh +0 -64
|
@@ -19,14 +19,14 @@
|
|
|
19
19
|
"required": ["sourcesScanned", "testsScanned", "gapCount", "gapBySeverity"],
|
|
20
20
|
"properties": {
|
|
21
21
|
"sourcesScanned": { "type": "integer", "minimum": 0 },
|
|
22
|
-
"testsScanned":
|
|
23
|
-
"gapCount":
|
|
22
|
+
"testsScanned": { "type": "integer", "minimum": 0 },
|
|
23
|
+
"gapCount": { "type": "integer", "minimum": 0 },
|
|
24
24
|
"gapBySeverity": {
|
|
25
25
|
"type": "object",
|
|
26
26
|
"additionalProperties": false,
|
|
27
27
|
"properties": {
|
|
28
|
-
"blocking":
|
|
29
|
-
"important":
|
|
28
|
+
"blocking": { "type": "integer", "minimum": 0 },
|
|
29
|
+
"important": { "type": "integer", "minimum": 0 },
|
|
30
30
|
"suggestion": { "type": "integer", "minimum": 0 }
|
|
31
31
|
},
|
|
32
32
|
"required": ["blocking", "important", "suggestion"]
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"required": ["sourcePath", "symbol", "kind", "severity", "expectedTestPaths", "hint"],
|
|
42
42
|
"properties": {
|
|
43
43
|
"sourcePath": { "type": "string", "minLength": 1 },
|
|
44
|
-
"symbol":
|
|
44
|
+
"symbol": { "type": "string", "minLength": 1 },
|
|
45
45
|
"kind": {
|
|
46
46
|
"type": "string",
|
|
47
47
|
"description": "Pattern id from the rules file (e.g. public_func, composable_fun, named_export)."
|
|
@@ -3,14 +3,14 @@
|
|
|
3
3
|
"$id": "https://github.com/mmerterden/multi-agent-pipeline/pipeline/schemas/token-budget.json",
|
|
4
4
|
"description": "Per-phase token budget for lazy-loaded pipeline docs. Enforced by smoke-token-budget.sh.",
|
|
5
5
|
"phases": {
|
|
6
|
-
"phase-0-init":
|
|
7
|
-
"phase-1-analysis":
|
|
8
|
-
"phase-2-planning":
|
|
9
|
-
"phase-3-dev":
|
|
10
|
-
"phase-4-review":
|
|
11
|
-
"phase-5-test":
|
|
12
|
-
"phase-6-commit":
|
|
13
|
-
"phase-7-report":
|
|
6
|
+
"phase-0-init": { "max_tokens": 12400, "warn_tokens": 10900 },
|
|
7
|
+
"phase-1-analysis": { "max_tokens": 3750, "warn_tokens": 3300 },
|
|
8
|
+
"phase-2-planning": { "max_tokens": 6500, "warn_tokens": 5750 },
|
|
9
|
+
"phase-3-dev": { "max_tokens": 7900, "warn_tokens": 6950 },
|
|
10
|
+
"phase-4-review": { "max_tokens": 13250, "warn_tokens": 11650 },
|
|
11
|
+
"phase-5-test": { "max_tokens": 2550, "warn_tokens": 2250 },
|
|
12
|
+
"phase-6-commit": { "max_tokens": 6150, "warn_tokens": 5400 },
|
|
13
|
+
"phase-7-report": { "max_tokens": 5600, "warn_tokens": 4950 }
|
|
14
14
|
},
|
|
15
15
|
"total_max_tokens": 51000,
|
|
16
16
|
"note": "Token estimate = ceil(chars / 4). Per-phase budget rule: warn = current+10% (rounded to nearest 50), max = current+25%. Gives ~6 edit cycles of headroom before warn trips - intentionally quiet under normal maintenance, loud when a phase grows unusually. Only the active phase is loaded (lazy). Recalibrated at v10.0.0 after the validator/consistency/simplifier/lesson gate contracts landed in phases 1-4. Recalibrated again at v10.9.0 after the verify-by-test (Phase 4 Step 3.7), update-check (Phase 0 Step 0.6), immutable-test (Phase 3 GREEN) and redTests re-entry contracts landed - Step 3.7 prose was compressed to a pointer into refs/features/verify-by-test.md before the recalibration. Total bumped 50000 -> 51000 at v12.5.0 after the worktree residue/traversal-prune contract (Phase 0 + Phase 5 heal) and the Reflexion causal-diagnosis contract (Phase 4 lesson memory) landed; the prose was compressed first (161 tokens reclaimed) and every per-phase max still passes - only the aggregate needed room."
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"file": { "type": "string" },
|
|
25
25
|
"line": { "type": ["integer", "null"] },
|
|
26
26
|
"issue": { "type": "string", "minLength": 1 },
|
|
27
|
-
"fix":
|
|
27
|
+
"fix": { "type": ["string", "null"] },
|
|
28
28
|
"reason": { "type": ["string", "null"] },
|
|
29
29
|
"reviewer": { "type": ["string", "null"] },
|
|
30
30
|
"file_sha": {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# Pipeline Scripts - Category Index
|
|
2
2
|
|
|
3
|
-
The `pipeline/scripts/` directory holds
|
|
3
|
+
The `pipeline/scripts/` directory holds 148 shell scripts + 45 Node.js `.mjs` scripts in a flat layout. v6.0.0 evaluated moving them into `smoke/`, `hooks/`, `runtime/` subdirs and decided against it: each path change would need to propagate through `package.json` globs, `install.js` deploy logic, npm scripts, sibling script calls, and CI workflow references - a 40+ file touch radius with high regression risk for minor polish value.
|
|
4
4
|
|
|
5
|
-
Instead, this README is
|
|
5
|
+
Instead, this README is a curated category index of the key scripts, not an exhaustive listing. Use it to navigate.
|
|
6
6
|
|
|
7
|
-
## Smoke scripts (
|
|
7
|
+
## Smoke scripts (118 files - `smoke-*.sh`)
|
|
8
8
|
|
|
9
9
|
Validate contracts. Each emits `══ <name> smoke: N passed, M failed ══` on completion (the `smoke-personal-data.sh` + `smoke-token-budget.sh` suites use `PASS:` / `warnings:` variants). Run via `npm test` (chained) or individually.
|
|
10
10
|
|
|
@@ -57,6 +57,7 @@ Validate contracts. Each emits `══ <name> smoke: N passed, M failed ══`
|
|
|
57
57
|
- `smoke-search.sh` - `/multi-agent:search` log search + smart ranking
|
|
58
58
|
- `smoke-subagent-validators.sh` - Phase 1/2 validator schemas
|
|
59
59
|
- `smoke-telemetry.sh` - opt-in telemetry contract
|
|
60
|
+
- `smoke-learning-curve.sh` - `learning-curve.mjs` metrics-trend contract
|
|
60
61
|
- `smoke-validator-contradiction.sh` - Phase 4 approved/blocking contradiction detection
|
|
61
62
|
- `smoke-pre-commit.sh` - pre-commit hook integration
|
|
62
63
|
|
|
@@ -81,12 +82,13 @@ Shell scripts invoked during pipeline execution.
|
|
|
81
82
|
- `github-ssh-setup.sh` - first-run SSH key setup helper
|
|
82
83
|
- `sync-parity-check.sh` - manual parity verification helper
|
|
83
84
|
|
|
84
|
-
## Node.js helpers (
|
|
85
|
+
## Node.js helpers (45 `.mjs` files)
|
|
85
86
|
|
|
86
87
|
- `aggregate-metrics.mjs` - collects telemetry aggregates
|
|
88
|
+
- `learning-curve.mjs` - time-bucketed trend over `metrics.jsonl` (first-pass clean rate, review cycles, rework/task, tokens/task, cache ratio); `--bucket`, `--since`, `--json`, `--markdown`
|
|
87
89
|
- `eval-triage.mjs` - runs the 10 triage eval fixtures
|
|
88
90
|
- `gen-skills-index.mjs` - regenerates `pipeline/skills/shared/README.md` skill catalog
|
|
89
|
-
- `migrate-prefs.mjs` - preference migration driver (v2.0.0
|
|
91
|
+
- `migrate-prefs.mjs` - preference migration driver (v2.0.0 through v2.4.0)
|
|
90
92
|
- `migrate-state.mjs` - generic migration runner (reads `schemas/migrations/`)
|
|
91
93
|
- `token-budget-report.mjs` - per-phase token usage report
|
|
92
94
|
- `validate-analysis.mjs`, `validate-planning.mjs`, `validate-reviewer.mjs`, `validate-triage.mjs` - per-phase output validators
|
|
@@ -230,7 +230,9 @@ if (opts.markdown) {
|
|
|
230
230
|
const lines = [];
|
|
231
231
|
lines.push(`# Pipeline Metrics Summary`);
|
|
232
232
|
lines.push(``);
|
|
233
|
-
lines.push(
|
|
233
|
+
lines.push(
|
|
234
|
+
`_Source: \`${summary.source}\` · Events: ${summary.total_events} (${summary.parse_errors} parse errors) · Unique tasks: ${summary.unique_tasks}_`,
|
|
235
|
+
);
|
|
234
236
|
lines.push(``);
|
|
235
237
|
lines.push(`## Reviews`);
|
|
236
238
|
lines.push(``);
|
|
@@ -245,9 +247,13 @@ if (opts.markdown) {
|
|
|
245
247
|
lines.push(`| Category | Count | Rate |`);
|
|
246
248
|
lines.push(`|----------|-------|------|`);
|
|
247
249
|
lines.push(`| Raw findings | ${summary.triage_totals.raw} | - |`);
|
|
248
|
-
lines.push(
|
|
250
|
+
lines.push(
|
|
251
|
+
`| Accepted | ${summary.triage_totals.accepted} | ${summary.triage_totals.accept_rate ?? " - "} |`,
|
|
252
|
+
);
|
|
249
253
|
lines.push(`| Deferred | ${summary.triage_totals.deferred} | - |`);
|
|
250
|
-
lines.push(
|
|
254
|
+
lines.push(
|
|
255
|
+
`| Rejected | ${summary.triage_totals.rejected} | ${summary.triage_totals.reject_rate ?? " - "} |`,
|
|
256
|
+
);
|
|
251
257
|
|
|
252
258
|
if (Object.keys(summary.edge_cases).length) {
|
|
253
259
|
lines.push(``);
|
|
@@ -266,14 +272,20 @@ if (opts.markdown) {
|
|
|
266
272
|
lines.push(``);
|
|
267
273
|
lines.push(`| Model | Calls | Duration (ms) | Tokens In | Tokens Out | Cached | Cache % |`);
|
|
268
274
|
lines.push(`|-------|-------|---------------|-----------|------------|--------|---------|`);
|
|
269
|
-
for (const [m, t] of Object.entries(summary.cost_per_model).sort(
|
|
275
|
+
for (const [m, t] of Object.entries(summary.cost_per_model).sort(
|
|
276
|
+
(a, b) => b[1].calls - a[1].calls,
|
|
277
|
+
)) {
|
|
270
278
|
const pct = t.cache_ratio === null ? " - " : `${Math.round(t.cache_ratio * 100)}%`;
|
|
271
|
-
lines.push(
|
|
279
|
+
lines.push(
|
|
280
|
+
`| \`${m}\` | ${t.calls} | ${t.duration_ms} | ${t.tokens_in} | ${t.tokens_out} | ${t.tokens_cached} | ${pct} |`,
|
|
281
|
+
);
|
|
272
282
|
}
|
|
273
283
|
const oc = summary.cache_reuse;
|
|
274
284
|
const ocPct = oc.cache_ratio === null ? " - " : `${Math.round(oc.cache_ratio * 100)}%`;
|
|
275
285
|
lines.push(``);
|
|
276
|
-
lines.push(
|
|
286
|
+
lines.push(
|
|
287
|
+
`_Overall prompt-cache reuse: ${oc.tokens_cached.toLocaleString()} cached / ${(oc.tokens_in + oc.tokens_cached).toLocaleString()} total input tokens = **${ocPct}**._`,
|
|
288
|
+
);
|
|
277
289
|
}
|
|
278
290
|
console.log(lines.join("\n"));
|
|
279
291
|
process.exit(0);
|
|
@@ -40,7 +40,11 @@ function* walkSkills(dir) {
|
|
|
40
40
|
for (const entry of readdirSync(dir)) {
|
|
41
41
|
const full = join(dir, entry);
|
|
42
42
|
let st;
|
|
43
|
-
try {
|
|
43
|
+
try {
|
|
44
|
+
st = statSync(full);
|
|
45
|
+
} catch {
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
44
48
|
if (st.isDirectory()) yield* walkSkills(full);
|
|
45
49
|
else if (entry === "SKILL.md") yield full;
|
|
46
50
|
}
|
|
@@ -56,7 +60,7 @@ function parseFrontmatter(raw) {
|
|
|
56
60
|
const m = line.match(/^([\w-]+):\s*(.*)$/);
|
|
57
61
|
if (!m) continue;
|
|
58
62
|
let value = m[2].trim();
|
|
59
|
-
if (value.startsWith("
|
|
63
|
+
if (value.startsWith('"') && value.endsWith('"')) value = value.slice(1, -1);
|
|
60
64
|
out[m[1]] = value;
|
|
61
65
|
}
|
|
62
66
|
return out;
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* publish artifact.
|
|
11
11
|
*
|
|
12
12
|
* The authored lifecycle skills (index / reference / workflow / tools) live in the
|
|
13
|
-
* plugin repo and are NEVER touched here
|
|
13
|
+
* plugin repo and are NEVER touched here - only the `knowledge/` layer + plugin.json
|
|
14
14
|
* knowledge entries are regenerated.
|
|
15
15
|
*
|
|
16
16
|
* Routing uses the per-stack skill patterns (formerly carried by stack-swap.sh). Genuinely cross-stack
|
|
@@ -23,37 +23,83 @@
|
|
|
23
23
|
* Exit code 0 always (report-only); prints a JSON-ish summary + per-plugin diff.
|
|
24
24
|
*/
|
|
25
25
|
import {
|
|
26
|
-
readdirSync,
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
readdirSync,
|
|
27
|
+
existsSync,
|
|
28
|
+
rmSync,
|
|
29
|
+
cpSync,
|
|
30
|
+
readFileSync,
|
|
31
|
+
writeFileSync,
|
|
32
|
+
mkdirSync,
|
|
33
|
+
statSync,
|
|
34
|
+
} from "node:fs";
|
|
35
|
+
import { join } from "node:path";
|
|
29
36
|
|
|
30
37
|
const args = process.argv.slice(2);
|
|
31
|
-
const getArg = (k, d) => {
|
|
38
|
+
const getArg = (k, d) => {
|
|
39
|
+
const i = args.indexOf(k);
|
|
40
|
+
return i >= 0 ? args[i + 1] : d;
|
|
41
|
+
};
|
|
32
42
|
const HOME = process.env.HOME;
|
|
33
|
-
const PLUGINS_REPO = getArg(
|
|
34
|
-
const PIPE_ROOT = getArg(
|
|
35
|
-
const EXTERNAL = join(PIPE_ROOT,
|
|
36
|
-
const DRY = args.includes(
|
|
43
|
+
const PLUGINS_REPO = getArg("--plugins-repo", join(HOME, "multi-agent-plugins"));
|
|
44
|
+
const PIPE_ROOT = getArg("--pipeline", join(HOME, "multi-agent-pipeline"));
|
|
45
|
+
const EXTERNAL = join(PIPE_ROOT, "pipeline/skills/shared/external");
|
|
46
|
+
const DRY = args.includes("--dry-run");
|
|
37
47
|
|
|
38
|
-
const COMMON_PLUGIN =
|
|
39
|
-
const COMMON_SKILLS = [
|
|
48
|
+
const COMMON_PLUGIN = "ai-common-engineering-toolkit";
|
|
49
|
+
const COMMON_SKILLS = [
|
|
50
|
+
"accessibility-compliance-accessibility-audit",
|
|
51
|
+
"firebase",
|
|
52
|
+
"humanizer",
|
|
53
|
+
"council",
|
|
54
|
+
"search-first",
|
|
55
|
+
"agent-introspection-debugging",
|
|
56
|
+
"skill-creator",
|
|
57
|
+
"backlog",
|
|
58
|
+
];
|
|
40
59
|
|
|
41
60
|
// Apple/Xcode-only skills that match no stack pattern → iOS plugin only.
|
|
42
61
|
const APPLE_ONLY = [
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
62
|
+
"avkit",
|
|
63
|
+
"cryptokit",
|
|
64
|
+
"metrickit-diagnostics",
|
|
65
|
+
"pdfkit",
|
|
66
|
+
"spm-build-analysis",
|
|
67
|
+
"swiftlint",
|
|
68
|
+
"xcode-build-benchmark",
|
|
69
|
+
"xcode-build-fixer",
|
|
70
|
+
"xcode-build-orchestrator",
|
|
71
|
+
"xcode-compilation-analyzer",
|
|
72
|
+
"xcode-project-analyzer",
|
|
46
73
|
];
|
|
47
74
|
|
|
48
75
|
// stack → matching pattern (per-stack routing patterns (formerly in stack-swap.sh))
|
|
76
|
+
//
|
|
77
|
+
// A skill lands in every plugin whose pattern it matches, so a bare substring
|
|
78
|
+
// alternative that is also a suffix of another stack's skill name silently ships
|
|
79
|
+
// that skill twice. `architecture` did exactly that: it pulled
|
|
80
|
+
// `android-architecture` and `swift-architecture` into ai-backend-toolkit
|
|
81
|
+
// alongside their correct plugins, so a backend project loading the toolkit got
|
|
82
|
+
// Compose and SwiftUI architecture skills. It is anchored to the exact name now.
|
|
83
|
+
// Keep any new single-word alternative anchored unless it genuinely reads as a
|
|
84
|
+
// substring across stacks.
|
|
49
85
|
const STACK_PATTERNS = {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
86
|
+
"ai-ios-engineering-toolkit":
|
|
87
|
+
/(swiftui|swift-|ios-|hig-|apple-|storekit|widgetkit|healthkit|homekit|mapkit|musickit|passkit|pencilkit|realitykit|weatherkit|alarmkit|callkit|cloudkit|coreml|core-|eventkit|energykit|permissionkit|tipkit|shareplay|live-activities|background-processing|app-store|app-clips|app-intents|authentication|contacts-framework|device-integrity|macos-|natural-language|photos-camera|push-notifications|speech-recognition|swiftdata|vision-framework|debugging-instruments|help-skills)/i,
|
|
88
|
+
"ai-android-engineering-toolkit":
|
|
89
|
+
/(android|compose-|kotlin-|room-database|retrofit-|gradle-|play-store)/i,
|
|
90
|
+
"ai-backend-toolkit":
|
|
91
|
+
/(fastapi|nodejs|docker|api-pattern|api-security|github-actions|observability|^architecture$|monorepo|clean-code|debugging-strategies|agentflow|closed-loop|context-compression|python-patterns|database-patterns|rest-api-design|testing-backend|ci-cd-pipelines)/i,
|
|
92
|
+
"ai-frontend-engineering-toolkit":
|
|
93
|
+
/(react-|nextjs-|typescript-|tailwind-|vue-|web-accessibility|web-performance|css-modern|web-testing|html-semantic)/i,
|
|
54
94
|
};
|
|
55
95
|
|
|
56
|
-
const isDir = (p) => {
|
|
96
|
+
const isDir = (p) => {
|
|
97
|
+
try {
|
|
98
|
+
return statSync(p).isDirectory();
|
|
99
|
+
} catch {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
};
|
|
57
103
|
const listDirs = (p) => (existsSync(p) ? readdirSync(p).filter((n) => isDir(join(p, n))) : []);
|
|
58
104
|
|
|
59
105
|
// --- 1. enumerate authoring source ---------------------------------------
|
|
@@ -69,22 +115,67 @@ for (const p of Object.keys(STACK_PATTERNS)) desired[p] = new Set();
|
|
|
69
115
|
|
|
70
116
|
const unrouted = [];
|
|
71
117
|
for (const skill of allSkills) {
|
|
72
|
-
if (COMMON_SKILLS.includes(skill)) {
|
|
73
|
-
|
|
118
|
+
if (COMMON_SKILLS.includes(skill)) {
|
|
119
|
+
desired[COMMON_PLUGIN].add(skill);
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
if (APPLE_ONLY.includes(skill)) {
|
|
123
|
+
desired["ai-ios-engineering-toolkit"].add(skill);
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
74
126
|
let matched = false;
|
|
75
127
|
for (const [plugin, re] of Object.entries(STACK_PATTERNS)) {
|
|
76
|
-
if (re.test(skill)) {
|
|
128
|
+
if (re.test(skill)) {
|
|
129
|
+
desired[plugin].add(skill);
|
|
130
|
+
matched = true;
|
|
131
|
+
}
|
|
77
132
|
}
|
|
78
133
|
if (!matched) unrouted.push(skill);
|
|
79
134
|
}
|
|
80
135
|
|
|
136
|
+
// --- 2b. routing audit ---------------------------------------------------
|
|
137
|
+
// The loop above adds a skill to EVERY plugin whose pattern matches, so a bare
|
|
138
|
+
// substring alternative that also reads as a suffix of another stack's skill
|
|
139
|
+
// name ships that skill into two plugins. `architecture` did that to
|
|
140
|
+
// android-architecture and swift-architecture, which is how ai-backend-toolkit
|
|
141
|
+
// came to carry Compose and SwiftUI guidance.
|
|
142
|
+
//
|
|
143
|
+
// --check-routing runs this audit alone. It reads nothing but the authoring
|
|
144
|
+
// source in this repo, so CI can gate it without a checkout of the plugins repo.
|
|
145
|
+
const multiRouted = [];
|
|
146
|
+
for (const skill of allSkills) {
|
|
147
|
+
if (COMMON_SKILLS.includes(skill) || APPLE_ONLY.includes(skill)) continue;
|
|
148
|
+
const owners = Object.entries(STACK_PATTERNS)
|
|
149
|
+
.filter(([, re]) => re.test(skill))
|
|
150
|
+
.map(([p]) => p);
|
|
151
|
+
if (owners.length > 1) multiRouted.push(`${skill} -> ${owners.join(" + ")}`);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (args.includes("--check-routing")) {
|
|
155
|
+
if (multiRouted.length) {
|
|
156
|
+
console.error(
|
|
157
|
+
`check-routing: ${multiRouted.length} skill(s) routed to more than one stack plugin`,
|
|
158
|
+
);
|
|
159
|
+
for (const m of multiRouted) console.error(` FAIL ${m}`);
|
|
160
|
+
console.error("fix: anchor the offending STACK_PATTERNS alternative to the exact skill name");
|
|
161
|
+
process.exit(1);
|
|
162
|
+
}
|
|
163
|
+
console.log(
|
|
164
|
+
`check-routing: clean (${allSkills.length} skills, ${unrouted.length} unrouted, 0 multi-routed)`,
|
|
165
|
+
);
|
|
166
|
+
process.exit(0);
|
|
167
|
+
}
|
|
168
|
+
|
|
81
169
|
// --- 3. sync each plugin's knowledge/ + regenerate plugin.json -----------
|
|
82
170
|
const report = [];
|
|
83
171
|
for (const [plugin, want] of Object.entries(desired)) {
|
|
84
|
-
const pdir = join(PLUGINS_REPO,
|
|
85
|
-
const kdir = join(pdir,
|
|
86
|
-
const pjPath = join(pdir,
|
|
87
|
-
if (!existsSync(pjPath)) {
|
|
172
|
+
const pdir = join(PLUGINS_REPO, "plugins", plugin);
|
|
173
|
+
const kdir = join(pdir, "skills", "knowledge");
|
|
174
|
+
const pjPath = join(pdir, ".claude-plugin", "plugin.json");
|
|
175
|
+
if (!existsSync(pjPath)) {
|
|
176
|
+
report.push({ plugin, error: "plugin.json missing" });
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
88
179
|
|
|
89
180
|
const have = new Set(listDirs(kdir));
|
|
90
181
|
const toAdd = [...want].filter((s) => !have.has(s)).sort();
|
|
@@ -98,16 +189,16 @@ for (const [plugin, want] of Object.entries(desired)) {
|
|
|
98
189
|
}
|
|
99
190
|
|
|
100
191
|
// regenerate plugin.json skills[]: keep non-knowledge entries, rebuild knowledge set
|
|
101
|
-
const pj = JSON.parse(readFileSync(pjPath,
|
|
102
|
-
const nonKnowledge = (pj.skills || []).filter((s) => !s.startsWith(
|
|
192
|
+
const pj = JSON.parse(readFileSync(pjPath, "utf8"));
|
|
193
|
+
const nonKnowledge = (pj.skills || []).filter((s) => !s.startsWith("./skills/knowledge/"));
|
|
103
194
|
const finalKnowledge = (DRY ? [...want] : listDirs(kdir)).map((s) => `./skills/knowledge/${s}`);
|
|
104
195
|
const newSkills = [...new Set([...nonKnowledge, ...finalKnowledge])].sort();
|
|
105
196
|
const skillsChanged = JSON.stringify(pj.skills) !== JSON.stringify(newSkills);
|
|
106
197
|
|
|
107
|
-
const oldV = pj.version ||
|
|
198
|
+
const oldV = pj.version || "0.1.0";
|
|
108
199
|
let newV = oldV;
|
|
109
200
|
if (changed || skillsChanged) {
|
|
110
|
-
const [maj, min, patch] = oldV.split(
|
|
201
|
+
const [maj, min, patch] = oldV.split(".").map((n) => parseInt(n, 10) || 0);
|
|
111
202
|
newV = `${maj}.${min}.${patch + 1}`;
|
|
112
203
|
}
|
|
113
204
|
if (!DRY && (changed || skillsChanged)) {
|
|
@@ -115,23 +206,35 @@ for (const [plugin, want] of Object.entries(desired)) {
|
|
|
115
206
|
pj.version = newV;
|
|
116
207
|
writeFileSync(pjPath, `${JSON.stringify(pj, null, 2)}\n`);
|
|
117
208
|
}
|
|
118
|
-
report.push({
|
|
209
|
+
report.push({
|
|
210
|
+
plugin,
|
|
211
|
+
added: toAdd,
|
|
212
|
+
removed: toRemove,
|
|
213
|
+
skillCount: newSkills.length,
|
|
214
|
+
version: oldV === newV ? oldV : `${oldV} → ${newV}`,
|
|
215
|
+
bumped: oldV !== newV,
|
|
216
|
+
});
|
|
119
217
|
}
|
|
120
218
|
|
|
121
219
|
// --- 4. report ------------------------------------------------------------
|
|
122
|
-
console.log(`build-stack-plugins ${DRY ?
|
|
220
|
+
console.log(`build-stack-plugins ${DRY ? "(dry-run)" : ""}`);
|
|
123
221
|
console.log(`source: ${EXTERNAL} (${allSkills.length} skills)`);
|
|
124
222
|
console.log(`target: ${PLUGINS_REPO}`);
|
|
125
223
|
for (const r of report) {
|
|
126
|
-
if (r.error) {
|
|
127
|
-
|
|
224
|
+
if (r.error) {
|
|
225
|
+
console.log(` ✗ ${r.plugin}: ${r.error}`);
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
const flag = r.bumped ? "BUMPED" : "unchanged";
|
|
128
229
|
console.log(` ${r.plugin}: skills=${r.skillCount} version=${r.version} [${flag}]`);
|
|
129
|
-
if (r.added.length) console.log(` + ${r.added.join(
|
|
130
|
-
if (r.removed.length) console.log(` - ${r.removed.join(
|
|
230
|
+
if (r.added.length) console.log(` + ${r.added.join(", ")}`);
|
|
231
|
+
if (r.removed.length) console.log(` - ${r.removed.join(", ")}`);
|
|
131
232
|
}
|
|
132
233
|
if (unrouted.length) {
|
|
133
|
-
console.log(` ⚠ unrouted (matched no stack, not common/apple-only)
|
|
134
|
-
console.log(` ${unrouted.join(
|
|
234
|
+
console.log(` ⚠ unrouted (matched no stack, not common/apple-only) - NOT copied anywhere:`);
|
|
235
|
+
console.log(` ${unrouted.join(", ")}`);
|
|
135
236
|
}
|
|
136
237
|
const anyBump = report.some((r) => r.bumped);
|
|
137
|
-
console.log(
|
|
238
|
+
console.log(
|
|
239
|
+
anyBump ? "result: changes detected (versions bumped)" : "result: all plugins up to date",
|
|
240
|
+
);
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// check-derived-drift.mjs - resolve the upstream version for every entry in
|
|
3
|
+
// prefs `global.derivedSkillSources[]` and report drift.
|
|
4
|
+
//
|
|
5
|
+
// This exists because the resolution order is the whole correctness question,
|
|
6
|
+
// and prose in a skill file cannot be tested. The step used to read the
|
|
7
|
+
// installed plugin cache and call the answer authoritative. A cache is only as
|
|
8
|
+
// fresh as the last `claude marketplace update`, so it reported "up to date"
|
|
9
|
+
// for a derivation that was four releases behind, and the version it had been
|
|
10
|
+
// recorded from was the wrong manifest besides.
|
|
11
|
+
//
|
|
12
|
+
// Resolution order, first source that answers wins:
|
|
13
|
+
// 1. upstreamLocalClone a working copy of the upstream repo
|
|
14
|
+
// 2. upstreamRepoUrl the same manifest over `gh api`
|
|
15
|
+
// 3. the plugin cache a mirror; can only ever yield "unverified"
|
|
16
|
+
//
|
|
17
|
+
// Which manifest carries the version is `upstreamVersionSource`, default
|
|
18
|
+
// marketplace.json, because that is what a marketplace consumer resolves.
|
|
19
|
+
//
|
|
20
|
+
// Exit codes: 0 = no drift (or not configured), 1 = drift or unverified,
|
|
21
|
+
// 2 = a configured entry could not be resolved at all.
|
|
22
|
+
//
|
|
23
|
+
// Flags: --json machine-readable output for the scorecard gate.
|
|
24
|
+
|
|
25
|
+
import { Buffer } from "node:buffer";
|
|
26
|
+
import { execFileSync } from "node:child_process";
|
|
27
|
+
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
28
|
+
import { homedir } from "node:os";
|
|
29
|
+
import { join } from "node:path";
|
|
30
|
+
|
|
31
|
+
const asJson = process.argv.includes("--json");
|
|
32
|
+
// Overridable so the gate can be driven against fixtures. Real runs read the
|
|
33
|
+
// user's prefs; a test that had to write into $HOME to exercise this would be
|
|
34
|
+
// a test nobody runs.
|
|
35
|
+
const PREFS =
|
|
36
|
+
process.env.DERIVED_DRIFT_PREFS || join(homedir(), ".claude", "multi-agent-preferences.json");
|
|
37
|
+
const CACHE = process.env.DERIVED_DRIFT_CACHE || join(homedir(), ".claude", "plugins", "cache");
|
|
38
|
+
|
|
39
|
+
const expandHome = (p) => p.replace(/^\$HOME\b|^~/, homedir());
|
|
40
|
+
|
|
41
|
+
function readJson(path) {
|
|
42
|
+
try {
|
|
43
|
+
return JSON.parse(readFileSync(path, "utf-8"));
|
|
44
|
+
} catch {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// A manifest names its version either directly (plugin.json) or inside a
|
|
50
|
+
// plugins[] entry keyed by name (marketplace.json).
|
|
51
|
+
function versionFrom(manifest, pluginName) {
|
|
52
|
+
if (!manifest) return null;
|
|
53
|
+
if (Array.isArray(manifest.plugins)) {
|
|
54
|
+
const hit = manifest.plugins.find((p) => p.name === pluginName);
|
|
55
|
+
return hit?.version ?? null;
|
|
56
|
+
}
|
|
57
|
+
return manifest.version ?? null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function fromLocalClone(entry) {
|
|
61
|
+
if (!entry.upstreamLocalClone) return null;
|
|
62
|
+
const root = expandHome(entry.upstreamLocalClone);
|
|
63
|
+
const file =
|
|
64
|
+
entry.upstreamVersionSource === "plugin.json"
|
|
65
|
+
? join(root, "plugins", entry.upstreamPlugin, ".claude-plugin", "plugin.json")
|
|
66
|
+
: join(root, ".claude-plugin", "marketplace.json");
|
|
67
|
+
if (!existsSync(file)) return null;
|
|
68
|
+
const version = versionFrom(readJson(file), entry.upstreamPlugin);
|
|
69
|
+
if (!version) return null;
|
|
70
|
+
|
|
71
|
+
// A clone behind its own remote is still a stale answer, just a less stale
|
|
72
|
+
// one than the cache. Say so rather than presenting it as current.
|
|
73
|
+
let behind;
|
|
74
|
+
try {
|
|
75
|
+
const out = execFileSync("git", ["-C", root, "rev-list", "--count", "HEAD..@{u}"], {
|
|
76
|
+
encoding: "utf-8",
|
|
77
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
78
|
+
}).trim();
|
|
79
|
+
behind = Number(out) || 0;
|
|
80
|
+
} catch {
|
|
81
|
+
// No upstream branch configured, or not a git checkout at all.
|
|
82
|
+
behind = null;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return { version, source: "local clone", authoritative: true, behind };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function fromRepoApi(entry) {
|
|
89
|
+
if (!entry.upstreamRepoUrl) return null;
|
|
90
|
+
const m = entry.upstreamRepoUrl.match(/github\.com\/([^/]+)\/([^/.]+)/);
|
|
91
|
+
if (!m) return null;
|
|
92
|
+
const path =
|
|
93
|
+
entry.upstreamVersionSource === "plugin.json"
|
|
94
|
+
? `plugins/${entry.upstreamPlugin}/.claude-plugin/plugin.json`
|
|
95
|
+
: ".claude-plugin/marketplace.json";
|
|
96
|
+
try {
|
|
97
|
+
const raw = execFileSync(
|
|
98
|
+
"gh",
|
|
99
|
+
["api", `repos/${m[1]}/${m[2]}/contents/${path}`, "--jq", ".content"],
|
|
100
|
+
{ encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"] },
|
|
101
|
+
).trim();
|
|
102
|
+
const version = versionFrom(
|
|
103
|
+
JSON.parse(Buffer.from(raw, "base64").toString("utf-8")),
|
|
104
|
+
entry.upstreamPlugin,
|
|
105
|
+
);
|
|
106
|
+
return version ? { version, source: "repo api", authoritative: true, behind: 0 } : null;
|
|
107
|
+
} catch {
|
|
108
|
+
// 404 here means unreachable for the active account, not "no drift".
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function fromCache(entry) {
|
|
114
|
+
if (!entry.upstreamMarketplace) return null;
|
|
115
|
+
const dir = join(CACHE, entry.upstreamMarketplace, entry.upstreamPlugin);
|
|
116
|
+
if (!existsSync(dir)) return null;
|
|
117
|
+
const versions = readdirSync(dir)
|
|
118
|
+
.filter((d) => /^\d+\.\d+\.\d+$/.test(d))
|
|
119
|
+
.sort();
|
|
120
|
+
const newest = versions[versions.length - 1];
|
|
121
|
+
return newest
|
|
122
|
+
? { version: newest, source: "plugin cache", authoritative: false, behind: null }
|
|
123
|
+
: null;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const prefs = readJson(PREFS);
|
|
127
|
+
const entries = prefs?.global?.derivedSkillSources ?? [];
|
|
128
|
+
|
|
129
|
+
if (entries.length === 0) {
|
|
130
|
+
const msg = "check-derived-drift: no derived-skill sources configured";
|
|
131
|
+
console.log(
|
|
132
|
+
asJson ? JSON.stringify({ configured: 0, drifted: 0, unverified: 0, entries: [] }) : msg,
|
|
133
|
+
);
|
|
134
|
+
process.exit(0);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const rows = [];
|
|
138
|
+
for (const entry of entries) {
|
|
139
|
+
const resolved = fromLocalClone(entry) || fromRepoApi(entry) || fromCache(entry);
|
|
140
|
+
if (!resolved) {
|
|
141
|
+
rows.push({
|
|
142
|
+
label: entry.label,
|
|
143
|
+
plugin: entry.upstreamPlugin,
|
|
144
|
+
from: entry.derivedFromVersion,
|
|
145
|
+
upstream: null,
|
|
146
|
+
source: "unreachable",
|
|
147
|
+
status: "unreachable",
|
|
148
|
+
});
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
const drifted = resolved.version !== entry.derivedFromVersion;
|
|
152
|
+
const status = drifted
|
|
153
|
+
? "drift"
|
|
154
|
+
: resolved.authoritative
|
|
155
|
+
? "up to date"
|
|
156
|
+
: "unverified (cache only)";
|
|
157
|
+
rows.push({
|
|
158
|
+
label: entry.label,
|
|
159
|
+
plugin: entry.upstreamPlugin,
|
|
160
|
+
from: entry.derivedFromVersion,
|
|
161
|
+
upstream: resolved.version,
|
|
162
|
+
source: resolved.source,
|
|
163
|
+
cloneBehind: resolved.behind,
|
|
164
|
+
status,
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const drifted = rows.filter((r) => r.status === "drift").length;
|
|
169
|
+
const unverified = rows.filter((r) => r.status.startsWith("unverified")).length;
|
|
170
|
+
const unreachable = rows.filter((r) => r.status === "unreachable").length;
|
|
171
|
+
|
|
172
|
+
if (asJson) {
|
|
173
|
+
console.log(
|
|
174
|
+
JSON.stringify(
|
|
175
|
+
{ configured: rows.length, drifted, unverified, unreachable, entries: rows },
|
|
176
|
+
null,
|
|
177
|
+
2,
|
|
178
|
+
),
|
|
179
|
+
);
|
|
180
|
+
} else {
|
|
181
|
+
for (const r of rows) {
|
|
182
|
+
const behind = r.cloneBehind ? ` [clone ${r.cloneBehind} behind its remote]` : "";
|
|
183
|
+
console.log(
|
|
184
|
+
` ${r.status === "up to date" ? "✓" : "✗"} ${r.plugin}: recorded ${r.from}, ` +
|
|
185
|
+
`upstream ${r.upstream ?? "?"} (${r.source})${behind} -> ${r.status}`,
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
console.log("");
|
|
189
|
+
console.log(
|
|
190
|
+
`══ check-derived-drift: ${rows.length - drifted - unverified - unreachable} passed, ` +
|
|
191
|
+
`${drifted + unverified + unreachable} failed ══`,
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (unreachable > 0) process.exit(2);
|
|
196
|
+
process.exit(drifted + unverified > 0 ? 1 : 0);
|
|
@@ -33,8 +33,12 @@ function mdFiles(dir, acc = []) {
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
const targets = [
|
|
36
|
-
...readdirSync(repoRoot)
|
|
37
|
-
|
|
36
|
+
...readdirSync(repoRoot)
|
|
37
|
+
.filter((f) => f.endsWith(".md"))
|
|
38
|
+
.map((f) => join(repoRoot, f)),
|
|
39
|
+
...["docs", "examples", "pipeline"]
|
|
40
|
+
.filter((d) => existsSync(join(repoRoot, d)))
|
|
41
|
+
.flatMap((d) => mdFiles(join(repoRoot, d))),
|
|
38
42
|
// CHANGELOG-archive.md is frozen history - it legitimately references docs
|
|
39
43
|
// that were deleted in later releases.
|
|
40
44
|
]
|