UNPKG

@stackmemoryai/stackmemory

Version:

Project-scoped memory for AI coding tools. Durable context across sessions with MCP integration, frames, smart retrieval, Claude Code skills, and automatic hooks.

170 lines (169 loc) 5.28 kB
import { fileURLToPath as __fileURLToPath } from 'url'; import { dirname as __pathDirname } from 'path'; const __filename = __fileURLToPath(import.meta.url); const __dirname = __pathDirname(__filename); import { Command } from "commander"; import Database from "better-sqlite3"; import { join } from "path"; import { existsSync, readFileSync } from "fs"; function createLogCommand() { const log = new Command("log").alias("history").description("View recent activity log").option("-n, --lines <n>", "Number of entries to show", "20").option("-t, --type <type>", "Filter by type (task, frame, event, sync)").option("-f, --follow", "Follow log in real-time").action(async (options) => { const projectRoot = process.cwd(); const dbPath = join(projectRoot, ".stackmemory", "context.db"); const tasksPath = join(projectRoot, ".stackmemory", "tasks.jsonl"); if (!existsSync(dbPath)) { console.log( '\u274C StackMemory not initialized. Run "stackmemory init" first.' ); return; } const limit = parseInt(options.lines); const activities = []; const db = new Database(dbPath); if (!options.type || options.type === "frame") { try { const frames = db.prepare( ` SELECT id, type, name, state, created_at, updated_at FROM frames ORDER BY updated_at DESC LIMIT ? ` ).all(limit); frames.forEach((f) => { activities.push({ timestamp: f.updated_at || f.created_at, type: "frame", action: f.state === "closed" ? "closed" : "opened", details: `[${f.type}] ${f.name || f.id.slice(0, 10)}` }); }); } catch { } } if (!options.type || options.type === "event") { try { const events = db.prepare( ` SELECT id, type, data, timestamp FROM events ORDER BY timestamp DESC LIMIT ? ` ).all(limit); events.forEach((e) => { let data = {}; try { data = JSON.parse(e.data); } catch { } activities.push({ timestamp: e.timestamp, type: "event", action: e.type, details: data.message || data.content || data.decision || "" }); }); } catch { } } if (!options.type || options.type === "task") { try { const tasks = db.prepare( ` SELECT id, title, status, type, timestamp FROM task_cache ORDER BY timestamp DESC LIMIT ? ` ).all(limit); tasks.forEach((t) => { activities.push({ timestamp: t.timestamp, type: "task", action: t.type?.replace("task_", "") || t.status, details: t.title }); }); } catch { } } db.close(); if (!options.type || options.type === "sync") { const mappingsPath = join( projectRoot, ".stackmemory", "linear-mappings.json" ); if (existsSync(mappingsPath)) { try { const mappings = JSON.parse(readFileSync(mappingsPath, "utf-8")); mappings.slice(-limit).forEach((m) => { activities.push({ timestamp: Math.floor(m.lastSyncTimestamp / 1e3), type: "sync", action: "synced", details: `${m.linearIdentifier}` }); }); } catch { } } } activities.sort((a, b) => b.timestamp - a.timestamp); console.log(` \u{1F4DC} Activity Log `); const typeIcon = { frame: "\u{1F4C1}", event: "\u26A1", task: "\u{1F4CB}", sync: "\u{1F504}" }; activities.slice(0, limit).forEach((activity) => { const date = new Date(activity.timestamp * 1e3); const timeStr = date.toLocaleTimeString("en-US", { hour: "2-digit", minute: "2-digit" }); const dateStr = date.toLocaleDateString("en-US", { month: "short", day: "numeric" }); const icon = typeIcon[activity.type] || "\u{1F4DD}"; console.log( `${icon} ${dateStr} ${timeStr} ${activity.action.padEnd(12)} ${activity.details.slice(0, 50)}` ); }); console.log(""); if (options.follow) { console.log("\u{1F440} Watching for changes... (Ctrl+C to stop)\n"); try { const chokidar = await import("chokidar"); const watcher = chokidar.watch(join(projectRoot, ".stackmemory"), { persistent: true, ignoreInitial: true }); watcher.on("change", (path) => { const now = /* @__PURE__ */ new Date(); const timeStr = now.toLocaleTimeString("en-US", { hour: "2-digit", minute: "2-digit" }); console.log( `\u{1F504} ${timeStr} File changed: ${path.split("/").pop()}` ); }); await new Promise(() => { }); } catch { console.log("Install chokidar for follow mode: npm i chokidar"); } } }); return log; } export { createLogCommand }; //# sourceMappingURL=log.js.map