kerf-cli 0.1.3 → 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
@@ -576,10 +577,13 @@ function Dashboard({ sessionFilePath, interval }) {
576
577
  const timer = setInterval(refresh, interval);
577
578
  return () => clearInterval(timer);
578
579
  }, [sessionFilePath, interval]);
579
- useInput((input) => {
580
- if (input === "q") exit();
581
- if (input === "b") setShowBudget((prev) => !prev);
582
- });
580
+ useInput(
581
+ (input) => {
582
+ if (input === "q") exit();
583
+ if (input === "b") setShowBudget((prev) => !prev);
584
+ },
585
+ { isActive: process.stdin.isTTY ?? false }
586
+ );
583
587
  if (!session || session.messages.length === 0) {
584
588
  return /* @__PURE__ */ jsx3(Box3, { paddingX: 1, children: /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "Waiting for session data..." }) });
585
589
  }
@@ -659,6 +663,12 @@ function registerWatchCommand(program2) {
659
663
  );
660
664
  process.exit(0);
661
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
+ }
662
672
  const { waitUntilExit } = render(
663
673
  React2.createElement(Dashboard, { sessionFilePath, interval })
664
674
  );
@@ -670,6 +680,7 @@ function registerWatchCommand(program2) {
670
680
  import React3 from "react";
671
681
  import { render as render2 } from "ink";
672
682
  import { glob as glob2 } from "glob";
683
+ import chalk from "chalk";
673
684
 
674
685
  // src/core/estimator.ts
675
686
  import { glob } from "glob";
@@ -859,17 +870,42 @@ function registerEstimateCommand(program2) {
859
870
  }
860
871
  if (opts.compare) {
861
872
  const models = ["sonnet", "opus", "haiku"];
862
- for (const model of models) {
863
- const estimate2 = await estimateTaskCost(task, { model, files, cwd: process.cwd() });
864
- if (opts.json) {
873
+ if (opts.json) {
874
+ for (const model of models) {
875
+ const estimate2 = await estimateTaskCost(task, { model, files, cwd: process.cwd() });
865
876
  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
877
  }
878
+ return;
872
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
+ );
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();
873
909
  return;
874
910
  }
875
911
  const estimate = await estimateTaskCost(task, {
@@ -889,7 +925,7 @@ function registerEstimateCommand(program2) {
889
925
  }
890
926
 
891
927
  // src/cli/commands/budget.ts
892
- import chalk from "chalk";
928
+ import chalk2 from "chalk";
893
929
 
894
930
  // src/core/budgetManager.ts
895
931
  import dayjs3 from "dayjs";
@@ -1095,12 +1131,12 @@ function registerBudgetCommand(program2) {
1095
1131
  const projectPath = opts.project || process.cwd();
1096
1132
  const amountNum = parseFloat(amount);
1097
1133
  if (isNaN(amountNum) || amountNum <= 0) {
1098
- console.log(chalk.red("Budget amount must be a positive number."));
1134
+ console.log(chalk2.red("Budget amount must be a positive number."));
1099
1135
  process.exit(1);
1100
1136
  }
1101
1137
  manager.setBudget(projectPath, amountNum, opts.period);
1102
1138
  console.log(
1103
- chalk.green(`Budget set: ${formatCost(amountNum)}/${opts.period} for ${projectPath}`)
1139
+ chalk2.green(`Budget set: ${formatCost(amountNum)}/${opts.period} for ${projectPath}`)
1104
1140
  );
1105
1141
  manager.close();
1106
1142
  });
@@ -1123,14 +1159,14 @@ function registerBudgetCommand(program2) {
1123
1159
  const filled = Math.round(Math.min(pct, 100) / 100 * barWidth);
1124
1160
  const empty = barWidth - filled;
1125
1161
  const color = pct < 50 ? "green" : pct < 80 ? "yellow" : "red";
1126
- const barColor = color === "green" ? chalk.green : color === "yellow" ? chalk.yellow : chalk.red;
1127
- 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"));
1128
1164
  console.log(` Period: ${status.period} (${status.periodStart.slice(0, 10)} to ${status.periodEnd.slice(0, 10)})`);
1129
1165
  console.log(` Budget: ${formatCost(status.budget)}`);
1130
1166
  console.log(` Spent: ${barColor(formatCost(status.spent))}`);
1131
1167
  console.log(` ${barColor("[" + "\u2588".repeat(filled) + "\u2591".repeat(empty) + "]")} ${pct.toFixed(1)}%`);
1132
1168
  if (status.isOverBudget) {
1133
- console.log(chalk.red.bold(`
1169
+ console.log(chalk2.red.bold(`
1134
1170
  OVER BUDGET by ${formatCost(status.spent - status.budget)}`));
1135
1171
  }
1136
1172
  console.log();
@@ -1144,12 +1180,12 @@ function registerBudgetCommand(program2) {
1144
1180
  manager.close();
1145
1181
  return;
1146
1182
  }
1147
- console.log(chalk.bold.cyan("\n kerf-cli budget list\n"));
1183
+ console.log(chalk2.bold.cyan("\n kerf-cli budget list\n"));
1148
1184
  for (const p of projects) {
1149
1185
  const budgetStr = p.budget ? `${formatCost(p.budget)}/${p.period}` : "no budget";
1150
1186
  const spentStr = p.spent > 0 ? ` (spent: ${formatCost(p.spent)})` : "";
1151
- console.log(` ${chalk.bold(p.name)} \u2014 ${budgetStr}${spentStr}`);
1152
- console.log(chalk.dim(` ${p.path}`));
1187
+ console.log(` ${chalk2.bold(p.name)} \u2014 ${budgetStr}${spentStr}`);
1188
+ console.log(chalk2.dim(` ${p.path}`));
1153
1189
  }
1154
1190
  console.log();
1155
1191
  manager.close();
@@ -1159,7 +1195,7 @@ function registerBudgetCommand(program2) {
1159
1195
  const projectPath = opts.project || process.cwd();
1160
1196
  const removed = manager.removeBudget(projectPath);
1161
1197
  if (removed) {
1162
- console.log(chalk.green("Budget removed."));
1198
+ console.log(chalk2.green("Budget removed."));
1163
1199
  } else {
1164
1200
  console.log("No budget found for this project.");
1165
1201
  }
@@ -1168,7 +1204,7 @@ function registerBudgetCommand(program2) {
1168
1204
  }
1169
1205
 
1170
1206
  // src/cli/commands/audit.ts
1171
- import chalk2 from "chalk";
1207
+ import chalk3 from "chalk";
1172
1208
 
1173
1209
  // src/audit/ghostTokens.ts
1174
1210
  function calculateGrade(percentUsable) {
@@ -1431,14 +1467,14 @@ function registerAuditCommand(program2) {
1431
1467
  console.log(JSON.stringify(result, null, 2));
1432
1468
  return;
1433
1469
  }
1434
- const gradeColor = result.grade === "A" ? chalk2.green : result.grade === "B" ? chalk2.yellow : chalk2.red;
1435
- 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"));
1436
1472
  console.log(
1437
1473
  ` Context Window Health: ${gradeColor.bold(result.grade)} (${result.contextOverhead.percentUsable.toFixed(0)}% usable)
1438
1474
  `
1439
1475
  );
1440
1476
  if (!opts.claudeMdOnly) {
1441
- console.log(chalk2.bold(" Ghost Token Breakdown:"));
1477
+ console.log(chalk3.bold(" Ghost Token Breakdown:"));
1442
1478
  const oh = result.contextOverhead;
1443
1479
  const fmt = (label, tokens) => {
1444
1480
  const pct = (tokens / CONTEXT_WINDOW_SIZE * 100).toFixed(1);
@@ -1458,27 +1494,27 @@ function registerAuditCommand(program2) {
1458
1494
  }
1459
1495
  if (!opts.mcpOnly && result.claudeMdAnalysis) {
1460
1496
  const cma = result.claudeMdAnalysis;
1461
- console.log(chalk2.bold(" CLAUDE.md Analysis:"));
1497
+ console.log(chalk3.bold(" CLAUDE.md Analysis:"));
1462
1498
  console.log(
1463
- ` Lines: ${cma.totalLines}${cma.isOverLineLimit ? chalk2.yellow(" (over 200 limit)") : ""}`
1499
+ ` Lines: ${cma.totalLines}${cma.isOverLineLimit ? chalk3.yellow(" (over 200 limit)") : ""}`
1464
1500
  );
1465
1501
  console.log(` Tokens: ${cma.totalTokens.toLocaleString()}`);
1466
1502
  console.log(
1467
- ` 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"}`
1468
1504
  );
1469
1505
  if (opts.claudeMdOnly) {
1470
1506
  console.log();
1471
- console.log(chalk2.bold(" Sections:"));
1507
+ console.log(chalk3.bold(" Sections:"));
1472
1508
  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*") : "";
1509
+ const zone = section.attentionZone === "low-middle" ? chalk3.red(" [dead zone]") : chalk3.green(" [high attention]");
1510
+ const critical = section.hasCriticalRules ? chalk3.yellow(" *critical rules*") : "";
1475
1511
  console.log(
1476
1512
  ` ${section.title.padEnd(30)} ${String(section.tokens).padStart(5)} tokens L${section.lineStart}-${section.lineEnd}${zone}${critical}`
1477
1513
  );
1478
1514
  }
1479
1515
  if (cma.suggestedReorder.length > 0) {
1480
1516
  console.log();
1481
- console.log(chalk2.bold(" Suggested section order:"));
1517
+ console.log(chalk3.bold(" Suggested section order:"));
1482
1518
  cma.suggestedReorder.forEach((title, i) => {
1483
1519
  console.log(` ${i + 1}. ${title}`);
1484
1520
  });
@@ -1486,42 +1522,42 @@ function registerAuditCommand(program2) {
1486
1522
  }
1487
1523
  console.log();
1488
1524
  } else if (!opts.mcpOnly && !result.claudeMdAnalysis) {
1489
- console.log(chalk2.yellow(" No CLAUDE.md found in current directory or git root.\n"));
1525
+ console.log(chalk3.yellow(" No CLAUDE.md found in current directory or git root.\n"));
1490
1526
  }
1491
1527
  if (opts.mcpOnly && result.mcpServers.length > 0) {
1492
- console.log(chalk2.bold(" MCP Servers:"));
1528
+ console.log(chalk3.bold(" MCP Servers:"));
1493
1529
  for (const server of result.mcpServers) {
1494
- const heavy = server.isHeavy ? chalk2.red(" [heavy]") : "";
1530
+ const heavy = server.isHeavy ? chalk3.red(" [heavy]") : "";
1495
1531
  console.log(
1496
1532
  ` ${server.name.padEnd(20)} ${String(server.toolCount).padStart(3)} tools ${server.estimatedTokens.toLocaleString().padStart(6)} tokens${heavy}`
1497
1533
  );
1498
1534
  }
1499
1535
  console.log();
1500
1536
  } else if (opts.mcpOnly && result.mcpServers.length === 0) {
1501
- console.log(chalk2.dim(" No MCP servers configured.\n"));
1537
+ console.log(chalk3.dim(" No MCP servers configured.\n"));
1502
1538
  }
1503
1539
  if (result.recommendations.length > 0) {
1504
1540
  const filteredRecs = opts.claudeMdOnly ? result.recommendations.filter((r) => r.category === "claude-md") : opts.mcpOnly ? result.recommendations.filter((r) => r.category === "mcp") : result.recommendations;
1505
1541
  if (filteredRecs.length > 0) {
1506
- console.log(chalk2.bold(" Recommendations:"));
1542
+ console.log(chalk3.bold(" Recommendations:"));
1507
1543
  filteredRecs.forEach((rec, i) => {
1508
- const priorityColor = rec.priority === "high" ? chalk2.red : rec.priority === "medium" ? chalk2.yellow : chalk2.dim;
1544
+ const priorityColor = rec.priority === "high" ? chalk3.red : rec.priority === "medium" ? chalk3.yellow : chalk3.dim;
1509
1545
  console.log(
1510
1546
  ` ${i + 1}. ${priorityColor(`[${rec.priority.toUpperCase()}]`)} ${rec.action}`
1511
1547
  );
1512
- console.log(chalk2.dim(` Impact: ${rec.impact}`));
1548
+ console.log(chalk3.dim(` Impact: ${rec.impact}`));
1513
1549
  });
1514
1550
  }
1515
1551
  }
1516
1552
  console.log();
1517
1553
  if (opts.fix) {
1518
- 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"));
1519
1555
  }
1520
1556
  });
1521
1557
  }
1522
1558
 
1523
1559
  // src/cli/commands/report.ts
1524
- import chalk3 from "chalk";
1560
+ import chalk4 from "chalk";
1525
1561
  import dayjs4 from "dayjs";
1526
1562
  function registerReportCommand(program2) {
1527
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) => {
@@ -1613,10 +1649,10 @@ function registerReportCommand(program2) {
1613
1649
  return;
1614
1650
  }
1615
1651
  const periodLabel = opts.period === "today" ? now.format("ddd, MMM D, YYYY") : opts.period;
1616
- console.log(chalk3.bold.cyan(`
1652
+ console.log(chalk4.bold.cyan(`
1617
1653
  kerf-cli report -- ${periodLabel}
1618
1654
  `));
1619
- console.log(` Total Cost: ${chalk3.bold(formatCost(totalCost))}`);
1655
+ console.log(` Total Cost: ${chalk4.bold(formatCost(totalCost))}`);
1620
1656
  console.log(` Total Tokens: ${formatTokens(totalInput)} in / ${formatTokens(totalOutput)} out`);
1621
1657
  console.log(` Cache Hit Rate: ${cacheHitRate.toFixed(1)}%`);
1622
1658
  console.log(` Sessions: ${sessionSummaries.length}`);
@@ -1629,7 +1665,7 @@ function registerReportCommand(program2) {
1629
1665
  existing.sessions++;
1630
1666
  byModel.set(s.model, existing);
1631
1667
  }
1632
- console.log(chalk3.bold(" Model Breakdown:"));
1668
+ console.log(chalk4.bold(" Model Breakdown:"));
1633
1669
  for (const [model, data] of byModel) {
1634
1670
  const pct = totalCost > 0 ? (data.cost / totalCost * 100).toFixed(1) : "0";
1635
1671
  const shortModel = model.replace("claude-", "").replace(/-20\d{6}$/, "");
@@ -1640,7 +1676,7 @@ function registerReportCommand(program2) {
1640
1676
  console.log();
1641
1677
  }
1642
1678
  if (opts.sessions) {
1643
- console.log(chalk3.bold(" Session Breakdown:"));
1679
+ console.log(chalk4.bold(" Session Breakdown:"));
1644
1680
  for (const s of sessionSummaries.sort((a, b) => b.cost - a.cost)) {
1645
1681
  console.log(` ${s.id.slice(0, 12)} ${formatCost(s.cost)} ${s.messages} msgs [${s.model}]`);
1646
1682
  }
@@ -1648,7 +1684,7 @@ function registerReportCommand(program2) {
1648
1684
  }
1649
1685
  const hourly = aggregateCosts(allMessages, "hour");
1650
1686
  if (hourly.length > 1) {
1651
- console.log(chalk3.bold(" Hourly:"));
1687
+ console.log(chalk4.bold(" Hourly:"));
1652
1688
  const maxCost = Math.max(...hourly.map((h) => h.totalCost));
1653
1689
  for (const h of hourly.slice(-8)) {
1654
1690
  const barLen = maxCost > 0 ? Math.round(h.totalCost / maxCost * 12) : 0;
@@ -1661,39 +1697,39 @@ function registerReportCommand(program2) {
1661
1697
  }
1662
1698
 
1663
1699
  // src/cli/commands/init.ts
1664
- import chalk4 from "chalk";
1700
+ import chalk5 from "chalk";
1665
1701
  import { mkdirSync as mkdirSync2, existsSync as existsSync5, readFileSync as readFileSync5, writeFileSync, copyFileSync } from "node:fs";
1666
1702
  import { join as join6, dirname as dirname2 } from "node:path";
1667
1703
  import { homedir as homedir4 } from "node:os";
1668
1704
  function registerInitCommand(program2) {
1669
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) => {
1670
- console.log(chalk4.bold.cyan("\n Welcome to kerf-cli!\n"));
1706
+ console.log(chalk5.bold.cyan("\n Welcome to kerf-cli!\n"));
1671
1707
  console.log(" Setting up cost intelligence for Claude Code...\n");
1672
1708
  const kerfDir = join6(homedir4(), ".kerf");
1673
1709
  if (!existsSync5(kerfDir)) {
1674
1710
  mkdirSync2(kerfDir, { recursive: true });
1675
- console.log(chalk4.green(" Created ~/.kerf/"));
1711
+ console.log(chalk5.green(" Created ~/.kerf/"));
1676
1712
  }
1677
1713
  if (!opts.hooksOnly) {
1678
1714
  try {
1679
1715
  const db = initDatabase();
1680
1716
  runMigrations(db);
1681
1717
  db.close();
1682
- console.log(chalk4.green(" Created ~/.kerf/kerf.db"));
1718
+ console.log(chalk5.green(" Created ~/.kerf/kerf.db"));
1683
1719
  } catch (err) {
1684
- console.log(chalk4.red(` Failed to create database: ${err}`));
1720
+ console.log(chalk5.red(` Failed to create database: ${err}`));
1685
1721
  }
1686
1722
  }
1687
1723
  try {
1688
1724
  const { execSync: execSync2 } = await import("node:child_process");
1689
1725
  try {
1690
1726
  execSync2("which rtk", { stdio: "ignore" });
1691
- console.log(chalk4.green(" Detected RTK (command compression) -- compatible!"));
1727
+ console.log(chalk5.green(" Detected RTK (command compression) -- compatible!"));
1692
1728
  } catch {
1693
1729
  }
1694
1730
  try {
1695
1731
  execSync2("which ccusage", { stdio: "ignore" });
1696
- console.log(chalk4.green(" Detected ccusage -- will import historical data"));
1732
+ console.log(chalk5.green(" Detected ccusage -- will import historical data"));
1697
1733
  } catch {
1698
1734
  }
1699
1735
  } catch {
@@ -1707,21 +1743,21 @@ function registerInitCommand(program2) {
1707
1743
  Hooks will be added to ${opts.global ? "~/.claude" : ".claude"}/settings.json`);
1708
1744
  try {
1709
1745
  installHooks(settingsPath);
1710
- console.log(chalk4.green("\n Hooks installed"));
1746
+ console.log(chalk5.green("\n Hooks installed"));
1711
1747
  } catch (err) {
1712
- console.log(chalk4.yellow(`
1748
+ console.log(chalk5.yellow(`
1713
1749
  Skipped hook installation: ${err}`));
1714
1750
  }
1715
1751
  }
1716
- console.log(chalk4.bold("\n Recommended settings for your setup:"));
1717
- console.log(chalk4.dim(" Add to .claude/settings.json or ~/.claude/settings.json:"));
1718
- 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({
1719
1755
  env: {
1720
1756
  MAX_THINKING_TOKENS: "10000",
1721
1757
  CLAUDE_AUTOCOMPACT_PCT_OVERRIDE: "50"
1722
1758
  }
1723
1759
  }, null, 4).split("\n").map((l) => " " + l).join("\n")));
1724
- 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"));
1725
1761
  });
1726
1762
  }
1727
1763
  function installHooks(settingsPath) {
@@ -1747,8 +1783,10 @@ function installHooks(settingsPath) {
1747
1783
  }
1748
1784
 
1749
1785
  // src/cli/index.ts
1786
+ var require2 = createRequire(import.meta.url);
1787
+ var pkg = require2("../../package.json");
1750
1788
  var program = new Command();
1751
- 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.");
1752
1790
  registerWatchCommand(program);
1753
1791
  registerEstimateCommand(program);
1754
1792
  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-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 // key is \"YYYY-MM-DD-HH\", convert to parseable format\n const parts = key.split(\"-\");\n const dateStr = `${parts[0]}-${parts[1]}-${parts[2]}T${parts[3]}:00:00`;\n return dayjs(dateStr).format(\"MMM D, h A\");\n }\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 { execSync } from \"node:child_process\";\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\nfunction findGitRootClaudeMd(): string[] {\n try {\n const gitRoot = execSync(\"git rev-parse --show-toplevel\", {\n encoding: \"utf-8\",\n stdio: [\"pipe\", \"pipe\", \"ignore\"],\n }).trim();\n if (gitRoot && gitRoot !== process.cwd()) {\n return [\n join(gitRoot, \"CLAUDE.md\"),\n join(gitRoot, \".claude\", \"CLAUDE.md\"),\n ];\n }\n } catch {\n // Not in a git repo\n }\n return [];\n}\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 ...findGitRootClaudeMd(),\n join(homedir(), \".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 // Ghost token breakdown — show unless --claude-md-only\n if (!opts.claudeMdOnly) {\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 // CLAUDE.md analysis — show unless --mcp-only\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\n if (opts.claudeMdOnly) {\n // Show per-section breakdown when claude-md-only\n console.log();\n console.log(chalk.bold(\" Sections:\"));\n for (const section of cma.sections) {\n const zone = section.attentionZone === \"low-middle\" ? chalk.red(\" [dead zone]\") : chalk.green(\" [high attention]\");\n const critical = section.hasCriticalRules ? chalk.yellow(\" *critical rules*\") : \"\";\n console.log(\n ` ${section.title.padEnd(30)} ${String(section.tokens).padStart(5)} tokens L${section.lineStart}-${section.lineEnd}${zone}${critical}`,\n );\n }\n\n if (cma.suggestedReorder.length > 0) {\n console.log();\n console.log(chalk.bold(\" Suggested section order:\"));\n cma.suggestedReorder.forEach((title, i) => {\n console.log(` ${i + 1}. ${title}`);\n });\n }\n }\n console.log();\n } else if (!opts.mcpOnly && !result.claudeMdAnalysis) {\n console.log(chalk.yellow(\" No CLAUDE.md found in current directory or git root.\\n\"));\n }\n\n // MCP details — show when --mcp-only\n if (opts.mcpOnly && result.mcpServers.length > 0) {\n console.log(chalk.bold(\" MCP Servers:\"));\n for (const server of result.mcpServers) {\n const heavy = server.isHeavy ? chalk.red(\" [heavy]\") : \"\";\n console.log(\n ` ${server.name.padEnd(20)} ${String(server.toolCount).padStart(3)} tools ${server.estimatedTokens.toLocaleString().padStart(6)} tokens${heavy}`,\n );\n }\n console.log();\n } else if (opts.mcpOnly && result.mcpServers.length === 0) {\n console.log(chalk.dim(\" No MCP servers configured.\\n\"));\n }\n\n // Recommendations — always show\n if (result.recommendations.length > 0) {\n const filteredRecs = opts.claudeMdOnly\n ? result.recommendations.filter((r) => r.category === \"claude-md\")\n : opts.mcpOnly\n ? result.recommendations.filter((r) => r.category === \"mcp\")\n : result.recommendations;\n\n if (filteredRecs.length > 0) {\n console.log(chalk.bold(\" Recommendations:\"));\n filteredRecs.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 }\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":";;;AAAA,SAAS,eAAe;;;ACAxB,OAAOA,YAAW;AAClB,SAAS,cAAc;;;ACDvB,SAAS,cAAc,gBAAgB;AACvC,SAAS,eAAe;AACxB,SAAS,QAAAC,OAAM,gBAAgB;AAG/B,OAAO,WAAW;AAClB,SAAS,aAAa;;;ACNtB,SAAS,eAAe;AACxB,SAAS,YAAY;AAGd,IAAM,iBAA6B;AAAA,EACxC,cAAc;AAAA,EACd,wBAAwB;AAAA,EACxB,sBAAsB;AAAA,EACtB,iBAAiB;AAAA,EACjB,SAAS,KAAK,QAAQ,GAAG,OAAO;AAAA,EAChC,aAAa;AACf;AAEO,IAAM,sBAAsB;AAC5B,IAAM,uBAAuB;AAC7B,IAAM,wBAAwB;AAC9B,IAAM,4BAA4B;AAClC,IAAM,sBAAsB;AAC5B,IAAM,uBAAuB;AAE7B,IAAM,sBAAsB,KAAK,QAAQ,GAAG,WAAW,UAAU;AACjE,IAAM,yBAAyB,KAAK,QAAQ,GAAG,WAAW,eAAe;AACzE,IAAM,eAAe,KAAK,QAAQ,GAAG,SAAS,SAAS;AACvD,IAAM,mBAAmB,KAAK,QAAQ,GAAG,SAAS,mBAAmB;;;ADA5E,SAAS,aAAa,KAAoD;AACxE,SAAO,IAAI,SAAS,SAAS,IAAI,SAAS,IAAI,OAAO,SAAS;AAChE;AAEA,SAAS,iBAAiB,KAAqC;AAC7D,SAAO,IAAI,SAAS,MAAM;AAC5B;AAEA,SAAS,aAAa,KAAqC;AACzD,SAAO,IAAI,SAAS,SAAS;AAC/B;AAEA,SAAS,iBAAiB,KAA8B;AACtD,SAAO,IAAI,aAAa,MAAM,EAAE,YAAY;AAC9C;AAEO,SAAS,eAAe,MAAsC;AACnE,QAAM,UAAU,KAAK,KAAK;AAC1B,MAAI,CAAC,QAAS,QAAO;AACrB,MAAI;AACF,WAAO,KAAK,MAAM,OAAO;AAAA,EAC3B,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,kBAAkB,SAAiB,WAAoC;AACrF,QAAM,QAAQ,QAAQ,MAAM,IAAI;AAChC,QAAM,aAAa,oBAAI,IAA2B;AAClD,MAAI,mBAAmB;AAEvB,aAAW,QAAQ,OAAO;AACxB,UAAM,MAAM,eAAe,IAAI;AAC/B,QAAI,CAAC,IAAK;AAEV,UAAM,QAAQ,aAAa,GAAG;AAC9B,QAAI,CAAC,MAAO;AAEZ,UAAM,KAAK,iBAAiB,GAAG,KAAK,QAAQ,kBAAkB;AAC9D,UAAM,QAAQ,aAAa,GAAG,KAAK;AACnC,UAAM,YAAY,iBAAiB,GAAG;AAEtC,UAAM,WAAW,WAAW,IAAI,EAAE;AAClC,UAAM,cAA4B;AAAA,MAChC,cAAc,MAAM,gBAAgB,UAAU,MAAM,gBAAgB;AAAA,MACpE,eAAe,MAAM,iBAAiB,UAAU,MAAM,iBAAiB;AAAA,MACvE,6BACE,MAAM,+BAA+B,UAAU,MAAM,+BAA+B;AAAA,MACtF,yBACE,MAAM,2BAA2B,UAAU,MAAM,2BAA2B;AAAA,IAChF;AAGA,eAAW,IAAI,IAAI;AAAA,MACjB;AAAA,MACA,OAAO,UAAU,YAAY,QAAQ,UAAU,SAAS;AAAA,MACxD;AAAA,MACA,OAAO;AAAA,MACP,cAAc,IAAI,kBAAkB,UAAU,gBAAgB;AAAA,IAChE,CAAC;AAAA,EACH;AAEA,SAAO,MAAM,KAAK,WAAW,OAAO,CAAC;AACvC;AAEO,SAAS,iBAAiB,UAAiC;AAChE,QAAM,UAAU,aAAa,UAAU,OAAO;AAC9C,QAAM,YAAY,SAAS,UAAU,QAAQ;AAC7C,QAAM,WAAW,kBAAkB,SAAS,SAAS;AAErD,QAAM,SAAS,SAAS;AAAA,IACtB,CAAC,KAAK,SAAS;AAAA,MACb,OAAO,IAAI,QAAQ,IAAI,MAAM;AAAA,MAC7B,QAAQ,IAAI,SAAS,IAAI,MAAM;AAAA,MAC/B,WAAW,IAAI,YAAY,IAAI,MAAM;AAAA,MACrC,eAAe,IAAI,gBAAgB,IAAI,MAAM;AAAA,MAC7C,MAAM,IAAI,QAAQ,IAAI,gBAAgB;AAAA,IACxC;AAAA,IACA,EAAE,OAAO,GAAG,QAAQ,GAAG,WAAW,GAAG,eAAe,GAAG,MAAM,EAAE;AAAA,EACjE;AAEA,QAAM,aAAa,SAAS,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,KAAK;AAEzD,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,kBAAkB,OAAO;AAAA,IACzB,mBAAmB,OAAO;AAAA,IAC1B,sBAAsB,OAAO;AAAA,IAC7B,0BAA0B,OAAO;AAAA,IACjC,cAAc,OAAO;AAAA,IACrB,WAAW,WAAW,CAAC,KAAK;AAAA,IAC5B,SAAS,WAAW,WAAW,SAAS,CAAC,KAAK;AAAA,IAC9C,cAAc,SAAS;AAAA,EACzB;AACF;AAEA,eAAsB,eAAe,SAAqC;AACxE,QAAM,MAAM,WAAW;AACvB,QAAM,QAAkB,CAAC;AAEzB,iBAAe,KAAK,YAAmC;AACrD,QAAI;AACJ,QAAI;AACF,gBAAU,MAAM,QAAQ,YAAY,EAAE,eAAe,KAAK,CAAC;AAAA,IAC7D,QAAQ;AACN;AAAA,IACF;AACA,eAAW,SAAS,SAAS;AAC3B,YAAM,WAAWC,MAAK,YAAY,MAAM,IAAI;AAC5C,UAAI,MAAM,YAAY,GAAG;AACvB,cAAM,KAAK,QAAQ;AAAA,MACrB,WAAW,MAAM,KAAK,SAAS,QAAQ,GAAG;AACxC,cAAM,KAAK,QAAQ;AAAA,MACrB;AAAA,IACF;AAAA,EACF;AAEA,QAAM,KAAK,GAAG;AACd,SAAO;AACT;AAEA,eAAsB,kBAAkB,SAA0C;AAChF,QAAM,QAAQ,MAAM,eAAe,OAAO;AAC1C,QAAM,SAAS,MAAM,EAAE,SAAS,sBAAsB,MAAM;AAC5D,QAAM,iBAAgC,CAAC;AAEvC,aAAW,YAAY,OAAO;AAC5B,QAAI;AACF,YAAM,OAAO,SAAS,QAAQ;AAC9B,YAAM,eAAe,MAAM,KAAK,KAAK;AACrC,UAAI,aAAa,QAAQ,MAAM,GAAG;AAChC,cAAM,UAAU,aAAa,UAAU,OAAO;AAC9C,cAAM,YAAY,SAAS,UAAU,QAAQ;AAC7C,cAAM,WAAW,kBAAkB,SAAS,SAAS;AAErD,YAAI,SAAS,WAAW,EAAG;AAE3B,cAAM,aAAa,SAAS,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,KAAK;AACzD,uBAAe,KAAK;AAAA,UAClB;AAAA,UACA;AAAA,UACA;AAAA,UACA,WAAW,WAAW,CAAC,KAAK;AAAA,UAC5B,SAAS,WAAW,WAAW,SAAS,CAAC,KAAK;AAAA,UAC9C,cAAc,KAAK;AAAA,QACrB,CAAC;AAAA,MACH;AAAA,IACF,QAAQ;AACN;AAAA,IACF;AAAA,EACF;AAEA,SAAO,eAAe;AAAA,IACpB,CAAC,GAAG,MAAM,EAAE,aAAa,QAAQ,IAAI,EAAE,aAAa,QAAQ;AAAA,EAC9D;AACF;;;AEpLA,SAAgB,UAAU,iBAAiB;AAC3C,SAAS,OAAAC,MAAK,QAAAC,OAAM,UAAU,cAAc;;;ACA5C,SAAS,KAAK,YAAY;;;ACD1B,OAAOC,YAAW;AAClB,OAAO,aAAa;AAapBC,OAAM,OAAO,OAAO;AAEb,IAAM,gBAA+B;AAAA,EAC1C,4BAA4B;AAAA,IAC1B,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,eAAe;AAAA,EACjB;AAAA,EACA,0BAA0B;AAAA,IACxB,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,eAAe;AAAA,EACjB;AAAA,EACA,2BAA2B;AAAA,IACzB,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,eAAe;AAAA,EACjB;AACF;AAGA,IAAM,gBAAwC;AAAA,EAC5C,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,OAAO;AACT;AAEO,SAAS,oBAAoB,OAA6B;AAC/D,QAAM,WAAW,cAAc,KAAK,KAAK;AAEzC,MAAI,cAAc,QAAQ,EAAG,QAAO,cAAc,QAAQ;AAC1D,QAAM,QAAQ,OAAO,KAAK,aAAa,EAAE,KAAK,CAAC,MAAM,SAAS,WAAW,CAAC,KAAK,EAAE,WAAW,QAAQ,CAAC;AACrG,MAAI,MAAO,QAAO,cAAc,KAAK;AAErC,SAAO,cAAc,0BAA0B;AACjD;AAEO,SAAS,qBAAqB,KAAmC;AAEtE,MAAI,IAAI,iBAAiB,QAAQ,IAAI,eAAe,GAAG;AACrD,WAAO;AAAA,MACL,WAAW;AAAA,MACX,YAAY;AAAA,MACZ,eAAe;AAAA,MACf,mBAAmB;AAAA,MACnB,WAAW,IAAI;AAAA,IACjB;AAAA,EACF;AAEA,QAAM,UAAU,oBAAoB,IAAI,KAAK;AAC7C,QAAM,UAAU;AAGhB,QAAM,YAAa,IAAI,MAAM,eAAe,QAAQ,QAAS;AAC7D,QAAM,aAAc,IAAI,MAAM,gBAAgB,QAAQ,SAAU;AAChE,QAAM,gBAAiB,IAAI,MAAM,0BAA0B,QAAQ,YAAa;AAChF,QAAM,oBAAqB,IAAI,MAAM,8BAA8B,QAAQ,gBAAiB;AAE5F,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,WAAW,YAAY,aAAa,gBAAgB;AAAA,EACtD;AACF;AAwCO,SAAS,sBAAsB,UAAyC;AAC7E,MAAI,SAAS,SAAS,GAAG;AACvB,WAAO,EAAE,kBAAkB,GAAG,iBAAiB,GAAG,qBAAqB,GAAG,kBAAkB,EAAE;AAAA,EAChG;AAGA,QAAM,SAAS,SAAS,MAAM,GAAG;AACjC,QAAM,YAAYC,OAAM,OAAO,CAAC,EAAE,SAAS;AAC3C,QAAM,WAAWA,OAAM,OAAO,OAAO,SAAS,CAAC,EAAE,SAAS;AAC1D,QAAM,kBAAkB,SAAS,KAAK,WAAW,UAAU,IAAI;AAE/D,MAAI,mBAAmB,GAAG;AACxB,WAAO,EAAE,kBAAkB,GAAG,iBAAiB,GAAG,qBAAqB,GAAG,kBAAkB,EAAE;AAAA,EAChG;AAEA,MAAI,YAAY;AAChB,MAAI,cAAc;AAClB,aAAW,OAAO,QAAQ;AACxB,iBAAa,qBAAqB,GAAG,EAAE;AACvC,mBAAe,IAAI,MAAM,eAAe,IAAI,MAAM;AAAA,EACpD;AAEA,QAAM,mBAAmB,YAAY;AACrC,QAAM,kBAAkB,cAAc;AACtC,QAAM,gBAAgB,uBAAuB;AAC7C,QAAM,sBAAsB,mBAAmB;AAG/C,QAAM,cAAcA,OAAM,EAAE,SAAS,sBAAsB,MAAM;AACjE,QAAM,UAAUA,OAAM,EAAE,KAAK,aAAa,UAAU,IAAI;AACxD,QAAM,mBAAmB,KAAK,IAAI,GAAG,gBAAgB,OAAO;AAE5D,SAAO,EAAE,kBAAkB,iBAAiB,qBAAqB,iBAAiB;AACpF;AAEO,SAAS,eACd,UACA,QACkB;AAClB,QAAM,SAAS,oBAAI,IAAkE;AAErF,aAAW,OAAO,UAAU;AAC1B,UAAM,MAAM,aAAa,IAAI,WAAW,MAAM;AAC9C,UAAM,QAAQ,OAAO,IAAI,GAAG,KAAK,EAAE,UAAU,CAAC,GAAG,UAAU,oBAAI,IAAI,EAAE;AACrE,UAAM,SAAS,KAAK,GAAG;AACvB,UAAM,SAAS,IAAI,IAAI,GAAG,MAAM,GAAG,EAAE,CAAC,KAAK,IAAI,EAAE;AACjD,WAAO,IAAI,KAAK,KAAK;AAAA,EACvB;AAEA,SAAO,MAAM,KAAK,OAAO,QAAQ,CAAC,EAAE,IAAI,CAAC,CAAC,KAAK,KAAK,MAAM;AACxD,QAAI,YAAY;AAChB,QAAI,aAAa;AACjB,QAAI,cAAc;AAElB,eAAW,OAAO,MAAM,UAAU;AAChC,mBAAa,qBAAqB,GAAG,EAAE;AACvC,oBAAc,IAAI,MAAM;AACxB,qBAAe,IAAI,MAAM;AAAA,IAC3B;AAEA,WAAO;AAAA,MACL,QAAQ;AAAA,MACR,aAAa,kBAAkB,KAAK,MAAM;AAAA,MAC1C;AAAA,MACA,kBAAkB;AAAA,MAClB,mBAAmB;AAAA,MACnB,cAAc,MAAM,SAAS;AAAA,MAC7B,cAAc,MAAM,SAAS;AAAA,IAC/B;AAAA,EACF,CAAC;AACH;AAEA,SAAS,aAAa,WAAmB,QAAmC;AAC1E,QAAM,IAAIA,OAAM,SAAS;AACzB,UAAQ,QAAQ;AAAA,IACd,KAAK;AACH,aAAO,EAAE,OAAO,eAAe;AAAA,IACjC,KAAK;AACH,aAAO,EAAE,OAAO,YAAY;AAAA,IAC9B,KAAK;AAEH,YAAM,OAAO,EAAE,KAAK;AACpB,YAAM,cAAc,KAAK,MAAM,OAAO,oBAAoB,IAAI;AAC9D,aAAO,GAAG,EAAE,OAAO,YAAY,CAAC,KAAK,WAAW;AAAA,IAClD,KAAK;AACH,aAAO,GAAG,EAAE,YAAY,CAAC,KAAK,OAAO,EAAE,QAAQ,CAAC,EAAE,SAAS,GAAG,GAAG,CAAC;AAAA,IACpE,KAAK;AACH,aAAO,EAAE,OAAO,SAAS;AAAA,IAC3B,KAAK;AAAA,IACL;AACE,aAAO;AAAA,EACX;AACF;AAEA,SAAS,kBAAkB,KAAa,QAAmC;AACzE,UAAQ,QAAQ;AAAA,IACd,KAAK,QAAQ;AAEX,YAAM,QAAQ,IAAI,MAAM,GAAG;AAC3B,YAAM,UAAU,GAAG,MAAM,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC;AAC/D,aAAOA,OAAM,OAAO,EAAE,OAAO,YAAY;AAAA,IAC3C;AAAA,IACA,KAAK;AACH,aAAOA,OAAM,GAAG,EAAE,OAAO,YAAY;AAAA,IACvC,KAAK;AACH,aAAO,QAAQ,IAAI,MAAM,IAAI,EAAE,CAAC,CAAC;AAAA,IACnC,KAAK;AACH,aAAOA,OAAM,GAAG,EAAE,OAAO,WAAW;AAAA,IACtC;AACE,aAAO;AAAA,EACX;AACF;AAEO,SAAS,WAAW,MAAsB;AAC/C,SAAO,IAAI,KAAK,QAAQ,CAAC,CAAC;AAC5B;AAEO,SAAS,aAAa,QAAwB;AACnD,MAAI,UAAU,IAAW,QAAO,IAAI,SAAS,KAAW,QAAQ,CAAC,CAAC;AAClE,MAAI,UAAU,IAAO,QAAO,IAAI,SAAS,KAAO,QAAQ,CAAC,CAAC;AAC1D,SAAO,OAAO,MAAM;AACtB;;;AD5NQ,cAEA,YAFA;AAXD,SAAS,UAAU,EAAE,OAAO,cAAc,UAAU,kBAAkB,MAAM,GAAmB;AACpG,QAAM,MAAM,eAAe,IAAK,QAAQ,eAAgB,MAAM;AAC9D,QAAM,QAAQ,MAAM,KAAK,UAAU,MAAM,KAAK,WAAW;AAEzD,QAAM,QAAQ,KAAK,MAAM,mBAAmB,EAAE;AAC9C,QAAM,OAAO,KAAK,MAAM,mBAAmB,EAAE;AAC7C,QAAM,UAAU,QAAQ,IAAI,GAAG,KAAK,KAAK,IAAI,MAAM,GAAG,IAAI;AAE1D,SACE,oBAAC,OAAI,eAAc,UAAS,UAAU,GACpC,+BAAC,QACC;AAAA,wBAAC,QAAK,OAAe,iBAAM;AAAA,IAC3B,oBAAC,QAAK,MAAI,MAAE,qBAAW,KAAK,GAAE;AAAA,IAC9B,qBAAC,QAAK,UAAQ,MAAC;AAAA;AAAA,MAAK,WAAW,YAAY;AAAA,MAAE;AAAA,OAAO;AAAA,IACpD,oBAAC,QAAK,iBAAG;AAAA,IACT,qBAAC,QAAK,OAAe;AAAA,iBAAW,QAAQ;AAAA,MAAE;AAAA,OAAI;AAAA,IAC9C,oBAAC,QAAK,iBAAG;AAAA,IACT,qBAAC,QAAK;AAAA;AAAA,MAAE;AAAA,MAAQ;AAAA,OAAU;AAAA,IAC1B,qBAAC,QAAK,UAAQ,MAAC;AAAA;AAAA,MAAG;AAAA,MAAM;AAAA,OAAC;AAAA,KAC3B,GACF;AAEJ;;;AEjCA,SAAS,OAAAC,MAAK,QAAAC,aAAY;AA0BlB,gBAAAC,MAIA,QAAAC,aAJA;AAjBD,SAAS,WAAW,EAAE,MAAM,OAAO,SAAS,GAAoB;AACrE,QAAM,WAAW;AACjB,QAAM,UAAU,QAAQ,IAAK,OAAO,QAAS,MAAM;AACnD,QAAM,aAAa,KAAK,IAAI,GAAG,KAAK,IAAI,SAAS,GAAG,CAAC;AACrD,QAAM,cAAc,KAAK,MAAO,aAAa,MAAO,QAAQ;AAC5D,QAAM,aAAa,WAAW;AAE9B,QAAM,QAAQ,UAAU,KAAK,UAAU,UAAU,KAAK,WAAW;AAEjE,QAAM,SAAS,SAAS,OAAO,WAAW;AAC1C,QAAM,QAAQ,SAAS,OAAO,UAAU;AAExC,QAAM,UAAU,CAAC,MAAc,GAAG,KAAK,MAAM,IAAI,GAAI,CAAC;AAEtD,SACE,gBAAAA,MAACH,MAAA,EAAI,eAAc,UAAS,UAAU,GACpC;AAAA,oBAAAG,MAACF,OAAA,EACC;AAAA,sBAAAC,KAACD,OAAA,EAAK,eAAC;AAAA,MACP,gBAAAC,KAACD,OAAA,EAAK,OAAe,kBAAO;AAAA,MAC5B,gBAAAC,KAACD,OAAA,EAAK,UAAQ,MAAE,iBAAM;AAAA,MACtB,gBAAAC,KAACD,OAAA,EAAK,eAAC;AAAA,MACP,gBAAAE,MAACF,OAAA,EAAK;AAAA;AAAA,QAAE,QAAQ,QAAQ,CAAC;AAAA,QAAE;AAAA,SAAC;AAAA,MAC5B,gBAAAE,MAACF,OAAA,EAAK;AAAA;AAAA,QAAI,QAAQ,IAAI;AAAA,QAAE;AAAA,QAAI,QAAQ,KAAK;AAAA,QAAE;AAAA,SAAO;AAAA,OACpD;AAAA,IACA,gBAAAE,MAACF,OAAA,EAAK,UAAQ,MACX;AAAA;AAAA,MAAK;AAAA,MAAQ,QAAQ,SAAS,YAAY;AAAA,MAAE;AAAA,MAAW,QAAQ,SAAS,YAAY;AAAA,MAAE;AAAA,MACtF,QAAQ,SAAS,QAAQ;AAAA,MAAE;AAAA,MAAe,QAAQ,SAAS,QAAQ;AAAA,MAAE;AAAA,OACxE;AAAA,KACF;AAEJ;;;ACxCA,SAAS,gBAAAG,eAAc,kBAAkB;AACzC,SAAS,gBAAgB;AACzB,SAAS,QAAAC,aAAY;AACrB,SAAS,WAAAC,gBAAe;AAYxB,SAAS,sBAAgC;AACvC,MAAI;AACF,UAAM,UAAU,SAAS,iCAAiC;AAAA,MACxD,UAAU;AAAA,MACV,OAAO,CAAC,QAAQ,QAAQ,QAAQ;AAAA,IAClC,CAAC,EAAE,KAAK;AACR,QAAI,WAAW,YAAY,QAAQ,IAAI,GAAG;AACxC,aAAO;AAAA,QACLC,MAAK,SAAS,WAAW;AAAA,QACzBA,MAAK,SAAS,WAAW,WAAW;AAAA,MACtC;AAAA,IACF;AAAA,EACF,QAAQ;AAAA,EAER;AACA,SAAO,CAAC;AACV;AAKO,SAAS,eAAe,MAAsB;AACnD,SAAO,KAAK,KAAK,KAAK,SAAS,GAAG;AACpC;AAKO,SAAS,gBAAgB,UAA0B;AACxD,MAAI;AACF,UAAM,UAAUC,cAAa,UAAU,OAAO;AAC9C,WAAO,eAAe,OAAO;AAAA,EAC/B,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAaO,SAAS,sBAAsB,SAAoC;AACxE,QAAM,QAAQ,QAAQ,MAAM,IAAI;AAChC,QAAM,WAA8B,CAAC;AACrC,MAAI,eAAe;AACnB,MAAI,iBAA2B,CAAC;AAChC,MAAI,mBAAmB;AAEvB,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,UAAM,OAAO,MAAM,CAAC;AACpB,QAAI,KAAK,WAAW,KAAK,GAAG;AAE1B,UAAI,eAAe,SAAS,KAAK,iBAAiB,YAAY;AAC5D,cAAM,iBAAiB,eAAe,KAAK,IAAI;AAC/C,iBAAS,KAAK;AAAA,UACZ,OAAO;AAAA,UACP,SAAS;AAAA,UACT,QAAQ,eAAe,cAAc;AAAA,UACrC,WAAW;AAAA,UACX,SAAS;AAAA,QACX,CAAC;AAAA,MACH;AACA,qBAAe,KAAK,QAAQ,UAAU,EAAE;AACxC,uBAAiB,CAAC;AAClB,yBAAmB,IAAI;AAAA,IACzB,OAAO;AACL,qBAAe,KAAK,IAAI;AAAA,IAC1B;AAAA,EACF;AAGA,MAAI,eAAe,SAAS,GAAG;AAC7B,UAAM,iBAAiB,eAAe,KAAK,IAAI;AAC/C,aAAS,KAAK;AAAA,MACZ,OAAO;AAAA,MACP,SAAS;AAAA,MACT,QAAQ,eAAe,cAAc;AAAA,MACrC,WAAW;AAAA,MACX,SAAS,MAAM;AAAA,IACjB,CAAC;AAAA,EACH;AAEA,SAAO;AACT;AAKO,SAAS,gBAAgB,UAA2G;AACzI,QAAM,QAAQ,WACV,CAAC,QAAQ,IACT;AAAA,IACED,MAAK,QAAQ,IAAI,GAAG,WAAW;AAAA,IAC/BA,MAAK,QAAQ,IAAI,GAAG,WAAW,WAAW;AAAA,IAC1C,GAAG,oBAAoB;AAAA,IACvBA,MAAKE,SAAQ,GAAG,WAAW,WAAW;AAAA,EACxC;AAEJ,aAAW,KAAK,OAAO;AACrB,QAAI,WAAW,CAAC,GAAG;AACjB,YAAM,UAAUD,cAAa,GAAG,OAAO;AACvC,YAAM,WAAW,sBAAsB,OAAO;AAC9C,YAAM,cAAc,SAAS,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,QAAQ,CAAC;AACjE,YAAM,gBAAgB,SAAS,OAAO,CAAC,MAAM,EAAE,SAAS,GAAG;AAC3D,aAAO,EAAE,aAAa,UAAU,cAAc;AAAA,IAChD;AAAA,EACF;AAEA,SAAO,EAAE,aAAa,GAAG,UAAU,CAAC,GAAG,eAAe,CAAC,EAAE;AAC3D;AAKO,SAAS,oBAAqC;AACnD,QAAM,UAA2B,CAAC;AAClC,QAAM,QAAQ;AAAA,IACZD,MAAK,QAAQ,IAAI,GAAG,WAAW;AAAA,IAC/BA,MAAKE,SAAQ,GAAG,cAAc;AAAA,EAChC;AAEA,aAAW,cAAc,OAAO;AAC9B,QAAI,CAAC,WAAW,UAAU,EAAG;AAC7B,QAAI;AACF,YAAM,MAAM,KAAK,MAAMD,cAAa,YAAY,OAAO,CAAC;AACxD,YAAM,aAAa,IAAI,cAAc,IAAI,eAAe,CAAC;AACzD,iBAAW,CAAC,MAAM,MAAM,KAAK,OAAO,QAAQ,UAAU,GAAG;AACvD,cAAM,MAAM;AAEZ,cAAM,QAAQ,MAAM,QAAQ,IAAI,KAAK,IAAI,IAAI,QAAQ,CAAC;AACtD,cAAM,YAAY,MAAM,UAAU;AAClC,cAAM,kBAAkB,YAAY;AACpC,gBAAQ,KAAK;AAAA,UACX;AAAA,UACA;AAAA,UACA;AAAA,UACA,SAAS,YAAY;AAAA,QACvB,CAAC;AAAA,MACH;AAAA,IACF,QAAQ;AACN;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAKO,SAAS,wBAAwB,cAAwC;AAC9E,QAAM,WAAW,gBAAgB,YAAY;AAC7C,QAAM,aAAa,kBAAkB;AACrC,QAAM,gBAAgB,WAAW,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,iBAAiB,CAAC;AAE9E,QAAM,gBACJ,uBACA,wBACA,gBACA,SAAS,cACT;AAEF,QAAM,kBAAkB,sBAAsB;AAC9C,QAAM,gBAAiB,kBAAkB,sBAAuB;AAEhE,SAAO;AAAA,IACL,cAAc;AAAA,IACd,cAAc;AAAA,IACd,UAAU,SAAS;AAAA,IACnB,UAAU;AAAA,IACV,mBAAmB;AAAA,IACnB;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;;;AJlJQ,gBAAAE,MAyBA,QAAAC,aAzBA;AA9BD,SAAS,UAAU,EAAE,iBAAiB,SAAS,GAAmB;AACvE,QAAM,EAAE,KAAK,IAAI,OAAO;AACxB,QAAM,CAAC,SAAS,UAAU,IAAI,SAA+B,IAAI;AACjE,QAAM,CAAC,UAAU,WAAW,IAAI,SAA0B,wBAAwB,CAAC;AACnF,QAAM,CAAC,YAAY,aAAa,IAAI,SAAS,KAAK;AAElD,YAAU,MAAM;AACd,aAAS,UAAU;AACjB,UAAI;AACF,cAAM,SAAS,iBAAiB,eAAe;AAC/C,mBAAW,MAAM;AACjB,oBAAY,wBAAwB,CAAC;AAAA,MACvC,QAAQ;AAAA,MAER;AAAA,IACF;AAEA,YAAQ;AACR,UAAM,QAAQ,YAAY,SAAS,QAAQ;AAC3C,WAAO,MAAM,cAAc,KAAK;AAAA,EAClC,GAAG,CAAC,iBAAiB,QAAQ,CAAC;AAE9B,WAAS,CAAC,UAAU;AAClB,QAAI,UAAU,IAAK,MAAK;AACxB,QAAI,UAAU,IAAK,eAAc,CAAC,SAAS,CAAC,IAAI;AAAA,EAClD,CAAC;AAED,MAAI,CAAC,WAAW,QAAQ,SAAS,WAAW,GAAG;AAC7C,WACE,gBAAAD,KAACE,MAAA,EAAI,UAAU,GACb,0BAAAF,KAACG,OAAA,EAAK,UAAQ,MAAC,yCAA2B,GAC5C;AAAA,EAEJ;AAEA,QAAM,YAAY,QAAQ,SAAS;AAAA,IACjC,CAAC,KAAK,QAAQ,MAAM,qBAAqB,GAAG,EAAE;AAAA,IAC9C;AAAA,EACF;AACA,QAAM,WAAW,sBAAsB,QAAQ,QAAQ;AACvD,QAAM,QAAQ,QAAQ,SAAS,QAAQ,SAAS,SAAS,CAAC,GAAG,SAAS;AACtE,QAAM,eAAe,SAAS,uBAAuB,YAAY;AAEjE,QAAM,aAAa,QAAQ,mBAAmB,QAAQ;AACtD,QAAM,aAAa,aAAa,QAAQ;AAGxC,QAAM,iBAAiB,QAAQ,SAAS,MAAM,EAAE;AAEhD,SACE,gBAAAF,MAACC,MAAA,EAAI,eAAc,UACjB;AAAA,oBAAAD,MAACC,MAAA,EAAI,aAAY,SAAQ,aAAY,QAAO,UAAU,GACpD;AAAA,sBAAAF,KAACG,OAAA,EAAK,MAAI,MAAC,OAAM,QAAO,4BAExB;AAAA,MACA,gBAAAF,MAACE,OAAA,EAAK;AAAA;AAAA,QAAa,QAAQ,UAAU,MAAM,GAAG,CAAC;AAAA,QAAE;AAAA,SAAG;AAAA,MACpD,gBAAAF,MAACE,OAAA,EAAK;AAAA;AAAA,QAAI,QAAQ;AAAA,QAAa;AAAA,SAAS;AAAA,MACxC,gBAAAH,KAACG,OAAA,EAAK,UAAQ,MAAC,iCAAmB;AAAA,OACpC;AAAA,IAEA,gBAAAH;AAAA,MAAC;AAAA;AAAA,QACC,OAAO;AAAA,QACP;AAAA,QACA,UAAU,SAAS;AAAA,QACnB,kBAAkB,SAAS;AAAA,QAC3B;AAAA;AAAA,IACF;AAAA,IAEA,gBAAAA,KAAC,cAAW,MAAM,YAAY,OAAO,qBAAqB,UAAoB;AAAA,IAE9E,gBAAAC,MAACC,MAAA,EAAI,eAAc,UAAS,UAAU,GAAG,WAAW,GAClD;AAAA,sBAAAF,KAACG,OAAA,EAAK,MAAI,MAAC,8BAAgB;AAAA,MAC1B,eAAe,IAAI,CAAC,KAAK,MAAM;AAC9B,cAAM,OAAO,qBAAqB,GAAG;AACrC,eACE,gBAAAF,MAACE,OAAA,EACC;AAAA,0BAAAH,KAACG,OAAA,EAAK,UAAQ,MAAE,cAAI,UAAU,MAAM,IAAI,EAAE,GAAE;AAAA,UAC5C,gBAAAF,MAACE,OAAA,EAAK;AAAA;AAAA,YAAE,aAAa,IAAI,MAAM,eAAe,IAAI,MAAM,aAAa;AAAA,YAAE;AAAA,aAAI;AAAA,UAC3E,gBAAAF,MAACE,OAAA,EAAK,OAAM,UAAS;AAAA;AAAA,YAAE,WAAW,KAAK,SAAS;AAAA,aAAE;AAAA,aAHzC,CAIX;AAAA,MAEJ,CAAC;AAAA,OACH;AAAA,KACF;AAEJ;;;AHpGO,SAAS,qBAAqBC,UAAwB;AAC3D,EAAAA,SACG,QAAQ,OAAO,EACf,YAAY,oCAAoC,EAChD,OAAO,sBAAsB,0BAA0B,EACvD,OAAO,wBAAwB,uCAAuC,EACtE,OAAO,uBAAuB,0BAA0B,MAAM,EAC9D,OAAO,cAAc,gBAAgB,EACrC,OAAO,OAAO,SAAS;AACtB,UAAM,WAAW,SAAS,KAAK,UAAU,EAAE;AAE3C,QAAI;AAEJ,QAAI,KAAK,SAAS;AAChB,YAAM,WAAW,MAAM,kBAAkB,KAAK,OAAO;AACrD,YAAM,QAAQ,SAAS,KAAK,CAAC,MAAM,EAAE,UAAU,WAAW,KAAK,OAAO,CAAC;AACvE,wBAAkB,OAAO;AAAA,IAC3B,OAAO;AACL,YAAM,WAAW,MAAM,kBAAkB,KAAK,OAAO;AACrD,wBAAkB,SAAS,CAAC,GAAG;AAAA,IACjC;AAEA,QAAI,CAAC,iBAAiB;AACpB,cAAQ;AAAA,QACN;AAAA,MACF;AACA,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,UAAM,EAAE,cAAc,IAAI;AAAA,MACxBC,OAAM,cAAc,WAAW,EAAE,iBAAiB,SAAS,CAAC;AAAA,IAC9D;AACA,UAAM,cAAc;AAAA,EACtB,CAAC;AACL;;;AQxCA,OAAOC,YAAW;AAClB,SAAS,UAAAC,eAAc;AAEvB,SAAS,QAAAC,aAAY;;;ACFrB,SAAS,YAAY;AAarB,IAAM,sBAAiE;AAAA,EACrE,QAAQ,EAAE,OAAO,EAAE,KAAK,GAAG,UAAU,GAAG,MAAM,EAAE,GAAG,qBAAqB,IAAK;AAAA,EAC7E,QAAQ,EAAE,OAAO,EAAE,KAAK,GAAG,UAAU,IAAI,MAAM,GAAG,GAAG,qBAAqB,IAAK;AAAA,EAC/E,SAAS,EAAE,OAAO,EAAE,KAAK,IAAI,UAAU,IAAI,MAAM,GAAG,GAAG,qBAAqB,KAAK;AACnF;AAEA,IAAM,kBAAkB,CAAC,QAAQ,UAAU,YAAY,kBAAkB,eAAe,iBAAiB,QAAQ;AACjH,IAAM,mBAAmB,CAAC,YAAY,WAAW,cAAc,aAAa,SAAS,UAAU,WAAW,YAAY,YAAY,cAAc;AAEhJ,SAAS,iBAAiB,iBAAyC;AACjE,QAAM,QAAQ,gBAAgB,YAAY;AAC1C,MAAI,gBAAgB,KAAK,CAAC,MAAM,MAAM,SAAS,CAAC,CAAC,EAAG,QAAO;AAC3D,MAAI,iBAAiB,KAAK,CAAC,MAAM,MAAM,SAAS,CAAC,CAAC,EAAG,QAAO;AAC5D,SAAO;AACT;AAEA,eAAsB,iBACpB,iBACA,UAAoC,CAAC,GACd;AACvB,QAAM,QAAQ,QAAQ,SAAS;AAC/B,QAAM,MAAM,QAAQ,OAAO,QAAQ,IAAI;AACvC,QAAM,UAAU,oBAAoB,KAAK;AACzC,QAAM,UAAU;AAGhB,QAAM,WAAW,wBAAwB;AAGzC,MAAI,aAAa;AACjB,MAAI,WAAW,QAAQ,SAAS,CAAC;AAEjC,MAAI,SAAS,WAAW,GAAG;AAEzB,QAAI;AACF,YAAM,EAAE,UAAAC,UAAS,IAAI,MAAM,OAAO,oBAAoB;AACtD,YAAM,SAASA,UAAS,wEAAwE;AAAA,QAC9F;AAAA,QACA,UAAU;AAAA,MACZ,CAAC;AACD,iBAAW,OACR,MAAM,IAAI,EACV,OAAO,OAAO,EACd,IAAI,CAAC,MAAM,GAAG,GAAG,IAAI,CAAC,EAAE;AAAA,IAC7B,QAAQ;AAAA,IAER;AAAA,EACF;AAEA,aAAW,eAAe,UAAU;AAClC,UAAM,UAAU,MAAM,KAAK,aAAa,EAAE,KAAK,UAAU,KAAK,CAAC;AAC/D,eAAW,KAAK,SAAS;AACvB,oBAAc,gBAAgB,CAAC;AAAA,IACjC;AAAA,EACF;AAGA,QAAM,aAAa,iBAAiB,eAAe;AACnD,QAAM,UAAU,oBAAoB,UAAU;AAG9C,QAAM,iBAAiB,SAAS,gBAAgB;AAChD,QAAM,iBAAiB;AAEvB,WAAS,qBAAqB,OAAuB;AACnD,QAAI,YAAY;AAChB,aAAS,OAAO,GAAG,QAAQ,OAAO,QAAQ;AACxC,YAAM,sBAAsB,OAAO,KAAK,QAAQ;AAChD,YAAM,cAAc,iBAAiB;AAErC,UAAI;AACJ,UAAI,QAAQ,GAAG;AAEb,6BAAsB,cAAc,QAAQ,QAAS;AAAA,MACvD,OAAO;AAEL,cAAM,eAAe,cAAc;AACnC,cAAM,iBAAiB,eAAe,IAAI;AAC1C,6BACG,eAAe,QAAQ,YAAa,UACpC,iBAAiB,QAAQ,QAAS;AAAA,MACvC;AAEA,YAAM,aAAc,QAAQ,sBAAsB,QAAQ,SAAU;AACpE,mBAAa,qBAAqB;AAAA,IACpC;AACA,WAAO;AAAA,EACT;AAEA,QAAM,UAAU,qBAAqB,QAAQ,MAAM,GAAG;AACtD,QAAM,eAAe,qBAAqB,QAAQ,MAAM,QAAQ;AAChE,QAAM,WAAW,qBAAqB,QAAQ,MAAM,IAAI;AAGxD,QAAM,sBAAsB,iBAAiB,QAAQ,MAAM;AAC3D,QAAM,uBAAuB,QAAQ,sBAAsB,QAAQ,MAAM;AACzE,QAAM,uBAAuB,sBAAsB;AAGnD,QAAM,gBAAgB,uBAAuB;AAC7C,QAAM,kBAAmB,gBAAgB,eAAe,KAAM;AAG9D,QAAM,kBAA4B,CAAC;AACnC,MAAI,UAAU,UAAU;AACtB,UAAM,gBAAgB,oBAAoB,QAAQ;AAClD,UAAM,aAAa,qBAAqB,QAAQ,MAAM,QAAQ;AAG9D,UAAM,QAAQ,QAAQ,SAAS,oBAAoB,QAAQ,EAAE;AAC7D,QAAI,QAAQ,GAAG;AACb,sBAAgB;AAAA,QACd,4BAA4B,WAAW,eAAe,eAAe,KAAK,CAAC,KAAK,MAAM,QAAQ,CAAC,CAAC;AAAA,MAClG;AAAA,IACF;AAAA,EACF;AAEA,MAAI,UAAU,UAAU;AACtB,UAAM,cAAc,oBAAoB,MAAM;AAC9C,UAAM,QAAQ,YAAY,SAAS,QAAQ;AAC3C,oBAAgB,KAAK,0BAA0B,WAAW,eAAe,KAAK,CAAC,KAAK,MAAM,QAAQ,CAAC,CAAC,SAAS;AAAA,EAC/G;AAEA,MAAI,SAAS,gBAAgB,IAAI;AAC/B,oBAAgB,KAAK,+BAA+B,MAAM,SAAS,eAAe,QAAQ,CAAC,CAAC,uCAAuC;AAAA,EACrI;AAEA,MAAI,aAAa,KAAO;AACtB,oBAAgB,KAAK,wBAAwB,aAAa,KAAM,QAAQ,CAAC,CAAC,sCAAsC;AAAA,EAClH;AAEA,SAAO;AAAA,IACL;AAAA,IACA,gBAAgB,QAAQ;AAAA,IACxB,iBAAiB;AAAA,MACf,OAAO,KAAK,MAAM,mBAAmB;AAAA,MACrC,QAAQ,KAAK,MAAM,oBAAoB;AAAA,MACvC,QAAQ,KAAK,MAAM,oBAAoB;AAAA,IACzC;AAAA,IACA,eAAe;AAAA,MACb,KAAK,WAAW,OAAO;AAAA,MACvB,UAAU,WAAW,YAAY;AAAA,MACjC,MAAM,WAAW,QAAQ;AAAA,IAC3B;AAAA,IACA,iBAAiB,SAAS;AAAA,IAC1B;AAAA,IACA,iBAAiB,KAAK,MAAM,eAAe;AAAA,IAC3C;AAAA,EACF;AACF;;;AClKA,SAAS,OAAAC,MAAK,QAAAC,aAAY;AAapB,SAGA,OAAAC,MAHA,QAAAC,aAAA;AALC,SAAS,aAAa,EAAE,MAAM,SAAS,GAAsB;AAClE,QAAM,UAAU,CAAC,MAAc,IAAI,IAAI,KAAM,QAAQ,CAAC,CAAC;AAEvD,SACE,gBAAAA,MAACH,MAAA,EAAI,eAAc,UAAS,aAAY,SAAQ,aAAY,QAAO,UAAU,GAAG,UAAU,GACxF;AAAA,oBAAAG,MAACF,OAAA,EAAK,MAAI,MAAC,OAAM,QAAO;AAAA;AAAA,MACD;AAAA,MAAK;AAAA,OAC5B;AAAA,IACA,gBAAAC,KAACD,OAAA,EAAK,eAAC;AAAA,IACP,gBAAAE,MAACF,OAAA,EAAK;AAAA;AAAA,MACG,gBAAAC,KAACD,OAAA,EAAK,MAAI,MAAE,mBAAS,OAAM;AAAA,OACpC;AAAA,IACA,gBAAAE,MAACF,OAAA,EAAK;AAAA;AAAA,MACc,SAAS,eAAe;AAAA,MAAI;AAAA,MAAE,SAAS,eAAe;AAAA,MAAK;AAAA,MAAY;AAAA,MACxF,SAAS,eAAe;AAAA,MAAS;AAAA,OACpC;AAAA,IACA,gBAAAE,MAACF,OAAA,EAAK;AAAA;AAAA,MACI,QAAQ,SAAS,UAAU;AAAA,MAAE;AAAA,OACvC;AAAA,IACA,gBAAAE,MAACF,OAAA,EAAK;AAAA;AAAA,MACe,QAAQ,SAAS,eAAe;AAAA,MAAE;AAAA,OACvD;AAAA,IACA,gBAAAC,KAACD,OAAA,EAAK,eAAC;AAAA,IACP,gBAAAC,KAACD,OAAA,EAAK,MAAI,MAAC,6BAAe;AAAA,IAC1B,gBAAAE,MAACF,OAAA,EACE;AAAA;AAAA,MAAK;AAAA,MAAU,gBAAAC,KAACD,OAAA,EAAK,OAAM,SAAS,mBAAS,cAAc,KAAI;AAAA,OAClE;AAAA,IACA,gBAAAE,MAACF,OAAA,EACE;AAAA;AAAA,MAAK;AAAA,MAAU,gBAAAC,KAACD,OAAA,EAAK,OAAM,UAAU,mBAAS,cAAc,UAAS;AAAA,OACxE;AAAA,IACA,gBAAAE,MAACF,OAAA,EACE;AAAA;AAAA,MAAK;AAAA,MAAU,gBAAAC,KAACD,OAAA,EAAK,OAAM,OAAO,mBAAS,cAAc,MAAK;AAAA,OACjE;AAAA,IACA,gBAAAC,KAACD,OAAA,EAAK,eAAC;AAAA,IACP,gBAAAE,MAACF,OAAA,EAAK;AAAA;AAAA,MAAgB,SAAS;AAAA,MAAgB;AAAA,OAAkB;AAAA,IAChE,SAAS,gBAAgB,IAAI,CAAC,KAAK,MAClC,gBAAAE,MAACF,OAAA,EAAa,OAAM,QACjB;AAAA;AAAA,MAAS;AAAA,SADD,CAEX,CACD;AAAA,KACH;AAEJ;;;AF5CO,SAAS,wBAAwBG,UAAwB;AAC9D,EAAAA,SACG,QAAQ,iBAAiB,EACzB,YAAY,4BAA4B,EACxC,OAAO,uBAAuB,yBAAyB,QAAQ,EAC/D,OAAO,sBAAsB,qCAAqC,EAClE,OAAO,aAAa,yCAAyC,EAC7D,OAAO,UAAU,gBAAgB,EACjC,OAAO,OAAO,MAAc,SAAS;AACpC,UAAM,QAAkB,CAAC;AACzB,QAAI,KAAK,OAAO;AACd,YAAM,UAAU,MAAMC,MAAK,KAAK,OAAO,EAAE,UAAU,KAAK,CAAC;AACzD,YAAM,KAAK,GAAG,OAAO;AAAA,IACvB;AAEA,QAAI,KAAK,SAAS;AAChB,YAAM,SAAS,CAAC,UAAU,QAAQ,OAAO;AACzC,iBAAW,SAAS,QAAQ;AAC1B,cAAMC,YAAW,MAAM,iBAAiB,MAAM,EAAE,OAAO,OAAO,KAAK,QAAQ,IAAI,EAAE,CAAC;AAClF,YAAI,KAAK,MAAM;AACb,kBAAQ,IAAI,KAAK,UAAUA,WAAU,MAAM,CAAC,CAAC;AAAA,QAC/C,OAAO;AACL,gBAAM,EAAE,eAAAC,eAAc,IAAIC;AAAA,YACxBC,OAAM,cAAc,cAAc,EAAE,MAAM,UAAAH,UAAS,CAAC;AAAA,UACtD;AACA,gBAAMC,eAAc;AAAA,QACtB;AAAA,MACF;AACA;AAAA,IACF;AAEA,UAAM,WAAW,MAAM,iBAAiB,MAAM;AAAA,MAC5C,OAAO,KAAK;AAAA,MACZ;AAAA,MACA,KAAK,QAAQ,IAAI;AAAA,IACnB,CAAC;AAED,QAAI,KAAK,MAAM;AACb,cAAQ,IAAI,KAAK,UAAU,UAAU,MAAM,CAAC,CAAC;AAC7C;AAAA,IACF;AAEA,UAAM,EAAE,cAAc,IAAIC;AAAA,MACxBC,OAAM,cAAc,cAAc,EAAE,MAAM,SAAS,CAAC;AAAA,IACtD;AACA,UAAM,cAAc;AAAA,EACtB,CAAC;AACL;;;AGrDA,OAAO,WAAW;;;ACDlB,OAAOC,YAAW;AAClB,OAAOC,cAAa;AACpB,SAAS,YAAAC,iBAAgB;;;ACFzB,OAAO,cAAc;AACrB,SAAS,WAAW,cAAAC,mBAAkB;AACtC,SAAS,eAAe;AAGjB,SAAS,aAAa,QAAoC;AAC/D,QAAM,OAAO,UAAU;AACvB,QAAM,MAAM,QAAQ,IAAI;AACxB,MAAI,CAACC,YAAW,GAAG,GAAG;AACpB,cAAU,KAAK,EAAE,WAAW,KAAK,CAAC;AAAA,EACpC;AAEA,QAAM,KAAK,IAAI,SAAS,IAAI;AAC5B,KAAG,OAAO,oBAAoB;AAC9B,KAAG,OAAO,mBAAmB;AAE7B,eAAa,EAAE;AACf,SAAO;AACT;AAEA,SAAS,aAAa,IAA6B;AACjD,KAAG,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GA8BP;AACH;;;AC5CA,IAAM,aAA0B;AAAA,EAC9B;AAAA,IACE,SAAS;AAAA,IACT,aAAa;AAAA,IACb,GAAG,KAAK;AAAA,IAER;AAAA,EACF;AACF;AAEO,SAAS,cAAc,IAA6B;AACzD,KAAG,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA,GAKP;AAED,QAAM,UAAU,IAAI;AAAA,IAClB,GACG,QAAQ,uCAAuC,EAC/C,IAAI,EACJ,IAAI,CAAC,QAAa,IAAI,OAAiB;AAAA,EAC5C;AAEA,aAAW,aAAa,YAAY;AAClC,QAAI,CAAC,QAAQ,IAAI,UAAU,OAAO,GAAG;AACnC,gBAAU,GAAG,EAAE;AACf,SAAG,QAAQ,oDAAoD,EAAE,IAAI,UAAU,OAAO;AAAA,IACxF;AAAA,EACF;AACF;;;AF/BAC,OAAM,OAAOC,QAAO;AAIb,IAAM,gBAAN,MAAoB;AAAA,EACjB;AAAA,EAER,YAAY,QAAiB;AAC3B,SAAK,KAAK,aAAa,MAAM;AAC7B,kBAAc,KAAK,EAAE;AAAA,EACvB;AAAA,EAEQ,mBAAmB,aAA6B;AACtD,UAAM,OAAOC,UAAS,WAAW,KAAK;AACtC,UAAM,WAAW,KAAK,GACnB,QAAQ,wCAAwC,EAChD,IAAI,WAAW;AAElB,QAAI,SAAU,QAAO,SAAS;AAE9B,UAAM,SAAS,KAAK,GACjB,QAAQ,iDAAiD,EACzD,IAAI,MAAM,WAAW;AAExB,WAAO,OAAO,OAAO,eAAe;AAAA,EACtC;AAAA,EAEA,UAAU,aAAqB,QAAgB,QAA4B;AACzE,UAAM,YAAY,KAAK,mBAAmB,WAAW;AACrD,SAAK,GACF;AAAA,MACC;AAAA;AAAA;AAAA;AAAA,IAIF,EACC,IAAI,WAAW,QAAQ,MAAM;AAAA,EAClC;AAAA,EAEA,UAAU,aAAsE;AAC9E,UAAM,UAAU,KAAK,GAClB,QAAQ,wCAAwC,EAChD,IAAI,WAAW;AAElB,QAAI,CAAC,QAAS,QAAO;AAErB,UAAM,SAAS,KAAK,GACjB,QAAQ,8FAA8F,EACtG,IAAI,QAAQ,EAAE;AAEjB,QAAI,CAAC,OAAQ,QAAO;AACpB,WAAO,EAAE,QAAQ,OAAO,YAAY,QAAQ,OAAO,OAAO;AAAA,EAC5D;AAAA,EAEA,YACE,aACA,WACA,UACA,WACA,SACA,WACM;AACN,UAAM,YAAY,KAAK,mBAAmB,WAAW;AACrD,SAAK,GACF;AAAA,MACC;AAAA;AAAA,IAEF,EACC,IAAI,WAAW,WAAW,UAAU,WAAW,SAAS,SAAS;AAAA,EACtE;AAAA,EAEA,SAAS,aAAqB,QAA8B;AAC1D,UAAM,UAAU,KAAK,GAClB,QAAQ,wCAAwC,EAChD,IAAI,WAAW;AAElB,QAAI,CAAC,QAAS,QAAO;AAErB,UAAM,QAAQ,eAAe,MAAM;AACnC,UAAM,SAAS,KAAK,GACjB;AAAA,MACC;AAAA;AAAA;AAAA,IAGF,EACC,IAAI,QAAQ,IAAI,MAAM,YAAY,CAAC;AAEtC,WAAO,OAAO;AAAA,EAChB;AAAA,EAEA,YAAY,aAA0C;AACpD,UAAM,eAAe,KAAK,UAAU,WAAW;AAC/C,QAAI,CAAC,aAAc,QAAO;AAE1B,UAAM,QAAQ,KAAK,SAAS,aAAa,aAAa,MAAM;AAC5D,UAAM,YAAY,KAAK,IAAI,GAAG,aAAa,SAAS,KAAK;AACzD,UAAM,cAAc,aAAa,SAAS,IAAK,QAAQ,aAAa,SAAU,MAAM;AAEpF,UAAM,cAAc,eAAe,aAAa,MAAM;AACtD,UAAM,YAAY,aAAa,aAAa,MAAM;AAElD,WAAO;AAAA,MACL,QAAQ,aAAa;AAAA,MACrB;AAAA,MACA;AAAA,MACA;AAAA,MACA,cAAc,QAAQ,aAAa;AAAA,MACnC,QAAQ,aAAa;AAAA,MACrB,aAAa,YAAY,YAAY;AAAA,MACrC,WAAW,UAAU,YAAY;AAAA,IACnC;AAAA,EACF;AAAA,EAEA,eAAmH;AACjH,UAAM,OAAO,KAAK,GACf;AAAA,MACC;AAAA;AAAA;AAAA;AAAA,IAIF,EACC,IAAI;AAEP,WAAO,KAAK,IAAI,CAAC,SAAS;AAAA,MACxB,MAAM,IAAI;AAAA,MACV,MAAM,IAAI;AAAA,MACV,QAAQ,IAAI;AAAA,MACZ,QAAQ,IAAI;AAAA,MACZ,OAAO,IAAI,SAAS,KAAK,SAAS,IAAI,MAAM,IAAI,MAAsB,IAAI;AAAA,IAC5E,EAAE;AAAA,EACJ;AAAA,EAEA,aAAa,aAA8B;AACzC,UAAM,UAAU,KAAK,GAClB,QAAQ,wCAAwC,EAChD,IAAI,WAAW;AAElB,QAAI,CAAC,QAAS,QAAO;AAErB,UAAM,SAAS,KAAK,GAAG,QAAQ,0CAA0C,EAAE,IAAI,QAAQ,EAAE;AACzF,WAAO,OAAO,UAAU;AAAA,EAC1B;AAAA,EAEA,QAAc;AACZ,SAAK,GAAG,MAAM;AAAA,EAChB;AACF;AAEA,SAAS,eAAe,QAAmC;AACzD,UAAQ,QAAQ;AAAA,IACd,KAAK;AACH,aAAOF,OAAM,EAAE,QAAQ,KAAK;AAAA,IAC9B,KAAK;AACH,aAAOA,OAAM,EAAE,QAAQ,SAA6B;AAAA,IACtD,KAAK;AACH,aAAOA,OAAM,EAAE,QAAQ,OAAO;AAAA,EAClC;AACF;AAEA,SAAS,aAAa,QAAmC;AACvD,UAAQ,QAAQ;AAAA,IACd,KAAK;AACH,aAAOA,OAAM,EAAE,MAAM,KAAK;AAAA,IAC5B,KAAK;AACH,aAAOA,OAAM,EAAE,MAAM,SAA6B;AAAA,IACpD,KAAK;AACH,aAAOA,OAAM,EAAE,MAAM,OAAO;AAAA,EAChC;AACF;;;AD3KO,SAAS,sBAAsBG,UAAwB;AAC5D,QAAM,SAASA,SAAQ,QAAQ,QAAQ,EAAE,YAAY,+BAA+B;AAEpF,SACG,QAAQ,cAAc,EACtB,YAAY,gCAAgC,EAC5C,OAAO,yBAAyB,wCAAwC,QAAQ,EAChF,OAAO,oBAAoB,cAAc,EACzC,OAAO,CAAC,QAAgB,SAAS;AAChC,UAAM,UAAU,IAAI,cAAc;AAClC,UAAM,cAAc,KAAK,WAAW,QAAQ,IAAI;AAChD,UAAM,YAAY,WAAW,MAAM;AAEnC,QAAI,MAAM,SAAS,KAAK,aAAa,GAAG;AACtC,cAAQ,IAAI,MAAM,IAAI,0CAA0C,CAAC;AACjE,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,YAAQ,UAAU,aAAa,WAAW,KAAK,MAAM;AACrD,YAAQ;AAAA,MACN,MAAM,MAAM,eAAe,WAAW,SAAS,CAAC,IAAI,KAAK,MAAM,QAAQ,WAAW,EAAE;AAAA,IACtF;AACA,YAAQ,MAAM;AAAA,EAChB,CAAC;AAEH,SACG,QAAQ,MAAM,EACd,YAAY,6BAA6B,EACzC,OAAO,oBAAoB,cAAc,EACzC,OAAO,UAAU,gBAAgB,EACjC,OAAO,CAAC,SAAS;AAChB,UAAM,UAAU,IAAI,cAAc;AAClC,UAAM,cAAc,KAAK,WAAW,QAAQ,IAAI;AAChD,UAAM,SAAS,QAAQ,YAAY,WAAW;AAE9C,QAAI,CAAC,QAAQ;AACX,cAAQ,IAAI,gFAAgF;AAC5F,cAAQ,MAAM;AACd;AAAA,IACF;AAEA,QAAI,KAAK,MAAM;AACb,cAAQ,IAAI,KAAK,UAAU,QAAQ,MAAM,CAAC,CAAC;AAC3C,cAAQ,MAAM;AACd;AAAA,IACF;AAEA,UAAM,MAAM,OAAO;AACnB,UAAM,WAAW;AACjB,UAAM,SAAS,KAAK,MAAO,KAAK,IAAI,KAAK,GAAG,IAAI,MAAO,QAAQ;AAC/D,UAAM,QAAQ,WAAW;AACzB,UAAM,QAAQ,MAAM,KAAK,UAAU,MAAM,KAAK,WAAW;AACzD,UAAM,WAAW,UAAU,UAAU,MAAM,QAAQ,UAAU,WAAW,MAAM,SAAS,MAAM;AAE7F,YAAQ,IAAI,MAAM,KAAK,KAAK,uBAAuB,CAAC;AACpD,YAAQ,IAAI,cAAc,OAAO,MAAM,KAAK,OAAO,YAAY,MAAM,GAAG,EAAE,CAAC,OAAO,OAAO,UAAU,MAAM,GAAG,EAAE,CAAC,GAAG;AAClH,YAAQ,IAAI,cAAc,WAAW,OAAO,MAAM,CAAC,EAAE;AACrD,YAAQ,IAAI,cAAc,SAAS,WAAW,OAAO,KAAK,CAAC,CAAC,EAAE;AAC9D,YAAQ,IAAI,KAAK,SAAS,MAAM,SAAS,OAAO,MAAM,IAAI,SAAS,OAAO,KAAK,IAAI,GAAG,CAAC,IAAI,IAAI,QAAQ,CAAC,CAAC,GAAG;AAE5G,QAAI,OAAO,cAAc;AACvB,cAAQ,IAAI,MAAM,IAAI,KAAK;AAAA,mBAAsB,WAAW,OAAO,QAAQ,OAAO,MAAM,CAAC,EAAE,CAAC;AAAA,IAC9F;AACA,YAAQ,IAAI;AAEZ,YAAQ,MAAM;AAAA,EAChB,CAAC;AAEH,SACG,QAAQ,MAAM,EACd,YAAY,0BAA0B,EACtC,OAAO,MAAM;AACZ,UAAM,UAAU,IAAI,cAAc;AAClC,UAAM,WAAW,QAAQ,aAAa;AAEtC,QAAI,SAAS,WAAW,GAAG;AACzB,cAAQ,IAAI,0EAA0E;AACtF,cAAQ,MAAM;AACd;AAAA,IACF;AAEA,YAAQ,IAAI,MAAM,KAAK,KAAK,4BAA4B,CAAC;AACzD,eAAW,KAAK,UAAU;AACxB,YAAM,YAAY,EAAE,SAAS,GAAG,WAAW,EAAE,MAAM,CAAC,IAAI,EAAE,MAAM,KAAK;AACrE,YAAM,WAAW,EAAE,QAAQ,IAAI,YAAY,WAAW,EAAE,KAAK,CAAC,MAAM;AACpE,cAAQ,IAAI,KAAK,MAAM,KAAK,EAAE,IAAI,CAAC,WAAM,SAAS,GAAG,QAAQ,EAAE;AAC/D,cAAQ,IAAI,MAAM,IAAI,OAAO,EAAE,IAAI,EAAE,CAAC;AAAA,IACxC;AACA,YAAQ,IAAI;AAEZ,YAAQ,MAAM;AAAA,EAChB,CAAC;AAEH,SACG,QAAQ,QAAQ,EAChB,YAAY,mCAAmC,EAC/C,OAAO,oBAAoB,cAAc,EACzC,OAAO,CAAC,SAAS;AAChB,UAAM,UAAU,IAAI,cAAc;AAClC,UAAM,cAAc,KAAK,WAAW,QAAQ,IAAI;AAChD,UAAM,UAAU,QAAQ,aAAa,WAAW;AAEhD,QAAI,SAAS;AACX,cAAQ,IAAI,MAAM,MAAM,iBAAiB,CAAC;AAAA,IAC5C,OAAO;AACL,cAAQ,IAAI,mCAAmC;AAAA,IACjD;AAEA,YAAQ,MAAM;AAAA,EAChB,CAAC;AACL;;;AIlHA,OAAOC,YAAW;;;ACaX,SAAS,eAAe,eAAqC;AAClE,MAAI,iBAAiB,GAAI,QAAO;AAChC,MAAI,iBAAiB,GAAI,QAAO;AAChC,MAAI,iBAAiB,GAAI,QAAO;AAChC,SAAO;AACT;AAEO,SAAS,mBAAmB,cAAyC;AAC1E,QAAM,WAAW,wBAAwB,YAAY;AACrD,QAAM,aAAa,kBAAkB;AACrC,QAAM,QAAQ,eAAe,SAAS,aAAa;AAEnD,QAAM,YAAY;AAAA,IAChB,EAAE,OAAO,iBAAiB,QAAQ,SAAS,cAAc,SAAU,SAAS,eAAe,sBAAuB,IAAI;AAAA,IACtH,EAAE,OAAO,kBAAkB,QAAQ,SAAS,cAAc,SAAU,SAAS,eAAe,sBAAuB,IAAI;AAAA,IACvH,EAAE,OAAO,cAAc,WAAW,MAAM,SAAS,QAAQ,SAAS,UAAU,SAAU,SAAS,WAAW,sBAAuB,IAAI;AAAA,IACrI,EAAE,OAAO,aAAa,QAAQ,SAAS,UAAU,SAAU,SAAS,WAAW,sBAAuB,IAAI;AAAA,IAC1G,EAAE,OAAO,sBAAsB,QAAQ,SAAS,mBAAmB,SAAU,SAAS,oBAAoB,sBAAuB,IAAI;AAAA,EACvI;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,eAAe,SAAS;AAAA,IACxB;AAAA,EACF;AACF;;;ACzCA,SAAS,gBAAAC,eAAc,cAAAC,mBAAkB;AACzC,SAAS,QAAAC,aAAY;AAIrB,IAAM,wBAAwB;AAC9B,IAAM,mBAAmB;AACzB,IAAM,aAAa;AAInB,SAAS,iBAAiB,UAAkB,OAA8B;AACxE,QAAM,MAAM,QAAQ,IAAI,WAAW,QAAQ;AAC3C,MAAI,OAAO,IAAK,QAAO;AACvB,MAAI,OAAO,IAAK,QAAO;AACvB,SAAO;AACT;AAEO,SAAS,aAAa,UAA4C;AACvE,QAAM,QAAQ,WACV,CAAC,QAAQ,IACT;AAAA,IACEC,MAAK,QAAQ,IAAI,GAAG,WAAW;AAAA,IAC/BA,MAAK,QAAQ,IAAI,GAAG,WAAW,WAAW;AAAA,EAC5C;AAEJ,MAAI,eAA8B;AAClC,aAAW,KAAK,OAAO;AACrB,QAAIC,YAAW,CAAC,GAAG;AACjB,qBAAe;AACf;AAAA,IACF;AAAA,EACF;AAEA,MAAI,CAAC,aAAc,QAAO;AAE1B,QAAM,UAAUC,cAAa,cAAc,OAAO;AAClD,QAAM,QAAQ,QAAQ,MAAM,IAAI;AAChC,QAAM,aAAa,MAAM;AACzB,QAAM,cAAc,sBAAsB,OAAO;AACjD,QAAM,cAAc,YAAY,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,QAAQ,CAAC;AAEpE,MAAI,0BAA0B;AAC9B,QAAM,kBAA4B,CAAC;AAEnC,QAAM,WAA8B,YAAY,IAAI,CAAC,MAAM;AACzD,UAAM,YAAY,EAAE,YAAY,EAAE,WAAW;AAC7C,UAAM,gBAAgB,iBAAiB,UAAU,UAAU;AAC3D,UAAM,mBAAmB,sBAAsB,KAAK,EAAE,OAAO;AAE7D,QAAI,oBAAoB,kBAAkB,cAAc;AACtD;AAAA,IACF;AAEA,QAAI,iBAAiB,KAAK,EAAE,KAAK,KAAK,iBAAiB,KAAK,EAAE,OAAO,GAAG;AACtE,sBAAgB,KAAK,EAAE,KAAK;AAAA,IAC9B;AAEA,WAAO;AAAA,MACL,OAAO,EAAE;AAAA,MACT,SAAS,EAAE;AAAA,MACX,QAAQ,EAAE;AAAA,MACV,WAAW,EAAE;AAAA,MACb,SAAS,EAAE;AAAA,MACX;AAAA,MACA;AAAA,IACF;AAAA,EACF,CAAC;AAGD,QAAM,WAAW,SAAS,OAAO,CAAC,MAAM,EAAE,gBAAgB;AAC1D,QAAM,SAAS,SAAS,OAAO,CAAC,MAAM,CAAC,EAAE,oBAAoB,EAAE,UAAU,GAAG;AAC5E,QAAM,QAAQ,SAAS,OAAO,CAAC,MAAM,CAAC,EAAE,oBAAoB,EAAE,SAAS,GAAG;AAC1E,QAAM,mBAAmB,CAAC,GAAG,UAAU,GAAG,QAAQ,GAAG,KAAK,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK;AAE9E,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,iBAAiB,aAAa;AAAA,IAC9B;AAAA,EACF;AACF;;;ACnFA,SAAS,gBAAAC,eAAc,cAAAC,mBAAkB;AACzC,SAAS,QAAAC,aAAY;AACrB,SAAS,WAAAC,gBAAe;AAcxB,IAAM,mBAA2C;AAAA,EAC/C,YAAY;AAAA,EACZ,WAAW;AAAA,EACX,YAAY;AAAA,EACZ,QAAQ;AAAA,EACR,OAAO;AACT;AAEO,SAAS,aAA0B;AACxC,QAAM,UAA2B,CAAC;AAClC,QAAM,cAAc;AAAA,IAClBC,MAAK,QAAQ,IAAI,GAAG,WAAW;AAAA,IAC/BA,MAAKC,SAAQ,GAAG,cAAc;AAAA,EAChC;AAEA,aAAW,cAAc,aAAa;AACpC,QAAI,CAACC,YAAW,UAAU,EAAG;AAC7B,QAAI;AACF,YAAM,MAAM,KAAK,MAAMC,cAAa,YAAY,OAAO,CAAC;AACxD,YAAM,aAAa,IAAI,cAAc,IAAI,eAAe,CAAC;AACzD,iBAAW,CAAC,MAAM,MAAM,KAAK,OAAO,QAAQ,UAAU,GAAG;AACvD,cAAM,MAAM;AACZ,cAAM,QAAQ,MAAM,QAAQ,IAAI,KAAK,IAAI,IAAI,QAAQ,CAAC;AACtD,cAAM,YAAY,MAAM,UAAU;AAClC,cAAM,kBAAkB,YAAY;AACpC,gBAAQ,KAAK;AAAA,UACX;AAAA,UACA;AAAA,UACA;AAAA,UACA,SAAS,YAAY;AAAA,QACvB,CAAC;AAAA,MACH;AAAA,IACF,QAAQ;AACN;AAAA,IACF;AAAA,EACF;AAEA,QAAM,aAAa,QAAQ,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,WAAW,CAAC;AAClE,QAAM,cAAc,QAAQ,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,iBAAiB,CAAC;AACzE,QAAM,eAAe,QAAQ,OAAO,CAAC,MAAM,EAAE,OAAO;AAGpD,MAAI,gBAAgB;AACpB,QAAM,gBAAgB;AAAA,IACpBH,MAAK,QAAQ,IAAI,GAAG,WAAW,eAAe;AAAA,IAC9CA,MAAKC,SAAQ,GAAG,WAAW,eAAe;AAAA,EAC5C;AACA,aAAW,MAAM,eAAe;AAC9B,QAAI,CAACC,YAAW,EAAE,EAAG;AACrB,QAAI;AACF,YAAM,WAAW,KAAK,MAAMC,cAAa,IAAI,OAAO,CAAC;AACrD,UAAI,SAAS,oBAAoB,SAAS,aAAa;AACrD,wBAAgB;AAChB;AAAA,MACF;AAAA,IACF,QAAQ;AACN;AAAA,IACF;AAAA,EACF;AAEA,QAAM,kBAAkB,gBAAgB,KAAK,MAAM,cAAc,IAAI,IAAI;AAEzE,QAAM,kBAA4B,CAAC;AACnC,aAAW,UAAU,cAAc;AACjC,oBAAgB;AAAA,MACd,IAAI,OAAO,IAAI,SAAS,OAAO,SAAS,WAAW,OAAO,gBAAgB,eAAe,CAAC;AAAA,IAC5F;AAAA,EACF;AAEA,aAAW,UAAU,SAAS;AAC5B,UAAM,MAAM,iBAAiB,OAAO,KAAK,YAAY,CAAC;AACtD,QAAI,KAAK;AACP,sBAAgB,KAAK,GAAG,OAAO,IAAI,KAAK,GAAG,EAAE;AAAA,IAC/C;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;;;AChGO,SAAS,aAAa,cAAoC;AAC/D,QAAM,cAAc,mBAAmB,YAAY;AACnD,QAAM,mBAAmB,aAAa,YAAY;AAClD,QAAM,cAAc,WAAW;AAE/B,QAAM,kBAAyC,CAAC;AAGhD,MAAI,kBAAkB;AACpB,QAAI,iBAAiB,0BAA0B,GAAG;AAChD,sBAAgB,KAAK;AAAA,QACnB,UAAU;AAAA,QACV,QAAQ;AAAA,QACR,QAAQ,4BAAuB,iBAAiB,uBAAuB;AAAA,QACvE,UAAU;AAAA,MACZ,CAAC;AAAA,IACH;AAEA,QAAI,iBAAiB,iBAAiB;AACpC,sBAAgB,KAAK;AAAA,QACnB,UAAU;AAAA,QACV,QAAQ,GAAG,iBAAiB,aAAa,GAAG;AAAA,QAC5C,QAAQ,gBAAgB,iBAAiB,UAAU;AAAA,QACnD,UAAU;AAAA,MACZ,CAAC;AAAA,IACH;AAEA,UAAM,gBAAgB,iBAAiB,SAAS,OAAO,CAAC,MAAM,EAAE,SAAS,GAAG;AAC5E,eAAW,WAAW,eAAe;AACnC,sBAAgB,KAAK;AAAA,QACnB,UAAU;AAAA,QACV,QAAQ,IAAI,QAAQ,MAAM;AAAA,QAC1B,QAAQ,SAAS,QAAQ,KAAK,yBAAyB,QAAQ,MAAM;AAAA,QACrE,UAAU;AAAA,MACZ,CAAC;AAAA,IACH;AAAA,EACF;AAGA,aAAW,UAAU,YAAY,cAAc;AAC7C,oBAAgB,KAAK;AAAA,MACnB,UAAU;AAAA,MACV,QAAQ,IAAI,OAAO,gBAAgB,eAAe,CAAC;AAAA,MACnD,QAAQ,eAAe,OAAO,IAAI,SAAS,OAAO,SAAS;AAAA,MAC3D,UAAU;AAAA,IACZ,CAAC;AAAA,EACH;AAEA,MAAI,CAAC,YAAY,iBAAiB,YAAY,aAAa,IAAI;AAC7D,oBAAgB,KAAK;AAAA,MACnB,UAAU;AAAA,MACV,QAAQ,IAAI,KAAK,MAAM,YAAY,cAAc,IAAI,EAAE,eAAe,CAAC;AAAA,MACvE,QAAQ;AAAA,MACR,UAAU;AAAA,IACZ,CAAC;AAAA,EACH;AAEA,aAAW,OAAO,YAAY,iBAAiB;AAC7C,QAAI,IAAI,SAAS,KAAK,GAAG;AACvB,sBAAgB,KAAK;AAAA,QACnB,UAAU;AAAA,QACV,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR,UAAU;AAAA,MACZ,CAAC;AAAA,IACH;AAAA,EACF;AAGA,MAAI,YAAY,gBAAgB,IAAI;AAClC,oBAAgB,KAAK;AAAA,MACnB,UAAU;AAAA,MACV,QAAQ,QAAQ,YAAY,cAAc,QAAQ,CAAC,CAAC;AAAA,MACpD,QAAQ,wCAAwC,YAAY,KAAK,sBAAsB,YAAY,SAAS,cAAc,eAAe,CAAC;AAAA,MAC1I,UAAU;AAAA,IACZ,CAAC;AAAA,EACH;AAGA,QAAM,gBAAgB,EAAE,MAAM,GAAG,QAAQ,GAAG,KAAK,EAAE;AACnD,kBAAgB,KAAK,CAAC,GAAG,MAAM,cAAc,EAAE,QAAQ,IAAI,cAAc,EAAE,QAAQ,CAAC;AAEpF,SAAO;AAAA,IACL,OAAO,YAAY;AAAA,IACnB,iBAAiB,YAAY;AAAA,IAC7B;AAAA,IACA,YAAY,YAAY;AAAA,IACxB;AAAA,EACF;AACF;;;AJzFO,SAAS,qBAAqBC,UAAwB;AAC3D,EAAAA,SACG,QAAQ,OAAO,EACf,YAAY,+BAA+B,EAC3C,OAAO,SAAS,uBAAuB,EACvC,OAAO,oBAAoB,sBAAsB,EACjD,OAAO,cAAc,wBAAwB,EAC7C,OAAO,UAAU,gBAAgB,EACjC,OAAO,CAAC,SAAS;AAChB,UAAM,SAAS,aAAa;AAE5B,QAAI,KAAK,MAAM;AACb,cAAQ,IAAI,KAAK,UAAU,QAAQ,MAAM,CAAC,CAAC;AAC3C;AAAA,IACF;AAEA,UAAM,aACJ,OAAO,UAAU,MAAMC,OAAM,QAC7B,OAAO,UAAU,MAAMA,OAAM,SAC7BA,OAAM;AAER,YAAQ,IAAIA,OAAM,KAAK,KAAK,6BAA6B,CAAC;AAC1D,YAAQ;AAAA,MACN,4BAA4B,WAAW,KAAK,OAAO,KAAK,CAAC,KAAK,OAAO,gBAAgB,cAAc,QAAQ,CAAC,CAAC;AAAA;AAAA,IAC/G;AAGA,QAAI,CAAC,KAAK,cAAc;AACtB,cAAQ,IAAIA,OAAM,KAAK,0BAA0B,CAAC;AAClD,YAAM,KAAK,OAAO;AAClB,YAAM,MAAM,CAAC,OAAe,WAAmB;AAC7C,cAAM,OAAQ,SAAS,sBAAuB,KAAK,QAAQ,CAAC;AAC5D,eAAO,OAAO,MAAM,OAAO,EAAE,CAAC,IAAI,OAAO,eAAe,EAAE,SAAS,CAAC,CAAC,YAAY,GAAG;AAAA,MACtF;AACA,cAAQ,IAAI,IAAI,kBAAkB,GAAG,YAAY,CAAC;AAClD,cAAQ,IAAI,IAAI,mBAAmB,GAAG,YAAY,CAAC;AACnD,cAAQ,IAAI,IAAI,cAAc,OAAO,WAAW,MAAM,UAAU,GAAG,QAAQ,CAAC;AAC5E,cAAQ,IAAI,IAAI,cAAc,GAAG,QAAQ,CAAC;AAC1C,cAAQ,IAAI,IAAI,uBAAuB,GAAG,iBAAiB,CAAC;AAC5D,cAAQ,IAAI,SAAS,IAAI,OAAO,EAAE,CAAC;AACnC,cAAQ,IAAI,IAAI,mBAAmB,GAAG,aAAa,CAAC;AACpD,cAAQ;AAAA,QACN,OAAO,oBAAoB,OAAO,EAAE,CAAC,IAAI,GAAG,gBAAgB,eAAe,EAAE,SAAS,CAAC,CAAC,YAAY,GAAG,cAAc,QAAQ,CAAC,CAAC;AAAA;AAAA,MACjI;AAAA,IACF;AAGA,QAAI,CAAC,KAAK,WAAW,OAAO,kBAAkB;AAC5C,YAAM,MAAM,OAAO;AACnB,cAAQ,IAAIA,OAAM,KAAK,uBAAuB,CAAC;AAC/C,cAAQ;AAAA,QACN,cAAc,IAAI,UAAU,GAAG,IAAI,kBAAkBA,OAAM,OAAO,mBAAmB,IAAI,EAAE;AAAA,MAC7F;AACA,cAAQ,IAAI,eAAe,IAAI,YAAY,eAAe,CAAC,EAAE;AAC7D,cAAQ;AAAA,QACN,oCAAoC,IAAI,0BAA0B,IAAIA,OAAM,IAAI,OAAO,IAAI,uBAAuB,CAAC,IAAI,GAAG;AAAA,MAC5H;AAEA,UAAI,KAAK,cAAc;AAErB,gBAAQ,IAAI;AACZ,gBAAQ,IAAIA,OAAM,KAAK,aAAa,CAAC;AACrC,mBAAW,WAAW,IAAI,UAAU;AAClC,gBAAM,OAAO,QAAQ,kBAAkB,eAAeA,OAAM,IAAI,cAAc,IAAIA,OAAM,MAAM,mBAAmB;AACjH,gBAAM,WAAW,QAAQ,mBAAmBA,OAAM,OAAO,mBAAmB,IAAI;AAChF,kBAAQ;AAAA,YACN,OAAO,QAAQ,MAAM,OAAO,EAAE,CAAC,IAAI,OAAO,QAAQ,MAAM,EAAE,SAAS,CAAC,CAAC,aAAa,QAAQ,SAAS,IAAI,QAAQ,OAAO,GAAG,IAAI,GAAG,QAAQ;AAAA,UAC1I;AAAA,QACF;AAEA,YAAI,IAAI,iBAAiB,SAAS,GAAG;AACnC,kBAAQ,IAAI;AACZ,kBAAQ,IAAIA,OAAM,KAAK,4BAA4B,CAAC;AACpD,cAAI,iBAAiB,QAAQ,CAAC,OAAO,MAAM;AACzC,oBAAQ,IAAI,OAAO,IAAI,CAAC,KAAK,KAAK,EAAE;AAAA,UACtC,CAAC;AAAA,QACH;AAAA,MACF;AACA,cAAQ,IAAI;AAAA,IACd,WAAW,CAAC,KAAK,WAAW,CAAC,OAAO,kBAAkB;AACpD,cAAQ,IAAIA,OAAM,OAAO,0DAA0D,CAAC;AAAA,IACtF;AAGA,QAAI,KAAK,WAAW,OAAO,WAAW,SAAS,GAAG;AAChD,cAAQ,IAAIA,OAAM,KAAK,gBAAgB,CAAC;AACxC,iBAAW,UAAU,OAAO,YAAY;AACtC,cAAM,QAAQ,OAAO,UAAUA,OAAM,IAAI,UAAU,IAAI;AACvD,gBAAQ;AAAA,UACN,OAAO,OAAO,KAAK,OAAO,EAAE,CAAC,IAAI,OAAO,OAAO,SAAS,EAAE,SAAS,CAAC,CAAC,WAAW,OAAO,gBAAgB,eAAe,EAAE,SAAS,CAAC,CAAC,UAAU,KAAK;AAAA,QACpJ;AAAA,MACF;AACA,cAAQ,IAAI;AAAA,IACd,WAAW,KAAK,WAAW,OAAO,WAAW,WAAW,GAAG;AACzD,cAAQ,IAAIA,OAAM,IAAI,gCAAgC,CAAC;AAAA,IACzD;AAGA,QAAI,OAAO,gBAAgB,SAAS,GAAG;AACrC,YAAM,eAAe,KAAK,eACtB,OAAO,gBAAgB,OAAO,CAAC,MAAM,EAAE,aAAa,WAAW,IAC/D,KAAK,UACH,OAAO,gBAAgB,OAAO,CAAC,MAAM,EAAE,aAAa,KAAK,IACzD,OAAO;AAEb,UAAI,aAAa,SAAS,GAAG;AAC3B,gBAAQ,IAAIA,OAAM,KAAK,oBAAoB,CAAC;AAC5C,qBAAa,QAAQ,CAAC,KAAK,MAAM;AAC/B,gBAAM,gBACJ,IAAI,aAAa,SAASA,OAAM,MAChC,IAAI,aAAa,WAAWA,OAAM,SAClCA,OAAM;AACR,kBAAQ;AAAA,YACN,OAAO,IAAI,CAAC,KAAK,cAAc,IAAI,IAAI,SAAS,YAAY,CAAC,GAAG,CAAC,IAAI,IAAI,MAAM;AAAA,UACjF;AACA,kBAAQ,IAAIA,OAAM,IAAI,kBAAkB,IAAI,MAAM,EAAE,CAAC;AAAA,QACvD,CAAC;AAAA,MACH;AAAA,IACF;AACA,YAAQ,IAAI;AAEZ,QAAI,KAAK,KAAK;AACZ,cAAQ,IAAIA,OAAM,OAAO,+DAA+D,CAAC;AAAA,IAC3F;AAAA,EACF,CAAC;AACL;;;AKjIA,OAAOC,YAAW;AAClB,OAAOC,YAAW;AAUX,SAAS,sBAAsBC,UAAwB;AAC5D,EAAAA,SACG,QAAQ,QAAQ,EAChB,YAAY,yBAAyB,EACrC,OAAO,qBAAqB,sCAAsC,OAAO,EACzE,OAAO,wBAAwB,4BAA4B,EAC3D,OAAO,WAAW,0BAA0B,EAC5C,OAAO,cAAc,4BAA4B,EACjD,OAAO,SAAS,eAAe,EAC/B,OAAO,UAAU,gBAAgB,EACjC,OAAO,OAAO,SAAS;AACtB,UAAM,QAAQ,MAAM,eAAe,KAAK,OAAO;AAE/C,QAAI,MAAM,WAAW,GAAG;AACtB,cAAQ,IAAI,kEAAkE;AAC9E;AAAA,IACF;AAGA,UAAM,MAAMC,OAAM;AAClB,QAAI;AACJ,YAAQ,KAAK,QAAQ;AAAA,MACnB,KAAK;AACH,iBAAS,IAAI,QAAQ,KAAK;AAC1B;AAAA,MACF,KAAK;AACH,iBAAS,IAAI,SAAS,GAAG,KAAK;AAC9B;AAAA,MACF,KAAK;AACH,iBAAS,IAAI,SAAS,IAAI,KAAK;AAC/B;AAAA,MACF,KAAK;AACH,iBAASA,OAAM,YAAY;AAC3B;AAAA,MACF;AACE,iBAAS,IAAI,QAAQ,KAAK;AAAA,IAC9B;AAGA,UAAM,cAA+B,CAAC;AACtC,UAAM,mBAAyF,CAAC;AAEhG,eAAW,QAAQ,OAAO;AACxB,UAAI;AACF,cAAM,UAAU,iBAAiB,IAAI;AACrC,cAAM,mBAAmB,QAAQ,SAAS;AAAA,UAAO,CAAC,MAChDA,OAAM,EAAE,SAAS,EAAE,QAAQ,MAAM;AAAA,QACnC;AACA,YAAI,iBAAiB,WAAW,EAAG;AAEnC,oBAAY,KAAK,GAAG,gBAAgB;AAEpC,cAAM,cAAc,iBAAiB;AAAA,UACnC,CAAC,KAAK,MAAM,MAAM,qBAAqB,CAAC,EAAE;AAAA,UAC1C;AAAA,QACF;AACA,yBAAiB,KAAK;AAAA,UACpB,IAAI,QAAQ;AAAA,UACZ,OAAO,iBAAiB,CAAC,GAAG,SAAS;AAAA,UACrC,MAAM;AAAA,UACN,UAAU,iBAAiB;AAAA,QAC7B,CAAC;AAAA,MACH,QAAQ;AACN;AAAA,MACF;AAAA,IACF;AAEA,QAAI,YAAY,WAAW,GAAG;AAC5B,cAAQ,IAAI,6BAA6B,KAAK,MAAM,EAAE;AACtD;AAAA,IACF;AAGA,QAAI,YAAY;AAChB,QAAI,aAAa;AACjB,QAAI,cAAc;AAClB,QAAI,iBAAiB;AAErB,eAAW,OAAO,aAAa;AAC7B,mBAAa,qBAAqB,GAAG,EAAE;AACvC,oBAAc,IAAI,MAAM;AACxB,qBAAe,IAAI,MAAM;AACzB,wBAAkB,IAAI,MAAM;AAAA,IAC9B;AAEA,UAAM,iBAAiB,aAAa;AACpC,UAAM,eAAe,iBAAiB,IAAK,iBAAiB,iBAAkB,MAAM;AAEpF,QAAI,KAAK,MAAM;AACb,cAAQ;AAAA,QACN,KAAK;AAAA,UACH;AAAA,YACE,QAAQ,KAAK;AAAA,YACb;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA,UAAU;AAAA,UACZ;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,MACF;AACA;AAAA,IACF;AAEA,QAAI,KAAK,KAAK;AACZ,cAAQ,IAAI,oCAAoC;AAChD,iBAAW,KAAK,kBAAkB;AAChC,gBAAQ,IAAI,GAAG,EAAE,EAAE,IAAI,EAAE,KAAK,IAAI,EAAE,KAAK,QAAQ,CAAC,CAAC,IAAI,EAAE,QAAQ,EAAE;AAAA,MACrE;AACA;AAAA,IACF;AAGA,UAAM,cAAc,KAAK,WAAW,UAAU,IAAI,OAAO,kBAAkB,IAAI,KAAK;AACpF,YAAQ,IAAIC,OAAM,KAAK,KAAK;AAAA,uBAA0B,WAAW;AAAA,CAAI,CAAC;AACtE,YAAQ,IAAI,uBAAuBA,OAAM,KAAK,WAAW,SAAS,CAAC,CAAC,EAAE;AACtE,YAAQ,IAAI,uBAAuB,aAAa,UAAU,CAAC,SAAS,aAAa,WAAW,CAAC,MAAM;AACnG,YAAQ,IAAI,uBAAuB,aAAa,QAAQ,CAAC,CAAC,GAAG;AAC7D,YAAQ,IAAI,uBAAuB,iBAAiB,MAAM,EAAE;AAC5D,YAAQ,IAAI;AAEZ,QAAI,KAAK,SAAS,KAAK,UAAU;AAE/B,YAAM,UAAU,oBAAI,IAAgD;AACpE,iBAAW,KAAK,kBAAkB;AAChC,cAAM,WAAW,QAAQ,IAAI,EAAE,KAAK,KAAK,EAAE,MAAM,GAAG,UAAU,EAAE;AAChE,iBAAS,QAAQ,EAAE;AACnB,iBAAS;AACT,gBAAQ,IAAI,EAAE,OAAO,QAAQ;AAAA,MAC/B;AAEA,cAAQ,IAAIA,OAAM,KAAK,oBAAoB,CAAC;AAC5C,iBAAW,CAAC,OAAO,IAAI,KAAK,SAAS;AACnC,cAAM,MAAM,YAAY,KAAM,KAAK,OAAO,YAAa,KAAK,QAAQ,CAAC,IAAI;AACzE,cAAM,aAAa,MAAM,QAAQ,WAAW,EAAE,EAAE,QAAQ,aAAa,EAAE;AACvE,gBAAQ;AAAA,UACN,OAAO,UAAU,KAAK,WAAW,KAAK,IAAI,CAAC,KAAK,GAAG,SAAS,KAAK,QAAQ;AAAA,QAC3E;AAAA,MACF;AACA,cAAQ,IAAI;AAAA,IACd;AAEA,QAAI,KAAK,UAAU;AACjB,cAAQ,IAAIA,OAAM,KAAK,sBAAsB,CAAC;AAC9C,iBAAW,KAAK,iBAAiB,KAAK,CAAC,GAAG,MAAM,EAAE,OAAO,EAAE,IAAI,GAAG;AAChE,gBAAQ,IAAI,OAAO,EAAE,GAAG,MAAM,GAAG,EAAE,CAAC,KAAK,WAAW,EAAE,IAAI,CAAC,KAAK,EAAE,QAAQ,WAAW,EAAE,KAAK,GAAG;AAAA,MACjG;AACA,cAAQ,IAAI;AAAA,IACd;AAGA,UAAM,SAAS,eAAe,aAAa,MAAM;AACjD,QAAI,OAAO,SAAS,GAAG;AACrB,cAAQ,IAAIA,OAAM,KAAK,WAAW,CAAC;AACnC,YAAM,UAAU,KAAK,IAAI,GAAG,OAAO,IAAI,CAAC,MAAM,EAAE,SAAS,CAAC;AAC1D,iBAAW,KAAK,OAAO,MAAM,EAAE,GAAG;AAChC,cAAM,SAAS,UAAU,IAAI,KAAK,MAAO,EAAE,YAAY,UAAW,EAAE,IAAI;AACxE,cAAM,MAAM,SAAS,OAAO,MAAM,IAAI,SAAS,OAAO,KAAK,MAAM;AACjE,gBAAQ,IAAI,OAAO,EAAE,YAAY,OAAO,EAAE,CAAC,IAAI,GAAG,IAAI,WAAW,EAAE,SAAS,CAAC,EAAE;AAAA,MACjF;AACA,cAAQ,IAAI;AAAA,IACd;AAAA,EACF,CAAC;AACL;;;AChLA,OAAOC,YAAW;AAClB,SAAS,aAAAC,YAAW,cAAAC,aAAY,gBAAAC,eAAc,eAAe,oBAAoB;AACjF,SAAS,QAAAC,OAAM,WAAAC,gBAAe;AAC9B,SAAS,WAAAC,gBAAe;AAIjB,SAAS,oBAAoBC,UAAwB;AAC1D,EAAAA,SACG,QAAQ,MAAM,EACd,YAAY,yCAAyC,EACrD,OAAO,YAAY,wBAAwB,EAC3C,OAAO,gBAAgB,oBAAoB,EAC3C,OAAO,cAAc,wBAAwB,EAC7C,OAAO,WAAW,2BAA2B,EAC7C,OAAO,OAAO,SAAS;AACtB,YAAQ,IAAIC,OAAM,KAAK,KAAK,4BAA4B,CAAC;AACzD,YAAQ,IAAI,qDAAqD;AAGjE,UAAM,UAAUC,MAAKC,SAAQ,GAAG,OAAO;AACvC,QAAI,CAACC,YAAW,OAAO,GAAG;AACxB,MAAAC,WAAU,SAAS,EAAE,WAAW,KAAK,CAAC;AACtC,cAAQ,IAAIJ,OAAM,MAAM,oBAAoB,CAAC;AAAA,IAC/C;AAEA,QAAI,CAAC,KAAK,WAAW;AAEnB,UAAI;AACF,cAAM,KAAK,aAAa;AACxB,sBAAc,EAAE;AAChB,WAAG,MAAM;AACT,gBAAQ,IAAIA,OAAM,MAAM,2BAA2B,CAAC;AAAA,MACtD,SAAS,KAAK;AACZ,gBAAQ,IAAIA,OAAM,IAAI,gCAAgC,GAAG,EAAE,CAAC;AAAA,MAC9D;AAAA,IACF;AAGA,QAAI;AACF,YAAM,EAAE,UAAAK,UAAS,IAAI,MAAM,OAAO,oBAAoB;AACtD,UAAI;AACF,QAAAA,UAAS,aAAa,EAAE,OAAO,SAAS,CAAC;AACzC,gBAAQ,IAAIL,OAAM,MAAM,qDAAqD,CAAC;AAAA,MAChF,QAAQ;AAAA,MAAsB;AAE9B,UAAI;AACF,QAAAK,UAAS,iBAAiB,EAAE,OAAO,SAAS,CAAC;AAC7C,gBAAQ,IAAIL,OAAM,MAAM,mDAAmD,CAAC;AAAA,MAC9E,QAAQ;AAAA,MAAsB;AAAA,IAChC,QAAQ;AAAA,IAAe;AAGvB,QAAI,KAAK,UAAU,OAAO;AACxB,YAAM,eAAe,KAAK,SACtBC,MAAKC,SAAQ,GAAG,WAAW,eAAe,IAC1CD,MAAK,QAAQ,IAAI,GAAG,WAAW,eAAe;AAElD,cAAQ,IAAI,kCAAkC;AAC9C,cAAQ,IAAI,oDAAoD;AAChE,cAAQ,IAAI,sCAAsC;AAClD,cAAQ,IAAI;AAAA,2BAA8B,KAAK,SAAS,cAAc,SAAS,gBAAgB;AAE/F,UAAI;AACF,qBAAa,YAAY;AACzB,gBAAQ,IAAID,OAAM,MAAM,qBAAqB,CAAC;AAAA,MAChD,SAAS,KAAK;AACZ,gBAAQ,IAAIA,OAAM,OAAO;AAAA,+BAAkC,GAAG,EAAE,CAAC;AAAA,MACnE;AAAA,IACF;AAEA,YAAQ,IAAIA,OAAM,KAAK,0CAA0C,CAAC;AAClE,YAAQ,IAAIA,OAAM,IAAI,4DAA4D,CAAC;AACnF,YAAQ,IAAIA,OAAM,IAAI,KAAK,UAAU;AAAA,MACnC,KAAK;AAAA,QACH,qBAAqB;AAAA,QACrB,iCAAiC;AAAA,MACnC;AAAA,IACF,GAAG,MAAM,CAAC,EAAE,MAAM,IAAI,EAAE,IAAI,OAAK,SAAS,CAAC,EAAE,KAAK,IAAI,CAAC,CAAC;AAExD,YAAQ,IAAIA,OAAM,KAAK,KAAK,yDAAyD,CAAC;AAAA,EACxF,CAAC;AACL;AAEA,SAAS,aAAa,cAA4B;AAChD,QAAM,MAAMM,SAAQ,YAAY;AAChC,MAAI,CAACH,YAAW,GAAG,GAAG;AACpB,IAAAC,WAAU,KAAK,EAAE,WAAW,KAAK,CAAC;AAAA,EACpC;AAEA,MAAI,WAAoC,CAAC;AACzC,MAAID,YAAW,YAAY,GAAG;AAE5B,UAAM,aAAa,eAAe;AAClC,iBAAa,cAAc,UAAU;AACrC,eAAW,KAAK,MAAMI,cAAa,cAAc,OAAO,CAAC;AAAA,EAC3D;AAEA,QAAM,QAAS,SAAS,SAAS,CAAC;AAGlC,MAAI,CAAC,MAAM,cAAc;AACvB,UAAM,eAAe,CAAC;AAAA,EACxB;AAGA,MAAI,CAAC,MAAM,MAAM;AACf,UAAM,OAAO,CAAC;AAAA,EAChB;AAEA,WAAS,QAAQ;AACjB,gBAAc,cAAc,KAAK,UAAU,UAAU,MAAM,CAAC,CAAC;AAC/D;;;AtBzGA,IAAM,UAAU,IAAI,QAAQ;AAE5B,QACG,KAAK,UAAU,EACf,QAAQ,OAAO,EACf,YAAY,2DAA2D;AAG1E,qBAAqB,OAAO;AAC5B,wBAAwB,OAAO;AAC/B,sBAAsB,OAAO;AAC7B,qBAAqB,OAAO;AAC5B,sBAAsB,OAAO;AAC7B,oBAAoB,OAAO;AAG3B,QAAQ,OAAO,YAAY;AACzB,QAAM,QAAQ,SAAS,KAAK,CAAC,MAAM,EAAE,KAAK,MAAM,OAAO,GAAG,WAAW,CAAC,GAAG,EAAE,MAAM,OAAO,CAAC;AAC3F,CAAC;AAED,QAAQ,MAAM;","names":["React","join","join","Box","Text","dayjs","dayjs","dayjs","Box","Text","jsx","jsxs","readFileSync","join","homedir","join","readFileSync","homedir","jsx","jsxs","Box","Text","program","React","React","render","glob","execSync","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","execSync","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 { createRequire } from \"node:module\";\nimport { 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 require = createRequire(import.meta.url);\nconst pkg = require(\"../../package.json\");\n\nconst program = new Command();\n\nprogram\n .name(\"kerf-cli\")\n .version(pkg.version)\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 if (!process.stdin.isTTY) {\n console.log(\n \"kerf-cli watch requires an interactive terminal (TTY). Run it directly in a terminal tab, not piped.\",\n );\n process.exit(1);\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(\n (input) => {\n if (input === \"q\") exit();\n if (input === \"b\") setShowBudget((prev) => !prev);\n },\n { isActive: process.stdin.isTTY ?? false },\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 // key is \"YYYY-MM-DD-HH\", convert to parseable format\n const parts = key.split(\"-\");\n const dateStr = `${parts[0]}-${parts[1]}-${parts[2]}T${parts[3]}:00:00`;\n return dayjs(dateStr).format(\"MMM D, h A\");\n }\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 { execSync } from \"node:child_process\";\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\nfunction findGitRootClaudeMd(): string[] {\n try {\n const gitRoot = execSync(\"git rev-parse --show-toplevel\", {\n encoding: \"utf-8\",\n stdio: [\"pipe\", \"pipe\", \"ignore\"],\n }).trim();\n if (gitRoot && gitRoot !== process.cwd()) {\n return [\n join(gitRoot, \"CLAUDE.md\"),\n join(gitRoot, \".claude\", \"CLAUDE.md\"),\n ];\n }\n } catch {\n // Not in a git repo\n }\n return [];\n}\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 ...findGitRootClaudeMd(),\n join(homedir(), \".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 chalk from \"chalk\";\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\n if (opts.json) {\n for (const model of models) {\n const estimate = await estimateTaskCost(task, { model, files, cwd: process.cwd() });\n console.log(JSON.stringify(estimate, null, 2));\n }\n return;\n }\n\n // Print comparison table\n const estimates = await Promise.all(\n models.map(async (model) => ({\n model,\n estimate: await estimateTaskCost(task, { model, files, cwd: process.cwd() }),\n })),\n );\n\n console.log(chalk.bold.cyan(`\\n kerf-cli estimate: '${task}'\\n`));\n console.log(\n ` ${\"Model\".padEnd(10)} ${\"Turns\".padEnd(14)} ${\"Low\".padEnd(12)} ${\"Expected\".padEnd(12)} ${\"High\".padEnd(12)}`,\n );\n console.log(\" \" + \"-\".repeat(58));\n for (const { model, estimate } of estimates) {\n const turns = `${estimate.estimatedTurns.low}-${estimate.estimatedTurns.high}`;\n console.log(\n ` ${model.padEnd(10)} ${turns.padEnd(14)} ${chalk.green(estimate.estimatedCost.low.padEnd(12))} ${chalk.yellow(estimate.estimatedCost.expected.padEnd(12))} ${chalk.red(estimate.estimatedCost.high.padEnd(12))}`,\n );\n }\n console.log();\n\n const cheapest = estimates[2]; // haiku\n const priciest = estimates[1]; // opus\n console.log(\n chalk.dim(` Cheapest: ${cheapest.model} at ${cheapest.estimate.estimatedCost.expected}`),\n );\n console.log(\n chalk.dim(` Priciest: ${priciest.model} at ${priciest.estimate.estimatedCost.expected}`),\n );\n console.log();\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 // Ghost token breakdown — show unless --claude-md-only\n if (!opts.claudeMdOnly) {\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 // CLAUDE.md analysis — show unless --mcp-only\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\n if (opts.claudeMdOnly) {\n // Show per-section breakdown when claude-md-only\n console.log();\n console.log(chalk.bold(\" Sections:\"));\n for (const section of cma.sections) {\n const zone = section.attentionZone === \"low-middle\" ? chalk.red(\" [dead zone]\") : chalk.green(\" [high attention]\");\n const critical = section.hasCriticalRules ? chalk.yellow(\" *critical rules*\") : \"\";\n console.log(\n ` ${section.title.padEnd(30)} ${String(section.tokens).padStart(5)} tokens L${section.lineStart}-${section.lineEnd}${zone}${critical}`,\n );\n }\n\n if (cma.suggestedReorder.length > 0) {\n console.log();\n console.log(chalk.bold(\" Suggested section order:\"));\n cma.suggestedReorder.forEach((title, i) => {\n console.log(` ${i + 1}. ${title}`);\n });\n }\n }\n console.log();\n } else if (!opts.mcpOnly && !result.claudeMdAnalysis) {\n console.log(chalk.yellow(\" No CLAUDE.md found in current directory or git root.\\n\"));\n }\n\n // MCP details — show when --mcp-only\n if (opts.mcpOnly && result.mcpServers.length > 0) {\n console.log(chalk.bold(\" MCP Servers:\"));\n for (const server of result.mcpServers) {\n const heavy = server.isHeavy ? chalk.red(\" [heavy]\") : \"\";\n console.log(\n ` ${server.name.padEnd(20)} ${String(server.toolCount).padStart(3)} tools ${server.estimatedTokens.toLocaleString().padStart(6)} tokens${heavy}`,\n );\n }\n console.log();\n } else if (opts.mcpOnly && result.mcpServers.length === 0) {\n console.log(chalk.dim(\" No MCP servers configured.\\n\"));\n }\n\n // Recommendations — always show\n if (result.recommendations.length > 0) {\n const filteredRecs = opts.claudeMdOnly\n ? result.recommendations.filter((r) => r.category === \"claude-md\")\n : opts.mcpOnly\n ? result.recommendations.filter((r) => r.category === \"mcp\")\n : result.recommendations;\n\n if (filteredRecs.length > 0) {\n console.log(chalk.bold(\" Recommendations:\"));\n filteredRecs.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 }\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":";;;AAAA,SAAS,qBAAqB;AAC9B,SAAS,eAAe;;;ACDxB,OAAOA,YAAW;AAClB,SAAS,cAAc;;;ACDvB,SAAS,cAAc,gBAAgB;AACvC,SAAS,eAAe;AACxB,SAAS,QAAAC,OAAM,gBAAgB;AAG/B,OAAO,WAAW;AAClB,SAAS,aAAa;;;ACNtB,SAAS,eAAe;AACxB,SAAS,YAAY;AAGd,IAAM,iBAA6B;AAAA,EACxC,cAAc;AAAA,EACd,wBAAwB;AAAA,EACxB,sBAAsB;AAAA,EACtB,iBAAiB;AAAA,EACjB,SAAS,KAAK,QAAQ,GAAG,OAAO;AAAA,EAChC,aAAa;AACf;AAEO,IAAM,sBAAsB;AAC5B,IAAM,uBAAuB;AAC7B,IAAM,wBAAwB;AAC9B,IAAM,4BAA4B;AAClC,IAAM,sBAAsB;AAC5B,IAAM,uBAAuB;AAE7B,IAAM,sBAAsB,KAAK,QAAQ,GAAG,WAAW,UAAU;AACjE,IAAM,yBAAyB,KAAK,QAAQ,GAAG,WAAW,eAAe;AACzE,IAAM,eAAe,KAAK,QAAQ,GAAG,SAAS,SAAS;AACvD,IAAM,mBAAmB,KAAK,QAAQ,GAAG,SAAS,mBAAmB;;;ADA5E,SAAS,aAAa,KAAoD;AACxE,SAAO,IAAI,SAAS,SAAS,IAAI,SAAS,IAAI,OAAO,SAAS;AAChE;AAEA,SAAS,iBAAiB,KAAqC;AAC7D,SAAO,IAAI,SAAS,MAAM;AAC5B;AAEA,SAAS,aAAa,KAAqC;AACzD,SAAO,IAAI,SAAS,SAAS;AAC/B;AAEA,SAAS,iBAAiB,KAA8B;AACtD,SAAO,IAAI,aAAa,MAAM,EAAE,YAAY;AAC9C;AAEO,SAAS,eAAe,MAAsC;AACnE,QAAM,UAAU,KAAK,KAAK;AAC1B,MAAI,CAAC,QAAS,QAAO;AACrB,MAAI;AACF,WAAO,KAAK,MAAM,OAAO;AAAA,EAC3B,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,kBAAkB,SAAiB,WAAoC;AACrF,QAAM,QAAQ,QAAQ,MAAM,IAAI;AAChC,QAAM,aAAa,oBAAI,IAA2B;AAClD,MAAI,mBAAmB;AAEvB,aAAW,QAAQ,OAAO;AACxB,UAAM,MAAM,eAAe,IAAI;AAC/B,QAAI,CAAC,IAAK;AAEV,UAAM,QAAQ,aAAa,GAAG;AAC9B,QAAI,CAAC,MAAO;AAEZ,UAAM,KAAK,iBAAiB,GAAG,KAAK,QAAQ,kBAAkB;AAC9D,UAAM,QAAQ,aAAa,GAAG,KAAK;AACnC,UAAM,YAAY,iBAAiB,GAAG;AAEtC,UAAM,WAAW,WAAW,IAAI,EAAE;AAClC,UAAM,cAA4B;AAAA,MAChC,cAAc,MAAM,gBAAgB,UAAU,MAAM,gBAAgB;AAAA,MACpE,eAAe,MAAM,iBAAiB,UAAU,MAAM,iBAAiB;AAAA,MACvE,6BACE,MAAM,+BAA+B,UAAU,MAAM,+BAA+B;AAAA,MACtF,yBACE,MAAM,2BAA2B,UAAU,MAAM,2BAA2B;AAAA,IAChF;AAGA,eAAW,IAAI,IAAI;AAAA,MACjB;AAAA,MACA,OAAO,UAAU,YAAY,QAAQ,UAAU,SAAS;AAAA,MACxD;AAAA,MACA,OAAO;AAAA,MACP,cAAc,IAAI,kBAAkB,UAAU,gBAAgB;AAAA,IAChE,CAAC;AAAA,EACH;AAEA,SAAO,MAAM,KAAK,WAAW,OAAO,CAAC;AACvC;AAEO,SAAS,iBAAiB,UAAiC;AAChE,QAAM,UAAU,aAAa,UAAU,OAAO;AAC9C,QAAM,YAAY,SAAS,UAAU,QAAQ;AAC7C,QAAM,WAAW,kBAAkB,SAAS,SAAS;AAErD,QAAM,SAAS,SAAS;AAAA,IACtB,CAAC,KAAK,SAAS;AAAA,MACb,OAAO,IAAI,QAAQ,IAAI,MAAM;AAAA,MAC7B,QAAQ,IAAI,SAAS,IAAI,MAAM;AAAA,MAC/B,WAAW,IAAI,YAAY,IAAI,MAAM;AAAA,MACrC,eAAe,IAAI,gBAAgB,IAAI,MAAM;AAAA,MAC7C,MAAM,IAAI,QAAQ,IAAI,gBAAgB;AAAA,IACxC;AAAA,IACA,EAAE,OAAO,GAAG,QAAQ,GAAG,WAAW,GAAG,eAAe,GAAG,MAAM,EAAE;AAAA,EACjE;AAEA,QAAM,aAAa,SAAS,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,KAAK;AAEzD,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,kBAAkB,OAAO;AAAA,IACzB,mBAAmB,OAAO;AAAA,IAC1B,sBAAsB,OAAO;AAAA,IAC7B,0BAA0B,OAAO;AAAA,IACjC,cAAc,OAAO;AAAA,IACrB,WAAW,WAAW,CAAC,KAAK;AAAA,IAC5B,SAAS,WAAW,WAAW,SAAS,CAAC,KAAK;AAAA,IAC9C,cAAc,SAAS;AAAA,EACzB;AACF;AAEA,eAAsB,eAAe,SAAqC;AACxE,QAAM,MAAM,WAAW;AACvB,QAAM,QAAkB,CAAC;AAEzB,iBAAe,KAAK,YAAmC;AACrD,QAAI;AACJ,QAAI;AACF,gBAAU,MAAM,QAAQ,YAAY,EAAE,eAAe,KAAK,CAAC;AAAA,IAC7D,QAAQ;AACN;AAAA,IACF;AACA,eAAW,SAAS,SAAS;AAC3B,YAAM,WAAWC,MAAK,YAAY,MAAM,IAAI;AAC5C,UAAI,MAAM,YAAY,GAAG;AACvB,cAAM,KAAK,QAAQ;AAAA,MACrB,WAAW,MAAM,KAAK,SAAS,QAAQ,GAAG;AACxC,cAAM,KAAK,QAAQ;AAAA,MACrB;AAAA,IACF;AAAA,EACF;AAEA,QAAM,KAAK,GAAG;AACd,SAAO;AACT;AAEA,eAAsB,kBAAkB,SAA0C;AAChF,QAAM,QAAQ,MAAM,eAAe,OAAO;AAC1C,QAAM,SAAS,MAAM,EAAE,SAAS,sBAAsB,MAAM;AAC5D,QAAM,iBAAgC,CAAC;AAEvC,aAAW,YAAY,OAAO;AAC5B,QAAI;AACF,YAAM,OAAO,SAAS,QAAQ;AAC9B,YAAM,eAAe,MAAM,KAAK,KAAK;AACrC,UAAI,aAAa,QAAQ,MAAM,GAAG;AAChC,cAAM,UAAU,aAAa,UAAU,OAAO;AAC9C,cAAM,YAAY,SAAS,UAAU,QAAQ;AAC7C,cAAM,WAAW,kBAAkB,SAAS,SAAS;AAErD,YAAI,SAAS,WAAW,EAAG;AAE3B,cAAM,aAAa,SAAS,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,KAAK;AACzD,uBAAe,KAAK;AAAA,UAClB;AAAA,UACA;AAAA,UACA;AAAA,UACA,WAAW,WAAW,CAAC,KAAK;AAAA,UAC5B,SAAS,WAAW,WAAW,SAAS,CAAC,KAAK;AAAA,UAC9C,cAAc,KAAK;AAAA,QACrB,CAAC;AAAA,MACH;AAAA,IACF,QAAQ;AACN;AAAA,IACF;AAAA,EACF;AAEA,SAAO,eAAe;AAAA,IACpB,CAAC,GAAG,MAAM,EAAE,aAAa,QAAQ,IAAI,EAAE,aAAa,QAAQ;AAAA,EAC9D;AACF;;;AEpLA,SAAgB,UAAU,iBAAiB;AAC3C,SAAS,OAAAC,MAAK,QAAAC,OAAM,UAAU,cAAc;;;ACA5C,SAAS,KAAK,YAAY;;;ACD1B,OAAOC,YAAW;AAClB,OAAO,aAAa;AAapBC,OAAM,OAAO,OAAO;AAEb,IAAM,gBAA+B;AAAA,EAC1C,4BAA4B;AAAA,IAC1B,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,eAAe;AAAA,EACjB;AAAA,EACA,0BAA0B;AAAA,IACxB,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,eAAe;AAAA,EACjB;AAAA,EACA,2BAA2B;AAAA,IACzB,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,eAAe;AAAA,EACjB;AACF;AAGA,IAAM,gBAAwC;AAAA,EAC5C,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,OAAO;AACT;AAEO,SAAS,oBAAoB,OAA6B;AAC/D,QAAM,WAAW,cAAc,KAAK,KAAK;AAEzC,MAAI,cAAc,QAAQ,EAAG,QAAO,cAAc,QAAQ;AAC1D,QAAM,QAAQ,OAAO,KAAK,aAAa,EAAE,KAAK,CAAC,MAAM,SAAS,WAAW,CAAC,KAAK,EAAE,WAAW,QAAQ,CAAC;AACrG,MAAI,MAAO,QAAO,cAAc,KAAK;AAErC,SAAO,cAAc,0BAA0B;AACjD;AAEO,SAAS,qBAAqB,KAAmC;AAEtE,MAAI,IAAI,iBAAiB,QAAQ,IAAI,eAAe,GAAG;AACrD,WAAO;AAAA,MACL,WAAW;AAAA,MACX,YAAY;AAAA,MACZ,eAAe;AAAA,MACf,mBAAmB;AAAA,MACnB,WAAW,IAAI;AAAA,IACjB;AAAA,EACF;AAEA,QAAM,UAAU,oBAAoB,IAAI,KAAK;AAC7C,QAAM,UAAU;AAGhB,QAAM,YAAa,IAAI,MAAM,eAAe,QAAQ,QAAS;AAC7D,QAAM,aAAc,IAAI,MAAM,gBAAgB,QAAQ,SAAU;AAChE,QAAM,gBAAiB,IAAI,MAAM,0BAA0B,QAAQ,YAAa;AAChF,QAAM,oBAAqB,IAAI,MAAM,8BAA8B,QAAQ,gBAAiB;AAE5F,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,WAAW,YAAY,aAAa,gBAAgB;AAAA,EACtD;AACF;AAwCO,SAAS,sBAAsB,UAAyC;AAC7E,MAAI,SAAS,SAAS,GAAG;AACvB,WAAO,EAAE,kBAAkB,GAAG,iBAAiB,GAAG,qBAAqB,GAAG,kBAAkB,EAAE;AAAA,EAChG;AAGA,QAAM,SAAS,SAAS,MAAM,GAAG;AACjC,QAAM,YAAYC,OAAM,OAAO,CAAC,EAAE,SAAS;AAC3C,QAAM,WAAWA,OAAM,OAAO,OAAO,SAAS,CAAC,EAAE,SAAS;AAC1D,QAAM,kBAAkB,SAAS,KAAK,WAAW,UAAU,IAAI;AAE/D,MAAI,mBAAmB,GAAG;AACxB,WAAO,EAAE,kBAAkB,GAAG,iBAAiB,GAAG,qBAAqB,GAAG,kBAAkB,EAAE;AAAA,EAChG;AAEA,MAAI,YAAY;AAChB,MAAI,cAAc;AAClB,aAAW,OAAO,QAAQ;AACxB,iBAAa,qBAAqB,GAAG,EAAE;AACvC,mBAAe,IAAI,MAAM,eAAe,IAAI,MAAM;AAAA,EACpD;AAEA,QAAM,mBAAmB,YAAY;AACrC,QAAM,kBAAkB,cAAc;AACtC,QAAM,gBAAgB,uBAAuB;AAC7C,QAAM,sBAAsB,mBAAmB;AAG/C,QAAM,cAAcA,OAAM,EAAE,SAAS,sBAAsB,MAAM;AACjE,QAAM,UAAUA,OAAM,EAAE,KAAK,aAAa,UAAU,IAAI;AACxD,QAAM,mBAAmB,KAAK,IAAI,GAAG,gBAAgB,OAAO;AAE5D,SAAO,EAAE,kBAAkB,iBAAiB,qBAAqB,iBAAiB;AACpF;AAEO,SAAS,eACd,UACA,QACkB;AAClB,QAAM,SAAS,oBAAI,IAAkE;AAErF,aAAW,OAAO,UAAU;AAC1B,UAAM,MAAM,aAAa,IAAI,WAAW,MAAM;AAC9C,UAAM,QAAQ,OAAO,IAAI,GAAG,KAAK,EAAE,UAAU,CAAC,GAAG,UAAU,oBAAI,IAAI,EAAE;AACrE,UAAM,SAAS,KAAK,GAAG;AACvB,UAAM,SAAS,IAAI,IAAI,GAAG,MAAM,GAAG,EAAE,CAAC,KAAK,IAAI,EAAE;AACjD,WAAO,IAAI,KAAK,KAAK;AAAA,EACvB;AAEA,SAAO,MAAM,KAAK,OAAO,QAAQ,CAAC,EAAE,IAAI,CAAC,CAAC,KAAK,KAAK,MAAM;AACxD,QAAI,YAAY;AAChB,QAAI,aAAa;AACjB,QAAI,cAAc;AAElB,eAAW,OAAO,MAAM,UAAU;AAChC,mBAAa,qBAAqB,GAAG,EAAE;AACvC,oBAAc,IAAI,MAAM;AACxB,qBAAe,IAAI,MAAM;AAAA,IAC3B;AAEA,WAAO;AAAA,MACL,QAAQ;AAAA,MACR,aAAa,kBAAkB,KAAK,MAAM;AAAA,MAC1C;AAAA,MACA,kBAAkB;AAAA,MAClB,mBAAmB;AAAA,MACnB,cAAc,MAAM,SAAS;AAAA,MAC7B,cAAc,MAAM,SAAS;AAAA,IAC/B;AAAA,EACF,CAAC;AACH;AAEA,SAAS,aAAa,WAAmB,QAAmC;AAC1E,QAAM,IAAIA,OAAM,SAAS;AACzB,UAAQ,QAAQ;AAAA,IACd,KAAK;AACH,aAAO,EAAE,OAAO,eAAe;AAAA,IACjC,KAAK;AACH,aAAO,EAAE,OAAO,YAAY;AAAA,IAC9B,KAAK;AAEH,YAAM,OAAO,EAAE,KAAK;AACpB,YAAM,cAAc,KAAK,MAAM,OAAO,oBAAoB,IAAI;AAC9D,aAAO,GAAG,EAAE,OAAO,YAAY,CAAC,KAAK,WAAW;AAAA,IAClD,KAAK;AACH,aAAO,GAAG,EAAE,YAAY,CAAC,KAAK,OAAO,EAAE,QAAQ,CAAC,EAAE,SAAS,GAAG,GAAG,CAAC;AAAA,IACpE,KAAK;AACH,aAAO,EAAE,OAAO,SAAS;AAAA,IAC3B,KAAK;AAAA,IACL;AACE,aAAO;AAAA,EACX;AACF;AAEA,SAAS,kBAAkB,KAAa,QAAmC;AACzE,UAAQ,QAAQ;AAAA,IACd,KAAK,QAAQ;AAEX,YAAM,QAAQ,IAAI,MAAM,GAAG;AAC3B,YAAM,UAAU,GAAG,MAAM,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC;AAC/D,aAAOA,OAAM,OAAO,EAAE,OAAO,YAAY;AAAA,IAC3C;AAAA,IACA,KAAK;AACH,aAAOA,OAAM,GAAG,EAAE,OAAO,YAAY;AAAA,IACvC,KAAK;AACH,aAAO,QAAQ,IAAI,MAAM,IAAI,EAAE,CAAC,CAAC;AAAA,IACnC,KAAK;AACH,aAAOA,OAAM,GAAG,EAAE,OAAO,WAAW;AAAA,IACtC;AACE,aAAO;AAAA,EACX;AACF;AAEO,SAAS,WAAW,MAAsB;AAC/C,SAAO,IAAI,KAAK,QAAQ,CAAC,CAAC;AAC5B;AAEO,SAAS,aAAa,QAAwB;AACnD,MAAI,UAAU,IAAW,QAAO,IAAI,SAAS,KAAW,QAAQ,CAAC,CAAC;AAClE,MAAI,UAAU,IAAO,QAAO,IAAI,SAAS,KAAO,QAAQ,CAAC,CAAC;AAC1D,SAAO,OAAO,MAAM;AACtB;;;AD5NQ,cAEA,YAFA;AAXD,SAAS,UAAU,EAAE,OAAO,cAAc,UAAU,kBAAkB,MAAM,GAAmB;AACpG,QAAM,MAAM,eAAe,IAAK,QAAQ,eAAgB,MAAM;AAC9D,QAAM,QAAQ,MAAM,KAAK,UAAU,MAAM,KAAK,WAAW;AAEzD,QAAM,QAAQ,KAAK,MAAM,mBAAmB,EAAE;AAC9C,QAAM,OAAO,KAAK,MAAM,mBAAmB,EAAE;AAC7C,QAAM,UAAU,QAAQ,IAAI,GAAG,KAAK,KAAK,IAAI,MAAM,GAAG,IAAI;AAE1D,SACE,oBAAC,OAAI,eAAc,UAAS,UAAU,GACpC,+BAAC,QACC;AAAA,wBAAC,QAAK,OAAe,iBAAM;AAAA,IAC3B,oBAAC,QAAK,MAAI,MAAE,qBAAW,KAAK,GAAE;AAAA,IAC9B,qBAAC,QAAK,UAAQ,MAAC;AAAA;AAAA,MAAK,WAAW,YAAY;AAAA,MAAE;AAAA,OAAO;AAAA,IACpD,oBAAC,QAAK,iBAAG;AAAA,IACT,qBAAC,QAAK,OAAe;AAAA,iBAAW,QAAQ;AAAA,MAAE;AAAA,OAAI;AAAA,IAC9C,oBAAC,QAAK,iBAAG;AAAA,IACT,qBAAC,QAAK;AAAA;AAAA,MAAE;AAAA,MAAQ;AAAA,OAAU;AAAA,IAC1B,qBAAC,QAAK,UAAQ,MAAC;AAAA;AAAA,MAAG;AAAA,MAAM;AAAA,OAAC;AAAA,KAC3B,GACF;AAEJ;;;AEjCA,SAAS,OAAAC,MAAK,QAAAC,aAAY;AA0BlB,gBAAAC,MAIA,QAAAC,aAJA;AAjBD,SAAS,WAAW,EAAE,MAAM,OAAO,SAAS,GAAoB;AACrE,QAAM,WAAW;AACjB,QAAM,UAAU,QAAQ,IAAK,OAAO,QAAS,MAAM;AACnD,QAAM,aAAa,KAAK,IAAI,GAAG,KAAK,IAAI,SAAS,GAAG,CAAC;AACrD,QAAM,cAAc,KAAK,MAAO,aAAa,MAAO,QAAQ;AAC5D,QAAM,aAAa,WAAW;AAE9B,QAAM,QAAQ,UAAU,KAAK,UAAU,UAAU,KAAK,WAAW;AAEjE,QAAM,SAAS,SAAS,OAAO,WAAW;AAC1C,QAAM,QAAQ,SAAS,OAAO,UAAU;AAExC,QAAM,UAAU,CAAC,MAAc,GAAG,KAAK,MAAM,IAAI,GAAI,CAAC;AAEtD,SACE,gBAAAA,MAACH,MAAA,EAAI,eAAc,UAAS,UAAU,GACpC;AAAA,oBAAAG,MAACF,OAAA,EACC;AAAA,sBAAAC,KAACD,OAAA,EAAK,eAAC;AAAA,MACP,gBAAAC,KAACD,OAAA,EAAK,OAAe,kBAAO;AAAA,MAC5B,gBAAAC,KAACD,OAAA,EAAK,UAAQ,MAAE,iBAAM;AAAA,MACtB,gBAAAC,KAACD,OAAA,EAAK,eAAC;AAAA,MACP,gBAAAE,MAACF,OAAA,EAAK;AAAA;AAAA,QAAE,QAAQ,QAAQ,CAAC;AAAA,QAAE;AAAA,SAAC;AAAA,MAC5B,gBAAAE,MAACF,OAAA,EAAK;AAAA;AAAA,QAAI,QAAQ,IAAI;AAAA,QAAE;AAAA,QAAI,QAAQ,KAAK;AAAA,QAAE;AAAA,SAAO;AAAA,OACpD;AAAA,IACA,gBAAAE,MAACF,OAAA,EAAK,UAAQ,MACX;AAAA;AAAA,MAAK;AAAA,MAAQ,QAAQ,SAAS,YAAY;AAAA,MAAE;AAAA,MAAW,QAAQ,SAAS,YAAY;AAAA,MAAE;AAAA,MACtF,QAAQ,SAAS,QAAQ;AAAA,MAAE;AAAA,MAAe,QAAQ,SAAS,QAAQ;AAAA,MAAE;AAAA,OACxE;AAAA,KACF;AAEJ;;;ACxCA,SAAS,gBAAAG,eAAc,kBAAkB;AACzC,SAAS,gBAAgB;AACzB,SAAS,QAAAC,aAAY;AACrB,SAAS,WAAAC,gBAAe;AAYxB,SAAS,sBAAgC;AACvC,MAAI;AACF,UAAM,UAAU,SAAS,iCAAiC;AAAA,MACxD,UAAU;AAAA,MACV,OAAO,CAAC,QAAQ,QAAQ,QAAQ;AAAA,IAClC,CAAC,EAAE,KAAK;AACR,QAAI,WAAW,YAAY,QAAQ,IAAI,GAAG;AACxC,aAAO;AAAA,QACLC,MAAK,SAAS,WAAW;AAAA,QACzBA,MAAK,SAAS,WAAW,WAAW;AAAA,MACtC;AAAA,IACF;AAAA,EACF,QAAQ;AAAA,EAER;AACA,SAAO,CAAC;AACV;AAKO,SAAS,eAAe,MAAsB;AACnD,SAAO,KAAK,KAAK,KAAK,SAAS,GAAG;AACpC;AAKO,SAAS,gBAAgB,UAA0B;AACxD,MAAI;AACF,UAAM,UAAUC,cAAa,UAAU,OAAO;AAC9C,WAAO,eAAe,OAAO;AAAA,EAC/B,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAaO,SAAS,sBAAsB,SAAoC;AACxE,QAAM,QAAQ,QAAQ,MAAM,IAAI;AAChC,QAAM,WAA8B,CAAC;AACrC,MAAI,eAAe;AACnB,MAAI,iBAA2B,CAAC;AAChC,MAAI,mBAAmB;AAEvB,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,UAAM,OAAO,MAAM,CAAC;AACpB,QAAI,KAAK,WAAW,KAAK,GAAG;AAE1B,UAAI,eAAe,SAAS,KAAK,iBAAiB,YAAY;AAC5D,cAAM,iBAAiB,eAAe,KAAK,IAAI;AAC/C,iBAAS,KAAK;AAAA,UACZ,OAAO;AAAA,UACP,SAAS;AAAA,UACT,QAAQ,eAAe,cAAc;AAAA,UACrC,WAAW;AAAA,UACX,SAAS;AAAA,QACX,CAAC;AAAA,MACH;AACA,qBAAe,KAAK,QAAQ,UAAU,EAAE;AACxC,uBAAiB,CAAC;AAClB,yBAAmB,IAAI;AAAA,IACzB,OAAO;AACL,qBAAe,KAAK,IAAI;AAAA,IAC1B;AAAA,EACF;AAGA,MAAI,eAAe,SAAS,GAAG;AAC7B,UAAM,iBAAiB,eAAe,KAAK,IAAI;AAC/C,aAAS,KAAK;AAAA,MACZ,OAAO;AAAA,MACP,SAAS;AAAA,MACT,QAAQ,eAAe,cAAc;AAAA,MACrC,WAAW;AAAA,MACX,SAAS,MAAM;AAAA,IACjB,CAAC;AAAA,EACH;AAEA,SAAO;AACT;AAKO,SAAS,gBAAgB,UAA2G;AACzI,QAAM,QAAQ,WACV,CAAC,QAAQ,IACT;AAAA,IACED,MAAK,QAAQ,IAAI,GAAG,WAAW;AAAA,IAC/BA,MAAK,QAAQ,IAAI,GAAG,WAAW,WAAW;AAAA,IAC1C,GAAG,oBAAoB;AAAA,IACvBA,MAAKE,SAAQ,GAAG,WAAW,WAAW;AAAA,EACxC;AAEJ,aAAW,KAAK,OAAO;AACrB,QAAI,WAAW,CAAC,GAAG;AACjB,YAAM,UAAUD,cAAa,GAAG,OAAO;AACvC,YAAM,WAAW,sBAAsB,OAAO;AAC9C,YAAM,cAAc,SAAS,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,QAAQ,CAAC;AACjE,YAAM,gBAAgB,SAAS,OAAO,CAAC,MAAM,EAAE,SAAS,GAAG;AAC3D,aAAO,EAAE,aAAa,UAAU,cAAc;AAAA,IAChD;AAAA,EACF;AAEA,SAAO,EAAE,aAAa,GAAG,UAAU,CAAC,GAAG,eAAe,CAAC,EAAE;AAC3D;AAKO,SAAS,oBAAqC;AACnD,QAAM,UAA2B,CAAC;AAClC,QAAM,QAAQ;AAAA,IACZD,MAAK,QAAQ,IAAI,GAAG,WAAW;AAAA,IAC/BA,MAAKE,SAAQ,GAAG,cAAc;AAAA,EAChC;AAEA,aAAW,cAAc,OAAO;AAC9B,QAAI,CAAC,WAAW,UAAU,EAAG;AAC7B,QAAI;AACF,YAAM,MAAM,KAAK,MAAMD,cAAa,YAAY,OAAO,CAAC;AACxD,YAAM,aAAa,IAAI,cAAc,IAAI,eAAe,CAAC;AACzD,iBAAW,CAAC,MAAM,MAAM,KAAK,OAAO,QAAQ,UAAU,GAAG;AACvD,cAAM,MAAM;AAEZ,cAAM,QAAQ,MAAM,QAAQ,IAAI,KAAK,IAAI,IAAI,QAAQ,CAAC;AACtD,cAAM,YAAY,MAAM,UAAU;AAClC,cAAM,kBAAkB,YAAY;AACpC,gBAAQ,KAAK;AAAA,UACX;AAAA,UACA;AAAA,UACA;AAAA,UACA,SAAS,YAAY;AAAA,QACvB,CAAC;AAAA,MACH;AAAA,IACF,QAAQ;AACN;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAKO,SAAS,wBAAwB,cAAwC;AAC9E,QAAM,WAAW,gBAAgB,YAAY;AAC7C,QAAM,aAAa,kBAAkB;AACrC,QAAM,gBAAgB,WAAW,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,iBAAiB,CAAC;AAE9E,QAAM,gBACJ,uBACA,wBACA,gBACA,SAAS,cACT;AAEF,QAAM,kBAAkB,sBAAsB;AAC9C,QAAM,gBAAiB,kBAAkB,sBAAuB;AAEhE,SAAO;AAAA,IACL,cAAc;AAAA,IACd,cAAc;AAAA,IACd,UAAU,SAAS;AAAA,IACnB,UAAU;AAAA,IACV,mBAAmB;AAAA,IACnB;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;;;AJ/IQ,gBAAAE,MAyBA,QAAAC,aAzBA;AAjCD,SAAS,UAAU,EAAE,iBAAiB,SAAS,GAAmB;AACvE,QAAM,EAAE,KAAK,IAAI,OAAO;AACxB,QAAM,CAAC,SAAS,UAAU,IAAI,SAA+B,IAAI;AACjE,QAAM,CAAC,UAAU,WAAW,IAAI,SAA0B,wBAAwB,CAAC;AACnF,QAAM,CAAC,YAAY,aAAa,IAAI,SAAS,KAAK;AAElD,YAAU,MAAM;AACd,aAAS,UAAU;AACjB,UAAI;AACF,cAAM,SAAS,iBAAiB,eAAe;AAC/C,mBAAW,MAAM;AACjB,oBAAY,wBAAwB,CAAC;AAAA,MACvC,QAAQ;AAAA,MAER;AAAA,IACF;AAEA,YAAQ;AACR,UAAM,QAAQ,YAAY,SAAS,QAAQ;AAC3C,WAAO,MAAM,cAAc,KAAK;AAAA,EAClC,GAAG,CAAC,iBAAiB,QAAQ,CAAC;AAE9B;AAAA,IACE,CAAC,UAAU;AACT,UAAI,UAAU,IAAK,MAAK;AACxB,UAAI,UAAU,IAAK,eAAc,CAAC,SAAS,CAAC,IAAI;AAAA,IAClD;AAAA,IACA,EAAE,UAAU,QAAQ,MAAM,SAAS,MAAM;AAAA,EAC3C;AAEA,MAAI,CAAC,WAAW,QAAQ,SAAS,WAAW,GAAG;AAC7C,WACE,gBAAAD,KAACE,MAAA,EAAI,UAAU,GACb,0BAAAF,KAACG,OAAA,EAAK,UAAQ,MAAC,yCAA2B,GAC5C;AAAA,EAEJ;AAEA,QAAM,YAAY,QAAQ,SAAS;AAAA,IACjC,CAAC,KAAK,QAAQ,MAAM,qBAAqB,GAAG,EAAE;AAAA,IAC9C;AAAA,EACF;AACA,QAAM,WAAW,sBAAsB,QAAQ,QAAQ;AACvD,QAAM,QAAQ,QAAQ,SAAS,QAAQ,SAAS,SAAS,CAAC,GAAG,SAAS;AACtE,QAAM,eAAe,SAAS,uBAAuB,YAAY;AAEjE,QAAM,aAAa,QAAQ,mBAAmB,QAAQ;AACtD,QAAM,aAAa,aAAa,QAAQ;AAGxC,QAAM,iBAAiB,QAAQ,SAAS,MAAM,EAAE;AAEhD,SACE,gBAAAF,MAACC,MAAA,EAAI,eAAc,UACjB;AAAA,oBAAAD,MAACC,MAAA,EAAI,aAAY,SAAQ,aAAY,QAAO,UAAU,GACpD;AAAA,sBAAAF,KAACG,OAAA,EAAK,MAAI,MAAC,OAAM,QAAO,4BAExB;AAAA,MACA,gBAAAF,MAACE,OAAA,EAAK;AAAA;AAAA,QAAa,QAAQ,UAAU,MAAM,GAAG,CAAC;AAAA,QAAE;AAAA,SAAG;AAAA,MACpD,gBAAAF,MAACE,OAAA,EAAK;AAAA;AAAA,QAAI,QAAQ;AAAA,QAAa;AAAA,SAAS;AAAA,MACxC,gBAAAH,KAACG,OAAA,EAAK,UAAQ,MAAC,iCAAmB;AAAA,OACpC;AAAA,IAEA,gBAAAH;AAAA,MAAC;AAAA;AAAA,QACC,OAAO;AAAA,QACP;AAAA,QACA,UAAU,SAAS;AAAA,QACnB,kBAAkB,SAAS;AAAA,QAC3B;AAAA;AAAA,IACF;AAAA,IAEA,gBAAAA,KAAC,cAAW,MAAM,YAAY,OAAO,qBAAqB,UAAoB;AAAA,IAE9E,gBAAAC,MAACC,MAAA,EAAI,eAAc,UAAS,UAAU,GAAG,WAAW,GAClD;AAAA,sBAAAF,KAACG,OAAA,EAAK,MAAI,MAAC,8BAAgB;AAAA,MAC1B,eAAe,IAAI,CAAC,KAAK,MAAM;AAC9B,cAAM,OAAO,qBAAqB,GAAG;AACrC,eACE,gBAAAF,MAACE,OAAA,EACC;AAAA,0BAAAH,KAACG,OAAA,EAAK,UAAQ,MAAE,cAAI,UAAU,MAAM,IAAI,EAAE,GAAE;AAAA,UAC5C,gBAAAF,MAACE,OAAA,EAAK;AAAA;AAAA,YAAE,aAAa,IAAI,MAAM,eAAe,IAAI,MAAM,aAAa;AAAA,YAAE;AAAA,aAAI;AAAA,UAC3E,gBAAAF,MAACE,OAAA,EAAK,OAAM,UAAS;AAAA;AAAA,YAAE,WAAW,KAAK,SAAS;AAAA,aAAE;AAAA,aAHzC,CAIX;AAAA,MAEJ,CAAC;AAAA,OACH;AAAA,KACF;AAEJ;;;AHvGO,SAAS,qBAAqBC,UAAwB;AAC3D,EAAAA,SACG,QAAQ,OAAO,EACf,YAAY,oCAAoC,EAChD,OAAO,sBAAsB,0BAA0B,EACvD,OAAO,wBAAwB,uCAAuC,EACtE,OAAO,uBAAuB,0BAA0B,MAAM,EAC9D,OAAO,cAAc,gBAAgB,EACrC,OAAO,OAAO,SAAS;AACtB,UAAM,WAAW,SAAS,KAAK,UAAU,EAAE;AAE3C,QAAI;AAEJ,QAAI,KAAK,SAAS;AAChB,YAAM,WAAW,MAAM,kBAAkB,KAAK,OAAO;AACrD,YAAM,QAAQ,SAAS,KAAK,CAAC,MAAM,EAAE,UAAU,WAAW,KAAK,OAAO,CAAC;AACvE,wBAAkB,OAAO;AAAA,IAC3B,OAAO;AACL,YAAM,WAAW,MAAM,kBAAkB,KAAK,OAAO;AACrD,wBAAkB,SAAS,CAAC,GAAG;AAAA,IACjC;AAEA,QAAI,CAAC,iBAAiB;AACpB,cAAQ;AAAA,QACN;AAAA,MACF;AACA,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,QAAI,CAAC,QAAQ,MAAM,OAAO;AACxB,cAAQ;AAAA,QACN;AAAA,MACF;AACA,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,UAAM,EAAE,cAAc,IAAI;AAAA,MACxBC,OAAM,cAAc,WAAW,EAAE,iBAAiB,SAAS,CAAC;AAAA,IAC9D;AACA,UAAM,cAAc;AAAA,EACtB,CAAC;AACL;;;AQ/CA,OAAOC,YAAW;AAClB,SAAS,UAAAC,eAAc;AAEvB,SAAS,QAAAC,aAAY;AACrB,OAAO,WAAW;;;ACHlB,SAAS,YAAY;AAarB,IAAM,sBAAiE;AAAA,EACrE,QAAQ,EAAE,OAAO,EAAE,KAAK,GAAG,UAAU,GAAG,MAAM,EAAE,GAAG,qBAAqB,IAAK;AAAA,EAC7E,QAAQ,EAAE,OAAO,EAAE,KAAK,GAAG,UAAU,IAAI,MAAM,GAAG,GAAG,qBAAqB,IAAK;AAAA,EAC/E,SAAS,EAAE,OAAO,EAAE,KAAK,IAAI,UAAU,IAAI,MAAM,GAAG,GAAG,qBAAqB,KAAK;AACnF;AAEA,IAAM,kBAAkB,CAAC,QAAQ,UAAU,YAAY,kBAAkB,eAAe,iBAAiB,QAAQ;AACjH,IAAM,mBAAmB,CAAC,YAAY,WAAW,cAAc,aAAa,SAAS,UAAU,WAAW,YAAY,YAAY,cAAc;AAEhJ,SAAS,iBAAiB,iBAAyC;AACjE,QAAM,QAAQ,gBAAgB,YAAY;AAC1C,MAAI,gBAAgB,KAAK,CAAC,MAAM,MAAM,SAAS,CAAC,CAAC,EAAG,QAAO;AAC3D,MAAI,iBAAiB,KAAK,CAAC,MAAM,MAAM,SAAS,CAAC,CAAC,EAAG,QAAO;AAC5D,SAAO;AACT;AAEA,eAAsB,iBACpB,iBACA,UAAoC,CAAC,GACd;AACvB,QAAM,QAAQ,QAAQ,SAAS;AAC/B,QAAM,MAAM,QAAQ,OAAO,QAAQ,IAAI;AACvC,QAAM,UAAU,oBAAoB,KAAK;AACzC,QAAM,UAAU;AAGhB,QAAM,WAAW,wBAAwB;AAGzC,MAAI,aAAa;AACjB,MAAI,WAAW,QAAQ,SAAS,CAAC;AAEjC,MAAI,SAAS,WAAW,GAAG;AAEzB,QAAI;AACF,YAAM,EAAE,UAAAC,UAAS,IAAI,MAAM,OAAO,oBAAoB;AACtD,YAAM,SAASA,UAAS,wEAAwE;AAAA,QAC9F;AAAA,QACA,UAAU;AAAA,MACZ,CAAC;AACD,iBAAW,OACR,MAAM,IAAI,EACV,OAAO,OAAO,EACd,IAAI,CAAC,MAAM,GAAG,GAAG,IAAI,CAAC,EAAE;AAAA,IAC7B,QAAQ;AAAA,IAER;AAAA,EACF;AAEA,aAAW,eAAe,UAAU;AAClC,UAAM,UAAU,MAAM,KAAK,aAAa,EAAE,KAAK,UAAU,KAAK,CAAC;AAC/D,eAAW,KAAK,SAAS;AACvB,oBAAc,gBAAgB,CAAC;AAAA,IACjC;AAAA,EACF;AAGA,QAAM,aAAa,iBAAiB,eAAe;AACnD,QAAM,UAAU,oBAAoB,UAAU;AAG9C,QAAM,iBAAiB,SAAS,gBAAgB;AAChD,QAAM,iBAAiB;AAEvB,WAAS,qBAAqB,OAAuB;AACnD,QAAI,YAAY;AAChB,aAAS,OAAO,GAAG,QAAQ,OAAO,QAAQ;AACxC,YAAM,sBAAsB,OAAO,KAAK,QAAQ;AAChD,YAAM,cAAc,iBAAiB;AAErC,UAAI;AACJ,UAAI,QAAQ,GAAG;AAEb,6BAAsB,cAAc,QAAQ,QAAS;AAAA,MACvD,OAAO;AAEL,cAAM,eAAe,cAAc;AACnC,cAAM,iBAAiB,eAAe,IAAI;AAC1C,6BACG,eAAe,QAAQ,YAAa,UACpC,iBAAiB,QAAQ,QAAS;AAAA,MACvC;AAEA,YAAM,aAAc,QAAQ,sBAAsB,QAAQ,SAAU;AACpE,mBAAa,qBAAqB;AAAA,IACpC;AACA,WAAO;AAAA,EACT;AAEA,QAAM,UAAU,qBAAqB,QAAQ,MAAM,GAAG;AACtD,QAAM,eAAe,qBAAqB,QAAQ,MAAM,QAAQ;AAChE,QAAM,WAAW,qBAAqB,QAAQ,MAAM,IAAI;AAGxD,QAAM,sBAAsB,iBAAiB,QAAQ,MAAM;AAC3D,QAAM,uBAAuB,QAAQ,sBAAsB,QAAQ,MAAM;AACzE,QAAM,uBAAuB,sBAAsB;AAGnD,QAAM,gBAAgB,uBAAuB;AAC7C,QAAM,kBAAmB,gBAAgB,eAAe,KAAM;AAG9D,QAAM,kBAA4B,CAAC;AACnC,MAAI,UAAU,UAAU;AACtB,UAAM,gBAAgB,oBAAoB,QAAQ;AAClD,UAAM,aAAa,qBAAqB,QAAQ,MAAM,QAAQ;AAG9D,UAAM,QAAQ,QAAQ,SAAS,oBAAoB,QAAQ,EAAE;AAC7D,QAAI,QAAQ,GAAG;AACb,sBAAgB;AAAA,QACd,4BAA4B,WAAW,eAAe,eAAe,KAAK,CAAC,KAAK,MAAM,QAAQ,CAAC,CAAC;AAAA,MAClG;AAAA,IACF;AAAA,EACF;AAEA,MAAI,UAAU,UAAU;AACtB,UAAM,cAAc,oBAAoB,MAAM;AAC9C,UAAM,QAAQ,YAAY,SAAS,QAAQ;AAC3C,oBAAgB,KAAK,0BAA0B,WAAW,eAAe,KAAK,CAAC,KAAK,MAAM,QAAQ,CAAC,CAAC,SAAS;AAAA,EAC/G;AAEA,MAAI,SAAS,gBAAgB,IAAI;AAC/B,oBAAgB,KAAK,+BAA+B,MAAM,SAAS,eAAe,QAAQ,CAAC,CAAC,uCAAuC;AAAA,EACrI;AAEA,MAAI,aAAa,KAAO;AACtB,oBAAgB,KAAK,wBAAwB,aAAa,KAAM,QAAQ,CAAC,CAAC,sCAAsC;AAAA,EAClH;AAEA,SAAO;AAAA,IACL;AAAA,IACA,gBAAgB,QAAQ;AAAA,IACxB,iBAAiB;AAAA,MACf,OAAO,KAAK,MAAM,mBAAmB;AAAA,MACrC,QAAQ,KAAK,MAAM,oBAAoB;AAAA,MACvC,QAAQ,KAAK,MAAM,oBAAoB;AAAA,IACzC;AAAA,IACA,eAAe;AAAA,MACb,KAAK,WAAW,OAAO;AAAA,MACvB,UAAU,WAAW,YAAY;AAAA,MACjC,MAAM,WAAW,QAAQ;AAAA,IAC3B;AAAA,IACA,iBAAiB,SAAS;AAAA,IAC1B;AAAA,IACA,iBAAiB,KAAK,MAAM,eAAe;AAAA,IAC3C;AAAA,EACF;AACF;;;AClKA,SAAS,OAAAC,MAAK,QAAAC,aAAY;AAapB,SAGA,OAAAC,MAHA,QAAAC,aAAA;AALC,SAAS,aAAa,EAAE,MAAM,SAAS,GAAsB;AAClE,QAAM,UAAU,CAAC,MAAc,IAAI,IAAI,KAAM,QAAQ,CAAC,CAAC;AAEvD,SACE,gBAAAA,MAACH,MAAA,EAAI,eAAc,UAAS,aAAY,SAAQ,aAAY,QAAO,UAAU,GAAG,UAAU,GACxF;AAAA,oBAAAG,MAACF,OAAA,EAAK,MAAI,MAAC,OAAM,QAAO;AAAA;AAAA,MACD;AAAA,MAAK;AAAA,OAC5B;AAAA,IACA,gBAAAC,KAACD,OAAA,EAAK,eAAC;AAAA,IACP,gBAAAE,MAACF,OAAA,EAAK;AAAA;AAAA,MACG,gBAAAC,KAACD,OAAA,EAAK,MAAI,MAAE,mBAAS,OAAM;AAAA,OACpC;AAAA,IACA,gBAAAE,MAACF,OAAA,EAAK;AAAA;AAAA,MACc,SAAS,eAAe;AAAA,MAAI;AAAA,MAAE,SAAS,eAAe;AAAA,MAAK;AAAA,MAAY;AAAA,MACxF,SAAS,eAAe;AAAA,MAAS;AAAA,OACpC;AAAA,IACA,gBAAAE,MAACF,OAAA,EAAK;AAAA;AAAA,MACI,QAAQ,SAAS,UAAU;AAAA,MAAE;AAAA,OACvC;AAAA,IACA,gBAAAE,MAACF,OAAA,EAAK;AAAA;AAAA,MACe,QAAQ,SAAS,eAAe;AAAA,MAAE;AAAA,OACvD;AAAA,IACA,gBAAAC,KAACD,OAAA,EAAK,eAAC;AAAA,IACP,gBAAAC,KAACD,OAAA,EAAK,MAAI,MAAC,6BAAe;AAAA,IAC1B,gBAAAE,MAACF,OAAA,EACE;AAAA;AAAA,MAAK;AAAA,MAAU,gBAAAC,KAACD,OAAA,EAAK,OAAM,SAAS,mBAAS,cAAc,KAAI;AAAA,OAClE;AAAA,IACA,gBAAAE,MAACF,OAAA,EACE;AAAA;AAAA,MAAK;AAAA,MAAU,gBAAAC,KAACD,OAAA,EAAK,OAAM,UAAU,mBAAS,cAAc,UAAS;AAAA,OACxE;AAAA,IACA,gBAAAE,MAACF,OAAA,EACE;AAAA;AAAA,MAAK;AAAA,MAAU,gBAAAC,KAACD,OAAA,EAAK,OAAM,OAAO,mBAAS,cAAc,MAAK;AAAA,OACjE;AAAA,IACA,gBAAAC,KAACD,OAAA,EAAK,eAAC;AAAA,IACP,gBAAAE,MAACF,OAAA,EAAK;AAAA;AAAA,MAAgB,SAAS;AAAA,MAAgB;AAAA,OAAkB;AAAA,IAChE,SAAS,gBAAgB,IAAI,CAAC,KAAK,MAClC,gBAAAE,MAACF,OAAA,EAAa,OAAM,QACjB;AAAA;AAAA,MAAS;AAAA,SADD,CAEX,CACD;AAAA,KACH;AAEJ;;;AF3CO,SAAS,wBAAwBG,UAAwB;AAC9D,EAAAA,SACG,QAAQ,iBAAiB,EACzB,YAAY,4BAA4B,EACxC,OAAO,uBAAuB,yBAAyB,QAAQ,EAC/D,OAAO,sBAAsB,qCAAqC,EAClE,OAAO,aAAa,yCAAyC,EAC7D,OAAO,UAAU,gBAAgB,EACjC,OAAO,OAAO,MAAc,SAAS;AACpC,UAAM,QAAkB,CAAC;AACzB,QAAI,KAAK,OAAO;AACd,YAAM,UAAU,MAAMC,MAAK,KAAK,OAAO,EAAE,UAAU,KAAK,CAAC;AACzD,YAAM,KAAK,GAAG,OAAO;AAAA,IACvB;AAEA,QAAI,KAAK,SAAS;AAChB,YAAM,SAAS,CAAC,UAAU,QAAQ,OAAO;AAEzC,UAAI,KAAK,MAAM;AACb,mBAAW,SAAS,QAAQ;AAC1B,gBAAMC,YAAW,MAAM,iBAAiB,MAAM,EAAE,OAAO,OAAO,KAAK,QAAQ,IAAI,EAAE,CAAC;AAClF,kBAAQ,IAAI,KAAK,UAAUA,WAAU,MAAM,CAAC,CAAC;AAAA,QAC/C;AACA;AAAA,MACF;AAGA,YAAM,YAAY,MAAM,QAAQ;AAAA,QAC9B,OAAO,IAAI,OAAO,WAAW;AAAA,UAC3B;AAAA,UACA,UAAU,MAAM,iBAAiB,MAAM,EAAE,OAAO,OAAO,KAAK,QAAQ,IAAI,EAAE,CAAC;AAAA,QAC7E,EAAE;AAAA,MACJ;AAEA,cAAQ,IAAI,MAAM,KAAK,KAAK;AAAA,wBAA2B,IAAI;AAAA,CAAK,CAAC;AACjE,cAAQ;AAAA,QACN,KAAK,QAAQ,OAAO,EAAE,CAAC,IAAI,QAAQ,OAAO,EAAE,CAAC,IAAI,MAAM,OAAO,EAAE,CAAC,IAAI,WAAW,OAAO,EAAE,CAAC,IAAI,OAAO,OAAO,EAAE,CAAC;AAAA,MACjH;AACA,cAAQ,IAAI,OAAO,IAAI,OAAO,EAAE,CAAC;AACjC,iBAAW,EAAE,OAAO,UAAAA,UAAS,KAAK,WAAW;AAC3C,cAAM,QAAQ,GAAGA,UAAS,eAAe,GAAG,IAAIA,UAAS,eAAe,IAAI;AAC5E,gBAAQ;AAAA,UACN,KAAK,MAAM,OAAO,EAAE,CAAC,IAAI,MAAM,OAAO,EAAE,CAAC,IAAI,MAAM,MAAMA,UAAS,cAAc,IAAI,OAAO,EAAE,CAAC,CAAC,IAAI,MAAM,OAAOA,UAAS,cAAc,SAAS,OAAO,EAAE,CAAC,CAAC,IAAI,MAAM,IAAIA,UAAS,cAAc,KAAK,OAAO,EAAE,CAAC,CAAC;AAAA,QAClN;AAAA,MACF;AACA,cAAQ,IAAI;AAEZ,YAAM,WAAW,UAAU,CAAC;AAC5B,YAAM,WAAW,UAAU,CAAC;AAC5B,cAAQ;AAAA,QACN,MAAM,IAAI,eAAe,SAAS,KAAK,OAAO,SAAS,SAAS,cAAc,QAAQ,EAAE;AAAA,MAC1F;AACA,cAAQ;AAAA,QACN,MAAM,IAAI,eAAe,SAAS,KAAK,OAAO,SAAS,SAAS,cAAc,QAAQ,EAAE;AAAA,MAC1F;AACA,cAAQ,IAAI;AACZ;AAAA,IACF;AAEA,UAAM,WAAW,MAAM,iBAAiB,MAAM;AAAA,MAC5C,OAAO,KAAK;AAAA,MACZ;AAAA,MACA,KAAK,QAAQ,IAAI;AAAA,IACnB,CAAC;AAED,QAAI,KAAK,MAAM;AACb,cAAQ,IAAI,KAAK,UAAU,UAAU,MAAM,CAAC,CAAC;AAC7C;AAAA,IACF;AAEA,UAAM,EAAE,cAAc,IAAIC;AAAA,MACxBC,OAAM,cAAc,cAAc,EAAE,MAAM,SAAS,CAAC;AAAA,IACtD;AACA,UAAM,cAAc;AAAA,EACtB,CAAC;AACL;;;AGlFA,OAAOC,YAAW;;;ACDlB,OAAOC,YAAW;AAClB,OAAOC,cAAa;AACpB,SAAS,YAAAC,iBAAgB;;;ACFzB,OAAO,cAAc;AACrB,SAAS,WAAW,cAAAC,mBAAkB;AACtC,SAAS,eAAe;AAGjB,SAAS,aAAa,QAAoC;AAC/D,QAAM,OAAO,UAAU;AACvB,QAAM,MAAM,QAAQ,IAAI;AACxB,MAAI,CAACC,YAAW,GAAG,GAAG;AACpB,cAAU,KAAK,EAAE,WAAW,KAAK,CAAC;AAAA,EACpC;AAEA,QAAM,KAAK,IAAI,SAAS,IAAI;AAC5B,KAAG,OAAO,oBAAoB;AAC9B,KAAG,OAAO,mBAAmB;AAE7B,eAAa,EAAE;AACf,SAAO;AACT;AAEA,SAAS,aAAa,IAA6B;AACjD,KAAG,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GA8BP;AACH;;;AC5CA,IAAM,aAA0B;AAAA,EAC9B;AAAA,IACE,SAAS;AAAA,IACT,aAAa;AAAA,IACb,GAAG,KAAK;AAAA,IAER;AAAA,EACF;AACF;AAEO,SAAS,cAAc,IAA6B;AACzD,KAAG,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA,GAKP;AAED,QAAM,UAAU,IAAI;AAAA,IAClB,GACG,QAAQ,uCAAuC,EAC/C,IAAI,EACJ,IAAI,CAAC,QAAa,IAAI,OAAiB;AAAA,EAC5C;AAEA,aAAW,aAAa,YAAY;AAClC,QAAI,CAAC,QAAQ,IAAI,UAAU,OAAO,GAAG;AACnC,gBAAU,GAAG,EAAE;AACf,SAAG,QAAQ,oDAAoD,EAAE,IAAI,UAAU,OAAO;AAAA,IACxF;AAAA,EACF;AACF;;;AF/BAC,OAAM,OAAOC,QAAO;AAIb,IAAM,gBAAN,MAAoB;AAAA,EACjB;AAAA,EAER,YAAY,QAAiB;AAC3B,SAAK,KAAK,aAAa,MAAM;AAC7B,kBAAc,KAAK,EAAE;AAAA,EACvB;AAAA,EAEQ,mBAAmB,aAA6B;AACtD,UAAM,OAAOC,UAAS,WAAW,KAAK;AACtC,UAAM,WAAW,KAAK,GACnB,QAAQ,wCAAwC,EAChD,IAAI,WAAW;AAElB,QAAI,SAAU,QAAO,SAAS;AAE9B,UAAM,SAAS,KAAK,GACjB,QAAQ,iDAAiD,EACzD,IAAI,MAAM,WAAW;AAExB,WAAO,OAAO,OAAO,eAAe;AAAA,EACtC;AAAA,EAEA,UAAU,aAAqB,QAAgB,QAA4B;AACzE,UAAM,YAAY,KAAK,mBAAmB,WAAW;AACrD,SAAK,GACF;AAAA,MACC;AAAA;AAAA;AAAA;AAAA,IAIF,EACC,IAAI,WAAW,QAAQ,MAAM;AAAA,EAClC;AAAA,EAEA,UAAU,aAAsE;AAC9E,UAAM,UAAU,KAAK,GAClB,QAAQ,wCAAwC,EAChD,IAAI,WAAW;AAElB,QAAI,CAAC,QAAS,QAAO;AAErB,UAAM,SAAS,KAAK,GACjB,QAAQ,8FAA8F,EACtG,IAAI,QAAQ,EAAE;AAEjB,QAAI,CAAC,OAAQ,QAAO;AACpB,WAAO,EAAE,QAAQ,OAAO,YAAY,QAAQ,OAAO,OAAO;AAAA,EAC5D;AAAA,EAEA,YACE,aACA,WACA,UACA,WACA,SACA,WACM;AACN,UAAM,YAAY,KAAK,mBAAmB,WAAW;AACrD,SAAK,GACF;AAAA,MACC;AAAA;AAAA,IAEF,EACC,IAAI,WAAW,WAAW,UAAU,WAAW,SAAS,SAAS;AAAA,EACtE;AAAA,EAEA,SAAS,aAAqB,QAA8B;AAC1D,UAAM,UAAU,KAAK,GAClB,QAAQ,wCAAwC,EAChD,IAAI,WAAW;AAElB,QAAI,CAAC,QAAS,QAAO;AAErB,UAAM,QAAQ,eAAe,MAAM;AACnC,UAAM,SAAS,KAAK,GACjB;AAAA,MACC;AAAA;AAAA;AAAA,IAGF,EACC,IAAI,QAAQ,IAAI,MAAM,YAAY,CAAC;AAEtC,WAAO,OAAO;AAAA,EAChB;AAAA,EAEA,YAAY,aAA0C;AACpD,UAAM,eAAe,KAAK,UAAU,WAAW;AAC/C,QAAI,CAAC,aAAc,QAAO;AAE1B,UAAM,QAAQ,KAAK,SAAS,aAAa,aAAa,MAAM;AAC5D,UAAM,YAAY,KAAK,IAAI,GAAG,aAAa,SAAS,KAAK;AACzD,UAAM,cAAc,aAAa,SAAS,IAAK,QAAQ,aAAa,SAAU,MAAM;AAEpF,UAAM,cAAc,eAAe,aAAa,MAAM;AACtD,UAAM,YAAY,aAAa,aAAa,MAAM;AAElD,WAAO;AAAA,MACL,QAAQ,aAAa;AAAA,MACrB;AAAA,MACA;AAAA,MACA;AAAA,MACA,cAAc,QAAQ,aAAa;AAAA,MACnC,QAAQ,aAAa;AAAA,MACrB,aAAa,YAAY,YAAY;AAAA,MACrC,WAAW,UAAU,YAAY;AAAA,IACnC;AAAA,EACF;AAAA,EAEA,eAAmH;AACjH,UAAM,OAAO,KAAK,GACf;AAAA,MACC;AAAA;AAAA;AAAA;AAAA,IAIF,EACC,IAAI;AAEP,WAAO,KAAK,IAAI,CAAC,SAAS;AAAA,MACxB,MAAM,IAAI;AAAA,MACV,MAAM,IAAI;AAAA,MACV,QAAQ,IAAI;AAAA,MACZ,QAAQ,IAAI;AAAA,MACZ,OAAO,IAAI,SAAS,KAAK,SAAS,IAAI,MAAM,IAAI,MAAsB,IAAI;AAAA,IAC5E,EAAE;AAAA,EACJ;AAAA,EAEA,aAAa,aAA8B;AACzC,UAAM,UAAU,KAAK,GAClB,QAAQ,wCAAwC,EAChD,IAAI,WAAW;AAElB,QAAI,CAAC,QAAS,QAAO;AAErB,UAAM,SAAS,KAAK,GAAG,QAAQ,0CAA0C,EAAE,IAAI,QAAQ,EAAE;AACzF,WAAO,OAAO,UAAU;AAAA,EAC1B;AAAA,EAEA,QAAc;AACZ,SAAK,GAAG,MAAM;AAAA,EAChB;AACF;AAEA,SAAS,eAAe,QAAmC;AACzD,UAAQ,QAAQ;AAAA,IACd,KAAK;AACH,aAAOF,OAAM,EAAE,QAAQ,KAAK;AAAA,IAC9B,KAAK;AACH,aAAOA,OAAM,EAAE,QAAQ,SAA6B;AAAA,IACtD,KAAK;AACH,aAAOA,OAAM,EAAE,QAAQ,OAAO;AAAA,EAClC;AACF;AAEA,SAAS,aAAa,QAAmC;AACvD,UAAQ,QAAQ;AAAA,IACd,KAAK;AACH,aAAOA,OAAM,EAAE,MAAM,KAAK;AAAA,IAC5B,KAAK;AACH,aAAOA,OAAM,EAAE,MAAM,SAA6B;AAAA,IACpD,KAAK;AACH,aAAOA,OAAM,EAAE,MAAM,OAAO;AAAA,EAChC;AACF;;;AD3KO,SAAS,sBAAsBG,UAAwB;AAC5D,QAAM,SAASA,SAAQ,QAAQ,QAAQ,EAAE,YAAY,+BAA+B;AAEpF,SACG,QAAQ,cAAc,EACtB,YAAY,gCAAgC,EAC5C,OAAO,yBAAyB,wCAAwC,QAAQ,EAChF,OAAO,oBAAoB,cAAc,EACzC,OAAO,CAAC,QAAgB,SAAS;AAChC,UAAM,UAAU,IAAI,cAAc;AAClC,UAAM,cAAc,KAAK,WAAW,QAAQ,IAAI;AAChD,UAAM,YAAY,WAAW,MAAM;AAEnC,QAAI,MAAM,SAAS,KAAK,aAAa,GAAG;AACtC,cAAQ,IAAIC,OAAM,IAAI,0CAA0C,CAAC;AACjE,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,YAAQ,UAAU,aAAa,WAAW,KAAK,MAAM;AACrD,YAAQ;AAAA,MACNA,OAAM,MAAM,eAAe,WAAW,SAAS,CAAC,IAAI,KAAK,MAAM,QAAQ,WAAW,EAAE;AAAA,IACtF;AACA,YAAQ,MAAM;AAAA,EAChB,CAAC;AAEH,SACG,QAAQ,MAAM,EACd,YAAY,6BAA6B,EACzC,OAAO,oBAAoB,cAAc,EACzC,OAAO,UAAU,gBAAgB,EACjC,OAAO,CAAC,SAAS;AAChB,UAAM,UAAU,IAAI,cAAc;AAClC,UAAM,cAAc,KAAK,WAAW,QAAQ,IAAI;AAChD,UAAM,SAAS,QAAQ,YAAY,WAAW;AAE9C,QAAI,CAAC,QAAQ;AACX,cAAQ,IAAI,gFAAgF;AAC5F,cAAQ,MAAM;AACd;AAAA,IACF;AAEA,QAAI,KAAK,MAAM;AACb,cAAQ,IAAI,KAAK,UAAU,QAAQ,MAAM,CAAC,CAAC;AAC3C,cAAQ,MAAM;AACd;AAAA,IACF;AAEA,UAAM,MAAM,OAAO;AACnB,UAAM,WAAW;AACjB,UAAM,SAAS,KAAK,MAAO,KAAK,IAAI,KAAK,GAAG,IAAI,MAAO,QAAQ;AAC/D,UAAM,QAAQ,WAAW;AACzB,UAAM,QAAQ,MAAM,KAAK,UAAU,MAAM,KAAK,WAAW;AACzD,UAAM,WAAW,UAAU,UAAUA,OAAM,QAAQ,UAAU,WAAWA,OAAM,SAASA,OAAM;AAE7F,YAAQ,IAAIA,OAAM,KAAK,KAAK,uBAAuB,CAAC;AACpD,YAAQ,IAAI,cAAc,OAAO,MAAM,KAAK,OAAO,YAAY,MAAM,GAAG,EAAE,CAAC,OAAO,OAAO,UAAU,MAAM,GAAG,EAAE,CAAC,GAAG;AAClH,YAAQ,IAAI,cAAc,WAAW,OAAO,MAAM,CAAC,EAAE;AACrD,YAAQ,IAAI,cAAc,SAAS,WAAW,OAAO,KAAK,CAAC,CAAC,EAAE;AAC9D,YAAQ,IAAI,KAAK,SAAS,MAAM,SAAS,OAAO,MAAM,IAAI,SAAS,OAAO,KAAK,IAAI,GAAG,CAAC,IAAI,IAAI,QAAQ,CAAC,CAAC,GAAG;AAE5G,QAAI,OAAO,cAAc;AACvB,cAAQ,IAAIA,OAAM,IAAI,KAAK;AAAA,mBAAsB,WAAW,OAAO,QAAQ,OAAO,MAAM,CAAC,EAAE,CAAC;AAAA,IAC9F;AACA,YAAQ,IAAI;AAEZ,YAAQ,MAAM;AAAA,EAChB,CAAC;AAEH,SACG,QAAQ,MAAM,EACd,YAAY,0BAA0B,EACtC,OAAO,MAAM;AACZ,UAAM,UAAU,IAAI,cAAc;AAClC,UAAM,WAAW,QAAQ,aAAa;AAEtC,QAAI,SAAS,WAAW,GAAG;AACzB,cAAQ,IAAI,0EAA0E;AACtF,cAAQ,MAAM;AACd;AAAA,IACF;AAEA,YAAQ,IAAIA,OAAM,KAAK,KAAK,4BAA4B,CAAC;AACzD,eAAW,KAAK,UAAU;AACxB,YAAM,YAAY,EAAE,SAAS,GAAG,WAAW,EAAE,MAAM,CAAC,IAAI,EAAE,MAAM,KAAK;AACrE,YAAM,WAAW,EAAE,QAAQ,IAAI,YAAY,WAAW,EAAE,KAAK,CAAC,MAAM;AACpE,cAAQ,IAAI,KAAKA,OAAM,KAAK,EAAE,IAAI,CAAC,WAAM,SAAS,GAAG,QAAQ,EAAE;AAC/D,cAAQ,IAAIA,OAAM,IAAI,OAAO,EAAE,IAAI,EAAE,CAAC;AAAA,IACxC;AACA,YAAQ,IAAI;AAEZ,YAAQ,MAAM;AAAA,EAChB,CAAC;AAEH,SACG,QAAQ,QAAQ,EAChB,YAAY,mCAAmC,EAC/C,OAAO,oBAAoB,cAAc,EACzC,OAAO,CAAC,SAAS;AAChB,UAAM,UAAU,IAAI,cAAc;AAClC,UAAM,cAAc,KAAK,WAAW,QAAQ,IAAI;AAChD,UAAM,UAAU,QAAQ,aAAa,WAAW;AAEhD,QAAI,SAAS;AACX,cAAQ,IAAIA,OAAM,MAAM,iBAAiB,CAAC;AAAA,IAC5C,OAAO;AACL,cAAQ,IAAI,mCAAmC;AAAA,IACjD;AAEA,YAAQ,MAAM;AAAA,EAChB,CAAC;AACL;;;AIlHA,OAAOC,YAAW;;;ACaX,SAAS,eAAe,eAAqC;AAClE,MAAI,iBAAiB,GAAI,QAAO;AAChC,MAAI,iBAAiB,GAAI,QAAO;AAChC,MAAI,iBAAiB,GAAI,QAAO;AAChC,SAAO;AACT;AAEO,SAAS,mBAAmB,cAAyC;AAC1E,QAAM,WAAW,wBAAwB,YAAY;AACrD,QAAM,aAAa,kBAAkB;AACrC,QAAM,QAAQ,eAAe,SAAS,aAAa;AAEnD,QAAM,YAAY;AAAA,IAChB,EAAE,OAAO,iBAAiB,QAAQ,SAAS,cAAc,SAAU,SAAS,eAAe,sBAAuB,IAAI;AAAA,IACtH,EAAE,OAAO,kBAAkB,QAAQ,SAAS,cAAc,SAAU,SAAS,eAAe,sBAAuB,IAAI;AAAA,IACvH,EAAE,OAAO,cAAc,WAAW,MAAM,SAAS,QAAQ,SAAS,UAAU,SAAU,SAAS,WAAW,sBAAuB,IAAI;AAAA,IACrI,EAAE,OAAO,aAAa,QAAQ,SAAS,UAAU,SAAU,SAAS,WAAW,sBAAuB,IAAI;AAAA,IAC1G,EAAE,OAAO,sBAAsB,QAAQ,SAAS,mBAAmB,SAAU,SAAS,oBAAoB,sBAAuB,IAAI;AAAA,EACvI;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,eAAe,SAAS;AAAA,IACxB;AAAA,EACF;AACF;;;ACzCA,SAAS,gBAAAC,eAAc,cAAAC,mBAAkB;AACzC,SAAS,QAAAC,aAAY;AAIrB,IAAM,wBAAwB;AAC9B,IAAM,mBAAmB;AACzB,IAAM,aAAa;AAInB,SAAS,iBAAiB,UAAkB,OAA8B;AACxE,QAAM,MAAM,QAAQ,IAAI,WAAW,QAAQ;AAC3C,MAAI,OAAO,IAAK,QAAO;AACvB,MAAI,OAAO,IAAK,QAAO;AACvB,SAAO;AACT;AAEO,SAAS,aAAa,UAA4C;AACvE,QAAM,QAAQ,WACV,CAAC,QAAQ,IACT;AAAA,IACEC,MAAK,QAAQ,IAAI,GAAG,WAAW;AAAA,IAC/BA,MAAK,QAAQ,IAAI,GAAG,WAAW,WAAW;AAAA,EAC5C;AAEJ,MAAI,eAA8B;AAClC,aAAW,KAAK,OAAO;AACrB,QAAIC,YAAW,CAAC,GAAG;AACjB,qBAAe;AACf;AAAA,IACF;AAAA,EACF;AAEA,MAAI,CAAC,aAAc,QAAO;AAE1B,QAAM,UAAUC,cAAa,cAAc,OAAO;AAClD,QAAM,QAAQ,QAAQ,MAAM,IAAI;AAChC,QAAM,aAAa,MAAM;AACzB,QAAM,cAAc,sBAAsB,OAAO;AACjD,QAAM,cAAc,YAAY,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,QAAQ,CAAC;AAEpE,MAAI,0BAA0B;AAC9B,QAAM,kBAA4B,CAAC;AAEnC,QAAM,WAA8B,YAAY,IAAI,CAAC,MAAM;AACzD,UAAM,YAAY,EAAE,YAAY,EAAE,WAAW;AAC7C,UAAM,gBAAgB,iBAAiB,UAAU,UAAU;AAC3D,UAAM,mBAAmB,sBAAsB,KAAK,EAAE,OAAO;AAE7D,QAAI,oBAAoB,kBAAkB,cAAc;AACtD;AAAA,IACF;AAEA,QAAI,iBAAiB,KAAK,EAAE,KAAK,KAAK,iBAAiB,KAAK,EAAE,OAAO,GAAG;AACtE,sBAAgB,KAAK,EAAE,KAAK;AAAA,IAC9B;AAEA,WAAO;AAAA,MACL,OAAO,EAAE;AAAA,MACT,SAAS,EAAE;AAAA,MACX,QAAQ,EAAE;AAAA,MACV,WAAW,EAAE;AAAA,MACb,SAAS,EAAE;AAAA,MACX;AAAA,MACA;AAAA,IACF;AAAA,EACF,CAAC;AAGD,QAAM,WAAW,SAAS,OAAO,CAAC,MAAM,EAAE,gBAAgB;AAC1D,QAAM,SAAS,SAAS,OAAO,CAAC,MAAM,CAAC,EAAE,oBAAoB,EAAE,UAAU,GAAG;AAC5E,QAAM,QAAQ,SAAS,OAAO,CAAC,MAAM,CAAC,EAAE,oBAAoB,EAAE,SAAS,GAAG;AAC1E,QAAM,mBAAmB,CAAC,GAAG,UAAU,GAAG,QAAQ,GAAG,KAAK,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK;AAE9E,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,iBAAiB,aAAa;AAAA,IAC9B;AAAA,EACF;AACF;;;ACnFA,SAAS,gBAAAC,eAAc,cAAAC,mBAAkB;AACzC,SAAS,QAAAC,aAAY;AACrB,SAAS,WAAAC,gBAAe;AAcxB,IAAM,mBAA2C;AAAA,EAC/C,YAAY;AAAA,EACZ,WAAW;AAAA,EACX,YAAY;AAAA,EACZ,QAAQ;AAAA,EACR,OAAO;AACT;AAEO,SAAS,aAA0B;AACxC,QAAM,UAA2B,CAAC;AAClC,QAAM,cAAc;AAAA,IAClBC,MAAK,QAAQ,IAAI,GAAG,WAAW;AAAA,IAC/BA,MAAKC,SAAQ,GAAG,cAAc;AAAA,EAChC;AAEA,aAAW,cAAc,aAAa;AACpC,QAAI,CAACC,YAAW,UAAU,EAAG;AAC7B,QAAI;AACF,YAAM,MAAM,KAAK,MAAMC,cAAa,YAAY,OAAO,CAAC;AACxD,YAAM,aAAa,IAAI,cAAc,IAAI,eAAe,CAAC;AACzD,iBAAW,CAAC,MAAM,MAAM,KAAK,OAAO,QAAQ,UAAU,GAAG;AACvD,cAAM,MAAM;AACZ,cAAM,QAAQ,MAAM,QAAQ,IAAI,KAAK,IAAI,IAAI,QAAQ,CAAC;AACtD,cAAM,YAAY,MAAM,UAAU;AAClC,cAAM,kBAAkB,YAAY;AACpC,gBAAQ,KAAK;AAAA,UACX;AAAA,UACA;AAAA,UACA;AAAA,UACA,SAAS,YAAY;AAAA,QACvB,CAAC;AAAA,MACH;AAAA,IACF,QAAQ;AACN;AAAA,IACF;AAAA,EACF;AAEA,QAAM,aAAa,QAAQ,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,WAAW,CAAC;AAClE,QAAM,cAAc,QAAQ,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,iBAAiB,CAAC;AACzE,QAAM,eAAe,QAAQ,OAAO,CAAC,MAAM,EAAE,OAAO;AAGpD,MAAI,gBAAgB;AACpB,QAAM,gBAAgB;AAAA,IACpBH,MAAK,QAAQ,IAAI,GAAG,WAAW,eAAe;AAAA,IAC9CA,MAAKC,SAAQ,GAAG,WAAW,eAAe;AAAA,EAC5C;AACA,aAAW,MAAM,eAAe;AAC9B,QAAI,CAACC,YAAW,EAAE,EAAG;AACrB,QAAI;AACF,YAAM,WAAW,KAAK,MAAMC,cAAa,IAAI,OAAO,CAAC;AACrD,UAAI,SAAS,oBAAoB,SAAS,aAAa;AACrD,wBAAgB;AAChB;AAAA,MACF;AAAA,IACF,QAAQ;AACN;AAAA,IACF;AAAA,EACF;AAEA,QAAM,kBAAkB,gBAAgB,KAAK,MAAM,cAAc,IAAI,IAAI;AAEzE,QAAM,kBAA4B,CAAC;AACnC,aAAW,UAAU,cAAc;AACjC,oBAAgB;AAAA,MACd,IAAI,OAAO,IAAI,SAAS,OAAO,SAAS,WAAW,OAAO,gBAAgB,eAAe,CAAC;AAAA,IAC5F;AAAA,EACF;AAEA,aAAW,UAAU,SAAS;AAC5B,UAAM,MAAM,iBAAiB,OAAO,KAAK,YAAY,CAAC;AACtD,QAAI,KAAK;AACP,sBAAgB,KAAK,GAAG,OAAO,IAAI,KAAK,GAAG,EAAE;AAAA,IAC/C;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;;;AChGO,SAAS,aAAa,cAAoC;AAC/D,QAAM,cAAc,mBAAmB,YAAY;AACnD,QAAM,mBAAmB,aAAa,YAAY;AAClD,QAAM,cAAc,WAAW;AAE/B,QAAM,kBAAyC,CAAC;AAGhD,MAAI,kBAAkB;AACpB,QAAI,iBAAiB,0BAA0B,GAAG;AAChD,sBAAgB,KAAK;AAAA,QACnB,UAAU;AAAA,QACV,QAAQ;AAAA,QACR,QAAQ,4BAAuB,iBAAiB,uBAAuB;AAAA,QACvE,UAAU;AAAA,MACZ,CAAC;AAAA,IACH;AAEA,QAAI,iBAAiB,iBAAiB;AACpC,sBAAgB,KAAK;AAAA,QACnB,UAAU;AAAA,QACV,QAAQ,GAAG,iBAAiB,aAAa,GAAG;AAAA,QAC5C,QAAQ,gBAAgB,iBAAiB,UAAU;AAAA,QACnD,UAAU;AAAA,MACZ,CAAC;AAAA,IACH;AAEA,UAAM,gBAAgB,iBAAiB,SAAS,OAAO,CAAC,MAAM,EAAE,SAAS,GAAG;AAC5E,eAAW,WAAW,eAAe;AACnC,sBAAgB,KAAK;AAAA,QACnB,UAAU;AAAA,QACV,QAAQ,IAAI,QAAQ,MAAM;AAAA,QAC1B,QAAQ,SAAS,QAAQ,KAAK,yBAAyB,QAAQ,MAAM;AAAA,QACrE,UAAU;AAAA,MACZ,CAAC;AAAA,IACH;AAAA,EACF;AAGA,aAAW,UAAU,YAAY,cAAc;AAC7C,oBAAgB,KAAK;AAAA,MACnB,UAAU;AAAA,MACV,QAAQ,IAAI,OAAO,gBAAgB,eAAe,CAAC;AAAA,MACnD,QAAQ,eAAe,OAAO,IAAI,SAAS,OAAO,SAAS;AAAA,MAC3D,UAAU;AAAA,IACZ,CAAC;AAAA,EACH;AAEA,MAAI,CAAC,YAAY,iBAAiB,YAAY,aAAa,IAAI;AAC7D,oBAAgB,KAAK;AAAA,MACnB,UAAU;AAAA,MACV,QAAQ,IAAI,KAAK,MAAM,YAAY,cAAc,IAAI,EAAE,eAAe,CAAC;AAAA,MACvE,QAAQ;AAAA,MACR,UAAU;AAAA,IACZ,CAAC;AAAA,EACH;AAEA,aAAW,OAAO,YAAY,iBAAiB;AAC7C,QAAI,IAAI,SAAS,KAAK,GAAG;AACvB,sBAAgB,KAAK;AAAA,QACnB,UAAU;AAAA,QACV,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR,UAAU;AAAA,MACZ,CAAC;AAAA,IACH;AAAA,EACF;AAGA,MAAI,YAAY,gBAAgB,IAAI;AAClC,oBAAgB,KAAK;AAAA,MACnB,UAAU;AAAA,MACV,QAAQ,QAAQ,YAAY,cAAc,QAAQ,CAAC,CAAC;AAAA,MACpD,QAAQ,wCAAwC,YAAY,KAAK,sBAAsB,YAAY,SAAS,cAAc,eAAe,CAAC;AAAA,MAC1I,UAAU;AAAA,IACZ,CAAC;AAAA,EACH;AAGA,QAAM,gBAAgB,EAAE,MAAM,GAAG,QAAQ,GAAG,KAAK,EAAE;AACnD,kBAAgB,KAAK,CAAC,GAAG,MAAM,cAAc,EAAE,QAAQ,IAAI,cAAc,EAAE,QAAQ,CAAC;AAEpF,SAAO;AAAA,IACL,OAAO,YAAY;AAAA,IACnB,iBAAiB,YAAY;AAAA,IAC7B;AAAA,IACA,YAAY,YAAY;AAAA,IACxB;AAAA,EACF;AACF;;;AJzFO,SAAS,qBAAqBC,UAAwB;AAC3D,EAAAA,SACG,QAAQ,OAAO,EACf,YAAY,+BAA+B,EAC3C,OAAO,SAAS,uBAAuB,EACvC,OAAO,oBAAoB,sBAAsB,EACjD,OAAO,cAAc,wBAAwB,EAC7C,OAAO,UAAU,gBAAgB,EACjC,OAAO,CAAC,SAAS;AAChB,UAAM,SAAS,aAAa;AAE5B,QAAI,KAAK,MAAM;AACb,cAAQ,IAAI,KAAK,UAAU,QAAQ,MAAM,CAAC,CAAC;AAC3C;AAAA,IACF;AAEA,UAAM,aACJ,OAAO,UAAU,MAAMC,OAAM,QAC7B,OAAO,UAAU,MAAMA,OAAM,SAC7BA,OAAM;AAER,YAAQ,IAAIA,OAAM,KAAK,KAAK,6BAA6B,CAAC;AAC1D,YAAQ;AAAA,MACN,4BAA4B,WAAW,KAAK,OAAO,KAAK,CAAC,KAAK,OAAO,gBAAgB,cAAc,QAAQ,CAAC,CAAC;AAAA;AAAA,IAC/G;AAGA,QAAI,CAAC,KAAK,cAAc;AACtB,cAAQ,IAAIA,OAAM,KAAK,0BAA0B,CAAC;AAClD,YAAM,KAAK,OAAO;AAClB,YAAM,MAAM,CAAC,OAAe,WAAmB;AAC7C,cAAM,OAAQ,SAAS,sBAAuB,KAAK,QAAQ,CAAC;AAC5D,eAAO,OAAO,MAAM,OAAO,EAAE,CAAC,IAAI,OAAO,eAAe,EAAE,SAAS,CAAC,CAAC,YAAY,GAAG;AAAA,MACtF;AACA,cAAQ,IAAI,IAAI,kBAAkB,GAAG,YAAY,CAAC;AAClD,cAAQ,IAAI,IAAI,mBAAmB,GAAG,YAAY,CAAC;AACnD,cAAQ,IAAI,IAAI,cAAc,OAAO,WAAW,MAAM,UAAU,GAAG,QAAQ,CAAC;AAC5E,cAAQ,IAAI,IAAI,cAAc,GAAG,QAAQ,CAAC;AAC1C,cAAQ,IAAI,IAAI,uBAAuB,GAAG,iBAAiB,CAAC;AAC5D,cAAQ,IAAI,SAAS,IAAI,OAAO,EAAE,CAAC;AACnC,cAAQ,IAAI,IAAI,mBAAmB,GAAG,aAAa,CAAC;AACpD,cAAQ;AAAA,QACN,OAAO,oBAAoB,OAAO,EAAE,CAAC,IAAI,GAAG,gBAAgB,eAAe,EAAE,SAAS,CAAC,CAAC,YAAY,GAAG,cAAc,QAAQ,CAAC,CAAC;AAAA;AAAA,MACjI;AAAA,IACF;AAGA,QAAI,CAAC,KAAK,WAAW,OAAO,kBAAkB;AAC5C,YAAM,MAAM,OAAO;AACnB,cAAQ,IAAIA,OAAM,KAAK,uBAAuB,CAAC;AAC/C,cAAQ;AAAA,QACN,cAAc,IAAI,UAAU,GAAG,IAAI,kBAAkBA,OAAM,OAAO,mBAAmB,IAAI,EAAE;AAAA,MAC7F;AACA,cAAQ,IAAI,eAAe,IAAI,YAAY,eAAe,CAAC,EAAE;AAC7D,cAAQ;AAAA,QACN,oCAAoC,IAAI,0BAA0B,IAAIA,OAAM,IAAI,OAAO,IAAI,uBAAuB,CAAC,IAAI,GAAG;AAAA,MAC5H;AAEA,UAAI,KAAK,cAAc;AAErB,gBAAQ,IAAI;AACZ,gBAAQ,IAAIA,OAAM,KAAK,aAAa,CAAC;AACrC,mBAAW,WAAW,IAAI,UAAU;AAClC,gBAAM,OAAO,QAAQ,kBAAkB,eAAeA,OAAM,IAAI,cAAc,IAAIA,OAAM,MAAM,mBAAmB;AACjH,gBAAM,WAAW,QAAQ,mBAAmBA,OAAM,OAAO,mBAAmB,IAAI;AAChF,kBAAQ;AAAA,YACN,OAAO,QAAQ,MAAM,OAAO,EAAE,CAAC,IAAI,OAAO,QAAQ,MAAM,EAAE,SAAS,CAAC,CAAC,aAAa,QAAQ,SAAS,IAAI,QAAQ,OAAO,GAAG,IAAI,GAAG,QAAQ;AAAA,UAC1I;AAAA,QACF;AAEA,YAAI,IAAI,iBAAiB,SAAS,GAAG;AACnC,kBAAQ,IAAI;AACZ,kBAAQ,IAAIA,OAAM,KAAK,4BAA4B,CAAC;AACpD,cAAI,iBAAiB,QAAQ,CAAC,OAAO,MAAM;AACzC,oBAAQ,IAAI,OAAO,IAAI,CAAC,KAAK,KAAK,EAAE;AAAA,UACtC,CAAC;AAAA,QACH;AAAA,MACF;AACA,cAAQ,IAAI;AAAA,IACd,WAAW,CAAC,KAAK,WAAW,CAAC,OAAO,kBAAkB;AACpD,cAAQ,IAAIA,OAAM,OAAO,0DAA0D,CAAC;AAAA,IACtF;AAGA,QAAI,KAAK,WAAW,OAAO,WAAW,SAAS,GAAG;AAChD,cAAQ,IAAIA,OAAM,KAAK,gBAAgB,CAAC;AACxC,iBAAW,UAAU,OAAO,YAAY;AACtC,cAAM,QAAQ,OAAO,UAAUA,OAAM,IAAI,UAAU,IAAI;AACvD,gBAAQ;AAAA,UACN,OAAO,OAAO,KAAK,OAAO,EAAE,CAAC,IAAI,OAAO,OAAO,SAAS,EAAE,SAAS,CAAC,CAAC,WAAW,OAAO,gBAAgB,eAAe,EAAE,SAAS,CAAC,CAAC,UAAU,KAAK;AAAA,QACpJ;AAAA,MACF;AACA,cAAQ,IAAI;AAAA,IACd,WAAW,KAAK,WAAW,OAAO,WAAW,WAAW,GAAG;AACzD,cAAQ,IAAIA,OAAM,IAAI,gCAAgC,CAAC;AAAA,IACzD;AAGA,QAAI,OAAO,gBAAgB,SAAS,GAAG;AACrC,YAAM,eAAe,KAAK,eACtB,OAAO,gBAAgB,OAAO,CAAC,MAAM,EAAE,aAAa,WAAW,IAC/D,KAAK,UACH,OAAO,gBAAgB,OAAO,CAAC,MAAM,EAAE,aAAa,KAAK,IACzD,OAAO;AAEb,UAAI,aAAa,SAAS,GAAG;AAC3B,gBAAQ,IAAIA,OAAM,KAAK,oBAAoB,CAAC;AAC5C,qBAAa,QAAQ,CAAC,KAAK,MAAM;AAC/B,gBAAM,gBACJ,IAAI,aAAa,SAASA,OAAM,MAChC,IAAI,aAAa,WAAWA,OAAM,SAClCA,OAAM;AACR,kBAAQ;AAAA,YACN,OAAO,IAAI,CAAC,KAAK,cAAc,IAAI,IAAI,SAAS,YAAY,CAAC,GAAG,CAAC,IAAI,IAAI,MAAM;AAAA,UACjF;AACA,kBAAQ,IAAIA,OAAM,IAAI,kBAAkB,IAAI,MAAM,EAAE,CAAC;AAAA,QACvD,CAAC;AAAA,MACH;AAAA,IACF;AACA,YAAQ,IAAI;AAEZ,QAAI,KAAK,KAAK;AACZ,cAAQ,IAAIA,OAAM,OAAO,+DAA+D,CAAC;AAAA,IAC3F;AAAA,EACF,CAAC;AACL;;;AKjIA,OAAOC,YAAW;AAClB,OAAOC,YAAW;AAUX,SAAS,sBAAsBC,UAAwB;AAC5D,EAAAA,SACG,QAAQ,QAAQ,EAChB,YAAY,yBAAyB,EACrC,OAAO,qBAAqB,sCAAsC,OAAO,EACzE,OAAO,wBAAwB,4BAA4B,EAC3D,OAAO,WAAW,0BAA0B,EAC5C,OAAO,cAAc,4BAA4B,EACjD,OAAO,SAAS,eAAe,EAC/B,OAAO,UAAU,gBAAgB,EACjC,OAAO,OAAO,SAAS;AACtB,UAAM,QAAQ,MAAM,eAAe,KAAK,OAAO;AAE/C,QAAI,MAAM,WAAW,GAAG;AACtB,cAAQ,IAAI,kEAAkE;AAC9E;AAAA,IACF;AAGA,UAAM,MAAMC,OAAM;AAClB,QAAI;AACJ,YAAQ,KAAK,QAAQ;AAAA,MACnB,KAAK;AACH,iBAAS,IAAI,QAAQ,KAAK;AAC1B;AAAA,MACF,KAAK;AACH,iBAAS,IAAI,SAAS,GAAG,KAAK;AAC9B;AAAA,MACF,KAAK;AACH,iBAAS,IAAI,SAAS,IAAI,KAAK;AAC/B;AAAA,MACF,KAAK;AACH,iBAASA,OAAM,YAAY;AAC3B;AAAA,MACF;AACE,iBAAS,IAAI,QAAQ,KAAK;AAAA,IAC9B;AAGA,UAAM,cAA+B,CAAC;AACtC,UAAM,mBAAyF,CAAC;AAEhG,eAAW,QAAQ,OAAO;AACxB,UAAI;AACF,cAAM,UAAU,iBAAiB,IAAI;AACrC,cAAM,mBAAmB,QAAQ,SAAS;AAAA,UAAO,CAAC,MAChDA,OAAM,EAAE,SAAS,EAAE,QAAQ,MAAM;AAAA,QACnC;AACA,YAAI,iBAAiB,WAAW,EAAG;AAEnC,oBAAY,KAAK,GAAG,gBAAgB;AAEpC,cAAM,cAAc,iBAAiB;AAAA,UACnC,CAAC,KAAK,MAAM,MAAM,qBAAqB,CAAC,EAAE;AAAA,UAC1C;AAAA,QACF;AACA,yBAAiB,KAAK;AAAA,UACpB,IAAI,QAAQ;AAAA,UACZ,OAAO,iBAAiB,CAAC,GAAG,SAAS;AAAA,UACrC,MAAM;AAAA,UACN,UAAU,iBAAiB;AAAA,QAC7B,CAAC;AAAA,MACH,QAAQ;AACN;AAAA,MACF;AAAA,IACF;AAEA,QAAI,YAAY,WAAW,GAAG;AAC5B,cAAQ,IAAI,6BAA6B,KAAK,MAAM,EAAE;AACtD;AAAA,IACF;AAGA,QAAI,YAAY;AAChB,QAAI,aAAa;AACjB,QAAI,cAAc;AAClB,QAAI,iBAAiB;AAErB,eAAW,OAAO,aAAa;AAC7B,mBAAa,qBAAqB,GAAG,EAAE;AACvC,oBAAc,IAAI,MAAM;AACxB,qBAAe,IAAI,MAAM;AACzB,wBAAkB,IAAI,MAAM;AAAA,IAC9B;AAEA,UAAM,iBAAiB,aAAa;AACpC,UAAM,eAAe,iBAAiB,IAAK,iBAAiB,iBAAkB,MAAM;AAEpF,QAAI,KAAK,MAAM;AACb,cAAQ;AAAA,QACN,KAAK;AAAA,UACH;AAAA,YACE,QAAQ,KAAK;AAAA,YACb;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA,UAAU;AAAA,UACZ;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,MACF;AACA;AAAA,IACF;AAEA,QAAI,KAAK,KAAK;AACZ,cAAQ,IAAI,oCAAoC;AAChD,iBAAW,KAAK,kBAAkB;AAChC,gBAAQ,IAAI,GAAG,EAAE,EAAE,IAAI,EAAE,KAAK,IAAI,EAAE,KAAK,QAAQ,CAAC,CAAC,IAAI,EAAE,QAAQ,EAAE;AAAA,MACrE;AACA;AAAA,IACF;AAGA,UAAM,cAAc,KAAK,WAAW,UAAU,IAAI,OAAO,kBAAkB,IAAI,KAAK;AACpF,YAAQ,IAAIC,OAAM,KAAK,KAAK;AAAA,uBAA0B,WAAW;AAAA,CAAI,CAAC;AACtE,YAAQ,IAAI,uBAAuBA,OAAM,KAAK,WAAW,SAAS,CAAC,CAAC,EAAE;AACtE,YAAQ,IAAI,uBAAuB,aAAa,UAAU,CAAC,SAAS,aAAa,WAAW,CAAC,MAAM;AACnG,YAAQ,IAAI,uBAAuB,aAAa,QAAQ,CAAC,CAAC,GAAG;AAC7D,YAAQ,IAAI,uBAAuB,iBAAiB,MAAM,EAAE;AAC5D,YAAQ,IAAI;AAEZ,QAAI,KAAK,SAAS,KAAK,UAAU;AAE/B,YAAM,UAAU,oBAAI,IAAgD;AACpE,iBAAW,KAAK,kBAAkB;AAChC,cAAM,WAAW,QAAQ,IAAI,EAAE,KAAK,KAAK,EAAE,MAAM,GAAG,UAAU,EAAE;AAChE,iBAAS,QAAQ,EAAE;AACnB,iBAAS;AACT,gBAAQ,IAAI,EAAE,OAAO,QAAQ;AAAA,MAC/B;AAEA,cAAQ,IAAIA,OAAM,KAAK,oBAAoB,CAAC;AAC5C,iBAAW,CAAC,OAAO,IAAI,KAAK,SAAS;AACnC,cAAM,MAAM,YAAY,KAAM,KAAK,OAAO,YAAa,KAAK,QAAQ,CAAC,IAAI;AACzE,cAAM,aAAa,MAAM,QAAQ,WAAW,EAAE,EAAE,QAAQ,aAAa,EAAE;AACvE,gBAAQ;AAAA,UACN,OAAO,UAAU,KAAK,WAAW,KAAK,IAAI,CAAC,KAAK,GAAG,SAAS,KAAK,QAAQ;AAAA,QAC3E;AAAA,MACF;AACA,cAAQ,IAAI;AAAA,IACd;AAEA,QAAI,KAAK,UAAU;AACjB,cAAQ,IAAIA,OAAM,KAAK,sBAAsB,CAAC;AAC9C,iBAAW,KAAK,iBAAiB,KAAK,CAAC,GAAG,MAAM,EAAE,OAAO,EAAE,IAAI,GAAG;AAChE,gBAAQ,IAAI,OAAO,EAAE,GAAG,MAAM,GAAG,EAAE,CAAC,KAAK,WAAW,EAAE,IAAI,CAAC,KAAK,EAAE,QAAQ,WAAW,EAAE,KAAK,GAAG;AAAA,MACjG;AACA,cAAQ,IAAI;AAAA,IACd;AAGA,UAAM,SAAS,eAAe,aAAa,MAAM;AACjD,QAAI,OAAO,SAAS,GAAG;AACrB,cAAQ,IAAIA,OAAM,KAAK,WAAW,CAAC;AACnC,YAAM,UAAU,KAAK,IAAI,GAAG,OAAO,IAAI,CAAC,MAAM,EAAE,SAAS,CAAC;AAC1D,iBAAW,KAAK,OAAO,MAAM,EAAE,GAAG;AAChC,cAAM,SAAS,UAAU,IAAI,KAAK,MAAO,EAAE,YAAY,UAAW,EAAE,IAAI;AACxE,cAAM,MAAM,SAAS,OAAO,MAAM,IAAI,SAAS,OAAO,KAAK,MAAM;AACjE,gBAAQ,IAAI,OAAO,EAAE,YAAY,OAAO,EAAE,CAAC,IAAI,GAAG,IAAI,WAAW,EAAE,SAAS,CAAC,EAAE;AAAA,MACjF;AACA,cAAQ,IAAI;AAAA,IACd;AAAA,EACF,CAAC;AACL;;;AChLA,OAAOC,YAAW;AAClB,SAAS,aAAAC,YAAW,cAAAC,aAAY,gBAAAC,eAAc,eAAe,oBAAoB;AACjF,SAAS,QAAAC,OAAM,WAAAC,gBAAe;AAC9B,SAAS,WAAAC,gBAAe;AAIjB,SAAS,oBAAoBC,UAAwB;AAC1D,EAAAA,SACG,QAAQ,MAAM,EACd,YAAY,yCAAyC,EACrD,OAAO,YAAY,wBAAwB,EAC3C,OAAO,gBAAgB,oBAAoB,EAC3C,OAAO,cAAc,wBAAwB,EAC7C,OAAO,WAAW,2BAA2B,EAC7C,OAAO,OAAO,SAAS;AACtB,YAAQ,IAAIC,OAAM,KAAK,KAAK,4BAA4B,CAAC;AACzD,YAAQ,IAAI,qDAAqD;AAGjE,UAAM,UAAUC,MAAKC,SAAQ,GAAG,OAAO;AACvC,QAAI,CAACC,YAAW,OAAO,GAAG;AACxB,MAAAC,WAAU,SAAS,EAAE,WAAW,KAAK,CAAC;AACtC,cAAQ,IAAIJ,OAAM,MAAM,oBAAoB,CAAC;AAAA,IAC/C;AAEA,QAAI,CAAC,KAAK,WAAW;AAEnB,UAAI;AACF,cAAM,KAAK,aAAa;AACxB,sBAAc,EAAE;AAChB,WAAG,MAAM;AACT,gBAAQ,IAAIA,OAAM,MAAM,2BAA2B,CAAC;AAAA,MACtD,SAAS,KAAK;AACZ,gBAAQ,IAAIA,OAAM,IAAI,gCAAgC,GAAG,EAAE,CAAC;AAAA,MAC9D;AAAA,IACF;AAGA,QAAI;AACF,YAAM,EAAE,UAAAK,UAAS,IAAI,MAAM,OAAO,oBAAoB;AACtD,UAAI;AACF,QAAAA,UAAS,aAAa,EAAE,OAAO,SAAS,CAAC;AACzC,gBAAQ,IAAIL,OAAM,MAAM,qDAAqD,CAAC;AAAA,MAChF,QAAQ;AAAA,MAAsB;AAE9B,UAAI;AACF,QAAAK,UAAS,iBAAiB,EAAE,OAAO,SAAS,CAAC;AAC7C,gBAAQ,IAAIL,OAAM,MAAM,mDAAmD,CAAC;AAAA,MAC9E,QAAQ;AAAA,MAAsB;AAAA,IAChC,QAAQ;AAAA,IAAe;AAGvB,QAAI,KAAK,UAAU,OAAO;AACxB,YAAM,eAAe,KAAK,SACtBC,MAAKC,SAAQ,GAAG,WAAW,eAAe,IAC1CD,MAAK,QAAQ,IAAI,GAAG,WAAW,eAAe;AAElD,cAAQ,IAAI,kCAAkC;AAC9C,cAAQ,IAAI,oDAAoD;AAChE,cAAQ,IAAI,sCAAsC;AAClD,cAAQ,IAAI;AAAA,2BAA8B,KAAK,SAAS,cAAc,SAAS,gBAAgB;AAE/F,UAAI;AACF,qBAAa,YAAY;AACzB,gBAAQ,IAAID,OAAM,MAAM,qBAAqB,CAAC;AAAA,MAChD,SAAS,KAAK;AACZ,gBAAQ,IAAIA,OAAM,OAAO;AAAA,+BAAkC,GAAG,EAAE,CAAC;AAAA,MACnE;AAAA,IACF;AAEA,YAAQ,IAAIA,OAAM,KAAK,0CAA0C,CAAC;AAClE,YAAQ,IAAIA,OAAM,IAAI,4DAA4D,CAAC;AACnF,YAAQ,IAAIA,OAAM,IAAI,KAAK,UAAU;AAAA,MACnC,KAAK;AAAA,QACH,qBAAqB;AAAA,QACrB,iCAAiC;AAAA,MACnC;AAAA,IACF,GAAG,MAAM,CAAC,EAAE,MAAM,IAAI,EAAE,IAAI,OAAK,SAAS,CAAC,EAAE,KAAK,IAAI,CAAC,CAAC;AAExD,YAAQ,IAAIA,OAAM,KAAK,KAAK,yDAAyD,CAAC;AAAA,EACxF,CAAC;AACL;AAEA,SAAS,aAAa,cAA4B;AAChD,QAAM,MAAMM,SAAQ,YAAY;AAChC,MAAI,CAACH,YAAW,GAAG,GAAG;AACpB,IAAAC,WAAU,KAAK,EAAE,WAAW,KAAK,CAAC;AAAA,EACpC;AAEA,MAAI,WAAoC,CAAC;AACzC,MAAID,YAAW,YAAY,GAAG;AAE5B,UAAM,aAAa,eAAe;AAClC,iBAAa,cAAc,UAAU;AACrC,eAAW,KAAK,MAAMI,cAAa,cAAc,OAAO,CAAC;AAAA,EAC3D;AAEA,QAAM,QAAS,SAAS,SAAS,CAAC;AAGlC,MAAI,CAAC,MAAM,cAAc;AACvB,UAAM,eAAe,CAAC;AAAA,EACxB;AAGA,MAAI,CAAC,MAAM,MAAM;AACf,UAAM,OAAO,CAAC;AAAA,EAChB;AAEA,WAAS,QAAQ;AACjB,gBAAc,cAAc,KAAK,UAAU,UAAU,MAAM,CAAC,CAAC;AAC/D;;;AtBxGA,IAAMC,WAAU,cAAc,YAAY,GAAG;AAC7C,IAAM,MAAMA,SAAQ,oBAAoB;AAExC,IAAM,UAAU,IAAI,QAAQ;AAE5B,QACG,KAAK,UAAU,EACf,QAAQ,IAAI,OAAO,EACnB,YAAY,2DAA2D;AAG1E,qBAAqB,OAAO;AAC5B,wBAAwB,OAAO;AAC/B,sBAAsB,OAAO;AAC7B,qBAAqB,OAAO;AAC5B,sBAAsB,OAAO;AAC7B,oBAAoB,OAAO;AAG3B,QAAQ,OAAO,YAAY;AACzB,QAAM,QAAQ,SAAS,KAAK,CAAC,MAAM,EAAE,KAAK,MAAM,OAAO,GAAG,WAAW,CAAC,GAAG,EAAE,MAAM,OAAO,CAAC;AAC3F,CAAC;AAED,QAAQ,MAAM;","names":["React","join","join","Box","Text","dayjs","dayjs","dayjs","Box","Text","jsx","jsxs","readFileSync","join","homedir","join","readFileSync","homedir","jsx","jsxs","Box","Text","program","React","React","render","glob","execSync","Box","Text","jsx","jsxs","program","glob","estimate","render","React","chalk","dayjs","isoWeek","basename","existsSync","existsSync","dayjs","isoWeek","basename","program","chalk","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","execSync","dirname","readFileSync","require"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kerf-cli",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "type": "module",
5
5
  "description": "Cost intelligence for Claude Code. Know before you spend.",
6
6
  "bin": {