session-preserver 0.1.1 → 0.1.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/index.js +66 -5
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -239,6 +239,9 @@ function cmdOpen(id) {
|
|
|
239
239
|
}
|
|
240
240
|
const cwd = bin.cwd && fs.existsSync(bin.cwd) ? bin.cwd : process.cwd();
|
|
241
241
|
console.log(`resuming in ${sess.tool}: ${bin.cmd} ${bin.args.join(" ")} (cwd: ${shortHome(cwd)})`);
|
|
242
|
+
// reset terminal for the child TUI (we may be exiting raw/alt-screen state)
|
|
243
|
+
if (process.stdout.isTTY) process.stdout.write("\x1b[?1049l\x1b[0m\x1b[?25h");
|
|
244
|
+
if (process.stdin.isTTY) { try { process.stdin.setRawMode(false); } catch {} }
|
|
242
245
|
const r = spawnSync(bin.cmd, bin.args, { cwd, stdio: "inherit" });
|
|
243
246
|
process.exit(r.status ?? 0);
|
|
244
247
|
}
|
|
@@ -252,12 +255,67 @@ function cmdLs(args) {
|
|
|
252
255
|
if (args.query) { const q = args.query.toLowerCase(); rows = rows.filter((r) => (r.title + " " + r.project).toLowerCase().includes(q)); }
|
|
253
256
|
rows = rows.slice(0, args.n);
|
|
254
257
|
if (!rows.length) { console.log("no sessions found"); return; }
|
|
255
|
-
|
|
258
|
+
if (args.plain || !process.stdout.isTTY || !process.stdin.isTTY) return printTable(rows);
|
|
259
|
+
pickInteractive(rows);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function printTable(rows) {
|
|
263
|
+
const idW = Math.max(...rows.map((r) => r.id.length));
|
|
256
264
|
console.log(`${"ID".padEnd(idW + 1)} ${"TOOL".padEnd(9)} ${"WHEN".padEnd(10)} ${"PROJECT".padEnd(38)} TITLE`);
|
|
257
265
|
for (const r of rows)
|
|
258
266
|
console.log(`${r.id.padEnd(idW + 1)} ${r.tool.padEnd(9)} ${reltime(r.updated).padEnd(10)} ${r.project.slice(-37).padEnd(38)} ${r.title.slice(0, 60)}`);
|
|
259
267
|
}
|
|
260
268
|
|
|
269
|
+
const rowLine = (r) =>
|
|
270
|
+
`${r.tool.padEnd(9)} ${reltime(r.updated).padEnd(10)} ${r.project.slice(-30).padEnd(30)} ${r.title.slice(0, 50)}`;
|
|
271
|
+
|
|
272
|
+
function pickInteractive(rows) {
|
|
273
|
+
let sel = 0, offset = 0;
|
|
274
|
+
const height = () => Math.max(5, (process.stdout.rows || 24) - 3);
|
|
275
|
+
|
|
276
|
+
const draw = () => {
|
|
277
|
+
const out = [`\x1b[H\x1b[J↑/↓ j/k move · PgUp/PgDn page · g/G top/bottom · enter resume · s summary · q quit [${sel + 1}/${rows.length}]\n`];
|
|
278
|
+
const h = height();
|
|
279
|
+
if (sel < offset) offset = sel;
|
|
280
|
+
if (sel >= offset + h) offset = sel - h + 1;
|
|
281
|
+
for (let i = offset; i < Math.min(rows.length, offset + h); i++)
|
|
282
|
+
out.push((i === sel ? "\x1b[7m" : "") + rowLine(rows[i]) + (i === sel ? "\x1b[0m" : ""));
|
|
283
|
+
process.stdout.write(out.join("\n") + "\n");
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
const done = (action) => {
|
|
287
|
+
process.stdin.setRawMode(false);
|
|
288
|
+
process.stdin.pause();
|
|
289
|
+
process.stdin.removeAllListeners("data");
|
|
290
|
+
process.stdout.write("\x1b[?1049l");
|
|
291
|
+
const sess = rows[sel];
|
|
292
|
+
if (action === "quit") return;
|
|
293
|
+
if (action === "open") return cmdOpen(sess.id);
|
|
294
|
+
if (action === "summary") return cmdSummary(sess.id);
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
process.stdin.setRawMode(true);
|
|
298
|
+
process.stdin.resume();
|
|
299
|
+
process.stdin.setEncoding("utf8");
|
|
300
|
+
process.stdin.on("data", (key) => {
|
|
301
|
+
const h = height();
|
|
302
|
+
if (key === "q" || key === "\x03" || key === "\x1b") return done("quit");
|
|
303
|
+
if (key === "\x1b[A" || key === "k") sel = Math.max(0, sel - 1);
|
|
304
|
+
else if (key === "\x1b[B" || key === "j") sel = Math.min(rows.length - 1, sel + 1);
|
|
305
|
+
else if (key === "\x1b[5~" || key === "\x02") sel = Math.max(0, sel - h); // PgUp / Ctrl-B
|
|
306
|
+
else if (key === "\x1b[6~" || key === "\x06") sel = Math.min(rows.length - 1, sel + h); // PgDn / Ctrl-F
|
|
307
|
+
else if (key === "g") sel = 0;
|
|
308
|
+
else if (key === "G") sel = rows.length - 1;
|
|
309
|
+
else if (key === "\r" || key === "\n") return done("open");
|
|
310
|
+
else if (key === "s") return done("summary");
|
|
311
|
+
else return;
|
|
312
|
+
draw();
|
|
313
|
+
});
|
|
314
|
+
process.stdout.write("\x1b[?1049h"); // alt screen (after raw mode so first draw is clean)
|
|
315
|
+
draw();
|
|
316
|
+
return done;
|
|
317
|
+
}
|
|
318
|
+
|
|
261
319
|
function cmdShow(id, n) {
|
|
262
320
|
const sess = find(id);
|
|
263
321
|
let pairs = [...conversation(sess)];
|
|
@@ -277,7 +335,8 @@ function usage(code) {
|
|
|
277
335
|
console.log(`session-preserver - centralized AI CLI session history
|
|
278
336
|
|
|
279
337
|
Usage:
|
|
280
|
-
session-preserver [ls] [-n N] [--since 2d] [--tool claude|codex|opencode] [query]
|
|
338
|
+
session-preserver [ls] [-n N] [--since 2d] [--tool claude|codex|opencode] [--plain] [query]
|
|
339
|
+
(interactive picker in a TTY: ↑/↓ move, enter resume, s copy summary, q quit; --plain forces table)
|
|
281
340
|
session-preserver show <id> [-n N]
|
|
282
341
|
session-preserver summary <id>
|
|
283
342
|
session-preserver path <id>
|
|
@@ -295,14 +354,16 @@ let cmd = "ls";
|
|
|
295
354
|
if (COMMANDS.has(argv[0])) cmd = argv.shift();
|
|
296
355
|
|
|
297
356
|
if (cmd === "ls") {
|
|
298
|
-
const args = { n:
|
|
357
|
+
const args = { n: 500, since: null, tool: null, query: null };
|
|
299
358
|
for (let i = 0; i < argv.length; i++) {
|
|
300
|
-
if (argv[i] === "-n") args.n = Number(argv[++i]);
|
|
359
|
+
if (argv[i] === "-n") { args.n = Number(argv[++i]); if (!args.n) args.n = Infinity; }
|
|
301
360
|
else if (argv[i] === "--since") args.since = argv[++i];
|
|
302
361
|
else if (argv[i] === "--tool") {
|
|
303
362
|
args.tool = argv[++i];
|
|
304
363
|
if (!SOURCES[args.tool]) { console.error(`unknown tool '${args.tool}' (claude|codex|opencode)`); process.exit(1); }
|
|
305
|
-
}
|
|
364
|
+
}
|
|
365
|
+
else if (argv[i] === "--plain") args.plain = true;
|
|
366
|
+
else if (!args.query) args.query = argv[i];
|
|
306
367
|
else usage(1);
|
|
307
368
|
}
|
|
308
369
|
cmdLs(args);
|