kerf-cli 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,12 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## v0.1.1 (2026-04-04)
4
+
5
+ - Fix: ContextBar crash when token usage exceeds 100% of context window
6
+ - Fix: Package published as `kerf-cli` (npm blocked unscoped `kerf`)
7
+
3
8
  ## v0.1.0 (2026-04-04)
4
9
 
5
10
  Initial release.
6
11
 
7
- - `kerf watch` — Real-time cost dashboard
8
- - `kerf estimate <task>` — Pre-flight cost estimation
9
- - `kerf budget set/show/list/remove` — Per-project budget management
10
- - `kerf audit` — Ghost token & CLAUDE.md audit
11
- - `kerf report` — Historical cost reports
12
- - `kerf init` — Setup hooks and database
12
+ - `npx kerf-cli watch` — Real-time cost dashboard
13
+ - `npx kerf-cli estimate <task>` — Pre-flight cost estimation
14
+ - `npx kerf-cli budget set/show/list/remove` — Per-project budget management
15
+ - `npx kerf-cli audit` — Ghost token & CLAUDE.md audit
16
+ - `npx kerf-cli report` — Historical cost reports
17
+ - `npx kerf-cli init` — Setup hooks and database
package/README.md CHANGED
@@ -1,10 +1,10 @@
1
- # kerf
1
+ # kerf-cli
2
2
 
3
3
  **Cost intelligence for Claude Code. Know before you spend.**
4
4
 
5
5
  > *kerf (n.) — the width of material removed by a cutting tool. Every token operation has a kerf.*
6
6
 
