@supersession/sup 0.1.3 → 0.1.5

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.
@@ -70,23 +70,64 @@ export function groundFiles(session) {
70
70
  git: git.get(path),
71
71
  }));
72
72
  }
73
- /** Notable, deduped shell commands (skip trivial navigation/inspection). */
73
+ // Read-only exploration/inspection tools. A handoff wants the commands that
74
+ // CHANGED or VERIFIED state (build, test, migrate, deploy, git), not the
75
+ // agent's spelunking (grep/find/gh api/sqlite SELECT) — that noise buries the
76
+ // signal an agent actually needs to continue.
77
+ const NOISE_HEADS = new Set([
78
+ "cd", "ls", "pwd", "cat", "echo", "printf", "clear", "which", "whoami", "env", "export",
79
+ "grep", "egrep", "fgrep", "rg", "ripgrep", "ag", "find", "fd", "locate",
80
+ "head", "tail", "less", "more", "tree", "wc", "du", "df", "stat", "file",
81
+ "sed", "awk", "jq", "yq", "sort", "uniq", "cut", "column", "xxd", "od",
82
+ "sqlite3", "gh", "dig", "ping", "host", "nslookup", "man", "help", "true", "false",
83
+ ]);
84
+ /** The verb of a single command segment: drop `VAR=val ` prefixes and redirects. */
85
+ function segmentHead(seg) {
86
+ // Trailing \s* (not \s+) so a quoted value is fully consumed even when the
87
+ // assignment is the whole segment — otherwise \S+ backtracks into the quote.
88
+ const stripped = seg.replace(/^(?:\w+=(?:"[^"]*"|'[^']*'|\S+)\s*)+/, "").trim();
89
+ return stripped.split(/\s+/)[0] ?? "";
90
+ }
91
+ /** Inline code-eval invocations are exploration, not project commands. */
92
+ function isInlineEval(cmd) {
93
+ return /\b(?:node|python3?|ruby|deno|bun)\b[^\n]*\s(?:-e|--eval|-c|--input-type)\b/.test(cmd);
94
+ }
95
+ /**
96
+ * A command is meaningful (worth a handoff) only if, after splitting compound
97
+ * one-liners on shell separators, at least one segment is a real verb — not a
98
+ * bare `VAR=val` assignment and not a read-only exploration tool. This kills
99
+ * `DB=…; sqlite3 …; echo …` spelunking while keeping `pnpm test | tail`.
100
+ */
101
+ function isMeaningfulCommand(cmd) {
102
+ if (isInlineEval(cmd))
103
+ return false;
104
+ // Split on command separators only — NOT single `|`. A pipe's first command
105
+ // is the primary one (its head decides), and splitting on `|` would shred
106
+ // grep/regex patterns like "a\|b" into verb-looking fragments.
107
+ const segments = cmd.split(/&&|\|\||;/);
108
+ return segments.some((seg) => {
109
+ const head = segmentHead(seg);
110
+ return head.length > 0 && !head.includes("=") && !NOISE_HEADS.has(head);
111
+ });
112
+ }
113
+ /** Notable, deduped shell commands (skip read-only navigation/inspection). */
74
114
  function keyCommands(session) {
75
115
  const seen = new Set();
76
116
  const out = [];
77
- const trivial = /^(cd|ls|pwd|cat|echo|clear|which|whoami)\b/;
78
117
  for (const turn of session.turns) {
79
118
  for (const a of turn.actions) {
80
119
  if (a.kind !== "run" || !a.command)
81
120
  continue;
82
121
  const cmd = a.command.split("\n")[0].trim();
83
- if (!cmd || trivial.test(cmd) || seen.has(cmd))
122
+ if (!cmd || seen.has(cmd))
123
+ continue;
124
+ if (!isMeaningfulCommand(cmd))
84
125
  continue;
85
126
  seen.add(cmd);
86
127
  out.push(cmd);
87
128
  }
88
129
  }
89
- return out.slice(0, 20);
130
+ return out.slice(0, 15);
90
131
  }
91
132
  function firstLines(text, n) {
92
133
  return text.split("\n").slice(0, n).join("\n").trim();
@@ -103,6 +144,22 @@ function inferNextStep(turns) {
103
144
  return firstLines(lastUser.text, 3);
104
145
  return "(no pending user request — continue the last thread)";
105
146
  }
147
+ /**
148
+ * One-line title + short summary for a session, used as cloud/list metadata.
149
+ * Prefers the source tool's own session name (Cursor names chats); otherwise
150
+ * the inferred goal. Same grounded-only rules as the rest of template distill.
151
+ */
152
+ export function sessionHeadline(session) {
153
+ const clip = (s, n) => {
154
+ const one = s.split("\n")[0].trim();
155
+ return one.length > n ? `${one.slice(0, n - 1)}…` : one;
156
+ };
157
+ const title = session.provenance.title?.trim() || inferGoal(session.turns);
158
+ return {
159
+ title: clip(title, 80),
160
+ summary: clip(summarizeState(session.turns), 200),
161
+ };
162
+ }
106
163
  export function distillTemplate(session, opts = {}) {
107
164
  const recentN = opts.recentTurns ?? DEFAULT_RECENT;
108
165
  const recentTurns = session.turns.slice(-recentN);
@@ -1 +1 @@
1
- {"version":3,"file":"template.js","sourceRoot":"","sources":["../../src/distill/template.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"template.js","sourceRoot":"","sources":["../../src/distill/template.ts"],"names":[],"mappings":"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"}
package/dist/engine.d.ts CHANGED
@@ -9,7 +9,7 @@ import { type Brief } from "./distill/template.js";
9
9
  import type { NsfSession } from "./nsf.js";
10
10
  export declare class NoSessionError extends Error {
11
11
  }
12
- /** Parse the latest Claude Code session for a directory, or throw NoSessionError. */
12
+ /** Parse the newest session (any supported tool) for a directory, or throw. */
13
13
  export declare function requireSession(cwd: string): NsfSession;
14
14
  export interface DistillResult {
15
15
  brief: Brief;
package/dist/engine.js CHANGED
@@ -5,19 +5,18 @@
5
5
  * over stdout, so a stray console.log would corrupt the protocol. Callers get
6
6
  * structured results and decide how (or whether) to print them.
7
7
  */
8
- import { findClaudeCodeSession } from "./discover.js";
9
- import { parseClaudeCode } from "./parsers/claude-code.js";
8
+ import { findLatestSession } from "./discover.js";
10
9
  import { redactSession } from "./redact.js";
11
10
  import { distillTemplate } from "./distill/template.js";
12
11
  import { distillSmart } from "./distill/smart.js";
13
12
  export class NoSessionError extends Error {
14
13
  }
15
- /** Parse the latest Claude Code session for a directory, or throw NoSessionError. */
14
+ /** Parse the newest session (any supported tool) for a directory, or throw. */
16
15
  export function requireSession(cwd) {
17
- const found = findClaudeCodeSession(cwd);
16
+ const found = findLatestSession(cwd);
18
17
  if (!found)
19
- throw new NoSessionError(`No Claude Code session found for ${cwd} (looked under ~/.claude/projects).`);
20
- const parsed = parseClaudeCode(found.filePath);
18
+ throw new NoSessionError(`No session found for ${cwd} (looked for Claude Code, Codex, and Cursor sessions).`);
19
+ const parsed = found.parse();
21
20
  if (parsed.turns.length === 0)
22
21
  throw new NoSessionError("Session file had no usable turns.");
23
22
  return parsed;
@@ -1 +1 @@
1
- {"version":3,"file":"engine.js","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,eAAe,EAAc,MAAM,uBAAuB,CAAC;AACpE,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAGlD,MAAM,OAAO,cAAe,SAAQ,KAAK;CAAG;AAE5C,qFAAqF;AACrF,MAAM,UAAU,cAAc,CAAC,GAAW;IACxC,MAAM,KAAK,GAAG,qBAAqB,CAAC,GAAG,CAAC,CAAC;IACzC,IAAI,CAAC,KAAK;QACR,MAAM,IAAI,cAAc,CAAC,oCAAoC,GAAG,qCAAqC,CAAC,CAAC;IACzG,MAAM,MAAM,GAAG,eAAe,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IAC/C,IAAI,MAAM,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,MAAM,IAAI,cAAc,CAAC,mCAAmC,CAAC,CAAC;IAC7F,OAAO,MAAM,CAAC;AAChB,CAAC;AASD;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,GAAe,EACf,aAAsB;IAEtB,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC;IAC7C,IAAI,aAAa,EAAE,CAAC;QAClB,OAAO,EAAE,KAAK,EAAE,eAAe,CAAC,OAAO,EAAE,EAAE,eAAe,EAAE,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IACxF,CAAC;IACD,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,MAAM,YAAY,CAAC,OAAO,EAAE,EAAE,eAAe,EAAE,IAAI,EAAE,CAAC,CAAC;IACzF,MAAM,IAAI,GAAG,OAAO;QAClB,CAAC,CAAC,SAAS;QACX,CAAC,CAAC,KAAK;YACL,CAAC,CAAC,8BAA8B,KAAK,0BAA0B;YAC/D,CAAC,CAAC,wGAAwG,CAAC;IAC/G,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AAClC,CAAC"}
1
+ {"version":3,"file":"engine.js","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,eAAe,EAAc,MAAM,uBAAuB,CAAC;AACpE,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAGlD,MAAM,OAAO,cAAe,SAAQ,KAAK;CAAG;AAE5C,+EAA+E;AAC/E,MAAM,UAAU,cAAc,CAAC,GAAW;IACxC,MAAM,KAAK,GAAG,iBAAiB,CAAC,GAAG,CAAC,CAAC;IACrC,IAAI,CAAC,KAAK;QACR,MAAM,IAAI,cAAc,CACtB,wBAAwB,GAAG,wDAAwD,CACpF,CAAC;IACJ,MAAM,MAAM,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC;IAC7B,IAAI,MAAM,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,MAAM,IAAI,cAAc,CAAC,mCAAmC,CAAC,CAAC;IAC7F,OAAO,MAAM,CAAC;AAChB,CAAC;AASD;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,GAAe,EACf,aAAsB;IAEtB,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC;IAC7C,IAAI,aAAa,EAAE,CAAC;QAClB,OAAO,EAAE,KAAK,EAAE,eAAe,CAAC,OAAO,EAAE,EAAE,eAAe,EAAE,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IACxF,CAAC;IACD,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,MAAM,YAAY,CAAC,OAAO,EAAE,EAAE,eAAe,EAAE,IAAI,EAAE,CAAC,CAAC;IACzF,MAAM,IAAI,GAAG,OAAO;QAClB,CAAC,CAAC,SAAS;QACX,CAAC,CAAC,KAAK;YACL,CAAC,CAAC,8BAA8B,KAAK,0BAA0B;YAC/D,CAAC,CAAC,wGAAwG,CAAC;IAC/G,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AAClC,CAAC"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * `sup facts` — extract the nuggets people scroll transcripts hunting for:
3
+ * credentials the agent generated, URLs and ports, and credential-ish prose
4
+ * lines ("your test login is admin@x.com / hunter2").
5
+ *
6
+ * LOCAL ONLY. This module is the mirror image of redact.ts: redaction strips
7
+ * secrets before anything leaves the machine; facts surfaces them ON the
8
+ * machine. Nothing here is ever pushed, written to a brief, or logged remotely.
9
+ */
10
+ import type { NsfSession } from "./nsf.js";
11
+ export interface Fact {
12
+ /** What was found (secret value, URL, or a verbatim prose line). */
13
+ value: string;
14
+ /** Category detail, e.g. "github-token" or "credential mention". */
15
+ label: string;
16
+ /** 1-based turn number, so the user can find it in context. */
17
+ turn: number;
18
+ }
19
+ export interface SessionFacts {
20
+ secrets: Fact[];
21
+ urls: Fact[];
22
+ credentialLines: Fact[];
23
+ }
24
+ export declare function extractFacts(session: NsfSession): SessionFacts;
package/dist/facts.js ADDED
@@ -0,0 +1,68 @@
1
+ /**
2
+ * `sup facts` — extract the nuggets people scroll transcripts hunting for:
3
+ * credentials the agent generated, URLs and ports, and credential-ish prose
4
+ * lines ("your test login is admin@x.com / hunter2").
5
+ *
6
+ * LOCAL ONLY. This module is the mirror image of redact.ts: redaction strips
7
+ * secrets before anything leaves the machine; facts surfaces them ON the
8
+ * machine. Nothing here is ever pushed, written to a brief, or logged remotely.
9
+ */
10
+ import { findSecrets } from "./redact.js";
11
+ const URL_RE = /\bhttps?:\/\/[^\s)"'`<>\]]+/g;
12
+ // Bare host:port the way dev servers get mentioned ("listening on localhost:3000").
13
+ // The lookbehind keeps it from double-reporting the host inside an http:// URL.
14
+ const HOSTPORT_RE = /(?<![/.\w])(?:localhost|127\.0\.0\.1|0\.0\.0\.0):\d{2,5}\b/g;
15
+ // A credential MENTION worth surfacing pairs a credential keyword with an
16
+ // actual value — not just prose that discusses credentials. The value signal
17
+ // must be an email or an explicit `keyword: value` / `keyword=value` pair;
18
+ // backticks alone are too common in prose to count. This keeps "test login:
19
+ // admin@x.com / Passw0rd!" while dropping "the credentials/tokens problem" and
20
+ // "OAuth login (commit `abc123`)".
21
+ const CRED_KEYWORD_RE = /\b(password|passwd|username|user ?name|login|credentials?|api ?key|access ?token)\b/i;
22
+ const EMAIL_RE = /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b/;
23
+ const KEYVAL_RE = /\b(pass\w*|pwd|user\w*|login|token|key|secret|cred\w*)\b\s*[:=]\s*\S{3,}/i;
24
+ function isCredentialMention(line) {
25
+ if (!CRED_KEYWORD_RE.test(line))
26
+ return false;
27
+ return EMAIL_RE.test(line) || KEYVAL_RE.test(line);
28
+ }
29
+ /** Dedupe by value, keeping the first (earliest turn) occurrence. */
30
+ function dedupe(facts) {
31
+ const seen = new Set();
32
+ return facts.filter((f) => (seen.has(f.value) ? false : (seen.add(f.value), true)));
33
+ }
34
+ export function extractFacts(session) {
35
+ const secrets = [];
36
+ const urls = [];
37
+ const credentialLines = [];
38
+ session.turns.forEach((t, i) => {
39
+ const turn = i + 1;
40
+ const texts = [t.text, ...t.actions.map((a) => a.command ?? "")].filter(Boolean);
41
+ for (const text of texts) {
42
+ for (const hit of findSecrets(text))
43
+ secrets.push({ value: hit.value, label: hit.name, turn });
44
+ for (const m of text.matchAll(URL_RE)) {
45
+ // Trailing punctuation is sentence noise, not part of the URL.
46
+ urls.push({ value: m[0].replace(/[.,;:!?]+$/, ""), label: "url", turn });
47
+ }
48
+ for (const m of text.matchAll(HOSTPORT_RE))
49
+ urls.push({ value: m[0], label: "port", turn });
50
+ }
51
+ // Credential mentions: only assistant prose (that's where "your login is…"
52
+ // lives), line-by-line so the printed fact reads in context.
53
+ if (t.role === "assistant" && t.text) {
54
+ for (const line of t.text.split("\n")) {
55
+ const trimmed = line.trim();
56
+ if (trimmed && isCredentialMention(trimmed)) {
57
+ credentialLines.push({ value: trimmed.slice(0, 160), label: "credential mention", turn });
58
+ }
59
+ }
60
+ }
61
+ });
62
+ return {
63
+ secrets: dedupe(secrets),
64
+ urls: dedupe(urls),
65
+ credentialLines: dedupe(credentialLines),
66
+ };
67
+ }
68
+ //# sourceMappingURL=facts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"facts.js","sourceRoot":"","sources":["../src/facts.ts"],"names":[],"mappings":"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"}
package/dist/nsf.d.ts CHANGED
@@ -40,6 +40,8 @@ export interface NsfProvenance {
40
40
  tool: string;
41
41
  /** The tool's native session id. */
42
42
  sessionId?: string;
43
+ /** Human title the source tool gave this session (e.g. Cursor's chat name). */
44
+ title?: string;
43
45
  /** Absolute working directory the session ran in. */
44
46
  cwd?: string;
45
47
  /** Git branch recorded at capture time, if any. */
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Codex CLI parser: rollout JSONL -> NSF.
3
+ *
4
+ * Grounded in the upstream format (verified 2026-07-15 against openai/codex
5
+ * codex-rs rollout recorder test fixtures), not a guess:
6
+ * - Files live at ~/.codex/sessions/YYYY/MM/DD/rollout-<ts>-<uuid>.jsonl
7
+ * - Each line: { timestamp, type, payload } (+ optional ordinal)
8
+ * - type "session_meta": payload has session_id/id, cwd, source; sibling
9
+ * `git` object may carry the branch.
10
+ * - type "response_item": payload is tagged by payload.type:
11
+ * "message" { role, content: [{type:"input_text"|"output_text", text}] }
12
+ * "function_call" { name, arguments (JSON string), call_id }
13
+ * "local_shell_call" { status, action: { command: [...] } }
14
+ * "reasoning", "function_call_output", ... -> gaps
15
+ * - type "event_msg" duplicates messages as UI events -> counted as gaps so
16
+ * turns aren't doubled.
17
+ * - type "compacted" is Codex's lossy in-place summarization -> a scar.
18
+ */
19
+ import type { NsfSession } from "../nsf.js";
20
+ export declare const CODEX_TOOL_ID = "codex";
21
+ /**
22
+ * Parse a Codex rollout JSONL file into NSF.
23
+ * @param filePath absolute path to the rollout-*.jsonl transcript.
24
+ */
25
+ export declare function parseCodex(filePath: string): NsfSession;
@@ -0,0 +1,212 @@
1
+ /**
2
+ * Codex CLI parser: rollout JSONL -> NSF.
3
+ *
4
+ * Grounded in the upstream format (verified 2026-07-15 against openai/codex
5
+ * codex-rs rollout recorder test fixtures), not a guess:
6
+ * - Files live at ~/.codex/sessions/YYYY/MM/DD/rollout-<ts>-<uuid>.jsonl
7
+ * - Each line: { timestamp, type, payload } (+ optional ordinal)
8
+ * - type "session_meta": payload has session_id/id, cwd, source; sibling
9
+ * `git` object may carry the branch.
10
+ * - type "response_item": payload is tagged by payload.type:
11
+ * "message" { role, content: [{type:"input_text"|"output_text", text}] }
12
+ * "function_call" { name, arguments (JSON string), call_id }
13
+ * "local_shell_call" { status, action: { command: [...] } }
14
+ * "reasoning", "function_call_output", ... -> gaps
15
+ * - type "event_msg" duplicates messages as UI events -> counted as gaps so
16
+ * turns aren't doubled.
17
+ * - type "compacted" is Codex's lossy in-place summarization -> a scar.
18
+ */
19
+ import { readFileSync } from "node:fs";
20
+ import { NSF_VERSION } from "../nsf.js";
21
+ export const CODEX_TOOL_ID = "codex";
22
+ /** Codex injects wrapped context as fake user messages; they aren't the user. */
23
+ const INJECTED_USER_RE = /^<(user_instructions|environment_context|ENVIRONMENT_CONTEXT)/i;
24
+ function contentText(content) {
25
+ if (!Array.isArray(content))
26
+ return "";
27
+ return content
28
+ .map((c) => {
29
+ const o = c;
30
+ return (o?.type === "input_text" || o?.type === "output_text") && typeof o.text === "string"
31
+ ? o.text
32
+ : "";
33
+ })
34
+ .filter(Boolean)
35
+ .join("\n\n")
36
+ .trim();
37
+ }
38
+ /** Best-effort shell command out of function_call arguments / shell action. */
39
+ function commandFrom(args) {
40
+ if (typeof args !== "object" || args === null)
41
+ return undefined;
42
+ const cmd = args.command ?? args.cmd;
43
+ if (typeof cmd === "string")
44
+ return cmd;
45
+ if (Array.isArray(cmd))
46
+ return cmd.filter((x) => typeof x === "string").join(" ");
47
+ return undefined;
48
+ }
49
+ /** Paths named in an apply_patch envelope ("*** Update File: src/x.ts" etc). */
50
+ function patchPaths(patch) {
51
+ const out = [];
52
+ for (const m of patch.matchAll(/^\*\*\* (?:Update|Add|Delete) File: (.+)$/gm)) {
53
+ out.push(m[1].trim());
54
+ }
55
+ return out;
56
+ }
57
+ function functionCallActions(name, rawArguments) {
58
+ let args = rawArguments;
59
+ if (typeof rawArguments === "string") {
60
+ try {
61
+ args = JSON.parse(rawArguments);
62
+ }
63
+ catch {
64
+ args = undefined;
65
+ }
66
+ }
67
+ const o = (typeof args === "object" && args !== null ? args : {});
68
+ if (name === "apply_patch") {
69
+ const patch = typeof o.input === "string" ? o.input : typeof rawArguments === "string" ? rawArguments : "";
70
+ const paths = patchPaths(patch);
71
+ if (paths.length === 0)
72
+ return [{ kind: "edit", rawName: name }];
73
+ return paths.map((path) => ({ kind: "edit", rawName: name, path }));
74
+ }
75
+ const path = typeof o.path === "string" ? o.path : typeof o.file_path === "string" ? o.file_path : undefined;
76
+ const command = commandFrom(o);
77
+ const kind = /shell|exec/.test(name)
78
+ ? "run"
79
+ : /read|view/.test(name)
80
+ ? "read"
81
+ : /write|create/.test(name)
82
+ ? "write"
83
+ : /search|grep|glob|find/.test(name)
84
+ ? "search"
85
+ : "other";
86
+ return [{ kind, rawName: name, path, command }];
87
+ }
88
+ /**
89
+ * Parse a Codex rollout JSONL file into NSF.
90
+ * @param filePath absolute path to the rollout-*.jsonl transcript.
91
+ */
92
+ export function parseCodex(filePath) {
93
+ const raw = readFileSync(filePath, "utf8");
94
+ const turns = [];
95
+ const filesSeen = new Set();
96
+ const filesOrdered = [];
97
+ const gapCounts = new Map();
98
+ const addGap = (reason) => gapCounts.set(reason, (gapCounts.get(reason) ?? 0) + 1);
99
+ const provenance = {
100
+ tool: CODEX_TOOL_ID,
101
+ rawBytes: Buffer.byteLength(raw, "utf8"),
102
+ };
103
+ let compactions = 0;
104
+ // function_call actions attach to the pending assistant turn (Codex records
105
+ // calls as separate lines, unlike Claude Code's in-message blocks).
106
+ let pendingActions = [];
107
+ const flushActions = () => {
108
+ if (pendingActions.length === 0)
109
+ return;
110
+ turns.push({ role: "assistant", text: "", actions: pendingActions });
111
+ pendingActions = [];
112
+ };
113
+ for (const line of raw.split("\n")) {
114
+ const trimmed = line.trim();
115
+ if (!trimmed)
116
+ continue;
117
+ let rec;
118
+ try {
119
+ rec = JSON.parse(trimmed);
120
+ }
121
+ catch {
122
+ addGap("unparseable line");
123
+ continue;
124
+ }
125
+ const payload = rec.payload ?? {};
126
+ switch (rec.type) {
127
+ case "session_meta": {
128
+ const id = payload.id ?? payload.session_id;
129
+ provenance.sessionId ??= typeof id === "string" ? id : undefined;
130
+ provenance.cwd ??= typeof payload.cwd === "string" ? payload.cwd : undefined;
131
+ const git = rec.git ?? payload.git;
132
+ const branch = git?.branch;
133
+ provenance.gitBranch ??= typeof branch === "string" ? branch : undefined;
134
+ break;
135
+ }
136
+ case "compacted":
137
+ compactions++;
138
+ addGap("compaction/summary event");
139
+ break;
140
+ case "response_item": {
141
+ const ptype = payload.type;
142
+ if (ptype === "message") {
143
+ const role = payload.role === "user" ? "user" : payload.role === "assistant" ? "assistant" : undefined;
144
+ const text = contentText(payload.content);
145
+ if (!role || !text) {
146
+ addGap("empty turn");
147
+ break;
148
+ }
149
+ if (role === "user" && INJECTED_USER_RE.test(text)) {
150
+ addGap("injected context message");
151
+ break;
152
+ }
153
+ if (role === "assistant" && pendingActions.length > 0) {
154
+ // Attach the actions Codex logged since the last prose turn.
155
+ turns.push({
156
+ role,
157
+ text,
158
+ actions: pendingActions,
159
+ timestamp: rec.timestamp ? Date.parse(rec.timestamp) : undefined,
160
+ });
161
+ pendingActions = [];
162
+ }
163
+ else {
164
+ flushActions();
165
+ turns.push({
166
+ role,
167
+ text,
168
+ actions: [],
169
+ timestamp: rec.timestamp ? Date.parse(rec.timestamp) : undefined,
170
+ });
171
+ }
172
+ }
173
+ else if (ptype === "function_call") {
174
+ const name = typeof payload.name === "string" ? payload.name : "unknown";
175
+ const actions = functionCallActions(name, payload.arguments);
176
+ pendingActions.push(...actions);
177
+ for (const a of actions) {
178
+ if (a.path && !filesSeen.has(a.path)) {
179
+ filesSeen.add(a.path);
180
+ filesOrdered.push(a.path);
181
+ }
182
+ }
183
+ }
184
+ else if (ptype === "local_shell_call") {
185
+ const command = commandFrom(payload.action);
186
+ pendingActions.push({ kind: "run", rawName: "local_shell_call", command });
187
+ }
188
+ else {
189
+ addGap(`non-conversational response item (${String(ptype ?? "unknown")})`);
190
+ }
191
+ break;
192
+ }
193
+ case "event_msg":
194
+ // Duplicates of message content as UI events — skip to avoid doubled turns.
195
+ addGap("ui event record");
196
+ break;
197
+ default:
198
+ addGap(`non-message record (${rec.type ?? "unknown"})`);
199
+ }
200
+ }
201
+ flushActions();
202
+ provenance.compactions = compactions;
203
+ const gaps = [...gapCounts.entries()].map(([reason, count]) => ({ reason, count }));
204
+ return {
205
+ nsfVersion: NSF_VERSION,
206
+ provenance,
207
+ turns,
208
+ filesTouched: filesOrdered,
209
+ gaps,
210
+ };
211
+ }
212
+ //# sourceMappingURL=codex.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codex.js","sourceRoot":"","sources":["../../src/parsers/codex.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Cursor parser: global state.vscdb (SQLite) -> NSF.
3
+ *
4
+ * Grounded in the REAL on-disk schema (verified 2026-07-15 against a live
5
+ * Cursor install), not a guess:
6
+ * - DB: <appdata>/Cursor/User/globalStorage/state.vscdb
7
+ * - Table `composerHeaders` (composerId, workspaceId, lastUpdatedAt,
8
+ * isSubagent, recency, value JSON) — value carries `name` (the chat title),
9
+ * `workspaceIdentifier.uri.fsPath` (cwd) and `trackedGitRepos[].branches`.
10
+ * - Table `cursorDiskKV`:
11
+ * key `composerData:<composerId>` -> { fullConversationHeadersOnly:
12
+ * [{bubbleId, type}] } — the ordered conversation.
13
+ * key `bubbleId:<composerId>:<bubbleId>` -> the message:
14
+ * { type: 1(user)|2(assistant), text, createdAt (ISO),
15
+ * toolFormerData?: { name, rawArgs, status } }.
16
+ *
17
+ * Uses node:sqlite (built-in, Node >= 22.5) — read-only, no native deps. The
18
+ * DB can be multi-GB, so bubbles are fetched by key, never table-scanned.
19
+ */
20
+ import type { NsfSession } from "../nsf.js";
21
+ export declare const CURSOR_TOOL_ID = "cursor";
22
+ export declare function cursorDbPath(): string;
23
+ /** Newest non-subagent Cursor conversation for a working directory, if any. */
24
+ export declare function findCursorComposer(cwd: string): {
25
+ composerId: string;
26
+ mtimeMs: number;
27
+ } | undefined;
28
+ /**
29
+ * Parse one Cursor conversation into NSF.
30
+ * @param dbPath absolute path to Cursor's global state.vscdb
31
+ * @param composerId the conversation to parse (from findCursorComposer)
32
+ */
33
+ export declare function parseCursor(dbPath: string, composerId: string): NsfSession;