@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.
Files changed (271) hide show
  1. package/CHANGELOG.md +83 -0
  2. package/README.md +18 -18
  3. package/docs/FIGMA_PIPELINE.md +34 -34
  4. package/docs/adr/0001-three-model-triage.md +12 -12
  5. package/docs/adr/0002-instruction-driven-flag.md +5 -5
  6. package/docs/adr/0003-unified-shared-skills.md +5 -5
  7. package/docs/adr/0004-zero-dependency-philosophy.md +5 -5
  8. package/docs/adr/0005-lazy-phase-docs.md +2 -2
  9. package/docs/adr/0006-skills-core-external-split.md +6 -6
  10. package/docs/adr/0007-multi-tool-adapter-framework.md +19 -19
  11. package/docs/adr/0008-installer-modularization-and-secret-leak-defense.md +19 -19
  12. package/docs/adr/README.md +1 -1
  13. package/docs/best-practices.md +3 -3
  14. package/docs/features.md +28 -28
  15. package/docs/performance.md +16 -16
  16. package/docs/recovery-guide.md +39 -39
  17. package/index.js +4 -4
  18. package/install/_common.mjs +5 -11
  19. package/install/_copilot-instructions.mjs +2 -2
  20. package/install/_dev-only-files.mjs +1 -1
  21. package/install/_platform-filter.mjs +1 -1
  22. package/install/_telemetry.mjs +1 -1
  23. package/install/claude.mjs +10 -9
  24. package/install/copilot.mjs +10 -19
  25. package/install/index.mjs +7 -15
  26. package/install/templates/copilot-instructions.md +54 -54
  27. package/install.js +1 -1
  28. package/package.json +13 -10
  29. package/pipeline/commands/multi-agent/SKILL.md +1 -1
  30. package/pipeline/commands/multi-agent/analysis/SKILL.md +1 -1
  31. package/pipeline/commands/multi-agent/analysis-resolve/SKILL.md +1 -1
  32. package/pipeline/commands/multi-agent/autopilot/SKILL.md +1 -1
  33. package/pipeline/commands/multi-agent/build-optimize/SKILL.md +1 -1
  34. package/pipeline/commands/multi-agent/create-jira/SKILL.md +1 -1
  35. package/pipeline/commands/multi-agent/design-check/SKILL.md +1 -1
  36. package/pipeline/commands/multi-agent/dev/SKILL.md +1 -1
  37. package/pipeline/commands/multi-agent/dev-autopilot/SKILL.md +1 -1
  38. package/pipeline/commands/multi-agent/dev-local/SKILL.md +1 -1
  39. package/pipeline/commands/multi-agent/dev-local-autopilot/SKILL.md +1 -1
  40. package/pipeline/commands/multi-agent/diff-explain/SKILL.md +1 -1
  41. package/pipeline/commands/multi-agent/finish/SKILL.md +6 -6
  42. package/pipeline/commands/multi-agent/forget/SKILL.md +1 -1
  43. package/pipeline/commands/multi-agent/garbage-collect/SKILL.md +1 -1
  44. package/pipeline/commands/multi-agent/help/SKILL.md +3 -3
  45. package/pipeline/commands/multi-agent/issue/SKILL.md +1 -1
  46. package/pipeline/commands/multi-agent/jira/SKILL.md +1 -1
  47. package/pipeline/commands/multi-agent/kill/SKILL.md +1 -1
  48. package/pipeline/commands/multi-agent/language/SKILL.md +1 -1
  49. package/pipeline/commands/multi-agent/local/SKILL.md +1 -1
  50. package/pipeline/commands/multi-agent/local-autopilot/SKILL.md +1 -1
  51. package/pipeline/commands/multi-agent/log/SKILL.md +1 -1
  52. package/pipeline/commands/multi-agent/manual-test/SKILL.md +1 -1
  53. package/pipeline/commands/multi-agent/prune-logs/SKILL.md +1 -1
  54. package/pipeline/commands/multi-agent/purge/SKILL.md +1 -1
  55. package/pipeline/commands/multi-agent/refactor/SKILL.md +16 -8
  56. package/pipeline/commands/multi-agent/resume/SKILL.md +2 -2
  57. package/pipeline/commands/multi-agent/review/SKILL.md +1 -1
  58. package/pipeline/commands/multi-agent/review-issue/SKILL.md +1 -1
  59. package/pipeline/commands/multi-agent/review-jira/SKILL.md +1 -1
  60. package/pipeline/commands/multi-agent/routines/SKILL.md +1 -1
  61. package/pipeline/commands/multi-agent/save/SKILL.md +1 -1
  62. package/pipeline/commands/multi-agent/scan/SKILL.md +1 -1
  63. package/pipeline/commands/multi-agent/search/SKILL.md +1 -1
  64. package/pipeline/commands/multi-agent/setup/SKILL.md +2 -2
  65. package/pipeline/commands/multi-agent/stack/SKILL.md +3 -3
  66. package/pipeline/commands/multi-agent/status/SKILL.md +1 -1
  67. package/pipeline/commands/multi-agent/sync/SKILL.md +5 -5
  68. package/pipeline/commands/multi-agent/test/SKILL.md +1 -1
  69. package/pipeline/commands/multi-agent/uninstall/SKILL.md +1 -1
  70. package/pipeline/commands/multi-agent/update/SKILL.md +3 -3
  71. package/pipeline/eval/run-metrics-fixture.json +60 -13
  72. package/pipeline/lib/account-resolver.sh +1 -1
  73. package/pipeline/lib/channels-multi-repo.sh +1 -1
  74. package/pipeline/lib/context-link-extractor.sh +1 -1
  75. package/pipeline/lib/credential-store.sh +1 -1
  76. package/pipeline/lib/fetch-confluence.sh +1 -1
  77. package/pipeline/lib/fetch-crashlytics.sh +1 -1
  78. package/pipeline/lib/fetch-fortify.sh +1 -1
  79. package/pipeline/lib/fetch-graylog.sh +1 -1
  80. package/pipeline/lib/fetch-swagger.sh +1 -1
  81. package/pipeline/lib/issue-fetcher.sh +1 -1
  82. package/pipeline/lib/multi-repo-pipeline.sh +1 -1
  83. package/pipeline/lib/repo-cache.sh +1 -1
  84. package/pipeline/lib/submodule-detector.sh +1 -1
  85. package/pipeline/multi-agent-refs/component-dispatch.md +1 -1
  86. package/pipeline/multi-agent-refs/component-generation.md +121 -0
  87. package/pipeline/multi-agent-refs/cross-cli-contract.md +1 -1
  88. package/pipeline/multi-agent-refs/features/model-fallback.md +2 -2
  89. package/pipeline/multi-agent-refs/phases/phase-4-review.md +1 -1
  90. package/pipeline/preferences-template.json +5 -11
  91. package/pipeline/schemas/agent-state.schema.json +39 -9
  92. package/pipeline/schemas/analysis-output.schema.json +18 -4
  93. package/pipeline/schemas/analysis-spec.schema.json +120 -32
  94. package/pipeline/schemas/clarify-output.schema.json +15 -5
  95. package/pipeline/schemas/design-check-config.schema.json +32 -11
  96. package/pipeline/schemas/dev-critic-output.schema.json +20 -5
  97. package/pipeline/schemas/figma-project-config.schema.json +42 -10
  98. package/pipeline/schemas/learnings-ledger.schema.json +10 -2
  99. package/pipeline/schemas/migrations/figma-config-1.0.0-to-2.0.0.mjs +1 -4
  100. package/pipeline/schemas/migrations/prefs-2.0.0-to-2.1.0.mjs +24 -7
  101. package/pipeline/schemas/plan-todos.schema.json +6 -3
  102. package/pipeline/schemas/planning-output.schema.json +5 -1
  103. package/pipeline/schemas/prefs.schema.json +91 -229
  104. package/pipeline/schemas/test-gap.schema.json +5 -5
  105. package/pipeline/schemas/token-budget.json +8 -8
  106. package/pipeline/schemas/triage-corpus.schema.json +1 -1
  107. package/pipeline/scripts/aggregate-metrics.mjs +18 -6
  108. package/pipeline/scripts/build-skills-index.mjs +6 -2
  109. package/pipeline/scripts/build-stack-plugins.mjs +142 -39
  110. package/pipeline/scripts/check-derived-drift.mjs +196 -0
  111. package/pipeline/scripts/check-md-links.mjs +6 -2
  112. package/pipeline/scripts/classify-plan-safety.mjs +20 -7
  113. package/pipeline/scripts/cost-budget-check.mjs +2 -1
  114. package/pipeline/scripts/cost-table.json +1 -1
  115. package/pipeline/scripts/diff-explain.mjs +7 -3
  116. package/pipeline/scripts/diff-risk-score.mjs +13 -3
  117. package/pipeline/scripts/eval-golden-tasks-live.mjs +8 -3
  118. package/pipeline/scripts/eval-golden-tasks.mjs +21 -9
  119. package/pipeline/scripts/eval-intent.mjs +8 -4
  120. package/pipeline/scripts/eval-mine-corpus.mjs +14 -4
  121. package/pipeline/scripts/evidence-gate.mjs +7 -2
  122. package/pipeline/scripts/fixtures/install-layout.tsv +3 -3
  123. package/pipeline/scripts/gen-mode-dispatch.mjs +38 -21
  124. package/pipeline/scripts/gen-skills-index.mjs +18 -3
  125. package/pipeline/scripts/learning-curve.mjs +13 -3
  126. package/pipeline/scripts/learnings-ledger.mjs +103 -36
  127. package/pipeline/scripts/lint-mcp-refs.mjs +13 -2
  128. package/pipeline/scripts/lint-skills.mjs +20 -9
  129. package/pipeline/scripts/localize-commands.mjs +6 -1
  130. package/pipeline/scripts/match-skills.mjs +15 -4
  131. package/pipeline/scripts/migrate-prefs.mjs +33 -16
  132. package/pipeline/scripts/phase-tracker.sh +3 -1
  133. package/pipeline/scripts/repo-map.mjs +110 -64
  134. package/pipeline/scripts/review-scope.mjs +7 -1
  135. package/pipeline/scripts/routine-registry.mjs +4 -9
  136. package/pipeline/scripts/run-aggregator.mjs +11 -5
  137. package/pipeline/scripts/run-metrics.mjs +13 -8
  138. package/pipeline/scripts/run-smokes.mjs +57 -3
  139. package/pipeline/scripts/scorecard.mjs +258 -0
  140. package/pipeline/scripts/smoke-context-budget.sh +72 -0
  141. package/pipeline/scripts/smoke-model-fallback.sh +1 -1
  142. package/pipeline/scripts/smoke-no-mcp-in-dev-phases.sh +86 -7
  143. package/pipeline/scripts/smoke-own-punctuation.sh +103 -0
  144. package/pipeline/scripts/smoke-workflow-audit.sh +43 -11
  145. package/pipeline/scripts/smoke-write-state.sh +49 -5
  146. package/pipeline/scripts/test-gap-rules/android.json +11 -11
  147. package/pipeline/scripts/test-gap-rules/ios.json +16 -11
  148. package/pipeline/scripts/test-gap-rules/node.json +19 -7
  149. package/pipeline/scripts/test-gap-rules/python.json +10 -4
  150. package/pipeline/scripts/test-gap-scan.mjs +44 -12
  151. package/pipeline/scripts/test-integrity-gate.mjs +5 -1
  152. package/pipeline/scripts/token-budget-report.mjs +44 -21
  153. package/pipeline/scripts/triage-memory.mjs +126 -34
  154. package/pipeline/scripts/uninstall.mjs +74 -30
  155. package/pipeline/scripts/validate-analysis-doc.mjs +15 -5
  156. package/pipeline/scripts/validate-diff-risk.mjs +32 -18
  157. package/pipeline/scripts/validate-test-gap.mjs +17 -7
  158. package/pipeline/scripts/validate-triage.mjs +17 -5
  159. package/pipeline/scripts/write-state.mjs +32 -9
  160. package/pipeline/skills/.skills-index.json +91 -91
  161. package/pipeline/skills/shared/README.md +57 -57
  162. package/pipeline/skills/shared/core/apple-archive-compliance/SKILL.md +1 -1
  163. package/pipeline/skills/shared/core/google-play-compliance/SKILL.md +1 -1
  164. package/pipeline/skills/shared/core/multi-agent/SKILL.md +26 -279
  165. package/pipeline/skills/shared/core/multi-agent-analysis/SKILL.md +1 -1
  166. package/pipeline/skills/shared/core/multi-agent-analysis-resolve/SKILL.md +1 -1
  167. package/pipeline/skills/shared/core/multi-agent-autopilot/SKILL.md +1 -1
  168. package/pipeline/skills/shared/core/multi-agent-build-optimize/SKILL.md +1 -1
  169. package/pipeline/skills/shared/core/multi-agent-create-jira/SKILL.md +1 -1
  170. package/pipeline/skills/shared/core/multi-agent-design-check/SKILL.md +1 -1
  171. package/pipeline/skills/shared/core/multi-agent-dev/SKILL.md +1 -1
  172. package/pipeline/skills/shared/core/multi-agent-dev-autopilot/SKILL.md +1 -1
  173. package/pipeline/skills/shared/core/multi-agent-dev-local/SKILL.md +1 -1
  174. package/pipeline/skills/shared/core/multi-agent-dev-local-autopilot/SKILL.md +1 -1
  175. package/pipeline/skills/shared/core/multi-agent-diff-explain/SKILL.md +1 -1
  176. package/pipeline/skills/shared/core/multi-agent-finish/SKILL.md +1 -1
  177. package/pipeline/skills/shared/core/multi-agent-forget/SKILL.md +1 -1
  178. package/pipeline/skills/shared/core/multi-agent-garbage-collect/SKILL.md +1 -1
  179. package/pipeline/skills/shared/core/multi-agent-help/SKILL.md +1 -1
  180. package/pipeline/skills/shared/core/multi-agent-issue/SKILL.md +1 -1
  181. package/pipeline/skills/shared/core/multi-agent-jira/SKILL.md +1 -1
  182. package/pipeline/skills/shared/core/multi-agent-kill/SKILL.md +1 -1
  183. package/pipeline/skills/shared/core/multi-agent-language/SKILL.md +1 -1
  184. package/pipeline/skills/shared/core/multi-agent-local/SKILL.md +1 -1
  185. package/pipeline/skills/shared/core/multi-agent-local-autopilot/SKILL.md +1 -1
  186. package/pipeline/skills/shared/core/multi-agent-log/SKILL.md +1 -1
  187. package/pipeline/skills/shared/core/multi-agent-manual-test/SKILL.md +1 -1
  188. package/pipeline/skills/shared/core/multi-agent-prune-logs/SKILL.md +1 -1
  189. package/pipeline/skills/shared/core/multi-agent-purge/SKILL.md +1 -1
  190. package/pipeline/skills/shared/core/multi-agent-refactor/SKILL.md +16 -8
  191. package/pipeline/skills/shared/core/multi-agent-resume/SKILL.md +1 -1
  192. package/pipeline/skills/shared/core/multi-agent-review/SKILL.md +1 -1
  193. package/pipeline/skills/shared/core/multi-agent-review-issue/SKILL.md +1 -1
  194. package/pipeline/skills/shared/core/multi-agent-review-jira/SKILL.md +1 -1
  195. package/pipeline/skills/shared/core/multi-agent-routines/SKILL.md +1 -1
  196. package/pipeline/skills/shared/core/multi-agent-save/SKILL.md +1 -1
  197. package/pipeline/skills/shared/core/multi-agent-scan/SKILL.md +1 -1
  198. package/pipeline/skills/shared/core/multi-agent-search/SKILL.md +1 -1
  199. package/pipeline/skills/shared/core/multi-agent-setup/SKILL.md +1 -1
  200. package/pipeline/skills/shared/core/multi-agent-stack/SKILL.md +3 -3
  201. package/pipeline/skills/shared/core/multi-agent-status/SKILL.md +1 -1
  202. package/pipeline/skills/shared/core/multi-agent-sync/SKILL.md +1 -1
  203. package/pipeline/skills/shared/core/multi-agent-test/SKILL.md +1 -1
  204. package/pipeline/skills/shared/core/multi-agent-uninstall/SKILL.md +1 -1
  205. package/pipeline/skills/shared/core/multi-agent-update/SKILL.md +1 -1
  206. package/pipeline/skills/shared/external/accessibility-compliance-accessibility-audit/SKILL.md +1 -1
  207. package/pipeline/skills/shared/external/agent-introspection-debugging/SKILL.md +4 -4
  208. package/pipeline/skills/shared/external/agentflow/SKILL.md +1 -1
  209. package/pipeline/skills/shared/external/android-jetpack-compose-expert/SKILL.md +1 -1
  210. package/pipeline/skills/shared/external/android_ui_verification/SKILL.md +1 -1
  211. package/pipeline/skills/shared/external/api-patterns/SKILL.md +1 -1
  212. package/pipeline/skills/shared/external/api-security-best-practices/SKILL.md +1 -1
  213. package/pipeline/skills/shared/external/app-store-changelog/SKILL.md +1 -1
  214. package/pipeline/skills/shared/external/backlog/BACKLOG.md +1 -1
  215. package/pipeline/skills/shared/external/backlog/SKILL.md +12 -12
  216. package/pipeline/skills/shared/external/ci-cd-pipelines/SKILL.md +1 -1
  217. package/pipeline/skills/shared/external/context-compression/SKILL.md +1 -1
  218. package/pipeline/skills/shared/external/council/SKILL.md +3 -3
  219. package/pipeline/skills/shared/external/css-modern/SKILL.md +1 -1
  220. package/pipeline/skills/shared/external/database-patterns/SKILL.md +1 -1
  221. package/pipeline/skills/shared/external/debugging-strategies/SKILL.md +1 -1
  222. package/pipeline/skills/shared/external/docker-expert/SKILL.md +1 -1
  223. package/pipeline/skills/shared/external/fastapi-pro/SKILL.md +1 -1
  224. package/pipeline/skills/shared/external/firebase/SKILL.md +1 -1
  225. package/pipeline/skills/shared/external/github-actions-templates/SKILL.md +1 -1
  226. package/pipeline/skills/shared/external/help-skills/SKILL.md +1 -1
  227. package/pipeline/skills/shared/external/hig-components-content/SKILL.md +1 -1
  228. package/pipeline/skills/shared/external/hig-components-layout/SKILL.md +1 -1
  229. package/pipeline/skills/shared/external/hig-components-status/SKILL.md +1 -1
  230. package/pipeline/skills/shared/external/hig-components-system/SKILL.md +1 -1
  231. package/pipeline/skills/shared/external/hig-foundations/SKILL.md +1 -1
  232. package/pipeline/skills/shared/external/hig-inputs/SKILL.md +1 -1
  233. package/pipeline/skills/shared/external/hig-patterns/SKILL.md +1 -1
  234. package/pipeline/skills/shared/external/hig-platforms/SKILL.md +1 -1
  235. package/pipeline/skills/shared/external/hig-technologies/SKILL.md +1 -1
  236. package/pipeline/skills/shared/external/html-semantic/SKILL.md +1 -1
  237. package/pipeline/skills/shared/external/humanizer/SKILL.md +1 -1
  238. package/pipeline/skills/shared/external/ios-debugger-agent/SKILL.md +1 -1
  239. package/pipeline/skills/shared/external/ios-developer/SKILL.md +1 -1
  240. package/pipeline/skills/shared/external/kotlin-coroutines-expert/SKILL.md +1 -1
  241. package/pipeline/skills/shared/external/macos-menubar-tuist-app/SKILL.md +1 -1
  242. package/pipeline/skills/shared/external/macos-spm-app-packaging/SKILL.md +1 -1
  243. package/pipeline/skills/shared/external/monorepo-architect/SKILL.md +1 -1
  244. package/pipeline/skills/shared/external/nextjs-app-router/SKILL.md +1 -1
  245. package/pipeline/skills/shared/external/nodejs-backend-patterns/SKILL.md +1 -1
  246. package/pipeline/skills/shared/external/observability-engineer/SKILL.md +1 -1
  247. package/pipeline/skills/shared/external/python-patterns/SKILL.md +1 -1
  248. package/pipeline/skills/shared/external/react-best-practices/SKILL.md +1 -1
  249. package/pipeline/skills/shared/external/rest-api-design/SKILL.md +1 -1
  250. package/pipeline/skills/shared/external/search-first/SKILL.md +2 -2
  251. package/pipeline/skills/shared/external/skill-creator/SKILL.md +12 -12
  252. package/pipeline/skills/shared/external/skill-creator/audit.md +21 -21
  253. package/pipeline/skills/shared/external/skill-creator/checklist.md +3 -3
  254. package/pipeline/skills/shared/external/skill-creator/examples.md +10 -10
  255. package/pipeline/skills/shared/external/skill-creator/label-check.md +17 -17
  256. package/pipeline/skills/shared/external/skill-creator/scripts/audit-panel.js +86 -50
  257. package/pipeline/skills/shared/external/skill-creator/template.md +9 -9
  258. package/pipeline/skills/shared/external/swift-concurrency-expert/SKILL.md +1 -1
  259. package/pipeline/skills/shared/external/swiftui-performance-audit/SKILL.md +1 -1
  260. package/pipeline/skills/shared/external/swiftui-ui-patterns/SKILL.md +1 -1
  261. package/pipeline/skills/shared/external/swiftui-view-refactor/SKILL.md +1 -1
  262. package/pipeline/skills/shared/external/tailwind-css/SKILL.md +1 -1
  263. package/pipeline/skills/shared/external/testing-backend/SKILL.md +1 -1
  264. package/pipeline/skills/shared/external/typescript-patterns/SKILL.md +1 -1
  265. package/pipeline/skills/shared/external/vue-composition/SKILL.md +1 -1
  266. package/pipeline/skills/shared/external/web-accessibility/SKILL.md +1 -1
  267. package/pipeline/skills/shared/external/web-performance/SKILL.md +1 -1
  268. package/pipeline/skills/shared/external/web-testing/SKILL.md +1 -1
  269. package/pipeline/skills/shared/external/xcode-build-benchmark/schemas/build-benchmark.schema.json +9 -49
  270. package/pipeline/skills/skills-index.md +57 -57
  271. 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("--")) { flags[k] = v; i++; }
