@tryglen/cli 0.15.0 → 0.17.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.
Files changed (48) hide show
  1. package/README.md +1 -0
  2. package/dist/_utils/agent-adapter.js +14 -0
  3. package/dist/_utils/agent-adapter.js.map +1 -1
  4. package/dist/_utils/collect-import-files.d.ts +4 -0
  5. package/dist/_utils/collect-import-files.js +55 -0
  6. package/dist/_utils/collect-import-files.js.map +1 -0
  7. package/dist/_utils/config.d.ts +2 -0
  8. package/dist/_utils/config.js +3 -0
  9. package/dist/_utils/config.js.map +1 -1
  10. package/dist/_utils/detect-transcript-format.d.ts +2 -0
  11. package/dist/_utils/detect-transcript-format.js +53 -0
  12. package/dist/_utils/detect-transcript-format.js.map +1 -0
  13. package/dist/_utils/parse-claude-transcript.d.ts +3 -0
  14. package/dist/_utils/parse-claude-transcript.js +124 -0
  15. package/dist/_utils/parse-claude-transcript.js.map +1 -0
  16. package/dist/_utils/parse-codex-transcript.d.ts +3 -0
  17. package/dist/_utils/parse-codex-transcript.js +111 -0
  18. package/dist/_utils/parse-codex-transcript.js.map +1 -0
  19. package/dist/_utils/parse-cursor-db.d.ts +5 -0
  20. package/dist/_utils/parse-cursor-db.js +227 -0
  21. package/dist/_utils/parse-cursor-db.js.map +1 -0
  22. package/dist/_utils/transcript-turns.d.ts +19 -0
  23. package/dist/_utils/transcript-turns.js +41 -0
  24. package/dist/_utils/transcript-turns.js.map +1 -0
  25. package/dist/_utils/turn-cache.d.ts +7 -0
  26. package/dist/_utils/turn-cache.js +63 -0
  27. package/dist/_utils/turn-cache.js.map +1 -0
  28. package/dist/bin.js +33 -13
  29. package/dist/bin.js.map +1 -1
  30. package/dist/commands/doctor.js +6 -0
  31. package/dist/commands/doctor.js.map +1 -1
  32. package/dist/commands/import.d.ts +7 -0
  33. package/dist/commands/import.js +236 -0
  34. package/dist/commands/import.js.map +1 -0
  35. package/dist/commands/ingest.js +79 -24
  36. package/dist/commands/ingest.js.map +1 -1
  37. package/dist/commands/install.js +42 -2
  38. package/dist/commands/install.js.map +1 -1
  39. package/dist/commands/onboard.js +1 -1
  40. package/dist/commands/onboard.js.map +1 -1
  41. package/dist/commands/pr-link.js +23 -10
  42. package/dist/commands/pr-link.js.map +1 -1
  43. package/dist/commands/session-start.d.ts +6 -7
  44. package/dist/commands/session-start.js +30 -10
  45. package/dist/commands/session-start.js.map +1 -1
  46. package/dist/commands/uninstall.js +5 -1
  47. package/dist/commands/uninstall.js.map +1 -1
  48. package/package.json +1 -1
package/README.md CHANGED
@@ -17,6 +17,7 @@ glen login
17
17
  | `glen logout` | Sign out and remove credentials + session state. |
18
18
  | `glen status` | Show connection status, active org, incognito state, and CLI version. |
19
19
  | `glen search "<query>"` | Search the team's shared glen memory. |
20
+ | `glen import <path…> [--dry-run]` | Import old local agent transcripts (Claude Code `~/.claude/projects`, Codex `~/.codex/sessions`, Cursor `state.vscdb`) into glen memory. Idempotent — re-running skips already-imported turns. `--dry-run` previews without writing. |
20
21
  | `glen incognito on\|off` | Toggle incognito mode: while on, nothing is recorded (recall keeps working). |
21
22
  | `glen org list` | List your organizations; active one is marked with `✓`. |
22
23
  | `glen org switch [slug]` | Switch the active organization for this machine (interactive picker without `slug`). |
@@ -12,6 +12,20 @@ const str = (v) => typeof v === "string" && v ? v : null;
12
12
  * idempotency key derivation.
13
13
  */