7
- [![npm version](https://img.shields.io/npm/v/kerf)](https://www.npmjs.com/package/kerf)
7
+ [![npm version](https://img.shields.io/npm/v/kerf-cli)](https://www.npmjs.com/package/kerf-cli)
8
8
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
9
9
  [![Node 20+](https://img.shields.io/badge/node-20%2B-green.svg)]()
10
10
  [![TypeScript](https://img.shields.io/badge/TypeScript-5.8-blue.svg)]()
@@ -32,16 +32,16 @@
32
32
 
33
33
  You don't know what you're spending until it's gone. Claude Code sessions burn through tokens fast — context overhead, MCP tools, bloated CLAUDE.md files — and there's no way to see it happening in real time.
34
34
 
35
- **kerf fixes that.**
35
+ **kerf-cli fixes that.**
36
36
 
37
37
  ---
38
38
 
39
39
  ## Quick Start
40
40
 
41
41
  ```bash
42
- npx kerf@latest init # Set up hooks & database
43
- npx kerf@latest watch # Real-time cost dashboard
44
- npx kerf@latest audit # Find ghost token waste
42
+ npx kerf-cli@latest init # Set up hooks & database
43
+ npx kerf-cli@latest watch # Real-time cost dashboard
44
+ npx kerf-cli@latest audit # Find ghost token waste
45
45
  ```
46
46
 
47
47
  ---
@@ -57,9 +57,9 @@ Live cost monitoring while Claude Code runs. See your burn rate, context usage,
57
57
  Know the cost before you start.
58
58
 
59
59
  ```bash
60
- $ kerf estimate 'refactor auth module'
60
+ $ npx kerf-cli estimate 'refactor auth module'
61
61
 
62
- ╭───────────────────────────────────────────────────────���──╮
62
+ ╭──────────────────────────────────────────────────────────╮
63
63
  │ kerf estimate: 'refactor auth module' │
64
64
  │ │
65
65
  │ Model: Sonnet 4 │
@@ -80,9 +80,9 @@ $ kerf estimate 'refactor auth module'
80
80
  Set spending limits and get warnings before you go over.
81
81
 
82
82
  ```bash
83
- kerf budget set 50 --period weekly
84
- kerf budget show
85
- kerf budget list
83
+ npx kerf-cli budget set 50 --period weekly
84
+ npx kerf-cli budget show
85
+ npx kerf-cli budget list
86
86
  ```
87
87
 
88
88
  ### Ghost Token Audit — `kerf audit`
@@ -90,7 +90,7 @@ kerf budget list
90
90
  Find and fix invisible token waste: system prompt overhead, MCP tool bloat, CLAUDE.md dead zones.
91
91
 
92
92
  ```bash
93
- $ kerf audit
93
+ $ npx kerf-cli audit
94
94
 
95
95
  Context Window Health: B (62% usable)
96
96
 
@@ -111,18 +111,18 @@ $ kerf audit
111
111
  Track spending over time with per-model and per-session breakdowns.
112
112
 
113
113
  ```bash
114
- kerf report # Today's costs
115
- kerf report --period week # Weekly summary
116
- kerf report --csv # Export for spreadsheets
117
- kerf report --sessions # Per-session breakdown
114
+ npx kerf-cli report # Today's costs
115
+ npx kerf-cli report --period week # Weekly summary
116
+ npx kerf-cli report --csv # Export for spreadsheets
117
+ npx kerf-cli report --sessions # Per-session breakdown
118
118
  ```
119
119
 
120
120
  ---
121
121
 
122
- ## Why kerf?
122
+ ## Why kerf-cli?
123
123
 
124
- | Feature | kerf | RTK | ccusage | token-optimizer |
125
- |---------|------|-----|---------|-----------------|
124
+ | Feature | kerf-cli | RTK | ccusage | token-optimizer |
125
+ |---------|----------|-----|---------|-----------------|
126
126
  | Real-time dashboard | Yes | No | No | No |
127
127
  | Pre-flight estimation | Yes | No | No | No |
128
128
  | Per-project budgets | Yes | No | No | No |
@@ -137,8 +137,8 @@ kerf report --sessions # Per-session breakdown
137
137
 
138
138
  ## Works With
139
139
 
140
- - **RTK** — complementary (kerf shows savings from RTK compression)
141
- - **ccusage** — compatible (kerf can import historical data)
140
+ - **RTK** — complementary (kerf-cli shows savings from RTK compression)
141
+ - **ccusage** — compatible (kerf-cli can import historical data)
142
142
  - **ECC** — compatible hooks
143
143
 
144
144
  ---
@@ -147,15 +147,20 @@ kerf report --sessions # Per-session breakdown
147
147
 
148
148
  | Command | Description |
149
149
  |---------|-------------|
150
- | `kerf` / `kerf watch` | Real-time cost dashboard (default) |
151
- | `kerf estimate <task>` | Pre-flight cost estimation |
152
- | `kerf budget set <amt>` | Set project budget |
153
- | `kerf budget show` | Show current budget status |
154
- | `kerf budget list` | List all project budgets |
155
- | `kerf audit` | Ghost token & CLAUDE.md audit |
156
- | `kerf audit --fix` | Auto-apply safe optimizations |
157
- | `kerf report` | Historical cost reports |
158
- | `kerf init` | Set up kerf (hooks, database) |
150
+ | `npx kerf-cli` / `npx kerf-cli watch` | Real-time cost dashboard (default) |
151
+ | `npx kerf-cli estimate <task>` | Pre-flight cost estimation |
152
+ | `npx kerf-cli budget set <amt>` | Set project budget |
153
+ | `npx kerf-cli budget show` | Show current budget status |
154
+ | `npx kerf-cli budget list` | List all project budgets |
155
+ | `npx kerf-cli audit` | Ghost token & CLAUDE.md audit |
156
+ | `npx kerf-cli audit --fix` | Auto-apply safe optimizations |
157
+ | `npx kerf-cli report` | Historical cost reports |
158
+ | `npx kerf-cli init` | Set up kerf (hooks, database) |
159
+
160
+ **Tip:** Add an alias for convenience:
161
+ ```bash
162
+ echo 'alias kerf="npx kerf-cli"' >> ~/.zshrc
163
+ ```
159
164
 
160
165
  ---
161
166
 
package/dist/index.js CHANGED
@@ -376,7 +376,8 @@ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
376
376
  function ContextBar({ used, total, overhead }) {
377
377
  const barWidth = 30;
378
378
  const usedPct = total > 0 ? used / total * 100 : 0;
379
- const filledCount = Math.round(usedPct / 100 * barWidth);
379
+ const clampedPct = Math.max(0, Math.min(usedPct, 100));
380
+ const filledCount = Math.round(clampedPct / 100 * barWidth);
380
381
  const emptyCount = barWidth - filledCount;
381
382
  const color = usedPct < 50 ? "green" : usedPct < 80 ? "yellow" : "red";
382
383
  const filled = "\u2588".repeat(filledCount);
@@ -572,7 +573,7 @@ function Dashboard({ sessionFilePath, interval }) {
572
573
  const recentMessages = session.messages.slice(-8);
573
574
  return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
574
575
  /* @__PURE__ */ jsxs3(Box3, { borderStyle: "round", borderColor: "cyan", paddingX: 1, children: [
575
- /* @__PURE__ */ jsx3(Text3, { bold: true, color: "cyan", children: "kerf watch" }),
576
+ /* @__PURE__ */ jsx3(Text3, { bold: true, color: "cyan", children: "kerf-cli watch" }),
576
577
  /* @__PURE__ */ jsxs3(Text3, { children: [
577
578
  " | session: ",
578
579
  session.sessionId.slice(0, 8),
@@ -632,7 +633,7 @@ function registerWatchCommand(program2) {
632
633
  }
633
634
  if (!sessionFilePath) {
634
635
  console.log(
635
- "No active Claude Code session found. Start Claude Code and run 'kerf watch' again."
636
+ "No active Claude Code session found. Start Claude Code and run 'kerf-cli watch' again."
636
637
  );
637
638
  process.exit(0);
638
639
  }
@@ -735,7 +736,7 @@ async function estimateTaskCost(taskDescription, options = {}) {
735
736
  recommendations.push(`Using Opus would cost ~${formatCost(expectedCost * ratio)} (${ratio.toFixed(0)}x more)`);
736
737
  }
737
738
  if (overhead.percentUsable < 60) {
738
- recommendations.push(`High ghost token overhead (${(100 - overhead.percentUsable).toFixed(0)}%). Run 'kerf audit' to optimize.`);
739
+ recommendations.push(`High ghost token overhead (${(100 - overhead.percentUsable).toFixed(0)}%). Run 'kerf-cli audit' to optimize.`);
739
740
  }
740
741
  if (fileTokens > 5e4) {
741
742
  recommendations.push(`Large file context (${(fileTokens / 1e3).toFixed(0)}K tokens). Consider narrowing scope.`);
@@ -767,7 +768,7 @@ function EstimateCard({ task, estimate }) {
767
768
  const formatK = (n) => `${(n / 1e3).toFixed(1)}K`;
768
769
  return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 2, paddingY: 1, children: [
769
770
  /* @__PURE__ */ jsxs4(Text4, { bold: true, color: "cyan", children: [
770
- "kerf estimate: '",
771
+ "kerf-cli estimate: '",
771
772
  task,
772
773
  "'"
773
774
  ] }),
@@ -1086,7 +1087,7 @@ function registerBudgetCommand(program2) {
1086
1087
  const projectPath = opts.project || process.cwd();
1087
1088
  const status = manager.checkBudget(projectPath);
1088
1089
  if (!status) {
1089
- console.log("No budget set for this project. Use 'kerf budget set <amount>' to set one.");
1090
+ console.log("No budget set for this project. Use 'kerf-cli budget set <amount>' to set one.");
1090
1091
  manager.close();
1091
1092
  return;
1092
1093
  }
@@ -1101,7 +1102,7 @@ function registerBudgetCommand(program2) {
1101
1102
  const empty = barWidth - filled;
1102
1103
  const color = pct < 50 ? "green" : pct < 80 ? "yellow" : "red";
1103
1104
  const barColor = color === "green" ? chalk.green : color === "yellow" ? chalk.yellow : chalk.red;
1104
- console.log(chalk.bold.cyan("\n kerf budget\n"));
1105
+ console.log(chalk.bold.cyan("\n kerf-cli budget\n"));
1105
1106
  console.log(` Period: ${status.period} (${status.periodStart.slice(0, 10)} to ${status.periodEnd.slice(0, 10)})`);
1106
1107
  console.log(` Budget: ${formatCost(status.budget)}`);
1107
1108
  console.log(` Spent: ${barColor(formatCost(status.spent))}`);
@@ -1117,11 +1118,11 @@ function registerBudgetCommand(program2) {
1117
1118
  const manager = new BudgetManager();
1118
1119
  const projects = manager.listProjects();
1119
1120
  if (projects.length === 0) {
1120
- console.log("No projects with budgets. Use 'kerf budget set <amount>' to set one.");
1121
+ console.log("No projects with budgets. Use 'kerf-cli budget set <amount>' to set one.");
1121
1122
  manager.close();
1122
1123
  return;
1123
1124
  }
1124
- console.log(chalk.bold.cyan("\n kerf budget list\n"));
1125
+ console.log(chalk.bold.cyan("\n kerf-cli budget list\n"));
1125
1126
  for (const p of projects) {
1126
1127
  const budgetStr = p.budget ? `${formatCost(p.budget)}/${p.period}` : "no budget";
1127
1128
  const spentStr = p.spent > 0 ? ` (spent: ${formatCost(p.spent)})` : "";
@@ -1409,7 +1410,7 @@ function registerAuditCommand(program2) {
1409
1410
  return;
1410
1411
  }
1411
1412
  const gradeColor = result.grade === "A" ? chalk2.green : result.grade === "B" ? chalk2.yellow : chalk2.red;
1412
- console.log(chalk2.bold.cyan("\n kerf audit report\n"));
1413
+ console.log(chalk2.bold.cyan("\n kerf-cli audit report\n"));
1413
1414
  console.log(
1414
1415
  ` Context Window Health: ${gradeColor.bold(result.grade)} (${result.contextOverhead.percentUsable.toFixed(0)}% usable)
1415
1416
  `
@@ -1556,7 +1557,7 @@ function registerReportCommand(program2) {
1556
1557
  }
1557
1558
  const periodLabel = opts.period === "today" ? now.format("ddd, MMM D, YYYY") : opts.period;
1558
1559
  console.log(chalk3.bold.cyan(`
1559
- kerf report -- ${periodLabel}
1560
+ kerf-cli report -- ${periodLabel}
1560
1561
  `));
1561
1562
  console.log(` Total Cost: ${chalk3.bold(formatCost(totalCost))}`);
1562
1563
  console.log(` Total Tokens: ${formatTokens(totalInput)} in / ${formatTokens(totalOutput)} out`);
@@ -1608,8 +1609,8 @@ import { mkdirSync as mkdirSync2, existsSync as existsSync5, readFileSync as rea
1608
1609
  import { join as join6, dirname as dirname2 } from "node:path";
1609
1610
  import { homedir as homedir4 } from "node:os";
1610
1611
  function registerInitCommand(program2) {
1611
- program2.command("init").description("Set up kerf for the current project").option("--global", "Install hooks globally").option("--hooks-only", "Only install hooks").option("--no-hooks", "Skip hook installation").option("--force", "Skip confirmation prompts").action(async (opts) => {
1612
- console.log(chalk4.bold.cyan("\n Welcome to kerf!\n"));
1612
+ program2.command("init").description("Set up kerf-cli for the current project").option("--global", "Install hooks globally").option("--hooks-only", "Only install hooks").option("--no-hooks", "Skip hook installation").option("--force", "Skip confirmation prompts").action(async (opts) => {
1613
+ console.log(chalk4.bold.cyan("\n Welcome to kerf-cli!\n"));
1613
1614
  console.log(" Setting up cost intelligence for Claude Code...\n");
1614
1615
  const kerfDir = join6(homedir4(), ".kerf");
1615
1616
  if (!existsSync5(kerfDir)) {
@@ -1663,7 +1664,7 @@ function registerInitCommand(program2) {
1663
1664
  CLAUDE_AUTOCOMPACT_PCT_OVERRIDE: "50"
1664
1665
  }
1665
1666
  }, null, 4).split("\n").map((l) => " " + l).join("\n")));
1666
- console.log(chalk4.bold.cyan("\n Run 'kerf watch' to start the live dashboard!\n"));
1667
+ console.log(chalk4.bold.cyan("\n Run 'kerf-cli watch' to start the live dashboard!\n"));
1667
1668
  });
1668
1669
  }
1669
1670
  function installHooks(settingsPath) {
@@ -1690,7 +1691,7 @@ function installHooks(settingsPath) {
1690
1691
 
1691
1692
  // src/cli/index.ts
1692
1693
  var program = new Command();
1693
- program.name("kerf").version("0.1.0").description("Cost intelligence for Claude Code. Know before you spend.");
1694
+ program.name("kerf-cli").version("0.1.0").description("Cost intelligence for Claude Code. Know before you spend.");
1694
1695
  registerWatchCommand(program);
1695
1696
  registerEstimateCommand(program);
1696
1697
  registerBudgetCommand(program);
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cli/index.ts","../src/cli/commands/watch.ts","../src/core/parser.ts","../src/core/config.ts","../src/cli/ui/Dashboard.tsx","../src/cli/ui/CostMeter.tsx","../src/core/costCalculator.ts","../src/cli/ui/ContextBar.tsx","../src/core/tokenCounter.ts","../src/cli/commands/estimate.ts","../src/core/estimator.ts","../src/cli/ui/EstimateCard.tsx","../src/cli/commands/budget.ts","../src/core/budgetManager.ts","../src/db/schema.ts","../src/db/migrations.ts","../src/cli/commands/audit.ts","../src/audit/ghostTokens.ts","../src/audit/claudeMdLinter.ts","../src/audit/mcpAnalyzer.ts","../src/audit/recommendations.ts","../src/cli/commands/report.ts","../src/cli/commands/init.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport { registerWatchCommand } from \"./commands/watch.js\";\nimport { registerEstimateCommand } from \"./commands/estimate.js\";\nimport { registerBudgetCommand } from \"./commands/budget.js\";\nimport { registerAuditCommand } from \"./commands/audit.js\";\nimport { registerReportCommand } from \"./commands/report.js\";\nimport { registerInitCommand } from \"./commands/init.js\";\n\nconst program = new Command();\n\nprogram\n .name(\"kerf\")\n .version(\"0.1.0\")\n .description(\"Cost intelligence for Claude Code. Know before you spend.\");\n\n// Register all subcommands\nregisterWatchCommand(program);\nregisterEstimateCommand(program);\nregisterBudgetCommand(program);\nregisterAuditCommand(program);\nregisterReportCommand(program);\nregisterInitCommand(program);\n\n// Default to watch if no command given\nprogram.action(async () => {\n await program.commands.find((c) => c.name() === \"watch\")?.parseAsync([], { from: \"user\" });\n});\n\nprogram.parse();\n","import React from \"react\";\nimport { render } from \"ink\";\nimport { Command } from \"commander\";\nimport { getActiveSessions } from \"../../core/parser.js\";\nimport { Dashboard } from \"../ui/Dashboard.js\";\n\nexport function registerWatchCommand(program: Command): void {\n program\n .command(\"watch\")\n .description(\"Real-time cost dashboard (default)\")\n .option(\"-s, --session <id>\", \"Watch a specific session\")\n .option(\"-p, --project <path>\", \"Watch sessions for a specific project\")\n .option(\"-i, --interval <ms>\", \"Polling interval in ms\", \"2000\")\n .option(\"--no-color\", \"Disable colors\")\n .action(async (opts) => {\n const interval = parseInt(opts.interval, 10);\n\n let sessionFilePath: string | undefined;\n\n if (opts.session) {\n const sessions = await getActiveSessions(opts.project);\n const found = sessions.find((s) => s.sessionId.startsWith(opts.session));\n sessionFilePath = found?.filePath;\n } else {\n const sessions = await getActiveSessions(opts.project);\n sessionFilePath = sessions[0]?.filePath;\n }\n\n if (!sessionFilePath) {\n console.log(\n \"No active Claude Code session found. Start Claude Code and run 'kerf watch' again.\",\n );\n process.exit(0);\n }\n\n const { waitUntilExit } = render(\n React.createElement(Dashboard, { sessionFilePath, interval }),\n );\n await waitUntilExit();\n });\n}\n","import { readFileSync, statSync } from \"node:fs\";\nimport { readdir } from \"node:fs/promises\";\nimport { join, basename } from \"node:path\";\nimport { createReadStream } from \"node:fs\";\nimport { createInterface } from \"node:readline\";\nimport dayjs from \"dayjs\";\nimport { watch } from \"chokidar\";\nimport { CLAUDE_PROJECTS_DIR, BILLING_WINDOW_HOURS } from \"./config.js\";\nimport type {\n RawJsonlMessage,\n ParsedMessage,\n SessionData,\n ParsedSession,\n MessageUsage,\n} from \"../types/jsonl.js\";\n\nconst DEFAULT_USAGE: MessageUsage = {\n input_tokens: 0,\n output_tokens: 0,\n cache_creation_input_tokens: 0,\n cache_read_input_tokens: 0,\n};\n\nfunction extractUsage(raw: RawJsonlMessage): Partial<MessageUsage> | null {\n return raw.message?.usage ?? raw.usage ?? raw.delta?.usage ?? null;\n}\n\nfunction extractMessageId(raw: RawJsonlMessage): string | null {\n return raw.message?.id ?? null;\n}\n\nfunction extractModel(raw: RawJsonlMessage): string | null {\n return raw.message?.model ?? null;\n}\n\nfunction extractTimestamp(raw: RawJsonlMessage): string {\n return raw.timestamp ?? dayjs().toISOString();\n}\n\nexport function parseJsonlLine(line: string): RawJsonlMessage | null {\n const trimmed = line.trim();\n if (!trimmed) return null;\n try {\n return JSON.parse(trimmed) as RawJsonlMessage;\n } catch {\n return null;\n }\n}\n\nexport function parseJsonlContent(content: string, sessionId: string): ParsedMessage[] {\n const lines = content.split(\"\\n\");\n const messageMap = new Map<string, ParsedMessage>();\n let anonymousCounter = 0;\n\n for (const line of lines) {\n const raw = parseJsonlLine(line);\n if (!raw) continue;\n\n const usage = extractUsage(raw);\n if (!usage) continue;\n\n const id = extractMessageId(raw) ?? `anon_${anonymousCounter++}`;\n const model = extractModel(raw) ?? \"unknown\";\n const timestamp = extractTimestamp(raw);\n\n const existing = messageMap.get(id);\n const parsedUsage: MessageUsage = {\n input_tokens: usage.input_tokens ?? existing?.usage.input_tokens ?? 0,\n output_tokens: usage.output_tokens ?? existing?.usage.output_tokens ?? 0,\n cache_creation_input_tokens:\n usage.cache_creation_input_tokens ?? existing?.usage.cache_creation_input_tokens ?? 0,\n cache_read_input_tokens:\n usage.cache_read_input_tokens ?? existing?.usage.cache_read_input_tokens ?? 0,\n };\n\n // Deduplicate by message id — take the LAST occurrence (handles streaming intermediates)\n messageMap.set(id, {\n id,\n model: model !== \"unknown\" ? model : existing?.model ?? \"unknown\",\n timestamp,\n usage: parsedUsage,\n totalCostUsd: raw.total_cost_usd ?? existing?.totalCostUsd ?? null,\n });\n }\n\n return Array.from(messageMap.values());\n}\n\nexport function parseSessionFile(filePath: string): ParsedSession {\n const content = readFileSync(filePath, \"utf-8\");\n const sessionId = basename(filePath, \".jsonl\");\n const messages = parseJsonlContent(content, sessionId);\n\n const totals = messages.reduce(\n (acc, msg) => ({\n input: acc.input + msg.usage.input_tokens,\n output: acc.output + msg.usage.output_tokens,\n cacheRead: acc.cacheRead + msg.usage.cache_read_input_tokens,\n cacheCreation: acc.cacheCreation + msg.usage.cache_creation_input_tokens,\n cost: acc.cost + (msg.totalCostUsd ?? 0),\n }),\n { input: 0, output: 0, cacheRead: 0, cacheCreation: 0, cost: 0 },\n );\n\n const timestamps = messages.map((m) => m.timestamp).sort();\n\n return {\n sessionId,\n filePath,\n messages,\n totalInputTokens: totals.input,\n totalOutputTokens: totals.output,\n totalCacheReadTokens: totals.cacheRead,\n totalCacheCreationTokens: totals.cacheCreation,\n totalCostUsd: totals.cost,\n startTime: timestamps[0] ?? \"\",\n endTime: timestamps[timestamps.length - 1] ?? \"\",\n messageCount: messages.length,\n };\n}\n\nexport async function findJsonlFiles(baseDir?: string): Promise<string[]> {\n const dir = baseDir ?? CLAUDE_PROJECTS_DIR;\n const files: string[] = [];\n\n async function walk(currentDir: string): Promise<void> {\n let entries;\n try {\n entries = await readdir(currentDir, { withFileTypes: true });\n } catch {\n return;\n }\n for (const entry of entries) {\n const fullPath = join(currentDir, entry.name);\n if (entry.isDirectory()) {\n await walk(fullPath);\n } else if (entry.name.endsWith(\".jsonl\")) {\n files.push(fullPath);\n }\n }\n }\n\n await walk(dir);\n return files;\n}\n\nexport async function getActiveSessions(baseDir?: string): Promise<SessionData[]> {\n const files = await findJsonlFiles(baseDir);\n const cutoff = dayjs().subtract(BILLING_WINDOW_HOURS, \"hour\");\n const activeSessions: SessionData[] = [];\n\n for (const filePath of files) {\n try {\n const stat = statSync(filePath);\n const lastModified = dayjs(stat.mtime);\n if (lastModified.isAfter(cutoff)) {\n const content = readFileSync(filePath, \"utf-8\");\n const sessionId = basename(filePath, \".jsonl\");\n const messages = parseJsonlContent(content, sessionId);\n\n if (messages.length === 0) continue;\n\n const timestamps = messages.map((m) => m.timestamp).sort();\n activeSessions.push({\n sessionId,\n filePath,\n messages,\n startTime: timestamps[0] ?? \"\",\n endTime: timestamps[timestamps.length - 1] ?? \"\",\n lastModified: stat.mtime,\n });\n }\n } catch {\n continue;\n }\n }\n\n return activeSessions.sort(\n (a, b) => b.lastModified.getTime() - a.lastModified.getTime(),\n );\n}\n\nexport interface StreamingParser {\n onMessage: (callback: (msg: ParsedMessage) => void) => void;\n stop: () => void;\n}\n\nexport function createStreamingParser(filePath: string): StreamingParser {\n const callbacks: Array<(msg: ParsedMessage) => void> = [];\n const sessionId = basename(filePath, \".jsonl\");\n let anonymousCounter = 0;\n\n const watcher = watch(filePath, { persistent: true });\n\n watcher.on(\"change\", () => {\n const content = readFileSync(filePath, \"utf-8\");\n const lines = content.split(\"\\n\");\n // Process only the last few lines for incremental updates\n const recentLines = lines.slice(-20);\n for (const line of recentLines) {\n const raw = parseJsonlLine(line);\n if (!raw) continue;\n const usage = extractUsage(raw);\n if (!usage) continue;\n\n const id = extractMessageId(raw) ?? `anon_${anonymousCounter++}`;\n const model = extractModel(raw) ?? \"unknown\";\n const timestamp = extractTimestamp(raw);\n\n const msg: ParsedMessage = {\n id,\n model,\n timestamp,\n usage: {\n input_tokens: usage.input_tokens ?? 0,\n output_tokens: usage.output_tokens ?? 0,\n cache_creation_input_tokens: usage.cache_creation_input_tokens ?? 0,\n cache_read_input_tokens: usage.cache_read_input_tokens ?? 0,\n },\n totalCostUsd: raw.total_cost_usd ?? null,\n };\n\n for (const cb of callbacks) {\n cb(msg);\n }\n }\n });\n\n return {\n onMessage(callback) {\n callbacks.push(callback);\n },\n stop() {\n watcher.close();\n },\n };\n}\n","import { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { KerfConfig } from \"../types/config.js\";\n\nexport const DEFAULT_CONFIG: KerfConfig = {\n defaultModel: \"sonnet\",\n budgetWarningThreshold: 80,\n budgetBlockThreshold: 100,\n pollingInterval: 2000,\n dataDir: join(homedir(), \".kerf\"),\n enableHooks: true,\n};\n\nexport const CONTEXT_WINDOW_SIZE = 200_000;\nexport const SYSTEM_PROMPT_TOKENS = 14_328;\nexport const BUILT_IN_TOOLS_TOKENS = 15_000;\nexport const AUTOCOMPACT_BUFFER_TOKENS = 33_000;\nexport const MCP_TOKENS_PER_TOOL = 600;\nexport const BILLING_WINDOW_HOURS = 5;\n\nexport const CLAUDE_PROJECTS_DIR = join(homedir(), \".claude\", \"projects\");\nexport const CLAUDE_SETTINGS_GLOBAL = join(homedir(), \".claude\", \"settings.json\");\nexport const KERF_DB_PATH = join(homedir(), \".kerf\", \"kerf.db\");\nexport const KERF_SESSION_LOG = join(homedir(), \".kerf\", \"session-log.jsonl\");\n\nexport function getConfig(): KerfConfig {\n return { ...DEFAULT_CONFIG };\n}\n","import React, { useState, useEffect } from \"react\";\nimport { Box, Text, useInput, useApp } from \"ink\";\nimport { CostMeter } from \"./CostMeter.js\";\nimport { ContextBar } from \"./ContextBar.js\";\nimport { parseSessionFile } from \"../../core/parser.js\";\nimport {\n calculateMessageCost,\n calculateCostVelocity,\n formatCost,\n formatTokens,\n} from \"../../core/costCalculator.js\";\nimport { estimateContextOverhead } from \"../../core/tokenCounter.js\";\nimport { CONTEXT_WINDOW_SIZE, BILLING_WINDOW_HOURS } from \"../../core/config.js\";\nimport type { ParsedSession } from \"../../types/jsonl.js\";\nimport type { ContextOverhead } from \"../../types/config.js\";\n\ninterface DashboardProps {\n sessionFilePath: string;\n interval: number;\n}\n\nexport function Dashboard({ sessionFilePath, interval }: DashboardProps) {\n const { exit } = useApp();\n const [session, setSession] = useState<ParsedSession | null>(null);\n const [overhead, setOverhead] = useState<ContextOverhead>(estimateContextOverhead());\n const [showBudget, setShowBudget] = useState(false);\n\n useEffect(() => {\n function refresh() {\n try {\n const parsed = parseSessionFile(sessionFilePath);\n setSession(parsed);\n setOverhead(estimateContextOverhead());\n } catch {\n // File may be in the middle of a write\n }\n }\n\n refresh();\n const timer = setInterval(refresh, interval);\n return () => clearInterval(timer);\n }, [sessionFilePath, interval]);\n\n useInput((input) => {\n if (input === \"q\") exit();\n if (input === \"b\") setShowBudget((prev) => !prev);\n });\n\n if (!session || session.messages.length === 0) {\n return (\n <Box paddingX={1}>\n <Text dimColor>Waiting for session data...</Text>\n </Box>\n );\n }\n\n const totalCost = session.messages.reduce(\n (sum, msg) => sum + calculateMessageCost(msg).totalCost,\n 0,\n );\n const velocity = calculateCostVelocity(session.messages);\n const model = session.messages[session.messages.length - 1]?.model ?? \"unknown\";\n const windowBudget = velocity.projectedWindowCost || totalCost * 3;\n\n const totalInput = session.totalInputTokens + session.totalCacheReadTokens;\n const usedTokens = totalInput + session.totalOutputTokens;\n\n // Recent messages for the log\n const recentMessages = session.messages.slice(-8);\n\n return (\n <Box flexDirection=\"column\">\n <Box borderStyle=\"round\" borderColor=\"cyan\" paddingX={1}>\n <Text bold color=\"cyan\">\n kerf watch\n </Text>\n <Text> | session: {session.sessionId.slice(0, 8)}...</Text>\n <Text> | {session.messageCount} messages</Text>\n <Text dimColor> (q=quit, b=budget)</Text>\n </Box>\n\n <CostMeter\n spent={totalCost}\n windowBudget={windowBudget}\n burnRate={velocity.dollarsPerMinute}\n minutesRemaining={velocity.minutesRemaining}\n model={model}\n />\n\n <ContextBar used={usedTokens} total={CONTEXT_WINDOW_SIZE} overhead={overhead} />\n\n <Box flexDirection=\"column\" paddingX={1} marginTop={1}>\n <Text bold>Recent Messages:</Text>\n {recentMessages.map((msg, i) => {\n const cost = calculateMessageCost(msg);\n return (\n <Text key={i}>\n <Text dimColor>{msg.timestamp.slice(11, 19)}</Text>\n <Text> {formatTokens(msg.usage.input_tokens + msg.usage.output_tokens)} tok</Text>\n <Text color=\"yellow\"> {formatCost(cost.totalCost)}</Text>\n </Text>\n );\n })}\n </Box>\n </Box>\n );\n}\n","import React from \"react\";\nimport { Box, Text } from \"ink\";\nimport { formatCost } from \"../../core/costCalculator.js\";\n\ninterface CostMeterProps {\n spent: number;\n windowBudget: number;\n burnRate: number;\n minutesRemaining: number;\n model: string;\n}\n\nexport function CostMeter({ spent, windowBudget, burnRate, minutesRemaining, model }: CostMeterProps) {\n const pct = windowBudget > 0 ? (spent / windowBudget) * 100 : 0;\n const color = pct < 50 ? \"green\" : pct < 80 ? \"yellow\" : \"red\";\n\n const hours = Math.floor(minutesRemaining / 60);\n const mins = Math.round(minutesRemaining % 60);\n const timeStr = hours > 0 ? `${hours}h ${mins}m` : `${mins}m`;\n\n return (\n <Box flexDirection=\"column\" paddingX={1}>\n <Text>\n <Text color={color}>{\">> \"}</Text>\n <Text bold>{formatCost(spent)}</Text>\n <Text dimColor> / ~{formatCost(windowBudget)} window</Text>\n <Text> | </Text>\n <Text color={color}>{formatCost(burnRate)}/min</Text>\n <Text> | </Text>\n <Text>~{timeStr} remaining</Text>\n <Text dimColor> [{model}]</Text>\n </Text>\n </Box>\n );\n}\n","import dayjs from \"dayjs\";\nimport isoWeek from \"dayjs/plugin/isoWeek.js\";\nimport { BILLING_WINDOW_HOURS } from \"./config.js\";\nimport type { ParsedMessage, ParsedSession } from \"../types/jsonl.js\";\nimport type {\n ModelPricing,\n PricingConfig,\n CostBreakdown,\n SessionCostSummary,\n CostVelocity,\n AggregationPeriod,\n AggregatedCost,\n} from \"../types/pricing.js\";\n\ndayjs.extend(isoWeek);\n\nexport const MODEL_PRICING: PricingConfig = {\n \"claude-sonnet-4-20250514\": {\n input: 3,\n output: 15,\n cacheRead: 0.3,\n cacheCreation: 3.75,\n },\n \"claude-opus-4-20250514\": {\n input: 15,\n output: 75,\n cacheRead: 1.5,\n cacheCreation: 18.75,\n },\n \"claude-haiku-4-20250514\": {\n input: 0.8,\n output: 4,\n cacheRead: 0.08,\n cacheCreation: 1.0,\n },\n};\n\n// Alias mappings for short model names\nconst MODEL_ALIASES: Record<string, string> = {\n sonnet: \"claude-sonnet-4-20250514\",\n opus: \"claude-opus-4-20250514\",\n haiku: \"claude-haiku-4-20250514\",\n};\n\nexport function resolveModelPricing(model: string): ModelPricing {\n const resolved = MODEL_ALIASES[model] ?? model;\n // Try exact match first, then prefix match\n if (MODEL_PRICING[resolved]) return MODEL_PRICING[resolved];\n const match = Object.keys(MODEL_PRICING).find((k) => resolved.startsWith(k) || k.startsWith(resolved));\n if (match) return MODEL_PRICING[match];\n // Default to sonnet pricing\n return MODEL_PRICING[\"claude-sonnet-4-20250514\"];\n}\n\nexport function calculateMessageCost(msg: ParsedMessage): CostBreakdown {\n // If authoritative cost is present, use it\n if (msg.totalCostUsd !== null && msg.totalCostUsd > 0) {\n return {\n inputCost: 0,\n outputCost: 0,\n cacheReadCost: 0,\n cacheCreationCost: 0,\n totalCost: msg.totalCostUsd,\n };\n }\n\n const pricing = resolveModelPricing(msg.model);\n const MILLION = 1_000_000;\n\n // Use multiply-then-divide to avoid floating point issues\n const inputCost = (msg.usage.input_tokens * pricing.input) / MILLION;\n const outputCost = (msg.usage.output_tokens * pricing.output) / MILLION;\n const cacheReadCost = (msg.usage.cache_read_input_tokens * pricing.cacheRead) / MILLION;\n const cacheCreationCost = (msg.usage.cache_creation_input_tokens * pricing.cacheCreation) / MILLION;\n\n return {\n inputCost,\n outputCost,\n cacheReadCost,\n cacheCreationCost,\n totalCost: inputCost + outputCost + cacheReadCost + cacheCreationCost,\n };\n}\n\nexport function calculateSessionCost(session: ParsedSession): SessionCostSummary {\n let totalCost = 0;\n const costBreakdown: CostBreakdown = {\n inputCost: 0,\n outputCost: 0,\n cacheReadCost: 0,\n cacheCreationCost: 0,\n totalCost: 0,\n };\n\n for (const msg of session.messages) {\n const msgCost = calculateMessageCost(msg);\n costBreakdown.inputCost += msgCost.inputCost;\n costBreakdown.outputCost += msgCost.outputCost;\n costBreakdown.cacheReadCost += msgCost.cacheReadCost;\n costBreakdown.cacheCreationCost += msgCost.cacheCreationCost;\n totalCost += msgCost.totalCost;\n }\n\n costBreakdown.totalCost = totalCost;\n\n return {\n sessionId: session.sessionId,\n totalCost,\n tokenBreakdown: {\n input: session.totalInputTokens,\n output: session.totalOutputTokens,\n cacheRead: session.totalCacheReadTokens,\n cacheCreation: session.totalCacheCreationTokens,\n },\n costBreakdown,\n model: session.messages[0]?.model ?? \"unknown\",\n messageCount: session.messageCount,\n startTime: session.startTime,\n endTime: session.endTime,\n };\n}\n\nexport function calculateCostVelocity(messages: ParsedMessage[]): CostVelocity {\n if (messages.length < 2) {\n return { dollarsPerMinute: 0, tokensPerMinute: 0, projectedWindowCost: 0, minutesRemaining: 0 };\n }\n\n // Use last 10 messages for velocity calculation\n const recent = messages.slice(-10);\n const firstTime = dayjs(recent[0].timestamp);\n const lastTime = dayjs(recent[recent.length - 1].timestamp);\n const durationMinutes = lastTime.diff(firstTime, \"minute\", true);\n\n if (durationMinutes <= 0) {\n return { dollarsPerMinute: 0, tokensPerMinute: 0, projectedWindowCost: 0, minutesRemaining: 0 };\n }\n\n let totalCost = 0;\n let totalTokens = 0;\n for (const msg of recent) {\n totalCost += calculateMessageCost(msg).totalCost;\n totalTokens += msg.usage.input_tokens + msg.usage.output_tokens;\n }\n\n const dollarsPerMinute = totalCost / durationMinutes;\n const tokensPerMinute = totalTokens / durationMinutes;\n const windowMinutes = BILLING_WINDOW_HOURS * 60;\n const projectedWindowCost = dollarsPerMinute * windowMinutes;\n\n // Time remaining in current billing window\n const windowStart = dayjs().subtract(BILLING_WINDOW_HOURS, \"hour\");\n const elapsed = dayjs().diff(windowStart, \"minute\", true);\n const minutesRemaining = Math.max(0, windowMinutes - elapsed);\n\n return { dollarsPerMinute, tokensPerMinute, projectedWindowCost, minutesRemaining };\n}\n\nexport function aggregateCosts(\n messages: ParsedMessage[],\n period: AggregationPeriod,\n): AggregatedCost[] {\n const groups = new Map<string, { messages: ParsedMessage[]; sessions: Set<string> }>();\n\n for (const msg of messages) {\n const key = getPeriodKey(msg.timestamp, period);\n const group = groups.get(key) ?? { messages: [], sessions: new Set() };\n group.messages.push(msg);\n group.sessions.add(msg.id.split(\"_\")[0] ?? msg.id);\n groups.set(key, group);\n }\n\n return Array.from(groups.entries()).map(([key, group]) => {\n let totalCost = 0;\n let totalInput = 0;\n let totalOutput = 0;\n\n for (const msg of group.messages) {\n totalCost += calculateMessageCost(msg).totalCost;\n totalInput += msg.usage.input_tokens;\n totalOutput += msg.usage.output_tokens;\n }\n\n return {\n period: key,\n periodLabel: formatPeriodLabel(key, period),\n totalCost,\n totalInputTokens: totalInput,\n totalOutputTokens: totalOutput,\n messageCount: group.messages.length,\n sessionCount: group.sessions.size,\n };\n });\n}\n\nfunction getPeriodKey(timestamp: string, period: AggregationPeriod): string {\n const d = dayjs(timestamp);\n switch (period) {\n case \"hour\":\n return d.format(\"YYYY-MM-DD-HH\");\n case \"day\":\n return d.format(\"YYYY-MM-DD\");\n case \"billing_window\":\n // 5-hour windows starting from midnight\n const hour = d.hour();\n const windowStart = Math.floor(hour / BILLING_WINDOW_HOURS) * BILLING_WINDOW_HOURS;\n return `${d.format(\"YYYY-MM-DD\")}-W${windowStart}`;\n case \"week\":\n return `${d.isoWeekYear()}-W${String(d.isoWeek()).padStart(2, \"0\")}`;\n case \"month\":\n return d.format(\"YYYY-MM\");\n case \"session\":\n default:\n return timestamp;\n }\n}\n\nfunction formatPeriodLabel(key: string, period: AggregationPeriod): string {\n switch (period) {\n case \"hour\":\n return dayjs(key, \"YYYY-MM-DD-HH\").format(\"MMM D, h A\");\n case \"day\":\n return dayjs(key).format(\"ddd, MMM D\");\n case \"week\":\n return `Week ${key.split(\"-W\")[1]}`;\n case \"month\":\n return dayjs(key).format(\"MMMM YYYY\");\n default:\n return key;\n }\n}\n\nexport function formatCost(cost: number): string {\n return `$${cost.toFixed(2)}`;\n}\n\nexport function formatTokens(tokens: number): string {\n if (tokens >= 1_000_000) return `${(tokens / 1_000_000).toFixed(1)}M`;\n if (tokens >= 1_000) return `${(tokens / 1_000).toFixed(1)}K`;\n return String(tokens);\n}\n","import React from \"react\";\nimport { Box, Text } from \"ink\";\nimport type { ContextOverhead } from \"../../types/config.js\";\n\ninterface ContextBarProps {\n used: number;\n total: number;\n overhead: ContextOverhead;\n}\n\nexport function ContextBar({ used, total, overhead }: ContextBarProps) {\n const barWidth = 30;\n const usedPct = total > 0 ? (used / total) * 100 : 0;\n const filledCount = Math.round((usedPct / 100) * barWidth);\n const emptyCount = barWidth - filledCount;\n\n const color = usedPct < 50 ? \"green\" : usedPct < 80 ? \"yellow\" : \"red\";\n\n const filled = \"\\u2588\".repeat(filledCount);\n const empty = \"\\u2591\".repeat(emptyCount);\n\n const formatK = (n: number) => `${Math.round(n / 1000)}K`;\n\n return (\n <Box flexDirection=\"column\" paddingX={1}>\n <Text>\n <Text>[</Text>\n <Text color={color}>{filled}</Text>\n <Text dimColor>{empty}</Text>\n <Text>]</Text>\n <Text> {usedPct.toFixed(0)}%</Text>\n <Text> | {formatK(used)} / {formatK(total)} tokens</Text>\n </Text>\n <Text dimColor>\n {\" \"}system({formatK(overhead.systemPrompt)}) + tools({formatK(overhead.builtInTools)}) + mcp(\n {formatK(overhead.mcpTools)}) + claude.md({formatK(overhead.claudeMd)})\n </Text>\n </Box>\n );\n}\n","import { readFileSync, existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport {\n CONTEXT_WINDOW_SIZE,\n SYSTEM_PROMPT_TOKENS,\n BUILT_IN_TOOLS_TOKENS,\n AUTOCOMPACT_BUFFER_TOKENS,\n MCP_TOKENS_PER_TOOL,\n} from \"./config.js\";\nimport type { ContextOverhead, McpServerInfo } from \"../types/config.js\";\n\nconst SUPPORTED_EXTENSIONS = new Set([\".md\", \".ts\", \".js\", \".json\", \".yaml\", \".yml\", \".py\", \".txt\"]);\n\n/**\n * Fast local heuristic: tokens ~= characters / 3.5\n */\nexport function estimateTokens(text: string): number {\n return Math.ceil(text.length / 3.5);\n}\n\n/**\n * Count tokens in a file using the heuristic\n */\nexport function countFileTokens(filePath: string): number {\n try {\n const content = readFileSync(filePath, \"utf-8\");\n return estimateTokens(content);\n } catch {\n return 0;\n }\n}\n\nexport interface ClaudeMdSection {\n title: string;\n content: string;\n tokens: number;\n lineStart: number;\n lineEnd: number;\n}\n\n/**\n * Parse CLAUDE.md into sections by ## headers\n */\nexport function parseClaudeMdSections(content: string): ClaudeMdSection[] {\n const lines = content.split(\"\\n\");\n const sections: ClaudeMdSection[] = [];\n let currentTitle = \"Preamble\";\n let currentContent: string[] = [];\n let currentLineStart = 1;\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n if (line.startsWith(\"## \")) {\n // Save previous section\n if (currentContent.length > 0 || currentTitle !== \"Preamble\") {\n const sectionContent = currentContent.join(\"\\n\");\n sections.push({\n title: currentTitle,\n content: sectionContent,\n tokens: estimateTokens(sectionContent),\n lineStart: currentLineStart,\n lineEnd: i,\n });\n }\n currentTitle = line.replace(/^##\\s*/, \"\");\n currentContent = [];\n currentLineStart = i + 1;\n } else {\n currentContent.push(line);\n }\n }\n\n // Save last section\n if (currentContent.length > 0) {\n const sectionContent = currentContent.join(\"\\n\");\n sections.push({\n title: currentTitle,\n content: sectionContent,\n tokens: estimateTokens(sectionContent),\n lineStart: currentLineStart,\n lineEnd: lines.length,\n });\n }\n\n return sections;\n}\n\n/**\n * Analyze CLAUDE.md token overhead\n */\nexport function analyzeClaudeMd(filePath?: string): { totalTokens: number; sections: ClaudeMdSection[]; heavySections: ClaudeMdSection[] } {\n const paths = filePath\n ? [filePath]\n : [\n join(process.cwd(), \"CLAUDE.md\"),\n join(process.cwd(), \".claude\", \"CLAUDE.md\"),\n ];\n\n for (const p of paths) {\n if (existsSync(p)) {\n const content = readFileSync(p, \"utf-8\");\n const sections = parseClaudeMdSections(content);\n const totalTokens = sections.reduce((sum, s) => sum + s.tokens, 0);\n const heavySections = sections.filter((s) => s.tokens > 500);\n return { totalTokens, sections, heavySections };\n }\n }\n\n return { totalTokens: 0, sections: [], heavySections: [] };\n}\n\n/**\n * Parse MCP server configurations and estimate token cost\n */\nexport function analyzeMcpServers(): McpServerInfo[] {\n const servers: McpServerInfo[] = [];\n const paths = [\n join(process.cwd(), \".mcp.json\"),\n join(homedir(), \".claude.json\"),\n ];\n\n for (const configPath of paths) {\n if (!existsSync(configPath)) continue;\n try {\n const raw = JSON.parse(readFileSync(configPath, \"utf-8\"));\n const mcpServers = raw.mcpServers ?? raw.mcp_servers ?? {};\n for (const [name, config] of Object.entries(mcpServers)) {\n const cfg = config as Record<string, unknown>;\n // Estimate tools: if tools array is present, use its length; otherwise default to 5\n const tools = Array.isArray(cfg.tools) ? cfg.tools : [];\n const toolCount = tools.length || 5;\n const estimatedTokens = toolCount * MCP_TOKENS_PER_TOOL;\n servers.push({\n name,\n toolCount,\n estimatedTokens,\n isHeavy: toolCount > 10,\n });\n }\n } catch {\n continue;\n }\n }\n\n return servers;\n}\n\n/**\n * Estimate total context overhead (ghost tokens)\n */\nexport function estimateContextOverhead(claudeMdPath?: string): ContextOverhead {\n const claudeMd = analyzeClaudeMd(claudeMdPath);\n const mcpServers = analyzeMcpServers();\n const mcpToolTokens = mcpServers.reduce((sum, s) => sum + s.estimatedTokens, 0);\n\n const totalOverhead =\n SYSTEM_PROMPT_TOKENS +\n BUILT_IN_TOOLS_TOKENS +\n mcpToolTokens +\n claudeMd.totalTokens +\n AUTOCOMPACT_BUFFER_TOKENS;\n\n const effectiveWindow = CONTEXT_WINDOW_SIZE - totalOverhead;\n const percentUsable = (effectiveWindow / CONTEXT_WINDOW_SIZE) * 100;\n\n return {\n systemPrompt: SYSTEM_PROMPT_TOKENS,\n builtInTools: BUILT_IN_TOOLS_TOKENS,\n claudeMd: claudeMd.totalTokens,\n mcpTools: mcpToolTokens,\n autocompactBuffer: AUTOCOMPACT_BUFFER_TOKENS,\n totalOverhead,\n effectiveWindow,\n percentUsable,\n };\n}\n","import React from \"react\";\nimport { render } from \"ink\";\nimport { Command } from \"commander\";\nimport { glob } from \"glob\";\nimport { estimateTaskCost } from \"../../core/estimator.js\";\nimport { EstimateCard } from \"../ui/EstimateCard.js\";\n\nexport function registerEstimateCommand(program: Command): void {\n program\n .command(\"estimate <task>\")\n .description(\"Pre-flight cost estimation\")\n .option(\"-m, --model <model>\", \"Model to estimate for\", \"sonnet\")\n .option(\"-f, --files <glob>\", \"Specific files that will be touched\")\n .option(\"--compare\", \"Show Sonnet vs Opus vs Haiku comparison\")\n .option(\"--json\", \"Output as JSON\")\n .action(async (task: string, opts) => {\n const files: string[] = [];\n if (opts.files) {\n const matched = await glob(opts.files, { absolute: true });\n files.push(...matched);\n }\n\n if (opts.compare) {\n const models = [\"sonnet\", \"opus\", \"haiku\"] as const;\n for (const model of models) {\n const estimate = await estimateTaskCost(task, { model, files, cwd: process.cwd() });\n if (opts.json) {\n console.log(JSON.stringify(estimate, null, 2));\n } else {\n const { waitUntilExit } = render(\n React.createElement(EstimateCard, { task, estimate }),\n );\n await waitUntilExit();\n }\n }\n return;\n }\n\n const estimate = await estimateTaskCost(task, {\n model: opts.model,\n files,\n cwd: process.cwd(),\n });\n\n if (opts.json) {\n console.log(JSON.stringify(estimate, null, 2));\n return;\n }\n\n const { waitUntilExit } = render(\n React.createElement(EstimateCard, { task, estimate }),\n );\n await waitUntilExit();\n });\n}\n","import { existsSync, readFileSync } from \"node:fs\";\nimport { glob } from \"glob\";\nimport { estimateTokens, countFileTokens, estimateContextOverhead } from \"./tokenCounter.js\";\nimport { resolveModelPricing, formatCost } from \"./costCalculator.js\";\nimport { BILLING_WINDOW_HOURS } from \"./config.js\";\nimport type { CostEstimate, EstimateOptions } from \"../types/config.js\";\n\ntype TaskComplexity = \"simple\" | \"medium\" | \"complex\";\n\ninterface ComplexityProfile {\n turns: { low: number; expected: number; high: number };\n outputTokensPerTurn: number;\n}\n\nconst COMPLEXITY_PROFILES: Record<TaskComplexity, ComplexityProfile> = {\n simple: { turns: { low: 2, expected: 3, high: 5 }, outputTokensPerTurn: 1000 },\n medium: { turns: { low: 5, expected: 10, high: 15 }, outputTokensPerTurn: 2000 },\n complex: { turns: { low: 15, expected: 25, high: 40 }, outputTokensPerTurn: 2500 },\n};\n\nconst SIMPLE_KEYWORDS = [\"typo\", \"rename\", \"fix typo\", \"update version\", \"change name\", \"remove unused\", \"delete\"];\nconst COMPLEX_KEYWORDS = [\"refactor\", \"rewrite\", \"new module\", \"implement\", \"build\", \"create\", \"migrate\", \"redesign\", \"overhaul\", \"architecture\"];\n\nfunction detectComplexity(taskDescription: string): TaskComplexity {\n const lower = taskDescription.toLowerCase();\n if (SIMPLE_KEYWORDS.some((k) => lower.includes(k))) return \"simple\";\n if (COMPLEX_KEYWORDS.some((k) => lower.includes(k))) return \"complex\";\n return \"medium\";\n}\n\nexport async function estimateTaskCost(\n taskDescription: string,\n options: Partial<EstimateOptions> = {},\n): Promise<CostEstimate> {\n const model = options.model ?? \"sonnet\";\n const cwd = options.cwd ?? process.cwd();\n const pricing = resolveModelPricing(model);\n const MILLION = 1_000_000;\n\n // Calculate context overhead\n const overhead = estimateContextOverhead();\n\n // Count file tokens\n let fileTokens = 0;\n let fileList = options.files ?? [];\n\n if (fileList.length === 0) {\n // Try to auto-detect from git status\n try {\n const { execSync } = await import(\"node:child_process\");\n const output = execSync(\"git diff --name-only HEAD 2>/dev/null || git ls-files -m 2>/dev/null\", {\n cwd,\n encoding: \"utf-8\",\n });\n fileList = output\n .split(\"\\n\")\n .filter(Boolean)\n .map((f) => `${cwd}/${f}`);\n } catch {\n // No git, no files\n }\n }\n\n for (const filePattern of fileList) {\n const matched = await glob(filePattern, { cwd, absolute: true });\n for (const f of matched) {\n fileTokens += countFileTokens(f);\n }\n }\n\n // Detect complexity and get profile\n const complexity = detectComplexity(taskDescription);\n const profile = COMPLEXITY_PROFILES[complexity];\n\n // Calculate per-turn costs\n const contextPerTurn = overhead.totalOverhead + fileTokens;\n const CACHE_HIT_RATE = 0.9;\n\n function estimateCostForTurns(turns: number): number {\n let totalCost = 0;\n for (let turn = 1; turn <= turns; turn++) {\n const conversationGrowth = (turn - 1) * profile.outputTokensPerTurn;\n const inputTokens = contextPerTurn + conversationGrowth;\n\n let effectiveInputCost: number;\n if (turn <= 2) {\n // First turns: no cache\n effectiveInputCost = (inputTokens * pricing.input) / MILLION;\n } else {\n // After turn 2: 90% cache hit\n const cachedTokens = inputTokens * CACHE_HIT_RATE;\n const uncachedTokens = inputTokens * (1 - CACHE_HIT_RATE);\n effectiveInputCost =\n (cachedTokens * pricing.cacheRead) / MILLION +\n (uncachedTokens * pricing.input) / MILLION;\n }\n\n const outputCost = (profile.outputTokensPerTurn * pricing.output) / MILLION;\n totalCost += effectiveInputCost + outputCost;\n }\n return totalCost;\n }\n\n const lowCost = estimateCostForTurns(profile.turns.low);\n const expectedCost = estimateCostForTurns(profile.turns.expected);\n const highCost = estimateCostForTurns(profile.turns.high);\n\n // Estimate total tokens for expected case\n const expectedInputTokens = contextPerTurn * profile.turns.expected;\n const expectedOutputTokens = profile.outputTokensPerTurn * profile.turns.expected;\n const expectedCachedTokens = expectedInputTokens * CACHE_HIT_RATE;\n\n // Window usage\n const windowMinutes = BILLING_WINDOW_HOURS * 60;\n const percentOfWindow = (expectedCost / (expectedCost * 3)) * 100; // rough estimate\n\n // Recommendations\n const recommendations: string[] = [];\n if (model !== \"sonnet\") {\n const sonnetPricing = resolveModelPricing(\"sonnet\");\n const sonnetCost = estimateCostForTurns(profile.turns.expected);\n // Recalculate with sonnet pricing isn't straightforward here,\n // so we do a ratio estimate\n const ratio = pricing.output / resolveModelPricing(\"sonnet\").output;\n if (ratio > 2) {\n recommendations.push(\n `Consider Sonnet to save ~${formatCost(expectedCost - expectedCost / ratio)} (${ratio.toFixed(0)}x cheaper)`,\n );\n }\n }\n\n if (model === \"sonnet\") {\n const opusPricing = resolveModelPricing(\"opus\");\n const ratio = opusPricing.output / pricing.output;\n recommendations.push(`Using Opus would cost ~${formatCost(expectedCost * ratio)} (${ratio.toFixed(0)}x more)`);\n }\n\n if (overhead.percentUsable < 60) {\n recommendations.push(`High ghost token overhead (${(100 - overhead.percentUsable).toFixed(0)}%). Run 'kerf audit' to optimize.`);\n }\n\n if (fileTokens > 50000) {\n recommendations.push(`Large file context (${(fileTokens / 1000).toFixed(0)}K tokens). Consider narrowing scope.`);\n }\n\n return {\n model,\n estimatedTurns: profile.turns,\n estimatedTokens: {\n input: Math.round(expectedInputTokens),\n output: Math.round(expectedOutputTokens),\n cached: Math.round(expectedCachedTokens),\n },\n estimatedCost: {\n low: formatCost(lowCost),\n expected: formatCost(expectedCost),\n high: formatCost(highCost),\n },\n contextOverhead: overhead.totalOverhead,\n fileTokens,\n percentOfWindow: Math.round(percentOfWindow),\n recommendations,\n };\n}\n","import React from \"react\";\nimport { Box, Text } from \"ink\";\nimport type { CostEstimate } from \"../../types/config.js\";\n\ninterface EstimateCardProps {\n task: string;\n estimate: CostEstimate;\n}\n\nexport function EstimateCard({ task, estimate }: EstimateCardProps) {\n const formatK = (n: number) => `${(n / 1000).toFixed(1)}K`;\n\n return (\n <Box flexDirection=\"column\" borderStyle=\"round\" borderColor=\"cyan\" paddingX={2} paddingY={1}>\n <Text bold color=\"cyan\">\n kerf estimate: '{task}'\n </Text>\n <Text> </Text>\n <Text>\n Model: <Text bold>{estimate.model}</Text>\n </Text>\n <Text>\n Estimated turns: {estimate.estimatedTurns.low}-{estimate.estimatedTurns.high} (expected:{\" \"}\n {estimate.estimatedTurns.expected})\n </Text>\n <Text>\n Files: {formatK(estimate.fileTokens)} tokens\n </Text>\n <Text>\n Context overhead: {formatK(estimate.contextOverhead)} tokens (ghost tokens)\n </Text>\n <Text> </Text>\n <Text bold>Estimated Cost:</Text>\n <Text>\n {\" \"}Low: <Text color=\"green\">{estimate.estimatedCost.low}</Text>\n </Text>\n <Text>\n {\" \"}Expected: <Text color=\"yellow\">{estimate.estimatedCost.expected}</Text>\n </Text>\n <Text>\n {\" \"}High: <Text color=\"red\">{estimate.estimatedCost.high}</Text>\n </Text>\n <Text> </Text>\n <Text>Window Usage: ~{estimate.percentOfWindow}% of 5-hour window</Text>\n {estimate.recommendations.map((rec, i) => (\n <Text key={i} color=\"cyan\">\n {\" -> \"}{rec}\n </Text>\n ))}\n </Box>\n );\n}\n","import { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport { BudgetManager } from \"../../core/budgetManager.js\";\nimport { formatCost } from \"../../core/costCalculator.js\";\n\nexport function registerBudgetCommand(program: Command): void {\n const budget = program.command(\"budget\").description(\"Per-project budget management\");\n\n budget\n .command(\"set <amount>\")\n .description(\"Set budget for current project\")\n .option(\"-p, --period <period>\", \"Budget period (daily|weekly|monthly)\", \"weekly\")\n .option(\"--project <path>\", \"Project path\")\n .action((amount: string, opts) => {\n const manager = new BudgetManager();\n const projectPath = opts.project || process.cwd();\n const amountNum = parseFloat(amount);\n\n if (isNaN(amountNum) || amountNum <= 0) {\n console.log(chalk.red(\"Budget amount must be a positive number.\"));\n process.exit(1);\n }\n\n manager.setBudget(projectPath, amountNum, opts.period);\n console.log(\n chalk.green(`Budget set: ${formatCost(amountNum)}/${opts.period} for ${projectPath}`),\n );\n manager.close();\n });\n\n budget\n .command(\"show\")\n .description(\"Show current project budget\")\n .option(\"--project <path>\", \"Project path\")\n .option(\"--json\", \"Output as JSON\")\n .action((opts) => {\n const manager = new BudgetManager();\n const projectPath = opts.project || process.cwd();\n const status = manager.checkBudget(projectPath);\n\n if (!status) {\n console.log(\"No budget set for this project. Use 'kerf budget set <amount>' to set one.\");\n manager.close();\n return;\n }\n\n if (opts.json) {\n console.log(JSON.stringify(status, null, 2));\n manager.close();\n return;\n }\n\n const pct = status.percentUsed;\n const barWidth = 20;\n const filled = Math.round((Math.min(pct, 100) / 100) * barWidth);\n const empty = barWidth - filled;\n const color = pct < 50 ? \"green\" : pct < 80 ? \"yellow\" : \"red\";\n const barColor = color === \"green\" ? chalk.green : color === \"yellow\" ? chalk.yellow : chalk.red;\n\n console.log(chalk.bold.cyan(\"\\n kerf budget\\n\"));\n console.log(` Period: ${status.period} (${status.periodStart.slice(0, 10)} to ${status.periodEnd.slice(0, 10)})`);\n console.log(` Budget: ${formatCost(status.budget)}`);\n console.log(` Spent: ${barColor(formatCost(status.spent))}`);\n console.log(` ${barColor(\"[\" + \"\\u2588\".repeat(filled) + \"\\u2591\".repeat(empty) + \"]\")} ${pct.toFixed(1)}%`);\n\n if (status.isOverBudget) {\n console.log(chalk.red.bold(`\\n OVER BUDGET by ${formatCost(status.spent - status.budget)}`));\n }\n console.log();\n\n manager.close();\n });\n\n budget\n .command(\"list\")\n .description(\"List all project budgets\")\n .action(() => {\n const manager = new BudgetManager();\n const projects = manager.listProjects();\n\n if (projects.length === 0) {\n console.log(\"No projects with budgets. Use 'kerf budget set <amount>' to set one.\");\n manager.close();\n return;\n }\n\n console.log(chalk.bold.cyan(\"\\n kerf budget list\\n\"));\n for (const p of projects) {\n const budgetStr = p.budget ? `${formatCost(p.budget)}/${p.period}` : \"no budget\";\n const spentStr = p.spent > 0 ? ` (spent: ${formatCost(p.spent)})` : \"\";\n console.log(` ${chalk.bold(p.name)} — ${budgetStr}${spentStr}`);\n console.log(chalk.dim(` ${p.path}`));\n }\n console.log();\n\n manager.close();\n });\n\n budget\n .command(\"remove\")\n .description(\"Remove budget for current project\")\n .option(\"--project <path>\", \"Project path\")\n .action((opts) => {\n const manager = new BudgetManager();\n const projectPath = opts.project || process.cwd();\n const removed = manager.removeBudget(projectPath);\n\n if (removed) {\n console.log(chalk.green(\"Budget removed.\"));\n } else {\n console.log(\"No budget found for this project.\");\n }\n\n manager.close();\n });\n}\n","import dayjs from \"dayjs\";\nimport isoWeek from \"dayjs/plugin/isoWeek.js\";\nimport { basename } from \"node:path\";\nimport { initDatabase } from \"../db/schema.js\";\nimport { runMigrations } from \"../db/migrations.js\";\nimport type { BudgetStatus } from \"../types/config.js\";\nimport type Database from \"better-sqlite3\";\n\ndayjs.extend(isoWeek);\n\ntype BudgetPeriod = \"daily\" | \"weekly\" | \"monthly\";\n\nexport class BudgetManager {\n private db: Database.Database;\n\n constructor(dbPath?: string) {\n this.db = initDatabase(dbPath);\n runMigrations(this.db);\n }\n\n private getOrCreateProject(projectPath: string): number {\n const name = basename(projectPath) || projectPath;\n const existing = this.db\n .prepare(\"SELECT id FROM projects WHERE path = ?\")\n .get(projectPath) as { id: number } | undefined;\n\n if (existing) return existing.id;\n\n const result = this.db\n .prepare(\"INSERT INTO projects (name, path) VALUES (?, ?)\")\n .run(name, projectPath);\n\n return Number(result.lastInsertRowid);\n }\n\n setBudget(projectPath: string, amount: number, period: BudgetPeriod): void {\n const projectId = this.getOrCreateProject(projectPath);\n this.db\n .prepare(\n `INSERT INTO budgets (project_id, amount_usd, period)\n VALUES (?, ?, ?)\n ON CONFLICT(project_id, period)\n DO UPDATE SET amount_usd = excluded.amount_usd`,\n )\n .run(projectId, amount, period);\n }\n\n getBudget(projectPath: string): { amount: number; period: BudgetPeriod } | null {\n const project = this.db\n .prepare(\"SELECT id FROM projects WHERE path = ?\")\n .get(projectPath) as { id: number } | undefined;\n\n if (!project) return null;\n\n const budget = this.db\n .prepare(\"SELECT amount_usd, period FROM budgets WHERE project_id = ? ORDER BY created_at DESC LIMIT 1\")\n .get(project.id) as { amount_usd: number; period: BudgetPeriod } | undefined;\n\n if (!budget) return null;\n return { amount: budget.amount_usd, period: budget.period };\n }\n\n recordUsage(\n projectPath: string,\n sessionId: string,\n tokensIn: number,\n tokensOut: number,\n costUsd: number,\n timestamp: string,\n ): void {\n const projectId = this.getOrCreateProject(projectPath);\n this.db\n .prepare(\n `INSERT OR IGNORE INTO usage_snapshots (project_id, session_id, tokens_in, tokens_out, cost_usd, timestamp)\n VALUES (?, ?, ?, ?, ?, ?)`,\n )\n .run(projectId, sessionId, tokensIn, tokensOut, costUsd, timestamp);\n }\n\n getUsage(projectPath: string, period: BudgetPeriod): number {\n const project = this.db\n .prepare(\"SELECT id FROM projects WHERE path = ?\")\n .get(projectPath) as { id: number } | undefined;\n\n if (!project) return 0;\n\n const start = getPeriodStart(period);\n const result = this.db\n .prepare(\n `SELECT COALESCE(SUM(cost_usd), 0) as total\n FROM usage_snapshots\n WHERE project_id = ? AND timestamp >= ?`,\n )\n .get(project.id, start.toISOString()) as { total: number };\n\n return result.total;\n }\n\n checkBudget(projectPath: string): BudgetStatus | null {\n const budgetConfig = this.getBudget(projectPath);\n if (!budgetConfig) return null;\n\n const spent = this.getUsage(projectPath, budgetConfig.period);\n const remaining = Math.max(0, budgetConfig.amount - spent);\n const percentUsed = budgetConfig.amount > 0 ? (spent / budgetConfig.amount) * 100 : 0;\n\n const periodStart = getPeriodStart(budgetConfig.period);\n const periodEnd = getPeriodEnd(budgetConfig.period);\n\n return {\n budget: budgetConfig.amount,\n spent,\n remaining,\n percentUsed,\n isOverBudget: spent > budgetConfig.amount,\n period: budgetConfig.period,\n periodStart: periodStart.toISOString(),\n periodEnd: periodEnd.toISOString(),\n };\n }\n\n listProjects(): Array<{ name: string; path: string; budget: number | null; period: string | null; spent: number }> {\n const rows = this.db\n .prepare(\n `SELECT p.name, p.path, b.amount_usd, b.period\n FROM projects p\n LEFT JOIN budgets b ON b.project_id = p.id\n ORDER BY p.name`,\n )\n .all() as Array<{ name: string; path: string; amount_usd: number | null; period: string | null }>;\n\n return rows.map((row) => ({\n name: row.name,\n path: row.path,\n budget: row.amount_usd,\n period: row.period,\n spent: row.period ? this.getUsage(row.path, row.period as BudgetPeriod) : 0,\n }));\n }\n\n removeBudget(projectPath: string): boolean {\n const project = this.db\n .prepare(\"SELECT id FROM projects WHERE path = ?\")\n .get(projectPath) as { id: number } | undefined;\n\n if (!project) return false;\n\n const result = this.db.prepare(\"DELETE FROM budgets WHERE project_id = ?\").run(project.id);\n return result.changes > 0;\n }\n\n close(): void {\n this.db.close();\n }\n}\n\nfunction getPeriodStart(period: BudgetPeriod): dayjs.Dayjs {\n switch (period) {\n case \"daily\":\n return dayjs().startOf(\"day\");\n case \"weekly\":\n return dayjs().startOf(\"isoWeek\" as dayjs.OpUnitType);\n case \"monthly\":\n return dayjs().startOf(\"month\");\n }\n}\n\nfunction getPeriodEnd(period: BudgetPeriod): dayjs.Dayjs {\n switch (period) {\n case \"daily\":\n return dayjs().endOf(\"day\");\n case \"weekly\":\n return dayjs().endOf(\"isoWeek\" as dayjs.OpUnitType);\n case \"monthly\":\n return dayjs().endOf(\"month\");\n }\n}\n","import Database from \"better-sqlite3\";\nimport { mkdirSync, existsSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport { KERF_DB_PATH } from \"../core/config.js\";\n\nexport function initDatabase(dbPath?: string): Database.Database {\n const path = dbPath ?? KERF_DB_PATH;\n const dir = dirname(path);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n\n const db = new Database(path);\n db.pragma(\"journal_mode = WAL\");\n db.pragma(\"foreign_keys = ON\");\n\n createTables(db);\n return db;\n}\n\nfunction createTables(db: Database.Database): void {\n db.exec(`\n CREATE TABLE IF NOT EXISTS projects (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n name TEXT NOT NULL,\n path TEXT NOT NULL UNIQUE,\n created_at TEXT NOT NULL DEFAULT (datetime('now'))\n );\n\n CREATE TABLE IF NOT EXISTS budgets (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n project_id INTEGER NOT NULL REFERENCES projects(id) ON DELETE CASCADE,\n amount_usd REAL NOT NULL,\n period TEXT NOT NULL CHECK (period IN ('daily', 'weekly', 'monthly')),\n created_at TEXT NOT NULL DEFAULT (datetime('now')),\n UNIQUE(project_id, period)\n );\n\n CREATE TABLE IF NOT EXISTS usage_snapshots (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n project_id INTEGER NOT NULL REFERENCES projects(id) ON DELETE CASCADE,\n session_id TEXT NOT NULL,\n tokens_in INTEGER NOT NULL DEFAULT 0,\n tokens_out INTEGER NOT NULL DEFAULT 0,\n cost_usd REAL NOT NULL DEFAULT 0,\n timestamp TEXT NOT NULL,\n UNIQUE(project_id, session_id, timestamp)\n );\n\n CREATE INDEX IF NOT EXISTS idx_usage_project_time\n ON usage_snapshots(project_id, timestamp);\n `);\n}\n","import type Database from \"better-sqlite3\";\n\ninterface Migration {\n version: number;\n description: string;\n up: (db: Database.Database) => void;\n}\n\nconst migrations: Migration[] = [\n {\n version: 1,\n description: \"Initial schema\",\n up(_db) {\n // Schema is created in schema.ts on init — this is a placeholder for future migrations\n },\n },\n];\n\nexport function runMigrations(db: Database.Database): void {\n db.exec(`\n CREATE TABLE IF NOT EXISTS schema_migrations (\n version INTEGER PRIMARY KEY,\n applied_at TEXT NOT NULL DEFAULT (datetime('now'))\n )\n `);\n\n const applied = new Set(\n db\n .prepare(\"SELECT version FROM schema_migrations\")\n .all()\n .map((row: any) => row.version as number),\n );\n\n for (const migration of migrations) {\n if (!applied.has(migration.version)) {\n migration.up(db);\n db.prepare(\"INSERT INTO schema_migrations (version) VALUES (?)\").run(migration.version);\n }\n }\n}\n","import { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport { runFullAudit } from \"../../audit/recommendations.js\";\nimport { CONTEXT_WINDOW_SIZE } from \"../../core/config.js\";\n\nexport function registerAuditCommand(program: Command): void {\n program\n .command(\"audit\")\n .description(\"Ghost token & CLAUDE.md audit\")\n .option(\"--fix\", \"Auto-apply safe fixes\")\n .option(\"--claude-md-only\", \"Only audit CLAUDE.md\")\n .option(\"--mcp-only\", \"Only audit MCP servers\")\n .option(\"--json\", \"Output as JSON\")\n .action((opts) => {\n const result = runFullAudit();\n\n if (opts.json) {\n console.log(JSON.stringify(result, null, 2));\n return;\n }\n\n const gradeColor =\n result.grade === \"A\" ? chalk.green :\n result.grade === \"B\" ? chalk.yellow :\n chalk.red;\n\n console.log(chalk.bold.cyan(\"\\n kerf audit report\\n\"));\n console.log(\n ` Context Window Health: ${gradeColor.bold(result.grade)} (${result.contextOverhead.percentUsable.toFixed(0)}% usable)\\n`,\n );\n\n if (!opts.mcpOnly) {\n console.log(chalk.bold(\" Ghost Token Breakdown:\"));\n const oh = result.contextOverhead;\n const fmt = (label: string, tokens: number) => {\n const pct = ((tokens / CONTEXT_WINDOW_SIZE) * 100).toFixed(1);\n return ` ${label.padEnd(22)} ${tokens.toLocaleString().padStart(6)} tokens (${pct}%)`;\n };\n console.log(fmt(\"System prompt:\", oh.systemPrompt));\n console.log(fmt(\"Built-in tools:\", oh.builtInTools));\n console.log(fmt(`MCP tools (${result.mcpServers.length} srv):`, oh.mcpTools));\n console.log(fmt(\"CLAUDE.md:\", oh.claudeMd));\n console.log(fmt(\"Autocompact buffer:\", oh.autocompactBuffer));\n console.log(\" \" + \"-\".repeat(40));\n console.log(fmt(\"Total overhead:\", oh.totalOverhead));\n console.log(\n ` ${\"Effective window:\".padEnd(22)} ${oh.effectiveWindow.toLocaleString().padStart(6)} tokens (${oh.percentUsable.toFixed(1)}%)\\n`,\n );\n }\n\n if (!opts.mcpOnly && result.claudeMdAnalysis) {\n const cma = result.claudeMdAnalysis;\n console.log(chalk.bold(\" CLAUDE.md Analysis:\"));\n console.log(\n ` Lines: ${cma.totalLines}${cma.isOverLineLimit ? chalk.yellow(\" (over 200 limit)\") : \"\"}`,\n );\n console.log(` Tokens: ${cma.totalTokens.toLocaleString()}`);\n console.log(\n ` Critical rules in dead zone: ${cma.criticalRulesInDeadZone > 0 ? chalk.red(String(cma.criticalRulesInDeadZone)) : \"0\"}`,\n );\n console.log();\n }\n\n if (result.recommendations.length > 0) {\n console.log(chalk.bold(\" Recommendations:\"));\n result.recommendations.forEach((rec, i) => {\n const priorityColor =\n rec.priority === \"high\" ? chalk.red :\n rec.priority === \"medium\" ? chalk.yellow :\n chalk.dim;\n console.log(\n ` ${i + 1}. ${priorityColor(`[${rec.priority.toUpperCase()}]`)} ${rec.action}`,\n );\n console.log(chalk.dim(` Impact: ${rec.impact}`));\n });\n }\n console.log();\n\n if (opts.fix) {\n console.log(chalk.yellow(\" --fix: Auto-fix is not yet implemented. Coming in v0.2.0.\\n\"));\n }\n });\n}\n","import { estimateContextOverhead, analyzeMcpServers } from \"../core/tokenCounter.js\";\nimport { CONTEXT_WINDOW_SIZE } from \"../core/config.js\";\nimport type { ContextOverhead, McpServerInfo } from \"../types/config.js\";\n\nexport type ContextGrade = \"A\" | \"B\" | \"C\" | \"D\";\n\nexport interface GhostTokenReport {\n grade: ContextGrade;\n overhead: ContextOverhead;\n mcpServers: McpServerInfo[];\n percentUsable: number;\n breakdown: Array<{ label: string; tokens: number; percent: number }>;\n}\n\nexport function calculateGrade(percentUsable: number): ContextGrade {\n if (percentUsable >= 70) return \"A\";\n if (percentUsable >= 50) return \"B\";\n if (percentUsable >= 30) return \"C\";\n return \"D\";\n}\n\nexport function analyzeGhostTokens(claudeMdPath?: string): GhostTokenReport {\n const overhead = estimateContextOverhead(claudeMdPath);\n const mcpServers = analyzeMcpServers();\n const grade = calculateGrade(overhead.percentUsable);\n\n const breakdown = [\n { label: \"System prompt\", tokens: overhead.systemPrompt, percent: (overhead.systemPrompt / CONTEXT_WINDOW_SIZE) * 100 },\n { label: \"Built-in tools\", tokens: overhead.builtInTools, percent: (overhead.builtInTools / CONTEXT_WINDOW_SIZE) * 100 },\n { label: `MCP tools (${mcpServers.length} srv)`, tokens: overhead.mcpTools, percent: (overhead.mcpTools / CONTEXT_WINDOW_SIZE) * 100 },\n { label: \"CLAUDE.md\", tokens: overhead.claudeMd, percent: (overhead.claudeMd / CONTEXT_WINDOW_SIZE) * 100 },\n { label: \"Autocompact buffer\", tokens: overhead.autocompactBuffer, percent: (overhead.autocompactBuffer / CONTEXT_WINDOW_SIZE) * 100 },\n ];\n\n return {\n grade,\n overhead,\n mcpServers,\n percentUsable: overhead.percentUsable,\n breakdown,\n };\n}\n","import { readFileSync, existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { estimateTokens, parseClaudeMdSections } from \"../core/tokenCounter.js\";\nimport type { ClaudeMdAnalysis, ClaudeMdSection } from \"../types/config.js\";\n\nconst CRITICAL_RULE_PATTERN = /\\b(NEVER|ALWAYS|MUST|IMPORTANT|CRITICAL)\\b/i;\nconst SKILL_CANDIDATES = /\\b(review|deploy|release|migration|template|boilerplate|scaffold)\\b/i;\nconst LINE_LIMIT = 200;\n\ntype AttentionZone = \"high-start\" | \"low-middle\" | \"high-end\";\n\nfunction getAttentionZone(position: number, total: number): AttentionZone {\n const pct = total > 0 ? position / total : 0;\n if (pct <= 0.3) return \"high-start\";\n if (pct >= 0.7) return \"high-end\";\n return \"low-middle\";\n}\n\nexport function lintClaudeMd(filePath?: string): ClaudeMdAnalysis | null {\n const paths = filePath\n ? [filePath]\n : [\n join(process.cwd(), \"CLAUDE.md\"),\n join(process.cwd(), \".claude\", \"CLAUDE.md\"),\n ];\n\n let resolvedPath: string | null = null;\n for (const p of paths) {\n if (existsSync(p)) {\n resolvedPath = p;\n break;\n }\n }\n\n if (!resolvedPath) return null;\n\n const content = readFileSync(resolvedPath, \"utf-8\");\n const lines = content.split(\"\\n\");\n const totalLines = lines.length;\n const rawSections = parseClaudeMdSections(content);\n const totalTokens = rawSections.reduce((sum, s) => sum + s.tokens, 0);\n\n let criticalRulesInDeadZone = 0;\n const sectionsToSkill: string[] = [];\n\n const sections: ClaudeMdSection[] = rawSections.map((s) => {\n const midpoint = (s.lineStart + s.lineEnd) / 2;\n const attentionZone = getAttentionZone(midpoint, totalLines);\n const hasCriticalRules = CRITICAL_RULE_PATTERN.test(s.content);\n\n if (hasCriticalRules && attentionZone === \"low-middle\") {\n criticalRulesInDeadZone++;\n }\n\n if (SKILL_CANDIDATES.test(s.title) || SKILL_CANDIDATES.test(s.content)) {\n sectionsToSkill.push(s.title);\n }\n\n return {\n title: s.title,\n content: s.content,\n tokens: s.tokens,\n lineStart: s.lineStart,\n lineEnd: s.lineEnd,\n hasCriticalRules,\n attentionZone,\n };\n });\n\n // Generate suggested reordering: critical rules first, then normal, then verbose\n const critical = sections.filter((s) => s.hasCriticalRules);\n const normal = sections.filter((s) => !s.hasCriticalRules && s.tokens <= 500);\n const heavy = sections.filter((s) => !s.hasCriticalRules && s.tokens > 500);\n const suggestedReorder = [...critical, ...normal, ...heavy].map((s) => s.title);\n\n return {\n totalLines,\n totalTokens,\n sections,\n criticalRulesInDeadZone,\n isOverLineLimit: totalLines > LINE_LIMIT,\n suggestedReorder,\n };\n}\n","import { readFileSync, existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { MCP_TOKENS_PER_TOOL } from \"../core/config.js\";\nimport type { McpServerInfo } from \"../types/config.js\";\n\nexport interface McpAnalysis {\n servers: McpServerInfo[];\n totalTools: number;\n totalTokens: number;\n heavyServers: McpServerInfo[];\n hasToolSearch: boolean;\n effectiveTokens: number;\n recommendations: string[];\n}\n\nconst CLI_ALTERNATIVES: Record<string, string> = {\n playwright: \"Consider using the built-in Bash tool with playwright CLI instead\",\n puppeteer: \"Consider using the built-in Bash tool with puppeteer scripts\",\n filesystem: \"Claude Code has built-in file tools (Read, Write, Edit, Glob, Grep)\",\n github: \"Consider using 'gh' CLI via Bash tool instead\",\n slack: \"Consider using 'slack' CLI or curl for API calls\",\n};\n\nexport function analyzeMcp(): McpAnalysis {\n const servers: McpServerInfo[] = [];\n const configPaths = [\n join(process.cwd(), \".mcp.json\"),\n join(homedir(), \".claude.json\"),\n ];\n\n for (const configPath of configPaths) {\n if (!existsSync(configPath)) continue;\n try {\n const raw = JSON.parse(readFileSync(configPath, \"utf-8\"));\n const mcpServers = raw.mcpServers ?? raw.mcp_servers ?? {};\n for (const [name, config] of Object.entries(mcpServers)) {\n const cfg = config as Record<string, unknown>;\n const tools = Array.isArray(cfg.tools) ? cfg.tools : [];\n const toolCount = tools.length || 5;\n const estimatedTokens = toolCount * MCP_TOKENS_PER_TOOL;\n servers.push({\n name,\n toolCount,\n estimatedTokens,\n isHeavy: toolCount > 10,\n });\n }\n } catch {\n continue;\n }\n }\n\n const totalTools = servers.reduce((sum, s) => sum + s.toolCount, 0);\n const totalTokens = servers.reduce((sum, s) => sum + s.estimatedTokens, 0);\n const heavyServers = servers.filter((s) => s.isHeavy);\n\n // Check if Tool Search is enabled (reduces overhead by ~85%)\n let hasToolSearch = false;\n const settingsPaths = [\n join(process.cwd(), \".claude\", \"settings.json\"),\n join(homedir(), \".claude\", \"settings.json\"),\n ];\n for (const sp of settingsPaths) {\n if (!existsSync(sp)) continue;\n try {\n const settings = JSON.parse(readFileSync(sp, \"utf-8\"));\n if (settings.enableToolSearch || settings.tool_search) {\n hasToolSearch = true;\n break;\n }\n } catch {\n continue;\n }\n }\n\n const effectiveTokens = hasToolSearch ? Math.round(totalTokens * 0.15) : totalTokens;\n\n const recommendations: string[] = [];\n for (const server of heavyServers) {\n recommendations.push(\n `'${server.name}' has ${server.toolCount} tools (${server.estimatedTokens.toLocaleString()} tokens). Consider enabling Tool Search to reduce overhead by ~85%.`,\n );\n }\n\n for (const server of servers) {\n const alt = CLI_ALTERNATIVES[server.name.toLowerCase()];\n if (alt) {\n recommendations.push(`${server.name}: ${alt}`);\n }\n }\n\n return {\n servers,\n totalTools,\n totalTokens,\n heavyServers,\n hasToolSearch,\n effectiveTokens,\n recommendations,\n };\n}\n","import { analyzeGhostTokens } from \"./ghostTokens.js\";\nimport { lintClaudeMd } from \"./claudeMdLinter.js\";\nimport { analyzeMcp } from \"./mcpAnalyzer.js\";\nimport type { AuditRecommendation, AuditResult } from \"../types/config.js\";\n\nexport function runFullAudit(claudeMdPath?: string): AuditResult {\n const ghostReport = analyzeGhostTokens(claudeMdPath);\n const claudeMdAnalysis = lintClaudeMd(claudeMdPath);\n const mcpAnalysis = analyzeMcp();\n\n const recommendations: AuditRecommendation[] = [];\n\n // CLAUDE.md recommendations\n if (claudeMdAnalysis) {\n if (claudeMdAnalysis.criticalRulesInDeadZone > 0) {\n recommendations.push({\n priority: \"high\",\n impact: \"improved rule adherence\",\n action: `Reorder CLAUDE.md — ${claudeMdAnalysis.criticalRulesInDeadZone} critical rule(s) in the low-attention dead zone (30-70% position). Move them to the top or bottom.`,\n category: \"claude-md\",\n });\n }\n\n if (claudeMdAnalysis.isOverLineLimit) {\n recommendations.push({\n priority: \"high\",\n impact: `${claudeMdAnalysis.totalLines - 200} lines over limit`,\n action: `CLAUDE.md is ${claudeMdAnalysis.totalLines} lines (limit: 200). Trim or move sections to skills.`,\n category: \"claude-md\",\n });\n }\n\n const heavySections = claudeMdAnalysis.sections.filter((s) => s.tokens > 500);\n for (const section of heavySections) {\n recommendations.push({\n priority: \"medium\",\n impact: `-${section.tokens} tokens/session`,\n action: `Move '${section.title}' section to a skill (${section.tokens} tokens — heavy section)`,\n category: \"claude-md\",\n });\n }\n }\n\n // MCP recommendations\n for (const server of mcpAnalysis.heavyServers) {\n recommendations.push({\n priority: \"medium\",\n impact: `-${server.estimatedTokens.toLocaleString()} tokens/session`,\n action: `MCP server '${server.name}' has ${server.toolCount} tools. Consider disabling if unused or enabling Tool Search.`,\n category: \"mcp\",\n });\n }\n\n if (!mcpAnalysis.hasToolSearch && mcpAnalysis.totalTools > 10) {\n recommendations.push({\n priority: \"high\",\n impact: `~${Math.round(mcpAnalysis.totalTokens * 0.85).toLocaleString()} tokens saved`,\n action: \"Enable Tool Search (deferred tool loading) to reduce MCP overhead by ~85%.\",\n category: \"mcp\",\n });\n }\n\n for (const rec of mcpAnalysis.recommendations) {\n if (rec.includes(\"CLI\")) {\n recommendations.push({\n priority: \"low\",\n impact: \"reduced token overhead\",\n action: rec,\n category: \"mcp\",\n });\n }\n }\n\n // Ghost token recommendations\n if (ghostReport.percentUsable < 50) {\n recommendations.push({\n priority: \"high\",\n impact: `only ${ghostReport.percentUsable.toFixed(0)}% usable`,\n action: `Context window health is poor (grade ${ghostReport.grade}). Total overhead: ${ghostReport.overhead.totalOverhead.toLocaleString()} tokens.`,\n category: \"ghost-tokens\",\n });\n }\n\n // Sort by priority\n const priorityOrder = { high: 0, medium: 1, low: 2 };\n recommendations.sort((a, b) => priorityOrder[a.priority] - priorityOrder[b.priority]);\n\n return {\n grade: ghostReport.grade,\n contextOverhead: ghostReport.overhead,\n claudeMdAnalysis,\n mcpServers: ghostReport.mcpServers,\n recommendations,\n };\n}\n","import { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport dayjs from \"dayjs\";\nimport { getActiveSessions, parseSessionFile, findJsonlFiles } from \"../../core/parser.js\";\nimport {\n calculateMessageCost,\n aggregateCosts,\n formatCost,\n formatTokens,\n} from \"../../core/costCalculator.js\";\nimport type { ParsedMessage } from \"../../types/jsonl.js\";\n\nexport function registerReportCommand(program: Command): void {\n program\n .command(\"report\")\n .description(\"Historical cost reports\")\n .option(\"--period <period>\", \"Time period (today|week|month|all)\", \"today\")\n .option(\"-p, --project <path>\", \"Filter to specific project\")\n .option(\"--model\", \"Show per-model breakdown\")\n .option(\"--sessions\", \"Show per-session breakdown\")\n .option(\"--csv\", \"Export as CSV\")\n .option(\"--json\", \"Export as JSON\")\n .action(async (opts) => {\n const files = await findJsonlFiles(opts.project);\n\n if (files.length === 0) {\n console.log(\"No session data found. Start using Claude Code to generate data.\");\n return;\n }\n\n // Determine time filter\n const now = dayjs();\n let cutoff: dayjs.Dayjs;\n switch (opts.period) {\n case \"today\":\n cutoff = now.startOf(\"day\");\n break;\n case \"week\":\n cutoff = now.subtract(7, \"day\");\n break;\n case \"month\":\n cutoff = now.subtract(30, \"day\");\n break;\n case \"all\":\n cutoff = dayjs(\"2000-01-01\");\n break;\n default:\n cutoff = now.startOf(\"day\");\n }\n\n // Parse all sessions and collect messages\n const allMessages: ParsedMessage[] = [];\n const sessionSummaries: Array<{ id: string; model: string; cost: number; messages: number }> = [];\n\n for (const file of files) {\n try {\n const session = parseSessionFile(file);\n const filteredMessages = session.messages.filter((m) =>\n dayjs(m.timestamp).isAfter(cutoff),\n );\n if (filteredMessages.length === 0) continue;\n\n allMessages.push(...filteredMessages);\n\n const sessionCost = filteredMessages.reduce(\n (sum, m) => sum + calculateMessageCost(m).totalCost,\n 0,\n );\n sessionSummaries.push({\n id: session.sessionId,\n model: filteredMessages[0]?.model ?? \"unknown\",\n cost: sessionCost,\n messages: filteredMessages.length,\n });\n } catch {\n continue;\n }\n }\n\n if (allMessages.length === 0) {\n console.log(`No data found for period: ${opts.period}`);\n return;\n }\n\n // Calculate totals\n let totalCost = 0;\n let totalInput = 0;\n let totalOutput = 0;\n let totalCacheRead = 0;\n\n for (const msg of allMessages) {\n totalCost += calculateMessageCost(msg).totalCost;\n totalInput += msg.usage.input_tokens;\n totalOutput += msg.usage.output_tokens;\n totalCacheRead += msg.usage.cache_read_input_tokens;\n }\n\n const totalCacheable = totalInput + totalCacheRead;\n const cacheHitRate = totalCacheable > 0 ? (totalCacheRead / totalCacheable) * 100 : 0;\n\n if (opts.json) {\n console.log(\n JSON.stringify(\n {\n period: opts.period,\n totalCost,\n totalInput,\n totalOutput,\n cacheHitRate,\n sessions: sessionSummaries,\n },\n null,\n 2,\n ),\n );\n return;\n }\n\n if (opts.csv) {\n console.log(\"session_id,model,cost_usd,messages\");\n for (const s of sessionSummaries) {\n console.log(`${s.id},${s.model},${s.cost.toFixed(4)},${s.messages}`);\n }\n return;\n }\n\n // Pretty print\n const periodLabel = opts.period === \"today\" ? now.format(\"ddd, MMM D, YYYY\") : opts.period;\n console.log(chalk.bold.cyan(`\\n kerf report -- ${periodLabel}\\n`));\n console.log(` Total Cost: ${chalk.bold(formatCost(totalCost))}`);\n console.log(` Total Tokens: ${formatTokens(totalInput)} in / ${formatTokens(totalOutput)} out`);\n console.log(` Cache Hit Rate: ${cacheHitRate.toFixed(1)}%`);\n console.log(` Sessions: ${sessionSummaries.length}`);\n console.log();\n\n if (opts.model || opts.sessions) {\n // Model breakdown\n const byModel = new Map<string, { cost: number; sessions: number }>();\n for (const s of sessionSummaries) {\n const existing = byModel.get(s.model) ?? { cost: 0, sessions: 0 };\n existing.cost += s.cost;\n existing.sessions++;\n byModel.set(s.model, existing);\n }\n\n console.log(chalk.bold(\" Model Breakdown:\"));\n for (const [model, data] of byModel) {\n const pct = totalCost > 0 ? ((data.cost / totalCost) * 100).toFixed(1) : \"0\";\n const shortModel = model.replace(\"claude-\", \"\").replace(/-20\\d{6}$/, \"\");\n console.log(\n ` ${shortModel}: ${formatCost(data.cost)} (${pct}%) -- ${data.sessions} session(s)`,\n );\n }\n console.log();\n }\n\n if (opts.sessions) {\n console.log(chalk.bold(\" Session Breakdown:\"));\n for (const s of sessionSummaries.sort((a, b) => b.cost - a.cost)) {\n console.log(` ${s.id.slice(0, 12)} ${formatCost(s.cost)} ${s.messages} msgs [${s.model}]`);\n }\n console.log();\n }\n\n // Hourly breakdown\n const hourly = aggregateCosts(allMessages, \"hour\");\n if (hourly.length > 1) {\n console.log(chalk.bold(\" Hourly:\"));\n const maxCost = Math.max(...hourly.map((h) => h.totalCost));\n for (const h of hourly.slice(-8)) {\n const barLen = maxCost > 0 ? Math.round((h.totalCost / maxCost) * 12) : 0;\n const bar = \"\\u2588\".repeat(barLen) + \"\\u2591\".repeat(12 - barLen);\n console.log(` ${h.periodLabel.padEnd(14)} ${bar} ${formatCost(h.totalCost)}`);\n }\n console.log();\n }\n });\n}\n","import { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport { mkdirSync, existsSync, readFileSync, writeFileSync, copyFileSync } from \"node:fs\";\nimport { join, dirname } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { initDatabase } from \"../../db/schema.js\";\nimport { runMigrations } from \"../../db/migrations.js\";\n\nexport function registerInitCommand(program: Command): void {\n program\n .command(\"init\")\n .description(\"Set up kerf for the current project\")\n .option(\"--global\", \"Install hooks globally\")\n .option(\"--hooks-only\", \"Only install hooks\")\n .option(\"--no-hooks\", \"Skip hook installation\")\n .option(\"--force\", \"Skip confirmation prompts\")\n .action(async (opts) => {\n console.log(chalk.bold.cyan(\"\\n Welcome to kerf!\\n\"));\n console.log(\" Setting up cost intelligence for Claude Code...\\n\");\n\n // Create ~/.kerf/ directory\n const kerfDir = join(homedir(), \".kerf\");\n if (!existsSync(kerfDir)) {\n mkdirSync(kerfDir, { recursive: true });\n console.log(chalk.green(\" Created ~/.kerf/\"));\n }\n\n if (!opts.hooksOnly) {\n // Initialize database\n try {\n const db = initDatabase();\n runMigrations(db);\n db.close();\n console.log(chalk.green(\" Created ~/.kerf/kerf.db\"));\n } catch (err) {\n console.log(chalk.red(` Failed to create database: ${err}`));\n }\n }\n\n // Detect existing tools\n try {\n const { execSync } = await import(\"node:child_process\");\n try {\n execSync(\"which rtk\", { stdio: \"ignore\" });\n console.log(chalk.green(\" Detected RTK (command compression) -- compatible!\"));\n } catch { /* not installed */ }\n\n try {\n execSync(\"which ccusage\", { stdio: \"ignore\" });\n console.log(chalk.green(\" Detected ccusage -- will import historical data\"));\n } catch { /* not installed */ }\n } catch { /* ignore */ }\n\n // Install hooks\n if (opts.hooks !== false) {\n const settingsPath = opts.global\n ? join(homedir(), \".claude\", \"settings.json\")\n : join(process.cwd(), \".claude\", \"settings.json\");\n\n console.log(\"\\n Install hooks? These enable:\");\n console.log(\" - Real-time token tracking (Notification hook)\");\n console.log(\" - Budget enforcement (Stop hook)\");\n console.log(`\\n Hooks will be added to ${opts.global ? \"~/.claude\" : \".claude\"}/settings.json`);\n\n try {\n installHooks(settingsPath);\n console.log(chalk.green(\"\\n Hooks installed\"));\n } catch (err) {\n console.log(chalk.yellow(`\\n Skipped hook installation: ${err}`));\n }\n }\n\n console.log(chalk.bold(\"\\n Recommended settings for your setup:\"));\n console.log(chalk.dim(' Add to .claude/settings.json or ~/.claude/settings.json:'));\n console.log(chalk.dim(JSON.stringify({\n env: {\n MAX_THINKING_TOKENS: \"10000\",\n CLAUDE_AUTOCOMPACT_PCT_OVERRIDE: \"50\",\n },\n }, null, 4).split(\"\\n\").map(l => \" \" + l).join(\"\\n\")));\n\n console.log(chalk.bold.cyan(\"\\n Run 'kerf watch' to start the live dashboard!\\n\"));\n });\n}\n\nfunction installHooks(settingsPath: string): void {\n const dir = dirname(settingsPath);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n\n let settings: Record<string, unknown> = {};\n if (existsSync(settingsPath)) {\n // Backup existing settings\n const backupPath = settingsPath + \".bak\";\n copyFileSync(settingsPath, backupPath);\n settings = JSON.parse(readFileSync(settingsPath, \"utf-8\"));\n }\n\n const hooks = (settings.hooks ?? {}) as Record<string, unknown[]>;\n\n // Add Notification hook if not present\n if (!hooks.Notification) {\n hooks.Notification = [];\n }\n\n // Add Stop hook if not present\n if (!hooks.Stop) {\n hooks.Stop = [];\n }\n\n settings.hooks = hooks;\n writeFileSync(settingsPath, JSON.stringify(settings, null, 2));\n}\n"],"mappings":"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","names":["React","join","join","Box","Text","dayjs","dayjs","dayjs","Box","Text","jsx","jsxs","readFileSync","join","homedir","readFileSync","join","homedir","jsx","jsxs","Box","Text","program","React","React","render","glob","Box","Text","jsx","jsxs","program","glob","estimate","waitUntilExit","render","React","dayjs","isoWeek","basename","existsSync","existsSync","dayjs","isoWeek","basename","program","chalk","readFileSync","existsSync","join","join","existsSync","readFileSync","readFileSync","existsSync","join","homedir","join","homedir","existsSync","readFileSync","program","chalk","chalk","dayjs","program","dayjs","chalk","chalk","mkdirSync","existsSync","readFileSync","join","dirname","homedir","program","chalk","join","homedir","existsSync","mkdirSync","dirname","readFileSync"]}
1
+ {"version":3,"sources":["../src/cli/index.ts","../src/cli/commands/watch.ts","../src/core/parser.ts","../src/core/config.ts","../src/cli/ui/Dashboard.tsx","../src/cli/ui/CostMeter.tsx","../src/core/costCalculator.ts","../src/cli/ui/ContextBar.tsx","../src/core/tokenCounter.ts","../src/cli/commands/estimate.ts","../src/core/estimator.ts","../src/cli/ui/EstimateCard.tsx","../src/cli/commands/budget.ts","../src/core/budgetManager.ts","../src/db/schema.ts","../src/db/migrations.ts","../src/cli/commands/audit.ts","../src/audit/ghostTokens.ts","../src/audit/claudeMdLinter.ts","../src/audit/mcpAnalyzer.ts","../src/audit/recommendations.ts","../src/cli/commands/report.ts","../src/cli/commands/init.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport { registerWatchCommand } from \"./commands/watch.js\";\nimport { registerEstimateCommand } from \"./commands/estimate.js\";\nimport { registerBudgetCommand } from \"./commands/budget.js\";\nimport { registerAuditCommand } from \"./commands/audit.js\";\nimport { registerReportCommand } from \"./commands/report.js\";\nimport { registerInitCommand } from \"./commands/init.js\";\n\nconst program = new Command();\n\nprogram\n .name(\"kerf-cli\")\n .version(\"0.1.0\")\n .description(\"Cost intelligence for Claude Code. Know before you spend.\");\n\n// Register all subcommands\nregisterWatchCommand(program);\nregisterEstimateCommand(program);\nregisterBudgetCommand(program);\nregisterAuditCommand(program);\nregisterReportCommand(program);\nregisterInitCommand(program);\n\n// Default to watch if no command given\nprogram.action(async () => {\n await program.commands.find((c) => c.name() === \"watch\")?.parseAsync([], { from: \"user\" });\n});\n\nprogram.parse();\n","import React from \"react\";\nimport { render } from \"ink\";\nimport { Command } from \"commander\";\nimport { getActiveSessions } from \"../../core/parser.js\";\nimport { Dashboard } from \"../ui/Dashboard.js\";\n\nexport function registerWatchCommand(program: Command): void {\n program\n .command(\"watch\")\n .description(\"Real-time cost dashboard (default)\")\n .option(\"-s, --session <id>\", \"Watch a specific session\")\n .option(\"-p, --project <path>\", \"Watch sessions for a specific project\")\n .option(\"-i, --interval <ms>\", \"Polling interval in ms\", \"2000\")\n .option(\"--no-color\", \"Disable colors\")\n .action(async (opts) => {\n const interval = parseInt(opts.interval, 10);\n\n let sessionFilePath: string | undefined;\n\n if (opts.session) {\n const sessions = await getActiveSessions(opts.project);\n const found = sessions.find((s) => s.sessionId.startsWith(opts.session));\n sessionFilePath = found?.filePath;\n } else {\n const sessions = await getActiveSessions(opts.project);\n sessionFilePath = sessions[0]?.filePath;\n }\n\n if (!sessionFilePath) {\n console.log(\n \"No active Claude Code session found. Start Claude Code and run 'kerf-cli watch' again.\",\n );\n process.exit(0);\n }\n\n const { waitUntilExit } = render(\n React.createElement(Dashboard, { sessionFilePath, interval }),\n );\n await waitUntilExit();\n });\n}\n","import { readFileSync, statSync } from \"node:fs\";\nimport { readdir } from \"node:fs/promises\";\nimport { join, basename } from \"node:path\";\nimport { createReadStream } from \"node:fs\";\nimport { createInterface } from \"node:readline\";\nimport dayjs from \"dayjs\";\nimport { watch } from \"chokidar\";\nimport { CLAUDE_PROJECTS_DIR, BILLING_WINDOW_HOURS } from \"./config.js\";\nimport type {\n RawJsonlMessage,\n ParsedMessage,\n SessionData,\n ParsedSession,\n MessageUsage,\n} from \"../types/jsonl.js\";\n\nconst DEFAULT_USAGE: MessageUsage = {\n input_tokens: 0,\n output_tokens: 0,\n cache_creation_input_tokens: 0,\n cache_read_input_tokens: 0,\n};\n\nfunction extractUsage(raw: RawJsonlMessage): Partial<MessageUsage> | null {\n return raw.message?.usage ?? raw.usage ?? raw.delta?.usage ?? null;\n}\n\nfunction extractMessageId(raw: RawJsonlMessage): string | null {\n return raw.message?.id ?? null;\n}\n\nfunction extractModel(raw: RawJsonlMessage): string | null {\n return raw.message?.model ?? null;\n}\n\nfunction extractTimestamp(raw: RawJsonlMessage): string {\n return raw.timestamp ?? dayjs().toISOString();\n}\n\nexport function parseJsonlLine(line: string): RawJsonlMessage | null {\n const trimmed = line.trim();\n if (!trimmed) return null;\n try {\n return JSON.parse(trimmed) as RawJsonlMessage;\n } catch {\n return null;\n }\n}\n\nexport function parseJsonlContent(content: string, sessionId: string): ParsedMessage[] {\n const lines = content.split(\"\\n\");\n const messageMap = new Map<string, ParsedMessage>();\n let anonymousCounter = 0;\n\n for (const line of lines) {\n const raw = parseJsonlLine(line);\n if (!raw) continue;\n\n const usage = extractUsage(raw);\n if (!usage) continue;\n\n const id = extractMessageId(raw) ?? `anon_${anonymousCounter++}`;\n const model = extractModel(raw) ?? \"unknown\";\n const timestamp = extractTimestamp(raw);\n\n const existing = messageMap.get(id);\n const parsedUsage: MessageUsage = {\n input_tokens: usage.input_tokens ?? existing?.usage.input_tokens ?? 0,\n output_tokens: usage.output_tokens ?? existing?.usage.output_tokens ?? 0,\n cache_creation_input_tokens:\n usage.cache_creation_input_tokens ?? existing?.usage.cache_creation_input_tokens ?? 0,\n cache_read_input_tokens:\n usage.cache_read_input_tokens ?? existing?.usage.cache_read_input_tokens ?? 0,\n };\n\n // Deduplicate by message id — take the LAST occurrence (handles streaming intermediates)\n messageMap.set(id, {\n id,\n model: model !== \"unknown\" ? model : existing?.model ?? \"unknown\",\n timestamp,\n usage: parsedUsage,\n totalCostUsd: raw.total_cost_usd ?? existing?.totalCostUsd ?? null,\n });\n }\n\n return Array.from(messageMap.values());\n}\n\nexport function parseSessionFile(filePath: string): ParsedSession {\n const content = readFileSync(filePath, \"utf-8\");\n const sessionId = basename(filePath, \".jsonl\");\n const messages = parseJsonlContent(content, sessionId);\n\n const totals = messages.reduce(\n (acc, msg) => ({\n input: acc.input + msg.usage.input_tokens,\n output: acc.output + msg.usage.output_tokens,\n cacheRead: acc.cacheRead + msg.usage.cache_read_input_tokens,\n cacheCreation: acc.cacheCreation + msg.usage.cache_creation_input_tokens,\n cost: acc.cost + (msg.totalCostUsd ?? 0),\n }),\n { input: 0, output: 0, cacheRead: 0, cacheCreation: 0, cost: 0 },\n );\n\n const timestamps = messages.map((m) => m.timestamp).sort();\n\n return {\n sessionId,\n filePath,\n messages,\n totalInputTokens: totals.input,\n totalOutputTokens: totals.output,\n totalCacheReadTokens: totals.cacheRead,\n totalCacheCreationTokens: totals.cacheCreation,\n totalCostUsd: totals.cost,\n startTime: timestamps[0] ?? \"\",\n endTime: timestamps[timestamps.length - 1] ?? \"\",\n messageCount: messages.length,\n };\n}\n\nexport async function findJsonlFiles(baseDir?: string): Promise<string[]> {\n const dir = baseDir ?? CLAUDE_PROJECTS_DIR;\n const files: string[] = [];\n\n async function walk(currentDir: string): Promise<void> {\n let entries;\n try {\n entries = await readdir(currentDir, { withFileTypes: true });\n } catch {\n return;\n }\n for (const entry of entries) {\n const fullPath = join(currentDir, entry.name);\n if (entry.isDirectory()) {\n await walk(fullPath);\n } else if (entry.name.endsWith(\".jsonl\")) {\n files.push(fullPath);\n }\n }\n }\n\n await walk(dir);\n return files;\n}\n\nexport async function getActiveSessions(baseDir?: string): Promise<SessionData[]> {\n const files = await findJsonlFiles(baseDir);\n const cutoff = dayjs().subtract(BILLING_WINDOW_HOURS, \"hour\");\n const activeSessions: SessionData[] = [];\n\n for (const filePath of files) {\n try {\n const stat = statSync(filePath);\n const lastModified = dayjs(stat.mtime);\n if (lastModified.isAfter(cutoff)) {\n const content = readFileSync(filePath, \"utf-8\");\n const sessionId = basename(filePath, \".jsonl\");\n const messages = parseJsonlContent(content, sessionId);\n\n if (messages.length === 0) continue;\n\n const timestamps = messages.map((m) => m.timestamp).sort();\n activeSessions.push({\n sessionId,\n filePath,\n messages,\n startTime: timestamps[0] ?? \"\",\n endTime: timestamps[timestamps.length - 1] ?? \"\",\n lastModified: stat.mtime,\n });\n }\n } catch {\n continue;\n }\n }\n\n return activeSessions.sort(\n (a, b) => b.lastModified.getTime() - a.lastModified.getTime(),\n );\n}\n\nexport interface StreamingParser {\n onMessage: (callback: (msg: ParsedMessage) => void) => void;\n stop: () => void;\n}\n\nexport function createStreamingParser(filePath: string): StreamingParser {\n const callbacks: Array<(msg: ParsedMessage) => void> = [];\n const sessionId = basename(filePath, \".jsonl\");\n let anonymousCounter = 0;\n\n const watcher = watch(filePath, { persistent: true });\n\n watcher.on(\"change\", () => {\n const content = readFileSync(filePath, \"utf-8\");\n const lines = content.split(\"\\n\");\n // Process only the last few lines for incremental updates\n const recentLines = lines.slice(-20);\n for (const line of recentLines) {\n const raw = parseJsonlLine(line);\n if (!raw) continue;\n const usage = extractUsage(raw);\n if (!usage) continue;\n\n const id = extractMessageId(raw) ?? `anon_${anonymousCounter++}`;\n const model = extractModel(raw) ?? \"unknown\";\n const timestamp = extractTimestamp(raw);\n\n const msg: ParsedMessage = {\n id,\n model,\n timestamp,\n usage: {\n input_tokens: usage.input_tokens ?? 0,\n output_tokens: usage.output_tokens ?? 0,\n cache_creation_input_tokens: usage.cache_creation_input_tokens ?? 0,\n cache_read_input_tokens: usage.cache_read_input_tokens ?? 0,\n },\n totalCostUsd: raw.total_cost_usd ?? null,\n };\n\n for (const cb of callbacks) {\n cb(msg);\n }\n }\n });\n\n return {\n onMessage(callback) {\n callbacks.push(callback);\n },\n stop() {\n watcher.close();\n },\n };\n}\n","import { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { KerfConfig } from \"../types/config.js\";\n\nexport const DEFAULT_CONFIG: KerfConfig = {\n defaultModel: \"sonnet\",\n budgetWarningThreshold: 80,\n budgetBlockThreshold: 100,\n pollingInterval: 2000,\n dataDir: join(homedir(), \".kerf\"),\n enableHooks: true,\n};\n\nexport const CONTEXT_WINDOW_SIZE = 200_000;\nexport const SYSTEM_PROMPT_TOKENS = 14_328;\nexport const BUILT_IN_TOOLS_TOKENS = 15_000;\nexport const AUTOCOMPACT_BUFFER_TOKENS = 33_000;\nexport const MCP_TOKENS_PER_TOOL = 600;\nexport const BILLING_WINDOW_HOURS = 5;\n\nexport const CLAUDE_PROJECTS_DIR = join(homedir(), \".claude\", \"projects\");\nexport const CLAUDE_SETTINGS_GLOBAL = join(homedir(), \".claude\", \"settings.json\");\nexport const KERF_DB_PATH = join(homedir(), \".kerf\", \"kerf.db\");\nexport const KERF_SESSION_LOG = join(homedir(), \".kerf\", \"session-log.jsonl\");\n\nexport function getConfig(): KerfConfig {\n return { ...DEFAULT_CONFIG };\n}\n","import React, { useState, useEffect } from \"react\";\nimport { Box, Text, useInput, useApp } from \"ink\";\nimport { CostMeter } from \"./CostMeter.js\";\nimport { ContextBar } from \"./ContextBar.js\";\nimport { parseSessionFile } from \"../../core/parser.js\";\nimport {\n calculateMessageCost,\n calculateCostVelocity,\n formatCost,\n formatTokens,\n} from \"../../core/costCalculator.js\";\nimport { estimateContextOverhead } from \"../../core/tokenCounter.js\";\nimport { CONTEXT_WINDOW_SIZE, BILLING_WINDOW_HOURS } from \"../../core/config.js\";\nimport type { ParsedSession } from \"../../types/jsonl.js\";\nimport type { ContextOverhead } from \"../../types/config.js\";\n\ninterface DashboardProps {\n sessionFilePath: string;\n interval: number;\n}\n\nexport function Dashboard({ sessionFilePath, interval }: DashboardProps) {\n const { exit } = useApp();\n const [session, setSession] = useState<ParsedSession | null>(null);\n const [overhead, setOverhead] = useState<ContextOverhead>(estimateContextOverhead());\n const [showBudget, setShowBudget] = useState(false);\n\n useEffect(() => {\n function refresh() {\n try {\n const parsed = parseSessionFile(sessionFilePath);\n setSession(parsed);\n setOverhead(estimateContextOverhead());\n } catch {\n // File may be in the middle of a write\n }\n }\n\n refresh();\n const timer = setInterval(refresh, interval);\n return () => clearInterval(timer);\n }, [sessionFilePath, interval]);\n\n useInput((input) => {\n if (input === \"q\") exit();\n if (input === \"b\") setShowBudget((prev) => !prev);\n });\n\n if (!session || session.messages.length === 0) {\n return (\n <Box paddingX={1}>\n <Text dimColor>Waiting for session data...</Text>\n </Box>\n );\n }\n\n const totalCost = session.messages.reduce(\n (sum, msg) => sum + calculateMessageCost(msg).totalCost,\n 0,\n );\n const velocity = calculateCostVelocity(session.messages);\n const model = session.messages[session.messages.length - 1]?.model ?? \"unknown\";\n const windowBudget = velocity.projectedWindowCost || totalCost * 3;\n\n const totalInput = session.totalInputTokens + session.totalCacheReadTokens;\n const usedTokens = totalInput + session.totalOutputTokens;\n\n // Recent messages for the log\n const recentMessages = session.messages.slice(-8);\n\n return (\n <Box flexDirection=\"column\">\n <Box borderStyle=\"round\" borderColor=\"cyan\" paddingX={1}>\n <Text bold color=\"cyan\">\n kerf-cli watch\n </Text>\n <Text> | session: {session.sessionId.slice(0, 8)}...</Text>\n <Text> | {session.messageCount} messages</Text>\n <Text dimColor> (q=quit, b=budget)</Text>\n </Box>\n\n <CostMeter\n spent={totalCost}\n windowBudget={windowBudget}\n burnRate={velocity.dollarsPerMinute}\n minutesRemaining={velocity.minutesRemaining}\n model={model}\n />\n\n <ContextBar used={usedTokens} total={CONTEXT_WINDOW_SIZE} overhead={overhead} />\n\n <Box flexDirection=\"column\" paddingX={1} marginTop={1}>\n <Text bold>Recent Messages:</Text>\n {recentMessages.map((msg, i) => {\n const cost = calculateMessageCost(msg);\n return (\n <Text key={i}>\n <Text dimColor>{msg.timestamp.slice(11, 19)}</Text>\n <Text> {formatTokens(msg.usage.input_tokens + msg.usage.output_tokens)} tok</Text>\n <Text color=\"yellow\"> {formatCost(cost.totalCost)}</Text>\n </Text>\n );\n })}\n </Box>\n </Box>\n );\n}\n","import React from \"react\";\nimport { Box, Text } from \"ink\";\nimport { formatCost } from \"../../core/costCalculator.js\";\n\ninterface CostMeterProps {\n spent: number;\n windowBudget: number;\n burnRate: number;\n minutesRemaining: number;\n model: string;\n}\n\nexport function CostMeter({ spent, windowBudget, burnRate, minutesRemaining, model }: CostMeterProps) {\n const pct = windowBudget > 0 ? (spent / windowBudget) * 100 : 0;\n const color = pct < 50 ? \"green\" : pct < 80 ? \"yellow\" : \"red\";\n\n const hours = Math.floor(minutesRemaining / 60);\n const mins = Math.round(minutesRemaining % 60);\n const timeStr = hours > 0 ? `${hours}h ${mins}m` : `${mins}m`;\n\n return (\n <Box flexDirection=\"column\" paddingX={1}>\n <Text>\n <Text color={color}>{\">> \"}</Text>\n <Text bold>{formatCost(spent)}</Text>\n <Text dimColor> / ~{formatCost(windowBudget)} window</Text>\n <Text> | </Text>\n <Text color={color}>{formatCost(burnRate)}/min</Text>\n <Text> | </Text>\n <Text>~{timeStr} remaining</Text>\n <Text dimColor> [{model}]</Text>\n </Text>\n </Box>\n );\n}\n","import dayjs from \"dayjs\";\nimport isoWeek from \"dayjs/plugin/isoWeek.js\";\nimport { BILLING_WINDOW_HOURS } from \"./config.js\";\nimport type { ParsedMessage, ParsedSession } from \"../types/jsonl.js\";\nimport type {\n ModelPricing,\n PricingConfig,\n CostBreakdown,\n SessionCostSummary,\n CostVelocity,\n AggregationPeriod,\n AggregatedCost,\n} from \"../types/pricing.js\";\n\ndayjs.extend(isoWeek);\n\nexport const MODEL_PRICING: PricingConfig = {\n \"claude-sonnet-4-20250514\": {\n input: 3,\n output: 15,\n cacheRead: 0.3,\n cacheCreation: 3.75,\n },\n \"claude-opus-4-20250514\": {\n input: 15,\n output: 75,\n cacheRead: 1.5,\n cacheCreation: 18.75,\n },\n \"claude-haiku-4-20250514\": {\n input: 0.8,\n output: 4,\n cacheRead: 0.08,\n cacheCreation: 1.0,\n },\n};\n\n// Alias mappings for short model names\nconst MODEL_ALIASES: Record<string, string> = {\n sonnet: \"claude-sonnet-4-20250514\",\n opus: \"claude-opus-4-20250514\",\n haiku: \"claude-haiku-4-20250514\",\n};\n\nexport function resolveModelPricing(model: string): ModelPricing {\n const resolved = MODEL_ALIASES[model] ?? model;\n // Try exact match first, then prefix match\n if (MODEL_PRICING[resolved]) return MODEL_PRICING[resolved];\n const match = Object.keys(MODEL_PRICING).find((k) => resolved.startsWith(k) || k.startsWith(resolved));\n if (match) return MODEL_PRICING[match];\n // Default to sonnet pricing\n return MODEL_PRICING[\"claude-sonnet-4-20250514\"];\n}\n\nexport function calculateMessageCost(msg: ParsedMessage): CostBreakdown {\n // If authoritative cost is present, use it\n if (msg.totalCostUsd !== null && msg.totalCostUsd > 0) {\n return {\n inputCost: 0,\n outputCost: 0,\n cacheReadCost: 0,\n cacheCreationCost: 0,\n totalCost: msg.totalCostUsd,\n };\n }\n\n const pricing = resolveModelPricing(msg.model);\n const MILLION = 1_000_000;\n\n // Use multiply-then-divide to avoid floating point issues\n const inputCost = (msg.usage.input_tokens * pricing.input) / MILLION;\n const outputCost = (msg.usage.output_tokens * pricing.output) / MILLION;\n const cacheReadCost = (msg.usage.cache_read_input_tokens * pricing.cacheRead) / MILLION;\n const cacheCreationCost = (msg.usage.cache_creation_input_tokens * pricing.cacheCreation) / MILLION;\n\n return {\n inputCost,\n outputCost,\n cacheReadCost,\n cacheCreationCost,\n totalCost: inputCost + outputCost + cacheReadCost + cacheCreationCost,\n };\n}\n\nexport function calculateSessionCost(session: ParsedSession): SessionCostSummary {\n let totalCost = 0;\n const costBreakdown: CostBreakdown = {\n inputCost: 0,\n outputCost: 0,\n cacheReadCost: 0,\n cacheCreationCost: 0,\n totalCost: 0,\n };\n\n for (const msg of session.messages) {\n const msgCost = calculateMessageCost(msg);\n costBreakdown.inputCost += msgCost.inputCost;\n costBreakdown.outputCost += msgCost.outputCost;\n costBreakdown.cacheReadCost += msgCost.cacheReadCost;\n costBreakdown.cacheCreationCost += msgCost.cacheCreationCost;\n totalCost += msgCost.totalCost;\n }\n\n costBreakdown.totalCost = totalCost;\n\n return {\n sessionId: session.sessionId,\n totalCost,\n tokenBreakdown: {\n input: session.totalInputTokens,\n output: session.totalOutputTokens,\n cacheRead: session.totalCacheReadTokens,\n cacheCreation: session.totalCacheCreationTokens,\n },\n costBreakdown,\n model: session.messages[0]?.model ?? \"unknown\",\n messageCount: session.messageCount,\n startTime: session.startTime,\n endTime: session.endTime,\n };\n}\n\nexport function calculateCostVelocity(messages: ParsedMessage[]): CostVelocity {\n if (messages.length < 2) {\n return { dollarsPerMinute: 0, tokensPerMinute: 0, projectedWindowCost: 0, minutesRemaining: 0 };\n }\n\n // Use last 10 messages for velocity calculation\n const recent = messages.slice(-10);\n const firstTime = dayjs(recent[0].timestamp);\n const lastTime = dayjs(recent[recent.length - 1].timestamp);\n const durationMinutes = lastTime.diff(firstTime, \"minute\", true);\n\n if (durationMinutes <= 0) {\n return { dollarsPerMinute: 0, tokensPerMinute: 0, projectedWindowCost: 0, minutesRemaining: 0 };\n }\n\n let totalCost = 0;\n let totalTokens = 0;\n for (const msg of recent) {\n totalCost += calculateMessageCost(msg).totalCost;\n totalTokens += msg.usage.input_tokens + msg.usage.output_tokens;\n }\n\n const dollarsPerMinute = totalCost / durationMinutes;\n const tokensPerMinute = totalTokens / durationMinutes;\n const windowMinutes = BILLING_WINDOW_HOURS * 60;\n const projectedWindowCost = dollarsPerMinute * windowMinutes;\n\n // Time remaining in current billing window\n const windowStart = dayjs().subtract(BILLING_WINDOW_HOURS, \"hour\");\n const elapsed = dayjs().diff(windowStart, \"minute\", true);\n const minutesRemaining = Math.max(0, windowMinutes - elapsed);\n\n return { dollarsPerMinute, tokensPerMinute, projectedWindowCost, minutesRemaining };\n}\n\nexport function aggregateCosts(\n messages: ParsedMessage[],\n period: AggregationPeriod,\n): AggregatedCost[] {\n const groups = new Map<string, { messages: ParsedMessage[]; sessions: Set<string> }>();\n\n for (const msg of messages) {\n const key = getPeriodKey(msg.timestamp, period);\n const group = groups.get(key) ?? { messages: [], sessions: new Set() };\n group.messages.push(msg);\n group.sessions.add(msg.id.split(\"_\")[0] ?? msg.id);\n groups.set(key, group);\n }\n\n return Array.from(groups.entries()).map(([key, group]) => {\n let totalCost = 0;\n let totalInput = 0;\n let totalOutput = 0;\n\n for (const msg of group.messages) {\n totalCost += calculateMessageCost(msg).totalCost;\n totalInput += msg.usage.input_tokens;\n totalOutput += msg.usage.output_tokens;\n }\n\n return {\n period: key,\n periodLabel: formatPeriodLabel(key, period),\n totalCost,\n totalInputTokens: totalInput,\n totalOutputTokens: totalOutput,\n messageCount: group.messages.length,\n sessionCount: group.sessions.size,\n };\n });\n}\n\nfunction getPeriodKey(timestamp: string, period: AggregationPeriod): string {\n const d = dayjs(timestamp);\n switch (period) {\n case \"hour\":\n return d.format(\"YYYY-MM-DD-HH\");\n case \"day\":\n return d.format(\"YYYY-MM-DD\");\n case \"billing_window\":\n // 5-hour windows starting from midnight\n const hour = d.hour();\n const windowStart = Math.floor(hour / BILLING_WINDOW_HOURS) * BILLING_WINDOW_HOURS;\n return `${d.format(\"YYYY-MM-DD\")}-W${windowStart}`;\n case \"week\":\n return `${d.isoWeekYear()}-W${String(d.isoWeek()).padStart(2, \"0\")}`;\n case \"month\":\n return d.format(\"YYYY-MM\");\n case \"session\":\n default:\n return timestamp;\n }\n}\n\nfunction formatPeriodLabel(key: string, period: AggregationPeriod): string {\n switch (period) {\n case \"hour\":\n return dayjs(key, \"YYYY-MM-DD-HH\").format(\"MMM D, h A\");\n case \"day\":\n return dayjs(key).format(\"ddd, MMM D\");\n case \"week\":\n return `Week ${key.split(\"-W\")[1]}`;\n case \"month\":\n return dayjs(key).format(\"MMMM YYYY\");\n default:\n return key;\n }\n}\n\nexport function formatCost(cost: number): string {\n return `$${cost.toFixed(2)}`;\n}\n\nexport function formatTokens(tokens: number): string {\n if (tokens >= 1_000_000) return `${(tokens / 1_000_000).toFixed(1)}M`;\n if (tokens >= 1_000) return `${(tokens / 1_000).toFixed(1)}K`;\n return String(tokens);\n}\n","import React from \"react\";\nimport { Box, Text } from \"ink\";\nimport type { ContextOverhead } from \"../../types/config.js\";\n\ninterface ContextBarProps {\n used: number;\n total: number;\n overhead: ContextOverhead;\n}\n\nexport function ContextBar({ used, total, overhead }: ContextBarProps) {\n const barWidth = 30;\n const usedPct = total > 0 ? (used / total) * 100 : 0;\n const clampedPct = Math.max(0, Math.min(usedPct, 100));\n const filledCount = Math.round((clampedPct / 100) * barWidth);\n const emptyCount = barWidth - filledCount;\n\n const color = usedPct < 50 ? \"green\" : usedPct < 80 ? \"yellow\" : \"red\";\n\n const filled = \"\\u2588\".repeat(filledCount);\n const empty = \"\\u2591\".repeat(emptyCount);\n\n const formatK = (n: number) => `${Math.round(n / 1000)}K`;\n\n return (\n <Box flexDirection=\"column\" paddingX={1}>\n <Text>\n <Text>[</Text>\n <Text color={color}>{filled}</Text>\n <Text dimColor>{empty}</Text>\n <Text>]</Text>\n <Text> {usedPct.toFixed(0)}%</Text>\n <Text> | {formatK(used)} / {formatK(total)} tokens</Text>\n </Text>\n <Text dimColor>\n {\" \"}system({formatK(overhead.systemPrompt)}) + tools({formatK(overhead.builtInTools)}) + mcp(\n {formatK(overhead.mcpTools)}) + claude.md({formatK(overhead.claudeMd)})\n </Text>\n </Box>\n );\n}\n","import { readFileSync, existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport {\n CONTEXT_WINDOW_SIZE,\n SYSTEM_PROMPT_TOKENS,\n BUILT_IN_TOOLS_TOKENS,\n AUTOCOMPACT_BUFFER_TOKENS,\n MCP_TOKENS_PER_TOOL,\n} from \"./config.js\";\nimport type { ContextOverhead, McpServerInfo } from \"../types/config.js\";\n\nconst SUPPORTED_EXTENSIONS = new Set([\".md\", \".ts\", \".js\", \".json\", \".yaml\", \".yml\", \".py\", \".txt\"]);\n\n/**\n * Fast local heuristic: tokens ~= characters / 3.5\n */\nexport function estimateTokens(text: string): number {\n return Math.ceil(text.length / 3.5);\n}\n\n/**\n * Count tokens in a file using the heuristic\n */\nexport function countFileTokens(filePath: string): number {\n try {\n const content = readFileSync(filePath, \"utf-8\");\n return estimateTokens(content);\n } catch {\n return 0;\n }\n}\n\nexport interface ClaudeMdSection {\n title: string;\n content: string;\n tokens: number;\n lineStart: number;\n lineEnd: number;\n}\n\n/**\n * Parse CLAUDE.md into sections by ## headers\n */\nexport function parseClaudeMdSections(content: string): ClaudeMdSection[] {\n const lines = content.split(\"\\n\");\n const sections: ClaudeMdSection[] = [];\n let currentTitle = \"Preamble\";\n let currentContent: string[] = [];\n let currentLineStart = 1;\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n if (line.startsWith(\"## \")) {\n // Save previous section\n if (currentContent.length > 0 || currentTitle !== \"Preamble\") {\n const sectionContent = currentContent.join(\"\\n\");\n sections.push({\n title: currentTitle,\n content: sectionContent,\n tokens: estimateTokens(sectionContent),\n lineStart: currentLineStart,\n lineEnd: i,\n });\n }\n currentTitle = line.replace(/^##\\s*/, \"\");\n currentContent = [];\n currentLineStart = i + 1;\n } else {\n currentContent.push(line);\n }\n }\n\n // Save last section\n if (currentContent.length > 0) {\n const sectionContent = currentContent.join(\"\\n\");\n sections.push({\n title: currentTitle,\n content: sectionContent,\n tokens: estimateTokens(sectionContent),\n lineStart: currentLineStart,\n lineEnd: lines.length,\n });\n }\n\n return sections;\n}\n\n/**\n * Analyze CLAUDE.md token overhead\n */\nexport function analyzeClaudeMd(filePath?: string): { totalTokens: number; sections: ClaudeMdSection[]; heavySections: ClaudeMdSection[] } {\n const paths = filePath\n ? [filePath]\n : [\n join(process.cwd(), \"CLAUDE.md\"),\n join(process.cwd(), \".claude\", \"CLAUDE.md\"),\n ];\n\n for (const p of paths) {\n if (existsSync(p)) {\n const content = readFileSync(p, \"utf-8\");\n const sections = parseClaudeMdSections(content);\n const totalTokens = sections.reduce((sum, s) => sum + s.tokens, 0);\n const heavySections = sections.filter((s) => s.tokens > 500);\n return { totalTokens, sections, heavySections };\n }\n }\n\n return { totalTokens: 0, sections: [], heavySections: [] };\n}\n\n/**\n * Parse MCP server configurations and estimate token cost\n */\nexport function analyzeMcpServers(): McpServerInfo[] {\n const servers: McpServerInfo[] = [];\n const paths = [\n join(process.cwd(), \".mcp.json\"),\n join(homedir(), \".claude.json\"),\n ];\n\n for (const configPath of paths) {\n if (!existsSync(configPath)) continue;\n try {\n const raw = JSON.parse(readFileSync(configPath, \"utf-8\"));\n const mcpServers = raw.mcpServers ?? raw.mcp_servers ?? {};\n for (const [name, config] of Object.entries(mcpServers)) {\n const cfg = config as Record<string, unknown>;\n // Estimate tools: if tools array is present, use its length; otherwise default to 5\n const tools = Array.isArray(cfg.tools) ? cfg.tools : [];\n const toolCount = tools.length || 5;\n const estimatedTokens = toolCount * MCP_TOKENS_PER_TOOL;\n servers.push({\n name,\n toolCount,\n estimatedTokens,\n isHeavy: toolCount > 10,\n });\n }\n } catch {\n continue;\n }\n }\n\n return servers;\n}\n\n/**\n * Estimate total context overhead (ghost tokens)\n */\nexport function estimateContextOverhead(claudeMdPath?: string): ContextOverhead {\n const claudeMd = analyzeClaudeMd(claudeMdPath);\n const mcpServers = analyzeMcpServers();\n const mcpToolTokens = mcpServers.reduce((sum, s) => sum + s.estimatedTokens, 0);\n\n const totalOverhead =\n SYSTEM_PROMPT_TOKENS +\n BUILT_IN_TOOLS_TOKENS +\n mcpToolTokens +\n claudeMd.totalTokens +\n AUTOCOMPACT_BUFFER_TOKENS;\n\n const effectiveWindow = CONTEXT_WINDOW_SIZE - totalOverhead;\n const percentUsable = (effectiveWindow / CONTEXT_WINDOW_SIZE) * 100;\n\n return {\n systemPrompt: SYSTEM_PROMPT_TOKENS,\n builtInTools: BUILT_IN_TOOLS_TOKENS,\n claudeMd: claudeMd.totalTokens,\n mcpTools: mcpToolTokens,\n autocompactBuffer: AUTOCOMPACT_BUFFER_TOKENS,\n totalOverhead,\n effectiveWindow,\n percentUsable,\n };\n}\n","import React from \"react\";\nimport { render } from \"ink\";\nimport { Command } from \"commander\";\nimport { glob } from \"glob\";\nimport { estimateTaskCost } from \"../../core/estimator.js\";\nimport { EstimateCard } from \"../ui/EstimateCard.js\";\n\nexport function registerEstimateCommand(program: Command): void {\n program\n .command(\"estimate <task>\")\n .description(\"Pre-flight cost estimation\")\n .option(\"-m, --model <model>\", \"Model to estimate for\", \"sonnet\")\n .option(\"-f, --files <glob>\", \"Specific files that will be touched\")\n .option(\"--compare\", \"Show Sonnet vs Opus vs Haiku comparison\")\n .option(\"--json\", \"Output as JSON\")\n .action(async (task: string, opts) => {\n const files: string[] = [];\n if (opts.files) {\n const matched = await glob(opts.files, { absolute: true });\n files.push(...matched);\n }\n\n if (opts.compare) {\n const models = [\"sonnet\", \"opus\", \"haiku\"] as const;\n for (const model of models) {\n const estimate = await estimateTaskCost(task, { model, files, cwd: process.cwd() });\n if (opts.json) {\n console.log(JSON.stringify(estimate, null, 2));\n } else {\n const { waitUntilExit } = render(\n React.createElement(EstimateCard, { task, estimate }),\n );\n await waitUntilExit();\n }\n }\n return;\n }\n\n const estimate = await estimateTaskCost(task, {\n model: opts.model,\n files,\n cwd: process.cwd(),\n });\n\n if (opts.json) {\n console.log(JSON.stringify(estimate, null, 2));\n return;\n }\n\n const { waitUntilExit } = render(\n React.createElement(EstimateCard, { task, estimate }),\n );\n await waitUntilExit();\n });\n}\n","import { existsSync, readFileSync } from \"node:fs\";\nimport { glob } from \"glob\";\nimport { estimateTokens, countFileTokens, estimateContextOverhead } from \"./tokenCounter.js\";\nimport { resolveModelPricing, formatCost } from \"./costCalculator.js\";\nimport { BILLING_WINDOW_HOURS } from \"./config.js\";\nimport type { CostEstimate, EstimateOptions } from \"../types/config.js\";\n\ntype TaskComplexity = \"simple\" | \"medium\" | \"complex\";\n\ninterface ComplexityProfile {\n turns: { low: number; expected: number; high: number };\n outputTokensPerTurn: number;\n}\n\nconst COMPLEXITY_PROFILES: Record<TaskComplexity, ComplexityProfile> = {\n simple: { turns: { low: 2, expected: 3, high: 5 }, outputTokensPerTurn: 1000 },\n medium: { turns: { low: 5, expected: 10, high: 15 }, outputTokensPerTurn: 2000 },\n complex: { turns: { low: 15, expected: 25, high: 40 }, outputTokensPerTurn: 2500 },\n};\n\nconst SIMPLE_KEYWORDS = [\"typo\", \"rename\", \"fix typo\", \"update version\", \"change name\", \"remove unused\", \"delete\"];\nconst COMPLEX_KEYWORDS = [\"refactor\", \"rewrite\", \"new module\", \"implement\", \"build\", \"create\", \"migrate\", \"redesign\", \"overhaul\", \"architecture\"];\n\nfunction detectComplexity(taskDescription: string): TaskComplexity {\n const lower = taskDescription.toLowerCase();\n if (SIMPLE_KEYWORDS.some((k) => lower.includes(k))) return \"simple\";\n if (COMPLEX_KEYWORDS.some((k) => lower.includes(k))) return \"complex\";\n return \"medium\";\n}\n\nexport async function estimateTaskCost(\n taskDescription: string,\n options: Partial<EstimateOptions> = {},\n): Promise<CostEstimate> {\n const model = options.model ?? \"sonnet\";\n const cwd = options.cwd ?? process.cwd();\n const pricing = resolveModelPricing(model);\n const MILLION = 1_000_000;\n\n // Calculate context overhead\n const overhead = estimateContextOverhead();\n\n // Count file tokens\n let fileTokens = 0;\n let fileList = options.files ?? [];\n\n if (fileList.length === 0) {\n // Try to auto-detect from git status\n try {\n const { execSync } = await import(\"node:child_process\");\n const output = execSync(\"git diff --name-only HEAD 2>/dev/null || git ls-files -m 2>/dev/null\", {\n cwd,\n encoding: \"utf-8\",\n });\n fileList = output\n .split(\"\\n\")\n .filter(Boolean)\n .map((f) => `${cwd}/${f}`);\n } catch {\n // No git, no files\n }\n }\n\n for (const filePattern of fileList) {\n const matched = await glob(filePattern, { cwd, absolute: true });\n for (const f of matched) {\n fileTokens += countFileTokens(f);\n }\n }\n\n // Detect complexity and get profile\n const complexity = detectComplexity(taskDescription);\n const profile = COMPLEXITY_PROFILES[complexity];\n\n // Calculate per-turn costs\n const contextPerTurn = overhead.totalOverhead + fileTokens;\n const CACHE_HIT_RATE = 0.9;\n\n function estimateCostForTurns(turns: number): number {\n let totalCost = 0;\n for (let turn = 1; turn <= turns; turn++) {\n const conversationGrowth = (turn - 1) * profile.outputTokensPerTurn;\n const inputTokens = contextPerTurn + conversationGrowth;\n\n let effectiveInputCost: number;\n if (turn <= 2) {\n // First turns: no cache\n effectiveInputCost = (inputTokens * pricing.input) / MILLION;\n } else {\n // After turn 2: 90% cache hit\n const cachedTokens = inputTokens * CACHE_HIT_RATE;\n const uncachedTokens = inputTokens * (1 - CACHE_HIT_RATE);\n effectiveInputCost =\n (cachedTokens * pricing.cacheRead) / MILLION +\n (uncachedTokens * pricing.input) / MILLION;\n }\n\n const outputCost = (profile.outputTokensPerTurn * pricing.output) / MILLION;\n totalCost += effectiveInputCost + outputCost;\n }\n return totalCost;\n }\n\n const lowCost = estimateCostForTurns(profile.turns.low);\n const expectedCost = estimateCostForTurns(profile.turns.expected);\n const highCost = estimateCostForTurns(profile.turns.high);\n\n // Estimate total tokens for expected case\n const expectedInputTokens = contextPerTurn * profile.turns.expected;\n const expectedOutputTokens = profile.outputTokensPerTurn * profile.turns.expected;\n const expectedCachedTokens = expectedInputTokens * CACHE_HIT_RATE;\n\n // Window usage\n const windowMinutes = BILLING_WINDOW_HOURS * 60;\n const percentOfWindow = (expectedCost / (expectedCost * 3)) * 100; // rough estimate\n\n // Recommendations\n const recommendations: string[] = [];\n if (model !== \"sonnet\") {\n const sonnetPricing = resolveModelPricing(\"sonnet\");\n const sonnetCost = estimateCostForTurns(profile.turns.expected);\n // Recalculate with sonnet pricing isn't straightforward here,\n // so we do a ratio estimate\n const ratio = pricing.output / resolveModelPricing(\"sonnet\").output;\n if (ratio > 2) {\n recommendations.push(\n `Consider Sonnet to save ~${formatCost(expectedCost - expectedCost / ratio)} (${ratio.toFixed(0)}x cheaper)`,\n );\n }\n }\n\n if (model === \"sonnet\") {\n const opusPricing = resolveModelPricing(\"opus\");\n const ratio = opusPricing.output / pricing.output;\n recommendations.push(`Using Opus would cost ~${formatCost(expectedCost * ratio)} (${ratio.toFixed(0)}x more)`);\n }\n\n if (overhead.percentUsable < 60) {\n recommendations.push(`High ghost token overhead (${(100 - overhead.percentUsable).toFixed(0)}%). Run 'kerf-cli audit' to optimize.`);\n }\n\n if (fileTokens > 50000) {\n recommendations.push(`Large file context (${(fileTokens / 1000).toFixed(0)}K tokens). Consider narrowing scope.`);\n }\n\n return {\n model,\n estimatedTurns: profile.turns,\n estimatedTokens: {\n input: Math.round(expectedInputTokens),\n output: Math.round(expectedOutputTokens),\n cached: Math.round(expectedCachedTokens),\n },\n estimatedCost: {\n low: formatCost(lowCost),\n expected: formatCost(expectedCost),\n high: formatCost(highCost),\n },\n contextOverhead: overhead.totalOverhead,\n fileTokens,\n percentOfWindow: Math.round(percentOfWindow),\n recommendations,\n };\n}\n","import React from \"react\";\nimport { Box, Text } from \"ink\";\nimport type { CostEstimate } from \"../../types/config.js\";\n\ninterface EstimateCardProps {\n task: string;\n estimate: CostEstimate;\n}\n\nexport function EstimateCard({ task, estimate }: EstimateCardProps) {\n const formatK = (n: number) => `${(n / 1000).toFixed(1)}K`;\n\n return (\n <Box flexDirection=\"column\" borderStyle=\"round\" borderColor=\"cyan\" paddingX={2} paddingY={1}>\n <Text bold color=\"cyan\">\n kerf-cli estimate: '{task}'\n </Text>\n <Text> </Text>\n <Text>\n Model: <Text bold>{estimate.model}</Text>\n </Text>\n <Text>\n Estimated turns: {estimate.estimatedTurns.low}-{estimate.estimatedTurns.high} (expected:{\" \"}\n {estimate.estimatedTurns.expected})\n </Text>\n <Text>\n Files: {formatK(estimate.fileTokens)} tokens\n </Text>\n <Text>\n Context overhead: {formatK(estimate.contextOverhead)} tokens (ghost tokens)\n </Text>\n <Text> </Text>\n <Text bold>Estimated Cost:</Text>\n <Text>\n {\" \"}Low: <Text color=\"green\">{estimate.estimatedCost.low}</Text>\n </Text>\n <Text>\n {\" \"}Expected: <Text color=\"yellow\">{estimate.estimatedCost.expected}</Text>\n </Text>\n <Text>\n {\" \"}High: <Text color=\"red\">{estimate.estimatedCost.high}</Text>\n </Text>\n <Text> </Text>\n <Text>Window Usage: ~{estimate.percentOfWindow}% of 5-hour window</Text>\n {estimate.recommendations.map((rec, i) => (\n <Text key={i} color=\"cyan\">\n {\" -> \"}{rec}\n </Text>\n ))}\n </Box>\n );\n}\n","import { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport { BudgetManager } from \"../../core/budgetManager.js\";\nimport { formatCost } from \"../../core/costCalculator.js\";\n\nexport function registerBudgetCommand(program: Command): void {\n const budget = program.command(\"budget\").description(\"Per-project budget management\");\n\n budget\n .command(\"set <amount>\")\n .description(\"Set budget for current project\")\n .option(\"-p, --period <period>\", \"Budget period (daily|weekly|monthly)\", \"weekly\")\n .option(\"--project <path>\", \"Project path\")\n .action((amount: string, opts) => {\n const manager = new BudgetManager();\n const projectPath = opts.project || process.cwd();\n const amountNum = parseFloat(amount);\n\n if (isNaN(amountNum) || amountNum <= 0) {\n console.log(chalk.red(\"Budget amount must be a positive number.\"));\n process.exit(1);\n }\n\n manager.setBudget(projectPath, amountNum, opts.period);\n console.log(\n chalk.green(`Budget set: ${formatCost(amountNum)}/${opts.period} for ${projectPath}`),\n );\n manager.close();\n });\n\n budget\n .command(\"show\")\n .description(\"Show current project budget\")\n .option(\"--project <path>\", \"Project path\")\n .option(\"--json\", \"Output as JSON\")\n .action((opts) => {\n const manager = new BudgetManager();\n const projectPath = opts.project || process.cwd();\n const status = manager.checkBudget(projectPath);\n\n if (!status) {\n console.log(\"No budget set for this project. Use 'kerf-cli budget set <amount>' to set one.\");\n manager.close();\n return;\n }\n\n if (opts.json) {\n console.log(JSON.stringify(status, null, 2));\n manager.close();\n return;\n }\n\n const pct = status.percentUsed;\n const barWidth = 20;\n const filled = Math.round((Math.min(pct, 100) / 100) * barWidth);\n const empty = barWidth - filled;\n const color = pct < 50 ? \"green\" : pct < 80 ? \"yellow\" : \"red\";\n const barColor = color === \"green\" ? chalk.green : color === \"yellow\" ? chalk.yellow : chalk.red;\n\n console.log(chalk.bold.cyan(\"\\n kerf-cli budget\\n\"));\n console.log(` Period: ${status.period} (${status.periodStart.slice(0, 10)} to ${status.periodEnd.slice(0, 10)})`);\n console.log(` Budget: ${formatCost(status.budget)}`);\n console.log(` Spent: ${barColor(formatCost(status.spent))}`);\n console.log(` ${barColor(\"[\" + \"\\u2588\".repeat(filled) + \"\\u2591\".repeat(empty) + \"]\")} ${pct.toFixed(1)}%`);\n\n if (status.isOverBudget) {\n console.log(chalk.red.bold(`\\n OVER BUDGET by ${formatCost(status.spent - status.budget)}`));\n }\n console.log();\n\n manager.close();\n });\n\n budget\n .command(\"list\")\n .description(\"List all project budgets\")\n .action(() => {\n const manager = new BudgetManager();\n const projects = manager.listProjects();\n\n if (projects.length === 0) {\n console.log(\"No projects with budgets. Use 'kerf-cli budget set <amount>' to set one.\");\n manager.close();\n return;\n }\n\n console.log(chalk.bold.cyan(\"\\n kerf-cli budget list\\n\"));\n for (const p of projects) {\n const budgetStr = p.budget ? `${formatCost(p.budget)}/${p.period}` : \"no budget\";\n const spentStr = p.spent > 0 ? ` (spent: ${formatCost(p.spent)})` : \"\";\n console.log(` ${chalk.bold(p.name)} — ${budgetStr}${spentStr}`);\n console.log(chalk.dim(` ${p.path}`));\n }\n console.log();\n\n manager.close();\n });\n\n budget\n .command(\"remove\")\n .description(\"Remove budget for current project\")\n .option(\"--project <path>\", \"Project path\")\n .action((opts) => {\n const manager = new BudgetManager();\n const projectPath = opts.project || process.cwd();\n const removed = manager.removeBudget(projectPath);\n\n if (removed) {\n console.log(chalk.green(\"Budget removed.\"));\n } else {\n console.log(\"No budget found for this project.\");\n }\n\n manager.close();\n });\n}\n","import dayjs from \"dayjs\";\nimport isoWeek from \"dayjs/plugin/isoWeek.js\";\nimport { basename } from \"node:path\";\nimport { initDatabase } from \"../db/schema.js\";\nimport { runMigrations } from \"../db/migrations.js\";\nimport type { BudgetStatus } from \"../types/config.js\";\nimport type Database from \"better-sqlite3\";\n\ndayjs.extend(isoWeek);\n\ntype BudgetPeriod = \"daily\" | \"weekly\" | \"monthly\";\n\nexport class BudgetManager {\n private db: Database.Database;\n\n constructor(dbPath?: string) {\n this.db = initDatabase(dbPath);\n runMigrations(this.db);\n }\n\n private getOrCreateProject(projectPath: string): number {\n const name = basename(projectPath) || projectPath;\n const existing = this.db\n .prepare(\"SELECT id FROM projects WHERE path = ?\")\n .get(projectPath) as { id: number } | undefined;\n\n if (existing) return existing.id;\n\n const result = this.db\n .prepare(\"INSERT INTO projects (name, path) VALUES (?, ?)\")\n .run(name, projectPath);\n\n return Number(result.lastInsertRowid);\n }\n\n setBudget(projectPath: string, amount: number, period: BudgetPeriod): void {\n const projectId = this.getOrCreateProject(projectPath);\n this.db\n .prepare(\n `INSERT INTO budgets (project_id, amount_usd, period)\n VALUES (?, ?, ?)\n ON CONFLICT(project_id, period)\n DO UPDATE SET amount_usd = excluded.amount_usd`,\n )\n .run(projectId, amount, period);\n }\n\n getBudget(projectPath: string): { amount: number; period: BudgetPeriod } | null {\n const project = this.db\n .prepare(\"SELECT id FROM projects WHERE path = ?\")\n .get(projectPath) as { id: number } | undefined;\n\n if (!project) return null;\n\n const budget = this.db\n .prepare(\"SELECT amount_usd, period FROM budgets WHERE project_id = ? ORDER BY created_at DESC LIMIT 1\")\n .get(project.id) as { amount_usd: number; period: BudgetPeriod } | undefined;\n\n if (!budget) return null;\n return { amount: budget.amount_usd, period: budget.period };\n }\n\n recordUsage(\n projectPath: string,\n sessionId: string,\n tokensIn: number,\n tokensOut: number,\n costUsd: number,\n timestamp: string,\n ): void {\n const projectId = this.getOrCreateProject(projectPath);\n this.db\n .prepare(\n `INSERT OR IGNORE INTO usage_snapshots (project_id, session_id, tokens_in, tokens_out, cost_usd, timestamp)\n VALUES (?, ?, ?, ?, ?, ?)`,\n )\n .run(projectId, sessionId, tokensIn, tokensOut, costUsd, timestamp);\n }\n\n getUsage(projectPath: string, period: BudgetPeriod): number {\n const project = this.db\n .prepare(\"SELECT id FROM projects WHERE path = ?\")\n .get(projectPath) as { id: number } | undefined;\n\n if (!project) return 0;\n\n const start = getPeriodStart(period);\n const result = this.db\n .prepare(\n `SELECT COALESCE(SUM(cost_usd), 0) as total\n FROM usage_snapshots\n WHERE project_id = ? AND timestamp >= ?`,\n )\n .get(project.id, start.toISOString()) as { total: number };\n\n return result.total;\n }\n\n checkBudget(projectPath: string): BudgetStatus | null {\n const budgetConfig = this.getBudget(projectPath);\n if (!budgetConfig) return null;\n\n const spent = this.getUsage(projectPath, budgetConfig.period);\n const remaining = Math.max(0, budgetConfig.amount - spent);\n const percentUsed = budgetConfig.amount > 0 ? (spent / budgetConfig.amount) * 100 : 0;\n\n const periodStart = getPeriodStart(budgetConfig.period);\n const periodEnd = getPeriodEnd(budgetConfig.period);\n\n return {\n budget: budgetConfig.amount,\n spent,\n remaining,\n percentUsed,\n isOverBudget: spent > budgetConfig.amount,\n period: budgetConfig.period,\n periodStart: periodStart.toISOString(),\n periodEnd: periodEnd.toISOString(),\n };\n }\n\n listProjects(): Array<{ name: string; path: string; budget: number | null; period: string | null; spent: number }> {\n const rows = this.db\n .prepare(\n `SELECT p.name, p.path, b.amount_usd, b.period\n FROM projects p\n LEFT JOIN budgets b ON b.project_id = p.id\n ORDER BY p.name`,\n )\n .all() as Array<{ name: string; path: string; amount_usd: number | null; period: string | null }>;\n\n return rows.map((row) => ({\n name: row.name,\n path: row.path,\n budget: row.amount_usd,\n period: row.period,\n spent: row.period ? this.getUsage(row.path, row.period as BudgetPeriod) : 0,\n }));\n }\n\n removeBudget(projectPath: string): boolean {\n const project = this.db\n .prepare(\"SELECT id FROM projects WHERE path = ?\")\n .get(projectPath) as { id: number } | undefined;\n\n if (!project) return false;\n\n const result = this.db.prepare(\"DELETE FROM budgets WHERE project_id = ?\").run(project.id);\n return result.changes > 0;\n }\n\n close(): void {\n this.db.close();\n }\n}\n\nfunction getPeriodStart(period: BudgetPeriod): dayjs.Dayjs {\n switch (period) {\n case \"daily\":\n return dayjs().startOf(\"day\");\n case \"weekly\":\n return dayjs().startOf(\"isoWeek\" as dayjs.OpUnitType);\n case \"monthly\":\n return dayjs().startOf(\"month\");\n }\n}\n\nfunction getPeriodEnd(period: BudgetPeriod): dayjs.Dayjs {\n switch (period) {\n case \"daily\":\n return dayjs().endOf(\"day\");\n case \"weekly\":\n return dayjs().endOf(\"isoWeek\" as dayjs.OpUnitType);\n case \"monthly\":\n return dayjs().endOf(\"month\");\n }\n}\n","import Database from \"better-sqlite3\";\nimport { mkdirSync, existsSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport { KERF_DB_PATH } from \"../core/config.js\";\n\nexport function initDatabase(dbPath?: string): Database.Database {\n const path = dbPath ?? KERF_DB_PATH;\n const dir = dirname(path);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n\n const db = new Database(path);\n db.pragma(\"journal_mode = WAL\");\n db.pragma(\"foreign_keys = ON\");\n\n createTables(db);\n return db;\n}\n\nfunction createTables(db: Database.Database): void {\n db.exec(`\n CREATE TABLE IF NOT EXISTS projects (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n name TEXT NOT NULL,\n path TEXT NOT NULL UNIQUE,\n created_at TEXT NOT NULL DEFAULT (datetime('now'))\n );\n\n CREATE TABLE IF NOT EXISTS budgets (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n project_id INTEGER NOT NULL REFERENCES projects(id) ON DELETE CASCADE,\n amount_usd REAL NOT NULL,\n period TEXT NOT NULL CHECK (period IN ('daily', 'weekly', 'monthly')),\n created_at TEXT NOT NULL DEFAULT (datetime('now')),\n UNIQUE(project_id, period)\n );\n\n CREATE TABLE IF NOT EXISTS usage_snapshots (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n project_id INTEGER NOT NULL REFERENCES projects(id) ON DELETE CASCADE,\n session_id TEXT NOT NULL,\n tokens_in INTEGER NOT NULL DEFAULT 0,\n tokens_out INTEGER NOT NULL DEFAULT 0,\n cost_usd REAL NOT NULL DEFAULT 0,\n timestamp TEXT NOT NULL,\n UNIQUE(project_id, session_id, timestamp)\n );\n\n CREATE INDEX IF NOT EXISTS idx_usage_project_time\n ON usage_snapshots(project_id, timestamp);\n `);\n}\n","import type Database from \"better-sqlite3\";\n\ninterface Migration {\n version: number;\n description: string;\n up: (db: Database.Database) => void;\n}\n\nconst migrations: Migration[] = [\n {\n version: 1,\n description: \"Initial schema\",\n up(_db) {\n // Schema is created in schema.ts on init — this is a placeholder for future migrations\n },\n },\n];\n\nexport function runMigrations(db: Database.Database): void {\n db.exec(`\n CREATE TABLE IF NOT EXISTS schema_migrations (\n version INTEGER PRIMARY KEY,\n applied_at TEXT NOT NULL DEFAULT (datetime('now'))\n )\n `);\n\n const applied = new Set(\n db\n .prepare(\"SELECT version FROM schema_migrations\")\n .all()\n .map((row: any) => row.version as number),\n );\n\n for (const migration of migrations) {\n if (!applied.has(migration.version)) {\n migration.up(db);\n db.prepare(\"INSERT INTO schema_migrations (version) VALUES (?)\").run(migration.version);\n }\n }\n}\n","import { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport { runFullAudit } from \"../../audit/recommendations.js\";\nimport { CONTEXT_WINDOW_SIZE } from \"../../core/config.js\";\n\nexport function registerAuditCommand(program: Command): void {\n program\n .command(\"audit\")\n .description(\"Ghost token & CLAUDE.md audit\")\n .option(\"--fix\", \"Auto-apply safe fixes\")\n .option(\"--claude-md-only\", \"Only audit CLAUDE.md\")\n .option(\"--mcp-only\", \"Only audit MCP servers\")\n .option(\"--json\", \"Output as JSON\")\n .action((opts) => {\n const result = runFullAudit();\n\n if (opts.json) {\n console.log(JSON.stringify(result, null, 2));\n return;\n }\n\n const gradeColor =\n result.grade === \"A\" ? chalk.green :\n result.grade === \"B\" ? chalk.yellow :\n chalk.red;\n\n console.log(chalk.bold.cyan(\"\\n kerf-cli audit report\\n\"));\n console.log(\n ` Context Window Health: ${gradeColor.bold(result.grade)} (${result.contextOverhead.percentUsable.toFixed(0)}% usable)\\n`,\n );\n\n if (!opts.mcpOnly) {\n console.log(chalk.bold(\" Ghost Token Breakdown:\"));\n const oh = result.contextOverhead;\n const fmt = (label: string, tokens: number) => {\n const pct = ((tokens / CONTEXT_WINDOW_SIZE) * 100).toFixed(1);\n return ` ${label.padEnd(22)} ${tokens.toLocaleString().padStart(6)} tokens (${pct}%)`;\n };\n console.log(fmt(\"System prompt:\", oh.systemPrompt));\n console.log(fmt(\"Built-in tools:\", oh.builtInTools));\n console.log(fmt(`MCP tools (${result.mcpServers.length} srv):`, oh.mcpTools));\n console.log(fmt(\"CLAUDE.md:\", oh.claudeMd));\n console.log(fmt(\"Autocompact buffer:\", oh.autocompactBuffer));\n console.log(\" \" + \"-\".repeat(40));\n console.log(fmt(\"Total overhead:\", oh.totalOverhead));\n console.log(\n ` ${\"Effective window:\".padEnd(22)} ${oh.effectiveWindow.toLocaleString().padStart(6)} tokens (${oh.percentUsable.toFixed(1)}%)\\n`,\n );\n }\n\n if (!opts.mcpOnly && result.claudeMdAnalysis) {\n const cma = result.claudeMdAnalysis;\n console.log(chalk.bold(\" CLAUDE.md Analysis:\"));\n console.log(\n ` Lines: ${cma.totalLines}${cma.isOverLineLimit ? chalk.yellow(\" (over 200 limit)\") : \"\"}`,\n );\n console.log(` Tokens: ${cma.totalTokens.toLocaleString()}`);\n console.log(\n ` Critical rules in dead zone: ${cma.criticalRulesInDeadZone > 0 ? chalk.red(String(cma.criticalRulesInDeadZone)) : \"0\"}`,\n );\n console.log();\n }\n\n if (result.recommendations.length > 0) {\n console.log(chalk.bold(\" Recommendations:\"));\n result.recommendations.forEach((rec, i) => {\n const priorityColor =\n rec.priority === \"high\" ? chalk.red :\n rec.priority === \"medium\" ? chalk.yellow :\n chalk.dim;\n console.log(\n ` ${i + 1}. ${priorityColor(`[${rec.priority.toUpperCase()}]`)} ${rec.action}`,\n );\n console.log(chalk.dim(` Impact: ${rec.impact}`));\n });\n }\n console.log();\n\n if (opts.fix) {\n console.log(chalk.yellow(\" --fix: Auto-fix is not yet implemented. Coming in v0.2.0.\\n\"));\n }\n });\n}\n","import { estimateContextOverhead, analyzeMcpServers } from \"../core/tokenCounter.js\";\nimport { CONTEXT_WINDOW_SIZE } from \"../core/config.js\";\nimport type { ContextOverhead, McpServerInfo } from \"../types/config.js\";\n\nexport type ContextGrade = \"A\" | \"B\" | \"C\" | \"D\";\n\nexport interface GhostTokenReport {\n grade: ContextGrade;\n overhead: ContextOverhead;\n mcpServers: McpServerInfo[];\n percentUsable: number;\n breakdown: Array<{ label: string; tokens: number; percent: number }>;\n}\n\nexport function calculateGrade(percentUsable: number): ContextGrade {\n if (percentUsable >= 70) return \"A\";\n if (percentUsable >= 50) return \"B\";\n if (percentUsable >= 30) return \"C\";\n return \"D\";\n}\n\nexport function analyzeGhostTokens(claudeMdPath?: string): GhostTokenReport {\n const overhead = estimateContextOverhead(claudeMdPath);\n const mcpServers = analyzeMcpServers();\n const grade = calculateGrade(overhead.percentUsable);\n\n const breakdown = [\n { label: \"System prompt\", tokens: overhead.systemPrompt, percent: (overhead.systemPrompt / CONTEXT_WINDOW_SIZE) * 100 },\n { label: \"Built-in tools\", tokens: overhead.builtInTools, percent: (overhead.builtInTools / CONTEXT_WINDOW_SIZE) * 100 },\n { label: `MCP tools (${mcpServers.length} srv)`, tokens: overhead.mcpTools, percent: (overhead.mcpTools / CONTEXT_WINDOW_SIZE) * 100 },\n { label: \"CLAUDE.md\", tokens: overhead.claudeMd, percent: (overhead.claudeMd / CONTEXT_WINDOW_SIZE) * 100 },\n { label: \"Autocompact buffer\", tokens: overhead.autocompactBuffer, percent: (overhead.autocompactBuffer / CONTEXT_WINDOW_SIZE) * 100 },\n ];\n\n return {\n grade,\n overhead,\n mcpServers,\n percentUsable: overhead.percentUsable,\n breakdown,\n };\n}\n","import { readFileSync, existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { estimateTokens, parseClaudeMdSections } from \"../core/tokenCounter.js\";\nimport type { ClaudeMdAnalysis, ClaudeMdSection } from \"../types/config.js\";\n\nconst CRITICAL_RULE_PATTERN = /\\b(NEVER|ALWAYS|MUST|IMPORTANT|CRITICAL)\\b/i;\nconst SKILL_CANDIDATES = /\\b(review|deploy|release|migration|template|boilerplate|scaffold)\\b/i;\nconst LINE_LIMIT = 200;\n\ntype AttentionZone = \"high-start\" | \"low-middle\" | \"high-end\";\n\nfunction getAttentionZone(position: number, total: number): AttentionZone {\n const pct = total > 0 ? position / total : 0;\n if (pct <= 0.3) return \"high-start\";\n if (pct >= 0.7) return \"high-end\";\n return \"low-middle\";\n}\n\nexport function lintClaudeMd(filePath?: string): ClaudeMdAnalysis | null {\n const paths = filePath\n ? [filePath]\n : [\n join(process.cwd(), \"CLAUDE.md\"),\n join(process.cwd(), \".claude\", \"CLAUDE.md\"),\n ];\n\n let resolvedPath: string | null = null;\n for (const p of paths) {\n if (existsSync(p)) {\n resolvedPath = p;\n break;\n }\n }\n\n if (!resolvedPath) return null;\n\n const content = readFileSync(resolvedPath, \"utf-8\");\n const lines = content.split(\"\\n\");\n const totalLines = lines.length;\n const rawSections = parseClaudeMdSections(content);\n const totalTokens = rawSections.reduce((sum, s) => sum + s.tokens, 0);\n\n let criticalRulesInDeadZone = 0;\n const sectionsToSkill: string[] = [];\n\n const sections: ClaudeMdSection[] = rawSections.map((s) => {\n const midpoint = (s.lineStart + s.lineEnd) / 2;\n const attentionZone = getAttentionZone(midpoint, totalLines);\n const hasCriticalRules = CRITICAL_RULE_PATTERN.test(s.content);\n\n if (hasCriticalRules && attentionZone === \"low-middle\") {\n criticalRulesInDeadZone++;\n }\n\n if (SKILL_CANDIDATES.test(s.title) || SKILL_CANDIDATES.test(s.content)) {\n sectionsToSkill.push(s.title);\n }\n\n return {\n title: s.title,\n content: s.content,\n tokens: s.tokens,\n lineStart: s.lineStart,\n lineEnd: s.lineEnd,\n hasCriticalRules,\n attentionZone,\n };\n });\n\n // Generate suggested reordering: critical rules first, then normal, then verbose\n const critical = sections.filter((s) => s.hasCriticalRules);\n const normal = sections.filter((s) => !s.hasCriticalRules && s.tokens <= 500);\n const heavy = sections.filter((s) => !s.hasCriticalRules && s.tokens > 500);\n const suggestedReorder = [...critical, ...normal, ...heavy].map((s) => s.title);\n\n return {\n totalLines,\n totalTokens,\n sections,\n criticalRulesInDeadZone,\n isOverLineLimit: totalLines > LINE_LIMIT,\n suggestedReorder,\n };\n}\n","import { readFileSync, existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { MCP_TOKENS_PER_TOOL } from \"../core/config.js\";\nimport type { McpServerInfo } from \"../types/config.js\";\n\nexport interface McpAnalysis {\n servers: McpServerInfo[];\n totalTools: number;\n totalTokens: number;\n heavyServers: McpServerInfo[];\n hasToolSearch: boolean;\n effectiveTokens: number;\n recommendations: string[];\n}\n\nconst CLI_ALTERNATIVES: Record<string, string> = {\n playwright: \"Consider using the built-in Bash tool with playwright CLI instead\",\n puppeteer: \"Consider using the built-in Bash tool with puppeteer scripts\",\n filesystem: \"Claude Code has built-in file tools (Read, Write, Edit, Glob, Grep)\",\n github: \"Consider using 'gh' CLI via Bash tool instead\",\n slack: \"Consider using 'slack' CLI or curl for API calls\",\n};\n\nexport function analyzeMcp(): McpAnalysis {\n const servers: McpServerInfo[] = [];\n const configPaths = [\n join(process.cwd(), \".mcp.json\"),\n join(homedir(), \".claude.json\"),\n ];\n\n for (const configPath of configPaths) {\n if (!existsSync(configPath)) continue;\n try {\n const raw = JSON.parse(readFileSync(configPath, \"utf-8\"));\n const mcpServers = raw.mcpServers ?? raw.mcp_servers ?? {};\n for (const [name, config] of Object.entries(mcpServers)) {\n const cfg = config as Record<string, unknown>;\n const tools = Array.isArray(cfg.tools) ? cfg.tools : [];\n const toolCount = tools.length || 5;\n const estimatedTokens = toolCount * MCP_TOKENS_PER_TOOL;\n servers.push({\n name,\n toolCount,\n estimatedTokens,\n isHeavy: toolCount > 10,\n });\n }\n } catch {\n continue;\n }\n }\n\n const totalTools = servers.reduce((sum, s) => sum + s.toolCount, 0);\n const totalTokens = servers.reduce((sum, s) => sum + s.estimatedTokens, 0);\n const heavyServers = servers.filter((s) => s.isHeavy);\n\n // Check if Tool Search is enabled (reduces overhead by ~85%)\n let hasToolSearch = false;\n const settingsPaths = [\n join(process.cwd(), \".claude\", \"settings.json\"),\n join(homedir(), \".claude\", \"settings.json\"),\n ];\n for (const sp of settingsPaths) {\n if (!existsSync(sp)) continue;\n try {\n const settings = JSON.parse(readFileSync(sp, \"utf-8\"));\n if (settings.enableToolSearch || settings.tool_search) {\n hasToolSearch = true;\n break;\n }\n } catch {\n continue;\n }\n }\n\n const effectiveTokens = hasToolSearch ? Math.round(totalTokens * 0.15) : totalTokens;\n\n const recommendations: string[] = [];\n for (const server of heavyServers) {\n recommendations.push(\n `'${server.name}' has ${server.toolCount} tools (${server.estimatedTokens.toLocaleString()} tokens). Consider enabling Tool Search to reduce overhead by ~85%.`,\n );\n }\n\n for (const server of servers) {\n const alt = CLI_ALTERNATIVES[server.name.toLowerCase()];\n if (alt) {\n recommendations.push(`${server.name}: ${alt}`);\n }\n }\n\n return {\n servers,\n totalTools,\n totalTokens,\n heavyServers,\n hasToolSearch,\n effectiveTokens,\n recommendations,\n };\n}\n","import { analyzeGhostTokens } from \"./ghostTokens.js\";\nimport { lintClaudeMd } from \"./claudeMdLinter.js\";\nimport { analyzeMcp } from \"./mcpAnalyzer.js\";\nimport type { AuditRecommendation, AuditResult } from \"../types/config.js\";\n\nexport function runFullAudit(claudeMdPath?: string): AuditResult {\n const ghostReport = analyzeGhostTokens(claudeMdPath);\n const claudeMdAnalysis = lintClaudeMd(claudeMdPath);\n const mcpAnalysis = analyzeMcp();\n\n const recommendations: AuditRecommendation[] = [];\n\n // CLAUDE.md recommendations\n if (claudeMdAnalysis) {\n if (claudeMdAnalysis.criticalRulesInDeadZone > 0) {\n recommendations.push({\n priority: \"high\",\n impact: \"improved rule adherence\",\n action: `Reorder CLAUDE.md — ${claudeMdAnalysis.criticalRulesInDeadZone} critical rule(s) in the low-attention dead zone (30-70% position). Move them to the top or bottom.`,\n category: \"claude-md\",\n });\n }\n\n if (claudeMdAnalysis.isOverLineLimit) {\n recommendations.push({\n priority: \"high\",\n impact: `${claudeMdAnalysis.totalLines - 200} lines over limit`,\n action: `CLAUDE.md is ${claudeMdAnalysis.totalLines} lines (limit: 200). Trim or move sections to skills.`,\n category: \"claude-md\",\n });\n }\n\n const heavySections = claudeMdAnalysis.sections.filter((s) => s.tokens > 500);\n for (const section of heavySections) {\n recommendations.push({\n priority: \"medium\",\n impact: `-${section.tokens} tokens/session`,\n action: `Move '${section.title}' section to a skill (${section.tokens} tokens — heavy section)`,\n category: \"claude-md\",\n });\n }\n }\n\n // MCP recommendations\n for (const server of mcpAnalysis.heavyServers) {\n recommendations.push({\n priority: \"medium\",\n impact: `-${server.estimatedTokens.toLocaleString()} tokens/session`,\n action: `MCP server '${server.name}' has ${server.toolCount} tools. Consider disabling if unused or enabling Tool Search.`,\n category: \"mcp\",\n });\n }\n\n if (!mcpAnalysis.hasToolSearch && mcpAnalysis.totalTools > 10) {\n recommendations.push({\n priority: \"high\",\n impact: `~${Math.round(mcpAnalysis.totalTokens * 0.85).toLocaleString()} tokens saved`,\n action: \"Enable Tool Search (deferred tool loading) to reduce MCP overhead by ~85%.\",\n category: \"mcp\",\n });\n }\n\n for (const rec of mcpAnalysis.recommendations) {\n if (rec.includes(\"CLI\")) {\n recommendations.push({\n priority: \"low\",\n impact: \"reduced token overhead\",\n action: rec,\n category: \"mcp\",\n });\n }\n }\n\n // Ghost token recommendations\n if (ghostReport.percentUsable < 50) {\n recommendations.push({\n priority: \"high\",\n impact: `only ${ghostReport.percentUsable.toFixed(0)}% usable`,\n action: `Context window health is poor (grade ${ghostReport.grade}). Total overhead: ${ghostReport.overhead.totalOverhead.toLocaleString()} tokens.`,\n category: \"ghost-tokens\",\n });\n }\n\n // Sort by priority\n const priorityOrder = { high: 0, medium: 1, low: 2 };\n recommendations.sort((a, b) => priorityOrder[a.priority] - priorityOrder[b.priority]);\n\n return {\n grade: ghostReport.grade,\n contextOverhead: ghostReport.overhead,\n claudeMdAnalysis,\n mcpServers: ghostReport.mcpServers,\n recommendations,\n };\n}\n","import { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport dayjs from \"dayjs\";\nimport { getActiveSessions, parseSessionFile, findJsonlFiles } from \"../../core/parser.js\";\nimport {\n calculateMessageCost,\n aggregateCosts,\n formatCost,\n formatTokens,\n} from \"../../core/costCalculator.js\";\nimport type { ParsedMessage } from \"../../types/jsonl.js\";\n\nexport function registerReportCommand(program: Command): void {\n program\n .command(\"report\")\n .description(\"Historical cost reports\")\n .option(\"--period <period>\", \"Time period (today|week|month|all)\", \"today\")\n .option(\"-p, --project <path>\", \"Filter to specific project\")\n .option(\"--model\", \"Show per-model breakdown\")\n .option(\"--sessions\", \"Show per-session breakdown\")\n .option(\"--csv\", \"Export as CSV\")\n .option(\"--json\", \"Export as JSON\")\n .action(async (opts) => {\n const files = await findJsonlFiles(opts.project);\n\n if (files.length === 0) {\n console.log(\"No session data found. Start using Claude Code to generate data.\");\n return;\n }\n\n // Determine time filter\n const now = dayjs();\n let cutoff: dayjs.Dayjs;\n switch (opts.period) {\n case \"today\":\n cutoff = now.startOf(\"day\");\n break;\n case \"week\":\n cutoff = now.subtract(7, \"day\");\n break;\n case \"month\":\n cutoff = now.subtract(30, \"day\");\n break;\n case \"all\":\n cutoff = dayjs(\"2000-01-01\");\n break;\n default:\n cutoff = now.startOf(\"day\");\n }\n\n // Parse all sessions and collect messages\n const allMessages: ParsedMessage[] = [];\n const sessionSummaries: Array<{ id: string; model: string; cost: number; messages: number }> = [];\n\n for (const file of files) {\n try {\n const session = parseSessionFile(file);\n const filteredMessages = session.messages.filter((m) =>\n dayjs(m.timestamp).isAfter(cutoff),\n );\n if (filteredMessages.length === 0) continue;\n\n allMessages.push(...filteredMessages);\n\n const sessionCost = filteredMessages.reduce(\n (sum, m) => sum + calculateMessageCost(m).totalCost,\n 0,\n );\n sessionSummaries.push({\n id: session.sessionId,\n model: filteredMessages[0]?.model ?? \"unknown\",\n cost: sessionCost,\n messages: filteredMessages.length,\n });\n } catch {\n continue;\n }\n }\n\n if (allMessages.length === 0) {\n console.log(`No data found for period: ${opts.period}`);\n return;\n }\n\n // Calculate totals\n let totalCost = 0;\n let totalInput = 0;\n let totalOutput = 0;\n let totalCacheRead = 0;\n\n for (const msg of allMessages) {\n totalCost += calculateMessageCost(msg).totalCost;\n totalInput += msg.usage.input_tokens;\n totalOutput += msg.usage.output_tokens;\n totalCacheRead += msg.usage.cache_read_input_tokens;\n }\n\n const totalCacheable = totalInput + totalCacheRead;\n const cacheHitRate = totalCacheable > 0 ? (totalCacheRead / totalCacheable) * 100 : 0;\n\n if (opts.json) {\n console.log(\n JSON.stringify(\n {\n period: opts.period,\n totalCost,\n totalInput,\n totalOutput,\n cacheHitRate,\n sessions: sessionSummaries,\n },\n null,\n 2,\n ),\n );\n return;\n }\n\n if (opts.csv) {\n console.log(\"session_id,model,cost_usd,messages\");\n for (const s of sessionSummaries) {\n console.log(`${s.id},${s.model},${s.cost.toFixed(4)},${s.messages}`);\n }\n return;\n }\n\n // Pretty print\n const periodLabel = opts.period === \"today\" ? now.format(\"ddd, MMM D, YYYY\") : opts.period;\n console.log(chalk.bold.cyan(`\\n kerf-cli report -- ${periodLabel}\\n`));\n console.log(` Total Cost: ${chalk.bold(formatCost(totalCost))}`);\n console.log(` Total Tokens: ${formatTokens(totalInput)} in / ${formatTokens(totalOutput)} out`);\n console.log(` Cache Hit Rate: ${cacheHitRate.toFixed(1)}%`);\n console.log(` Sessions: ${sessionSummaries.length}`);\n console.log();\n\n if (opts.model || opts.sessions) {\n // Model breakdown\n const byModel = new Map<string, { cost: number; sessions: number }>();\n for (const s of sessionSummaries) {\n const existing = byModel.get(s.model) ?? { cost: 0, sessions: 0 };\n existing.cost += s.cost;\n existing.sessions++;\n byModel.set(s.model, existing);\n }\n\n console.log(chalk.bold(\" Model Breakdown:\"));\n for (const [model, data] of byModel) {\n const pct = totalCost > 0 ? ((data.cost / totalCost) * 100).toFixed(1) : \"0\";\n const shortModel = model.replace(\"claude-\", \"\").replace(/-20\\d{6}$/, \"\");\n console.log(\n ` ${shortModel}: ${formatCost(data.cost)} (${pct}%) -- ${data.sessions} session(s)`,\n );\n }\n console.log();\n }\n\n if (opts.sessions) {\n console.log(chalk.bold(\" Session Breakdown:\"));\n for (const s of sessionSummaries.sort((a, b) => b.cost - a.cost)) {\n console.log(` ${s.id.slice(0, 12)} ${formatCost(s.cost)} ${s.messages} msgs [${s.model}]`);\n }\n console.log();\n }\n\n // Hourly breakdown\n const hourly = aggregateCosts(allMessages, \"hour\");\n if (hourly.length > 1) {\n console.log(chalk.bold(\" Hourly:\"));\n const maxCost = Math.max(...hourly.map((h) => h.totalCost));\n for (const h of hourly.slice(-8)) {\n const barLen = maxCost > 0 ? Math.round((h.totalCost / maxCost) * 12) : 0;\n const bar = \"\\u2588\".repeat(barLen) + \"\\u2591\".repeat(12 - barLen);\n console.log(` ${h.periodLabel.padEnd(14)} ${bar} ${formatCost(h.totalCost)}`);\n }\n console.log();\n }\n });\n}\n","import { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport { mkdirSync, existsSync, readFileSync, writeFileSync, copyFileSync } from \"node:fs\";\nimport { join, dirname } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { initDatabase } from \"../../db/schema.js\";\nimport { runMigrations } from \"../../db/migrations.js\";\n\nexport function registerInitCommand(program: Command): void {\n program\n .command(\"init\")\n .description(\"Set up kerf-cli for the current project\")\n .option(\"--global\", \"Install hooks globally\")\n .option(\"--hooks-only\", \"Only install hooks\")\n .option(\"--no-hooks\", \"Skip hook installation\")\n .option(\"--force\", \"Skip confirmation prompts\")\n .action(async (opts) => {\n console.log(chalk.bold.cyan(\"\\n Welcome to kerf-cli!\\n\"));\n console.log(\" Setting up cost intelligence for Claude Code...\\n\");\n\n // Create ~/.kerf/ directory\n const kerfDir = join(homedir(), \".kerf\");\n if (!existsSync(kerfDir)) {\n mkdirSync(kerfDir, { recursive: true });\n console.log(chalk.green(\" Created ~/.kerf/\"));\n }\n\n if (!opts.hooksOnly) {\n // Initialize database\n try {\n const db = initDatabase();\n runMigrations(db);\n db.close();\n console.log(chalk.green(\" Created ~/.kerf/kerf.db\"));\n } catch (err) {\n console.log(chalk.red(` Failed to create database: ${err}`));\n }\n }\n\n // Detect existing tools\n try {\n const { execSync } = await import(\"node:child_process\");\n try {\n execSync(\"which rtk\", { stdio: \"ignore\" });\n console.log(chalk.green(\" Detected RTK (command compression) -- compatible!\"));\n } catch { /* not installed */ }\n\n try {\n execSync(\"which ccusage\", { stdio: \"ignore\" });\n console.log(chalk.green(\" Detected ccusage -- will import historical data\"));\n } catch { /* not installed */ }\n } catch { /* ignore */ }\n\n // Install hooks\n if (opts.hooks !== false) {\n const settingsPath = opts.global\n ? join(homedir(), \".claude\", \"settings.json\")\n : join(process.cwd(), \".claude\", \"settings.json\");\n\n console.log(\"\\n Install hooks? These enable:\");\n console.log(\" - Real-time token tracking (Notification hook)\");\n console.log(\" - Budget enforcement (Stop hook)\");\n console.log(`\\n Hooks will be added to ${opts.global ? \"~/.claude\" : \".claude\"}/settings.json`);\n\n try {\n installHooks(settingsPath);\n console.log(chalk.green(\"\\n Hooks installed\"));\n } catch (err) {\n console.log(chalk.yellow(`\\n Skipped hook installation: ${err}`));\n }\n }\n\n console.log(chalk.bold(\"\\n Recommended settings for your setup:\"));\n console.log(chalk.dim(' Add to .claude/settings.json or ~/.claude/settings.json:'));\n console.log(chalk.dim(JSON.stringify({\n env: {\n MAX_THINKING_TOKENS: \"10000\",\n CLAUDE_AUTOCOMPACT_PCT_OVERRIDE: \"50\",\n },\n }, null, 4).split(\"\\n\").map(l => \" \" + l).join(\"\\n\")));\n\n console.log(chalk.bold.cyan(\"\\n Run 'kerf-cli watch' to start the live dashboard!\\n\"));\n });\n}\n\nfunction installHooks(settingsPath: string): void {\n const dir = dirname(settingsPath);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n\n let settings: Record<string, unknown> = {};\n if (existsSync(settingsPath)) {\n // Backup existing settings\n const backupPath = settingsPath + \".bak\";\n copyFileSync(settingsPath, backupPath);\n settings = JSON.parse(readFileSync(settingsPath, \"utf-8\"));\n }\n\n const hooks = (settings.hooks ?? {}) as Record<string, unknown[]>;\n\n // Add Notification hook if not present\n if (!hooks.Notification) {\n hooks.Notification = [];\n }\n\n // Add Stop hook if not present\n if (!hooks.Stop) {\n hooks.Stop = [];\n }\n\n settings.hooks = hooks;\n writeFileSync(settingsPath, JSON.stringify(settings, null, 2));\n}\n"],"mappings":"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","names":["React","join","join","Box","Text","dayjs","dayjs","dayjs","Box","Text","jsx","jsxs","readFileSync","join","homedir","readFileSync","join","homedir","jsx","jsxs","Box","Text","program","React","React","render","glob","Box","Text","jsx","jsxs","program","glob","estimate","waitUntilExit","render","React","dayjs","isoWeek","basename","existsSync","existsSync","dayjs","isoWeek","basename","program","chalk","readFileSync","existsSync","join","join","existsSync","readFileSync","readFileSync","existsSync","join","homedir","join","homedir","existsSync","readFileSync","program","chalk","chalk","dayjs","program","dayjs","chalk","chalk","mkdirSync","existsSync","readFileSync","join","dirname","homedir","program","chalk","join","homedir","existsSync","mkdirSync","dirname","readFileSync"]}
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "kerf-cli",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "type": "module",
5
5
  "description": "Cost intelligence for Claude Code. Know before you spend.",
6
6
  "bin": {
7
- "kerf": "./dist/index.js"
7
+ "kerf-cli": "./dist/index.js"
8
8
  },
9
9
  "scripts": {
10
10
  "build": "tsup",
package/kerf@0.1.0 DELETED
File without changes
package/npm DELETED
File without changes
package/tsup DELETED
File without changes
package/vitest DELETED
File without changes