ragent-cli 1.11.2 → 1.11.3

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
@@ -31,7 +31,7 @@ var require_package = __commonJS({
31
31
  "package.json"(exports2, module2) {
32
32
  module2.exports = {
33
33
  name: "ragent-cli",
34
- version: "1.11.2",
34
+ version: "1.11.3",
35
35
  description: "CLI agent for rAgent Live \u2014 browser-first terminal control plane for AI coding agents",
36
36
  main: "dist/index.js",
37
37
  bin: {
@@ -708,9 +708,146 @@ var CodexCliParserV2 = class {
708
708
  return [];
709
709
  }
710
710
  };
711
+ function isGeminiHistoryItem(value) {
712
+ if (!value || typeof value !== "object") return false;
713
+ const item = value;
714
+ return Boolean(item.role && (item.parts || item.content || item.message));
715
+ }
716
+ function findGeminiHistory(value, depth = 0) {
717
+ if (depth > 6 || !value || typeof value !== "object") return null;
718
+ if (Array.isArray(value)) {
719
+ if (value.some(isGeminiHistoryItem)) {
720
+ return value.filter(isGeminiHistoryItem);
721
+ }
722
+ for (const entry of value) {
723
+ const found = findGeminiHistory(entry, depth + 1);
724
+ if (found) return found;
725
+ }
726
+ return null;
727
+ }
728
+ const obj = value;
729
+ for (const key of ["history", "messages", "turns", "contents", "conversation"]) {
730
+ const found = findGeminiHistory(obj[key], depth + 1);
731
+ if (found) return found;
732
+ }
733
+ for (const entry of Object.values(obj)) {
734
+ const found = findGeminiHistory(entry, depth + 1);
735
+ if (found) return found;
736
+ }
737
+ return null;
738
+ }
739
+ function geminiPartsFromItem(item) {
740
+ if (Array.isArray(item.parts)) return item.parts;
741
+ const source = item.content ?? item.message;
742
+ if (typeof source === "string") return [{ text: source }];
743
+ if (source && typeof source === "object" && Array.isArray(source.parts)) {
744
+ return source.parts;
745
+ }
746
+ return [];
747
+ }
748
+ function geminiRole(role) {
749
+ const normalized = role?.toLowerCase();
750
+ if (normalized === "user") return "user";
751
+ if (normalized === "model" || normalized === "assistant") return "assistant";
752
+ return null;
753
+ }
754
+ var GeminiCliParserV2 = class {
755
+ name = "gemini-cli-v2";
756
+ parseLine(line) {
757
+ return this.parseDocument(line);
758
+ }
759
+ parseDocument(document) {
760
+ let root;
761
+ try {
762
+ root = JSON.parse(document);
763
+ } catch (error) {
764
+ debugTranscriptParse("gemini", document, error);
765
+ return [];
766
+ }
767
+ const history = findGeminiHistory(root);
768
+ if (!history) return [];
769
+ const ops = [];
770
+ history.forEach((item, index) => {
771
+ const role = geminiRole(item.role);
772
+ if (!role) return;
773
+ const parts = geminiPartsFromItem(item);
774
+ const timestamp = item.timestamp ?? item.createdAt ?? item.createTime ?? now();
775
+ const turnId = item.id ?? `gemini-${index}`;
776
+ const blocks = [];
777
+ for (const part of parts) {
778
+ const text = typeof part.text === "string" ? part.text.trim() : "";
779
+ if (text) {
780
+ blocks.push({
781
+ op: "insert_block",
782
+ turnId,
783
+ block: makeTextBlock(genBlockId("txt"), text, true)
784
+ });
785
+ }
786
+ if (part.functionCall?.name) {
787
+ const callId = genBlockId("call");
788
+ blocks.push({
789
+ op: "insert_block",
790
+ turnId,
791
+ block: makeToolBlock(
792
+ genBlockId("tool"),
793
+ callId,
794
+ part.functionCall.name,
795
+ "running",
796
+ part.functionCall.args
797
+ )
798
+ });
799
+ }
800
+ if (part.functionResponse?.name) {
801
+ const output = part.functionResponse.response;
802
+ const blockId = genBlockId("tool");
803
+ blocks.push({
804
+ op: "insert_block",
805
+ turnId,
806
+ block: makeToolBlock(
807
+ blockId,
808
+ genBlockId("call"),
809
+ part.functionResponse.name,
810
+ "completed",
811
+ void 0,
812
+ true
813
+ )
814
+ });
815
+ if (output !== void 0) {
816
+ blocks.push({
817
+ op: "append_tool_output",
818
+ turnId,
819
+ blockId,
820
+ text: typeof output === "string" ? output : JSON.stringify(output, null, 2)
821
+ });
822
+ }
823
+ }
824
+ }
825
+ if (blocks.length === 0) return;
826
+ ops.push({
827
+ op: "upsert_turn",
828
+ turn: {
829
+ turnId,
830
+ role,
831
+ startedAt: timestamp,
832
+ updatedAt: timestamp,
833
+ isComplete: false
834
+ }
835
+ });
836
+ ops.push(...blocks);
837
+ ops.push({
838
+ op: "complete_turn",
839
+ turnId,
840
+ endedAt: timestamp,
841
+ updatedAt: timestamp
842
+ });
843
+ });
844
+ return ops;
845
+ }
846
+ };
711
847
  var V2_FACTORIES = {
712
848
  "Claude Code": () => new ClaudeCodeParserV2(),
713
- "Codex CLI": () => new CodexCliParserV2()
849
+ "Codex CLI": () => new CodexCliParserV2(),
850
+ "Gemini CLI": () => new GeminiCliParserV2()
714
851
  };
715
852
  function getParserV2(agentType) {
716
853
  if (!agentType) return void 0;
@@ -1046,18 +1183,110 @@ var CodexCliParser = class {
1046
1183
  return null;
1047
1184
  }
1048
1185
  };
1186
+ function isGeminiHistoryItem2(value) {
1187
+ if (!value || typeof value !== "object") return false;
1188
+ const item = value;
1189
+ return Boolean(item.role && (item.parts || item.content || item.message));
1190
+ }
1191
+ function findGeminiHistory2(value, depth = 0) {
1192
+ if (depth > 6 || !value || typeof value !== "object") return null;
1193
+ if (Array.isArray(value)) {
1194
+ if (value.some(isGeminiHistoryItem2)) return value.filter(isGeminiHistoryItem2);
1195
+ for (const entry of value) {
1196
+ const found = findGeminiHistory2(entry, depth + 1);
1197
+ if (found) return found;
1198
+ }
1199
+ return null;
1200
+ }
1201
+ const obj = value;
1202
+ for (const key of ["history", "messages", "turns", "contents", "conversation"]) {
1203
+ const found = findGeminiHistory2(obj[key], depth + 1);
1204
+ if (found) return found;
1205
+ }
1206
+ for (const entry of Object.values(obj)) {
1207
+ const found = findGeminiHistory2(entry, depth + 1);
1208
+ if (found) return found;
1209
+ }
1210
+ return null;
1211
+ }
1212
+ function geminiPartsFromItem2(item) {
1213
+ if (Array.isArray(item.parts)) return item.parts;
1214
+ const source = item.content ?? item.message;
1215
+ if (typeof source === "string") return [{ text: source }];
1216
+ if (source && typeof source === "object" && Array.isArray(source.parts)) {
1217
+ return source.parts;
1218
+ }
1219
+ return [];
1220
+ }
1221
+ var GeminiCliParser = class {
1222
+ name = "gemini-cli";
1223
+ parseLine(line) {
1224
+ const turns = this.parseDocument(line);
1225
+ return turns[0] ?? null;
1226
+ }
1227
+ parseDocument(document) {
1228
+ let root;
1229
+ try {
1230
+ root = JSON.parse(document);
1231
+ } catch {
1232
+ return [];
1233
+ }
1234
+ const history = findGeminiHistory2(root);
1235
+ if (!history) return [];
1236
+ const turns = [];
1237
+ history.forEach((item, index) => {
1238
+ const normalizedRole = item.role?.toLowerCase();
1239
+ const role = normalizedRole === "model" || normalizedRole === "assistant" ? "assistant" : normalizedRole === "user" ? "user" : null;
1240
+ if (!role) return;
1241
+ const textBlocks = [];
1242
+ const tools = [];
1243
+ for (const part of geminiPartsFromItem2(item)) {
1244
+ if (typeof part.text === "string" && part.text.trim()) {
1245
+ textBlocks.push(part.text.trim());
1246
+ }
1247
+ if (part.functionCall?.name) {
1248
+ tools.push({
1249
+ name: part.functionCall.name,
1250
+ input: part.functionCall.args,
1251
+ status: "started"
1252
+ });
1253
+ }
1254
+ if (part.functionResponse?.name) {
1255
+ const response = part.functionResponse.response;
1256
+ tools.push({
1257
+ name: part.functionResponse.name,
1258
+ status: "completed",
1259
+ result: response === void 0 ? void 0 : typeof response === "string" ? response : JSON.stringify(response, null, 2)
1260
+ });
1261
+ }
1262
+ }
1263
+ const content = textBlocks.join("\n").trim();
1264
+ if (!content && tools.length === 0) return;
1265
+ turns.push({
1266
+ turnId: item.id ?? `gemini-${index}`,
1267
+ role,
1268
+ content,
1269
+ tools: tools.length > 0 ? tools : void 0,
1270
+ timestamp: item.timestamp ?? item.createdAt ?? item.createTime ?? (/* @__PURE__ */ new Date()).toISOString()
1271
+ });
1272
+ });
1273
+ return turns;
1274
+ }
1275
+ };
1049
1276
  function parseProcessPid(sessionId) {
1050
1277
  if (!sessionId.startsWith("process:")) return null;
1051
1278
  const pid = parseInt(sessionId.slice("process:".length).split(":")[0] ?? "", 10);
1052
1279
  return Number.isFinite(pid) && pid > 0 ? pid : null;
1053
1280
  }
1054
1281
  function pathMatchesAgentTranscript(target, agentType) {
1055
- if (!target.endsWith(".jsonl")) return false;
1282
+ if (!target.endsWith(".jsonl") && !target.endsWith(".json")) return false;
1056
1283
  const isClaudeTranscript = target.includes("/.claude/");
1057
1284
  const isCodexTranscript = target.includes("/.codex/sessions/");
1285
+ const isGeminiTranscript = target.includes("/.gemini/tmp/") && target.includes("/chats/session-") && target.endsWith(".json");
1058
1286
  if (agentType === "Claude Code") return isClaudeTranscript;
1059
1287
  if (agentType === "Codex CLI") return isCodexTranscript;
1060
- return isClaudeTranscript || isCodexTranscript;
1288
+ if (agentType === "Gemini CLI") return isGeminiTranscript;
1289
+ return isClaudeTranscript || isCodexTranscript || isGeminiTranscript;
1061
1290
  }
1062
1291
  function getNewestPath(paths) {
1063
1292
  const ranked = paths.map((target) => {
@@ -1144,11 +1373,40 @@ function resolveUserHome(pid) {
1144
1373
  } catch {
1145
1374
  }
1146
1375
  try {
1147
- return os3.userInfo().homedir || null;
1376
+ return process.env.HOME || os3.userInfo().homedir || null;
1148
1377
  } catch {
1149
1378
  return null;
1150
1379
  }
1151
1380
  }
1381
+ function discoverGeminiTranscriptFromHome(pid) {
1382
+ const userHome = resolveUserHome(pid);
1383
+ if (!userHome) return null;
1384
+ const tmpDir = path2.join(userHome, ".gemini", "tmp");
1385
+ if (!fs2.existsSync(tmpDir)) return null;
1386
+ const matches = [];
1387
+ const stack = [tmpDir];
1388
+ let visited = 0;
1389
+ while (stack.length > 0 && visited < 2e3) {
1390
+ const current = stack.pop();
1391
+ if (!current) continue;
1392
+ visited++;
1393
+ let entries;
1394
+ try {
1395
+ entries = fs2.readdirSync(current, { withFileTypes: true });
1396
+ } catch {
1397
+ continue;
1398
+ }
1399
+ for (const entry of entries) {
1400
+ const fullPath = path2.join(current, entry.name);
1401
+ if (entry.isDirectory()) {
1402
+ stack.push(fullPath);
1403
+ } else if (entry.isFile() && entry.name.startsWith("session-") && entry.name.endsWith(".json") && fullPath.includes(`${path2.sep}chats${path2.sep}`)) {
1404
+ matches.push(fullPath);
1405
+ }
1406
+ }
1407
+ }
1408
+ return getNewestPath(matches);
1409
+ }
1152
1410
  function discoverViaCwd(paneCwd, pid) {
1153
1411
  const userHome = resolveUserHome(pid);
1154
1412
  if (!userHome) return null;
@@ -1183,6 +1441,10 @@ function discoverTranscriptFile(sessionId, agentType) {
1183
1441
  const cwdResult = discoverViaProcessCwd(processPid);
1184
1442
  if (cwdResult) return cwdResult;
1185
1443
  }
1444
+ if (agentType === "Gemini CLI") {
1445
+ const geminiResult = discoverGeminiTranscriptFromHome(processPid);
1446
+ if (geminiResult) return geminiResult;
1447
+ }
1186
1448
  return null;
1187
1449
  }
1188
1450
  if (sessionId.startsWith("tmux:")) {
@@ -1217,6 +1479,9 @@ function discoverTranscriptFile(sessionId, agentType) {
1217
1479
  } catch {
1218
1480
  }
1219
1481
  }
1482
+ if (agentType === "Gemini CLI") {
1483
+ return discoverGeminiTranscriptFromHome(panePid ?? void 0);
1484
+ }
1220
1485
  return null;
1221
1486
  }
1222
1487
  return null;
@@ -1241,6 +1506,7 @@ var TranscriptWatcher = class {
1241
1506
  pollTimer = null;
1242
1507
  stopped = false;
1243
1508
  subscriberCount = 0;
1509
+ documentFingerprint = "";
1244
1510
  constructor(filePath, parser, callbacks, parserV2, sessionId) {
1245
1511
  this.filePath = filePath;
1246
1512
  this.parser = parser;
@@ -1310,6 +1576,10 @@ var TranscriptWatcher = class {
1310
1576
  }
1311
1577
  /** Read and replay the full transcript from the start of the file. */
1312
1578
  replayFromStart() {
1579
+ if (this.parser.parseDocument) {
1580
+ this.readDocument(true);
1581
+ return;
1582
+ }
1313
1583
  const savedOffset = this.offset;
1314
1584
  this.offset = 0;
1315
1585
  this.partialLine = "";
@@ -1351,6 +1621,10 @@ var TranscriptWatcher = class {
1351
1621
  }
1352
1622
  }
1353
1623
  readNewData() {
1624
+ if (this.parser.parseDocument) {
1625
+ this.readDocument(false);
1626
+ return;
1627
+ }
1354
1628
  let fd;
1355
1629
  try {
1356
1630
  fd = fs2.openSync(this.filePath, "r");
@@ -1371,6 +1645,38 @@ var TranscriptWatcher = class {
1371
1645
  fs2.closeSync(fd);
1372
1646
  }
1373
1647
  }
1648
+ readDocument(force) {
1649
+ let content;
1650
+ let stat;
1651
+ try {
1652
+ stat = fs2.statSync(this.filePath);
1653
+ content = fs2.readFileSync(this.filePath, "utf-8");
1654
+ } catch {
1655
+ return;
1656
+ }
1657
+ const fingerprint = `${stat.mtimeMs}:${stat.size}`;
1658
+ if (!force && fingerprint === this.documentFingerprint) return;
1659
+ this.documentFingerprint = fingerprint;
1660
+ const parsedTurns = this.parser.parseDocument?.(content) ?? [];
1661
+ this.seq++;
1662
+ this.turns = parsedTurns.slice(-MAX_REPLAY_TURNS).map((turn) => ({
1663
+ turnId: turn.turnId,
1664
+ role: turn.role,
1665
+ content: turn.content,
1666
+ tools: turn.tools,
1667
+ timestamp: turn.timestamp
1668
+ }));
1669
+ this.callbacks.onTurnsSnapshot?.(this.turns, this.seq);
1670
+ if (this.parserV2?.parseDocument && this.callbacks.onSnapshot) {
1671
+ const ops = this.parserV2.parseDocument(content);
1672
+ let nextSnapshot = createEmptySnapshot(this.snapshot.sessionId);
1673
+ if (ops.length > 0) {
1674
+ nextSnapshot = applyOps(nextSnapshot, ops);
1675
+ }
1676
+ this.snapshot = trimSnapshot(nextSnapshot);
1677
+ this.callbacks.onSnapshot(this.snapshot, this.seq);
1678
+ }
1679
+ }
1374
1680
  processChunk(chunk) {
1375
1681
  const combined = this.partialLine + chunk;
1376
1682
  const lines = combined.split("\n");
@@ -1413,7 +1719,8 @@ var TranscriptWatcher = class {
1413
1719
  };
1414
1720
  var PARSER_FACTORIES = {
1415
1721
  "Claude Code": () => new ClaudeCodeParser(),
1416
- "Codex CLI": () => new CodexCliParser()
1722
+ "Codex CLI": () => new CodexCliParser(),
1723
+ "Gemini CLI": () => new GeminiCliParser()
1417
1724
  };
1418
1725
  function getParser(agentType) {
1419
1726
  if (!agentType) return void 0;
@@ -1462,6 +1769,12 @@ var TranscriptWatcherManager = class {
1462
1769
  onOps: this.sendOpsFn ? (ops, seq) => {
1463
1770
  this.sendOpsFn(sessionId, ops, seq);
1464
1771
  } : void 0,
1772
+ onTurnsSnapshot: (turns, seq) => {
1773
+ this.sendSnapshotFn(sessionId, turns, seq);
1774
+ },
1775
+ onSnapshot: this.sendV2SnapshotFn ? (snapshot, seq) => {
1776
+ this.sendV2SnapshotFn(sessionId, snapshot, seq);
1777
+ } : void 0,
1465
1778
  onError: (error) => {
1466
1779
  log5.warn("transcript watcher error", { sessionId, error: String(error) });
1467
1780
  }
@@ -1520,6 +1833,12 @@ var TranscriptWatcherManager = class {
1520
1833
  onOps: this.sendOpsFn ? (ops, seq) => {
1521
1834
  this.sendOpsFn(sessionId, ops, seq);
1522
1835
  } : void 0,
1836
+ onTurnsSnapshot: (turns, seq) => {
1837
+ this.sendSnapshotFn(sessionId, turns, seq);
1838
+ },
1839
+ onSnapshot: this.sendV2SnapshotFn ? (snapshot, seq) => {
1840
+ this.sendV2SnapshotFn(sessionId, snapshot, seq);
1841
+ } : void 0,
1523
1842
  onError: (error) => {
1524
1843
  log5.warn("transcript watcher error", { sessionId, error: String(error) });
1525
1844
  }
@@ -1577,7 +1896,7 @@ var TranscriptWatcherManager = class {
1577
1896
 
1578
1897
  // src/sessions.ts
1579
1898
  var isMac = os4.platform() === "darwin";
1580
- var MARKDOWN_AGENT_TYPES = /* @__PURE__ */ new Set(["Claude Code", "Codex CLI"]);
1899
+ var MARKDOWN_AGENT_TYPES = /* @__PURE__ */ new Set(["Claude Code", "Codex CLI", "Gemini CLI"]);
1581
1900
  function getProcessStartTime(pid) {
1582
1901
  if (isMac) return null;
1583
1902
  try {
@@ -1629,6 +1948,7 @@ function classifyAgent(command, options) {
1629
1948
  ".claude": "Claude Code",
1630
1949
  "claude-code": "Claude Code",
1631
1950
  ".codex": "Codex CLI",
1951
+ ".gemini": "Gemini CLI",
1632
1952
  ".aider": "aider"
1633
1953
  };
1634
1954
  const agentType = cwdAgentMap[dirName];
package/dist/sbom.json CHANGED
@@ -1166,8 +1166,8 @@
1166
1166
  {
1167
1167
  "type": "library",
1168
1168
  "name": "ragent-cli",
1169
- "version": "1.11.2",
1170
- "bom-ref": "ragent-live|ragent-cli@1.11.2",
1169
+ "version": "1.11.3",
1170
+ "bom-ref": "ragent-live|ragent-cli@1.11.3",
1171
1171
  "author": "Intellimetrics",
1172
1172
  "description": "CLI agent for rAgent Live — browser-first terminal control plane for AI coding agents",
1173
1173
  "licenses": [
@@ -1178,7 +1178,7 @@
1178
1178
  }
1179
1179
  }
1180
1180
  ],
1181
- "purl": "pkg:npm/ragent-cli@1.11.2",
1181
+ "purl": "pkg:npm/ragent-cli@1.11.3",
1182
1182
  "externalReferences": [
1183
1183
  {
1184
1184
  "url": "https://github.com/chadlindell/ragent-live/issues",
@@ -1303,7 +1303,7 @@
1303
1303
  "ragent-live|@emnapi/wasi-threads@1.2.1",
1304
1304
  "ragent-live|@pkgjs/parseargs@0.11.0",
1305
1305
  "ragent-live|@tybys/wasm-util@0.10.1",
1306
- "ragent-live|ragent-cli@1.11.2"
1306
+ "ragent-live|ragent-cli@1.11.3"
1307
1307
  ]
1308
1308
  },
1309
1309
  {
@@ -1436,7 +1436,7 @@
1436
1436
  ]
1437
1437
  },
1438
1438
  {
1439
- "ref": "ragent-live|ragent-cli@1.11.2",
1439
+ "ref": "ragent-live|ragent-cli@1.11.3",
1440
1440
  "dependsOn": [
1441
1441
  "ragent-live|@azure/web-pubsub-client@1.0.4",
1442
1442
  "ragent-live|commander@14.0.3",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ragent-cli",
3
- "version": "1.11.2",
3
+ "version": "1.11.3",
4
4
  "description": "CLI agent for rAgent Live — browser-first terminal control plane for AI coding agents",
5
5
  "main": "dist/index.js",
6
6
  "bin": {