@zephyrdeng/pi-review 0.16.0 → 0.17.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.
@@ -0,0 +1,145 @@
1
+ import assert from "node:assert/strict";
2
+ import fs from "node:fs";
3
+ import os from "node:os";
4
+ import path from "node:path";
5
+ import { test } from "vitest";
6
+ import { aggregateScreenMemory, formatScreenMemoryAscii, loadUserPatterns, parseScreenPatternsFile, readScreenMemory, recordScreenMemory, screenMemoryEnabled, screenMemoryFilePath, screenPatternsFilePath, MEMORY_ENTRY_KEEP, MEMORY_ENTRY_LIMIT, } from "./screen-memory.js";
7
+ function tmpEnv() {
8
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), "pi-review-mem-"));
9
+ return { env: { PI_REVIEW_CONFIG: path.join(dir, "config.json") }, dir };
10
+ }
11
+ const FLAGGED = {
12
+ path: "src/order.ts",
13
+ hunk: "placeOrder",
14
+ startLine: 10,
15
+ endLine: 42,
16
+ catchAll: 0.82,
17
+ patterns: [],
18
+ code: "for (let i = 0; i <= items.length; i++) {",
19
+ };
20
+ test("parseScreenPatternsFile validates entries and skips bad ones with warnings", () => {
21
+ const load = parseScreenPatternsFile(JSON.stringify({
22
+ patterns: {
23
+ good_rule: { title: "T", severity: "major", category: "security", recommendation: "R" },
24
+ "Bad-Id": { title: "T", severity: "major", category: "security", recommendation: "R" },
25
+ bad_severity: { title: "T", severity: "fatal", category: "security", recommendation: "R" },
26
+ bad_shape: "not an object",
27
+ },
28
+ disabled: ["noisy_rule", 42],
29
+ unknown_key: "ignored",
30
+ }), "test.json");
31
+ assert.deepEqual(Object.keys(load.patterns), ["good_rule"]);
32
+ assert.deepEqual(load.disabled, ["noisy_rule"]);
33
+ assert.equal(load.warnings.length, 4); // Bad-Id + bad_severity + bad_shape + non-string disabled entry
34
+ assert.ok(load.warnings.some((w) => w.includes('"disabled"')));
35
+ });
36
+ test("parseScreenPatternsFile treats invalid JSON as empty with a warning", () => {
37
+ const load = parseScreenPatternsFile("{nope", "broken.json");
38
+ assert.deepEqual(load.patterns, {});
39
+ assert.equal(load.warnings.length, 1);
40
+ });
41
+ test("loadUserPatterns merges machine file then PI_REVIEW_SCREEN_PATTERNS, last wins", () => {
42
+ const { env, dir } = tmpEnv();
43
+ fs.writeFileSync(screenPatternsFilePath(env), JSON.stringify({
44
+ patterns: { a_rule: { title: "machine", severity: "minor", category: "correctness", recommendation: "r" } },
45
+ disabled: ["d1"],
46
+ }));
47
+ const extra = path.join(dir, "project.json");
48
+ fs.writeFileSync(extra, JSON.stringify({
49
+ patterns: {
50
+ a_rule: { title: "project override", severity: "critical", category: "security", recommendation: "r2" },
51
+ b_rule: { title: "project only", severity: "major", category: "data-loss", recommendation: "r" },
52
+ },
53
+ disabled: ["d2"],
54
+ }));
55
+ const load = loadUserPatterns({ ...env, PI_REVIEW_SCREEN_PATTERNS: extra });
56
+ assert.equal(load.patterns.a_rule.title, "project override");
57
+ assert.equal(load.patterns.b_rule.title, "project only");
58
+ assert.deepEqual(load.disabled.sort(), ["d1", "d2"]);
59
+ assert.equal(load.files.length, 2);
60
+ });
61
+ test("recordScreenMemory refreshes verdicts on re-flag, appends new hashes, and writes 0600", () => {
62
+ const { env } = tmpEnv();
63
+ const file = screenMemoryFilePath(env);
64
+ recordScreenMemory([FLAGGED], env);
65
+ recordScreenMemory([FLAGGED], env); // same hash: refreshes, seen++
66
+ let entries = readScreenMemory(file);
67
+ assert.equal(entries.length, 1);
68
+ assert.equal(entries[0].unmatched, true);
69
+ assert.equal(entries[0].seen, 2);
70
+ assert.deepEqual(entries[0].lines, [10, 42]);
71
+ assert.equal(fs.statSync(file).mode & 0o777, 0o600); // snippets can embed flagged secrets
72
+ // a promoted pattern flips the stored verdict in place
73
+ recordScreenMemory([{ ...FLAGGED, patterns: ["off_by_one_loop"] }], env);
74
+ entries = readScreenMemory(file);
75
+ assert.equal(entries.length, 1);
76
+ assert.equal(entries[0].unmatched, false);
77
+ assert.deepEqual(entries[0].patterns, ["off_by_one_loop"]);
78
+ assert.equal(entries[0].seen, 3);
79
+ // changed code → new hash → new entry
80
+ recordScreenMemory([{ ...FLAGGED, code: "different code body" }], env);
81
+ entries = readScreenMemory(file);
82
+ assert.equal(entries.length, 2);
83
+ // disabled env writes nothing
84
+ recordScreenMemory([{ ...FLAGGED, hunk: "other", code: "z" }], { ...env, PI_REVIEW_SCREEN_MEMORY: "0" });
85
+ assert.equal(readScreenMemory(file).length, 2);
86
+ assert.equal(screenMemoryEnabled({ PI_REVIEW_SCREEN_MEMORY: "off" }), false);
87
+ });
88
+ test("recordScreenMemory caps the log at MEMORY_ENTRY_LIMIT", () => {
89
+ const { env } = tmpEnv();
90
+ const file = screenMemoryFilePath(env);
91
+ const seed = Array.from({ length: MEMORY_ENTRY_LIMIT }, (_, i) => ({
92
+ ts: "t",
93
+ path: "f.ts",
94
+ hunk: `h${i}`,
95
+ lines: [1, 2],
96
+ patterns: [],
97
+ unmatched: true,
98
+ hash: `seed${i}`,
99
+ }));
100
+ fs.writeFileSync(file, `${seed.map((e) => JSON.stringify(e)).join("\n")}\n`);
101
+ recordScreenMemory([FLAGGED], env);
102
+ const entries = readScreenMemory(file);
103
+ assert.equal(entries.length, MEMORY_ENTRY_KEEP);
104
+ assert.equal(entries[entries.length - 1].hunk, "placeOrder"); // newest kept
105
+ });
106
+ test("aggregateScreenMemory groups unmatched signals and counts pattern hits", () => {
107
+ const entries = [
108
+ { ts: "1", path: "a.ts", hunk: "f", lines: [1, 5], catchAll: 0.8, patterns: [], unmatched: true, hash: "1", code: " first line\n second" },
109
+ { ts: "2", path: "a.ts", hunk: "f", lines: [1, 6], catchAll: 0.9, patterns: [], unmatched: true, hash: "2", code: " changed first" },
110
+ { ts: "3", path: "b.ts", hunk: "g", lines: [1, 3], patterns: ["sql_injection"], unmatched: false, hash: "3" },
111
+ { ts: "4", path: "b.ts", hunk: "h", lines: [4, 9], patterns: ["sql_injection", "float_money"], unmatched: false, hash: "4" },
112
+ ];
113
+ const stats = aggregateScreenMemory(entries);
114
+ assert.equal(stats.entries, 4);
115
+ assert.equal(stats.files, 2);
116
+ assert.equal(stats.unmatchedEntries, 2);
117
+ assert.deepEqual(stats.patternHits, [
118
+ { id: "sql_injection", count: 2 },
119
+ { id: "float_money", count: 1 },
120
+ ]);
121
+ assert.equal(stats.unmatched.length, 1);
122
+ assert.equal(stats.unmatched[0].count, 2);
123
+ assert.equal(stats.unmatched[0].lastCatchAll, 0.9);
124
+ assert.equal(stats.unmatched[0].sample, "changed first");
125
+ });
126
+ test("formatScreenMemoryAscii renders catalog, hits, and promotion hint", () => {
127
+ const stats = aggregateScreenMemory([
128
+ { ts: "1", path: "a.ts", hunk: "f", lines: [1, 5], catchAll: 0.8, patterns: [], unmatched: true, hash: "1", code: "code" },
129
+ ]);
130
+ const text = formatScreenMemoryAscii(stats, {
131
+ file: "/tmp/mem.jsonl",
132
+ patternsFile: "/tmp/screen-patterns.json",
133
+ catalog: { builtin: 21, custom: 2, overrides: 1, disabled: 1, recording: true },
134
+ });
135
+ assert.ok(text.includes("21 builtin · 2 custom (1 overrides builtin) · 1 disabled"));
136
+ assert.ok(text.includes("a.ts"));
137
+ assert.ok(text.includes("screen-patterns.json"));
138
+ const empty = formatScreenMemoryAscii(aggregateScreenMemory([]), {
139
+ file: "/tmp/mem.jsonl",
140
+ patternsFile: "/tmp/screen-patterns.json",
141
+ catalog: { builtin: 21, custom: 0, overrides: 0, disabled: 0, recording: true },
142
+ });
143
+ assert.ok(empty.includes("none yet"));
144
+ });
145
+ //# sourceMappingURL=screen-memory.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"screen-memory.test.js","sourceRoot":"","sources":["../src/screen-memory.test.ts"],"names":[],"mappings":"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"}
package/dist/screen.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { type JevConnection, type JevFetch, type JevUsage } from "./jev.js";
2
+ import { type FlaggedScreenHunk } from "./screen-memory.js";
2
3
  import type { ParsedArgs, ReviewFinding } from "./types.js";
