ccompactor 0.1.1 → 0.1.2
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/adapters/claude.js +109 -122
- package/dist/adapters/claude.js.map +1 -1
- package/dist/adapters/codex.d.ts +1 -1
- package/dist/adapters/codex.js +7 -4
- package/dist/adapters/codex.js.map +1 -1
- package/dist/adapters/pi.d.ts +1 -1
- package/dist/adapters/pi.js +8 -4
- package/dist/adapters/pi.js.map +1 -1
- package/dist/adapters/types.d.ts +24 -2
- package/dist/adapters/types.js +33 -5
- package/dist/adapters/types.js.map +1 -1
- package/dist/artifact/render.js +2 -2
- package/dist/artifact/transcript.d.ts +31 -0
- package/dist/artifact/transcript.js +82 -0
- package/dist/artifact/transcript.js.map +1 -0
- package/dist/cli.js +21 -6
- package/dist/cli.js.map +1 -1
- package/dist/discover/index.d.ts +1 -0
- package/dist/discover/index.js.map +1 -1
- package/dist/extract.d.ts +4 -0
- package/dist/extract.js +27 -0
- package/dist/extract.js.map +1 -1
- package/dist/tui/App.d.ts +8 -0
- package/dist/tui/App.js +513 -0
- package/dist/tui/App.js.map +1 -0
- package/dist/tui/details.d.ts +37 -0
- package/dist/tui/details.js +75 -0
- package/dist/tui/details.js.map +1 -0
- package/dist/tui/index.d.ts +3 -2
- package/dist/tui/index.js +10 -129
- package/dist/tui/index.js.map +1 -1
- package/dist/tui/mouse.d.ts +37 -0
- package/dist/tui/mouse.js +41 -0
- package/dist/tui/mouse.js.map +1 -0
- package/package.json +5 -4
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { readSession } from '../discover/index.js';
|
|
2
|
+
import { buildLedgers } from '../ledgers/index.js';
|
|
3
|
+
/** Load metadata and the newest `previewCount` turns. */
|
|
4
|
+
export async function loadDetails(ref, previewCount = 30) {
|
|
5
|
+
// `light` throughout: nothing here needs the original records, and keeping
|
|
6
|
+
// them for a handful of concurrent 200 MB transcripts is what exhausted the
|
|
7
|
+
// heap.
|
|
8
|
+
const ir = await readSession(ref, { light: true });
|
|
9
|
+
return describe(ir, previewCount);
|
|
10
|
+
}
|
|
11
|
+
export function describe(ir, previewCount) {
|
|
12
|
+
const ledgers = buildLedgers(ir);
|
|
13
|
+
const text = ir.messages.reduce((n, m) => n + (m.text?.length ?? 0), 0);
|
|
14
|
+
const first = ir.messages[0]?.timestamp;
|
|
15
|
+
const last = ir.messages.at(-1)?.timestamp;
|
|
16
|
+
return {
|
|
17
|
+
messages: ir.messages.length,
|
|
18
|
+
userTurns: ledgers.counts.userTurns,
|
|
19
|
+
toolCalls: ledgers.counts.toolCalls,
|
|
20
|
+
tokens: Math.ceil(text / 4),
|
|
21
|
+
...(str(ir.metadata['cwd']) ? { cwd: str(ir.metadata['cwd']) } : {}),
|
|
22
|
+
...(str(ir.metadata['branch']) ? { branch: str(ir.metadata['branch']) } : {}),
|
|
23
|
+
...(str(ir.metadata['model']) ? { model: str(ir.metadata['model']) } : {}),
|
|
24
|
+
...(str(ir.metadata['version']) ? { version: str(ir.metadata['version']) } : {}),
|
|
25
|
+
...(first ? { first } : {}),
|
|
26
|
+
...(last ? { last } : {}),
|
|
27
|
+
compactBoundaries: ir.compactBoundaries.length,
|
|
28
|
+
preview: preview(ir, previewCount),
|
|
29
|
+
diagnostics: ir.diagnostics.length,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/** The newest turns, oldest first. Tool calls become one line; results are dropped. */
|
|
33
|
+
function preview(ir, count) {
|
|
34
|
+
const out = [];
|
|
35
|
+
for (const message of ir.messages) {
|
|
36
|
+
const text = (message.text ?? '').trim();
|
|
37
|
+
if (message.isHumanTurn && text) {
|
|
38
|
+
out.push({ role: 'user', text: clip(text, 900), evt: message.eventIndex });
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
if (message.role === 'assistant') {
|
|
42
|
+
if (text) {
|
|
43
|
+
out.push({ role: 'agent', text: clip(text, 700), evt: message.eventIndex });
|
|
44
|
+
}
|
|
45
|
+
else if (message.toolName) {
|
|
46
|
+
out.push({
|
|
47
|
+
role: 'tool',
|
|
48
|
+
text: `${message.toolName} ${clip(stringify(message.toolInput), 90)}`,
|
|
49
|
+
evt: message.eventIndex,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return out.slice(-count);
|
|
55
|
+
}
|
|
56
|
+
function str(value) {
|
|
57
|
+
return typeof value === 'string' && value.length > 0 ? value : undefined;
|
|
58
|
+
}
|
|
59
|
+
function stringify(value) {
|
|
60
|
+
if (value === undefined)
|
|
61
|
+
return '';
|
|
62
|
+
if (typeof value === 'string')
|
|
63
|
+
return value;
|
|
64
|
+
try {
|
|
65
|
+
return JSON.stringify(value);
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
return '';
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function clip(text, max) {
|
|
72
|
+
const single = text.replace(/\s+/g, ' ').trim();
|
|
73
|
+
return single.length <= max ? single : `${single.slice(0, max - 1)}…`;
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=details.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"details.js","sourceRoot":"","sources":["../../src/tui/details.ts"],"names":[],"mappings":"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"}
|
package/dist/tui/index.d.ts
CHANGED
package/dist/tui/index.js
CHANGED
|
@@ -1,140 +1,21 @@
|
|
|
1
|
-
import { jsx as _jsx
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
/**
|
|
3
3
|
* `ccompactor --tui`
|
|
4
4
|
*
|
|
5
|
-
* The
|
|
6
|
-
*
|
|
7
|
-
* command — in that order, because that is the order anyone actually works in.
|
|
8
|
-
*
|
|
9
|
-
* It hands the terminal over to the launched agent rather than embedding it:
|
|
10
|
-
* a TUI that keeps the screen while another agent runs in a pty is a TUI that
|
|
11
|
-
* breaks both.
|
|
5
|
+
* The interactive mode, which is the whole product for anyone who does not
|
|
6
|
+
* already know the session id they want.
|
|
12
7
|
*/
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import { listSessions, fuzzyScore } from '../discover/index.js';
|
|
16
|
-
import { extractSession } from '../extract.js';
|
|
17
|
-
import { plan, installed } from '../handoff/index.js';
|
|
18
|
-
import { THEME, hazard, sizeLabel } from './theme.js';
|
|
19
|
-
function App({ outDir, anyProject, onDone }) {
|
|
20
|
-
const { exit } = useApp();
|
|
21
|
-
const { stdout } = useStdout();
|
|
22
|
-
const [rows, setRows] = useState([]);
|
|
23
|
-
const [loading, setLoading] = useState(true);
|
|
24
|
-
const [query, setQuery] = useState('');
|
|
25
|
-
const [searching, setSearching] = useState(false);
|
|
26
|
-
const [cursor, setCursor] = useState(0);
|
|
27
|
-
const [screen, setScreen] = useState('browse');
|
|
28
|
-
const [status, setStatus] = useState('');
|
|
29
|
-
const [artifact, setArtifact] = useState();
|
|
30
|
-
const [targets, setTargets] = useState([]);
|
|
31
|
-
const height = Math.max(6, (stdout?.rows ?? 30) - 8);
|
|
32
|
-
useEffect(() => {
|
|
33
|
-
void (async () => {
|
|
34
|
-
setRows(await listSessions({ anyProject }));
|
|
35
|
-
setTargets(installed());
|
|
36
|
-
setLoading(false);
|
|
37
|
-
})();
|
|
38
|
-
}, [anyProject]);
|
|
39
|
-
const filtered = useMemo(() => {
|
|
40
|
-
if (query.trim().length === 0)
|
|
41
|
-
return rows;
|
|
42
|
-
return rows
|
|
43
|
-
.map((ref) => ({ ref, score: fuzzyScore(query, `${ref.agent} ${ref.id} ${ref.path}`) }))
|
|
44
|
-
.filter((hit) => hit.score > 0)
|
|
45
|
-
.sort((a, b) => b.score - a.score)
|
|
46
|
-
.map((hit) => hit.ref);
|
|
47
|
-
}, [rows, query]);
|
|
48
|
-
const selected = filtered[Math.min(cursor, Math.max(0, filtered.length - 1))];
|
|
49
|
-
const runExtract = useCallback(async () => {
|
|
50
|
-
if (!selected)
|
|
51
|
-
return;
|
|
52
|
-
setStatus(`extracting ${selected.agent}:${selected.id} …`);
|
|
53
|
-
try {
|
|
54
|
-
const result = await extractSession(`${selected.agent}:${selected.id}`, { outDir, anyProject }, (stage, message) => setStatus(`${stage}: ${message}`));
|
|
55
|
-
setArtifact(result.written[0] ?? `${outDir}/handoff.md`);
|
|
56
|
-
setStatus(`wrote ${result.rendered.tokens} tokens in ${result.elapsedMs} ms (${result.llm}) — h for handoff`);
|
|
57
|
-
setScreen('handoff');
|
|
58
|
-
}
|
|
59
|
-
catch (error) {
|
|
60
|
-
setStatus(`error: ${error.message}`);
|
|
61
|
-
}
|
|
62
|
-
}, [selected, outDir, anyProject]);
|
|
63
|
-
const launch = useCallback(async () => {
|
|
64
|
-
if (!artifact)
|
|
65
|
-
return;
|
|
66
|
-
const target = targets[0];
|
|
67
|
-
if (!target) {
|
|
68
|
-
setStatus('no launchable agent found on PATH');
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
const command = plan(target, artifact, process.cwd());
|
|
72
|
-
// The terminal is handed over rather than shared: the child owns it.
|
|
73
|
-
setStatus(`launching ${command.display}`);
|
|
74
|
-
exit();
|
|
75
|
-
const { spawnSync } = await import('node:child_process');
|
|
76
|
-
const result = spawnSync(command.program, command.argv, { stdio: 'inherit' });
|
|
77
|
-
onDone(result.status ?? 0);
|
|
78
|
-
}, [artifact, targets, exit, onDone]);
|
|
79
|
-
useInput((input, key) => {
|
|
80
|
-
if (key.ctrl && input === 'c') {
|
|
81
|
-
exit();
|
|
82
|
-
onDone(0);
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
if (searching) {
|
|
86
|
-
if (key.return || key.escape) {
|
|
87
|
-
setSearching(false);
|
|
88
|
-
}
|
|
89
|
-
else if (key.backspace || key.delete) {
|
|
90
|
-
setQuery((q) => q.slice(0, -1));
|
|
91
|
-
}
|
|
92
|
-
else if (input && !key.ctrl && !key.meta) {
|
|
93
|
-
setQuery((q) => q + input);
|
|
94
|
-
setCursor(0);
|
|
95
|
-
}
|
|
96
|
-
return;
|
|
97
|
-
}
|
|
98
|
-
if (input === 'q') {
|
|
99
|
-
exit();
|
|
100
|
-
onDone(0);
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
if (input === '/') {
|
|
104
|
-
setSearching(true);
|
|
105
|
-
setQuery('');
|
|
106
|
-
return;
|
|
107
|
-
}
|
|
108
|
-
if (key.escape) {
|
|
109
|
-
setScreen('browse');
|
|
110
|
-
return;
|
|
111
|
-
}
|
|
112
|
-
if (key.upArrow)
|
|
113
|
-
setCursor((c) => Math.max(0, c - 1));
|
|
114
|
-
if (key.downArrow)
|
|
115
|
-
setCursor((c) => Math.min(filtered.length - 1, c + 1));
|
|
116
|
-
if (key.return && selected)
|
|
117
|
-
setScreen('detail');
|
|
118
|
-
if (input === 'e')
|
|
119
|
-
void runExtract();
|
|
120
|
-
if (input === 'h' && artifact)
|
|
121
|
-
setScreen('handoff');
|
|
122
|
-
});
|
|
123
|
-
if (loading)
|
|
124
|
-
return _jsx(Text, { color: THEME.yellow, children: "scanning agent stores \u2026" });
|
|
125
|
-
const visible = filtered.slice(Math.max(0, cursor - height + 3), Math.max(height - 2, cursor + 1));
|
|
126
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { children: hazard(stdout?.columns).map((cell, i) => (_jsx(Text, { color: cell.dim ? THEME.stripeDim : THEME.yellow, children: cell.ch }, i))) }), _jsxs(Box, { children: [_jsx(Text, { bold: true, backgroundColor: THEME.yellow, color: THEME.onYellow, children: ' ccompactor ' }), _jsxs(Text, { color: THEME.muted, children: [' ', filtered.length, " of ", rows.length, " session(s)", anyProject ? ' · all projects' : ''] })] }), searching ? (_jsxs(Box, { children: [_jsx(Text, { bold: true, color: THEME.yellow, children: "search: " }), _jsx(Text, { children: query }), _jsx(Text, { color: THEME.yellowInk, children: "\u258F" })] })) : (_jsxs(Text, { color: THEME.muted, children: ["/ search \u00B7 \u2191\u2193 move \u00B7 enter detail \u00B7", ' ', _jsx(Text, { color: THEME.yellowInk, children: "e" }), " extract \u00B7", ' ', _jsx(Text, { color: THEME.yellowInk, children: "h" }), " handoff \u00B7 q quit"] })), _jsx(Box, { flexDirection: "column", marginTop: 1, children: visible.map((ref, index) => {
|
|
127
|
-
const active = filtered[cursor]?.id === ref.id;
|
|
128
|
-
return (_jsxs(Text, { ...(active
|
|
129
|
-
? { backgroundColor: THEME.yellow, color: THEME.onYellow, bold: true }
|
|
130
|
-
: {}), children: [active ? '❯ ' : ' ', ref.agent.padEnd(7), ref.id.slice(0, 36).padEnd(38), sizeLabel(ref.bytes).padStart(8), ' ', new Date(ref.mtime).toISOString().slice(0, 16).replace('T', ' ')] }, `${ref.path}:${index}`));
|
|
131
|
-
}) }), screen !== 'browse' && selected && (_jsxs(Box, { flexDirection: "column", marginTop: 1, borderStyle: "round", borderColor: THEME.yellow, paddingX: 1, children: [_jsxs(Text, { bold: true, color: THEME.yellowInk, children: [selected.agent, ":", selected.id] }), _jsx(Text, { color: THEME.muted, children: selected.path }), screen === 'detail' && (_jsxs(Text, { children: [sizeLabel(selected.bytes), " \u00B7 modified", ' ', new Date(selected.mtime).toISOString().slice(0, 19).replace('T', ' '), '\n', "Press e to extract."] })), screen === 'handoff' && artifact && (_jsxs(Text, { children: ["artifact: ", artifact, '\n', "targets on PATH: ", targets.join(', ') || 'none', '\n', targets[0] ? plan(targets[0], artifact, process.cwd()).display : '', '\n', "Press h again or enter to launch."] }))] })), status.length > 0 && (_jsx(Box, { marginTop: 1, children: _jsx(Text, { color: THEME.yellowInk, children: status }) }))] }));
|
|
132
|
-
}
|
|
8
|
+
import { render } from 'ink';
|
|
9
|
+
import { App } from './App.js';
|
|
133
10
|
export async function runTui(options = {}) {
|
|
11
|
+
if (!process.stdin.isTTY) {
|
|
12
|
+
process.stderr.write('the TUI needs a terminal: stdin is not a tty. Use `ccompactor list` and `ccompactor extract` instead.\n');
|
|
13
|
+
return 1;
|
|
14
|
+
}
|
|
134
15
|
let code = 0;
|
|
135
16
|
const app = render(_jsx(App, { outDir: options.outDir ?? '.ccompactor', anyProject: options.anyProject ?? true, onDone: (value) => {
|
|
136
17
|
code = value;
|
|
137
|
-
} }));
|
|
18
|
+
} }), { exitOnCtrlC: false });
|
|
138
19
|
await app.waitUntilExit();
|
|
139
20
|
return code;
|
|
140
21
|
}
|
package/dist/tui/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tui/index.tsx"],"names":[],"mappings":";AAAA
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tui/index.tsx"],"names":[],"mappings":";AAAA;;;;;GAKG;AACH,OAAO,EAAE,MAAM,EAAE,MAAM,KAAK,CAAA;AAE5B,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAO9B,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,UAAsB,EAAE;IACnD,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QACzB,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,yGAAyG,CAC1G,CAAA;QACD,OAAO,CAAC,CAAA;IACV,CAAC;IACD,IAAI,IAAI,GAAG,CAAC,CAAA;IACZ,MAAM,GAAG,GAAG,MAAM,CAChB,KAAC,GAAG,IACF,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,aAAa,EACvC,UAAU,EAAE,OAAO,CAAC,UAAU,IAAI,IAAI,EACtC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;YACxB,IAAI,GAAG,KAAK,CAAA;QACd,CAAC,GACD,EACF,EAAE,WAAW,EAAE,KAAK,EAAE,CACvB,CAAA;IACD,MAAM,GAAG,CAAC,aAAa,EAAE,CAAA;IACzB,OAAO,IAAI,CAAA;AACb,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mouse support, by hand.
|
|
3
|
+
*
|
|
4
|
+
* Ink does not surface mouse events: a click arrives on stdin as an escape
|
|
5
|
+
* sequence that its key parser discards. Verified by sending a click to an Ink
|
|
6
|
+
* app and watching `useInput` stay silent. So the sequences are enabled here,
|
|
7
|
+
* parsed here, and delivered through a callback.
|
|
8
|
+
*
|
|
9
|
+
* SGR mode (`?1006`) is what makes this tractable: the coordinates are plain
|
|
10
|
+
* decimal and there is no 223-column limit, unlike the original X10 encoding.
|
|
11
|
+
* A press is `ESC [ < b ; x ; y M`, a release ends in `m`.
|
|
12
|
+
*/
|
|
13
|
+
import type { Writable } from 'node:stream';
|
|
14
|
+
export type MouseKind = 'click' | 'wheel-up' | 'wheel-down' | 'other';
|
|
15
|
+
export interface MouseEvent {
|
|
16
|
+
kind: MouseKind;
|
|
17
|
+
/** 1-based column, as the terminal reports it. */
|
|
18
|
+
x: number;
|
|
19
|
+
/** 1-based row, as the terminal reports it. */
|
|
20
|
+
y: number;
|
|
21
|
+
/** The raw SGR button code, for anything this does not model. */
|
|
22
|
+
button: number;
|
|
23
|
+
}
|
|
24
|
+
/** Turn on click and wheel reporting, and return a function that turns it off. */
|
|
25
|
+
export declare function enableMouse(out: Writable): () => void;
|
|
26
|
+
/**
|
|
27
|
+
* Pull every mouse event out of a chunk of stdin.
|
|
28
|
+
*
|
|
29
|
+
* Returns the events and the chunk with those sequences removed, so a caller can
|
|
30
|
+
* decide whether anything else is worth passing on. Anything that is not a
|
|
31
|
+
* complete sequence is ignored rather than buffered: a partial escape at the end
|
|
32
|
+
* of a chunk is far more likely to be a keystroke than a click.
|
|
33
|
+
*/
|
|
34
|
+
export declare function readMouse(data: string): {
|
|
35
|
+
events: MouseEvent[];
|
|
36
|
+
rest: string;
|
|
37
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
const ENABLE = '\u001b[?1000h\u001b[?1006h';
|
|
2
|
+
const DISABLE = '\u001b[?1000l\u001b[?1006l';
|
|
3
|
+
/** Turn on click and wheel reporting, and return a function that turns it off. */
|
|
4
|
+
export function enableMouse(out) {
|
|
5
|
+
out.write(ENABLE);
|
|
6
|
+
return () => out.write(DISABLE);
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Pull every mouse event out of a chunk of stdin.
|
|
10
|
+
*
|
|
11
|
+
* Returns the events and the chunk with those sequences removed, so a caller can
|
|
12
|
+
* decide whether anything else is worth passing on. Anything that is not a
|
|
13
|
+
* complete sequence is ignored rather than buffered: a partial escape at the end
|
|
14
|
+
* of a chunk is far more likely to be a keystroke than a click.
|
|
15
|
+
*/
|
|
16
|
+
export function readMouse(data) {
|
|
17
|
+
const events = [];
|
|
18
|
+
let rest = data;
|
|
19
|
+
const pattern = /\u001b\[<(\d+);(\d+);(\d+)([Mm])/g;
|
|
20
|
+
rest = rest.replace(pattern, (_match, b, x, y, final) => {
|
|
21
|
+
if (final === 'm')
|
|
22
|
+
return ''; // releases carry nothing we act on
|
|
23
|
+
const button = Number.parseInt(b, 10);
|
|
24
|
+
const kind = (button & 64) !== 0
|
|
25
|
+
? (button & 1) === 0
|
|
26
|
+
? 'wheel-up'
|
|
27
|
+
: 'wheel-down'
|
|
28
|
+
: (button & 3) === 0
|
|
29
|
+
? 'click'
|
|
30
|
+
: 'other';
|
|
31
|
+
events.push({
|
|
32
|
+
kind,
|
|
33
|
+
x: Number.parseInt(x, 10),
|
|
34
|
+
y: Number.parseInt(y, 10),
|
|
35
|
+
button,
|
|
36
|
+
});
|
|
37
|
+
return '';
|
|
38
|
+
});
|
|
39
|
+
return { events, rest };
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=mouse.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mouse.js","sourceRoot":"","sources":["../../src/tui/mouse.ts"],"names":[],"mappings":"AAcA,MAAM,MAAM,GAAG,4BAA4B,CAAA;AAC3C,MAAM,OAAO,GAAG,4BAA4B,CAAA;AAc5C,kFAAkF;AAClF,MAAM,UAAU,WAAW,CAAC,GAAa;IACvC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;IACjB,OAAO,GAAG,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;AACjC,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,SAAS,CAAC,IAAY;IACpC,MAAM,MAAM,GAAiB,EAAE,CAAA;IAC/B,IAAI,IAAI,GAAG,IAAI,CAAA;IACf,MAAM,OAAO,GAAG,mCAAmC,CAAA;IACnD,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,MAAM,EAAE,CAAS,EAAE,CAAS,EAAE,CAAS,EAAE,KAAa,EAAE,EAAE;QACtF,IAAI,KAAK,KAAK,GAAG;YAAE,OAAO,EAAE,CAAA,CAAC,mCAAmC;QAChE,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;QACrC,MAAM,IAAI,GACR,CAAC,MAAM,GAAG,EAAE,CAAC,KAAK,CAAC;YACjB,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC;gBAClB,CAAC,CAAC,UAAU;gBACZ,CAAC,CAAC,YAAY;YAChB,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC;gBAClB,CAAC,CAAC,OAAO;gBACT,CAAC,CAAC,OAAO,CAAA;QACf,MAAM,CAAC,IAAI,CAAC;YACV,IAAI;YACJ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC;YACzB,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC;YACzB,MAAM;SACP,CAAC,CAAA;QACF,OAAO,EAAE,CAAA;IACX,CAAC,CAAC,CAAA;IACF,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAA;AACzB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ccompactor",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Extract any coding-agent session (Claude Code, Codex, Pi) into a compact, verified, provenance-linked handoff that any other agent can continue from.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -27,11 +27,12 @@
|
|
|
27
27
|
],
|
|
28
28
|
"scripts": {
|
|
29
29
|
"build": "tsc -p tsconfig.json",
|
|
30
|
-
"
|
|
30
|
+
"watch": "tsc -p tsconfig.json --watch --preserveWatchOutput",
|
|
31
|
+
"dev": "npm run build --silent && node dist/cli.js",
|
|
31
32
|
"test": "tsc -p tsconfig.test.json && cd dist-test && node --test",
|
|
32
33
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
33
|
-
"
|
|
34
|
-
"
|
|
34
|
+
"prepack": "node scripts/guard-vendor.mjs",
|
|
35
|
+
"prepublishOnly": "node scripts/guard-vendor.mjs && npm run build"
|
|
35
36
|
},
|
|
36
37
|
"dependencies": {
|
|
37
38
|
"commander": "^13.1.0",
|