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.
- package/LICENSE +21 -0
- package/README.md +512 -0
- package/demo-repo/README.md +24 -0
- package/demo-repo/package-lock.json +4483 -0
- package/demo-repo/package.json +17 -0
- package/demo-repo/src/config.js +5 -0
- package/demo-repo/src/db.js +31 -0
- package/demo-repo/src/server.js +13 -0
- package/demo-repo/src/userController.js +22 -0
- package/demo-repo/src/userRepo.js +18 -0
- package/demo-repo/src/userService.js +59 -0
- package/demo-repo/test/user.test.js +12 -0
- package/demo-repo-fintech/README.md +27 -0
- package/demo-repo-fintech/package-lock.json +1023 -0
- package/demo-repo-fintech/package.json +14 -0
- package/demo-repo-fintech/pitstop-repro-ledger-order-ord-conc-1-charged-2.test.mjs +175 -0
- package/demo-repo-fintech/pitstop-repro-ledger-order-ord-retry-2-charged-2.test.mjs +175 -0
- package/demo-repo-fintech/pitstop-repro-ledger-order-ord-webhook-3-charged-2.test.mjs +175 -0
- package/demo-repo-fintech/src/gateway.js +26 -0
- package/demo-repo-fintech/src/server.js +61 -0
- package/demo-repo-generators/package-lock.json +1508 -0
- package/demo-repo-generators/package.json +17 -0
- package/demo-repo-generators/public/index.html +10 -0
- package/demo-repo-generators/scripts/build.js +17 -0
- package/demo-repo-generators/src/counter.js +16 -0
- package/demo-repo-integrity/README.md +49 -0
- package/demo-repo-integrity/package.json +12 -0
- package/demo-repo-integrity/src/money.js +19 -0
- package/demo-repo-integrity/test/money.test.js +17 -0
- package/dist/analyzers/accessibility.d.ts +18 -0
- package/dist/analyzers/accessibility.js +248 -0
- package/dist/analyzers/accessibility.js.map +1 -0
- package/dist/analyzers/dependencyGraph.d.ts +14 -0
- package/dist/analyzers/dependencyGraph.js +206 -0
- package/dist/analyzers/dependencyGraph.js.map +1 -0
- package/dist/analyzers/devex.d.ts +19 -0
- package/dist/analyzers/devex.js +301 -0
- package/dist/analyzers/devex.js.map +1 -0
- package/dist/analyzers/duplication.d.ts +2 -0
- package/dist/analyzers/duplication.js +47 -0
- package/dist/analyzers/duplication.js.map +1 -0
- package/dist/analyzers/index.d.ts +12 -0
- package/dist/analyzers/index.js +38 -0
- package/dist/analyzers/index.js.map +1 -0
- package/dist/analyzers/integrity/assertionLiteralTamper.d.ts +2 -0
- package/dist/analyzers/integrity/assertionLiteralTamper.js +164 -0
- package/dist/analyzers/integrity/assertionLiteralTamper.js.map +1 -0
- package/dist/analyzers/integrity/exceptionSwallow.d.ts +2 -0
- package/dist/analyzers/integrity/exceptionSwallow.js +198 -0
- package/dist/analyzers/integrity/exceptionSwallow.js.map +1 -0
- package/dist/analyzers/integrity/exitCheat.d.ts +2 -0
- package/dist/analyzers/integrity/exitCheat.js +31 -0
- package/dist/analyzers/integrity/exitCheat.js.map +1 -0
- package/dist/analyzers/integrity/git.d.ts +10 -0
- package/dist/analyzers/integrity/git.js +181 -0
- package/dist/analyzers/integrity/git.js.map +1 -0
- package/dist/analyzers/integrity/hardcodedMatch.d.ts +2 -0
- package/dist/analyzers/integrity/hardcodedMatch.js +93 -0
- package/dist/analyzers/integrity/hardcodedMatch.js.map +1 -0
- package/dist/analyzers/integrity/helpers.d.ts +21 -0
- package/dist/analyzers/integrity/helpers.js +66 -0
- package/dist/analyzers/integrity/helpers.js.map +1 -0
- package/dist/analyzers/integrity/index.d.ts +10 -0
- package/dist/analyzers/integrity/index.js +27 -0
- package/dist/analyzers/integrity/index.js.map +1 -0
- package/dist/analyzers/integrity/mockOverreach.d.ts +2 -0
- package/dist/analyzers/integrity/mockOverreach.js +72 -0
- package/dist/analyzers/integrity/mockOverreach.js.map +1 -0
- package/dist/analyzers/integrity/suppressionCreep.d.ts +2 -0
- package/dist/analyzers/integrity/suppressionCreep.js +27 -0
- package/dist/analyzers/integrity/suppressionCreep.js.map +1 -0
- package/dist/analyzers/integrity/testTamper.d.ts +2 -0
- package/dist/analyzers/integrity/testTamper.js +153 -0
- package/dist/analyzers/integrity/testTamper.js.map +1 -0
- package/dist/analyzers/integrity/types.d.ts +63 -0
- package/dist/analyzers/integrity/types.js +13 -0
- package/dist/analyzers/integrity/types.js.map +1 -0
- package/dist/analyzers/ledger/attacks.d.ts +17 -0
- package/dist/analyzers/ledger/attacks.js +135 -0
- package/dist/analyzers/ledger/attacks.js.map +1 -0
- package/dist/analyzers/ledger/discover.d.ts +15 -0
- package/dist/analyzers/ledger/discover.js +122 -0
- package/dist/analyzers/ledger/discover.js.map +1 -0
- package/dist/analyzers/ledger/evidence.d.ts +22 -0
- package/dist/analyzers/ledger/evidence.js +127 -0
- package/dist/analyzers/ledger/evidence.js.map +1 -0
- package/dist/analyzers/ledger/harness.d.ts +4 -0
- package/dist/analyzers/ledger/harness.js +270 -0
- package/dist/analyzers/ledger/harness.js.map +1 -0
- package/dist/analyzers/ledger/index.d.ts +14 -0
- package/dist/analyzers/ledger/index.js +116 -0
- package/dist/analyzers/ledger/index.js.map +1 -0
- package/dist/analyzers/ledger/types.d.ts +31 -0
- package/dist/analyzers/ledger/types.js +2 -0
- package/dist/analyzers/ledger/types.js.map +1 -0
- package/dist/analyzers/perf.d.ts +2 -0
- package/dist/analyzers/perf.js +106 -0
- package/dist/analyzers/perf.js.map +1 -0
- package/dist/analyzers/reliability.d.ts +22 -0
- package/dist/analyzers/reliability.js +339 -0
- package/dist/analyzers/reliability.js.map +1 -0
- package/dist/analyzers/routes.d.ts +17 -0
- package/dist/analyzers/routes.js +115 -0
- package/dist/analyzers/routes.js.map +1 -0
- package/dist/analyzers/security.d.ts +2 -0
- package/dist/analyzers/security.js +302 -0
- package/dist/analyzers/security.js.map +1 -0
- package/dist/analyzers/suiteRunner.d.ts +64 -0
- package/dist/analyzers/suiteRunner.js +381 -0
- package/dist/analyzers/suiteRunner.js.map +1 -0
- package/dist/analyzers/tests.d.ts +2 -0
- package/dist/analyzers/tests.js +215 -0
- package/dist/analyzers/tests.js.map +1 -0
- package/dist/analyzers/types.d.ts +203 -0
- package/dist/analyzers/types.js +2 -0
- package/dist/analyzers/types.js.map +1 -0
- package/dist/analyzers/util.d.ts +35 -0
- package/dist/analyzers/util.js +209 -0
- package/dist/analyzers/util.js.map +1 -0
- package/dist/ciCheckA.d.ts +2 -0
- package/dist/ciCheckA.js +5 -0
- package/dist/ciCheckA.js.map +1 -0
- package/dist/ciCheckB.d.ts +2 -0
- package/dist/ciCheckB.js +6 -0
- package/dist/ciCheckB.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +65 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/budget.d.ts +33 -0
- package/dist/commands/budget.js +108 -0
- package/dist/commands/budget.js.map +1 -0
- package/dist/commands/ci.d.ts +14 -0
- package/dist/commands/ci.js +157 -0
- package/dist/commands/ci.js.map +1 -0
- package/dist/commands/demo.d.ts +2 -0
- package/dist/commands/demo.js +163 -0
- package/dist/commands/demo.js.map +1 -0
- package/dist/commands/digest.d.ts +8 -0
- package/dist/commands/digest.js +189 -0
- package/dist/commands/digest.js.map +1 -0
- package/dist/commands/doctor.d.ts +2 -0
- package/dist/commands/doctor.js +98 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/drive.d.ts +3 -0
- package/dist/commands/drive.js +170 -0
- package/dist/commands/drive.js.map +1 -0
- package/dist/commands/gate.d.ts +2 -0
- package/dist/commands/gate.js +136 -0
- package/dist/commands/gate.js.map +1 -0
- package/dist/commands/honesty.d.ts +2 -0
- package/dist/commands/honesty.js +182 -0
- package/dist/commands/honesty.js.map +1 -0
- package/dist/commands/inspect.d.ts +2 -0
- package/dist/commands/inspect.js +244 -0
- package/dist/commands/inspect.js.map +1 -0
- package/dist/commands/install.d.ts +2 -0
- package/dist/commands/install.js +81 -0
- package/dist/commands/install.js.map +1 -0
- package/dist/commands/integrity.d.ts +2 -0
- package/dist/commands/integrity.js +74 -0
- package/dist/commands/integrity.js.map +1 -0
- package/dist/commands/memory.d.ts +6 -0
- package/dist/commands/memory.js +90 -0
- package/dist/commands/memory.js.map +1 -0
- package/dist/commands/pen.d.ts +17 -0
- package/dist/commands/pen.js +158 -0
- package/dist/commands/pen.js.map +1 -0
- package/dist/commands/prompt.d.ts +2 -0
- package/dist/commands/prompt.js +37 -0
- package/dist/commands/prompt.js.map +1 -0
- package/dist/commands/readyCheck.d.ts +21 -0
- package/dist/commands/readyCheck.js +68 -0
- package/dist/commands/readyCheck.js.map +1 -0
- package/dist/commands/report.d.ts +2 -0
- package/dist/commands/report.js +48 -0
- package/dist/commands/report.js.map +1 -0
- package/dist/commands/repro.d.ts +3 -0
- package/dist/commands/repro.js +61 -0
- package/dist/commands/repro.js.map +1 -0
- package/dist/commands/scan.d.ts +27 -0
- package/dist/commands/scan.js +348 -0
- package/dist/commands/scan.js.map +1 -0
- package/dist/commands/share.d.ts +2 -0
- package/dist/commands/share.js +163 -0
- package/dist/commands/share.js.map +1 -0
- package/dist/commands/trends.d.ts +2 -0
- package/dist/commands/trends.js +103 -0
- package/dist/commands/trends.js.map +1 -0
- package/dist/commands/try.d.ts +12 -0
- package/dist/commands/try.js +208 -0
- package/dist/commands/try.js.map +1 -0
- package/dist/commands/verify.d.ts +44 -0
- package/dist/commands/verify.js +381 -0
- package/dist/commands/verify.js.map +1 -0
- package/dist/commands/watch.d.ts +10 -0
- package/dist/commands/watch.js +70 -0
- package/dist/commands/watch.js.map +1 -0
- package/dist/evidence.d.ts +37 -0
- package/dist/evidence.js +61 -0
- package/dist/evidence.js.map +1 -0
- package/dist/graph/correlate.d.ts +7 -0
- package/dist/graph/correlate.js +246 -0
- package/dist/graph/correlate.js.map +1 -0
- package/dist/graph/integrity.d.ts +22 -0
- package/dist/graph/integrity.js +39 -0
- package/dist/graph/integrity.js.map +1 -0
- package/dist/installer/targets.d.ts +23 -0
- package/dist/installer/targets.js +162 -0
- package/dist/installer/targets.js.map +1 -0
- package/dist/memory/store.d.ts +33 -0
- package/dist/memory/store.js +131 -0
- package/dist/memory/store.js.map +1 -0
- package/dist/pen/dynamic.d.ts +36 -0
- package/dist/pen/dynamic.js +622 -0
- package/dist/pen/dynamic.js.map +1 -0
- package/dist/pen/fix.d.ts +35 -0
- package/dist/pen/fix.js +239 -0
- package/dist/pen/fix.js.map +1 -0
- package/dist/pen/proof.d.ts +46 -0
- package/dist/pen/proof.js +112 -0
- package/dist/pen/proof.js.map +1 -0
- package/dist/pen/report.d.ts +11 -0
- package/dist/pen/report.js +258 -0
- package/dist/pen/report.js.map +1 -0
- package/dist/pen/static.d.ts +17 -0
- package/dist/pen/static.js +565 -0
- package/dist/pen/static.js.map +1 -0
- package/dist/pen/store.d.ts +11 -0
- package/dist/pen/store.js +36 -0
- package/dist/pen/store.js.map +1 -0
- package/dist/pen/types.d.ts +112 -0
- package/dist/pen/types.js +53 -0
- package/dist/pen/types.js.map +1 -0
- package/dist/report/format.d.ts +126 -0
- package/dist/report/format.js +706 -0
- package/dist/report/format.js.map +1 -0
- package/dist/report/score.d.ts +47 -0
- package/dist/report/score.js +164 -0
- package/dist/report/score.js.map +1 -0
- package/dist/repro/framework.d.ts +26 -0
- package/dist/repro/framework.js +224 -0
- package/dist/repro/framework.js.map +1 -0
- package/dist/repro/generate.d.ts +14 -0
- package/dist/repro/generate.js +669 -0
- package/dist/repro/generate.js.map +1 -0
- package/dist/repro/ids.d.ts +32 -0
- package/dist/repro/ids.js +175 -0
- package/dist/repro/ids.js.map +1 -0
- package/dist/repro/index.d.ts +24 -0
- package/dist/repro/index.js +69 -0
- package/dist/repro/index.js.map +1 -0
- package/dist/repro/pen.d.ts +26 -0
- package/dist/repro/pen.js +321 -0
- package/dist/repro/pen.js.map +1 -0
- package/dist/repro/run.d.ts +18 -0
- package/dist/repro/run.js +36 -0
- package/dist/repro/run.js.map +1 -0
- package/dist/sandbox/proxy.d.ts +41 -0
- package/dist/sandbox/proxy.js +231 -0
- package/dist/sandbox/proxy.js.map +1 -0
- package/dist/sandbox/startCmd.d.ts +24 -0
- package/dist/sandbox/startCmd.js +71 -0
- package/dist/sandbox/startCmd.js.map +1 -0
- package/dist/ui/spinner.d.ts +18 -0
- package/dist/ui/spinner.js +26 -0
- package/dist/ui/spinner.js.map +1 -0
- package/dist/verify/metrics.d.ts +13 -0
- package/dist/verify/metrics.js +72 -0
- package/dist/verify/metrics.js.map +1 -0
- package/package.json +68 -0
- package/scripts/cheat-demo.cjs +102 -0
- package/templates/ledger/preload.cjs +331 -0
- package/templates/pen/preload.cjs +196 -0
- package/templates/pitstop.prompt.md +371 -0
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { buildEdges } from "../analyzers/dependencyGraph.js";
|
|
3
|
+
import { findingIdFor, ledgerFindingId } from "../repro/ids.js";
|
|
4
|
+
/**
|
|
5
|
+
* HEURISTIC (not magic):
|
|
6
|
+
* We treat every Phase-1 output that points at one or more files as a flat
|
|
7
|
+
* "finding". Findings are correlated when they touch the same file, or files
|
|
8
|
+
* that are within 1-2 hops of each other in the dependency graph (so e.g. a
|
|
9
|
+
* buggy leaf and its only caller group together). Within a correlated group we
|
|
10
|
+
* guess the root cause as the highest-scoring finding, where score combines
|
|
11
|
+
* severity with graph centrality: a file many other files depend on is more
|
|
12
|
+
* likely to be the root cause than a leaf file. This is a best-effort guess and
|
|
13
|
+
* is clearly labeled as such in the popup.
|
|
14
|
+
*/
|
|
15
|
+
const SEVERITY_RANK = {
|
|
16
|
+
critical: 5,
|
|
17
|
+
high: 4,
|
|
18
|
+
severe: 4,
|
|
19
|
+
error: 4,
|
|
20
|
+
medium: 3,
|
|
21
|
+
moderate: 3,
|
|
22
|
+
warning: 2,
|
|
23
|
+
low: 2,
|
|
24
|
+
info: 1,
|
|
25
|
+
unknown: 2,
|
|
26
|
+
};
|
|
27
|
+
function severityRank(s) {
|
|
28
|
+
return SEVERITY_RANK[s?.toLowerCase()] ?? 2;
|
|
29
|
+
}
|
|
30
|
+
/** Flatten Phase-1 results into a list of file-based findings. */
|
|
31
|
+
export function collectFindings(repo, r) {
|
|
32
|
+
const findings = [];
|
|
33
|
+
for (const i of r.security.issues) {
|
|
34
|
+
findings.push({
|
|
35
|
+
id: findingIdFor("security", i.type, i.file, i.description),
|
|
36
|
+
source: "security",
|
|
37
|
+
severity: i.severity,
|
|
38
|
+
type: i.type,
|
|
39
|
+
description: i.description,
|
|
40
|
+
files: i.file ? [path.relative(repo, i.file)] : [],
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
for (const c of r.duplication.clones) {
|
|
44
|
+
findings.push({
|
|
45
|
+
id: findingIdFor("duplication", "duplication", c.files[0], `${c.lines}`),
|
|
46
|
+
source: "duplication",
|
|
47
|
+
severity: "low",
|
|
48
|
+
type: "duplication",
|
|
49
|
+
description: `duplicate code (${c.lines} lines)`,
|
|
50
|
+
files: c.files.map((f) => path.relative(repo, f)),
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
for (const cyc of r.dependencyGraph.circular) {
|
|
54
|
+
const files = cyc.map((f) => path.relative(repo, f));
|
|
55
|
+
findings.push({
|
|
56
|
+
id: findingIdFor("graph", "circular", cyc[0], files.join(" → ")),
|
|
57
|
+
source: "graph",
|
|
58
|
+
severity: "medium",
|
|
59
|
+
type: "circular",
|
|
60
|
+
description: `circular dependency: ${files.join(" → ")}`,
|
|
61
|
+
files,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
for (const i of r.accessibility.issues) {
|
|
65
|
+
findings.push({
|
|
66
|
+
id: findingIdFor("a11y", i.type, i.file, i.description),
|
|
67
|
+
source: "a11y",
|
|
68
|
+
severity: i.severity,
|
|
69
|
+
type: "a11y",
|
|
70
|
+
description: i.description,
|
|
71
|
+
files: i.file ? [path.relative(repo, i.file)] : [],
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
for (const f of r.reliability.flakyTests) {
|
|
75
|
+
findings.push({
|
|
76
|
+
id: findingIdFor("reliability", "flaky-test", f.file, f.name),
|
|
77
|
+
source: "reliability",
|
|
78
|
+
severity: "warning",
|
|
79
|
+
type: "flaky-test",
|
|
80
|
+
description: `flaky test (outcome changed across ${r.reliability.runs} runs): ${f.name}`,
|
|
81
|
+
files: f.file ? [path.relative(repo, f.file)] : [],
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
for (const i of r.reliability.raceSmells) {
|
|
85
|
+
findings.push({
|
|
86
|
+
id: findingIdFor("reliability", "race-condition", i.file, i.description),
|
|
87
|
+
source: "reliability",
|
|
88
|
+
severity: i.severity,
|
|
89
|
+
type: "race-condition",
|
|
90
|
+
description: i.description,
|
|
91
|
+
files: i.file ? [path.relative(repo, i.file)] : [],
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
for (const i of r.devex.unusedExports) {
|
|
95
|
+
findings.push({
|
|
96
|
+
id: findingIdFor("devex", "unused-export", i.file, i.description),
|
|
97
|
+
source: "devex",
|
|
98
|
+
severity: i.severity,
|
|
99
|
+
type: "unused-export",
|
|
100
|
+
description: i.description,
|
|
101
|
+
files: i.file ? [path.relative(repo, i.file)] : [],
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
for (const d of r.devex.duplicateFunctions) {
|
|
105
|
+
findings.push({
|
|
106
|
+
id: findingIdFor("devex", "duplicate-function", d.files[0]?.file, `${d.name}@${d.files[0]?.file}`),
|
|
107
|
+
source: "devex",
|
|
108
|
+
severity: "medium",
|
|
109
|
+
type: "duplicate-function",
|
|
110
|
+
description: `near-identical function bodies (${(d.similarity * 100).toFixed(0)}% similar, ${d.lines} lines): ${d.name} in ${d.files
|
|
111
|
+
.map((f) => path.basename(f.file))
|
|
112
|
+
.join(" vs ")}`,
|
|
113
|
+
files: d.files.map((f) => path.relative(repo, f.file)),
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
return findings;
|
|
117
|
+
}
|
|
118
|
+
export function correlate(repo, r) {
|
|
119
|
+
const findings = collectFindings(repo, r);
|
|
120
|
+
const built = buildEdges(repo);
|
|
121
|
+
const edges = built?.edges ?? new Map();
|
|
122
|
+
// Centrality = in-degree in the dependency graph.
|
|
123
|
+
const inDegree = new Map();
|
|
124
|
+
for (const deps of edges.values()) {
|
|
125
|
+
for (const d of deps)
|
|
126
|
+
inDegree.set(d, (inDegree.get(d) ?? 0) + 1);
|
|
127
|
+
}
|
|
128
|
+
// Undirected adjacency for hop-distance checks.
|
|
129
|
+
const adj = new Map();
|
|
130
|
+
const link = (a, b) => {
|
|
131
|
+
if (!adj.has(a))
|
|
132
|
+
adj.set(a, new Set());
|
|
133
|
+
adj.get(a).add(b);
|
|
134
|
+
};
|
|
135
|
+
for (const [u, deps] of edges) {
|
|
136
|
+
link(u, u);
|
|
137
|
+
for (const v of deps) {
|
|
138
|
+
link(u, v);
|
|
139
|
+
link(v, u);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
const resolve = (f) => path.resolve(repo, f);
|
|
143
|
+
const distance = (a, b) => {
|
|
144
|
+
if (!adj.has(a) || !adj.has(b))
|
|
145
|
+
return Infinity;
|
|
146
|
+
if (a === b)
|
|
147
|
+
return 0;
|
|
148
|
+
const q = [[a, 0]];
|
|
149
|
+
const seen = new Set([a]);
|
|
150
|
+
while (q.length) {
|
|
151
|
+
const [n, d] = q.shift();
|
|
152
|
+
for (const nb of adj.get(n) ?? []) {
|
|
153
|
+
if (nb === b)
|
|
154
|
+
return d + 1;
|
|
155
|
+
if (!seen.has(nb)) {
|
|
156
|
+
seen.add(nb);
|
|
157
|
+
q.push([nb, d + 1]);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return Infinity;
|
|
162
|
+
};
|
|
163
|
+
const centrality = (f) => Math.max(0, ...f.files.map((fl) => inDegree.get(resolve(fl)) ?? 0));
|
|
164
|
+
const score = (f) => severityRank(f.severity) * 10 + centrality(f);
|
|
165
|
+
const correlated = (a, b) => {
|
|
166
|
+
if (a.files.some((f) => b.files.includes(f)))
|
|
167
|
+
return true;
|
|
168
|
+
for (const fa of a.files) {
|
|
169
|
+
for (const fb of b.files) {
|
|
170
|
+
if (distance(resolve(fa), resolve(fb)) <= 2)
|
|
171
|
+
return true;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return false;
|
|
175
|
+
};
|
|
176
|
+
// Union-find clustering over the findings.
|
|
177
|
+
const parent = findings.map((_, i) => i);
|
|
178
|
+
const find = (x) => {
|
|
179
|
+
while (parent[x] !== x) {
|
|
180
|
+
parent[x] = parent[parent[x]];
|
|
181
|
+
x = parent[x];
|
|
182
|
+
}
|
|
183
|
+
return x;
|
|
184
|
+
};
|
|
185
|
+
const union = (a, b) => {
|
|
186
|
+
parent[find(a)] = find(b);
|
|
187
|
+
};
|
|
188
|
+
for (let i = 0; i < findings.length; i++) {
|
|
189
|
+
for (let j = i + 1; j < findings.length; j++) {
|
|
190
|
+
if (correlated(findings[i], findings[j]))
|
|
191
|
+
union(i, j);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
const groups = new Map();
|
|
195
|
+
for (let i = 0; i < findings.length; i++) {
|
|
196
|
+
const root = find(i);
|
|
197
|
+
if (!groups.has(root))
|
|
198
|
+
groups.set(root, []);
|
|
199
|
+
groups.get(root).push(i);
|
|
200
|
+
}
|
|
201
|
+
const clusters = [];
|
|
202
|
+
for (const idxs of groups.values()) {
|
|
203
|
+
if (idxs.length < 2)
|
|
204
|
+
continue;
|
|
205
|
+
const group = idxs.map((i) => findings[i]);
|
|
206
|
+
// Root cause = highest score (severity + centrality). Ties broken by order.
|
|
207
|
+
let root = group[0];
|
|
208
|
+
for (const f of group)
|
|
209
|
+
if (score(f) > score(root))
|
|
210
|
+
root = f;
|
|
211
|
+
const symptoms = group.filter((f) => f !== root);
|
|
212
|
+
const sharedFiles = [...new Set(group.flatMap((f) => f.files))];
|
|
213
|
+
clusters.push({ rootCause: root, symptoms, sharedFiles, size: group.length });
|
|
214
|
+
}
|
|
215
|
+
clusters.sort((a, b) => b.size - a.size);
|
|
216
|
+
// A PROVEN double-charge from ledger mode is always the top-ranked cluster.
|
|
217
|
+
// Ledger findings are never merged into the heuristic clusters: they are
|
|
218
|
+
// observed facts (the mocked gateway was actually hit twice), not guesses, so
|
|
219
|
+
// they lead the report unconditionally.
|
|
220
|
+
const ledgerClusters = buildLedgerClusters(repo, r);
|
|
221
|
+
if (ledgerClusters.length > 0)
|
|
222
|
+
clusters.unshift(...ledgerClusters);
|
|
223
|
+
return { clusters, findings };
|
|
224
|
+
}
|
|
225
|
+
function buildLedgerClusters(repo, r) {
|
|
226
|
+
if (!r.ledger || r.ledger.evidence.length === 0)
|
|
227
|
+
return [];
|
|
228
|
+
return r.ledger.evidence.map((ev) => {
|
|
229
|
+
const files = ev.endpointFile ? [ev.endpointFile] : [];
|
|
230
|
+
const rootCause = {
|
|
231
|
+
id: ledgerFindingId(ev),
|
|
232
|
+
source: "ledger",
|
|
233
|
+
severity: "critical",
|
|
234
|
+
type: "proven-double-charge",
|
|
235
|
+
description: ev.summary,
|
|
236
|
+
files,
|
|
237
|
+
};
|
|
238
|
+
return {
|
|
239
|
+
rootCause,
|
|
240
|
+
symptoms: [],
|
|
241
|
+
sharedFiles: files,
|
|
242
|
+
size: 1,
|
|
243
|
+
};
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
//# sourceMappingURL=correlate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"correlate.js","sourceRoot":"","sources":["../../src/graph/correlate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { FileChange, IntegrityFinding, IntegrityReport, Verdict } from "../analyzers/integrity/types.js";
|
|
2
|
+
import { runDetectors } from "../analyzers/integrity/index.js";
|
|
3
|
+
/**
|
|
4
|
+
* Combine all detector output into a single integrity report with an overall
|
|
5
|
+
* verdict.
|
|
6
|
+
*
|
|
7
|
+
* CALIBRATION (the contract that keeps this feature trustworthy):
|
|
8
|
+
* - CLEAN : no detector fired.
|
|
9
|
+
* - SUSPICIOUS : only "suspicious" findings — human judgment required,
|
|
10
|
+
* NEVER auto-blocked.
|
|
11
|
+
* - CONFIRMED_CHEAT : at least one "confirmed" finding — auto-blocked, because
|
|
12
|
+
* the pattern is unambiguous (whole test file deleted, a
|
|
13
|
+
* whole test-case block removed, or process.exit(0) added
|
|
14
|
+
* inside application code).
|
|
15
|
+
*
|
|
16
|
+
* Everything that is merely suggestive stays "suspicious" so a legitimate
|
|
17
|
+
* refactor that updates a test (because the spec changed) surfaces with full
|
|
18
|
+
* evidence for a human to judge — it is never silently reverted.
|
|
19
|
+
*/
|
|
20
|
+
export declare function buildIntegrityReport(repo: string, from: string, to: string, changes: FileChange[]): IntegrityReport;
|
|
21
|
+
export { runDetectors };
|
|
22
|
+
export type { IntegrityReport, Verdict, IntegrityFinding };
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { runDetectors } from "../analyzers/integrity/index.js";
|
|
2
|
+
/**
|
|
3
|
+
* Combine all detector output into a single integrity report with an overall
|
|
4
|
+
* verdict.
|
|
5
|
+
*
|
|
6
|
+
* CALIBRATION (the contract that keeps this feature trustworthy):
|
|
7
|
+
* - CLEAN : no detector fired.
|
|
8
|
+
* - SUSPICIOUS : only "suspicious" findings — human judgment required,
|
|
9
|
+
* NEVER auto-blocked.
|
|
10
|
+
* - CONFIRMED_CHEAT : at least one "confirmed" finding — auto-blocked, because
|
|
11
|
+
* the pattern is unambiguous (whole test file deleted, a
|
|
12
|
+
* whole test-case block removed, or process.exit(0) added
|
|
13
|
+
* inside application code).
|
|
14
|
+
*
|
|
15
|
+
* Everything that is merely suggestive stays "suspicious" so a legitimate
|
|
16
|
+
* refactor that updates a test (because the spec changed) surfaces with full
|
|
17
|
+
* evidence for a human to judge — it is never silently reverted.
|
|
18
|
+
*/
|
|
19
|
+
export function buildIntegrityReport(repo, from, to, changes) {
|
|
20
|
+
const findings = runDetectors(changes);
|
|
21
|
+
const confirmed = findings.filter((f) => f.confidence === "confirmed").length;
|
|
22
|
+
const suspicious = findings.filter((f) => f.confidence === "suspicious").length;
|
|
23
|
+
let verdict = "CLEAN";
|
|
24
|
+
if (confirmed > 0)
|
|
25
|
+
verdict = "CONFIRMED_CHEAT";
|
|
26
|
+
else if (suspicious > 0)
|
|
27
|
+
verdict = "SUSPICIOUS";
|
|
28
|
+
return {
|
|
29
|
+
timestamp: new Date().toISOString(),
|
|
30
|
+
repo,
|
|
31
|
+
from,
|
|
32
|
+
to,
|
|
33
|
+
verdict,
|
|
34
|
+
findings,
|
|
35
|
+
summary: { confirmed, suspicious, total: findings.length },
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
export { runDetectors };
|
|
39
|
+
//# sourceMappingURL=integrity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"integrity.js","sourceRoot":"","sources":["../../src/graph/integrity.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;AAE/D;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,oBAAoB,CAClC,IAAY,EACZ,IAAY,EACZ,EAAU,EACV,OAAqB;IAErB,MAAM,QAAQ,GAAuB,YAAY,CAAC,OAAO,CAAC,CAAC;IAE3D,MAAM,SAAS,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,KAAK,WAAW,CAAC,CAAC,MAAM,CAAC;IAC9E,MAAM,UAAU,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,KAAK,YAAY,CAAC,CAAC,MAAM,CAAC;IAEhF,IAAI,OAAO,GAAY,OAAO,CAAC;IAC/B,IAAI,SAAS,GAAG,CAAC;QAAE,OAAO,GAAG,iBAAiB,CAAC;SAC1C,IAAI,UAAU,GAAG,CAAC;QAAE,OAAO,GAAG,YAAY,CAAC;IAEhD,OAAO;QACL,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACnC,IAAI;QACJ,IAAI;QACJ,EAAE;QACF,OAAO;QACP,QAAQ;QACR,OAAO,EAAE,EAAE,SAAS,EAAE,UAAU,EAAE,KAAK,EAAE,QAAQ,CAAC,MAAM,EAAE;KAC3D,CAAC;AACJ,CAAC;AAED,OAAO,EAAE,YAAY,EAAE,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export interface InstallTarget {
|
|
2
|
+
path: string;
|
|
3
|
+
tool: string;
|
|
4
|
+
level: "project" | "home";
|
|
5
|
+
transform?: "skill" | "workflow" | "gemini";
|
|
6
|
+
note?: string;
|
|
7
|
+
}
|
|
8
|
+
/** Absolute project-level targets, relative to the given repo/cwd. */
|
|
9
|
+
export declare function projectTargets(cwd: string): InstallTarget[];
|
|
10
|
+
/** Absolute user-level (home) targets. */
|
|
11
|
+
export declare function homeTargets(): InstallTarget[];
|
|
12
|
+
export declare function getTargets(cwd: string): InstallTarget[];
|
|
13
|
+
/** Locate templates/pitstop.prompt.md relative to this module or cwd. */
|
|
14
|
+
export declare function resolveTemplatePath(): string;
|
|
15
|
+
/**
|
|
16
|
+
* Render the file content for a target. The Claude Code skill variant rewrites
|
|
17
|
+
* the frontmatter to { name, description } as skills require. The Antigravity
|
|
18
|
+
* workflow variant normalizes the frontmatter to `description` only (their
|
|
19
|
+
* required field) and keeps the title + numbered steps body as-is. The Gemini
|
|
20
|
+
* CLI variant converts to TOML ({ description = "...", prompt = """ body """ }),
|
|
21
|
+
* the format .gemini/commands/*.toml files must use.
|
|
22
|
+
*/
|
|
23
|
+
export declare function renderContent(target: InstallTarget, templateText: string): string;
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import os from "node:os";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
/** Absolute project-level targets, relative to the given repo/cwd. */
|
|
6
|
+
export function projectTargets(cwd) {
|
|
7
|
+
const p = (...parts) => path.resolve(cwd, ...parts);
|
|
8
|
+
return [
|
|
9
|
+
{ path: p(".claude", "commands", "pitstop.md"), tool: "Claude Code (project)", level: "project" },
|
|
10
|
+
{
|
|
11
|
+
path: p(".claude", "skills", "pitstop", "SKILL.md"),
|
|
12
|
+
tool: "Claude Code Skill",
|
|
13
|
+
level: "project",
|
|
14
|
+
transform: "skill",
|
|
15
|
+
},
|
|
16
|
+
{ path: p(".cursor", "commands", "pitstop.md"), tool: "Cursor (project)", level: "project" },
|
|
17
|
+
{ path: p(".opencode", "commands", "pitstop.md"), tool: "OpenCode (project)", level: "project" },
|
|
18
|
+
{
|
|
19
|
+
path: p(".opencode", "command", "pitstop.md"),
|
|
20
|
+
tool: "OpenCode (project, legacy)",
|
|
21
|
+
level: "project",
|
|
22
|
+
},
|
|
23
|
+
// Antigravity workflows: markdown files in .agent/workflows/ (singular,
|
|
24
|
+
// current docs) with YAML frontmatter `description` + title + steps,
|
|
25
|
+
// invoked as /pitstop. Older versions/3rd-party guides use the plural
|
|
26
|
+
// .agents/workflows/ — write both, one is harmless dead weight.
|
|
27
|
+
{
|
|
28
|
+
path: p(".agent", "workflows", "pitstop.md"),
|
|
29
|
+
tool: "Antigravity (project)",
|
|
30
|
+
level: "project",
|
|
31
|
+
transform: "workflow",
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
path: p(".agents", "workflows", "pitstop.md"),
|
|
35
|
+
tool: "Antigravity (project, plural legacy)",
|
|
36
|
+
level: "project",
|
|
37
|
+
transform: "workflow",
|
|
38
|
+
},
|
|
39
|
+
// Kilo Code current docs: slash commands live in .kilo/commands/ (project)
|
|
40
|
+
// and ~/.config/kilo/commands/ (global). .kilocode/workflows/ is the LEGACY
|
|
41
|
+
// location the new extension auto-migrates on startup — kept for old builds.
|
|
42
|
+
{ path: p(".kilo", "commands", "pitstop.md"), tool: "Kilo Code (project)", level: "project" },
|
|
43
|
+
{
|
|
44
|
+
path: p(".kilocode", "workflows", "pitstop.md"),
|
|
45
|
+
tool: "Kilo Code (project, legacy — auto-migrated)",
|
|
46
|
+
level: "project",
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
path: p(".gemini", "commands", "pitstop.toml"),
|
|
50
|
+
tool: "Gemini CLI (project)",
|
|
51
|
+
level: "project",
|
|
52
|
+
transform: "gemini",
|
|
53
|
+
},
|
|
54
|
+
];
|
|
55
|
+
}
|
|
56
|
+
/** Absolute user-level (home) targets. */
|
|
57
|
+
export function homeTargets() {
|
|
58
|
+
const h = (...parts) => path.join(os.homedir(), ...parts);
|
|
59
|
+
return [
|
|
60
|
+
{
|
|
61
|
+
path: h(".codex", "prompts", "pitstop.md"),
|
|
62
|
+
tool: "Codex CLI (user)",
|
|
63
|
+
level: "home",
|
|
64
|
+
note: "Codex supports prompts only at user level",
|
|
65
|
+
},
|
|
66
|
+
{ path: h(".claude", "commands", "pitstop.md"), tool: "Claude Code (user)", level: "home" },
|
|
67
|
+
{ path: h(".cursor", "commands", "pitstop.md"), tool: "Cursor (user)", level: "home" },
|
|
68
|
+
// OpenCode current docs: global commands in ~/.config/opencode/commands/;
|
|
69
|
+
// ~/.opencode/commands/ is the older location — keep both.
|
|
70
|
+
{
|
|
71
|
+
path: h(".config", "opencode", "commands", "pitstop.md"),
|
|
72
|
+
tool: "OpenCode (user)",
|
|
73
|
+
level: "home",
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
path: h(".opencode", "commands", "pitstop.md"),
|
|
77
|
+
tool: "OpenCode (user, legacy)",
|
|
78
|
+
level: "home",
|
|
79
|
+
},
|
|
80
|
+
// Antigravity global workflows (home) mirror the project layout.
|
|
81
|
+
{
|
|
82
|
+
path: h(".agent", "workflows", "pitstop.md"),
|
|
83
|
+
tool: "Antigravity (user)",
|
|
84
|
+
level: "home",
|
|
85
|
+
transform: "workflow",
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
path: h(".agents", "workflows", "pitstop.md"),
|
|
89
|
+
tool: "Antigravity (user, plural legacy)",
|
|
90
|
+
level: "home",
|
|
91
|
+
transform: "workflow",
|
|
92
|
+
},
|
|
93
|
+
{ path: h(".config", "kilo", "commands", "pitstop.md"), tool: "Kilo Code (user)", level: "home" },
|
|
94
|
+
{
|
|
95
|
+
path: h(".kilocode", "workflows", "pitstop.md"),
|
|
96
|
+
tool: "Kilo Code (user, legacy — auto-migrated)",
|
|
97
|
+
level: "home",
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
path: h(".gemini", "commands", "pitstop.toml"),
|
|
101
|
+
tool: "Gemini CLI (user)",
|
|
102
|
+
level: "home",
|
|
103
|
+
transform: "gemini",
|
|
104
|
+
},
|
|
105
|
+
];
|
|
106
|
+
}
|
|
107
|
+
export function getTargets(cwd) {
|
|
108
|
+
return [...projectTargets(cwd), ...homeTargets()];
|
|
109
|
+
}
|
|
110
|
+
/** Locate templates/pitstop.prompt.md relative to this module or cwd. */
|
|
111
|
+
export function resolveTemplatePath() {
|
|
112
|
+
const fromModule = fileURLToPath(new URL("../../templates/pitstop.prompt.md", import.meta.url));
|
|
113
|
+
if (fs.existsSync(fromModule))
|
|
114
|
+
return fromModule;
|
|
115
|
+
const fromCwd = path.resolve(process.cwd(), "templates", "pitstop.prompt.md");
|
|
116
|
+
if (fs.existsSync(fromCwd))
|
|
117
|
+
return fromCwd;
|
|
118
|
+
throw new Error("could not locate templates/pitstop.prompt.md");
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Render the file content for a target. The Claude Code skill variant rewrites
|
|
122
|
+
* the frontmatter to { name, description } as skills require. The Antigravity
|
|
123
|
+
* workflow variant normalizes the frontmatter to `description` only (their
|
|
124
|
+
* required field) and keeps the title + numbered steps body as-is. The Gemini
|
|
125
|
+
* CLI variant converts to TOML ({ description = "...", prompt = """ body """ }),
|
|
126
|
+
* the format .gemini/commands/*.toml files must use.
|
|
127
|
+
*/
|
|
128
|
+
export function renderContent(target, templateText) {
|
|
129
|
+
if (target.transform === "skill") {
|
|
130
|
+
const m = templateText.match(/^---\s*\n([\s\S]*?)\n---\s*\n?([\s\S]*)$/);
|
|
131
|
+
if (!m)
|
|
132
|
+
return templateText;
|
|
133
|
+
const fm = m[1];
|
|
134
|
+
const body = m[2];
|
|
135
|
+
const descMatch = fm.match(/description:\s*(.+?)\s*$/m);
|
|
136
|
+
const description = descMatch ? descMatch[1].trim() : "";
|
|
137
|
+
return `---\nname: pitstop\ndescription: ${description}\n---\n\n${body}`;
|
|
138
|
+
}
|
|
139
|
+
if (target.transform === "workflow") {
|
|
140
|
+
const m = templateText.match(/^---\s*\n([\s\S]*?)\n---\s*\n?([\s\S]*)$/);
|
|
141
|
+
if (!m)
|
|
142
|
+
return templateText;
|
|
143
|
+
const fm = m[1];
|
|
144
|
+
const body = m[2];
|
|
145
|
+
const descMatch = fm.match(/description:\s*(.+?)\s*$/m);
|
|
146
|
+
const description = descMatch ? descMatch[1].trim() : "";
|
|
147
|
+
return `---\ndescription: ${description}\n---\n\n${body}`;
|
|
148
|
+
}
|
|
149
|
+
if (target.transform === "gemini") {
|
|
150
|
+
const m = templateText.match(/^---\s*\n([\s\S]*?)\n---\s*\n?([\s\S]*)$/);
|
|
151
|
+
if (!m)
|
|
152
|
+
return templateText;
|
|
153
|
+
const fm = m[1];
|
|
154
|
+
const body = m[2];
|
|
155
|
+
const descMatch = fm.match(/description:\s*(.+?)\s*$/m);
|
|
156
|
+
let description = descMatch ? descMatch[1].trim() : "";
|
|
157
|
+
description = description.replace(/^"|"$/g, "");
|
|
158
|
+
return `description = "${description}"\nprompt = """\n${body.trim()}\n"""\n`;
|
|
159
|
+
}
|
|
160
|
+
return templateText;
|
|
161
|
+
}
|
|
162
|
+
//# sourceMappingURL=targets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"targets.js","sourceRoot":"","sources":["../../src/installer/targets.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export type MemoryType = "decision" | "fix" | "rejection";
|
|
2
|
+
export interface MemoryEntry {
|
|
3
|
+
id: string;
|
|
4
|
+
timestamp: string;
|
|
5
|
+
type: MemoryType;
|
|
6
|
+
summary: string;
|
|
7
|
+
context: string;
|
|
8
|
+
relatedFiles: string[];
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Identify "this repo" by hashing the git remote URL (fallback: absolute path).
|
|
12
|
+
* The hash determines the memory file name so memory is stable across checkouts.
|
|
13
|
+
*/
|
|
14
|
+
export declare function identifyRepo(repo: string): string;
|
|
15
|
+
export declare function globalMemoryPath(repo: string): string;
|
|
16
|
+
export declare function localMemoryPath(repo: string): string;
|
|
17
|
+
export declare function loadEntries(repo: string): MemoryEntry[];
|
|
18
|
+
export interface AddInput {
|
|
19
|
+
type: MemoryType;
|
|
20
|
+
summary: string;
|
|
21
|
+
context?: string;
|
|
22
|
+
relatedFiles?: string[];
|
|
23
|
+
mirror?: boolean;
|
|
24
|
+
}
|
|
25
|
+
export declare function addEntry(repo: string, input: AddInput): MemoryEntry;
|
|
26
|
+
/**
|
|
27
|
+
* 1–2 hop undirected neighborhood of the given files in the dependency graph,
|
|
28
|
+
* as a set of absolute paths. Used to decide whether a memory entry is
|
|
29
|
+
* "relevant" to a file or cluster.
|
|
30
|
+
*/
|
|
31
|
+
export declare function buildNeighborhood(repo: string, files: string[]): Set<string>;
|
|
32
|
+
export declare function relevantEntriesForFiles(repo: string, files: string[]): MemoryEntry[];
|
|
33
|
+
export declare function relevantEntries(repo: string, filePath: string): MemoryEntry[];
|