kerf-cli 0.1.2 → 0.1.4

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/dist/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/cli/index.ts
4
+ import { createRequire } from "node:module";
4
5
  import { Command } from "commander";
5
6
 
6
7
  // src/cli/commands/watch.ts
@@ -314,8 +315,11 @@ function getPeriodKey(timestamp, period) {
314
315
  }
315
316
  function formatPeriodLabel(key, period) {
316
317
  switch (period) {
317
- case "hour":
318
- return dayjs2(key, "YYYY-MM-DD-HH").format("MMM D, h A");
318
+ case "hour": {
319
+ const parts = key.split("-");
320
+ const dateStr = `${parts[0]}-${parts[1]}-${parts[2]}T${parts[3]}:00:00`;
321
+ return dayjs2(dateStr).format("MMM D, h A");
322
+ }
319
323
  case "day":
320
324
  return dayjs2(key).format("ddd, MMM D");
321
325
  case "week":
@@ -419,8 +423,25 @@ function ContextBar({ used, total, overhead }) {
419
423
 
420
424
  // src/core/tokenCounter.ts
421
425
  import { readFileSync as readFileSync2, existsSync } from "node:fs";
426
+ import { execSync } from "node:child_process";
422
427
  import { join as join3 } from "node:path";
423
428
  import { homedir as homedir2 } from "node:os";
429
+ function findGitRootClaudeMd() {
430
+ try {
431
+ const gitRoot = execSync("git rev-parse --show-toplevel", {
432
+ encoding: "utf-8",
433
+ stdio: ["pipe", "pipe", "ignore"]
434
+ }).trim();
435
+ if (gitRoot && gitRoot !== process.cwd()) {
436
+ return [
437
+ join3(gitRoot, "CLAUDE.md"),
438
+ join3(gitRoot, ".claude", "CLAUDE.md")
439
+ ];
440
+ }
441
+ } catch {
442
+ }
443
+ return [];
444
+ }
424
445
  function estimateTokens(text) {
425
446
  return Math.ceil(text.length / 3.5);
426
447
  }
@@ -473,7 +494,9 @@ function parseClaudeMdSections(content) {
473
494
  function analyzeClaudeMd(filePath) {
474
495
  const paths = filePath ? [filePath] : [
475
496
  join3(process.cwd(), "CLAUDE.md"),
476
- join3(process.cwd(), ".claude", "CLAUDE.md")
497
+ join3(process.cwd(), ".claude", "CLAUDE.md"),
498
+ ...findGitRootClaudeMd(),
499
+ join3(homedir2(), ".claude", "CLAUDE.md")
477
500
  ];
478
501
  for (const p of paths) {
479
502
  if (existsSync(p)) {
@@ -554,10 +577,13 @@ function Dashboard({ sessionFilePath, interval }) {
554
577
  const timer = setInterval(refresh, interval);
555
578
  return () => clearInterval(timer);
556
579
  }, [sessionFilePath, interval]);
557
- useInput((input) => {
558
- if (input === "q") exit();
559
- if (input === "b") setShowBudget((prev) => !prev);
560
- });
580
+ useInput(
581
+ (input) => {
582
+ if (input === "q") exit();
583
+ if (input === "b") setShowBudget((prev) => !prev);
584
+ },
585
+ { isActive: process.stdin.isTTY ?? false }
586
+ );
561
587
  if (!session || session.messages.length === 0) {
562
588
  return /* @__PURE__ */ jsx3(Box3, { paddingX: 1, children: /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "Waiting for session data..." }) });
563
589
  }
@@ -637,6 +663,12 @@ function registerWatchCommand(program2) {
637
663
  );
638
664
  process.exit(0);
639
665
  }
666
+ if (!process.stdin.isTTY) {
667
+ console.log(
668
+ "kerf-cli watch requires an interactive terminal (TTY). Run it directly in a terminal tab, not piped."
669
+ );
670
+ process.exit(1);
671
+ }
640
672
  const { waitUntilExit } = render(
641
673
  React2.createElement(Dashboard, { sessionFilePath, interval })
642
674
  );
@@ -648,6 +680,7 @@ function registerWatchCommand(program2) {
648
680
  import React3 from "react";
649
681
  import { render as render2 } from "ink";
650
682
  import { glob as glob2 } from "glob";
683
+ import chalk from "chalk";
651
684
 
652
685
  // src/core/estimator.ts
653
686
  import { glob } from "glob";
@@ -674,8 +707,8 @@ async function estimateTaskCost(taskDescription, options = {}) {
674
707
  let fileList = options.files ?? [];
675
708
  if (fileList.length === 0) {
676
709
  try {
677
- const { execSync } = await import("node:child_process");
678
- const output = execSync("git diff --name-only HEAD 2>/dev/null || git ls-files -m 2>/dev/null", {
710
+ const { execSync: execSync2 } = await import("node:child_process");
711
+ const output = execSync2("git diff --name-only HEAD 2>/dev/null || git ls-files -m 2>/dev/null", {
679
712
  cwd,
680
713
  encoding: "utf-8"
681
714
  });
@@ -837,17 +870,42 @@ function registerEstimateCommand(program2) {
837
870
  }
838
871
  if (opts.compare) {
839
872
  const models = ["sonnet", "opus", "haiku"];
840
- for (const model of models) {
841
- const estimate2 = await estimateTaskCost(task, { model, files, cwd: process.cwd() });
842
- if (opts.json) {
873
+ if (opts.json) {
874
+ for (const model of models) {
875
+ const estimate2 = await estimateTaskCost(task, { model, files, cwd: process.cwd() });
843
876
  console.log(JSON.stringify(estimate2, null, 2));
844
- } else {
845
- const { waitUntilExit: waitUntilExit2 } = render2(
846
- React3.createElement(EstimateCard, { task, estimate: estimate2 })
847
- );
848
- await waitUntilExit2();
849
877
  }
878
+ return;
879
+ }
880
+ const estimates = await Promise.all(
881
+ models.map(async (model) => ({
882
+ model,
883
+ estimate: await estimateTaskCost(task, { model, files, cwd: process.cwd() })
884
+ }))
885
+ );
886
+ console.log(chalk.bold.cyan(`
887
+ kerf-cli estimate: '${task}'
888
+ `));
889
+ console.log(
890
+ ` ${"Model".padEnd(10)} ${"Turns".padEnd(14)} ${"Low".padEnd(12)} ${"Expected".padEnd(12)} ${"High".padEnd(12)}`
891
+ );
892
+ console.log(" " + "-".repeat(58));
893
+ for (const { model, estimate: estimate2 } of estimates) {
894
+ const turns = `${estimate2.estimatedTurns.low}-${estimate2.estimatedTurns.high}`;
895
+ console.log(
896
+ ` ${model.padEnd(10)} ${turns.padEnd(14)} ${chalk.green(estimate2.estimatedCost.low.padEnd(12))} ${chalk.yellow(estimate2.estimatedCost.expected.padEnd(12))} ${chalk.red(estimate2.estimatedCost.high.padEnd(12))}`
897
+ );
850
898
  }
899
+ console.log();
900
+ const cheapest = estimates[2];
901
+ const priciest = estimates[1];
902
+ console.log(
903
+ chalk.dim(` Cheapest: ${cheapest.model} at ${cheapest.estimate.estimatedCost.expected}`)
904
+ );
905
+ console.log(
906
+ chalk.dim(` Priciest: ${priciest.model} at ${priciest.estimate.estimatedCost.expected}`)
907
+ );
908
+ console.log();
851
909
  return;
852
910
  }
853
911
  const estimate = await estimateTaskCost(task, {
@@ -867,7 +925,7 @@ function registerEstimateCommand(program2) {
867
925
  }
868
926
 
869
927
  // src/cli/commands/budget.ts
870
- import chalk from "chalk";
928
+ import chalk2 from "chalk";
871
929
 
872
930
  // src/core/budgetManager.ts
873
931
  import dayjs3 from "dayjs";
@@ -1073,12 +1131,12 @@ function registerBudgetCommand(program2) {
1073
1131
  const projectPath = opts.project || process.cwd();
1074
1132
  const amountNum = parseFloat(amount);
1075
1133
  if (isNaN(amountNum) || amountNum <= 0) {
1076
- console.log(chalk.red("Budget amount must be a positive number."));
1134
+ console.log(chalk2.red("Budget amount must be a positive number."));
1077
1135
  process.exit(1);
1078
1136
  }
1079
1137
  manager.setBudget(projectPath, amountNum, opts.period);
1080
1138
  console.log(
1081
- chalk.green(`Budget set: ${formatCost(amountNum)}/${opts.period} for ${projectPath}`)
1139
+ chalk2.green(`Budget set: ${formatCost(amountNum)}/${opts.period} for ${projectPath}`)
1082
1140
  );
1083
1141
  manager.close();
1084
1142
  });
@@ -1101,14 +1159,14 @@ function registerBudgetCommand(program2) {
1101
1159
  const filled = Math.round(Math.min(pct, 100) / 100 * barWidth);
1102
1160
  const empty = barWidth - filled;
1103
1161
  const color = pct < 50 ? "green" : pct < 80 ? "yellow" : "red";
1104
- const barColor = color === "green" ? chalk.green : color === "yellow" ? chalk.yellow : chalk.red;
1105
- console.log(chalk.bold.cyan("\n kerf-cli budget\n"));
1162
+ const barColor = color === "green" ? chalk2.green : color === "yellow" ? chalk2.yellow : chalk2.red;
1163
+ console.log(chalk2.bold.cyan("\n kerf-cli budget\n"));
1106
1164
  console.log(` Period: ${status.period} (${status.periodStart.slice(0, 10)} to ${status.periodEnd.slice(0, 10)})`);
1107
1165
  console.log(` Budget: ${formatCost(status.budget)}`);
1108
1166
  console.log(` Spent: ${barColor(formatCost(status.spent))}`);
1109
1167
  console.log(` ${barColor("[" + "\u2588".repeat(filled) + "\u2591".repeat(empty) + "]")} ${pct.toFixed(1)}%`);
1110
1168
  if (status.isOverBudget) {
1111
- console.log(chalk.red.bold(`
1169
+ console.log(chalk2.red.bold(`
1112
1170
  OVER BUDGET by ${formatCost(status.spent - status.budget)}`));
1113
1171
  }
1114
1172
  console.log();
@@ -1122,12 +1180,12 @@ function registerBudgetCommand(program2) {
1122
1180
  manager.close();
1123
1181
  return;
1124
1182
  }
1125
- console.log(chalk.bold.cyan("\n kerf-cli budget list\n"));
1183
+ console.log(chalk2.bold.cyan("\n kerf-cli budget list\n"));
1126
1184
  for (const p of projects) {
1127
1185
  const budgetStr = p.budget ? `${formatCost(p.budget)}/${p.period}` : "no budget";
1128
1186
  const spentStr = p.spent > 0 ? ` (spent: ${formatCost(p.spent)})` : "";
1129
- console.log(` ${chalk.bold(p.name)} \u2014 ${budgetStr}${spentStr}`);
1130
- console.log(chalk.dim(` ${p.path}`));
1187
+ console.log(` ${chalk2.bold(p.name)} \u2014 ${budgetStr}${spentStr}`);
1188
+ console.log(chalk2.dim(` ${p.path}`));
1131
1189
  }
1132
1190
  console.log();
1133
1191
  manager.close();
@@ -1137,7 +1195,7 @@ function registerBudgetCommand(program2) {
1137
1195
  const projectPath = opts.project || process.cwd();
1138
1196
  const removed = manager.removeBudget(projectPath);
1139
1197
  if (removed) {
1140
- console.log(chalk.green("Budget removed."));
1198
+ console.log(chalk2.green("Budget removed."));
1141
1199
  } else {
1142
1200
  console.log("No budget found for this project.");
1143
1201
  }
@@ -1146,7 +1204,7 @@ function registerBudgetCommand(program2) {
1146
1204
  }
1147
1205
 
1148
1206
  // src/cli/commands/audit.ts
1149
- import chalk2 from "chalk";
1207
+ import chalk3 from "chalk";
1150
1208
 
1151
1209
  // src/audit/ghostTokens.ts
1152
1210
  function calculateGrade(percentUsable) {
@@ -1409,14 +1467,14 @@ function registerAuditCommand(program2) {
1409
1467
  console.log(JSON.stringify(result, null, 2));
1410
1468
  return;
1411
1469
  }
1412
- const gradeColor = result.grade === "A" ? chalk2.green : result.grade === "B" ? chalk2.yellow : chalk2.red;
1413
- console.log(chalk2.bold.cyan("\n kerf-cli audit report\n"));
1470
+ const gradeColor = result.grade === "A" ? chalk3.green : result.grade === "B" ? chalk3.yellow : chalk3.red;
1471
+ console.log(chalk3.bold.cyan("\n kerf-cli audit report\n"));
1414
1472
  console.log(
1415
1473
  ` Context Window Health: ${gradeColor.bold(result.grade)} (${result.contextOverhead.percentUsable.toFixed(0)}% usable)
1416
1474
  `
1417
1475
  );
1418
- if (!opts.mcpOnly) {
1419
- console.log(chalk2.bold(" Ghost Token Breakdown:"));
1476
+ if (!opts.claudeMdOnly) {
1477
+ console.log(chalk3.bold(" Ghost Token Breakdown:"));
1420
1478
  const oh = result.contextOverhead;
1421
1479
  const fmt = (label, tokens) => {
1422
1480
  const pct = (tokens / CONTEXT_WINDOW_SIZE * 100).toFixed(1);
@@ -1436,35 +1494,70 @@ function registerAuditCommand(program2) {
1436
1494
  }
1437
1495
  if (!opts.mcpOnly && result.claudeMdAnalysis) {
1438
1496
  const cma = result.claudeMdAnalysis;
1439
- console.log(chalk2.bold(" CLAUDE.md Analysis:"));
1497
+ console.log(chalk3.bold(" CLAUDE.md Analysis:"));
1440
1498
  console.log(
1441
- ` Lines: ${cma.totalLines}${cma.isOverLineLimit ? chalk2.yellow(" (over 200 limit)") : ""}`
1499
+ ` Lines: ${cma.totalLines}${cma.isOverLineLimit ? chalk3.yellow(" (over 200 limit)") : ""}`
1442
1500
  );
1443
1501
  console.log(` Tokens: ${cma.totalTokens.toLocaleString()}`);
1444
1502
  console.log(
1445
- ` Critical rules in dead zone: ${cma.criticalRulesInDeadZone > 0 ? chalk2.red(String(cma.criticalRulesInDeadZone)) : "0"}`
1503
+ ` Critical rules in dead zone: ${cma.criticalRulesInDeadZone > 0 ? chalk3.red(String(cma.criticalRulesInDeadZone)) : "0"}`
1446
1504
  );
1505
+ if (opts.claudeMdOnly) {
1506
+ console.log();
1507
+ console.log(chalk3.bold(" Sections:"));
1508
+ for (const section of cma.sections) {
1509
+ const zone = section.attentionZone === "low-middle" ? chalk3.red(" [dead zone]") : chalk3.green(" [high attention]");
1510
+ const critical = section.hasCriticalRules ? chalk3.yellow(" *critical rules*") : "";
1511
+ console.log(
1512
+ ` ${section.title.padEnd(30)} ${String(section.tokens).padStart(5)} tokens L${section.lineStart}-${section.lineEnd}${zone}${critical}`
1513
+ );
1514
+ }
1515
+ if (cma.suggestedReorder.length > 0) {
1516
+ console.log();
1517
+ console.log(chalk3.bold(" Suggested section order:"));
1518
+ cma.suggestedReorder.forEach((title, i) => {
1519
+ console.log(` ${i + 1}. ${title}`);
1520
+ });
1521
+ }
1522
+ }
1447
1523
  console.log();
1524
+ } else if (!opts.mcpOnly && !result.claudeMdAnalysis) {
1525
+ console.log(chalk3.yellow(" No CLAUDE.md found in current directory or git root.\n"));
1448
1526
  }
1449
- if (result.recommendations.length > 0) {
1450
- console.log(chalk2.bold(" Recommendations:"));
1451
- result.recommendations.forEach((rec, i) => {
1452
- const priorityColor = rec.priority === "high" ? chalk2.red : rec.priority === "medium" ? chalk2.yellow : chalk2.dim;
1527
+ if (opts.mcpOnly && result.mcpServers.length > 0) {
1528
+ console.log(chalk3.bold(" MCP Servers:"));
1529
+ for (const server of result.mcpServers) {
1530
+ const heavy = server.isHeavy ? chalk3.red(" [heavy]") : "";
1453
1531
  console.log(
1454
- ` ${i + 1}. ${priorityColor(`[${rec.priority.toUpperCase()}]`)} ${rec.action}`
1532
+ ` ${server.name.padEnd(20)} ${String(server.toolCount).padStart(3)} tools ${server.estimatedTokens.toLocaleString().padStart(6)} tokens${heavy}`
1455
1533
  );
1456
- console.log(chalk2.dim(` Impact: ${rec.impact}`));
1457
- });
1534
+ }
1535
+ console.log();
1536
+ } else if (opts.mcpOnly && result.mcpServers.length === 0) {
1537
+ console.log(chalk3.dim(" No MCP servers configured.\n"));
1538
+ }
1539
+ if (result.recommendations.length > 0) {
1540
+ const filteredRecs = opts.claudeMdOnly ? result.recommendations.filter((r) => r.category === "claude-md") : opts.mcpOnly ? result.recommendations.filter((r) => r.category === "mcp") : result.recommendations;
1541
+ if (filteredRecs.length > 0) {
1542
+ console.log(chalk3.bold(" Recommendations:"));
1543
+ filteredRecs.forEach((rec, i) => {
1544
+ const priorityColor = rec.priority === "high" ? chalk3.red : rec.priority === "medium" ? chalk3.yellow : chalk3.dim;
1545
+ console.log(
1546
+ ` ${i + 1}. ${priorityColor(`[${rec.priority.toUpperCase()}]`)} ${rec.action}`
1547
+ );
1548
+ console.log(chalk3.dim(` Impact: ${rec.impact}`));
1549
+ });
1550
+ }
1458
1551
  }
1459
1552
  console.log();
1460
1553
  if (opts.fix) {
1461
- console.log(chalk2.yellow(" --fix: Auto-fix is not yet implemented. Coming in v0.2.0.\n"));
1554
+ console.log(chalk3.yellow(" --fix: Auto-fix is not yet implemented. Coming in v0.2.0.\n"));
1462
1555
  }
1463
1556
  });
1464
1557
  }
1465
1558
 
1466
1559
  // src/cli/commands/report.ts
1467
- import chalk3 from "chalk";
1560
+ import chalk4 from "chalk";
1468
1561
  import dayjs4 from "dayjs";
1469
1562
  function registerReportCommand(program2) {
1470
1563
  program2.command("report").description("Historical cost reports").option("--period <period>", "Time period (today|week|month|all)", "today").option("-p, --project <path>", "Filter to specific project").option("--model", "Show per-model breakdown").option("--sessions", "Show per-session breakdown").option("--csv", "Export as CSV").option("--json", "Export as JSON").action(async (opts) => {
@@ -1556,10 +1649,10 @@ function registerReportCommand(program2) {
1556
1649
  return;
1557
1650
  }
1558
1651
  const periodLabel = opts.period === "today" ? now.format("ddd, MMM D, YYYY") : opts.period;
1559
- console.log(chalk3.bold.cyan(`
1652
+ console.log(chalk4.bold.cyan(`
1560
1653
  kerf-cli report -- ${periodLabel}
1561
1654
  `));
1562
- console.log(` Total Cost: ${chalk3.bold(formatCost(totalCost))}`);
1655
+ console.log(` Total Cost: ${chalk4.bold(formatCost(totalCost))}`);
1563
1656
  console.log(` Total Tokens: ${formatTokens(totalInput)} in / ${formatTokens(totalOutput)} out`);
1564
1657
  console.log(` Cache Hit Rate: ${cacheHitRate.toFixed(1)}%`);
1565
1658
  console.log(` Sessions: ${sessionSummaries.length}`);
@@ -1572,7 +1665,7 @@ function registerReportCommand(program2) {
1572
1665
  existing.sessions++;
1573
1666
  byModel.set(s.model, existing);
1574
1667
  }
1575
- console.log(chalk3.bold(" Model Breakdown:"));
1668
+ console.log(chalk4.bold(" Model Breakdown:"));
1576
1669
  for (const [model, data] of byModel) {
1577
1670
  const pct = totalCost > 0 ? (data.cost / totalCost * 100).toFixed(1) : "0";
1578
1671
  const shortModel = model.replace("claude-", "").replace(/-20\d{6}$/, "");
@@ -1583,7 +1676,7 @@ function registerReportCommand(program2) {
1583
1676
  console.log();
1584
1677
  }
1585
1678
  if (opts.sessions) {
1586
- console.log(chalk3.bold(" Session Breakdown:"));
1679
+ console.log(chalk4.bold(" Session Breakdown:"));
1587
1680
  for (const s of sessionSummaries.sort((a, b) => b.cost - a.cost)) {
1588
1681
  console.log(` ${s.id.slice(0, 12)} ${formatCost(s.cost)} ${s.messages} msgs [${s.model}]`);
1589
1682
  }
@@ -1591,7 +1684,7 @@ function registerReportCommand(program2) {
1591
1684
  }
1592
1685
  const hourly = aggregateCosts(allMessages, "hour");
1593
1686
  if (hourly.length > 1) {
1594
- console.log(chalk3.bold(" Hourly:"));
1687
+ console.log(chalk4.bold(" Hourly:"));
1595
1688
  const maxCost = Math.max(...hourly.map((h) => h.totalCost));
1596
1689
  for (const h of hourly.slice(-8)) {
1597
1690
  const barLen = maxCost > 0 ? Math.round(h.totalCost / maxCost * 12) : 0;
@@ -1604,39 +1697,39 @@ function registerReportCommand(program2) {
1604
1697
  }
1605
1698
 
1606
1699
  // src/cli/commands/init.ts
1607
- import chalk4 from "chalk";
1700
+ import chalk5 from "chalk";
1608
1701
  import { mkdirSync as mkdirSync2, existsSync as existsSync5, readFileSync as readFileSync5, writeFileSync, copyFileSync } from "node:fs";
1609
1702
  import { join as join6, dirname as dirname2 } from "node:path";
1610
1703
  import { homedir as homedir4 } from "node:os";
1611
1704
  function registerInitCommand(program2) {
1612
1705
  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"));
1706
+ console.log(chalk5.bold.cyan("\n Welcome to kerf-cli!\n"));
1614
1707
  console.log(" Setting up cost intelligence for Claude Code...\n");
1615
1708
  const kerfDir = join6(homedir4(), ".kerf");
1616
1709
  if (!existsSync5(kerfDir)) {
1617
1710
  mkdirSync2(kerfDir, { recursive: true });
1618
- console.log(chalk4.green(" Created ~/.kerf/"));
1711
+ console.log(chalk5.green(" Created ~/.kerf/"));
1619
1712
  }
1620
1713
  if (!opts.hooksOnly) {
1621
1714
  try {
1622
1715
  const db = initDatabase();
1623
1716
  runMigrations(db);
1624
1717
  db.close();
1625
- console.log(chalk4.green(" Created ~/.kerf/kerf.db"));
1718
+ console.log(chalk5.green(" Created ~/.kerf/kerf.db"));
1626
1719
  } catch (err) {
1627
- console.log(chalk4.red(` Failed to create database: ${err}`));
1720
+ console.log(chalk5.red(` Failed to create database: ${err}`));
1628
1721
  }
1629
1722
  }
1630
1723
  try {
1631
- const { execSync } = await import("node:child_process");
1724
+ const { execSync: execSync2 } = await import("node:child_process");
1632
1725
  try {
1633
- execSync("which rtk", { stdio: "ignore" });
1634
- console.log(chalk4.green(" Detected RTK (command compression) -- compatible!"));
1726
+ execSync2("which rtk", { stdio: "ignore" });
1727
+ console.log(chalk5.green(" Detected RTK (command compression) -- compatible!"));
1635
1728
  } catch {
1636
1729
  }
1637
1730
  try {
1638
- execSync("which ccusage", { stdio: "ignore" });
1639
- console.log(chalk4.green(" Detected ccusage -- will import historical data"));
1731
+ execSync2("which ccusage", { stdio: "ignore" });
1732
+ console.log(chalk5.green(" Detected ccusage -- will import historical data"));
1640
1733
  } catch {
1641
1734
  }
1642
1735
  } catch {
@@ -1650,21 +1743,21 @@ function registerInitCommand(program2) {
1650
1743
  Hooks will be added to ${opts.global ? "~/.claude" : ".claude"}/settings.json`);
1651
1744
  try {
1652
1745
  installHooks(settingsPath);
1653
- console.log(chalk4.green("\n Hooks installed"));
1746
+ console.log(chalk5.green("\n Hooks installed"));
1654
1747
  } catch (err) {
1655
- console.log(chalk4.yellow(`
1748
+ console.log(chalk5.yellow(`
1656
1749
  Skipped hook installation: ${err}`));
1657
1750
  }
1658
1751
  }
1659
- console.log(chalk4.bold("\n Recommended settings for your setup:"));
1660
- console.log(chalk4.dim(" Add to .claude/settings.json or ~/.claude/settings.json:"));
1661
- console.log(chalk4.dim(JSON.stringify({
1752
+ console.log(chalk5.bold("\n Recommended settings for your setup:"));
1753
+ console.log(chalk5.dim(" Add to .claude/settings.json or ~/.claude/settings.json:"));
1754
+ console.log(chalk5.dim(JSON.stringify({
1662
1755
  env: {
1663
1756
  MAX_THINKING_TOKENS: "10000",
1664
1757
  CLAUDE_AUTOCOMPACT_PCT_OVERRIDE: "50"
1665
1758
  }
1666
1759
  }, null, 4).split("\n").map((l) => " " + l).join("\n")));
1667
- console.log(chalk4.bold.cyan("\n Run 'kerf-cli watch' to start the live dashboard!\n"));
1760
+ console.log(chalk5.bold.cyan("\n Run 'kerf-cli watch' to start the live dashboard!\n"));
1668
1761
  });
1669
1762
  }
1670
1763
  function installHooks(settingsPath) {
@@ -1690,8 +1783,10 @@ function installHooks(settingsPath) {
1690
1783
  }
1691
1784
 
1692
1785
  // src/cli/index.ts
1786
+ var require2 = createRequire(import.meta.url);
1787
+ var pkg = require2("../../package.json");
1693
1788
  var program = new Command();
1694
- program.name("kerf-cli").version("0.1.0").description("Cost intelligence for Claude Code. Know before you spend.");
1789
+ program.name("kerf-cli").version(pkg.version).description("Cost intelligence for Claude Code. Know before you spend.");
1695
1790
  registerWatchCommand(program);
1696
1791
  registerEstimateCommand(program);
1697
1792
  registerBudgetCommand(program);