sessionpipe-conformance 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/cli.js +197 -50
- package/dist/cli.js.map +3 -3
- package/dist/index.js +197 -50
- package/dist/index.js.map +3 -3
- package/package.json +1 -1
package/README.md
CHANGED
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// sessionpipe-conformance 0.
|
|
2
|
+
// sessionpipe-conformance 0.2.0 · Apache-2.0 · https://sessionpipe.org
|
|
3
3
|
|
|
4
4
|
// src/cli.ts
|
|
5
5
|
import { readdirSync as readdirSync5, statSync as statSync4 } from "node:fs";
|
|
@@ -9,7 +9,16 @@ import path7 from "node:path";
|
|
|
9
9
|
import { readFileSync as readFileSync6 } from "node:fs";
|
|
10
10
|
|
|
11
11
|
// ../core/src/adapters/antigravity.ts
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
existsSync as existsSync2,
|
|
14
|
+
mkdirSync as mkdirSync2,
|
|
15
|
+
readdirSync as readdirSync2,
|
|
16
|
+
readFileSync as readFileSync2,
|
|
17
|
+
realpathSync,
|
|
18
|
+
statSync as statSync2,
|
|
19
|
+
unlinkSync as unlinkSync2,
|
|
20
|
+
writeFileSync as writeFileSync2
|
|
21
|
+
} from "node:fs";
|
|
13
22
|
import path3 from "node:path";
|
|
14
23
|
|
|
15
24
|
// ../core/src/paths.ts
|
|
@@ -217,28 +226,67 @@ function readAntigravity(lines, from) {
|
|
|
217
226
|
}
|
|
218
227
|
|
|
219
228
|
// ../core/src/adapters/claude-shaped.ts
|
|
220
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
229
|
+
import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
|
|
221
230
|
import path2 from "node:path";
|
|
222
231
|
var OURS = /sessionpipe[^"\n]*[\\/]hook\.js"?\s+[a-z][a-z-]*\s+[A-Za-z]+\s*$/;
|
|
223
232
|
var isOurs = (h) => Array.isArray(h?.hooks) && h.hooks.some((x) => typeof x.command === "string" && OURS.test(x.command));
|
|
224
|
-
function
|
|
233
|
+
function readJsonFile(file) {
|
|
234
|
+
let raw;
|
|
225
235
|
try {
|
|
226
|
-
|
|
236
|
+
raw = readFileSync(file, "utf8");
|
|
227
237
|
} catch {
|
|
228
|
-
return {};
|
|
238
|
+
return { ok: true, existed: false, value: {}, raw: "", indent: " ", eol: "\n", finalNewline: true };
|
|
229
239
|
}
|
|
240
|
+
let value;
|
|
241
|
+
try {
|
|
242
|
+
value = JSON.parse(raw);
|
|
243
|
+
} catch {
|
|
244
|
+
const hint = /^\s*\/\/|\/\*/m.test(raw) ? "not plain JSON (it has comments)" : "not plain JSON";
|
|
245
|
+
return { ok: false, existed: true, reason: hint };
|
|
246
|
+
}
|
|
247
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
248
|
+
return { ok: false, existed: true, reason: "not a JSON object" };
|
|
249
|
+
const eol = raw.includes("\r\n") ? "\r\n" : "\n";
|
|
250
|
+
const m = /^( +|\t+)\S/m.exec(raw);
|
|
251
|
+
const indent = m?.[1] ?? " ";
|
|
252
|
+
return {
|
|
253
|
+
ok: true,
|
|
254
|
+
existed: true,
|
|
255
|
+
value,
|
|
256
|
+
raw,
|
|
257
|
+
indent,
|
|
258
|
+
eol,
|
|
259
|
+
finalNewline: /\r?\n$/.test(raw)
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
function formatJson(value, f) {
|
|
263
|
+
const text = JSON.stringify(value, null, f.indent).replace(/\n/g, f.eol);
|
|
264
|
+
return f.finalNewline ? text + f.eol : text;
|
|
230
265
|
}
|
|
231
266
|
function entryFor(event, harness, cmd, o) {
|
|
232
267
|
const matcher = o.matcherFor(event);
|
|
233
268
|
return {
|
|
234
269
|
...matcher !== void 0 ? { matcher } : {},
|
|
235
|
-
hooks: [{ type: "command", command: cmd([harness, event]).command, timeout: o.timeout }]
|
|
270
|
+
hooks: [{ type: "command", command: cmd([harness, event]).command, timeout: o.timeoutFor?.(event) ?? o.timeout }]
|
|
236
271
|
};
|
|
237
272
|
}
|
|
238
273
|
function installClaudeShaped(file, harness, events, cmd, o) {
|
|
239
|
-
const
|
|
274
|
+
const f = readJsonFile(file);
|
|
275
|
+
if (!f.ok) return { file, changed: false, skipped: true, note: `${f.reason}; not touched` };
|
|
276
|
+
const settings2 = f.value;
|
|
240
277
|
const hooks = settings2.hooks && typeof settings2.hooks === "object" ? settings2.hooks : {};
|
|
241
278
|
let changed = false;
|
|
279
|
+
const want = new Set(events);
|
|
280
|
+
for (const ev of Object.keys(hooks)) {
|
|
281
|
+
if (want.has(ev)) continue;
|
|
282
|
+
const list = Array.isArray(hooks[ev]) ? hooks[ev] : [];
|
|
283
|
+
const kept = list.filter((h) => !isOurs(h));
|
|
284
|
+
if (kept.length !== list.length) {
|
|
285
|
+
changed = true;
|
|
286
|
+
if (kept.length) hooks[ev] = kept;
|
|
287
|
+
else delete hooks[ev];
|
|
288
|
+
}
|
|
289
|
+
}
|
|
242
290
|
for (const ev of events) {
|
|
243
291
|
const list = Array.isArray(hooks[ev]) ? hooks[ev] : [];
|
|
244
292
|
const entry2 = entryFor(ev, harness, cmd, o);
|
|
@@ -257,19 +305,19 @@ function installClaudeShaped(file, harness, events, cmd, o) {
|
|
|
257
305
|
if (changed) {
|
|
258
306
|
settings2.hooks = hooks;
|
|
259
307
|
mkdirSync(path2.dirname(file), { recursive: true });
|
|
260
|
-
writeFileSync(file,
|
|
261
|
-
`);
|
|
308
|
+
writeFileSync(file, formatJson(settings2, f));
|
|
262
309
|
}
|
|
263
|
-
return { file, changed };
|
|
310
|
+
return { file, changed, created: !f.existed && changed };
|
|
264
311
|
}
|
|
265
|
-
function uninstallClaudeShaped(file, events) {
|
|
312
|
+
function uninstallClaudeShaped(file, events, opts = {}) {
|
|
266
313
|
if (!existsSync(file)) return { file, changed: false };
|
|
267
|
-
const
|
|
268
|
-
|
|
314
|
+
const f = readJsonFile(file);
|
|
315
|
+
if (!f.ok) return { file, changed: false, skipped: true, note: `${f.reason}; not touched` };
|
|
316
|
+
const settings2 = f.value;
|
|
269
317
|
const hooks = settings2.hooks;
|
|
270
318
|
if (!hooks || typeof hooks !== "object") return { file, changed: false };
|
|
271
319
|
let changed = false;
|
|
272
|
-
for (const ev of events) {
|
|
320
|
+
for (const ev of [...events, ...Object.keys(hooks)]) {
|
|
273
321
|
const list = Array.isArray(hooks[ev]) ? hooks[ev] : [];
|
|
274
322
|
const kept = list.filter((h) => !isOurs(h));
|
|
275
323
|
if (kept.length !== list.length) {
|
|
@@ -280,13 +328,17 @@ function uninstallClaudeShaped(file, events) {
|
|
|
280
328
|
}
|
|
281
329
|
if (!changed) return { file, changed: false };
|
|
282
330
|
if (!Object.keys(hooks).length) delete settings2.hooks;
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
331
|
+
if (opts.created && !Object.keys(settings2).length) {
|
|
332
|
+
unlinkSync(file);
|
|
333
|
+
return { file, changed: true, note: "removed (sessionpipe created it)" };
|
|
334
|
+
}
|
|
335
|
+
const next = formatJson(settings2, f);
|
|
336
|
+
if (next !== f.raw) writeFileSync(file, next);
|
|
286
337
|
return { file, changed: true };
|
|
287
338
|
}
|
|
288
339
|
function installedClaudeShaped(file, harness, events, cmd, o) {
|
|
289
|
-
const
|
|
340
|
+
const f = readJsonFile(file);
|
|
341
|
+
const hooks = f.ok ? f.value.hooks ?? {} : {};
|
|
290
342
|
let present = 0;
|
|
291
343
|
let current = 0;
|
|
292
344
|
for (const ev of events) {
|
|
@@ -295,6 +347,9 @@ function installedClaudeShaped(file, harness, events, cmd, o) {
|
|
|
295
347
|
if (mine.length) present++;
|
|
296
348
|
if (mine.length === 1 && JSON.stringify(mine[0]) === JSON.stringify(entryFor(ev, harness, cmd, o))) current++;
|
|
297
349
|
}
|
|
350
|
+
for (const ev of Object.keys(hooks))
|
|
351
|
+
if (!events.includes(ev) && (Array.isArray(hooks[ev]) ? hooks[ev] : []).some((h) => isOurs(h)))
|
|
352
|
+
return "stale";
|
|
298
353
|
if (!present) return "missing";
|
|
299
354
|
return current === events.length ? "current" : "stale";
|
|
300
355
|
}
|
|
@@ -309,10 +364,13 @@ var projectsDir = (env) => path3.join(gemini(env), "config", "projects");
|
|
|
309
364
|
var dataDirs = (env) => ["antigravity", "antigravity-cli", "antigravity-ide"].map((d) => path3.join(gemini(env), d));
|
|
310
365
|
var ID_RE = /^[A-Za-z0-9_-]{1,128}$/;
|
|
311
366
|
var validTool = (n) => typeof n === "string" && /^[A-Za-z0-9_.:-]{1,120}$/.test(n);
|
|
312
|
-
function entry(cmd) {
|
|
367
|
+
function entry(cmd, lean) {
|
|
313
368
|
const handler = (e) => ({ type: "command", command: cmd([NAME, e]).command, timeout: 5 });
|
|
314
369
|
const out = { enabled: true };
|
|
315
|
-
for (const e of AG_EVENTS)
|
|
370
|
+
for (const e of AG_EVENTS) {
|
|
371
|
+
if (lean && e === "PreToolUse") continue;
|
|
372
|
+
out[e] = /Tool/.test(e) ? [{ matcher: "*", hooks: [handler(e)] }] : [handler(e)];
|
|
373
|
+
}
|
|
316
374
|
return out;
|
|
317
375
|
}
|
|
318
376
|
var antigravity = {
|
|
@@ -320,34 +378,49 @@ var antigravity = {
|
|
|
320
378
|
events: AG_EVENTS,
|
|
321
379
|
detect: (env = process.env) => existsSync2(path3.join(gemini(env), "config")) || dataDirs(env).some((d) => existsSync2(d)),
|
|
322
380
|
configFiles: (env = process.env) => [hooksFile(env)],
|
|
323
|
-
install(cmd, env = process.env) {
|
|
381
|
+
install(cmd, env = process.env, opts = {}) {
|
|
324
382
|
const file = hooksFile(env);
|
|
325
|
-
const
|
|
326
|
-
|
|
383
|
+
const f = readJsonFile(file);
|
|
384
|
+
if (!f.ok) return [{ file, changed: false, skipped: true, note: `${f.reason}; not touched` }];
|
|
385
|
+
const hooks = f.value;
|
|
386
|
+
const e = entry(cmd, opts.lean);
|
|
327
387
|
if (JSON.stringify(hooks[HOOK_NAME]) === JSON.stringify(e)) return [{ file, changed: false }];
|
|
328
388
|
hooks[HOOK_NAME] = e;
|
|
329
389
|
mkdirSync2(path3.dirname(file), { recursive: true });
|
|
330
|
-
writeFileSync2(file,
|
|
331
|
-
`);
|
|
390
|
+
writeFileSync2(file, formatJson(hooks, f));
|
|
332
391
|
return [
|
|
333
|
-
{
|
|
392
|
+
{
|
|
393
|
+
file,
|
|
394
|
+
changed: true,
|
|
395
|
+
created: !f.existed,
|
|
396
|
+
note: "hooks.json is read when a conversation starts; open ones report after a restart"
|
|
397
|
+
}
|
|
334
398
|
];
|
|
335
399
|
},
|
|
336
|
-
uninstall(env = process.env) {
|
|
400
|
+
uninstall(env = process.env, opts = {}) {
|
|
337
401
|
const file = hooksFile(env);
|
|
338
402
|
if (!existsSync2(file)) return [{ file, changed: false }];
|
|
339
|
-
const
|
|
403
|
+
const f = readJsonFile(file);
|
|
404
|
+
if (!f.ok) return [{ file, changed: false, skipped: true, note: `${f.reason}; not touched` }];
|
|
405
|
+
const hooks = f.value;
|
|
340
406
|
if (!(HOOK_NAME in hooks)) return [{ file, changed: false }];
|
|
341
407
|
delete hooks[HOOK_NAME];
|
|
342
|
-
|
|
343
|
-
|
|
408
|
+
if (opts.created?.includes(file) && !Object.keys(hooks).length) {
|
|
409
|
+
unlinkSync2(file);
|
|
410
|
+
return [{ file, changed: true, note: "removed (sessionpipe created it)" }];
|
|
411
|
+
}
|
|
412
|
+
writeFileSync2(file, formatJson(hooks, f));
|
|
344
413
|
return [{ file, changed: true }];
|
|
345
414
|
},
|
|
346
|
-
installed(cmd, env = process.env) {
|
|
415
|
+
installed(cmd, env = process.env, opts = {}) {
|
|
347
416
|
const file = hooksFile(env);
|
|
348
|
-
const
|
|
417
|
+
const f = readJsonFile(file);
|
|
418
|
+
const have = f.ok ? f.value[HOOK_NAME] : void 0;
|
|
349
419
|
return [
|
|
350
|
-
{
|
|
420
|
+
{
|
|
421
|
+
file,
|
|
422
|
+
state: !have ? "missing" : JSON.stringify(have) === JSON.stringify(entry(cmd, opts.lean)) ? "current" : "stale"
|
|
423
|
+
}
|
|
351
424
|
];
|
|
352
425
|
},
|
|
353
426
|
fromHook(input) {
|
|
@@ -613,9 +686,18 @@ var CLAUDE_EVENTS = [
|
|
|
613
686
|
"SubagentStop",
|
|
614
687
|
"PreCompact",
|
|
615
688
|
"PostCompact",
|
|
616
|
-
"SessionEnd"
|
|
689
|
+
"SessionEnd",
|
|
690
|
+
"StopFailure"
|
|
617
691
|
];
|
|
618
|
-
var
|
|
692
|
+
var LEAN_SKIP = /* @__PURE__ */ new Set(["PreToolUse", "PostToolUseFailure"]);
|
|
693
|
+
var eventsFor = (lean) => lean ? CLAUDE_EVENTS.filter((e) => !LEAN_SKIP.has(e)) : [...CLAUDE_EVENTS];
|
|
694
|
+
var OPTS = {
|
|
695
|
+
matcherFor: (e) => /Tool|Permission/.test(e) ? "" : void 0,
|
|
696
|
+
timeout: 5,
|
|
697
|
+
// The hook waits up to 125 s for a verified control answer, and only when control
|
|
698
|
+
// is paired on this machine; everywhere else it exits in milliseconds as before.
|
|
699
|
+
timeoutFor: (e) => e === "PermissionRequest" ? 130 : void 0
|
|
700
|
+
};
|
|
619
701
|
var DESKTOP_ID = /^local_[A-Za-z0-9-]{1,64}$/;
|
|
620
702
|
function claudeDirs(env = process.env, home = HOME) {
|
|
621
703
|
const out = [];
|
|
@@ -646,11 +728,13 @@ var claudeCode = {
|
|
|
646
728
|
events: CLAUDE_EVENTS,
|
|
647
729
|
detect: (env = process.env) => claudeDirs(env).some((d) => existsSync3(d)),
|
|
648
730
|
configFiles: (env = process.env) => claudeDirs(env).map(settingsIn),
|
|
649
|
-
install: (cmd, env = process.env) => claudeDirs(env).map((d) => installClaudeShaped(settingsIn(d), NAME2,
|
|
650
|
-
uninstall: (env = process.env) => claudeDirs(env).map(
|
|
651
|
-
|
|
731
|
+
install: (cmd, env = process.env, opts = {}) => claudeDirs(env).map((d) => installClaudeShaped(settingsIn(d), NAME2, eventsFor(opts.lean), cmd, OPTS)),
|
|
732
|
+
uninstall: (env = process.env, opts = {}) => claudeDirs(env).map(
|
|
733
|
+
(d) => uninstallClaudeShaped(settingsIn(d), CLAUDE_EVENTS, { created: opts.created?.includes(settingsIn(d)) })
|
|
734
|
+
),
|
|
735
|
+
installed: (cmd, env = process.env, opts = {}) => claudeDirs(env).map((d) => ({
|
|
652
736
|
file: settingsIn(d),
|
|
653
|
-
state: installedClaudeShaped(settingsIn(d), NAME2,
|
|
737
|
+
state: installedClaudeShaped(settingsIn(d), NAME2, eventsFor(opts.lean), cmd, OPTS)
|
|
654
738
|
})),
|
|
655
739
|
fromHook(input) {
|
|
656
740
|
const event = input.argv[1] ?? "";
|
|
@@ -741,6 +825,15 @@ var claudeCode = {
|
|
|
741
825
|
case "Stop":
|
|
742
826
|
events.push(ev("turn.ended", { turn_id, reason: "stop" }));
|
|
743
827
|
break;
|
|
828
|
+
case "StopFailure":
|
|
829
|
+
events.push(
|
|
830
|
+
ev("turn.ended", {
|
|
831
|
+
turn_id,
|
|
832
|
+
reason: "error",
|
|
833
|
+
error: typeof s.error === "string" ? s.error.slice(0, 200) : void 0
|
|
834
|
+
})
|
|
835
|
+
);
|
|
836
|
+
break;
|
|
744
837
|
case "SubagentStart":
|
|
745
838
|
events.push(
|
|
746
839
|
ev("subagent.started", {
|
|
@@ -1252,6 +1345,7 @@ var CODEX_EVENTS = [
|
|
|
1252
1345
|
"SessionEnd"
|
|
1253
1346
|
];
|
|
1254
1347
|
var OPTS2 = { matcherFor: (e) => /Tool|Permission/.test(e) ? "" : void 0, timeout: 5 };
|
|
1348
|
+
var eventsFor2 = (lean) => lean ? CODEX_EVENTS.filter((e) => e !== "PreToolUse") : [...CODEX_EVENTS];
|
|
1255
1349
|
var codexHome = (env) => env.CODEX_HOME || path5.join(HOME, ".codex");
|
|
1256
1350
|
var hooksFile2 = (env) => path5.join(codexHome(env), "hooks.json");
|
|
1257
1351
|
var configFile = (env) => path5.join(codexHome(env), "config.toml");
|
|
@@ -1300,7 +1394,8 @@ function installNotify(cmd, env) {
|
|
|
1300
1394
|
return {
|
|
1301
1395
|
file,
|
|
1302
1396
|
changed: false,
|
|
1303
|
-
|
|
1397
|
+
skipped: true,
|
|
1398
|
+
note: `notify belongs to ${(n.argv?.[0] ?? n.raw).replace(/^.*\//, "").slice(0, 40)}; left alone. Codex will report nothing until you approve the hooks: start \`codex\` and accept the hook review`
|
|
1304
1399
|
};
|
|
1305
1400
|
}
|
|
1306
1401
|
write(withNotify(text, line));
|
|
@@ -1357,14 +1452,21 @@ var codex = {
|
|
|
1357
1452
|
events: CODEX_EVENTS,
|
|
1358
1453
|
detect: (env = process.env) => existsSync4(codexHome(env)),
|
|
1359
1454
|
configFiles: (env = process.env) => [hooksFile2(env), configFile(env)],
|
|
1360
|
-
install: (cmd, env = process.env) => [
|
|
1361
|
-
installClaudeShaped(hooksFile2(env), NAME3,
|
|
1455
|
+
install: (cmd, env = process.env, opts = {}) => [
|
|
1456
|
+
installClaudeShaped(hooksFile2(env), NAME3, eventsFor2(opts.lean), cmd, OPTS2),
|
|
1362
1457
|
installNotify(cmd, env)
|
|
1363
1458
|
],
|
|
1364
|
-
uninstall: (env = process.env) => [
|
|
1365
|
-
|
|
1459
|
+
uninstall: (env = process.env, opts = {}) => [
|
|
1460
|
+
uninstallClaudeShaped(hooksFile2(env), CODEX_EVENTS, { created: opts.created?.includes(hooksFile2(env)) }),
|
|
1461
|
+
uninstallNotify(env)
|
|
1462
|
+
],
|
|
1463
|
+
installed: (cmd, env = process.env, opts = {}) => {
|
|
1366
1464
|
const out = [
|
|
1367
|
-
{
|
|
1465
|
+
{
|
|
1466
|
+
file: hooksFile2(env),
|
|
1467
|
+
state: installedClaudeShaped(hooksFile2(env), NAME3, eventsFor2(opts.lean), cmd, OPTS2),
|
|
1468
|
+
note: "Codex runs non-managed hooks only after the review in `codex` (/hooks); until then nothing is reported unless the notify fallback is ours"
|
|
1469
|
+
}
|
|
1368
1470
|
];
|
|
1369
1471
|
let text = "";
|
|
1370
1472
|
try {
|
|
@@ -1552,6 +1654,7 @@ function codexFacts(file) {
|
|
|
1552
1654
|
}
|
|
1553
1655
|
|
|
1554
1656
|
// ../core/src/adapters/gemini-cli.ts
|
|
1657
|
+
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
1555
1658
|
import { existsSync as existsSync5, readFileSync as readFileSync5 } from "node:fs";
|
|
1556
1659
|
import path6 from "node:path";
|
|
1557
1660
|
var NAME4 = "gemini-cli";
|
|
@@ -1569,17 +1672,61 @@ var OPTS3 = { matcherFor: (e) => /Tool/.test(e) ? ".*" : void 0, timeout: 5e3 };
|
|
|
1569
1672
|
var geminiDir = (env) => env.GEMINI_CLI_HOME || path6.join(HOME, ".gemini");
|
|
1570
1673
|
var settings = (env) => path6.join(geminiDir(env), "settings.json");
|
|
1571
1674
|
var validTool4 = (n) => typeof n === "string" && /^[A-Za-z0-9_.:-]{1,120}$/.test(n);
|
|
1675
|
+
var eventsFor3 = (lean) => lean ? GEMINI_EVENTS.filter((e) => e !== "BeforeTool") : [...GEMINI_EVENTS];
|
|
1676
|
+
function geminiHooksActive(settingsFile) {
|
|
1677
|
+
let tools;
|
|
1678
|
+
try {
|
|
1679
|
+
tools = JSON.parse(readFileSync5(settingsFile, "utf8")).tools;
|
|
1680
|
+
} catch {
|
|
1681
|
+
}
|
|
1682
|
+
if (tools?.enableHooks === true) return { active: true };
|
|
1683
|
+
if (tools?.enableHooks === false)
|
|
1684
|
+
return { active: false, note: "inactive: tools.enableHooks is false in settings.json" };
|
|
1685
|
+
let version = "";
|
|
1686
|
+
try {
|
|
1687
|
+
version = execFileSync3("gemini", ["--version"], {
|
|
1688
|
+
encoding: "utf8",
|
|
1689
|
+
timeout: 4e3,
|
|
1690
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
1691
|
+
}).trim();
|
|
1692
|
+
} catch {
|
|
1693
|
+
return {
|
|
1694
|
+
active: true,
|
|
1695
|
+
note: "could not run `gemini --version`; hooks need tools.enableHooks on versions before 0.46"
|
|
1696
|
+
};
|
|
1697
|
+
}
|
|
1698
|
+
const m = /(\d+)\.(\d+)/.exec(version);
|
|
1699
|
+
if (m && (Number(m[1]) > 0 || Number(m[2]) >= 46)) return { active: true };
|
|
1700
|
+
return {
|
|
1701
|
+
active: false,
|
|
1702
|
+
note: `inactive: Gemini CLI ${version || "?"} runs hooks only with "tools": {"enableHooks": true} in settings.json`
|
|
1703
|
+
};
|
|
1704
|
+
}
|
|
1572
1705
|
var geminiCli = {
|
|
1573
1706
|
name: NAME4,
|
|
1574
1707
|
events: GEMINI_EVENTS,
|
|
1575
1708
|
// ~/.gemini also belongs to Antigravity; Gemini CLI is here when its settings or its chat logs exist.
|
|
1576
1709
|
detect: (env = process.env) => existsSync5(settings(env)) || existsSync5(path6.join(geminiDir(env), "tmp")),
|
|
1577
1710
|
configFiles: (env = process.env) => [settings(env)],
|
|
1578
|
-
install: (cmd, env = process.env
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1711
|
+
install: (cmd, env = process.env, opts = {}) => {
|
|
1712
|
+
const r2 = installClaudeShaped(settings(env), NAME4, eventsFor3(opts.lean), cmd, OPTS3);
|
|
1713
|
+
const a = geminiHooksActive(settings(env));
|
|
1714
|
+
return [a.note && !r2.skipped ? { ...r2, note: [r2.note, a.note].filter(Boolean).join("; ") } : r2];
|
|
1715
|
+
},
|
|
1716
|
+
uninstall: (env = process.env, opts = {}) => [
|
|
1717
|
+
uninstallClaudeShaped(settings(env), GEMINI_EVENTS, { created: opts.created?.includes(settings(env)) })
|
|
1582
1718
|
],
|
|
1719
|
+
installed: (cmd, env = process.env, opts = {}) => {
|
|
1720
|
+
const state = installedClaudeShaped(settings(env), NAME4, eventsFor3(opts.lean), cmd, OPTS3);
|
|
1721
|
+
const a = geminiHooksActive(settings(env));
|
|
1722
|
+
return [
|
|
1723
|
+
{
|
|
1724
|
+
file: settings(env),
|
|
1725
|
+
state: state === "current" && !a.active ? "inactive" : state,
|
|
1726
|
+
...a.note ? { note: a.note } : {}
|
|
1727
|
+
}
|
|
1728
|
+
];
|
|
1729
|
+
},
|
|
1583
1730
|
fromHook(input) {
|
|
1584
1731
|
const event = input.argv[1] ?? "";
|
|
1585
1732
|
let s = {};
|