openpitstop 1.0.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 (274) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +512 -0
  3. package/demo-repo/README.md +24 -0
  4. package/demo-repo/package-lock.json +4483 -0
  5. package/demo-repo/package.json +17 -0
  6. package/demo-repo/src/config.js +5 -0
  7. package/demo-repo/src/db.js +31 -0
  8. package/demo-repo/src/server.js +13 -0
  9. package/demo-repo/src/userController.js +22 -0
  10. package/demo-repo/src/userRepo.js +18 -0
  11. package/demo-repo/src/userService.js +59 -0
  12. package/demo-repo/test/user.test.js +12 -0
  13. package/demo-repo-fintech/README.md +27 -0
  14. package/demo-repo-fintech/package-lock.json +1023 -0
  15. package/demo-repo-fintech/package.json +14 -0
  16. package/demo-repo-fintech/pitstop-repro-ledger-order-ord-conc-1-charged-2.test.mjs +175 -0
  17. package/demo-repo-fintech/pitstop-repro-ledger-order-ord-retry-2-charged-2.test.mjs +175 -0
  18. package/demo-repo-fintech/pitstop-repro-ledger-order-ord-webhook-3-charged-2.test.mjs +175 -0
  19. package/demo-repo-fintech/src/gateway.js +26 -0
  20. package/demo-repo-fintech/src/server.js +61 -0
  21. package/demo-repo-generators/package-lock.json +1508 -0
  22. package/demo-repo-generators/package.json +17 -0
  23. package/demo-repo-generators/public/index.html +10 -0
  24. package/demo-repo-generators/scripts/build.js +17 -0
  25. package/demo-repo-generators/src/counter.js +16 -0
  26. package/demo-repo-integrity/README.md +49 -0
  27. package/demo-repo-integrity/package.json +12 -0
  28. package/demo-repo-integrity/src/money.js +19 -0
  29. package/demo-repo-integrity/test/money.test.js +17 -0
  30. package/dist/analyzers/accessibility.d.ts +18 -0
  31. package/dist/analyzers/accessibility.js +248 -0
  32. package/dist/analyzers/accessibility.js.map +1 -0
  33. package/dist/analyzers/dependencyGraph.d.ts +14 -0
  34. package/dist/analyzers/dependencyGraph.js +206 -0
  35. package/dist/analyzers/dependencyGraph.js.map +1 -0
  36. package/dist/analyzers/devex.d.ts +19 -0
  37. package/dist/analyzers/devex.js +301 -0
  38. package/dist/analyzers/devex.js.map +1 -0
  39. package/dist/analyzers/duplication.d.ts +2 -0
  40. package/dist/analyzers/duplication.js +47 -0
  41. package/dist/analyzers/duplication.js.map +1 -0
  42. package/dist/analyzers/index.d.ts +12 -0
  43. package/dist/analyzers/index.js +38 -0
  44. package/dist/analyzers/index.js.map +1 -0
  45. package/dist/analyzers/integrity/assertionLiteralTamper.d.ts +2 -0
  46. package/dist/analyzers/integrity/assertionLiteralTamper.js +164 -0
  47. package/dist/analyzers/integrity/assertionLiteralTamper.js.map +1 -0
  48. package/dist/analyzers/integrity/exceptionSwallow.d.ts +2 -0
  49. package/dist/analyzers/integrity/exceptionSwallow.js +198 -0
  50. package/dist/analyzers/integrity/exceptionSwallow.js.map +1 -0
  51. package/dist/analyzers/integrity/exitCheat.d.ts +2 -0
  52. package/dist/analyzers/integrity/exitCheat.js +31 -0
  53. package/dist/analyzers/integrity/exitCheat.js.map +1 -0
  54. package/dist/analyzers/integrity/git.d.ts +10 -0
  55. package/dist/analyzers/integrity/git.js +181 -0
  56. package/dist/analyzers/integrity/git.js.map +1 -0
  57. package/dist/analyzers/integrity/hardcodedMatch.d.ts +2 -0
  58. package/dist/analyzers/integrity/hardcodedMatch.js +93 -0
  59. package/dist/analyzers/integrity/hardcodedMatch.js.map +1 -0
  60. package/dist/analyzers/integrity/helpers.d.ts +21 -0
  61. package/dist/analyzers/integrity/helpers.js +66 -0
  62. package/dist/analyzers/integrity/helpers.js.map +1 -0
  63. package/dist/analyzers/integrity/index.d.ts +10 -0
  64. package/dist/analyzers/integrity/index.js +27 -0
  65. package/dist/analyzers/integrity/index.js.map +1 -0
  66. package/dist/analyzers/integrity/mockOverreach.d.ts +2 -0
  67. package/dist/analyzers/integrity/mockOverreach.js +72 -0
  68. package/dist/analyzers/integrity/mockOverreach.js.map +1 -0
  69. package/dist/analyzers/integrity/suppressionCreep.d.ts +2 -0
  70. package/dist/analyzers/integrity/suppressionCreep.js +27 -0
  71. package/dist/analyzers/integrity/suppressionCreep.js.map +1 -0
  72. package/dist/analyzers/integrity/testTamper.d.ts +2 -0
  73. package/dist/analyzers/integrity/testTamper.js +153 -0
  74. package/dist/analyzers/integrity/testTamper.js.map +1 -0
  75. package/dist/analyzers/integrity/types.d.ts +63 -0
  76. package/dist/analyzers/integrity/types.js +13 -0
  77. package/dist/analyzers/integrity/types.js.map +1 -0
  78. package/dist/analyzers/ledger/attacks.d.ts +17 -0
  79. package/dist/analyzers/ledger/attacks.js +135 -0
  80. package/dist/analyzers/ledger/attacks.js.map +1 -0
  81. package/dist/analyzers/ledger/discover.d.ts +15 -0
  82. package/dist/analyzers/ledger/discover.js +122 -0
  83. package/dist/analyzers/ledger/discover.js.map +1 -0
  84. package/dist/analyzers/ledger/evidence.d.ts +22 -0
  85. package/dist/analyzers/ledger/evidence.js +127 -0
  86. package/dist/analyzers/ledger/evidence.js.map +1 -0
  87. package/dist/analyzers/ledger/harness.d.ts +4 -0
  88. package/dist/analyzers/ledger/harness.js +270 -0
  89. package/dist/analyzers/ledger/harness.js.map +1 -0
  90. package/dist/analyzers/ledger/index.d.ts +14 -0
  91. package/dist/analyzers/ledger/index.js +116 -0
  92. package/dist/analyzers/ledger/index.js.map +1 -0
  93. package/dist/analyzers/ledger/types.d.ts +31 -0
  94. package/dist/analyzers/ledger/types.js +2 -0
  95. package/dist/analyzers/ledger/types.js.map +1 -0
  96. package/dist/analyzers/perf.d.ts +2 -0
  97. package/dist/analyzers/perf.js +106 -0
  98. package/dist/analyzers/perf.js.map +1 -0
  99. package/dist/analyzers/reliability.d.ts +22 -0
  100. package/dist/analyzers/reliability.js +339 -0
  101. package/dist/analyzers/reliability.js.map +1 -0
  102. package/dist/analyzers/routes.d.ts +17 -0
  103. package/dist/analyzers/routes.js +115 -0
  104. package/dist/analyzers/routes.js.map +1 -0
  105. package/dist/analyzers/security.d.ts +2 -0
  106. package/dist/analyzers/security.js +302 -0
  107. package/dist/analyzers/security.js.map +1 -0
  108. package/dist/analyzers/suiteRunner.d.ts +64 -0
  109. package/dist/analyzers/suiteRunner.js +381 -0
  110. package/dist/analyzers/suiteRunner.js.map +1 -0
  111. package/dist/analyzers/tests.d.ts +2 -0
  112. package/dist/analyzers/tests.js +215 -0
  113. package/dist/analyzers/tests.js.map +1 -0
  114. package/dist/analyzers/types.d.ts +203 -0
  115. package/dist/analyzers/types.js +2 -0
  116. package/dist/analyzers/types.js.map +1 -0
  117. package/dist/analyzers/util.d.ts +35 -0
  118. package/dist/analyzers/util.js +209 -0
  119. package/dist/analyzers/util.js.map +1 -0
  120. package/dist/ciCheckA.d.ts +2 -0
  121. package/dist/ciCheckA.js +5 -0
  122. package/dist/ciCheckA.js.map +1 -0
  123. package/dist/ciCheckB.d.ts +2 -0
  124. package/dist/ciCheckB.js +6 -0
  125. package/dist/ciCheckB.js.map +1 -0
  126. package/dist/cli.d.ts +2 -0
  127. package/dist/cli.js +65 -0
  128. package/dist/cli.js.map +1 -0
  129. package/dist/commands/budget.d.ts +33 -0
  130. package/dist/commands/budget.js +108 -0
  131. package/dist/commands/budget.js.map +1 -0
  132. package/dist/commands/ci.d.ts +14 -0
  133. package/dist/commands/ci.js +157 -0
  134. package/dist/commands/ci.js.map +1 -0
  135. package/dist/commands/demo.d.ts +2 -0
  136. package/dist/commands/demo.js +163 -0
  137. package/dist/commands/demo.js.map +1 -0
  138. package/dist/commands/digest.d.ts +8 -0
  139. package/dist/commands/digest.js +189 -0
  140. package/dist/commands/digest.js.map +1 -0
  141. package/dist/commands/doctor.d.ts +2 -0
  142. package/dist/commands/doctor.js +98 -0
  143. package/dist/commands/doctor.js.map +1 -0
  144. package/dist/commands/drive.d.ts +3 -0
  145. package/dist/commands/drive.js +170 -0
  146. package/dist/commands/drive.js.map +1 -0
  147. package/dist/commands/gate.d.ts +2 -0
  148. package/dist/commands/gate.js +136 -0
  149. package/dist/commands/gate.js.map +1 -0
  150. package/dist/commands/honesty.d.ts +2 -0
  151. package/dist/commands/honesty.js +182 -0
  152. package/dist/commands/honesty.js.map +1 -0
  153. package/dist/commands/inspect.d.ts +2 -0
  154. package/dist/commands/inspect.js +244 -0
  155. package/dist/commands/inspect.js.map +1 -0
  156. package/dist/commands/install.d.ts +2 -0
  157. package/dist/commands/install.js +81 -0
  158. package/dist/commands/install.js.map +1 -0
  159. package/dist/commands/integrity.d.ts +2 -0
  160. package/dist/commands/integrity.js +74 -0
  161. package/dist/commands/integrity.js.map +1 -0
  162. package/dist/commands/memory.d.ts +6 -0
  163. package/dist/commands/memory.js +90 -0
  164. package/dist/commands/memory.js.map +1 -0
  165. package/dist/commands/pen.d.ts +17 -0
  166. package/dist/commands/pen.js +158 -0
  167. package/dist/commands/pen.js.map +1 -0
  168. package/dist/commands/prompt.d.ts +2 -0
  169. package/dist/commands/prompt.js +37 -0
  170. package/dist/commands/prompt.js.map +1 -0
  171. package/dist/commands/readyCheck.d.ts +21 -0
  172. package/dist/commands/readyCheck.js +68 -0
  173. package/dist/commands/readyCheck.js.map +1 -0
  174. package/dist/commands/report.d.ts +2 -0
  175. package/dist/commands/report.js +48 -0
  176. package/dist/commands/report.js.map +1 -0
  177. package/dist/commands/repro.d.ts +3 -0
  178. package/dist/commands/repro.js +61 -0
  179. package/dist/commands/repro.js.map +1 -0
  180. package/dist/commands/scan.d.ts +27 -0
  181. package/dist/commands/scan.js +348 -0
  182. package/dist/commands/scan.js.map +1 -0
  183. package/dist/commands/share.d.ts +2 -0
  184. package/dist/commands/share.js +163 -0
  185. package/dist/commands/share.js.map +1 -0
  186. package/dist/commands/trends.d.ts +2 -0
  187. package/dist/commands/trends.js +103 -0
  188. package/dist/commands/trends.js.map +1 -0
  189. package/dist/commands/try.d.ts +12 -0
  190. package/dist/commands/try.js +208 -0
  191. package/dist/commands/try.js.map +1 -0
  192. package/dist/commands/verify.d.ts +44 -0
  193. package/dist/commands/verify.js +381 -0
  194. package/dist/commands/verify.js.map +1 -0
  195. package/dist/commands/watch.d.ts +10 -0
  196. package/dist/commands/watch.js +70 -0
  197. package/dist/commands/watch.js.map +1 -0
  198. package/dist/evidence.d.ts +37 -0
  199. package/dist/evidence.js +61 -0
  200. package/dist/evidence.js.map +1 -0
  201. package/dist/graph/correlate.d.ts +7 -0
  202. package/dist/graph/correlate.js +246 -0
  203. package/dist/graph/correlate.js.map +1 -0
  204. package/dist/graph/integrity.d.ts +22 -0
  205. package/dist/graph/integrity.js +39 -0
  206. package/dist/graph/integrity.js.map +1 -0
  207. package/dist/installer/targets.d.ts +23 -0
  208. package/dist/installer/targets.js +162 -0
  209. package/dist/installer/targets.js.map +1 -0
  210. package/dist/memory/store.d.ts +33 -0
  211. package/dist/memory/store.js +131 -0
  212. package/dist/memory/store.js.map +1 -0
  213. package/dist/pen/dynamic.d.ts +36 -0
  214. package/dist/pen/dynamic.js +622 -0
  215. package/dist/pen/dynamic.js.map +1 -0
  216. package/dist/pen/fix.d.ts +35 -0
  217. package/dist/pen/fix.js +239 -0
  218. package/dist/pen/fix.js.map +1 -0
  219. package/dist/pen/proof.d.ts +46 -0
  220. package/dist/pen/proof.js +112 -0
  221. package/dist/pen/proof.js.map +1 -0
  222. package/dist/pen/report.d.ts +11 -0
  223. package/dist/pen/report.js +258 -0
  224. package/dist/pen/report.js.map +1 -0
  225. package/dist/pen/static.d.ts +17 -0
  226. package/dist/pen/static.js +565 -0
  227. package/dist/pen/static.js.map +1 -0
  228. package/dist/pen/store.d.ts +11 -0
  229. package/dist/pen/store.js +36 -0
  230. package/dist/pen/store.js.map +1 -0
  231. package/dist/pen/types.d.ts +112 -0
  232. package/dist/pen/types.js +53 -0
  233. package/dist/pen/types.js.map +1 -0
  234. package/dist/report/format.d.ts +126 -0
  235. package/dist/report/format.js +706 -0
  236. package/dist/report/format.js.map +1 -0
  237. package/dist/report/score.d.ts +47 -0
  238. package/dist/report/score.js +164 -0
  239. package/dist/report/score.js.map +1 -0
  240. package/dist/repro/framework.d.ts +26 -0
  241. package/dist/repro/framework.js +224 -0
  242. package/dist/repro/framework.js.map +1 -0
  243. package/dist/repro/generate.d.ts +14 -0
  244. package/dist/repro/generate.js +669 -0
  245. package/dist/repro/generate.js.map +1 -0
  246. package/dist/repro/ids.d.ts +32 -0
  247. package/dist/repro/ids.js +175 -0
  248. package/dist/repro/ids.js.map +1 -0
  249. package/dist/repro/index.d.ts +24 -0
  250. package/dist/repro/index.js +69 -0
  251. package/dist/repro/index.js.map +1 -0
  252. package/dist/repro/pen.d.ts +26 -0
  253. package/dist/repro/pen.js +321 -0
  254. package/dist/repro/pen.js.map +1 -0
  255. package/dist/repro/run.d.ts +18 -0
  256. package/dist/repro/run.js +36 -0
  257. package/dist/repro/run.js.map +1 -0
  258. package/dist/sandbox/proxy.d.ts +41 -0
  259. package/dist/sandbox/proxy.js +231 -0
  260. package/dist/sandbox/proxy.js.map +1 -0
  261. package/dist/sandbox/startCmd.d.ts +24 -0
  262. package/dist/sandbox/startCmd.js +71 -0
  263. package/dist/sandbox/startCmd.js.map +1 -0
  264. package/dist/ui/spinner.d.ts +18 -0
  265. package/dist/ui/spinner.js +26 -0
  266. package/dist/ui/spinner.js.map +1 -0
  267. package/dist/verify/metrics.d.ts +13 -0
  268. package/dist/verify/metrics.js +72 -0
  269. package/dist/verify/metrics.js.map +1 -0
  270. package/package.json +68 -0
  271. package/scripts/cheat-demo.cjs +102 -0
  272. package/templates/ledger/preload.cjs +331 -0
  273. package/templates/pen/preload.cjs +196 -0
  274. package/templates/pitstop.prompt.md +371 -0
