greprag 5.38.0 → 5.40.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,270 @@
1
+ "use strict";
2
+ /** fix-trigger — repair-trigger condition interpreter (CLI MIRROR).
3
+ *
4
+ * MIRROR of packages/core/src/fix-trigger.ts (the canonical source — the SAME
5
+ * module the server validates repair rows with). The CLI ships as a
6
+ * zero-dependency `tsc` artifact — greprag-hook cannot import @greprag/core
7
+ * at runtime (core carries `pg`) — so, exactly like src/secret-scrubber.ts,
8
+ * the pure interpreter is mirrored here. KEEP IN SYNC with core; everything
9
+ * below the Grammar marker is byte-identical, and the sync guard in
10
+ * tests/test-guard-dispatch.cjs fails the build on drift.
11
+ *
12
+ * docs/mechanic-repairs.md D3/D5/D8; contracts: docs/mechanic-engine-contracts.md.
13
+ */
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.FIX_TRIGGER_OPS = void 0;
16
+ exports.normalizePathSegments = normalizePathSegments;
17
+ exports.compileSafeRegex = compileSafeRegex;
18
+ exports.evaluateCondition = evaluateCondition;
19
+ exports.validateCondition = validateCondition;
20
+ /** The closed operator set. */
21
+ exports.FIX_TRIGGER_OPS = [
22
+ 'contains', 'not_contains', 'equals', 'regex',
23
+ 'path_under', 'path_equals', 'starts_with', 'gt', 'lt',
24
+ ];
25
+ const MAX_DEPTH = 16;
26
+ const MAX_REGEX_LENGTH = 200;
27
+ const ALLOWED_REGEX_FLAGS = /^[imsu]*$/;
28
+ /** Heuristic linear-time guard: reject a quantified group that itself contains
29
+ * a quantifier — the (a+)+ / (a*){2,} catastrophic-backtracking shape. Coarse
30
+ * (rejects some safe patterns) but never admits the known-bad shape. */
31
+ const NESTED_QUANTIFIER = /\((?:[^()\\]|\\.)*(?:[*+]|\{\d+(?:,\d*)?\})(?:[^()\\]|\\.)*\)\s*(?:[*+?]|\{\d+(?:,\d*)?\})/;
32
+ // ---------- Helpers ----------------------------------------------------------
33
+ /** Dot-path lookup into tool_input. Absent anywhere → undefined. */
34
+ function lookupField(input, field) {
35
+ let cur = input;
36
+ for (const seg of field.split('.')) {
37
+ if (cur === null || typeof cur !== 'object')
38
+ return undefined;
39
+ cur = cur[seg];
40
+ }
41
+ return cur;
42
+ }
43
+ function asString(v) {
44
+ if (typeof v === 'string')
45
+ return v;
46
+ if (typeof v === 'number' || typeof v === 'boolean')
47
+ return String(v);
48
+ return null;
49
+ }
50
+ /** Normalize a path to lowercase forward-slash segments with '.'/'..' resolved
51
+ * (no fs access — pure string work). Drive letters keep their colon segment. */
52
+ function normalizePathSegments(p) {
53
+ const raw = p.replace(/\\/g, '/').toLowerCase().split('/');
54
+ const out = [];
55
+ for (const seg of raw) {
56
+ if (seg === '' || seg === '.')
57
+ continue;
58
+ if (seg === '..') {
59
+ out.pop();
60
+ continue;
61
+ }
62
+ out.push(seg);
63
+ }
64
+ return out;
65
+ }
66
+ function isAbsoluteLike(p) {
67
+ return /^([a-zA-Z]:[\\/]|[\\/])/.test(p);
68
+ }
69
+ /** path_under: absolute value → normalized segment-prefix test; relative value
70
+ * → its segments appear contiguously anywhere in the path ("under any
71
+ * node_modules dir"). Normalization happens on both sides — no `../` escape. */
72
+ function pathUnder(pathValue, underValue) {
73
+ const pathSegs = normalizePathSegments(pathValue);
74
+ const underSegs = normalizePathSegments(underValue);
75
+ if (underSegs.length === 0)
76
+ return false;
77
+ if (isAbsoluteLike(underValue)) {
78
+ if (underSegs.length > pathSegs.length)
79
+ return false;
80
+ return underSegs.every((s, i) => pathSegs[i] === s);
81
+ }
82
+ outer: for (let i = 0; i + underSegs.length <= pathSegs.length; i++) {
83
+ for (let j = 0; j < underSegs.length; j++) {
84
+ if (pathSegs[i + j] !== underSegs[j])
85
+ continue outer;
86
+ }
87
+ return true;
88
+ }
89
+ return false;
90
+ }
91
+ /** Compile a regex under the safety rails; null when rejected. */
92
+ function compileSafeRegex(pattern, flags, log) {
93
+ if (pattern.length > MAX_REGEX_LENGTH) {
94
+ log?.('fix-trigger: regex rejected (length cap)', { length: pattern.length });
95
+ return null;
96
+ }
97
+ const f = flags ?? '';
98
+ if (!ALLOWED_REGEX_FLAGS.test(f)) {
99
+ log?.('fix-trigger: regex rejected (flags)', { flags: f });
100
+ return null;
101
+ }
102
+ if (NESTED_QUANTIFIER.test(pattern)) {
103
+ log?.('fix-trigger: regex rejected (nested quantifier)', { pattern });
104
+ return null;
105
+ }
106
+ try {
107
+ return new RegExp(pattern, f);
108
+ }
109
+ catch {
110
+ log?.('fix-trigger: regex rejected (syntax)', { pattern });
111
+ return null;
112
+ }
113
+ }
114
+ // ---------- Evaluation -------------------------------------------------------
115
+ function evalClause(clause, input, log) {
116
+ const raw = lookupField(input, clause.field);
117
+ if (raw === undefined || raw === null)
118
+ return false; // absent field = false
119
+ switch (clause.op) {
120
+ case 'contains': {
121
+ const s = asString(raw);
122
+ const v = asString(clause.value);
123
+ return s !== null && v !== null && s.includes(v);
124
+ }
125
+ case 'not_contains': {
126
+ const s = asString(raw);
127
+ const v = asString(clause.value);
128
+ return s !== null && v !== null && !s.includes(v);
129
+ }
130
+ case 'equals': {
131
+ const s = asString(raw);
132
+ const v = asString(clause.value);
133
+ return s !== null && v !== null && s === v;
134
+ }
135
+ case 'starts_with': {
136
+ const s = asString(raw);
137
+ const v = asString(clause.value);
138
+ return s !== null && v !== null && s.startsWith(v);
139
+ }
140
+ case 'regex': {
141
+ const s = asString(raw);
142
+ const v = asString(clause.value);
143
+ if (s === null || v === null)
144
+ return false;
145
+ const re = compileSafeRegex(v, clause.flags, log);
146
+ return re !== null && re.test(s);
147
+ }
148
+ case 'path_under': {
149
+ const s = asString(raw);
150
+ const v = asString(clause.value);
151
+ return s !== null && v !== null && pathUnder(s, v);
152
+ }
153
+ case 'path_equals': {
154
+ const s = asString(raw);
155
+ const v = asString(clause.value);
156
+ if (s === null || v === null)
157
+ return false;
158
+ return normalizePathSegments(s).join('/') === normalizePathSegments(v).join('/');
159
+ }
160
+ case 'gt':
161
+ case 'lt': {
162
+ const n = typeof raw === 'number' ? raw : Number(asString(raw));
163
+ const v = typeof clause.value === 'number' ? clause.value : Number(asString(clause.value));
164
+ if (!Number.isFinite(n) || !Number.isFinite(v))
165
+ return false;
166
+ return clause.op === 'gt' ? n > v : n < v;
167
+ }
168
+ default:
169
+ log?.('fix-trigger: unknown operator (clause = false)', { op: clause.op });
170
+ return false;
171
+ }
172
+ }
173
+ /** Tri-state inner walk: `invalid` means "malformed/over-deep — never fire".
174
+ * Invalid must NOT be inverted by `not` (a broken subtree can't become a
175
+ * reason to fire), so it propagates as invalid through every combinator. */
176
+ function evalNode(condition, toolInput, log, depth) {
177
+ if (condition === true)
178
+ return true;
179
+ if (depth > MAX_DEPTH) {
180
+ log?.('fix-trigger: condition too deep (= false)', { depth });
181
+ return 'invalid';
182
+ }
183
+ if (condition === null || typeof condition !== 'object' || Array.isArray(condition)) {
184
+ log?.('fix-trigger: malformed condition (= false)');
185
+ return 'invalid';
186
+ }
187
+ const c = condition;
188
+ if (Array.isArray(c.all)) {
189
+ if (c.all.length === 0)
190
+ return false;
191
+ let result = true;
192
+ for (const sub of c.all) {
193
+ const r = evalNode(sub, toolInput, log, depth + 1);
194
+ if (r === 'invalid')
195
+ return 'invalid';
196
+ result = result && r;
197
+ }
198
+ return result;
199
+ }
200
+ if (Array.isArray(c.any)) {
201
+ let sawInvalid = false;
202
+ for (const sub of c.any) {
203
+ const r = evalNode(sub, toolInput, log, depth + 1);
204
+ if (r === 'invalid') {
205
+ sawInvalid = true;
206
+ continue;
207
+ }
208
+ if (r)
209
+ return true;
210
+ }
211
+ return sawInvalid ? 'invalid' : false;
212
+ }
213
+ if ('not' in c) {
214
+ const r = evalNode(c.not, toolInput, log, depth + 1);
215
+ return r === 'invalid' ? 'invalid' : !r;
216
+ }
217
+ if (typeof c.field === 'string' && typeof c.op === 'string') {
218
+ return evalClause(c, toolInput, log);
219
+ }
220
+ log?.('fix-trigger: unrecognized condition shape (= false)');
221
+ return 'invalid';
222
+ }
223
+ /** Evaluate a condition against one event's tool_input. Pure, never throws:
224
+ * every malformed shape evaluates false (and logs via the optional logger). */
225
+ function evaluateCondition(condition, toolInput, log, depth = 0) {
226
+ return evalNode(condition, toolInput, log, depth) === true;
227
+ }
228
+ /** Authoring-time validation: is this a well-formed condition the interpreter
229
+ * will honor? Returns the list of problems (empty = valid). Used server-side
230
+ * before a repair row is stored, so a row can't be born unevaluable. */
231
+ function validateCondition(condition, depth = 0) {
232
+ if (condition === true)
233
+ return [];
234
+ if (depth > MAX_DEPTH)
235
+ return [`condition nesting exceeds ${MAX_DEPTH}`];
236
+ if (condition === null || typeof condition !== 'object' || Array.isArray(condition)) {
237
+ return ['condition must be true, a field clause, or all/any/not'];
238
+ }
239
+ const c = condition;
240
+ if (Array.isArray(c.all)) {
241
+ if (c.all.length === 0)
242
+ return ['all: must be non-empty'];
243
+ return c.all.flatMap(sub => validateCondition(sub, depth + 1));
244
+ }
245
+ if (Array.isArray(c.any)) {
246
+ if (c.any.length === 0)
247
+ return ['any: must be non-empty'];
248
+ return c.any.flatMap(sub => validateCondition(sub, depth + 1));
249
+ }
250
+ if ('not' in c)
251
+ return validateCondition(c.not, depth + 1);
252
+ if (typeof c.field !== 'string' || c.field.length === 0)
253
+ return ['clause missing field'];
254
+ if (typeof c.op !== 'string')
255
+ return ['clause missing op'];
256
+ if (!exports.FIX_TRIGGER_OPS.includes(c.op)) {
257
+ return [`unknown op '${c.op}'`];
258
+ }
259
+ if (c.op === 'regex') {
260
+ const v = typeof c.value === 'string' ? c.value : '';
261
+ if (compileSafeRegex(v, typeof c.flags === 'string' ? c.flags : undefined) === null) {
262
+ return [`regex rejected: '${v}'`];
263
+ }
264
+ }
265
+ else if (c.value === undefined) {
266
+ return [`op '${c.op}' requires a value`];
267
+ }
268
+ return [];
269
+ }
270
+ //# sourceMappingURL=fix-trigger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fix-trigger.js","sourceRoot":"","sources":["../src/fix-trigger.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,92 @@
1
+ /** guard — the PreToolUse repair dispatcher (Chip B, docs/mechanic-repairs.md
2
+ * D1/D2/D5/D8; contracts: docs/mechanic-engine-contracts.md).
3
+ *
4
+ * The ad-blocker model (D5): rules live server-side; each session pulls its
5
+ * matchset at SessionStart into a local cache; every PreToolUse check runs
6
+ * locally in microseconds; fire tallies sync back async via a detached child
7
+ * process and NEVER block the tool call. Total guard posture: local-only on
8
+ * the critical path, any error → fail-open (emit nothing, exit 0).
9
+ *
10
+ * Latch journal placement (documented per contract): latches live INSIDE the
11
+ * matchset cache file under the `latchState` key — one file to read on the
12
+ * hot path, and a refetch preserves it (fetchAndCacheMatchset merges the old
13
+ * latchState into the fresh server body).
14
+ *
15
+ * Cadence `cooldown:N` is interpreted as N MINUTES since lastFired (the local
16
+ * journal stores timestamps, not turn counters; the server-side row keeps the
17
+ * authoritative turn-based view).
18
+ */
19
+ export declare function killSwitchPath(): string;
20
+ export declare function matchsetCachePath(projectId: string): string;
21
+ export declare function tallySpoolPath(projectId: string): string;
22
+ interface RepairTrigger {
23
+ event?: string;
24
+ matcher?: string;
25
+ condition?: unknown;
26
+ action?: string;
27
+ mode?: string;
28
+ }
29
+ interface RepairObj {
30
+ verb?: string;
31
+ status?: string;
32
+ trigger?: RepairTrigger;
33
+ cadence?: string;
34
+ resetScope?: string;
35
+ failMode?: string;
36
+ }
37
+ export interface MatchsetRepair {
38
+ id: string;
39
+ nodeId: string;
40
+ text: string;
41
+ scope?: string;
42
+ trust?: string;
43
+ repair: RepairObj;
44
+ }
45
+ export interface MatchsetCacheFile {
46
+ triggersVersion: string;
47
+ repairs: MatchsetRepair[];
48
+ /** When the cache was last written from the server. */
49
+ fetchedAt?: string;
50
+ /** Local latch journal: nodeId → latch key (sessionId or "project") → ISO. */
51
+ latchState?: Record<string, Record<string, string>>;
52
+ }
53
+ export interface GuardInput {
54
+ hook_event_name?: string;
55
+ tool_name?: string;
56
+ tool_input?: Record<string, unknown>;
57
+ session_id?: string;
58
+ cwd?: string;
59
+ }
60
+ export interface GuardOutput {
61
+ hookSpecificOutput: {
62
+ hookEventName: string;
63
+ additionalContext?: string;
64
+ permissionDecision?: string;
65
+ permissionDecisionReason?: string;
66
+ };
67
+ }
68
+ /** D8 transform denylist (sensitive fields) — kept for the local refusal leg.
69
+ * v1 refuses ALL transforms locally regardless; the list documents the rail
70
+ * the server enforces at authoring time. */
71
+ export declare const TRANSFORM_SENSITIVE_FIELDS: string[];
72
+ /** D8 injection envelope — repair text always lands wrapped as a standing note
73
+ * (data, not a user instruction). */
74
+ export declare function wrapStandingNote(text: string): string;
75
+ export declare function readMatchsetCache(projectId: string): MatchsetCacheFile | null;
76
+ /** Run the dispatcher over one PreToolUse event. Pure given the cache contents
77
+ * read from disk; mutates only local state files (cache latchState + spool).
78
+ * Returns the hook output to emit, or null for silence. NO network — tallies
79
+ * flush via a detached child (spawnTallyFlush). */
80
+ export declare function runGuard(input: GuardInput, projectId: string): GuardOutput | null;
81
+ /** Flush every tally spool in the state dir to the server. Runs in the
82
+ * detached child (guard-flush) — hard 5s timeout per POST; failure keeps the
83
+ * spool for the next flush. */
84
+ export declare function flushTallySpools(apiUrl: string, apiKey: string): Promise<void>;
85
+ /** GET the matchset and write the cache, preserving the local latch journal.
86
+ * Hard 3s timeout; any failure leaves the existing cache untouched. */
87
+ export declare function fetchAndCacheMatchset(apiUrl: string, apiKey: string, projectId: string): Promise<string | null>;
88
+ /** UserPromptSubmit freshness leg: when the cache is stale (>2 min since last
89
+ * server contact), spawn a detached `guard-refresh` child that re-fetches and
90
+ * rewrites the cache iff triggersVersion changed. Never blocks the turn. */
91
+ export declare function maybeSpawnGuardRefresh(cwd: string, projectId: string): void;
92
+ export {};