flatbread-cli 0.3.0 → 0.3.1

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.
@@ -2,9 +2,11 @@
2
2
 
3
3
  ## deja-vu
4
4
 
5
- `src/adapters/antigravity.ts` reads conversations the way deja-vu reads Windsurf trajectories
6
- (`internal/sources/windsurf.go`), and `test/fixtures/antigravity/*.pb` are its fixtures
7
- (`fixtures/registry/windsurf/cascade/`).
5
+ `src/adapters/antigravity.ts` reads `.pb` conversations the way deja-vu reads Windsurf trajectories
6
+ (`internal/sources/windsurf.go`) and transcripts the way it reads Antigravity's
7
+ (`internal/sources/antigravity.go`). `test/fixtures/antigravity/` holds its fixtures
8
+ (`fixtures/registry/windsurf/cascade/`, `fixtures/registry/antigravity/`) and transcripts from
9
+ `internal/sources/antigravity_toolcalls_test.go`.
8
10
 
9
11
  ```
10
12
  MIT License
@@ -4810,11 +4810,11 @@ var HARNESS_INFO = {
4810
4810
  antigravity: {
4811
4811
  id: "antigravity",
4812
4812
  label: "Google Antigravity",
4813
- formatLabel: "IDE conversations/<id>.pb (Windsurf's encrypted trajectory)",
4813
+ formatLabel: "app brain/<id>/.system_generated/logs/transcript.jsonl, or IDE conversations/<id>.pb",
4814
4814
  confidence: "documented",
4815
4815
  implemented: true,
4816
4816
  humanTest: "A6",
4817
- note: "The IDE only, prompts and replies only. Read the way deja-vu reads Windsurf, which held on all nine IDE 1.23.2 conversations measured. Tool calls are undocumented and not read; the standalone Antigravity app is not read yet."
4817
+ note: "Both stores measured on one machine. The app's transcript is read the way deja-vu reads it: prompts, replies and the planner's tool calls, not tool output. The IDE's .pb is read the way deja-vu reads Windsurf: prompts and replies only."
4818
4818
  },
4819
4819
  terminal: {
4820
4820
  id: "terminal",
@@ -6052,11 +6052,12 @@ import fs3 from "node:fs";
6052
6052
  import os2 from "node:os";
6053
6053
  import path3 from "node:path";
6054
6054
  import { fileURLToPath } from "node:url";
6055
- var ANTIGRAVITY_ADAPTER_VERSION = 1;
6055
+ var ANTIGRAVITY_ADAPTER_VERSION = 2;
6056
6056
  var KEY = Buffer.from("safeCodeiumworldKeYsecretBalloon");
6057
6057
  var NONCE = 12;
6058
6058
  var TAG = 16;
6059
6059
  var CONVERSATION = /[\\/]\.gemini[\\/]antigravity[^\\/]*[\\/]conversations[\\/][^\\/]+\.pb$/i;
6060
+ var TRANSCRIPT = /[\\/]\.gemini[\\/]antigravity[^\\/]*[\\/]brain[\\/][^\\/]+[\\/]\.system_generated[\\/]logs[\\/]transcript\.jsonl$/i;
6060
6061
  var PB_VARINT = 0;
6061
6062
  var PB_FIXED64 = 1;
6062
6063
  var PB_BYTES = 2;
@@ -6214,6 +6215,82 @@ function decodeTrajectory2(plain) {
6214
6215
  function homeOf(env) {
6215
6216
  return (process.platform === "win32" ? env.USERPROFILE : env.HOME) || os2.homedir();
6216
6217
  }
6218
+ var USER_BLOCKS = [/<ADDITIONAL_METADATA>[\s\S]*?<\/ADDITIONAL_METADATA>/g, /<USER_SETTINGS_CHANGE>[\s\S]*?<\/USER_SETTINGS_CHANGE>/g];
6219
+ var ARTIFACT_COMMENT = /^Comments on artifact URI:.*$|^The user has (?:approved|rejected) this document\.$/gm;
6220
+ var REQUEST_TAG = /[ \t]*\n?\s*<\/?USER_REQUEST>\s*/g;
6221
+ function cleanUserContent(text2) {
6222
+ let out = text2;
6223
+ for (const block of USER_BLOCKS) out = out.replace(block, "");
6224
+ out = out.replace(ARTIFACT_COMMENT, "");
6225
+ return out.replace(REQUEST_TAG, "\n").trim();
6226
+ }
6227
+ function decodeArg(value) {
6228
+ if (typeof value !== "string" || !value.startsWith('"')) return value;
6229
+ try {
6230
+ return JSON.parse(value);
6231
+ } catch {
6232
+ return value;
6233
+ }
6234
+ }
6235
+ function isRecord(value) {
6236
+ return typeof value === "object" && value !== null && !Array.isArray(value);
6237
+ }
6238
+ function decodeTranscript(text2) {
6239
+ const events = [];
6240
+ const unread = /* @__PURE__ */ new Map();
6241
+ let cwd;
6242
+ let steps = 0;
6243
+ let line = 0;
6244
+ for (const raw of text2.replace(/\r\n/g, "\n").split("\n")) {
6245
+ line++;
6246
+ if (raw.trim() === "") continue;
6247
+ let record;
6248
+ try {
6249
+ record = JSON.parse(raw);
6250
+ } catch {
6251
+ tally(unread, "malformed line");
6252
+ continue;
6253
+ }
6254
+ if (!isRecord(record)) {
6255
+ tally(unread, "malformed line");
6256
+ continue;
6257
+ }
6258
+ steps++;
6259
+ const source = typeof record["source"] === "string" ? record["source"] : "";
6260
+ const type = typeof record["type"] === "string" ? record["type"] : "";
6261
+ const content = typeof record["content"] === "string" ? record["content"] : "";
6262
+ const created = typeof record["created_at"] === "string" && !Number.isNaN(Date.parse(record["created_at"])) ? record["created_at"] : void 0;
6263
+ const at = created ? { timestamp: created } : {};
6264
+ if (source === "USER_EXPLICIT") {
6265
+ const prompt2 = cleanUserContent(content);
6266
+ if (prompt2) events.push({ kind: "prompt", ...at, text: prompt2 });
6267
+ continue;
6268
+ }
6269
+ if (source !== "MODEL") {
6270
+ tally(unread, type ? `${source || "no"} source, type ${type}` : `${source || "no"} source`);
6271
+ continue;
6272
+ }
6273
+ if (type !== "" && type !== "PLANNER_RESPONSE") {
6274
+ tally(unread, `tool step ${type}`);
6275
+ continue;
6276
+ }
6277
+ if (content.trim()) events.push({ kind: "text", ...at, text: content.trim() });
6278
+ const calls = Array.isArray(record["tool_calls"]) ? record["tool_calls"] : [];
6279
+ calls.forEach((call, k) => {
6280
+ if (!isRecord(call) || typeof call["name"] !== "string") return;
6281
+ const args = {};
6282
+ if (isRecord(call["args"])) for (const [key, value] of Object.entries(call["args"])) args[key] = decodeArg(value);
6283
+ if (cwd === void 0 && call["name"] === "run_command" && typeof args["Cwd"] === "string" && args["Cwd"]) cwd = args["Cwd"];
6284
+ const step = typeof record["step_index"] === "number" ? record["step_index"] : `line${line}`;
6285
+ events.push({ kind: "tool_call", ...at, id: `${step}.${k}`, name: call["name"], input: args });
6286
+ });
6287
+ }
6288
+ if (cwd !== void 0) events.unshift({ kind: "meta", cwd });
6289
+ return { events, steps, unread };
6290
+ }
6291
+ function transcriptId(file) {
6292
+ return path3.basename(path3.dirname(path3.dirname(path3.dirname(file))));
6293
+ }
6217
6294
  function listConversations(env = process.env) {
6218
6295
  const gemini = path3.join(homeOf(env), ".gemini");
6219
6296
  let roots;
@@ -6222,29 +6299,71 @@ function listConversations(env = process.env) {
6222
6299
  } catch {
6223
6300
  return [];
6224
6301
  }
6225
- const found = [];
6226
- for (const root of roots) {
6227
- const dir = path3.join(gemini, root, "conversations");
6228
- let names;
6302
+ const statFile = (file) => {
6229
6303
  try {
6230
- names = fs3.readdirSync(dir);
6304
+ const stat = fs3.statSync(file);
6305
+ return stat.isFile() ? stat : null;
6231
6306
  } catch {
6232
- continue;
6307
+ return null;
6233
6308
  }
6234
- for (const name of names) {
6235
- if (!name.toLowerCase().endsWith(".pb")) continue;
6236
- const file = path3.join(dir, name);
6237
- try {
6238
- const stat = fs3.statSync(file);
6239
- if (stat.isFile()) found.push({ file, mtime: stat.mtimeMs });
6240
- } catch {
6241
- continue;
6242
- }
6309
+ };
6310
+ const readNames = (dir) => {
6311
+ try {
6312
+ return fs3.readdirSync(dir);
6313
+ } catch {
6314
+ return [];
6243
6315
  }
6244
- }
6245
- return found.sort((a, b) => b.mtime - a.mtime).map((entry) => entry.file);
6316
+ };
6317
+ const pb = /* @__PURE__ */ new Map();
6318
+ const transcripts = /* @__PURE__ */ new Map();
6319
+ for (const root of roots) {
6320
+ const conversations = path3.join(gemini, root, "conversations");
6321
+ for (const name of readNames(conversations)) {
6322
+ if (!name.toLowerCase().endsWith(".pb")) continue;
6323
+ const file = path3.join(conversations, name);
6324
+ const stat = statFile(file);
6325
+ if (stat === null) continue;
6326
+ const id = name.slice(0, -".pb".length).toLowerCase();
6327
+ const kept = pb.get(id);
6328
+ if (kept === void 0 || stat.size > kept.size) pb.set(id, { file, mtime: stat.mtimeMs, size: stat.size });
6329
+ }
6330
+ const brain = path3.join(gemini, root, "brain");
6331
+ for (const name of readNames(brain)) {
6332
+ const file = path3.join(brain, name, ".system_generated", "logs", "transcript.jsonl");
6333
+ const stat = statFile(file);
6334
+ if (stat === null) continue;
6335
+ const id = name.toLowerCase();
6336
+ const kept = transcripts.get(id);
6337
+ if (kept === void 0 || stat.size > kept.size) transcripts.set(id, { file, mtime: stat.mtimeMs, size: stat.size });
6338
+ }
6339
+ }
6340
+ for (const id of transcripts.keys()) pb.delete(id);
6341
+ return [...pb.values(), ...transcripts.values()].sort((a, b) => b.mtime - a.mtime).map((entry) => entry.file);
6246
6342
  }
6247
6343
  var REFUSAL_TEST = "A6";
6344
+ async function readTranscript(file) {
6345
+ const trajectory = decodeTranscript(await fs3.promises.readFile(file, "utf8"));
6346
+ const id = transcriptId(file);
6347
+ const said = trajectory.events.filter((e) => e.kind === "prompt" || e.kind === "text").length;
6348
+ if (said === 0) {
6349
+ return {
6350
+ refused: true,
6351
+ reason: `${id}'s transcript has ${trajectory.steps} record(s) and no prompt or reply this reader knows.`,
6352
+ humanTest: REFUSAL_TEST
6353
+ };
6354
+ }
6355
+ return {
6356
+ events: trajectory.events,
6357
+ capabilities: { tokens: "unavailable", filesChanged: "unavailable" },
6358
+ sessionId: id,
6359
+ unreadFields: [...trajectory.unread].sort((a, b) => b[1] - a[1]).map(([field, count]) => `${field} x${count}`),
6360
+ notes: [
6361
+ `${trajectory.steps} record(s), ${said} prompt/reply event(s)`,
6362
+ "tool steps' bodies and checkpoints are not read; tool calls come from the planner's tool_calls",
6363
+ "CONFIDENCE: documented (deja-vu's transcript reader, measured on app 2.24.0). See HUMAN-TESTS.md A6."
6364
+ ]
6365
+ };
6366
+ }
6248
6367
  var antigravityAdapter = {
6249
6368
  id: "antigravity",
6250
6369
  version: ANTIGRAVITY_ADAPTER_VERSION,
@@ -6252,9 +6371,13 @@ var antigravityAdapter = {
6252
6371
  if (CONVERSATION.test(input.path)) {
6253
6372
  return { score: 85, reason: "a conversations/<id>.pb under ~/.gemini/antigravity" };
6254
6373
  }
6255
- return { score: 0, reason: "not an Antigravity conversations/<id>.pb" };
6374
+ if (TRANSCRIPT.test(input.path)) {
6375
+ return { score: 85, reason: "a brain/<id>/.system_generated/logs/transcript.jsonl under ~/.gemini/antigravity" };
6376
+ }
6377
+ return { score: 0, reason: "not an Antigravity conversations/<id>.pb or brain transcript" };
6256
6378
  },
6257
6379
  async read(input) {
6380
+ if (input.path.toLowerCase().endsWith(".jsonl")) return readTranscript(input.path);
6258
6381
  const plain = openConversation(await fs3.promises.readFile(input.path));
6259
6382
  if (plain === null) {
6260
6383
  return {
@@ -6305,6 +6428,18 @@ var antigravityAdapter = {
6305
6428
  const target = files[0];
6306
6429
  if (target === void 0) return lines2;
6307
6430
  lines2.push("", `inspecting ${target}`);
6431
+ if (target.toLowerCase().endsWith(".jsonl")) {
6432
+ try {
6433
+ const trajectory = decodeTranscript(await fs3.promises.readFile(target, "utf8"));
6434
+ lines2.push(` records: ${trajectory.steps}, events: ${trajectory.events.filter((e) => e.kind !== "meta").length}`);
6435
+ for (const [field, count] of [...trajectory.unread].sort((a, b) => b[1] - a[1]).slice(0, 15)) {
6436
+ lines2.push(` ${String(count).padStart(6)} ${field}`);
6437
+ }
6438
+ } catch (error) {
6439
+ lines2.push(` unreadable: ${error instanceof Error ? error.message : String(error)}`);
6440
+ }
6441
+ return lines2;
6442
+ }
6308
6443
  let plain;
6309
6444
  try {
6310
6445
  plain = openConversation(await fs3.promises.readFile(target));
@@ -10934,7 +11069,7 @@ function logCapture(dir, line, now = (/* @__PURE__ */ new Date()).toISOString())
10934
11069
 
10935
11070
  // src/mcp.ts
10936
11071
  var MCP_PROTOCOL_VERSIONS = ["2025-06-18", "2025-03-26", "2024-11-05"];
10937
- var MCP_SERVER_VERSION = "0.3.0";
11072
+ var MCP_SERVER_VERSION = "0.3.1";
10938
11073
  function defaultMcpDeps(storeDir2, bucketDir2) {
10939
11074
  return {
10940
11075
  storeDir: storeDir2,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flatbread-cli",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "Flatbread: read a coding agent's session off disk, redact it, share it, and pull a run's Context Capsule into another agent.",
5
5
  "type": "module",
6
6
  "bin": {