@@ -0,0 +1,381 @@
1
+ import { Command } from "commander";
2
+ import chalk from "chalk";
3
+ import boxen from "boxen";
4
+ import Table from "cli-table3";
5
+ import path from "node:path";
6
+ import fs from "node:fs";
7
+ import { analyzeTests } from "../analyzers/tests.js";
8
+ import { analyzePerf } from "../analyzers/perf.js";
9
+ import { analyzeSecurity } from "../analyzers/security.js";
10
+ import { analyzeDuplication } from "../analyzers/duplication.js";
11
+ import { addEntry } from "../memory/store.js";
12
+ import { newestModifiedFile } from "../analyzers/util.js";
13
+ import { classifyRisk, deltasOf, metricsOf } from "../verify/metrics.js";
14
+ import { computeScore } from "../report/score.js";
15
+ import { getDiff } from "../analyzers/integrity/git.js";
16
+ import { buildIntegrityReport } from "../graph/integrity.js";
17
+ import { seal, checkEvidence } from "../evidence.js";
18
+ /**
19
+ * Every verify run doubles as an integrity gate: it diffs the working tree
20
+ * against the last commit (HEAD) and runs the AI-agent-cheat detectors. A
21
+ * SUSPICIOUS or CONFIRMED_CHEAT verdict BLOCKS the change regardless of test
22
+ * or perf numbers.
23
+ */
24
+ function integrityGate(repo) {
25
+ const changes = getDiff(repo, "HEAD");
26
+ const report = buildIntegrityReport(repo, "HEAD", "working tree", changes);
27
+ return { verdict: report.verdict, findings: report.findings, summary: report.summary };
28
+ }
29
+ function readBaseline(repo) {
30
+ const p = path.join(repo, ".pitstop", "scan-latest.json");
31
+ if (!fs.existsSync(p))
32
+ return null;
33
+ try {
34
+ // PowerShell (and some editors) write UTF-8 JSON with a BOM; JSON.parse
35
+ // rejects it, so strip a leading U+FEFF before parsing.
36
+ const raw = fs.readFileSync(p, "utf8");
37
+ const clean = raw.charCodeAt(0) === 0xfeff ? raw.slice(1) : raw;
38
+ return JSON.parse(clean);
39
+ }
40
+ catch {
41
+ return null;
42
+ }
43
+ }
44
+ async function currentMetrics(repo) {
45
+ const [tests, perf, security, duplication] = await Promise.all([
46
+ analyzeTests(repo),
47
+ analyzePerf(repo),
48
+ analyzeSecurity(repo),
49
+ analyzeDuplication(repo),
50
+ ]);
51
+ return {
52
+ tests: {
53
+ total: tests.total,
54
+ passed: tests.passed,
55
+ failed: tests.failed,
56
+ durationMs: tests.durationMs,
57
+ coverage: tests.coverage,
58
+ },
59
+ perf: {
60
+ buildTimeMs: perf.buildTimeMs,
61
+ bundleSizeBytes: perf.bundleSizeBytes,
62
+ },
63
+ securityCount: security.issues.length,
64
+ duplicationCount: duplication.cloneCount,
65
+ };
66
+ }
67
+ /**
68
+ * Verify re-measures only the fast metrics (tests/perf/security/dup), so the
69
+ * "current" OpenPitStop Score is computed from the last scan with exactly those
70
+ * categories patched in — the other categories keep their scan snapshot.
71
+ */
72
+ function currentScoreOf(baselineResult, current, integrityPenalty) {
73
+ // Only patch categories that actually ran in the baseline scan; a category
74
+ // that printed "skipped" must stay skipped on both sides of the delta,
75
+ // otherwise the score could move for purely toolchain reasons.
76
+ const hybrid = { ...baselineResult };
77
+ if (hybrid.tests.status === "ok") {
78
+ hybrid.tests = {
79
+ status: "ok",
80
+ total: current.tests.total,
81
+ passed: current.tests.passed,
82
+ failed: current.tests.failed,
83
+ durationMs: current.tests.durationMs,
84
+ coverage: current.tests.coverage,
85
+ };
86
+ }
87
+ if (hybrid.perf.status === "ok") {
88
+ hybrid.perf = {
89
+ status: "ok",
90
+ buildTimeMs: current.perf.buildTimeMs,
91
+ bundleSizeBytes: current.perf.bundleSizeBytes,
92
+ };
93
+ }
94
+ if (hybrid.security.status === "ok") {
95
+ const synthetic = Array.from({ length: current.securityCount }, () => ({
96
+ type: "security",
97
+ severity: "high",
98
+ description: "current security findings (from verify)",
99
+ }));
100
+ hybrid.security = { status: "ok", issues: synthetic };
101
+ }
102
+ if (hybrid.duplication.status === "ok") {
103
+ hybrid.duplication = { status: "ok", cloneCount: current.duplicationCount, clones: [] };
104
+ }
105
+ return computeScore(hybrid, { integrityPenalty });
106
+ }
107
+ /**
108
+ * The shared verify pipeline (also used by `pitstop gate`): re-measure the
109
+ * fast metrics, diff against the scan baseline, run the integrity gate, check
110
+ * the baseline's evidence signature, and write a sealed verify report. Returns
111
+ * everything a renderer needs plus the exit code to propagate.
112
+ */
113
+ export async function runVerify(repo) {
114
+ const baselineResult = readBaseline(repo);
115
+ if (!baselineResult) {
116
+ return {
117
+ repo,
118
+ missingBaseline: true,
119
+ stale: false,
120
+ risk: "High",
121
+ integrity: {
122
+ verdict: "CLEAN",
123
+ findings: [],
124
+ summary: { confirmed: 0, suspicious: 0, total: 0 },
125
+ },
126
+ blocked: false,
127
+ current: {
128
+ tests: { total: 0, passed: 0, failed: 0, durationMs: 0 },
129
+ perf: {},
130
+ securityCount: 0,
131
+ duplicationCount: 0,
132
+ },
133
+ baseline: {
134
+ tests: { total: 0, passed: 0, failed: 0, durationMs: 0 },
135
+ perf: {},
136
+ securityCount: 0,
137
+ duplicationCount: 0,
138
+ },
139
+ deltas: {
140
+ passed: 0,
141
+ failed: 0,
142
+ durationMs: 0,
143
+ coverage: 0,
144
+ buildTimeMs: 0,
145
+ bundleSizeBytes: 0,
146
+ security: 0,
147
+ duplication: 0,
148
+ },
149
+ baselineScore: { score: 0, grade: "F", categories: [], analyzed: 0, total: 0 },
150
+ currentScore: { score: 0, grade: "F", categories: [], analyzed: 0, total: 0 },
151
+ scoreDelta: 0,
152
+ exitCode: 1,
153
+ };
154
+ }
155
+ // Evidence signature: recompute the digest of the stored scan and compare.
156
+ // A baseline edited after OpenPitStop wrote it breaks the chain — the score
157
+ // delta is then computed against a document OpenPitStop cannot vouch for.
158
+ const evidence = checkEvidence(baselineResult);
159
+ const baseline = metricsOf(baselineResult);
160
+ const current = await currentMetrics(repo);
161
+ const d = deltasOf(baseline, current);
162
+ // Baseline staleness: if files changed after the baseline scan, the delta
163
+ // and score are computed against a snapshot that no longer matches the
164
+ // working tree — warn loudly instead of silently presenting stale math.
165
+ let staleNote = "";
166
+ const newest = newestModifiedFile(repo);
167
+ const baselineAt = Date.parse(baselineResult.timestamp);
168
+ if (newest && baselineAt && newest.mtimeMs > baselineAt + 1000) {
169
+ const relFile = path.relative(repo, newest.file) || newest.file;
170
+ staleNote =
171
+ `baseline is STALE — ${relFile} changed ${new Date(newest.mtimeMs).toISOString()}, ` +
172
+ `after the baseline scan (${baselineResult.timestamp}). Score delta vs baseline is ` +
173
+ "approximate; re-run `pitstop scan` for a fresh baseline.";
174
+ }
175
+ const stale = staleNote !== "";
176
+ const risk = classifyRisk(baseline, current, d);
177
+ // Integrity gate — runs automatically on every verify, gating the commit.
178
+ const integrity = integrityGate(repo);
179
+ const blocked = integrity.verdict === "SUSPICIOUS" || integrity.verdict === "CONFIRMED_CHEAT";
180
+ const integrityPenalty = blocked
181
+ ? integrity.verdict === "CONFIRMED_CHEAT"
182
+ ? 25
183
+ : 10
184
+ : 0;
185
+ const baselineScore = computeScore(baselineResult);
186
+ const currentScore = currentScoreOf(baselineResult, current, integrityPenalty);
187
+ const scoreDelta = currentScore.score - baselineScore.score;
188
+ const outDir = path.join(repo, ".pitstop");
189
+ fs.mkdirSync(outDir, { recursive: true });
190
+ const ts = new Date().toISOString().replace(/[:.]/g, "-");
191
+ const file = path.join(outDir, `verify-${ts}.json`);
192
+ const report = {
193
+ timestamp: new Date().toISOString(),
194
+ repo,
195
+ baselineTimestamp: baselineResult.timestamp,
196
+ risk,
197
+ blocked,
198
+ stale,
199
+ staleNote: stale ? staleNote : undefined,
200
+ evidence,
201
+ status: blocked ? "BLOCKED" : evidence.status === "tampered" ? "SUSPICIOUS_EVIDENCE" : "OK",
202
+ exitCode: blocked
203
+ ? integrity.verdict === "CONFIRMED_CHEAT"
204
+ ? 2
205
+ : 1
206
+ : risk === "High"
207
+ ? 1
208
+ : 0,
209
+ integrity,
210
+ deltas: d,
211
+ baseline,
212
+ current,
213
+ score: {
214
+ baseline: baselineScore.score,
215
+ current: currentScore.score,
216
+ delta: scoreDelta,
217
+ grade: currentScore.grade,
218
+ },
219
+ };
220
+ fs.writeFileSync(file, JSON.stringify(seal(report, `pitstop verify result for ${repo}`), null, 2));
221
+ return {
222
+ repo,
223
+ missingBaseline: false,
224
+ baselineTimestamp: baselineResult.timestamp,
225
+ evidence,
226
+ stale,
227
+ staleNote: stale ? staleNote : undefined,
228
+ risk,
229
+ integrity,
230
+ blocked,
231
+ current,
232
+ baseline,
233
+ deltas: d,
234
+ baselineScore,
235
+ currentScore,
236
+ scoreDelta,
237
+ exitCode: blocked
238
+ ? integrity.verdict === "CONFIRMED_CHEAT"
239
+ ? 2
240
+ : 1
241
+ : risk === "High"
242
+ ? 1
243
+ : 0,
244
+ file,
245
+ };
246
+ }
247
+ function fmtBundle(b) {
248
+ return b == null ? "—" : `${(b / 1024).toFixed(1)} KB`;
249
+ }
250
+ function fmtMs(n) {
251
+ return n == null ? "—" : `${n} ms`;
252
+ }
253
+ function fmtPct(n) {
254
+ return n == null ? "—" : `${n.toFixed(1)}%`;
255
+ }
256
+ function signed(n) {
257
+ return n > 0 ? `+${n}` : `${n}`;
258
+ }
259
+ function deltaBytes(n) {
260
+ if (n === 0)
261
+ return "0";
262
+ return `${n > 0 ? "+" : ""}${(n / 1024).toFixed(1)} KB`;
263
+ }
264
+ function deltaMs(n) {
265
+ if (n === 0)
266
+ return "0";
267
+ return `${n > 0 ? "+" : ""}${n} ms`;
268
+ }
269
+ function deltaPct(n) {
270
+ if (n === 0)
271
+ return "0";
272
+ return `${n > 0 ? "+" : ""}${n.toFixed(1)}%`;
273
+ }
274
+ function evidenceLine(ev) {
275
+ if (!ev || ev.status === "missing") {
276
+ return chalk.dim(`evidence: untracked (baseline carries no OpenPitStop signature)`);
277
+ }
278
+ if (ev.status === "verified") {
279
+ return chalk.green(`evidence: ✓ signed ${ev.digest.slice(0, 12)}…`);
280
+ }
281
+ return chalk.red(`evidence: ✗ TAMPERED — ${ev.reason ?? "digest mismatch"} ` +
282
+ `(expected ${ev.expected?.slice(0, 12)}…, got ${ev.digest.slice(0, 12)}…)`);
283
+ }
284
+ function renderVerifyBox(o) {
285
+ const d = o.deltas;
286
+ const baseline = o.baseline;
287
+ const current = o.current;
288
+ const baselineScore = o.baselineScore;
289
+ const currentScore = o.currentScore;
290
+ const table = new Table({
291
+ head: ["Metric", "Baseline", "Current", "Δ"],
292
+ style: { head: ["cyan"], border: [] },
293
+ });
294
+ const row = (name, baseStr, curStr, deltaNum, deltaStr, higherIsBetter) => {
295
+ const col = deltaNum === 0
296
+ ? chalk.dim
297
+ : (higherIsBetter ? deltaNum > 0 : deltaNum < 0)
298
+ ? chalk.green
299
+ : chalk.red;
300
+ table.push([chalk.bold(name), baseStr, curStr, col(deltaStr)]);
301
+ };
302
+ row("Tests passed", String(baseline.tests.passed), String(current.tests.passed), d.passed, signed(d.passed), true);
303
+ row("Tests failed", String(baseline.tests.failed), String(current.tests.failed), d.failed, signed(d.failed), false);
304
+ row("Duration", fmtMs(baseline.tests.durationMs), fmtMs(current.tests.durationMs), d.durationMs, deltaMs(d.durationMs), false);
305
+ row("Coverage", fmtPct(baseline.tests.coverage), fmtPct(current.tests.coverage), d.coverage, deltaPct(d.coverage), true);
306
+ row("Build time", fmtMs(baseline.perf.buildTimeMs), fmtMs(current.perf.buildTimeMs), d.buildTimeMs, deltaMs(d.buildTimeMs), false);
307
+ row("Bundle size", fmtBundle(baseline.perf.bundleSizeBytes), fmtBundle(current.perf.bundleSizeBytes), d.bundleSizeBytes, deltaBytes(d.bundleSizeBytes), false);
308
+ row("Security findings", String(baseline.securityCount), String(current.securityCount), d.security, signed(d.security), false);
309
+ row("Duplication clones", String(baseline.duplicationCount), String(current.duplicationCount), d.duplication, signed(d.duplication), false);
310
+ row("OpenPitStop score", `${baselineScore.score}/100 (${baselineScore.grade})`, `${currentScore.score}/100 (${currentScore.grade})`, o.scoreDelta, o.scoreDelta > 0 ? `+${o.scoreDelta} pts` : `${o.scoreDelta} pts`, true);
311
+ const riskColor = o.risk === "High" ? chalk.red : o.risk === "Medium" ? chalk.yellow : chalk.green;
312
+ let riskLine;
313
+ if (o.blocked) {
314
+ riskLine = `${chalk.bold("Regression risk:")} ${chalk.red("BLOCKED — integrity violation")} (${o.integrity.verdict})`;
315
+ }
316
+ else {
317
+ riskLine = `${chalk.bold("Regression risk:")} ${riskColor(o.risk.toUpperCase())}`;
318
+ }
319
+ const integrityParts = [];
320
+ if (o.blocked) {
321
+ integrityParts.push(chalk.red(`${chalk.bold("Integrity gate:")} ${o.integrity.verdict} — ` +
322
+ `${o.integrity.summary.confirmed} confirmed · ${o.integrity.summary.suspicious} suspicious`));
323
+ for (const f of o.integrity.findings) {
324
+ const tag = f.confidence === "confirmed" ? chalk.red("CONFIRMED") : chalk.yellow("SUSPICIOUS");
325
+ const loc = f.line ? `${f.file}:${f.line}` : f.file;
326
+ integrityParts.push(` ${tag} ${chalk.bold(f.detector)} / ${f.pattern} @ ${chalk.cyan(loc)}`, ` ${f.evidence}`);
327
+ }
328
+ }
329
+ const contentParts = [];
330
+ if (o.stale) {
331
+ contentParts.push(chalk.yellow(`⚠ ${o.staleNote}`));
332
+ }
333
+ contentParts.push(evidenceLine(o.evidence));
334
+ if (o.evidence && o.evidence.status === "tampered") {
335
+ contentParts.push(chalk.red(`${chalk.bold("Evidence gate:")} the baseline was edited after OpenPitStop signed it — ` +
336
+ "score delta is against an untrustworthy snapshot. Re-run `pitstop scan`."));
337
+ }
338
+ if (o.stale)
339
+ contentParts.push("");
340
+ contentParts.push(...integrityParts, riskLine);
341
+ contentParts.push("", table.toString());
342
+ const content = contentParts.join("\n");
343
+ const color = o.blocked ? "red" : o.risk === "High" ? "red" : o.risk === "Medium" ? "yellow" : "green";
344
+ return {
345
+ text: content,
346
+ color,
347
+ };
348
+ }
349
+ export const verify = new Command("verify")
350
+ .description("Re-run tests/perf, diff against the last scan baseline, AND gate on the integrity diff since HEAD — " +
351
+ "SUSPICIOUS/CONFIRMED_CHEAT verifies exit 1/2 with 'BLOCKED — integrity violation'.")
352
+ .argument("[repo]", "path to the repo to verify", ".")
353
+ .action(async (repoArg) => {
354
+ const repo = path.resolve(repoArg);
355
+ console.log(chalk.cyan(`\nVerifying ${repo} ...\n`));
356
+ const outcome = await runVerify(repo);
357
+ if (outcome.missingBaseline) {
358
+ console.log(chalk.red("no baseline found — run `pitstop scan` first to create .pitstop/scan-latest.json"));
359
+ process.exitCode = 1;
360
+ return;
361
+ }
362
+ const { text, color } = renderVerifyBox(outcome);
363
+ console.log(boxen(text, {
364
+ title: " PITSTOP — Verify ",
365
+ titleAlignment: "center",
366
+ borderStyle: "double",
367
+ padding: 1,
368
+ borderColor: color,
369
+ }));
370
+ console.log(chalk.dim(`\nReport written to ${outcome.file}\n`));
371
+ const summary = outcome.blocked
372
+ ? `verify BLOCKED (integrity ${outcome.integrity.verdict})`
373
+ : `verify ${outcome.risk}: score ${outcome.currentScore.score}/100 (${outcome.currentScore.grade}) · tests ${signed(outcome.deltas.failed)} fail, bundle ${deltaBytes(outcome.deltas.bundleSizeBytes)}, security ${signed(outcome.deltas.security)}`;
374
+ addEntry(repo, {
375
+ type: "fix",
376
+ summary,
377
+ context: `verify against baseline ${outcome.baselineTimestamp} — integrity ${outcome.integrity.verdict}`,
378
+ });
379
+ process.exitCode = outcome.exitCode;
380
+ });
381
+ //# sourceMappingURL=verify.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verify.js","sourceRoot":"","sources":["../../src/commands/verify.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import { Command } from "commander";
2
+ /**
3
+ * `pitstop watch` — the live shield.
4
+ *
5
+ * Polls the working tree; the moment a file changes it runs the fast static
6
+ * pass and prints the score delta + any new security issues. A dev server's
7
+ * worth of feedback with a `try`-scan's worth of cost. Ctrl+C stops it.
8
+ */
9
+ export declare const watch: Command;
10
+ export default watch;
@@ -0,0 +1,70 @@
1
+ import { Command } from "commander";
2
+ import chalk from "chalk";
3
+ import path from "node:path";
4
+ import { performance } from "node:perf_hooks";
5
+ import { newestModifiedFile } from "../analyzers/util.js";
6
+ import { tryScan } from "./try.js";
7
+ import { persistScan } from "./scan.js";
8
+ import { stampFindings } from "../repro/ids.js";
9
+ import { correlate } from "../graph/correlate.js";
10
+ import { computeScore } from "../report/score.js";
11
+ /**
12
+ * `pitstop watch` — the live shield.
13
+ *
14
+ * Polls the working tree; the moment a file changes it runs the fast static
15
+ * pass and prints the score delta + any new security issues. A dev server's
16
+ * worth of feedback with a `try`-scan's worth of cost. Ctrl+C stops it.
17
+ */
18
+ export const watch = new Command("watch")
19
+ .description("Live shield: poll the repo and re-run the fast static pass whenever a file changes, " +
20
+ "printing the score delta and any new security issues. Ctrl+C stops.")
21
+ .argument("[repo]", "path to the repo to watch (default: current dir)", ".")
22
+ .option("--interval <ms>", "poll interval in ms (default 10000)", "10000")
23
+ .action(async (repoArg, options) => {
24
+ const repo = path.resolve(repoArg);
25
+ const interval = Math.max(2000, Math.floor(Number(options.interval) || 10000));
26
+ let lastMtime = newestModifiedFile(repo)?.mtimeMs ?? 0;
27
+ let lastScore = null;
28
+ let lastSecrets = 0;
29
+ console.log(chalk.cyan(`\nWatching ${repo} (every ${interval}ms — Ctrl+C to stop)\n`));
30
+ const tick = async () => {
31
+ const now = newestModifiedFile(repo);
32
+ if (!now || now.mtimeMs === lastMtime)
33
+ return;
34
+ lastMtime = now.mtimeMs;
35
+ const changed = path.relative(repo, now.file).replace(/\\/g, "/");
36
+ const t0 = performance.now();
37
+ let line;
38
+ try {
39
+ const result = await tryScan(repo);
40
+ stampFindings(result);
41
+ result.clusters = correlate(repo, result).clusters;
42
+ persistScan(repo, result);
43
+ const sc = computeScore(result);
44
+ const secrets = result.security.status === "ok" ? result.security.issues.length : 0;
45
+ const newSecrets = secrets > lastSecrets ? ` · ${chalk.red(`+${secrets - lastSecrets} security`)}` : "";
46
+ const delta = lastScore != null
47
+ ? sc.score - lastScore >= 0
48
+ ? chalk.green(`(+${sc.score - lastScore})`)
49
+ : chalk.red(`(${sc.score - lastScore})`)
50
+ : "";
51
+ line = `${new Date().toISOString().slice(11, 19)} score ${sc.score}/100 (${sc.grade})${delta} · security ${secrets}${newSecrets} · changed ${changed} · ${Math.round(performance.now() - t0)}ms`;
52
+ lastScore = sc.score;
53
+ lastSecrets = secrets;
54
+ }
55
+ catch (err) {
56
+ line = `${new Date().toISOString().slice(11, 19)} scan failed on ${changed}: ${err.message}`;
57
+ }
58
+ console.log(line);
59
+ };
60
+ await new Promise((resolve) => {
61
+ const timer = setInterval(() => void tick(), interval);
62
+ process.on("SIGINT", () => {
63
+ clearInterval(timer);
64
+ console.log(chalk.dim("\nwatch stopped — baseline refreshed with every change."));
65
+ resolve();
66
+ });
67
+ });
68
+ });
69
+ export default watch;
70
+ //# sourceMappingURL=watch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"watch.js","sourceRoot":"","sources":["../../src/commands/watch.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * evidence.ts — OpenPitStop's tamper-evident evidence chain.
3
+ *
4
+ * Every scan/cluster/verify document OpenPitStop writes carries a deterministic
5
+ * cryptographic digest of its own content. If anyone edits the JSON after the
6
+ * fact — a score inflated, a finding deleted — the digest stops matching and
7
+ * OpenPitStop reports the chain as broken. No key material is involved: this is
8
+ * not "who signed this" but "not a byte changed since OpenPitStop wrote it",
9
+ * which is the property that makes agent report honesty checkable.
10
+ */
11
+ export interface OpenPitStopEvidence {
12
+ scheme: "pitstop-canonical-sha256-v1";
13
+ /** sha256 hex over the canonical JSON of the document (evidence excluded). */
14
+ digest: string;
15
+ /** Human description of what the digest covers. */
16
+ of: string;
17
+ signedAt: string;
18
+ }
19
+ /** Canonical JSON: keys sorted recursively, no whitespace, floats verbatim. */
20
+ export declare function canonicalize(value: unknown): string;
21
+ export declare function digestOf(value: unknown): string;
22
+ /** Attach a signed evidence block to a document (returns the document itself). */
23
+ export declare function seal<T extends object>(doc: T, of: string): T & {
24
+ evidence: OpenPitStopEvidence;
25
+ };
26
+ export type EvidenceStatus = "verified" | "tampered" | "missing";
27
+ export interface EvidenceCheck {
28
+ status: EvidenceStatus;
29
+ digest: string;
30
+ expected?: string;
31
+ reason?: string;
32
+ }
33
+ /**
34
+ * Recompute the digest over a sealed document (excluding its own evidence
35
+ * field) and compare against the claimed signature.
36
+ */
37
+ export declare function checkEvidence(value: unknown): EvidenceCheck;
@@ -0,0 +1,61 @@
1
+ import { createHash } from "node:crypto";
2
+ /** The evidence field itself is never part of the digest it protects. */
3
+ const EXCLUDED_KEYS = new Set(["evidence"]);
4
+ /** Canonical JSON: keys sorted recursively, no whitespace, floats verbatim. */
5
+ export function canonicalize(value) {
6
+ if (value === null || typeof value !== "object")
7
+ return JSON.stringify(value);
8
+ if (Array.isArray(value))
9
+ return "[" + value.map((v) => canonicalize(v)).join(",") + "]";
10
+ const obj = value;
11
+ const parts = [];
12
+ for (const key of Object.keys(obj).sort()) {
13
+ if (EXCLUDED_KEYS.has(key))
14
+ continue;
15
+ if (obj[key] === undefined)
16
+ continue; // JSON.stringify drops these on write
17
+ parts.push(JSON.stringify(key) + ":" + canonicalize(obj[key]));
18
+ }
19
+ return "{" + parts.join(",") + "}";
20
+ }
21
+ export function digestOf(value) {
22
+ return createHash("sha256").update(canonicalize(value), "utf8").digest("hex");
23
+ }
24
+ /** Attach a signed evidence block to a document (returns the document itself). */
25
+ export function seal(doc, of) {
26
+ const evidence = {
27
+ scheme: "pitstop-canonical-sha256-v1",
28
+ digest: digestOf(doc),
29
+ of,
30
+ signedAt: new Date().toISOString(),
31
+ };
32
+ return { ...doc, evidence };
33
+ }
34
+ /**
35
+ * Recompute the digest over a sealed document (excluding its own evidence
36
+ * field) and compare against the claimed signature.
37
+ */
38
+ export function checkEvidence(value) {
39
+ const ev = value?.evidence;
40
+ const digest = digestOf(value);
41
+ if (!ev || !ev.digest) {
42
+ return {
43
+ status: "missing",
44
+ digest,
45
+ reason: "document carries no OpenPitStop evidence block — written by a non-OpenPitStop tool or pre-0.6.0",
46
+ };
47
+ }
48
+ if (ev.scheme !== "pitstop-canonical-sha256-v1") {
49
+ return { status: "tampered", digest, expected: ev.digest, reason: `unrecognized scheme "${ev.scheme}"` };
50
+ }
51
+ if (digest !== ev.digest) {
52
+ return {
53
+ status: "tampered",
54
+ digest,
55
+ expected: ev.digest,
56
+ reason: "digest does not match document content — evidence was edited after OpenPitStop wrote it",
57
+ };
58
+ }
59
+ return { status: "verified", digest };
60
+ }
61
+ //# sourceMappingURL=evidence.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"evidence.js","sourceRoot":"","sources":["../src/evidence.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAsBzC,yEAAyE;AACzE,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC;AAE5C,+EAA+E;AAC/E,MAAM,UAAU,YAAY,CAAC,KAAc;IACzC,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IAC9E,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC;IACzF,MAAM,GAAG,GAAG,KAAgC,CAAC;IAC7C,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;QAC1C,IAAI,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,SAAS;QACrC,IAAI,GAAG,CAAC,GAAG,CAAC,KAAK,SAAS;YAAE,SAAS,CAAC,sCAAsC;QAC5E,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,GAAG,GAAG,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IACjE,CAAC;IACD,OAAO,GAAG,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC;AACrC,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,KAAc;IACrC,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAChF,CAAC;AAED,kFAAkF;AAClF,MAAM,UAAU,IAAI,CAAmB,GAAM,EAAE,EAAU;IACvD,MAAM,QAAQ,GAAwB;QACpC,MAAM,EAAE,6BAA6B;QACrC,MAAM,EAAE,QAAQ,CAAC,GAAG,CAAC;QACrB,EAAE;QACF,QAAQ,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;KACnC,CAAC;IACF,OAAO,EAAE,GAAG,GAAG,EAAE,QAAQ,EAAE,CAAC;AAC9B,CAAC;AAWD;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,KAAc;IAC1C,MAAM,EAAE,GAAI,KAA4C,EAAE,QAAQ,CAAC;IACnE,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC/B,IAAI,CAAC,EAAE,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC;QACtB,OAAO;YACL,MAAM,EAAE,SAAS;YACjB,MAAM;YACN,MAAM,EAAE,iGAAiG;SAC1G,CAAC;IACJ,CAAC;IACD,IAAI,EAAE,CAAC,MAAM,KAAK,6BAA6B,EAAE,CAAC;QAChD,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,wBAAwB,EAAE,CAAC,MAAM,GAAG,EAAE,CAAC;IAC3G,CAAC;IACD,IAAI,MAAM,KAAK,EAAE,CAAC,MAAM,EAAE,CAAC;QACzB,OAAO;YACL,MAAM,EAAE,UAAU;YAClB,MAAM;YACN,QAAQ,EAAE,EAAE,CAAC,MAAM;YACnB,MAAM,EAAE,yFAAyF;SAClG,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,CAAC;AACxC,CAAC"}
@@ -0,0 +1,7 @@
1
+ import type { ScanResult, Cluster, ClusterFinding } from "../analyzers/types.js";
2
+ /** Flatten Phase-1 results into a list of file-based findings. */
3
+ export declare function collectFindings(repo: string, r: ScanResult): ClusterFinding[];
4
+ export declare function correlate(repo: string, r: ScanResult): {
5
+ clusters: Cluster[];
6
+ findings: ClusterFinding[];
7
+ };