kerf-cli 0.1.3 → 0.1.5
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/ARCHITECTURE.md +198 -0
- package/README.md +245 -51
- package/dist/index.js +96 -61
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/CHANGELOG.md +0 -17
- package/USAGE.md +0 -644
package/dist/index.js
CHANGED
|
@@ -576,10 +576,13 @@ function Dashboard({ sessionFilePath, interval }) {
|
|
|
576
576
|
const timer = setInterval(refresh, interval);
|
|
577
577
|
return () => clearInterval(timer);
|
|
578
578
|
}, [sessionFilePath, interval]);
|
|
579
|
-
useInput(
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
579
|
+
useInput(
|
|
580
|
+
(input) => {
|
|
581
|
+
if (input === "q") exit();
|
|
582
|
+
if (input === "b") setShowBudget((prev) => !prev);
|
|
583
|
+
},
|
|
584
|
+
{ isActive: process.stdin.isTTY ?? false }
|
|
585
|
+
);
|
|
583
586
|
if (!session || session.messages.length === 0) {
|
|
584
587
|
return /* @__PURE__ */ jsx3(Box3, { paddingX: 1, children: /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "Waiting for session data..." }) });
|
|
585
588
|
}
|
|
@@ -659,6 +662,12 @@ function registerWatchCommand(program2) {
|
|
|
659
662
|
);
|
|
660
663
|
process.exit(0);
|
|
661
664
|
}
|
|
665
|
+
if (!process.stdin.isTTY) {
|
|
666
|
+
console.log(
|
|
667
|
+
"kerf-cli watch requires an interactive terminal (TTY). Run it directly in a terminal tab, not piped."
|
|
668
|
+
);
|
|
669
|
+
process.exit(1);
|
|
670
|
+
}
|
|
662
671
|
const { waitUntilExit } = render(
|
|
663
672
|
React2.createElement(Dashboard, { sessionFilePath, interval })
|
|
664
673
|
);
|
|
@@ -670,6 +679,7 @@ function registerWatchCommand(program2) {
|
|
|
670
679
|
import React3 from "react";
|
|
671
680
|
import { render as render2 } from "ink";
|
|
672
681
|
import { glob as glob2 } from "glob";
|
|
682
|
+
import chalk from "chalk";
|
|
673
683
|
|
|
674
684
|
// src/core/estimator.ts
|
|
675
685
|
import { glob } from "glob";
|
|
@@ -859,17 +869,42 @@ function registerEstimateCommand(program2) {
|
|
|
859
869
|
}
|
|
860
870
|
if (opts.compare) {
|
|
861
871
|
const models = ["sonnet", "opus", "haiku"];
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
872
|
+
if (opts.json) {
|
|
873
|
+
for (const model of models) {
|
|
874
|
+
const estimate2 = await estimateTaskCost(task, { model, files, cwd: process.cwd() });
|
|
865
875
|
console.log(JSON.stringify(estimate2, null, 2));
|
|
866
|
-
} else {
|
|
867
|
-
const { waitUntilExit: waitUntilExit2 } = render2(
|
|
868
|
-
React3.createElement(EstimateCard, { task, estimate: estimate2 })
|
|
869
|
-
);
|
|
870
|
-
await waitUntilExit2();
|
|
871
876
|
}
|
|
877
|
+
return;
|
|
872
878
|
}
|
|
879
|
+
const estimates = await Promise.all(
|
|
880
|
+
models.map(async (model) => ({
|
|
881
|
+
model,
|
|
882
|
+
estimate: await estimateTaskCost(task, { model, files, cwd: process.cwd() })
|
|
883
|
+
}))
|
|
884
|
+
);
|
|
885
|
+
console.log(chalk.bold.cyan(`
|
|
886
|
+
kerf-cli estimate: '${task}'
|
|
887
|
+
`));
|
|
888
|
+
console.log(
|
|
889
|
+
` ${"Model".padEnd(10)} ${"Turns".padEnd(14)} ${"Low".padEnd(12)} ${"Expected".padEnd(12)} ${"High".padEnd(12)}`
|
|
890
|
+
);
|
|
891
|
+
console.log(" " + "-".repeat(58));
|
|
892
|
+
for (const { model, estimate: estimate2 } of estimates) {
|
|
893
|
+
const turns = `${estimate2.estimatedTurns.low}-${estimate2.estimatedTurns.high}`;
|
|
894
|
+
console.log(
|
|
895
|
+
` ${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))}`
|
|
896
|
+
);
|
|
897
|
+
}
|
|
898
|
+
console.log();
|
|
899
|
+
const cheapest = estimates[2];
|
|
900
|
+
const priciest = estimates[1];
|
|
901
|
+
console.log(
|
|
902
|
+
chalk.dim(` Cheapest: ${cheapest.model} at ${cheapest.estimate.estimatedCost.expected}`)
|
|
903
|
+
);
|
|
904
|
+
console.log(
|
|
905
|
+
chalk.dim(` Priciest: ${priciest.model} at ${priciest.estimate.estimatedCost.expected}`)
|
|
906
|
+
);
|
|
907
|
+
console.log();
|
|
873
908
|
return;
|
|
874
909
|
}
|
|
875
910
|
const estimate = await estimateTaskCost(task, {
|
|
@@ -889,7 +924,7 @@ function registerEstimateCommand(program2) {
|
|
|
889
924
|
}
|
|
890
925
|
|
|
891
926
|
// src/cli/commands/budget.ts
|
|
892
|
-
import
|
|
927
|
+
import chalk2 from "chalk";
|
|
893
928
|
|
|
894
929
|
// src/core/budgetManager.ts
|
|
895
930
|
import dayjs3 from "dayjs";
|
|
@@ -1095,12 +1130,12 @@ function registerBudgetCommand(program2) {
|
|
|
1095
1130
|
const projectPath = opts.project || process.cwd();
|
|
1096
1131
|
const amountNum = parseFloat(amount);
|
|
1097
1132
|
if (isNaN(amountNum) || amountNum <= 0) {
|
|
1098
|
-
console.log(
|
|
1133
|
+
console.log(chalk2.red("Budget amount must be a positive number."));
|
|
1099
1134
|
process.exit(1);
|
|
1100
1135
|
}
|
|
1101
1136
|
manager.setBudget(projectPath, amountNum, opts.period);
|
|
1102
1137
|
console.log(
|
|
1103
|
-
|
|
1138
|
+
chalk2.green(`Budget set: ${formatCost(amountNum)}/${opts.period} for ${projectPath}`)
|
|
1104
1139
|
);
|
|
1105
1140
|
manager.close();
|
|
1106
1141
|
});
|
|
@@ -1123,14 +1158,14 @@ function registerBudgetCommand(program2) {
|
|
|
1123
1158
|
const filled = Math.round(Math.min(pct, 100) / 100 * barWidth);
|
|
1124
1159
|
const empty = barWidth - filled;
|
|
1125
1160
|
const color = pct < 50 ? "green" : pct < 80 ? "yellow" : "red";
|
|
1126
|
-
const barColor = color === "green" ?
|
|
1127
|
-
console.log(
|
|
1161
|
+
const barColor = color === "green" ? chalk2.green : color === "yellow" ? chalk2.yellow : chalk2.red;
|
|
1162
|
+
console.log(chalk2.bold.cyan("\n kerf-cli budget\n"));
|
|
1128
1163
|
console.log(` Period: ${status.period} (${status.periodStart.slice(0, 10)} to ${status.periodEnd.slice(0, 10)})`);
|
|
1129
1164
|
console.log(` Budget: ${formatCost(status.budget)}`);
|
|
1130
1165
|
console.log(` Spent: ${barColor(formatCost(status.spent))}`);
|
|
1131
1166
|
console.log(` ${barColor("[" + "\u2588".repeat(filled) + "\u2591".repeat(empty) + "]")} ${pct.toFixed(1)}%`);
|
|
1132
1167
|
if (status.isOverBudget) {
|
|
1133
|
-
console.log(
|
|
1168
|
+
console.log(chalk2.red.bold(`
|
|
1134
1169
|
OVER BUDGET by ${formatCost(status.spent - status.budget)}`));
|
|
1135
1170
|
}
|
|
1136
1171
|
console.log();
|
|
@@ -1144,12 +1179,12 @@ function registerBudgetCommand(program2) {
|
|
|
1144
1179
|
manager.close();
|
|
1145
1180
|
return;
|
|
1146
1181
|
}
|
|
1147
|
-
console.log(
|
|
1182
|
+
console.log(chalk2.bold.cyan("\n kerf-cli budget list\n"));
|
|
1148
1183
|
for (const p of projects) {
|
|
1149
1184
|
const budgetStr = p.budget ? `${formatCost(p.budget)}/${p.period}` : "no budget";
|
|
1150
1185
|
const spentStr = p.spent > 0 ? ` (spent: ${formatCost(p.spent)})` : "";
|
|
1151
|
-
console.log(` ${
|
|
1152
|
-
console.log(
|
|
1186
|
+
console.log(` ${chalk2.bold(p.name)} \u2014 ${budgetStr}${spentStr}`);
|
|
1187
|
+
console.log(chalk2.dim(` ${p.path}`));
|
|
1153
1188
|
}
|
|
1154
1189
|
console.log();
|
|
1155
1190
|
manager.close();
|
|
@@ -1159,7 +1194,7 @@ function registerBudgetCommand(program2) {
|
|
|
1159
1194
|
const projectPath = opts.project || process.cwd();
|
|
1160
1195
|
const removed = manager.removeBudget(projectPath);
|
|
1161
1196
|
if (removed) {
|
|
1162
|
-
console.log(
|
|
1197
|
+
console.log(chalk2.green("Budget removed."));
|
|
1163
1198
|
} else {
|
|
1164
1199
|
console.log("No budget found for this project.");
|
|
1165
1200
|
}
|
|
@@ -1168,7 +1203,7 @@ function registerBudgetCommand(program2) {
|
|
|
1168
1203
|
}
|
|
1169
1204
|
|
|
1170
1205
|
// src/cli/commands/audit.ts
|
|
1171
|
-
import
|
|
1206
|
+
import chalk3 from "chalk";
|
|
1172
1207
|
|
|
1173
1208
|
// src/audit/ghostTokens.ts
|
|
1174
1209
|
function calculateGrade(percentUsable) {
|
|
@@ -1431,14 +1466,14 @@ function registerAuditCommand(program2) {
|
|
|
1431
1466
|
console.log(JSON.stringify(result, null, 2));
|
|
1432
1467
|
return;
|
|
1433
1468
|
}
|
|
1434
|
-
const gradeColor = result.grade === "A" ?
|
|
1435
|
-
console.log(
|
|
1469
|
+
const gradeColor = result.grade === "A" ? chalk3.green : result.grade === "B" ? chalk3.yellow : chalk3.red;
|
|
1470
|
+
console.log(chalk3.bold.cyan("\n kerf-cli audit report\n"));
|
|
1436
1471
|
console.log(
|
|
1437
1472
|
` Context Window Health: ${gradeColor.bold(result.grade)} (${result.contextOverhead.percentUsable.toFixed(0)}% usable)
|
|
1438
1473
|
`
|
|
1439
1474
|
);
|
|
1440
1475
|
if (!opts.claudeMdOnly) {
|
|
1441
|
-
console.log(
|
|
1476
|
+
console.log(chalk3.bold(" Ghost Token Breakdown:"));
|
|
1442
1477
|
const oh = result.contextOverhead;
|
|
1443
1478
|
const fmt = (label, tokens) => {
|
|
1444
1479
|
const pct = (tokens / CONTEXT_WINDOW_SIZE * 100).toFixed(1);
|
|
@@ -1458,27 +1493,27 @@ function registerAuditCommand(program2) {
|
|
|
1458
1493
|
}
|
|
1459
1494
|
if (!opts.mcpOnly && result.claudeMdAnalysis) {
|
|
1460
1495
|
const cma = result.claudeMdAnalysis;
|
|
1461
|
-
console.log(
|
|
1496
|
+
console.log(chalk3.bold(" CLAUDE.md Analysis:"));
|
|
1462
1497
|
console.log(
|
|
1463
|
-
` Lines: ${cma.totalLines}${cma.isOverLineLimit ?
|
|
1498
|
+
` Lines: ${cma.totalLines}${cma.isOverLineLimit ? chalk3.yellow(" (over 200 limit)") : ""}`
|
|
1464
1499
|
);
|
|
1465
1500
|
console.log(` Tokens: ${cma.totalTokens.toLocaleString()}`);
|
|
1466
1501
|
console.log(
|
|
1467
|
-
` Critical rules in dead zone: ${cma.criticalRulesInDeadZone > 0 ?
|
|
1502
|
+
` Critical rules in dead zone: ${cma.criticalRulesInDeadZone > 0 ? chalk3.red(String(cma.criticalRulesInDeadZone)) : "0"}`
|
|
1468
1503
|
);
|
|
1469
1504
|
if (opts.claudeMdOnly) {
|
|
1470
1505
|
console.log();
|
|
1471
|
-
console.log(
|
|
1506
|
+
console.log(chalk3.bold(" Sections:"));
|
|
1472
1507
|
for (const section of cma.sections) {
|
|
1473
|
-
const zone = section.attentionZone === "low-middle" ?
|
|
1474
|
-
const critical = section.hasCriticalRules ?
|
|
1508
|
+
const zone = section.attentionZone === "low-middle" ? chalk3.red(" [dead zone]") : chalk3.green(" [high attention]");
|
|
1509
|
+
const critical = section.hasCriticalRules ? chalk3.yellow(" *critical rules*") : "";
|
|
1475
1510
|
console.log(
|
|
1476
1511
|
` ${section.title.padEnd(30)} ${String(section.tokens).padStart(5)} tokens L${section.lineStart}-${section.lineEnd}${zone}${critical}`
|
|
1477
1512
|
);
|
|
1478
1513
|
}
|
|
1479
1514
|
if (cma.suggestedReorder.length > 0) {
|
|
1480
1515
|
console.log();
|
|
1481
|
-
console.log(
|
|
1516
|
+
console.log(chalk3.bold(" Suggested section order:"));
|
|
1482
1517
|
cma.suggestedReorder.forEach((title, i) => {
|
|
1483
1518
|
console.log(` ${i + 1}. ${title}`);
|
|
1484
1519
|
});
|
|
@@ -1486,42 +1521,42 @@ function registerAuditCommand(program2) {
|
|
|
1486
1521
|
}
|
|
1487
1522
|
console.log();
|
|
1488
1523
|
} else if (!opts.mcpOnly && !result.claudeMdAnalysis) {
|
|
1489
|
-
console.log(
|
|
1524
|
+
console.log(chalk3.yellow(" No CLAUDE.md found in current directory or git root.\n"));
|
|
1490
1525
|
}
|
|
1491
1526
|
if (opts.mcpOnly && result.mcpServers.length > 0) {
|
|
1492
|
-
console.log(
|
|
1527
|
+
console.log(chalk3.bold(" MCP Servers:"));
|
|
1493
1528
|
for (const server of result.mcpServers) {
|
|
1494
|
-
const heavy = server.isHeavy ?
|
|
1529
|
+
const heavy = server.isHeavy ? chalk3.red(" [heavy]") : "";
|
|
1495
1530
|
console.log(
|
|
1496
1531
|
` ${server.name.padEnd(20)} ${String(server.toolCount).padStart(3)} tools ${server.estimatedTokens.toLocaleString().padStart(6)} tokens${heavy}`
|
|
1497
1532
|
);
|
|
1498
1533
|
}
|
|
1499
1534
|
console.log();
|
|
1500
1535
|
} else if (opts.mcpOnly && result.mcpServers.length === 0) {
|
|
1501
|
-
console.log(
|
|
1536
|
+
console.log(chalk3.dim(" No MCP servers configured.\n"));
|
|
1502
1537
|
}
|
|
1503
1538
|
if (result.recommendations.length > 0) {
|
|
1504
1539
|
const filteredRecs = opts.claudeMdOnly ? result.recommendations.filter((r) => r.category === "claude-md") : opts.mcpOnly ? result.recommendations.filter((r) => r.category === "mcp") : result.recommendations;
|
|
1505
1540
|
if (filteredRecs.length > 0) {
|
|
1506
|
-
console.log(
|
|
1541
|
+
console.log(chalk3.bold(" Recommendations:"));
|
|
1507
1542
|
filteredRecs.forEach((rec, i) => {
|
|
1508
|
-
const priorityColor = rec.priority === "high" ?
|
|
1543
|
+
const priorityColor = rec.priority === "high" ? chalk3.red : rec.priority === "medium" ? chalk3.yellow : chalk3.dim;
|
|
1509
1544
|
console.log(
|
|
1510
1545
|
` ${i + 1}. ${priorityColor(`[${rec.priority.toUpperCase()}]`)} ${rec.action}`
|
|
1511
1546
|
);
|
|
1512
|
-
console.log(
|
|
1547
|
+
console.log(chalk3.dim(` Impact: ${rec.impact}`));
|
|
1513
1548
|
});
|
|
1514
1549
|
}
|
|
1515
1550
|
}
|
|
1516
1551
|
console.log();
|
|
1517
1552
|
if (opts.fix) {
|
|
1518
|
-
console.log(
|
|
1553
|
+
console.log(chalk3.yellow(" --fix: Auto-fix is not yet implemented. Coming in v0.2.0.\n"));
|
|
1519
1554
|
}
|
|
1520
1555
|
});
|
|
1521
1556
|
}
|
|
1522
1557
|
|
|
1523
1558
|
// src/cli/commands/report.ts
|
|
1524
|
-
import
|
|
1559
|
+
import chalk4 from "chalk";
|
|
1525
1560
|
import dayjs4 from "dayjs";
|
|
1526
1561
|
function registerReportCommand(program2) {
|
|
1527
1562
|
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) => {
|
|
@@ -1613,10 +1648,10 @@ function registerReportCommand(program2) {
|
|
|
1613
1648
|
return;
|
|
1614
1649
|
}
|
|
1615
1650
|
const periodLabel = opts.period === "today" ? now.format("ddd, MMM D, YYYY") : opts.period;
|
|
1616
|
-
console.log(
|
|
1651
|
+
console.log(chalk4.bold.cyan(`
|
|
1617
1652
|
kerf-cli report -- ${periodLabel}
|
|
1618
1653
|
`));
|
|
1619
|
-
console.log(` Total Cost: ${
|
|
1654
|
+
console.log(` Total Cost: ${chalk4.bold(formatCost(totalCost))}`);
|
|
1620
1655
|
console.log(` Total Tokens: ${formatTokens(totalInput)} in / ${formatTokens(totalOutput)} out`);
|
|
1621
1656
|
console.log(` Cache Hit Rate: ${cacheHitRate.toFixed(1)}%`);
|
|
1622
1657
|
console.log(` Sessions: ${sessionSummaries.length}`);
|
|
@@ -1629,7 +1664,7 @@ function registerReportCommand(program2) {
|
|
|
1629
1664
|
existing.sessions++;
|
|
1630
1665
|
byModel.set(s.model, existing);
|
|
1631
1666
|
}
|
|
1632
|
-
console.log(
|
|
1667
|
+
console.log(chalk4.bold(" Model Breakdown:"));
|
|
1633
1668
|
for (const [model, data] of byModel) {
|
|
1634
1669
|
const pct = totalCost > 0 ? (data.cost / totalCost * 100).toFixed(1) : "0";
|
|
1635
1670
|
const shortModel = model.replace("claude-", "").replace(/-20\d{6}$/, "");
|
|
@@ -1640,7 +1675,7 @@ function registerReportCommand(program2) {
|
|
|
1640
1675
|
console.log();
|
|
1641
1676
|
}
|
|
1642
1677
|
if (opts.sessions) {
|
|
1643
|
-
console.log(
|
|
1678
|
+
console.log(chalk4.bold(" Session Breakdown:"));
|
|
1644
1679
|
for (const s of sessionSummaries.sort((a, b) => b.cost - a.cost)) {
|
|
1645
1680
|
console.log(` ${s.id.slice(0, 12)} ${formatCost(s.cost)} ${s.messages} msgs [${s.model}]`);
|
|
1646
1681
|
}
|
|
@@ -1648,7 +1683,7 @@ function registerReportCommand(program2) {
|
|
|
1648
1683
|
}
|
|
1649
1684
|
const hourly = aggregateCosts(allMessages, "hour");
|
|
1650
1685
|
if (hourly.length > 1) {
|
|
1651
|
-
console.log(
|
|
1686
|
+
console.log(chalk4.bold(" Hourly:"));
|
|
1652
1687
|
const maxCost = Math.max(...hourly.map((h) => h.totalCost));
|
|
1653
1688
|
for (const h of hourly.slice(-8)) {
|
|
1654
1689
|
const barLen = maxCost > 0 ? Math.round(h.totalCost / maxCost * 12) : 0;
|
|
@@ -1661,39 +1696,39 @@ function registerReportCommand(program2) {
|
|
|
1661
1696
|
}
|
|
1662
1697
|
|
|
1663
1698
|
// src/cli/commands/init.ts
|
|
1664
|
-
import
|
|
1699
|
+
import chalk5 from "chalk";
|
|
1665
1700
|
import { mkdirSync as mkdirSync2, existsSync as existsSync5, readFileSync as readFileSync5, writeFileSync, copyFileSync } from "node:fs";
|
|
1666
1701
|
import { join as join6, dirname as dirname2 } from "node:path";
|
|
1667
1702
|
import { homedir as homedir4 } from "node:os";
|
|
1668
1703
|
function registerInitCommand(program2) {
|
|
1669
1704
|
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) => {
|
|
1670
|
-
console.log(
|
|
1705
|
+
console.log(chalk5.bold.cyan("\n Welcome to kerf-cli!\n"));
|
|
1671
1706
|
console.log(" Setting up cost intelligence for Claude Code...\n");
|
|
1672
1707
|
const kerfDir = join6(homedir4(), ".kerf");
|
|
1673
1708
|
if (!existsSync5(kerfDir)) {
|
|
1674
1709
|
mkdirSync2(kerfDir, { recursive: true });
|
|
1675
|
-
console.log(
|
|
1710
|
+
console.log(chalk5.green(" Created ~/.kerf/"));
|
|
1676
1711
|
}
|
|
1677
1712
|
if (!opts.hooksOnly) {
|
|
1678
1713
|
try {
|
|
1679
1714
|
const db = initDatabase();
|
|
1680
1715
|
runMigrations(db);
|
|
1681
1716
|
db.close();
|
|
1682
|
-
console.log(
|
|
1717
|
+
console.log(chalk5.green(" Created ~/.kerf/kerf.db"));
|
|
1683
1718
|
} catch (err) {
|
|
1684
|
-
console.log(
|
|
1719
|
+
console.log(chalk5.red(` Failed to create database: ${err}`));
|
|
1685
1720
|
}
|
|
1686
1721
|
}
|
|
1687
1722
|
try {
|
|
1688
1723
|
const { execSync: execSync2 } = await import("node:child_process");
|
|
1689
1724
|
try {
|
|
1690
1725
|
execSync2("which rtk", { stdio: "ignore" });
|
|
1691
|
-
console.log(
|
|
1726
|
+
console.log(chalk5.green(" Detected RTK (command compression) -- compatible!"));
|
|
1692
1727
|
} catch {
|
|
1693
1728
|
}
|
|
1694
1729
|
try {
|
|
1695
1730
|
execSync2("which ccusage", { stdio: "ignore" });
|
|
1696
|
-
console.log(
|
|
1731
|
+
console.log(chalk5.green(" Detected ccusage -- will import historical data"));
|
|
1697
1732
|
} catch {
|
|
1698
1733
|
}
|
|
1699
1734
|
} catch {
|
|
@@ -1707,21 +1742,21 @@ function registerInitCommand(program2) {
|
|
|
1707
1742
|
Hooks will be added to ${opts.global ? "~/.claude" : ".claude"}/settings.json`);
|
|
1708
1743
|
try {
|
|
1709
1744
|
installHooks(settingsPath);
|
|
1710
|
-
console.log(
|
|
1745
|
+
console.log(chalk5.green("\n Hooks installed"));
|
|
1711
1746
|
} catch (err) {
|
|
1712
|
-
console.log(
|
|
1747
|
+
console.log(chalk5.yellow(`
|
|
1713
1748
|
Skipped hook installation: ${err}`));
|
|
1714
1749
|
}
|
|
1715
1750
|
}
|
|
1716
|
-
console.log(
|
|
1717
|
-
console.log(
|
|
1718
|
-
console.log(
|
|
1751
|
+
console.log(chalk5.bold("\n Recommended settings for your setup:"));
|
|
1752
|
+
console.log(chalk5.dim(" Add to .claude/settings.json or ~/.claude/settings.json:"));
|
|
1753
|
+
console.log(chalk5.dim(JSON.stringify({
|
|
1719
1754
|
env: {
|
|
1720
1755
|
MAX_THINKING_TOKENS: "10000",
|
|
1721
1756
|
CLAUDE_AUTOCOMPACT_PCT_OVERRIDE: "50"
|
|
1722
1757
|
}
|
|
1723
1758
|
}, null, 4).split("\n").map((l) => " " + l).join("\n")));
|
|
1724
|
-
console.log(
|
|
1759
|
+
console.log(chalk5.bold.cyan("\n Run 'kerf-cli watch' to start the live dashboard!\n"));
|
|
1725
1760
|
});
|
|
1726
1761
|
}
|
|
1727
1762
|
function installHooks(settingsPath) {
|
|
@@ -1748,7 +1783,7 @@ function installHooks(settingsPath) {
|
|
|
1748
1783
|
|
|
1749
1784
|
// src/cli/index.ts
|
|
1750
1785
|
var program = new Command();
|
|
1751
|
-
program.name("kerf-cli").version("0.1.
|
|
1786
|
+
program.name("kerf-cli").version("0.1.5").description("Cost intelligence for Claude Code. Know before you spend.");
|
|
1752
1787
|
registerWatchCommand(program);
|
|
1753
1788
|
registerEstimateCommand(program);
|
|
1754
1789
|
registerBudgetCommand(program);
|