@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.
- package/dist/cli.js +112 -20
- package/dist/index.js +21 -0
- 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.
|
|
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: "/
|
|
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
|
|
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
|
-
|
|
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(
|
|
9544
|
+
console.log(style.yellow(`
|
|
9545
|
+
Session persistence manager not active.
|
|
9546
|
+
`));
|
|
9489
9547
|
return;
|
|
9490
9548
|
}
|
|
9491
|
-
const
|
|
9492
|
-
if (
|
|
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
|
|
9503
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
9533
|
-
if (
|
|
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
|
|
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
|
}
|