olakai-cli 0.7.0 → 0.8.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.
@@ -32,7 +32,9 @@ import {
32
32
  var TOOL_IDS = [
33
33
  "claude-code",
34
34
  "codex",
35
- "cursor"
35
+ "cursor",
36
+ "gemini-cli",
37
+ "antigravity"
36
38
  ];
37
39
  var registry = /* @__PURE__ */ new Map();
38
40
  function registerPlugin(plugin) {
@@ -81,6 +83,16 @@ var TOOL_DESCRIPTORS = {
81
83
  scope: "global",
82
84
  hooksPath: (_projectRoot, homeDir) => path.join(homeDir, ".cursor", "hooks.json"),
83
85
  source: "CURSOR"
86
+ },
87
+ "gemini-cli": {
88
+ scope: "global",
89
+ hooksPath: (_projectRoot, homeDir) => path.join(homeDir, ".gemini", "settings.json"),
90
+ source: "GEMINI_CLI"
91
+ },
92
+ antigravity: {
93
+ scope: "global",
94
+ hooksPath: (_projectRoot, homeDir) => path.join(homeDir, ".gemini", "config", "hooks.json"),
95
+ source: "ANTIGRAVITY"
84
96
  }
85
97
  };
86
98
  function getToolScope(toolId) {
@@ -786,7 +798,7 @@ function parseTranscript(raw) {
786
798
  // src/monitor/plugins/claude-code/hook.ts
787
799
  var noopDebug = () => {
788
800
  };
789
- function extractFromTranscript(transcriptPath, debugLog4 = noopDebug) {
801
+ function extractFromTranscript(transcriptPath, debugLog6 = noopDebug) {
790
802
  const empty = {
791
803
  prompt: "",
792
804
  response: "",
@@ -804,7 +816,7 @@ function extractFromTranscript(transcriptPath, debugLog4 = noopDebug) {
804
816
  try {
805
817
  raw = fs4.readFileSync(transcriptPath, "utf-8");
806
818
  } catch (err) {
807
- debugLog4("transcript-read-failed", {
819
+ debugLog6("transcript-read-failed", {
808
820
  transcriptPath,
809
821
  error: err.message
810
822
  });
@@ -1060,8 +1072,8 @@ function mergeCodexHooks(existing, events = SUPPORTED_HOOK_EVENT_NAMES) {
1060
1072
  const merged = { ...existing ?? {} };
1061
1073
  for (const event of events) {
1062
1074
  const existingGroups = merged[event] ?? [];
1063
- const hasOlakaiHook = existingGroups.some(groupContainsOlakaiHandler);
1064
- if (hasOlakaiHook) {
1075
+ const hasOlakaiHook2 = existingGroups.some(groupContainsOlakaiHandler);
1076
+ if (hasOlakaiHook2) {
1065
1077
  merged[event] = existingGroups;
1066
1078
  } else {
1067
1079
  merged[event] = [...existingGroups, buildOlakaiHookGroup(event)];
@@ -1397,7 +1409,7 @@ function findRolloutPathForSession(sessionId, sessionsDir = getCodexSessionsDir(
1397
1409
  matches.sort((a, b) => b.mtimeMs - a.mtimeMs);
1398
1410
  return matches[0].path;
1399
1411
  }
1400
- function parseRolloutContent(raw, debugLog4 = noopDebug2) {
1412
+ function parseRolloutContent(raw, debugLog6 = noopDebug2) {
1401
1413
  const result = emptyRollout();
1402
1414
  if (!raw) return result;
1403
1415
  const lines = raw.split("\n");
@@ -1414,7 +1426,7 @@ function parseRolloutContent(raw, debugLog4 = noopDebug2) {
1414
1426
  try {
1415
1427
  parsed = JSON.parse(trimmed);
1416
1428
  } catch (err) {
1417
- debugLog4("rollout-line-parse-failed", {
1429
+ debugLog6("rollout-line-parse-failed", {
1418
1430
  line: trimmed.slice(0, 120),
1419
1431
  error: err.message
1420
1432
  });
@@ -1491,12 +1503,12 @@ function numberOrZero(value) {
1491
1503
  return typeof value === "number" && Number.isFinite(value) ? value : 0;
1492
1504
  }
1493
1505
  function loadRolloutForSession(sessionId, options = {}) {
1494
- const debugLog4 = options.debugLog ?? noopDebug2;
1506
+ const debugLog6 = options.debugLog ?? noopDebug2;
1495
1507
  const sessionsDir = options.sessionsDir ?? getCodexSessionsDir();
1496
1508
  const result = emptyRollout();
1497
1509
  const rolloutPath = findRolloutPathForSession(sessionId, sessionsDir);
1498
1510
  if (!rolloutPath) {
1499
- debugLog4("rollout-not-found", {
1511
+ debugLog6("rollout-not-found", {
1500
1512
  sessionId,
1501
1513
  sessionsDir
1502
1514
  });
@@ -1506,13 +1518,13 @@ function loadRolloutForSession(sessionId, options = {}) {
1506
1518
  try {
1507
1519
  raw = fs9.readFileSync(rolloutPath, "utf-8");
1508
1520
  } catch (err) {
1509
- debugLog4("rollout-read-failed", {
1521
+ debugLog6("rollout-read-failed", {
1510
1522
  rolloutPath,
1511
1523
  error: err.message
1512
1524
  });
1513
1525
  return result;
1514
1526
  }
1515
- const parsed = parseRolloutContent(raw, debugLog4);
1527
+ const parsed = parseRolloutContent(raw, debugLog6);
1516
1528
  parsed.rolloutPath = rolloutPath;
1517
1529
  return parsed;
1518
1530
  }
@@ -1565,32 +1577,32 @@ function buildCodexPayload(eventName, eventData, config, rollout = emptyRollout(
1565
1577
  };
1566
1578
  }
1567
1579
  function handleCodexHook(eventName, payloadJson, config, options = {}) {
1568
- const debugLog4 = options.debugLog ?? noopDebug3;
1580
+ const debugLog6 = options.debugLog ?? noopDebug3;
1569
1581
  if (!isSupportedCodexEvent(eventName)) {
1570
- debugLog4("hook-unknown-event", eventName);
1582
+ debugLog6("hook-unknown-event", eventName);
1571
1583
  return null;
1572
1584
  }
1573
1585
  if (eventName === "UserPromptSubmit") {
1574
- debugLog4("user-prompt-submit-dropped", { eventName });
1586
+ debugLog6("user-prompt-submit-dropped", { eventName });
1575
1587
  return null;
1576
1588
  }
1577
1589
  const eventData = payloadJson ?? {};
1578
- debugLog4("event-parsed", { eventName, eventData });
1590
+ debugLog6("event-parsed", { eventName, eventData });
1579
1591
  const sessionId = typeof eventData.session_id === "string" ? eventData.session_id : "";
1580
1592
  let rollout = emptyRollout();
1581
1593
  if (eventName === "Stop" && sessionId) {
1582
1594
  rollout = loadRolloutForSession(sessionId, {
1583
- debugLog: debugLog4,
1595
+ debugLog: debugLog6,
1584
1596
  sessionsDir: options.sessionsDir
1585
1597
  });
1586
1598
  }
1587
1599
  const payload = buildCodexPayload(eventName, eventData, config, rollout);
1588
1600
  if (!payload) return null;
1589
1601
  if (!payload.prompt && !payload.response) {
1590
- debugLog4("payload-empty", { eventName, sessionId });
1602
+ debugLog6("payload-empty", { eventName, sessionId });
1591
1603
  return null;
1592
1604
  }
1593
- debugLog4("payload-built", payload);
1605
+ debugLog6("payload-built", payload);
1594
1606
  return payload;
1595
1607
  }
1596
1608
 
@@ -2529,6 +2541,1348 @@ function whichCursorOnPath() {
2529
2541
  }
2530
2542
  registerPlugin(cursorPlugin);
2531
2543
 
2544
+ // src/monitor/plugins/gemini-cli/index.ts
2545
+ import * as fs20 from "fs";
2546
+ import { spawnSync as spawnSync4 } from "child_process";
2547
+
2548
+ // src/monitor/plugins/gemini-cli/install.ts
2549
+ import * as fs18 from "fs";
2550
+ import * as os10 from "os";
2551
+ import * as path18 from "path";
2552
+
2553
+ // src/monitor/plugins/gemini-cli/config.ts
2554
+ import * as fs16 from "fs";
2555
+ import * as path15 from "path";
2556
+ function getGeminiCliConfigPath(projectRoot) {
2557
+ return getMonitorConfigPath(projectRoot, "gemini-cli");
2558
+ }
2559
+ function loadGeminiCliConfig(projectRoot) {
2560
+ const filePath = getGeminiCliConfigPath(projectRoot);
2561
+ try {
2562
+ if (!fs16.existsSync(filePath)) return null;
2563
+ const raw = fs16.readFileSync(filePath, "utf-8");
2564
+ return JSON.parse(raw);
2565
+ } catch {
2566
+ return null;
2567
+ }
2568
+ }
2569
+ function writeGeminiCliConfig(projectRoot, config) {
2570
+ const filePath = getGeminiCliConfigPath(projectRoot);
2571
+ const dir = path15.dirname(filePath);
2572
+ if (!fs16.existsSync(dir)) {
2573
+ fs16.mkdirSync(dir, { recursive: true });
2574
+ }
2575
+ fs16.writeFileSync(filePath, JSON.stringify(config, null, 2) + "\n", "utf-8");
2576
+ try {
2577
+ fs16.chmodSync(filePath, 384);
2578
+ } catch {
2579
+ }
2580
+ }
2581
+ function deleteGeminiCliConfig(projectRoot) {
2582
+ const filePath = getGeminiCliConfigPath(projectRoot);
2583
+ if (!fs16.existsSync(filePath)) return false;
2584
+ try {
2585
+ fs16.unlinkSync(filePath);
2586
+ return true;
2587
+ } catch {
2588
+ return false;
2589
+ }
2590
+ }
2591
+
2592
+ // src/monitor/plugins/gemini-cli/paths.ts
2593
+ import * as os9 from "os";
2594
+ import * as path16 from "path";
2595
+ var GEMINI_HOME_DIRNAME = ".gemini";
2596
+ var GEMINI_SETTINGS_FILENAME = "settings.json";
2597
+ function getGeminiHomeDir(homeDir = os9.homedir()) {
2598
+ return path16.join(homeDir, GEMINI_HOME_DIRNAME);
2599
+ }
2600
+ function getGeminiSettingsPath(homeDir = os9.homedir()) {
2601
+ return path16.join(getGeminiHomeDir(homeDir), GEMINI_SETTINGS_FILENAME);
2602
+ }
2603
+
2604
+ // src/monitor/plugins/gemini-cli/settings.ts
2605
+ import * as fs17 from "fs";
2606
+ import * as path17 from "path";
2607
+ var OLAKAI_HOOK_MARKER4 = "olakai monitor hook --tool gemini-cli";
2608
+ var GEMINI_HOOK_TIMEOUT_MS = 5e3;
2609
+ var SUPPORTED_GEMINI_HOOK_EVENTS = [
2610
+ "SessionStart",
2611
+ "SessionEnd",
2612
+ "BeforeModel",
2613
+ "AfterModel",
2614
+ "BeforeTool",
2615
+ "AfterTool"
2616
+ ];
2617
+ function buildOlakaiHookEntry(event) {
2618
+ return {
2619
+ matcher: "",
2620
+ hooks: [
2621
+ {
2622
+ type: "command",
2623
+ command: `olakai monitor hook --tool gemini-cli ${event}`,
2624
+ timeout: GEMINI_HOOK_TIMEOUT_MS
2625
+ }
2626
+ ]
2627
+ };
2628
+ }
2629
+ var HOOK_DEFINITIONS = Object.fromEntries(
2630
+ SUPPORTED_GEMINI_HOOK_EVENTS.map((event) => [event, [buildOlakaiHookEntry(event)]])
2631
+ );
2632
+ function entryContainsOlakaiHook(entry) {
2633
+ if (!Array.isArray(entry.hooks)) return false;
2634
+ return entry.hooks.some(
2635
+ (h) => typeof h?.command === "string" && h.command.includes(OLAKAI_HOOK_MARKER4)
2636
+ );
2637
+ }
2638
+ function mergeHooksSettings2(existing, definitions = HOOK_DEFINITIONS) {
2639
+ const merged = {
2640
+ ...existing ?? {}
2641
+ };
2642
+ for (const [event, defaultEntries] of Object.entries(definitions)) {
2643
+ const existingEntries = merged[event] ?? [];
2644
+ const hasOlakaiHook2 = existingEntries.some(entryContainsOlakaiHook);
2645
+ if (hasOlakaiHook2) {
2646
+ merged[event] = existingEntries;
2647
+ } else {
2648
+ merged[event] = [...existingEntries, ...defaultEntries];
2649
+ }
2650
+ }
2651
+ return merged;
2652
+ }
2653
+ function removeOlakaiHooks(existing) {
2654
+ if (!existing) return void 0;
2655
+ const cleaned = {};
2656
+ for (const [event, entries] of Object.entries(existing)) {
2657
+ if (!Array.isArray(entries)) continue;
2658
+ const filteredEntries = [];
2659
+ for (const entry of entries) {
2660
+ const handlers = Array.isArray(entry.hooks) ? entry.hooks : [];
2661
+ const remaining = handlers.filter(
2662
+ (h) => typeof h?.command !== "string" || !h.command.includes(OLAKAI_HOOK_MARKER4)
2663
+ );
2664
+ if (remaining.length === 0 && handlers.length > 0) {
2665
+ continue;
2666
+ }
2667
+ if (remaining.length === handlers.length) {
2668
+ filteredEntries.push(entry);
2669
+ } else {
2670
+ filteredEntries.push({ ...entry, hooks: remaining });
2671
+ }
2672
+ }
2673
+ if (filteredEntries.length > 0) {
2674
+ cleaned[event] = filteredEntries;
2675
+ }
2676
+ }
2677
+ return Object.keys(cleaned).length > 0 ? cleaned : void 0;
2678
+ }
2679
+ function hasOlakaiHooksInstalled2(settings) {
2680
+ const hooks = settings.hooks;
2681
+ if (!hooks) return false;
2682
+ for (const entries of Object.values(hooks)) {
2683
+ if (!Array.isArray(entries)) continue;
2684
+ if (entries.some(entryContainsOlakaiHook)) return true;
2685
+ }
2686
+ return false;
2687
+ }
2688
+ function readJsonFile2(filePath) {
2689
+ try {
2690
+ if (!fs17.existsSync(filePath)) return null;
2691
+ const content = fs17.readFileSync(filePath, "utf-8");
2692
+ if (!content.trim()) return null;
2693
+ return JSON.parse(content);
2694
+ } catch {
2695
+ return null;
2696
+ }
2697
+ }
2698
+ function writeJsonFile2(filePath, data) {
2699
+ const dir = path17.dirname(filePath);
2700
+ if (!fs17.existsSync(dir)) {
2701
+ fs17.mkdirSync(dir, { recursive: true });
2702
+ }
2703
+ fs17.writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n", "utf-8");
2704
+ }
2705
+
2706
+ // src/monitor/plugins/gemini-cli/install.ts
2707
+ var GEMINI_CLI_SOURCE = "gemini-cli";
2708
+ var GEMINI_CLI_AGENT_SOURCE = "GEMINI_CLI";
2709
+ var GEMINI_CLI_AGENT_CATEGORY = "CODING";
2710
+ async function installGeminiCli(opts) {
2711
+ const projectRoot = opts.projectRoot ?? process.cwd();
2712
+ const homeDir = opts.homeDir ?? os10.homedir();
2713
+ const token = getValidToken();
2714
+ if (!token) {
2715
+ console.error("Not logged in. Run 'olakai login' first.");
2716
+ process.exit(1);
2717
+ }
2718
+ console.log("Setting up Gemini CLI monitoring for this workspace...\n");
2719
+ console.log(
2720
+ `Note: Gemini CLI hooks are installed globally per user (~/${GEMINI_HOME_DIRNAME}/${GEMINI_SETTINGS_FILENAME}).`
2721
+ );
2722
+ console.log(
2723
+ `Activity from this workspace (${projectRoot}) will be associated with`
2724
+ );
2725
+ console.log("the agent you select below.\n");
2726
+ const agent = await provisionSelfMonitorAgent({
2727
+ projectRoot,
2728
+ source: GEMINI_CLI_AGENT_SOURCE,
2729
+ displayName: "Gemini CLI",
2730
+ category: GEMINI_CLI_AGENT_CATEGORY
2731
+ });
2732
+ const monitoringEndpoint = `${getBaseUrl()}/api/monitoring/prompt`;
2733
+ const apiKey = agent.apiKey?.key;
2734
+ if (!apiKey) {
2735
+ console.error(
2736
+ "Internal error: agent provisioned but no API key returned. Please re-run 'olakai monitor init'."
2737
+ );
2738
+ process.exit(1);
2739
+ }
2740
+ const geminiDir = getGeminiHomeDir(homeDir);
2741
+ if (!fs18.existsSync(geminiDir)) {
2742
+ fs18.mkdirSync(geminiDir, { recursive: true });
2743
+ }
2744
+ const settingsPath = getGeminiSettingsPath(homeDir);
2745
+ const existingSettings = readJsonFile2(settingsPath) ?? {};
2746
+ const mergedHooks = mergeHooksSettings2(existingSettings.hooks);
2747
+ const updatedSettings = {
2748
+ ...existingSettings,
2749
+ hooks: mergedHooks
2750
+ };
2751
+ writeJsonFile2(settingsPath, updatedSettings);
2752
+ const monitorConfig = {
2753
+ agentId: agent.id,
2754
+ apiKey,
2755
+ agentName: agent.name,
2756
+ source: GEMINI_CLI_SOURCE,
2757
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
2758
+ monitoringEndpoint
2759
+ };
2760
+ writeGeminiCliConfig(projectRoot, monitorConfig);
2761
+ const configPath = getGeminiCliConfigPath(projectRoot);
2762
+ const configRel = path18.relative(projectRoot, configPath);
2763
+ const settingsDisplay = `~/${path18.join(GEMINI_HOME_DIRNAME, GEMINI_SETTINGS_FILENAME)}`;
2764
+ console.log("");
2765
+ console.log(`\u2713 Agent "${agent.name}" configured (ID: ${agent.id})`);
2766
+ if (agent.apiKey?.key) {
2767
+ console.log("\u2713 API key generated");
2768
+ }
2769
+ console.log(`\u2713 Gemini CLI hooks configured in ${settingsDisplay}`);
2770
+ console.log(`\u2713 Monitor config saved to ${configRel}`);
2771
+ console.log("");
2772
+ console.log(
2773
+ "Congrats! Monitoring is now active. Gemini CLI will report activity to Olakai"
2774
+ );
2775
+ console.log(`on each model turn.`);
2776
+ console.log("");
2777
+ console.log(
2778
+ `\u26A0 Ensure ${OLAKAI_DIR}/ is in your .gitignore (it contains your API key)`
2779
+ );
2780
+ console.log(
2781
+ `\u26A0 Gemini CLI hooks require gemini >= 0.26.0. Earlier versions silently skip them.`
2782
+ );
2783
+ console.log("");
2784
+ console.log("To check status: olakai monitor status --tool gemini-cli");
2785
+ console.log("To disable: olakai monitor disable --tool gemini-cli");
2786
+ return {
2787
+ agentId: agent.id,
2788
+ agentName: agent.name,
2789
+ source: GEMINI_CLI_SOURCE,
2790
+ monitoringEndpoint
2791
+ };
2792
+ }
2793
+ async function uninstallGeminiCli(opts) {
2794
+ const projectRoot = opts.projectRoot ?? process.cwd();
2795
+ const homeDir = opts.homeDir ?? os10.homedir();
2796
+ const settingsPath = getGeminiSettingsPath(homeDir);
2797
+ const settings = readJsonFile2(settingsPath);
2798
+ if (settings && hasOlakaiHooksInstalled2(settings)) {
2799
+ const cleanedHooks = removeOlakaiHooks(settings.hooks);
2800
+ const next = { ...settings };
2801
+ if (cleanedHooks === void 0) {
2802
+ delete next.hooks;
2803
+ } else {
2804
+ next.hooks = cleanedHooks;
2805
+ }
2806
+ const remainingKeys = Object.keys(next).filter((k) => k !== "hooks");
2807
+ const cleanedHasNoHooks = !next.hooks || Object.keys(next.hooks).length === 0;
2808
+ if (cleanedHasNoHooks && remainingKeys.length === 0) {
2809
+ try {
2810
+ fs18.unlinkSync(settingsPath);
2811
+ } catch {
2812
+ }
2813
+ } else {
2814
+ writeJsonFile2(settingsPath, next);
2815
+ }
2816
+ console.log(
2817
+ `\u2713 Olakai hooks removed from ~/${path18.join(GEMINI_HOME_DIRNAME, GEMINI_SETTINGS_FILENAME)}`
2818
+ );
2819
+ } else {
2820
+ console.log("No Olakai hooks found in Gemini CLI settings.");
2821
+ }
2822
+ if (!opts.keepConfig) {
2823
+ const configPath = getGeminiCliConfigPath(projectRoot);
2824
+ const configRel = path18.relative(projectRoot, configPath);
2825
+ if (deleteGeminiCliConfig(projectRoot)) {
2826
+ console.log(`\u2713 Monitor config removed (${configRel})`);
2827
+ }
2828
+ } else {
2829
+ const configPath = getGeminiCliConfigPath(projectRoot);
2830
+ const configRel = path18.relative(projectRoot, configPath);
2831
+ console.log(`Monitor config retained at ${configRel}`);
2832
+ }
2833
+ console.log("");
2834
+ console.log(
2835
+ "Monitoring disabled. Run 'olakai monitor init --tool gemini-cli' to re-enable."
2836
+ );
2837
+ }
2838
+
2839
+ // src/monitor/plugins/gemini-cli/status.ts
2840
+ import * as os11 from "os";
2841
+ import * as path19 from "path";
2842
+ async function getGeminiCliStatus(opts) {
2843
+ const projectRoot = opts?.projectRoot ?? process.cwd();
2844
+ const homeDir = opts?.homeDir ?? os11.homedir();
2845
+ const configPath = getGeminiCliConfigPath(projectRoot);
2846
+ const config = loadGeminiCliConfig(projectRoot);
2847
+ const settings = readJsonFile2(getGeminiSettingsPath(homeDir));
2848
+ const hooksConfigured = settings ? hasOlakaiHooksInstalled2(settings) : false;
2849
+ if (!config) {
2850
+ return {
2851
+ toolId: "gemini-cli",
2852
+ configured: false,
2853
+ hooksConfigured,
2854
+ configPath,
2855
+ notes: hooksConfigured ? [
2856
+ `Gemini CLI hooks present in ~/${GEMINI_HOME_DIRNAME}/${GEMINI_SETTINGS_FILENAME} but no monitor config in this workspace \u2014 re-run init.`
2857
+ ] : []
2858
+ };
2859
+ }
2860
+ return {
2861
+ toolId: "gemini-cli",
2862
+ configured: true,
2863
+ hooksConfigured,
2864
+ agentId: config.agentId,
2865
+ agentName: config.agentName,
2866
+ source: config.source,
2867
+ apiKeyMasked: config.apiKey.slice(0, 12) + "...",
2868
+ monitoringEndpoint: config.monitoringEndpoint,
2869
+ configuredAt: config.createdAt,
2870
+ configPath
2871
+ };
2872
+ }
2873
+
2874
+ // src/monitor/plugins/gemini-cli/pairing-state.ts
2875
+ import * as fs19 from "fs";
2876
+ import * as os12 from "os";
2877
+ import * as path20 from "path";
2878
+ var STATE_DIR_SEGMENTS3 = [".olakai", "gemini-pairings"];
2879
+ function getPairingsDir2(homeDir) {
2880
+ return path20.join(homeDir, ...STATE_DIR_SEGMENTS3);
2881
+ }
2882
+ function sanitizeKeyFragment2(value) {
2883
+ return value.replace(/[^A-Za-z0-9._-]/g, "_");
2884
+ }
2885
+ function getPairingFile2(sessionId, homeDir) {
2886
+ return path20.join(
2887
+ getPairingsDir2(homeDir),
2888
+ `${sanitizeKeyFragment2(sessionId)}.json`
2889
+ );
2890
+ }
2891
+ function ensurePairingsDir(homeDir) {
2892
+ const dir = getPairingsDir2(homeDir);
2893
+ try {
2894
+ if (!fs19.existsSync(dir)) {
2895
+ fs19.mkdirSync(dir, { recursive: true, mode: 448 });
2896
+ }
2897
+ } catch {
2898
+ }
2899
+ }
2900
+ function readPendingTurn(sessionId, homeDir = os12.homedir()) {
2901
+ if (!sessionId) return null;
2902
+ const file = getPairingFile2(sessionId, homeDir);
2903
+ try {
2904
+ if (!fs19.existsSync(file)) return null;
2905
+ const raw = fs19.readFileSync(file, "utf-8");
2906
+ return JSON.parse(raw);
2907
+ } catch {
2908
+ return null;
2909
+ }
2910
+ }
2911
+ function writePendingTurn(sessionId, pending, homeDir = os12.homedir()) {
2912
+ if (!sessionId) return;
2913
+ ensurePairingsDir(homeDir);
2914
+ const file = getPairingFile2(sessionId, homeDir);
2915
+ try {
2916
+ fs19.writeFileSync(file, JSON.stringify(pending, null, 2) + "\n", "utf-8");
2917
+ try {
2918
+ fs19.chmodSync(file, 384);
2919
+ } catch {
2920
+ }
2921
+ } catch {
2922
+ }
2923
+ }
2924
+ function updatePendingTurn(sessionId, mutator, homeDir = os12.homedir()) {
2925
+ if (!sessionId) return null;
2926
+ const current = readPendingTurn(sessionId, homeDir) ?? {
2927
+ prompt: "",
2928
+ stashedAt: (/* @__PURE__ */ new Date()).toISOString(),
2929
+ toolCallCount: 0,
2930
+ toolNames: []
2931
+ };
2932
+ const next = mutator(current);
2933
+ writePendingTurn(sessionId, next, homeDir);
2934
+ return next;
2935
+ }
2936
+ function clearPendingTurn(sessionId, homeDir = os12.homedir()) {
2937
+ if (!sessionId) return;
2938
+ const file = getPairingFile2(sessionId, homeDir);
2939
+ try {
2940
+ if (fs19.existsSync(file)) fs19.unlinkSync(file);
2941
+ } catch {
2942
+ }
2943
+ }
2944
+
2945
+ // src/monitor/plugins/gemini-cli/hook.ts
2946
+ var noopDebug4 = () => {
2947
+ };
2948
+ var SUPPORTED_EVENTS2 = /* @__PURE__ */ new Set([
2949
+ "SessionStart",
2950
+ "SessionEnd",
2951
+ "BeforeModel",
2952
+ "AfterModel",
2953
+ "BeforeTool",
2954
+ "AfterTool"
2955
+ ]);
2956
+ function isSupportedGeminiEvent(eventName) {
2957
+ return SUPPORTED_EVENTS2.has(eventName);
2958
+ }
2959
+ function asString2(value) {
2960
+ return typeof value === "string" && value.trim() ? value : void 0;
2961
+ }
2962
+ function asNumber2(value) {
2963
+ if (typeof value !== "number" || !Number.isFinite(value)) return 0;
2964
+ return value;
2965
+ }
2966
+ function isRecord(value) {
2967
+ return typeof value === "object" && value !== null;
2968
+ }
2969
+ function messageContentToText(content) {
2970
+ if (typeof content === "string") return content;
2971
+ if (Array.isArray(content)) {
2972
+ return content.map((p) => typeof p === "string" ? p : asString2(p?.text) ?? "").join("");
2973
+ }
2974
+ if (isRecord(content) && Array.isArray(content.parts)) {
2975
+ return messageContentToText(content.parts);
2976
+ }
2977
+ return "";
2978
+ }
2979
+ function extractStructuredPrompt(llmRequest) {
2980
+ if (!isRecord(llmRequest) || !Array.isArray(llmRequest.messages)) {
2981
+ return void 0;
2982
+ }
2983
+ for (let i = llmRequest.messages.length - 1; i >= 0; i--) {
2984
+ const msg = llmRequest.messages[i];
2985
+ if (msg && msg.role === "user") {
2986
+ const text = messageContentToText(msg.content).trim();
2987
+ if (text) return text;
2988
+ }
2989
+ }
2990
+ return void 0;
2991
+ }
2992
+ function extractStructuredModel(llmRequest) {
2993
+ return isRecord(llmRequest) ? asString2(llmRequest.model) : void 0;
2994
+ }
2995
+ function extractStructuredResponseDelta(llmResponse) {
2996
+ if (!isRecord(llmResponse)) return "";
2997
+ if (typeof llmResponse.text === "string" && llmResponse.text) {
2998
+ return llmResponse.text;
2999
+ }
3000
+ const parts = llmResponse.candidates?.[0]?.content?.parts;
3001
+ if (Array.isArray(parts)) {
3002
+ return parts.map((p) => typeof p === "string" ? p : asString2(p?.text) ?? "").join("");
3003
+ }
3004
+ return "";
3005
+ }
3006
+ function isStructuredStreamComplete(llmResponse) {
3007
+ if (!isRecord(llmResponse) || !Array.isArray(llmResponse.candidates)) {
3008
+ return false;
3009
+ }
3010
+ return llmResponse.candidates.some((c) => asString2(c?.finishReason));
3011
+ }
3012
+ function extractGeminiTokens(payload) {
3013
+ const meta = payload.llm_response?.usageMetadata;
3014
+ if (isRecord(meta)) {
3015
+ const inputTokens2 = asNumber2(meta.promptTokenCount);
3016
+ const outputTokens2 = asNumber2(meta.candidatesTokenCount);
3017
+ const explicitTotal2 = asNumber2(meta.totalTokenCount);
3018
+ const totalTokens2 = explicitTotal2 > 0 ? explicitTotal2 : inputTokens2 + outputTokens2;
3019
+ return { inputTokens: inputTokens2, outputTokens: outputTokens2, totalTokens: totalTokens2 };
3020
+ }
3021
+ const usage = payload.usage ?? {};
3022
+ const inputTokens = asNumber2(usage.input_tokens);
3023
+ const outputTokens = asNumber2(usage.output_tokens);
3024
+ const explicitTotal = asNumber2(usage.total_tokens);
3025
+ const totalTokens = explicitTotal > 0 ? explicitTotal : inputTokens + outputTokens;
3026
+ return { inputTokens, outputTokens, totalTokens };
3027
+ }
3028
+ function usesStructuredSchema(event) {
3029
+ return isRecord(event.llm_request) || isRecord(event.llm_response);
3030
+ }
3031
+ function buildPayload(args) {
3032
+ if (!args.prompt && !args.response) return null;
3033
+ const customData = {
3034
+ hookEvent: args.hookEvent ?? "AfterModel",
3035
+ sessionId: args.sessionId,
3036
+ cwd: args.cwd ?? "",
3037
+ inputTokens: args.inputTokens,
3038
+ outputTokens: args.outputTokens,
3039
+ toolCallCount: args.toolCallCount,
3040
+ toolNames: args.toolNames
3041
+ };
3042
+ if (args.sessionStartedAt) {
3043
+ customData.sessionStartedAt = args.sessionStartedAt;
3044
+ }
3045
+ if (args.extra) {
3046
+ customData.stashedExtra = args.extra;
3047
+ }
3048
+ return {
3049
+ prompt: args.prompt,
3050
+ response: args.response,
3051
+ chatId: args.sessionId,
3052
+ source: args.config.source,
3053
+ modelName: args.modelName,
3054
+ tokens: args.totalTokens,
3055
+ customData
3056
+ };
3057
+ }
3058
+ function buildAfterModelPayload(event, stashed, config) {
3059
+ const sessionId = asString2(event.session_id) ?? `gemini-cli-${Date.now()}`;
3060
+ const prompt = stashed?.prompt ?? asString2(event.prompt) ?? "";
3061
+ const response = asString2(event.response) ?? "";
3062
+ if (!prompt && !response) return null;
3063
+ const { inputTokens, outputTokens, totalTokens } = extractGeminiTokens(event);
3064
+ const modelName = asString2(event.model) ?? stashed?.modelName ?? void 0;
3065
+ return buildPayload({
3066
+ sessionId,
3067
+ prompt,
3068
+ response,
3069
+ modelName,
3070
+ inputTokens,
3071
+ outputTokens,
3072
+ totalTokens,
3073
+ cwd: asString2(event.cwd) ?? stashed?.cwd,
3074
+ toolCallCount: stashed?.toolCallCount ?? 0,
3075
+ toolNames: stashed?.toolNames ?? [],
3076
+ sessionStartedAt: stashed?.sessionStartedAt,
3077
+ extra: stashed?.extra,
3078
+ hookEvent: event.hook_event_name ?? "AfterModel",
3079
+ config
3080
+ });
3081
+ }
3082
+ function handleGeminiHook(eventName, payloadJson, config, options = {}) {
3083
+ const debugLog6 = options.debugLog ?? noopDebug4;
3084
+ if (!isSupportedGeminiEvent(eventName)) {
3085
+ debugLog6("hook-unknown-event", eventName);
3086
+ return null;
3087
+ }
3088
+ const event = payloadJson ?? {};
3089
+ debugLog6("event-parsed", { eventName, event });
3090
+ const structured = usesStructuredSchema(event);
3091
+ const sessionId = asString2(event.session_id) ?? "";
3092
+ if (!sessionId) {
3093
+ if (eventName !== "AfterModel") {
3094
+ debugLog6("missing-session-id", { eventName });
3095
+ return null;
3096
+ }
3097
+ if (structured) {
3098
+ const prompt = extractStructuredPrompt(event.llm_request) ?? "";
3099
+ const response = extractStructuredResponseDelta(event.llm_response);
3100
+ const { inputTokens, outputTokens, totalTokens } = extractGeminiTokens(event);
3101
+ const payload2 = buildPayload({
3102
+ sessionId: `gemini-cli-${Date.now()}`,
3103
+ prompt,
3104
+ response,
3105
+ modelName: extractStructuredModel(event.llm_request),
3106
+ inputTokens,
3107
+ outputTokens,
3108
+ totalTokens,
3109
+ cwd: asString2(event.cwd),
3110
+ toolCallCount: 0,
3111
+ toolNames: [],
3112
+ config
3113
+ });
3114
+ if (!payload2) debugLog6("after-model-empty-no-session", { eventName });
3115
+ return payload2;
3116
+ }
3117
+ const payload = buildAfterModelPayload(event, null, config);
3118
+ if (!payload) {
3119
+ debugLog6("after-model-empty-no-session", { eventName });
3120
+ return null;
3121
+ }
3122
+ return payload;
3123
+ }
3124
+ switch (eventName) {
3125
+ case "SessionStart": {
3126
+ writePendingTurn(
3127
+ sessionId,
3128
+ {
3129
+ prompt: "",
3130
+ accumulatedResponse: "",
3131
+ cwd: asString2(event.cwd),
3132
+ sessionStartedAt: (/* @__PURE__ */ new Date()).toISOString(),
3133
+ stashedAt: (/* @__PURE__ */ new Date()).toISOString(),
3134
+ toolCallCount: 0,
3135
+ toolNames: []
3136
+ },
3137
+ options.homeDir
3138
+ );
3139
+ return null;
3140
+ }
3141
+ case "BeforeModel": {
3142
+ const structuredPrompt = extractStructuredPrompt(event.llm_request);
3143
+ const structuredModel = extractStructuredModel(event.llm_request);
3144
+ updatePendingTurn(
3145
+ sessionId,
3146
+ (current) => ({
3147
+ ...current,
3148
+ prompt: structuredPrompt ?? asString2(event.prompt) ?? "",
3149
+ modelName: structuredModel ?? asString2(event.model) ?? current.modelName,
3150
+ cwd: asString2(event.cwd) ?? current.cwd,
3151
+ stashedAt: (/* @__PURE__ */ new Date()).toISOString(),
3152
+ // New model call — reset the streamed-response accumulator and
3153
+ // the per-turn tool counters so they only reflect THIS call.
3154
+ accumulatedResponse: "",
3155
+ toolCallCount: 0,
3156
+ toolNames: []
3157
+ }),
3158
+ options.homeDir
3159
+ );
3160
+ return null;
3161
+ }
3162
+ case "BeforeTool": {
3163
+ const toolName = asString2(event.tool_name);
3164
+ updatePendingTurn(
3165
+ sessionId,
3166
+ (current) => {
3167
+ const nextToolNames = toolName ? current.toolNames.includes(toolName) ? current.toolNames : [...current.toolNames, toolName] : current.toolNames;
3168
+ return {
3169
+ ...current,
3170
+ toolCallCount: current.toolCallCount + 1,
3171
+ toolNames: nextToolNames
3172
+ };
3173
+ },
3174
+ options.homeDir
3175
+ );
3176
+ return null;
3177
+ }
3178
+ case "AfterTool": {
3179
+ return null;
3180
+ }
3181
+ case "AfterModel": {
3182
+ if (!structured) {
3183
+ const stashed = readPendingTurn(sessionId, options.homeDir);
3184
+ const payload2 = buildAfterModelPayload(event, stashed, config);
3185
+ clearPendingTurn(sessionId, options.homeDir);
3186
+ if (!payload2) debugLog6("after-model-empty", { sessionId });
3187
+ return payload2;
3188
+ }
3189
+ const delta = extractStructuredResponseDelta(event.llm_response);
3190
+ const complete = isStructuredStreamComplete(event.llm_response);
3191
+ const structuredPrompt = extractStructuredPrompt(event.llm_request);
3192
+ const structuredModel = extractStructuredModel(event.llm_request);
3193
+ const updated = updatePendingTurn(
3194
+ sessionId,
3195
+ (current) => ({
3196
+ ...current,
3197
+ prompt: structuredPrompt ?? current.prompt,
3198
+ modelName: structuredModel ?? current.modelName,
3199
+ cwd: asString2(event.cwd) ?? current.cwd,
3200
+ accumulatedResponse: (current.accumulatedResponse ?? "") + delta
3201
+ }),
3202
+ options.homeDir
3203
+ );
3204
+ if (!complete) {
3205
+ debugLog6("after-model-streaming", {
3206
+ sessionId,
3207
+ deltaLen: delta.length
3208
+ });
3209
+ return null;
3210
+ }
3211
+ const { inputTokens, outputTokens, totalTokens } = extractGeminiTokens(event);
3212
+ const response = updated?.accumulatedResponse ?? "";
3213
+ const payload = buildPayload({
3214
+ sessionId,
3215
+ prompt: structuredPrompt ?? updated?.prompt ?? "",
3216
+ response,
3217
+ modelName: structuredModel ?? updated?.modelName,
3218
+ inputTokens,
3219
+ outputTokens,
3220
+ totalTokens,
3221
+ cwd: asString2(event.cwd) ?? updated?.cwd,
3222
+ toolCallCount: updated?.toolCallCount ?? 0,
3223
+ toolNames: updated?.toolNames ?? [],
3224
+ sessionStartedAt: updated?.sessionStartedAt,
3225
+ extra: updated?.extra,
3226
+ hookEvent: event.hook_event_name ?? "AfterModel",
3227
+ config
3228
+ });
3229
+ updatePendingTurn(
3230
+ sessionId,
3231
+ (current) => ({
3232
+ ...current,
3233
+ accumulatedResponse: "",
3234
+ toolCallCount: 0,
3235
+ toolNames: []
3236
+ }),
3237
+ options.homeDir
3238
+ );
3239
+ if (!payload) {
3240
+ debugLog6("after-model-empty", { sessionId });
3241
+ } else {
3242
+ debugLog6("payload-built", {
3243
+ sessionId,
3244
+ responseLen: response.length,
3245
+ totalTokens
3246
+ });
3247
+ }
3248
+ return payload;
3249
+ }
3250
+ case "SessionEnd": {
3251
+ clearPendingTurn(sessionId, options.homeDir);
3252
+ return null;
3253
+ }
3254
+ }
3255
+ return null;
3256
+ }
3257
+
3258
+ // src/monitor/plugins/gemini-cli/index.ts
3259
+ var TOOL_ID4 = "gemini-cli";
3260
+ function debugLog4(label, data) {
3261
+ if (process.env.OLAKAI_MONITOR_DEBUG !== "1") return;
3262
+ try {
3263
+ const logPath = `/tmp/olakai-monitor-debug-${process.pid}.log`;
3264
+ const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] gemini-cli/${label}: ${typeof data === "string" ? data : JSON.stringify(data, null, 2)}
3265
+ `;
3266
+ fs20.appendFileSync(logPath, line, "utf-8");
3267
+ } catch {
3268
+ }
3269
+ }
3270
+ function resolveGeminiCliProjectRoot(eventData, fallbackCwd) {
3271
+ const payloadCwd = typeof eventData.cwd === "string" && eventData.cwd.trim() ? eventData.cwd : fallbackCwd;
3272
+ return findConfiguredWorkspace(payloadCwd, [TOOL_ID4]);
3273
+ }
3274
+ var geminiCliPlugin = {
3275
+ id: TOOL_ID4,
3276
+ displayName: "Gemini CLI",
3277
+ install(opts) {
3278
+ return installGeminiCli(opts);
3279
+ },
3280
+ uninstall(opts) {
3281
+ return uninstallGeminiCli(opts);
3282
+ },
3283
+ status(opts) {
3284
+ return getGeminiCliStatus(opts);
3285
+ },
3286
+ async handleHook(eventName, payloadJson) {
3287
+ const eventData = payloadJson ?? {};
3288
+ debugLog4("hook-fired", { eventName, hasPayload: payloadJson != null });
3289
+ const projectRoot = resolveGeminiCliProjectRoot(
3290
+ eventData,
3291
+ process.cwd()
3292
+ );
3293
+ if (!projectRoot) {
3294
+ debugLog4("config-not-found", {
3295
+ startDir: typeof eventData.cwd === "string" && eventData.cwd.trim() ? eventData.cwd : process.cwd()
3296
+ });
3297
+ return null;
3298
+ }
3299
+ const config = loadGeminiCliConfig(projectRoot);
3300
+ if (!config) {
3301
+ debugLog4("monitor-config-missing", { projectRoot });
3302
+ return null;
3303
+ }
3304
+ const payload = handleGeminiHook(eventName, eventData, config, {
3305
+ debugLog: debugLog4
3306
+ });
3307
+ if (!payload) return null;
3308
+ return {
3309
+ payload,
3310
+ transport: {
3311
+ endpoint: config.monitoringEndpoint,
3312
+ apiKey: config.apiKey,
3313
+ projectRoot
3314
+ }
3315
+ };
3316
+ },
3317
+ async detectInstalled(opts) {
3318
+ const projectRoot = opts?.projectRoot ?? process.cwd();
3319
+ try {
3320
+ if (fs20.existsSync(getGeminiCliConfigPath(projectRoot))) {
3321
+ return true;
3322
+ }
3323
+ } catch {
3324
+ }
3325
+ try {
3326
+ if (fs20.existsSync(getGeminiSettingsPath())) {
3327
+ return true;
3328
+ }
3329
+ if (fs20.existsSync(getGeminiHomeDir())) {
3330
+ return true;
3331
+ }
3332
+ } catch {
3333
+ }
3334
+ return detectGeminiBinaryOnPath();
3335
+ }
3336
+ };
3337
+ function detectGeminiBinaryOnPath() {
3338
+ try {
3339
+ const probe = spawnSync4(
3340
+ process.platform === "win32" ? "where" : "which",
3341
+ ["gemini"],
3342
+ {
3343
+ stdio: ["ignore", "pipe", "ignore"],
3344
+ timeout: 1e3
3345
+ }
3346
+ );
3347
+ if (probe.status === 0 && probe.stdout && probe.stdout.toString().trim()) {
3348
+ return true;
3349
+ }
3350
+ } catch {
3351
+ }
3352
+ return false;
3353
+ }
3354
+ registerPlugin(geminiCliPlugin);
3355
+
3356
+ // src/monitor/plugins/antigravity/index.ts
3357
+ import * as fs25 from "fs";
3358
+ import { spawnSync as spawnSync5 } from "child_process";
3359
+
3360
+ // src/monitor/plugins/antigravity/install.ts
3361
+ import * as fs23 from "fs";
3362
+ import * as os14 from "os";
3363
+ import * as path24 from "path";
3364
+
3365
+ // src/monitor/plugins/antigravity/config.ts
3366
+ import * as fs21 from "fs";
3367
+ import * as path21 from "path";
3368
+ function getAntigravityConfigPath(projectRoot) {
3369
+ return getMonitorConfigPath(projectRoot, "antigravity");
3370
+ }
3371
+ function loadAntigravityConfig(projectRoot) {
3372
+ const filePath = getAntigravityConfigPath(projectRoot);
3373
+ try {
3374
+ if (!fs21.existsSync(filePath)) return null;
3375
+ const raw = fs21.readFileSync(filePath, "utf-8");
3376
+ return JSON.parse(raw);
3377
+ } catch {
3378
+ return null;
3379
+ }
3380
+ }
3381
+ function writeAntigravityConfig(projectRoot, config) {
3382
+ const filePath = getAntigravityConfigPath(projectRoot);
3383
+ const dir = path21.dirname(filePath);
3384
+ if (!fs21.existsSync(dir)) {
3385
+ fs21.mkdirSync(dir, { recursive: true });
3386
+ }
3387
+ fs21.writeFileSync(filePath, JSON.stringify(config, null, 2) + "\n", "utf-8");
3388
+ try {
3389
+ fs21.chmodSync(filePath, 384);
3390
+ } catch {
3391
+ }
3392
+ }
3393
+ function deleteAntigravityConfig(projectRoot) {
3394
+ const filePath = getAntigravityConfigPath(projectRoot);
3395
+ if (!fs21.existsSync(filePath)) return false;
3396
+ try {
3397
+ fs21.unlinkSync(filePath);
3398
+ return true;
3399
+ } catch {
3400
+ return false;
3401
+ }
3402
+ }
3403
+
3404
+ // src/monitor/plugins/antigravity/paths.ts
3405
+ import * as os13 from "os";
3406
+ import * as path22 from "path";
3407
+ var GEMINI_HOME_DIRNAME2 = ".gemini";
3408
+ var ANTIGRAVITY_CONFIG_DIRNAME = "config";
3409
+ var ANTIGRAVITY_HOOKS_FILENAME = "hooks.json";
3410
+ var ANTIGRAVITY_CLI_DIRNAME = "antigravity-cli";
3411
+ function getGeminiHomeDir2(homeDir = os13.homedir()) {
3412
+ return path22.join(homeDir, GEMINI_HOME_DIRNAME2);
3413
+ }
3414
+ function getGeminiConfigDir(homeDir = os13.homedir()) {
3415
+ return path22.join(getGeminiHomeDir2(homeDir), ANTIGRAVITY_CONFIG_DIRNAME);
3416
+ }
3417
+ function getGeminiConfigHooksPath(homeDir = os13.homedir()) {
3418
+ return path22.join(getGeminiConfigDir(homeDir), ANTIGRAVITY_HOOKS_FILENAME);
3419
+ }
3420
+ function getAntigravityCliDir(homeDir = os13.homedir()) {
3421
+ return path22.join(getGeminiHomeDir2(homeDir), ANTIGRAVITY_CLI_DIRNAME);
3422
+ }
3423
+
3424
+ // src/monitor/plugins/antigravity/settings.ts
3425
+ import * as fs22 from "fs";
3426
+ import * as path23 from "path";
3427
+ var OLAKAI_HOOK_NAME = "olakai-monitor";
3428
+ var OLAKAI_HOOK_MARKER5 = "olakai monitor hook --tool antigravity";
3429
+ var ANTIGRAVITY_HOOK_TIMEOUT_SECONDS = 30;
3430
+ var SUPPORTED_ANTIGRAVITY_HOOK_EVENTS = ["Stop"];
3431
+ function buildOlakaiHookCommand(event) {
3432
+ return {
3433
+ type: "command",
3434
+ command: `${OLAKAI_HOOK_MARKER5} ${event}`,
3435
+ timeout: ANTIGRAVITY_HOOK_TIMEOUT_SECONDS
3436
+ };
3437
+ }
3438
+ function buildOlakaiHookEntry2() {
3439
+ const entry = {};
3440
+ for (const event of SUPPORTED_ANTIGRAVITY_HOOK_EVENTS) {
3441
+ entry[event] = [buildOlakaiHookCommand(event)];
3442
+ }
3443
+ return entry;
3444
+ }
3445
+ function mergeHooksFile(existing) {
3446
+ const merged = { ...existing ?? {} };
3447
+ merged[OLAKAI_HOOK_NAME] = buildOlakaiHookEntry2();
3448
+ return merged;
3449
+ }
3450
+ function entryContainsOlakaiHook2(entry) {
3451
+ for (const commands of Object.values(entry)) {
3452
+ if (!Array.isArray(commands)) continue;
3453
+ if (commands.some(
3454
+ (c) => typeof c?.command === "string" && c.command.includes(OLAKAI_HOOK_MARKER5)
3455
+ )) {
3456
+ return true;
3457
+ }
3458
+ }
3459
+ return false;
3460
+ }
3461
+ function removeOlakaiHooks2(existing) {
3462
+ if (!existing) return void 0;
3463
+ const cleaned = {};
3464
+ for (const [name, entry] of Object.entries(existing)) {
3465
+ if (name === OLAKAI_HOOK_NAME) continue;
3466
+ if (!entry || typeof entry !== "object") continue;
3467
+ const cleanedEntry = {};
3468
+ for (const [event, commands] of Object.entries(entry)) {
3469
+ if (!Array.isArray(commands)) {
3470
+ cleanedEntry[event] = commands;
3471
+ continue;
3472
+ }
3473
+ const remaining = commands.filter(
3474
+ (c) => typeof c?.command !== "string" || !c.command.includes(OLAKAI_HOOK_MARKER5)
3475
+ );
3476
+ if (remaining.length > 0) {
3477
+ cleanedEntry[event] = remaining;
3478
+ }
3479
+ }
3480
+ if (Object.keys(cleanedEntry).length > 0) {
3481
+ cleaned[name] = cleanedEntry;
3482
+ }
3483
+ }
3484
+ return Object.keys(cleaned).length > 0 ? cleaned : void 0;
3485
+ }
3486
+ function hasOlakaiHook(hooksFile) {
3487
+ if (!hooksFile) return false;
3488
+ for (const entry of Object.values(hooksFile)) {
3489
+ if (entry && entryContainsOlakaiHook2(entry)) return true;
3490
+ }
3491
+ return false;
3492
+ }
3493
+ function readJsonFile3(filePath) {
3494
+ try {
3495
+ if (!fs22.existsSync(filePath)) return null;
3496
+ const content = fs22.readFileSync(filePath, "utf-8");
3497
+ if (!content.trim()) return null;
3498
+ return JSON.parse(content);
3499
+ } catch {
3500
+ return null;
3501
+ }
3502
+ }
3503
+ function writeJsonFile3(filePath, data) {
3504
+ const dir = path23.dirname(filePath);
3505
+ if (!fs22.existsSync(dir)) {
3506
+ fs22.mkdirSync(dir, { recursive: true });
3507
+ }
3508
+ fs22.writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n", "utf-8");
3509
+ }
3510
+
3511
+ // src/monitor/plugins/antigravity/install.ts
3512
+ var ANTIGRAVITY_SOURCE = "antigravity";
3513
+ var ANTIGRAVITY_AGENT_SOURCE = "ANTIGRAVITY";
3514
+ var ANTIGRAVITY_AGENT_CATEGORY = "CODING";
3515
+ var HOOKS_DISPLAY = "~/.gemini/config/hooks.json";
3516
+ async function installAntigravity(opts) {
3517
+ const projectRoot = opts.projectRoot ?? process.cwd();
3518
+ const homeDir = opts.homeDir ?? os14.homedir();
3519
+ const token = getValidToken();
3520
+ if (!token) {
3521
+ console.error("Not logged in. Run 'olakai login' first.");
3522
+ process.exit(1);
3523
+ }
3524
+ console.log("Setting up Antigravity monitoring for this workspace...\n");
3525
+ console.log(
3526
+ `Note: Antigravity hooks are installed globally per user (${HOOKS_DISPLAY}).`
3527
+ );
3528
+ console.log(
3529
+ `Activity from this workspace (${projectRoot}) will be associated with`
3530
+ );
3531
+ console.log("the agent you select below.\n");
3532
+ const agent = await provisionSelfMonitorAgent({
3533
+ projectRoot,
3534
+ source: ANTIGRAVITY_AGENT_SOURCE,
3535
+ displayName: "Antigravity",
3536
+ category: ANTIGRAVITY_AGENT_CATEGORY
3537
+ });
3538
+ const monitoringEndpoint = `${getBaseUrl()}/api/monitoring/prompt`;
3539
+ const apiKey = agent.apiKey?.key;
3540
+ if (!apiKey) {
3541
+ console.error(
3542
+ "Internal error: agent provisioned but no API key returned. Please re-run 'olakai monitor init'."
3543
+ );
3544
+ process.exit(1);
3545
+ }
3546
+ const configDir = getGeminiConfigDir(homeDir);
3547
+ if (!fs23.existsSync(configDir)) {
3548
+ fs23.mkdirSync(configDir, { recursive: true });
3549
+ }
3550
+ const hooksPath = getGeminiConfigHooksPath(homeDir);
3551
+ const existingHooks = readJsonFile3(hooksPath) ?? {};
3552
+ const mergedHooks = mergeHooksFile(existingHooks);
3553
+ writeJsonFile3(hooksPath, mergedHooks);
3554
+ const monitorConfig = {
3555
+ agentId: agent.id,
3556
+ apiKey,
3557
+ agentName: agent.name,
3558
+ source: ANTIGRAVITY_SOURCE,
3559
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
3560
+ monitoringEndpoint
3561
+ };
3562
+ writeAntigravityConfig(projectRoot, monitorConfig);
3563
+ const configPath = getAntigravityConfigPath(projectRoot);
3564
+ const configRel = path24.relative(projectRoot, configPath);
3565
+ console.log("");
3566
+ console.log(`\u2713 Agent "${agent.name}" configured (ID: ${agent.id})`);
3567
+ if (agent.apiKey?.key) {
3568
+ console.log("\u2713 API key generated");
3569
+ }
3570
+ console.log(`\u2713 Antigravity Stop hook configured in ${HOOKS_DISPLAY}`);
3571
+ console.log(`\u2713 Monitor config saved to ${configRel}`);
3572
+ console.log("");
3573
+ console.log(
3574
+ "Congrats! Monitoring is now active. Antigravity will report activity to Olakai"
3575
+ );
3576
+ console.log(`when the agent finishes a turn (Stop hook).`);
3577
+ console.log("");
3578
+ console.log(
3579
+ `\u26A0 Ensure ${OLAKAI_DIR}/ is in your .gitignore (it contains your API key)`
3580
+ );
3581
+ console.log(
3582
+ `\u26A0 Antigravity hooks are global (${HOOKS_DISPLAY}) and require an agy build with hooks support.`
3583
+ );
3584
+ console.log(
3585
+ `\u26A0 Turn capture is interactive-only \u2014 headless 'agy -p' runs skip hooks.`
3586
+ );
3587
+ console.log("");
3588
+ console.log("To check status: olakai monitor status --tool antigravity");
3589
+ console.log("To disable: olakai monitor disable --tool antigravity");
3590
+ return {
3591
+ agentId: agent.id,
3592
+ agentName: agent.name,
3593
+ source: ANTIGRAVITY_SOURCE,
3594
+ monitoringEndpoint
3595
+ };
3596
+ }
3597
+ async function uninstallAntigravity(opts) {
3598
+ const projectRoot = opts.projectRoot ?? process.cwd();
3599
+ const homeDir = opts.homeDir ?? os14.homedir();
3600
+ const hooksPath = getGeminiConfigHooksPath(homeDir);
3601
+ const hooksFile = readJsonFile3(hooksPath);
3602
+ if (hooksFile && hasOlakaiHook(hooksFile)) {
3603
+ const cleaned = removeOlakaiHooks2(hooksFile);
3604
+ if (cleaned === void 0) {
3605
+ try {
3606
+ fs23.unlinkSync(hooksPath);
3607
+ } catch {
3608
+ }
3609
+ } else {
3610
+ writeJsonFile3(hooksPath, cleaned);
3611
+ }
3612
+ console.log(`\u2713 Olakai hooks removed from ${HOOKS_DISPLAY}`);
3613
+ } else {
3614
+ console.log("No Olakai hooks found in Antigravity hooks.json.");
3615
+ }
3616
+ if (!opts.keepConfig) {
3617
+ const configPath = getAntigravityConfigPath(projectRoot);
3618
+ const configRel = path24.relative(projectRoot, configPath);
3619
+ if (deleteAntigravityConfig(projectRoot)) {
3620
+ console.log(`\u2713 Monitor config removed (${configRel})`);
3621
+ }
3622
+ } else {
3623
+ const configPath = getAntigravityConfigPath(projectRoot);
3624
+ const configRel = path24.relative(projectRoot, configPath);
3625
+ console.log(`Monitor config retained at ${configRel}`);
3626
+ }
3627
+ console.log("");
3628
+ console.log(
3629
+ "Monitoring disabled. Run 'olakai monitor init --tool antigravity' to re-enable."
3630
+ );
3631
+ }
3632
+
3633
+ // src/monitor/plugins/antigravity/status.ts
3634
+ import * as os15 from "os";
3635
+ import * as path25 from "path";
3636
+ var HOOKS_DISPLAY2 = "~/.gemini/config/hooks.json";
3637
+ async function getAntigravityStatus(opts) {
3638
+ const projectRoot = opts?.projectRoot ?? process.cwd();
3639
+ const homeDir = opts?.homeDir ?? os15.homedir();
3640
+ const configPath = getAntigravityConfigPath(projectRoot);
3641
+ const config = loadAntigravityConfig(projectRoot);
3642
+ const hooksFile = readJsonFile3(
3643
+ getGeminiConfigHooksPath(homeDir)
3644
+ );
3645
+ const hooksConfigured = hasOlakaiHook(hooksFile);
3646
+ if (!config) {
3647
+ return {
3648
+ toolId: "antigravity",
3649
+ configured: false,
3650
+ hooksConfigured,
3651
+ configPath,
3652
+ notes: hooksConfigured ? [
3653
+ `Antigravity hooks present in ${HOOKS_DISPLAY2} but no monitor config in this workspace \u2014 re-run init.`
3654
+ ] : []
3655
+ };
3656
+ }
3657
+ return {
3658
+ toolId: "antigravity",
3659
+ configured: true,
3660
+ hooksConfigured,
3661
+ agentId: config.agentId,
3662
+ agentName: config.agentName,
3663
+ source: config.source,
3664
+ apiKeyMasked: config.apiKey.slice(0, 12) + "...",
3665
+ monitoringEndpoint: config.monitoringEndpoint,
3666
+ configuredAt: config.createdAt,
3667
+ configPath
3668
+ };
3669
+ }
3670
+
3671
+ // src/monitor/plugins/antigravity/hook.ts
3672
+ import * as fs24 from "fs";
3673
+
3674
+ // src/monitor/plugins/antigravity/transcript.ts
3675
+ var USER_REQUEST_RE = /<USER_REQUEST>([\s\S]*?)<\/USER_REQUEST>/;
3676
+ function parseTranscriptLines(raw) {
3677
+ const lines = [];
3678
+ for (const line of raw.split("\n")) {
3679
+ const trimmed = line.trim();
3680
+ if (!trimmed) continue;
3681
+ try {
3682
+ const parsed = JSON.parse(trimmed);
3683
+ if (parsed && typeof parsed === "object") {
3684
+ lines.push(parsed);
3685
+ }
3686
+ } catch {
3687
+ }
3688
+ }
3689
+ return lines;
3690
+ }
3691
+ function asContentString(value) {
3692
+ return typeof value === "string" ? value : "";
3693
+ }
3694
+ function extractPrompt(lines) {
3695
+ let lastContent = null;
3696
+ for (const line of lines) {
3697
+ if (line?.type === "USER_INPUT") {
3698
+ lastContent = asContentString(line.content);
3699
+ }
3700
+ }
3701
+ if (lastContent === null) return "";
3702
+ const match = USER_REQUEST_RE.exec(lastContent);
3703
+ if (match) {
3704
+ return match[1].trim();
3705
+ }
3706
+ return lastContent.trim();
3707
+ }
3708
+ function extractResponse(lines) {
3709
+ let lastContent = null;
3710
+ for (const line of lines) {
3711
+ if (line?.type === "PLANNER_RESPONSE") {
3712
+ lastContent = asContentString(line.content);
3713
+ }
3714
+ }
3715
+ if (lastContent === null) return "";
3716
+ return lastContent.trim();
3717
+ }
3718
+
3719
+ // src/monitor/plugins/antigravity/hook.ts
3720
+ var noopDebug5 = () => {
3721
+ };
3722
+ var SUPPORTED_EVENTS3 = /* @__PURE__ */ new Set(["Stop"]);
3723
+ function isSupportedAntigravityEvent(eventName) {
3724
+ return SUPPORTED_EVENTS3.has(eventName);
3725
+ }
3726
+ function asString3(value) {
3727
+ return typeof value === "string" && value.trim() ? value : void 0;
3728
+ }
3729
+ function getWorkspacePath(payload) {
3730
+ const paths = payload.workspacePaths;
3731
+ if (Array.isArray(paths)) {
3732
+ const first = paths[0];
3733
+ if (typeof first === "string" && first.trim()) return first;
3734
+ }
3735
+ return void 0;
3736
+ }
3737
+ function extractFromTranscript2(transcriptPath, debugLog6 = noopDebug5) {
3738
+ if (!transcriptPath) return { prompt: "", response: "" };
3739
+ let raw;
3740
+ try {
3741
+ raw = fs24.readFileSync(transcriptPath, "utf-8");
3742
+ } catch (err) {
3743
+ debugLog6("transcript-read-failed", {
3744
+ transcriptPath,
3745
+ error: err.message
3746
+ });
3747
+ return { prompt: "", response: "" };
3748
+ }
3749
+ const lines = parseTranscriptLines(raw);
3750
+ return {
3751
+ prompt: extractPrompt(lines),
3752
+ response: extractResponse(lines)
3753
+ };
3754
+ }
3755
+ function buildStopPayload(payload, extracted, config) {
3756
+ const prompt = extracted.prompt ?? "";
3757
+ const response = extracted.response ?? "";
3758
+ if (!prompt.trim() && !response.trim()) return null;
3759
+ const conversationId = asString3(payload.conversationId) ?? `antigravity-${Date.now()}`;
3760
+ const cwd = getWorkspacePath(payload) ?? "";
3761
+ const customData = {
3762
+ hookEvent: "Stop",
3763
+ conversationId,
3764
+ cwd,
3765
+ terminationReason: asString3(payload.terminationReason) ?? ""
3766
+ };
3767
+ return {
3768
+ prompt,
3769
+ response,
3770
+ chatId: conversationId,
3771
+ source: config.source,
3772
+ tokens: 0,
3773
+ customData
3774
+ };
3775
+ }
3776
+ function handleAntigravityHook(eventName, payloadJson, config, options = {}) {
3777
+ const debugLog6 = options.debugLog ?? noopDebug5;
3778
+ if (!isSupportedAntigravityEvent(eventName)) {
3779
+ debugLog6("hook-unknown-event", eventName);
3780
+ return null;
3781
+ }
3782
+ const payload = payloadJson ?? {};
3783
+ debugLog6("event-parsed", { eventName, payload });
3784
+ const extracted = extractFromTranscript2(payload.transcriptPath, debugLog6);
3785
+ const result = buildStopPayload(payload, extracted, config);
3786
+ if (!result) {
3787
+ debugLog6("stop-empty", { conversationId: payload.conversationId });
3788
+ return null;
3789
+ }
3790
+ debugLog6("payload-built", result);
3791
+ return result;
3792
+ }
3793
+
3794
+ // src/monitor/plugins/antigravity/index.ts
3795
+ var TOOL_ID5 = "antigravity";
3796
+ function debugLog5(label, data) {
3797
+ if (process.env.OLAKAI_MONITOR_DEBUG !== "1") return;
3798
+ try {
3799
+ const logPath = `/tmp/olakai-monitor-debug-${process.pid}.log`;
3800
+ const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] antigravity/${label}: ${typeof data === "string" ? data : JSON.stringify(data, null, 2)}
3801
+ `;
3802
+ fs25.appendFileSync(logPath, line, "utf-8");
3803
+ } catch {
3804
+ }
3805
+ }
3806
+ function resolveAntigravityProjectRoot(payload, fallbackCwd) {
3807
+ const startDir = getWorkspacePath(payload) ?? fallbackCwd;
3808
+ return findConfiguredWorkspace(startDir, [TOOL_ID5]);
3809
+ }
3810
+ var antigravityPlugin = {
3811
+ id: TOOL_ID5,
3812
+ displayName: "Antigravity",
3813
+ install(opts) {
3814
+ return installAntigravity(opts);
3815
+ },
3816
+ uninstall(opts) {
3817
+ return uninstallAntigravity(opts);
3818
+ },
3819
+ status(opts) {
3820
+ return getAntigravityStatus(opts);
3821
+ },
3822
+ async handleHook(eventName, payloadJson) {
3823
+ const payload = payloadJson ?? {};
3824
+ debugLog5("hook-fired", { eventName, hasPayload: payloadJson != null });
3825
+ const projectRoot = resolveAntigravityProjectRoot(payload, process.cwd());
3826
+ if (!projectRoot) {
3827
+ debugLog5("config-not-found", {
3828
+ startDir: getWorkspacePath(payload) ?? process.cwd()
3829
+ });
3830
+ return null;
3831
+ }
3832
+ const config = loadAntigravityConfig(projectRoot);
3833
+ if (!config) {
3834
+ debugLog5("monitor-config-missing", { projectRoot });
3835
+ return null;
3836
+ }
3837
+ const result = handleAntigravityHook(eventName, payload, config, {
3838
+ debugLog: debugLog5
3839
+ });
3840
+ if (!result) return null;
3841
+ return {
3842
+ payload: result,
3843
+ transport: {
3844
+ endpoint: config.monitoringEndpoint,
3845
+ apiKey: config.apiKey,
3846
+ projectRoot
3847
+ }
3848
+ };
3849
+ },
3850
+ async detectInstalled(opts) {
3851
+ const projectRoot = opts?.projectRoot ?? process.cwd();
3852
+ try {
3853
+ if (fs25.existsSync(getAntigravityConfigPath(projectRoot))) {
3854
+ return true;
3855
+ }
3856
+ } catch {
3857
+ }
3858
+ try {
3859
+ if (fs25.existsSync(getAntigravityCliDir())) {
3860
+ return true;
3861
+ }
3862
+ } catch {
3863
+ }
3864
+ return detectAgyBinaryOnPath();
3865
+ }
3866
+ };
3867
+ function detectAgyBinaryOnPath() {
3868
+ try {
3869
+ const probe = spawnSync5(
3870
+ process.platform === "win32" ? "where" : "which",
3871
+ ["agy"],
3872
+ {
3873
+ stdio: ["ignore", "pipe", "ignore"],
3874
+ timeout: 1e3
3875
+ }
3876
+ );
3877
+ if (probe.status === 0 && probe.stdout && probe.stdout.toString().trim()) {
3878
+ return true;
3879
+ }
3880
+ } catch {
3881
+ }
3882
+ return false;
3883
+ }
3884
+ registerPlugin(antigravityPlugin);
3885
+
2532
3886
  // src/monitor/install.ts
2533
3887
  async function runMonitorInstall(toolId, opts = {}) {
2534
3888
  const plugin = getPlugin(toolId);
@@ -2575,6 +3929,18 @@ export {
2575
3929
  writeCursorConfig,
2576
3930
  getCursorHooksPath,
2577
3931
  mergeCursorHooks,
3932
+ loadGeminiCliConfig,
3933
+ writeGeminiCliConfig,
3934
+ getGeminiSettingsPath,
3935
+ mergeHooksSettings2 as mergeHooksSettings,
3936
+ readJsonFile2 as readJsonFile,
3937
+ writeJsonFile2 as writeJsonFile,
3938
+ loadAntigravityConfig,
3939
+ writeAntigravityConfig,
3940
+ getGeminiConfigHooksPath,
3941
+ mergeHooksFile,
3942
+ readJsonFile3 as readJsonFile2,
3943
+ writeJsonFile3 as writeJsonFile2,
2578
3944
  getToolScope,
2579
3945
  getToolSource,
2580
3946
  readRegistry,
@@ -2584,4 +3950,4 @@ export {
2584
3950
  formatRegistryTable,
2585
3951
  runMonitorInstall
2586
3952
  };
2587
- //# sourceMappingURL=chunk-75YQWZ4Q.js.map
3953
+ //# sourceMappingURL=chunk-E33XD5CO.js.map