claude-scope 0.6.10 → 0.6.14

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.
Files changed (2) hide show
  1. package/dist/claude-scope.cjs +244 -61
  2. package/package.json +12 -2
@@ -102,7 +102,7 @@ var Renderer = class {
102
102
  if (!lineMap.has(line)) {
103
103
  lineMap.set(line, []);
104
104
  }
105
- lineMap.get(line).push(widget);
105
+ lineMap.get(line)?.push(widget);
106
106
  }
107
107
  const lines = [];
108
108
  const sortedLines = Array.from(lineMap.entries()).sort((a, b) => a[0] - b[0]);
@@ -392,8 +392,8 @@ var StdinProvider = class {
392
392
  };
393
393
 
394
394
  // src/providers/transcript-provider.ts
395
- var import_fs = require("fs");
396
- var import_readline = require("readline");
395
+ var import_node_fs = require("node:fs");
396
+ var import_node_readline = require("node:readline");
397
397
  var TranscriptProvider = class {
398
398
  MAX_TOOLS = 20;
399
399
  /**
@@ -402,13 +402,13 @@ var TranscriptProvider = class {
402
402
  * @returns Array of tool entries, limited to last 20
403
403
  */
404
404
  async parseTools(transcriptPath) {
405
- if (!(0, import_fs.existsSync)(transcriptPath)) {
405
+ if (!(0, import_node_fs.existsSync)(transcriptPath)) {
406
406
  return [];
407
407
  }
408
408
  const toolMap = /* @__PURE__ */ new Map();
409
409
  try {
410
- const fileStream = (0, import_fs.createReadStream)(transcriptPath, { encoding: "utf-8" });
411
- const rl = (0, import_readline.createInterface)({
410
+ const fileStream = (0, import_node_fs.createReadStream)(transcriptPath, { encoding: "utf-8" });
411
+ const rl = (0, import_node_readline.createInterface)({
412
412
  input: fileStream,
413
413
  crlfDelay: Infinity
414
414
  });
@@ -487,7 +487,7 @@ var TranscriptProvider = class {
487
487
  */
488
488
  truncateCommand(cmd) {
489
489
  if (cmd.length <= 30) return cmd;
490
- return cmd.slice(0, 30) + "...";
490
+ return `${cmd.slice(0, 30)}...`;
491
491
  }
492
492
  };
493
493
 
@@ -1491,23 +1491,58 @@ function formatTool(name, target, colors) {
1491
1491
  }
1492
1492
  return nameStr;
1493
1493
  }
1494
+ function pluralizeTool(name) {
1495
+ const irregular = {
1496
+ Task: "Tasks",
1497
+ Bash: "Bash",
1498
+ Edit: "Edits",
1499
+ Read: "Reads",
1500
+ Write: "Writes",
1501
+ Grep: "Greps",
1502
+ Glob: "Globs"
1503
+ };
1504
+ return irregular[name] || `${name}s`;
1505
+ }
1494
1506
  var activeToolsStyles = {
1495
1507
  /**
1496
- * balanced: Running tools with ◐ spinner, completed aggregated with ×count
1508
+ * balanced: Group tools by name, showing running and completed counts together
1509
+ * - Running + completed: "ToolName (1 running, 6 done)"
1510
+ * - Only completed: "Tools: 6"
1511
+ * - No symbols, just text format
1497
1512
  */
1498
1513
  balanced: (data, colors) => {
1499
1514
  const parts = [];
1500
- for (const tool of data.running.slice(-2)) {
1501
- const indicator = colors ? colorize("\u25D0", colors.tools.running) : "\u25D0";
1502
- parts.push(
1503
- `${indicator} ${formatTool(tool.name, tool.target, colors ?? getDefaultColors())}`
1504
- );
1515
+ const c = colors ?? getDefaultColors();
1516
+ const allToolNames = /* @__PURE__ */ new Set();
1517
+ for (const tool of data.running) {
1518
+ allToolNames.add(tool.name);
1505
1519
  }
1506
- const sorted = Array.from(data.completed.entries()).sort((a, b) => b[1] - a[1]).slice(0, 4);
1507
- for (const [name, count] of sorted) {
1508
- const check = colors ? colorize("\u2713", colors.tools.completed) : "\u2713";
1509
- const countStr = colors ? colorize(`\xD7${count}`, colors.tools.count) : `\xD7${count}`;
1510
- parts.push(`${check} ${name} ${countStr}`);
1520
+ for (const name of data.completed.keys()) {
1521
+ allToolNames.add(name);
1522
+ }
1523
+ const runningCounts = /* @__PURE__ */ new Map();
1524
+ for (const tool of data.running) {
1525
+ runningCounts.set(tool.name, (runningCounts.get(tool.name) ?? 0) + 1);
1526
+ }
1527
+ for (const name of allToolNames) {
1528
+ const runningCount = runningCounts.get(name) ?? 0;
1529
+ const completedCount = data.completed.get(name) ?? 0;
1530
+ if (runningCount > 0 && completedCount > 0) {
1531
+ const nameStr = colorize(name, c.tools.name);
1532
+ const runningStr = colorize(`${runningCount} running`, c.tools.running);
1533
+ const doneStr = colorize(`${completedCount} done`, c.tools.completed);
1534
+ parts.push(`${nameStr} (${runningStr}, ${doneStr})`);
1535
+ } else if (completedCount > 0) {
1536
+ const pluralName = pluralizeTool(name);
1537
+ const nameStr = colorize(pluralName, c.tools.name);
1538
+ const countStr = colorize(`${completedCount}`, c.tools.count);
1539
+ parts.push(`${nameStr}: ${countStr}`);
1540
+ } else if (runningCount > 0) {
1541
+ const nameStr = colorize(name, c.tools.name);
1542
+ const runningStr = colorize(`${runningCount} running`, c.tools.running);
1543
+ const doneStr = colorize("0 done", c.tools.completed);
1544
+ parts.push(`${nameStr} (${runningStr}, ${doneStr})`);
1545
+ }
1511
1546
  }
1512
1547
  if (parts.length === 0) {
1513
1548
  return "";
@@ -1758,7 +1793,7 @@ var ActiveToolsWidget = class extends StdinDataWidget {
1758
1793
  * @param context - Render context
1759
1794
  * @returns Rendered string or null if no tools
1760
1795
  */
1761
- renderWithData(data, context) {
1796
+ renderWithData(_data, _context) {
1762
1797
  if (!this.renderData || this.tools.length === 0) {
1763
1798
  return null;
1764
1799
  }
@@ -1788,6 +1823,113 @@ function createWidgetMetadata(name, description, version = "1.0.0", author = "cl
1788
1823
  };
1789
1824
  }
1790
1825
 
1826
+ // src/storage/cache-manager.ts
1827
+ var import_node_fs2 = require("node:fs");
1828
+ var import_node_os = require("node:os");
1829
+ var import_node_path = require("node:path");
1830
+ var DEFAULT_CACHE_PATH = `${(0, import_node_os.homedir)()}/.config/claude-scope/cache.json`;
1831
+ var DEFAULT_EXPIRY_MS = 5 * 60 * 1e3;
1832
+ var CacheManager = class {
1833
+ cachePath;
1834
+ expiryMs;
1835
+ constructor(options) {
1836
+ this.cachePath = options?.cachePath ?? DEFAULT_CACHE_PATH;
1837
+ this.expiryMs = options?.expiryMs ?? DEFAULT_EXPIRY_MS;
1838
+ this.ensureCacheDir();
1839
+ }
1840
+ /**
1841
+ * Get cached usage data for a session
1842
+ * @param sessionId - Session identifier
1843
+ * @returns Cached usage if valid and not expired, null otherwise
1844
+ */
1845
+ getCachedUsage(sessionId) {
1846
+ const cache = this.loadCache();
1847
+ const cached = cache.sessions[sessionId];
1848
+ if (!cached) {
1849
+ return null;
1850
+ }
1851
+ const age = Date.now() - cached.timestamp;
1852
+ if (age > this.expiryMs) {
1853
+ delete cache.sessions[sessionId];
1854
+ this.saveCache(cache);
1855
+ return null;
1856
+ }
1857
+ return cached;
1858
+ }
1859
+ /**
1860
+ * Store usage data for a session
1861
+ * @param sessionId - Session identifier
1862
+ * @param usage - Context usage data to cache
1863
+ */
1864
+ setCachedUsage(sessionId, usage) {
1865
+ const cache = this.loadCache();
1866
+ cache.sessions[sessionId] = {
1867
+ timestamp: Date.now(),
1868
+ usage
1869
+ };
1870
+ this.saveCache(cache);
1871
+ }
1872
+ /**
1873
+ * Clear all cached data (useful for testing)
1874
+ */
1875
+ clearCache() {
1876
+ const emptyCache = {
1877
+ sessions: {},
1878
+ version: 1
1879
+ };
1880
+ this.saveCache(emptyCache);
1881
+ }
1882
+ /**
1883
+ * Clean up expired sessions
1884
+ */
1885
+ cleanupExpired() {
1886
+ const cache = this.loadCache();
1887
+ const now = Date.now();
1888
+ for (const [sessionId, cached] of Object.entries(cache.sessions)) {
1889
+ const age = now - cached.timestamp;
1890
+ if (age > this.expiryMs) {
1891
+ delete cache.sessions[sessionId];
1892
+ }
1893
+ }
1894
+ this.saveCache(cache);
1895
+ }
1896
+ /**
1897
+ * Load cache from file
1898
+ */
1899
+ loadCache() {
1900
+ if (!(0, import_node_fs2.existsSync)(this.cachePath)) {
1901
+ return { sessions: {}, version: 1 };
1902
+ }
1903
+ try {
1904
+ const content = (0, import_node_fs2.readFileSync)(this.cachePath, "utf-8");
1905
+ return JSON.parse(content);
1906
+ } catch {
1907
+ return { sessions: {}, version: 1 };
1908
+ }
1909
+ }
1910
+ /**
1911
+ * Save cache to file
1912
+ */
1913
+ saveCache(cache) {
1914
+ try {
1915
+ (0, import_node_fs2.writeFileSync)(this.cachePath, JSON.stringify(cache, null, 2), "utf-8");
1916
+ } catch {
1917
+ }
1918
+ }
1919
+ /**
1920
+ * Ensure cache directory exists
1921
+ */
1922
+ ensureCacheDir() {
1923
+ try {
1924
+ const dir = (0, import_node_path.dirname)(this.cachePath);
1925
+ if (!(0, import_node_fs2.existsSync)(dir)) {
1926
+ (0, import_node_fs2.mkdirSync)(dir, { recursive: true });
1927
+ }
1928
+ } catch {
1929
+ }
1930
+ }
1931
+ };
1932
+
1791
1933
  // src/ui/utils/formatters.ts
1792
1934
  function formatDuration(ms) {
1793
1935
  if (ms <= 0) return "0s";
@@ -1931,9 +2073,11 @@ var CacheMetricsWidget = class extends StdinDataWidget {
1931
2073
  theme;
1932
2074
  style = "balanced";
1933
2075
  renderData;
2076
+ cacheManager;
1934
2077
  constructor(theme) {
1935
2078
  super();
1936
2079
  this.theme = theme ?? DEFAULT_THEME;
2080
+ this.cacheManager = new CacheManager();
1937
2081
  }
1938
2082
  /**
1939
2083
  * Set display style
@@ -1943,10 +2087,16 @@ var CacheMetricsWidget = class extends StdinDataWidget {
1943
2087
  }
1944
2088
  /**
1945
2089
  * Calculate cache metrics from context usage data
1946
- * Returns null if no usage data is available
2090
+ * Returns null if no usage data is available (current or cached)
1947
2091
  */
1948
2092
  calculateMetrics(data) {
1949
- const usage = data.context_window?.current_usage;
2093
+ let usage = data.context_window?.current_usage;
2094
+ if (!usage) {
2095
+ const cached = this.cacheManager.getCachedUsage(data.session_id);
2096
+ if (cached) {
2097
+ usage = cached.usage;
2098
+ }
2099
+ }
1950
2100
  if (!usage) {
1951
2101
  return null;
1952
2102
  }
@@ -1969,9 +2119,19 @@ var CacheMetricsWidget = class extends StdinDataWidget {
1969
2119
  }
1970
2120
  /**
1971
2121
  * Update widget with new data and calculate metrics
2122
+ * Stores valid usage data in cache for future use
1972
2123
  */
1973
2124
  async update(data) {
1974
2125
  await super.update(data);
2126
+ const usage = data.context_window?.current_usage;
2127
+ if (usage) {
2128
+ this.cacheManager.setCachedUsage(data.session_id, {
2129
+ input_tokens: usage.input_tokens,
2130
+ output_tokens: usage.output_tokens,
2131
+ cache_creation_input_tokens: usage.cache_creation_input_tokens,
2132
+ cache_read_input_tokens: usage.cache_read_input_tokens
2133
+ });
2134
+ }
1975
2135
  const metrics = this.calculateMetrics(data);
1976
2136
  this.renderData = metrics ?? void 0;
1977
2137
  }
@@ -2000,9 +2160,9 @@ var CacheMetricsWidget = class extends StdinDataWidget {
2000
2160
  var DEFAULT_WIDGET_STYLE = "balanced";
2001
2161
 
2002
2162
  // src/providers/config-provider.ts
2003
- var fs = __toESM(require("fs/promises"), 1);
2004
- var os = __toESM(require("os"), 1);
2005
- var path = __toESM(require("path"), 1);
2163
+ var fs = __toESM(require("node:fs/promises"), 1);
2164
+ var os = __toESM(require("node:os"), 1);
2165
+ var path = __toESM(require("node:path"), 1);
2006
2166
  var ConfigProvider = class {
2007
2167
  cachedCounts;
2008
2168
  lastScan = 0;
@@ -2253,7 +2413,7 @@ var ConfigCountWidget = class {
2253
2413
  const { claudeMdCount, rulesCount, mcpCount, hooksCount } = this.configs;
2254
2414
  return claudeMdCount > 0 || rulesCount > 0 || mcpCount > 0 || hooksCount > 0;
2255
2415
  }
2256
- async render(context) {
2416
+ async render(_context) {
2257
2417
  if (!this.configs) {
2258
2418
  return null;
2259
2419
  }
@@ -2312,7 +2472,7 @@ var contextStyles = {
2312
2472
  const bar = progressBar(data.percent, 10);
2313
2473
  const output = `\u{1F9E0} [${bar}] ${data.percent}%`;
2314
2474
  if (!colors) return output;
2315
- return `\u{1F9E0} ` + colorize(`[${bar}] ${data.percent}%`, getContextColor(data.percent, colors));
2475
+ return `\u{1F9E0} ${colorize(`[${bar}] ${data.percent}%`, getContextColor(data.percent, colors))}`;
2316
2476
  },
2317
2477
  verbose: (data, colors) => {
2318
2478
  const usedFormatted = data.used.toLocaleString();
@@ -2355,9 +2515,11 @@ var ContextWidget = class extends StdinDataWidget {
2355
2515
  );
2356
2516
  colors;
2357
2517
  styleFn = contextStyles.balanced;
2518
+ cacheManager;
2358
2519
  constructor(colors) {
2359
2520
  super();
2360
2521
  this.colors = colors ?? DEFAULT_THEME;
2522
+ this.cacheManager = new CacheManager();
2361
2523
  }
2362
2524
  setStyle(style = "balanced") {
2363
2525
  const fn = contextStyles[style];
@@ -2365,10 +2527,32 @@ var ContextWidget = class extends StdinDataWidget {
2365
2527
  this.styleFn = fn;
2366
2528
  }
2367
2529
  }
2530
+ /**
2531
+ * Update widget with new data, storing valid values in cache
2532
+ */
2533
+ async update(data) {
2534
+ await super.update(data);
2535
+ const { current_usage } = data.context_window;
2536
+ if (current_usage) {
2537
+ this.cacheManager.setCachedUsage(data.session_id, {
2538
+ input_tokens: current_usage.input_tokens,
2539
+ output_tokens: current_usage.output_tokens,
2540
+ cache_creation_input_tokens: current_usage.cache_creation_input_tokens,
2541
+ cache_read_input_tokens: current_usage.cache_read_input_tokens
2542
+ });
2543
+ }
2544
+ }
2368
2545
  renderWithData(data, _context) {
2369
2546
  const { current_usage, context_window_size } = data.context_window;
2370
- if (!current_usage) return null;
2371
- const used = current_usage.input_tokens + current_usage.cache_creation_input_tokens + current_usage.cache_read_input_tokens + current_usage.output_tokens;
2547
+ let usage = current_usage;
2548
+ if (!usage) {
2549
+ const cached = this.cacheManager.getCachedUsage(data.session_id);
2550
+ if (cached) {
2551
+ usage = cached.usage;
2552
+ }
2553
+ }
2554
+ if (!usage) return null;
2555
+ const used = usage.input_tokens + usage.cache_creation_input_tokens + usage.cache_read_input_tokens + usage.output_tokens;
2372
2556
  const percent = Math.round(used / context_window_size * 100);
2373
2557
  const renderData = {
2374
2558
  used,
@@ -2377,19 +2561,21 @@ var ContextWidget = class extends StdinDataWidget {
2377
2561
  };
2378
2562
  return this.styleFn(renderData, this.colors.context);
2379
2563
  }
2564
+ isEnabled() {
2565
+ return true;
2566
+ }
2380
2567
  };
2381
2568
 
2382
2569
  // src/widgets/cost/styles.ts
2570
+ function balancedStyle(data, colors) {
2571
+ const formatted = formatCostUSD(data.costUsd);
2572
+ if (!colors) return formatted;
2573
+ const amountStr = data.costUsd.toFixed(2);
2574
+ return colorize("$", colors.currency) + colorize(amountStr, colors.amount);
2575
+ }
2383
2576
  var costStyles = {
2384
- balanced: (data, colors) => {
2385
- const formatted = formatCostUSD(data.costUsd);
2386
- if (!colors) return formatted;
2387
- const amountStr = data.costUsd.toFixed(2);
2388
- return colorize("$", colors.currency) + colorize(amountStr, colors.amount);
2389
- },
2390
- compact: (data, colors) => {
2391
- return costStyles.balanced(data, colors);
2392
- },
2577
+ balanced: balancedStyle,
2578
+ compact: balancedStyle,
2393
2579
  playful: (data, colors) => {
2394
2580
  const formatted = formatCostUSD(data.costUsd);
2395
2581
  if (!colors) return `\u{1F4B0} ${formatted}`;
@@ -2481,7 +2667,7 @@ var durationStyles = {
2481
2667
  const colored = colorize(`${hours}`, colors.value) + colorize("h", colors.unit) + colorize(` ${minutes}`, colors.value) + colorize("m", colors.unit);
2482
2668
  return `\u231B ${colored}`;
2483
2669
  }
2484
- return `\u231B ` + colorize(`${minutes}`, colors.value) + colorize("m", colors.unit);
2670
+ return `\u231B ${colorize(`${minutes}`, colors.value)}${colorize("m", colors.unit)}`;
2485
2671
  },
2486
2672
  technical: (data, colors) => {
2487
2673
  const value = `${Math.floor(data.durationMs)}ms`;
@@ -2712,7 +2898,7 @@ var GitTagWidget = class {
2712
2898
  async initialize(context) {
2713
2899
  this.enabled = context.config?.enabled !== false;
2714
2900
  }
2715
- async render(context) {
2901
+ async render(_context) {
2716
2902
  if (!this.enabled || !this.git || !this.cwd) {
2717
2903
  return null;
2718
2904
  }
@@ -2861,7 +3047,7 @@ var GitWidget = class {
2861
3047
  async initialize(context) {
2862
3048
  this.enabled = context.config?.enabled !== false;
2863
3049
  }
2864
- async render(context) {
3050
+ async render(_context) {
2865
3051
  if (!this.enabled || !this.git || !this.cwd) {
2866
3052
  return null;
2867
3053
  }
@@ -3251,7 +3437,7 @@ function getStraightIndices(cards, highCard) {
3251
3437
  cardIndicesByRank.set(value, []);
3252
3438
  uniqueValues.add(value);
3253
3439
  }
3254
- cardIndicesByRank.get(value).push(i);
3440
+ cardIndicesByRank.get(value)?.push(i);
3255
3441
  }
3256
3442
  const sortedValues = Array.from(uniqueValues).sort((a, b) => b - a);
3257
3443
  if (sortedValues.includes(14)) {
@@ -3283,7 +3469,7 @@ function getStraightFlushHighCard(cards, suit) {
3283
3469
  return getStraightHighCard(suitCards);
3284
3470
  }
3285
3471
  function getStraightFlushIndices(cards, highCard, suit) {
3286
- const suitCards = cards.filter((c) => c.suit === suit);
3472
+ const _suitCards = cards.filter((c) => c.suit === suit);
3287
3473
  const suitCardIndices = [];
3288
3474
  const indexMap = /* @__PURE__ */ new Map();
3289
3475
  for (let i = 0; i < cards.length; i++) {
@@ -3561,23 +3747,20 @@ function getHandAbbreviation(handResult) {
3561
3747
  const abbreviation = HAND_ABBREVIATIONS[handResult.name] ?? "\u2014";
3562
3748
  return `${abbreviation} (${handResult.name})`;
3563
3749
  }
3750
+ function balancedStyle2(data, colors) {
3751
+ const { holeCards, boardCards, handResult } = data;
3752
+ const participatingSet = new Set(handResult?.participatingIndices || []);
3753
+ const handStr = holeCards.map((hc, idx) => formatCardByParticipation(hc, participatingSet.has(idx))).join("");
3754
+ const boardStr = boardCards.map((bc, idx) => formatCardByParticipation(bc, participatingSet.has(idx + 2))).join("");
3755
+ const labelColor = colors?.participating ?? lightGray;
3756
+ const handLabel = colorize2("Hand:", labelColor);
3757
+ const boardLabel = colorize2("Board:", labelColor);
3758
+ return `${handLabel} ${handStr}| ${boardLabel} ${boardStr}\u2192 ${formatHandResult(handResult, colors)}`;
3759
+ }
3564
3760
  var pokerStyles = {
3565
- balanced: (data, colors) => {
3566
- const { holeCards, boardCards, handResult } = data;
3567
- const participatingSet = new Set(handResult?.participatingIndices || []);
3568
- const handStr = holeCards.map((hc, idx) => formatCardByParticipation(hc, participatingSet.has(idx))).join("");
3569
- const boardStr = boardCards.map((bc, idx) => formatCardByParticipation(bc, participatingSet.has(idx + 2))).join("");
3570
- const labelColor = colors?.participating ?? lightGray;
3571
- const handLabel = colorize2("Hand:", labelColor);
3572
- const boardLabel = colorize2("Board:", labelColor);
3573
- return `${handLabel} ${handStr}| ${boardLabel} ${boardStr}\u2192 ${formatHandResult(handResult, colors)}`;
3574
- },
3575
- compact: (data, colors) => {
3576
- return pokerStyles.balanced(data, colors);
3577
- },
3578
- playful: (data, colors) => {
3579
- return pokerStyles.balanced(data, colors);
3580
- },
3761
+ balanced: balancedStyle2,
3762
+ compact: balancedStyle2,
3763
+ playful: balancedStyle2,
3581
3764
  "compact-verbose": (data, colors) => {
3582
3765
  const { holeCards, boardCards, handResult } = data;
3583
3766
  const participatingSet = new Set(handResult?.participatingIndices || []);
@@ -3668,7 +3851,7 @@ var PokerWidget = class extends StdinDataWidget {
3668
3851
  * Format card with appropriate color (red for ♥♦, gray for ♠♣)
3669
3852
  */
3670
3853
  formatCardColor(card) {
3671
- const color = isRedSuit(card.suit) ? "red" : "gray";
3854
+ const _color = isRedSuit(card.suit) ? "red" : "gray";
3672
3855
  return formatCard(card);
3673
3856
  }
3674
3857
  renderWithData(_data, _context) {
@@ -3739,7 +3922,7 @@ async function main() {
3739
3922
  await registry.register(new EmptyLineWidget());
3740
3923
  const renderer = new Renderer({
3741
3924
  separator: " \u2502 ",
3742
- onError: (error, widget) => {
3925
+ onError: (_error, _widget) => {
3743
3926
  },
3744
3927
  showErrors: false
3745
3928
  });
@@ -3751,7 +3934,7 @@ async function main() {
3751
3934
  timestamp: Date.now()
3752
3935
  });
3753
3936
  return lines.join("\n");
3754
- } catch (error) {
3937
+ } catch (_error) {
3755
3938
  const fallback = await tryGitFallback();
3756
3939
  return fallback;
3757
3940
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-scope",
3
- "version": "0.6.10",
3
+ "version": "0.6.14",
4
4
  "description": "Claude Code plugin for session status and analytics",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -25,7 +25,10 @@
25
25
  "test:coverage": "c8 --check-coverage --lines=80 --functions=75 --statements=80 --branches=65 --reporter=text --reporter=html --exclude='tests/**' --exclude='**/*.test.ts' tsx --test tests/e2e/stdin-flow.test.ts tests/integration/cli-flow.integration.test.ts tests/integration/five-widgets.integration.test.ts tests/unit/cli.test.ts tests/unit/types.test.ts tests/unit/core/*.test.ts tests/unit/data/*.test.ts tests/unit/utils/*.test.ts tests/unit/widgets/*.test.ts tests/unit/ui/theme/*.test.ts",
26
26
  "test:snapshot": "SNAPSHOT_UPDATE=true tsx --test tests/e2e/stdin-flow.test.ts tests/integration/cli-flow.integration.test.ts tests/integration/five-widgets.integration.test.ts tests/unit/cli.test.ts tests/unit/types.test.ts tests/unit/core/*.test.ts tests/unit/data/*.test.ts tests/unit/utils/*.test.ts tests/unit/widgets/*.test.ts",
27
27
  "test:snapshot:verify": "tsx --test tests/e2e/stdin-flow.test.ts tests/integration/cli-flow.integration.test.ts tests/integration/five-widgets.integration.test.ts tests/unit/cli.test.ts tests/unit/types.test.ts tests/unit/core/*.test.ts tests/unit/data/*.test.ts tests/unit/utils/*.test.ts tests/unit/widgets/*.test.ts",
28
- "dev": "tsx src/index.ts"
28
+ "dev": "tsx src/index.ts",
29
+ "prepare": "husky",
30
+ "lint": "biome check .",
31
+ "lint:fix": "biome check --write ."
29
32
  },
30
33
  "devDependencies": {
31
34
  "@biomejs/biome": "^2.3.11",
@@ -33,11 +36,18 @@
33
36
  "c8": "^10.1.3",
34
37
  "chai": "^6.2.2",
35
38
  "esbuild": "^0.24.2",
39
+ "husky": "^9.1.7",
40
+ "lint-staged": "^16.2.7",
36
41
  "rimraf": "^6.1.2",
37
42
  "tsx": "^4.19.2",
38
43
  "typescript": "^5.7.2"
39
44
  },
40
45
  "engines": {
41
46
  "node": ">=18.0.0"
47
+ },
48
+ "lint-staged": {
49
+ "*.{js,ts,cjs,mjs,d.cts,d.mts,jsx,tsx,json,jsonc}": [
50
+ "biome check --write --no-errors-on-unmatched --files-ignore-unknown=true"
51
+ ]
42
52
  }
43
53
  }