57
- else flags[k] = true;
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 { /* not a git repo, or git too old for --path-format */ }
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 { /* not a git repo */ }
97
+ } catch {
98
+ /* not a git repo */
99
+ }
94
100
  return "default";
95
101
  }
96
102
 
97
103
  function corpusPath(slug) {
98
- return join(homedir(), ".claude", "memory", "multi-agent", slug || repoSlug(), "triage-corpus.jsonl");
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 execFileSync("git", ["rev-parse", "--show-toplevel"], {
111
- encoding: "utf8",
112
- stdio: ["ignore", "pipe", "ignore"],
113
- }).trim() || null;
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 execFileSync("git", ["hash-object", "--", file], {
123
- cwd: top,
124
- encoding: "utf8",
125
- stdio: ["ignore", "pipe", "ignore"],
126
- }).trim() || null;
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","a","an","and","or","but","of","in","on","at","to","for","with","by","is","are",
134
- "be","been","being","this","that","these","those","it","its","as","not","no","do","does",
135
- "did","done","have","has","had","will","would","should","could","may","might","can","cant",
136
- "don","wont","you","your","i","we","they","them","he","she","his","her","our","their"
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).toLowerCase().matchAll(/[a-z][a-z0-9_]{2,}/g)) {
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 { rows.push(JSON.parse(line)); }
179
- catch { /* skip corrupted line */ }
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 { triage = JSON.parse(readFileSync(triagePath, "utf8")); }
202
- catch (e) { die(`cannot parse triage JSON: ${e.message}`, 1); }
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(corpus.map((r) => `${r.task_id}::${r.classification}::${r.file}::${r.issue}`));
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", triage.accepted || []],
211
- ["deferred", (triage.deferred || []).map((d) => ({ ...d.finding, _reason: d.reason }))],
212
- ["rejected", (triage.rejected || []).map((d) => ({ ...d.finding, _reason: d.reason }))],
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(JSON.stringify({ ok: true, slug, total: filtered.length, hits: ranked }) + "\n");
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(corpusPath(flags["repo-slug"] ? String(flags["repo-slug"]) : repoSlug()) + "\n");
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": ingest(); break;
317
- case "query": query(); break;
318
- case "path": pathCmd(); break;
319
- case "stats": statsCmd(); break;
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 = flags.includes("--copilot-chat") || flags.includes("--all") || !explicitlyTargeted;
89
- const forAntigravity = flags.includes("--antigravity") || flags.includes("--all") || !explicitlyTargeted;
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
- !((h.matcher === "Bash" || h.matcher === "Bash(git commit:*)") &&
280
- h.hooks?.some((sub) => sub.command?.includes("pre-commit-check.sh"))),
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) console.log(" - Claude Code (~/.claude/{commands/multi-agent, scripts, multi-agent-refs, schemas, lib} + pipeline skills + pipeline agent files)");
317
- if (forCopilot) console.log(" - Copilot CLI (~/.copilot/{scripts, schemas, lib} + pipeline skills + pipeline agent files + copilot-instructions.md section)");
318
- if (forCursor) console.log(` - Cursor (${adapterTarget}/.cursor/rules/multi-agent-* + .cursorrules)`);
319
- if (forCopilotChat) console.log(` - GitHub Copilot Chat (${adapterTarget}/.github/copilot-instructions.md block + .github/instructions/multi-agent-*)`);
320
- if (forAntigravity) console.log(` - Antigravity (${adapterTarget}/.agent/rules + .agent/workflows + AGENTS.md block + .agent/mcp_config.json)`);
321
- if (forCodex) console.log(" - OpenAI Codex CLI (~/.codex/prompts/multi-agent.md + AGENTS.md block + config.toml mcp block)");
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(" - Personal access tokens in OS credential store (Keychain / Credential Manager / libsecret)");
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(skills, (name) => name === "multi-agent" || name.startsWith("multi-agent-"));
361
- n += rmMatchingDirs(skills, (name) => name === "figma-ios" || name === "figma-android" || name === "figma-common" || name === "figma-to-component");
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(skills, (name) => name === "multi-agent" || name.startsWith("multi-agent-"));
384
- n += rmMatchingDirs(skills, (name) => name === "figma-ios" || name === "figma-android" || name === "figma-common" || name === "figma-to-component");
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(" note: .agent/mcp_config.json left untouched (may hold user servers) - remove pipeline entries manually if present");
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(" note: ~/.codex/config.toml left untouched (may hold user MCP servers) - remove pipeline entries manually if present");
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(" Kept your pipeline settings + logs. To remove those too, re-run with --all-data.");
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(" Full cleanup done. Still on disk (user-owned, remove by hand if you want a bare system):");
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(" macOS: security find-generic-password -s \"<service>\" -w");
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("front-matter ui_tests: true but no Section 15.6 UI test scenarios found (Locked 31)");
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("front-matter a11y_depth: full but no Section 16.2 screen-reader walkthrough found");
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('a line is a bare "TBD"/"Not applicable"/"N/A" body; omit the section instead (Locked 2)');
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(`business rule ${id} is defined but never referenced downstream (test/acceptance/analytics) - Locked 31 traceability`);
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(`validate-analysis-doc: FAIL (${errors.length} error(s), ${warns.length} warning(s))`);
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") errors.push(`schemaVersion must be "1.1.0", got ${JSON.stringify(obj.schemaVersion)}`);
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) errors.push("task.base must be a non-empty string");
58
- if (typeof obj.task.head !== "string" || !obj.task.head.length) errors.push("task.head must be a non-empty string");
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) errors.push(`totals.${k} must be a non-negative integer`);
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) errors.push("totals.max_score must be a non-negative number");
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`); return;
81
+ errors.push(`${p} must be an object`);
82
+ return;
77
83
  }
78
- if (typeof row.path !== "string" || !row.path.length) errors.push(`${p}.path must be a non-empty string`);
79
- if (typeof row.score !== "number" || row.score < 0) errors.push(`${p}.score must be a non-negative number`);
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) errors.push(`${p}.${k} must be a non-negative integer`);
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 row.signals.forEach((s, j) => {
85
- const sp = `${p}.signals[${j}]`;
86
- if (!ALLOWED_SIGNALS.has(s.name)) errors.push(`${sp}.name "${s.name}" not in allowed set`);
87
- if (typeof s.weight !== "number") errors.push(`${sp}.weight must be a number`);
88
- if (typeof s.value !== "number") errors.push(`${sp}.value must be a number`);
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(`files[] must be sorted by score descending (row ${i} score ${obj.files[i].score} > row ${i - 1} score ${obj.files[i - 1].score})`);
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 { obj = JSON.parse(raw); }
107
- catch (e) {
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)) return ["root must be an object"];
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)) errors.push(`stack must be one of ${Array.from(ALLOWED_STACK).join(",")}`);
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) errors.push(`totals.${k} must be a non-negative integer`);
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) { errors.push(`${p} must be object`); return; }
64
- if (typeof g.sourcePath !== "string" || !g.sourcePath.length) errors.push(`${p}.sourcePath required`);
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) errors.push(`${p}.expectedTestPaths must be a non-empty array`);
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 { obj = JSON.parse(raw); } catch (e) {
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
  }