3
4
  /** Probability floor for both the catch-all and per-pattern questions. */
4
5
  export declare const SCREEN_THRESHOLD = 0.6;
@@ -49,7 +50,23 @@ export interface ScreenResult {
49
50
  /** Screen pre-sliced hunks; pure of filesystem and process exit so tests drive it with a mocked fetch. */
50
51
  export declare function screenHunks(connection: JevConnection, hunks: ScreenHunk[], options?: {
51
52
  fetchImpl?: JevFetch;
53
+ patterns?: Record<string, ScreenPattern>;
52
54
  }): Promise<ScreenResult>;
55
+ /**
56
+ * Flagged hunks paired back with their code, in the shape the memory log
57
+ * stores. hunkReports and hunks share order and length by construction.
58
+ */
59
+ export declare function collectFlaggedHunks(result: ScreenResult, hunks: ScreenHunk[]): FlaggedScreenHunk[];
60
+ /**
61
+ * Builtin catalog + user pattern files − disabled ids. The effective catalog
62
+ * is rebuilt per run so edits to screen-patterns.json take effect immediately.
63
+ */
64
+ export declare function effectiveScreenPatterns(env: NodeJS.ProcessEnv): {
65
+ patterns: Record<string, ScreenPattern>;
66
+ warnings: string[];
67
+ };
53
68
  export declare function formatScreenAscii(result: ScreenResult): string;
54
69
  export declare function runScreen(parsed: ParsedArgs): Promise<never>;
70
+ /** `pi-review screen-memory`: report the accumulated signal log and catalog state. */
71
+ export declare function runScreenMemory(env?: NodeJS.ProcessEnv): never;
55
72
  //# sourceMappingURL=screen.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"screen.d.ts","sourceRoot":"","sources":["../src/screen.ts"],"names":[],"mappings":"AAaA,OAAO,EAIL,KAAK,aAAa,EAClB,KAAK,QAAQ,EACb,KAAK,QAAQ,EACd,MAAM,UAAU,CAAC;AAElB,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE5D,0EAA0E;AAC1E,eAAO,MAAM,gBAAgB,MAAM,CAAC;AAEpC,mHAAmH;AACnH,eAAO,MAAM,kBAAkB,KAAK,CAAC;AAErC,wFAAwF;AACxF,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAE1C,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,aAAa,GAAG,UAAU,GAAG,WAAW,GAAG,aAAa,CAAC;IACnE,cAAc,EAAE,MAAM,CAAC;CACxB;AAED;;;;GAIG;AACH,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAiDzD,CAAC;AAIF,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd;AAYD,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,GAAE,MAA2B,GAAG,UAAU,EAAE,CA6C1G;AAED,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,qEAAqE;IACrE,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,sDAAsD;IACtD,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,OAAO,GAAG,cAAc,CAAC;IACjC,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,WAAW,EAAE,gBAAgB,EAAE,CAAC;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,QAAQ,CAAC;CAClB;AAiCD,0GAA0G;AAC1G,wBAAsB,WAAW,CAC/B,UAAU,EAAE,aAAa,EACzB,KAAK,EAAE,UAAU,EAAE,EACnB,OAAO,GAAE;IAAE,SAAS,CAAC,EAAE,QAAQ,CAAA;CAAO,GACrC,OAAO,CAAC,YAAY,CAAC,CAgFvB;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAmB9D;AAED,wBAAsB,SAAS,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,KAAK,CAAC,CAuClE"}
1
+ {"version":3,"file":"screen.d.ts","sourceRoot":"","sources":["../src/screen.ts"],"names":[],"mappings":"AAkBA,OAAO,EAIL,KAAK,aAAa,EAClB,KAAK,QAAQ,EACb,KAAK,QAAQ,EACd,MAAM,UAAU,CAAC;AAClB,OAAO,EASL,KAAK,iBAAiB,EACvB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE5D,0EAA0E;AAC1E,eAAO,MAAM,gBAAgB,MAAM,CAAC;AAEpC,mHAAmH;AACnH,eAAO,MAAM,kBAAkB,KAAK,CAAC;AAErC,wFAAwF;AACxF,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAE1C,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,aAAa,GAAG,UAAU,GAAG,WAAW,GAAG,aAAa,CAAC;IACnE,cAAc,EAAE,MAAM,CAAC;CACxB;AAED;;;;GAIG;AACH,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAkIzD,CAAC;AAEF,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd;AAqBD,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,GAAE,MAA2B,GAAG,UAAU,EAAE,CA6C1G;AAED,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,qEAAqE;IACrE,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,sDAAsD;IACtD,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,OAAO,GAAG,cAAc,CAAC;IACjC,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,WAAW,EAAE,gBAAgB,EAAE,CAAC;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,QAAQ,CAAC;CAClB;AAiCD,0GAA0G;AAC1G,wBAAsB,WAAW,CAC/B,UAAU,EAAE,aAAa,EACzB,KAAK,EAAE,UAAU,EAAE,EACnB,OAAO,GAAE;IAAE,SAAS,CAAC,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAA;CAAO,GAC/E,OAAO,CAAC,YAAY,CAAC,CAkFvB;AAOD;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,UAAU,EAAE,GAAG,iBAAiB,EAAE,CAgBlG;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,GAAG;IAC/D,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IACxC,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB,CAiBA;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAmB9D;AAED,wBAAsB,SAAS,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,KAAK,CAAC,CA8ClE;AAED,sFAAsF;AACtF,wBAAgB,eAAe,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,KAAK,CAuB3E"}
package/dist/screen.js CHANGED
@@ -8,8 +8,14 @@
8
8
  // findings, so the gate never silently drops a strong catch-all signal —
9
9
  // deeper review (full `pi-review`) is the escape hatch, not silent loss.
10
10
  // Mechanism and measurements: docs/research/jev-screening-case.md.
11
+ //
12
+ // The catalog grows from use: user pattern files (screen-patterns.json) merge
13
+ // over the builtin set, and flagged hunks append to screen-memory.jsonl so
14
+ // recurring unmatched signals can be promoted into patterns — see
15
+ // screen-memory.ts and `pi-review screen-memory`.
11
16
  import fs from "node:fs";
12
17
  import { evaluateNouls, resolveJevConnection, GENERIC_NOUL_CRITERIA, } from "./jev.js";
18
+ import { aggregateScreenMemory, formatScreenMemoryAscii, loadUserPatterns, readScreenMemory, recordScreenMemory, screenMemoryEnabled, screenMemoryFilePath, screenPatternsFilePath, } from "./screen-memory.js";
13
19
  import { expandMaybeHome, fail } from "./utils.js";
14
20
  /** Probability floor for both the catch-all and per-pattern questions. */
15
21
  export const SCREEN_THRESHOLD = 0.6;
@@ -71,15 +77,104 @@ export const SCREEN_PATTERNS = {
71
77
  category: "correctness",
72
78
  recommendation: "Validate arguments at the trust boundary.",
73
79
  },
80
+ // --- Extended catalog: patterns distilled from community rule sets
81
+ // (Semgrep registry, ESLint/typescript-eslint, SonarSource, CWE Top 25).
82
+ // All are hunk-local yes/no judgments — cross-hunk taint stays with full review.
83
+ hardcoded_secret: {
84
+ title: "Hardcoded credential, API key, or private key embedded in source",
85
+ severity: "critical",
86
+ category: "security",
87
+ recommendation: "Move secrets to environment or a secret store; rotate the exposed value.",
88
+ },
89
+ command_injection: {
90
+ title: "OS command built by interpolating user-controlled input",
91
+ severity: "critical",
92
+ category: "security",
93
+ recommendation: "Use execFile/spawn with an argv array or validate input against an allowlist.",
94
+ },
95
+ xss_inner_html: {
96
+ title: "Unescaped data written to innerHTML or dangerouslySetInnerHTML",
97
+ severity: "critical",
98
+ category: "security",
99
+ recommendation: "Render with textContent or sanitize through a vetted library.",
100
+ },
101
+ path_traversal: {
102
+ title: "User-controlled input joined into a filesystem path without normalization",
103
+ severity: "critical",
104
+ category: "security",
105
+ recommendation: "Confine to a base directory and reject .. segments after resolve().",
106
+ },
107
+ weak_random: {
108
+ title: "Weak RNG (Math.random or equivalent) used for a security-sensitive value",
109
+ severity: "major",
110
+ category: "security",
111
+ recommendation: "Use crypto.randomBytes/randomUUID or another CSPRNG for tokens and ids.",
112
+ },
113
+ regex_redos: {
114
+ title: "Regex with nested quantifiers can backtrack catastrophically (ReDoS)",
115
+ severity: "major",
116
+ category: "security",
117
+ recommendation: "Rewrite without nested quantifiers over overlapping classes, or cap input length.",
118
+ },
119
+ truthy_default: {
120
+ title: "`value || fallback` treats valid falsy values (0, \"\", false) as missing",
121
+ severity: "major",
122
+ category: "correctness",
123
+ recommendation: "Use `value ?? fallback` or an explicit null/undefined check.",
124
+ },
125
+ string_sort: {
126
+ title: "Array .sort() without a comparator orders numbers lexicographically",
127
+ severity: "major",
128
+ category: "correctness",
129
+ recommendation: "Pass a comparator, e.g. `arr.sort((a, b) => a - b)`.",
130
+ },
131
+ ignored_error: {
132
+ title: "Error return value or promise rejection left unhandled",
133
+ severity: "major",
134
+ category: "correctness",
135
+ recommendation: "Check the error result or attach .catch; silent failure hides corruption.",
136
+ },
137
+ mutable_default_arg: {
138
+ title: "Mutable default argument shared across calls (e.g. def f(items=[]))",
139
+ severity: "major",
140
+ category: "correctness",
141
+ recommendation: "Default to None/null and allocate a fresh object inside the body.",
142
+ },
143
+ bare_except: {
144
+ title: "Catch-all exception handler swallows errors silently",
145
+ severity: "minor",
146
+ category: "correctness",
147
+ recommendation: "Catch specific exceptions and log or re-raise unexpected ones.",
148
+ },
149
+ unclosed_resource: {
150
+ title: "Opened resource (file, response body, connection) not closed on every path",
151
+ severity: "major",
152
+ category: "data-loss",
153
+ recommendation: "Use try/finally, defer, or a context manager to guarantee close.",
154
+ },
155
+ await_in_loop: {
156
+ title: "await inside a loop serializes independent async work",
157
+ severity: "minor",
158
+ category: "performance",
159
+ recommendation: "Batch with bounded Promise.all unless ordering is required.",
160
+ },
74
161
  };
