@mmerterden/multi-agent-pipeline 12.6.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 +83 -0
- package/README.md +18 -18
- 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 +28 -28
- package/docs/performance.md +16 -16
- package/docs/recovery-guide.md +39 -39
- package/index.js +4 -4
- package/install/_common.mjs +5 -11
- package/install/_copilot-instructions.mjs +2 -2
- package/install/_dev-only-files.mjs +1 -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 +13 -10
- package/pipeline/commands/multi-agent/SKILL.md +1 -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 +1 -1
- 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 +3 -3
- 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 +16 -8
- package/pipeline/commands/multi-agent/resume/SKILL.md +2 -2
- package/pipeline/commands/multi-agent/review/SKILL.md +1 -1
- 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 +5 -5
- package/pipeline/commands/multi-agent/test/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/uninstall/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/update/SKILL.md +3 -3
- 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 +1 -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 +1 -1
- package/pipeline/multi-agent-refs/features/model-fallback.md +2 -2
- package/pipeline/multi-agent-refs/phases/phase-4-review.md +1 -1
- package/pipeline/preferences-template.json +5 -11
- package/pipeline/schemas/agent-state.schema.json +39 -9
- 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 +32 -11
- 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/plan-todos.schema.json +6 -3
- package/pipeline/schemas/planning-output.schema.json +5 -1
- package/pipeline/schemas/prefs.schema.json +91 -229
- 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/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 +20 -7
- package/pipeline/scripts/cost-budget-check.mjs +2 -1
- 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 +3 -3
- 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 +103 -36
- package/pipeline/scripts/lint-mcp-refs.mjs +13 -2
- 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/migrate-prefs.mjs +33 -16
- package/pipeline/scripts/phase-tracker.sh +3 -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-context-budget.sh +72 -0
- 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-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 +126 -34
- package/pipeline/scripts/uninstall.mjs +74 -30
- 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/.skills-index.json +91 -91
- package/pipeline/skills/shared/README.md +57 -57
- package/pipeline/skills/shared/core/apple-archive-compliance/SKILL.md +1 -1
- 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 +1 -1
- 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 +1 -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 +16 -8
- package/pipeline/skills/shared/core/multi-agent-resume/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-review/SKILL.md +1 -1
- 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 +1 -1
- package/pipeline/skills/shared/core/multi-agent-test/SKILL.md +1 -1
- 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 +57 -57
- package/pipeline/scripts/smoke-plugin-validate.sh +0 -64
|
@@ -53,8 +53,10 @@ for (let i = 1; i < argv.length; i++) {
|
|
|
53
53
|
if (a.startsWith("--")) {
|
|
54
54
|
const k = a.slice(2);
|
|
55
55
|
const v = argv[i + 1];
|
|
56
|
-
if (v !== undefined && !v.startsWith("--")) {
|
|
57
|
-
|
|
56
|
+
if (v !== undefined && !v.startsWith("--")) {
|
|
57
|
+
flags[k] = v;
|
|
58
|
+
i++;
|
|
59
|
+
} else flags[k] = true;
|
|
58
60
|
}
|
|
59
61
|
}
|
|
60
62
|
|
|
@@ -75,7 +77,7 @@ function repoSlug() {
|
|
|
75
77
|
const commonDir = execFileSync(
|
|
76
78
|
"git",
|
|
77
79
|
["rev-parse", "--path-format=absolute", "--git-common-dir"],
|
|
78
|
-
{ encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }
|
|
80
|
+
{ encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] },
|
|
79
81
|
).trim();
|
|
80
82
|
if (commonDir) {
|
|
81
83
|
const base = basename(commonDir);
|
|
@@ -83,19 +85,30 @@ function repoSlug() {
|
|
|
83
85
|
const slug = basename(root);
|
|
84
86
|
if (slug) return slug;
|
|
85
87
|
}
|
|
86
|
-
} catch {
|
|
88
|
+
} catch {
|
|
89
|
+
/* not a git repo, or git too old for --path-format */
|
|
90
|
+
}
|
|
87
91
|
try {
|
|
88
92
|
const top = execFileSync("git", ["rev-parse", "--show-toplevel"], {
|
|
89
93
|
encoding: "utf8",
|
|
90
94
|
stdio: ["ignore", "pipe", "ignore"],
|
|
91
95
|
}).trim();
|
|
92
96
|
if (top) return basename(top);
|
|
93
|
-
} catch {
|
|
97
|
+
} catch {
|
|
98
|
+
/* not a git repo */
|
|
99
|
+
}
|
|
94
100
|
return "default";
|
|
95
101
|
}
|
|
96
102
|
|
|
97
103
|
function corpusPath(slug) {
|
|
98
|
-
return join(
|
|
104
|
+
return join(
|
|
105
|
+
homedir(),
|
|
106
|
+
".claude",
|
|
107
|
+
"memory",
|
|
108
|
+
"multi-agent",
|
|
109
|
+
slug || repoSlug(),
|
|
110
|
+
"triage-corpus.jsonl",
|
|
111
|
+
);
|
|
99
112
|
}
|
|
100
113
|
|
|
101
114
|
function ensureDir(p) {
|
|
@@ -107,10 +120,12 @@ function ensureDir(p) {
|
|
|
107
120
|
// flagged stale so a reused finding is verified, not trusted blindly.
|
|
108
121
|
function repoTop() {
|
|
109
122
|
try {
|
|
110
|
-
return
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
123
|
+
return (
|
|
124
|
+
execFileSync("git", ["rev-parse", "--show-toplevel"], {
|
|
125
|
+
encoding: "utf8",
|
|
126
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
127
|
+
}).trim() || null
|
|
128
|
+
);
|
|
114
129
|
} catch {
|
|
115
130
|
return null;
|
|
116
131
|
}
|
|
@@ -119,27 +134,84 @@ function repoTop() {
|
|
|
119
134
|
function fileSha(file, top) {
|
|
120
135
|
if (!file || !top) return null;
|
|
121
136
|
try {
|
|
122
|
-
return
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
137
|
+
return (
|
|
138
|
+
execFileSync("git", ["hash-object", "--", file], {
|
|
139
|
+
cwd: top,
|
|
140
|
+
encoding: "utf8",
|
|
141
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
142
|
+
}).trim() || null
|
|
143
|
+
);
|
|
127
144
|
} catch {
|
|
128
145
|
return null;
|
|
129
146
|
}
|
|
130
147
|
}
|
|
131
148
|
|
|
132
149
|
const STOP = new Set([
|
|
133
|
-
"the",
|
|
134
|
-
"
|
|
135
|
-
"
|
|
136
|
-
"
|
|
150
|
+
"the",
|
|
151
|
+
"a",
|
|
152
|
+
"an",
|
|
153
|
+
"and",
|
|
154
|
+
"or",
|
|
155
|
+
"but",
|
|
156
|
+
"of",
|
|
157
|
+
"in",
|
|
158
|
+
"on",
|
|
159
|
+
"at",
|
|
160
|
+
"to",
|
|
161
|
+
"for",
|
|
162
|
+
"with",
|
|
163
|
+
"by",
|
|
164
|
+
"is",
|
|
165
|
+
"are",
|
|
166
|
+
"be",
|
|
167
|
+
"been",
|
|
168
|
+
"being",
|
|
169
|
+
"this",
|
|
170
|
+
"that",
|
|
171
|
+
"these",
|
|
172
|
+
"those",
|
|
173
|
+
"it",
|
|
174
|
+
"its",
|
|
175
|
+
"as",
|
|
176
|
+
"not",
|
|
177
|
+
"no",
|
|
178
|
+
"do",
|
|
179
|
+
"does",
|
|
180
|
+
"did",
|
|
181
|
+
"done",
|
|
182
|
+
"have",
|
|
183
|
+
"has",
|
|
184
|
+
"had",
|
|
185
|
+
"will",
|
|
186
|
+
"would",
|
|
187
|
+
"should",
|
|
188
|
+
"could",
|
|
189
|
+
"may",
|
|
190
|
+
"might",
|
|
191
|
+
"can",
|
|
192
|
+
"cant",
|
|
193
|
+
"don",
|
|
194
|
+
"wont",
|
|
195
|
+
"you",
|
|
196
|
+
"your",
|
|
197
|
+
"i",
|
|
198
|
+
"we",
|
|
199
|
+
"they",
|
|
200
|
+
"them",
|
|
201
|
+
"he",
|
|
202
|
+
"she",
|
|
203
|
+
"his",
|
|
204
|
+
"her",
|
|
205
|
+
"our",
|
|
206
|
+
"their",
|
|
137
207
|
]);
|
|
138
208
|
|
|
139
209
|
function tokenize(text) {
|
|
140
210
|
if (!text) return new Set();
|
|
141
211
|
const out = new Set();
|
|
142
|
-
for (const m of String(text)
|
|
212
|
+
for (const m of String(text)
|
|
213
|
+
.toLowerCase()
|
|
214
|
+
.matchAll(/[a-z][a-z0-9_]{2,}/g)) {
|
|
143
215
|
if (!STOP.has(m[0])) out.add(m[0]);
|
|
144
216
|
}
|
|
145
217
|
return out;
|
|
@@ -175,8 +247,11 @@ function readCorpus(slug) {
|
|
|
175
247
|
const rows = [];
|
|
176
248
|
for (const line of raw.split("\n")) {
|
|
177
249
|
if (!line.trim()) continue;
|
|
178
|
-
try {
|
|
179
|
-
|
|
250
|
+
try {
|
|
251
|
+
rows.push(JSON.parse(line));
|
|
252
|
+
} catch {
|
|
253
|
+
/* skip corrupted line */
|
|
254
|
+
}
|
|
180
255
|
}
|
|
181
256
|
return rows;
|
|
182
257
|
}
|
|
@@ -198,18 +273,23 @@ function ingest() {
|
|
|
198
273
|
const slug = repoSlug();
|
|
199
274
|
|
|
200
275
|
let triage;
|
|
201
|
-
try {
|
|
202
|
-
|
|
276
|
+
try {
|
|
277
|
+
triage = JSON.parse(readFileSync(triagePath, "utf8"));
|
|
278
|
+
} catch (e) {
|
|
279
|
+
die(`cannot parse triage JSON: ${e.message}`, 1);
|
|
280
|
+
}
|
|
203
281
|
|
|
204
282
|
const ts = new Date().toISOString();
|
|
205
283
|
const top = repoTop();
|
|
206
284
|
const corpus = readCorpus(slug);
|
|
207
|
-
const seen = new Set(
|
|
285
|
+
const seen = new Set(
|
|
286
|
+
corpus.map((r) => `${r.task_id}::${r.classification}::${r.file}::${r.issue}`),
|
|
287
|
+
);
|
|
208
288
|
|
|
209
289
|
const buckets = [
|
|
210
|
-
["accepted",
|
|
211
|
-
["deferred",
|
|
212
|
-
["rejected",
|
|
290
|
+
["accepted", triage.accepted || []],
|
|
291
|
+
["deferred", (triage.deferred || []).map((d) => ({ ...d.finding, _reason: d.reason }))],
|
|
292
|
+
["rejected", (triage.rejected || []).map((d) => ({ ...d.finding, _reason: d.reason }))],
|
|
213
293
|
];
|
|
214
294
|
|
|
215
295
|
let written = 0;
|
|
@@ -283,12 +363,16 @@ function query() {
|
|
|
283
363
|
score: Math.round(score * 1000) / 1000,
|
|
284
364
|
};
|
|
285
365
|
});
|
|
286
|
-
process.stdout.write(
|
|
366
|
+
process.stdout.write(
|
|
367
|
+
JSON.stringify({ ok: true, slug, total: filtered.length, hits: ranked }) + "\n",
|
|
368
|
+
);
|
|
287
369
|
process.exit(0);
|
|
288
370
|
}
|
|
289
371
|
|
|
290
372
|
function pathCmd() {
|
|
291
|
-
process.stdout.write(
|
|
373
|
+
process.stdout.write(
|
|
374
|
+
corpusPath(flags["repo-slug"] ? String(flags["repo-slug"]) : repoSlug()) + "\n",
|
|
375
|
+
);
|
|
292
376
|
process.exit(0);
|
|
293
377
|
}
|
|
294
378
|
|
|
@@ -313,10 +397,18 @@ function statsCmd() {
|
|
|
313
397
|
}
|
|
314
398
|
|
|
315
399
|
switch (SUB) {
|
|
316
|
-
case "ingest":
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
case "
|
|
400
|
+
case "ingest":
|
|
401
|
+
ingest();
|
|
402
|
+
break;
|
|
403
|
+
case "query":
|
|
404
|
+
query();
|
|
405
|
+
break;
|
|
406
|
+
case "path":
|
|
407
|
+
pathCmd();
|
|
408
|
+
break;
|
|
409
|
+
case "stats":
|
|
410
|
+
statsCmd();
|
|
411
|
+
break;
|
|
320
412
|
default:
|
|
321
413
|
process.stderr.write("usage: triage-memory.mjs <ingest|query|path|stats> [flags]\n");
|
|
322
414
|
process.exit(64);
|
|
@@ -64,10 +64,7 @@ export const PIPELINE_AGENT_FILES = [
|
|
|
64
64
|
* Non-multi-agent core skills the installer ships from
|
|
65
65
|
* pipeline/skills/shared/core/ (the multi-agent* dirs are matched by prefix).
|
|
66
66
|
*/
|
|
67
|
-
export const PIPELINE_CORE_SKILL_DIRS = [
|
|
68
|
-
"apple-archive-compliance",
|
|
69
|
-
"google-play-compliance",
|
|
70
|
-
];
|
|
67
|
+
export const PIPELINE_CORE_SKILL_DIRS = ["apple-archive-compliance", "google-play-compliance"];
|
|
71
68
|
|
|
72
69
|
const flags = process.argv.slice(2).filter((a) => a !== "uninstall");
|
|
73
70
|
|
|
@@ -79,14 +76,15 @@ const TOOL_FLAGS = [
|
|
|
79
76
|
"--antigravity",
|
|
80
77
|
"--codex",
|
|
81
78
|
];
|
|
82
|
-
const explicitlyTargeted =
|
|
83
|
-
flags.some((f) => TOOL_FLAGS.includes(f)) || flags.includes("--all");
|
|
79
|
+
const explicitlyTargeted = flags.some((f) => TOOL_FLAGS.includes(f)) || flags.includes("--all");
|
|
84
80
|
|
|
85
81
|
const forClaude = flags.includes("--claude") || flags.includes("--all") || !explicitlyTargeted;
|
|
86
82
|
const forCopilot = flags.includes("--copilot") || flags.includes("--all") || !explicitlyTargeted;
|
|
87
83
|
const forCursor = flags.includes("--cursor") || flags.includes("--all") || !explicitlyTargeted;
|
|
88
|
-
const forCopilotChat =
|
|
89
|
-
|
|
84
|
+
const forCopilotChat =
|
|
85
|
+
flags.includes("--copilot-chat") || flags.includes("--all") || !explicitlyTargeted;
|
|
86
|
+
const forAntigravity =
|
|
87
|
+
flags.includes("--antigravity") || flags.includes("--all") || !explicitlyTargeted;
|
|
90
88
|
const forCodex = flags.includes("--codex") || flags.includes("--all") || !explicitlyTargeted;
|
|
91
89
|
|
|
92
90
|
const dryRun = flags.includes("--dry-run");
|
|
@@ -194,8 +192,7 @@ function legacyTrailingContent(section) {
|
|
|
194
192
|
function stripManagedBlock(filePath) {
|
|
195
193
|
if (!existsSync(filePath)) return false;
|
|
196
194
|
const content = readFileSync(filePath, "utf-8");
|
|
197
|
-
const re =
|
|
198
|
-
/\s*<!-- multi-agent-pipeline:begin -->[\s\S]*?<!-- multi-agent-pipeline:end -->\s*/m;
|
|
195
|
+
const re = /\s*<!-- multi-agent-pipeline:begin -->[\s\S]*?<!-- multi-agent-pipeline:end -->\s*/m;
|
|
199
196
|
if (!re.test(content)) {
|
|
200
197
|
// Copilot's install (install/copilot.mjs) does not wrap the block in
|
|
201
198
|
// begin/end markers. v11.4.1+ writes an explicit end marker after the
|
|
@@ -276,8 +273,10 @@ function cleanClaudeSettings(settingsPath) {
|
|
|
276
273
|
// entry as ours either way.
|
|
277
274
|
settings.hooks.PreToolUse = settings.hooks.PreToolUse.filter(
|
|
278
275
|
(h) =>
|
|
279
|
-
!(
|
|
280
|
-
h.
|
|
276
|
+
!(
|
|
277
|
+
(h.matcher === "Bash" || h.matcher === "Bash(git commit:*)") &&
|
|
278
|
+
h.hooks?.some((sub) => sub.command?.includes("pre-commit-check.sh"))
|
|
279
|
+
),
|
|
281
280
|
);
|
|
282
281
|
if (settings.hooks.PreToolUse.length < before) touched = true;
|
|
283
282
|
if (settings.hooks.PreToolUse.length === 0) delete settings.hooks.PreToolUse;
|
|
@@ -313,12 +312,28 @@ export async function main() {
|
|
|
313
312
|
if (dryRun) console.log(" [DRY-RUN] Nothing will be modified.");
|
|
314
313
|
console.log("");
|
|
315
314
|
console.log(" Targets:");
|
|
316
|
-
if (forClaude)
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
if (
|
|
321
|
-
|
|
315
|
+
if (forClaude)
|
|
316
|
+
console.log(
|
|
317
|
+
" - Claude Code (~/.claude/{commands/multi-agent, scripts, multi-agent-refs, schemas, lib} + pipeline skills + pipeline agent files)",
|
|
318
|
+
);
|
|
319
|
+
if (forCopilot)
|
|
320
|
+
console.log(
|
|
321
|
+
" - Copilot CLI (~/.copilot/{scripts, schemas, lib} + pipeline skills + pipeline agent files + copilot-instructions.md section)",
|
|
322
|
+
);
|
|
323
|
+
if (forCursor)
|
|
324
|
+
console.log(` - Cursor (${adapterTarget}/.cursor/rules/multi-agent-* + .cursorrules)`);
|
|
325
|
+
if (forCopilotChat)
|
|
326
|
+
console.log(
|
|
327
|
+
` - GitHub Copilot Chat (${adapterTarget}/.github/copilot-instructions.md block + .github/instructions/multi-agent-*)`,
|
|
328
|
+
);
|
|
329
|
+
if (forAntigravity)
|
|
330
|
+
console.log(
|
|
331
|
+
` - Antigravity (${adapterTarget}/.agent/rules + .agent/workflows + AGENTS.md block + .agent/mcp_config.json)`,
|
|
332
|
+
);
|
|
333
|
+
if (forCodex)
|
|
334
|
+
console.log(
|
|
335
|
+
" - OpenAI Codex CLI (~/.codex/prompts/multi-agent.md + AGENTS.md block + config.toml mcp block)",
|
|
336
|
+
);
|
|
322
337
|
console.log("");
|
|
323
338
|
if (allData) {
|
|
324
339
|
console.log(" FULL CLEANUP (--all-data) also removes:");
|
|
@@ -327,7 +342,9 @@ export async function main() {
|
|
|
327
342
|
console.log("");
|
|
328
343
|
}
|
|
329
344
|
console.log(" PRESERVED (never touched):");
|
|
330
|
-
console.log(
|
|
345
|
+
console.log(
|
|
346
|
+
" - Personal access tokens in OS credential store (Keychain / Credential Manager / libsecret)",
|
|
347
|
+
);
|
|
331
348
|
console.log(" - ~/.claude/CLAUDE.md (your customizations)");
|
|
332
349
|
console.log(" - ~/.claude/rules/ (user-owned; installed write-if-missing, never overwritten)");
|
|
333
350
|
if (!allData) console.log(" - ~/.claude/multi-agent-preferences.json (your settings)");
|
|
@@ -357,8 +374,18 @@ export async function main() {
|
|
|
357
374
|
// (e.g. the git-attribution rule) -- asymmetric data loss. Leave rules
|
|
358
375
|
// in place on uninstall, exactly like CLAUDE.md and preferences.
|
|
359
376
|
const skills = join(CLAUDE, "skills");
|
|
360
|
-
let n = rmMatchingDirs(
|
|
361
|
-
|
|
377
|
+
let n = rmMatchingDirs(
|
|
378
|
+
skills,
|
|
379
|
+
(name) => name === "multi-agent" || name.startsWith("multi-agent-"),
|
|
380
|
+
);
|
|
381
|
+
n += rmMatchingDirs(
|
|
382
|
+
skills,
|
|
383
|
+
(name) =>
|
|
384
|
+
name === "figma-ios" ||
|
|
385
|
+
name === "figma-android" ||
|
|
386
|
+
name === "figma-common" ||
|
|
387
|
+
name === "figma-to-component",
|
|
388
|
+
);
|
|
362
389
|
n += rmMatchingDirs(skills, (name) => PIPELINE_CORE_SKILL_DIRS.includes(name));
|
|
363
390
|
if (n > 0) console.log(` removed ${n} skill dir(s) under ${skills}`);
|
|
364
391
|
rmIfExists(join(skills, ".skills-index.json"));
|
|
@@ -380,8 +407,18 @@ export async function main() {
|
|
|
380
407
|
rmIfExists(join(COP, "lib"));
|
|
381
408
|
rmIfExists(join(COP, ".pipeline-version"));
|
|
382
409
|
const skills = join(COP, "skills");
|
|
383
|
-
let n = rmMatchingDirs(
|
|
384
|
-
|
|
410
|
+
let n = rmMatchingDirs(
|
|
411
|
+
skills,
|
|
412
|
+
(name) => name === "multi-agent" || name.startsWith("multi-agent-"),
|
|
413
|
+
);
|
|
414
|
+
n += rmMatchingDirs(
|
|
415
|
+
skills,
|
|
416
|
+
(name) =>
|
|
417
|
+
name === "figma-ios" ||
|
|
418
|
+
name === "figma-android" ||
|
|
419
|
+
name === "figma-common" ||
|
|
420
|
+
name === "figma-to-component",
|
|
421
|
+
);
|
|
385
422
|
n += rmMatchingDirs(skills, (name) => PIPELINE_CORE_SKILL_DIRS.includes(name));
|
|
386
423
|
if (n > 0) console.log(` removed ${n} skill dir(s) under ${skills}`);
|
|
387
424
|
rmIfExists(join(skills, ".skills-index.json"));
|
|
@@ -434,7 +471,9 @@ export async function main() {
|
|
|
434
471
|
if (n > 0) console.log(` removed ${n} .agent file(s)`);
|
|
435
472
|
stripManagedBlock(join(adapterTarget, "AGENTS.md"));
|
|
436
473
|
if (existsSync(join(adapterTarget, ".agent", "mcp_config.json")))
|
|
437
|
-
console.log(
|
|
474
|
+
console.log(
|
|
475
|
+
" note: .agent/mcp_config.json left untouched (may hold user servers) - remove pipeline entries manually if present",
|
|
476
|
+
);
|
|
438
477
|
}
|
|
439
478
|
|
|
440
479
|
if (forCodex && HOME) {
|
|
@@ -444,23 +483,29 @@ export async function main() {
|
|
|
444
483
|
rmIfExists(join(CODEX, "prompts", "multi-agent.md"));
|
|
445
484
|
stripManagedBlock(join(CODEX, "AGENTS.md"));
|
|
446
485
|
if (existsSync(join(CODEX, "config.toml")))
|
|
447
|
-
console.log(
|
|
486
|
+
console.log(
|
|
487
|
+
" note: ~/.codex/config.toml left untouched (may hold user MCP servers) - remove pipeline entries manually if present",
|
|
488
|
+
);
|
|
448
489
|
}
|
|
449
490
|
|
|
450
491
|
console.log("");
|
|
451
492
|
console.log(" ✓ Pipeline removed.");
|
|
452
493
|
if (!allData) {
|
|
453
494
|
console.log("");
|
|
454
|
-
console.log(
|
|
495
|
+
console.log(
|
|
496
|
+
" Kept your pipeline settings + logs. To remove those too, re-run with --all-data.",
|
|
497
|
+
);
|
|
455
498
|
} else {
|
|
456
499
|
console.log("");
|
|
457
|
-
console.log(
|
|
500
|
+
console.log(
|
|
501
|
+
" Full cleanup done. Still on disk (user-owned, remove by hand if you want a bare system):",
|
|
502
|
+
);
|
|
458
503
|
console.log(" - ~/.claude/CLAUDE.md");
|
|
459
504
|
console.log(" - ~/.claude/rules/");
|
|
460
505
|
}
|
|
461
506
|
console.log("");
|
|
462
507
|
console.log(" Personal access tokens were NOT touched. Inspect with:");
|
|
463
|
-
console.log(
|
|
508
|
+
console.log(' macOS: security find-generic-password -s "<service>" -w');
|
|
464
509
|
console.log(" Windows: cmdkey /list:<target>");
|
|
465
510
|
console.log(" Linux: secret-tool search service <service>");
|
|
466
511
|
console.log("");
|
|
@@ -470,8 +515,7 @@ export async function main() {
|
|
|
470
515
|
|
|
471
516
|
// Run only when executed directly (node uninstall.mjs). Guard lets tests
|
|
472
517
|
// import the exported constants without triggering the uninstall.
|
|
473
|
-
const isMainModule =
|
|
474
|
-
process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href;
|
|
518
|
+
const isMainModule = process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href;
|
|
475
519
|
if (isMainModule) {
|
|
476
520
|
main().catch((e) => {
|
|
477
521
|
console.error("uninstall failed:", e.message);
|
|
@@ -131,7 +131,9 @@ function main() {
|
|
|
131
131
|
headingBlobLower.includes("ui test") ||
|
|
132
132
|
headingBlobLower.includes("ui-test");
|
|
133
133
|
if (!hasUi) {
|
|
134
|
-
errors.push(
|
|
134
|
+
errors.push(
|
|
135
|
+
"front-matter ui_tests: true but no Section 15.6 UI test scenarios found (Locked 31)",
|
|
136
|
+
);
|
|
135
137
|
}
|
|
136
138
|
}
|
|
137
139
|
if (a11yDepth === "full") {
|
|
@@ -140,7 +142,9 @@ function main() {
|
|
|
140
142
|
headingBlobLower.includes("walkthrough") ||
|
|
141
143
|
headingBlobLower.includes("gezinme");
|
|
142
144
|
if (!hasWalkthrough) {
|
|
143
|
-
errors.push(
|
|
145
|
+
errors.push(
|
|
146
|
+
"front-matter a11y_depth: full but no Section 16.2 screen-reader walkthrough found",
|
|
147
|
+
);
|
|
144
148
|
}
|
|
145
149
|
}
|
|
146
150
|
|
|
@@ -156,7 +160,9 @@ function main() {
|
|
|
156
160
|
|
|
157
161
|
// 4. Placeholder whole-section bodies (Locked 2)
|
|
158
162
|
if (/^\s*(TBD|Not applicable|N\/A)\s*$/im.test(text)) {
|
|
159
|
-
warns.push(
|
|
163
|
+
warns.push(
|
|
164
|
+
'a line is a bare "TBD"/"Not applicable"/"N/A" body; omit the section instead (Locked 2)',
|
|
165
|
+
);
|
|
160
166
|
}
|
|
161
167
|
|
|
162
168
|
// 5. Traceability (Full mode only, Locked 31)
|
|
@@ -173,7 +179,9 @@ function main() {
|
|
|
173
179
|
}
|
|
174
180
|
for (const [id, n] of counts) {
|
|
175
181
|
if (n < 2) {
|
|
176
|
-
warns.push(
|
|
182
|
+
warns.push(
|
|
183
|
+
`business rule ${id} is defined but never referenced downstream (test/acceptance/analytics) - Locked 31 traceability`,
|
|
184
|
+
);
|
|
177
185
|
}
|
|
178
186
|
}
|
|
179
187
|
}
|
|
@@ -182,7 +190,9 @@ function main() {
|
|
|
182
190
|
for (const w of warns) console.error(`WARN: ${w}`);
|
|
183
191
|
for (const e of errors) console.error(`ERROR: ${e}`);
|
|
184
192
|
if (errors.length > 0) {
|
|
185
|
-
console.error(
|
|
193
|
+
console.error(
|
|
194
|
+
`validate-analysis-doc: FAIL (${errors.length} error(s), ${warns.length} warning(s))`,
|
|
195
|
+
);
|
|
186
196
|
process.exit(1);
|
|
187
197
|
}
|
|
188
198
|
if (strict && warns.length > 0) {
|
|
@@ -49,22 +49,27 @@ function validate(obj) {
|
|
|
49
49
|
if (typeof obj !== "object" || obj === null || Array.isArray(obj)) {
|
|
50
50
|
return ["root must be an object"];
|
|
51
51
|
}
|
|
52
|
-
if (obj.schemaVersion !== "1.1.0")
|
|
52
|
+
if (obj.schemaVersion !== "1.1.0")
|
|
53
|
+
errors.push(`schemaVersion must be "1.1.0", got ${JSON.stringify(obj.schemaVersion)}`);
|
|
53
54
|
|
|
54
55
|
if (typeof obj.task !== "object" || obj.task === null) {
|
|
55
56
|
errors.push("task must be an object");
|
|
56
57
|
} else {
|
|
57
|
-
if (typeof obj.task.base !== "string" || !obj.task.base.length)
|
|
58
|
-
|
|
58
|
+
if (typeof obj.task.base !== "string" || !obj.task.base.length)
|
|
59
|
+
errors.push("task.base must be a non-empty string");
|
|
60
|
+
if (typeof obj.task.head !== "string" || !obj.task.head.length)
|
|
61
|
+
errors.push("task.head must be a non-empty string");
|
|
59
62
|
}
|
|
60
63
|
|
|
61
64
|
if (typeof obj.totals !== "object" || obj.totals === null) {
|
|
62
65
|
errors.push("totals must be an object");
|
|
63
66
|
} else {
|
|
64
67
|
for (const k of ["files", "loc_added", "loc_removed"]) {
|
|
65
|
-
if (!Number.isInteger(obj.totals[k]) || obj.totals[k] < 0)
|
|
68
|
+
if (!Number.isInteger(obj.totals[k]) || obj.totals[k] < 0)
|
|
69
|
+
errors.push(`totals.${k} must be a non-negative integer`);
|
|
66
70
|
}
|
|
67
|
-
if (typeof obj.totals.max_score !== "number" || obj.totals.max_score < 0)
|
|
71
|
+
if (typeof obj.totals.max_score !== "number" || obj.totals.max_score < 0)
|
|
72
|
+
errors.push("totals.max_score must be a non-negative number");
|
|
68
73
|
}
|
|
69
74
|
|
|
70
75
|
if (!Array.isArray(obj.files)) {
|
|
@@ -73,25 +78,33 @@ function validate(obj) {
|
|
|
73
78
|
obj.files.forEach((row, i) => {
|
|
74
79
|
const p = `files[${i}]`;
|
|
75
80
|
if (typeof row !== "object" || row === null) {
|
|
76
|
-
errors.push(`${p} must be an object`);
|
|
81
|
+
errors.push(`${p} must be an object`);
|
|
82
|
+
return;
|
|
77
83
|
}
|
|
78
|
-
if (typeof row.path !== "string" || !row.path.length)
|
|
79
|
-
|
|
84
|
+
if (typeof row.path !== "string" || !row.path.length)
|
|
85
|
+
errors.push(`${p}.path must be a non-empty string`);
|
|
86
|
+
if (typeof row.score !== "number" || row.score < 0)
|
|
87
|
+
errors.push(`${p}.score must be a non-negative number`);
|
|
80
88
|
for (const k of ["loc_added", "loc_removed"]) {
|
|
81
|
-
if (!Number.isInteger(row[k]) || row[k] < 0)
|
|
89
|
+
if (!Number.isInteger(row[k]) || row[k] < 0)
|
|
90
|
+
errors.push(`${p}.${k} must be a non-negative integer`);
|
|
82
91
|
}
|
|
83
92
|
if (!Array.isArray(row.signals)) errors.push(`${p}.signals must be an array`);
|
|
84
|
-
else
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
93
|
+
else
|
|
94
|
+
row.signals.forEach((s, j) => {
|
|
95
|
+
const sp = `${p}.signals[${j}]`;
|
|
96
|
+
if (!ALLOWED_SIGNALS.has(s.name))
|
|
97
|
+
errors.push(`${sp}.name "${s.name}" not in allowed set`);
|
|
98
|
+
if (typeof s.weight !== "number") errors.push(`${sp}.weight must be a number`);
|
|
99
|
+
if (typeof s.value !== "number") errors.push(`${sp}.value must be a number`);
|
|
100
|
+
});
|
|
90
101
|
});
|
|
91
102
|
// Sort invariant
|
|
92
103
|
for (let i = 1; i < obj.files.length; i++) {
|
|
93
104
|
if (obj.files[i].score > obj.files[i - 1].score) {
|
|
94
|
-
errors.push(
|
|
105
|
+
errors.push(
|
|
106
|
+
`files[] must be sorted by score descending (row ${i} score ${obj.files[i].score} > row ${i - 1} score ${obj.files[i - 1].score})`,
|
|
107
|
+
);
|
|
95
108
|
break;
|
|
96
109
|
}
|
|
97
110
|
}
|
|
@@ -103,8 +116,9 @@ function validate(obj) {
|
|
|
103
116
|
(async () => {
|
|
104
117
|
const raw = await readInput();
|
|
105
118
|
let obj;
|
|
106
|
-
try {
|
|
107
|
-
|
|
119
|
+
try {
|
|
120
|
+
obj = JSON.parse(raw);
|
|
121
|
+
} catch (e) {
|
|
108
122
|
console.log(JSON.stringify({ ok: false, errors: [`JSON parse error: ${e.message}`] }));
|
|
109
123
|
process.exit(1);
|
|
110
124
|
}
|
|
@@ -34,15 +34,18 @@ function readInput() {
|
|
|
34
34
|
|
|
35
35
|
function validate(obj) {
|
|
36
36
|
const errors = [];
|
|
37
|
-
if (typeof obj !== "object" || obj === null || Array.isArray(obj))
|
|
37
|
+
if (typeof obj !== "object" || obj === null || Array.isArray(obj))
|
|
38
|
+
return ["root must be an object"];
|
|
38
39
|
if (obj.schemaVersion !== "1.0.0") errors.push(`schemaVersion must be "1.0.0"`);
|
|
39
|
-
if (!ALLOWED_STACK.has(obj.stack))
|
|
40
|
+
if (!ALLOWED_STACK.has(obj.stack))
|
|
41
|
+
errors.push(`stack must be one of ${Array.from(ALLOWED_STACK).join(",")}`);
|
|
40
42
|
|
|
41
43
|
if (typeof obj.totals !== "object" || obj.totals === null) {
|
|
42
44
|
errors.push("totals must be an object");
|
|
43
45
|
} else {
|
|
44
46
|
for (const k of ["sourcesScanned", "testsScanned", "gapCount"]) {
|
|
45
|
-
if (!Number.isInteger(obj.totals[k]) || obj.totals[k] < 0)
|
|
47
|
+
if (!Number.isInteger(obj.totals[k]) || obj.totals[k] < 0)
|
|
48
|
+
errors.push(`totals.${k} must be a non-negative integer`);
|
|
46
49
|
}
|
|
47
50
|
if (typeof obj.totals.gapBySeverity !== "object" || obj.totals.gapBySeverity === null) {
|
|
48
51
|
errors.push("totals.gapBySeverity must be an object");
|
|
@@ -60,12 +63,17 @@ function validate(obj) {
|
|
|
60
63
|
} else {
|
|
61
64
|
obj.gaps.forEach((g, i) => {
|
|
62
65
|
const p = `gaps[${i}]`;
|
|
63
|
-
if (typeof g !== "object" || g === null) {
|
|
64
|
-
|
|
66
|
+
if (typeof g !== "object" || g === null) {
|
|
67
|
+
errors.push(`${p} must be object`);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
if (typeof g.sourcePath !== "string" || !g.sourcePath.length)
|
|
71
|
+
errors.push(`${p}.sourcePath required`);
|
|
65
72
|
if (typeof g.symbol !== "string" || !g.symbol.length) errors.push(`${p}.symbol required`);
|
|
66
73
|
if (typeof g.kind !== "string" || !g.kind.length) errors.push(`${p}.kind required`);
|
|
67
74
|
if (!ALLOWED_SEVERITY.has(g.severity)) errors.push(`${p}.severity invalid: ${g.severity}`);
|
|
68
|
-
if (!Array.isArray(g.expectedTestPaths) || g.expectedTestPaths.length === 0)
|
|
75
|
+
if (!Array.isArray(g.expectedTestPaths) || g.expectedTestPaths.length === 0)
|
|
76
|
+
errors.push(`${p}.expectedTestPaths must be a non-empty array`);
|
|
69
77
|
if (typeof g.hint !== "string") errors.push(`${p}.hint must be a string`);
|
|
70
78
|
});
|
|
71
79
|
}
|
|
@@ -76,7 +84,9 @@ function validate(obj) {
|
|
|
76
84
|
(async () => {
|
|
77
85
|
const raw = await readInput();
|
|
78
86
|
let obj;
|
|
79
|
-
try {
|
|
87
|
+
try {
|
|
88
|
+
obj = JSON.parse(raw);
|
|
89
|
+
} catch (e) {
|
|
80
90
|
console.log(JSON.stringify({ ok: false, errors: [`JSON parse error: ${e.message}`] }));
|
|
81
91
|
process.exit(1);
|
|
82
92
|
}
|