kerf-cli 0.1.2 → 0.1.3

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
@@ -314,8 +314,11 @@ function getPeriodKey(timestamp, period) {
314
314
  }
315
315
  function formatPeriodLabel(key, period) {
316
316
  switch (period) {
317
- case "hour":
318
- return dayjs2(key, "YYYY-MM-DD-HH").format("MMM D, h A");
317
+ case "hour": {
318
+ const parts = key.split("-");
319
+ const dateStr = `${parts[0]}-${parts[1]}-${parts[2]}T${parts[3]}:00:00`;
320
+ return dayjs2(dateStr).format("MMM D, h A");
321
+ }
319
322
  case "day":
320
323
  return dayjs2(key).format("ddd, MMM D");
321
324
  case "week":
@@ -419,8 +422,25 @@ function ContextBar({ used, total, overhead }) {
419
422
 
420
423
  // src/core/tokenCounter.ts
421
424
  import { readFileSync as readFileSync2, existsSync } from "node:fs";
425
+ import { execSync } from "node:child_process";
422
426
  import { join as join3 } from "node:path";
423
427
  import { homedir as homedir2 } from "node:os";
428
+ function findGitRootClaudeMd() {
429
+ try {
430
+ const gitRoot = execSync("git rev-parse --show-toplevel", {
431
+ encoding: "utf-8",
432
+ stdio: ["pipe", "pipe", "ignore"]
433
+ }).trim();
434
+ if (gitRoot && gitRoot !== process.cwd()) {
435
+ return [
436
+ join3(gitRoot, "CLAUDE.md"),
437
+ join3(gitRoot, ".claude", "CLAUDE.md")
438
+ ];
439
+ }
440
+ } catch {
441
+ }
442
+ return [];
443
+ }
424
444
  function estimateTokens(text) {
425
445
  return Math.ceil(text.length / 3.5);
426
446
  }
@@ -473,7 +493,9 @@ function parseClaudeMdSections(content) {
473
493
  function analyzeClaudeMd(filePath) {
474
494
  const paths = filePath ? [filePath] : [
475
495
  join3(process.cwd(), "CLAUDE.md"),
476
- join3(process.cwd(), ".claude", "CLAUDE.md")
496
+ join3(process.cwd(), ".claude", "CLAUDE.md"),
497
+ ...findGitRootClaudeMd(),
498
+ join3(homedir2(), ".claude", "CLAUDE.md")
477
499
  ];
478
500
  for (const p of paths) {
479
501
  if (existsSync(p)) {
@@ -674,8 +696,8 @@ async function estimateTaskCost(taskDescription, options = {}) {
674
696
  let fileList = options.files ?? [];
675
697
  if (fileList.length === 0) {
676
698
  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", {
699
+ const { execSync: execSync2 } = await import("node:child_process");
700
+ const output = execSync2("git diff --name-only HEAD 2>/dev/null || git ls-files -m 2>/dev/null", {
679
701
  cwd,
680
702
  encoding: "utf-8"
681
703
  });
@@ -1415,7 +1437,7 @@ function registerAuditCommand(program2) {
1415
1437
  ` Context Window Health: ${gradeColor.bold(result.grade)} (${result.contextOverhead.percentUsable.toFixed(0)}% usable)
1416
1438
  `
1417
1439
  );
1418
- if (!opts.mcpOnly) {
1440
+ if (!opts.claudeMdOnly) {
1419
1441
  console.log(chalk2.bold(" Ghost Token Breakdown:"));
1420
1442
  const oh = result.contextOverhead;
1421
1443
  const fmt = (label, tokens) => {
@@ -1444,17 +1466,52 @@ function registerAuditCommand(program2) {
1444
1466
  console.log(
1445
1467
  ` Critical rules in dead zone: ${cma.criticalRulesInDeadZone > 0 ? chalk2.red(String(cma.criticalRulesInDeadZone)) : "0"}`
1446
1468
  );
1469
+ if (opts.claudeMdOnly) {
1470
+ console.log();
1471
+ console.log(chalk2.bold(" Sections:"));
1472
+ for (const section of cma.sections) {
1473
+ const zone = section.attentionZone === "low-middle" ? chalk2.red(" [dead zone]") : chalk2.green(" [high attention]");
1474
+ const critical = section.hasCriticalRules ? chalk2.yellow(" *critical rules*") : "";
1475
+ console.log(
1476
+ ` ${section.title.padEnd(30)} ${String(section.tokens).padStart(5)} tokens L${section.lineStart}-${section.lineEnd}${zone}${critical}`
1477
+ );
1478
+ }
1479
+ if (cma.suggestedReorder.length > 0) {
1480
+ console.log();
1481
+ console.log(chalk2.bold(" Suggested section order:"));
1482
+ cma.suggestedReorder.forEach((title, i) => {
1483
+ console.log(` ${i + 1}. ${title}`);
1484
+ });
1485
+ }
1486
+ }
1447
1487
  console.log();
1488
+ } else if (!opts.mcpOnly && !result.claudeMdAnalysis) {
1489
+ console.log(chalk2.yellow(" No CLAUDE.md found in current directory or git root.\n"));
1448
1490
  }
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;
1491
+ if (opts.mcpOnly && result.mcpServers.length > 0) {
1492
+ console.log(chalk2.bold(" MCP Servers:"));
1493
+ for (const server of result.mcpServers) {
1494
+ const heavy = server.isHeavy ? chalk2.red(" [heavy]") : "";
1453
1495
  console.log(
1454
- ` ${i + 1}. ${priorityColor(`[${rec.priority.toUpperCase()}]`)} ${rec.action}`
1496
+ ` ${server.name.padEnd(20)} ${String(server.toolCount).padStart(3)} tools ${server.estimatedTokens.toLocaleString().padStart(6)} tokens${heavy}`
1455
1497
  );
1456
- console.log(chalk2.dim(` Impact: ${rec.impact}`));
1457
- });
1498
+ }
1499
+ console.log();
1500
+ } else if (opts.mcpOnly && result.mcpServers.length === 0) {
1501
+ console.log(chalk2.dim(" No MCP servers configured.\n"));
1502
+ }
1503
+ if (result.recommendations.length > 0) {
1504
+ const filteredRecs = opts.claudeMdOnly ? result.recommendations.filter((r) => r.category === "claude-md") : opts.mcpOnly ? result.recommendations.filter((r) => r.category === "mcp") : result.recommendations;
1505
+ if (filteredRecs.length > 0) {
1506
+ console.log(chalk2.bold(" Recommendations:"));
1507
+ filteredRecs.forEach((rec, i) => {
1508
+ const priorityColor = rec.priority === "high" ? chalk2.red : rec.priority === "medium" ? chalk2.yellow : chalk2.dim;
1509
+ console.log(
1510
+ ` ${i + 1}. ${priorityColor(`[${rec.priority.toUpperCase()}]`)} ${rec.action}`
1511
+ );
1512
+ console.log(chalk2.dim(` Impact: ${rec.impact}`));
1513
+ });
1514
+ }
1458
1515
  }
1459
1516
  console.log();
1460
1517
  if (opts.fix) {
@@ -1628,14 +1685,14 @@ function registerInitCommand(program2) {
1628
1685
  }
1629
1686
  }
1630
1687
  try {
1631
- const { execSync } = await import("node:child_process");
1688
+ const { execSync: execSync2 } = await import("node:child_process");
1632
1689
  try {
1633
- execSync("which rtk", { stdio: "ignore" });
1690
+ execSync2("which rtk", { stdio: "ignore" });
1634
1691
  console.log(chalk4.green(" Detected RTK (command compression) -- compatible!"));
1635
1692
  } catch {
1636
1693
  }
1637
1694
  try {
1638
- execSync("which ccusage", { stdio: "ignore" });
1695
+ execSync2("which ccusage", { stdio: "ignore" });
1639
1696
  console.log(chalk4.green(" Detected ccusage -- will import historical data"));
1640
1697
  } catch {
1641
1698
  }