@supersession/sup 0.1.3 → 0.1.4

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/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,56 @@
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
+ // Prose lines that talk about credentials without matching a key pattern.
16
+ const CRED_LINE_RE = /\b(password|passwd|username|user ?name|login|credentials?|api ?key|access ?token)\b/i;
17
+ /** Dedupe by value, keeping the first (earliest turn) occurrence. */
18
+ function dedupe(facts) {
19
+ const seen = new Set();
20
+ return facts.filter((f) => (seen.has(f.value) ? false : (seen.add(f.value), true)));
21
+ }
22
+ export function extractFacts(session) {
23
+ const secrets = [];
24
+ const urls = [];
25
+ const credentialLines = [];
26
+ session.turns.forEach((t, i) => {
27
+ const turn = i + 1;
28
+ const texts = [t.text, ...t.actions.map((a) => a.command ?? "")].filter(Boolean);
29
+ for (const text of texts) {
30
+ for (const hit of findSecrets(text))
31
+ secrets.push({ value: hit.value, label: hit.name, turn });
32
+ for (const m of text.matchAll(URL_RE)) {
33
+ // Trailing punctuation is sentence noise, not part of the URL.
34
+ urls.push({ value: m[0].replace(/[.,;:!?]+$/, ""), label: "url", turn });
35
+ }
36
+ for (const m of text.matchAll(HOSTPORT_RE))
37
+ urls.push({ value: m[0], label: "port", turn });
38
+ }
39
+ // Credential mentions: only assistant prose (that's where "your login is…"
40
+ // lives), line-by-line so the printed fact reads in context.
41
+ if (t.role === "assistant" && t.text) {
42
+ for (const line of t.text.split("\n")) {
43
+ const trimmed = line.trim();
44
+ if (trimmed && CRED_LINE_RE.test(trimmed)) {
45
+ credentialLines.push({ value: trimmed.slice(0, 160), label: "credential mention", turn });
46
+ }
47
+ }
48
+ }
49
+ });
50
+ return {
51
+ secrets: dedupe(secrets),
52
+ urls: dedupe(urls),
53
+ credentialLines: dedupe(credentialLines),
54
+ };
55
+ }
56
+ //# 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;
@@ -0,0 +1,224 @@
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 { existsSync } from "node:fs";
21
+ import { createRequire } from "node:module";
22
+ import { homedir, platform } from "node:os";
23
+ import { join } from "node:path";
24
+ import { NSF_VERSION } from "../nsf.js";
25
+ export const CURSOR_TOOL_ID = "cursor";
26
+ export function cursorDbPath() {
27
+ const base = platform() === "darwin"
28
+ ? join(homedir(), "Library", "Application Support")
29
+ : platform() === "win32"
30
+ ? (process.env.APPDATA ?? join(homedir(), "AppData", "Roaming"))
31
+ : (process.env.XDG_CONFIG_HOME ?? join(homedir(), ".config"));
32
+ return join(base, "Cursor", "User", "globalStorage", "state.vscdb");
33
+ }
34
+ // node:sqlite is the whole reason this parser has zero dependencies, but it
35
+ // needs Node >= 22.5. Loaded lazily so the rest of sup works on older Nodes.
36
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
37
+ function openDb(path) {
38
+ let sqlite;
39
+ try {
40
+ sqlite = createRequire(import.meta.url)("node:sqlite");
41
+ }
42
+ catch {
43
+ throw new Error("Reading Cursor sessions needs Node 22.5+ (built-in node:sqlite).");
44
+ }
45
+ return new sqlite.DatabaseSync(path, { readOnly: true });
46
+ }
47
+ function parseHeaderRow(row) {
48
+ try {
49
+ const v = JSON.parse(row.value);
50
+ if (v.isDraft)
51
+ return undefined;
52
+ const branches = v.trackedGitRepos?.[0]?.branches ?? [];
53
+ const branch = branches.reduce((best, b) => ((b.lastInteractionAt ?? 0) > (best?.lastInteractionAt ?? -1) ? b : best), undefined)?.branchName;
54
+ return {
55
+ composerId: row.composerId,
56
+ lastUpdatedAt: row.lastUpdatedAt,
57
+ name: v.name,
58
+ fsPath: v.workspaceIdentifier?.uri?.fsPath ?? v.trackedGitRepos?.[0]?.repoPath,
59
+ branch,
60
+ };
61
+ }
62
+ catch {
63
+ return undefined;
64
+ }
65
+ }
66
+ /** Newest non-subagent Cursor conversation for a working directory, if any. */
67
+ export function findCursorComposer(cwd) {
68
+ const dbPath = cursorDbPath();
69
+ if (!existsSync(dbPath))
70
+ return undefined;
71
+ let db;
72
+ try {
73
+ db = openDb(dbPath);
74
+ }
75
+ catch {
76
+ return undefined; // old Node or locked DB — discovery degrades gracefully
77
+ }
78
+ try {
79
+ const rows = db
80
+ .prepare(`SELECT composerId, lastUpdatedAt, value FROM composerHeaders
81
+ WHERE isSubagent = 0 ORDER BY recency DESC LIMIT 200`)
82
+ .all();
83
+ for (const row of rows) {
84
+ const h = parseHeaderRow(row);
85
+ if (h?.fsPath === cwd)
86
+ return { composerId: h.composerId, mtimeMs: h.lastUpdatedAt };
87
+ }
88
+ return undefined;
89
+ }
90
+ finally {
91
+ db.close();
92
+ }
93
+ }
94
+ /** Map a Cursor tool name (e.g. "read_file_v2", "ripgrep_raw_search") to NSF. */
95
+ function normalizeCursorTool(name) {
96
+ if (/terminal|shell|command|exec/.test(name))
97
+ return "run";
98
+ if (/edit|apply|write|create|replace/.test(name))
99
+ return "edit";
100
+ if (/read|view|open/.test(name))
101
+ return "read";
102
+ if (/search|grep|glob|codebase|list_dir|ripgrep/.test(name))
103
+ return "search";
104
+ return "other";
105
+ }
106
+ function actionFrom(tfd) {
107
+ const rawName = typeof tfd.name === "string" ? tfd.name : "unknown";
108
+ let path;
109
+ let command;
110
+ const rawArgs = tfd.rawArgs ?? tfd.params;
111
+ if (typeof rawArgs === "string") {
112
+ try {
113
+ const a = JSON.parse(rawArgs);
114
+ const p = a.path ?? a.targetFile ?? a.target_file ?? a.file_path;
115
+ path = typeof p === "string" ? p : undefined;
116
+ command = typeof a.command === "string" ? a.command : undefined;
117
+ }
118
+ catch {
119
+ // unparseable args — the tool name alone is still signal
120
+ }
121
+ }
122
+ return {
123
+ kind: normalizeCursorTool(rawName),
124
+ rawName,
125
+ path,
126
+ command,
127
+ failed: tfd.status === "error" ? true : undefined,
128
+ };
129
+ }
130
+ /**
131
+ * Parse one Cursor conversation into NSF.
132
+ * @param dbPath absolute path to Cursor's global state.vscdb
133
+ * @param composerId the conversation to parse (from findCursorComposer)
134
+ */
135
+ export function parseCursor(dbPath, composerId) {
136
+ const db = openDb(dbPath);
137
+ try {
138
+ const kv = db.prepare(`SELECT value FROM cursorDiskKV WHERE key = ?`);
139
+ const header = db
140
+ .prepare(`SELECT composerId, lastUpdatedAt, value FROM composerHeaders WHERE composerId = ?`)
141
+ .get(composerId);
142
+ const head = header ? parseHeaderRow(header) : undefined;
143
+ const dataRow = kv.get(`composerData:${composerId}`);
144
+ if (!dataRow)
145
+ throw new Error(`Cursor conversation ${composerId} not found in ${dbPath}`);
146
+ const data = JSON.parse(dataRow.value);
147
+ const order = data.fullConversationHeadersOnly ?? [];
148
+ const turns = [];
149
+ const filesSeen = new Set();
150
+ const filesOrdered = [];
151
+ const gapCounts = new Map();
152
+ const addGap = (reason) => gapCounts.set(reason, (gapCounts.get(reason) ?? 0) + 1);
153
+ let rawBytes = 0;
154
+ for (const { bubbleId } of order) {
155
+ const row = kv.get(`bubbleId:${composerId}:${bubbleId}`);
156
+ if (!row) {
157
+ addGap("missing bubble record");
158
+ continue;
159
+ }
160
+ rawBytes += row.value.length;
161
+ let bubble;
162
+ try {
163
+ bubble = JSON.parse(row.value);
164
+ }
165
+ catch {
166
+ addGap("unparseable bubble");
167
+ continue;
168
+ }
169
+ const role = bubble.type === 1 ? "user" : bubble.type === 2 ? "assistant" : undefined;
170
+ if (!role) {
171
+ addGap("bubble with unknown type");
172
+ continue;
173
+ }
174
+ const text = (bubble.text ?? "").trim();
175
+ const actions = [];
176
+ if (bubble.toolFormerData && typeof bubble.toolFormerData === "object") {
177
+ const a = actionFrom(bubble.toolFormerData);
178
+ actions.push(a);
179
+ if (a.path && !filesSeen.has(a.path)) {
180
+ filesSeen.add(a.path);
181
+ filesOrdered.push(a.path);
182
+ }
183
+ }
184
+ if (!text && actions.length === 0) {
185
+ addGap("empty turn");
186
+ continue;
187
+ }
188
+ // Cursor stores each tool call as its own assistant bubble; fold
189
+ // consecutive action-only bubbles into the previous assistant turn so
190
+ // NSF reads as conversation, not a tool ledger.
191
+ const prev = turns[turns.length - 1];
192
+ if (role === "assistant" && !text && prev?.role === "assistant") {
193
+ prev.actions.push(...actions);
194
+ continue;
195
+ }
196
+ turns.push({
197
+ role,
198
+ text,
199
+ actions,
200
+ timestamp: bubble.createdAt ? Date.parse(bubble.createdAt) : undefined,
201
+ });
202
+ }
203
+ const gaps = [...gapCounts.entries()].map(([reason, count]) => ({ reason, count }));
204
+ return {
205
+ nsfVersion: NSF_VERSION,
206
+ provenance: {
207
+ tool: CURSOR_TOOL_ID,
208
+ sessionId: composerId,
209
+ title: head?.name,
210
+ cwd: head?.fsPath,
211
+ gitBranch: head?.branch,
212
+ rawBytes,
213
+ compactions: 0,
214
+ },
215
+ turns,
216
+ filesTouched: filesOrdered,
217
+ gaps,
218
+ };
219
+ }
220
+ finally {
221
+ db.close();
222
+ }
223
+ }
224
+ //# sourceMappingURL=cursor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cursor.js","sourceRoot":"","sources":["../../src/parsers/cursor.ts"],"names":[],"mappings":"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"}