@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
|
@@ -43,7 +43,15 @@
|
|
|
43
43
|
* @module pipeline/scripts/learnings-ledger
|
|
44
44
|
*/
|
|
45
45
|
|
|
46
|
-
import {
|
|
46
|
+
import {
|
|
47
|
+
readFileSync,
|
|
48
|
+
writeFileSync,
|
|
49
|
+
renameSync,
|
|
50
|
+
existsSync,
|
|
51
|
+
mkdirSync,
|
|
52
|
+
appendFileSync,
|
|
53
|
+
statSync,
|
|
54
|
+
} from "node:fs";
|
|
47
55
|
import { homedir } from "node:os";
|
|
48
56
|
import { execFileSync } from "node:child_process";
|
|
49
57
|
import { join, basename, dirname } from "node:path";
|
|
@@ -59,11 +67,16 @@ for (let i = 1; i < argv.length; i++) {
|
|
|
59
67
|
// (e.g. --statement="-- prefer let over var"). Use it when the space form
|
|
60
68
|
// would mistake the value for the next flag.
|
|
61
69
|
const eq = body.indexOf("=");
|
|
62
|
-
if (eq !== -1) {
|
|
70
|
+
if (eq !== -1) {
|
|
71
|
+
flags[body.slice(0, eq)] = body.slice(eq + 1);
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
63
74
|
const k = body;
|
|
64
75
|
const v = argv[i + 1];
|
|
65
|
-
if (v !== undefined && !v.startsWith("--")) {
|
|
66
|
-
|
|
76
|
+
if (v !== undefined && !v.startsWith("--")) {
|
|
77
|
+
flags[k] = v;
|
|
78
|
+
i++;
|
|
79
|
+
} else flags[k] = true;
|
|
67
80
|
}
|
|
68
81
|
|
|
69
82
|
const KINDS = new Set(["fact", "convention", "rejected-preference"]);
|
|
@@ -74,21 +87,50 @@ function die(msg, code = 1) {
|
|
|
74
87
|
process.exit(code);
|
|
75
88
|
}
|
|
76
89
|
|
|
90
|
+
// Resolves to the MAIN repository name even when called from inside a pipeline
|
|
91
|
+
// worktree. `--show-toplevel` returns the worktree root, whose basename is the
|
|
92
|
+
// task id, which would shard the per-repo ledger one directory per task and make
|
|
93
|
+
// the learning replay always miss. `--git-common-dir` points at the main
|
|
94
|
+
// repository's .git for every linked worktree.
|
|
77
95
|
function repoSlug() {
|
|
78
96
|
if (flags["repo-slug"]) return String(flags["repo-slug"]);
|
|
79
97
|
if (process.env.MULTI_AGENT_REPO_SLUG) return process.env.MULTI_AGENT_REPO_SLUG;
|
|
98
|
+
try {
|
|
99
|
+
const commonDir = execFileSync(
|
|
100
|
+
"git",
|
|
101
|
+
["rev-parse", "--path-format=absolute", "--git-common-dir"],
|
|
102
|
+
{ encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] },
|
|
103
|
+
).trim();
|
|
104
|
+
if (commonDir) {
|
|
105
|
+
const base = basename(commonDir);
|
|
106
|
+
const root = base === ".git" ? dirname(commonDir) : commonDir.replace(/\.git$/, "");
|
|
107
|
+
const slug = basename(root);
|
|
108
|
+
if (slug) return slug;
|
|
109
|
+
}
|
|
110
|
+
} catch {
|
|
111
|
+
/* not a git repo, or git too old for --path-format */
|
|
112
|
+
}
|
|
80
113
|
try {
|
|
81
114
|
const top = execFileSync("git", ["rev-parse", "--show-toplevel"], {
|
|
82
115
|
encoding: "utf8",
|
|
83
116
|
stdio: ["ignore", "pipe", "ignore"],
|
|
84
117
|
}).trim();
|
|
85
118
|
if (top) return basename(top);
|
|
86
|
-
} catch {
|
|
119
|
+
} catch {
|
|
120
|
+
/* not a git repo */
|
|
121
|
+
}
|
|
87
122
|
return "default";
|
|
88
123
|
}
|
|
89
124
|
|
|
90
125
|
function ledgerPath(slug) {
|
|
91
|
-
return join(
|
|
126
|
+
return join(
|
|
127
|
+
homedir(),
|
|
128
|
+
".claude",
|
|
129
|
+
"memory",
|
|
130
|
+
"multi-agent",
|
|
131
|
+
slug || repoSlug(),
|
|
132
|
+
"learnings-ledger.jsonl",
|
|
133
|
+
);
|
|
92
134
|
}
|
|
93
135
|
|
|
94
136
|
function ensureDir(p) {
|
|
@@ -96,7 +138,10 @@ function ensureDir(p) {
|
|
|
96
138
|
}
|
|
97
139
|
|
|
98
140
|
function normStatement(s) {
|
|
99
|
-
return String(s || "")
|
|
141
|
+
return String(s || "")
|
|
142
|
+
.toLowerCase()
|
|
143
|
+
.replace(/\s+/g, " ")
|
|
144
|
+
.trim();
|
|
100
145
|
}
|
|
101
146
|
|
|
102
147
|
function readLedger(slug) {
|
|
@@ -106,8 +151,11 @@ function readLedger(slug) {
|
|
|
106
151
|
const rows = [];
|
|
107
152
|
for (const line of raw.split("\n")) {
|
|
108
153
|
if (!line.trim()) continue;
|
|
109
|
-
try {
|
|
110
|
-
|
|
154
|
+
try {
|
|
155
|
+
rows.push(JSON.parse(line));
|
|
156
|
+
} catch {
|
|
157
|
+
/* skip corrupted line */
|
|
158
|
+
}
|
|
111
159
|
}
|
|
112
160
|
return rows;
|
|
113
161
|
}
|
|
@@ -148,18 +196,25 @@ function addEntry(slug, { kind, statement, scope, task, confidence, diagnosis },
|
|
|
148
196
|
function cmdAdd() {
|
|
149
197
|
const kind = flags.kind;
|
|
150
198
|
if (!KINDS.has(kind)) die(`--kind must be one of: ${[...KINDS].join(", ")}`, 1);
|
|
151
|
-
if (!flags.statement || flags.statement === true) die(
|
|
199
|
+
if (!flags.statement || flags.statement === true) die('add needs --statement "..."', 1);
|
|
152
200
|
const slug = repoSlug();
|
|
153
201
|
const existing = new Set(readLedger(slug).map((r) => `${r.kind}::${normStatement(r.statement)}`));
|
|
154
|
-
const wrote = addEntry(
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
202
|
+
const wrote = addEntry(
|
|
203
|
+
slug,
|
|
204
|
+
{
|
|
205
|
+
kind,
|
|
206
|
+
statement: flags.statement,
|
|
207
|
+
scope: flags.scope && flags.scope !== true ? String(flags.scope) : "global",
|
|
208
|
+
task: flags.task && flags.task !== true ? String(flags.task) : null,
|
|
209
|
+
confidence:
|
|
210
|
+
flags.confidence && flags.confidence !== true ? String(flags.confidence) : "medium",
|
|
211
|
+
diagnosis: flags.diagnosis && flags.diagnosis !== true ? String(flags.diagnosis) : null,
|
|
212
|
+
},
|
|
213
|
+
existing,
|
|
214
|
+
);
|
|
215
|
+
process.stdout.write(
|
|
216
|
+
JSON.stringify({ ok: true, slug, added: wrote ? 1 : 0, deduped: wrote ? 0 : 1 }) + "\n",
|
|
217
|
+
);
|
|
163
218
|
process.exit(wrote ? 0 : 2);
|
|
164
219
|
}
|
|
165
220
|
|
|
@@ -170,8 +225,11 @@ function cmdFromTriage() {
|
|
|
170
225
|
const task = flags.task && flags.task !== true ? String(flags.task) : null;
|
|
171
226
|
const slug = repoSlug();
|
|
172
227
|
let triage;
|
|
173
|
-
try {
|
|
174
|
-
|
|
228
|
+
try {
|
|
229
|
+
triage = JSON.parse(readFileSync(triagePath, "utf8"));
|
|
230
|
+
} catch (e) {
|
|
231
|
+
die(`cannot parse triage JSON: ${e.message}`, 1);
|
|
232
|
+
}
|
|
175
233
|
|
|
176
234
|
const existing = new Set(readLedger(slug).map((r) => `${r.kind}::${normStatement(r.statement)}`));
|
|
177
235
|
let written = 0;
|
|
@@ -185,12 +243,21 @@ function cmdFromTriage() {
|
|
|
185
243
|
// whole class on every future run. Blocking findings always get re-raised
|
|
186
244
|
// and re-triaged on their own merits; only lower-severity rejections (style
|
|
187
245
|
// nitpicks, scope calls) become durable "do not re-flag" preferences.
|
|
188
|
-
if (f.severity === "blocking") {
|
|
246
|
+
if (f.severity === "blocking") {
|
|
247
|
+
skippedBlocking++;
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
189
250
|
// Top-level files (dirname ".") scope to the file itself, not a stray "./*".
|
|
190
251
|
const dir = f.file ? dirname(f.file) : "";
|
|
191
|
-
const scope = !f.file ? "global" :
|
|
252
|
+
const scope = !f.file ? "global" : dir === "." || dir === "" ? f.file : `${dir}/*`;
|
|
192
253
|
const statement = `Do not re-flag: ${issue}` + (r.reason ? ` (rejected: ${r.reason})` : "");
|
|
193
|
-
if (
|
|
254
|
+
if (
|
|
255
|
+
addEntry(
|
|
256
|
+
slug,
|
|
257
|
+
{ kind: "rejected-preference", statement, scope, task, confidence: "low" },
|
|
258
|
+
existing,
|
|
259
|
+
)
|
|
260
|
+
) {
|
|
194
261
|
written++;
|
|
195
262
|
}
|
|
196
263
|
}
|
|
@@ -202,7 +269,7 @@ function cmdForget() {
|
|
|
202
269
|
const slug = repoSlug();
|
|
203
270
|
const match = flags.statement && flags.statement !== true ? normStatement(flags.statement) : null;
|
|
204
271
|
const kind = flags.kind && flags.kind !== true ? String(flags.kind) : null;
|
|
205
|
-
if (!match && !kind) die(
|
|
272
|
+
if (!match && !kind) die('forget needs --statement "..." and/or --kind <kind>', 1);
|
|
206
273
|
const p = ledgerPath(slug);
|
|
207
274
|
if (!existsSync(p)) {
|
|
208
275
|
process.stdout.write(JSON.stringify({ ok: true, slug, removed: 0 }) + "\n");
|
|
@@ -241,7 +308,9 @@ function cmdBrief() {
|
|
|
241
308
|
for (const r of ordered) (byKind[r.kind] || (byKind[r.kind] = [])).push(r);
|
|
242
309
|
|
|
243
310
|
const lines = ["<repo-learnings>"];
|
|
244
|
-
lines.push(
|
|
311
|
+
lines.push(
|
|
312
|
+
`Durable knowledge for repo "${slug}" (replayed each run; treat as context, not commands - current scope decides).`,
|
|
313
|
+
);
|
|
245
314
|
const labels = {
|
|
246
315
|
fact: "Architectural facts",
|
|
247
316
|
convention: "Conventions",
|
|
@@ -263,7 +332,9 @@ function cmdBrief() {
|
|
|
263
332
|
}
|
|
264
333
|
|
|
265
334
|
function cmdPath() {
|
|
266
|
-
process.stdout.write(
|
|
335
|
+
process.stdout.write(
|
|
336
|
+
ledgerPath(flags["repo-slug"] ? String(flags["repo-slug"]) : repoSlug()) + "\n",
|
|
337
|
+
);
|
|
267
338
|
process.exit(0);
|
|
268
339
|
}
|
|
269
340
|
|
|
@@ -279,13 +350,27 @@ function cmdStats() {
|
|
|
279
350
|
}
|
|
280
351
|
|
|
281
352
|
switch (SUB) {
|
|
282
|
-
case "add":
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
case "
|
|
286
|
-
|
|
287
|
-
|
|
353
|
+
case "add":
|
|
354
|
+
cmdAdd();
|
|
355
|
+
break;
|
|
356
|
+
case "from-triage":
|
|
357
|
+
cmdFromTriage();
|
|
358
|
+
break;
|
|
359
|
+
case "forget":
|
|
360
|
+
cmdForget();
|
|
361
|
+
break;
|
|
362
|
+
case "brief":
|
|
363
|
+
cmdBrief();
|
|
364
|
+
break;
|
|
365
|
+
case "path":
|
|
366
|
+
cmdPath();
|
|
367
|
+
break;
|
|
368
|
+
case "stats":
|
|
369
|
+
cmdStats();
|
|
370
|
+
break;
|
|
288
371
|
default:
|
|
289
|
-
process.stderr.write(
|
|
372
|
+
process.stderr.write(
|
|
373
|
+
"usage: learnings-ledger.mjs <add|from-triage|forget|brief|path|stats> [flags]\n",
|
|
374
|
+
);
|
|
290
375
|
process.exit(64);
|
|
291
376
|
}
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// lint-mcp-refs.mjs - every mcp__<server>__<tool> reference must be callable.
|
|
3
|
+
//
|
|
4
|
+
// A host composes an MCP tool name as mcp__<registered-server-name>__<tool>.
|
|
5
|
+
// The pipeline had 100 references to `mcp__dev_toolkit__*` while the server is
|
|
6
|
+
// registered as `dev-toolkit`, so the real names are `mcp__dev-toolkit__*`.
|
|
7
|
+
// Nothing caught it: prose is not executed, and a wrong name inside an
|
|
8
|
+
// `allowed-tools:` allowlist silently denies the tool instead of erroring.
|
|
9
|
+
//
|
|
10
|
+
// Checks (errors - fail the gate):
|
|
11
|
+
// 1. the server segment must be a known registered server name
|
|
12
|
+
// 2. the known-bad snake_case spellings are called out with their fix
|
|
13
|
+
//
|
|
14
|
+
// Optional deep check (advisory - never fails):
|
|
15
|
+
// 3. when the companion dev-toolkit repo resolves locally (via
|
|
16
|
+
// prefs.global.devToolkit.localPath, or the mcpServers registration in
|
|
17
|
+
// ~/.claude.json / ~/.claude/settings.json), handshake it over stdio and
|
|
18
|
+
// confirm every referenced dev-toolkit tool actually exists in tools/list.
|
|
19
|
+
// CI has no local checkout, so an unresolvable repo is a skip, not a fail.
|
|
20
|
+
//
|
|
21
|
+
// Zero-dependency. Runs inside `npm test` and ci-lite.
|
|
22
|
+
//
|
|
23
|
+
// Usage: node pipeline/scripts/lint-mcp-refs.mjs [--strict-tools]
|
|
24
|
+
// Exit: 0 clean, 1 unknown server segment (or a missing tool with --strict-tools).
|
|
25
|
+
|
|
26
|
+
import { readFileSync, existsSync, readdirSync, statSync } from "node:fs";
|
|
27
|
+
import { homedir } from "node:os";
|
|
28
|
+
import { dirname, join, resolve } from "node:path";
|
|
29
|
+
import { fileURLToPath } from "node:url";
|
|
30
|
+
import { spawnSync } from "node:child_process";
|
|
31
|
+
|
|
32
|
+
const repoRoot = join(dirname(fileURLToPath(import.meta.url)), "..", "..");
|
|
33
|
+
const strictTools = process.argv.includes("--strict-tools");
|
|
34
|
+
|
|
35
|
+
// Servers the pipeline is allowed to reference. The key is the registered
|
|
36
|
+
// server name as it appears in an mcpServers config, which is what the host
|
|
37
|
+
// interpolates into the tool name.
|
|
38
|
+
const KNOWN_SERVERS = {
|
|
39
|
+
"dev-toolkit": "companion device/browser toolkit (@mmerterden/dev-toolkit-mcp)",
|
|
40
|
+
claude_ai_Figma: "Figma MCP (analysis phase only)",
|
|
41
|
+
XcodeBuildMCP: "optional third-party Xcode build server",
|
|
42
|
+
};
|
|
43
|
+
// Spellings that look right but cannot resolve to a registered server.
|
|
44
|
+
const KNOWN_BAD = { dev_toolkit: "dev-toolkit", "claude-ai-Figma": "claude_ai_Figma" };
|
|
45
|
+
|
|
46
|
+
const SCAN_DIRS = ["pipeline/commands", "pipeline/skills", "pipeline/multi-agent-refs"];
|
|
47
|
+
const SCAN_EXT = /\.(md|sh|mjs|json)$/;
|
|
48
|
+
const PRUNE = new Set([
|
|
49
|
+
"node_modules",
|
|
50
|
+
".git",
|
|
51
|
+
".worktrees",
|
|
52
|
+
"Pods",
|
|
53
|
+
".build",
|
|
54
|
+
"DerivedData",
|
|
55
|
+
".next",
|
|
56
|
+
]);
|
|
57
|
+
|
|
58
|
+
function walk(dir, out = []) {
|
|
59
|
+
let entries;
|
|
60
|
+
try {
|
|
61
|
+
entries = readdirSync(dir, { withFileTypes: true });
|
|
62
|
+
} catch {
|
|
63
|
+
return out;
|
|
64
|
+
}
|
|
65
|
+
for (const e of entries) {
|
|
66
|
+
if (PRUNE.has(e.name)) continue;
|
|
67
|
+
const p = join(dir, e.name);
|
|
68
|
+
if (e.isDirectory()) walk(p, out);
|
|
69
|
+
else if (SCAN_EXT.test(e.name)) out.push(p);
|
|
70
|
+
}
|
|
71
|
+
return out;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const files = SCAN_DIRS.flatMap((d) => walk(join(repoRoot, d)));
|
|
75
|
+
const REF = /mcp__([A-Za-z0-9_-]+?)__([a-z0-9_]+)/g;
|
|
76
|
+
|
|
77
|
+
const errors = [];
|
|
78
|
+
const refs = new Map(); // server -> Map(tool -> Set(file:line))
|
|
79
|
+
|
|
80
|
+
for (const file of files) {
|
|
81
|
+
const rel = file.slice(repoRoot.length + 1);
|
|
82
|
+
const lines = readFileSync(file, "utf8").split("\n");
|
|
83
|
+
lines.forEach((line, i) => {
|
|
84
|
+
for (const m of line.matchAll(REF)) {
|
|
85
|
+
const [, server, tool] = m;
|
|
86
|
+
// Prose writes families as mcp__dev-toolkit__ios_* - a wildcard, not a tool.
|
|
87
|
+
const next = line[m.index + m[0].length];
|
|
88
|
+
if (next === "*" || tool.endsWith("_")) continue;
|
|
89
|
+
const where = `${rel}:${i + 1}`;
|
|
90
|
+
if (!refs.has(server)) refs.set(server, new Map());
|
|
91
|
+
const tools = refs.get(server);
|
|
92
|
+
if (!tools.has(tool)) tools.set(tool, new Set());
|
|
93
|
+
tools.get(tool).add(where);
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
for (const [server, tools] of refs) {
|
|
99
|
+
if (server in KNOWN_SERVERS) continue;
|
|
100
|
+
const fix = KNOWN_BAD[server];
|
|
101
|
+
const sample = [...tools.values()][0].values().next().value;
|
|
102
|
+
errors.push(
|
|
103
|
+
fix
|
|
104
|
+
? `unknown MCP server segment "${server}" (${tools.size} tool refs, e.g. ${sample}) - the registered server is "${fix}", so the prefix must be mcp__${fix}__`
|
|
105
|
+
: `unknown MCP server segment "${server}" (${tools.size} tool refs, e.g. ${sample}) - add it to KNOWN_SERVERS in lint-mcp-refs.mjs if this server is expected, or fix the prefix to match the registered server name`,
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// ---- optional deep check: do the referenced dev-toolkit tools exist? --------
|
|
110
|
+
|
|
111
|
+
function resolveToolkitRepo() {
|
|
112
|
+
const prefsPath = join(homedir(), ".claude", "multi-agent-preferences.json");
|
|
113
|
+
if (existsSync(prefsPath)) {
|
|
114
|
+
try {
|
|
115
|
+
const dt = JSON.parse(readFileSync(prefsPath, "utf8"))?.global?.devToolkit;
|
|
116
|
+
if (dt && dt.enabled === false) return null;
|
|
117
|
+
if (dt?.localPath) {
|
|
118
|
+
const p = dt.localPath.replace(/^~|\$HOME/, homedir());
|
|
119
|
+
if (existsSync(join(p, "package.json"))) return p;
|
|
120
|
+
}
|
|
121
|
+
} catch {
|
|
122
|
+
/* fall through to registration lookup */
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
const candidates = [];
|
|
126
|
+
const collect = (obj) => {
|
|
127
|
+
for (const [name, cfg] of Object.entries(obj || {})) {
|
|
128
|
+
const arg = Array.isArray(cfg?.args) ? cfg.args.find((a) => /\.(mjs|js)$/.test(a)) : null;
|
|
129
|
+
if (name.includes("dev") && arg) candidates.push(dirname(resolve(arg)));
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
for (const cfgPath of [
|
|
133
|
+
join(homedir(), ".claude.json"),
|
|
134
|
+
join(homedir(), ".claude", "settings.json"),
|
|
135
|
+
]) {
|
|
136
|
+
if (!existsSync(cfgPath)) continue;
|
|
137
|
+
try {
|
|
138
|
+
const json = JSON.parse(readFileSync(cfgPath, "utf8"));
|
|
139
|
+
collect(json.mcpServers);
|
|
140
|
+
for (const proj of Object.values(json.projects || {})) collect(proj.mcpServers);
|
|
141
|
+
} catch {
|
|
142
|
+
/* ignore unreadable config */
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
for (const c of candidates) {
|
|
146
|
+
try {
|
|
147
|
+
const pkg = JSON.parse(readFileSync(join(c, "package.json"), "utf8"));
|
|
148
|
+
if (pkg.dependencies?.["@modelcontextprotocol/sdk"] && statSync(join(c, ".git"))) return c;
|
|
149
|
+
} catch {
|
|
150
|
+
/* not the toolkit */
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function listTools(repo) {
|
|
157
|
+
const frames =
|
|
158
|
+
[
|
|
159
|
+
'{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"lint-mcp-refs","version":"1"}}}',
|
|
160
|
+
'{"jsonrpc":"2.0","method":"notifications/initialized"}',
|
|
161
|
+
'{"jsonrpc":"2.0","id":2,"method":"tools/list"}',
|
|
162
|
+
].join("\n") + "\n";
|
|
163
|
+
const r = spawnSync(process.execPath, ["index.js"], {
|
|
164
|
+
cwd: repo,
|
|
165
|
+
input: frames,
|
|
166
|
+
encoding: "utf8",
|
|
167
|
+
timeout: 20000,
|
|
168
|
+
maxBuffer: 32 * 1024 * 1024,
|
|
169
|
+
});
|
|
170
|
+
if (r.error || !r.stdout) return null;
|
|
171
|
+
for (const line of r.stdout.split("\n")) {
|
|
172
|
+
if (!line.trim()) continue;
|
|
173
|
+
try {
|
|
174
|
+
const msg = JSON.parse(line);
|
|
175
|
+
if (msg.id === 2 && msg.result?.tools) return msg.result.tools.map((t) => t.name);
|
|
176
|
+
} catch {
|
|
177
|
+
/* not a frame */
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const referencedToolkitTools = refs.get("dev-toolkit");
|
|
184
|
+
let deep = "skipped (no local dev-toolkit checkout)";
|
|
185
|
+
if (referencedToolkitTools) {
|
|
186
|
+
const repo = resolveToolkitRepo();
|
|
187
|
+
const served = repo ? listTools(repo) : null;
|
|
188
|
+
if (!served) {
|
|
189
|
+
deep = repo ? `skipped (${repo} did not answer tools/list)` : deep;
|
|
190
|
+
} else {
|
|
191
|
+
const missing = [...referencedToolkitTools.keys()].filter((t) => !served.includes(t));
|
|
192
|
+
if (missing.length === 0) {
|
|
193
|
+
deep = `all ${referencedToolkitTools.size} referenced tools exist in the live server (${served.length} served)`;
|
|
194
|
+
} else {
|
|
195
|
+
const msg = `dev-toolkit tools referenced but not served (${served.length} served): ${missing
|
|
196
|
+
.map((t) => `${t} [${[...referencedToolkitTools.get(t)][0]}]`)
|
|
197
|
+
.join(", ")}`;
|
|
198
|
+
deep = `${missing.length} referenced tool(s) not served`;
|
|
199
|
+
if (strictTools) errors.push(msg);
|
|
200
|
+
else console.warn(` WARNING - ${msg}`);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const totalRefs = [...refs.values()].reduce((n, tools) => n + tools.size, 0);
|
|
206
|
+
console.log(
|
|
207
|
+
`lint-mcp-refs: ${totalRefs} distinct tool references across ${refs.size} server(s) in ${files.length} files`,
|
|
208
|
+
);
|
|
209
|
+
for (const [server, tools] of refs) {
|
|
210
|
+
console.log(` ${server in KNOWN_SERVERS ? "✓" : "✗"} mcp__${server}__* : ${tools.size} tools`);
|
|
211
|
+
}
|
|
212
|
+
console.log(` tool-existence check: ${deep}`);
|
|
213
|
+
|
|
214
|
+
if (errors.length > 0) {
|
|
215
|
+
console.error(`lint-mcp-refs: ${errors.length} error(s)`);
|
|
216
|
+
for (const e of errors) console.error(" " + e);
|
|
217
|
+
process.exit(1);
|
|
218
|
+
}
|
|
@@ -14,12 +14,12 @@
|
|
|
14
14
|
// - description: required, non-empty, single line, <= 1024 chars
|
|
15
15
|
// - user-invocable: when present, must be true or false
|
|
16
16
|
// - platform: when present, must be ios | android | common | all
|
|
17
|
+
// - description: must carry a routing/trigger clause ("use when ..."), the
|
|
18
|
+
// one thing it is for - blocking since all 193 skills have one
|
|
17
19
|
//
|
|
18
20
|
// Spec-conformance signals (warnings - never fail the gate):
|
|
19
21
|
// - name should match the agent-skills spec pattern
|
|
20
22
|
// (lowercase letters/digits/hyphens, <= 64 chars)
|
|
21
|
-
// - description should carry a routing/trigger clause ("use when ...")
|
|
22
|
-
// so the model can decide when to load the skill
|
|
23
23
|
// - a body over ~500 lines with no companion-file reference should be
|
|
24
24
|
// split into references/ files loaded on demand
|
|
25
25
|
|
|
@@ -99,12 +99,19 @@ for (const file of files) {
|
|
|
99
99
|
if (!isBlockScalar && words < 6) {
|
|
100
100
|
warn(file, `description is terse (${words} words) - say what it does AND when to use it`);
|
|
101
101
|
}
|
|
102
|
-
// Routing quality (
|
|
103
|
-
//
|
|
104
|
-
//
|
|
105
|
-
|
|
102
|
+
// Routing quality (BLOCKING since the backlog reached zero). A description
|
|
103
|
+
// without a trigger clause tells the model what the skill is and nothing
|
|
104
|
+
// about when to reach for it, which is the one job the description has -
|
|
105
|
+
// it is the routing rule, not a summary.
|
|
106
|
+
//
|
|
107
|
+
// This was warn-only while 93 of 193 skills were missing one, because a
|
|
108
|
+
// gate that fires on half the tree gets ignored. All 93 were written, so
|
|
109
|
+
// the baseline is zero and it can hold the line. Block scalars are skipped
|
|
110
|
+
// for the same parser reason as above.
|
|
111
|
+
const ROUTING_RE =
|
|
112
|
+
/\b(use (this|it|when|for|whenever)|when (the|a|an|you|asked|working|building|implementing|writing|reviewing)|triggers?|invoked?( when| for)?|applies when|activated?)\b/i;
|
|
106
113
|
if (!isBlockScalar && !ROUTING_RE.test(desc)) {
|
|
107
|
-
|
|
114
|
+
fail(file, 'description has no routing/trigger clause (e.g. "use when ...")');
|
|
108
115
|
}
|
|
109
116
|
}
|
|
110
117
|
|
|
@@ -115,9 +122,13 @@ for (const file of files) {
|
|
|
115
122
|
const bodyLines = body.split("\n").length;
|
|
116
123
|
if (bodyLines > 500) {
|
|
117
124
|
const referencesCompanion =
|
|
118
|
-
/\]\((?!https?:|mailto:|#)[^)]+\)/.test(body) ||
|
|
125
|
+
/\]\((?!https?:|mailto:|#)[^)]+\)/.test(body) ||
|
|
126
|
+
/`[^`\n]*\/[^`\n]*\.(md|sh|mjs|json|py|txt)`/.test(body);
|
|
119
127
|
if (!referencesCompanion) {
|
|
120
|
-
warn(
|
|
128
|
+
warn(
|
|
129
|
+
file,
|
|
130
|
+
`body is ${bodyLines} lines with no companion-file reference - consider splitting into references/`,
|
|
131
|
+
);
|
|
121
132
|
}
|
|
122
133
|
}
|
|
123
134
|
|
|
@@ -116,7 +116,12 @@ if (invokedDirectly) {
|
|
|
116
116
|
console.error("usage: localize-commands.mjs <apply|restore> [--dir <commandsDir>]");
|
|
117
117
|
process.exit(2);
|
|
118
118
|
}
|
|
119
|
-
let dir = join(
|
|
119
|
+
let dir = join(
|
|
120
|
+
process.env.HOME || process.env.USERPROFILE || "",
|
|
121
|
+
".claude",
|
|
122
|
+
"commands",
|
|
123
|
+
"multi-agent",
|
|
124
|
+
);
|
|
120
125
|
const dirFlag = args.indexOf("--dir");
|
|
121
126
|
if (dirFlag >= 0) {
|
|
122
127
|
if (!args[dirFlag + 1]) {
|
|
@@ -38,7 +38,9 @@ const repoRoot = resolve(here, "..", "..");
|
|
|
38
38
|
|
|
39
39
|
const args = process.argv.slice(2);
|
|
40
40
|
if (args.length === 0 || args[0].startsWith("--")) {
|
|
41
|
-
console.error(
|
|
41
|
+
console.error(
|
|
42
|
+
'Usage: match-skills.mjs "<task text>" [--touched-files a,b] [--stack ios] [--limit 12] [--json]',
|
|
43
|
+
);
|
|
42
44
|
process.exit(1);
|
|
43
45
|
}
|
|
44
46
|
|
|
@@ -51,7 +53,11 @@ const opts = {
|
|
|
51
53
|
json: false,
|
|
52
54
|
};
|
|
53
55
|
for (let i = 1; i < args.length; i++) {
|
|
54
|
-
if (args[i] === "--touched-files")
|
|
56
|
+
if (args[i] === "--touched-files")
|
|
57
|
+
opts.touchedFiles = (args[++i] || "")
|
|
58
|
+
.split(",")
|
|
59
|
+
.map((s) => s.trim())
|
|
60
|
+
.filter(Boolean);
|
|
55
61
|
else if (args[i] === "--stack") opts.stack = (args[++i] || "").trim().toLowerCase();
|
|
56
62
|
else if (args[i] === "--limit") opts.limit = parseInt(args[++i], 10) || 12;
|
|
57
63
|
else if (args[i] === "--index") opts.indexPath = resolve(args[++i]);
|
|
@@ -86,8 +92,13 @@ function globToRe(glob) {
|
|
|
86
92
|
// `**/` means zero-or-more path segments, so a leading `**/` must also
|
|
87
93
|
// match top-level files (no dir prefix). Without the optional slash,
|
|
88
94
|
// `**/Auth/**` compiled to `.*\/Auth\/.*` and missed root-level `Auth/...`.
|
|
89
|
-
if (glob[i + 2] === "/") {
|
|
90
|
-
|
|
95
|
+
if (glob[i + 2] === "/") {
|
|
96
|
+
re += "(?:.*/)?";
|
|
97
|
+
i += 2;
|
|
98
|
+
} else {
|
|
99
|
+
re += ".*";
|
|
100
|
+
i++;
|
|
101
|
+
}
|
|
91
102
|
} else if (c === "*") re += "[^/]*";
|
|
92
103
|
else if (c === "?") re += ".";
|
|
93
104
|
else if (".+()|{}[]^$\\".includes(c)) re += "\\" + c;
|
|
@@ -16,9 +16,13 @@ PROJECT_ROOT="${1:?usage: memory-load.sh <project-root>}"
|
|
|
16
16
|
MEM_DIR="$PROJECT_ROOT/.multi-agent/memory"
|
|
17
17
|
MEM_INDEX="$MEM_DIR/MEMORY.md"
|
|
18
18
|
|
|
19
|
-
# Pref-gate: locate
|
|
19
|
+
# Pref-gate: locate the preferences file (home-dir first, then XDG config).
|
|
20
|
+
# The canonical name written by the installer is multi-agent-preferences.json;
|
|
21
|
+
# the bare preferences.json names are kept only as legacy fallbacks.
|
|
20
22
|
prefs_file=""
|
|
21
23
|
for candidate in \
|
|
24
|
+
"$HOME/.claude/multi-agent-preferences.json" \
|
|
25
|
+
"$HOME/.config/multi-agent-pipeline/multi-agent-preferences.json" \
|
|
22
26
|
"$HOME/.claude/preferences.json" \
|
|
23
27
|
"$HOME/.config/multi-agent-pipeline/preferences.json"
|
|
24
28
|
do
|