@systemfsoftware/omp-claude-compat 1.1.4 → 1.1.5

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/dist/index.js CHANGED
@@ -1,1493 +1 @@
1
- import { Cause, Config, Context, Effect, Either, Exit, Layer, ManagedRuntime, Match, MutableHashMap, Option, ParseResult, Ref, Schema, Stream } from "effect";
2
- import { CommandExecutor } from "@effect/platform/CommandExecutor";
3
- import { FileSystem } from "@effect/platform/FileSystem";
4
- import * as PathModule from "@effect/platform/Path";
5
- import { Path } from "@effect/platform/Path";
6
- import { Command } from "@effect/platform";
7
- import { homedir } from "node:os";
8
- import { NodeCommandExecutor, NodeFileSystem } from "@effect/platform-node";
9
- //#region ../../../node_modules/.pnpm/@jsr+std__collections@1.3.0/node_modules/@jsr/std__collections/deep_merge.js
10
- /** Default merging options - cached to avoid object allocation on each call */ const DEFAULT_OPTIONS = {
11
- arrays: "merge",
12
- sets: "merge",
13
- maps: "merge"
14
- };
15
- function deepMerge(record, other, options) {
16
- return deepMergeInternal(record, other, /* @__PURE__ */ new Set(), options ?? DEFAULT_OPTIONS);
17
- }
18
- function deepMergeInternal(record, other, seen, options) {
19
- const result = {};
20
- const keys = /* @__PURE__ */ new Set([...getKeys(record), ...getKeys(other)]);
21
- for (const key of keys) {
22
- if (key === "__proto__") continue;
23
- const a = record[key];
24
- if (!Object.hasOwn(other, key)) {
25
- result[key] = a;
26
- continue;
27
- }
28
- const b = other[key];
29
- if (isNonNullObject(a) && isNonNullObject(b) && !seen.has(a) && !seen.has(b)) {
30
- seen.add(a);
31
- seen.add(b);
32
- result[key] = mergeObjects(a, b, seen, options);
33
- continue;
34
- }
35
- result[key] = b;
36
- }
37
- return result;
38
- }
39
- function mergeObjects(left, right, seen, options) {
40
- if (isMergeable(left) && isMergeable(right)) return deepMergeInternal(left, right, seen, options);
41
- if (isIterable(left) && isIterable(right)) {
42
- if (Array.isArray(left) && Array.isArray(right)) {
43
- if (options.arrays === "merge") return left.concat(right);
44
- return right;
45
- }
46
- if (left instanceof Map && right instanceof Map) {
47
- if (options.maps === "merge") {
48
- const result = new Map(left);
49
- for (const [k, v] of right) result.set(k, v);
50
- return result;
51
- }
52
- return right;
53
- }
54
- if (left instanceof Set && right instanceof Set) {
55
- if (options.sets === "merge") {
56
- const result = new Set(left);
57
- for (const v of right) result.add(v);
58
- return result;
59
- }
60
- return right;
61
- }
62
- }
63
- return right;
64
- }
65
- /**
66
- * Test whether a value is mergeable or not
67
- * Builtins that look like objects, null and user defined classes
68
- * are not considered mergeable (it means that reference will be copied)
69
- */ function isMergeable(value) {
70
- return Object.getPrototypeOf(value) === Object.prototype;
71
- }
72
- function isIterable(value) {
73
- return typeof value[Symbol.iterator] === "function";
74
- }
75
- function isNonNullObject(value) {
76
- return value !== null && typeof value === "object";
77
- }
78
- function getKeys(record) {
79
- const keys = Object.keys(record);
80
- const symbols = Object.getOwnPropertySymbols(record);
81
- if (symbols.length === 0) return keys;
82
- for (const sym of symbols) if (Object.prototype.propertyIsEnumerable.call(record, sym)) keys.push(sym);
83
- return keys;
84
- }
85
- //#endregion
86
- //#region ../../../node_modules/.pnpm/@jsr+std__toml@1.0.11/node_modules/@jsr/std__toml/_parser.js
87
- /**
88
- * Copy of `import { isLeap } from "@std/datetime";` because it cannot be impoted as long as it is unstable.
89
- */ function isLeap(yearNumber) {
90
- return yearNumber % 4 === 0 && yearNumber % 100 !== 0 || yearNumber % 400 === 0;
91
- }
92
- var Scanner = class {
93
- #whitespace = /[ \t]/;
94
- #position = 0;
95
- #source;
96
- constructor(source) {
97
- this.#source = source;
98
- }
99
- get position() {
100
- return this.#position;
101
- }
102
- get source() {
103
- return this.#source;
104
- }
105
- /**
106
- * Get current character
107
- * @param index - relative index from current position
108
- */ char(index = 0) {
109
- return this.#source[this.#position + index] ?? "";
110
- }
111
- /**
112
- * Get sliced string
113
- * @param start - start position relative from current position
114
- * @param end - end position relative from current position
115
- */ slice(start, end) {
116
- return this.#source.slice(this.#position + start, this.#position + end);
117
- }
118
- /**
119
- * Move position to next
120
- */ next(count = 1) {
121
- this.#position += count;
122
- }
123
- skipWhitespaces() {
124
- while (this.#whitespace.test(this.char()) && !this.eof()) this.next();
125
- if (!this.isCurrentCharEOL() && /\s/.test(this.char())) {
126
- const escaped = "\\u" + this.char().charCodeAt(0).toString(16);
127
- const position = this.#position;
128
- throw new SyntaxError(`Cannot parse the TOML: It contains invalid whitespace at position '${position}': \`${escaped}\``);
129
- }
130
- }
131
- nextUntilChar(options = { skipComments: true }) {
132
- while (!this.eof()) {
133
- const char = this.char();
134
- if (this.#whitespace.test(char) || this.isCurrentCharEOL()) this.next();
135
- else if (options.skipComments && this.char() === "#") while (!this.isCurrentCharEOL() && !this.eof()) this.next();
136
- else break;
137
- }
138
- }
139
- /**
140
- * Position reached EOF or not
141
- */ eof() {
142
- return this.#position >= this.#source.length;
143
- }
144
- isCurrentCharEOL() {
145
- return this.char() === "\n" || this.startsWith("\r\n");
146
- }
147
- startsWith(searchString) {
148
- return this.#source.startsWith(searchString, this.#position);
149
- }
150
- match(regExp) {
151
- if (!regExp.sticky) throw new Error(`RegExp ${regExp} does not have a sticky 'y' flag`);
152
- regExp.lastIndex = this.#position;
153
- return this.#source.match(regExp);
154
- }
155
- };
156
- function success(body) {
157
- return {
158
- ok: true,
159
- body
160
- };
161
- }
162
- function failure() {
163
- return { ok: false };
164
- }
165
- /**
166
- * Creates a nested object from the keys and values.
167
- *
168
- * e.g. `unflat(["a", "b", "c"], 1)` returns `{ a: { b: { c: 1 } } }`
169
- */ function unflat(keys, values = { __proto__: null }) {
170
- return keys.reduceRight((acc, key) => ({ [key]: acc }), values);
171
- }
172
- function isObject(value) {
173
- return typeof value === "object" && value !== null;
174
- }
175
- function getTargetValue(target, keys) {
176
- const key = keys[0];
177
- if (!key) throw new Error("Cannot parse the TOML: key length is not a positive number");
178
- return target[key];
179
- }
180
- function deepAssignTable(target, table) {
181
- const { keys, type, value } = table;
182
- const currentValue = getTargetValue(target, keys);
183
- if (currentValue === void 0) return Object.assign(target, unflat(keys, value));
184
- if (Array.isArray(currentValue)) {
185
- deepAssign(currentValue.at(-1), {
186
- type,
187
- keys: keys.slice(1),
188
- value
189
- });
190
- return target;
191
- }
192
- if (isObject(currentValue)) {
193
- deepAssign(currentValue, {
194
- type,
195
- keys: keys.slice(1),
196
- value
197
- });
198
- return target;
199
- }
200
- throw new Error("Unexpected assign");
201
- }
202
- function deepAssignTableArray(target, table) {
203
- const { type, keys, value } = table;
204
- const currentValue = getTargetValue(target, keys);
205
- if (currentValue === void 0) return Object.assign(target, unflat(keys, [value]));
206
- if (Array.isArray(currentValue)) {
207
- if (table.keys.length === 1) currentValue.push(value);
208
- else deepAssign(currentValue.at(-1), {
209
- type: table.type,
210
- keys: table.keys.slice(1),
211
- value: table.value
212
- });
213
- return target;
214
- }
215
- if (isObject(currentValue)) {
216
- deepAssign(currentValue, {
217
- type,
218
- keys: keys.slice(1),
219
- value
220
- });
221
- return target;
222
- }
223
- throw new Error("Unexpected assign");
224
- }
225
- function deepAssign(target, body) {
226
- switch (body.type) {
227
- case "Block": return deepMerge(target, body.value);
228
- case "Table": return deepAssignTable(target, body);
229
- case "TableArray": return deepAssignTableArray(target, body);
230
- }
231
- }
232
- function or(parsers) {
233
- return (scanner) => {
234
- for (const parse of parsers) {
235
- const result = parse(scanner);
236
- if (result.ok) return result;
237
- }
238
- return failure();
239
- };
240
- }
241
- /** Join the parse results of the given parser into an array.
242
- *
243
- * If the parser fails at the first attempt, it will return an empty array.
244
- */ function join(parser, separator) {
245
- const Separator = character(separator);
246
- return (scanner) => {
247
- const out = [];
248
- const first = parser(scanner);
249
- if (!first.ok) return success(out);
250
- out.push(first.body);
251
- while (!scanner.eof()) {
252
- if (!Separator(scanner).ok) break;
253
- const result = parser(scanner);
254
- if (!result.ok) throw new SyntaxError(`Invalid token after "${separator}"`);
255
- out.push(result.body);
256
- }
257
- return success(out);
258
- };
259
- }
260
- /** Join the parse results of the given parser into an array.
261
- *
262
- * This requires the parser to succeed at least once.
263
- */ function join1(parser, separator) {
264
- const Separator = character(separator);
265
- return (scanner) => {
266
- const first = parser(scanner);
267
- if (!first.ok) return failure();
268
- const out = [first.body];
269
- while (!scanner.eof()) {
270
- if (!Separator(scanner).ok) break;
271
- const result = parser(scanner);
272
- if (!result.ok) throw new SyntaxError(`Invalid token after "${separator}"`);
273
- out.push(result.body);
274
- }
275
- return success(out);
276
- };
277
- }
278
- function kv(keyParser, separator, valueParser) {
279
- const Separator = character(separator);
280
- return (scanner) => {
281
- const position = scanner.position;
282
- const key = keyParser(scanner);
283
- if (!key.ok) return failure();
284
- if (!Separator(scanner).ok) throw new SyntaxError(`key/value pair doesn't have "${separator}"`);
285
- const value = valueParser(scanner);
286
- if (!value.ok) {
287
- const lineEndIndex = scanner.source.indexOf("\n", scanner.position);
288
- const endPosition = lineEndIndex > 0 ? lineEndIndex : scanner.source.length;
289
- const line = scanner.source.slice(position, endPosition);
290
- throw new SyntaxError(`Cannot parse value on line '${line}'`);
291
- }
292
- return success(unflat(key.body, value.body));
293
- };
294
- }
295
- function merge(parser) {
296
- return (scanner) => {
297
- const result = parser(scanner);
298
- if (!result.ok) return failure();
299
- let body = { __proto__: null };
300
- for (const record of result.body) if (typeof record === "object" && record !== null) body = deepMerge(body, record);
301
- return success(body);
302
- };
303
- }
304
- function repeat(parser) {
305
- return (scanner) => {
306
- const body = [];
307
- while (!scanner.eof()) {
308
- const result = parser(scanner);
309
- if (!result.ok) break;
310
- body.push(result.body);
311
- scanner.nextUntilChar();
312
- }
313
- if (body.length === 0) return failure();
314
- return success(body);
315
- };
316
- }
317
- function surround(left, parser, right) {
318
- const Left = character(left);
319
- const Right = character(right);
320
- return (scanner) => {
321
- if (!Left(scanner).ok) return failure();
322
- const result = parser(scanner);
323
- if (!result.ok) throw new SyntaxError(`Invalid token after "${left}"`);
324
- if (!Right(scanner).ok) throw new SyntaxError(`Not closed by "${right}" after started with "${left}"`);
325
- return success(result.body);
326
- };
327
- }
328
- function character(str) {
329
- return (scanner) => {
330
- scanner.skipWhitespaces();
331
- if (!scanner.startsWith(str)) return failure();
332
- scanner.next(str.length);
333
- scanner.skipWhitespaces();
334
- return success(void 0);
335
- };
336
- }
337
- const BARE_KEY_REGEXP = /[A-Za-z0-9_-]+/y;
338
- function bareKey(scanner) {
339
- scanner.skipWhitespaces();
340
- const key = scanner.match(BARE_KEY_REGEXP)?.[0];
341
- if (!key) return failure();
342
- scanner.next(key.length);
343
- return success(key);
344
- }
345
- function escapeSequence(scanner) {
346
- if (scanner.char() !== "\\") return failure();
347
- scanner.next();
348
- switch (scanner.char()) {
349
- case "b":
350
- scanner.next();
351
- return success("\b");
352
- case "t":
353
- scanner.next();
354
- return success(" ");
355
- case "n":
356
- scanner.next();
357
- return success("\n");
358
- case "f":
359
- scanner.next();
360
- return success("\f");
361
- case "r":
362
- scanner.next();
363
- return success("\r");
364
- case "u":
365
- case "U": {
366
- const codePointLen = scanner.char() === "u" ? 4 : 6;
367
- const codePoint = parseInt("0x" + scanner.slice(1, 1 + codePointLen), 16);
368
- const str = String.fromCodePoint(codePoint);
369
- scanner.next(codePointLen + 1);
370
- return success(str);
371
- }
372
- case "\"":
373
- scanner.next();
374
- return success("\"");
375
- case "\\":
376
- scanner.next();
377
- return success("\\");
378
- default: throw new SyntaxError(`Invalid escape sequence: \\${scanner.char()}`);
379
- }
380
- }
381
- function basicString(scanner) {
382
- scanner.skipWhitespaces();
383
- if (scanner.char() !== "\"") return failure();
384
- scanner.next();
385
- const acc = [];
386
- while (scanner.char() !== "\"" && !scanner.eof()) {
387
- if (scanner.char() === "\n") throw new SyntaxError("Single-line string cannot contain EOL");
388
- const escapedChar = escapeSequence(scanner);
389
- if (escapedChar.ok) acc.push(escapedChar.body);
390
- else {
391
- acc.push(scanner.char());
392
- scanner.next();
393
- }
394
- }
395
- if (scanner.eof()) throw new SyntaxError(`Single-line string is not closed:\n${acc.join("")}`);
396
- scanner.next();
397
- return success(acc.join(""));
398
- }
399
- function literalString(scanner) {
400
- scanner.skipWhitespaces();
401
- if (scanner.char() !== "'") return failure();
402
- scanner.next();
403
- const acc = [];
404
- while (scanner.char() !== "'" && !scanner.eof()) {
405
- if (scanner.char() === "\n") throw new SyntaxError("Single-line string cannot contain EOL");
406
- acc.push(scanner.char());
407
- scanner.next();
408
- }
409
- if (scanner.eof()) throw new SyntaxError(`Single-line string is not closed:\n${acc.join("")}`);
410
- scanner.next();
411
- return success(acc.join(""));
412
- }
413
- function multilineBasicString(scanner) {
414
- scanner.skipWhitespaces();
415
- if (!scanner.startsWith("\"\"\"")) return failure();
416
- scanner.next(3);
417
- if (scanner.char() === "\n") scanner.next();
418
- else if (scanner.startsWith("\r\n")) scanner.next(2);
419
- const acc = [];
420
- while (!scanner.startsWith("\"\"\"") && !scanner.eof()) {
421
- if (scanner.startsWith("\\\n")) {
422
- scanner.next();
423
- scanner.nextUntilChar({ skipComments: false });
424
- continue;
425
- } else if (scanner.startsWith("\\\r\n")) {
426
- scanner.next();
427
- scanner.nextUntilChar({ skipComments: false });
428
- continue;
429
- }
430
- const escapedChar = escapeSequence(scanner);
431
- if (escapedChar.ok) acc.push(escapedChar.body);
432
- else {
433
- acc.push(scanner.char());
434
- scanner.next();
435
- }
436
- }
437
- if (scanner.eof()) throw new SyntaxError(`Multi-line string is not closed:\n${acc.join("")}`);
438
- if (scanner.char(3) === "\"") {
439
- acc.push("\"");
440
- scanner.next();
441
- }
442
- scanner.next(3);
443
- return success(acc.join(""));
444
- }
445
- function multilineLiteralString(scanner) {
446
- scanner.skipWhitespaces();
447
- if (!scanner.startsWith("'''")) return failure();
448
- scanner.next(3);
449
- if (scanner.char() === "\n") scanner.next();
450
- else if (scanner.startsWith("\r\n")) scanner.next(2);
451
- const acc = [];
452
- while (!scanner.startsWith("'''") && !scanner.eof()) {
453
- acc.push(scanner.char());
454
- scanner.next();
455
- }
456
- if (scanner.eof()) throw new SyntaxError(`Multi-line string is not closed:\n${acc.join("")}`);
457
- if (scanner.char(3) === "'") {
458
- acc.push("'");
459
- scanner.next();
460
- }
461
- scanner.next(3);
462
- return success(acc.join(""));
463
- }
464
- const BOOLEAN_REGEXP = /(?:true|false)\b/y;
465
- function boolean(scanner) {
466
- scanner.skipWhitespaces();
467
- const match = scanner.match(BOOLEAN_REGEXP);
468
- if (!match) return failure();
469
- const string = match[0];
470
- scanner.next(string.length);
471
- return success(string === "true");
472
- }
473
- const INFINITY_MAP = /* @__PURE__ */ new Map([
474
- ["inf", Infinity],
475
- ["+inf", Infinity],
476
- ["-inf", -Infinity]
477
- ]);
478
- const INFINITY_REGEXP = /[+-]?inf\b/y;
479
- function infinity(scanner) {
480
- scanner.skipWhitespaces();
481
- const match = scanner.match(INFINITY_REGEXP);
482
- if (!match) return failure();
483
- const string = match[0];
484
- scanner.next(string.length);
485
- return success(INFINITY_MAP.get(string));
486
- }
487
- const NAN_REGEXP = /[+-]?nan\b/y;
488
- function nan(scanner) {
489
- scanner.skipWhitespaces();
490
- const match = scanner.match(NAN_REGEXP);
491
- if (!match) return failure();
492
- const string = match[0];
493
- scanner.next(string.length);
494
- return success(NaN);
495
- }
496
- const dottedKey = join1(or([
497
- bareKey,
498
- basicString,
499
- literalString
500
- ]), ".");
501
- const BINARY_REGEXP = /0b[01]+(?:_[01]+)*\b/y;
502
- function binary(scanner) {
503
- scanner.skipWhitespaces();
504
- const match = scanner.match(BINARY_REGEXP)?.[0];
505
- if (!match) return failure();
506
- scanner.next(match.length);
507
- const value = match.slice(2).replaceAll("_", "");
508
- const number = parseInt(value, 2);
509
- return isNaN(number) ? failure() : success(number);
510
- }
511
- const OCTAL_REGEXP = /0o[0-7]+(?:_[0-7]+)*\b/y;
512
- function octal(scanner) {
513
- scanner.skipWhitespaces();
514
- const match = scanner.match(OCTAL_REGEXP)?.[0];
515
- if (!match) return failure();
516
- scanner.next(match.length);
517
- const value = match.slice(2).replaceAll("_", "");
518
- const number = parseInt(value, 8);
519
- return isNaN(number) ? failure() : success(number);
520
- }
521
- const HEX_REGEXP = /0x[0-9a-f]+(?:_[0-9a-f]+)*\b/iy;
522
- function hex(scanner) {
523
- scanner.skipWhitespaces();
524
- const match = scanner.match(HEX_REGEXP)?.[0];
525
- if (!match) return failure();
526
- scanner.next(match.length);
527
- const value = match.slice(2).replaceAll("_", "");
528
- const number = parseInt(value, 16);
529
- return isNaN(number) ? failure() : success(number);
530
- }
531
- const INTEGER_REGEXP = /[+-]?(?:0|[1-9][0-9]*(?:_[0-9]+)*)\b/y;
532
- function integer(scanner) {
533
- scanner.skipWhitespaces();
534
- const match = scanner.match(INTEGER_REGEXP)?.[0];
535
- if (!match) return failure();
536
- scanner.next(match.length);
537
- const value = match.replaceAll("_", "");
538
- return success(parseInt(value, 10));
539
- }
540
- const FLOAT_REGEXP = /[+-]?(?:0|[1-9][0-9]*(?:_[0-9]+)*)(?:\.[0-9]+(?:_[0-9]+)*)?(?:e[+-]?[0-9]+(?:_[0-9]+)*)?\b/iy;
541
- function float(scanner) {
542
- scanner.skipWhitespaces();
543
- const match = scanner.match(FLOAT_REGEXP)?.[0];
544
- if (!match) return failure();
545
- scanner.next(match.length);
546
- const value = match.replaceAll("_", "");
547
- const float = parseFloat(value);
548
- if (isNaN(float)) return failure();
549
- return success(float);
550
- }
551
- const DATE_TIME_REGEXP = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})(?:[ 0-9TZ.:+-]+)?\b/y;
552
- function dateTime(scanner) {
553
- scanner.skipWhitespaces();
554
- const match = scanner.match(DATE_TIME_REGEXP);
555
- if (!match) return failure();
556
- const string = match[0];
557
- scanner.next(string.length);
558
- const groups = match.groups;
559
- if (groups.month == "02") {
560
- const days = parseInt(groups.day);
561
- if (days > 29) throw new SyntaxError(`Invalid date string "${match}"`);
562
- const year = parseInt(groups.year);
563
- if (days > 28 && !isLeap(year)) throw new SyntaxError(`Invalid date string "${match}"`);
564
- }
565
- const date = new Date(string.trim());
566
- if (isNaN(date.getTime())) throw new SyntaxError(`Invalid date string "${match}"`);
567
- return success(date);
568
- }
569
- const LOCAL_TIME_REGEXP = /(\d{2}):(\d{2}):(\d{2})(?:\.[0-9]+)?\b/y;
570
- function localTime(scanner) {
571
- scanner.skipWhitespaces();
572
- const match = scanner.match(LOCAL_TIME_REGEXP)?.[0];
573
- if (!match) return failure();
574
- scanner.next(match.length);
575
- return success(match);
576
- }
577
- function arrayValue(scanner) {
578
- scanner.skipWhitespaces();
579
- if (scanner.char() !== "[") return failure();
580
- scanner.next();
581
- const array = [];
582
- while (!scanner.eof()) {
583
- scanner.nextUntilChar();
584
- const result = value(scanner);
585
- if (!result.ok) break;
586
- array.push(result.body);
587
- scanner.skipWhitespaces();
588
- if (scanner.char() !== ",") break;
589
- scanner.next();
590
- }
591
- scanner.nextUntilChar();
592
- if (scanner.char() !== "]") throw new SyntaxError("Array is not closed");
593
- scanner.next();
594
- return success(array);
595
- }
596
- function inlineTable(scanner) {
597
- scanner.nextUntilChar();
598
- if (scanner.char(1) === "}") {
599
- scanner.next(2);
600
- return success({ __proto__: null });
601
- }
602
- const pairs = surround("{", join(pair, ","), "}")(scanner);
603
- if (!pairs.ok) return failure();
604
- let table = { __proto__: null };
605
- for (const pair of pairs.body) table = deepMerge(table, pair);
606
- return success(table);
607
- }
608
- const value = or([
609
- multilineBasicString,
610
- multilineLiteralString,
611
- basicString,
612
- literalString,
613
- boolean,
614
- infinity,
615
- nan,
616
- dateTime,
617
- localTime,
618
- binary,
619
- octal,
620
- hex,
621
- float,
622
- integer,
623
- arrayValue,
624
- inlineTable
625
- ]);
626
- const pair = kv(dottedKey, "=", value);
627
- function block(scanner) {
628
- scanner.nextUntilChar();
629
- const result = merge(repeat(pair))(scanner);
630
- if (result.ok) return success({
631
- type: "Block",
632
- value: result.body
633
- });
634
- return failure();
635
- }
636
- const tableHeader = surround("[", dottedKey, "]");
637
- function table(scanner) {
638
- scanner.nextUntilChar();
639
- const header = tableHeader(scanner);
640
- if (!header.ok) return failure();
641
- scanner.nextUntilChar();
642
- const b = block(scanner);
643
- return success({
644
- type: "Table",
645
- keys: header.body,
646
- value: b.ok ? b.body.value : { __proto__: null }
647
- });
648
- }
649
- const tableArrayHeader = surround("[[", dottedKey, "]]");
650
- function tableArray(scanner) {
651
- scanner.nextUntilChar();
652
- const header = tableArrayHeader(scanner);
653
- if (!header.ok) return failure();
654
- scanner.nextUntilChar();
655
- const b = block(scanner);
656
- return success({
657
- type: "TableArray",
658
- keys: header.body,
659
- value: b.ok ? b.body.value : { __proto__: null }
660
- });
661
- }
662
- function toml(scanner) {
663
- const blocks = repeat(or([
664
- block,
665
- tableArray,
666
- table
667
- ]))(scanner);
668
- if (!blocks.ok) return success({ __proto__: null });
669
- return success(blocks.body.reduce(deepAssign, { __proto__: null }));
670
- }
671
- function createParseErrorMessage(scanner, message) {
672
- const lines = scanner.source.slice(0, scanner.position).split("\n");
673
- return `Parse error on line ${lines.length}, column ${lines.at(-1)?.length ?? 0}: ${message}`;
674
- }
675
- function parserFactory(parser) {
676
- return (tomlString) => {
677
- const scanner = new Scanner(tomlString);
678
- try {
679
- const result = parser(scanner);
680
- if (result.ok && scanner.eof()) return result.body;
681
- const message = `Unexpected character: "${scanner.char()}"`;
682
- throw new SyntaxError(createParseErrorMessage(scanner, message));
683
- } catch (error) {
684
- if (error instanceof Error) throw new SyntaxError(createParseErrorMessage(scanner, error.message));
685
- throw new SyntaxError(createParseErrorMessage(scanner, "Invalid error type caught"));
686
- }
687
- };
688
- }
689
- //#endregion
690
- //#region ../../../node_modules/.pnpm/@jsr+std__toml@1.0.11/node_modules/@jsr/std__toml/parse.js
691
- /**
692
- * Parses a {@link https://toml.io | TOML} string into an object.
693
- *
694
- * @example Usage
695
- * ```ts
696
- * import { parse } from "@std/toml/parse";
697
- * import { assertEquals } from "@std/assert";
698
- *
699
- * const tomlString = `title = "TOML Example"
700
- * [owner]
701
- * name = "Alice"
702
- * bio = "Alice is a programmer."`;
703
- *
704
- * const obj = parse(tomlString);
705
- * assertEquals(obj, { title: "TOML Example", owner: { name: "Alice", bio: "Alice is a programmer." } });
706
- * ```
707
- * @param tomlString TOML string to be parsed.
708
- * @returns The parsed JS object.
709
- */ function parse(tomlString) {
710
- return parserFactory(toml)(tomlString);
711
- }
712
- //#endregion
713
- //#region ../../packages/omp-utils/dist/index.js
714
- /**
715
- * ACL: detect and extract shell commands from context-mode tool invocations.
716
- *
717
- * Context-mode tools (ctx_execute, ctx_batch_execute) can execute shell
718
- * commands that should be inspected by shell-guard hooks.
719
- */
720
- const CONTEXT_MODE_SHELL_TOOLS = {
721
- ctx_execute: true,
722
- ctx_batch_execute: true
723
- };
724
- function isContextModeShellTool(toolName, input) {
725
- if (!CONTEXT_MODE_SHELL_TOOLS[toolName]) return false;
726
- if (toolName === "ctx_execute") return input["language"] === "shell";
727
- return true;
728
- }
729
- function extractShellCommand(toolName, input) {
730
- if (!isContextModeShellTool(toolName, input)) return void 0;
731
- if (toolName === "ctx_execute") return typeof input["code"] === "string" ? input["code"] : void 0;
732
- if (Array.isArray(input["commands"])) return input["commands"].map((entry) => typeof entry === "object" && entry !== null ? entry["command"] : "").filter((cmd) => typeof cmd === "string").join("\n");
733
- }
734
- /**
735
- * ACL: match OMP tool names against Claude Code hook matcher patterns.
736
- *
737
- * Hook matchers are pipe-separated regex patterns (e.g. "Write|Edit").
738
- * An empty/undefined matcher matches everything.
739
- */
740
- const regexCache = /* @__PURE__ */ new Map();
741
- function matchesMatcher(toolName, matcher) {
742
- if (!matcher || matcher.length === 0) return true;
743
- const pattern = matcher.split("|").map((part) => part.trim()).filter(Boolean).join("|");
744
- if (pattern.length === 0) return true;
745
- const cached = regexCache.get(pattern);
746
- if (cached !== void 0) return cached.test(toolName);
747
- const regex = new RegExp(`^(?:${pattern})$`);
748
- regexCache.set(pattern, regex);
749
- return regex.test(toolName);
750
- }
751
- function sessionIds(getSessionId) {
752
- return {
753
- session_id: getSessionId(),
754
- agent_id: null
755
- };
756
- }
757
- /**
758
- * Create a telemetry emitter for a plugin.
759
- *
760
- * @param plugin - The event prefix (e.g. `claude_compat`, `agent_discipline`).
761
- * @param logger - The `pi.logger` instance injected by the host at factory time.
762
- */
763
- function createTelemetry(plugin, logger) {
764
- return (eventName, fields) => {
765
- try {
766
- logger.info(eventName, {
767
- plugin,
768
- event: eventName,
769
- ...fields
770
- });
771
- } catch {}
772
- };
773
- }
774
- /**
775
- * Schema: TOML config — the inner vocabulary of `systemfsoftware.toml`.
776
- *
777
- * Shape: a record of string key to string array. The only consumer of the inner
778
- * keys is the agent-discipline plugin (it reads `no_delegate_skills`); other
779
- * plugins may add their own keys without this schema changing. Branding makes
780
- * a parsed config distinguishable from a raw record and surfaces shape errors
781
- * through the loader's parse path.
782
- *
783
- * Pure declaration. No behavior, no `@std/toml`, no I/O. The ACL owns the
784
- * boundary crossing; the executor owns the I/O.
785
- */
786
- const TomlConfig = Schema.Record({
787
- key: Schema.String,
788
- value: Schema.Array(Schema.String)
789
- }).pipe(Schema.brand("TomlConfig"));
790
- /**
791
- * ACL: TOML text → `TomlConfig`.
792
- *
793
- * The only file in this package that imports `@std/toml`. Anything that
794
- * decodes from outside the domain types routes through here so the foreign
795
- * parser is contained at one boundary.
796
- *
797
- * `Schema.transformOrFail` with `strict: true` makes the decode go through
798
- * Schema's identity contract — branding is earned by `ParseResult.decode`,
799
- * never by a cast. Encode is `Forbidden` because the TOML format is the
800
- * source, not the destination. Constitutes ACL1 (see `omp/AGENTS.md`).
801
- */
802
- const TomlConfigFromText = Schema.transformOrFail(Schema.String, Schema.typeSchema(TomlConfig), {
803
- strict: true,
804
- decode: (raw) => ParseResult.try({
805
- try: () => parse(raw),
806
- catch: (e) => new ParseResult.Unexpected(e, "TOML parse error")
807
- }).pipe(ParseResult.flatMap((parsed) => ParseResult.decodeUnknown(TomlConfig)(parsed))),
808
- encode: (_, _d, ast) => ParseResult.fail(new ParseResult.Forbidden(ast, _, "TomlConfigFromText is decode-only"))
809
- });
810
- const CONFIG_FILE = "systemfsoftware.toml";
811
- const EMPTY_CONFIG = Schema.decodeSync(TomlConfig)({});
812
- var TomlLoader = class extends Context.Tag("TomlLoader")() {};
813
- Layer.effect(TomlLoader, Effect.gen(function* () {
814
- const cache = yield* Ref.make(MutableHashMap.empty());
815
- const fs = yield* FileSystem;
816
- const path = yield* Path;
817
- return TomlLoader.of({ load: Effect.fn("TomlLoader.load")(function* (cwd) {
818
- const cached = yield* Ref.get(cache);
819
- const existing = MutableHashMap.get(cached, cwd);
820
- if (Option.isSome(existing)) return existing.value;
821
- const configPath = path.join(cwd, CONFIG_FILE);
822
- if (!(yield* fs.exists(configPath))) {
823
- yield* Ref.update(cache, (m) => (MutableHashMap.set(m, cwd, EMPTY_CONFIG), m));
824
- return EMPTY_CONFIG;
825
- }
826
- const result = yield* fs.readFileString(configPath).pipe(Effect.flatMap(Schema.decodeUnknown(TomlConfigFromText)), Effect.tapError((error) => Effect.logWarning(`[toml-loader] malformed ${CONFIG_FILE} at ${configPath} — failing open (no config)`, error)), Effect.catchAll(() => Effect.succeed(EMPTY_CONFIG)));
827
- yield* Ref.update(cache, (m) => (MutableHashMap.set(m, cwd, result), m));
828
- return result;
829
- }) });
830
- }));
831
- /**
832
- * ACL: translate OMP tool input shapes to Claude Code hook input shapes.
833
- *
834
- * OMP sends `edits: [{ old_text, new_text }]` and `path`;
835
- * Claude Code hooks expect `old_string`/`new_string` and `file_path`.
836
- */
837
- const FILE_TOOLS = {
838
- Write: true,
839
- Edit: true,
840
- Read: true,
841
- MultiEdit: true,
842
- Update: true,
843
- Create: true
844
- };
845
- const EDIT_TOOLS = {
846
- Edit: true,
847
- MultiEdit: true,
848
- Update: true
849
- };
850
- function isOmpEditArray(value) {
851
- return Array.isArray(value) && value.length > 0 && value.every((entry) => typeof entry === "object" && entry !== null && ("new_text" in entry || "old_text" in entry));
852
- }
853
- function normalizeToolInput(toolName, input) {
854
- let out = input;
855
- if (FILE_TOOLS[toolName] === true && "path" in out && !("file_path" in out)) {
856
- const { path, ...rest } = out;
857
- out = {
858
- file_path: path,
859
- ...rest
860
- };
861
- }
862
- if (EDIT_TOOLS[toolName] === true && "edits" in out && isOmpEditArray(out["edits"]) && !("new_string" in out)) {
863
- const claudeEdits = out["edits"].map((entry) => ({
864
- old_string: typeof entry["old_text"] === "string" ? entry["old_text"] : "",
865
- new_string: typeof entry["new_text"] === "string" ? entry["new_text"] : ""
866
- }));
867
- out = {
868
- ...out,
869
- edits: claudeEdits,
870
- old_string: claudeEdits.map((entry) => entry["old_string"]).join("\n"),
871
- new_string: claudeEdits.map((entry) => entry["new_string"]).join("\n")
872
- };
873
- }
874
- return out;
875
- }
876
- /**
877
- * ACL: normalize OMP tool names to Claude Code convention.
878
- *
879
- * OMP emits lowercase tool names (write, bash, read);
880
- * Claude Code hook system expects capitalized (Write, Bash, Read).
881
- */
882
- function normalizeToolName(name) {
883
- if (name.length === 0) return name;
884
- return name.charAt(0).toUpperCase() + name.slice(1);
885
- }
886
- Schema.Struct({
887
- code: Schema.Number,
888
- stdout: Schema.String,
889
- stderr: Schema.String
890
- });
891
- var Block = class extends Schema.TaggedClass()("Block", { reason: Schema.String }) {};
892
- var Allow = class extends Schema.TaggedClass()("Allow", {}) {};
893
- var Warning = class extends Schema.TaggedClass()("Warning", { message: Schema.String }) {};
894
- Schema.Union(Block, Allow, Warning);
895
- var Blocked = class extends Schema.TaggedClass()("Blocked", { reason: Schema.String }) {};
896
- var Continue = class extends Schema.TaggedClass()("Continue", {
897
- warning: Schema.optional(Schema.String),
898
- updatedInput: Schema.optional(Schema.Record({
899
- key: Schema.String,
900
- value: Schema.Unknown
901
- }))
902
- }) {};
903
- Schema.Union(Blocked, Continue);
904
- //#endregion
905
- //#region src/hook-output.acl.ts
906
- const ParsedHookOutputSchema = Schema.Struct({
907
- decision: Schema.optional(Schema.String),
908
- reason: Schema.optional(Schema.String),
909
- hookSpecificOutput: Schema.optional(Schema.Struct({
910
- permissionDecision: Schema.optional(Schema.String),
911
- permissionDecisionReason: Schema.optional(Schema.String),
912
- updatedInput: Schema.optional(Schema.Record({
913
- key: Schema.String,
914
- value: Schema.Unknown
915
- }))
916
- }))
917
- });
918
- const parseHookOutput = Schema.decodeUnknownEither(Schema.parseJson(ParsedHookOutputSchema));
919
- //#endregion
920
- //#region src/hook-settings.acl.ts
921
- const HookCommand = Schema.Struct({
922
- type: Schema.Literal("command"),
923
- command: Schema.String,
924
- async: Schema.optional(Schema.Boolean),
925
- timeout: Schema.optional(Schema.Number)
926
- });
927
- const HookEntry = Schema.Struct({
928
- matcher: Schema.optional(Schema.String),
929
- hooks: Schema.Array(HookCommand)
930
- });
931
- const HookGroups = Schema.Struct({
932
- PreToolUse: Schema.optionalWith(Schema.Array(HookEntry), {
933
- exact: true,
934
- default: () => []
935
- }),
936
- PostToolUse: Schema.optionalWith(Schema.Array(HookEntry), {
937
- exact: true,
938
- default: () => []
939
- }),
940
- UserPromptSubmit: Schema.optionalWith(Schema.Array(HookEntry), {
941
- exact: true,
942
- default: () => []
943
- }),
944
- Stop: Schema.optionalWith(Schema.Array(HookEntry), {
945
- exact: true,
946
- default: () => []
947
- }),
948
- SessionStart: Schema.optionalWith(Schema.Array(HookEntry), {
949
- exact: true,
950
- default: () => []
951
- }),
952
- SessionEnd: Schema.optionalWith(Schema.Array(HookEntry), {
953
- exact: true,
954
- default: () => []
955
- })
956
- });
957
- const SettingsWrapped = Schema.Struct({
958
- hooks: HookGroups,
959
- disableAllHooks: Schema.optional(Schema.Boolean)
960
- });
961
- const SettingsFlat = Schema.Struct({
962
- ...HookGroups.fields,
963
- disableAllHooks: Schema.optional(Schema.Boolean)
964
- });
965
- const SettingsJSON = Schema.Union(SettingsWrapped, SettingsFlat);
966
- const decodeSettings = Schema.decodeUnknownEither(SettingsJSON);
967
- function parseSettings(json) {
968
- return Either.map(decodeSettings(json), (s) => {
969
- if ("hooks" in s) return s;
970
- const { disableAllHooks: d, ...hookGroups } = s;
971
- return {
972
- hooks: hookGroups,
973
- disableAllHooks: d
974
- };
975
- });
976
- }
977
- const ALL_HOOK_EVENTS = [
978
- "PreToolUse",
979
- "PostToolUse",
980
- "UserPromptSubmit",
981
- "SessionStart",
982
- "SessionEnd",
983
- "Stop"
984
- ];
985
- function mergeSettings(settings) {
986
- const merged = { hooks: {
987
- PreToolUse: [],
988
- PostToolUse: [],
989
- UserPromptSubmit: [],
990
- SessionStart: [],
991
- SessionEnd: [],
992
- Stop: []
993
- } };
994
- for (const s of settings) {
995
- for (const event of ALL_HOOK_EVENTS) merged.hooks[event] = (merged.hooks[event] ?? []).concat(Array.from(s.hooks[event]));
996
- if (s.disableAllHooks !== void 0) merged.disableAllHooks = s.disableAllHooks;
997
- }
998
- return merged;
999
- }
1000
- function isHooksDisabled(settings) {
1001
- return settings.disableAllHooks === true;
1002
- }
1003
- //#endregion
1004
- //#region src/hook-verdict.workflow.ts
1005
- var HookVerdictError = class extends Schema.TaggedError()("HookVerdictError", { raw: Schema.String }) {};
1006
- var ExitBlock = class extends Schema.TaggedClass()("ExitBlock", {}) {};
1007
- var ExitParse = class extends Schema.TaggedClass()("ExitParse", {}) {};
1008
- var ExitOther = class extends Schema.TaggedClass()("ExitOther", {}) {};
1009
- Schema.Union(ExitBlock, ExitParse, ExitOther);
1010
- const classifyExit = (code) => Match.value(code).pipe(Match.when(2, () => new ExitBlock({})), Match.when(0, () => new ExitParse({})), Match.orElse(() => new ExitOther({})));
1011
- const blockReason = (stderr, event) => Match.value(stderr.trim()).pipe(Match.when("", () => `Blocked by ${event} hook`), Match.orElse((trimmed) => trimmed));
1012
- const decideFromParsed = (parsed, event) => Match.value(parsed.hookSpecificOutput?.permissionDecision ?? parsed.decision).pipe(Match.when("deny", () => new Block({ reason: parsed.hookSpecificOutput?.permissionDecisionReason ?? `Blocked by ${event} hook` })), Match.when("block", () => new Block({ reason: parsed.reason ?? `Blocked by ${event} hook` })), Match.orElse(() => new Allow({})));
1013
- const decideFromNonStandardExit = (stderr) => Match.value(stderr.trim()).pipe(Match.when("", () => new Allow({})), Match.orElse((message) => new Warning({ message })));
1014
- const interpretHookResult = (result, event) => Match.value(classifyExit(result.code)).pipe(Match.tag("ExitBlock", () => Either.right(new Block({ reason: blockReason(result.stderr, event) }))), Match.tag("ExitParse", () => Either.match(parseHookOutput(result.stdout), {
1015
- onLeft: () => Either.left(new HookVerdictError({ raw: result.stdout })),
1016
- onRight: (parsed) => Either.right(decideFromParsed(parsed, event))
1017
- })), Match.tag("ExitOther", () => Either.right(decideFromNonStandardExit(result.stderr))), Match.exhaustive);
1018
- //#endregion
1019
- //#region src/hook-dispatcher.executor.ts
1020
- var HookDispatcherExecutorDeps = class extends Context.Tag("HookDispatcherExecutorDeps")() {};
1021
- function resolveCommandPath(command, cwd) {
1022
- const trimmed = command.replace(/"\$OMP_PROJECT_DIR"|'\$OMP_PROJECT_DIR'/g, JSON.stringify(cwd)).replace(/"\$\{OMP_PROJECT_DIR\}"|'\$\{OMP_PROJECT_DIR\}'/g, JSON.stringify(cwd)).replace(/"\$CLAUDE_PROJECT_DIR"|'\$CLAUDE_PROJECT_DIR'/g, JSON.stringify(cwd)).replace(/"\$\{CLAUDE_PROJECT_DIR\}"|'\$\{CLAUDE_PROJECT_DIR\}'/g, JSON.stringify(cwd)).replace(/\$OMP_PROJECT_DIR|\$\{OMP_PROJECT_DIR\}/g, JSON.stringify(cwd)).replace(/\$CLAUDE_PROJECT_DIR|\$\{CLAUDE_PROJECT_DIR\}/g, JSON.stringify(cwd)).trim();
1023
- return {
1024
- cmd: "sh",
1025
- args: ["-c", trimmed.startsWith("\"") && trimmed.endsWith("\"") ? trimmed.slice(1, -1) : trimmed]
1026
- };
1027
- }
1028
- function hookNameFromCommand(command) {
1029
- return command.split(/[\\/]/).pop() ?? command;
1030
- }
1031
- const loadSettingsFile = Effect.fn("loadSettingsFile")(function* (path) {
1032
- const content = yield* (yield* FileSystem).readFileString(path).pipe(Effect.catchAll(() => Effect.succeed("")));
1033
- if (content === "") return null;
1034
- const jsonOrError = Schema.decodeUnknownEither(Schema.parseJson(Schema.Record({
1035
- key: Schema.String,
1036
- value: Schema.Unknown
1037
- })))(content);
1038
- if (Either.isLeft(jsonOrError)) return null;
1039
- const json = jsonOrError.right;
1040
- const either = parseSettings(json);
1041
- return Either.isLeft(either) ? null : either.right;
1042
- });
1043
- const loadSettings = Effect.fn("loadSettings")(function* (cwd) {
1044
- const paths = [
1045
- `${homedir()}/.claude/settings.json`,
1046
- `${cwd}/.claude/settings.json`,
1047
- `${cwd}/.claude/settings.local.json`,
1048
- "/etc/claude-code/managed-settings.json"
1049
- ];
1050
- return yield* loadSettingsWithPaths(paths);
1051
- });
1052
- const loadSettingsWithPaths = Effect.fn("loadSettingsWithPaths")(function* (paths) {
1053
- const results = [];
1054
- for (const p of paths) {
1055
- const s = yield* loadSettingsFile(p);
1056
- if (s !== null) results.push(s);
1057
- }
1058
- if (results.length === 0) return null;
1059
- return mergeSettings(results);
1060
- });
1061
- const runHookScript = Effect.fn("runHookScript")(function* (command, input, cwd, timeoutMs) {
1062
- const executor = yield* CommandExecutor;
1063
- const { cmd, args } = resolveCommandPath(command, cwd);
1064
- const stdinText = JSON.stringify(input);
1065
- const hookCommand = Command.make(cmd, ...args).pipe(Command.workingDirectory(cwd), Command.env({
1066
- OMP_PROJECT_DIR: cwd,
1067
- CLAUDE_PROJECT_DIR: cwd
1068
- }), Command.feed(stdinText), Command.stdout("pipe"), Command.stderr("pipe"));
1069
- return yield* Effect.scoped(Effect.gen(function* () {
1070
- const process = yield* executor.start(hookCommand);
1071
- const stdout = yield* process.stdout.pipe(Stream.decodeText(), Stream.mkString);
1072
- const stderr = yield* process.stderr.pipe(Stream.decodeText(), Stream.mkString);
1073
- const exitCode = yield* process.exitCode;
1074
- return {
1075
- code: typeof exitCode === "number" ? exitCode : Number(exitCode),
1076
- stdout,
1077
- stderr
1078
- };
1079
- })).pipe(Effect.timeout(timeoutMs), Effect.catchTag("TimeoutException", () => Effect.succeed({
1080
- code: -1,
1081
- stdout: "",
1082
- stderr: `timeout after ${timeoutMs}ms`
1083
- })));
1084
- });
1085
- const runHooksForEvent = Effect.fn("runHooksForEvent")(function* (entries, matchValue, input, ctx, event) {
1086
- const cwd = ctx.cwd;
1087
- const { tel } = yield* HookDispatcherExecutorDeps;
1088
- let warning;
1089
- let inputModified = false;
1090
- let currentInput = input;
1091
- for (const entry of entries) {
1092
- if (!matchesMatcher(matchValue, entry.matcher)) continue;
1093
- for (const hook of entry.hooks) {
1094
- const hookName = hookNameFromCommand(hook.command);
1095
- const hookStart = performance.now();
1096
- const timeoutMs = (hook.timeout ?? 10) * 1e3;
1097
- if (hook.async) {
1098
- yield* Effect.forkDaemon(runHookScript(hook.command, currentInput, cwd, timeoutMs).pipe(Effect.tap((result) => Effect.sync(() => {
1099
- const durationMs = Math.round(performance.now() - hookStart);
1100
- tel("hook.executed", {
1101
- hook: hookName,
1102
- duration_ms: durationMs,
1103
- exit_code: result.code
1104
- });
1105
- })), Effect.catchAll(() => Effect.sync(() => {
1106
- const durationMs = Math.round(performance.now() - hookStart);
1107
- tel("hook.executed", {
1108
- hook: hookName,
1109
- duration_ms: durationMs,
1110
- exit_code: null
1111
- });
1112
- }))));
1113
- continue;
1114
- }
1115
- const result = yield* runHookScript(hook.command, currentInput, cwd, timeoutMs).pipe(Effect.tap((r) => Effect.sync(() => {
1116
- const durationMs = Math.round(performance.now() - hookStart);
1117
- tel("hook.executed", {
1118
- hook: hookName,
1119
- duration_ms: durationMs,
1120
- exit_code: r.code
1121
- });
1122
- })), Effect.tapError(() => Effect.sync(() => {
1123
- const durationMs = Math.round(performance.now() - hookStart);
1124
- tel("hook.executed", {
1125
- hook: hookName,
1126
- duration_ms: durationMs,
1127
- exit_code: null
1128
- });
1129
- })));
1130
- const verdict = interpretHookResult(result, event);
1131
- const decision = Either.match(verdict, {
1132
- onLeft: (err) => Match.value(err).pipe(Match.tag("HookVerdictError", (e) => new Warning({ message: `Hook exited 0 but produced invalid JSON: ${e.raw.slice(0, 200)}` })), Match.exhaustive),
1133
- onRight: (d) => d
1134
- });
1135
- const outcome = Match.value(decision).pipe(Match.tag("Block", (d) => new Blocked({ reason: d.reason })), Match.tag("Warning", (d) => new Continue({ warning: d.message })), Match.tag("Allow", () => {
1136
- const either = parseHookOutput(result.stdout);
1137
- return new Continue({ updatedInput: Either.isRight(either) ? either.right.hookSpecificOutput?.updatedInput : void 0 });
1138
- }), Match.exhaustive);
1139
- const hookExit = Match.value(outcome).pipe(Match.tag("Blocked", (b) => Option.some({
1140
- block: true,
1141
- reason: b.reason
1142
- })), Match.tag("Continue", (c) => {
1143
- if (c.warning !== void 0 && warning === void 0) warning = c.warning;
1144
- if (c.updatedInput !== void 0) {
1145
- currentInput = {
1146
- ...currentInput,
1147
- ...c.updatedInput
1148
- };
1149
- inputModified = true;
1150
- }
1151
- return Option.none();
1152
- }), Match.exhaustive);
1153
- if (Option.isSome(hookExit)) return hookExit.value;
1154
- }
1155
- }
1156
- return {
1157
- ...inputModified ? { updatedInput: currentInput } : {},
1158
- ...warning !== void 0 ? { warning } : {}
1159
- };
1160
- });
1161
- const runPreToolUseHooks = Effect.fn("runPreToolUseHooks")(function* (settings, event, ctx) {
1162
- const { tel } = yield* HookDispatcherExecutorDeps;
1163
- if (isHooksDisabled(settings)) return void 0;
1164
- const claudeToolName = normalizeToolName(event.toolName);
1165
- const sessionData = sessionIds(() => ctx.sessionManager.getSessionId());
1166
- const input = {
1167
- ...sessionData,
1168
- tool_name: claudeToolName,
1169
- tool_input: normalizeToolInput(claudeToolName, event.input),
1170
- tool_call_id: event.toolCallId
1171
- };
1172
- const shellCommand = extractShellCommand(event.toolName, event.input);
1173
- if (shellCommand !== void 0 && shellCommand.length > 0) {
1174
- const bashInput = {
1175
- ...sessionData,
1176
- tool_name: "Bash",
1177
- tool_input: { command: shellCommand },
1178
- tool_call_id: event.toolCallId
1179
- };
1180
- const bashResult = yield* runHooksForEvent(settings.hooks.PreToolUse, "Bash", bashInput, ctx, "PreToolUse");
1181
- if (bashResult.block) {
1182
- tel("tool_call.decision", {
1183
- tool_name: claudeToolName,
1184
- decision: "block",
1185
- reason: bashResult.reason ?? `Bash blocked for ${shellCommand}`
1186
- });
1187
- return bashResult.reason === void 0 ? { block: true } : {
1188
- block: true,
1189
- reason: bashResult.reason
1190
- };
1191
- }
1192
- }
1193
- const result = yield* runHooksForEvent(settings.hooks.PreToolUse, claudeToolName, input, ctx, "PreToolUse");
1194
- if (result.block) {
1195
- tel("tool_call.decision", {
1196
- tool_name: claudeToolName,
1197
- decision: "block",
1198
- reason: result.reason ?? void 0
1199
- });
1200
- return result.reason === void 0 ? { block: true } : {
1201
- block: true,
1202
- reason: result.reason
1203
- };
1204
- }
1205
- if (result.updatedInput && typeof result.updatedInput === "object" && "tool_input" in result.updatedInput && result.updatedInput["tool_input"] && typeof result.updatedInput["tool_input"] === "object") {
1206
- const updated = result.updatedInput["tool_input"];
1207
- for (const [key, value] of Object.entries(updated)) event.input[key] = value;
1208
- }
1209
- tel("tool_call.decision", {
1210
- tool_name: claudeToolName,
1211
- decision: "allow"
1212
- });
1213
- });
1214
- const runPostToolUseHooks = Effect.fn("runPostToolUseHooks")(function* (settings, event, ctx) {
1215
- const claudeToolName = normalizeToolName(event.toolName);
1216
- if (isHooksDisabled(settings)) return void 0;
1217
- const input = {
1218
- ...sessionIds(() => ctx.sessionManager.getSessionId()),
1219
- tool_name: claudeToolName,
1220
- tool_input: normalizeToolInput(claudeToolName, event.input),
1221
- tool_call_id: event.toolCallId,
1222
- output: event.content,
1223
- is_error: event.isError ?? false
1224
- };
1225
- return yield* runHooksForEvent(settings.hooks.PostToolUse, claudeToolName, input, ctx, "PostToolUse");
1226
- });
1227
- const runUserPromptSubmitHooks = Effect.fn("runUserPromptSubmitHooks")(function* (settings, event, ctx) {
1228
- const entries = settings.hooks.UserPromptSubmit;
1229
- if (isHooksDisabled(settings)) return void 0;
1230
- if (entries.length === 0) return void 0;
1231
- const cwd = ctx.cwd;
1232
- let injected = "";
1233
- const input = {
1234
- ...sessionIds(() => ctx.sessionManager.getSessionId()),
1235
- prompt: event.text,
1236
- source: event.source
1237
- };
1238
- for (const entry of entries) for (const hook of entry.hooks) {
1239
- const result = yield* runHookScript(hook.command, input, cwd, (hook.timeout ?? 10) * 1e3);
1240
- if (result.code !== 0) continue;
1241
- const stdout = result.stdout.trim();
1242
- if (stdout.length > 0) injected += (injected.length > 0 ? "\n\n" : "") + stdout;
1243
- }
1244
- if (injected.length === 0) return void 0;
1245
- const result = { text: `${injected}\n\n${event.text}` };
1246
- if (event.images !== void 0) result.images = event.images;
1247
- return result;
1248
- });
1249
- const runSessionStartHooks = Effect.fn("runSessionStartHooks")(function* (settings, reason, ctx) {
1250
- const { tel } = yield* HookDispatcherExecutorDeps;
1251
- if (isHooksDisabled(settings)) return;
1252
- const entries = settings.hooks.SessionStart;
1253
- if (entries.length === 0) return;
1254
- const cwd = ctx.cwd;
1255
- const input = {
1256
- ...sessionIds(() => ctx.sessionManager.getSessionId()),
1257
- reason
1258
- };
1259
- for (const entry of entries) {
1260
- if (entry.matcher && !matchesMatcher(reason, entry.matcher)) continue;
1261
- for (const hook of entry.hooks) {
1262
- const hookName = hookNameFromCommand(hook.command);
1263
- const timeoutMs = (hook.timeout ?? 10) * 1e3;
1264
- if (hook.async) {
1265
- yield* Effect.forkDaemon(runHookScript(hook.command, input, cwd, timeoutMs).pipe(Effect.tap((result) => Effect.sync(() => {
1266
- tel("hook.executed", {
1267
- hook: hookName,
1268
- exit_code: result.code
1269
- });
1270
- })), Effect.catchAll(() => Effect.sync(() => {
1271
- tel("hook.executed", {
1272
- hook: hookName,
1273
- exit_code: null
1274
- });
1275
- }))));
1276
- continue;
1277
- }
1278
- yield* runHookScript(hook.command, input, cwd, timeoutMs).pipe(Effect.tap((result) => Effect.sync(() => {
1279
- tel("hook.executed", {
1280
- hook: hookName,
1281
- exit_code: result.code
1282
- });
1283
- })), Effect.catchAll(() => Effect.sync(() => {
1284
- tel("hook.executed", {
1285
- hook: hookName,
1286
- exit_code: null
1287
- });
1288
- })));
1289
- }
1290
- }
1291
- });
1292
- const runLifecycleHooks = Effect.fn("runLifecycleHooks")(function* (entries, ctx) {
1293
- if (entries.length === 0) return;
1294
- const { tel } = yield* HookDispatcherExecutorDeps;
1295
- const cwd = ctx.cwd;
1296
- const input = { ...sessionIds(() => ctx.sessionManager.getSessionId()) };
1297
- for (const entry of entries) for (const hook of entry.hooks) {
1298
- const hookName = hookNameFromCommand(hook.command);
1299
- const timeoutMs = (hook.timeout ?? 10) * 1e3;
1300
- if (hook.async) yield* Effect.forkDaemon(runHookScript(hook.command, input, cwd, timeoutMs).pipe(Effect.tap((result) => Effect.sync(() => {
1301
- tel("hook.executed", {
1302
- hook: hookName,
1303
- exit_code: result.code
1304
- });
1305
- })), Effect.catchAll(() => Effect.sync(() => {
1306
- tel("hook.executed", {
1307
- hook: hookName,
1308
- exit_code: null
1309
- });
1310
- }))));
1311
- else yield* runHookScript(hook.command, input, cwd, timeoutMs).pipe(Effect.tap((result) => Effect.sync(() => {
1312
- tel("hook.executed", {
1313
- hook: hookName,
1314
- exit_code: result.code
1315
- });
1316
- })), Effect.catchAll(() => Effect.sync(() => {
1317
- tel("hook.executed", {
1318
- hook: hookName,
1319
- exit_code: null
1320
- });
1321
- })));
1322
- }
1323
- });
1324
- //#endregion
1325
- //#region src/runtime.ts
1326
- const nodeLayer = NodeCommandExecutor.layer.pipe(Layer.provideMerge(NodeFileSystem.layer), Layer.provideMerge(PathModule.layer));
1327
- const runtime = ManagedRuntime.make(nodeLayer);
1328
- //#endregion
1329
- //#region src/hook-dispatcher.handler.ts
1330
- const HookDispatcherTask = (pi) => Layer.effectDiscard(Effect.sync(() => {
1331
- const tel = createTelemetry("claude_compat", pi.logger);
1332
- const telLayer = Layer.succeed(HookDispatcherExecutorDeps, { tel });
1333
- const runSafe = async (effect) => {
1334
- const exit = await runtime.runPromise(effect.pipe(Effect.provide(telLayer), Effect.exit));
1335
- if (Exit.isFailure(exit)) throw Cause.squash(exit.cause);
1336
- return exit.value;
1337
- };
1338
- pi.on("tool_call", (event, ctx) => runSafe(Effect.gen(function* () {
1339
- const settings = yield* loadSettings(ctx.cwd);
1340
- if (!settings) return void 0;
1341
- return yield* runPreToolUseHooks(settings, event, ctx);
1342
- })));
1343
- pi.on("tool_result", (event, ctx) => runSafe(Effect.gen(function* () {
1344
- const settings = yield* loadSettings(ctx.cwd);
1345
- if (!settings) return void 0;
1346
- const result = yield* runPostToolUseHooks(settings, event, ctx);
1347
- if (result?.block) return {
1348
- isError: true,
1349
- content: [{
1350
- type: "text",
1351
- text: result.reason ?? "Blocked by PostToolUse hook"
1352
- }]
1353
- };
1354
- if (result?.warning) return {
1355
- content: [...event.content ?? [], {
1356
- type: "text",
1357
- text: result.warning
1358
- }],
1359
- isError: event.isError
1360
- };
1361
- })));
1362
- pi.on("input", (event, ctx) => runSafe(Effect.gen(function* () {
1363
- const settings = yield* loadSettings(ctx.cwd);
1364
- if (!settings) return void 0;
1365
- return yield* runUserPromptSubmitHooks(settings, event, ctx);
1366
- })));
1367
- pi.on("session_start", (_event, ctx) => runSafe(Effect.gen(function* () {
1368
- const settings = yield* loadSettings(ctx.cwd);
1369
- if (!settings) return void 0;
1370
- yield* runSessionStartHooks(settings, "start", ctx);
1371
- })));
1372
- pi.on("session_compact", (_event, ctx) => runSafe(Effect.gen(function* () {
1373
- const settings = yield* loadSettings(ctx.cwd);
1374
- if (!settings) return void 0;
1375
- yield* runSessionStartHooks(settings, "compact", ctx);
1376
- })));
1377
- pi.on("agent_start", (_event, ctx) => runSafe(Effect.gen(function* () {
1378
- const settings = yield* loadSettings(ctx.cwd);
1379
- if (!settings) return void 0;
1380
- yield* runSessionStartHooks(settings, "resume", ctx);
1381
- })));
1382
- pi.on("session_shutdown", (_event, ctx) => runSafe(Effect.gen(function* () {
1383
- const settings = yield* loadSettings(ctx.cwd);
1384
- if (!settings) return void 0;
1385
- if (settings.disableAllHooks) return void 0;
1386
- yield* runLifecycleHooks(settings.hooks.SessionEnd, ctx);
1387
- })));
1388
- pi.on("session_stop", (_event, ctx) => runSafe(Effect.gen(function* () {
1389
- const settings = yield* loadSettings(ctx.cwd);
1390
- if (!settings) return void 0;
1391
- if (settings.disableAllHooks) return void 0;
1392
- yield* runLifecycleHooks(settings.hooks.Stop, ctx);
1393
- })));
1394
- }));
1395
- //#endregion
1396
- //#region src/inject-instructions.executor.ts
1397
- const extractRefs = Effect.fn("extractRefs")(function* (content, baseDir, projectDir) {
1398
- const fs = yield* FileSystem;
1399
- const path = yield* PathModule.Path;
1400
- const refs = [];
1401
- for (const rawLine of content.split("\n")) {
1402
- const noMarker = rawLine.trim().replace(/^[-*+]\s+/, "");
1403
- if (!noMarker.startsWith("@")) continue;
1404
- const ref = noMarker.slice(1).trim();
1405
- if (!ref || ref.includes(" ")) continue;
1406
- if (path.isAbsolute(ref)) continue;
1407
- const baseResolved = path.resolve(baseDir, ref);
1408
- if (baseResolved.startsWith(projectDir + "/") || baseResolved === projectDir) {
1409
- const baseExists = yield* Effect.either(fs.exists(baseResolved));
1410
- if (Either.isRight(baseExists) && baseExists.right) {
1411
- refs.push({
1412
- sourcePath: baseDir,
1413
- resolvedPath: baseResolved
1414
- });
1415
- continue;
1416
- }
1417
- }
1418
- const rootResolved = path.resolve(projectDir, ref);
1419
- if ((rootResolved.startsWith(projectDir + "/") || rootResolved === projectDir) && rootResolved !== baseResolved) refs.push({
1420
- sourcePath: projectDir,
1421
- resolvedPath: rootResolved
1422
- });
1423
- }
1424
- return refs;
1425
- });
1426
- const loadReferencedContent = Effect.fn("loadReferencedContent")(function* (projectDir) {
1427
- const fs = yield* FileSystem;
1428
- const path = yield* PathModule.Path;
1429
- const claudeMdPaths = [path.resolve(projectDir, "CLAUDE.md"), path.resolve(projectDir, ".claude", "CLAUDE.md")];
1430
- const allRefs = [];
1431
- for (const filePath of claudeMdPaths) {
1432
- const content = yield* Effect.either(fs.readFileString(filePath, "utf-8"));
1433
- if (Either.isRight(content)) {
1434
- const refs = yield* extractRefs(content.right, path.dirname(filePath), projectDir);
1435
- allRefs.push(...refs);
1436
- }
1437
- }
1438
- const seen = /* @__PURE__ */ new Set();
1439
- const uniqueRefs = [];
1440
- for (const ref of allRefs) if (!seen.has(ref.resolvedPath)) {
1441
- seen.add(ref.resolvedPath);
1442
- uniqueRefs.push(ref);
1443
- }
1444
- const validRefs = [];
1445
- for (const ref of uniqueRefs) {
1446
- const exists = yield* Effect.either(fs.exists(ref.resolvedPath));
1447
- if (Either.isRight(exists) && exists.right) validRefs.push(ref);
1448
- }
1449
- if (validRefs.length === 0) return "";
1450
- const parts = ["# Injected @-references from CLAUDE.md"];
1451
- parts.push("The following files were @-imported by CLAUDE.md and contain project rules.");
1452
- parts.push("");
1453
- for (const ref of validRefs) {
1454
- const relativePath = ref.resolvedPath.slice(projectDir.length + 1);
1455
- parts.push(`## ${relativePath}`);
1456
- const refContent = yield* Effect.either(fs.readFileString(ref.resolvedPath, "utf-8"));
1457
- if (Either.isRight(refContent)) parts.push(refContent.right);
1458
- else parts.push(`[error reading ${relativePath}]`);
1459
- parts.push("");
1460
- }
1461
- return parts.join("\n");
1462
- });
1463
- //#endregion
1464
- //#region src/inject-instructions.handler.ts
1465
- const InjectInstructionsTask = (pi) => Layer.effectDiscard(Effect.sync(() => {
1466
- pi.on("before_agent_start", async (event, _ctx) => {
1467
- const exit = await runtime.runPromise(Effect.gen(function* () {
1468
- return yield* loadReferencedContent(yield* Config.string("CLAUDE_PROJECT_DIR").pipe(Config.withDefault(process.cwd())));
1469
- }).pipe(Effect.exit));
1470
- if (Exit.isFailure(exit)) throw Cause.squash(exit.cause);
1471
- const injected = exit.value;
1472
- if (injected === "") return void 0;
1473
- return { systemPrompt: [
1474
- ...event.systemPrompt,
1475
- "",
1476
- injected
1477
- ] };
1478
- });
1479
- }));
1480
- //#endregion
1481
- //#region src/index.ts
1482
- function claudeCompatExtension(pi) {
1483
- Effect.runSync(Effect.scoped(Layer.build(Layer.mergeAll(InjectInstructionsTask(pi), HookDispatcherTask(pi)))));
1484
- ManagedRuntime.make(Layer.mergeAll(InjectInstructionsTask(pi), HookDispatcherTask(pi)));
1485
- process.on("SIGINT", () => {
1486
- runtime.dispose();
1487
- });
1488
- process.on("SIGTERM", () => {
1489
- runtime.dispose();
1490
- });
1491
- }
1492
- //#endregion
1493
- export { claudeCompatExtension as default };
1
+ const e=e=>{let t=async e=>{let[t,n]=await Promise.all([import(`./runtime-DwpyNRPD.js`).then(e=>e.default),import(`effect`)]),{Cause:r,Effect:i,Exit:a}=n,o=e.pipe(i.exit),s=await t.runPromise(o);if(a.isFailure(s))throw r.squash(s.cause);return s.value};e.on(`tool_call`,async(e,n)=>{let{loadSettings:r,runPreToolUseHooks:i}=await import(`./hook-dispatcher.executor-ZebBJ5Sk.js`),{Effect:a}=await import(`effect`);return t(a.gen(function*(){let t=yield*r(n.cwd);if(t)return yield*i(t,e,n)}))}),e.on(`tool_result`,async(e,n)=>{let{loadSettings:r,runPostToolUseHooks:i}=await import(`./hook-dispatcher.executor-ZebBJ5Sk.js`),{Effect:a}=await import(`effect`);return t(a.gen(function*(){let t=yield*r(n.cwd);if(!t)return;let a=yield*i(t,e,n);if(a?.block)return{isError:!0,content:[{type:`text`,text:a.reason??`Blocked by PostToolUse hook`}]};if(a?.warning)return{content:[...e.content??[],{type:`text`,text:a.warning}],isError:e.isError}}))}),e.on(`input`,async(e,n)=>{let{loadSettings:r,runUserPromptSubmitHooks:i}=await import(`./hook-dispatcher.executor-ZebBJ5Sk.js`),{Effect:a}=await import(`effect`);return t(a.gen(function*(){let t=yield*r(n.cwd);if(t)return yield*i(t,e,n)}))}),e.on(`session_start`,async(e,n)=>{let{loadSettings:r,runSessionStartHooks:i}=await import(`./hook-dispatcher.executor-ZebBJ5Sk.js`),{Effect:a}=await import(`effect`);return t(a.gen(function*(){let e=yield*r(n.cwd);e&&(yield*i(e,`start`,n))}))}),e.on(`session_compact`,async(e,n)=>{let{loadSettings:r,runSessionStartHooks:i}=await import(`./hook-dispatcher.executor-ZebBJ5Sk.js`),{Effect:a}=await import(`effect`);return t(a.gen(function*(){let e=yield*r(n.cwd);e&&(yield*i(e,`compact`,n))}))}),e.on(`agent_start`,async(e,n)=>{let{loadSettings:r,runSessionStartHooks:i}=await import(`./hook-dispatcher.executor-ZebBJ5Sk.js`),{Effect:a}=await import(`effect`);return t(a.gen(function*(){let e=yield*r(n.cwd);e&&(yield*i(e,`resume`,n))}))}),e.on(`session_shutdown`,async(e,n)=>{let{loadSettings:r,runLifecycleHooks:i}=await import(`./hook-dispatcher.executor-ZebBJ5Sk.js`),{Effect:a}=await import(`effect`);return t(a.gen(function*(){let e=yield*r(n.cwd);e&&(e.disableAllHooks||(yield*i(e.hooks.SessionEnd,n)))}))}),e.on(`session_stop`,async(e,n)=>{let{loadSettings:r,runLifecycleHooks:i}=await import(`./hook-dispatcher.executor-ZebBJ5Sk.js`),{Effect:a}=await import(`effect`);return t(a.gen(function*(){let e=yield*r(n.cwd);e&&(e.disableAllHooks||(yield*i(e.hooks.Stop,n)))}))})},t=e=>{e.on(`before_agent_start`,async(e,t)=>{let n=await import(`./runtime-DwpyNRPD.js`).then(e=>e.default),{loadReferencedContent:r}=await import(`./inject-instructions.executor-CwW6M_qL.js`),{Cause:i,Config:a,Effect:o,Exit:s}=await import(`effect`),c=await n.runPromise(o.gen(function*(){let e=yield*a.string(`CLAUDE_PROJECT_DIR`).pipe(a.withDefault(process.cwd()));return yield*r(e)}).pipe(o.exit));if(s.isFailure(c))throw i.squash(c.cause);let l=c.value;if(l!==``)return{systemPrompt:[...e.systemPrompt,``,l]}})};function n(n){import(`./runtime-DwpyNRPD.js`),e(n),t(n),process.on(`SIGINT`,()=>{import(`./runtime-DwpyNRPD.js`).then(({default:e})=>e.dispose())}),process.on(`SIGTERM`,()=>{import(`./runtime-DwpyNRPD.js`).then(({default:e})=>e.dispose())})}export{n as default};