reindex
Re-parse zsh and bash history files to refresh the local index after new shell activity. Run this to keep your history searchable.
Instructions
Re-parse ~/.zsh_history and ~/.bash_history into the local index. Run after new shell activity.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
No arguments | |||
Implementation Reference
- src/index.ts:100-105 (handler)The handler for the 'reindex' tool: calls loadHistoryFiles() to parse shell history files, indexEntries() to store them in SQLite, then ingestExtendedLog() for hook-captured data.
if (name === "reindex") { const entries = loadHistoryFiles(); const r = indexEntries(db, entries); const ext = ingestExtendedLog(db); return { content: [{ type: "text", text: `parsed=${entries.length} inserted=${r.inserted} skipped=${r.skipped} ext_applied=${ext.applied} ext_inserted=${ext.inserted}` }] }; } - src/index.ts:38-42 (registration)Registration of the 'reindex' tool in the TOOLS array with its name, description, and empty input schema.
{ name: "reindex", description: "Re-parse ~/.zsh_history and ~/.bash_history into the local index. Run after new shell activity.", inputSchema: { type: "object", properties: {}, additionalProperties: false }, }, - src/indexer.ts:63-82 (helper)loadHistoryFiles() reads and parses .zsh_history and .bash_history into Entry objects (used by the reindex handler).
export function loadHistoryFiles(): Entry[] { const all: Entry[] = []; const zsh = process.env.HISTFILE && process.env.HISTFILE.includes("zsh") ? process.env.HISTFILE : join(HOME, ".zsh_history"); const bash = join(HOME, ".bash_history"); if (existsSync(zsh)) { try { const txt = readFileSync(zsh, { encoding: "utf-8" }); all.push(...parseZshHistory(txt)); } catch { const buf = readFileSync(zsh); all.push(...parseZshHistory(buf.toString("latin1"))); } } if (existsSync(bash)) { all.push(...parseBashHistory(readFileSync(bash, "utf-8"))); } return all; } - src/indexer.ts:84-100 (helper)indexEntries() inserts parsed history entries into the SQLite database in a transaction, deduplicating via hash (used by the reindex handler).
export function indexEntries(db: Database.Database, entries: Entry[]): { inserted: number; skipped: number } { const ins = db.prepare(` INSERT OR IGNORE INTO commands (cmd, ts, shell, cwd, exit_code, duration_ms, hash) VALUES (?, ?, ?, ?, ?, ?, ?) `); let inserted = 0, skipped = 0; const tx = db.transaction((rows: Entry[]) => { for (const e of rows) { const cmd = redact(e.cmd); const h = hashEntry({ ...e, cmd }); const r = ins.run(cmd, e.ts, e.shell, e.cwd, e.exit_code, e.duration_ms, h); if (r.changes > 0) inserted++; else skipped++; } }); tx(entries); return { inserted, skipped }; } - src/extended-log.ts:47-108 (helper)ingestExtendedLog() processes hook-captured extended entries from extended.log, updating existing entries or inserting new ones (called by the reindex handler).
export function ingestExtendedLog(db: Database.Database): { applied: number; inserted: number } { if (!existsSync(LOG_PATH)) return { applied: 0, inserted: 0 }; let offset = 0; if (existsSync(STATE_PATH)) { const v = parseInt(readFileSync(STATE_PATH, "utf-8").trim(), 10); if (Number.isFinite(v)) offset = v; } const size = statSync(LOG_PATH).size; if (size < offset) offset = 0; // log rotated/truncated if (size === offset) return { applied: 0, inserted: 0 }; const fd = readFileSync(LOG_PATH); const slice = fd.subarray(offset).toString("utf-8"); const lines = slice.split("\n"); // last line may be partial — keep its bytes for next run const tail = slice.endsWith("\n") ? "" : lines.pop() ?? ""; const consumed = size - Buffer.byteLength(tail, "utf-8"); const updateByHash = db.prepare(` UPDATE commands SET cwd = ?, exit_code = ?, duration_ms = ? WHERE hash = ? AND (cwd IS NULL OR exit_code IS NULL) `); const insert = db.prepare(` INSERT OR IGNORE INTO commands (cmd, ts, shell, cwd, exit_code, duration_ms, hash) VALUES (?, ?, ?, ?, ?, ?, ?) `); let applied = 0, inserted = 0; const tx = db.transaction((rows: ExtEntry[]) => { for (const e of rows) { const cmd = redact(e.cmd); // Try to match existing zsh/bash row by (shell, ts, cmd) — but we don't know shell here. // Try both common shells. let updated = false; for (const shell of ["zsh", "bash", "fish"]) { const h = createHash("sha1").update(`${shell}|${e.ts}|${cmd}`).digest("hex"); const r = updateByHash.run(e.cwd, e.exit_code, e.duration_ms, h); if (r.changes > 0) { applied++; updated = true; break; } } if (!updated) { // Insert as a new "ext" entry; shell unknown but mark as 'ext'. const h = createHash("sha1").update(`ext|${e.ts}|${cmd}`).digest("hex"); const r = insert.run(cmd, e.ts, "ext", e.cwd, e.exit_code, e.duration_ms, h); if (r.changes > 0) inserted++; } } }); const parsed: ExtEntry[] = []; for (const line of lines) { const t = line.trim(); if (!t) continue; const p = parseLine(t); if (p) parsed.push(p); } tx(parsed); writeFileSync(STATE_PATH, String(consumed)); return { applied, inserted }; }