@pikaa-ai/pikaa 0.3.22 → 0.3.23

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.
Files changed (3) hide show
  1. package/dist/cli.js +112 -20
  2. package/dist/index.js +21 -0
  3. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -5228,6 +5228,27 @@ class SessionPersistenceManager {
5228
5228
  return null;
5229
5229
  }
5230
5230
  }
5231
+ unbindSession() {
5232
+ for (const unsub of this.unsubscribers) {
5233
+ try {
5234
+ unsub();
5235
+ } catch {}
5236
+ }
5237
+ this.unsubscribers = [];
5238
+ }
5239
+ resumeIntoSession(session, threadId) {
5240
+ const restored = this.loadSession(threadId);
5241
+ if (!restored)
5242
+ return null;
5243
+ this.unbindSession();
5244
+ session.threadId = restored.thread.id;
5245
+ session.setHistory(restored.items);
5246
+ if (restored.thread.model) {
5247
+ session.model = restored.thread.model;
5248
+ }
5249
+ this.bindSession(session, restored.thread.role);
5250
+ return restored;
5251
+ }
5231
5252
  listSessions(options) {
5232
5253
  return this.store.listThreads(options);
5233
5254
  }
@@ -6635,7 +6656,7 @@ function parsePatch(oldSrc, newSrc, contextLines = 3) {
6635
6656
  // package.json
6636
6657
  var package_default = {
6637
6658
  name: "@pikaa-ai/pikaa",
6638
- version: "0.3.22",
6659
+ version: "0.3.23",
6639
6660
  description: "PIKAA CLI - AI coding agent that runs locally in your terminal.",
6640
6661
  main: "./dist/index.js",
6641
6662
  module: "./dist/index.js",
@@ -8837,7 +8858,8 @@ var AVAILABLE_SLASH_COMMANDS = [
8837
8858
  { name: "/skills", description: "List domain skills in workspace & global" },
8838
8859
  { name: "/memories", description: "View learned preferences & memories" },
8839
8860
  { name: "/worktrees", description: "List active isolated Git Worktrees" },
8840
- { name: "/sessions", description: "List saved past sessions from SQLite store" },
8861
+ { name: "/resume", description: "Resume a previous conversation session (/resume [id])" },
8862
+ { name: "/sessions", description: "List and manage saved past sessions" },
8841
8863
  { name: "/role", description: "Select and inspect specialized agent roles & personas" },
8842
8864
  { name: "/security", description: "Scan codebase for vulnerabilities & secrets (Strix)" },
8843
8865
  { name: "/agents", description: "List active sub-agents & execution status" },
@@ -8945,9 +8967,10 @@ async function handleSlashCommand(input, ctx) {
8945
8967
  case "/worktree":
8946
8968
  await printWorktrees(ctx);
8947
8969
  return true;
8970
+ case "/resume":
8948
8971
  case "/sessions":
8949
8972
  case "/session":
8950
- await printSessions(ctx);
8973
+ await handleResumeCommand(ctx, args);
8951
8974
  return true;
8952
8975
  case "/security":
8953
8976
  case "/audit":
@@ -9482,41 +9505,104 @@ function printMemories(ctx) {
9482
9505
  }
9483
9506
  console.log();
9484
9507
  }
9485
- async function printSessions(ctx) {
9508
+ function formatRelativeTime(timestamp) {
9509
+ const diffMs = Math.max(0, Date.now() - timestamp);
9510
+ const diffSec = Math.floor(diffMs / 1000);
9511
+ if (diffSec < 60)
9512
+ return "just now";
9513
+ const diffMin = Math.floor(diffSec / 60);
9514
+ if (diffMin < 60)
9515
+ return `${diffMin}m ago`;
9516
+ const diffHours = Math.floor(diffMin / 60);
9517
+ if (diffHours < 24)
9518
+ return `${diffHours}h ago`;
9519
+ const diffDays = Math.floor(diffHours / 24);
9520
+ if (diffDays < 30)
9521
+ return `${diffDays}d ago`;
9522
+ return new Date(timestamp).toLocaleDateString();
9523
+ }
9524
+ function printRecentConversation(items) {
9525
+ if (!items || items.length === 0)
9526
+ return;
9527
+ const dialog = items.filter((i) => i.type === "user_message" || i.type === "agent_message" || i.role === "user" || i.role === "assistant").slice(-3);
9528
+ if (dialog.length === 0)
9529
+ return;
9530
+ console.log(style.dim(" Recent context:"));
9531
+ for (const item of dialog) {
9532
+ const isUser = item.role === "user" || item.type === "user_message";
9533
+ const text = typeof item.content === "string" ? item.content : item.text || "";
9534
+ const singleLine = text.trim().replace(/\s+/g, " ");
9535
+ const preview = singleLine.length > 85 ? singleLine.slice(0, 82) + "..." : singleLine;
9536
+ const badge = isUser ? style.brand(" \u276F User:") : style.cyan(" \u25CF Assistant:");
9537
+ console.log(`${badge} ${style.dim(preview)}`);
9538
+ }
9539
+ console.log();
9540
+ }
9541
+ async function handleResumeCommand(ctx, args = []) {
9486
9542
  const storage = ctx.storageManager;
9487
9543
  if (!storage) {
9488
- console.log(style.yellow("Session persistence manager not active."));
9544
+ console.log(style.yellow(`
9545
+ Session persistence manager not active.
9546
+ `));
9489
9547
  return;
9490
9548
  }
9491
- const threads = storage.listSessions();
9492
- if (threads.length === 0) {
9549
+ const rawThreads = storage.listSessions();
9550
+ if (rawThreads.length === 0) {
9493
9551
  console.log(style.dim(`
9494
- No saved sessions in SQLite database.
9552
+ No saved conversation sessions found in SQLite database.
9495
9553
  `));
9496
9554
  return;
9497
9555
  }
9556
+ const threads = [...rawThreads].sort((a, b) => b.updatedAt - a.updatedAt);
9557
+ const filterArg = args[0]?.trim();
9558
+ let defaultIdx = 0;
9559
+ if (filterArg) {
9560
+ const foundIdx = threads.findIndex((t) => t.id === filterArg || t.id.toLowerCase().startsWith(filterArg.toLowerCase()));
9561
+ if (foundIdx !== -1) {
9562
+ defaultIdx = foundIdx;
9563
+ }
9564
+ }
9498
9565
  if (!process.stdin.isTTY || false || !process.stdin.readable) {
9566
+ if (filterArg) {
9567
+ const match = threads[defaultIdx];
9568
+ const restored = storage.resumeIntoSession(ctx.session, match.id);
9569
+ if (restored) {
9570
+ console.log(style.green(`
9571
+ \u2713 Resumed past session: ${style.bold(match.id)} (${restored.items.length} items, model: ${restored.thread.model || ctx.session.model})
9572
+ `));
9573
+ printRecentConversation(restored.items);
9574
+ } else {
9575
+ console.log(style.red(`
9576
+ \u2715 Failed to load session '${match.id}'.
9577
+ `));
9578
+ }
9579
+ return;
9580
+ }
9499
9581
  console.log();
9500
9582
  console.log(style.bold(" Saved Sessions in SQLite Store:"));
9501
9583
  for (const t of threads) {
9502
- const dateStr = new Date(t.updatedAt).toLocaleString();
9503
- console.log(` \u2022 ${style.cyan(t.id)} [${style.dim(t.model)}] - ${style.dim(dateStr)} (${t.itemsCount} items)`);
9584
+ const timeStr = formatRelativeTime(t.updatedAt);
9585
+ const titleStr = t.title && t.title !== "New Session" ? ` - "${t.title}"` : "";
9586
+ console.log(` \u2022 ${style.cyan(t.id)} [${style.dim(t.model)}]${titleStr} - ${style.dim(timeStr)} (${t.itemsCount ?? 0} items)`);
9504
9587
  }
9505
9588
  console.log();
9506
9589
  return;
9507
9590
  }
9508
9591
  const items = threads.map((t) => {
9509
- const dateStr = new Date(t.updatedAt).toLocaleString();
9592
+ const timeStr = formatRelativeTime(t.updatedAt);
9593
+ const titlePart = t.title && t.title !== "New Session" ? `"${t.title.slice(0, 36)}"` : undefined;
9510
9594
  return {
9511
9595
  id: t.id,
9512
9596
  label: t.id,
9513
- description: `${t.model} \xB7 ${t.itemsCount} items \xB7 ${dateStr}`
9597
+ badge: t.model,
9598
+ description: [titlePart, `${t.itemsCount ?? 0} items`, timeStr].filter(Boolean).join(" \xB7 ")
9514
9599
  };
9515
9600
  });
9516
9601
  const res = await promptInteractiveList({
9517
- title: `\uD83D\uDDC4\uFE0F Saved SQLite Sessions (${threads.length} total)`,
9602
+ title: `\uD83D\uDDC4\uFE0F Resume Past Conversation (${threads.length} saved)`,
9518
9603
  items,
9519
9604
  mode: "select",
9605
+ defaultIndex: defaultIdx,
9520
9606
  onAction: (key, item, idx) => {
9521
9607
  if (key === "d") {
9522
9608
  storage.deleteSession(item.id);
@@ -9526,18 +9612,24 @@ async function printSessions(ctx) {
9526
9612
  }
9527
9613
  return false;
9528
9614
  },
9529
- customKeyHints: "\u2191/\u2193: navigate \xB7 Enter: resume session \xB7 d: delete \xB7 Esc: exit"
9615
+ customKeyHints: "\u2191/\u2193: navigate \xB7 Enter: resume conversation \xB7 d: delete \xB7 Esc: cancel"
9530
9616
  });
9531
9617
  if (res.action === "select" && res.selectedItem) {
9532
- const session = storage.loadSession(res.selectedItem.id);
9533
- if (session) {
9534
- ctx.session.setHistory(session.items);
9535
- if (session.thread.model)
9536
- ctx.session.model = session.thread.model;
9618
+ const restored = storage.resumeIntoSession(ctx.session, res.selectedItem.id);
9619
+ if (restored) {
9537
9620
  console.log(style.green(`
9538
- \u2713 Resumed past session: ${style.bold(res.selectedItem.id)} (${session.items.length} items)
9621
+ \u2713 Resumed past conversation: ${style.bold(res.selectedItem.id)} (${restored.items.length} items, model: ${restored.thread.model || ctx.session.model})
9622
+ `));
9623
+ printRecentConversation(restored.items);
9624
+ } else {
9625
+ console.log(style.red(`
9626
+ \u2715 Failed to load session '${res.selectedItem.id}'.
9539
9627
  `));
9540
9628
  }
9629
+ } else {
9630
+ console.log(style.dim(`
9631
+ Session resume cancelled.
9632
+ `));
9541
9633
  }
9542
9634
  }
9543
9635
  function printSessionStats(ctx) {
package/dist/index.js CHANGED
@@ -9100,6 +9100,27 @@ class SessionPersistenceManager {
9100
9100
  return null;
9101
9101
  }
9102
9102
  }
9103
+ unbindSession() {
9104
+ for (const unsub of this.unsubscribers) {
9105
+ try {
9106
+ unsub();
9107
+ } catch {}
9108
+ }
9109
+ this.unsubscribers = [];
9110
+ }
9111
+ resumeIntoSession(session2, threadId) {
9112
+ const restored = this.loadSession(threadId);
9113
+ if (!restored)
9114
+ return null;
9115
+ this.unbindSession();
9116
+ session2.threadId = restored.thread.id;
9117
+ session2.setHistory(restored.items);
9118
+ if (restored.thread.model) {
9119
+ session2.model = restored.thread.model;
9120
+ }
9121
+ this.bindSession(session2, restored.thread.role);
9122
+ return restored;
9123
+ }
9103
9124
  listSessions(options) {
9104
9125
  return this.store.listThreads(options);
9105
9126
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pikaa-ai/pikaa",
3
- "version": "0.3.22",
3
+ "version": "0.3.23",
4
4
  "description": "PIKAA CLI - AI coding agent that runs locally in your terminal.",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",