75
- const PATTERN_IDS = Object.keys(SCREEN_PATTERNS);
76
- const CONTROL_KEYWORDS = new Set(["if", "for", "while", "switch", "catch", "else", "do", "return", "new"]);
162
+ const CONTROL_KEYWORDS = new Set([
163
+ "if", "for", "while", "switch", "catch", "else", "do", "return", "new",
164
+ // Compound statements / keywords that could otherwise capture as a name:
165
+ // `with open(...)`, `except (A, B)`, `match (a, b)`, `case (1, 2)`,
166
+ // `assert (x, y)`, `elif (cond)`, `func`/`def` anonymous-style lines.
167
+ "with", "except", "match", "case", "assert", "elif", "func", "def",
168
+ ]);
77
169
  /**
78
170
  * Declaration-boundary heuristic: function/class/interface/type/arrow
79
- * declarations and method signatures. Language-agnostic enough for TS/JS/Go-
80
- * style sources; a file with no recognizable boundaries becomes one hunk.
171
+ * declarations, method signatures, Python `def`, and Go `func` (including
172
+ * methods with a receiver). The method-signature tail requires a `:`/`{`
173
+ * (type annotation, return type, or body open) so a bare call like `foo()`
174
+ * never becomes a boundary. Language-agnostic enough for TS/JS/Python/Go-style
175
+ * sources; a file with no recognizable boundaries becomes one hunk.
81
176
  */
