@pikaa-ai/pikaa 0.3.21 → 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 CHANGED
@@ -1084,6 +1084,9 @@ class EphemeralWorkspaceManager {
1084
1084
  /^scratch_/i,
1085
1085
  /^temp_/i,
1086
1086
  /^preview_.*\.html$/i,
1087
+ /.*_log\.txt$/i,
1088
+ /\.log$/i,
1089
+ /^check\.(ps1|bat|cmd|sh)$/i,
1087
1090
  /\.tmp$/i,
1088
1091
  /\.bak$/i,
1089
1092
  /~$/i
@@ -5225,6 +5228,27 @@ class SessionPersistenceManager {
5225
5228
  return null;
5226
5229
  }
5227
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
+ }
5228
5252
  listSessions(options) {
5229
5253
  return this.store.listThreads(options);
5230
5254
  }
@@ -6632,7 +6656,7 @@ function parsePatch(oldSrc, newSrc, contextLines = 3) {
6632
6656
  // package.json
6633
6657
  var package_default = {
6634
6658
  name: "@pikaa-ai/pikaa",
6635
- version: "0.3.21",
6659
+ version: "0.3.23",
6636
6660
  description: "PIKAA CLI - AI coding agent that runs locally in your terminal.",
6637
6661
  main: "./dist/index.js",
6638
6662
  module: "./dist/index.js",
@@ -8834,7 +8858,8 @@ var AVAILABLE_SLASH_COMMANDS = [
8834
8858
  { name: "/skills", description: "List domain skills in workspace & global" },
8835
8859
  { name: "/memories", description: "View learned preferences & memories" },
8836
8860
  { name: "/worktrees", description: "List active isolated Git Worktrees" },
8837
- { 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" },
8838
8863
  { name: "/role", description: "Select and inspect specialized agent roles & personas" },
8839
8864
  { name: "/security", description: "Scan codebase for vulnerabilities & secrets (Strix)" },
8840
8865
  { name: "/agents", description: "List active sub-agents & execution status" },
@@ -8942,9 +8967,10 @@ async function handleSlashCommand(input, ctx) {
8942
8967
  case "/worktree":
8943
8968
  await printWorktrees(ctx);
8944
8969
  return true;
8970
+ case "/resume":
8945
8971
  case "/sessions":
8946
8972
  case "/session":
8947
- await printSessions(ctx);
8973
+ await handleResumeCommand(ctx, args);
8948
8974
  return true;
8949
8975
  case "/security":
8950
8976
  case "/audit":
@@ -9479,41 +9505,104 @@ function printMemories(ctx) {
9479
9505
  }
9480
9506
  console.log();
9481
9507
  }
9482
- 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 = []) {
9483
9542
  const storage = ctx.storageManager;
9484
9543
  if (!storage) {
9485
- console.log(style.yellow("Session persistence manager not active."));
9544
+ console.log(style.yellow(`
9545
+ Session persistence manager not active.
9546
+ `));
9486
9547
  return;
9487
9548
  }
9488
- const threads = storage.listSessions();
9489
- if (threads.length === 0) {
9549
+ const rawThreads = storage.listSessions();
9550
+ if (rawThreads.length === 0) {
9490
9551
  console.log(style.dim(`
9491
- No saved sessions in SQLite database.
9552
+ No saved conversation sessions found in SQLite database.
9492
9553
  `));
9493
9554
  return;
9494
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
+ }
9495
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
+ }
9496
9581
  console.log();
9497
9582
  console.log(style.bold(" Saved Sessions in SQLite Store:"));
9498
9583
  for (const t of threads) {
9499
- const dateStr = new Date(t.updatedAt).toLocaleString();
9500
- 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)`);
9501
9587
  }
9502
9588
  console.log();
9503
9589
  return;
9504
9590
  }
9505
9591
  const items = threads.map((t) => {
9506
- 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;
9507
9594
  return {
9508
9595
  id: t.id,
9509
9596
  label: t.id,
9510
- 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 ")
9511
9599
  };
9512
9600
  });
9513
9601
  const res = await promptInteractiveList({
9514
- title: `\uD83D\uDDC4\uFE0F Saved SQLite Sessions (${threads.length} total)`,
9602
+ title: `\uD83D\uDDC4\uFE0F Resume Past Conversation (${threads.length} saved)`,
9515
9603
  items,
9516
9604
  mode: "select",
9605
+ defaultIndex: defaultIdx,
9517
9606
  onAction: (key, item, idx) => {
9518
9607
  if (key === "d") {
9519
9608
  storage.deleteSession(item.id);
@@ -9523,18 +9612,24 @@ async function printSessions(ctx) {
9523
9612
  }
9524
9613
  return false;
9525
9614
  },
9526
- 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"
9527
9616
  });
9528
9617
  if (res.action === "select" && res.selectedItem) {
9529
- const session = storage.loadSession(res.selectedItem.id);
9530
- if (session) {
9531
- ctx.session.setHistory(session.items);
9532
- if (session.thread.model)
9533
- ctx.session.model = session.thread.model;
9618
+ const restored = storage.resumeIntoSession(ctx.session, res.selectedItem.id);
9619
+ if (restored) {
9534
9620
  console.log(style.green(`
9535
- \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}'.
9536
9627
  `));
9537
9628
  }
9629
+ } else {
9630
+ console.log(style.dim(`
9631
+ Session resume cancelled.
9632
+ `));
9538
9633
  }
9539
9634
  }
9540
9635
  function printSessionStats(ctx) {
package/dist/index.js CHANGED
@@ -2367,6 +2367,9 @@ class EphemeralWorkspaceManager {
2367
2367
  /^scratch_/i,
2368
2368
  /^temp_/i,
2369
2369
  /^preview_.*\.html$/i,
2370
+ /.*_log\.txt$/i,
2371
+ /\.log$/i,
2372
+ /^check\.(ps1|bat|cmd|sh)$/i,
2370
2373
  /\.tmp$/i,
2371
2374
  /\.bak$/i,
2372
2375
  /~$/i
@@ -9097,6 +9100,27 @@ class SessionPersistenceManager {
9097
9100
  return null;
9098
9101
  }
9099
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
+ }
9100
9124
  listSessions(options) {
9101
9125
  return this.store.listThreads(options);
9102
9126
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pikaa-ai/pikaa",
3
- "version": "0.3.21",
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",
@@ -17,12 +17,13 @@ All AI agents, sub-agents, and domain skills must adhere to these non-negotiable
17
17
  NEVER DUMP TEMPORARY, SCRATCH, OR DRAFT FILES INTO THE WORKSPACE ROOT OR RANDOM FOLDERS.
18
18
  ```
19
19
 
20
- * ❌ **Forbidden Patterns**:
21
- * `temp_*`, `tmp_*`, `scratch_*`, `draft_*`, `sandbox_*`
22
- * `test_preview.html`, `design_draft.tsx`, `preview.html`, `mock_*.json`
23
- * One-off scripts dumped into root directory (`test.js`, `run.py`, `script.sh`)
20
+ * ❌ **Forbidden Anti-Patterns**:
21
+ * Temporary scripts or logs in project root: `build_log.txt`, `dev_log.txt`, `output.log`, `*.log`, `*_log.txt`, `check.ps1`, `test.ps1`, `script.sh`
22
+ * Command output redirection to files (e.g. `npm run build > build_log.txt 2>&1`)
23
+ * `temp_*`, `tmp_*`, `scratch_*`, `draft_*`, `sandbox_*`, `test_preview.html`, `preview.html`, `mock_*.json`
24
24
  * ✅ **Mandatory Practice**:
25
- * Implement code **in-place** directly within the project's real directory architecture (e.g., `src/components/`, `src/pages/`, `lib/`, `tests/`).
25
+ * **Direct Command Execution**: Run build, test, and verification commands directly via the `shell` tool and inspect standard output / standard error directly from the returned tool output. Never dump stdout/stderr into `.txt` or `.log` files.
26
+ * **In-Place Architecture**: Implement code directly within the project's real directory architecture (e.g., `components/`, `src/components/`, `app/`, `lib/`, `tests/`).
26
27
  * If a file path is ambiguous, inspect existing project structure (`list_dir`, `find_files`) to locate the correct directory before writing.
27
28
 
28
29
  ---
@@ -12,9 +12,10 @@ You are Groupy, an expert autonomous AI coding assistant. You are running as a c
12
12
 
13
13
  ## Global Guardian Rails (Zero-Pollution & Workspace Integrity)
14
14
 
15
- - **ABSOLUTE BAN ON TEMPORARY / SCRATCH FILES**:
15
+ - **ABSOLUTE BAN ON TEMPORARY / SCRATCH / LOG FILES**:
16
16
  * **NEVER** create temporary, draft, or scratch files in the workspace root or arbitrary subfolders (e.g., `temp_*`, `tmp_*`, `scratch_*`, `draft_*`, `preview.html`, `test_design.*`, `sandbox_*`, `mock_*.json`).
17
- * **MANDATORY IN-PLACE EDITING**: All new code, UI components, styles, or scripts must be written directly into the actual project's intended architecture (e.g. `src/components/`, `src/views/`, `app/`, `lib/`, `tests/`).
17
+ * **NO LOG DUMPING & NO SCRIPT WRAPPERS**: Do NOT redirect command output to log files (e.g. `npm run build > build_log.txt`) or create temporary helper scripts in root (e.g. `check.ps1`, `test.bat`, `run.sh`). Run all commands directly via the `shell` tool and read stdout/stderr directly from the tool output.
18
+ * **MANDATORY IN-PLACE EDITING**: All new code, UI components, styles, or scripts must be written directly into the actual project's intended architecture (e.g. `src/components/`, `components/`, `app/`, `lib/`, `tests/`).
18
19
  * **FRONTEND & DESIGN RULE**: When asked to design UI or pages, implement production-grade components directly inside the project's source tree matching existing framework patterns. Do NOT dump standalone preview HTML/JSX files in root.
19
20
  * **SELF-CLEANUP PROTOCOL**: If any transient test script/artifact is absolutely required for a one-off sanity run, it MUST be removed before concluding the turn.
20
21
  * **NO TRASH LEFT BEHIND**: Before reporting completion, ensure the workspace is clean and unpolluted (`git status`).