@zephyrdeng/pi-review 0.16.1 → 0.17.1

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,320 @@
1
+ // pi-review screen memory: the screening catalog learns from use.
2
+ //
3
+ // Two mechanisms share the pi-review state dir (siblings of config.json):
4
+ //
5
+ // - screen-patterns.json — a user-declared pattern catalog merged over the
6
+ // builtin SCREEN_PATTERNS, plus a `disabled` list to retire entries that
7
+ // misfire on this codebase. PI_REVIEW_SCREEN_PATTERNS names one extra file
8
+ // (e.g. a project catalog committed to the repo) which loads last and wins
9
+ // id conflicts — that is how a team shares its own defect patterns.
10
+ //
11
+ // - screen-memory.jsonl — every flagged hunk appends one JSONL entry with a
12
+ // code hash and its verdict. `pi-review screen-memory` aggregates the log:
13
+ // recurring unmatched signals are the evidence for new patterns, and hit
14
+ // frequencies show which entries earn their place. Promotion is always a
15
+ // human/agent edit of screen-patterns.json — screen never rewrites its own
16
+ // catalog. PI_REVIEW_SCREEN_MEMORY=0 disables recording; the file path moves
17
+ // with PI_REVIEW_SCREEN_MEMORY_FILE.
18
+ //
19
+ // Both files are advisory: malformed lines and invalid JSON warn on stderr and
20
+ // degrade to "nothing loaded" — memory must never break the gate.
21
+ import crypto from "node:crypto";
22
+ import fs from "node:fs";
23
+ import path from "node:path";
24
+ import { configFilePath } from "./pi-config.js";
25
+ import { expandMaybeHome } from "./utils.js";
26
+ /** Hard cap keeps the log self-bounding; on overflow the newest entries stay. */
27
+ export const MEMORY_ENTRY_LIMIT = 500;
28
+ export const MEMORY_ENTRY_KEEP = 400;
29
+ /** Stored code snippets never exceed this many characters. */
30
+ export const SNIPPET_LIMIT = 1600;
31
+ const VALID_SEVERITIES = new Set(["critical", "major", "minor"]);
32
+ const VALID_CATEGORIES = new Set(["correctness", "security", "data-loss", "performance"]);
33
+ const PATTERN_ID_RE = /^[a-z][a-z0-9_]{0,63}$/;
34
+ /** pi-review state dir: the directory containing the effective config file. */
35
+ function stateDir(env) {
36
+ return path.dirname(configFilePath(env));
37
+ }
38
+ /** Machine-level user catalog, sibling of config.json. */
39
+ export function screenPatternsFilePath(env = process.env) {
40
+ return path.join(stateDir(env), "screen-patterns.json");
41
+ }
42
+ export function screenMemoryFilePath(env = process.env) {
43
+ const override = env.PI_REVIEW_SCREEN_MEMORY_FILE?.trim();
44
+ if (override)
45
+ return expandMaybeHome(override);
46
+ return path.join(stateDir(env), "screen-memory.jsonl");
47
+ }
48
+ export function screenMemoryEnabled(env = process.env) {
49
+ const raw = env.PI_REVIEW_SCREEN_MEMORY?.trim().toLowerCase();
50
+ return raw !== "0" && raw !== "false" && raw !== "off";
51
+ }
52
+ /**
53
+ * Parse one patterns file, lenient like pi-config: bad entries warn and are
54
+ * skipped, unknown keys are ignored, invalid JSON yields an empty file.
55
+ * Shape: { "patterns": {id: {title, severity, category, recommendation}},
56
+ * "disabled": [id, ...] }
57
+ */
58
+ export function parseScreenPatternsFile(text, source) {
59
+ const warnings = [];
60
+ let raw;
61
+ try {
62
+ raw = JSON.parse(text);
63
+ }
64
+ catch (error) {
65
+ return {
66
+ patterns: {},
67
+ disabled: [],
68
+ warnings: [`${source} is not valid JSON (${error.message}); treating it as empty`],
69
+ };
70
+ }
71
+ if (raw === null || typeof raw !== "object" || Array.isArray(raw)) {
72
+ return { patterns: {}, disabled: [], warnings: [`${source} must contain a JSON object; treating it as empty`] };
73
+ }
74
+ const record = raw;
75
+ const patterns = {};
76
+ if (record.patterns !== undefined && record.patterns !== null) {
77
+ if (typeof record.patterns !== "object" || Array.isArray(record.patterns)) {
78
+ warnings.push(`${source}: "patterns" must be an object; ignoring it`);
79
+ }
80
+ else {
81
+ for (const [id, value] of Object.entries(record.patterns)) {
82
+ if (!PATTERN_ID_RE.test(id)) {
83
+ warnings.push(`${source}: pattern id "${id}" must match ${PATTERN_ID_RE}; skipping it`);
84
+ continue;
85
+ }
86
+ const entry = value;
87
+ const problems = [];
88
+ if (entry === null || typeof entry !== "object" || Array.isArray(entry))
89
+ problems.push("entry must be an object");
90
+ else {
91
+ if (typeof entry.title !== "string" || !entry.title.trim())
92
+ problems.push("title must be a non-empty string");
93
+ if (typeof entry.severity !== "string" || !VALID_SEVERITIES.has(entry.severity))
94
+ problems.push(`severity must be one of ${[...VALID_SEVERITIES].join("/")}`);
95
+ if (typeof entry.category !== "string" || !VALID_CATEGORIES.has(entry.category))
96
+ problems.push(`category must be one of ${[...VALID_CATEGORIES].join("/")}`);
97
+ if (typeof entry.recommendation !== "string" || !entry.recommendation.trim())
98
+ problems.push("recommendation must be a non-empty string");
99
+ }
100
+ if (problems.length > 0) {
101
+ warnings.push(`${source}: pattern "${id}" invalid (${problems.join("; ")}); skipping it`);
102
+ continue;
103
+ }
104
+ patterns[id] = {
105
+ title: entry.title.trim(),
106
+ severity: entry.severity,
107
+ category: entry.category,
108
+ recommendation: entry.recommendation.trim(),
109
+ };
110
+ }
111
+ }
112
+ }
113
+ const disabled = [];
114
+ if (record.disabled !== undefined && record.disabled !== null) {
115
+ if (!Array.isArray(record.disabled)) {
116
+ warnings.push(`${source}: "disabled" must be an array of pattern ids; ignoring it`);
117
+ }
118
+ else {
119
+ for (const value of record.disabled) {
120
+ if (typeof value === "string" && value.trim())
121
+ disabled.push(value.trim());
122
+ else
123
+ warnings.push(`${source}: "disabled" entries must be pattern id strings; skipping one`);
124
+ }
125
+ }
126
+ }
127
+ return { patterns, disabled, warnings };
128
+ }
129
+ /**
130
+ * Load user-declared patterns: the machine file first, then the extra file
131
+ * named by PI_REVIEW_SCREEN_PATTERNS (typically a project catalog checked
132
+ * into the repo). Later files override earlier ones on id conflicts, so a
133
+ * project can re-tune or replace a builtin entry. Missing files are skipped
134
+ * silently; unreadable ones warn.
135
+ */
136
+ export function loadUserPatterns(env = process.env) {
137
+ const files = [screenPatternsFilePath(env)];
138
+ const extra = env.PI_REVIEW_SCREEN_PATTERNS?.trim();
139
+ if (extra)
140
+ files.push(expandMaybeHome(extra));
141
+ const merged = { patterns: {}, disabled: [], warnings: [], files: [] };
142
+ for (const file of files) {
143
+ if (!fs.existsSync(file))
144
+ continue;
145
+ let load;
146
+ try {
147
+ load = parseScreenPatternsFile(fs.readFileSync(file, "utf8"), file);
148
+ }
149
+ catch (error) {
150
+ merged.warnings.push(`cannot read ${file}: ${error.message}`);
151
+ continue;
152
+ }
153
+ merged.files.push(file);
154
+ Object.assign(merged.patterns, load.patterns);
155
+ merged.disabled.push(...load.disabled);
156
+ merged.warnings.push(...load.warnings);
157
+ }
158
+ merged.disabled = [...new Set(merged.disabled)];
159
+ return merged;
160
+ }
161
+ /** Lenient JSONL read: malformed lines are skipped, a missing file is empty. */
162
+ export function readScreenMemory(file) {
163
+ let text;
164
+ try {
165
+ text = fs.readFileSync(file, "utf8");
166
+ }
167
+ catch {
168
+ return [];
169
+ }
170
+ const entries = [];
171
+ for (const line of text.split("\n")) {
172
+ if (!line.trim())
173
+ continue;
174
+ try {
175
+ const entry = JSON.parse(line);
176
+ if (entry && typeof entry === "object" && typeof entry.path === "string" && typeof entry.hunk === "string") {
177
+ entries.push(entry);
178
+ }
179
+ }
180
+ catch {
181
+ // malformed line: drop it — the log is advisory telemetry
182
+ }
183
+ }
184
+ return entries;
185
+ }
186
+ /**
187
+ * Record flagged hunks in the memory log. No-ops when recording is disabled
188
+ * or nothing was flagged. A repeated (path, hunk, code hash) refreshes the
189
+ * stored verdict in place and bumps `seen` — so promoting a pattern visibly
190
+ * flips its entry from unmatched to a hit — while new hashes append. The file
191
+ * is rewritten capped at MEMORY_ENTRY_LIMIT entries (newest MEMORY_ENTRY_KEEP
192
+ * survive) via tmp+rename with 0600 permissions: hunk code can embed the very
193
+ * secrets the catalog flags. Best-effort: any failure warns on stderr and
194
+ * never fails the gate.
195
+ */
196
+ export function recordScreenMemory(flagged, env = process.env) {
197
+ if (flagged.length === 0 || !screenMemoryEnabled(env))
198
+ return;
199
+ try {
200
+ const file = screenMemoryFilePath(env);
201
+ const existing = readScreenMemory(file);
202
+ const byKey = new Map(existing.map((e) => [`${e.path}\u0000${e.hunk}\u0000${e.hash}`, e]));
203
+ const ts = new Date().toISOString();
204
+ let touched = 0;
205
+ for (const item of flagged) {
206
+ const hash = crypto.createHash("sha256").update(item.code).digest("hex");
207
+ const key = `${item.path}\u0000${item.hunk}\u0000${hash}`;
208
+ const snippet = item.code.length <= SNIPPET_LIMIT ? item.code : `${item.code.slice(0, SNIPPET_LIMIT)}…`;
209
+ const prev = byKey.get(key);
210
+ if (prev) {
211
+ prev.ts = ts;
212
+ prev.lines = [item.startLine, item.endLine];
213
+ prev.patterns = item.patterns;
214
+ prev.unmatched = item.patterns.length === 0;
215
+ if (item.catchAll !== undefined)
216
+ prev.catchAll = item.catchAll;
217
+ prev.seen = (prev.seen ?? 1) + 1;
218
+ touched += 1;
219
+ continue;
220
+ }
221
+ byKey.set(key, {
222
+ ts,
223
+ path: item.path,
224
+ hunk: item.hunk,
225
+ lines: [item.startLine, item.endLine],
226
+ ...(item.catchAll !== undefined ? { catchAll: item.catchAll } : {}),
227
+ patterns: item.patterns,
228
+ unmatched: item.patterns.length === 0,
229
+ hash,
230
+ code: snippet,
231
+ seen: 1,
232
+ });
233
+ touched += 1;
234
+ }
235
+ if (touched === 0)
236
+ return;
237
+ const all = [...byKey.values()];
238
+ const kept = all.length > MEMORY_ENTRY_LIMIT ? all.slice(all.length - MEMORY_ENTRY_KEEP) : all;
239
+ fs.mkdirSync(path.dirname(file), { recursive: true });
240
+ const tmp = `${file}.${process.pid}.tmp`;
241
+ fs.writeFileSync(tmp, `${kept.map((entry) => JSON.stringify(entry)).join("\n")}\n`, { encoding: "utf8", mode: 0o600 });
242
+ fs.renameSync(tmp, file);
243
+ try {
244
+ fs.chmodSync(file, 0o600); // a file created before this hardening keeps its old mode otherwise
245
+ }
246
+ catch {
247
+ // best-effort tightening; the tmp+rename above is the real guard
248
+ }
249
+ }
250
+ catch (error) {
251
+ process.stderr.write(`pi-review: screen memory write failed (${error.message}); continuing\n`);
252
+ }
253
+ }
254
+ export function aggregateScreenMemory(entries) {
255
+ const files = new Set();
256
+ const hits = new Map();
257
+ const groups = new Map();
258
+ let unmatchedEntries = 0;
259
+ for (const entry of entries) {
260
+ files.add(entry.path);
261
+ for (const id of entry.patterns ?? []) {
262
+ hits.set(id, (hits.get(id) ?? 0) + 1);
263
+ }
264
+ if (!entry.unmatched)
265
+ continue;
266
+ unmatchedEntries += entry.seen ?? 1;
267
+ const key = `${entry.path}\u0000${entry.hunk}`;
268
+ const group = groups.get(key) ?? {
269
+ path: entry.path,
270
+ hunk: entry.hunk,
271
+ count: 0,
272
+ lastLines: [0, 0],
273
+ sample: "",
274
+ };
275
+ group.count += entry.seen ?? 1;
276
+ group.lastLines = entry.lines ?? [0, 0];
277
+ group.lastCatchAll = entry.catchAll;
278
+ group.sample = (entry.code ?? "").split("\n").map((l) => l.trim()).find((l) => l.length > 0)?.slice(0, 100) ?? "";
279
+ groups.set(key, group);
280
+ }
281
+ return {
282
+ entries: entries.length,
283
+ files: files.size,
284
+ unmatchedEntries,
285
+ patternHits: [...hits.entries()].map(([id, count]) => ({ id, count })).sort((a, b) => b.count - a.count),
286
+ unmatched: [...groups.values()].sort((a, b) => b.count - a.count),
287
+ };
288
+ }
289
+ export function formatScreenMemoryAscii(stats, meta) {
290
+ const lines = ["── pi-review screen memory " + "─".repeat(14)];
291
+ lines.push(` Log ${meta.file} · ${stats.entries} entr${stats.entries === 1 ? "y" : "ies"} across ${stats.files} file${stats.files === 1 ? "" : "s"}` +
292
+ (meta.catalog.recording ? "" : " (recording off: PI_REVIEW_SCREEN_MEMORY=0)"));
293
+ lines.push(` Catalog ${meta.catalog.builtin} builtin · ${meta.catalog.custom} custom` +
294
+ (meta.catalog.overrides > 0 ? ` (${meta.catalog.overrides} override${meta.catalog.overrides === 1 ? "s" : ""} builtin)` : "") +
295
+ ` · ${meta.catalog.disabled} disabled`);
296
+ if (stats.patternHits.length > 0) {
297
+ lines.push(` Hits ${stats.patternHits.map((h) => `${h.id} ×${h.count}`).join(" ")}`);
298
+ }
299
+ if (stats.unmatched.length > 0) {
300
+ lines.push(` Unmatched ${stats.unmatchedEntries} signal${stats.unmatchedEntries === 1 ? "" : "s"} in ${stats.unmatched.length} hunk${stats.unmatched.length === 1 ? "" : "s"} — promotion candidates:`);
301
+ for (const group of stats.unmatched.slice(0, 10)) {
302
+ const p = group.lastCatchAll !== undefined ? ` p=${group.lastCatchAll.toFixed(2)}` : "";
303
+ lines.push(` ${group.path} ${group.hunk} lines ${group.lastLines[0]}-${group.lastLines[1]}${p} ×${group.count}`);
304
+ if (group.sample)
305
+ lines.push(` ${group.sample}`);
306
+ }
307
+ if (stats.unmatched.length > 10)
308
+ lines.push(` … ${stats.unmatched.length - 10} more`);
309
+ }
310
+ else if (stats.entries === 0) {
311
+ lines.push(" Unmatched none yet — run `pi-review screen` to accumulate signals");
312
+ }
313
+ else {
314
+ lines.push(" Unmatched none — every flagged hunk matched the catalog");
315
+ }
316
+ lines.push(` Promote ${meta.patternsFile} (or PI_REVIEW_SCREEN_PATTERNS)`);
317
+ lines.push("─".repeat(39));
318
+ return lines.join("\n");
319
+ }
320
+ //# sourceMappingURL=screen-memory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"screen-memory.js","sourceRoot":"","sources":["../src/screen-memory.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=screen-memory.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"screen-memory.test.d.ts","sourceRoot":"","sources":["../src/screen-memory.test.ts"],"names":[],"mappings":""}
@@ -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"}