82
- const BOUNDARY = /^\s*(?:(?:export\s+)?(?:default\s+)?(?:async\s+)?function\s+(\w+)|(?:export\s+)?(?:abstract\s+)?class\s+(\w+)|(?:export\s+)?interface\s+(\w+)|(?:export\s+)?type\s+(\w+)\s*=|(?:export\s+)?(?:const|let|var)\s+(\w+)\s*=\s*(?:async\s*)?(?:\([^)]*\)|[\w$]+)\s*=>|(?:public\s+|private\s+|protected\s+)?(?:static\s+)?(?:async\s+)?(\w+)\s*\([^)]*\)\s*[:\w\[\]<>|, ]*\{?\s*$)/;
177
+ const BOUNDARY = /^\s*(?:(?:export\s+)?(?:default\s+)?(?:async\s+)?function\s+(\w+)|(?:export\s+)?(?:abstract\s+)?class\s+(\w+)|(?:export\s+)?interface\s+(\w+)|(?:export\s+)?type\s+(\w+)\s*=|(?:export\s+)?(?:const|let|var)\s+(\w+)\s*=\s*(?:async\s*)?(?:\([^)]*\)|[\w$]+)\s*=>|(?:async\s+)?def\s+(\w+)\s*\(|func\s+(?:\([^)]*\)\s*)?(\w+)\s*\(|(?:public\s+|private\s+|protected\s+)?(?:static\s+)?(?:async\s+)?(\w+)\s*\([^)]*\)\s*(?:\{|[:\w\[\]<>|, ]+\{?)\s*$)/;
83
178
  export function sliceHunks(path, text, maxHunks = MAX_HUNKS_PER_FILE) {
84
179
  const lines = text.split("\n");
85
180
  const boundaries = [];
@@ -153,13 +248,15 @@ async function evaluateChunked(connection, state, questions, fetchImpl) {
153
248
  /** Screen pre-sliced hunks; pure of filesystem and process exit so tests drive it with a mocked fetch. */
154
249
  export async function screenHunks(connection, hunks, options = {}) {
155
250
  const startedAt = Date.now();
251
+ const catalog = options.patterns ?? SCREEN_PATTERNS;
252
+ const patternIds = Object.keys(catalog);
156
253
  const questions = {};
157
254
  hunks.forEach((hunk, i) => {
158
255
  questions[`d${i}`] =
159
256
  `Does hunk "${hunk.id}" contain a real correctness, security, or data-loss defect that should block merge? ` +
160
257
  "Ignore style and hypothetical concerns.";
161
- PATTERN_IDS.forEach((patternId, p) => {
162
- questions[`p${i}_${p}`] = `Does hunk "${hunk.id}" contain this defect: ${SCREEN_PATTERNS[patternId].title}?`;
258
+ patternIds.forEach((patternId, p) => {
259
+ questions[`p${i}_${p}`] = `Does hunk "${hunk.id}" contain this defect: ${catalog[patternId].title}?`;
163
260
  });
164
261
  });
165
262
  const state = {
@@ -176,11 +273,11 @@ export async function screenHunks(connection, hunks, options = {}) {
176
273
  const hunkReports = [];
177
274
  hunks.forEach((hunk, i) => {
178
275
  const catchAll = result.probabilities[`d${i}`];
179
- const hits = PATTERN_IDS.map((patternId, p) => ({ patternId, probability: result.probabilities[`p${i}_${p}`] }))
276
+ const hits = patternIds.map((patternId, p) => ({ patternId, probability: result.probabilities[`p${i}_${p}`] }))
180
277
  .filter((hit) => hit.probability !== undefined && hit.probability >= SCREEN_THRESHOLD)
181
278
  .sort((a, b) => b.probability - a.probability);
182
279
  for (const hit of hits) {
183
- const pattern = SCREEN_PATTERNS[hit.patternId];
280
+ const pattern = catalog[hit.patternId];
184
281
  findings.push({
185
282
  id: `S${findings.length + 1}`,
186
283
  severity: pattern.severity,
@@ -226,9 +323,58 @@ export async function screenHunks(connection, hunks, options = {}) {
226
323
  ...(result.usage ? { usage: result.usage } : {}),
227
324
  };
228
325
  }
326
+ /** The flag rule shared by the ASCII report and memory capture. */
327
+ function isFlaggedReport(report) {
328
+ return (report.defectProbability ?? 0) >= SCREEN_THRESHOLD || report.patterns.length > 0;
329
+ }
330
+ /**
331
+ * Flagged hunks paired back with their code, in the shape the memory log
332
+ * stores. hunkReports and hunks share order and length by construction.
333
+ */
334
+ export function collectFlaggedHunks(result, hunks) {
335
+ const flagged = [];
336
+ result.hunkReports.forEach((report, i) => {
337
+ if (!isFlaggedReport(report))
338
+ return;
339
+ const hunk = hunks[i];
340
+ flagged.push({
341
+ path: report.path,
342
+ hunk: report.id,
343
+ startLine: report.startLine,
344
+ endLine: report.endLine,
345
+ catchAll: report.defectProbability,
346
+ patterns: report.patterns,
347
+ code: hunk.code,
348
+ });
349
+ });
350
+ return flagged;
351
+ }
352
+ /**
353
+ * Builtin catalog + user pattern files − disabled ids. The effective catalog
354
+ * is rebuilt per run so edits to screen-patterns.json take effect immediately.
355
+ */
356
+ export function effectiveScreenPatterns(env) {
357
+ const user = loadUserPatterns(env);
358
+ const patterns = { ...SCREEN_PATTERNS, ...user.patterns };
359
+ for (const id of user.disabled) {
360
+ if (id in patterns) {
361
+ // A patterns file committed to a repo can silently weaken a CI gate —
362
+ // make every builtin removal audible, louder for critical severity.
363
+ const severityNote = SCREEN_PATTERNS[id]?.severity === "critical" ? " (critical severity)" : "";
364
+ if (id in SCREEN_PATTERNS) {
365
+ user.warnings.push(`disabled pattern "${id}" removes a builtin${severityNote} entry`);
366
+ }
367
+ delete patterns[id];
368
+ }
369
+ else {
370
+ user.warnings.push(`disabled pattern "${id}" is not in the catalog; ignoring it`);
371
+ }
372
+ }
373
+ return { patterns, warnings: user.warnings };
374
+ }
229
375
  export function formatScreenAscii(result) {
230
376
  const lines = ["── pi-review screen " + "─".repeat(20)];
231
- const flagged = result.hunkReports.filter((h) => (h.defectProbability ?? 0) >= SCREEN_THRESHOLD || h.patterns.length > 0);
377
+ const flagged = result.hunkReports.filter(isFlaggedReport);
232
378
  lines.push(` Status ${result.status}`);
233
379
  lines.push(` Hunks ${result.hunkReports.length} screened, ${flagged.length} flagged ` +
234
380
  `(${result.questions} questions, ${result.calls} call${result.calls === 1 ? "" : "s"}, ${(result.durationMs / 1000).toFixed(1)}s)`);
@@ -254,6 +400,10 @@ export async function runScreen(parsed) {
254
400
  process.stderr.write("pi-review: screen requires a TypeSafe API key: export TYPESAFE_API_KEY (see README — Jev enhancement mode).\n");
255
401
  process.exit(4);
256
402
  }
403
+ const catalog = effectiveScreenPatterns(process.env);
404
+ for (const warning of catalog.warnings) {
405
+ process.stderr.write(`pi-review: screen patterns: ${warning}\n`);
406
+ }
257
407
  const hunks = [];
258
408
  for (const raw of paths) {
259
409
  const file = expandMaybeHome(raw.startsWith("@") ? raw.slice(1) : raw);
@@ -271,14 +421,36 @@ export async function runScreen(parsed) {
271
421
  }
272
422
  let result;
273
423
  try {
274
- result = await screenHunks(connection, hunks);
424
+ result = await screenHunks(connection, hunks, { patterns: catalog.patterns });
275
425
  }
276
426
  catch (error) {
277
427
  process.stderr.write(`pi-review: screen failed: ${error.message}\n`);
278
428
  process.exit(4);
279
429
  }
430
+ recordScreenMemory(collectFlaggedHunks(result, hunks), process.env);
280
431
  process.stdout.write(`${formatScreenAscii(result)}\n`);
281
432
  process.stderr.write(`PI_REVIEW_SCREEN_JSON: ${JSON.stringify(result)}\n`);
282
433
  process.exit(result.status === "clean" ? 0 : 1);
283
434
  }
435
+ /** `pi-review screen-memory`: report the accumulated signal log and catalog state. */
436
+ export function runScreenMemory(env = process.env) {
437
+ const file = screenMemoryFilePath(env);
438
+ const entries = readScreenMemory(file);
439
+ const user = loadUserPatterns(env);
440
+ for (const warning of user.warnings) {
441
+ process.stderr.write(`pi-review: screen patterns: ${warning}\n`);
442
+ }
443
+ const stats = aggregateScreenMemory(entries);
444
+ const overrides = Object.keys(user.patterns).filter((id) => id in SCREEN_PATTERNS).length;
445
+ const catalogMeta = {
446
+ builtin: Object.keys(SCREEN_PATTERNS).length,
447
+ custom: Object.keys(user.patterns).length,
448
+ overrides,
449
+ disabled: user.disabled.length,
450
+ recording: screenMemoryEnabled(env),
451
+ };
452
+ process.stdout.write(`${formatScreenMemoryAscii(stats, { file, patternsFile: screenPatternsFilePath(env), catalog: catalogMeta })}\n`);
453
+ process.stderr.write(`PI_REVIEW_SCREEN_MEMORY_JSON: ${JSON.stringify({ file, catalog: catalogMeta, ...stats })}\n`);
454
+ process.exit(0);
455
+ }
284
456
  //# sourceMappingURL=screen.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"screen.js","sourceRoot":"","sources":["../src/screen.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,iCAAiC;AACjC,EAAE;AACF,0EAA0E;AAC1E,6EAA6E;AAC7E,4EAA4E;AAC5E,uEAAuE;AACvE,yEAAyE;AACzE,yEAAyE;AACzE,mEAAmE;AAEnE,OAAO,EAAE,MAAM,SAAS,CAAC;AAEzB,OAAO,EACL,aAAa,EACb,oBAAoB,EACpB,qBAAqB,GAItB,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,eAAe,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAGnD,0EAA0E;AAC1E,MAAM,CAAC,MAAM,gBAAgB,GAAG,GAAG,CAAC;AAEpC,mHAAmH;AACnH,MAAM,CAAC,MAAM,kBAAkB,GAAG,EAAE,CAAC;AAErC,wFAAwF;AACxF,MAAM,CAAC,MAAM,sBAAsB,GAAG,GAAG,CAAC;AAS1C;;;;GAIG;AACH,MAAM,CAAC,MAAM,eAAe,GAAkC;IAC5D,eAAe,EAAE;QACf,KAAK,EAAE,4CAA4C;QACnD,QAAQ,EAAE,UAAU;QACpB,QAAQ,EAAE,aAAa;QACvB,cAAc,EAAE,+CAA+C;KAChE;IACD,eAAe,EAAE;QACf,KAAK,EAAE,0CAA0C;QACjD,QAAQ,EAAE,UAAU;QACpB,QAAQ,EAAE,WAAW;QACrB,cAAc,EAAE,sFAAsF;KACvG;IACD,aAAa,EAAE;QACb,KAAK,EAAE,wCAAwC;QAC/C,QAAQ,EAAE,UAAU;QACpB,QAAQ,EAAE,UAAU;QACpB,cAAc,EAAE,qEAAqE;KACtF;IACD,gBAAgB,EAAE;QAChB,KAAK,EAAE,+CAA+C;QACtD,QAAQ,EAAE,OAAO;QACjB,QAAQ,EAAE,aAAa;QACvB,cAAc,EAAE,qDAAqD;KACtE;IACD,WAAW,EAAE;QACX,KAAK,EAAE,8BAA8B;QACrC,QAAQ,EAAE,OAAO;QACjB,QAAQ,EAAE,aAAa;QACvB,cAAc,EAAE,2DAA2D;KAC5E;IACD,cAAc,EAAE;QACd,KAAK,EAAE,0CAA0C;QACjD,QAAQ,EAAE,OAAO;QACjB,QAAQ,EAAE,aAAa;QACvB,cAAc,EAAE,iFAAiF;KAClG;IACD,cAAc,EAAE;QACd,KAAK,EAAE,2CAA2C;QAClD,QAAQ,EAAE,OAAO;QACjB,QAAQ,EAAE,aAAa;QACvB,cAAc,EAAE,8CAA8C;KAC/D;IACD,kBAAkB,EAAE;QAClB,KAAK,EAAE,0BAA0B;QACjC,QAAQ,EAAE,OAAO;QACjB,QAAQ,EAAE,aAAa;QACvB,cAAc,EAAE,2CAA2C;KAC5D;CACF,CAAC;AAEF,MAAM,WAAW,GAAG,MAAM,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;AAUjD,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAC;AAE3G;;;;GAIG;AACH,MAAM,QAAQ,GACZ,gXAAgX,CAAC;AAEnX,MAAM,UAAU,UAAU,CAAC,IAAY,EAAE,IAAY,EAAE,WAAmB,kBAAkB;IAC1F,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC/B,MAAM,UAAU,GAA2C,EAAE,CAAC;IAC9D,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;QAC5B,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClC,IAAI,CAAC,KAAK;YAAE,OAAO;QACnB,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC;QACjE,IAAI,CAAC,IAAI,IAAI,gBAAgB,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,OAAO;QAChD,UAAU,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACnC,CAAC,CAAC,CAAC;IAEH,MAAM,IAAI,GAAG,IAAI,GAAG,EAAkB,CAAC;IACvC,MAAM,QAAQ,GAAG,CAAC,IAAY,EAAU,EAAE;QACxC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;QACpC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QAClB,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,CAAC,EAAE,CAAC;IACzC,CAAC,CAAC;IAEF,MAAM,KAAK,GAAiB,EAAE,CAAC;IAC/B,MAAM,IAAI,GAAG,CAAC,IAAY,EAAE,UAAkB,EAAE,QAAgB,EAAQ,EAAE;QACxE,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,UAAU,EAAE,QAAQ,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC9D,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE;YAAE,OAAO;QACzB,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,UAAU,GAAG,CAAC,EAAE,OAAO,EAAE,QAAQ,GAAG,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;IACnG,CAAC,CAAC;IAEF,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC5B,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QAClC,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,CAAC,QAAQ,EAAE,CAAC,EAAE,UAAU,CAAC,CAAC,CAAE,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;IAC5C,UAAU,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,CAAC,EAAE,EAAE;QACjC,MAAM,GAAG,GAAG,CAAC,GAAG,CAAC,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,CAAE,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;QACxF,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;IAEH,oEAAoE;IACpE,IAAI,KAAK,CAAC,MAAM,GAAG,QAAQ,EAAE,CAAC;QAC5B,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;QACtC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAE,CAAC;QACpC,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAE,CAAC;QACtC,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;QAC5B,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,GAAG,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACrE,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAwBD;;;GAGG;AACH,KAAK,UAAU,eAAe,CAC5B,UAAyB,EACzB,KAAc,EACd,SAAiC,EACjC,SAAoB;IAEpB,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAC1C,MAAM,aAAa,GAA2B,EAAE,CAAC;IACjD,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,IAAI,KAA2B,CAAC;IAChC,IAAI,KAAyB,CAAC;IAC9B,KAAK,IAAI,MAAM,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,MAAM,IAAI,sBAAsB,EAAE,CAAC;QAC/E,MAAM,KAAK,GAAG,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,sBAAsB,CAAC,CAAC,CAAC;QACzF,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,UAAU,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,IAAI,KAAK,EAAE,qBAAqB,CAAC,CAAC;QACxG,MAAM,CAAC,MAAM,CAAC,aAAa,EAAE,MAAM,CAAC,aAAa,CAAC,CAAC;QACnD,KAAK,IAAI,CAAC,CAAC;QACX,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;YACjB,KAAK,GAAG;gBACN,WAAW,EAAE,CAAC,KAAK,EAAE,WAAW,IAAI,CAAC,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,WAAW;gBACjE,YAAY,EAAE,CAAC,KAAK,EAAE,YAAY,IAAI,CAAC,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,YAAY;aACrE,CAAC;QACJ,CAAC;QACD,KAAK,GAAG,MAAM,CAAC,KAAK,IAAI,KAAK,CAAC;IAChC,CAAC;IACD,OAAO,EAAE,aAAa,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;AAC5F,CAAC;AAED,0GAA0G;AAC1G,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,UAAyB,EACzB,KAAmB,EACnB,UAAoC,EAAE;IAEtC,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAC7B,MAAM,SAAS,GAA2B,EAAE,CAAC;IAC7C,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE;QACxB,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC;YAChB,cAAc,IAAI,CAAC,EAAE,uFAAuF;gBAC5G,yCAAyC,CAAC;QAC5C,WAAW,CAAC,OAAO,CAAC,CAAC,SAAS,EAAE,CAAC,EAAE,EAAE;YACnC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,cAAc,IAAI,CAAC,EAAE,0BAA0B,eAAe,CAAC,SAAS,CAAE,CAAC,KAAK,GAAG,CAAC;QAChH,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,MAAM,KAAK,GAAG;QACZ,IAAI,EAAE,wHAAwH;QAC9H,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YAC1B,EAAE,EAAE,IAAI,CAAC,EAAE;YACX,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,KAAK,EAAE,GAAG,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,OAAO,EAAE;YAC1C,IAAI,EAAE,IAAI,CAAC,IAAI;SAChB,CAAC,CAAC;KACJ,CAAC;IACF,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC,UAAU,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC;IAEtF,MAAM,QAAQ,GAAoB,EAAE,CAAC;IACrC,MAAM,WAAW,GAAuB,EAAE,CAAC;IAC3C,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE;QACxB,MAAM,QAAQ,GAAG,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC/C,MAAM,IAAI,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;aAC7G,MAAM,CAAC,CAAC,GAAG,EAAqD,EAAE,CAAC,GAAG,CAAC,WAAW,KAAK,SAAS,IAAI,GAAG,CAAC,WAAW,IAAI,gBAAgB,CAAC;aACxI,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,GAAG,CAAC,CAAC,WAAW,CAAC,CAAC;QAEjD,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,MAAM,OAAO,GAAG,eAAe,CAAC,GAAG,CAAC,SAAS,CAAE,CAAC;YAChD,QAAQ,CAAC,IAAI,CAAC;gBACZ,EAAE,EAAE,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE;gBAC7B,QAAQ,EAAE,OAAO,CAAC,QAAQ;gBAC1B,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,OAAO,EAAE,OAAO,CAAC,KAAK;gBACtB,UAAU,EAAE,IAAI;gBAChB,OAAO,EACL,mBAAmB,IAAI,CAAC,EAAE,MAAM,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,OAAO,2BAA2B;oBACtG,IAAI,GAAG,CAAC,SAAS,QAAQ,GAAG,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,cAAc,OAAO,CAAC,QAAQ,IAAI;gBACvF,cAAc,EAAE,OAAO,CAAC,cAAc;gBACtC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE;aAC/D,CAAC,CAAC;QACL,CAAC;QACD,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,IAAI,gBAAgB,EAAE,CAAC;YAChF,QAAQ,CAAC,IAAI,CAAC;gBACZ,EAAE,EAAE,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE;gBAC7B,QAAQ,EAAE,OAAO;gBACjB,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,OAAO,EAAE,uBAAuB,IAAI,CAAC,EAAE,kDAAkD;gBACzF,UAAU,EAAE,IAAI;gBAChB,OAAO,EACL,8CAA8C,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,cAAc,IAAI,CAAC,EAAE,IAAI;oBAC1F,IAAI,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,OAAO,4CAA4C,gBAAgB,GAAG;gBAChH,cAAc,EAAE,mEAAmE;gBACnF,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE;aAC/D,CAAC,CAAC;QACL,CAAC;QACD,WAAW,CAAC,IAAI,CAAC;YACf,EAAE,EAAE,IAAI,CAAC,EAAE;YACX,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,GAAG,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,iBAAiB,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAClE,QAAQ,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,SAAS,CAAC;SAC3C,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,OAAO;QACL,MAAM,EAAE,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,OAAO;QACtD,QAAQ;QACR,WAAW;QACX,SAAS,EAAE,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,MAAM;QACxC,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS;QAClC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAChD,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACjD,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,MAAoB;IACpD,MAAM,KAAK,GAAG,CAAC,sBAAsB,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;IACxD,MAAM,OAAO,GAAG,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,iBAAiB,IAAI,CAAC,CAAC,IAAI,gBAAgB,IAAI,CAAC,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC1H,KAAK,CAAC,IAAI,CAAC,gBAAgB,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;IAC5C,KAAK,CAAC,IAAI,CACR,gBAAgB,MAAM,CAAC,WAAW,CAAC,MAAM,cAAc,OAAO,CAAC,MAAM,WAAW;QAC9E,IAAI,MAAM,CAAC,SAAS,eAAe,MAAM,CAAC,KAAK,QAAQ,MAAM,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CACrI,CAAC;IACF,KAAK,CAAC,IAAI,CAAC,gBAAgB,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;IACrD,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC/B,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACf,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;YACtC,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,QAAQ,EAAE,SAAS,IAAI,GAAG,IAAI,OAAO,CAAC,QAAQ,EAAE,OAAO,IAAI,GAAG,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC;YAC/H,KAAK,CAAC,IAAI,CAAC,KAAK,OAAO,CAAC,EAAE,KAAK,CAAC,OAAO,CAAC,QAAQ,IAAI,GAAG,CAAC,CAAC,WAAW,EAAE,KAAK,KAAK,EAAE,CAAC,CAAC;YACpF,KAAK,CAAC,IAAI,CAAC,SAAS,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;QACzC,CAAC;IACH,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;IAC3B,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,MAAkB;IAChD,MAAM,KAAK,GAAG,MAAM,CAAC,WAAW,IAAI,EAAE,CAAC;IACvC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,IAAI,CAAC,qEAAqE,CAAC,CAAC;IAC9E,CAAC;IACD,MAAM,UAAU,GAAG,oBAAoB,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACrD,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,+GAA+G,CAChH,CAAC;QACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,KAAK,GAAiB,EAAE,CAAC;IAC/B,KAAK,MAAM,GAAG,IAAI,KAAK,EAAE,CAAC;QACxB,MAAM,IAAI,GAAG,eAAe,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAE,CAAC;QACxE,IAAI,IAAY,CAAC;QACjB,IAAI,CAAC;YACH,IAAI,GAAG,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QACvC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,uBAAuB,IAAI,KAAM,KAAe,CAAC,OAAO,EAAE,CAAC,CAAC;QACnE,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,GAAG,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC;IACxC,CAAC;IACD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,IAAI,CAAC,sDAAsD,CAAC,CAAC;IAC/D,CAAC;IAED,IAAI,MAAoB,CAAC;IACzB,IAAI,CAAC;QACH,MAAM,GAAG,MAAM,WAAW,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;IAChD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,6BAA8B,KAAe,CAAC,OAAO,IAAI,CAAC,CAAC;QAChF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,iBAAiB,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACvD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,0BAA0B,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC3E,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAClD,CAAC"}
1
+ {"version":3,"file":"screen.js","sourceRoot":"","sources":["../src/screen.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,9 @@
1
1
  import assert from "node:assert/strict";
2
+ import fs from "node:fs";
3
+ import os from "node:os";
4
+ import path from "node:path";
2
5
  import { test } from "vitest";
3
- import { sliceHunks, screenHunks, formatScreenAscii, SCREEN_PATTERNS, SCREEN_THRESHOLD, MAX_QUESTIONS_PER_CALL, } from "./screen.js";
6
+ import { sliceHunks, screenHunks, formatScreenAscii, effectiveScreenPatterns, collectFlaggedHunks, SCREEN_PATTERNS, SCREEN_THRESHOLD, MAX_QUESTIONS_PER_CALL, } from "./screen.js";
4
7
  const CONN = { apiKey: "test-key", baseUrl: "https://example.test", model: "jev-test" };
5
8
  const FIXTURE = `import { randomUUID } from "node:crypto";
6
9
 
@@ -57,6 +60,16 @@ test("sliceHunks skips control-flow keywords and empty headers", () => {
57
60
  const hunks = sliceHunks("x.ts", "export function main() {\n if (a) {\n for (let i = 0;;) {}\n }\n}\n");
58
61
  assert.deepEqual(hunks.map((h) => h.id), ["main"]);
59
62
  });
63
+ test("sliceHunks slices Python def/class and Go func/method declarations", () => {
64
+ const py = `import os\n\n\ndef helper(x=[]):\n x.append(1)\n\n\nclass Service:\n def handle(self, req):\n return req\n\n\nasync def fetch(url):\n pass\n`;
65
+ assert.deepEqual(sliceHunks("svc.py", py).map((h) => h.id), ["header", "helper", "Service", "handle", "fetch"]);
66
+ const go = `package svc\n\nfunc placeOrder(items []Item) error {\n\treturn nil\n}\n\nfunc (s *Service) List(ctx context.Context) {\n}\n`;
67
+ assert.deepEqual(sliceHunks("svc.go", go).map((h) => h.id), ["header", "placeOrder", "List"]);
68
+ });
69
+ test("sliceHunks does not split on with/except/case compound statements or bare calls", () => {
70
+ const py = `def run():\n with open(f) as fh:\n data = fh.read()\n try:\n parse(data)\n except (ValueError, KeyError):\n pass\n`;
71
+ assert.deepEqual(sliceHunks("run.py", py).map((h) => h.id), ["run"]);
72
+ });
60
73
  test("sliceHunks falls back to one file hunk when no boundary matches", () => {
61
74
  const hunks = sliceHunks("plain.txt", "alpha\nbeta\ngamma\n");
62
75
  assert.equal(hunks.length, 1);
@@ -117,6 +130,61 @@ test("screenHunks chunks question volumes past the per-call cap", async () => {
117
130
  assert.ok(result.calls >= 2);
118
131
  assert.equal(result.status, "clean");
119
132
  });
133
+ test("screenHunks screens against a caller-supplied catalog", async () => {
134
+ const hunks = sliceHunks("order-service.ts", FIXTURE);
135
+ const placeIdx = hunks.findIndex((h) => h.id === "placeOrder");
136
+ const custom = {
137
+ my_rule: {
138
+ title: "A custom defect rule",
139
+ severity: "minor",
140
+ category: "correctness",
141
+ recommendation: "Fix it.",
142
+ },
143
+ };
144
+ const result = await screenHunks(CONN, hunks, {
145
+ patterns: custom,
146
+ fetchImpl: noulFetch({ [`p${placeIdx}_0`]: 0.9 }),
147
+ });
148
+ assert.equal(result.questions, hunks.length * 2); // catch-all + 1 custom pattern
149
+ assert.equal(result.findings.length, 1);
150
+ assert.equal(result.findings[0].summary, "A custom defect rule");
151
+ assert.ok(result.findings[0].details.includes("my_rule"));
152
+ });
153
+ test("effectiveScreenPatterns merges user files and honors disabled ids", () => {
154
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), "pi-review-screen-"));
155
+ const configPath = path.join(dir, "config.json");
156
+ fs.writeFileSync(path.join(dir, "screen-patterns.json"), JSON.stringify({
157
+ patterns: {
158
+ team_rule: { title: "Team rule", severity: "minor", category: "correctness", recommendation: "Do it." },
159
+ off_by_one_loop: { title: "Team override of off-by-one", severity: "minor", category: "correctness", recommendation: "Fix." },
160
+ },
161
+ disabled: ["missing_validation", "nonexistent_id"],
162
+ }));
163
+ const env = { PI_REVIEW_CONFIG: configPath };
164
+ const { patterns, warnings } = effectiveScreenPatterns(env);
165
+ assert.equal(patterns.team_rule.title, "Team rule");
166
+ assert.equal(patterns.off_by_one_loop.title, "Team override of off-by-one");
167
+ assert.equal(patterns.missing_validation, undefined);
168
+ assert.ok(warnings.some((w) => w.includes("nonexistent_id")));
169
+ // builtin count: 21 shipped patterns; override replaced one, disable removed one
170
+ assert.equal(Object.keys(patterns).length, Object.keys(SCREEN_PATTERNS).length + 1 - 1);
171
+ });
172
+ test("collectFlaggedHunks pairs flagged reports with their code", () => {
173
+ const hunks = sliceHunks("order-service.ts", FIXTURE);
174
+ const reports = hunks.map((h, i) => ({
175
+ id: h.id,
176
+ path: h.path,
177
+ startLine: h.startLine,
178
+ endLine: h.endLine,
179
+ defectProbability: i === 0 ? 0.9 : 0.1,
180
+ patterns: i === 1 ? ["string_sort"] : [],
181
+ }));
182
+ const flagged = collectFlaggedHunks({ status: "has_findings", findings: [], hunkReports: reports, questions: 0, calls: 1, durationMs: 1 }, hunks);
183
+ assert.equal(flagged.length, 2);
184
+ assert.equal(flagged[0].hunk, hunks[0].id);
185
+ assert.equal(flagged[0].code, hunks[0].code);
186
+ assert.deepEqual(flagged[1].patterns, ["string_sort"]);
187
+ });
120
188
  test("screenHunks treats missing answers as no-signal", async () => {
121
189
  const hunks = sliceHunks("order-service.ts", FIXTURE);
122
190
  const result = await screenHunks(CONN, hunks, {