session-preserver 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/index.js +57 -3
- 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,60 @@ 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↑/↓ move · enter resume in its CLI · s copy summary · q quit\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
|
+
if (key === "q" || key === "\x03" || key === "\x1b") return done("quit");
|
|
302
|
+
if (key === "\x1b[A" || key === "k") { sel = Math.max(0, sel - 1); return draw(); } // up
|
|
303
|
+
if (key === "\x1b[B" || key === "j") { sel = Math.min(rows.length - 1, sel + 1); return draw(); } // down
|
|
304
|
+
if (key === "\r" || key === "\n") return done("open");
|
|
305
|
+
if (key === "s") return done("summary");
|
|
306
|
+
});
|
|
307
|
+
process.stdout.write("\x1b[?1049h"); // alt screen (after raw mode so first draw is clean)
|
|
308
|
+
draw();
|
|
309
|
+
return done;
|
|
310
|
+
}
|
|
311
|
+
|
|
261
312
|
function cmdShow(id, n) {
|
|
262
313
|
const sess = find(id);
|
|
263
314
|
let pairs = [...conversation(sess)];
|
|
@@ -277,7 +328,8 @@ function usage(code) {
|
|
|
277
328
|
console.log(`session-preserver - centralized AI CLI session history
|
|
278
329
|
|
|
279
330
|
Usage:
|
|
280
|
-
session-preserver [ls] [-n N] [--since 2d] [--tool claude|codex|opencode] [query]
|
|
331
|
+
session-preserver [ls] [-n N] [--since 2d] [--tool claude|codex|opencode] [--plain] [query]
|
|
332
|
+
(interactive picker in a TTY: ↑/↓ move, enter resume, s copy summary, q quit; --plain forces table)
|
|
281
333
|
session-preserver show <id> [-n N]
|
|
282
334
|
session-preserver summary <id>
|
|
283
335
|
session-preserver path <id>
|
|
@@ -302,7 +354,9 @@ if (cmd === "ls") {
|
|
|
302
354
|
else if (argv[i] === "--tool") {
|
|
303
355
|
args.tool = argv[++i];
|
|
304
356
|
if (!SOURCES[args.tool]) { console.error(`unknown tool '${args.tool}' (claude|codex|opencode)`); process.exit(1); }
|
|
305
|
-
}
|
|
357
|
+
}
|
|
358
|
+
else if (argv[i] === "--plain") args.plain = true;
|
|
359
|
+
else if (!args.query) args.query = argv[i];
|
|
306
360
|
else usage(1);
|
|
307
361
|
}
|
|
308
362
|
cmdLs(args);
|