tktuner 0.2.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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/index.ts","../package.json","../src/lib/version.ts","../src/cli/login.ts","../src/lib/api-client.ts","../src/lib/credentials.ts","../src/lib/paths.ts","../src/cli/logout.ts","../../core/src/analysis/waste.ts","../../core/src/analysis/cache.ts","../../core/src/analysis/classify.ts","../../core/src/analysis/quota.ts","../../core/src/merge.ts","../../core/src/pricing/prices.json","../../core/src/pricing/pricing.ts","../../core/src/report/format.ts","../../core/src/types.ts","../../core/src/report/model.ts","../../core/src/sync.ts","../src/cli/report.ts","../src/lib/prices-io.ts","../src/render/colors.ts","../src/render/table.ts","../src/render/terminal.ts","../src/store/db.ts","../src/store/schema.ts","../src/store/queries.ts","../src/cli/scan.ts","../src/adapters/claude-code/discover.ts","../src/adapters/claude-code/jsonl.ts","../src/adapters/claude-code/parse.ts","../src/adapters/claude-code/adapter.ts","../src/cli/sync.ts","../src/lib/privacy.ts","../src/cli/whoami.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport { CLI_VERSION } from \"../lib/version\";\nimport { login } from \"./login\";\nimport { logout } from \"./logout\";\nimport { report } from \"./report\";\nimport { scan } from \"./scan\";\nimport { sync } from \"./sync\";\nimport { whoami } from \"./whoami\";\n\nconst program = new Command();\n\nprogram\n .name(\"tktuner\")\n .description(\n \"Local-first token usage analytics for Claude Code power users.\\nScans ~/.claude/projects session logs and shows where you waste tokens and quota.\",\n )\n .version(CLI_VERSION);\n\nprogram\n .command(\"scan\")\n .description(\"Incrementally parse Claude Code session logs into the local database\")\n .option(\"--claude-dir <path>\", \"Claude projects directory (default: ~/.claude/projects)\")\n .option(\"--db <path>\", \"database file (default: ~/.local/share/tktuner/tktuner.db)\")\n .option(\"--full\", \"ignore incremental state and reparse every file\")\n .option(\"--verbose\", \"print each file as it is parsed\")\n .action(async (opts) => {\n await scan(opts);\n });\n\nprogram\n .command(\"report\")\n .description(\"Analyze scanned data: cost by model/phase, waste, cache health, quota pressure\")\n .option(\"--days <n>\", \"analysis window in days\", \"30\")\n .option(\"--all\", \"analyze the entire history\")\n .option(\"--project <filter>\", \"only sessions whose project name contains this substring\")\n .option(\"--json\", \"machine-readable JSON output\")\n .option(\"--no-color\", \"disable colors\")\n .option(\"--db <path>\", \"database file (default: ~/.local/share/tktuner/tktuner.db)\")\n .option(\"--prices <path>\", \"JSON file overriding the bundled model price table\")\n .option(\"--debug-session <id>\", \"per-turn phase breakdown for one session (id or slug prefix)\")\n .action(async (opts) => {\n await report(opts);\n });\n\nprogram\n .command(\"login\")\n .description(\"Connect this machine to your tktuner.com account (device flow)\")\n .option(\"--api <url>\", \"API base URL (default: https://tktuner.com)\")\n .action(async (opts) => {\n await login(opts);\n });\n\nprogram\n .command(\"logout\")\n .description(\"Disconnect this machine and delete local credentials\")\n .action(async () => {\n await logout();\n });\n\nprogram\n .command(\"whoami\")\n .description(\"Show the connected account, device and sync state\")\n .option(\"--db <path>\", \"database file (default: ~/.local/share/tktuner/tktuner.db)\")\n .action(async (opts) => {\n await whoami(opts);\n });\n\nprogram\n .command(\"sync\")\n .description(\"Scan locally, then upload new usage metadata (never content) to your dashboard\")\n .option(\"--quiet\", \"no output, always exit 0 (for the SessionEnd hook)\")\n .option(\"--full\", \"re-push the entire local database (idempotent server-side)\")\n .option(\"--dry-run\", \"show what would be uploaded without sending anything\")\n .option(\"--no-scan\", \"skip the local scan step\")\n .option(\"--db <path>\", \"database file (default: ~/.local/share/tktuner/tktuner.db)\")\n .option(\"--claude-dir <path>\", \"Claude projects directory (default: ~/.claude/projects)\")\n .option(\"--api <url>\", \"override the API base URL from credentials\")\n .action(async (opts) => {\n // commander turns --no-scan into { scan: false }\n await sync({ ...opts, noScan: opts.scan === false });\n });\n\nconst hook = program\n .command(\"hook\")\n .description(\"Manage the opt-in Claude Code SessionEnd hook (auto-sync when a session ends)\");\nhook\n .command(\"install\")\n .description(\"Add the SessionEnd hook to ~/.claude/settings.json (merge-safe, backed up)\")\n .option(\"--yes\", \"skip the confirmation prompt\")\n .action(async (opts) => {\n const { hookInstall } = await import(\"./hook\");\n await hookInstall(opts);\n });\nhook\n .command(\"uninstall\")\n .description(\"Remove only tktuner's hook entry, leaving everything else untouched\")\n .action(async () => {\n const { hookUninstall } = await import(\"./hook\");\n await hookUninstall();\n });\nhook\n .command(\"status\")\n .description(\"Show whether the auto-sync hook is installed\")\n .action(async () => {\n const { hookStatus } = await import(\"./hook\");\n hookStatus();\n });\n\nprogram.parseAsync(process.argv).catch((err: unknown) => {\n console.error(err instanceof Error ? err.message : String(err));\n process.exitCode = 1;\n});\n","{\n \"name\": \"tktuner\",\n \"version\": \"0.2.0\",\n \"description\": \"Local-first token usage analytics and cost optimization for Claude Code power users. Stop paying Fable prices for Haiku work.\",\n \"license\": \"MIT\",\n \"type\": \"module\",\n \"bin\": {\n \"tktuner\": \"dist/cli.js\"\n },\n \"files\": [\n \"dist\",\n \"README.md\",\n \"LICENSE\"\n ],\n \"engines\": {\n \"node\": \">=20\"\n },\n \"scripts\": {\n \"build\": \"tsup\",\n \"dev\": \"tsx src/cli/index.ts\",\n \"test\": \"vitest run\",\n \"test:watch\": \"vitest\",\n \"typecheck\": \"tsc --noEmit\",\n \"prepublishOnly\": \"pnpm test && pnpm build\"\n },\n \"keywords\": [\n \"claude-code\",\n \"claude\",\n \"tokens\",\n \"usage\",\n \"analytics\",\n \"cost\",\n \"cli\"\n ],\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/tktuner/tktuner.git\"\n },\n \"dependencies\": {\n \"better-sqlite3\": \"^12.2.0\",\n \"commander\": \"^14.0.0\",\n \"picocolors\": \"^1.1.1\"\n },\n \"devDependencies\": {\n \"@tktuner/core\": \"workspace:*\",\n \"@types/better-sqlite3\": \"^7.6.13\",\n \"@types/node\": \"^24.0.0\",\n \"tsup\": \"^8.5.0\",\n \"tsx\": \"^4.20.0\",\n \"typescript\": \"^5.6.3\",\n \"vitest\": \"^3.2.0\"\n }\n}\n","import pkg from \"../../package.json\";\n\nexport const CLI_VERSION: string = pkg.version;\n","// tktuner login — RFC-8628-style device flow against the TkTuner API.\nimport { spawn } from \"node:child_process\";\nimport { hostname, userInfo } from \"node:os\";\nimport pc from \"picocolors\";\nimport { apiCall, apiRequest } from \"../lib/api-client\";\nimport { readCredentials, writeCredentials } from \"../lib/credentials\";\nimport { CLI_VERSION } from \"../lib/version\";\n\nexport const DEFAULT_API_URL = \"https://tktuner.com\";\n\nexport interface LoginOptions {\n api?: string;\n}\n\nconst sleep = (ms: number): Promise<void> => new Promise((resolve) => setTimeout(resolve, ms));\n\n/** Best-effort browser open — the URL is ALWAYS printed (WSL2 often can't open). */\nfunction openBrowser(url: string): void {\n const candidates: string[][] =\n process.platform === \"darwin\"\n ? [[\"open\", url]]\n : process.platform === \"win32\"\n ? [[\"cmd\", \"/c\", \"start\", \"\", url]]\n : [\n [\"wslview\", url],\n [\"xdg-open\", url],\n ];\n for (const [cmd, ...args] of candidates) {\n try {\n const child = spawn(cmd as string, args, { stdio: \"ignore\", detached: true });\n child.on(\"error\", () => {});\n child.unref();\n return;\n } catch {\n // try the next opener\n }\n }\n}\n\nexport async function login(opts: LoginOptions): Promise<void> {\n const existing = readCredentials();\n if (existing) {\n console.log(\n `Already logged in (device ${pc.bold(existing.deviceName)} → ${existing.apiUrl}).\\n` +\n `Run ${pc.bold(\"tktuner logout\")} first to switch accounts.`,\n );\n return;\n }\n const apiUrl = opts.api ?? DEFAULT_API_URL;\n const deviceName = `${userInfo().username}@${hostname()}`.slice(0, 80);\n\n const start = await apiCall(\"POST\", apiUrl, \"/v1/device/code\", {\n body: { deviceName, cliVersion: CLI_VERSION },\n });\n const deviceCode = start.device_code as string;\n const userCode = start.user_code as string;\n const verifyUrl = (start.verification_uri_complete ?? start.verification_uri) as string;\n let intervalMs = ((start.interval as number) ?? 5) * 1000;\n const deadline = Date.now() + ((start.expires_in as number) ?? 900) * 1000;\n\n console.log(`\\nOpen ${pc.bold(pc.cyan(verifyUrl))}`);\n console.log(`and confirm this code: ${pc.bold(userCode)}\\n`);\n openBrowser(verifyUrl);\n process.stdout.write(\"Waiting for approval\");\n\n while (Date.now() < deadline) {\n await sleep(intervalMs);\n process.stdout.write(\".\");\n const { status, json } = await apiRequest(\"POST\", apiUrl, \"/v1/device/token\", {\n body: { device_code: deviceCode },\n });\n if (status === 200) {\n writeCredentials({\n apiUrl,\n deviceToken: json.device_token as string,\n deviceId: json.device_id as string,\n deviceName,\n });\n console.log(`\\n\\n${pc.green(\"Logged in.\")} Device ${pc.bold(deviceName)} is connected.`);\n console.log(`Run ${pc.bold(\"tktuner sync\")} to upload your usage metadata.`);\n return;\n }\n if (status === 202) continue;\n const code = ((json.error ?? {}) as { code?: string }).code;\n if (code === \"SLOW_DOWN\") {\n intervalMs += 2000;\n continue;\n }\n const message = ((json.error ?? {}) as { message?: string }).message ?? `HTTP ${status}`;\n console.error(`\\n${pc.red(\"Login failed:\")} ${message}`);\n process.exitCode = 1;\n return;\n }\n console.error(`\\n${pc.red(\"Login timed out.\")} Run tktuner login again.`);\n process.exitCode = 1;\n}\n","// Minimal fetch wrapper for the TkTuner API. Gzips JSON request bodies when\n// asked (sync payloads compress ~8x).\nimport { gzipSync } from \"node:zlib\";\n\nexport interface ApiResponse {\n status: number;\n json: Record<string, unknown>;\n}\n\nexport class ApiError extends Error {\n constructor(\n readonly status: number,\n readonly code: string,\n message: string,\n ) {\n super(message);\n }\n}\n\ninterface RequestOpts {\n body?: unknown;\n token?: string;\n gzip?: boolean;\n}\n\nexport async function apiRequest(\n method: \"GET\" | \"POST\" | \"DELETE\",\n apiUrl: string,\n path: string,\n opts: RequestOpts = {},\n): Promise<ApiResponse> {\n const headers: Record<string, string> = { accept: \"application/json\" };\n if (opts.token) headers.authorization = `Bearer ${opts.token}`;\n let body: string | Buffer | undefined;\n if (opts.body !== undefined) {\n headers[\"content-type\"] = \"application/json\";\n const raw = JSON.stringify(opts.body);\n if (opts.gzip) {\n headers[\"content-encoding\"] = \"gzip\";\n body = gzipSync(Buffer.from(raw, \"utf8\"));\n } else {\n body = raw;\n }\n }\n const res = await fetch(`${apiUrl.replace(/\\/$/, \"\")}${path}`, { method, headers, body });\n let json: Record<string, unknown> = {};\n try {\n json = (await res.json()) as Record<string, unknown>;\n } catch {\n // 204s and empty bodies are fine\n }\n return { status: res.status, json };\n}\n\n/** Throws ApiError on any non-2xx (use apiRequest directly to branch on status). */\nexport async function apiCall(\n method: \"GET\" | \"POST\" | \"DELETE\",\n apiUrl: string,\n path: string,\n opts: RequestOpts = {},\n): Promise<Record<string, unknown>> {\n const { status, json } = await apiRequest(method, apiUrl, path, opts);\n if (status >= 200 && status < 300) return json;\n const err = (json.error ?? {}) as { code?: string; message?: string };\n throw new ApiError(status, err.code ?? \"UNKNOWN\", err.message ?? `HTTP ${status}`);\n}\n","// Device credentials at ~/.local/share/tktuner/credentials.json (chmod 600).\nimport { chmodSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { dataDir } from \"./paths\";\n\nexport interface Credentials {\n apiUrl: string;\n deviceToken: string;\n deviceId: string;\n deviceName: string;\n}\n\nexport function credentialsPath(): string {\n return join(dataDir(), \"credentials.json\");\n}\n\nexport function readCredentials(): Credentials | null {\n try {\n const raw = readFileSync(credentialsPath(), \"utf8\");\n const parsed = JSON.parse(raw) as Partial<Credentials>;\n if (!parsed.apiUrl || !parsed.deviceToken || !parsed.deviceId) return null;\n return parsed as Credentials;\n } catch {\n return null;\n }\n}\n\nexport function writeCredentials(creds: Credentials): void {\n mkdirSync(dataDir(), { recursive: true });\n const path = credentialsPath();\n writeFileSync(path, `${JSON.stringify(creds, null, 2)}\\n`, { mode: 0o600 });\n chmodSync(path, 0o600); // mode above is ignored when the file already exists\n}\n\nexport function deleteCredentials(): void {\n if (existsSync(credentialsPath())) rmSync(credentialsPath());\n}\n","import { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\n/** tktuner data directory: $XDG_DATA_HOME/tktuner or ~/.local/share/tktuner. */\nexport function dataDir(): string {\n const xdg = process.env.XDG_DATA_HOME;\n const base = xdg && xdg.trim() !== \"\" ? xdg : join(homedir(), \".local\", \"share\");\n return join(base, \"tktuner\");\n}\n","// tktuner logout — revokes the device token (best-effort) and deletes credentials.\nimport pc from \"picocolors\";\nimport { apiCall } from \"../lib/api-client\";\nimport { deleteCredentials, readCredentials } from \"../lib/credentials\";\n\nexport async function logout(): Promise<void> {\n const creds = readCredentials();\n if (!creds) {\n console.log(\"Not logged in.\");\n return;\n }\n try {\n await apiCall(\"POST\", creds.apiUrl, \"/v1/device/revoke-self\", { token: creds.deviceToken });\n } catch {\n // offline or already revoked — deleting local credentials is what matters\n }\n deleteCredentials();\n console.log(`${pc.green(\"Logged out.\")} Device ${pc.bold(creds.deviceName)} was disconnected.`);\n}\n","import type { PriceTable } from \"../pricing/pricing\";\nimport type { Phase, TurnEvent } from \"../types\";\nimport type { ClassifiedTurn } from \"./classify\";\n\nexport function isPremiumModel(model: string): boolean {\n return (\n model.startsWith(\"claude-opus\") ||\n model.startsWith(\"claude-fable\") ||\n model.startsWith(\"claude-mythos\")\n );\n}\n\n/** Cheapest model that comfortably handles each phase. */\nexport const IDEAL_MODEL_BY_PHASE: Partial<Record<Phase, string>> = {\n exploration: \"claude-haiku-4-5\",\n execution: \"claude-sonnet-4-5\",\n};\n\n/** Waste attribution groups a session together with all its subagent runs. */\nexport function rootSessionId(turn: TurnEvent): string {\n return turn.parentSessionId ?? turn.sessionId;\n}\n\nexport interface PhaseModelCell {\n turns: number;\n costUsd: number;\n wasteUsd: number;\n tokens: number;\n}\n\nexport interface SessionAggregate {\n sessionId: string;\n project: string;\n slug?: string;\n firstTs: number;\n lastTs: number;\n turns: number;\n costUsd: number;\n wasteUsd: number;\n wasteExplorationUsd: number;\n wasteExecutionUsd: number;\n premiumTurns: number;\n models: Set<string>;\n}\n\nexport interface WasteReport {\n totalCostUsd: number;\n totalWasteUsd: number;\n wasteByPhase: { exploration: number; execution: number };\n fastModeExtraUsd: number;\n unpricedTurns: number;\n unpricedModels: string[];\n /** model -> phase -> aggregate (uses smoothed phases). */\n matrix: Map<string, Map<Phase, PhaseModelCell>>;\n perSession: Map<string, SessionAggregate>;\n perProject: Map<string, { costUsd: number; wasteUsd: number; turns: number }>;\n}\n\n/**\n * Core waste metric: for premium-model turns spent in exploration/execution,\n * the delta between what the turn cost and what the same token counts would\n * have cost on the phase-appropriate model. Approximation (stated in the\n * report): the cheaper model is assumed to produce the same token counts.\n */\nexport function computeWaste(classified: ClassifiedTurn[], prices: PriceTable): WasteReport {\n const report: WasteReport = {\n totalCostUsd: 0,\n totalWasteUsd: 0,\n wasteByPhase: { exploration: 0, execution: 0 },\n fastModeExtraUsd: 0,\n unpricedTurns: 0,\n unpricedModels: [],\n matrix: new Map(),\n perSession: new Map(),\n perProject: new Map(),\n };\n const unpricedModels = new Set<string>();\n\n for (const { turn, phaseSmoothed } of classified) {\n const cost = prices.turnCostUsd(turn);\n if (cost === null) {\n report.unpricedTurns++;\n unpricedModels.add(turn.model);\n continue;\n }\n report.totalCostUsd += cost;\n\n if (turn.speed === \"fast\") {\n const standard = prices.costAtModel(turn.model, turn);\n if (standard !== null) report.fastModeExtraUsd += Math.max(0, cost - standard);\n }\n\n let wasteUsd = 0;\n const idealModel = IDEAL_MODEL_BY_PHASE[phaseSmoothed];\n if (idealModel && isPremiumModel(turn.model)) {\n const idealCost = prices.costAtModel(idealModel, turn);\n if (idealCost !== null) {\n wasteUsd = Math.max(0, cost - idealCost);\n report.totalWasteUsd += wasteUsd;\n if (phaseSmoothed === \"exploration\") report.wasteByPhase.exploration += wasteUsd;\n if (phaseSmoothed === \"execution\") report.wasteByPhase.execution += wasteUsd;\n }\n }\n\n // model × phase matrix\n let phaseMap = report.matrix.get(turn.model);\n if (!phaseMap) {\n phaseMap = new Map();\n report.matrix.set(turn.model, phaseMap);\n }\n let cell = phaseMap.get(phaseSmoothed);\n if (!cell) {\n cell = { turns: 0, costUsd: 0, wasteUsd: 0, tokens: 0 };\n phaseMap.set(phaseSmoothed, cell);\n }\n cell.turns++;\n cell.costUsd += cost;\n cell.wasteUsd += wasteUsd;\n cell.tokens +=\n turn.usage.input + turn.usage.output + turn.usage.cacheRead + turn.usage.cacheWrite;\n\n // per root session (subagent cost rolls up into the spawning session)\n const sid = rootSessionId(turn);\n let sess = report.perSession.get(sid);\n if (!sess) {\n sess = {\n sessionId: sid,\n project: \"\",\n slug: undefined,\n firstTs: Infinity,\n lastTs: 0,\n turns: 0,\n costUsd: 0,\n wasteUsd: 0,\n wasteExplorationUsd: 0,\n wasteExecutionUsd: 0,\n premiumTurns: 0,\n models: new Set(),\n };\n report.perSession.set(sid, sess);\n }\n sess.turns++;\n sess.costUsd += cost;\n sess.wasteUsd += wasteUsd;\n if (phaseSmoothed === \"exploration\") sess.wasteExplorationUsd += wasteUsd;\n if (phaseSmoothed === \"execution\") sess.wasteExecutionUsd += wasteUsd;\n if (isPremiumModel(turn.model)) sess.premiumTurns++;\n sess.models.add(turn.model);\n if (turn.timestamp > 0) {\n sess.firstTs = Math.min(sess.firstTs, turn.timestamp);\n sess.lastTs = Math.max(sess.lastTs, turn.timestamp);\n }\n if (!sess.project && turn.projectPath) sess.project = turn.projectPath;\n if (!sess.slug && !turn.isSidechain && turn.slug) sess.slug = turn.slug;\n\n // per project\n const project = turn.projectPath || \"(unknown)\";\n let proj = report.perProject.get(project);\n if (!proj) {\n proj = { costUsd: 0, wasteUsd: 0, turns: 0 };\n report.perProject.set(project, proj);\n }\n proj.costUsd += cost;\n proj.wasteUsd += wasteUsd;\n proj.turns++;\n }\n\n report.unpricedModels = [...unpricedModels].sort();\n return report;\n}\n","import type { SideEvent, TurnEvent } from \"../types\";\nimport { rootSessionId } from \"./waste\";\n\nexport interface CacheStats {\n turns: number;\n input: number;\n cacheRead: number;\n cacheWrite: number;\n /** cacheRead / (cacheRead + cacheWrite + input); null when no input at all. */\n hitRate: number | null;\n}\n\nexport function cacheStats(turns: Iterable<TurnEvent>): CacheStats {\n const s: CacheStats = { turns: 0, input: 0, cacheRead: 0, cacheWrite: 0, hitRate: null };\n for (const t of turns) {\n s.turns++;\n s.input += t.usage.input;\n s.cacheRead += t.usage.cacheRead;\n s.cacheWrite += t.usage.cacheWrite;\n }\n const denom = s.input + s.cacheRead + s.cacheWrite;\n s.hitRate = denom > 0 ? s.cacheRead / denom : null;\n return s;\n}\n\nexport function cacheByProject(turns: TurnEvent[]): Map<string, CacheStats> {\n return groupStats(turns, (t) => t.projectPath || \"(unknown)\");\n}\n\nexport function cacheBySession(turns: TurnEvent[]): Map<string, CacheStats> {\n return groupStats(turns, rootSessionId);\n}\n\nfunction groupStats(turns: TurnEvent[], key: (t: TurnEvent) => string): Map<string, CacheStats> {\n const groups = new Map<string, TurnEvent[]>();\n for (const t of turns) {\n const k = key(t);\n const list = groups.get(k);\n if (list) list.push(t);\n else groups.set(k, [t]);\n }\n const out = new Map<string, CacheStats>();\n for (const [k, list] of groups) out.set(k, cacheStats(list));\n return out;\n}\n\nexport interface CompactionSummary {\n total: number;\n bySession: Map<string, number>;\n /** Tokens reported dropped by compaction (pre - post), summed. */\n tokensCompacted: number;\n}\n\nexport function summarizeCompactions(events: SideEvent[]): CompactionSummary {\n const out: CompactionSummary = { total: 0, bySession: new Map(), tokensCompacted: 0 };\n for (const e of events) {\n if (e.kind !== \"compact_boundary\") continue;\n out.total++;\n out.bySession.set(e.sessionId, (out.bySession.get(e.sessionId) ?? 0) + 1);\n const pre = typeof e.data.preTokens === \"number\" ? e.data.preTokens : 0;\n const post = typeof e.data.postTokens === \"number\" ? e.data.postTokens : 0;\n if (pre > post) out.tokensCompacted += pre - post;\n }\n return out;\n}\n","import type { Phase, SideEvent, TurnEvent } from \"../types\";\n\n/** Tools that mutate state — their presence marks an execution turn. */\nexport const WRITE_TOOLS = new Set([\n \"Edit\",\n \"Write\",\n \"NotebookEdit\",\n \"Bash\",\n \"MultiEdit\",\n \"mcp__ide__executeCode\",\n]);\n\n/** Read-only tools — exploration when nothing stronger is present. */\nexport const READ_TOOLS = new Set([\n \"Read\",\n \"Grep\",\n \"Glob\",\n \"LS\",\n \"WebFetch\",\n \"WebSearch\",\n \"ToolSearch\",\n \"TaskOutput\",\n \"BashOutput\",\n \"NotebookRead\",\n \"Monitor\",\n]);\n\n/** Subagent spawners. 'Task' is the pre-2.1 name, kept for old logs. */\nexport const DELEGATION_TOOLS = new Set([\"Agent\", \"Workflow\", \"Task\"]);\n\nexport const PLANNING_TOOLS = new Set([\"ExitPlanMode\", \"EnterPlanMode\", \"exit_plan_mode\"]);\n\n/** No-tool turns with at least this much thinking are treated as planning. */\nexport const THINKING_PLANNING_MIN_CHARS = 1500;\n\n/** Odd-sized majority-vote window used to smooth single-turn phase islands. */\nexport const SMOOTHING_WINDOW = 5;\n\nexport interface ClassifiedTurn {\n turn: TurnEvent;\n phase: Phase;\n phaseSmoothed: Phase;\n}\n\ninterface Span {\n start: number;\n end: number;\n}\n\n/**\n * Reconstructs plan-mode intervals per session from plan_mode /\n * plan_mode_exit events. An unclosed span extends to the end of the session.\n */\nexport function buildPlanSpans(events: SideEvent[]): Map<string, Span[]> {\n const bySession = new Map<string, SideEvent[]>();\n for (const e of events) {\n if (e.kind !== \"plan_mode\" && e.kind !== \"plan_mode_exit\") continue;\n if (e.timestamp === undefined) continue;\n const list = bySession.get(e.sessionId);\n if (list) list.push(e);\n else bySession.set(e.sessionId, [e]);\n }\n const spans = new Map<string, Span[]>();\n for (const [sessionId, list] of bySession) {\n list.sort((a, b) => (a.timestamp ?? 0) - (b.timestamp ?? 0));\n const out: Span[] = [];\n let openStart: number | undefined;\n for (const e of list) {\n if (e.kind === \"plan_mode\") {\n openStart ??= e.timestamp;\n } else if (openStart !== undefined) {\n out.push({ start: openStart, end: e.timestamp ?? openStart });\n openStart = undefined;\n }\n }\n if (openStart !== undefined) out.push({ start: openStart, end: Infinity });\n spans.set(sessionId, out);\n }\n return spans;\n}\n\nexport function rawPhase(turn: TurnEvent, inPlanSpan: boolean): Phase {\n if (inPlanSpan || turn.tools.some((t) => PLANNING_TOOLS.has(t))) return \"planning\";\n if (turn.tools.some((t) => DELEGATION_TOOLS.has(t))) return \"delegation\";\n if (turn.tools.some((t) => WRITE_TOOLS.has(t))) return \"execution\";\n if (turn.tools.some((t) => READ_TOOLS.has(t))) return \"exploration\";\n if (turn.numToolUses === 0 && turn.thinkingChars >= THINKING_PLANNING_MIN_CHARS) {\n return \"planning\";\n }\n return \"chat\";\n}\n\n/**\n * Classifies every turn, then majority-smooths within each session so that\n * one-off islands (a single Read amid heavy editing) adopt the surrounding\n * phase. Ties keep the raw phase.\n */\nexport function classifyTurns(turns: TurnEvent[], events: SideEvent[]): ClassifiedTurn[] {\n const spans = buildPlanSpans(events);\n const bySession = new Map<string, TurnEvent[]>();\n for (const t of turns) {\n const list = bySession.get(t.sessionId);\n if (list) list.push(t);\n else bySession.set(t.sessionId, [t]);\n }\n\n const out: ClassifiedTurn[] = [];\n for (const [sessionId, sessionTurns] of bySession) {\n sessionTurns.sort((a, b) => a.timestamp - b.timestamp || (a.uuid < b.uuid ? -1 : 1));\n const sessionSpans = spans.get(sessionId) ?? [];\n const raw = sessionTurns.map((t) =>\n rawPhase(\n t,\n sessionSpans.some((s) => t.timestamp >= s.start && t.timestamp <= s.end),\n ),\n );\n const smoothed = smoothPhases(raw);\n for (let i = 0; i < sessionTurns.length; i++) {\n out.push({\n turn: sessionTurns[i] as TurnEvent,\n phase: raw[i] as Phase,\n phaseSmoothed: smoothed[i] as Phase,\n });\n }\n }\n return out;\n}\n\nexport function smoothPhases(raw: Phase[]): Phase[] {\n const half = Math.floor(SMOOTHING_WINDOW / 2);\n return raw.map((phase, i) => {\n const counts = new Map<Phase, number>();\n const lo = Math.max(0, i - half);\n const hi = Math.min(raw.length - 1, i + half);\n for (let j = lo; j <= hi; j++) {\n const p = raw[j] as Phase;\n counts.set(p, (counts.get(p) ?? 0) + 1);\n }\n let maxN = 0;\n for (const n of counts.values()) maxN = Math.max(maxN, n);\n const winners = [...counts.entries()].filter(([, n]) => n === maxN).map(([p]) => p);\n return winners.length === 1 ? (winners[0] as Phase) : phase;\n });\n}\n","import type { PriceTable } from \"../pricing/pricing\";\nimport type { TurnEvent } from \"../types\";\n\nexport const WINDOW_MS = 5 * 60 * 60 * 1000;\n\n/**\n * Anthropic-style rolling 5h session window: a window opens at the first\n * activity after the previous window expired and lasts exactly 5 hours.\n * This is an estimate of quota accounting, not an official reconstruction.\n */\nexport interface QuotaWindow {\n start: number;\n end: number;\n turns: number;\n totalTokens: number;\n outputTokens: number;\n costUsd: number;\n unpricedTurns: number;\n}\n\nexport function build5hWindows(turns: TurnEvent[], prices: PriceTable): QuotaWindow[] {\n const sorted = turns.filter((t) => t.timestamp > 0).sort((a, b) => a.timestamp - b.timestamp);\n const windows: QuotaWindow[] = [];\n let current: QuotaWindow | undefined;\n for (const t of sorted) {\n if (!current || t.timestamp >= current.end) {\n current = {\n start: t.timestamp,\n end: t.timestamp + WINDOW_MS,\n turns: 0,\n totalTokens: 0,\n outputTokens: 0,\n costUsd: 0,\n unpricedTurns: 0,\n };\n windows.push(current);\n }\n current.turns++;\n current.totalTokens += t.usage.input + t.usage.output + t.usage.cacheRead + t.usage.cacheWrite;\n current.outputTokens += t.usage.output;\n const cost = prices.turnCostUsd(t);\n if (cost === null) current.unpricedTurns++;\n else current.costUsd += cost;\n }\n return windows;\n}\n\nexport interface WeekBucket {\n weekKey: string;\n start: number;\n turns: number;\n totalTokens: number;\n costUsd: number;\n}\n\nexport function weeklyRollup(turns: TurnEvent[], prices: PriceTable): WeekBucket[] {\n const byWeek = new Map<string, WeekBucket>();\n for (const t of turns) {\n if (t.timestamp <= 0) continue;\n const weekKey = isoWeekKey(t.timestamp);\n let bucket = byWeek.get(weekKey);\n if (!bucket) {\n bucket = { weekKey, start: t.timestamp, turns: 0, totalTokens: 0, costUsd: 0 };\n byWeek.set(weekKey, bucket);\n }\n bucket.start = Math.min(bucket.start, t.timestamp);\n bucket.turns++;\n bucket.totalTokens += t.usage.input + t.usage.output + t.usage.cacheRead + t.usage.cacheWrite;\n const cost = prices.turnCostUsd(t);\n if (cost !== null) bucket.costUsd += cost;\n }\n return [...byWeek.values()].sort((a, b) => a.start - b.start);\n}\n\n/** ISO-8601 week key (local time), e.g. 2026-W28. */\nexport function isoWeekKey(ts: number): string {\n const d = new Date(ts);\n const thursday = new Date(d.getFullYear(), d.getMonth(), d.getDate());\n const weekday = (thursday.getDay() + 6) % 7; // Monday = 0\n thursday.setDate(thursday.getDate() - weekday + 3);\n const year = thursday.getFullYear();\n const jan4 = new Date(year, 0, 4);\n const week1Monday = new Date(year, 0, 4 - ((jan4.getDay() + 6) % 7));\n const week = Math.round((thursday.getTime() - week1Monday.getTime()) / (7 * 86400000)) + 1;\n return `${year}-W${String(week).padStart(2, \"0\")}`;\n}\n","import type { TurnEvent } from \"./types\";\n\n/**\n * Collapses per-content-block lines into billable turns. Usage is taken as a\n * per-field max across the group (lines repeat the same final usage object);\n * tool names and thinking/text lengths are aggregated across the group.\n */\nexport function mergeTurnLines(lines: TurnEvent[]): TurnEvent[] {\n const groups = new Map<string, TurnEvent[]>();\n const order: string[] = [];\n for (const line of lines) {\n const key = line.messageId ?? line.uuid;\n const group = groups.get(key);\n if (group) group.push(line);\n else {\n groups.set(key, [line]);\n order.push(key);\n }\n }\n return order.map((key) => {\n const group = groups.get(key) as TurnEvent[];\n const first = group[0] as TurnEvent;\n if (group.length === 1) return first;\n const timestamps = group.map((l) => l.timestamp).filter((t) => t > 0);\n return {\n ...first,\n usage: {\n input: Math.max(...group.map((l) => l.usage.input)),\n output: Math.max(...group.map((l) => l.usage.output)),\n cacheRead: Math.max(...group.map((l) => l.usage.cacheRead)),\n cacheWrite: Math.max(...group.map((l) => l.usage.cacheWrite)),\n cacheWrite5m: Math.max(...group.map((l) => l.usage.cacheWrite5m)),\n cacheWrite1h: Math.max(...group.map((l) => l.usage.cacheWrite1h)),\n },\n tools: group.flatMap((l) => l.tools),\n numToolUses: group.reduce((n, l) => n + l.numToolUses, 0),\n thinkingChars: group.reduce((n, l) => n + l.thinkingChars, 0),\n textChars: group.reduce((n, l) => n + l.textChars, 0),\n timestamp: timestamps.length > 0 ? Math.min(...timestamps) : first.timestamp,\n stopReason: [...group].reverse().find((l) => l.stopReason)?.stopReason,\n speed: group.find((l) => l.speed)?.speed,\n inferenceGeo: group.find((l) => l.inferenceGeo)?.inferenceGeo,\n };\n });\n}\n","{\n \"version\": \"2026-07-12\",\n \"source\": \"https://platform.claude.com/docs/en/about-claude/pricing\",\n \"currency\": \"USD\",\n \"per_mtok\": {\n \"claude-fable-5\": {\n \"input\": 10,\n \"output\": 50,\n \"cache_read\": 1,\n \"cache_write_5m\": 12.5,\n \"cache_write_1h\": 20\n },\n \"claude-mythos-5\": {\n \"input\": 10,\n \"output\": 50,\n \"cache_read\": 1,\n \"cache_write_5m\": 12.5,\n \"cache_write_1h\": 20\n },\n \"claude-opus-4-8\": {\n \"input\": 5,\n \"output\": 25,\n \"cache_read\": 0.5,\n \"cache_write_5m\": 6.25,\n \"cache_write_1h\": 10,\n \"fast_input\": 10,\n \"fast_output\": 50\n },\n \"claude-opus-4-7\": {\n \"input\": 5,\n \"output\": 25,\n \"cache_read\": 0.5,\n \"cache_write_5m\": 6.25,\n \"cache_write_1h\": 10,\n \"fast_input\": 30,\n \"fast_output\": 150\n },\n \"claude-opus-4-6\": {\n \"input\": 5,\n \"output\": 25,\n \"cache_read\": 0.5,\n \"cache_write_5m\": 6.25,\n \"cache_write_1h\": 10\n },\n \"claude-opus-4-5\": {\n \"input\": 5,\n \"output\": 25,\n \"cache_read\": 0.5,\n \"cache_write_5m\": 6.25,\n \"cache_write_1h\": 10\n },\n \"claude-opus-4-1\": {\n \"input\": 15,\n \"output\": 75,\n \"cache_read\": 1.5,\n \"cache_write_5m\": 18.75,\n \"cache_write_1h\": 30\n },\n \"claude-opus-4\": {\n \"input\": 15,\n \"output\": 75,\n \"cache_read\": 1.5,\n \"cache_write_5m\": 18.75,\n \"cache_write_1h\": 30\n },\n \"claude-sonnet-5\": [\n {\n \"until\": \"2026-09-01\",\n \"input\": 2,\n \"output\": 10,\n \"cache_read\": 0.2,\n \"cache_write_5m\": 2.5,\n \"cache_write_1h\": 4\n },\n {\n \"from\": \"2026-09-01\",\n \"input\": 3,\n \"output\": 15,\n \"cache_read\": 0.3,\n \"cache_write_5m\": 3.75,\n \"cache_write_1h\": 6\n }\n ],\n \"claude-sonnet-4-6\": {\n \"input\": 3,\n \"output\": 15,\n \"cache_read\": 0.3,\n \"cache_write_5m\": 3.75,\n \"cache_write_1h\": 6\n },\n \"claude-sonnet-4-5\": {\n \"input\": 3,\n \"output\": 15,\n \"cache_read\": 0.3,\n \"cache_write_5m\": 3.75,\n \"cache_write_1h\": 6\n },\n \"claude-sonnet-4\": {\n \"input\": 3,\n \"output\": 15,\n \"cache_read\": 0.3,\n \"cache_write_5m\": 3.75,\n \"cache_write_1h\": 6\n },\n \"claude-3-7-sonnet\": {\n \"input\": 3,\n \"output\": 15,\n \"cache_read\": 0.3,\n \"cache_write_5m\": 3.75,\n \"cache_write_1h\": 6\n },\n \"claude-3-5-sonnet\": {\n \"input\": 3,\n \"output\": 15,\n \"cache_read\": 0.3,\n \"cache_write_5m\": 3.75,\n \"cache_write_1h\": 6\n },\n \"claude-haiku-4-5\": {\n \"input\": 1,\n \"output\": 5,\n \"cache_read\": 0.1,\n \"cache_write_5m\": 1.25,\n \"cache_write_1h\": 2\n },\n \"claude-haiku-3-5\": {\n \"input\": 0.8,\n \"output\": 4,\n \"cache_read\": 0.08,\n \"cache_write_5m\": 1,\n \"cache_write_1h\": 1.6\n },\n \"claude-3-5-haiku\": {\n \"input\": 0.8,\n \"output\": 4,\n \"cache_read\": 0.08,\n \"cache_write_5m\": 1,\n \"cache_write_1h\": 1.6\n },\n \"claude-3-haiku\": {\n \"input\": 0.25,\n \"output\": 1.25,\n \"cache_read\": 0.03,\n \"cache_write_5m\": 0.3,\n \"cache_write_1h\": 0.5\n },\n \"claude-3-opus\": {\n \"input\": 15,\n \"output\": 75,\n \"cache_read\": 1.5,\n \"cache_write_5m\": 18.75,\n \"cache_write_1h\": 30\n }\n }\n}\n","import type { TurnEvent } from \"../types\";\nimport bundledPrices from \"./prices.json\";\n\nexport interface RawPrice {\n input: number | null;\n output: number | null;\n cache_read?: number | null;\n cache_write_5m?: number | null;\n cache_write_1h?: number | null;\n /** Fast-mode premium rates (cache multipliers stack on these). */\n fast_input?: number | null;\n fast_output?: number | null;\n /** Optional validity window (ISO dates) for time-dependent pricing. */\n from?: string;\n until?: string;\n}\n\nexport type PriceEntry = RawPrice | RawPrice[];\n\nexport interface PricesFile {\n version: string;\n source?: string;\n currency?: string;\n per_mtok: Record<string, PriceEntry>;\n}\n\nexport interface ResolvedPrice {\n input: number;\n output: number;\n cacheRead: number;\n cacheWrite5m: number;\n cacheWrite1h: number;\n}\n\nexport interface PriceContext {\n ts?: number;\n speed?: string;\n inferenceGeo?: string;\n}\n\nconst US_GEO_MULTIPLIER = 1.1;\n\nexport class PriceTable {\n private readonly keysByLength: string[];\n\n constructor(private readonly file: PricesFile) {\n this.keysByLength = Object.keys(file.per_mtok).sort((a, b) => b.length - a.length);\n }\n\n static bundled(): PriceTable {\n return new PriceTable(bundledPrices as PricesFile);\n }\n\n /** Bundled table with a user's partial table merged on top (per-model override). */\n static withOverrideData(raw: Partial<PricesFile>): PriceTable {\n const base = bundledPrices as PricesFile;\n return new PriceTable({\n ...base,\n ...raw,\n per_mtok: { ...base.per_mtok, ...(raw.per_mtok ?? {}) },\n });\n }\n\n get version(): string {\n return this.file.version;\n }\n\n /**\n * Model id -> price entry via exact match, then longest-prefix match (covers\n * date-stamped ids like claude-haiku-4-5-20251001). Null when unknown.\n */\n resolve(model: string, ctx: PriceContext = {}): ResolvedPrice | null {\n const entry = this.file.per_mtok[model] ?? this.prefixMatch(model);\n if (!entry) return null;\n const variant = pickVariant(entry, ctx.ts);\n if (!variant || variant.input == null || variant.output == null) return null;\n\n let price: ResolvedPrice;\n if (ctx.speed === \"fast\" && variant.fast_input != null && variant.fast_output != null) {\n // Cache multipliers stack on top of fast-mode pricing.\n price = {\n input: variant.fast_input,\n output: variant.fast_output,\n cacheRead: variant.fast_input * 0.1,\n cacheWrite5m: variant.fast_input * 1.25,\n cacheWrite1h: variant.fast_input * 2,\n };\n } else {\n price = {\n input: variant.input,\n output: variant.output,\n cacheRead: variant.cache_read ?? variant.input * 0.1,\n cacheWrite5m: variant.cache_write_5m ?? variant.input * 1.25,\n cacheWrite1h: variant.cache_write_1h ?? variant.input * 2,\n };\n }\n if (ctx.inferenceGeo === \"us\") {\n price = {\n input: price.input * US_GEO_MULTIPLIER,\n output: price.output * US_GEO_MULTIPLIER,\n cacheRead: price.cacheRead * US_GEO_MULTIPLIER,\n cacheWrite5m: price.cacheWrite5m * US_GEO_MULTIPLIER,\n cacheWrite1h: price.cacheWrite1h * US_GEO_MULTIPLIER,\n };\n }\n return price;\n }\n\n /** Cost of one turn in USD. 0 for zero-usage turns; null when unpriceable. */\n turnCostUsd(turn: TurnEvent): number | null {\n return this.costAtModel(turn.model, turn, { keepSpeed: true });\n }\n\n /**\n * Cost of a turn's token counts billed as `model` — used for the \"what if\n * this ran on the ideal model\" comparison. Keeps the turn's inference geo\n * (switching model would not change it) but assumes standard speed unless\n * `keepSpeed` is set.\n */\n costAtModel(model: string, turn: TurnEvent, opts: { keepSpeed?: boolean } = {}): number | null {\n const u = turn.usage;\n if (u.input + u.output + u.cacheRead + u.cacheWrite === 0) return 0;\n const price = this.resolve(model, {\n ts: turn.timestamp > 0 ? turn.timestamp : undefined,\n speed: opts.keepSpeed ? turn.speed : undefined,\n inferenceGeo: turn.inferenceGeo,\n });\n if (!price) return null;\n // Prefer the 5m/1h breakdown when it accounts for the total; otherwise\n // bill all cache writes at the (cheaper) 5m rate — conservative.\n let write5m = u.cacheWrite5m;\n let write1h = u.cacheWrite1h;\n if (write5m + write1h !== u.cacheWrite) {\n write5m = u.cacheWrite;\n write1h = 0;\n }\n return (\n (u.input * price.input +\n u.output * price.output +\n u.cacheRead * price.cacheRead +\n write5m * price.cacheWrite5m +\n write1h * price.cacheWrite1h) /\n 1e6\n );\n }\n\n private prefixMatch(model: string): PriceEntry | undefined {\n for (const key of this.keysByLength) {\n if (model.startsWith(key)) return this.file.per_mtok[key];\n }\n return undefined;\n }\n}\n\nfunction pickVariant(entry: PriceEntry, ts?: number): RawPrice | undefined {\n if (!Array.isArray(entry)) return entry;\n if (entry.length === 0) return undefined;\n const at = ts ?? Date.now();\n for (const v of entry) {\n const from = v.from ? Date.parse(v.from) : -Infinity;\n const until = v.until ? Date.parse(v.until) : Infinity;\n if (at >= from && at < until) return v;\n }\n return entry[entry.length - 1];\n}\n","export function fmtUsd(n: number | null | undefined): string {\n if (n === null || n === undefined) return \"n/a\";\n if (n === 0) return \"$0.00\";\n if (n > 0 && n < 0.01) return \"<$0.01\";\n return `$${n.toLocaleString(\"en-US\", { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;\n}\n\nexport function fmtTokens(n: number): string {\n if (n < 1000) return String(n);\n if (n < 1e6) return `${(n / 1e3).toFixed(1)}k`;\n if (n < 1e9) return `${(n / 1e6).toFixed(1)}M`;\n return `${(n / 1e9).toFixed(2)}B`;\n}\n\nexport function fmtPct(x: number | null | undefined): string {\n if (x === null || x === undefined) return \"—\";\n return `${Math.round(x * 100)}%`;\n}\n\nexport function fmtDate(ts: number): string {\n if (!Number.isFinite(ts) || ts <= 0) return \"—\";\n const d = new Date(ts);\n const mm = String(d.getMonth() + 1).padStart(2, \"0\");\n const dd = String(d.getDate()).padStart(2, \"0\");\n return `${d.getFullYear()}-${mm}-${dd}`;\n}\n\nexport function fmtDateTime(ts: number): string {\n if (!Number.isFinite(ts) || ts <= 0) return \"—\";\n const d = new Date(ts);\n const mm = String(d.getMonth() + 1).padStart(2, \"0\");\n const dd = String(d.getDate()).padStart(2, \"0\");\n const hh = String(d.getHours()).padStart(2, \"0\");\n const mi = String(d.getMinutes()).padStart(2, \"0\");\n return `${mm}-${dd} ${hh}:${mi}`;\n}\n\nexport function fmtTime(ts: number): string {\n if (!Number.isFinite(ts) || ts <= 0) return \"—\";\n const d = new Date(ts);\n return `${String(d.getHours()).padStart(2, \"0\")}:${String(d.getMinutes()).padStart(2, \"0\")}:${String(d.getSeconds()).padStart(2, \"0\")}`;\n}\n\nexport function shortId(id: string): string {\n return id.length > 8 ? id.slice(0, 8) : id;\n}\n\nexport function truncate(s: string, max: number): string {\n return s.length <= max ? s : `${s.slice(0, Math.max(0, max - 1))}…`;\n}\n\nexport function basename(p: string): string {\n if (!p) return \"—\";\n const parts = p.split(\"/\").filter(Boolean);\n return parts.length > 0 ? (parts[parts.length - 1] as string) : p;\n}\n\n/** claude-opus-4-8-20260101 -> opus-4-8 (display only). */\nexport function prettyModel(model: string): string {\n return model.replace(/^claude-/, \"\").replace(/-\\d{8}$/, \"\");\n}\n","/**\n * Agent-agnostic core types. Every input adapter (Claude Code today, Codex\n * later) normalizes its native log format into TurnEvent/SideEvent, so the\n * store, analysis and report layers never touch adapter-specific shapes.\n */\n\nexport type AgentKind = \"claude-code\" | \"codex\";\n\nexport const PHASES = [\"exploration\", \"planning\", \"execution\", \"delegation\", \"chat\"] as const;\nexport type Phase = (typeof PHASES)[number];\n\nexport interface TokenUsage {\n input: number;\n output: number;\n cacheRead: number;\n /** Total cache-creation tokens (5m + 1h when a breakdown exists). */\n cacheWrite: number;\n cacheWrite5m: number;\n cacheWrite1h: number;\n}\n\n/**\n * One billable assistant turn. Privacy invariant: no message text, thinking\n * content or tool inputs — only numbers, names and identifiers.\n */\nexport interface TurnEvent {\n agent: AgentKind;\n uuid: string;\n /**\n * API message id. Claude Code writes one JSONL line PER CONTENT BLOCK of a\n * response, each repeating the same usage — lines sharing a messageId are\n * one billable turn and must be merged before any cost math.\n */\n messageId?: string;\n requestId?: string;\n sessionId: string;\n /** For subagent turns: the session that spawned this agent. */\n parentSessionId?: string;\n /** For subagent turns: stable agent id (matches the log filename). */\n agentId?: string;\n isSidechain: boolean;\n /** Real project root, taken from the `cwd` field of the log line. */\n projectPath: string;\n /** Human-friendly random session slug (not content-derived). */\n slug?: string;\n /** Epoch milliseconds (UTC). 0 when the line had no parseable timestamp. */\n timestamp: number;\n model: string;\n usage: TokenUsage;\n /** tool_use block names, in order of appearance. */\n tools: string[];\n numToolUses: number;\n thinkingChars: number;\n textChars: number;\n stopReason?: string;\n gitBranch?: string;\n cliVersion?: string;\n /** usage.speed — 'fast' turns bill at fast-mode premium rates. */\n speed?: string;\n /** usage.inference_geo — 'us' applies a 1.1x pricing multiplier. */\n inferenceGeo?: string;\n filePath: string;\n}\n\nexport type SideEventKind =\n | \"plan_mode\"\n | \"plan_mode_exit\"\n | \"compact_boundary\"\n | \"turn_duration\"\n | \"agent_result\";\n\n/**\n * Non-turn signal extracted from the logs (plan-mode boundaries, compaction,\n * subagent result summaries). `data` holds a whitelisted, content-free payload.\n */\nexport interface SideEvent {\n kind: SideEventKind;\n sessionId: string;\n timestamp?: number;\n filePath: string;\n data: Record<string, unknown>;\n}\n\nexport type LogFileKind = \"session\" | \"subagent\" | \"other\";\n\nexport interface DiscoveredFile {\n path: string;\n kind: LogFileKind;\n sizeBytes: number;\n /** Truncated to integer ms so equality checks match the stored value. */\n mtimeMs: number;\n sessionId?: string;\n agentId?: string;\n parentSessionId?: string;\n}\n\nexport interface ParseBatch {\n turns: TurnEvent[];\n events: SideEvent[];\n /**\n * Absolute byte offset after the last newline-terminated line. Safe resume\n * point: an unterminated trailing line is never included here.\n */\n bytesConsumed: number;\n linesParsed: number;\n parseErrors: number;\n unknownTypes: Record<string, number>;\n}\n\nexport interface AgentAdapter {\n readonly kind: AgentKind;\n detect(rootOverride?: string): { found: boolean; rootDirs: string[] };\n listLogFiles(rootDirs: string[]): DiscoveredFile[];\n parseFile(file: DiscoveredFile, fromOffset: number): AsyncIterable<ParseBatch>;\n}\n","import {\n cacheByProject,\n cacheBySession,\n cacheStats,\n summarizeCompactions,\n} from \"../analysis/cache\";\nimport { classifyTurns } from \"../analysis/classify\";\nimport { build5hWindows, WINDOW_MS, weeklyRollup } from \"../analysis/quota\";\nimport {\n computeWaste,\n isPremiumModel,\n rootSessionId,\n type SessionAggregate,\n} from \"../analysis/waste\";\nimport type { PriceTable } from \"../pricing/pricing\";\nimport { PHASES, type Phase, type SideEvent, type TurnEvent } from \"../types\";\nimport { basename, fmtDate, fmtDateTime, fmtPct, fmtTokens, fmtUsd, shortId } from \"./format\";\n\nexport interface ReportRange {\n days: number | null;\n project: string | null;\n}\n\nexport interface ReportModel {\n generatedAt: string;\n priceTableVersion: string;\n range: { days: number | null; since: string | null; until: string; project: string | null };\n overview: {\n sessions: number;\n turns: number;\n subagentTurns: number;\n projects: number;\n totalTokens: number;\n tokens: { input: number; output: number; cacheRead: number; cacheWrite: number };\n totalCostUsd: number;\n unpricedTurns: number;\n unpricedModels: string[];\n };\n byModel: Array<{\n model: string;\n turns: number;\n input: number;\n output: number;\n cacheRead: number;\n cacheWrite: number;\n costUsd: number | null;\n costShare: number | null;\n }>;\n phaseMatrix: Array<{\n model: string;\n phases: Record<Phase, { turns: number; costUsd: number; wasteUsd: number }>;\n }>;\n waste: {\n totalUsd: number;\n explorationUsd: number;\n executionUsd: number;\n pctOfSpend: number | null;\n fastModeExtraUsd: number;\n assumption: string;\n };\n cache: {\n overall: { hitRate: number | null; input: number; cacheRead: number; cacheWrite: number };\n byProject: Array<{\n project: string;\n turns: number;\n hitRate: number | null;\n cacheRead: number;\n cacheWrite: number;\n }>;\n compactions: { total: number; tokensCompacted: number; sessionsAffected: number };\n };\n topSessions: Array<{\n sessionId: string;\n label: string;\n project: string;\n date: string;\n turns: number;\n costUsd: number;\n wasteUsd: number;\n antiPattern: string;\n }>;\n subagentLeaks: Array<{ agentType: string; model: string; runs: number; costUsd: number }>;\n quota: {\n windowHours: number;\n last7d: Array<{\n start: string;\n startTs: number;\n turns: number;\n totalTokens: number;\n costUsd: number;\n }>;\n weekly: Array<{ week: string; turns: number; totalTokens: number; costUsd: number }>;\n note: string;\n };\n recommendations: string[];\n}\n\nexport const WASTE_EPSILON = 0.005;\nconst QUOTA_NOTE =\n \"Estimated reconstruction of Anthropic's rolling 5h usage windows — not official quota accounting.\";\nconst WASTE_ASSUMPTION =\n \"Waste assumes the cheaper model would consume the same token counts; actual savings vary.\";\n\nexport function buildReport(\n turns: TurnEvent[],\n events: SideEvent[],\n prices: PriceTable,\n range: ReportRange,\n): ReportModel {\n const sessionIds = new Set(turns.map((t) => t.sessionId));\n const relevantEvents = events.filter((e) => sessionIds.has(e.sessionId));\n const classified = classifyTurns(turns, events);\n const waste = computeWaste(classified, prices);\n\n // ---- overview -----------------------------------------------------------\n const rootSessions = new Set<string>();\n const projects = new Set<string>();\n const tokens = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };\n let subagentTurns = 0;\n let minTs = Infinity;\n let maxTs = 0;\n for (const t of turns) {\n rootSessions.add(rootSessionId(t));\n if (t.projectPath) projects.add(t.projectPath);\n tokens.input += t.usage.input;\n tokens.output += t.usage.output;\n tokens.cacheRead += t.usage.cacheRead;\n tokens.cacheWrite += t.usage.cacheWrite;\n if (t.isSidechain) subagentTurns++;\n if (t.timestamp > 0) {\n minTs = Math.min(minTs, t.timestamp);\n maxTs = Math.max(maxTs, t.timestamp);\n }\n }\n const totalTokens = tokens.input + tokens.output + tokens.cacheRead + tokens.cacheWrite;\n\n // ---- by model ------------------------------------------------------------\n interface ModelAgg {\n turns: number;\n input: number;\n output: number;\n cacheRead: number;\n cacheWrite: number;\n costUsd: number;\n unpriced: boolean;\n }\n const byModelMap = new Map<string, ModelAgg>();\n for (const t of turns) {\n const u = t.usage;\n if (u.input + u.output + u.cacheRead + u.cacheWrite === 0) continue; // synthetic placeholders\n let agg = byModelMap.get(t.model);\n if (!agg) {\n agg = {\n turns: 0,\n input: 0,\n output: 0,\n cacheRead: 0,\n cacheWrite: 0,\n costUsd: 0,\n unpriced: false,\n };\n byModelMap.set(t.model, agg);\n }\n agg.turns++;\n agg.input += u.input;\n agg.output += u.output;\n agg.cacheRead += u.cacheRead;\n agg.cacheWrite += u.cacheWrite;\n const cost = prices.turnCostUsd(t);\n if (cost === null) agg.unpriced = true;\n else agg.costUsd += cost;\n }\n const byModel = [...byModelMap.entries()]\n .map(([model, a]) => ({\n model,\n turns: a.turns,\n input: a.input,\n output: a.output,\n cacheRead: a.cacheRead,\n cacheWrite: a.cacheWrite,\n costUsd: a.unpriced ? null : a.costUsd,\n costShare: !a.unpriced && waste.totalCostUsd > 0 ? a.costUsd / waste.totalCostUsd : null,\n }))\n .sort((a, b) => (b.costUsd ?? 0) - (a.costUsd ?? 0) || b.turns - a.turns);\n\n // ---- phase matrix ----------------------------------------------------------\n const phaseMatrix = [...waste.matrix.entries()]\n .map(([model, phases]) => {\n const row = {} as Record<Phase, { turns: number; costUsd: number; wasteUsd: number }>;\n let total = 0;\n for (const phase of PHASES) {\n const cell = phases.get(phase);\n row[phase] = {\n turns: cell?.turns ?? 0,\n costUsd: cell?.costUsd ?? 0,\n wasteUsd: cell?.wasteUsd ?? 0,\n };\n total += row[phase].costUsd;\n }\n return { model, phases: row, total };\n })\n .sort((a, b) => b.total - a.total)\n .map(({ model, phases }) => ({ model, phases }));\n\n // ---- cache ---------------------------------------------------------------\n const overallCache = cacheStats(turns);\n const cacheProjects = [...cacheByProject(turns).entries()]\n .map(([project, s]) => ({\n // basename keeps the model identical whether projectPath is the real cwd\n // (local CLI) or the hashed id/name pair (synced server data).\n project: basename(project),\n turns: s.turns,\n hitRate: s.hitRate,\n cacheRead: s.cacheRead,\n cacheWrite: s.cacheWrite,\n }))\n .filter((p) => p.turns >= 10)\n .sort((a, b) => (a.hitRate ?? 1) - (b.hitRate ?? 1))\n .slice(0, 8);\n const compactions = summarizeCompactions(relevantEvents);\n\n // ---- top sessions ---------------------------------------------------------\n const cachePerSession = cacheBySession(turns);\n const topSessions = [...waste.perSession.values()]\n .sort((a, b) => b.wasteUsd - a.wasteUsd || b.costUsd - a.costUsd)\n .slice(0, 5)\n .map((s) => ({\n sessionId: s.sessionId,\n label: s.slug ?? shortId(s.sessionId),\n project: basename(s.project),\n date: fmtDate(s.firstTs === Infinity ? 0 : s.firstTs),\n turns: s.turns,\n costUsd: s.costUsd,\n wasteUsd: s.wasteUsd,\n antiPattern: dominantAntiPattern(s, cachePerSession.get(s.sessionId)?.hitRate ?? null),\n }));\n\n // ---- subagent leaks --------------------------------------------------------\n const agentTypeById = new Map<string, string>();\n for (const e of relevantEvents) {\n if (e.kind !== \"agent_result\") continue;\n const id = typeof e.data.agentId === \"string\" ? e.data.agentId : undefined;\n const type = typeof e.data.agentType === \"string\" ? e.data.agentType : undefined;\n if (id && type) agentTypeById.set(id, type);\n }\n interface ChildAgg {\n costUsd: number;\n premiumModel: string | null;\n }\n const children = new Map<string, ChildAgg>();\n for (const { turn } of classified) {\n if (!turn.agentId) continue;\n let child = children.get(turn.agentId);\n if (!child) {\n child = { costUsd: 0, premiumModel: null };\n children.set(turn.agentId, child);\n }\n const cost = prices.turnCostUsd(turn);\n if (cost !== null) child.costUsd += cost;\n if (!child.premiumModel && isPremiumModel(turn.model)) child.premiumModel = turn.model;\n }\n const leakGroups = new Map<\n string,\n { agentType: string; model: string; runs: number; costUsd: number }\n >();\n for (const [agentId, child] of children) {\n if (!child.premiumModel) continue;\n const agentType = agentTypeById.get(agentId) ?? \"subagent\";\n const key = `${agentType}\u0000${child.premiumModel}`;\n let g = leakGroups.get(key);\n if (!g) {\n g = { agentType, model: child.premiumModel, runs: 0, costUsd: 0 };\n leakGroups.set(key, g);\n }\n g.runs++;\n g.costUsd += child.costUsd;\n }\n const subagentLeaks = [...leakGroups.values()].sort((a, b) => b.costUsd - a.costUsd);\n\n // ---- quota -----------------------------------------------------------------\n const windows = build5hWindows(turns, prices);\n const last7dStart = maxTs > 0 ? maxTs - 7 * 86400000 : 0;\n const last7d = windows\n .filter((w) => w.start >= last7dStart)\n .map((w) => ({\n start: fmtDateTime(w.start),\n startTs: w.start,\n turns: w.turns,\n totalTokens: w.totalTokens,\n costUsd: w.costUsd,\n }));\n const weekly = weeklyRollup(turns, prices).map((w) => ({\n week: w.weekKey,\n turns: w.turns,\n totalTokens: w.totalTokens,\n costUsd: w.costUsd,\n }));\n\n // ---- recommendations ---------------------------------------------------------\n const spanDays = Math.max(1, (maxTs - (minTs === Infinity ? maxTs : minTs)) / 86400000);\n const monthly = (x: number): number => (x / spanDays) * 30;\n const recommendations: string[] = [];\n if (monthly(waste.wasteByPhase.exploration) >= 1) {\n recommendations.push(\n `Delegate exploration to a Haiku-pinned subagent (frontmatter: model: haiku, effort: low) — ` +\n `roughly ${fmtUsd(monthly(waste.wasteByPhase.exploration))}/month of premium-model exploration to reclaim.`,\n );\n }\n if (monthly(waste.wasteByPhase.execution) >= 1) {\n recommendations.push(\n `Run edit/build-heavy work on Sonnet (settings \"model\": \"opusplan\", or a sonnet implementer subagent) — ` +\n `roughly ${fmtUsd(monthly(waste.wasteByPhase.execution))}/month spent running premium models in execution.`,\n );\n }\n const leakRuns = subagentLeaks.reduce((n, l) => n + l.runs, 0);\n if (leakRuns > 0) {\n recommendations.push(\n `${leakRuns} subagent run(s) executed on a premium model — pin model: (haiku/sonnet) in .claude/agents/*.md frontmatter so delegation actually saves money.`,\n );\n }\n if (overallCache.hitRate !== null && overallCache.hitRate < 0.7) {\n recommendations.push(\n `Cache hit rate is ${fmtPct(overallCache.hitRate)} — cold starts repay your context at full price. Prefer resuming sessions (claude --continue) over starting fresh ones.`,\n );\n }\n if (monthly(waste.fastModeExtraUsd) >= 1) {\n recommendations.push(\n `Fast mode premium added ${fmtUsd(waste.fastModeExtraUsd)} in this window — reserve /fast for latency-critical work.`,\n );\n }\n if (compactions.total >= 5) {\n recommendations.push(\n `${compactions.total} context compaction(s) re-summarized ~${fmtTokens(compactions.tokensCompacted)} tokens — split long sessions or push exploration into subagents to keep the main context lean.`,\n );\n }\n\n return {\n generatedAt: new Date().toISOString(),\n priceTableVersion: prices.version,\n range: {\n days: range.days,\n since: minTs === Infinity ? null : new Date(minTs).toISOString(),\n until: new Date(maxTs > 0 ? maxTs : Date.now()).toISOString(),\n project: range.project,\n },\n overview: {\n sessions: rootSessions.size,\n turns: turns.length,\n subagentTurns,\n projects: projects.size,\n totalTokens,\n tokens,\n totalCostUsd: waste.totalCostUsd,\n unpricedTurns: waste.unpricedTurns,\n unpricedModels: waste.unpricedModels,\n },\n byModel,\n phaseMatrix,\n waste: {\n totalUsd: waste.totalWasteUsd,\n explorationUsd: waste.wasteByPhase.exploration,\n executionUsd: waste.wasteByPhase.execution,\n pctOfSpend: waste.totalCostUsd > 0 ? waste.totalWasteUsd / waste.totalCostUsd : null,\n fastModeExtraUsd: waste.fastModeExtraUsd,\n assumption: WASTE_ASSUMPTION,\n },\n cache: {\n overall: {\n hitRate: overallCache.hitRate,\n input: overallCache.input,\n cacheRead: overallCache.cacheRead,\n cacheWrite: overallCache.cacheWrite,\n },\n byProject: cacheProjects,\n compactions: {\n total: compactions.total,\n tokensCompacted: compactions.tokensCompacted,\n sessionsAffected: compactions.bySession.size,\n },\n },\n topSessions,\n subagentLeaks,\n quota: { windowHours: WINDOW_MS / 3600000, last7d, weekly, note: QUOTA_NOTE },\n recommendations,\n };\n}\n\nfunction dominantAntiPattern(s: SessionAggregate, hitRate: number | null): string {\n const parts: string[] = [];\n if (s.wasteExplorationUsd > WASTE_EPSILON && s.wasteExplorationUsd >= s.wasteExecutionUsd) {\n parts.push(`premium model on exploration (${fmtUsd(s.wasteExplorationUsd)})`);\n } else if (s.wasteExecutionUsd > WASTE_EPSILON) {\n parts.push(`premium model on execution (${fmtUsd(s.wasteExecutionUsd)})`);\n }\n if (parts.length === 0 && hitRate !== null && hitRate < 0.5 && s.turns >= 20) {\n parts.push(`low cache reuse (${fmtPct(hitRate)} hit rate)`);\n }\n return parts.length > 0 ? (parts[0] as string) : \"—\";\n}\n","// Wire types for `tktuner sync` -> POST /v1/sync. Pure type declarations —\n// the CLI produces them (after the privacy transform) and the API validates\n// them with zod server-side. The privacy invariant lives here: no cwd, no\n// gitBranch, no filePath, and never any message/tool content.\nimport type { TokenUsage } from \"./types\";\n\nexport const SYNC_MAX_TURNS_PER_BATCH = 2000;\nexport const SYNC_MAX_EVENTS_PER_BATCH = 5000;\n\nexport interface SyncTurnWire {\n /** coalesce(messageId, uuid) — the server's idempotent upsert key. */\n turnKey: string;\n uuid: string;\n messageId?: string;\n requestId?: string;\n sessionId: string;\n agentId?: string;\n parentSessionId?: string;\n isSidechain: boolean;\n /** sha256(cwd) truncated to 16 hex chars. */\n projectId: string;\n /** basename(cwd). */\n projectName: string;\n slug?: string;\n /** Epoch ms. */\n ts: number;\n model: string;\n usage: TokenUsage;\n tools: string[];\n numToolUses: number;\n thinkingChars: number;\n textChars: number;\n stopReason?: string;\n /** Claude Code version that wrote the log line. */\n version?: string;\n speed?: string;\n inferenceGeo?: string;\n}\n\nexport interface SyncEventWire {\n /** Local sqlite autoincrement id (traceability only — dedup is content-based). */\n localEventId: number;\n sessionId: string;\n ts?: number;\n kind: string;\n data: Record<string, unknown>;\n}\n\nexport interface SyncPushBody {\n turns: SyncTurnWire[];\n events: SyncEventWire[];\n cliVersion?: string;\n}\n\nexport interface SyncPushResult {\n turnsUpserted: number;\n eventsInserted: number;\n}\n","import { buildReport, type PriceTable } from \"@tktuner/core\";\nimport pc from \"picocolors\";\nimport { loadPriceTable } from \"../lib/prices-io\";\nimport { setColors } from \"../render/colors\";\nimport { renderDebugSession, renderReport } from \"../render/terminal\";\nimport { openDb } from \"../store/db\";\nimport { Store } from \"../store/queries\";\n\nexport interface ReportOptions {\n days: string;\n all?: boolean;\n project?: string;\n json?: boolean;\n color?: boolean; // commander --no-color => color: false\n db?: string;\n prices?: string;\n debugSession?: string;\n}\n\nexport async function report(opts: ReportOptions): Promise<void> {\n if (opts.color === false || opts.json) setColors(false);\n\n const days = opts.all ? null : Number(opts.days);\n if (days !== null && (!Number.isFinite(days) || days <= 0)) {\n console.error(pc.red(`Invalid --days value: ${opts.days}`));\n process.exitCode = 1;\n return;\n }\n\n const db = openDb(opts.db);\n try {\n const store = new Store(db);\n if (store.countTurns() === 0) {\n console.log(`No data yet — run ${pc.bold(\"tktuner scan\")} first.`);\n return;\n }\n\n let prices: PriceTable;\n try {\n prices = loadPriceTable(opts.prices);\n } catch (err) {\n console.error(pc.red(`Could not load price override ${opts.prices}: ${String(err)}`));\n process.exitCode = 1;\n return;\n }\n\n const sinceTs = days === null ? undefined : Date.now() - days * 86400000;\n const turns = store.loadTurns({ sinceTs, projectLike: opts.project });\n if (turns.length === 0) {\n console.log(\n `No turns in the selected window${opts.project ? ` for project filter \"${opts.project}\"` : \"\"}. Try --all or a larger --days.`,\n );\n return;\n }\n const events = store.loadEvents();\n\n if (opts.debugSession) {\n console.log(renderDebugSession(turns, events, prices, opts.debugSession));\n return;\n }\n\n const model = buildReport(turns, events, prices, {\n days,\n project: opts.project ?? null,\n });\n if (opts.json) {\n console.log(JSON.stringify(model, null, 2));\n } else {\n console.log(renderReport(model));\n }\n } finally {\n db.close();\n }\n}\n","import { readFileSync } from \"node:fs\";\nimport { type PricesFile, PriceTable } from \"@tktuner/core\";\n\n/** Bundled price table, optionally with a user's JSON file merged on top. */\nexport function loadPriceTable(overridePath?: string): PriceTable {\n if (!overridePath) return PriceTable.bundled();\n const raw = JSON.parse(readFileSync(overridePath, \"utf8\")) as Partial<PricesFile>;\n return PriceTable.withOverrideData(raw);\n}\n","import pc from \"picocolors\";\n\nexport type Painter = (s: string) => string;\n\n/** Live-bound color palette; setColors(false) turns everything into identity. */\nexport let c: typeof pc = pc;\n\nexport function setColors(enabled: boolean): void {\n c = pc.createColors(enabled) as typeof pc;\n}\n","import type { Painter } from \"./colors\";\n\nexport interface Column {\n header: string;\n align?: \"left\" | \"right\";\n}\n\nexport type Cell = string | { text: string; paint?: Painter };\n\nconst MAX_CELL_WIDTH = 44;\n\n/**\n * Minimal dependency-free table renderer. Width math uses the plain text;\n * paint functions are applied after padding so ANSI codes never skew layout.\n */\nexport function renderTable(cols: Column[], rows: Cell[][], indent = \" \"): string {\n const plain = (cell: Cell): string => (typeof cell === \"string\" ? cell : cell.text);\n const widths = cols.map((col, i) => {\n let w = col.header.length;\n for (const row of rows) {\n const cell = row[i];\n if (cell !== undefined) w = Math.max(w, Math.min(plain(cell).length, MAX_CELL_WIDTH));\n }\n return w;\n });\n\n const renderRow = (cells: Cell[], painter?: Painter): string => {\n const parts = cols.map((col, i) => {\n const cell = cells[i] ?? \"\";\n let text = plain(cell);\n if (text.length > MAX_CELL_WIDTH) text = `${text.slice(0, MAX_CELL_WIDTH - 1)}…`;\n const w = widths[i] ?? 0;\n const padded = col.align === \"right\" ? text.padStart(w) : text.padEnd(w);\n const paint = typeof cell === \"object\" ? cell.paint : undefined;\n const chosen = paint ?? painter;\n return chosen ? chosen(padded) : padded;\n });\n return indent + parts.join(\" \");\n };\n\n const lines: string[] = [];\n lines.push(renderRow(cols.map((c) => c.header)));\n lines.push(indent + widths.map((w) => \"─\".repeat(w)).join(\"──\"));\n for (const row of rows) lines.push(renderRow(row));\n return lines.join(\"\\n\");\n}\n","import {\n basename,\n type ClassifiedTurn,\n classifyTurns,\n fmtDate,\n fmtPct,\n fmtTime,\n fmtTokens,\n fmtUsd,\n PHASES,\n type PriceTable,\n prettyModel,\n type ReportModel,\n rootSessionId,\n type SideEvent,\n type TurnEvent,\n truncate,\n WASTE_EPSILON,\n} from \"@tktuner/core\";\nimport { c } from \"./colors\";\nimport { type Cell, renderTable } from \"./table\";\n\nexport function renderReport(m: ReportModel): string {\n const out: string[] = [];\n const rangeLabel = m.range.days === null ? \"all history\" : `last ${m.range.days} days`;\n const span = `${m.range.since ? fmtDate(Date.parse(m.range.since)) : \"—\"} → ${fmtDate(Date.parse(m.range.until))}`;\n const filter = m.range.project ? ` · project ~ \"${m.range.project}\"` : \"\";\n out.push(`${c.bold(\"TkTuner report\")} — ${rangeLabel} (${span})${filter}`);\n\n // Overview\n const o = m.overview;\n const lines: string[] = [];\n lines.push(\n `Sessions ${c.bold(String(o.sessions))} · assistant turns ${c.bold(o.turns.toLocaleString(\"en-US\"))}` +\n (o.subagentTurns > 0 ? ` (${o.subagentTurns.toLocaleString(\"en-US\")} in subagents)` : \"\") +\n ` · projects ${c.bold(String(o.projects))}`,\n );\n lines.push(\n `Est. cost (API-equivalent): ${c.bold(c.green(fmtUsd(o.totalCostUsd)))} ` +\n `tokens: ${c.bold(fmtTokens(o.totalTokens))} — in ${fmtTokens(o.tokens.input)} · out ${fmtTokens(o.tokens.output)} · cache read ${fmtTokens(o.tokens.cacheRead)} · cache write ${fmtTokens(o.tokens.cacheWrite)}`,\n );\n if (o.unpricedTurns > 0) {\n lines.push(\n c.yellow(\n `${o.unpricedTurns} turn(s) on unpriced model(s) excluded from cost: ${o.unpricedModels.join(\", \")} — update prices.json or use --prices`,\n ),\n );\n }\n out.push(`${header(\"Overview\")}\\n${lines.join(\"\\n\")}`);\n\n // By model\n out.push(\n `${header(\"By model\")}\\n${renderTable(\n [\n { header: \"Model\" },\n { header: \"Turns\", align: \"right\" },\n { header: \"Input\", align: \"right\" },\n { header: \"Output\", align: \"right\" },\n { header: \"Cache read\", align: \"right\" },\n { header: \"Cache write\", align: \"right\" },\n { header: \"Est. cost\", align: \"right\" },\n { header: \"%\", align: \"right\" },\n ],\n m.byModel.map((r) => [\n prettyModel(r.model),\n String(r.turns),\n fmtTokens(r.input),\n fmtTokens(r.output),\n fmtTokens(r.cacheRead),\n fmtTokens(r.cacheWrite),\n { text: fmtUsd(r.costUsd), paint: r.costUsd === null ? c.yellow : undefined },\n fmtPct(r.costShare),\n ]),\n )}`,\n );\n\n // Phase × model\n const matrixRows: Cell[][] = m.phaseMatrix.map((row) => {\n const cells: Cell[] = [prettyModel(row.model)];\n for (const phase of PHASES) {\n const cell = row.phases[phase];\n if (cell.turns === 0) cells.push({ text: \"·\", paint: c.dim });\n else if (cell.wasteUsd > WASTE_EPSILON) {\n cells.push({ text: fmtUsd(cell.costUsd), paint: c.red });\n } else cells.push(fmtUsd(cell.costUsd));\n }\n return cells;\n });\n const wasteLines: string[] = [];\n wasteLines.push(\n `${c.bold(c.red(\"⚠ waste\"))} (premium model on cheap work): ${c.bold(c.red(fmtUsd(m.waste.totalUsd)))}` +\n (m.waste.pctOfSpend !== null ? ` = ${c.bold(fmtPct(m.waste.pctOfSpend))} of spend` : \"\"),\n );\n wasteLines.push(\n ` exploration on premium: ${fmtUsd(m.waste.explorationUsd)} · execution on premium: ${fmtUsd(m.waste.executionUsd)}` +\n (m.waste.fastModeExtraUsd > WASTE_EPSILON\n ? ` · fast-mode premium: ${fmtUsd(m.waste.fastModeExtraUsd)}`\n : \"\"),\n );\n out.push(\n `${header(\"Cost by phase × model\")}\\n${renderTable(\n [{ header: \"Model\" }, ...PHASES.map((p) => ({ header: cap(p), align: \"right\" as const }))],\n matrixRows,\n )}\\n\\n${wasteLines.join(\"\\n\")}`,\n );\n\n // Cache health\n const ch = m.cache;\n const cacheLines = [\n `Overall hit rate: ${paintHitRate(ch.overall.hitRate)} (read ${fmtTokens(ch.overall.cacheRead)} · write ${fmtTokens(ch.overall.cacheWrite)} · uncached input ${fmtTokens(ch.overall.input)})`,\n ];\n if (ch.compactions.total > 0) {\n cacheLines.push(\n `Compactions: ${ch.compactions.total} across ${ch.compactions.sessionsAffected} session(s) · ~${fmtTokens(ch.compactions.tokensCompacted)} tokens re-summarized`,\n );\n }\n const cacheTable =\n ch.byProject.length > 0\n ? `\\n${renderTable(\n [\n { header: \"Project (worst hit rate first)\" },\n { header: \"Turns\", align: \"right\" },\n { header: \"Hit rate\", align: \"right\" },\n { header: \"Read\", align: \"right\" },\n { header: \"Write\", align: \"right\" },\n ],\n ch.byProject.map((p) => [\n basename(p.project),\n String(p.turns),\n {\n text: fmtPct(p.hitRate),\n paint: p.hitRate !== null && p.hitRate < 0.6 ? c.yellow : undefined,\n },\n fmtTokens(p.cacheRead),\n fmtTokens(p.cacheWrite),\n ]),\n )}`\n : \"\";\n out.push(`${header(\"Cache health\")}\\n${cacheLines.join(\"\\n\")}${cacheTable}`);\n\n // Top sessions\n if (m.topSessions.length > 0) {\n out.push(\n `${header(\"Top 5 most wasteful sessions\")}\\n${renderTable(\n [\n { header: \"#\", align: \"right\" },\n { header: \"Session\" },\n { header: \"Project\" },\n { header: \"Date\" },\n { header: \"Turns\", align: \"right\" },\n { header: \"Cost\", align: \"right\" },\n { header: \"Waste\", align: \"right\" },\n { header: \"Dominant anti-pattern\" },\n ],\n m.topSessions.map((s, i) => [\n String(i + 1),\n s.label,\n s.project,\n s.date,\n String(s.turns),\n fmtUsd(s.costUsd),\n { text: fmtUsd(s.wasteUsd), paint: s.wasteUsd > WASTE_EPSILON ? c.red : undefined },\n s.antiPattern,\n ]),\n )}\\n${c.dim(` drill down: tktuner report --debug-session <session>`)}`,\n );\n }\n\n // Subagent leaks\n if (m.subagentLeaks.length > 0) {\n out.push(\n `${header(\"Subagent leaks (premium model in delegated runs)\")}\\n${renderTable(\n [\n { header: \"Agent type\" },\n { header: \"Model\" },\n { header: \"Runs\", align: \"right\" },\n { header: \"Est. cost\", align: \"right\" },\n ],\n m.subagentLeaks.map((l) => [\n l.agentType,\n prettyModel(l.model),\n String(l.runs),\n { text: fmtUsd(l.costUsd), paint: c.red },\n ]),\n )}\\n${c.dim(\" fix: pin model: haiku|sonnet in the agent frontmatter (.claude/agents/*.md)\")}`,\n );\n } else {\n out.push(`${header(\"Subagent leaks\")}\\n${c.green(\"No subagent runs on premium models.\")}`);\n }\n\n // Quota pressure\n const maxWindowCost = Math.max(...m.quota.last7d.map((w) => w.costUsd), 0);\n const quotaParts: string[] = [];\n if (m.quota.last7d.length > 0) {\n quotaParts.push(\n renderTable(\n [\n { header: `5h window start (last 7d)` },\n { header: \"Turns\", align: \"right\" },\n { header: \"Tokens\", align: \"right\" },\n { header: \"Cost-equivalent\", align: \"right\" },\n { header: \"\" },\n ],\n m.quota.last7d.map((w) => [\n w.start,\n String(w.turns),\n fmtTokens(w.totalTokens),\n fmtUsd(w.costUsd),\n { text: bar(w.costUsd, maxWindowCost, 14), paint: c.cyan },\n ]),\n ),\n );\n }\n const maxWeekCost = Math.max(...m.quota.weekly.map((w) => w.costUsd), 0);\n if (m.quota.weekly.length > 0) {\n quotaParts.push(\n renderTable(\n [\n { header: \"Week\" },\n { header: \"Turns\", align: \"right\" },\n { header: \"Tokens\", align: \"right\" },\n { header: \"Cost-equivalent\", align: \"right\" },\n { header: \"\" },\n ],\n m.quota.weekly.map((w) => [\n w.week,\n String(w.turns),\n fmtTokens(w.totalTokens),\n fmtUsd(w.costUsd),\n { text: bar(w.costUsd, maxWeekCost, 14), paint: c.magenta },\n ]),\n ),\n );\n }\n quotaParts.push(c.dim(` ${m.quota.note}`));\n out.push(`${header(\"Quota pressure\")}\\n${quotaParts.join(\"\\n\\n\")}`);\n\n // Recommendations\n if (m.recommendations.length > 0) {\n out.push(\n `${header(\"Recommendations\")}\\n${m.recommendations\n .map((r, i) => ` ${c.green(`${i + 1}.`)} ${r}`)\n .join(\"\\n\")}`,\n );\n }\n\n out.push(\n c.dim(\n [\n m.waste.assumption,\n `Prices v${m.priceTableVersion} (platform.claude.com) — override with --prices <file>.`,\n ].join(\"\\n\"),\n ),\n );\n return `\\n${out.join(\"\\n\\n\")}\\n`;\n}\n\nexport function renderDebugSession(\n turns: TurnEvent[],\n events: SideEvent[],\n prices: PriceTable,\n idPrefix: string,\n): string {\n const match = turns.filter(\n (t) =>\n t.sessionId.startsWith(idPrefix) ||\n rootSessionId(t).startsWith(idPrefix) ||\n t.slug?.startsWith(idPrefix),\n );\n if (match.length === 0) {\n return c.yellow(`No turns found for session prefix \"${idPrefix}\".`);\n }\n const classified = classifyTurns(match, events).sort(\n (a, b) => a.turn.timestamp - b.turn.timestamp,\n );\n const first = classified[0] as ClassifiedTurn;\n const totalCost = classified.reduce((acc, ct) => acc + (prices.turnCostUsd(ct.turn) ?? 0), 0);\n const sessions = new Set(match.map((t) => t.sessionId));\n\n const head =\n `${c.bold(\"Session debug\")} — ${idPrefix} (${sessions.size} session file(s), ${match.length} turns)\\n` +\n `project ${first.turn.projectPath || \"—\"} · ${fmtDate(first.turn.timestamp)} · est. cost ${c.bold(fmtUsd(totalCost))}`;\n\n const table = renderTable(\n [\n { header: \"Time\" },\n { header: \"Model\" },\n { header: \"Raw\", align: \"left\" },\n { header: \"Smoothed\", align: \"left\" },\n { header: \"Think\", align: \"right\" },\n { header: \"Out\", align: \"right\" },\n { header: \"Cost\", align: \"right\" },\n { header: \"Tools\" },\n ],\n classified.map((ct) => [\n fmtTime(ct.turn.timestamp),\n prettyModel(ct.turn.model),\n ct.phase,\n ct.phase === ct.phaseSmoothed\n ? ct.phaseSmoothed\n : { text: ct.phaseSmoothed, paint: c.yellow },\n fmtTokens(ct.turn.thinkingChars),\n fmtTokens(ct.turn.usage.output),\n fmtUsd(prices.turnCostUsd(ct.turn)),\n truncate(ct.turn.tools.join(\",\"), 40) || c.dim(\"—\"),\n ]),\n );\n\n const sessionEvents = events\n .filter((e) => sessions.has(e.sessionId) && e.kind !== \"turn_duration\" && e.timestamp)\n .sort((a, b) => (a.timestamp ?? 0) - (b.timestamp ?? 0))\n .map((e) => ` ${fmtTime(e.timestamp ?? 0)} ${e.kind}`)\n .join(\"\\n\");\n\n return `\\n${head}\\n\\n${table}${sessionEvents ? `\\n\\n${c.bold(\"Events\")}\\n${sessionEvents}` : \"\"}\\n`;\n}\n\nfunction header(title: string): string {\n const line = \"━\".repeat(Math.max(4, 62 - title.length));\n return c.bold(c.cyan(`━━ ${title} ${line.slice(0, Math.max(2, 58 - title.length))}`));\n}\n\nfunction cap(s: string): string {\n return s.charAt(0).toUpperCase() + s.slice(1);\n}\n\nfunction paintHitRate(rate: number | null): string {\n const text = fmtPct(rate);\n if (rate === null) return text;\n if (rate >= 0.8) return c.green(c.bold(text));\n if (rate >= 0.6) return c.yellow(c.bold(text));\n return c.red(c.bold(text));\n}\n\nfunction bar(value: number, max: number, width: number): string {\n if (max <= 0 || value <= 0) return \"\";\n return \"█\".repeat(Math.max(1, Math.round((value / max) * width)));\n}\n","import { mkdirSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport Database from \"better-sqlite3\";\nimport { dataDir } from \"../lib/paths\";\nimport { DDL, SCHEMA_VERSION } from \"./schema\";\n\nexport function defaultDbPath(): string {\n return join(dataDir(), \"tktuner.db\");\n}\n\nexport function openDb(path?: string): Database.Database {\n const dbPath = path ?? defaultDbPath();\n mkdirSync(dirname(dbPath), { recursive: true });\n const db = new Database(dbPath);\n db.pragma(\"journal_mode = WAL\");\n migrate(db);\n return db;\n}\n\nfunction migrate(db: Database.Database): void {\n rebuildIfPreRelease(db);\n db.exec(DDL);\n const row = db.prepare(`SELECT value FROM meta WHERE key = 'schema_version'`).get() as\n | { value: string }\n | undefined;\n const current = row ? Number(row.value) : 0;\n if (current > SCHEMA_VERSION) {\n throw new Error(\n `Database schema v${current} is newer than this tktuner build supports (v${SCHEMA_VERSION}). Upgrade tktuner.`,\n );\n }\n // v1 is the baseline; future versions add ALTERs gated on `current` here.\n db.prepare(\n `INSERT INTO meta (key, value) VALUES ('schema_version', ?)\n ON CONFLICT(key) DO UPDATE SET value = excluded.value`,\n ).run(String(SCHEMA_VERSION));\n}\n\n/**\n * Everything in the db derives from the JSONL logs, so an old-shape database\n * is rebuilt rather than migrated (rescanning is cheap). Detects pre-release\n * schemas by the absence of turns.message_id.\n */\nfunction rebuildIfPreRelease(db: Database.Database): void {\n const table = db\n .prepare(`SELECT name FROM sqlite_master WHERE type = 'table' AND name = 'turns'`)\n .get();\n if (!table) return;\n const cols = db.prepare(`PRAGMA table_info(turns)`).all() as { name: string }[];\n if (cols.some((c) => c.name === \"message_id\")) return;\n db.exec(`DROP TABLE IF EXISTS turns; DROP TABLE IF EXISTS events; DROP TABLE IF EXISTS files;`);\n}\n","export const SCHEMA_VERSION = 1;\n\nexport const DDL = `\nCREATE TABLE IF NOT EXISTS files (\n path TEXT PRIMARY KEY,\n mtime_ms INTEGER NOT NULL,\n size INTEGER NOT NULL,\n byte_offset INTEGER NOT NULL DEFAULT 0,\n kind TEXT NOT NULL,\n session_id TEXT,\n agent_id TEXT,\n parent_session_id TEXT,\n last_scanned_at INTEGER NOT NULL\n);\n\nCREATE TABLE IF NOT EXISTS turns (\n uuid TEXT PRIMARY KEY,\n message_id TEXT,\n request_id TEXT,\n session_id TEXT NOT NULL,\n agent_id TEXT,\n parent_session_id TEXT,\n is_sidechain INTEGER NOT NULL DEFAULT 0,\n ts INTEGER NOT NULL DEFAULT 0,\n model TEXT NOT NULL,\n input_tokens INTEGER NOT NULL DEFAULT 0,\n output_tokens INTEGER NOT NULL DEFAULT 0,\n cache_creation_tokens INTEGER NOT NULL DEFAULT 0,\n cache_read_tokens INTEGER NOT NULL DEFAULT 0,\n cache_5m_tokens INTEGER NOT NULL DEFAULT 0,\n cache_1h_tokens INTEGER NOT NULL DEFAULT 0,\n tools TEXT NOT NULL DEFAULT '[]',\n num_tool_uses INTEGER NOT NULL DEFAULT 0,\n thinking_chars INTEGER NOT NULL DEFAULT 0,\n text_chars INTEGER NOT NULL DEFAULT 0,\n stop_reason TEXT,\n cwd TEXT,\n slug TEXT,\n git_branch TEXT,\n version TEXT,\n speed TEXT,\n inference_geo TEXT,\n file_path TEXT NOT NULL\n);\nCREATE INDEX IF NOT EXISTS idx_turns_session ON turns(session_id);\nCREATE INDEX IF NOT EXISTS idx_turns_ts ON turns(ts);\nCREATE INDEX IF NOT EXISTS idx_turns_file ON turns(file_path);\nCREATE INDEX IF NOT EXISTS idx_turns_msg ON turns(message_id);\n\nCREATE TABLE IF NOT EXISTS events (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n session_id TEXT NOT NULL,\n ts INTEGER,\n kind TEXT NOT NULL,\n data TEXT NOT NULL DEFAULT '{}',\n file_path TEXT NOT NULL\n);\nCREATE INDEX IF NOT EXISTS idx_events_session ON events(session_id, ts);\nCREATE INDEX IF NOT EXISTS idx_events_file ON events(file_path);\n\nCREATE TABLE IF NOT EXISTS meta (\n key TEXT PRIMARY KEY,\n value TEXT NOT NULL\n);\n`;\n","import {\n basename,\n type DiscoveredFile,\n mergeTurnLines,\n type ParseBatch,\n type SideEvent,\n type TurnEvent,\n} from \"@tktuner/core\";\nimport type Database from \"better-sqlite3\";\n\nexport interface FileState {\n path: string;\n mtimeMs: number;\n size: number;\n byteOffset: number;\n kind: string;\n}\n\nexport interface TurnFilter {\n sinceTs?: number;\n projectLike?: string;\n}\n\ninterface TurnRow {\n uuid: string;\n message_id: string | null;\n request_id: string | null;\n session_id: string;\n agent_id: string | null;\n parent_session_id: string | null;\n is_sidechain: number;\n ts: number;\n model: string;\n input_tokens: number;\n output_tokens: number;\n cache_creation_tokens: number;\n cache_read_tokens: number;\n cache_5m_tokens: number;\n cache_1h_tokens: number;\n tools: string;\n num_tool_uses: number;\n thinking_chars: number;\n text_chars: number;\n stop_reason: string | null;\n cwd: string | null;\n slug: string | null;\n git_branch: string | null;\n version: string | null;\n speed: string | null;\n inference_geo: string | null;\n file_path: string;\n}\n\nexport class Store {\n private readonly stmtGetFile;\n private readonly stmtUpsertFile;\n private readonly stmtInsertTurn;\n private readonly stmtInsertEvent;\n private readonly txIngest;\n\n constructor(private readonly db: Database.Database) {\n this.stmtGetFile = db.prepare(\n `SELECT path, mtime_ms AS mtimeMs, size, byte_offset AS byteOffset, kind\n FROM files WHERE path = ?`,\n );\n this.stmtUpsertFile = db.prepare(\n `INSERT INTO files (path, mtime_ms, size, byte_offset, kind, session_id, agent_id, parent_session_id, last_scanned_at)\n VALUES (@path, @mtimeMs, @size, @byteOffset, @kind, @sessionId, @agentId, @parentSessionId, @scannedAt)\n ON CONFLICT(path) DO UPDATE SET\n mtime_ms = excluded.mtime_ms,\n size = excluded.size,\n byte_offset = excluded.byte_offset,\n last_scanned_at = excluded.last_scanned_at`,\n );\n this.stmtInsertTurn = db.prepare(\n `INSERT OR IGNORE INTO turns (\n uuid, message_id, request_id, session_id, agent_id, parent_session_id, is_sidechain, ts, model,\n input_tokens, output_tokens, cache_creation_tokens, cache_read_tokens,\n cache_5m_tokens, cache_1h_tokens, tools, num_tool_uses, thinking_chars,\n text_chars, stop_reason, cwd, slug, git_branch, version, speed, inference_geo, file_path\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n );\n this.stmtInsertEvent = db.prepare(\n `INSERT INTO events (session_id, ts, kind, data, file_path) VALUES (?, ?, ?, ?, ?)`,\n );\n this.txIngest = db.transaction((file: DiscoveredFile, batch: ParseBatch) => {\n let newTurns = 0;\n for (const t of batch.turns) {\n const info = this.stmtInsertTurn.run(\n t.uuid,\n t.messageId ?? null,\n t.requestId ?? null,\n t.sessionId,\n t.agentId ?? null,\n t.parentSessionId ?? null,\n t.isSidechain ? 1 : 0,\n t.timestamp,\n t.model,\n t.usage.input,\n t.usage.output,\n t.usage.cacheWrite,\n t.usage.cacheRead,\n t.usage.cacheWrite5m,\n t.usage.cacheWrite1h,\n JSON.stringify(t.tools),\n t.numToolUses,\n t.thinkingChars,\n t.textChars,\n t.stopReason ?? null,\n t.projectPath || null,\n t.slug ?? null,\n t.gitBranch ?? null,\n t.cliVersion ?? null,\n t.speed ?? null,\n t.inferenceGeo ?? null,\n t.filePath,\n );\n newTurns += info.changes;\n }\n for (const e of batch.events) {\n this.stmtInsertEvent.run(\n e.sessionId,\n e.timestamp ?? null,\n e.kind,\n JSON.stringify(e.data),\n e.filePath,\n );\n }\n this.stmtUpsertFile.run({\n path: file.path,\n mtimeMs: file.mtimeMs,\n size: file.sizeBytes,\n byteOffset: batch.bytesConsumed,\n kind: file.kind,\n sessionId: file.sessionId ?? null,\n agentId: file.agentId ?? null,\n parentSessionId: file.parentSessionId ?? null,\n scannedAt: Date.now(),\n });\n return { newTurns, newEvents: batch.events.length };\n });\n }\n\n getFileState(path: string): FileState | undefined {\n return this.stmtGetFile.get(path) as FileState | undefined;\n }\n\n /** Remove everything ingested from one file so it can be reparsed cleanly. */\n deleteFileData(path: string): void {\n this.db.prepare(`DELETE FROM turns WHERE file_path = ?`).run(path);\n this.db.prepare(`DELETE FROM events WHERE file_path = ?`).run(path);\n this.db.prepare(`DELETE FROM files WHERE path = ?`).run(path);\n }\n\n ingestBatch(file: DiscoveredFile, batch: ParseBatch): { newTurns: number; newEvents: number } {\n return this.txIngest(file, batch) as { newTurns: number; newEvents: number };\n }\n\n /** Merge unknown line-type counts into the persistent parser-evolution log. */\n bumpUnknownTypes(counts: Record<string, number>): void {\n if (Object.keys(counts).length === 0) return;\n const merged = { ...this.getUnknownTypes() };\n for (const [k, v] of Object.entries(counts)) merged[k] = (merged[k] ?? 0) + v;\n this.setMeta(\"unknown_line_types\", JSON.stringify(merged));\n }\n\n getUnknownTypes(): Record<string, number> {\n const raw = this.getMeta(\"unknown_line_types\");\n if (!raw) return {};\n try {\n return JSON.parse(raw) as Record<string, number>;\n } catch {\n return {};\n }\n }\n\n getMeta(key: string): string | undefined {\n const row = this.db.prepare(`SELECT value FROM meta WHERE key = ?`).get(key) as\n | { value: string }\n | undefined;\n return row?.value;\n }\n\n setMeta(key: string, value: string): void {\n this.db\n .prepare(\n `INSERT INTO meta (key, value) VALUES (?, ?)\n ON CONFLICT(key) DO UPDATE SET value = excluded.value`,\n )\n .run(key, value);\n }\n\n /** Real (merged) turn count — content-block lines collapse by message_id. */\n countTurns(): number {\n const row = this.db\n .prepare(`SELECT COUNT(DISTINCT COALESCE(message_id, uuid)) AS n FROM turns`)\n .get() as { n: number };\n return row.n;\n }\n\n /**\n * Loads billable turns. Raw rows are one-per-content-block (Claude Code\n * splits an API response across JSONL lines, repeating the same usage), so\n * rows sharing a message_id are merged here into a single TurnEvent.\n *\n * The project filter matches the project NAME (basename of cwd), case-\n * insensitive — the same semantics as the dashboard, which only ever sees\n * hashed ids + names. Full-path matching would also catch unrelated\n * directories that merely contain the string.\n */\n loadTurns(filter: TurnFilter = {}): TurnEvent[] {\n const clauses: string[] = [];\n const params: unknown[] = [];\n if (filter.sinceTs !== undefined) {\n clauses.push(`ts >= ?`);\n params.push(filter.sinceTs);\n }\n const where = clauses.length ? `WHERE ${clauses.join(\" AND \")}` : \"\";\n let rows = this.db\n .prepare(`SELECT * FROM turns ${where} ORDER BY ts ASC, uuid ASC`)\n .all(...params) as TurnRow[];\n if (filter.projectLike) {\n const needle = filter.projectLike.toLowerCase();\n rows = rows.filter((r) =>\n basename(r.cwd ?? \"\")\n .toLowerCase()\n .includes(needle),\n );\n }\n return mergeTurnLines(rows.map(rowToTurn));\n }\n\n /** Distinct turnKeys touched by raw lines after `rowid` + the new watermark. */\n newTurnKeysSince(rowid: number): { keys: string[]; maxRowid: number } {\n const rows = this.db\n .prepare(`SELECT rowid AS rid, COALESCE(message_id, uuid) AS k FROM turns WHERE rowid > ?`)\n .all(rowid) as { rid: number; k: string }[];\n const keys = new Set<string>();\n let maxRowid = rowid;\n for (const r of rows) {\n keys.add(r.k);\n if (r.rid > maxRowid) maxRowid = r.rid;\n }\n return { keys: [...keys], maxRowid };\n }\n\n /**\n * Loads and merges the ENTIRE line group of each turnKey (late-arriving\n * content blocks re-push the whole merged turn — the server upsert\n * overwrites). Each merged turn carries its group's max rowid so the sync\n * watermark can advance per confirmed batch.\n */\n loadTurnGroups(keys: string[]): { turn: TurnEvent; maxRowid: number }[] {\n if (keys.length === 0) return [];\n const lines: TurnEvent[] = [];\n const maxRowidByKey = new Map<string, number>();\n for (let i = 0; i < keys.length; i += 500) {\n const chunk = keys.slice(i, i + 500);\n const placeholders = chunk.map(() => \"?\").join(\",\");\n const rows = this.db\n .prepare(\n `SELECT rowid AS rid, * FROM turns\n WHERE COALESCE(message_id, uuid) IN (${placeholders})\n ORDER BY ts ASC, uuid ASC`,\n )\n .all(...chunk) as (TurnRow & { rid: number })[];\n for (const r of rows) {\n const key = r.message_id ?? r.uuid;\n const prev = maxRowidByKey.get(key) ?? 0;\n if (r.rid > prev) maxRowidByKey.set(key, r.rid);\n lines.push(rowToTurn(r));\n }\n }\n return mergeTurnLines(lines)\n .map((turn) => ({\n turn,\n maxRowid: maxRowidByKey.get(turn.messageId ?? turn.uuid) ?? 0,\n }))\n .sort((a, b) => a.maxRowid - b.maxRowid);\n }\n\n /** Raw events after `id`, oldest first (insert-only; id is the watermark). */\n eventsSince(id: number): { id: number; event: SideEvent }[] {\n const rows = this.db\n .prepare(\n `SELECT id, session_id, ts, kind, data, file_path FROM events WHERE id > ? ORDER BY id ASC`,\n )\n .all(id) as {\n id: number;\n session_id: string;\n ts: number | null;\n kind: string;\n data: string;\n file_path: string;\n }[];\n return rows.map((r) => {\n let data: Record<string, unknown> = {};\n try {\n data = JSON.parse(r.data) as Record<string, unknown>;\n } catch {\n // keep {}\n }\n return {\n id: r.id,\n event: {\n kind: r.kind as SideEvent[\"kind\"],\n sessionId: r.session_id,\n timestamp: r.ts ?? undefined,\n filePath: r.file_path,\n data,\n },\n };\n });\n }\n\n loadEvents(): SideEvent[] {\n const rows = this.db\n .prepare(`SELECT session_id, ts, kind, data, file_path FROM events ORDER BY ts ASC`)\n .all() as {\n session_id: string;\n ts: number | null;\n kind: string;\n data: string;\n file_path: string;\n }[];\n return rows.map((r) => {\n let data: Record<string, unknown> = {};\n try {\n data = JSON.parse(r.data) as Record<string, unknown>;\n } catch {\n // keep {}\n }\n return {\n kind: r.kind as SideEvent[\"kind\"],\n sessionId: r.session_id,\n timestamp: r.ts ?? undefined,\n filePath: r.file_path,\n data,\n };\n });\n }\n}\n\nfunction rowToTurn(r: TurnRow): TurnEvent {\n let tools: string[] = [];\n try {\n const parsed = JSON.parse(r.tools) as unknown;\n if (Array.isArray(parsed)) tools = parsed.filter((x): x is string => typeof x === \"string\");\n } catch {\n // keep []\n }\n return {\n agent: \"claude-code\",\n uuid: r.uuid,\n messageId: r.message_id ?? undefined,\n requestId: r.request_id ?? undefined,\n sessionId: r.session_id,\n parentSessionId: r.parent_session_id ?? undefined,\n agentId: r.agent_id ?? undefined,\n isSidechain: r.is_sidechain === 1,\n projectPath: r.cwd ?? \"\",\n slug: r.slug ?? undefined,\n timestamp: r.ts,\n model: r.model,\n usage: {\n input: r.input_tokens,\n output: r.output_tokens,\n cacheRead: r.cache_read_tokens,\n cacheWrite: r.cache_creation_tokens,\n cacheWrite5m: r.cache_5m_tokens,\n cacheWrite1h: r.cache_1h_tokens,\n },\n tools,\n numToolUses: r.num_tool_uses,\n thinkingChars: r.thinking_chars,\n textChars: r.text_chars,\n stopReason: r.stop_reason ?? undefined,\n gitBranch: r.git_branch ?? undefined,\n cliVersion: r.version ?? undefined,\n speed: r.speed ?? undefined,\n inferenceGeo: r.inference_geo ?? undefined,\n filePath: r.file_path,\n };\n}\n","import pc from \"picocolors\";\nimport { ClaudeCodeAdapter } from \"../adapters/claude-code/adapter\";\nimport { openDb } from \"../store/db\";\nimport { Store } from \"../store/queries\";\n\nexport interface ScanOptions {\n claudeDir?: string;\n db?: string;\n full?: boolean;\n verbose?: boolean;\n /** Suppress success output (used by `tktuner sync --quiet`). */\n silent?: boolean;\n}\n\nexport interface ScanResult {\n files: number;\n skipped: number;\n newTurns: number;\n newEvents: number;\n parseErrors: number;\n unknownTypes: Record<string, number>;\n seconds: number;\n}\n\nexport async function scan(opts: ScanOptions): Promise<ScanResult | null> {\n const adapter = new ClaudeCodeAdapter();\n const det = adapter.detect(opts.claudeDir);\n if (!det.found) {\n console.error(\n pc.red(\n `No Claude Code data found at ${opts.claudeDir ?? \"~/.claude/projects\"}. ` +\n `Use --claude-dir to point at your Claude projects directory.`,\n ),\n );\n process.exitCode = 1;\n return null;\n }\n\n const t0 = performance.now();\n const db = openDb(opts.db);\n const store = new Store(db);\n const result: ScanResult = {\n files: 0,\n skipped: 0,\n newTurns: 0,\n newEvents: 0,\n parseErrors: 0,\n unknownTypes: {},\n seconds: 0,\n };\n\n try {\n const files = adapter.listLogFiles(det.rootDirs);\n for (const file of files) {\n const state = store.getFileState(file.path);\n let fromOffset = 0;\n if (opts.full) {\n store.deleteFileData(file.path);\n } else if (state) {\n if (state.mtimeMs === file.mtimeMs && state.size === file.sizeBytes) {\n result.skipped++;\n continue;\n }\n if (file.sizeBytes >= state.byteOffset) {\n fromOffset = state.byteOffset;\n } else {\n // File shrank: it was rewritten, not appended. Reparse from zero.\n store.deleteFileData(file.path);\n }\n }\n\n for await (const batch of adapter.parseFile(file, fromOffset)) {\n const ingested = store.ingestBatch(file, batch);\n result.newTurns += ingested.newTurns;\n result.newEvents += ingested.newEvents;\n result.parseErrors += batch.parseErrors;\n for (const [k, v] of Object.entries(batch.unknownTypes)) {\n result.unknownTypes[k] = (result.unknownTypes[k] ?? 0) + v;\n }\n }\n result.files++;\n if (opts.verbose) console.log(pc.dim(` ${file.kind.padEnd(8)} ${file.path}`));\n }\n store.bumpUnknownTypes(result.unknownTypes);\n } finally {\n db.close();\n }\n\n result.seconds = (performance.now() - t0) / 1000;\n if (!opts.silent) {\n console.log(\n `${pc.bold(\"scan complete\")} — ${result.files} file(s) parsed, ${result.skipped} unchanged, ` +\n `${pc.green(String(result.newTurns))} new assistant line(s), ${result.newEvents} event(s) in ${result.seconds.toFixed(1)}s`,\n );\n if (result.parseErrors > 0) {\n console.log(pc.yellow(` ${result.parseErrors} unparseable line(s) skipped`));\n }\n const unknown = Object.entries(result.unknownTypes);\n if (unknown.length > 0) {\n const summary = unknown.map(([k, v]) => `${k}×${v}`).join(\", \");\n console.log(\n pc.yellow(` unknown line type(s) skipped (parser may need an update): ${summary}`),\n );\n }\n }\n return result;\n}\n","import { type Dirent, existsSync, readdirSync, type Stats, statSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join, relative, sep } from \"node:path\";\nimport type { DiscoveredFile } from \"@tktuner/core\";\n\nconst SESSION_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\\.jsonl$/i;\nconst AGENT_RE = /^agent-([0-9a-z_-]+)\\.jsonl$/i;\n/** Directories inside project dirs that never contain session logs. */\nconst SKIP_DIRS = new Set([\"memory\", \"tool-results\"]);\n\nexport function detectClaudeRoot(override?: string): { found: boolean; rootDirs: string[] } {\n const root = override ?? join(homedir(), \".claude\", \"projects\");\n return existsSync(root) ? { found: true, rootDirs: [root] } : { found: false, rootDirs: [] };\n}\n\n/**\n * Recursively lists every session-log JSONL under the projects root.\n * Layout (verified against Claude Code 2.1.x):\n * <project>/<session-uuid>.jsonl -> session\n * <project>/<session-uuid>/subagents/agent-<id>.jsonl -> subagent\n * <project>/<session-uuid>/subagents/workflows/wf_<id>/agent-<id>.jsonl -> subagent\n * .../workflows/wf_<id>/journal.jsonl -> skipped (no usage)\n * Anything else that ends in .jsonl is kept as 'other' and parsed defensively.\n */\nexport function listLogFiles(rootDirs: string[]): DiscoveredFile[] {\n const out: DiscoveredFile[] = [];\n for (const root of rootDirs) {\n let entries: Dirent[];\n try {\n entries = readdirSync(root, { recursive: true, withFileTypes: true });\n } catch {\n continue;\n }\n for (const ent of entries) {\n if (!ent.isFile() || !ent.name.endsWith(\".jsonl\")) continue;\n if (ent.name === \"journal.jsonl\") continue;\n const dirent = ent as unknown as { parentPath?: string; path?: string };\n const parent = dirent.parentPath ?? dirent.path ?? root;\n const full = join(parent, ent.name);\n const segs = relative(root, full).split(sep);\n if (segs.some((s) => SKIP_DIRS.has(s))) continue;\n\n let st: Stats;\n try {\n st = statSync(full);\n } catch {\n continue; // deleted between listing and stat\n }\n const base = { path: full, sizeBytes: st.size, mtimeMs: Math.trunc(st.mtimeMs) };\n\n const agentMatch = AGENT_RE.exec(ent.name);\n const subIdx = segs.indexOf(\"subagents\");\n if (agentMatch && subIdx > 0) {\n out.push({\n ...base,\n kind: \"subagent\",\n agentId: agentMatch[1],\n parentSessionId: segs[subIdx - 1],\n });\n } else if (segs.length === 2 && SESSION_RE.test(ent.name)) {\n out.push({ ...base, kind: \"session\", sessionId: ent.name.slice(0, -\".jsonl\".length) });\n } else {\n out.push({ ...base, kind: \"other\" });\n }\n }\n }\n out.sort((a, b) => (a.path < b.path ? -1 : a.path > b.path ? 1 : 0));\n return out;\n}\n","import { createReadStream } from \"node:fs\";\n\nexport interface JsonlLine {\n text: string;\n /** Absolute byte offset just past this line (incl. its newline when terminated). */\n endOffset: number;\n /** false only for a trailing line with no newline (likely mid-write). */\n terminated: boolean;\n}\n\nconst NL = 0x0a;\nconst CR = 0x0d;\n\n/**\n * Streams a JSONL file line by line with exact byte-offset accounting so scans\n * can resume incrementally. Splitting on 0x0A is safe in UTF-8 (no multi-byte\n * sequence contains it). Tolerates multi-megabyte lines: memory use is bounded\n * by the largest single line, never the file size.\n */\nexport async function* iterateJsonlLines(path: string, fromOffset = 0): AsyncGenerator<JsonlLine> {\n const stream = createReadStream(path, { start: fromOffset });\n let pending: Buffer[] = [];\n let pendingBytes = 0;\n let offset = fromOffset;\n\n for await (const chunk of stream as AsyncIterable<Buffer>) {\n let start = 0;\n while (start < chunk.length) {\n const idx = chunk.indexOf(NL, start);\n if (idx === -1) {\n pending.push(chunk.subarray(start));\n pendingBytes += chunk.length - start;\n break;\n }\n const slice = chunk.subarray(start, idx);\n const lineBuf = pending.length > 0 ? Buffer.concat([...pending, slice]) : slice;\n pending = [];\n pendingBytes = 0;\n offset += lineBuf.length + 1;\n yield { text: lineText(lineBuf), endOffset: offset, terminated: true };\n start = idx + 1;\n }\n }\n\n if (pendingBytes > 0) {\n const lineBuf = Buffer.concat(pending);\n yield { text: lineText(lineBuf), endOffset: offset + lineBuf.length, terminated: false };\n }\n}\n\nfunction lineText(buf: Buffer): string {\n const end = buf.length > 0 && buf[buf.length - 1] === CR ? buf.length - 1 : buf.length;\n return buf.toString(\"utf8\", 0, end);\n}\n","import type { DiscoveredFile, SideEvent, TurnEvent } from \"@tktuner/core\";\n\n/**\n * Known non-conversational line types. They carry no usage; several have no\n * uuid or timestamp. Skipped silently (unlike genuinely unknown types, which\n * are counted so the parser can evolve with the log format).\n */\nconst SIDECAR_TYPES = new Set([\n \"ai-title\",\n \"mode\",\n \"permission-mode\",\n \"last-prompt\",\n \"agent-name\",\n \"file-history-snapshot\",\n \"queue-operation\",\n \"queued-command\",\n \"started\",\n \"result\",\n \"frame-link\",\n \"summary\",\n]);\n\nexport interface LineOutcome {\n turn?: TurnEvent;\n event?: SideEvent;\n unknownType?: string;\n parseError?: boolean;\n}\n\n/**\n * Parses one JSONL line into a normalized outcome. Defensive by design: any\n * malformed line degrades to a counter, never a crash. Privacy invariant:\n * message text, thinking content and tool inputs are never extracted — only\n * numeric lengths, tool names, usage numbers and identifiers.\n */\nexport function parseLine(raw: string, file: DiscoveredFile): LineOutcome {\n const text = raw.trim();\n if (text === \"\") return {};\n let obj: Record<string, unknown>;\n try {\n const parsed: unknown = JSON.parse(text);\n if (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) {\n return { parseError: true };\n }\n obj = parsed as Record<string, unknown>;\n } catch {\n return { parseError: true };\n }\n const type = obj.type;\n if (typeof type !== \"string\") return { parseError: true };\n\n switch (type) {\n case \"assistant\":\n return assistantToTurn(obj, file);\n case \"user\":\n return userToEvent(obj, file);\n case \"system\":\n return systemToEvent(obj, file);\n case \"attachment\":\n return attachmentToEvent(obj, file);\n default:\n return SIDECAR_TYPES.has(type) ? {} : { unknownType: type };\n }\n}\n\nfunction assistantToTurn(obj: Record<string, unknown>, file: DiscoveredFile): LineOutcome {\n const uuid = str(obj.uuid);\n const msg = obj.message;\n if (!uuid || typeof msg !== \"object\" || msg === null) return { parseError: true };\n const message = msg as Record<string, unknown>;\n const usage = asRecord(message.usage);\n const cacheCreation = asRecord(usage.cache_creation);\n const write5m = num(cacheCreation.ephemeral_5m_input_tokens);\n const write1h = num(cacheCreation.ephemeral_1h_input_tokens);\n const cacheWrite =\n usage.cache_creation_input_tokens !== undefined\n ? num(usage.cache_creation_input_tokens)\n : write5m + write1h;\n\n const tools: string[] = [];\n let thinkingChars = 0;\n let textChars = 0;\n const content = Array.isArray(message.content) ? message.content : [];\n for (const rawBlock of content) {\n if (typeof rawBlock !== \"object\" || rawBlock === null) continue;\n const block = rawBlock as Record<string, unknown>;\n if (block.type === \"tool_use\") {\n const name = str(block.name);\n if (name) tools.push(name);\n } else if (block.type === \"thinking\") {\n thinkingChars += typeof block.thinking === \"string\" ? block.thinking.length : 0;\n } else if (block.type === \"text\") {\n textChars += typeof block.text === \"string\" ? block.text.length : 0;\n }\n }\n\n const turn: TurnEvent = {\n agent: \"claude-code\",\n uuid,\n messageId: str(message.id),\n requestId: str(obj.requestId),\n sessionId: str(obj.sessionId) ?? file.sessionId ?? \"unknown\",\n parentSessionId: file.parentSessionId,\n agentId: str(obj.agentId) ?? file.agentId,\n isSidechain: obj.isSidechain === true,\n projectPath: str(obj.cwd) ?? \"\",\n slug: str(obj.slug),\n timestamp: parseTs(obj.timestamp) ?? 0,\n model: str(message.model) ?? \"unknown\",\n usage: {\n input: num(usage.input_tokens),\n output: num(usage.output_tokens),\n cacheRead: num(usage.cache_read_input_tokens),\n cacheWrite,\n cacheWrite5m: write5m,\n cacheWrite1h: write1h,\n },\n tools,\n numToolUses: tools.length,\n thinkingChars,\n textChars,\n stopReason: str(message.stop_reason),\n gitBranch: str(obj.gitBranch),\n cliVersion: str(obj.version),\n speed: str(usage.speed),\n inferenceGeo: str(usage.inference_geo),\n filePath: file.path,\n };\n return { turn };\n}\n\n/**\n * User lines never carry usage. The only signal extracted is the Agent-tool\n * result summary (`toolUseResult`), whitelisted field by field — its `prompt`\n * and `content` fields are content and must never be stored.\n */\nfunction userToEvent(obj: Record<string, unknown>, file: DiscoveredFile): LineOutcome {\n const tur = obj.toolUseResult;\n if (typeof tur !== \"object\" || tur === null || Array.isArray(tur)) return {};\n const r = tur as Record<string, unknown>;\n const agentId = str(r.agentId);\n const looksLikeAgentResult =\n agentId !== undefined && (\"resolvedModel\" in r || \"agentType\" in r || \"totalTokens\" in r);\n if (!looksLikeAgentResult) return {};\n return {\n event: {\n kind: \"agent_result\",\n sessionId: str(obj.sessionId) ?? file.sessionId ?? \"unknown\",\n timestamp: parseTs(obj.timestamp),\n filePath: file.path,\n data: compact({\n agentId,\n agentType: str(r.agentType),\n resolvedModel: str(r.resolvedModel),\n status: str(r.status),\n totalDurationMs: numOrU(r.totalDurationMs),\n totalTokens: numOrU(r.totalTokens),\n totalToolUseCount: numOrU(r.totalToolUseCount),\n }),\n },\n };\n}\n\nfunction systemToEvent(obj: Record<string, unknown>, file: DiscoveredFile): LineOutcome {\n const subtype = str(obj.subtype);\n const sessionId = str(obj.sessionId) ?? file.sessionId ?? \"unknown\";\n const timestamp = parseTs(obj.timestamp);\n if (subtype === \"compact_boundary\") {\n const meta = asRecord(obj.compactMetadata);\n return {\n event: {\n kind: \"compact_boundary\",\n sessionId,\n timestamp,\n filePath: file.path,\n data: compact({\n trigger: str(meta.trigger),\n preTokens: numOrU(meta.preTokens),\n postTokens: numOrU(meta.postTokens),\n cumulativeDroppedTokens: numOrU(meta.cumulativeDroppedTokens),\n durationMs: numOrU(meta.durationMs),\n }),\n },\n };\n }\n if (subtype === \"turn_duration\") {\n return {\n event: {\n kind: \"turn_duration\",\n sessionId,\n timestamp,\n filePath: file.path,\n data: compact({\n durationMs: numOrU(obj.durationMs),\n messageCount: numOrU(obj.messageCount),\n }),\n },\n };\n }\n return {};\n}\n\nfunction attachmentToEvent(obj: Record<string, unknown>, file: DiscoveredFile): LineOutcome {\n const att = asRecord(obj.attachment);\n const attType = str(att.type);\n if (attType !== \"plan_mode\" && attType !== \"plan_mode_reentry\" && attType !== \"plan_mode_exit\") {\n return {};\n }\n return {\n event: {\n kind: attType === \"plan_mode_exit\" ? \"plan_mode_exit\" : \"plan_mode\",\n sessionId: str(obj.sessionId) ?? file.sessionId ?? \"unknown\",\n timestamp: parseTs(obj.timestamp),\n filePath: file.path,\n data: {},\n },\n };\n}\n\nfunction str(v: unknown): string | undefined {\n return typeof v === \"string\" ? v : undefined;\n}\n\nfunction num(v: unknown): number {\n return typeof v === \"number\" && Number.isFinite(v) ? v : 0;\n}\n\nfunction numOrU(v: unknown): number | undefined {\n return typeof v === \"number\" && Number.isFinite(v) ? v : undefined;\n}\n\nfunction parseTs(v: unknown): number | undefined {\n if (typeof v !== \"string\") return undefined;\n const ts = Date.parse(v);\n return Number.isFinite(ts) ? ts : undefined;\n}\n\nfunction asRecord(v: unknown): Record<string, unknown> {\n return typeof v === \"object\" && v !== null && !Array.isArray(v)\n ? (v as Record<string, unknown>)\n : {};\n}\n\nfunction compact(data: Record<string, unknown>): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(data)) if (v !== undefined) out[k] = v;\n return out;\n}\n","import type { AgentAdapter, DiscoveredFile, ParseBatch } from \"@tktuner/core\";\nimport { detectClaudeRoot, listLogFiles } from \"./discover\";\nimport { iterateJsonlLines } from \"./jsonl\";\nimport { parseLine } from \"./parse\";\n\nconst BATCH_LINES = 2000;\n\nexport class ClaudeCodeAdapter implements AgentAdapter {\n readonly kind = \"claude-code\" as const;\n\n detect(rootOverride?: string): { found: boolean; rootDirs: string[] } {\n return detectClaudeRoot(rootOverride);\n }\n\n listLogFiles(rootDirs: string[]): DiscoveredFile[] {\n return listLogFiles(rootDirs);\n }\n\n async *parseFile(file: DiscoveredFile, fromOffset: number): AsyncIterable<ParseBatch> {\n let batch = emptyBatch(fromOffset);\n for await (const line of iterateJsonlLines(file.path, fromOffset)) {\n const outcome = parseLine(line.text, file);\n batch.linesParsed++;\n if (line.terminated) {\n batch.bytesConsumed = line.endOffset;\n if (outcome.turn) batch.turns.push(outcome.turn);\n if (outcome.event) batch.events.push(outcome.event);\n if (outcome.unknownType) {\n batch.unknownTypes[outcome.unknownType] =\n (batch.unknownTypes[outcome.unknownType] ?? 0) + 1;\n }\n if (outcome.parseError) batch.parseErrors++;\n } else {\n // Trailing line with no newline: usually a live session mid-write.\n // Ingest a complete turn if one parsed (uuid PK makes the re-read on\n // the next scan idempotent), but never events (no natural key) and\n // never advance bytesConsumed past the last terminated line. A parse\n // failure here is expected, not an error.\n if (outcome.turn) batch.turns.push(outcome.turn);\n }\n if (batch.linesParsed >= BATCH_LINES) {\n yield batch;\n batch = emptyBatch(batch.bytesConsumed);\n }\n }\n // Always yield the final (possibly empty) batch so the caller can\n // checkpoint file state even when nothing new was read.\n yield batch;\n }\n}\n\nfunction emptyBatch(offset: number): ParseBatch {\n return {\n turns: [],\n events: [],\n bytesConsumed: offset,\n linesParsed: 0,\n parseErrors: 0,\n unknownTypes: {},\n };\n}\n","// tktuner sync — scan locally, then push NEW merged turns + events to the API.\n//\n// Incremental selection: raw sqlite rows with rowid > watermark identify the\n// touched turnKeys; the WHOLE line group of each key is reloaded and re-merged\n// (mergeTurnLines) so late-arriving content blocks of a message overwrite the\n// server row via the (user_id, turn_key) upsert. The watermark only advances\n// after a 2xx, so a crash re-sends at most one batch — idempotent by design.\n//\n// --quiet (used by the SessionEnd hook) NEVER fails: errors go to\n// ~/.local/share/tktuner/sync.log and the exit code stays 0 so Claude Code is\n// never disturbed.\nimport { appendFileSync, mkdirSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport {\n SYNC_MAX_EVENTS_PER_BATCH,\n SYNC_MAX_TURNS_PER_BATCH,\n type SyncEventWire,\n} from \"@tktuner/core\";\nimport pc from \"picocolors\";\nimport { apiCall } from \"../lib/api-client\";\nimport { readCredentials } from \"../lib/credentials\";\nimport { dataDir } from \"../lib/paths\";\nimport { toSyncEvent, toSyncTurn } from \"../lib/privacy\";\nimport { CLI_VERSION } from \"../lib/version\";\nimport { openDb } from \"../store/db\";\nimport { Store } from \"../store/queries\";\nimport { scan } from \"./scan\";\n\nexport interface SyncOptions {\n quiet?: boolean;\n full?: boolean;\n dryRun?: boolean;\n noScan?: boolean;\n db?: string;\n claudeDir?: string;\n api?: string;\n}\n\nexport interface SyncOutcome {\n turnsPushed: number;\n eventsPushed: number;\n batches: number;\n}\n\nfunction logFailure(err: unknown): void {\n try {\n mkdirSync(dataDir(), { recursive: true });\n const line = `${new Date().toISOString()} sync failed: ${\n err instanceof Error ? err.message : String(err)\n }\\n`;\n appendFileSync(join(dataDir(), \"sync.log\"), line);\n } catch {\n // even logging is best-effort in quiet mode\n }\n}\n\nexport async function sync(opts: SyncOptions): Promise<SyncOutcome | null> {\n const say = (msg: string): void => {\n if (!opts.quiet) console.log(msg);\n };\n try {\n const creds = readCredentials();\n if (!creds) {\n if (opts.quiet) return null; // hook installed but not logged in — stay silent\n console.log(`Not logged in. Run ${pc.bold(\"tktuner login\")} first.`);\n process.exitCode = 1;\n return null;\n }\n const apiUrl = opts.api ?? creds.apiUrl;\n\n if (!opts.noScan) {\n await scan({ db: opts.db, claudeDir: opts.claudeDir, silent: opts.quiet });\n }\n\n const db = openDb(opts.db);\n try {\n const store = new Store(db);\n\n // New destination (device/api changed) or --full => re-push everything.\n const boundDevice = store.getMeta(\"sync_device_id\");\n const boundApi = store.getMeta(\"sync_api_url\");\n if (opts.full || boundDevice !== creds.deviceId || boundApi !== apiUrl) {\n store.setMeta(\"sync_last_turn_rowid\", \"0\");\n store.setMeta(\"sync_last_event_id\", \"0\");\n store.setMeta(\"sync_device_id\", creds.deviceId);\n store.setMeta(\"sync_api_url\", apiUrl);\n }\n const turnWm = Number(store.getMeta(\"sync_last_turn_rowid\") ?? \"0\");\n const eventWm = Number(store.getMeta(\"sync_last_event_id\") ?? \"0\");\n\n const { keys } = store.newTurnKeysSince(turnWm);\n const groups = store.loadTurnGroups(keys);\n const pendingEvents = store.eventsSince(eventWm);\n\n if (opts.dryRun) {\n say(\n `${pc.bold(\"dry run\")} — would push ${pc.green(String(groups.length))} turn(s) and ${pendingEvents.length} event(s) to ${apiUrl}`,\n );\n for (const g of groups.slice(0, 2)) {\n say(pc.dim(JSON.stringify(toSyncTurn(g.turn), null, 2)));\n }\n return { turnsPushed: 0, eventsPushed: 0, batches: 0 };\n }\n\n if (groups.length === 0 && pendingEvents.length === 0) {\n say(`${pc.bold(\"sync\")} — already up to date (${apiUrl})`);\n return { turnsPushed: 0, eventsPushed: 0, batches: 0 };\n }\n\n const t0 = performance.now();\n let turnCursor = 0;\n let eventCursor = 0;\n let turnsPushed = 0;\n let eventsPushed = 0;\n let batches = 0;\n while (turnCursor < groups.length || eventCursor < pendingEvents.length) {\n const turnBatch = groups.slice(turnCursor, turnCursor + SYNC_MAX_TURNS_PER_BATCH);\n const eventBatch = pendingEvents.slice(\n eventCursor,\n eventCursor + SYNC_MAX_EVENTS_PER_BATCH,\n );\n const eventWires: SyncEventWire[] = eventBatch.map((e) => toSyncEvent(e.id, e.event));\n await apiCall(\"POST\", apiUrl, \"/v1/sync\", {\n token: creds.deviceToken,\n gzip: true,\n body: {\n turns: turnBatch.map((g) => toSyncTurn(g.turn)),\n events: eventWires,\n cliVersion: CLI_VERSION,\n },\n });\n // 2xx confirmed — advance the watermarks past this batch.\n if (turnBatch.length > 0) {\n const maxRowid = turnBatch[turnBatch.length - 1]?.maxRowid ?? turnWm;\n store.setMeta(\"sync_last_turn_rowid\", String(maxRowid));\n }\n if (eventBatch.length > 0) {\n const maxId = eventBatch[eventBatch.length - 1]?.id ?? eventWm;\n store.setMeta(\"sync_last_event_id\", String(maxId));\n }\n turnCursor += turnBatch.length;\n eventCursor += eventBatch.length;\n turnsPushed += turnBatch.length;\n eventsPushed += eventWires.length;\n batches++;\n }\n const seconds = ((performance.now() - t0) / 1000).toFixed(1);\n say(\n `${pc.bold(\"sync complete\")} — ${pc.green(String(turnsPushed))} turn(s), ${eventsPushed} event(s) in ${batches} batch(es), ${seconds}s → ${apiUrl}`,\n );\n return { turnsPushed, eventsPushed, batches };\n } finally {\n db.close();\n }\n } catch (err) {\n if (opts.quiet) {\n logFailure(err);\n return null; // exit 0 — never disturb the hook caller\n }\n console.error(pc.red(err instanceof Error ? err.message : String(err)));\n process.exitCode = 1;\n return null;\n }\n}\n","// The sync privacy transform. Everything that leaves the machine goes through\n// toSyncTurn/toSyncEvent: cwd becomes a truncated hash + basename, and\n// gitBranch/filePath are dropped entirely. The local DB already contains no\n// message text, thinking content or tool inputs.\nimport { createHash } from \"node:crypto\";\nimport {\n basename,\n type SideEvent,\n type SyncEventWire,\n type SyncTurnWire,\n type TurnEvent,\n} from \"@tktuner/core\";\n\n/** sha256(cwd) truncated to 16 hex chars — stable id; the path never leaves. */\nexport function projectIdOf(cwd: string): string {\n return createHash(\"sha256\").update(cwd).digest(\"hex\").slice(0, 16);\n}\n\nexport function projectNameOf(cwd: string): string {\n return cwd ? basename(cwd).slice(0, 120) : \"unknown\";\n}\n\nexport function toSyncTurn(t: TurnEvent): SyncTurnWire {\n return {\n turnKey: t.messageId ?? t.uuid,\n uuid: t.uuid,\n messageId: t.messageId,\n requestId: t.requestId,\n sessionId: t.sessionId,\n agentId: t.agentId,\n parentSessionId: t.parentSessionId,\n isSidechain: t.isSidechain,\n projectId: projectIdOf(t.projectPath),\n projectName: projectNameOf(t.projectPath),\n slug: t.slug?.slice(0, 120),\n ts: t.timestamp,\n model: t.model.slice(0, 120),\n usage: t.usage,\n tools: t.tools.slice(0, 500).map((name) => name.slice(0, 100)),\n numToolUses: t.numToolUses,\n thinkingChars: t.thinkingChars,\n textChars: t.textChars,\n stopReason: t.stopReason?.slice(0, 60),\n version: t.cliVersion?.slice(0, 40),\n speed: t.speed?.slice(0, 20),\n inferenceGeo: t.inferenceGeo?.slice(0, 20),\n };\n}\n\nexport function toSyncEvent(id: number, e: SideEvent): SyncEventWire {\n return {\n localEventId: id,\n sessionId: e.sessionId,\n ts: e.timestamp,\n kind: e.kind,\n data: e.data, // already whitelisted at parse time — never prompt/content\n };\n}\n","// tktuner whoami — account + device + sync state.\nimport pc from \"picocolors\";\nimport { ApiError, apiCall } from \"../lib/api-client\";\nimport { readCredentials } from \"../lib/credentials\";\nimport { openDb } from \"../store/db\";\nimport { Store } from \"../store/queries\";\n\nexport async function whoami(opts: { db?: string }): Promise<void> {\n const creds = readCredentials();\n if (!creds) {\n console.log(`Not logged in. Run ${pc.bold(\"tktuner login\")}.`);\n return;\n }\n try {\n const status = await apiCall(\"GET\", creds.apiUrl, \"/v1/sync/status\", {\n token: creds.deviceToken,\n });\n console.log(`${pc.bold(String(status.email))} · device ${creds.deviceName} → ${creds.apiUrl}`);\n console.log(\n `server: ${pc.bold(String(status.serverTurnCount))} turns · last sync ${\n status.lastSyncedAt ? String(status.lastSyncedAt) : \"never\"\n }`,\n );\n } catch (err) {\n if (err instanceof ApiError && err.status === 401) {\n console.error(pc.red(`Device token was revoked. Run ${pc.bold(\"tktuner login\")} again.`));\n process.exitCode = 1;\n return;\n }\n throw err;\n }\n const db = openDb(opts.db);\n try {\n const store = new Store(db);\n const wm = store.getMeta(\"sync_last_turn_rowid\") ?? \"0\";\n console.log(`local: ${store.countTurns()} turns · sync watermark @ row ${wm}`);\n } finally {\n db.close();\n }\n}\n"],"mappings":"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","names":["join","join","pc","pc","report","pc","readFileSync","readFileSync","pc","c","mkdirSync","join","join","mkdirSync","c","pc","pc","existsSync","homedir","join","pc","mkdirSync","join","pc","mkdirSync","join","pc","pc","pc"]}
@@ -0,0 +1,114 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/cli/hook.ts
4
+ import { copyFileSync, existsSync, readFileSync, writeFileSync } from "fs";
5
+ import { homedir } from "os";
6
+ import { join } from "path";
7
+ import { createInterface } from "readline/promises";
8
+ import pc from "picocolors";
9
+ var HOOK_COMMAND = "tktuner sync --quiet";
10
+ function settingsPath() {
11
+ const base = process.env.CLAUDE_CONFIG_DIR ?? join(homedir(), ".claude");
12
+ return join(base, "settings.json");
13
+ }
14
+ function isOurs(cmd) {
15
+ return cmd.type === "command" && typeof cmd.command === "string" && cmd.command.includes("tktuner sync");
16
+ }
17
+ function readSettings(path) {
18
+ if (!existsSync(path)) return {};
19
+ try {
20
+ return JSON.parse(readFileSync(path, "utf8"));
21
+ } catch {
22
+ return null;
23
+ }
24
+ }
25
+ function isInstalled(settings) {
26
+ const entries = settings.hooks?.SessionEnd ?? [];
27
+ return entries.some((e) => (e.hooks ?? []).some(isOurs));
28
+ }
29
+ async function confirm(message) {
30
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
31
+ try {
32
+ const answer = await rl.question(`${message} [y/N] `);
33
+ return /^y(es)?$/i.test(answer.trim());
34
+ } finally {
35
+ rl.close();
36
+ }
37
+ }
38
+ function writeSettings(path, settings) {
39
+ if (existsSync(path)) copyFileSync(path, `${path}.tktuner-bak`);
40
+ writeFileSync(path, `${JSON.stringify(settings, null, 2)}
41
+ `);
42
+ }
43
+ async function hookInstall(opts) {
44
+ const path = settingsPath();
45
+ const settings = readSettings(path);
46
+ if (settings === null) {
47
+ console.error(
48
+ pc.red(`${path} is not valid JSON \u2014 fix it manually first (nothing was changed).`)
49
+ );
50
+ process.exitCode = 1;
51
+ return;
52
+ }
53
+ if (isInstalled(settings)) {
54
+ console.log(`Hook already installed in ${path}.`);
55
+ return;
56
+ }
57
+ console.log(`This adds a SessionEnd hook to ${pc.bold(path)}:`);
58
+ console.log(pc.dim(` ${HOOK_COMMAND} (runs when a Claude Code session ends; exit 0 always)`));
59
+ if (!opts.yes && !await confirm("Install?")) {
60
+ console.log("Aborted \u2014 nothing changed.");
61
+ return;
62
+ }
63
+ settings.hooks ??= {};
64
+ settings.hooks.SessionEnd ??= [];
65
+ settings.hooks.SessionEnd.push({
66
+ hooks: [{ type: "command", command: HOOK_COMMAND, timeout: 120 }]
67
+ });
68
+ writeSettings(path, settings);
69
+ console.log(
70
+ `${pc.green("Installed.")} Every Claude Code session now syncs on exit. Backup at ${path}.tktuner-bak \xB7 remove with ${pc.bold("tktuner hook uninstall")}.`
71
+ );
72
+ }
73
+ async function hookUninstall() {
74
+ const path = settingsPath();
75
+ const settings = readSettings(path);
76
+ if (settings === null) {
77
+ console.error(pc.red(`${path} is not valid JSON \u2014 fix it manually (nothing was changed).`));
78
+ process.exitCode = 1;
79
+ return;
80
+ }
81
+ if (!isInstalled(settings)) {
82
+ console.log("Hook is not installed.");
83
+ return;
84
+ }
85
+ const entries = settings.hooks?.SessionEnd ?? [];
86
+ const kept = entries.map((e) => ({ ...e, hooks: (e.hooks ?? []).filter((c) => !isOurs(c)) })).filter((e) => (e.hooks?.length ?? 0) > 0);
87
+ if (settings.hooks) {
88
+ if (kept.length > 0) settings.hooks.SessionEnd = kept;
89
+ else delete settings.hooks.SessionEnd;
90
+ if (Object.keys(settings.hooks).length === 0) delete settings.hooks;
91
+ }
92
+ writeSettings(path, settings);
93
+ console.log(`${pc.green("Removed.")} ${path} otherwise untouched.`);
94
+ }
95
+ function hookStatus() {
96
+ const path = settingsPath();
97
+ const settings = readSettings(path);
98
+ if (settings === null) {
99
+ console.log(`${path} exists but is not valid JSON.`);
100
+ return;
101
+ }
102
+ console.log(
103
+ isInstalled(settings) ? `${pc.green("installed")} \u2014 SessionEnd runs "${HOOK_COMMAND}" (${path})` : `not installed \u2014 run ${pc.bold("tktuner hook install")}`
104
+ );
105
+ }
106
+ export {
107
+ HOOK_COMMAND,
108
+ hookInstall,
109
+ hookStatus,
110
+ hookUninstall,
111
+ isInstalled,
112
+ settingsPath
113
+ };
114
+ //# sourceMappingURL=hook-QKTYBOHD.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/hook.ts"],"sourcesContent":["// tktuner hook install|uninstall|status — opt-in SessionEnd hook in\n// ~/.claude/settings.json that runs `tktuner sync --quiet` when a Claude Code\n// session ends. Merge-safe: every unknown key is preserved byte-for-byte at\n// the JSON level, a .tktuner-bak backup is written first, and an unparseable\n// settings file ABORTS instead of being clobbered.\nimport { copyFileSync, existsSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { createInterface } from \"node:readline/promises\";\nimport pc from \"picocolors\";\n\nexport const HOOK_COMMAND = \"tktuner sync --quiet\";\n\ninterface HookCommand {\n type: string;\n command: string;\n timeout?: number;\n}\n\ninterface HookEntry {\n matcher?: string;\n hooks?: HookCommand[];\n}\n\ntype Settings = Record<string, unknown> & {\n hooks?: Record<string, HookEntry[]>;\n};\n\nexport function settingsPath(): string {\n const base = process.env.CLAUDE_CONFIG_DIR ?? join(homedir(), \".claude\");\n return join(base, \"settings.json\");\n}\n\nfunction isOurs(cmd: HookCommand): boolean {\n return (\n cmd.type === \"command\" &&\n typeof cmd.command === \"string\" &&\n cmd.command.includes(\"tktuner sync\")\n );\n}\n\nfunction readSettings(path: string): Settings | null {\n if (!existsSync(path)) return {};\n try {\n return JSON.parse(readFileSync(path, \"utf8\")) as Settings;\n } catch {\n return null; // unparseable — never overwrite\n }\n}\n\nexport function isInstalled(settings: Settings): boolean {\n const entries = settings.hooks?.SessionEnd ?? [];\n return entries.some((e) => (e.hooks ?? []).some(isOurs));\n}\n\nasync function confirm(message: string): Promise<boolean> {\n const rl = createInterface({ input: process.stdin, output: process.stdout });\n try {\n const answer = await rl.question(`${message} [y/N] `);\n return /^y(es)?$/i.test(answer.trim());\n } finally {\n rl.close();\n }\n}\n\nfunction writeSettings(path: string, settings: Settings): void {\n if (existsSync(path)) copyFileSync(path, `${path}.tktuner-bak`);\n writeFileSync(path, `${JSON.stringify(settings, null, 2)}\\n`);\n}\n\nexport interface HookOptions {\n yes?: boolean;\n}\n\nexport async function hookInstall(opts: HookOptions): Promise<void> {\n const path = settingsPath();\n const settings = readSettings(path);\n if (settings === null) {\n console.error(\n pc.red(`${path} is not valid JSON — fix it manually first (nothing was changed).`),\n );\n process.exitCode = 1;\n return;\n }\n if (isInstalled(settings)) {\n console.log(`Hook already installed in ${path}.`);\n return;\n }\n console.log(`This adds a SessionEnd hook to ${pc.bold(path)}:`);\n console.log(pc.dim(` ${HOOK_COMMAND} (runs when a Claude Code session ends; exit 0 always)`));\n if (!opts.yes && !(await confirm(\"Install?\"))) {\n console.log(\"Aborted — nothing changed.\");\n return;\n }\n settings.hooks ??= {};\n settings.hooks.SessionEnd ??= [];\n settings.hooks.SessionEnd.push({\n hooks: [{ type: \"command\", command: HOOK_COMMAND, timeout: 120 }],\n });\n writeSettings(path, settings);\n console.log(\n `${pc.green(\"Installed.\")} Every Claude Code session now syncs on exit. ` +\n `Backup at ${path}.tktuner-bak · remove with ${pc.bold(\"tktuner hook uninstall\")}.`,\n );\n}\n\nexport async function hookUninstall(): Promise<void> {\n const path = settingsPath();\n const settings = readSettings(path);\n if (settings === null) {\n console.error(pc.red(`${path} is not valid JSON — fix it manually (nothing was changed).`));\n process.exitCode = 1;\n return;\n }\n if (!isInstalled(settings)) {\n console.log(\"Hook is not installed.\");\n return;\n }\n const entries = settings.hooks?.SessionEnd ?? [];\n const kept = entries\n .map((e) => ({ ...e, hooks: (e.hooks ?? []).filter((c) => !isOurs(c)) }))\n .filter((e) => (e.hooks?.length ?? 0) > 0);\n if (settings.hooks) {\n if (kept.length > 0) settings.hooks.SessionEnd = kept;\n else delete settings.hooks.SessionEnd;\n if (Object.keys(settings.hooks).length === 0) delete settings.hooks;\n }\n writeSettings(path, settings);\n console.log(`${pc.green(\"Removed.\")} ${path} otherwise untouched.`);\n}\n\nexport function hookStatus(): void {\n const path = settingsPath();\n const settings = readSettings(path);\n if (settings === null) {\n console.log(`${path} exists but is not valid JSON.`);\n return;\n }\n console.log(\n isInstalled(settings)\n ? `${pc.green(\"installed\")} — SessionEnd runs \"${HOOK_COMMAND}\" (${path})`\n : `not installed — run ${pc.bold(\"tktuner hook install\")}`,\n );\n}\n"],"mappings":"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","names":[]}