14
14
  const adaptAgentEvent = (agent, event, transcriptLines) => {
15
+ if (agent === "cursor") {
16
+ // Cursor hooks carry no usable transcript; priorAssistant comes from the
17
+ // local turn-cache (populated by afterAgentResponse) and is read by
18
+ // ingest.ts, not here. Common payload fields carry the model.
19
+ const fields = {};
20
+ const model = str(event.model);
21
+ if (model)
22
+ fields.model = model;
23
+ return {
24
+ sessionFields: fields,
25
+ priorAssistant: null,
26
+ usesTranscript: false,
27
+ };
28
+ }
15
29
  if (agent === "codex") {
16
30
  const fields = {};
17
31
  const model = str(event.model);
@@ -1 +1 @@
1
- {"version":3,"file":"agent-adapter.js","sourceRoot":"","sources":["../../src/_utils/agent-adapter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAaxD,gDAAgD;AAChD,MAAM,GAAG,GAAG,CAAC,CAAU,EAAiB,EAAE,CACxC,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAExC;;;;;;;;;GASG;AACH,MAAM,eAAe,GAAG,CACtB,KAAyB,EACzB,KAAgB,EAChB,eAAkC,EACnB,EAAE;IACjB,IAAI,KAAK,KAAK,OAAO,EAAE,CAAC;QACtB,MAAM,MAAM,GAAkB,EAAE,CAAC;QACjC,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAC/B,IAAI,KAAK;YAAE,MAAM,CAAC,KAAK,GAAG,KAAK,CAAC;QAChC,MAAM,cAAc,GAAG,GAAG,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;QAClD,IAAI,cAAc;YAAE,MAAM,CAAC,cAAc,GAAG,cAAc,CAAC;QAC3D,MAAM,MAAM,GAAG,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QACjC,IAAI,MAAM;YAAE,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC;QACnC,OAAO;YACL,aAAa,EAAE,MAAM;YACrB,cAAc,EACZ,OAAO,KAAK,CAAC,sBAAsB,KAAK,QAAQ;gBAC9C,CAAC,CAAC,KAAK,CAAC,sBAAsB;gBAC9B,CAAC,CAAC,IAAI;YACV,cAAc,EAAE,KAAK;SACtB,CAAC;IACJ,CAAC;IAED,iFAAiF;IACjF,MAAM,EAAE,cAAc,EAAE,GAAG,eAAe,CAAC,eAAe,CAAC,CAAC;IAC5D,MAAM,MAAM,GAAkB,EAAE,CAAC;IACjC,MAAM,cAAc,GAAG,GAAG,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;IAClD,IAAI,cAAc;QAAE,MAAM,CAAC,cAAc,GAAG,cAAc,CAAC;IAC3D,MAAM,MAAM,GAAG,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IACjC,IAAI,MAAM;QAAE,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC;IACnC,OAAO;QACL,aAAa,EAAE,MAAM;QACrB,cAAc;QACd,cAAc,EAAE,IAAI;KACrB,CAAC;AACJ,CAAC,CAAC;AAEF,OAAO,EAAE,eAAe,EAAE,CAAC"}
1
+ {"version":3,"file":"agent-adapter.js","sourceRoot":"","sources":["../../src/_utils/agent-adapter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAaxD,gDAAgD;AAChD,MAAM,GAAG,GAAG,CAAC,CAAU,EAAiB,EAAE,CACxC,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAExC;;;;;;;;;GASG;AACH,MAAM,eAAe,GAAG,CACtB,KAAyB,EACzB,KAAgB,EAChB,eAAkC,EACnB,EAAE;IACjB,IAAI,KAAK,KAAK,QAAQ,EAAE,CAAC;QACvB,yEAAyE;QACzE,oEAAoE;QACpE,8DAA8D;QAC9D,MAAM,MAAM,GAAkB,EAAE,CAAC;QACjC,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAC/B,IAAI,KAAK;YAAE,MAAM,CAAC,KAAK,GAAG,KAAK,CAAC;QAChC,OAAO;YACL,aAAa,EAAE,MAAM;YACrB,cAAc,EAAE,IAAI;YACpB,cAAc,EAAE,KAAK;SACtB,CAAC;IACJ,CAAC;IAED,IAAI,KAAK,KAAK,OAAO,EAAE,CAAC;QACtB,MAAM,MAAM,GAAkB,EAAE,CAAC;QACjC,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAC/B,IAAI,KAAK;YAAE,MAAM,CAAC,KAAK,GAAG,KAAK,CAAC;QAChC,MAAM,cAAc,GAAG,GAAG,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;QAClD,IAAI,cAAc;YAAE,MAAM,CAAC,cAAc,GAAG,cAAc,CAAC;QAC3D,MAAM,MAAM,GAAG,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QACjC,IAAI,MAAM;YAAE,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC;QACnC,OAAO;YACL,aAAa,EAAE,MAAM;YACrB,cAAc,EACZ,OAAO,KAAK,CAAC,sBAAsB,KAAK,QAAQ;gBAC9C,CAAC,CAAC,KAAK,CAAC,sBAAsB;gBAC9B,CAAC,CAAC,IAAI;YACV,cAAc,EAAE,KAAK;SACtB,CAAC;IACJ,CAAC;IAED,iFAAiF;IACjF,MAAM,EAAE,cAAc,EAAE,GAAG,eAAe,CAAC,eAAe,CAAC,CAAC;IAC5D,MAAM,MAAM,GAAkB,EAAE,CAAC;IACjC,MAAM,cAAc,GAAG,GAAG,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;IAClD,IAAI,cAAc;QAAE,MAAM,CAAC,cAAc,GAAG,cAAc,CAAC;IAC3D,MAAM,MAAM,GAAG,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IACjC,IAAI,MAAM;QAAE,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC;IACnC,OAAO;QACL,aAAa,EAAE,MAAM;QACrB,cAAc;QACd,cAAc,EAAE,IAAI;KACrB,CAAC;AACJ,CAAC,CAAC;AAEF,OAAO,EAAE,eAAe,EAAE,CAAC"}
@@ -0,0 +1,4 @@
1
+ import { type CliError } from "./cli-error.js";
2
+ import { type Result } from "./result.js";
3
+ declare const collectImportFiles: (paths: readonly string[]) => Promise<Result<string[], CliError>>;
4
+ export { collectImportFiles };
@@ -0,0 +1,55 @@
1
+ import { readdir, stat } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ import { causeOf, cliErr } from "./cli-error.js";
4
+ import { err, ok } from "./result.js";
5
+ // A file is an import candidate if it looks like a transcript container:
6
+ // Claude Code / Codex session JSONL, or a Cursor SQLite store. Claude Code
7
+ // subagent sidechains (<session>/subagents/agent-*.jsonl) are skipped — they
8
+ // are internal tool chatter, not the user's conversation.
9
+ const isCandidate = (path) => !path.split(/[\\/]/).includes("subagents") &&
10
+ (path.endsWith(".jsonl") || path.endsWith(".vscdb"));
11
+ const walk = async (dir) => {
12
+ const entries = await readdir(dir, { withFileTypes: true });
13
+ const nested = await Promise.all(entries.map(async (entry) => {
14
+ const path = join(dir, entry.name);
15
+ if (entry.isDirectory())
16
+ return walk(path);
17
+ return entry.isFile() && isCandidate(path) ? [path] : [];
18
+ }));
19
+ return nested.flat();
20
+ };
21
+ // Expand the user-supplied paths into concrete transcript files. A directory
22
+ // is scanned recursively for candidates; an explicitly named file is always
23
+ // kept (the user pointed at it — let format detection decide what it is).
24
+ const collectImportFiles = async (paths) => {
25
+ const collected = await Promise.all(paths.map(async (path) => {
26
+ const info = await (async () => {
27
+ try {
28
+ return await stat(path);
29
+ }
30
+ catch (e) {
31
+ return causeOf(e);
32
+ }
33
+ })();
34
+ if (typeof info === "string")
35
+ return err(cliErr({
36
+ what: "path not found",
37
+ why: info,
38
+ path,
39
+ remedy: "pass a transcript file or directory — Claude Code: ~/.claude/projects · Codex: ~/.codex/sessions · Cursor: ~/.config/Cursor/User/globalStorage/state.vscdb (macOS: ~/Library/Application Support/Cursor/…)",
40
+ }));
41
+ if (info.isDirectory())
42
+ return ok(await walk(path));
43
+ return ok([path]);
44
+ }));
45
+ const failed = collected.find((r) => !r.ok);
46
+ if (failed && !failed.ok)
47
+ return failed;
48
+ const files = collected
49
+ .flatMap((r) => (r.ok ? r.value : []))
50
+ .filter((f, i, all) => all.indexOf(f) === i)
51
+ .sort();
52
+ return ok(files);
53
+ };
54
+ export { collectImportFiles };
55
+ //# sourceMappingURL=collect-import-files.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collect-import-files.js","sourceRoot":"","sources":["../../src/_utils/collect-import-files.ts"],"names":[],"mappings":"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"}
@@ -16,11 +16,13 @@ declare const config: {
16
16
  artifactSearchUrl: string;
17
17
  commitAnchorUrl: string;
18
18
  codeSearchUrl: string;
19
+ importUrl: string;
19
20
  ingestTimeoutMs: number;
20
21
  searchTimeoutMs: number;
21
22
  forgetTimeoutMs: number;
22
23
  listOrgsTimeoutMs: number;
23
24
  commitAnchorTimeoutMs: number;
24
25
  codeSearchTimeoutMs: number;
26
+ importTimeoutMs: number;
25
27
  };
26
28
  export { config };
@@ -19,6 +19,7 @@ const config = {
19
19
  artifactSearchUrl: `${baseUrl}/api/mcp/route/artifactSearch`,
20
20
  commitAnchorUrl: `${baseUrl}/api/mcp/route/commitAnchor`,
21
21
  codeSearchUrl: `${baseUrl}/api/mcp/route/codeSearch`,
22
+ importUrl: `${baseUrl}/api/mcp/route/importTranscript`,
22
23
  ingestTimeoutMs: 8000,
23
24
  searchTimeoutMs: 15_000,
24
25
  forgetTimeoutMs: 15_000,
@@ -26,6 +27,8 @@ const config = {
26
27
  commitAnchorTimeoutMs: 3000,
27
28
  // Longer than search: the server runs a scoped LLM ranking AND renders transcripts.
28
29
  codeSearchTimeoutMs: 30_000,
30
+ // Per-turn write; generous because imported turns can carry near-cap content.
31
+ importTimeoutMs: 15_000,
29
32
  };
30
33
  export { config };
31
34
  //# sourceMappingURL=config.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/_utils/config.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,gEAAgE;AAChE,MAAM,OAAO,GAAG,CACd,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,yBAAyB,CACvD,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AAEtB,MAAM,MAAM,GAAG;IACb,OAAO;IACP,UAAU,EAAE,GAAG,OAAO,cAAc;IACpC,WAAW,EAAE,GAAG,OAAO,2BAA2B;IAClD,OAAO,EAAE,GAAG,OAAO,qBAAqB;IACxC,SAAS,EAAE,GAAG,OAAO,uBAAuB;IAC5C,SAAS,EAAE,GAAG,OAAO,uBAAuB;IAC5C,SAAS,EAAE,GAAG,OAAO,6BAA6B;IAClD,WAAW,EAAE,GAAG,OAAO,yBAAyB;IAChD,cAAc,EAAE,GAAG,OAAO,4BAA4B;IACtD,WAAW,EAAE,GAAG,OAAO,yBAAyB;IAChD,cAAc,EAAE,GAAG,OAAO,4BAA4B;IACtD,mBAAmB,EAAE,GAAG,OAAO,iCAAiC;IAChE,iBAAiB,EAAE,GAAG,OAAO,+BAA+B;IAC5D,cAAc,EAAE,GAAG,OAAO,4BAA4B;IACtD,iBAAiB,EAAE,GAAG,OAAO,+BAA+B;IAC5D,eAAe,EAAE,GAAG,OAAO,6BAA6B;IACxD,aAAa,EAAE,GAAG,OAAO,2BAA2B;IACpD,eAAe,EAAE,IAAI;IACrB,eAAe,EAAE,MAAM;IACvB,eAAe,EAAE,MAAM;IACvB,iBAAiB,EAAE,MAAM;IACzB,qBAAqB,EAAE,IAAI;IAC3B,oFAAoF;IACpF,mBAAmB,EAAE,MAAM;CAC5B,CAAC;AAEF,OAAO,EAAE,MAAM,EAAE,CAAC"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/_utils/config.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,gEAAgE;AAChE,MAAM,OAAO,GAAG,CACd,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,yBAAyB,CACvD,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AAEtB,MAAM,MAAM,GAAG;IACb,OAAO;IACP,UAAU,EAAE,GAAG,OAAO,cAAc;IACpC,WAAW,EAAE,GAAG,OAAO,2BAA2B;IAClD,OAAO,EAAE,GAAG,OAAO,qBAAqB;IACxC,SAAS,EAAE,GAAG,OAAO,uBAAuB;IAC5C,SAAS,EAAE,GAAG,OAAO,uBAAuB;IAC5C,SAAS,EAAE,GAAG,OAAO,6BAA6B;IAClD,WAAW,EAAE,GAAG,OAAO,yBAAyB;IAChD,cAAc,EAAE,GAAG,OAAO,4BAA4B;IACtD,WAAW,EAAE,GAAG,OAAO,yBAAyB;IAChD,cAAc,EAAE,GAAG,OAAO,4BAA4B;IACtD,mBAAmB,EAAE,GAAG,OAAO,iCAAiC;IAChE,iBAAiB,EAAE,GAAG,OAAO,+BAA+B;IAC5D,cAAc,EAAE,GAAG,OAAO,4BAA4B;IACtD,iBAAiB,EAAE,GAAG,OAAO,+BAA+B;IAC5D,eAAe,EAAE,GAAG,OAAO,6BAA6B;IACxD,aAAa,EAAE,GAAG,OAAO,2BAA2B;IACpD,SAAS,EAAE,GAAG,OAAO,iCAAiC;IACtD,eAAe,EAAE,IAAI;IACrB,eAAe,EAAE,MAAM;IACvB,eAAe,EAAE,MAAM;IACvB,iBAAiB,EAAE,MAAM;IACzB,qBAAqB,EAAE,IAAI;IAC3B,oFAAoF;IACpF,mBAAmB,EAAE,MAAM;IAC3B,8EAA8E;IAC9E,eAAe,EAAE,MAAM;CACxB,CAAC;AAEF,OAAO,EAAE,MAAM,EAAE,CAAC"}
@@ -0,0 +1,2 @@
1
+ declare const detectTranscriptFormat: (filePath: string, sampleLines: readonly string[]) => "claude-code" | "codex" | "cursor" | null;
2
+ export { detectTranscriptFormat };
@@ -0,0 +1,53 @@
1
+ const parseLine = (line) => {
2
+ try {
3
+ const parsed = JSON.parse(line);
4
+ return parsed !== null && typeof parsed === "object"
5
+ ? parsed
6
+ : null;
7
+ }
8
+ catch {
9
+ return null;
10
+ }
11
+ };
12
+ const CODEX_ENVELOPE_TYPES = new Set([
13
+ "session_meta",
14
+ "response_item",
15
+ "event_msg",
16
+ "turn_context",
17
+ "compacted",
18
+ ]);
19
+ // Sniff which agent wrote a transcript file. Cursor is identified by its
20
+ // SQLite container; the two JSONL formats are told apart by their envelopes:
21
+ // Claude Code lines carry uuid/parentUuid tree fields, Codex rollout lines a
22
+ // {timestamp, type, payload} wrapper (or, pre-envelope, bare Responses-API
23
+ // message items with input_text/output_text parts).
24
+ const detectTranscriptFormat = (filePath, sampleLines) => {
25
+ if (filePath.endsWith(".vscdb"))
26
+ return "cursor";
27
+ return (sampleLines
28
+ .slice(0, 25)
29
+ .map(parseLine)
30
+ .filter((e) => e !== null)
31
+ .map((e) => {
32
+ if ("parentUuid" in e || "leafUuid" in e)
33
+ return "claude-code";
34
+ if (typeof e.sessionId === "string" &&
35
+ (e.type === "user" || e.type === "assistant" || e.type === "summary"))
36
+ return "claude-code";
37
+ if (typeof e.type === "string" &&
38
+ CODEX_ENVELOPE_TYPES.has(e.type) &&
39
+ "payload" in e)
40
+ return "codex";
41
+ if (e.type === "message" &&
42
+ Array.isArray(e.content) &&
43
+ e.content.some((p) => p !== null &&
44
+ typeof p === "object" &&
45
+ (p.type === "input_text" ||
46
+ p.type === "output_text")))
47
+ return "codex";
48
+ return null;
49
+ })
50
+ .find((f) => f !== null) ?? null);
51
+ };
52
+ export { detectTranscriptFormat };
53
+ //# sourceMappingURL=detect-transcript-format.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detect-transcript-format.js","sourceRoot":"","sources":["../../src/_utils/detect-transcript-format.ts"],"names":[],"mappings":"AAEA,MAAM,SAAS,GAAG,CAAC,IAAY,EAAgB,EAAE;IAC/C,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAY,CAAC;QAC3C,OAAO,MAAM,KAAK,IAAI,IAAI,OAAO,MAAM,KAAK,QAAQ;YAClD,CAAC,CAAE,MAAgB;YACnB,CAAC,CAAC,IAAI,CAAC;IACX,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,oBAAoB,GAAG,IAAI,GAAG,CAAC;IACnC,cAAc;IACd,eAAe;IACf,WAAW;IACX,cAAc;IACd,WAAW;CACZ,CAAC,CAAC;AAEH,yEAAyE;AACzE,6EAA6E;AAC7E,6EAA6E;AAC7E,2EAA2E;AAC3E,oDAAoD;AACpD,MAAM,sBAAsB,GAAG,CAC7B,QAAgB,EAChB,WAA8B,EACa,EAAE;IAC7C,IAAI,QAAQ,CAAC,QAAQ,CAAC,QAAQ,CAAC;QAAE,OAAO,QAAQ,CAAC;IACjD,OAAO,CACL,WAAW;SACR,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;SACZ,GAAG,CAAC,SAAS,CAAC;SACd,MAAM,CAAC,CAAC,CAAC,EAAc,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC;SACrC,GAAG,CAAC,CAAC,CAAC,EAAkC,EAAE;QACzC,IAAI,YAAY,IAAI,CAAC,IAAI,UAAU,IAAI,CAAC;YAAE,OAAO,aAAa,CAAC;QAC/D,IACE,OAAO,CAAC,CAAC,SAAS,KAAK,QAAQ;YAC/B,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,IAAI,CAAC,CAAC,IAAI,KAAK,WAAW,IAAI,CAAC,CAAC,IAAI,KAAK,SAAS,CAAC;YAErE,OAAO,aAAa,CAAC;QACvB,IACE,OAAO,CAAC,CAAC,IAAI,KAAK,QAAQ;YAC1B,oBAAoB,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;YAChC,SAAS,IAAI,CAAC;YAEd,OAAO,OAAO,CAAC;QACjB,IACE,CAAC,CAAC,IAAI,KAAK,SAAS;YACpB,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC;YACxB,CAAC,CAAC,OAAO,CAAC,IAAI,CACZ,CAAC,CAAC,EAAE,EAAE,CACJ,CAAC,KAAK,IAAI;gBACV,OAAO,CAAC,KAAK,QAAQ;gBACrB,CAAE,CAAW,CAAC,IAAI,KAAK,YAAY;oBAChC,CAAW,CAAC,IAAI,KAAK,aAAa,CAAC,CACzC;YAED,OAAO,OAAO,CAAC;QACjB,OAAO,IAAI,CAAC;IACd,CAAC,CAAC;SACD,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC,IAAI,IAAI,CACnC,CAAC;AACJ,CAAC,CAAC;AAEF,OAAO,EAAE,sBAAsB,EAAE,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { ParsedTranscript } from "./transcript-turns.js";
2
+ declare const parseClaudeTranscript: (lines: readonly string[], fallbackSessionId: string) => ParsedTranscript | null;
3
+ export { parseClaudeTranscript };
@@ -0,0 +1,124 @@
1
+ const parseLine = (line) => {
2
+ try {
3
+ const parsed = JSON.parse(line);
4
+ return parsed !== null && typeof parsed === "object"
5
+ ? parsed
6
+ : null;
7
+ }
8
+ catch {
9
+ return null;
10
+ }
11
+ };
12
+ const messageOf = (e) => e.message !== null && typeof e.message === "object"
13
+ ? e.message
14
+ : null;
15
+ // Text of a Claude message: a plain string (typical human prompt) or the
16
+ // concatenated `text` content blocks (assistant output; thinking/tool_use
17
+ // blocks are not conversation text).
18
+ const textOf = (content) => {
19
+ if (typeof content === "string")
20
+ return content;
21
+ if (!Array.isArray(content))
22
+ return "";
23
+ return content
24
+ .filter((p) => p !== null &&
25
+ typeof p === "object" &&
26
+ p.type === "text" &&
27
+ typeof p.text === "string")
28
+ .map((p) => p.text)
29
+ .join("\n");
30
+ };
31
+ // User-role lines that are tool results, not human prompts: a tool_result
32
+ // content block and/or the top-level toolUseResult sibling.
33
+ const isToolResult = (e) => {
34
+ if (e.toolUseResult !== undefined)
35
+ return true;
36
+ const content = messageOf(e)?.content;
37
+ return (Array.isArray(content) &&
38
+ content.some((p) => p !== null &&
39
+ typeof p === "object" &&
40
+ p.type === "tool_result"));
41
+ };
42
+ // Harness-injected user lines that were never typed by the human.
43
+ const INJECTED_PREFIXES = [
44
+ "<command-name>",
45
+ "<command-message>",
46
+ "<local-command-stdout>",
47
+ "<local-command-stderr>",
48
+ "Caveat:",
49
+ ];
50
+ // Claude Code lines form a tree via uuid → parentUuid (edits/retries/resume
51
+ // forks create branches). Follow the chain up from the active leaf — the last
52
+ // line in file order that is nobody's parent — so abandoned branches aren't
53
+ // imported twice. Falls back to file order when the chain is unwalkable
54
+ // (format drift), which matches the active branch in almost all sessions.
55
+ const activeBranch = (entries) => {
56
+ const withUuid = entries.filter((e) => typeof e.uuid === "string");
57
+ const byUuid = new Map(withUuid.map((e) => [e.uuid, e]));
58
+ const parents = new Set(entries
59
+ .map((e) => e.parentUuid)
60
+ .filter((p) => typeof p === "string"));
61
+ const leaf = [...withUuid]
62
+ .reverse()
63
+ .find((e) => !parents.has(e.uuid));
64
+ if (!leaf)
65
+ return entries;
66
+ const walk = (e, acc, seen) => {
67
+ if (!e || typeof e.uuid !== "string" || seen.has(e.uuid))
68
+ return acc;
69
+ return walk(typeof e.parentUuid === "string" ? byUuid.get(e.parentUuid) : undefined, [e, ...acc], new Set([...seen, e.uuid]));
70
+ };
71
+ const chain = walk(leaf, [], new Set());
72
+ return chain.length > 0 ? chain : entries;
73
+ };
74
+ // Parse one Claude Code session file (~/.claude/projects/<munged-cwd>/<uuid>.jsonl)
75
+ // into the normalized transcript shape. Tolerant by design: unknown line types,
76
+ // missing fields, and unparseable lines are skipped, never fatal — the format
77
+ // is undocumented and drifts across versions.
78
+ const parseClaudeTranscript = (lines, fallbackSessionId) => {
79
+ const entries = lines.map(parseLine).filter((e) => e !== null);
80
+ const conversation = activeBranch(entries).filter((e) => {
81
+ if (e.type !== "user" && e.type !== "assistant")
82
+ return false;
83
+ if (e.isSidechain === true)
84
+ return false; // subagent chatter
85
+ if (e.isMeta === true)
86
+ return false; // harness bookkeeping
87
+ if (e.isCompactSummary === true)
88
+ return false; // compaction artifact
89
+ return messageOf(e) !== null;
90
+ });
91
+ const messages = [];
92
+ for (const e of conversation) {
93
+ if (e.type === "user") {
94
+ if (isToolResult(e))
95
+ continue;
96
+ const text = textOf(messageOf(e)?.content);
97
+ if (INJECTED_PREFIXES.some((p) => text.startsWith(p)))
98
+ continue;
99
+ messages.push({ role: "user", text });
100
+ }
101
+ else {
102
+ messages.push({ role: "assistant", text: textOf(messageOf(e)?.content) });
103
+ }
104
+ }
105
+ if (messages.every((m) => m.text.trim() === ""))
106
+ return null;
107
+ const first = entries.find((e) => typeof e.sessionId === "string");
108
+ const firstCwd = entries.find((e) => typeof e.cwd === "string");
109
+ const firstBranch = entries.find((e) => typeof e.gitBranch === "string" && e.gitBranch !== "");
110
+ const lastModel = [...conversation]
111
+ .reverse()
112
+ .map((e) => messageOf(e)?.model)
113
+ .find((m) => typeof m === "string");
114
+ return {
115
+ sessionId: first?.sessionId ?? fallbackSessionId,
116
+ agent: "claude-code",
117
+ cwd: firstCwd?.cwd,
118
+ branch: firstBranch?.gitBranch,
119
+ model: lastModel,
120
+ messages,
121
+ };
122
+ };
123
+ export { parseClaudeTranscript };
124
+ //# sourceMappingURL=parse-claude-transcript.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse-claude-transcript.js","sourceRoot":"","sources":["../../src/_utils/parse-claude-transcript.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { ParsedTranscript } from "./transcript-turns.js";
2
+ declare const parseCodexTranscript: (lines: readonly string[], fallbackSessionId: string) => ParsedTranscript | null;
3
+ export { parseCodexTranscript };
@@ -0,0 +1,111 @@
1
+ const parseLine = (line) => {
2
+ try {
3
+ const parsed = JSON.parse(line);
4
+ return parsed !== null && typeof parsed === "object"
5
+ ? parsed
6
+ : null;
7
+ }
8
+ catch {
9
+ return null;
10
+ }
11
+ };
12
+ // User-role message items Codex injects itself (environment context, AGENTS.md
13
+ // instructions, permission notes) — never typed by the human.
14
+ const INJECTED_PREFIXES = [
15
+ "<environment_context>",
16
+ "<user_instructions>",
17
+ "<permissions",
18
+ "<turn_context>",
19
+ "<ide_context>",
20
+ ];
21
+ // Text of a Responses-API message item: `input_text` parts (user) and
22
+ // `output_text` parts (assistant); other part kinds (images, refusals) are
23
+ // not conversation text.
24
+ const textOf = (content) => {
25
+ if (!Array.isArray(content))
26
+ return "";
27
+ return content
28
+ .filter((p) => p !== null &&
29
+ typeof p === "object" &&
30
+ (p.type === "input_text" ||
31
+ p.type === "output_text") &&
32
+ typeof p.text === "string")
33
+ .map((p) => p.text)
34
+ .join("\n");
35
+ };
36
+ const messageFrom = (item) => {
37
+ if (item.type !== "message")
38
+ return null;
39
+ const role = item.role;
40
+ if (role !== "user" && role !== "assistant")
41
+ return null; // skip system
42
+ const text = textOf(item.content);
43
+ if (role === "user" && INJECTED_PREFIXES.some((p) => text.startsWith(p)))
44
+ return null;
45
+ return { role, text };
46
+ };
47
+ // Parse one Codex CLI rollout file (~/.codex/sessions/YYYY/MM/DD/
48
+ // rollout-<date>-<uuid>.jsonl) into the normalized transcript shape. Only
49
+ // `session_meta` / `turn_context` / `response_item` lines matter — `event_msg`
50
+ // lines duplicate the same content as a UI stream, so they are skipped to
51
+ // avoid double-importing every message. Pre-envelope rollouts (bare
52
+ // ResponseItem objects without {timestamp,type,payload}) are handled too.
53
+ // Tolerant by design: unknown types and malformed lines are skipped.
54
+ const parseCodexTranscript = (lines, fallbackSessionId) => {
55
+ const parsed = lines.map(parseLine).filter((e) => e !== null);
56
+ const result = {
57
+ sessionId: undefined,
58
+ cwd: undefined,
59
+ branch: undefined,
60
+ model: undefined,
61
+ messages: [],
62
+ };
63
+ for (const line of parsed) {
64
+ const payload = line.payload !== null && typeof line.payload === "object"
65
+ ? line.payload
66
+ : null;
67
+ if (line.type === "session_meta" && payload) {
68
+ const git = payload.git !== null && typeof payload.git === "object"
69
+ ? payload.git
70
+ : null;
71
+ result.sessionId ??=
72
+ (typeof payload.id === "string" ? payload.id : undefined) ??
73
+ (typeof payload.session_id === "string"
74
+ ? payload.session_id
75
+ : undefined);
76
+ result.cwd ??= typeof payload.cwd === "string" ? payload.cwd : undefined;
77
+ result.branch ??=
78
+ git && typeof git.branch === "string" ? git.branch : undefined;
79
+ continue;
80
+ }
81
+ if (line.type === "turn_context" && payload) {
82
+ result.cwd ??= typeof payload.cwd === "string" ? payload.cwd : undefined;
83
+ // Latest turn_context wins: the model can change mid-session.
84
+ if (typeof payload.model === "string")
85
+ result.model = payload.model;
86
+ continue;
87
+ }
88
+ // Envelope form (current) or a bare pre-envelope ResponseItem (2025-era
89
+ // rollouts wrote items without the {timestamp,type,payload} wrapper).
90
+ const item = line.type === "response_item" && payload
91
+ ? payload
92
+ : payload === null && line.type === "message"
93
+ ? line
94
+ : null;
95
+ const message = item ? messageFrom(item) : null;
96
+ if (message)
97
+ result.messages.push(message);
98
+ }
99
+ if (result.messages.every((m) => m.text.trim() === ""))
100
+ return null;
101
+ return {
102
+ sessionId: result.sessionId ?? fallbackSessionId,
103
+ agent: "codex",
104
+ cwd: result.cwd,
105
+ branch: result.branch,
106
+ model: result.model,
107
+ messages: result.messages,
108
+ };
109
+ };
110
+ export { parseCodexTranscript };
111
+ //# sourceMappingURL=parse-codex-transcript.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse-codex-transcript.js","sourceRoot":"","sources":["../../src/_utils/parse-codex-transcript.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import { type CliError } from "./cli-error.js";
2
+ import { type Result } from "./result.js";
3
+ import type { ParsedTranscript } from "./transcript-turns.js";
4
+ declare const parseCursorDb: (dbPath: string) => Promise<Result<ParsedTranscript[], CliError>>;
5
+ export { parseCursorDb };