@agentreel/agent 0.1.0
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/LICENSE +21 -0
- package/README.md +74 -0
- package/dist/cli.js +1148 -0
- package/dist/cli.js.map +1 -0
- package/package.json +67 -0
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/paths.ts","../src/hooks/install.ts","../src/cli.ts","../src/commands/init.ts","../src/config.ts","../src/db.ts","../src/commands/status.ts","../src/commands/auth.ts","../src/upload/client.ts","../src/commands/watch.ts","../src/cursor/paths.ts","../src/cursor/watcher.ts","../src/cursor/entries.ts","../src/cursor/diff.ts","../src/redact/ignore.ts","../src/redact/patterns.ts","../src/redact/scrubber.ts","../src/cursor/session.ts","../src/commands/push.ts","../src/upload/queue.ts","../src/hooks/handler.ts"],"sourcesContent":["import { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { mkdirSync } from \"node:fs\";\n\nexport const HOME = homedir();\nexport const AGENTREEL_DIR = join(HOME, \".agentreel\");\nexport const DB_PATH = join(AGENTREEL_DIR, \"sessions.db\");\nexport const CONFIG_PATH = join(AGENTREEL_DIR, \"config.json\");\nexport const QUEUE_DIR = join(AGENTREEL_DIR, \"queue\");\nexport const LOG_PATH = join(AGENTREEL_DIR, \"agent.log\");\n\nexport const CLAUDE_DIR = join(HOME, \".claude\");\nexport const CLAUDE_SETTINGS_PATH = join(CLAUDE_DIR, \"settings.json\");\n\nexport function ensureAgentreelDir(): void {\n mkdirSync(AGENTREEL_DIR, { recursive: true });\n mkdirSync(QUEUE_DIR, { recursive: true });\n}\n","import { readFileSync, writeFileSync, existsSync, copyFileSync, mkdirSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport { CLAUDE_DIR, CLAUDE_SETTINGS_PATH } from \"../paths.js\";\n\nconst HOOK_MARKER = \"agentreel:v1\";\n\nconst HOOK_EVENTS = [\n \"SessionStart\",\n \"SessionEnd\",\n \"UserPromptSubmit\",\n \"PreToolUse\",\n \"PostToolUse\",\n \"Notification\",\n \"Stop\",\n \"SubagentStop\",\n \"PreCompact\",\n] as const;\n\ntype HookEntry = {\n type: \"command\";\n command: string;\n timeout?: number;\n};\n\ntype HookGroup = {\n matcher?: string;\n hooks: HookEntry[];\n __agentreel?: string;\n};\n\ntype ClaudeSettings = {\n hooks?: Record<string, HookGroup[]>;\n [key: string]: unknown;\n};\n\nfunction readSettings(): ClaudeSettings {\n if (!existsSync(CLAUDE_SETTINGS_PATH)) return {};\n const raw = readFileSync(CLAUDE_SETTINGS_PATH, \"utf8\");\n if (!raw.trim()) return {};\n return JSON.parse(raw) as ClaudeSettings;\n}\n\nfunction backupSettings(): string | null {\n if (!existsSync(CLAUDE_SETTINGS_PATH)) return null;\n const stamp = new Date().toISOString().replace(/[:.]/g, \"-\");\n const backup = `${CLAUDE_SETTINGS_PATH}.agentreel-backup-${stamp}`;\n copyFileSync(CLAUDE_SETTINGS_PATH, backup);\n return backup;\n}\n\nfunction buildHookEntry(event: string, hookCommandPrefix: string): HookEntry {\n // Final command shape: `<prefix> hook <Event>`. The prefix can be an\n // absolute path to dist/cli.js (global / dev installs) or an `npx -y …`\n // form (when the user ran us via npx and we don't have a stable on-disk\n // location to point Claude Code at).\n return {\n type: \"command\",\n command: `${hookCommandPrefix} hook ${event}`,\n timeout: 5,\n };\n}\n\nexport function quotePath(p: string): string {\n return /[\\s'\"$`\\\\]/.test(p) ? `\"${p.replace(/\"/g, '\\\\\"')}\"` : p;\n}\n\nexport interface InstallResult {\n backup: string | null;\n installedEvents: string[];\n hookCommandPrefix: string;\n}\n\nexport function installClaudeCodeHooks(hookCommandPrefix: string): InstallResult {\n mkdirSync(CLAUDE_DIR, { recursive: true });\n mkdirSync(dirname(CLAUDE_SETTINGS_PATH), { recursive: true });\n const backup = backupSettings();\n\n const settings = readSettings();\n settings.hooks ??= {};\n\n for (const event of HOOK_EVENTS) {\n const groups = settings.hooks[event] ?? [];\n // Drop any prior agentreel-managed group so re-running is idempotent.\n const filtered = groups.filter((g) => g.__agentreel !== HOOK_MARKER);\n filtered.push({\n matcher: \".*\",\n __agentreel: HOOK_MARKER,\n hooks: [buildHookEntry(event, hookCommandPrefix)],\n });\n settings.hooks[event] = filtered;\n }\n\n writeFileSync(CLAUDE_SETTINGS_PATH, JSON.stringify(settings, null, 2) + \"\\n\", \"utf8\");\n return { backup, installedEvents: [...HOOK_EVENTS], hookCommandPrefix };\n}\n\nexport function uninstallClaudeCodeHooks(): { backup: string | null } {\n if (!existsSync(CLAUDE_SETTINGS_PATH)) return { backup: null };\n const backup = backupSettings();\n const settings = readSettings();\n if (settings.hooks) {\n for (const event of Object.keys(settings.hooks)) {\n const groups = settings.hooks[event] ?? [];\n const remaining = groups.filter((g) => g.__agentreel !== HOOK_MARKER);\n if (remaining.length === 0) delete settings.hooks[event];\n else settings.hooks[event] = remaining;\n }\n if (Object.keys(settings.hooks).length === 0) delete settings.hooks;\n }\n writeFileSync(CLAUDE_SETTINGS_PATH, JSON.stringify(settings, null, 2) + \"\\n\", \"utf8\");\n return { backup };\n}\n","import { Command } from \"commander\";\nimport { initCommand } from \"./commands/init.js\";\nimport { statusCommand } from \"./commands/status.js\";\nimport { linkCommand, logoutCommand, uninstallCommand } from \"./commands/auth.js\";\nimport { watchCommand } from \"./commands/watch.js\";\nimport { pushCommand } from \"./commands/push.js\";\nimport { runHook } from \"./hooks/handler.js\";\n\nconst program = new Command();\n\nprogram\n .name(\"agentreel\")\n .description(\"AgentReel — capture Claude Code and Cursor sessions locally\")\n .version(\"0.0.0\");\n\nprogram\n .command(\"init\")\n .description(\"install Claude Code hooks and create the local SQLite buffer\")\n .action(async () => {\n await initCommand();\n });\n\nprogram\n .command(\"status\")\n .description(\"show local capture status, recent sessions, queue depth\")\n .action(async () => {\n await statusCommand();\n });\n\nprogram\n .command(\"watch\")\n .description(\"watch Cursor's local history and capture edits as events\")\n .action(async () => {\n await watchCommand();\n });\n\nprogram\n .command(\"link [api-key]\")\n .description(\"authenticate the local agent with agentreel.dev\")\n .option(\"--api <url>\", \"override the API base URL (default https://api.agentreel.dev)\")\n .action(async (apiKey: string | undefined, opts: { api?: string }) => {\n await linkCommand(apiKey, opts);\n });\n\nprogram\n .command(\"push\")\n .description(\"upload pending events to agentreel.dev\")\n .action(async () => {\n await pushCommand();\n });\n\nprogram\n .command(\"logout\")\n .description(\"clear local credentials\")\n .action(async () => {\n await logoutCommand();\n });\n\nprogram\n .command(\"uninstall\")\n .description(\"remove AgentReel hooks from ~/.claude/settings.json\")\n .action(async () => {\n await uninstallCommand();\n });\n\nprogram\n .command(\"hook <event>\")\n .description(\"internal: hook handler invoked by Claude Code (reads JSON from stdin)\")\n .action(async (event: string) => {\n await runHook(event);\n });\n\nprogram.parseAsync(process.argv).catch((err) => {\n // Top-level: if we got this far on a hook invocation, something is very wrong.\n // For all other commands, print and exit non-zero.\n const cmd = process.argv[2];\n if (cmd === \"hook\") {\n process.exit(0);\n }\n console.error(err);\n process.exit(1);\n});\n","import { realpathSync } from \"node:fs\";\nimport { fileURLToPath } from \"node:url\";\nimport { sep } from \"node:path\";\nimport pc from \"picocolors\";\nimport { installClaudeCodeHooks, quotePath } from \"../hooks/install.js\";\nimport { readConfig, writeConfig } from \"../config.js\";\nimport { getDb } from \"../db.js\";\nimport { ensureAgentreelDir } from \"../paths.js\";\n\nconst PACKAGE_NAME = \"@agentreel/agent\";\n\ninterface ResolvedBinary {\n absolutePath: string;\n isEphemeral: boolean; // true when running from an npx temp dir\n}\n\nfunction resolveAgentBinary(): ResolvedBinary {\n // tsup builds to dist/cli.js with a node shebang; npm symlinks it to\n // <prefix>/bin/agentreel. The realpath through that symlink is stable for\n // npm i -g and for in-tree development. For `npx` installs it points\n // inside ~/.npm/_npx/<hash>/ which gets cleaned eventually — we detect\n // that and fall back to an `npx -y …` hook command instead.\n const here = fileURLToPath(import.meta.url);\n const real = realpathSync(here);\n const isEphemeral = real.includes(`${sep}_npx${sep}`) || real.includes(\"/_npx/\");\n return { absolutePath: real, isEphemeral };\n}\n\nfunction chooseHookPrefix(bin: ResolvedBinary): { prefix: string; mode: \"absolute\" | \"npx\" } {\n if (process.env.AGENTREEL_HOOK_COMMAND) {\n return { prefix: process.env.AGENTREEL_HOOK_COMMAND, mode: \"absolute\" };\n }\n if (bin.isEphemeral) {\n return { prefix: `npx -y ${PACKAGE_NAME}`, mode: \"npx\" };\n }\n return { prefix: quotePath(bin.absolutePath), mode: \"absolute\" };\n}\n\nexport async function initCommand(): Promise<void> {\n ensureAgentreelDir();\n\n // Touch the DB so the file exists and migrations run.\n getDb();\n\n const cfg = readConfig();\n if (!cfg.installedAt) cfg.installedAt = Date.now();\n cfg.hooksInstalled = true;\n writeConfig(cfg);\n\n const binary = resolveAgentBinary();\n const { prefix, mode } = chooseHookPrefix(binary);\n const result = installClaudeCodeHooks(prefix);\n\n console.log(pc.bold(pc.cyan(\"\\n AgentReel \")) + pc.dim(\"Loom for AI coding sessions\\n\"));\n console.log(pc.green(\"✓\") + \" Created ~/.agentreel/sessions.db\");\n console.log(pc.green(\"✓\") + \" Wrote ~/.agentreel/config.json\");\n console.log(\n pc.green(\"✓\") +\n ` Installed ${result.installedEvents.length} Claude Code hooks → ~/.claude/settings.json`,\n );\n if (result.backup) {\n console.log(pc.dim(` (backup: ${result.backup})`));\n }\n console.log();\n console.log(pc.dim(\" Hook command: \") + pc.dim(`${prefix} hook <Event>`));\n if (mode === \"npx\") {\n console.log(\n pc.dim(\" \") +\n pc.yellow(\"•\") +\n pc.dim(\n ` Hooks resolve via npx every time Claude Code fires an event.\\n ` +\n ` For faster cold starts, run: npm i -g ${PACKAGE_NAME}`,\n ),\n );\n }\n console.log();\n console.log(pc.bold(\"Next:\") + \" open Claude Code and run a prompt.\");\n console.log(\" then \" + pc.cyan(\"agentreel status\") + \" to see captured events.\\n\");\n}\n","import { readFileSync, writeFileSync, existsSync, chmodSync } from \"node:fs\";\nimport { CONFIG_PATH, ensureAgentreelDir } from \"./paths.js\";\n\nexport interface AgentConfig {\n apiKey?: string;\n apiBaseUrl: string;\n workspaceId?: string;\n installedAt?: number;\n hooksInstalled?: boolean;\n schemaVersion: 1;\n}\n\nconst DEFAULT: AgentConfig = {\n apiBaseUrl: \"https://api.agentreel.dev\",\n schemaVersion: 1,\n};\n\nexport function readConfig(): AgentConfig {\n if (!existsSync(CONFIG_PATH)) return { ...DEFAULT };\n try {\n const raw = readFileSync(CONFIG_PATH, \"utf8\");\n return { ...DEFAULT, ...JSON.parse(raw) };\n } catch {\n return { ...DEFAULT };\n }\n}\n\nexport function writeConfig(cfg: AgentConfig): void {\n ensureAgentreelDir();\n // mode 0o600 — config.json holds the API key; only the owning user\n // should ever be able to read it. chmod after write to be sure even\n // if the file already existed with looser perms.\n writeFileSync(CONFIG_PATH, JSON.stringify(cfg, null, 2) + \"\\n\", {\n encoding: \"utf8\",\n mode: 0o600,\n });\n try {\n chmodSync(CONFIG_PATH, 0o600);\n } catch {\n // Non-Unix or filesystem without mode bits — best effort.\n }\n}\n","import Database from \"better-sqlite3\";\nimport type { Database as DB } from \"better-sqlite3\";\nimport { DB_PATH, ensureAgentreelDir } from \"./paths.js\";\nimport type { AgentEvent, Session, Tool } from \"@agentreel/shared-types\";\n\nlet _db: DB | null = null;\n\nexport function getDb(): DB {\n if (_db) return _db;\n ensureAgentreelDir();\n const db = new Database(DB_PATH);\n db.pragma(\"journal_mode = WAL\");\n db.pragma(\"synchronous = NORMAL\");\n migrate(db);\n _db = db;\n return db;\n}\n\nfunction migrate(db: DB): void {\n db.exec(`\n CREATE TABLE IF NOT EXISTS sessions (\n id TEXT PRIMARY KEY,\n tool TEXT NOT NULL,\n started_at INTEGER NOT NULL,\n ended_at INTEGER,\n cwd TEXT,\n total_cost_cents INTEGER,\n total_tokens INTEGER\n );\n CREATE TABLE IF NOT EXISTS events (\n id TEXT PRIMARY KEY,\n session_id TEXT NOT NULL,\n tool TEXT NOT NULL,\n type TEXT NOT NULL,\n ts INTEGER NOT NULL,\n cwd TEXT,\n payload TEXT NOT NULL,\n uploaded_at INTEGER\n );\n CREATE INDEX IF NOT EXISTS idx_events_session ON events(session_id, ts);\n CREATE INDEX IF NOT EXISTS idx_events_pending ON events(uploaded_at) WHERE uploaded_at IS NULL;\n `);\n}\n\nexport function upsertSession(s: Session): void {\n const db = getDb();\n db.prepare(\n `INSERT INTO sessions (id, tool, started_at, ended_at, cwd, total_cost_cents, total_tokens)\n VALUES (@id, @tool, @startedAt, @endedAt, @cwd, @totalCostCents, @totalTokens)\n ON CONFLICT(id) DO UPDATE SET\n ended_at = COALESCE(excluded.ended_at, sessions.ended_at),\n cwd = COALESCE(excluded.cwd, sessions.cwd),\n total_cost_cents = COALESCE(excluded.total_cost_cents, sessions.total_cost_cents),\n total_tokens = COALESCE(excluded.total_tokens, sessions.total_tokens)`,\n ).run({\n id: s.id,\n tool: s.tool,\n startedAt: s.startedAt,\n endedAt: s.endedAt ?? null,\n cwd: s.cwd ?? null,\n totalCostCents: s.totalCostCents ?? null,\n totalTokens: s.totalTokens ?? null,\n });\n}\n\nexport function insertEvent(e: AgentEvent): void {\n const db = getDb();\n db.prepare(\n `INSERT OR IGNORE INTO events (id, session_id, tool, type, ts, cwd, payload)\n VALUES (?, ?, ?, ?, ?, ?, ?)`,\n ).run(e.id, e.sessionId, e.tool, e.type, e.ts, e.cwd ?? null, JSON.stringify(e.payload ?? {}));\n}\n\nexport function countEvents(): { total: number; pending: number } {\n const db = getDb();\n const total = db.prepare(`SELECT COUNT(*) AS c FROM events`).get() as { c: number };\n const pending = db\n .prepare(`SELECT COUNT(*) AS c FROM events WHERE uploaded_at IS NULL`)\n .get() as { c: number };\n return { total: total.c, pending: pending.c };\n}\n\nexport function listRecentSessions(limit = 10): Array<{\n id: string;\n tool: Tool;\n started_at: number;\n ended_at: number | null;\n cwd: string | null;\n}> {\n const db = getDb();\n return db\n .prepare(\n `SELECT id, tool, started_at, ended_at, cwd\n FROM sessions ORDER BY started_at DESC LIMIT ?`,\n )\n .all(limit) as Array<{\n id: string;\n tool: Tool;\n started_at: number;\n ended_at: number | null;\n cwd: string | null;\n }>;\n}\n","import pc from \"picocolors\";\nimport { existsSync } from \"node:fs\";\nimport { CLAUDE_SETTINGS_PATH, DB_PATH, CONFIG_PATH } from \"../paths.js\";\nimport { countEvents, listRecentSessions } from \"../db.js\";\nimport { readConfig } from \"../config.js\";\n\nfunction fmtDuration(ms: number): string {\n const s = Math.round(ms / 1000);\n if (s < 60) return `${s}s`;\n const m = Math.floor(s / 60);\n if (m < 60) return `${m}m ${s % 60}s`;\n const h = Math.floor(m / 60);\n return `${h}h ${m % 60}m`;\n}\n\nexport async function statusCommand(): Promise<void> {\n const cfg = readConfig();\n console.log(pc.bold(pc.cyan(\"AgentReel status\\n\")));\n console.log(\" config: \" + (existsSync(CONFIG_PATH) ? CONFIG_PATH : pc.red(\"missing\")));\n console.log(\" database: \" + (existsSync(DB_PATH) ? DB_PATH : pc.red(\"not initialized\")));\n console.log(\n \" claude hooks: \" +\n (existsSync(CLAUDE_SETTINGS_PATH) ? CLAUDE_SETTINGS_PATH : pc.red(\"not installed\")),\n );\n console.log(\" api base: \" + cfg.apiBaseUrl);\n console.log(\" authenticated: \" + (cfg.apiKey ? pc.green(\"yes\") : pc.yellow(\"no\")));\n console.log();\n\n if (!existsSync(DB_PATH)) {\n console.log(pc.yellow(\"Run `agentreel init` to install hooks.\"));\n return;\n }\n\n const { total, pending } = countEvents();\n console.log(` events captured: ${total}`);\n console.log(` pending upload: ${pending}`);\n console.log();\n\n const sessions = listRecentSessions(5);\n if (sessions.length === 0) {\n console.log(pc.dim(\" no sessions yet — start a Claude Code session to capture one.\"));\n return;\n }\n console.log(pc.bold(\"recent sessions:\"));\n for (const s of sessions) {\n const dur = s.ended_at ? fmtDuration(s.ended_at - s.started_at) : pc.dim(\"active\");\n const ts = new Date(s.started_at).toLocaleString();\n console.log(` ${pc.dim(s.id.slice(0, 8))} ${s.tool.padEnd(11)} ${ts} ${dur}`);\n }\n}\n","import pc from \"picocolors\";\nimport { readConfig, writeConfig } from \"../config.js\";\nimport { postIngest } from \"../upload/client.js\";\n\nexport async function logoutCommand(): Promise<void> {\n const cfg = readConfig();\n cfg.apiKey = undefined;\n cfg.workspaceId = undefined;\n writeConfig(cfg);\n console.log(pc.green(\"✓\") + \" Cleared local credentials.\");\n}\n\ninterface LinkOpts {\n api?: string;\n}\n\nexport async function linkCommand(rawKey: string | undefined, opts: LinkOpts): Promise<void> {\n const key = (rawKey ?? (await promptHidden(\"Paste your AgentReel API key: \"))).trim();\n if (!key) {\n console.error(pc.red(\"✗ No key provided.\"));\n process.exit(1);\n }\n if (!key.startsWith(\"ar_live_\")) {\n console.error(pc.red(\"✗ Keys start with `ar_live_`. Did you paste the right value?\"));\n process.exit(1);\n }\n\n const cfg = readConfig();\n cfg.apiKey = key;\n if (opts.api) cfg.apiBaseUrl = opts.api;\n\n console.log(pc.dim(` validating against ${cfg.apiBaseUrl}…`));\n try {\n await postIngest(cfg, {});\n } catch (err) {\n console.error(pc.red(\"✗ Validation failed: \") + (err as Error).message);\n process.exit(1);\n }\n writeConfig(cfg);\n console.log(pc.green(\"✓\") + \" Linked.\");\n console.log(pc.dim(\" api: \") + cfg.apiBaseUrl);\n console.log(pc.dim(\" key: \") + key.slice(0, 12) + \"…\");\n}\n\nexport async function uninstallCommand(): Promise<void> {\n const { uninstallClaudeCodeHooks } = await import(\"../hooks/install.js\");\n const { backup } = uninstallClaudeCodeHooks();\n console.log(pc.green(\"✓\") + \" Removed AgentReel hooks from ~/.claude/settings.json\");\n if (backup) console.log(pc.dim(` (backup: ${backup})`));\n}\n\nconst CTRL_C = 0x03;\nconst BACKSPACE = 0x7f;\nconst BACKSPACE_ALT = 0x08;\nconst NEWLINE = 0x0a;\nconst CARRIAGE = 0x0d;\n\nfunction promptHidden(prompt: string): Promise<string> {\n return new Promise((resolve) => {\n process.stdout.write(prompt);\n let buf = \"\";\n const stdin = process.stdin;\n stdin.setRawMode?.(true);\n stdin.resume();\n stdin.setEncoding(\"utf8\");\n const onData = (chunk: string) => {\n for (const ch of chunk) {\n const code = ch.charCodeAt(0);\n if (code === NEWLINE || code === CARRIAGE) {\n stdin.setRawMode?.(false);\n stdin.pause();\n stdin.removeListener(\"data\", onData);\n process.stdout.write(\"\\n\");\n return resolve(buf);\n }\n if (code === CTRL_C) {\n stdin.setRawMode?.(false);\n process.exit(130);\n }\n if (code === BACKSPACE || code === BACKSPACE_ALT) {\n buf = buf.slice(0, -1);\n } else {\n buf += ch;\n }\n }\n };\n stdin.on(\"data\", onData);\n });\n}\n","import type { AgentConfig } from \"../config.js\";\n\nexport interface IngestSession {\n id: string;\n tool: string;\n started_at: number;\n ended_at?: number | null;\n cwd?: string | null;\n total_cost_cents?: number | null;\n total_tokens?: number | null;\n}\n\nexport interface IngestEvent {\n id: string;\n session_id: string;\n ts: number;\n type: string;\n tool: string;\n cwd?: string | null;\n payload: unknown;\n}\n\nexport interface IngestResponse {\n ok: boolean;\n sessions_written?: number;\n events_written?: number;\n error?: string;\n reason?: string;\n}\n\nexport async function postIngest(\n cfg: AgentConfig,\n body: { sessions?: IngestSession[]; events?: IngestEvent[] },\n): Promise<IngestResponse> {\n if (!cfg.apiKey) {\n throw new Error(\"Not linked. Run: agentreel link <key>\");\n }\n const url = `${cfg.apiBaseUrl.replace(/\\/$/, \"\")}/api/v1/sessions/ingest`;\n const res = await fetch(url, {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n authorization: `Bearer ${cfg.apiKey}`,\n },\n body: JSON.stringify(body),\n });\n let data: IngestResponse;\n try {\n data = (await res.json()) as IngestResponse;\n } catch {\n throw new Error(`Ingest returned ${res.status} with non-JSON body`);\n }\n if (!res.ok || !data.ok) {\n throw new Error(\n `Ingest failed: ${res.status} ${data.error ?? \"\"} ${data.reason ?? \"\"}`.trim(),\n );\n }\n return data;\n}\n","import pc from \"picocolors\";\nimport { existsSync } from \"node:fs\";\nimport { basename } from \"node:path\";\nimport { cursorHistoryDir } from \"../cursor/paths.js\";\nimport { startCursorWatcher } from \"../cursor/watcher.js\";\nimport { CursorSessionManager } from \"../cursor/session.js\";\nimport { ensureAgentreelDir } from \"../paths.js\";\nimport { getDb } from \"../db.js\";\n\nexport async function watchCommand(): Promise<void> {\n ensureAgentreelDir();\n // Touch DB so the schema exists.\n getDb();\n\n const dir = cursorHistoryDir();\n if (!existsSync(dir)) {\n console.error(pc.red(\"✗ Cursor history directory not found:\"));\n console.error(\" \" + dir);\n console.error();\n console.error(pc.dim(\"Open Cursor at least once, edit a file, then re-run.\"));\n console.error(pc.dim(\"(Or set AGENTREEL_CURSOR_HISTORY_DIR to a custom path.)\"));\n process.exit(1);\n }\n\n console.log(pc.bold(pc.cyan(\"AgentReel · Cursor watcher\\n\")));\n console.log(pc.dim(\" watching \") + dir);\n console.log(pc.dim(\" press Ctrl+C to stop\\n\"));\n\n const sessions = new CursorSessionManager();\n\n const watcher = startCursorWatcher(dir, async (snap) => {\n const { sessionId, isNew } = sessions.ingest(snap);\n const ts = new Date(snap.timestamp).toLocaleTimeString();\n const ws = snap.workspace ? basename(snap.workspace) : pc.dim(\"no-workspace\");\n const file = snap.filePath.split(\"/\").slice(-2).join(\"/\");\n const sourceLabel =\n snap.source === \"cursor-ai\"\n ? pc.magenta(\"ai\")\n : snap.source === \"cursor-manual\"\n ? pc.cyan(\"man\")\n : pc.dim(\"?\");\n const stats = snap.binary\n ? pc.dim(\"binary\")\n : `${pc.green(\"+\" + snap.added)} ${pc.red(\"-\" + snap.removed)}`;\n if (isNew) {\n console.log(\n `${pc.dim(ts)} ${pc.yellow(\"session\")} ${pc.dim(sessionId)} ${ws}`,\n );\n }\n console.log(`${pc.dim(ts)} edit ${sourceLabel} ${file.padEnd(36)} ${stats}`);\n });\n\n // Graceful shutdown — flush sessions, close watcher.\n const shutdown = async () => {\n console.log(pc.dim(\"\\n closing sessions…\"));\n sessions.closeAll();\n await watcher.close();\n process.exit(0);\n };\n process.on(\"SIGINT\", shutdown);\n process.on(\"SIGTERM\", shutdown);\n}\n","import { homedir, platform } from \"node:os\";\nimport { join } from \"node:path\";\n\n// Cursor is a VS Code fork; its history layout matches Code's:\n// <userData>/User/History/<hashId>/{entries.json, <id>.<ext>, ...}\nexport function defaultCursorHistoryDir(): string {\n const home = homedir();\n switch (platform()) {\n case \"darwin\":\n return join(home, \"Library\", \"Application Support\", \"Cursor\", \"User\", \"History\");\n case \"win32\": {\n const appData = process.env.APPDATA ?? join(home, \"AppData\", \"Roaming\");\n return join(appData, \"Cursor\", \"User\", \"History\");\n }\n default:\n return join(home, \".config\", \"Cursor\", \"User\", \"History\");\n }\n}\n\nexport function cursorHistoryDir(): string {\n return process.env.AGENTREEL_CURSOR_HISTORY_DIR ?? defaultCursorHistoryDir();\n}\n","import chokidar, { type FSWatcher } from \"chokidar\";\nimport { readFile } from \"node:fs/promises\";\nimport { existsSync } from \"node:fs\";\nimport { basename, dirname, join } from \"node:path\";\nimport {\n findWorkspaceRoot,\n isProbablyBinary,\n readEntries,\n resourceToPath,\n type HistoryEntry,\n} from \"./entries.js\";\nimport { computeDiff } from \"./diff.js\";\nimport { isIgnored } from \"../redact/ignore.js\";\nimport { scrubString } from \"../redact/scrubber.js\";\n\nexport interface SnapshotEvent {\n filePath: string;\n workspace: string | null;\n source: \"cursor-ai\" | \"cursor-manual\" | \"unknown\";\n timestamp: number;\n patch: string;\n added: number;\n removed: number;\n binary: boolean;\n}\n\nexport type SnapshotHandler = (e: SnapshotEvent) => void | Promise<void>;\n\nexport interface WatcherOptions {\n /** Discard the very first entry for any tracked file (Cursor's \"first observation\" snapshot). Default true. */\n skipFirstObservation?: boolean;\n}\n\nexport function startCursorWatcher(\n historyDir: string,\n onSnapshot: SnapshotHandler,\n opts: WatcherOptions = {},\n): FSWatcher {\n const skipFirst = opts.skipFirstObservation ?? true;\n\n const watcher = chokidar.watch(historyDir, {\n ignoreInitial: true,\n depth: 2,\n persistent: true,\n awaitWriteFinish: { stabilityThreshold: 120, pollInterval: 40 },\n });\n\n watcher.on(\"add\", async (path) => {\n try {\n const folder = dirname(path);\n const file = basename(path);\n // The folder name is the resource hash; siblings include entries.json\n // and one snapshot file per saved version.\n if (file === \"entries.json\") return;\n // Wait briefly for entries.json to mention this file — Cursor writes\n // the snapshot before updating the manifest in some versions.\n const ctx = await waitForEntry(folder, file, 2_000);\n if (!ctx) return;\n await processSnapshot(folder, ctx, skipFirst, onSnapshot);\n } catch (err) {\n // The watcher must never crash the host process.\n // eslint-disable-next-line no-console\n console.error(\"[agentreel] cursor watcher: error processing\", path, err);\n }\n });\n\n return watcher;\n}\n\ninterface SnapshotContext {\n resource: string;\n current: HistoryEntry;\n previous: HistoryEntry | null;\n currentIdx: number;\n}\n\nasync function waitForEntry(\n folder: string,\n fileId: string,\n totalMs: number,\n): Promise<SnapshotContext | null> {\n const start = Date.now();\n while (Date.now() - start < totalMs) {\n const file = await readEntries(folder);\n if (file) {\n const idx = file.entries.findIndex((e) => e.id === fileId);\n if (idx >= 0) {\n const current = file.entries[idx];\n if (!current) return null;\n const previous = idx > 0 ? (file.entries[idx - 1] ?? null) : null;\n return { resource: file.resource, current, previous, currentIdx: idx };\n }\n }\n await sleep(150);\n }\n return null;\n}\n\nasync function processSnapshot(\n folder: string,\n ctx: SnapshotContext,\n skipFirst: boolean,\n onSnapshot: SnapshotHandler,\n): Promise<void> {\n const filePath = resourceToPath(ctx.resource);\n if (!filePath) return;\n\n // Skip files inside common dependency / build dirs — high noise, low value.\n if (NOISY_PATH.test(filePath)) return;\n\n // Honor per-workspace .agentreelignore (gitignore syntax).\n const workspace = findWorkspaceRoot(filePath);\n if (isIgnored(workspace, filePath)) return;\n\n if (!ctx.previous) {\n // First time Cursor has seen this file — no diff to compute.\n if (skipFirst) return;\n }\n\n const newSnapshot = join(folder, ctx.current.id);\n let before = \"\";\n let after = \"\";\n if (ctx.previous) {\n const prevPath = join(folder, ctx.previous.id);\n if (existsSync(prevPath)) {\n before = await safeRead(prevPath);\n }\n }\n if (existsSync(newSnapshot)) {\n after = await safeRead(newSnapshot);\n }\n\n // No real change — chokidar can fire spurious \"add\" events on some\n // filesystems. Skip silently.\n if (before === after) return;\n\n const binary = isProbablyBinary(filePath);\n let patch = \"\";\n let added = 0;\n let removed = 0;\n if (!binary) {\n // Scrub BEFORE diffing so secrets never enter the patch text. We diff\n // the redacted versions instead — the dashboard will still show the\n // shape of the change, just with [REDACTED:*] in place of values.\n const beforeSafe = scrubString(before);\n const afterSafe = scrubString(after);\n const result = computeDiff(beforeSafe, afterSafe);\n patch = result.patch;\n added = result.added;\n removed = result.removed;\n }\n\n const source = classifySource(ctx.current.source);\n onSnapshot({\n filePath,\n workspace,\n source,\n timestamp: ctx.current.timestamp ?? Date.now(),\n patch,\n added,\n removed,\n binary,\n });\n}\n\nfunction classifySource(raw?: string): SnapshotEvent[\"source\"] {\n if (!raw) return \"cursor-manual\";\n const s = raw.toLowerCase();\n if (s.includes(\"composer\") || s.includes(\"ai\") || s.includes(\"chat\")) return \"cursor-ai\";\n return \"cursor-manual\";\n}\n\nasync function safeRead(path: string): Promise<string> {\n try {\n return await readFile(path, \"utf8\");\n } catch {\n return \"\";\n }\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((r) => setTimeout(r, ms));\n}\n\nconst NOISY_PATH =\n /[\\\\/](node_modules|\\.next|\\.turbo|dist|build|\\.git|coverage|\\.cache|\\.venv|venv|target|out)[\\\\/]/;\n","import { readFile } from \"node:fs/promises\";\nimport { existsSync, statSync } from \"node:fs\";\nimport { join, dirname, resolve, sep } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nexport interface HistoryEntry {\n id: string; // filename of the snapshot, e.g. \"rEMc.ts\"\n timestamp: number; // ms epoch\n source?: string; // e.g. \"Cursor.Composer\" for AI edits, \"\" for manual\n}\n\nexport interface HistoryFile {\n version: number;\n resource: string; // file:///...\n entries: HistoryEntry[];\n}\n\nexport async function readEntries(historyFolder: string): Promise<HistoryFile | null> {\n const path = join(historyFolder, \"entries.json\");\n if (!existsSync(path)) return null;\n try {\n const raw = await readFile(path, \"utf8\");\n return JSON.parse(raw) as HistoryFile;\n } catch {\n return null;\n }\n}\n\nexport function resourceToPath(resource: string): string | null {\n if (!resource.startsWith(\"file://\")) return null;\n try {\n return fileURLToPath(resource);\n } catch {\n return null;\n }\n}\n\n// Walk up from `path` looking for a directory that contains `.git`. That\n// directory is the workspace root for the purposes of grouping events.\nexport function findWorkspaceRoot(path: string): string | null {\n let dir = dirname(resolve(path));\n while (dir && dir !== sep) {\n const git = join(dir, \".git\");\n try {\n if (existsSync(git)) return dir;\n } catch {\n // ignore\n }\n const parent = dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n return null;\n}\n\nexport function isProbablyBinary(path: string): boolean {\n try {\n const s = statSync(path);\n if (s.size > 1024 * 1024) return true; // >1MB — skip diff\n } catch {\n return false;\n }\n // Lightweight extension allowlist for v1. Anything else, treat as binary\n // and skip diffing (we still record the event, just without a patch).\n const text =\n /\\.(ts|tsx|js|jsx|mjs|cjs|json|jsonc|md|mdx|css|scss|html|xml|yaml|yml|toml|sh|bash|zsh|fish|py|rb|go|rs|java|kt|swift|c|cc|cpp|h|hpp|cs|php|sql|prisma|graphql|gql|env|gitignore|dockerfile|tf|hcl|lua|vue|svelte|astro|txt|csv|tsv|log|conf|ini)$/i;\n return !text.test(path);\n}\n","import { diffLines } from \"diff\";\nimport DiffMatchPatch from \"diff-match-patch\";\n\nexport interface DiffResult {\n /** diff-match-patch wire format — compact, reversible. Stored on the event. */\n patch: string;\n /** Line-level adds/removes — surfaced in the dashboard list. */\n added: number;\n removed: number;\n}\n\nconst dmp = new DiffMatchPatch.diff_match_patch();\n// Allow expensive cleanup on small diffs; cap at 1MB to avoid pathological inputs.\ndmp.Diff_Timeout = 1.0;\n\nexport function computeDiff(before: string, after: string): DiffResult {\n // Patch — compact DMP format, much smaller than unified diff for small edits.\n const patches = dmp.patch_make(before, after);\n const patch = dmp.patch_toText(patches);\n\n // Line-level stats (cheap; users see these in the timeline list).\n let added = 0;\n let removed = 0;\n for (const part of diffLines(before, after)) {\n const n =\n part.count ??\n (part.value.match(/\\n/g)?.length ?? (part.value.length > 0 ? 1 : 0));\n if (part.added) added += n;\n else if (part.removed) removed += n;\n }\n\n return { patch, added, removed };\n}\n","import { existsSync, readFileSync, statSync } from \"node:fs\";\nimport { join, relative, sep } from \"node:path\";\nimport ignore, { type Ignore } from \"ignore\";\n\nconst FILENAME = \".agentreelignore\";\nconst TTL_MS = 5_000; // re-read the file at most every 5 seconds per workspace\n\ninterface CacheEntry {\n matcher: Ignore | null;\n loadedAt: number;\n fileMtime: number;\n}\n\nconst cache = new Map<string, CacheEntry>();\n\nfunction loadFor(workspace: string): Ignore | null {\n const path = join(workspace, FILENAME);\n if (!existsSync(path)) return null;\n try {\n const raw = readFileSync(path, \"utf8\");\n return ignore({ allowRelativePaths: true }).add(raw);\n } catch {\n return null;\n }\n}\n\nfunction getCached(workspace: string): Ignore | null {\n const entry = cache.get(workspace);\n const path = join(workspace, FILENAME);\n let mtime = 0;\n try {\n mtime = existsSync(path) ? statSync(path).mtimeMs : 0;\n } catch {\n mtime = 0;\n }\n const now = Date.now();\n if (entry && now - entry.loadedAt < TTL_MS && entry.fileMtime === mtime) {\n return entry.matcher;\n }\n const matcher = loadFor(workspace);\n cache.set(workspace, { matcher, loadedAt: now, fileMtime: mtime });\n return matcher;\n}\n\n/**\n * Returns true if `filePath` should be skipped because it matches a rule\n * in the workspace's `.agentreelignore`. Returns false when the workspace\n * has no ignore file or the path doesn't match.\n */\nexport function isIgnored(workspace: string | null | undefined, filePath: string): boolean {\n if (!workspace) return false;\n const matcher = getCached(workspace);\n if (!matcher) return false;\n const rel = relative(workspace, filePath);\n // ignore can't reason about paths that escape the workspace.\n if (!rel || rel.startsWith(\"..\") || rel.startsWith(sep)) return false;\n // gitignore syntax expects forward slashes.\n return matcher.ignores(rel.split(sep).join(\"/\"));\n}\n","// Order matters. Multiline / specific patterns first, generic last.\n\nexport interface RedactionRule {\n name: string;\n re: RegExp;\n replacement: string | ((match: string) => string);\n}\n\nexport const PATTERNS: RedactionRule[] = [\n // PEM-encoded private keys (multiline, must run early)\n {\n name: \"pem-private-key\",\n re: /-----BEGIN [A-Z0-9 ]*PRIVATE KEY-----[\\s\\S]+?-----END [A-Z0-9 ]*PRIVATE KEY-----/g,\n replacement: \"[REDACTED:private-key]\",\n },\n\n // JWT (header.payload.signature) — eyJ-prefixed base64url segments\n {\n name: \"jwt\",\n re: /\\beyJ[A-Za-z0-9_-]{8,}\\.eyJ[A-Za-z0-9_-]{8,}\\.[A-Za-z0-9_-]{8,}\\b/g,\n replacement: \"[REDACTED:jwt]\",\n },\n\n // GitHub fine-grained PATs (84 chars after prefix is the official format)\n {\n name: \"github-fine-grained-pat\",\n re: /\\bgithub_pat_[A-Za-z0-9_]{82,}\\b/g,\n replacement: \"[REDACTED:gh-fine-pat]\",\n },\n // GitHub OAuth, PAT, app, server, refresh tokens\n {\n name: \"github-token\",\n re: /\\bgh[pousr]_[A-Za-z0-9]{36,255}\\b/g,\n replacement: \"[REDACTED:gh-token]\",\n },\n\n // Anthropic\n {\n name: \"anthropic-key\",\n re: /\\bsk-ant-(?:api\\d{2}-)?[A-Za-z0-9_-]{40,}\\b/g,\n replacement: \"[REDACTED:anthropic-key]\",\n },\n\n // OpenAI (sk-proj-..., sk-svcacct-..., legacy sk-...)\n {\n name: \"openai-key\",\n re: /\\bsk-(?:proj-|svcacct-|admin-)?[A-Za-z0-9_-]{20,}\\b/g,\n replacement: \"[REDACTED:openai-key]\",\n },\n\n // Stripe\n {\n name: \"stripe-secret\",\n re: /\\bsk_(?:test|live)_[A-Za-z0-9]{16,}\\b/g,\n replacement: \"[REDACTED:stripe-secret]\",\n },\n {\n name: \"stripe-restricted\",\n re: /\\brk_(?:test|live)_[A-Za-z0-9]{16,}\\b/g,\n replacement: \"[REDACTED:stripe-restricted]\",\n },\n {\n name: \"stripe-publishable\",\n re: /\\bpk_(?:test|live)_[A-Za-z0-9]{16,}\\b/g,\n replacement: \"[REDACTED:stripe-publishable]\",\n },\n {\n name: \"stripe-webhook\",\n re: /\\bwhsec_[A-Za-z0-9]{32,}\\b/g,\n replacement: \"[REDACTED:stripe-webhook]\",\n },\n\n // AWS access key IDs (and STS / temporary forms)\n {\n name: \"aws-access-key-id\",\n re: /\\b(?:AKIA|ASIA|AGPA|AROA|AIDA|ANPA|ANVA|AIPA)[0-9A-Z]{16}\\b/g,\n replacement: \"[REDACTED:aws-key-id]\",\n },\n\n // Slack tokens\n {\n name: \"slack-token\",\n re: /\\bxox[baprs]-[A-Za-z0-9-]{10,}\\b/g,\n replacement: \"[REDACTED:slack-token]\",\n },\n\n // Google API keys\n {\n name: \"google-api-key\",\n re: /\\bAIza[A-Za-z0-9_-]{35}\\b/g,\n replacement: \"[REDACTED:google-api-key]\",\n },\n\n // npm tokens\n {\n name: \"npm-token\",\n re: /\\bnpm_[A-Za-z0-9]{36}\\b/g,\n replacement: \"[REDACTED:npm-token]\",\n },\n\n // dotenv-style KEY=VALUE on its own line, where the KEY name looks sensitive.\n // This is a fallback for arbitrary secrets that don't match a specific\n // provider pattern. Captures the key, replaces the value.\n {\n name: \"dotenv-secret\",\n re: /^(\\s*(?:export\\s+)?[A-Z][A-Z0-9_]*?(?:KEY|TOKEN|SECRET|PASSWORD|PASSWD|PWD|API|AUTH|CREDENTIAL|PRIVATE|SESSION|COOKIE|BEARER|DSN)[A-Z0-9_]*\\s*=\\s*)(['\"]?)([^\\n'\"]{4,})\\2/gm,\n replacement: (m: string) => {\n // m is the full match; we keep the \"KEY=\" and quote, replace the value.\n // Re-run a small regex against m to preserve the prefix.\n const inner =\n /^(\\s*(?:export\\s+)?[A-Z][A-Z0-9_]*\\s*=\\s*)(['\"]?)([^\\n'\"]{4,})\\2/.exec(m);\n if (!inner) return \"[REDACTED:dotenv-secret]\";\n const [, prefix, quote] = inner;\n return `${prefix ?? \"\"}${quote ?? \"\"}[REDACTED:dotenv-secret]${quote ?? \"\"}`;\n },\n },\n\n // Email addresses\n {\n name: \"email\",\n re: /\\b[A-Za-z0-9._%+\\-]+@[A-Za-z0-9.\\-]+\\.[A-Za-z]{2,}\\b/g,\n replacement: \"[REDACTED:email]\",\n },\n\n // IPv4 — validates each octet is 0-255 to cut version-string false positives.\n // Skips three benign forms below in the post-filter.\n {\n name: \"ipv4\",\n re: /\\b(?:(?:25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]?\\d)\\.){3}(?:25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]?\\d)\\b/g,\n replacement: (m: string) => {\n if (m === \"0.0.0.0\" || m === \"127.0.0.1\" || m === \"255.255.255.255\") return m;\n return \"[REDACTED:ip]\";\n },\n },\n];\n\n// Sensitive key-name shapes used by the recursive object walker. If an\n// object's key name matches AND the value is a non-trivial string, we redact\n// the whole value regardless of provider-pattern match.\n// Names that almost-always carry a secret value. Avoid generic words like\n// `session` (matches `session_id`) or bare `token` (matches `csrf_token`\n// inputs that are themselves not sensitive in our context).\nexport const SENSITIVE_KEY_RE =\n /(?:^|[_\\-.])(?:api[_-]?key|access[_-]?token|secret|password|passwd|pwd|authorization|bearer|credential|private[_-]?key|client[_-]?secret|webhook[_-]?secret|service[_-]?account|refresh[_-]?token)(?:$|[_\\-.])/i;\n","import { PATTERNS, SENSITIVE_KEY_RE } from \"./patterns.js\";\n\n/** Run all redaction patterns against a string. Idempotent. */\nexport function scrubString(input: string): string {\n if (!input) return input;\n let s = input;\n for (const rule of PATTERNS) {\n if (typeof rule.replacement === \"function\") {\n s = s.replace(rule.re, rule.replacement);\n } else {\n s = s.replace(rule.re, rule.replacement);\n }\n }\n return s;\n}\n\n/**\n * Recursively redact a JSON-shaped value. Strings get pattern-scrubbed;\n * object properties whose KEY name looks sensitive (`apiKey`, `password`,\n * etc.) have their entire string value replaced — that catches arbitrary\n * secrets that don't match any provider pattern.\n *\n * Cycle-safe via a WeakSet seen.\n */\nexport function scrubAny<T>(value: T, seen: WeakSet<object> = new WeakSet()): T {\n if (value == null) return value;\n if (typeof value === \"string\") return scrubString(value) as T;\n if (typeof value !== \"object\") return value;\n if (seen.has(value as object)) return value;\n seen.add(value as object);\n\n if (Array.isArray(value)) {\n return value.map((v) => scrubAny(v, seen)) as T;\n }\n\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(value as Record<string, unknown>)) {\n if (SENSITIVE_KEY_RE.test(k) && typeof v === \"string\" && v.length >= 4) {\n out[k] = \"[REDACTED:by-key-name]\";\n } else {\n out[k] = scrubAny(v, seen);\n }\n }\n return out as T;\n}\n","import { nanoid } from \"nanoid\";\nimport { insertEvent, upsertSession } from \"../db.js\";\nimport type { AgentEvent } from \"@agentreel/shared-types\";\nimport type { SnapshotEvent } from \"./watcher.js\";\n\nconst IDLE_MS = 5 * 60 * 1000; // 5 minute gap = new session\nconst GLOBAL_KEY = \"__global__\";\n\ninterface OpenSession {\n id: string;\n startedAt: number;\n lastTs: number;\n cwd: string;\n}\n\nexport class CursorSessionManager {\n private open = new Map<string, OpenSession>();\n\n ingest(snapshot: SnapshotEvent): { sessionId: string; isNew: boolean } {\n const key = snapshot.workspace ?? GLOBAL_KEY;\n const ts = snapshot.timestamp;\n const cwd = snapshot.workspace ?? \"\";\n\n let session = this.open.get(key);\n let isNew = false;\n if (!session || ts - session.lastTs > IDLE_MS) {\n // Close out the previous session for this key, if any.\n if (session) this.closeSession(session, session.lastTs);\n session = {\n id: `cur_${nanoid(10)}`,\n startedAt: ts,\n lastTs: ts,\n cwd,\n };\n this.open.set(key, session);\n isNew = true;\n upsertSession({\n id: session.id,\n tool: \"cursor\",\n startedAt: ts,\n cwd,\n });\n } else {\n session.lastTs = ts;\n }\n\n const event: AgentEvent = {\n id: nanoid(),\n sessionId: session.id,\n tool: \"cursor\",\n type: \"tool_use_post\",\n ts,\n cwd,\n payload: {\n tool_name: \"Edit\",\n file_path: snapshot.filePath,\n added: snapshot.added,\n removed: snapshot.removed,\n binary: snapshot.binary,\n source: snapshot.source,\n patch: snapshot.patch,\n },\n };\n insertEvent(event);\n return { sessionId: session.id, isNew };\n }\n\n /** Stamp ended_at on every open session — call on shutdown. */\n closeAll(): void {\n const now = Date.now();\n for (const s of this.open.values()) this.closeSession(s, Math.max(s.lastTs, now));\n this.open.clear();\n }\n\n private closeSession(s: OpenSession, endedAt: number) {\n upsertSession({\n id: s.id,\n tool: \"cursor\",\n startedAt: s.startedAt,\n endedAt,\n cwd: s.cwd,\n });\n }\n}\n","import pc from \"picocolors\";\nimport { readConfig } from \"../config.js\";\nimport { postIngest } from \"../upload/client.js\";\nimport { markUploaded, takePendingBatch } from \"../upload/queue.js\";\n\nexport async function pushCommand(): Promise<void> {\n const cfg = readConfig();\n if (!cfg.apiKey) {\n console.error(pc.red(\"✗ Not linked. Run \") + pc.cyan(\"agentreel link <api-key>\"));\n process.exit(1);\n }\n\n let totalSessions = 0;\n let totalEvents = 0;\n\n while (true) {\n const batch = takePendingBatch(500);\n if (batch.events.length === 0) break;\n\n console.log(\n pc.dim(` uploading ${batch.events.length} events across ${batch.sessions.length} sessions…`),\n );\n const res = await postIngest(cfg, { sessions: batch.sessions, events: batch.events });\n markUploaded(batch.eventIds);\n totalSessions += res.sessions_written ?? 0;\n totalEvents += res.events_written ?? 0;\n }\n\n if (totalEvents === 0) {\n console.log(pc.green(\"✓\") + \" queue is empty — nothing to upload.\");\n return;\n }\n console.log(\n pc.green(\"✓\") +\n ` uploaded ${totalEvents} events · ${totalSessions} session rows touched`,\n );\n}\n","import { getDb } from \"../db.js\";\nimport type { IngestEvent, IngestSession } from \"./client.js\";\n\ninterface DBSessionRow {\n id: string;\n tool: string;\n started_at: number;\n ended_at: number | null;\n cwd: string | null;\n total_cost_cents: number | null;\n total_tokens: number | null;\n}\n\ninterface DBEventRow {\n id: string;\n session_id: string;\n tool: string;\n type: string;\n ts: number;\n cwd: string | null;\n payload: string;\n}\n\nexport interface PendingBatch {\n sessions: IngestSession[];\n events: IngestEvent[];\n eventIds: string[]; // for the post-write mark\n}\n\nexport function takePendingBatch(maxEvents = 500): PendingBatch {\n const db = getDb();\n const eventRows = db\n .prepare(\n `SELECT id, session_id, tool, type, ts, cwd, payload\n FROM events\n WHERE uploaded_at IS NULL\n ORDER BY ts ASC\n LIMIT ?`,\n )\n .all(maxEvents) as DBEventRow[];\n\n if (eventRows.length === 0) return { sessions: [], events: [], eventIds: [] };\n\n const sessionIds = [...new Set(eventRows.map((e) => e.session_id))];\n const placeholders = sessionIds.map(() => \"?\").join(\",\");\n const sessionRows = db\n .prepare(\n `SELECT id, tool, started_at, ended_at, cwd, total_cost_cents, total_tokens\n FROM sessions WHERE id IN (${placeholders})`,\n )\n .all(...sessionIds) as DBSessionRow[];\n\n const sessions: IngestSession[] = sessionRows.map((s) => ({\n id: s.id,\n tool: s.tool,\n started_at: s.started_at,\n ended_at: s.ended_at,\n cwd: s.cwd,\n total_cost_cents: s.total_cost_cents,\n total_tokens: s.total_tokens,\n }));\n\n const events: IngestEvent[] = eventRows.map((e) => ({\n id: e.id,\n session_id: e.session_id,\n ts: e.ts,\n type: e.type,\n tool: e.tool,\n cwd: e.cwd,\n payload: safeParse(e.payload),\n }));\n\n return { sessions, events, eventIds: eventRows.map((r) => r.id) };\n}\n\nexport function markUploaded(eventIds: string[]): void {\n if (eventIds.length === 0) return;\n const db = getDb();\n const now = Date.now();\n const stmt = db.prepare(`UPDATE events SET uploaded_at = ? WHERE id = ?`);\n const tx = db.transaction((ids: string[]) => {\n for (const id of ids) stmt.run(now, id);\n });\n tx(eventIds);\n}\n\nfunction safeParse(s: string): unknown {\n try {\n return JSON.parse(s);\n } catch {\n return s;\n }\n}\n","import { nanoid } from \"nanoid\";\nimport { appendFileSync } from \"node:fs\";\nimport type { AgentEvent, ClaudeCodeHookInput, EventType } from \"@agentreel/shared-types\";\nimport { insertEvent, upsertSession } from \"../db.js\";\nimport { LOG_PATH, ensureAgentreelDir } from \"../paths.js\";\nimport { scrubAny } from \"../redact/scrubber.js\";\n\nconst HOOK_EVENT_TO_TYPE: Record<string, EventType> = {\n SessionStart: \"session_start\",\n SessionEnd: \"session_end\",\n UserPromptSubmit: \"user_prompt_submit\",\n PreToolUse: \"tool_use_pre\",\n PostToolUse: \"tool_use_post\",\n Notification: \"notification\",\n Stop: \"stop\",\n SubagentStop: \"subagent_stop\",\n PreCompact: \"pre_compact\",\n};\n\nasync function readStdin(): Promise<string> {\n if (process.stdin.isTTY) return \"\";\n const chunks: Buffer[] = [];\n for await (const chunk of process.stdin) {\n chunks.push(chunk as Buffer);\n }\n return Buffer.concat(chunks).toString(\"utf8\");\n}\n\nfunction logError(err: unknown): void {\n try {\n ensureAgentreelDir();\n const line = `[${new Date().toISOString()}] hook error: ${\n err instanceof Error ? err.stack ?? err.message : String(err)\n }\\n`;\n appendFileSync(LOG_PATH, line);\n } catch {\n // swallow — never block Claude Code\n }\n}\n\nexport async function runHook(eventArg?: string): Promise<void> {\n // Hooks must never block or fail Claude Code. Wrap everything.\n try {\n const raw = await readStdin();\n if (!raw.trim()) return;\n const input = JSON.parse(raw) as ClaudeCodeHookInput;\n\n const hookEventName = input.hook_event_name ?? eventArg ?? \"Unknown\";\n const type: EventType = HOOK_EVENT_TO_TYPE[hookEventName] ?? \"unknown\";\n const ts = Date.now();\n const sessionId = input.session_id ?? \"unknown-session\";\n\n if (type === \"session_start\") {\n upsertSession({\n id: sessionId,\n tool: \"claude-code\",\n startedAt: ts,\n cwd: input.cwd,\n });\n } else if (type === \"session_end\") {\n upsertSession({\n id: sessionId,\n tool: \"claude-code\",\n startedAt: ts,\n endedAt: ts,\n cwd: input.cwd,\n });\n } else {\n // Make sure the session row exists so events have a parent.\n upsertSession({\n id: sessionId,\n tool: \"claude-code\",\n startedAt: ts,\n cwd: input.cwd,\n });\n }\n\n // Scrub the entire payload before persistence — never let raw secrets\n // touch SQLite, even briefly. scrubAny handles strings recursively and\n // also redacts values whose KEY name looks sensitive.\n const safePayload = scrubAny(input);\n const event: AgentEvent = {\n id: nanoid(),\n sessionId,\n tool: \"claude-code\",\n type,\n ts,\n cwd: input.cwd,\n payload: safePayload,\n };\n insertEvent(event);\n } catch (err) {\n logError(err);\n }\n}\n"],"mappings":";;;;;;;;;;;;AAAA,SAAS,eAAe;AACxB,SAAS,YAAY;AACrB,SAAS,iBAAiB;AAYnB,SAAS,qBAA2B;AACzC,YAAU,eAAe,EAAE,WAAW,KAAK,CAAC;AAC5C,YAAU,WAAW,EAAE,WAAW,KAAK,CAAC;AAC1C;AAjBA,IAIa,MACA,eACA,SACA,aACA,WACA,UAEA,YACA;AAZb;AAAA;AAAA;AAIO,IAAM,OAAO,QAAQ;AACrB,IAAM,gBAAgB,KAAK,MAAM,YAAY;AAC7C,IAAM,UAAU,KAAK,eAAe,aAAa;AACjD,IAAM,cAAc,KAAK,eAAe,aAAa;AACrD,IAAM,YAAY,KAAK,eAAe,OAAO;AAC7C,IAAM,WAAW,KAAK,eAAe,WAAW;AAEhD,IAAM,aAAa,KAAK,MAAM,SAAS;AACvC,IAAM,uBAAuB,KAAK,YAAY,eAAe;AAAA;AAAA;;;ACZpE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,SAAS,cAAc,eAAe,YAAY,cAAc,aAAAA,kBAAiB;AACjF,SAAS,eAAe;AAkCxB,SAAS,eAA+B;AACtC,MAAI,CAAC,WAAW,oBAAoB,EAAG,QAAO,CAAC;AAC/C,QAAM,MAAM,aAAa,sBAAsB,MAAM;AACrD,MAAI,CAAC,IAAI,KAAK,EAAG,QAAO,CAAC;AACzB,SAAO,KAAK,MAAM,GAAG;AACvB;AAEA,SAAS,iBAAgC;AACvC,MAAI,CAAC,WAAW,oBAAoB,EAAG,QAAO;AAC9C,QAAM,SAAQ,oBAAI,KAAK,GAAE,YAAY,EAAE,QAAQ,SAAS,GAAG;AAC3D,QAAM,SAAS,GAAG,oBAAoB,qBAAqB,KAAK;AAChE,eAAa,sBAAsB,MAAM;AACzC,SAAO;AACT;AAEA,SAAS,eAAe,OAAe,mBAAsC;AAK3E,SAAO;AAAA,IACL,MAAM;AAAA,IACN,SAAS,GAAG,iBAAiB,SAAS,KAAK;AAAA,IAC3C,SAAS;AAAA,EACX;AACF;AAEO,SAAS,UAAU,GAAmB;AAC3C,SAAO,aAAa,KAAK,CAAC,IAAI,IAAI,EAAE,QAAQ,MAAM,KAAK,CAAC,MAAM;AAChE;AAQO,SAAS,uBAAuB,mBAA0C;AAC/E,EAAAA,WAAU,YAAY,EAAE,WAAW,KAAK,CAAC;AACzC,EAAAA,WAAU,QAAQ,oBAAoB,GAAG,EAAE,WAAW,KAAK,CAAC;AAC5D,QAAM,SAAS,eAAe;AAE9B,QAAM,WAAW,aAAa;AAC9B,WAAS,UAAU,CAAC;AAEpB,aAAW,SAAS,aAAa;AAC/B,UAAM,SAAS,SAAS,MAAM,KAAK,KAAK,CAAC;AAEzC,UAAM,WAAW,OAAO,OAAO,CAAC,MAAM,EAAE,gBAAgB,WAAW;AACnE,aAAS,KAAK;AAAA,MACZ,SAAS;AAAA,MACT,aAAa;AAAA,MACb,OAAO,CAAC,eAAe,OAAO,iBAAiB,CAAC;AAAA,IAClD,CAAC;AACD,aAAS,MAAM,KAAK,IAAI;AAAA,EAC1B;AAEA,gBAAc,sBAAsB,KAAK,UAAU,UAAU,MAAM,CAAC,IAAI,MAAM,MAAM;AACpF,SAAO,EAAE,QAAQ,iBAAiB,CAAC,GAAG,WAAW,GAAG,kBAAkB;AACxE;AAEO,SAAS,2BAAsD;AACpE,MAAI,CAAC,WAAW,oBAAoB,EAAG,QAAO,EAAE,QAAQ,KAAK;AAC7D,QAAM,SAAS,eAAe;AAC9B,QAAM,WAAW,aAAa;AAC9B,MAAI,SAAS,OAAO;AAClB,eAAW,SAAS,OAAO,KAAK,SAAS,KAAK,GAAG;AAC/C,YAAM,SAAS,SAAS,MAAM,KAAK,KAAK,CAAC;AACzC,YAAM,YAAY,OAAO,OAAO,CAAC,MAAM,EAAE,gBAAgB,WAAW;AACpE,UAAI,UAAU,WAAW,EAAG,QAAO,SAAS,MAAM,KAAK;AAAA,UAClD,UAAS,MAAM,KAAK,IAAI;AAAA,IAC/B;AACA,QAAI,OAAO,KAAK,SAAS,KAAK,EAAE,WAAW,EAAG,QAAO,SAAS;AAAA,EAChE;AACA,gBAAc,sBAAsB,KAAK,UAAU,UAAU,MAAM,CAAC,IAAI,MAAM,MAAM;AACpF,SAAO,EAAE,OAAO;AAClB;AA/GA,IAIM,aAEA;AANN;AAAA;AAAA;AAEA;AAEA,IAAM,cAAc;AAEpB,IAAM,cAAc;AAAA,MAClB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA;AAAA;;;AChBA,SAAS,eAAe;;;ACIxB;AAJA,SAAS,oBAAoB;AAC7B,SAAS,qBAAqB;AAC9B,SAAS,WAAW;AACpB,OAAO,QAAQ;;;ACFf;AADA,SAAS,gBAAAC,eAAc,iBAAAC,gBAAe,cAAAC,aAAY,iBAAiB;AAYnE,IAAM,UAAuB;AAAA,EAC3B,YAAY;AAAA,EACZ,eAAe;AACjB;AAEO,SAAS,aAA0B;AACxC,MAAI,CAACA,YAAW,WAAW,EAAG,QAAO,EAAE,GAAG,QAAQ;AAClD,MAAI;AACF,UAAM,MAAMF,cAAa,aAAa,MAAM;AAC5C,WAAO,EAAE,GAAG,SAAS,GAAG,KAAK,MAAM,GAAG,EAAE;AAAA,EAC1C,QAAQ;AACN,WAAO,EAAE,GAAG,QAAQ;AAAA,EACtB;AACF;AAEO,SAAS,YAAY,KAAwB;AAClD,qBAAmB;AAInB,EAAAC,eAAc,aAAa,KAAK,UAAU,KAAK,MAAM,CAAC,IAAI,MAAM;AAAA,IAC9D,UAAU;AAAA,IACV,MAAM;AAAA,EACR,CAAC;AACD,MAAI;AACF,cAAU,aAAa,GAAK;AAAA,EAC9B,QAAQ;AAAA,EAER;AACF;;;ACvCA;AAFA,OAAO,cAAc;AAKrB,IAAI,MAAiB;AAEd,SAAS,QAAY;AAC1B,MAAI,IAAK,QAAO;AAChB,qBAAmB;AACnB,QAAM,KAAK,IAAI,SAAS,OAAO;AAC/B,KAAG,OAAO,oBAAoB;AAC9B,KAAG,OAAO,sBAAsB;AAChC,UAAQ,EAAE;AACV,QAAM;AACN,SAAO;AACT;AAEA,SAAS,QAAQ,IAAc;AAC7B,KAAG,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAsBP;AACH;AAEO,SAAS,cAAc,GAAkB;AAC9C,QAAM,KAAK,MAAM;AACjB,KAAG;AAAA,IACD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOF,EAAE,IAAI;AAAA,IACJ,IAAI,EAAE;AAAA,IACN,MAAM,EAAE;AAAA,IACR,WAAW,EAAE;AAAA,IACb,SAAS,EAAE,WAAW;AAAA,IACtB,KAAK,EAAE,OAAO;AAAA,IACd,gBAAgB,EAAE,kBAAkB;AAAA,IACpC,aAAa,EAAE,eAAe;AAAA,EAChC,CAAC;AACH;AAEO,SAAS,YAAY,GAAqB;AAC/C,QAAM,KAAK,MAAM;AACjB,KAAG;AAAA,IACD;AAAA;AAAA,EAEF,EAAE,IAAI,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,MAAM,KAAK,UAAU,EAAE,WAAW,CAAC,CAAC,CAAC;AAC/F;AAEO,SAAS,cAAkD;AAChE,QAAM,KAAK,MAAM;AACjB,QAAM,QAAQ,GAAG,QAAQ,kCAAkC,EAAE,IAAI;AACjE,QAAM,UAAU,GACb,QAAQ,4DAA4D,EACpE,IAAI;AACP,SAAO,EAAE,OAAO,MAAM,GAAG,SAAS,QAAQ,EAAE;AAC9C;AAEO,SAAS,mBAAmB,QAAQ,IAMxC;AACD,QAAM,KAAK,MAAM;AACjB,SAAO,GACJ;AAAA,IACC;AAAA;AAAA,EAEF,EACC,IAAI,KAAK;AAOd;;;AF/FA;AAEA,IAAM,eAAe;AAOrB,SAAS,qBAAqC;AAM5C,QAAM,OAAO,cAAc,YAAY,GAAG;AAC1C,QAAM,OAAO,aAAa,IAAI;AAC9B,QAAM,cAAc,KAAK,SAAS,GAAG,GAAG,OAAO,GAAG,EAAE,KAAK,KAAK,SAAS,QAAQ;AAC/E,SAAO,EAAE,cAAc,MAAM,YAAY;AAC3C;AAEA,SAAS,iBAAiB,KAAmE;AAC3F,MAAI,QAAQ,IAAI,wBAAwB;AACtC,WAAO,EAAE,QAAQ,QAAQ,IAAI,wBAAwB,MAAM,WAAW;AAAA,EACxE;AACA,MAAI,IAAI,aAAa;AACnB,WAAO,EAAE,QAAQ,UAAU,YAAY,IAAI,MAAM,MAAM;AAAA,EACzD;AACA,SAAO,EAAE,QAAQ,UAAU,IAAI,YAAY,GAAG,MAAM,WAAW;AACjE;AAEA,eAAsB,cAA6B;AACjD,qBAAmB;AAGnB,QAAM;AAEN,QAAM,MAAM,WAAW;AACvB,MAAI,CAAC,IAAI,YAAa,KAAI,cAAc,KAAK,IAAI;AACjD,MAAI,iBAAiB;AACrB,cAAY,GAAG;AAEf,QAAM,SAAS,mBAAmB;AAClC,QAAM,EAAE,QAAQ,KAAK,IAAI,iBAAiB,MAAM;AAChD,QAAM,SAAS,uBAAuB,MAAM;AAE5C,UAAQ,IAAI,GAAG,KAAK,GAAG,KAAK,iBAAiB,CAAC,IAAI,GAAG,IAAI,+BAA+B,CAAC;AACzF,UAAQ,IAAI,GAAG,MAAM,QAAG,IAAI,mCAAmC;AAC/D,UAAQ,IAAI,GAAG,MAAM,QAAG,IAAI,iCAAiC;AAC7D,UAAQ;AAAA,IACN,GAAG,MAAM,QAAG,IACV,cAAc,OAAO,gBAAgB,MAAM;AAAA,EAC/C;AACA,MAAI,OAAO,QAAQ;AACjB,YAAQ,IAAI,GAAG,IAAI,cAAc,OAAO,MAAM,GAAG,CAAC;AAAA,EACpD;AACA,UAAQ,IAAI;AACZ,UAAQ,IAAI,GAAG,IAAI,kBAAkB,IAAI,GAAG,IAAI,GAAG,MAAM,eAAe,CAAC;AACzE,MAAI,SAAS,OAAO;AAClB,YAAQ;AAAA,MACN,GAAG,IAAI,IAAI,IACT,GAAG,OAAO,QAAG,IACb,GAAG;AAAA,QACD;AAAA,4CAC6C,YAAY;AAAA,MAC3D;AAAA,IACJ;AAAA,EACF;AACA,UAAQ,IAAI;AACZ,UAAQ,IAAI,GAAG,KAAK,OAAO,IAAI,qCAAqC;AACpE,UAAQ,IAAI,gBAAgB,GAAG,KAAK,kBAAkB,IAAI,4BAA4B;AACxF;;;AG5EA;AAFA,OAAOE,SAAQ;AACf,SAAS,cAAAC,mBAAkB;AAK3B,SAAS,YAAY,IAAoB;AACvC,QAAM,IAAI,KAAK,MAAM,KAAK,GAAI;AAC9B,MAAI,IAAI,GAAI,QAAO,GAAG,CAAC;AACvB,QAAM,IAAI,KAAK,MAAM,IAAI,EAAE;AAC3B,MAAI,IAAI,GAAI,QAAO,GAAG,CAAC,KAAK,IAAI,EAAE;AAClC,QAAM,IAAI,KAAK,MAAM,IAAI,EAAE;AAC3B,SAAO,GAAG,CAAC,KAAK,IAAI,EAAE;AACxB;AAEA,eAAsB,gBAA+B;AACnD,QAAM,MAAM,WAAW;AACvB,UAAQ,IAAIC,IAAG,KAAKA,IAAG,KAAK,oBAAoB,CAAC,CAAC;AAClD,UAAQ,IAAI,uBAAuBC,YAAW,WAAW,IAAI,cAAcD,IAAG,IAAI,SAAS,EAAE;AAC7F,UAAQ,IAAI,uBAAuBC,YAAW,OAAO,IAAI,UAAUD,IAAG,IAAI,iBAAiB,EAAE;AAC7F,UAAQ;AAAA,IACN,uBACGC,YAAW,oBAAoB,IAAI,uBAAuBD,IAAG,IAAI,eAAe;AAAA,EACrF;AACA,UAAQ,IAAI,sBAAsB,IAAI,UAAU;AAChD,UAAQ,IAAI,uBAAuB,IAAI,SAASA,IAAG,MAAM,KAAK,IAAIA,IAAG,OAAO,IAAI,EAAE;AAClF,UAAQ,IAAI;AAEZ,MAAI,CAACC,YAAW,OAAO,GAAG;AACxB,YAAQ,IAAID,IAAG,OAAO,wCAAwC,CAAC;AAC/D;AAAA,EACF;AAEA,QAAM,EAAE,OAAO,QAAQ,IAAI,YAAY;AACvC,UAAQ,IAAI,uBAAuB,KAAK,EAAE;AAC1C,UAAQ,IAAI,uBAAuB,OAAO,EAAE;AAC5C,UAAQ,IAAI;AAEZ,QAAM,WAAW,mBAAmB,CAAC;AACrC,MAAI,SAAS,WAAW,GAAG;AACzB,YAAQ,IAAIA,IAAG,IAAI,sEAAiE,CAAC;AACrF;AAAA,EACF;AACA,UAAQ,IAAIA,IAAG,KAAK,kBAAkB,CAAC;AACvC,aAAW,KAAK,UAAU;AACxB,UAAM,MAAM,EAAE,WAAW,YAAY,EAAE,WAAW,EAAE,UAAU,IAAIA,IAAG,IAAI,QAAQ;AACjF,UAAM,KAAK,IAAI,KAAK,EAAE,UAAU,EAAE,eAAe;AACjD,YAAQ,IAAI,KAAKA,IAAG,IAAI,EAAE,GAAG,MAAM,GAAG,CAAC,CAAC,CAAC,KAAK,EAAE,KAAK,OAAO,EAAE,CAAC,KAAK,EAAE,KAAK,GAAG,EAAE;AAAA,EAClF;AACF;;;ACjDA,OAAOE,SAAQ;;;AC8Bf,eAAsB,WACpB,KACA,MACyB;AACzB,MAAI,CAAC,IAAI,QAAQ;AACf,UAAM,IAAI,MAAM,uCAAuC;AAAA,EACzD;AACA,QAAM,MAAM,GAAG,IAAI,WAAW,QAAQ,OAAO,EAAE,CAAC;AAChD,QAAM,MAAM,MAAM,MAAM,KAAK;AAAA,IAC3B,QAAQ;AAAA,IACR,SAAS;AAAA,MACP,gBAAgB;AAAA,MAChB,eAAe,UAAU,IAAI,MAAM;AAAA,IACrC;AAAA,IACA,MAAM,KAAK,UAAU,IAAI;AAAA,EAC3B,CAAC;AACD,MAAI;AACJ,MAAI;AACF,WAAQ,MAAM,IAAI,KAAK;AAAA,EACzB,QAAQ;AACN,UAAM,IAAI,MAAM,mBAAmB,IAAI,MAAM,qBAAqB;AAAA,EACpE;AACA,MAAI,CAAC,IAAI,MAAM,CAAC,KAAK,IAAI;AACvB,UAAM,IAAI;AAAA,MACR,kBAAkB,IAAI,MAAM,IAAI,KAAK,SAAS,EAAE,IAAI,KAAK,UAAU,EAAE,GAAG,KAAK;AAAA,IAC/E;AAAA,EACF;AACA,SAAO;AACT;;;ADtDA,eAAsB,gBAA+B;AACnD,QAAM,MAAM,WAAW;AACvB,MAAI,SAAS;AACb,MAAI,cAAc;AAClB,cAAY,GAAG;AACf,UAAQ,IAAIC,IAAG,MAAM,QAAG,IAAI,6BAA6B;AAC3D;AAMA,eAAsB,YAAY,QAA4B,MAA+B;AAC3F,QAAM,OAAO,UAAW,MAAM,aAAa,gCAAgC,GAAI,KAAK;AACpF,MAAI,CAAC,KAAK;AACR,YAAQ,MAAMA,IAAG,IAAI,yBAAoB,CAAC;AAC1C,YAAQ,KAAK,CAAC;AAAA,EAChB;AACA,MAAI,CAAC,IAAI,WAAW,UAAU,GAAG;AAC/B,YAAQ,MAAMA,IAAG,IAAI,mEAA8D,CAAC;AACpF,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,MAAM,WAAW;AACvB,MAAI,SAAS;AACb,MAAI,KAAK,IAAK,KAAI,aAAa,KAAK;AAEpC,UAAQ,IAAIA,IAAG,IAAI,wBAAwB,IAAI,UAAU,QAAG,CAAC;AAC7D,MAAI;AACF,UAAM,WAAW,KAAK,CAAC,CAAC;AAAA,EAC1B,SAAS,KAAK;AACZ,YAAQ,MAAMA,IAAG,IAAI,4BAAuB,IAAK,IAAc,OAAO;AACtE,YAAQ,KAAK,CAAC;AAAA,EAChB;AACA,cAAY,GAAG;AACf,UAAQ,IAAIA,IAAG,MAAM,QAAG,IAAI,UAAU;AACtC,UAAQ,IAAIA,IAAG,IAAI,SAAS,IAAI,IAAI,UAAU;AAC9C,UAAQ,IAAIA,IAAG,IAAI,SAAS,IAAI,IAAI,MAAM,GAAG,EAAE,IAAI,QAAG;AACxD;AAEA,eAAsB,mBAAkC;AACtD,QAAM,EAAE,0BAAAC,0BAAyB,IAAI,MAAM;AAC3C,QAAM,EAAE,OAAO,IAAIA,0BAAyB;AAC5C,UAAQ,IAAID,IAAG,MAAM,QAAG,IAAI,uDAAuD;AACnF,MAAI,OAAQ,SAAQ,IAAIA,IAAG,IAAI,cAAc,MAAM,GAAG,CAAC;AACzD;AAEA,IAAM,SAAS;AACf,IAAM,YAAY;AAClB,IAAM,gBAAgB;AACtB,IAAM,UAAU;AAChB,IAAM,WAAW;AAEjB,SAAS,aAAa,QAAiC;AACrD,SAAO,IAAI,QAAQ,CAACE,aAAY;AAC9B,YAAQ,OAAO,MAAM,MAAM;AAC3B,QAAI,MAAM;AACV,UAAM,QAAQ,QAAQ;AACtB,UAAM,aAAa,IAAI;AACvB,UAAM,OAAO;AACb,UAAM,YAAY,MAAM;AACxB,UAAM,SAAS,CAAC,UAAkB;AAChC,iBAAW,MAAM,OAAO;AACtB,cAAM,OAAO,GAAG,WAAW,CAAC;AAC5B,YAAI,SAAS,WAAW,SAAS,UAAU;AACzC,gBAAM,aAAa,KAAK;AACxB,gBAAM,MAAM;AACZ,gBAAM,eAAe,QAAQ,MAAM;AACnC,kBAAQ,OAAO,MAAM,IAAI;AACzB,iBAAOA,SAAQ,GAAG;AAAA,QACpB;AACA,YAAI,SAAS,QAAQ;AACnB,gBAAM,aAAa,KAAK;AACxB,kBAAQ,KAAK,GAAG;AAAA,QAClB;AACA,YAAI,SAAS,aAAa,SAAS,eAAe;AAChD,gBAAM,IAAI,MAAM,GAAG,EAAE;AAAA,QACvB,OAAO;AACL,iBAAO;AAAA,QACT;AAAA,MACF;AAAA,IACF;AACA,UAAM,GAAG,QAAQ,MAAM;AAAA,EACzB,CAAC;AACH;;;AExFA,OAAOC,SAAQ;AACf,SAAS,cAAAC,mBAAkB;AAC3B,SAAS,YAAAC,iBAAgB;;;ACFzB,SAAS,WAAAC,UAAS,gBAAgB;AAClC,SAAS,QAAAC,aAAY;AAId,SAAS,0BAAkC;AAChD,QAAM,OAAOD,SAAQ;AACrB,UAAQ,SAAS,GAAG;AAAA,IAClB,KAAK;AACH,aAAOC,MAAK,MAAM,WAAW,uBAAuB,UAAU,QAAQ,SAAS;AAAA,IACjF,KAAK,SAAS;AACZ,YAAM,UAAU,QAAQ,IAAI,WAAWA,MAAK,MAAM,WAAW,SAAS;AACtE,aAAOA,MAAK,SAAS,UAAU,QAAQ,SAAS;AAAA,IAClD;AAAA,IACA;AACE,aAAOA,MAAK,MAAM,WAAW,UAAU,QAAQ,SAAS;AAAA,EAC5D;AACF;AAEO,SAAS,mBAA2B;AACzC,SAAO,QAAQ,IAAI,gCAAgC,wBAAwB;AAC7E;;;ACrBA,OAAO,cAAkC;AACzC,SAAS,YAAAC,iBAAgB;AACzB,SAAS,cAAAC,mBAAkB;AAC3B,SAAS,UAAU,WAAAC,UAAS,QAAAC,aAAY;;;ACHxC,SAAS,gBAAgB;AACzB,SAAS,cAAAC,aAAY,gBAAgB;AACrC,SAAS,QAAAC,OAAM,WAAAC,UAAS,SAAS,OAAAC,YAAW;AAC5C,SAAS,iBAAAC,sBAAqB;AAc9B,eAAsB,YAAY,eAAoD;AACpF,QAAM,OAAOH,MAAK,eAAe,cAAc;AAC/C,MAAI,CAACD,YAAW,IAAI,EAAG,QAAO;AAC9B,MAAI;AACF,UAAM,MAAM,MAAM,SAAS,MAAM,MAAM;AACvC,WAAO,KAAK,MAAM,GAAG;AAAA,EACvB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,eAAe,UAAiC;AAC9D,MAAI,CAAC,SAAS,WAAW,SAAS,EAAG,QAAO;AAC5C,MAAI;AACF,WAAOI,eAAc,QAAQ;AAAA,EAC/B,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAIO,SAAS,kBAAkB,MAA6B;AAC7D,MAAI,MAAMF,SAAQ,QAAQ,IAAI,CAAC;AAC/B,SAAO,OAAO,QAAQC,MAAK;AACzB,UAAM,MAAMF,MAAK,KAAK,MAAM;AAC5B,QAAI;AACF,UAAID,YAAW,GAAG,EAAG,QAAO;AAAA,IAC9B,QAAQ;AAAA,IAER;AACA,UAAM,SAASE,SAAQ,GAAG;AAC1B,QAAI,WAAW,IAAK;AACpB,UAAM;AAAA,EACR;AACA,SAAO;AACT;AAEO,SAAS,iBAAiB,MAAuB;AACtD,MAAI;AACF,UAAM,IAAI,SAAS,IAAI;AACvB,QAAI,EAAE,OAAO,OAAO,KAAM,QAAO;AAAA,EACnC,QAAQ;AACN,WAAO;AAAA,EACT;AAGA,QAAM,OACJ;AACF,SAAO,CAAC,KAAK,KAAK,IAAI;AACxB;;;ACnEA,SAAS,iBAAiB;AAC1B,OAAO,oBAAoB;AAU3B,IAAM,MAAM,IAAI,eAAe,iBAAiB;AAEhD,IAAI,eAAe;AAEZ,SAAS,YAAY,QAAgB,OAA2B;AAErE,QAAM,UAAU,IAAI,WAAW,QAAQ,KAAK;AAC5C,QAAM,QAAQ,IAAI,aAAa,OAAO;AAGtC,MAAI,QAAQ;AACZ,MAAI,UAAU;AACd,aAAW,QAAQ,UAAU,QAAQ,KAAK,GAAG;AAC3C,UAAM,IACJ,KAAK,UACJ,KAAK,MAAM,MAAM,KAAK,GAAG,WAAW,KAAK,MAAM,SAAS,IAAI,IAAI;AACnE,QAAI,KAAK,MAAO,UAAS;AAAA,aAChB,KAAK,QAAS,YAAW;AAAA,EACpC;AAEA,SAAO,EAAE,OAAO,OAAO,QAAQ;AACjC;;;AChCA,SAAS,cAAAG,aAAY,gBAAAC,eAAc,YAAAC,iBAAgB;AACnD,SAAS,QAAAC,OAAM,UAAU,OAAAC,YAAW;AACpC,OAAO,YAA6B;AAEpC,IAAM,WAAW;AACjB,IAAM,SAAS;AAQf,IAAM,QAAQ,oBAAI,IAAwB;AAE1C,SAAS,QAAQ,WAAkC;AACjD,QAAM,OAAOD,MAAK,WAAW,QAAQ;AACrC,MAAI,CAACH,YAAW,IAAI,EAAG,QAAO;AAC9B,MAAI;AACF,UAAM,MAAMC,cAAa,MAAM,MAAM;AACrC,WAAO,OAAO,EAAE,oBAAoB,KAAK,CAAC,EAAE,IAAI,GAAG;AAAA,EACrD,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,UAAU,WAAkC;AACnD,QAAM,QAAQ,MAAM,IAAI,SAAS;AACjC,QAAM,OAAOE,MAAK,WAAW,QAAQ;AACrC,MAAI,QAAQ;AACZ,MAAI;AACF,YAAQH,YAAW,IAAI,IAAIE,UAAS,IAAI,EAAE,UAAU;AAAA,EACtD,QAAQ;AACN,YAAQ;AAAA,EACV;AACA,QAAM,MAAM,KAAK,IAAI;AACrB,MAAI,SAAS,MAAM,MAAM,WAAW,UAAU,MAAM,cAAc,OAAO;AACvE,WAAO,MAAM;AAAA,EACf;AACA,QAAM,UAAU,QAAQ,SAAS;AACjC,QAAM,IAAI,WAAW,EAAE,SAAS,UAAU,KAAK,WAAW,MAAM,CAAC;AACjE,SAAO;AACT;AAOO,SAAS,UAAU,WAAsC,UAA2B;AACzF,MAAI,CAAC,UAAW,QAAO;AACvB,QAAM,UAAU,UAAU,SAAS;AACnC,MAAI,CAAC,QAAS,QAAO;AACrB,QAAM,MAAM,SAAS,WAAW,QAAQ;AAExC,MAAI,CAAC,OAAO,IAAI,WAAW,IAAI,KAAK,IAAI,WAAWE,IAAG,EAAG,QAAO;AAEhE,SAAO,QAAQ,QAAQ,IAAI,MAAMA,IAAG,EAAE,KAAK,GAAG,CAAC;AACjD;;;AClDO,IAAM,WAA4B;AAAA;AAAA,EAEvC;AAAA,IACE,MAAM;AAAA,IACN,IAAI;AAAA,IACJ,aAAa;AAAA,EACf;AAAA;AAAA,EAGA;AAAA,IACE,MAAM;AAAA,IACN,IAAI;AAAA,IACJ,aAAa;AAAA,EACf;AAAA;AAAA,EAGA;AAAA,IACE,MAAM;AAAA,IACN,IAAI;AAAA,IACJ,aAAa;AAAA,EACf;AAAA;AAAA,EAEA;AAAA,IACE,MAAM;AAAA,IACN,IAAI;AAAA,IACJ,aAAa;AAAA,EACf;AAAA;AAAA,EAGA;AAAA,IACE,MAAM;AAAA,IACN,IAAI;AAAA,IACJ,aAAa;AAAA,EACf;AAAA;AAAA,EAGA;AAAA,IACE,MAAM;AAAA,IACN,IAAI;AAAA,IACJ,aAAa;AAAA,EACf;AAAA;AAAA,EAGA;AAAA,IACE,MAAM;AAAA,IACN,IAAI;AAAA,IACJ,aAAa;AAAA,EACf;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,IAAI;AAAA,IACJ,aAAa;AAAA,EACf;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,IAAI;AAAA,IACJ,aAAa;AAAA,EACf;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,IAAI;AAAA,IACJ,aAAa;AAAA,EACf;AAAA;AAAA,EAGA;AAAA,IACE,MAAM;AAAA,IACN,IAAI;AAAA,IACJ,aAAa;AAAA,EACf;AAAA;AAAA,EAGA;AAAA,IACE,MAAM;AAAA,IACN,IAAI;AAAA,IACJ,aAAa;AAAA,EACf;AAAA;AAAA,EAGA;AAAA,IACE,MAAM;AAAA,IACN,IAAI;AAAA,IACJ,aAAa;AAAA,EACf;AAAA;AAAA,EAGA;AAAA,IACE,MAAM;AAAA,IACN,IAAI;AAAA,IACJ,aAAa;AAAA,EACf;AAAA;AAAA;AAAA;AAAA,EAKA;AAAA,IACE,MAAM;AAAA,IACN,IAAI;AAAA,IACJ,aAAa,CAAC,MAAc;AAG1B,YAAM,QACJ,mEAAmE,KAAK,CAAC;AAC3E,UAAI,CAAC,MAAO,QAAO;AACnB,YAAM,CAAC,EAAE,QAAQ,KAAK,IAAI;AAC1B,aAAO,GAAG,UAAU,EAAE,GAAG,SAAS,EAAE,2BAA2B,SAAS,EAAE;AAAA,IAC5E;AAAA,EACF;AAAA;AAAA,EAGA;AAAA,IACE,MAAM;AAAA,IACN,IAAI;AAAA,IACJ,aAAa;AAAA,EACf;AAAA;AAAA;AAAA,EAIA;AAAA,IACE,MAAM;AAAA,IACN,IAAI;AAAA,IACJ,aAAa,CAAC,MAAc;AAC1B,UAAI,MAAM,aAAa,MAAM,eAAe,MAAM,kBAAmB,QAAO;AAC5E,aAAO;AAAA,IACT;AAAA,EACF;AACF;AAQO,IAAM,mBACX;;;AC5IK,SAAS,YAAY,OAAuB;AACjD,MAAI,CAAC,MAAO,QAAO;AACnB,MAAI,IAAI;AACR,aAAW,QAAQ,UAAU;AAC3B,QAAI,OAAO,KAAK,gBAAgB,YAAY;AAC1C,UAAI,EAAE,QAAQ,KAAK,IAAI,KAAK,WAAW;AAAA,IACzC,OAAO;AACL,UAAI,EAAE,QAAQ,KAAK,IAAI,KAAK,WAAW;AAAA,IACzC;AAAA,EACF;AACA,SAAO;AACT;AAUO,SAAS,SAAY,OAAU,OAAwB,oBAAI,QAAQ,GAAM;AAC9E,MAAI,SAAS,KAAM,QAAO;AAC1B,MAAI,OAAO,UAAU,SAAU,QAAO,YAAY,KAAK;AACvD,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,MAAI,KAAK,IAAI,KAAe,EAAG,QAAO;AACtC,OAAK,IAAI,KAAe;AAExB,MAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,WAAO,MAAM,IAAI,CAAC,MAAM,SAAS,GAAG,IAAI,CAAC;AAAA,EAC3C;AAEA,QAAM,MAA+B,CAAC;AACtC,aAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,KAAgC,GAAG;AACrE,QAAI,iBAAiB,KAAK,CAAC,KAAK,OAAO,MAAM,YAAY,EAAE,UAAU,GAAG;AACtE,UAAI,CAAC,IAAI;AAAA,IACX,OAAO;AACL,UAAI,CAAC,IAAI,SAAS,GAAG,IAAI;AAAA,IAC3B;AAAA,EACF;AACA,SAAO;AACT;;;ALXO,SAAS,mBACd,YACA,YACA,OAAuB,CAAC,GACb;AACX,QAAM,YAAY,KAAK,wBAAwB;AAE/C,QAAM,UAAU,SAAS,MAAM,YAAY;AAAA,IACzC,eAAe;AAAA,IACf,OAAO;AAAA,IACP,YAAY;AAAA,IACZ,kBAAkB,EAAE,oBAAoB,KAAK,cAAc,GAAG;AAAA,EAChE,CAAC;AAED,UAAQ,GAAG,OAAO,OAAO,SAAS;AAChC,QAAI;AACF,YAAM,SAASC,SAAQ,IAAI;AAC3B,YAAM,OAAO,SAAS,IAAI;AAG1B,UAAI,SAAS,eAAgB;AAG7B,YAAM,MAAM,MAAM,aAAa,QAAQ,MAAM,GAAK;AAClD,UAAI,CAAC,IAAK;AACV,YAAM,gBAAgB,QAAQ,KAAK,WAAW,UAAU;AAAA,IAC1D,SAAS,KAAK;AAGZ,cAAQ,MAAM,gDAAgD,MAAM,GAAG;AAAA,IACzE;AAAA,EACF,CAAC;AAED,SAAO;AACT;AASA,eAAe,aACb,QACA,QACA,SACiC;AACjC,QAAM,QAAQ,KAAK,IAAI;AACvB,SAAO,KAAK,IAAI,IAAI,QAAQ,SAAS;AACnC,UAAM,OAAO,MAAM,YAAY,MAAM;AACrC,QAAI,MAAM;AACR,YAAM,MAAM,KAAK,QAAQ,UAAU,CAAC,MAAM,EAAE,OAAO,MAAM;AACzD,UAAI,OAAO,GAAG;AACZ,cAAM,UAAU,KAAK,QAAQ,GAAG;AAChC,YAAI,CAAC,QAAS,QAAO;AACrB,cAAM,WAAW,MAAM,IAAK,KAAK,QAAQ,MAAM,CAAC,KAAK,OAAQ;AAC7D,eAAO,EAAE,UAAU,KAAK,UAAU,SAAS,UAAU,YAAY,IAAI;AAAA,MACvE;AAAA,IACF;AACA,UAAM,MAAM,GAAG;AAAA,EACjB;AACA,SAAO;AACT;AAEA,eAAe,gBACb,QACA,KACA,WACA,YACe;AACf,QAAM,WAAW,eAAe,IAAI,QAAQ;AAC5C,MAAI,CAAC,SAAU;AAGf,MAAI,WAAW,KAAK,QAAQ,EAAG;AAG/B,QAAM,YAAY,kBAAkB,QAAQ;AAC5C,MAAI,UAAU,WAAW,QAAQ,EAAG;AAEpC,MAAI,CAAC,IAAI,UAAU;AAEjB,QAAI,UAAW;AAAA,EACjB;AAEA,QAAM,cAAcC,MAAK,QAAQ,IAAI,QAAQ,EAAE;AAC/C,MAAI,SAAS;AACb,MAAI,QAAQ;AACZ,MAAI,IAAI,UAAU;AAChB,UAAM,WAAWA,MAAK,QAAQ,IAAI,SAAS,EAAE;AAC7C,QAAIC,YAAW,QAAQ,GAAG;AACxB,eAAS,MAAM,SAAS,QAAQ;AAAA,IAClC;AAAA,EACF;AACA,MAAIA,YAAW,WAAW,GAAG;AAC3B,YAAQ,MAAM,SAAS,WAAW;AAAA,EACpC;AAIA,MAAI,WAAW,MAAO;AAEtB,QAAM,SAAS,iBAAiB,QAAQ;AACxC,MAAI,QAAQ;AACZ,MAAI,QAAQ;AACZ,MAAI,UAAU;AACd,MAAI,CAAC,QAAQ;AAIX,UAAM,aAAa,YAAY,MAAM;AACrC,UAAM,YAAY,YAAY,KAAK;AACnC,UAAM,SAAS,YAAY,YAAY,SAAS;AAChD,YAAQ,OAAO;AACf,YAAQ,OAAO;AACf,cAAU,OAAO;AAAA,EACnB;AAEA,QAAM,SAAS,eAAe,IAAI,QAAQ,MAAM;AAChD,aAAW;AAAA,IACT;AAAA,IACA;AAAA,IACA;AAAA,IACA,WAAW,IAAI,QAAQ,aAAa,KAAK,IAAI;AAAA,IAC7C;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AACH;AAEA,SAAS,eAAe,KAAuC;AAC7D,MAAI,CAAC,IAAK,QAAO;AACjB,QAAM,IAAI,IAAI,YAAY;AAC1B,MAAI,EAAE,SAAS,UAAU,KAAK,EAAE,SAAS,IAAI,KAAK,EAAE,SAAS,MAAM,EAAG,QAAO;AAC7E,SAAO;AACT;AAEA,eAAe,SAAS,MAA+B;AACrD,MAAI;AACF,WAAO,MAAMC,UAAS,MAAM,MAAM;AAAA,EACpC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,MAAM,IAA2B;AACxC,SAAO,IAAI,QAAQ,CAAC,MAAM,WAAW,GAAG,EAAE,CAAC;AAC7C;AAEA,IAAM,aACJ;;;AMzLF,SAAS,cAAc;AAKvB,IAAM,UAAU,IAAI,KAAK;AACzB,IAAM,aAAa;AASZ,IAAM,uBAAN,MAA2B;AAAA,EACxB,OAAO,oBAAI,IAAyB;AAAA,EAE5C,OAAO,UAAgE;AACrE,UAAM,MAAM,SAAS,aAAa;AAClC,UAAM,KAAK,SAAS;AACpB,UAAM,MAAM,SAAS,aAAa;AAElC,QAAI,UAAU,KAAK,KAAK,IAAI,GAAG;AAC/B,QAAI,QAAQ;AACZ,QAAI,CAAC,WAAW,KAAK,QAAQ,SAAS,SAAS;AAE7C,UAAI,QAAS,MAAK,aAAa,SAAS,QAAQ,MAAM;AACtD,gBAAU;AAAA,QACR,IAAI,OAAO,OAAO,EAAE,CAAC;AAAA,QACrB,WAAW;AAAA,QACX,QAAQ;AAAA,QACR;AAAA,MACF;AACA,WAAK,KAAK,IAAI,KAAK,OAAO;AAC1B,cAAQ;AACR,oBAAc;AAAA,QACZ,IAAI,QAAQ;AAAA,QACZ,MAAM;AAAA,QACN,WAAW;AAAA,QACX;AAAA,MACF,CAAC;AAAA,IACH,OAAO;AACL,cAAQ,SAAS;AAAA,IACnB;AAEA,UAAM,QAAoB;AAAA,MACxB,IAAI,OAAO;AAAA,MACX,WAAW,QAAQ;AAAA,MACnB,MAAM;AAAA,MACN,MAAM;AAAA,MACN;AAAA,MACA;AAAA,MACA,SAAS;AAAA,QACP,WAAW;AAAA,QACX,WAAW,SAAS;AAAA,QACpB,OAAO,SAAS;AAAA,QAChB,SAAS,SAAS;AAAA,QAClB,QAAQ,SAAS;AAAA,QACjB,QAAQ,SAAS;AAAA,QACjB,OAAO,SAAS;AAAA,MAClB;AAAA,IACF;AACA,gBAAY,KAAK;AACjB,WAAO,EAAE,WAAW,QAAQ,IAAI,MAAM;AAAA,EACxC;AAAA;AAAA,EAGA,WAAiB;AACf,UAAM,MAAM,KAAK,IAAI;AACrB,eAAW,KAAK,KAAK,KAAK,OAAO,EAAG,MAAK,aAAa,GAAG,KAAK,IAAI,EAAE,QAAQ,GAAG,CAAC;AAChF,SAAK,KAAK,MAAM;AAAA,EAClB;AAAA,EAEQ,aAAa,GAAgB,SAAiB;AACpD,kBAAc;AAAA,MACZ,IAAI,EAAE;AAAA,MACN,MAAM;AAAA,MACN,WAAW,EAAE;AAAA,MACb;AAAA,MACA,KAAK,EAAE;AAAA,IACT,CAAC;AAAA,EACH;AACF;;;AR7EA;AAGA,eAAsB,eAA8B;AAClD,qBAAmB;AAEnB,QAAM;AAEN,QAAM,MAAM,iBAAiB;AAC7B,MAAI,CAACC,YAAW,GAAG,GAAG;AACpB,YAAQ,MAAMC,IAAG,IAAI,4CAAuC,CAAC;AAC7D,YAAQ,MAAM,OAAO,GAAG;AACxB,YAAQ,MAAM;AACd,YAAQ,MAAMA,IAAG,IAAI,sDAAsD,CAAC;AAC5E,YAAQ,MAAMA,IAAG,IAAI,yDAAyD,CAAC;AAC/E,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,UAAQ,IAAIA,IAAG,KAAKA,IAAG,KAAK,iCAA8B,CAAC,CAAC;AAC5D,UAAQ,IAAIA,IAAG,IAAI,aAAa,IAAI,GAAG;AACvC,UAAQ,IAAIA,IAAG,IAAI,0BAA0B,CAAC;AAE9C,QAAM,WAAW,IAAI,qBAAqB;AAE1C,QAAM,UAAU,mBAAmB,KAAK,OAAO,SAAS;AACtD,UAAM,EAAE,WAAW,MAAM,IAAI,SAAS,OAAO,IAAI;AACjD,UAAM,KAAK,IAAI,KAAK,KAAK,SAAS,EAAE,mBAAmB;AACvD,UAAM,KAAK,KAAK,YAAYC,UAAS,KAAK,SAAS,IAAID,IAAG,IAAI,cAAc;AAC5E,UAAM,OAAO,KAAK,SAAS,MAAM,GAAG,EAAE,MAAM,EAAE,EAAE,KAAK,GAAG;AACxD,UAAM,cACJ,KAAK,WAAW,cACZA,IAAG,QAAQ,IAAI,IACf,KAAK,WAAW,kBACdA,IAAG,KAAK,KAAK,IACbA,IAAG,IAAI,GAAG;AAClB,UAAM,QAAQ,KAAK,SACfA,IAAG,IAAI,QAAQ,IACf,GAAGA,IAAG,MAAM,MAAM,KAAK,KAAK,CAAC,IAAIA,IAAG,IAAI,MAAM,KAAK,OAAO,CAAC;AAC/D,QAAI,OAAO;AACT,cAAQ;AAAA,QACN,GAAGA,IAAG,IAAI,EAAE,CAAC,KAAKA,IAAG,OAAO,SAAS,CAAC,KAAKA,IAAG,IAAI,SAAS,CAAC,KAAK,EAAE;AAAA,MACrE;AAAA,IACF;AACA,YAAQ,IAAI,GAAGA,IAAG,IAAI,EAAE,CAAC,cAAc,WAAW,KAAK,KAAK,OAAO,EAAE,CAAC,KAAK,KAAK,EAAE;AAAA,EACpF,CAAC;AAGD,QAAM,WAAW,YAAY;AAC3B,YAAQ,IAAIA,IAAG,IAAI,4BAAuB,CAAC;AAC3C,aAAS,SAAS;AAClB,UAAM,QAAQ,MAAM;AACpB,YAAQ,KAAK,CAAC;AAAA,EAChB;AACA,UAAQ,GAAG,UAAU,QAAQ;AAC7B,UAAQ,GAAG,WAAW,QAAQ;AAChC;;;AS7DA,OAAOE,SAAQ;;;AC6BR,SAAS,iBAAiB,YAAY,KAAmB;AAC9D,QAAM,KAAK,MAAM;AACjB,QAAM,YAAY,GACf;AAAA,IACC;AAAA;AAAA;AAAA;AAAA;AAAA,EAKF,EACC,IAAI,SAAS;AAEhB,MAAI,UAAU,WAAW,EAAG,QAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAC,GAAG,UAAU,CAAC,EAAE;AAE5E,QAAM,aAAa,CAAC,GAAG,IAAI,IAAI,UAAU,IAAI,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;AAClE,QAAM,eAAe,WAAW,IAAI,MAAM,GAAG,EAAE,KAAK,GAAG;AACvD,QAAM,cAAc,GACjB;AAAA,IACC;AAAA,oCAC8B,YAAY;AAAA,EAC5C,EACC,IAAI,GAAG,UAAU;AAEpB,QAAM,WAA4B,YAAY,IAAI,CAAC,OAAO;AAAA,IACxD,IAAI,EAAE;AAAA,IACN,MAAM,EAAE;AAAA,IACR,YAAY,EAAE;AAAA,IACd,UAAU,EAAE;AAAA,IACZ,KAAK,EAAE;AAAA,IACP,kBAAkB,EAAE;AAAA,IACpB,cAAc,EAAE;AAAA,EAClB,EAAE;AAEF,QAAM,SAAwB,UAAU,IAAI,CAAC,OAAO;AAAA,IAClD,IAAI,EAAE;AAAA,IACN,YAAY,EAAE;AAAA,IACd,IAAI,EAAE;AAAA,IACN,MAAM,EAAE;AAAA,IACR,MAAM,EAAE;AAAA,IACR,KAAK,EAAE;AAAA,IACP,SAAS,UAAU,EAAE,OAAO;AAAA,EAC9B,EAAE;AAEF,SAAO,EAAE,UAAU,QAAQ,UAAU,UAAU,IAAI,CAAC,MAAM,EAAE,EAAE,EAAE;AAClE;AAEO,SAAS,aAAa,UAA0B;AACrD,MAAI,SAAS,WAAW,EAAG;AAC3B,QAAM,KAAK,MAAM;AACjB,QAAM,MAAM,KAAK,IAAI;AACrB,QAAM,OAAO,GAAG,QAAQ,gDAAgD;AACxE,QAAM,KAAK,GAAG,YAAY,CAAC,QAAkB;AAC3C,eAAW,MAAM,IAAK,MAAK,IAAI,KAAK,EAAE;AAAA,EACxC,CAAC;AACD,KAAG,QAAQ;AACb;AAEA,SAAS,UAAU,GAAoB;AACrC,MAAI;AACF,WAAO,KAAK,MAAM,CAAC;AAAA,EACrB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;;;ADvFA,eAAsB,cAA6B;AACjD,QAAM,MAAM,WAAW;AACvB,MAAI,CAAC,IAAI,QAAQ;AACf,YAAQ,MAAMC,IAAG,IAAI,yBAAoB,IAAIA,IAAG,KAAK,0BAA0B,CAAC;AAChF,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,MAAI,gBAAgB;AACpB,MAAI,cAAc;AAElB,SAAO,MAAM;AACX,UAAM,QAAQ,iBAAiB,GAAG;AAClC,QAAI,MAAM,OAAO,WAAW,EAAG;AAE/B,YAAQ;AAAA,MACNA,IAAG,IAAI,eAAe,MAAM,OAAO,MAAM,kBAAkB,MAAM,SAAS,MAAM,iBAAY;AAAA,IAC9F;AACA,UAAM,MAAM,MAAM,WAAW,KAAK,EAAE,UAAU,MAAM,UAAU,QAAQ,MAAM,OAAO,CAAC;AACpF,iBAAa,MAAM,QAAQ;AAC3B,qBAAiB,IAAI,oBAAoB;AACzC,mBAAe,IAAI,kBAAkB;AAAA,EACvC;AAEA,MAAI,gBAAgB,GAAG;AACrB,YAAQ,IAAIA,IAAG,MAAM,QAAG,IAAI,2CAAsC;AAClE;AAAA,EACF;AACA,UAAQ;AAAA,IACNA,IAAG,MAAM,QAAG,IACV,aAAa,WAAW,gBAAa,aAAa;AAAA,EACtD;AACF;;;AEpCA,SAAS,UAAAC,eAAc;AACvB,SAAS,sBAAsB;AAG/B;AAGA,IAAM,qBAAgD;AAAA,EACpD,cAAc;AAAA,EACd,YAAY;AAAA,EACZ,kBAAkB;AAAA,EAClB,YAAY;AAAA,EACZ,aAAa;AAAA,EACb,cAAc;AAAA,EACd,MAAM;AAAA,EACN,cAAc;AAAA,EACd,YAAY;AACd;AAEA,eAAe,YAA6B;AAC1C,MAAI,QAAQ,MAAM,MAAO,QAAO;AAChC,QAAM,SAAmB,CAAC;AAC1B,mBAAiB,SAAS,QAAQ,OAAO;AACvC,WAAO,KAAK,KAAe;AAAA,EAC7B;AACA,SAAO,OAAO,OAAO,MAAM,EAAE,SAAS,MAAM;AAC9C;AAEA,SAAS,SAAS,KAAoB;AACpC,MAAI;AACF,uBAAmB;AACnB,UAAM,OAAO,KAAI,oBAAI,KAAK,GAAE,YAAY,CAAC,iBACvC,eAAe,QAAQ,IAAI,SAAS,IAAI,UAAU,OAAO,GAAG,CAC9D;AAAA;AACA,mBAAe,UAAU,IAAI;AAAA,EAC/B,QAAQ;AAAA,EAER;AACF;AAEA,eAAsB,QAAQ,UAAkC;AAE9D,MAAI;AACF,UAAM,MAAM,MAAM,UAAU;AAC5B,QAAI,CAAC,IAAI,KAAK,EAAG;AACjB,UAAM,QAAQ,KAAK,MAAM,GAAG;AAE5B,UAAM,gBAAgB,MAAM,mBAAmB,YAAY;AAC3D,UAAM,OAAkB,mBAAmB,aAAa,KAAK;AAC7D,UAAM,KAAK,KAAK,IAAI;AACpB,UAAM,YAAY,MAAM,cAAc;AAEtC,QAAI,SAAS,iBAAiB;AAC5B,oBAAc;AAAA,QACZ,IAAI;AAAA,QACJ,MAAM;AAAA,QACN,WAAW;AAAA,QACX,KAAK,MAAM;AAAA,MACb,CAAC;AAAA,IACH,WAAW,SAAS,eAAe;AACjC,oBAAc;AAAA,QACZ,IAAI;AAAA,QACJ,MAAM;AAAA,QACN,WAAW;AAAA,QACX,SAAS;AAAA,QACT,KAAK,MAAM;AAAA,MACb,CAAC;AAAA,IACH,OAAO;AAEL,oBAAc;AAAA,QACZ,IAAI;AAAA,QACJ,MAAM;AAAA,QACN,WAAW;AAAA,QACX,KAAK,MAAM;AAAA,MACb,CAAC;AAAA,IACH;AAKA,UAAM,cAAc,SAAS,KAAK;AAClC,UAAM,QAAoB;AAAA,MACxB,IAAIC,QAAO;AAAA,MACX;AAAA,MACA,MAAM;AAAA,MACN;AAAA,MACA;AAAA,MACA,KAAK,MAAM;AAAA,MACX,SAAS;AAAA,IACX;AACA,gBAAY,KAAK;AAAA,EACnB,SAAS,KAAK;AACZ,aAAS,GAAG;AAAA,EACd;AACF;;;AlBtFA,IAAM,UAAU,IAAI,QAAQ;AAE5B,QACG,KAAK,WAAW,EAChB,YAAY,kEAA6D,EACzE,QAAQ,OAAO;AAElB,QACG,QAAQ,MAAM,EACd,YAAY,8DAA8D,EAC1E,OAAO,YAAY;AAClB,QAAM,YAAY;AACpB,CAAC;AAEH,QACG,QAAQ,QAAQ,EAChB,YAAY,yDAAyD,EACrE,OAAO,YAAY;AAClB,QAAM,cAAc;AACtB,CAAC;AAEH,QACG,QAAQ,OAAO,EACf,YAAY,0DAA0D,EACtE,OAAO,YAAY;AAClB,QAAM,aAAa;AACrB,CAAC;AAEH,QACG,QAAQ,gBAAgB,EACxB,YAAY,iDAAiD,EAC7D,OAAO,eAAe,+DAA+D,EACrF,OAAO,OAAO,QAA4B,SAA2B;AACpE,QAAM,YAAY,QAAQ,IAAI;AAChC,CAAC;AAEH,QACG,QAAQ,MAAM,EACd,YAAY,wCAAwC,EACpD,OAAO,YAAY;AAClB,QAAM,YAAY;AACpB,CAAC;AAEH,QACG,QAAQ,QAAQ,EAChB,YAAY,yBAAyB,EACrC,OAAO,YAAY;AAClB,QAAM,cAAc;AACtB,CAAC;AAEH,QACG,QAAQ,WAAW,EACnB,YAAY,qDAAqD,EACjE,OAAO,YAAY;AAClB,QAAM,iBAAiB;AACzB,CAAC;AAEH,QACG,QAAQ,cAAc,EACtB,YAAY,uEAAuE,EACnF,OAAO,OAAO,UAAkB;AAC/B,QAAM,QAAQ,KAAK;AACrB,CAAC;AAEH,QAAQ,WAAW,QAAQ,IAAI,EAAE,MAAM,CAAC,QAAQ;AAG9C,QAAM,MAAM,QAAQ,KAAK,CAAC;AAC1B,MAAI,QAAQ,QAAQ;AAClB,YAAQ,KAAK,CAAC;AAAA,EAChB;AACA,UAAQ,MAAM,GAAG;AACjB,UAAQ,KAAK,CAAC;AAChB,CAAC;","names":["mkdirSync","readFileSync","writeFileSync","existsSync","pc","existsSync","pc","existsSync","pc","pc","uninstallClaudeCodeHooks","resolve","pc","existsSync","basename","homedir","join","readFile","existsSync","dirname","join","existsSync","join","dirname","sep","fileURLToPath","existsSync","readFileSync","statSync","join","sep","dirname","join","existsSync","readFile","existsSync","pc","basename","pc","pc","nanoid","nanoid"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@agentreel/agent",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Loom for AI coding sessions — captures every Claude Code and Cursor session locally and ships it to a scrubbable, shareable replay.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"claude-code",
|
|
7
|
+
"cursor",
|
|
8
|
+
"ai",
|
|
9
|
+
"agent",
|
|
10
|
+
"observability",
|
|
11
|
+
"replay",
|
|
12
|
+
"timeline",
|
|
13
|
+
"developer-tools",
|
|
14
|
+
"agentreel"
|
|
15
|
+
],
|
|
16
|
+
"homepage": "https://agentreel.dev",
|
|
17
|
+
"bugs": {
|
|
18
|
+
"url": "https://github.com/agentreel/agentreel/issues"
|
|
19
|
+
},
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "git+https://github.com/agentreel/agentreel.git",
|
|
23
|
+
"directory": "apps/local-agent"
|
|
24
|
+
},
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"author": "AgentReel <hello@agentreel.dev>",
|
|
27
|
+
"type": "module",
|
|
28
|
+
"bin": {
|
|
29
|
+
"agentreel": "dist/cli.js"
|
|
30
|
+
},
|
|
31
|
+
"files": [
|
|
32
|
+
"dist",
|
|
33
|
+
"README.md",
|
|
34
|
+
"LICENSE"
|
|
35
|
+
],
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"better-sqlite3": "^11.5.0",
|
|
38
|
+
"chokidar": "^4.0.1",
|
|
39
|
+
"commander": "^12.1.0",
|
|
40
|
+
"diff": "^7.0.0",
|
|
41
|
+
"diff-match-patch": "^1.0.5",
|
|
42
|
+
"ignore": "^6.0.2",
|
|
43
|
+
"nanoid": "^5.0.7",
|
|
44
|
+
"picocolors": "^1.1.1"
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"@types/better-sqlite3": "^7.6.11",
|
|
48
|
+
"@types/diff": "^7.0.0",
|
|
49
|
+
"@types/diff-match-patch": "^1.0.36",
|
|
50
|
+
"@types/node": "^20.16.5",
|
|
51
|
+
"tsup": "^8.3.0",
|
|
52
|
+
"typescript": "^5.6.2",
|
|
53
|
+
"@agentreel/shared-types": "0.0.0"
|
|
54
|
+
},
|
|
55
|
+
"engines": {
|
|
56
|
+
"node": ">=20"
|
|
57
|
+
},
|
|
58
|
+
"publishConfig": {
|
|
59
|
+
"access": "public"
|
|
60
|
+
},
|
|
61
|
+
"scripts": {
|
|
62
|
+
"build": "tsup",
|
|
63
|
+
"dev": "tsup --watch",
|
|
64
|
+
"start": "node dist/cli.js",
|
|
65
|
+
"typecheck": "tsc --noEmit"
|
|
66
|
+
}
|
|
67
|
+
}
|