context-vault 3.0.1 → 3.0.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.
Files changed (2) hide show
  1. package/bin/cli.js +151 -6
  2. package/package.json +1 -1
package/bin/cli.js CHANGED
@@ -3308,7 +3308,7 @@ async function runRecall() {
3308
3308
  const { initDatabase, prepareStatements } =
3309
3309
  await import("@context-vault/core/db");
3310
3310
  const { embed } = await import("@context-vault/core/embed");
3311
- const { hybridSearch } = await import("@context-vault/core/retrieve/index");
3311
+ const { hybridSearch } = await import("@context-vault/core/search");
3312
3312
 
3313
3313
  db = await initDatabase(config.dbPath);
3314
3314
  const stmts = prepareStatements(db);
@@ -3436,13 +3436,158 @@ async function runSessionCapture() {
3436
3436
  }
3437
3437
 
3438
3438
  async function runSessionEnd() {
3439
- const { main } = await import("../src/hooks/session-end.mjs");
3440
- await main();
3439
+ let db;
3440
+ try {
3441
+ const raw = await new Promise((resolve) => {
3442
+ let data = "";
3443
+ process.stdin.on("data", (chunk) => (data += chunk));
3444
+ process.stdin.on("end", () => resolve(data));
3445
+ });
3446
+ if (!raw.trim()) return;
3447
+ let input;
3448
+ try {
3449
+ input = JSON.parse(raw);
3450
+ } catch {
3451
+ return;
3452
+ }
3453
+ const { session_id, transcript_path, cwd } = input ?? {};
3454
+ if (!transcript_path || !cwd) return;
3455
+
3456
+ // Read transcript (JSONL)
3457
+ let turns = [];
3458
+ try {
3459
+ const transcriptRaw = readFileSync(transcript_path, "utf-8");
3460
+ for (const line of transcriptRaw.split("\n")) {
3461
+ const trimmed = line.trim();
3462
+ if (!trimmed) continue;
3463
+ try { turns.push(JSON.parse(trimmed)); } catch {}
3464
+ }
3465
+ } catch { return; }
3466
+
3467
+ const extractText = (turn) => {
3468
+ if (typeof turn.content === "string") return turn.content;
3469
+ if (Array.isArray(turn.content))
3470
+ return turn.content.filter((b) => b.type === "text").map((b) => b.text).join(" ");
3471
+ return "";
3472
+ };
3473
+
3474
+ const userTurns = turns.filter((t) => t.role === "user");
3475
+ if (userTurns.length === 0) return;
3476
+
3477
+ // Tool use blocks
3478
+ const allToolUse = [];
3479
+ for (const turn of turns) {
3480
+ if (!Array.isArray(turn.content)) continue;
3481
+ for (const block of turn.content) {
3482
+ if (block.type === "tool_use") allToolUse.push(block);
3483
+ }
3484
+ }
3485
+
3486
+ // Files modified
3487
+ const seenFiles = new Set();
3488
+ const filesModified = [];
3489
+ for (const block of allToolUse) {
3490
+ if (block.name === "Write" || block.name === "Edit") {
3491
+ const path = block.input?.file_path ?? block.input?.path ?? null;
3492
+ if (path && !seenFiles.has(path)) { seenFiles.add(path); filesModified.push(path); }
3493
+ }
3494
+ }
3495
+
3496
+ // Commands run
3497
+ const commandsRun = [];
3498
+ for (const block of allToolUse) {
3499
+ if (block.name === "Bash") {
3500
+ const cmd = block.input?.command ?? block.input?.cmd ?? null;
3501
+ if (cmd) commandsRun.push(cmd.slice(0, 100));
3502
+ }
3503
+ }
3504
+
3505
+ // Tool counts
3506
+ const toolCounts = {};
3507
+ for (const block of allToolUse) {
3508
+ const name = block.name ?? "unknown";
3509
+ toolCounts[name] = (toolCounts[name] ?? 0) + 1;
3510
+ }
3511
+ const toolSummary = Object.entries(toolCounts)
3512
+ .sort((a, b) => b[1] - a[1])
3513
+ .map(([name, count]) => `${name}: ${count}`)
3514
+ .join(", ");
3515
+
3516
+ // Duration
3517
+ let durationStr = null;
3518
+ const timestampedTurns = turns.filter((t) => t.timestamp != null);
3519
+ if (timestampedTurns.length >= 2) {
3520
+ const diffMs = new Date(timestampedTurns[timestampedTurns.length - 1].timestamp) - new Date(timestampedTurns[0].timestamp);
3521
+ if (!isNaN(diffMs) && diffMs >= 0) {
3522
+ const totalSec = Math.round(diffMs / 1000);
3523
+ const hours = Math.floor(totalSec / 3600);
3524
+ const minutes = Math.floor((totalSec % 3600) / 60);
3525
+ const seconds = totalSec % 60;
3526
+ durationStr = hours > 0 ? `${hours}h ${minutes}m` : minutes > 0 ? `${minutes}m ${seconds}s` : `${seconds}s`;
3527
+ }
3528
+ }
3529
+
3530
+ const message_count = userTurns.length;
3531
+ const project = cwd.split("/").pop() || "unknown";
3532
+ const first_prompt = extractText(userTurns[0]).slice(0, 200);
3533
+ const last_prompt = message_count > 1 ? extractText(userTurns[message_count - 1]).slice(0, 200) : first_prompt;
3534
+
3535
+ // Build body
3536
+ const durationPart = durationStr ? `, ~${durationStr}` : "";
3537
+ const bodyLines = [
3538
+ `Session in ${project} (${message_count} exchange${message_count !== 1 ? "s" : ""}${durationPart}).`,
3539
+ "", "## What was done",
3540
+ `Opened with: ${first_prompt}`, "",
3541
+ `Closed with: ${last_prompt}`,
3542
+ ];
3543
+ const limitedFiles = filesModified.slice(0, 20);
3544
+ if (limitedFiles.length > 0) {
3545
+ bodyLines.push("", "## Files modified");
3546
+ for (const f of limitedFiles) bodyLines.push(`- ${f}`);
3547
+ if (filesModified.length > 20) bodyLines.push(`- ... and ${filesModified.length - 20} more`);
3548
+ }
3549
+ const limitedCmds = commandsRun.slice(0, 10);
3550
+ if (limitedCmds.length > 0) {
3551
+ bodyLines.push("", "## Key commands");
3552
+ for (const c of limitedCmds) bodyLines.push(`- ${c}`);
3553
+ if (commandsRun.length > 10) bodyLines.push(`- ... and ${commandsRun.length - 10} more`);
3554
+ }
3555
+ if (toolSummary) bodyLines.push("", "## Tools used", toolSummary);
3556
+ const body = bodyLines.join("\n");
3557
+
3558
+ // Save via core APIs
3559
+ const { resolveConfig } = await import("@context-vault/core/config");
3560
+ const config = resolveConfig();
3561
+ if (!config.vaultDirExists) return;
3562
+ const { initDatabase, prepareStatements, insertVec, deleteVec } =
3563
+ await import("@context-vault/core/db");
3564
+ const { captureAndIndex } = await import("@context-vault/core/capture");
3565
+ db = await initDatabase(config.dbPath);
3566
+ const stmts = prepareStatements(db);
3567
+ const ctx = {
3568
+ db, config, stmts,
3569
+ embed: async () => null,
3570
+ insertVec: (rowid, embedding) => insertVec(stmts, rowid, embedding),
3571
+ deleteVec: (rowid) => deleteVec(stmts, rowid),
3572
+ };
3573
+ const entry = await captureAndIndex(ctx, {
3574
+ kind: "session",
3575
+ title: `Session — ${project} ${new Date().toLocaleString("en-US", { month: "short", day: "numeric", year: "numeric", hour: "2-digit", minute: "2-digit" })}`,
3576
+ body,
3577
+ tags: ["session-end", "session-summary", project],
3578
+ source: "claude-code",
3579
+ meta: { session_id: session_id ?? null, cwd, message_count },
3580
+ });
3581
+ console.log(`context-vault session captured — id: ${entry.id}`);
3582
+ } catch {
3583
+ // fail silently — never block session end
3584
+ } finally {
3585
+ try { db?.close(); } catch {}
3586
+ }
3441
3587
  }
3442
3588
 
3443
3589
  async function runPostToolCall() {
3444
- const { main } = await import("../src/hooks/post-tool-call.mjs");
3445
- await main();
3590
+ // Removed in v3 post-tool-call hooks are no longer supported
3446
3591
  }
3447
3592
 
3448
3593
  async function runSave() {
@@ -3579,7 +3724,7 @@ async function runSearch() {
3579
3724
  const { initDatabase, prepareStatements } =
3580
3725
  await import("@context-vault/core/db");
3581
3726
  const { embed } = await import("@context-vault/core/embed");
3582
- const { hybridSearch } = await import("@context-vault/core/retrieve/index");
3727
+ const { hybridSearch } = await import("@context-vault/core/search");
3583
3728
 
3584
3729
  db = await initDatabase(config.dbPath);
3585
3730
  const stmts = prepareStatements(db);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "context-vault",
3
- "version": "3.0.1",
3
+ "version": "3.0.2",
4
4
  "type": "module",
5
5
  "description": "Persistent memory for AI agents — saves and searches knowledge across sessions",
6
6
  "bin": {