@aligndottech/cli 0.34.1 → 0.35.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.
- package/README.md +2 -0
- package/dist/commands/context.d.ts.map +1 -1
- package/dist/commands/context.js +5 -0
- package/dist/commands/context.js.map +1 -1
- package/dist/commands/decisions/index.d.ts.map +1 -1
- package/dist/commands/decisions/index.js +9 -1
- package/dist/commands/decisions/index.js.map +1 -1
- package/dist/commands/import/sessions.d.ts +16 -0
- package/dist/commands/import/sessions.d.ts.map +1 -0
- package/dist/commands/import/sessions.js +122 -0
- package/dist/commands/import/sessions.js.map +1 -0
- package/dist/commands/import.d.ts.map +1 -1
- package/dist/commands/import.js +2 -0
- package/dist/commands/import.js.map +1 -1
- package/dist/commands/push.d.ts +25 -0
- package/dist/commands/push.d.ts.map +1 -0
- package/dist/commands/push.js +98 -0
- package/dist/commands/push.js.map +1 -0
- package/dist/commands/ratify.d.ts +23 -0
- package/dist/commands/ratify.d.ts.map +1 -0
- package/dist/commands/ratify.js +51 -0
- package/dist/commands/ratify.js.map +1 -0
- package/dist/commands/why.d.ts.map +1 -1
- package/dist/commands/why.js +4 -1
- package/dist/commands/why.js.map +1 -1
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/answer-render.d.ts +2 -0
- package/dist/lib/answer-render.d.ts.map +1 -0
- package/dist/lib/answer-render.js +24 -0
- package/dist/lib/answer-render.js.map +1 -0
- package/dist/lib/capture-sources.d.ts +4 -0
- package/dist/lib/capture-sources.d.ts.map +1 -1
- package/dist/lib/capture-sources.js +1 -0
- package/dist/lib/capture-sources.js.map +1 -1
- package/dist/lib/decider-kind.d.ts +37 -0
- package/dist/lib/decider-kind.d.ts.map +1 -0
- package/dist/lib/decider-kind.js +41 -0
- package/dist/lib/decider-kind.js.map +1 -0
- package/dist/lib/decisions-context.d.ts +7 -0
- package/dist/lib/decisions-context.d.ts.map +1 -1
- package/dist/lib/decisions-context.js +29 -6
- package/dist/lib/decisions-context.js.map +1 -1
- package/dist/lib/gateway-client.d.ts +32 -0
- package/dist/lib/gateway-client.d.ts.map +1 -1
- package/dist/lib/gateway-client.js +10 -0
- package/dist/lib/gateway-client.js.map +1 -1
- package/dist/lib/git.d.ts +18 -0
- package/dist/lib/git.d.ts.map +1 -1
- package/dist/lib/git.js +31 -0
- package/dist/lib/git.js.map +1 -1
- package/dist/lib/import-defaults.d.ts +1 -0
- package/dist/lib/import-defaults.d.ts.map +1 -1
- package/dist/lib/import-defaults.js +1 -0
- package/dist/lib/import-defaults.js.map +1 -1
- package/dist/lib/local-db.d.ts +56 -1
- package/dist/lib/local-db.d.ts.map +1 -1
- package/dist/lib/local-db.js +104 -7
- package/dist/lib/local-db.js.map +1 -1
- package/dist/lib/local-gateway-client.d.ts +50 -0
- package/dist/lib/local-gateway-client.d.ts.map +1 -1
- package/dist/lib/local-gateway-client.js +90 -4
- package/dist/lib/local-gateway-client.js.map +1 -1
- package/dist/lib/local-llm.d.ts.map +1 -1
- package/dist/lib/local-llm.js +2 -1
- package/dist/lib/local-llm.js.map +1 -1
- package/dist/lib/personal-import.d.ts +26 -0
- package/dist/lib/personal-import.d.ts.map +1 -1
- package/dist/lib/personal-import.js +43 -0
- package/dist/lib/personal-import.js.map +1 -1
- package/dist/lib/sessions/adapters/claude-code.d.ts +4 -0
- package/dist/lib/sessions/adapters/claude-code.d.ts.map +1 -0
- package/dist/lib/sessions/adapters/claude-code.js +93 -0
- package/dist/lib/sessions/adapters/claude-code.js.map +1 -0
- package/dist/lib/sessions/adapters/codex.d.ts +6 -0
- package/dist/lib/sessions/adapters/codex.d.ts.map +1 -0
- package/dist/lib/sessions/adapters/codex.js +91 -0
- package/dist/lib/sessions/adapters/codex.js.map +1 -0
- package/dist/lib/sessions/adapters/cursor.d.ts +17 -0
- package/dist/lib/sessions/adapters/cursor.d.ts.map +1 -0
- package/dist/lib/sessions/adapters/cursor.js +31 -0
- package/dist/lib/sessions/adapters/cursor.js.map +1 -0
- package/dist/lib/sessions/adapters/gemini-cli.d.ts +15 -0
- package/dist/lib/sessions/adapters/gemini-cli.d.ts.map +1 -0
- package/dist/lib/sessions/adapters/gemini-cli.js +29 -0
- package/dist/lib/sessions/adapters/gemini-cli.js.map +1 -0
- package/dist/lib/sessions/adapters/opencode.d.ts +4 -0
- package/dist/lib/sessions/adapters/opencode.d.ts.map +1 -0
- package/dist/lib/sessions/adapters/opencode.js +100 -0
- package/dist/lib/sessions/adapters/opencode.js.map +1 -0
- package/dist/lib/sessions/adapters/pi.d.ts +4 -0
- package/dist/lib/sessions/adapters/pi.d.ts.map +1 -0
- package/dist/lib/sessions/adapters/pi.js +102 -0
- package/dist/lib/sessions/adapters/pi.js.map +1 -0
- package/dist/lib/sessions/extract-structured.d.ts +21 -0
- package/dist/lib/sessions/extract-structured.d.ts.map +1 -0
- package/dist/lib/sessions/extract-structured.js +72 -0
- package/dist/lib/sessions/extract-structured.js.map +1 -0
- package/dist/lib/sessions/fs-scan.d.ts +10 -0
- package/dist/lib/sessions/fs-scan.d.ts.map +1 -0
- package/dist/lib/sessions/fs-scan.js +33 -0
- package/dist/lib/sessions/fs-scan.js.map +1 -0
- package/dist/lib/sessions/registry.d.ts +11 -0
- package/dist/lib/sessions/registry.d.ts.map +1 -0
- package/dist/lib/sessions/registry.js +22 -0
- package/dist/lib/sessions/registry.js.map +1 -0
- package/dist/lib/sessions/source-url.d.ts +10 -0
- package/dist/lib/sessions/source-url.d.ts.map +1 -0
- package/dist/lib/sessions/source-url.js +20 -0
- package/dist/lib/sessions/source-url.js.map +1 -0
- package/dist/lib/sessions/types.d.ts +62 -0
- package/dist/lib/sessions/types.d.ts.map +1 -0
- package/dist/lib/sessions/types.js +19 -0
- package/dist/lib/sessions/types.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-code.js","sourceRoot":"","sources":["../../../../src/lib/sessions/adapters/claude-code.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { SessionAdapter } from '../types.js';
|
|
2
|
+
/** No per-project directory at all - codex partitions purely by date (YYYY/MM/DD), so
|
|
3
|
+
* every adapter here has to scan and match by content regardless of tree shape. */
|
|
4
|
+
export declare function locateSessionFilesUnder(sessionsRoot: string, cwd: string): string[];
|
|
5
|
+
export declare const codexAdapter: SessionAdapter;
|
|
6
|
+
//# sourceMappingURL=codex.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codex.d.ts","sourceRoot":"","sources":["../../../../src/lib/sessions/adapters/codex.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAoB,cAAc,EAAe,MAAM,aAAa,CAAC;AAoCjF;oFACoF;AACpF,wBAAgB,uBAAuB,CAAC,YAAY,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAGnF;AAyDD,eAAO,MAAM,YAAY,EAAE,cAQ1B,CAAC"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { findFilesRecursive, sortByMtimeDesc } from '../fs-scan.js';
|
|
5
|
+
function parseLine(raw) {
|
|
6
|
+
try {
|
|
7
|
+
return JSON.parse(raw);
|
|
8
|
+
}
|
|
9
|
+
catch {
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
function peekMeta(filePath) {
|
|
14
|
+
const firstLine = readFileSync(filePath, 'utf8').split('\n').find(l => l.trim());
|
|
15
|
+
if (!firstLine)
|
|
16
|
+
return { sessionId: null, cwd: null };
|
|
17
|
+
const line = parseLine(firstLine);
|
|
18
|
+
if (line?.type !== 'session_meta')
|
|
19
|
+
return { sessionId: null, cwd: null };
|
|
20
|
+
return { sessionId: line.payload?.session_id ?? null, cwd: line.payload?.cwd ?? null };
|
|
21
|
+
}
|
|
22
|
+
/** No per-project directory at all - codex partitions purely by date (YYYY/MM/DD), so
|
|
23
|
+
* every adapter here has to scan and match by content regardless of tree shape. */
|
|
24
|
+
export function locateSessionFilesUnder(sessionsRoot, cwd) {
|
|
25
|
+
const files = findFilesRecursive(sessionsRoot, /\.jsonl$/);
|
|
26
|
+
return sortByMtimeDesc(files.filter(f => peekMeta(f).cwd === cwd));
|
|
27
|
+
}
|
|
28
|
+
function joinText(content) {
|
|
29
|
+
if (!content)
|
|
30
|
+
return '';
|
|
31
|
+
return content.filter(b => typeof b.text === 'string').map(b => b.text).join('\n');
|
|
32
|
+
}
|
|
33
|
+
function parseCodexSession(filePath) {
|
|
34
|
+
const text = readFileSync(filePath, 'utf8');
|
|
35
|
+
const turns = [];
|
|
36
|
+
let sessionId = null;
|
|
37
|
+
let cwd = null;
|
|
38
|
+
let pendingResults = {};
|
|
39
|
+
for (const rawLine of text.split('\n')) {
|
|
40
|
+
if (!rawLine.trim())
|
|
41
|
+
continue;
|
|
42
|
+
const line = parseLine(rawLine);
|
|
43
|
+
if (!line)
|
|
44
|
+
continue;
|
|
45
|
+
if (line.type === 'session_meta') {
|
|
46
|
+
sessionId ??= line.payload?.session_id ?? null;
|
|
47
|
+
cwd ??= line.payload?.cwd ?? null;
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
if (line.type !== 'response_item')
|
|
51
|
+
continue;
|
|
52
|
+
const payload = line.payload;
|
|
53
|
+
if (payload?.type === 'custom_tool_call' && typeof payload.call_id === 'string' && typeof payload.name === 'string') {
|
|
54
|
+
// Attaches to the NEXT turn built (matches the pattern in pi.ts): codex records a
|
|
55
|
+
// tool call as its own response_item, not nested inside a message.
|
|
56
|
+
turns.push({
|
|
57
|
+
role: 'assistant', text: '', toolCalls: [{ id: payload.call_id, name: payload.name, arguments: payload.input }],
|
|
58
|
+
toolResults: {}, timestamp: line.timestamp ?? null,
|
|
59
|
+
});
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
if (payload?.type === 'custom_tool_call_output' && typeof payload.call_id === 'string') {
|
|
63
|
+
pendingResults[payload.call_id] = joinText(payload.output);
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
if (payload?.type !== 'message')
|
|
67
|
+
continue;
|
|
68
|
+
const role = payload.role;
|
|
69
|
+
// developer = Codex's own system-prompt boilerplate, not a real turn.
|
|
70
|
+
if (role !== 'user' && role !== 'assistant')
|
|
71
|
+
continue;
|
|
72
|
+
turns.push({ role, text: joinText(payload.content), toolCalls: [], toolResults: pendingResults, timestamp: line.timestamp ?? null });
|
|
73
|
+
pendingResults = {};
|
|
74
|
+
}
|
|
75
|
+
if (Object.keys(pendingResults).length > 0 && turns.length > 0) {
|
|
76
|
+
Object.assign(turns[turns.length - 1].toolResults, pendingResults);
|
|
77
|
+
}
|
|
78
|
+
if (!sessionId || turns.length === 0)
|
|
79
|
+
return null;
|
|
80
|
+
return { agent: 'codex', sessionId, cwd, turns };
|
|
81
|
+
}
|
|
82
|
+
export const codexAdapter = {
|
|
83
|
+
agent: 'codex',
|
|
84
|
+
fixtureVerified: true,
|
|
85
|
+
locateSessionFiles(cwd) {
|
|
86
|
+
const root = join(homedir(), '.codex', 'sessions');
|
|
87
|
+
return locateSessionFilesUnder(root, cwd);
|
|
88
|
+
},
|
|
89
|
+
parseSession: parseCodexSession,
|
|
90
|
+
};
|
|
91
|
+
//# sourceMappingURL=codex.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codex.js","sourceRoot":"","sources":["../../../../src/lib/sessions/adapters/codex.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type SessionAdapter } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* No fixture-verified content parser - see fixtures/sessions/README.md. Cursor's real
|
|
4
|
+
* product is a GUI IDE with no headless CLI reachable from this environment; its own
|
|
5
|
+
* installer script was refused by this session's own pipe-to-bash guard (the right call
|
|
6
|
+
* for an unreviewed remote script, independent of credentials).
|
|
7
|
+
*
|
|
8
|
+
* The documented JSONL path (`~/.cursor/projects/<project>/agent-transcripts/<session>/
|
|
9
|
+
* <session>.jsonl`) names its project directory literally, unlike gemini-cli's hash - but
|
|
10
|
+
* this session never observed a real one being created, so the exact naming (slug? raw
|
|
11
|
+
* path?) is unverified too. Finds every transcript file under the root rather than guessing
|
|
12
|
+
* at that naming. Cursor's second storage mode (SQLite at `~/.cursor/chats/<hash>/...`) is
|
|
13
|
+
* not scanned here at all - two unverified formats is not a reason to guess at both.
|
|
14
|
+
*/
|
|
15
|
+
export declare function locateSessionFilesUnder(cursorProjectsRoot: string): string[];
|
|
16
|
+
export declare const cursorAdapter: SessionAdapter;
|
|
17
|
+
//# sourceMappingURL=cursor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cursor.d.ts","sourceRoot":"","sources":["../../../../src/lib/sessions/adapters/cursor.ts"],"names":[],"mappings":"AAGA,OAAO,EAAyB,KAAK,cAAc,EAAgC,MAAM,aAAa,CAAC;AAEvG;;;;;;;;;;;;GAYG;AACH,wBAAgB,uBAAuB,CAAC,kBAAkB,EAAE,MAAM,GAAG,MAAM,EAAE,CAE5E;AAED,eAAO,MAAM,aAAa,EAAE,cAS3B,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { homedir } from 'node:os';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { findFilesRecursive } from '../fs-scan.js';
|
|
4
|
+
import { SessionFormatUnverifiedError } from '../types.js';
|
|
5
|
+
/**
|
|
6
|
+
* No fixture-verified content parser - see fixtures/sessions/README.md. Cursor's real
|
|
7
|
+
* product is a GUI IDE with no headless CLI reachable from this environment; its own
|
|
8
|
+
* installer script was refused by this session's own pipe-to-bash guard (the right call
|
|
9
|
+
* for an unreviewed remote script, independent of credentials).
|
|
10
|
+
*
|
|
11
|
+
* The documented JSONL path (`~/.cursor/projects/<project>/agent-transcripts/<session>/
|
|
12
|
+
* <session>.jsonl`) names its project directory literally, unlike gemini-cli's hash - but
|
|
13
|
+
* this session never observed a real one being created, so the exact naming (slug? raw
|
|
14
|
+
* path?) is unverified too. Finds every transcript file under the root rather than guessing
|
|
15
|
+
* at that naming. Cursor's second storage mode (SQLite at `~/.cursor/chats/<hash>/...`) is
|
|
16
|
+
* not scanned here at all - two unverified formats is not a reason to guess at both.
|
|
17
|
+
*/
|
|
18
|
+
export function locateSessionFilesUnder(cursorProjectsRoot) {
|
|
19
|
+
return findFilesRecursive(cursorProjectsRoot, /[/\\]agent-transcripts[/\\].*\.jsonl$/);
|
|
20
|
+
}
|
|
21
|
+
export const cursorAdapter = {
|
|
22
|
+
agent: 'cursor',
|
|
23
|
+
fixtureVerified: false,
|
|
24
|
+
locateSessionFiles(_cwd) {
|
|
25
|
+
return locateSessionFilesUnder(join(homedir(), '.cursor', 'projects'));
|
|
26
|
+
},
|
|
27
|
+
parseSession(_filePath) {
|
|
28
|
+
throw new SessionFormatUnverifiedError('cursor');
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
//# sourceMappingURL=cursor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cursor.js","sourceRoot":"","sources":["../../../../src/lib/sessions/adapters/cursor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,EAA8C,4BAA4B,EAAE,MAAM,aAAa,CAAC;AAEvG;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,uBAAuB,CAAC,kBAA0B;IAChE,OAAO,kBAAkB,CAAC,kBAAkB,EAAE,uCAAuC,CAAC,CAAC;AACzF,CAAC;AAED,MAAM,CAAC,MAAM,aAAa,GAAmB;IAC3C,KAAK,EAAE,QAAQ;IACf,eAAe,EAAE,KAAK;IACtB,kBAAkB,CAAC,IAAY;QAC7B,OAAO,uBAAuB,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC,CAAC;IACzE,CAAC;IACD,YAAY,CAAC,SAAiB;QAC5B,MAAM,IAAI,4BAA4B,CAAC,QAAQ,CAAC,CAAC;IACnD,CAAC;CACF,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type SessionAdapter } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* No fixture-verified content parser - see fixtures/sessions/README.md (blocked on a
|
|
4
|
+
* missing GEMINI_API_KEY / GOOGLE_GENAI_USE_VERTEXAI / GOOGLE_GENAI_USE_GCA, and this
|
|
5
|
+
* session cannot complete gemini-cli's interactive OAuth login).
|
|
6
|
+
*
|
|
7
|
+
* The documented storage path (`~/.gemini/tmp/<project_hash>/chats/`, JSON) hashes the
|
|
8
|
+
* project path into an opaque directory name this session never observed being computed -
|
|
9
|
+
* so unlike the four verified adapters, this cannot filter by cwd. It finds every chat file
|
|
10
|
+
* under the root instead of guessing at a hash, which is the honest version of "found
|
|
11
|
+
* gemini-cli data, cannot yet say which project it belongs to".
|
|
12
|
+
*/
|
|
13
|
+
export declare function locateSessionFilesUnder(geminiTmpRoot: string): string[];
|
|
14
|
+
export declare const geminiCliAdapter: SessionAdapter;
|
|
15
|
+
//# sourceMappingURL=gemini-cli.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gemini-cli.d.ts","sourceRoot":"","sources":["../../../../src/lib/sessions/adapters/gemini-cli.ts"],"names":[],"mappings":"AAGA,OAAO,EAAyB,KAAK,cAAc,EAAgC,MAAM,aAAa,CAAC;AAEvG;;;;;;;;;;GAUG;AACH,wBAAgB,uBAAuB,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,EAAE,CAEvE;AAED,eAAO,MAAM,gBAAgB,EAAE,cAS9B,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { homedir } from 'node:os';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { findFilesRecursive } from '../fs-scan.js';
|
|
4
|
+
import { SessionFormatUnverifiedError } from '../types.js';
|
|
5
|
+
/**
|
|
6
|
+
* No fixture-verified content parser - see fixtures/sessions/README.md (blocked on a
|
|
7
|
+
* missing GEMINI_API_KEY / GOOGLE_GENAI_USE_VERTEXAI / GOOGLE_GENAI_USE_GCA, and this
|
|
8
|
+
* session cannot complete gemini-cli's interactive OAuth login).
|
|
9
|
+
*
|
|
10
|
+
* The documented storage path (`~/.gemini/tmp/<project_hash>/chats/`, JSON) hashes the
|
|
11
|
+
* project path into an opaque directory name this session never observed being computed -
|
|
12
|
+
* so unlike the four verified adapters, this cannot filter by cwd. It finds every chat file
|
|
13
|
+
* under the root instead of guessing at a hash, which is the honest version of "found
|
|
14
|
+
* gemini-cli data, cannot yet say which project it belongs to".
|
|
15
|
+
*/
|
|
16
|
+
export function locateSessionFilesUnder(geminiTmpRoot) {
|
|
17
|
+
return findFilesRecursive(geminiTmpRoot, /[/\\]chats[/\\].*\.json$/);
|
|
18
|
+
}
|
|
19
|
+
export const geminiCliAdapter = {
|
|
20
|
+
agent: 'gemini-cli',
|
|
21
|
+
fixtureVerified: false,
|
|
22
|
+
locateSessionFiles(_cwd) {
|
|
23
|
+
return locateSessionFilesUnder(join(homedir(), '.gemini', 'tmp'));
|
|
24
|
+
},
|
|
25
|
+
parseSession(_filePath) {
|
|
26
|
+
throw new SessionFormatUnverifiedError('gemini-cli');
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
//# sourceMappingURL=gemini-cli.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gemini-cli.js","sourceRoot":"","sources":["../../../../src/lib/sessions/adapters/gemini-cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,EAA8C,4BAA4B,EAAE,MAAM,aAAa,CAAC;AAEvG;;;;;;;;;;GAUG;AACH,MAAM,UAAU,uBAAuB,CAAC,aAAqB;IAC3D,OAAO,kBAAkB,CAAC,aAAa,EAAE,0BAA0B,CAAC,CAAC;AACvE,CAAC;AAED,MAAM,CAAC,MAAM,gBAAgB,GAAmB;IAC9C,KAAK,EAAE,YAAY;IACnB,eAAe,EAAE,KAAK;IACtB,kBAAkB,CAAC,IAAY;QAC7B,OAAO,uBAAuB,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC,CAAC;IACpE,CAAC;IACD,YAAY,CAAC,SAAiB;QAC5B,MAAM,IAAI,4BAA4B,CAAC,YAAY,CAAC,CAAC;IACvD,CAAC;CACF,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"opencode.d.ts","sourceRoot":"","sources":["../../../../src/lib/sessions/adapters/opencode.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAoB,cAAc,EAAgC,MAAM,aAAa,CAAC;AAWlG,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAW7E;AAkFD,eAAO,MAAM,eAAe,EAAE,cAQ7B,CAAC"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { DatabaseSync } from 'node:sqlite';
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
/** opencode stores every session in one shared SQLite database, so a "session file" here is
|
|
6
|
+
* a composite `<dbPath>#<sessionId>` identifier rather than a real file - the split point
|
|
7
|
+
* every function in this module agrees on. */
|
|
8
|
+
function splitCompositeId(id) {
|
|
9
|
+
const hash = id.indexOf('#');
|
|
10
|
+
if (hash === -1)
|
|
11
|
+
return null;
|
|
12
|
+
return { dbPath: id.slice(0, hash), sessionId: id.slice(hash + 1) };
|
|
13
|
+
}
|
|
14
|
+
export function locateSessionFilesUnder(dbPath, cwd) {
|
|
15
|
+
if (!existsSync(dbPath))
|
|
16
|
+
return [];
|
|
17
|
+
const db = new DatabaseSync(dbPath, { readOnly: true });
|
|
18
|
+
try {
|
|
19
|
+
const rows = db.prepare('SELECT id FROM session WHERE directory = ? ORDER BY time_created DESC').all(cwd);
|
|
20
|
+
return rows.map(r => `${dbPath}#${r.id}`);
|
|
21
|
+
}
|
|
22
|
+
finally {
|
|
23
|
+
db.close();
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function parsePart(raw) {
|
|
27
|
+
try {
|
|
28
|
+
return JSON.parse(raw);
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function parseOpencodeSession(compositeId) {
|
|
35
|
+
const split = splitCompositeId(compositeId);
|
|
36
|
+
if (!split)
|
|
37
|
+
return null;
|
|
38
|
+
const { dbPath, sessionId } = split;
|
|
39
|
+
if (!existsSync(dbPath))
|
|
40
|
+
return null;
|
|
41
|
+
const db = new DatabaseSync(dbPath, { readOnly: true });
|
|
42
|
+
try {
|
|
43
|
+
const sessionRow = db.prepare('SELECT directory FROM session WHERE id = ?').get(sessionId);
|
|
44
|
+
if (!sessionRow)
|
|
45
|
+
return null;
|
|
46
|
+
const messages = db.prepare('SELECT id, data FROM message WHERE session_id = ? ORDER BY time_created').all(sessionId);
|
|
47
|
+
const turns = [];
|
|
48
|
+
for (const msg of messages) {
|
|
49
|
+
let msgData;
|
|
50
|
+
try {
|
|
51
|
+
msgData = JSON.parse(msg.data);
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
const role = msgData.role;
|
|
57
|
+
if (role !== 'user' && role !== 'assistant')
|
|
58
|
+
continue;
|
|
59
|
+
const parts = db.prepare('SELECT data FROM part WHERE message_id = ? ORDER BY time_created').all(msg.id);
|
|
60
|
+
let text = '';
|
|
61
|
+
const toolCalls = [];
|
|
62
|
+
const toolResults = {};
|
|
63
|
+
for (const partRow of parts) {
|
|
64
|
+
const part = parsePart(partRow.data);
|
|
65
|
+
if (!part)
|
|
66
|
+
continue;
|
|
67
|
+
if (part.type === 'text' && typeof part.text === 'string') {
|
|
68
|
+
text += (text ? '\n' : '') + part.text;
|
|
69
|
+
}
|
|
70
|
+
else if (part.type === 'tool' && typeof part.callID === 'string' && typeof part.tool === 'string') {
|
|
71
|
+
// Unlike the JSONL agents, opencode records a call and its result in ONE row
|
|
72
|
+
// (state.input / state.output) - no separate pending-result step needed.
|
|
73
|
+
toolCalls.push({ id: part.callID, name: part.tool, arguments: part.state?.input });
|
|
74
|
+
const output = part.state?.output;
|
|
75
|
+
if (output !== undefined) {
|
|
76
|
+
toolResults[part.callID] = typeof output === 'string' ? output : JSON.stringify(output);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
// step-start / step-finish / reasoning: internal bookkeeping, not a turn's content.
|
|
80
|
+
}
|
|
81
|
+
turns.push({ role, text, toolCalls, toolResults, timestamp: null });
|
|
82
|
+
}
|
|
83
|
+
if (turns.length === 0)
|
|
84
|
+
return null;
|
|
85
|
+
return { agent: 'opencode', sessionId, cwd: sessionRow.directory, turns };
|
|
86
|
+
}
|
|
87
|
+
finally {
|
|
88
|
+
db.close();
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
export const opencodeAdapter = {
|
|
92
|
+
agent: 'opencode',
|
|
93
|
+
fixtureVerified: true,
|
|
94
|
+
locateSessionFiles(cwd) {
|
|
95
|
+
const dbPath = join(homedir(), '.local', 'share', 'opencode', 'opencode.db');
|
|
96
|
+
return locateSessionFilesUnder(dbPath, cwd);
|
|
97
|
+
},
|
|
98
|
+
parseSession: parseOpencodeSession,
|
|
99
|
+
};
|
|
100
|
+
//# sourceMappingURL=opencode.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"opencode.js","sourceRoot":"","sources":["../../../../src/lib/sessions/adapters/opencode.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pi.d.ts","sourceRoot":"","sources":["../../../../src/lib/sessions/adapters/pi.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAoB,cAAc,EAAgC,MAAM,aAAa,CAAC;AA+BlG,wBAAgB,uBAAuB,CAAC,YAAY,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAGnF;AAuED,eAAO,MAAM,SAAS,EAAE,cAQvB,CAAC"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { findFilesRecursive, sortByMtimeDesc } from '../fs-scan.js';
|
|
5
|
+
function parseLine(raw) {
|
|
6
|
+
try {
|
|
7
|
+
return JSON.parse(raw);
|
|
8
|
+
}
|
|
9
|
+
catch {
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
/** Only pi's first line (`type: "session"`) carries cwd - unlike Claude Code, later lines
|
|
14
|
+
* do not repeat it, so this only needs to look at the first parseable line. */
|
|
15
|
+
function peekCwd(filePath) {
|
|
16
|
+
const firstLine = readFileSync(filePath, 'utf8').split('\n').find(l => l.trim());
|
|
17
|
+
if (!firstLine)
|
|
18
|
+
return null;
|
|
19
|
+
return parseLine(firstLine)?.cwd ?? null;
|
|
20
|
+
}
|
|
21
|
+
export function locateSessionFilesUnder(sessionsRoot, cwd) {
|
|
22
|
+
const files = findFilesRecursive(sessionsRoot, /\.jsonl$/);
|
|
23
|
+
return sortByMtimeDesc(files.filter(f => peekCwd(f) === cwd));
|
|
24
|
+
}
|
|
25
|
+
function extractText(content) {
|
|
26
|
+
let text = '';
|
|
27
|
+
const toolCalls = [];
|
|
28
|
+
if (typeof content === 'string') {
|
|
29
|
+
text = content;
|
|
30
|
+
}
|
|
31
|
+
else if (Array.isArray(content)) {
|
|
32
|
+
for (const block of content) {
|
|
33
|
+
if (!block || typeof block !== 'object')
|
|
34
|
+
continue;
|
|
35
|
+
const b = block;
|
|
36
|
+
if (b['type'] === 'text' && typeof b['text'] === 'string') {
|
|
37
|
+
text += (text ? '\n' : '') + b['text'];
|
|
38
|
+
}
|
|
39
|
+
else if (b['type'] === 'toolCall' && typeof b['id'] === 'string' && typeof b['name'] === 'string') {
|
|
40
|
+
toolCalls.push({ id: b['id'], name: b['name'], arguments: b['arguments'] });
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return { text, toolCalls };
|
|
45
|
+
}
|
|
46
|
+
/** pi's toolResult text lives in a `content: [{type:'text', text}]` array on its own
|
|
47
|
+
* message, same shape as a user/assistant message's text blocks. */
|
|
48
|
+
function extractResultText(content) {
|
|
49
|
+
return extractText(content).text;
|
|
50
|
+
}
|
|
51
|
+
function parsePiSession(filePath) {
|
|
52
|
+
const text = readFileSync(filePath, 'utf8');
|
|
53
|
+
const turns = [];
|
|
54
|
+
let sessionId = null;
|
|
55
|
+
let cwd = null;
|
|
56
|
+
// toolResult lines are their own top-level pi message (role: 'toolResult'), not nested
|
|
57
|
+
// inside the next user/assistant turn the way Claude Code embeds a tool_result block -
|
|
58
|
+
// so a pending result is carried forward and merged into whichever real turn comes next.
|
|
59
|
+
let pendingResults = {};
|
|
60
|
+
for (const rawLine of text.split('\n')) {
|
|
61
|
+
if (!rawLine.trim())
|
|
62
|
+
continue;
|
|
63
|
+
const line = parseLine(rawLine);
|
|
64
|
+
if (!line)
|
|
65
|
+
continue;
|
|
66
|
+
if (line.type === 'session') {
|
|
67
|
+
sessionId ??= line.id ?? null;
|
|
68
|
+
cwd ??= line.cwd ?? null;
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
if (line.type !== 'message')
|
|
72
|
+
continue;
|
|
73
|
+
const role = line.message?.role;
|
|
74
|
+
if (role === 'toolResult' && typeof line.message?.toolCallId === 'string') {
|
|
75
|
+
pendingResults[line.message.toolCallId] = extractResultText(line.message.content);
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
if (role !== 'user' && role !== 'assistant')
|
|
79
|
+
continue;
|
|
80
|
+
const { text: turnText, toolCalls } = extractText(line.message?.content);
|
|
81
|
+
turns.push({ role, text: turnText, toolCalls, toolResults: pendingResults, timestamp: line.timestamp ?? null });
|
|
82
|
+
pendingResults = {};
|
|
83
|
+
}
|
|
84
|
+
// A pi transcript ending immediately after a toolResult (nothing to attach it to yet) -
|
|
85
|
+
// fold it onto the last turn rather than dropping it.
|
|
86
|
+
if (Object.keys(pendingResults).length > 0 && turns.length > 0) {
|
|
87
|
+
Object.assign(turns[turns.length - 1].toolResults, pendingResults);
|
|
88
|
+
}
|
|
89
|
+
if (!sessionId || turns.length === 0)
|
|
90
|
+
return null;
|
|
91
|
+
return { agent: 'pi', sessionId, cwd, turns };
|
|
92
|
+
}
|
|
93
|
+
export const piAdapter = {
|
|
94
|
+
agent: 'pi',
|
|
95
|
+
fixtureVerified: true,
|
|
96
|
+
locateSessionFiles(cwd) {
|
|
97
|
+
const root = join(homedir(), '.pi', 'agent', 'sessions');
|
|
98
|
+
return locateSessionFilesUnder(root, cwd);
|
|
99
|
+
},
|
|
100
|
+
parseSession: parsePiSession,
|
|
101
|
+
};
|
|
102
|
+
//# sourceMappingURL=pi.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pi.js","sourceRoot":"","sources":["../../../../src/lib/sessions/adapters/pi.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { AgentName, CanonicalSession } from './types.js';
|
|
2
|
+
export interface SessionDecisionCandidate {
|
|
3
|
+
agent: AgentName;
|
|
4
|
+
sessionId: string;
|
|
5
|
+
/** The structured tool call's own id - unique within the session, and the value used to
|
|
6
|
+
* build the source_url's messageId segment. */
|
|
7
|
+
messageId: string;
|
|
8
|
+
question: string;
|
|
9
|
+
header: string | null;
|
|
10
|
+
options: Array<{
|
|
11
|
+
label: string;
|
|
12
|
+
description?: string;
|
|
13
|
+
}>;
|
|
14
|
+
/** The answer, verbatim - either a listed option's label or free text (Claude Code's
|
|
15
|
+
* "Other"). Never null here: a candidate with no resolvable answer is not returned at
|
|
16
|
+
* all (see extractAskUserQuestion). */
|
|
17
|
+
chosenLabel: string;
|
|
18
|
+
timestamp: string | null;
|
|
19
|
+
}
|
|
20
|
+
export declare function extractStructuredDecisions(session: CanonicalSession): SessionDecisionCandidate[];
|
|
21
|
+
//# sourceMappingURL=extract-structured.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extract-structured.d.ts","sourceRoot":"","sources":["../../../src/lib/sessions/extract-structured.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,gBAAgB,EAAe,MAAM,YAAY,CAAC;AAE3E,MAAM,WAAW,wBAAwB;IACvC,KAAK,EAAE,SAAS,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB;oDACgD;IAChD,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,OAAO,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACxD;;4CAEwC;IACxC,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAUD,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,gBAAgB,GAAG,wBAAwB,EAAE,CAEhG"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
const PRODUCERS = {
|
|
2
|
+
'claude-code': extractAskUserQuestion,
|
|
3
|
+
};
|
|
4
|
+
export function extractStructuredDecisions(session) {
|
|
5
|
+
return PRODUCERS[session.agent]?.(session) ?? [];
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Claude Code's AskUserQuestion tool_result is free text, not structured JSON - the tool
|
|
9
|
+
* itself renders it that way (`"<question>"="<answer>". <trailing sentence>`). Anchoring on
|
|
10
|
+
* the question's own text (echoed verbatim by the tool) rather than splitting on commas
|
|
11
|
+
* keeps this correct even when an answer itself contains a comma or a quote.
|
|
12
|
+
*/
|
|
13
|
+
function parseAnsweredText(resultText, question) {
|
|
14
|
+
const marker = `"${question}"=`;
|
|
15
|
+
const idx = resultText.indexOf(marker);
|
|
16
|
+
if (idx === -1)
|
|
17
|
+
return null;
|
|
18
|
+
const after = resultText.slice(idx + marker.length);
|
|
19
|
+
if (!after.startsWith('"'))
|
|
20
|
+
return null;
|
|
21
|
+
const body = after.slice(1);
|
|
22
|
+
// The two real trailing phrasings this tool emits, whichever fires depends on whether one
|
|
23
|
+
// question was asked or several. A quote not followed by either is inside the answer.
|
|
24
|
+
const suffixMatch = body.search(/"\.\s*(Read the answers|You can now continue)/);
|
|
25
|
+
if (suffixMatch !== -1)
|
|
26
|
+
return body.slice(0, suffixMatch);
|
|
27
|
+
// Last resort (e.g. this is the final question in a multi-question call, with nothing
|
|
28
|
+
// trailing but the closing quote): take up to the last quote in the string.
|
|
29
|
+
const lastQuote = body.lastIndexOf('"');
|
|
30
|
+
return lastQuote === -1 ? null : body.slice(0, lastQuote);
|
|
31
|
+
}
|
|
32
|
+
function extractAskUserQuestion(session) {
|
|
33
|
+
const candidates = [];
|
|
34
|
+
for (const turn of session.turns) {
|
|
35
|
+
for (const call of turn.toolCalls) {
|
|
36
|
+
if (call.name !== 'AskUserQuestion')
|
|
37
|
+
continue;
|
|
38
|
+
const input = call.arguments;
|
|
39
|
+
const q = input?.questions?.[0];
|
|
40
|
+
if (!q)
|
|
41
|
+
continue;
|
|
42
|
+
const resultText = findToolResult(session.turns, call.id);
|
|
43
|
+
if (resultText === null)
|
|
44
|
+
continue;
|
|
45
|
+
const chosenLabel = parseAnsweredText(resultText, q.question);
|
|
46
|
+
// A rejection ("the tool use was rejected...") or any other unrecognised result shape
|
|
47
|
+
// has no `"<question>"=` marker, so this is also where a decline is filtered out -
|
|
48
|
+
// nothing was decided, so nothing is a candidate.
|
|
49
|
+
if (chosenLabel === null)
|
|
50
|
+
continue;
|
|
51
|
+
candidates.push({
|
|
52
|
+
agent: session.agent,
|
|
53
|
+
sessionId: session.sessionId,
|
|
54
|
+
messageId: call.id,
|
|
55
|
+
question: q.question,
|
|
56
|
+
header: q.header ?? null,
|
|
57
|
+
options: q.options.map(o => ({ label: o.label, ...(o.description !== undefined ? { description: o.description } : {}) })),
|
|
58
|
+
chosenLabel,
|
|
59
|
+
timestamp: turn.timestamp,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return candidates;
|
|
64
|
+
}
|
|
65
|
+
function findToolResult(turns, toolCallId) {
|
|
66
|
+
for (const turn of turns) {
|
|
67
|
+
if (toolCallId in turn.toolResults)
|
|
68
|
+
return turn.toolResults[toolCallId];
|
|
69
|
+
}
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=extract-structured.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extract-structured.js","sourceRoot":"","sources":["../../../src/lib/sessions/extract-structured.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Every file under `root` (recursively) whose full path matches `pattern`. A missing or
|
|
3
|
+
* unreadable root is "this agent has never run here" - the normal case, not an error - so
|
|
4
|
+
* it returns [] rather than throwing. Every adapter's locateSessionFiles builds on this.
|
|
5
|
+
*/
|
|
6
|
+
export declare function findFilesRecursive(root: string, pattern: RegExp): string[];
|
|
7
|
+
/** Newest-first by mtime - agent-agnostic, so it works whether or not a format's own
|
|
8
|
+
* embedded timestamp was parseable. */
|
|
9
|
+
export declare function sortByMtimeDesc(files: string[]): string[];
|
|
10
|
+
//# sourceMappingURL=fs-scan.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fs-scan.d.ts","sourceRoot":"","sources":["../../../src/lib/sessions/fs-scan.ts"],"names":[],"mappings":"AAGA;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAiB1E;AAED;wCACwC;AACxC,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAEzD"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { readdirSync, statSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
/**
|
|
4
|
+
* Every file under `root` (recursively) whose full path matches `pattern`. A missing or
|
|
5
|
+
* unreadable root is "this agent has never run here" - the normal case, not an error - so
|
|
6
|
+
* it returns [] rather than throwing. Every adapter's locateSessionFiles builds on this.
|
|
7
|
+
*/
|
|
8
|
+
export function findFilesRecursive(root, pattern) {
|
|
9
|
+
let entries;
|
|
10
|
+
try {
|
|
11
|
+
entries = readdirSync(root, { withFileTypes: true });
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return [];
|
|
15
|
+
}
|
|
16
|
+
const found = [];
|
|
17
|
+
for (const entry of entries) {
|
|
18
|
+
const full = join(root, entry.name);
|
|
19
|
+
if (entry.isDirectory()) {
|
|
20
|
+
found.push(...findFilesRecursive(full, pattern));
|
|
21
|
+
}
|
|
22
|
+
else if (pattern.test(full)) {
|
|
23
|
+
found.push(full);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return found;
|
|
27
|
+
}
|
|
28
|
+
/** Newest-first by mtime - agent-agnostic, so it works whether or not a format's own
|
|
29
|
+
* embedded timestamp was parseable. */
|
|
30
|
+
export function sortByMtimeDesc(files) {
|
|
31
|
+
return [...files].sort((a, b) => statSync(b).mtimeMs - statSync(a).mtimeMs);
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=fs-scan.js.map
|