ai-hist 0.1.1 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +41 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +107 -33
- package/dist/index.js.map +1 -1
- package/dist/jsonl-sources.d.ts +33 -0
- package/dist/jsonl-sources.d.ts.map +1 -0
- package/dist/jsonl-sources.js +233 -0
- package/dist/jsonl-sources.js.map +1 -0
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -54,28 +54,60 @@ export interface Stats {
|
|
|
54
54
|
}
|
|
55
55
|
/** Resolve the SQLite path the Python CLI writes to. */
|
|
56
56
|
export declare function defaultDbPath(): string;
|
|
57
|
+
export interface OpenOptions {
|
|
58
|
+
/** Override the SQLite path (default: `$AI_HIST_DB` or `~/.local/share/ai-hist/ai-history.db`). */
|
|
59
|
+
dbPath?: string;
|
|
60
|
+
/**
|
|
61
|
+
* What to do when the SQLite DB is missing:
|
|
62
|
+
* - `'jsonl'` (default): scan local Claude/Codex/Cursor history files
|
|
63
|
+
* directly into an in-memory SQLite — works without the Python
|
|
64
|
+
* `ai-hist sync` tool installed.
|
|
65
|
+
* - `'error'`: throw with an install hint (legacy 0.1.x behavior).
|
|
66
|
+
*/
|
|
67
|
+
fallback?: 'jsonl' | 'error';
|
|
68
|
+
}
|
|
69
|
+
export interface OpenSourceInfo {
|
|
70
|
+
/** `'sqlite'` when the on-disk DB was used, `'jsonl'` when the fallback scan was. */
|
|
71
|
+
kind: 'sqlite' | 'jsonl';
|
|
72
|
+
/** SQLite DB path or, in jsonl mode, the paths that were scanned. */
|
|
73
|
+
path: string;
|
|
74
|
+
}
|
|
57
75
|
/**
|
|
58
76
|
* Open an `AiHist` reader. Async because sql.js initializes its WASM
|
|
59
|
-
* runtime lazily
|
|
60
|
-
*
|
|
61
|
-
*
|
|
77
|
+
* runtime lazily and the DB file is read asynchronously so the host
|
|
78
|
+
* process's event loop isn't blocked.
|
|
79
|
+
*
|
|
80
|
+
* Each call snapshots the data; to pick up later writes, call `reload()`
|
|
81
|
+
* (or open a fresh instance).
|
|
62
82
|
*/
|
|
63
|
-
export declare function openAiHist(opts?:
|
|
64
|
-
dbPath?: string;
|
|
65
|
-
}): Promise<AiHist>;
|
|
83
|
+
export declare function openAiHist(opts?: OpenOptions): Promise<AiHist>;
|
|
66
84
|
export declare class AiHist {
|
|
67
85
|
private readonly db;
|
|
68
|
-
private readonly
|
|
86
|
+
private readonly _source;
|
|
69
87
|
private closed;
|
|
70
|
-
/** @internal — use `openAiHist(
|
|
71
|
-
constructor(db: Database,
|
|
88
|
+
/** @internal — use `openAiHist(...)` to construct. */
|
|
89
|
+
constructor(db: Database, source: OpenSourceInfo);
|
|
90
|
+
/**
|
|
91
|
+
* Path the data came from. SQLite mode: the .db path. JSONL fallback
|
|
92
|
+
* mode: a comma-separated list of the scanned source paths.
|
|
93
|
+
*/
|
|
72
94
|
get dbPath(): string;
|
|
95
|
+
/** Which data path was used: `'sqlite'` (Python tool) or `'jsonl'` (fallback). */
|
|
96
|
+
get sourceKind(): 'sqlite' | 'jsonl';
|
|
73
97
|
close(): void;
|
|
74
98
|
/** Most recent prompts, newest first. */
|
|
75
99
|
recent(opts?: ListOptions): HistoryEntry[];
|
|
76
100
|
/**
|
|
77
101
|
* Group history into sessions, ordered by last activity (newest first).
|
|
78
102
|
* Sessions without a `session_id` are skipped.
|
|
103
|
+
*
|
|
104
|
+
* Implementation note: this used to use a correlated scalar subquery
|
|
105
|
+
* to pick `first_prompt`, which ran in O(sessions × rows) — ~19s on a
|
|
106
|
+
* 35K-row DB. Switched to `ROW_NUMBER() OVER (PARTITION BY session_id
|
|
107
|
+
* ORDER BY timestamp_ms)` so first-prompt picking is a single pass
|
|
108
|
+
* over the table (~300ms on the same DB). Plus the index ensure step
|
|
109
|
+
* in `openAiHist` keeps it fast even when the DB was written by the
|
|
110
|
+
* older Python CLI that didn't create `idx_history_session`.
|
|
79
111
|
*/
|
|
80
112
|
listSessions(opts?: ListOptions): SessionSummary[];
|
|
81
113
|
/** All prompts in a session, ordered oldest → newest. */
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAkB,EAAE,KAAK,QAAQ,EAAoB,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -12,9 +12,10 @@
|
|
|
12
12
|
* of data); revisit if anyone hits millions.
|
|
13
13
|
*/
|
|
14
14
|
import initSqlJs from 'sql.js';
|
|
15
|
-
import {
|
|
15
|
+
import { readFile, stat } from 'node:fs/promises';
|
|
16
16
|
import { homedir } from 'node:os';
|
|
17
17
|
import { join } from 'node:path';
|
|
18
|
+
import { scanLocalSources, LOCAL_SOURCE_PATHS } from './jsonl-sources.js';
|
|
18
19
|
/** Resolve the SQLite path the Python CLI writes to. */
|
|
19
20
|
export function defaultDbPath() {
|
|
20
21
|
const fromEnv = process.env.AI_HIST_DB;
|
|
@@ -31,25 +32,77 @@ function getSqlJs() {
|
|
|
31
32
|
}
|
|
32
33
|
/**
|
|
33
34
|
* Open an `AiHist` reader. Async because sql.js initializes its WASM
|
|
34
|
-
* runtime lazily
|
|
35
|
-
*
|
|
36
|
-
*
|
|
35
|
+
* runtime lazily and the DB file is read asynchronously so the host
|
|
36
|
+
* process's event loop isn't blocked.
|
|
37
|
+
*
|
|
38
|
+
* Each call snapshots the data; to pick up later writes, call `reload()`
|
|
39
|
+
* (or open a fresh instance).
|
|
37
40
|
*/
|
|
38
41
|
export async function openAiHist(opts = {}) {
|
|
39
42
|
const dbPath = opts.dbPath ?? defaultDbPath();
|
|
40
|
-
|
|
41
|
-
|
|
43
|
+
const fallback = opts.fallback ?? 'jsonl';
|
|
44
|
+
const SQL = await getSqlJs();
|
|
45
|
+
// Fast path: SQLite written by `ai-hist sync`.
|
|
46
|
+
if (await pathExists(dbPath)) {
|
|
47
|
+
const fileBuffer = await readFile(dbPath);
|
|
48
|
+
const db = new SQL.Database(fileBuffer);
|
|
49
|
+
// The Python CLI's schema doesn't create `idx_history_session` or
|
|
50
|
+
// `idx_history_timestamp`. Without them, listSessions degrades to
|
|
51
|
+
// an O(sessions × rows) full table scan and freezes the WASM
|
|
52
|
+
// single-threaded JS engine for tens of seconds on real-sized DBs.
|
|
53
|
+
// The DB is opened read-only over a buffer, but sql.js still lets
|
|
54
|
+
// us run `CREATE INDEX` against the in-memory copy — the index
|
|
55
|
+
// lives only for this session's lifetime and is rebuilt each
|
|
56
|
+
// `openAiHist` call. Fast (~30ms on 35K rows).
|
|
57
|
+
db.run('CREATE INDEX IF NOT EXISTS idx_history_session ON history(session_id)');
|
|
58
|
+
db.run('CREATE INDEX IF NOT EXISTS idx_history_timestamp ON history(timestamp_ms DESC)');
|
|
59
|
+
return new AiHist(db, { kind: 'sqlite', path: dbPath });
|
|
60
|
+
}
|
|
61
|
+
if (fallback === 'error') {
|
|
62
|
+
throw new Error(`ai-hist database not found at ${dbPath}. Run \`ai-hist sync\` first ` +
|
|
63
|
+
`(see https://github.com/AgentWorkforce/ai-hist).`);
|
|
64
|
+
}
|
|
65
|
+
// Fallback: scan local source files (Claude/Codex/Cursor) directly.
|
|
66
|
+
// No Python dependency; uses the same parsers documented in the Python
|
|
67
|
+
// CLI's source. Yields control to the event loop between sources so a
|
|
68
|
+
// large local history doesn't freeze the host.
|
|
69
|
+
const db = new SQL.Database();
|
|
70
|
+
db.run(`CREATE TABLE history (
|
|
71
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
72
|
+
source TEXT NOT NULL,
|
|
73
|
+
session_id TEXT,
|
|
74
|
+
project TEXT,
|
|
75
|
+
prompt TEXT NOT NULL,
|
|
76
|
+
timestamp_ms INTEGER NOT NULL,
|
|
77
|
+
UNIQUE(source, timestamp_ms, prompt)
|
|
78
|
+
)`);
|
|
79
|
+
db.run('CREATE INDEX idx_history_timestamp ON history (timestamp_ms DESC)');
|
|
80
|
+
db.run('CREATE INDEX idx_history_session ON history (session_id)');
|
|
81
|
+
// scanLocalSources is async with yields between sources so the event
|
|
82
|
+
// loop stays responsive while we scan many MB of JSONL.
|
|
83
|
+
const rows = await scanLocalSources();
|
|
84
|
+
const insert = db.prepare('INSERT OR IGNORE INTO history (source, session_id, project, prompt, timestamp_ms) VALUES (?, ?, ?, ?, ?)');
|
|
85
|
+
try {
|
|
86
|
+
db.exec('BEGIN');
|
|
87
|
+
for (const row of rows) {
|
|
88
|
+
insert.run([row.source, row.sessionId, row.project, row.prompt, row.timestampMs]);
|
|
89
|
+
}
|
|
90
|
+
db.exec('COMMIT');
|
|
91
|
+
}
|
|
92
|
+
finally {
|
|
93
|
+
insert.free();
|
|
94
|
+
}
|
|
95
|
+
const scannedPaths = `${LOCAL_SOURCE_PATHS.claude}, ${LOCAL_SOURCE_PATHS.codex}, ${LOCAL_SOURCE_PATHS.cursorRoot}`;
|
|
96
|
+
return new AiHist(db, { kind: 'jsonl', path: scannedPaths });
|
|
97
|
+
}
|
|
98
|
+
async function pathExists(p) {
|
|
42
99
|
try {
|
|
43
|
-
|
|
100
|
+
await stat(p);
|
|
101
|
+
return true;
|
|
44
102
|
}
|
|
45
103
|
catch {
|
|
46
|
-
|
|
47
|
-
`(see https://github.com/khaliqgant/ai-hist).`);
|
|
104
|
+
return false;
|
|
48
105
|
}
|
|
49
|
-
const SQL = await getSqlJs();
|
|
50
|
-
const fileBuffer = readFileSync(dbPath);
|
|
51
|
-
const db = new SQL.Database(fileBuffer);
|
|
52
|
-
return new AiHist(db, dbPath);
|
|
53
106
|
}
|
|
54
107
|
function rowToEntry(row) {
|
|
55
108
|
return {
|
|
@@ -97,15 +150,23 @@ function runQuery(db, sql, params) {
|
|
|
97
150
|
}
|
|
98
151
|
export class AiHist {
|
|
99
152
|
db;
|
|
100
|
-
|
|
153
|
+
_source;
|
|
101
154
|
closed = false;
|
|
102
|
-
/** @internal — use `openAiHist(
|
|
103
|
-
constructor(db,
|
|
155
|
+
/** @internal — use `openAiHist(...)` to construct. */
|
|
156
|
+
constructor(db, source) {
|
|
104
157
|
this.db = db;
|
|
105
|
-
this.
|
|
158
|
+
this._source = source;
|
|
106
159
|
}
|
|
160
|
+
/**
|
|
161
|
+
* Path the data came from. SQLite mode: the .db path. JSONL fallback
|
|
162
|
+
* mode: a comma-separated list of the scanned source paths.
|
|
163
|
+
*/
|
|
107
164
|
get dbPath() {
|
|
108
|
-
return this.
|
|
165
|
+
return this._source.path;
|
|
166
|
+
}
|
|
167
|
+
/** Which data path was used: `'sqlite'` (Python tool) or `'jsonl'` (fallback). */
|
|
168
|
+
get sourceKind() {
|
|
169
|
+
return this._source.kind;
|
|
109
170
|
}
|
|
110
171
|
close() {
|
|
111
172
|
if (this.closed)
|
|
@@ -126,6 +187,14 @@ export class AiHist {
|
|
|
126
187
|
/**
|
|
127
188
|
* Group history into sessions, ordered by last activity (newest first).
|
|
128
189
|
* Sessions without a `session_id` are skipped.
|
|
190
|
+
*
|
|
191
|
+
* Implementation note: this used to use a correlated scalar subquery
|
|
192
|
+
* to pick `first_prompt`, which ran in O(sessions × rows) — ~19s on a
|
|
193
|
+
* 35K-row DB. Switched to `ROW_NUMBER() OVER (PARTITION BY session_id
|
|
194
|
+
* ORDER BY timestamp_ms)` so first-prompt picking is a single pass
|
|
195
|
+
* over the table (~300ms on the same DB). Plus the index ensure step
|
|
196
|
+
* in `openAiHist` keeps it fast even when the DB was written by the
|
|
197
|
+
* older Python CLI that didn't create `idx_history_session`.
|
|
129
198
|
*/
|
|
130
199
|
listSessions(opts = {}) {
|
|
131
200
|
const limit = opts.limit ?? 50;
|
|
@@ -134,28 +203,33 @@ export class AiHist {
|
|
|
134
203
|
SELECT id, source, session_id, project, prompt, timestamp_ms
|
|
135
204
|
FROM history
|
|
136
205
|
WHERE session_id IS NOT NULL AND session_id != ''${sql}
|
|
206
|
+
),
|
|
207
|
+
ranked AS (
|
|
208
|
+
SELECT
|
|
209
|
+
session_id,
|
|
210
|
+
source,
|
|
211
|
+
project,
|
|
212
|
+
prompt,
|
|
213
|
+
timestamp_ms,
|
|
214
|
+
ROW_NUMBER() OVER (
|
|
215
|
+
PARTITION BY session_id, source, project
|
|
216
|
+
ORDER BY timestamp_ms ASC, id ASC
|
|
217
|
+
) AS rn_first,
|
|
218
|
+
COUNT(*) OVER (PARTITION BY session_id, source, project) AS prompt_count,
|
|
219
|
+
MIN(timestamp_ms) OVER (PARTITION BY session_id, source, project) AS first_activity_ms,
|
|
220
|
+
MAX(timestamp_ms) OVER (PARTITION BY session_id, source, project) AS last_activity_ms
|
|
221
|
+
FROM filtered
|
|
137
222
|
)
|
|
138
223
|
SELECT
|
|
139
224
|
session_id,
|
|
140
225
|
source,
|
|
141
226
|
project,
|
|
142
|
-
|
|
227
|
+
prompt AS first_prompt,
|
|
143
228
|
first_activity_ms,
|
|
144
229
|
last_activity_ms,
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
FROM (
|
|
149
|
-
SELECT
|
|
150
|
-
session_id,
|
|
151
|
-
source,
|
|
152
|
-
project,
|
|
153
|
-
COUNT(*) AS prompt_count,
|
|
154
|
-
MIN(timestamp_ms) AS first_activity_ms,
|
|
155
|
-
MAX(timestamp_ms) AS last_activity_ms
|
|
156
|
-
FROM filtered
|
|
157
|
-
GROUP BY session_id, source, project
|
|
158
|
-
) AS grouped
|
|
230
|
+
prompt_count
|
|
231
|
+
FROM ranked
|
|
232
|
+
WHERE rn_first = 1
|
|
159
233
|
ORDER BY last_activity_ms DESC
|
|
160
234
|
LIMIT ?`, [...params, limit]);
|
|
161
235
|
return rows.map((row) => ({
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,SAA8C,MAAM,QAAQ,CAAC;AACpE,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Direct JSONL parsers for Claude / Codex / Cursor history files.
|
|
3
|
+
*
|
|
4
|
+
* Used as a fallback when the SQLite database the Python `ai-hist sync`
|
|
5
|
+
* tool maintains isn't present — lets `npm install ai-hist` work
|
|
6
|
+
* standalone for users who have any of these CLIs locally.
|
|
7
|
+
*
|
|
8
|
+
* Ports the parser logic from the canonical Python `ai-hist` script
|
|
9
|
+
* (see https://github.com/AgentWorkforce/ai-hist/blob/main/ai-hist).
|
|
10
|
+
* Format drift in those upstream CLIs is the main risk; the Python
|
|
11
|
+
* source is the canonical reference.
|
|
12
|
+
*/
|
|
13
|
+
export type Source = 'claude' | 'codex' | 'cursor';
|
|
14
|
+
export interface RawRow {
|
|
15
|
+
source: Source;
|
|
16
|
+
sessionId: string | null;
|
|
17
|
+
project: string | null;
|
|
18
|
+
prompt: string;
|
|
19
|
+
timestampMs: number;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Scan all available local source files. Async + yields between sources
|
|
23
|
+
* so a host event loop (e.g. Electron's main process) stays responsive.
|
|
24
|
+
* Silently skips sources whose paths don't exist — that's the common
|
|
25
|
+
* case for users who only have one CLI.
|
|
26
|
+
*/
|
|
27
|
+
export declare function scanLocalSources(): Promise<RawRow[]>;
|
|
28
|
+
export declare const LOCAL_SOURCE_PATHS: {
|
|
29
|
+
claude: string;
|
|
30
|
+
codex: string;
|
|
31
|
+
cursorRoot: string;
|
|
32
|
+
};
|
|
33
|
+
//# sourceMappingURL=jsonl-sources.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jsonl-sources.d.ts","sourceRoot":"","sources":["../src/jsonl-sources.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAUH,MAAM,MAAM,MAAM,GAAG,QAAQ,GAAG,OAAO,GAAG,QAAQ,CAAC;AAEnD,MAAM,WAAW,MAAM;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;CACrB;AA2LD;;;;;GAKG;AACH,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAO1D;AAED,eAAO,MAAM,kBAAkB;;;;CAI9B,CAAC"}
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Direct JSONL parsers for Claude / Codex / Cursor history files.
|
|
3
|
+
*
|
|
4
|
+
* Used as a fallback when the SQLite database the Python `ai-hist sync`
|
|
5
|
+
* tool maintains isn't present — lets `npm install ai-hist` work
|
|
6
|
+
* standalone for users who have any of these CLIs locally.
|
|
7
|
+
*
|
|
8
|
+
* Ports the parser logic from the canonical Python `ai-hist` script
|
|
9
|
+
* (see https://github.com/AgentWorkforce/ai-hist/blob/main/ai-hist).
|
|
10
|
+
* Format drift in those upstream CLIs is the main risk; the Python
|
|
11
|
+
* source is the canonical reference.
|
|
12
|
+
*/
|
|
13
|
+
import { readFile, readdir, stat } from 'node:fs/promises';
|
|
14
|
+
import { homedir } from 'node:os';
|
|
15
|
+
import { join } from 'node:path';
|
|
16
|
+
function yieldToEventLoop() {
|
|
17
|
+
return new Promise((resolve) => setImmediate(resolve));
|
|
18
|
+
}
|
|
19
|
+
const CLAUDE_HISTORY = join(homedir(), '.claude', 'history.jsonl');
|
|
20
|
+
const CODEX_HISTORY = join(homedir(), '.codex', 'history.jsonl');
|
|
21
|
+
const CURSOR_ROOT = join(homedir(), '.cursor', 'projects');
|
|
22
|
+
async function safeStat(path) {
|
|
23
|
+
try {
|
|
24
|
+
const s = await stat(path);
|
|
25
|
+
return { size: s.size, mtimeMs: s.mtimeMs };
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
async function readLines(path) {
|
|
32
|
+
try {
|
|
33
|
+
const content = await readFile(path, 'utf8');
|
|
34
|
+
return content.split('\n');
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return [];
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function readJsonRecord(line) {
|
|
41
|
+
try {
|
|
42
|
+
const parsed = JSON.parse(line);
|
|
43
|
+
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
44
|
+
return parsed;
|
|
45
|
+
}
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function asString(value) {
|
|
53
|
+
return typeof value === 'string' && value.length > 0 ? value : null;
|
|
54
|
+
}
|
|
55
|
+
function asNumber(value) {
|
|
56
|
+
return typeof value === 'number' && Number.isFinite(value) ? value : null;
|
|
57
|
+
}
|
|
58
|
+
function parseClaudeLine(line) {
|
|
59
|
+
if (!line.trim())
|
|
60
|
+
return null;
|
|
61
|
+
const obj = readJsonRecord(line);
|
|
62
|
+
if (!obj)
|
|
63
|
+
return null;
|
|
64
|
+
const display = typeof obj.display === 'string' ? obj.display.trim() : '';
|
|
65
|
+
if (!display)
|
|
66
|
+
return null;
|
|
67
|
+
return {
|
|
68
|
+
source: 'claude',
|
|
69
|
+
sessionId: asString(obj.sessionId),
|
|
70
|
+
project: asString(obj.project),
|
|
71
|
+
prompt: display,
|
|
72
|
+
timestampMs: asNumber(obj.timestamp) ?? 0,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
function parseCodexLine(line) {
|
|
76
|
+
if (!line.trim())
|
|
77
|
+
return null;
|
|
78
|
+
const obj = readJsonRecord(line);
|
|
79
|
+
if (!obj)
|
|
80
|
+
return null;
|
|
81
|
+
const text = typeof obj.text === 'string' ? obj.text.trim() : '';
|
|
82
|
+
if (!text)
|
|
83
|
+
return null;
|
|
84
|
+
const ts = asNumber(obj.ts) ?? 0;
|
|
85
|
+
return {
|
|
86
|
+
source: 'codex',
|
|
87
|
+
// Python uses obj.session_id (snake_case).
|
|
88
|
+
sessionId: asString(obj.session_id ?? obj.sessionId),
|
|
89
|
+
project: null,
|
|
90
|
+
prompt: text,
|
|
91
|
+
// Python stores Codex's seconds-since-epoch as ms.
|
|
92
|
+
timestampMs: Math.trunc(ts * 1000),
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Cursor lines carry `{role, message: {content: [{type, text}, ...]}}` and
|
|
97
|
+
* NO per-line timestamp. The Python tool falls back to the file mtime;
|
|
98
|
+
* we do the same. User prompts are wrapped in `<user_query>...</user_query>`.
|
|
99
|
+
*/
|
|
100
|
+
function parseCursorLine(line) {
|
|
101
|
+
if (!line.trim())
|
|
102
|
+
return null;
|
|
103
|
+
const obj = readJsonRecord(line);
|
|
104
|
+
if (!obj)
|
|
105
|
+
return null;
|
|
106
|
+
if (obj.role !== 'user')
|
|
107
|
+
return null;
|
|
108
|
+
const message = obj.message;
|
|
109
|
+
if (!message || typeof message !== 'object')
|
|
110
|
+
return null;
|
|
111
|
+
const content = message.content;
|
|
112
|
+
let text = '';
|
|
113
|
+
if (typeof content === 'string') {
|
|
114
|
+
text = content;
|
|
115
|
+
}
|
|
116
|
+
else if (Array.isArray(content)) {
|
|
117
|
+
for (const c of content) {
|
|
118
|
+
if (c && typeof c === 'object' && c.type === 'text') {
|
|
119
|
+
const t = c.text;
|
|
120
|
+
if (typeof t === 'string') {
|
|
121
|
+
text = t;
|
|
122
|
+
break;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
text = text.trim();
|
|
128
|
+
if (!text)
|
|
129
|
+
return null;
|
|
130
|
+
if (text.startsWith('<user_query>') && text.endsWith('</user_query>')) {
|
|
131
|
+
text = text.slice('<user_query>'.length, -'</user_query>'.length).trim();
|
|
132
|
+
}
|
|
133
|
+
return text || null;
|
|
134
|
+
}
|
|
135
|
+
// `~/.cursor/projects/<encoded-path>/...` — encoded path is the absolute
|
|
136
|
+
// project path with `/` replaced by `-`. Python's `_decode_cursor_project`
|
|
137
|
+
// just rejoins on `-` → `/`. We do the same; it's lossy for any real `-` in
|
|
138
|
+
// a path segment, but matches the Python tool's behavior for parity.
|
|
139
|
+
function decodeCursorProject(encoded) {
|
|
140
|
+
return `/${encoded.replace(/-/g, '/')}`;
|
|
141
|
+
}
|
|
142
|
+
async function readDirSafe(path) {
|
|
143
|
+
try {
|
|
144
|
+
return await readdir(path);
|
|
145
|
+
}
|
|
146
|
+
catch {
|
|
147
|
+
return [];
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
async function scanClaude() {
|
|
151
|
+
const lines = await readLines(CLAUDE_HISTORY);
|
|
152
|
+
const rows = [];
|
|
153
|
+
for (const line of lines) {
|
|
154
|
+
const row = parseClaudeLine(line);
|
|
155
|
+
if (row)
|
|
156
|
+
rows.push(row);
|
|
157
|
+
}
|
|
158
|
+
return rows;
|
|
159
|
+
}
|
|
160
|
+
async function scanCodex() {
|
|
161
|
+
const lines = await readLines(CODEX_HISTORY);
|
|
162
|
+
const rows = [];
|
|
163
|
+
for (const line of lines) {
|
|
164
|
+
const row = parseCodexLine(line);
|
|
165
|
+
if (row)
|
|
166
|
+
rows.push(row);
|
|
167
|
+
}
|
|
168
|
+
return rows;
|
|
169
|
+
}
|
|
170
|
+
async function scanCursor() {
|
|
171
|
+
const root = await safeStat(CURSOR_ROOT);
|
|
172
|
+
if (!root)
|
|
173
|
+
return [];
|
|
174
|
+
const rows = [];
|
|
175
|
+
const projectDirs = await readDirSafe(CURSOR_ROOT);
|
|
176
|
+
for (const projectDirName of projectDirs) {
|
|
177
|
+
const projectDir = join(CURSOR_ROOT, projectDirName);
|
|
178
|
+
if (!(await safeStat(projectDir)))
|
|
179
|
+
continue;
|
|
180
|
+
const tsRoot = join(projectDir, 'agent-transcripts');
|
|
181
|
+
if (!(await safeStat(tsRoot)))
|
|
182
|
+
continue;
|
|
183
|
+
const projectPath = decodeCursorProject(projectDirName);
|
|
184
|
+
const sessionDirs = await readDirSafe(tsRoot);
|
|
185
|
+
for (const sessionDirName of sessionDirs) {
|
|
186
|
+
const sessionDir = join(tsRoot, sessionDirName);
|
|
187
|
+
if (!(await safeStat(sessionDir)))
|
|
188
|
+
continue;
|
|
189
|
+
const sessionId = sessionDirName;
|
|
190
|
+
const jsonl = join(sessionDir, `${sessionId}.jsonl`);
|
|
191
|
+
const fileStat = await safeStat(jsonl);
|
|
192
|
+
if (!fileStat)
|
|
193
|
+
continue;
|
|
194
|
+
const tsMs = Math.trunc(fileStat.mtimeMs);
|
|
195
|
+
for (const line of await readLines(jsonl)) {
|
|
196
|
+
const text = parseCursorLine(line);
|
|
197
|
+
if (!text)
|
|
198
|
+
continue;
|
|
199
|
+
rows.push({
|
|
200
|
+
source: 'cursor',
|
|
201
|
+
sessionId,
|
|
202
|
+
project: projectPath,
|
|
203
|
+
prompt: text,
|
|
204
|
+
timestampMs: tsMs,
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
// Yield between project dirs so very large cursor histories don't
|
|
209
|
+
// hold the event loop for seconds at a time.
|
|
210
|
+
await yieldToEventLoop();
|
|
211
|
+
}
|
|
212
|
+
return rows;
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Scan all available local source files. Async + yields between sources
|
|
216
|
+
* so a host event loop (e.g. Electron's main process) stays responsive.
|
|
217
|
+
* Silently skips sources whose paths don't exist — that's the common
|
|
218
|
+
* case for users who only have one CLI.
|
|
219
|
+
*/
|
|
220
|
+
export async function scanLocalSources() {
|
|
221
|
+
const claudeRows = await scanClaude();
|
|
222
|
+
await yieldToEventLoop();
|
|
223
|
+
const codexRows = await scanCodex();
|
|
224
|
+
await yieldToEventLoop();
|
|
225
|
+
const cursorRows = await scanCursor();
|
|
226
|
+
return [...claudeRows, ...codexRows, ...cursorRows];
|
|
227
|
+
}
|
|
228
|
+
export const LOCAL_SOURCE_PATHS = {
|
|
229
|
+
claude: CLAUDE_HISTORY,
|
|
230
|
+
codex: CODEX_HISTORY,
|
|
231
|
+
cursorRoot: CURSOR_ROOT,
|
|
232
|
+
};
|
|
233
|
+
//# sourceMappingURL=jsonl-sources.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jsonl-sources.js","sourceRoot":"","sources":["../src/jsonl-sources.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai-hist",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "TypeScript SDK for
|
|
3
|
+
"version": "0.2.3",
|
|
4
|
+
"description": "TypeScript SDK for browsing your Claude / Codex / Cursor history — reads the ai-hist SQLite DB when present, falls back to scanning the local JSONL files directly when not.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"private": false,
|
|
7
7
|
"type": "module",
|