claude-scope 0.8.3 → 0.8.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/claude-scope.cjs +393 -138
  2. package/package.json +7 -7
@@ -1229,7 +1229,7 @@ var init_renderer = __esm({
1229
1229
  if (!widget.isEnabled()) {
1230
1230
  continue;
1231
1231
  }
1232
- const line = widget.metadata.line ?? 0;
1232
+ const line = widget.getLine ? widget.getLine() : widget.metadata.line ?? 0;
1233
1233
  if (!lineMap.has(line)) {
1234
1234
  lineMap.set(line, []);
1235
1235
  }
@@ -1355,107 +1355,126 @@ var init_widget_registry = __esm({
1355
1355
  }
1356
1356
  });
1357
1357
 
1358
- // src/providers/transcript-provider.ts
1359
- var import_node_fs, import_node_readline, TranscriptProvider;
1360
- var init_transcript_provider = __esm({
1361
- "src/providers/transcript-provider.ts"() {
1358
+ // src/providers/mock-config-provider.ts
1359
+ var MockConfigProvider;
1360
+ var init_mock_config_provider = __esm({
1361
+ "src/providers/mock-config-provider.ts"() {
1362
1362
  "use strict";
1363
- import_node_fs = require("node:fs");
1364
- import_node_readline = require("node:readline");
1365
- TranscriptProvider = class {
1366
- MAX_TOOLS = 20;
1363
+ MockConfigProvider = class {
1367
1364
  /**
1368
- * Parse tools from a JSONL transcript file
1369
- * @param transcriptPath Path to the transcript file
1370
- * @returns Array of tool entries, limited to last 20
1365
+ * Return demo config counts
1366
+ * @returns Demo counts for CLAUDE.md, rules, MCPs, hooks
1371
1367
  */
1372
- async parseTools(transcriptPath) {
1373
- if (!(0, import_node_fs.existsSync)(transcriptPath)) {
1374
- return [];
1375
- }
1376
- const toolMap = /* @__PURE__ */ new Map();
1377
- try {
1378
- const fileStream = (0, import_node_fs.createReadStream)(transcriptPath, { encoding: "utf-8" });
1379
- const rl = (0, import_node_readline.createInterface)({
1380
- input: fileStream,
1381
- crlfDelay: Infinity
1382
- });
1383
- for await (const line of rl) {
1384
- if (!line.trim()) continue;
1385
- try {
1386
- const entry = JSON.parse(line);
1387
- this.processLine(entry, toolMap);
1388
- } catch {
1389
- }
1390
- }
1391
- const tools = Array.from(toolMap.values());
1392
- return tools.slice(-this.MAX_TOOLS);
1393
- } catch {
1394
- return [];
1395
- }
1368
+ async getConfigs() {
1369
+ return {
1370
+ claudeMdCount: 1,
1371
+ rulesCount: 3,
1372
+ mcpCount: 2,
1373
+ hooksCount: 4
1374
+ };
1375
+ }
1376
+ };
1377
+ }
1378
+ });
1379
+
1380
+ // src/providers/mock-git.ts
1381
+ var MockGit;
1382
+ var init_mock_git = __esm({
1383
+ "src/providers/mock-git.ts"() {
1384
+ "use strict";
1385
+ MockGit = class {
1386
+ // biome-ignore lint/correctness/noUnusedPrivateClassMembers: kept for API consistency with NativeGit
1387
+ cwd;
1388
+ constructor(cwd) {
1389
+ this.cwd = cwd;
1396
1390
  }
1397
1391
  /**
1398
- * Process a single transcript line and update tool map
1392
+ * Return demo git status
1393
+ * @returns Status with "main" branch
1399
1394
  */
1400
- processLine(line, toolMap) {
1401
- const blocks = line.message?.content ?? [];
1402
- const timestamp = /* @__PURE__ */ new Date();
1403
- for (const block of blocks) {
1404
- if (block.type === "tool_use" && block.id && block.name) {
1405
- const tool = {
1406
- id: block.id,
1407
- name: block.name,
1408
- target: this.extractTarget(block.name, block.input),
1409
- status: "running",
1410
- startTime: timestamp
1411
- };
1412
- toolMap.set(block.id, tool);
1413
- }
1414
- if (block.type === "tool_result" && block.tool_use_id) {
1415
- const existing = toolMap.get(block.tool_use_id);
1416
- if (existing) {
1417
- existing.status = block.is_error ? "error" : "completed";
1418
- existing.endTime = timestamp;
1419
- }
1420
- }
1421
- }
1395
+ async status() {
1396
+ return { current: "main" };
1422
1397
  }
1423
1398
  /**
1424
- * Extract target from tool input based on tool type
1399
+ * Return demo diff summary
1400
+ * @returns Diff with 3 files, 142 insertions, 27 deletions
1425
1401
  */
1426
- extractTarget(toolName, input) {
1427
- if (!input) return void 0;
1428
- switch (toolName) {
1429
- case "Read":
1430
- case "Write":
1431
- case "Edit":
1432
- return this.asString(input.file_path ?? input.path);
1433
- case "Glob":
1434
- return this.asString(input.pattern);
1435
- case "Grep":
1436
- return this.asString(input.pattern);
1437
- case "Bash": {
1438
- const cmd = this.asString(input.command);
1439
- return cmd ? this.truncateCommand(cmd) : void 0;
1440
- }
1441
- default:
1442
- return void 0;
1443
- }
1402
+ async diffSummary(_options) {
1403
+ return {
1404
+ fileCount: 3,
1405
+ files: [
1406
+ { file: "src/widget.ts", insertions: 85, deletions: 12 },
1407
+ { file: "src/config.ts", insertions: 42, deletions: 8 },
1408
+ { file: "tests/widget.test.ts", insertions: 15, deletions: 7 }
1409
+ ]
1410
+ };
1444
1411
  }
1445
1412
  /**
1446
- * Safely convert value to string
1413
+ * Return demo latest tag
1414
+ * @returns Current version tag
1447
1415
  */
1448
- asString(value) {
1449
- if (typeof value === "string") return value;
1450
- if (typeof value === "number") return String(value);
1451
- return void 0;
1416
+ async latestTag() {
1417
+ return "v0.8.3";
1452
1418
  }
1419
+ };
1420
+ }
1421
+ });
1422
+
1423
+ // src/providers/mock-transcript-provider.ts
1424
+ var MockTranscriptProvider;
1425
+ var init_mock_transcript_provider = __esm({
1426
+ "src/providers/mock-transcript-provider.ts"() {
1427
+ "use strict";
1428
+ MockTranscriptProvider = class {
1453
1429
  /**
1454
- * Truncate long commands to 30 chars
1430
+ * Return demo tool entries
1431
+ * @param path - Transcript path (ignored in mock)
1432
+ * @returns Array of demo tool entries
1455
1433
  */
1456
- truncateCommand(cmd) {
1457
- if (cmd.length <= 30) return cmd;
1458
- return `${cmd.slice(0, 30)}...`;
1434
+ async parseTools(_path) {
1435
+ const now = /* @__PURE__ */ new Date();
1436
+ const minuteAgo = new Date(now.getTime() - 60 * 1e3);
1437
+ return [
1438
+ {
1439
+ id: "tool_1",
1440
+ name: "Read",
1441
+ target: "src/config.ts",
1442
+ status: "completed",
1443
+ startTime: minuteAgo,
1444
+ endTime: minuteAgo
1445
+ },
1446
+ {
1447
+ id: "tool_2",
1448
+ name: "Edit",
1449
+ target: "src/config.ts",
1450
+ status: "completed",
1451
+ startTime: minuteAgo,
1452
+ endTime: minuteAgo
1453
+ },
1454
+ {
1455
+ id: "tool_3",
1456
+ name: "Read",
1457
+ target: "src/widget.ts",
1458
+ status: "completed",
1459
+ startTime: minuteAgo,
1460
+ endTime: minuteAgo
1461
+ },
1462
+ {
1463
+ id: "tool_4",
1464
+ name: "Bash",
1465
+ target: "npm test",
1466
+ status: "running",
1467
+ startTime: now
1468
+ },
1469
+ {
1470
+ id: "tool_5",
1471
+ name: "Edit",
1472
+ target: "src/styles.ts",
1473
+ status: "completed",
1474
+ startTime: minuteAgo,
1475
+ endTime: minuteAgo
1476
+ }
1477
+ ];
1459
1478
  }
1460
1479
  };
1461
1480
  }
@@ -1804,6 +1823,7 @@ var init_active_tools_widget = __esm({
1804
1823
  // Display on third line (0-indexed)
1805
1824
  };
1806
1825
  style = "balanced";
1826
+ _lineOverride;
1807
1827
  tools = [];
1808
1828
  renderData;
1809
1829
  /**
@@ -1813,6 +1833,12 @@ var init_active_tools_widget = __esm({
1813
1833
  setStyle(style) {
1814
1834
  this.style = style;
1815
1835
  }
1836
+ setLine(line) {
1837
+ this._lineOverride = line;
1838
+ }
1839
+ getLine() {
1840
+ return this._lineOverride ?? this.metadata.line ?? 0;
1841
+ }
1816
1842
  /**
1817
1843
  * Aggregate completed tools by name and sort by count (descending)
1818
1844
  * @param tools - Array of tool entries
@@ -1917,11 +1943,11 @@ var init_widget_types = __esm({
1917
1943
  });
1918
1944
 
1919
1945
  // src/storage/cache-manager.ts
1920
- var import_node_fs2, import_node_os2, import_node_path2, DEFAULT_CACHE_PATH, DEFAULT_EXPIRY_MS, CacheManager;
1946
+ var import_node_fs, import_node_os2, import_node_path2, DEFAULT_CACHE_PATH, DEFAULT_EXPIRY_MS, CacheManager;
1921
1947
  var init_cache_manager = __esm({
1922
1948
  "src/storage/cache-manager.ts"() {
1923
1949
  "use strict";
1924
- import_node_fs2 = require("node:fs");
1950
+ import_node_fs = require("node:fs");
1925
1951
  import_node_os2 = require("node:os");
1926
1952
  import_node_path2 = require("node:path");
1927
1953
  DEFAULT_CACHE_PATH = `${(0, import_node_os2.homedir)()}/.config/claude-scope/cache.json`;
@@ -1994,11 +2020,11 @@ var init_cache_manager = __esm({
1994
2020
  * Load cache from file
1995
2021
  */
1996
2022
  loadCache() {
1997
- if (!(0, import_node_fs2.existsSync)(this.cachePath)) {
2023
+ if (!(0, import_node_fs.existsSync)(this.cachePath)) {
1998
2024
  return { sessions: {}, version: 1 };
1999
2025
  }
2000
2026
  try {
2001
- const content = (0, import_node_fs2.readFileSync)(this.cachePath, "utf-8");
2027
+ const content = (0, import_node_fs.readFileSync)(this.cachePath, "utf-8");
2002
2028
  return JSON.parse(content);
2003
2029
  } catch {
2004
2030
  return { sessions: {}, version: 1 };
@@ -2009,7 +2035,7 @@ var init_cache_manager = __esm({
2009
2035
  */
2010
2036
  saveCache(cache) {
2011
2037
  try {
2012
- (0, import_node_fs2.writeFileSync)(this.cachePath, JSON.stringify(cache, null, 2), "utf-8");
2038
+ (0, import_node_fs.writeFileSync)(this.cachePath, JSON.stringify(cache, null, 2), "utf-8");
2013
2039
  } catch {
2014
2040
  }
2015
2041
  }
@@ -2019,8 +2045,8 @@ var init_cache_manager = __esm({
2019
2045
  ensureCacheDir() {
2020
2046
  try {
2021
2047
  const dir = (0, import_node_path2.dirname)(this.cachePath);
2022
- if (!(0, import_node_fs2.existsSync)(dir)) {
2023
- (0, import_node_fs2.mkdirSync)(dir, { recursive: true });
2048
+ if (!(0, import_node_fs.existsSync)(dir)) {
2049
+ (0, import_node_fs.mkdirSync)(dir, { recursive: true });
2024
2050
  }
2025
2051
  } catch {
2026
2052
  }
@@ -2093,13 +2119,13 @@ var init_styles2 = __esm({
2093
2119
  init_formatters();
2094
2120
  cacheMetricsStyles = {
2095
2121
  /**
2096
- * balanced: 💾 35.0k cache with color coding
2122
+ * balanced: 35.0k cache with color coding
2097
2123
  */
2098
2124
  balanced: (data, colors2) => {
2099
2125
  const { cacheRead, hitRate } = data;
2100
2126
  const color = colors2 ? getCacheColor(hitRate, colors2) : "";
2101
2127
  const amount = color ? `${color}${formatK(cacheRead)} cache` : `${formatK(cacheRead)} cache`;
2102
- return `\u{1F4BE} ${amount}`;
2128
+ return amount;
2103
2129
  },
2104
2130
  /**
2105
2131
  * compact: Cache: 35.0k
@@ -2183,6 +2209,7 @@ var init_cache_metrics_widget = __esm({
2183
2209
  // Third line
2184
2210
  );
2185
2211
  theme;
2212
+ _lineOverride;
2186
2213
  style = "balanced";
2187
2214
  renderData;
2188
2215
  cacheManager;
@@ -2198,6 +2225,12 @@ var init_cache_metrics_widget = __esm({
2198
2225
  setStyle(style) {
2199
2226
  this.style = style;
2200
2227
  }
2228
+ setLine(line) {
2229
+ this._lineOverride = line;
2230
+ }
2231
+ getLine() {
2232
+ return this._lineOverride ?? this.metadata.line ?? 0;
2233
+ }
2201
2234
  /**
2202
2235
  * Calculate cache metrics from context usage data
2203
2236
  * Returns null if no usage data is available (current or cached)
@@ -2477,75 +2510,104 @@ var configCountStyles;
2477
2510
  var init_styles3 = __esm({
2478
2511
  "src/widgets/config-count/styles.ts"() {
2479
2512
  "use strict";
2513
+ init_colors();
2480
2514
  configCountStyles = {
2481
- balanced: (data) => {
2515
+ balanced: (data, colors2) => {
2482
2516
  const { claudeMdCount, rulesCount, mcpCount, hooksCount } = data;
2483
2517
  const parts = [];
2518
+ const info = colors2?.semantic.info ?? "";
2519
+ const muted = colors2?.base.muted ?? "";
2484
2520
  if (claudeMdCount > 0) {
2485
- parts.push(`CLAUDE.md:${claudeMdCount}`);
2521
+ const label = info ? colorize("CLAUDE.md", info) : "CLAUDE.md";
2522
+ parts.push(`${label}:${claudeMdCount}`);
2486
2523
  }
2487
2524
  if (rulesCount > 0) {
2488
- parts.push(`rules:${rulesCount}`);
2525
+ const label = info ? colorize("rules", info) : "rules";
2526
+ parts.push(`${label}:${rulesCount}`);
2489
2527
  }
2490
2528
  if (mcpCount > 0) {
2491
- parts.push(`MCPs:${mcpCount}`);
2529
+ const label = info ? colorize("MCPs", info) : "MCPs";
2530
+ parts.push(`${label}:${mcpCount}`);
2492
2531
  }
2493
2532
  if (hooksCount > 0) {
2494
- parts.push(`hooks:${hooksCount}`);
2533
+ const label = info ? colorize("hooks", info) : "hooks";
2534
+ parts.push(`${label}:${hooksCount}`);
2495
2535
  }
2496
- return parts.join(" \u2502 ");
2536
+ const sep = muted ? colorize(" \u2502 ", muted) : " \u2502 ";
2537
+ return parts.join(sep);
2497
2538
  },
2498
- compact: (data) => {
2539
+ compact: (data, colors2) => {
2499
2540
  const { claudeMdCount, rulesCount, mcpCount, hooksCount } = data;
2500
2541
  const parts = [];
2542
+ const info = colors2?.semantic.info ?? "";
2543
+ const muted = colors2?.base.muted ?? "";
2501
2544
  if (claudeMdCount > 0) {
2502
- parts.push(`${claudeMdCount} docs`);
2545
+ const text = info ? colorize(`${claudeMdCount} docs`, info) : `${claudeMdCount} docs`;
2546
+ parts.push(text);
2503
2547
  }
2504
2548
  if (rulesCount > 0) {
2505
- parts.push(`${rulesCount} rules`);
2549
+ const text = info ? colorize(`${rulesCount} rules`, info) : `${rulesCount} rules`;
2550
+ parts.push(text);
2506
2551
  }
2507
2552
  if (mcpCount > 0) {
2508
- parts.push(`${mcpCount} MCPs`);
2553
+ const text = info ? colorize(`${mcpCount} MCPs`, info) : `${mcpCount} MCPs`;
2554
+ parts.push(text);
2509
2555
  }
2510
2556
  if (hooksCount > 0) {
2511
2557
  const hookLabel = hooksCount === 1 ? "hook" : "hooks";
2512
- parts.push(`${hooksCount} ${hookLabel}`);
2558
+ const text = info ? colorize(`${hooksCount} ${hookLabel}`, info) : `${hooksCount} ${hookLabel}`;
2559
+ parts.push(text);
2513
2560
  }
2514
- return parts.join(" \u2502 ");
2561
+ const sep = muted ? colorize(" \u2502 ", muted) : " \u2502 ";
2562
+ return parts.join(sep);
2515
2563
  },
2516
- playful: (data) => {
2564
+ playful: (data, colors2) => {
2517
2565
  const { claudeMdCount, rulesCount, mcpCount, hooksCount } = data;
2518
2566
  const parts = [];
2567
+ const info = colors2?.semantic.info ?? "";
2568
+ const muted = colors2?.base.muted ?? "";
2519
2569
  if (claudeMdCount > 0) {
2520
- parts.push(`\u{1F4C4} CLAUDE.md:${claudeMdCount}`);
2570
+ const text = info ? colorize(`CLAUDE.md:${claudeMdCount}`, info) : `CLAUDE.md:${claudeMdCount}`;
2571
+ parts.push(`\u{1F4C4} ${text}`);
2521
2572
  }
2522
2573
  if (rulesCount > 0) {
2523
- parts.push(`\u{1F4DC} rules:${rulesCount}`);
2574
+ const text = info ? colorize(`rules:${rulesCount}`, info) : `rules:${rulesCount}`;
2575
+ parts.push(`\u{1F4DC} ${text}`);
2524
2576
  }
2525
2577
  if (mcpCount > 0) {
2526
- parts.push(`\u{1F50C} MCPs:${mcpCount}`);
2578
+ const text = info ? colorize(`MCPs:${mcpCount}`, info) : `MCPs:${mcpCount}`;
2579
+ parts.push(`\u{1F50C} ${text}`);
2527
2580
  }
2528
2581
  if (hooksCount > 0) {
2529
- parts.push(`\u{1FA9D} hooks:${hooksCount}`);
2582
+ const text = info ? colorize(`hooks:${hooksCount}`, info) : `hooks:${hooksCount}`;
2583
+ parts.push(`\u{1FA9D} ${text}`);
2530
2584
  }
2531
- return parts.join(" \u2502 ");
2585
+ const sep = muted ? colorize(" \u2502 ", muted) : " \u2502 ";
2586
+ return parts.join(sep);
2532
2587
  },
2533
- verbose: (data) => {
2588
+ verbose: (data, colors2) => {
2534
2589
  const { claudeMdCount, rulesCount, mcpCount, hooksCount } = data;
2535
2590
  const parts = [];
2591
+ const info = colors2?.semantic.info ?? "";
2592
+ const muted = colors2?.base.muted ?? "";
2536
2593
  if (claudeMdCount > 0) {
2537
- parts.push(`${claudeMdCount} CLAUDE.md`);
2594
+ const text = info ? colorize(`${claudeMdCount} CLAUDE.md`, info) : `${claudeMdCount} CLAUDE.md`;
2595
+ parts.push(text);
2538
2596
  }
2539
2597
  if (rulesCount > 0) {
2540
- parts.push(`${rulesCount} rules`);
2598
+ const text = info ? colorize(`${rulesCount} rules`, info) : `${rulesCount} rules`;
2599
+ parts.push(text);
2541
2600
  }
2542
2601
  if (mcpCount > 0) {
2543
- parts.push(`${mcpCount} MCP servers`);
2602
+ const text = info ? colorize(`${mcpCount} MCP servers`, info) : `${mcpCount} MCP servers`;
2603
+ parts.push(text);
2544
2604
  }
2545
2605
  if (hooksCount > 0) {
2546
- parts.push(`${hooksCount} hook`);
2606
+ const text = info ? colorize(`${hooksCount} hooks`, info) : `${hooksCount} hooks`;
2607
+ parts.push(text);
2547
2608
  }
2548
- return parts.join(" \u2502 ");
2609
+ const sep = muted ? colorize(" \u2502 ", muted) : " \u2502 ";
2610
+ return parts.join(sep);
2549
2611
  }
2550
2612
  };
2551
2613
  }
@@ -2559,6 +2621,7 @@ var init_config_count_widget = __esm({
2559
2621
  init_style_types();
2560
2622
  init_widget_types();
2561
2623
  init_config_provider();
2624
+ init_theme();
2562
2625
  init_styles3();
2563
2626
  ConfigCountWidget = class {
2564
2627
  id = "config-count";
@@ -2570,16 +2633,28 @@ var init_config_count_widget = __esm({
2570
2633
  1
2571
2634
  // Second line
2572
2635
  );
2573
- configProvider = new ConfigProvider();
2636
+ configProvider;
2574
2637
  configs;
2575
2638
  cwd;
2639
+ themeColors;
2640
+ _lineOverride;
2576
2641
  styleFn = configCountStyles.balanced;
2642
+ constructor(configProvider, themeColors) {
2643
+ this.configProvider = configProvider ?? new ConfigProvider();
2644
+ this.themeColors = themeColors ?? DEFAULT_THEME;
2645
+ }
2577
2646
  setStyle(style = DEFAULT_WIDGET_STYLE) {
2578
2647
  const fn = configCountStyles[style];
2579
2648
  if (fn) {
2580
2649
  this.styleFn = fn;
2581
2650
  }
2582
2651
  }
2652
+ setLine(line) {
2653
+ this._lineOverride = line;
2654
+ }
2655
+ getLine() {
2656
+ return this._lineOverride ?? this.metadata.line ?? 0;
2657
+ }
2583
2658
  async initialize() {
2584
2659
  }
2585
2660
  async update(data) {
@@ -2602,9 +2677,10 @@ var init_config_count_widget = __esm({
2602
2677
  claudeMdCount,
2603
2678
  rulesCount,
2604
2679
  mcpCount,
2605
- hooksCount
2680
+ hooksCount,
2681
+ colors: this.themeColors
2606
2682
  };
2607
- return this.styleFn(renderData);
2683
+ return this.styleFn(renderData, this.themeColors);
2608
2684
  }
2609
2685
  async cleanup() {
2610
2686
  }
@@ -2718,6 +2794,7 @@ var init_context_widget = __esm({
2718
2794
  // First line
2719
2795
  );
2720
2796
  colors;
2797
+ _lineOverride;
2721
2798
  styleFn = contextStyles.balanced;
2722
2799
  cacheManager;
2723
2800
  lastSessionId;
@@ -2732,6 +2809,12 @@ var init_context_widget = __esm({
2732
2809
  this.styleFn = fn;
2733
2810
  }
2734
2811
  }
2812
+ setLine(line) {
2813
+ this._lineOverride = line;
2814
+ }
2815
+ getLine() {
2816
+ return this._lineOverride ?? this.metadata.line ?? 0;
2817
+ }
2735
2818
  /**
2736
2819
  * Update widget with new data, storing valid values in cache
2737
2820
  */
@@ -2840,6 +2923,7 @@ var init_cost_widget = __esm({
2840
2923
  // First line
2841
2924
  );
2842
2925
  colors;
2926
+ _lineOverride;
2843
2927
  styleFn = costStyles.balanced;
2844
2928
  constructor(colors2) {
2845
2929
  super();
@@ -2851,6 +2935,12 @@ var init_cost_widget = __esm({
2851
2935
  this.styleFn = fn;
2852
2936
  }
2853
2937
  }
2938
+ setLine(line) {
2939
+ this._lineOverride = line;
2940
+ }
2941
+ getLine() {
2942
+ return this._lineOverride ?? this.metadata.line ?? 0;
2943
+ }
2854
2944
  renderWithData(data, _context) {
2855
2945
  if (!data.cost || data.cost.total_cost_usd === void 0) return null;
2856
2946
  const renderData = {
@@ -2918,17 +3008,25 @@ var init_styles6 = __esm({
2918
3008
  const totalSeconds = Math.floor(data.durationMs / 1e3);
2919
3009
  const hours = Math.floor(totalSeconds / 3600);
2920
3010
  const minutes = Math.floor(totalSeconds % 3600 / 60);
3011
+ const seconds = totalSeconds % 60;
2921
3012
  if (!colors2) {
2922
3013
  if (hours > 0) {
2923
- return `\u231B ${hours}h ${minutes}m`;
3014
+ return `\u231B ${hours}h ${minutes}m ${seconds}s`;
2924
3015
  }
2925
- return `\u231B ${minutes}m`;
3016
+ if (minutes > 0) {
3017
+ return `\u231B ${minutes}m ${seconds}s`;
3018
+ }
3019
+ return `\u231B ${seconds}s`;
2926
3020
  }
2927
3021
  if (hours > 0) {
2928
- const colored = colorize(`${hours}`, colors2.value) + colorize("h", colors2.unit) + colorize(` ${minutes}`, colors2.value) + colorize("m", colors2.unit);
3022
+ const colored = colorize(`${hours}`, colors2.value) + colorize("h", colors2.unit) + colorize(` ${minutes}`, colors2.value) + colorize("m", colors2.unit) + colorize(` ${seconds}`, colors2.value) + colorize("s", colors2.unit);
3023
+ return `\u231B ${colored}`;
3024
+ }
3025
+ if (minutes > 0) {
3026
+ const colored = colorize(`${minutes}`, colors2.value) + colorize("m", colors2.unit) + colorize(` ${seconds}`, colors2.value) + colorize("s", colors2.unit);
2929
3027
  return `\u231B ${colored}`;
2930
3028
  }
2931
- return `\u231B ${colorize(`${minutes}`, colors2.value)}${colorize("m", colors2.unit)}`;
3029
+ return `\u231B ${colorize(`${seconds}`, colors2.value)}${colorize("s", colors2.unit)}`;
2932
3030
  },
2933
3031
  technical: (data, colors2) => {
2934
3032
  const value = `${Math.floor(data.durationMs)}ms`;
@@ -2971,6 +3069,7 @@ var init_duration_widget = __esm({
2971
3069
  // First line
2972
3070
  );
2973
3071
  colors;
3072
+ _lineOverride;
2974
3073
  styleFn = durationStyles.balanced;
2975
3074
  constructor(colors2) {
2976
3075
  super();
@@ -2982,6 +3081,12 @@ var init_duration_widget = __esm({
2982
3081
  this.styleFn = fn;
2983
3082
  }
2984
3083
  }
3084
+ setLine(line) {
3085
+ this._lineOverride = line;
3086
+ }
3087
+ getLine() {
3088
+ return this._lineOverride ?? this.metadata.line ?? 0;
3089
+ }
2985
3090
  renderWithData(data, _context) {
2986
3091
  if (!data.cost || data.cost.total_duration_ms === void 0) return null;
2987
3092
  const renderData = {
@@ -3124,6 +3229,7 @@ var init_git_tag_widget = __esm({
3124
3229
  enabled = true;
3125
3230
  cwd = null;
3126
3231
  colors;
3232
+ _lineOverride;
3127
3233
  styleFn = gitTagStyles.balanced;
3128
3234
  /**
3129
3235
  * @param gitFactory - Optional factory function for creating IGit instances
@@ -3141,6 +3247,12 @@ var init_git_tag_widget = __esm({
3141
3247
  this.styleFn = fn;
3142
3248
  }
3143
3249
  }
3250
+ setLine(line) {
3251
+ this._lineOverride = line;
3252
+ }
3253
+ getLine() {
3254
+ return this._lineOverride ?? this.metadata.line ?? 0;
3255
+ }
3144
3256
  async initialize(context) {
3145
3257
  this.enabled = context.config?.enabled !== false;
3146
3258
  }
@@ -3291,6 +3403,7 @@ var init_git_widget = __esm({
3291
3403
  enabled = true;
3292
3404
  cwd = null;
3293
3405
  colors;
3406
+ _lineOverride;
3294
3407
  styleFn = gitStyles.balanced;
3295
3408
  /**
3296
3409
  * @param gitFactory - Optional factory function for creating IGit instances
@@ -3308,6 +3421,12 @@ var init_git_widget = __esm({
3308
3421
  this.styleFn = fn;
3309
3422
  }
3310
3423
  }
3424
+ setLine(line) {
3425
+ this._lineOverride = line;
3426
+ }
3427
+ getLine() {
3428
+ return this._lineOverride ?? this.metadata.line ?? 0;
3429
+ }
3311
3430
  async initialize(context) {
3312
3431
  this.enabled = context.config?.enabled !== false;
3313
3432
  }
@@ -3432,6 +3551,7 @@ var init_lines_widget = __esm({
3432
3551
  // First line
3433
3552
  );
3434
3553
  colors;
3554
+ _lineOverride;
3435
3555
  styleFn = linesStyles.balanced;
3436
3556
  constructor(colors2) {
3437
3557
  super();
@@ -3443,6 +3563,12 @@ var init_lines_widget = __esm({
3443
3563
  this.styleFn = fn;
3444
3564
  }
3445
3565
  }
3566
+ setLine(line) {
3567
+ this._lineOverride = line;
3568
+ }
3569
+ getLine() {
3570
+ return this._lineOverride ?? this.metadata.line ?? 0;
3571
+ }
3446
3572
  renderWithData(data, _context) {
3447
3573
  const added = data.cost?.total_lines_added ?? 0;
3448
3574
  const removed = data.cost?.total_lines_removed ?? 0;
@@ -3525,6 +3651,7 @@ var init_model_widget = __esm({
3525
3651
  // First line
3526
3652
  );
3527
3653
  colors;
3654
+ _lineOverride;
3528
3655
  styleFn = modelStyles.balanced;
3529
3656
  constructor(colors2) {
3530
3657
  super();
@@ -3536,6 +3663,12 @@ var init_model_widget = __esm({
3536
3663
  this.styleFn = fn;
3537
3664
  }
3538
3665
  }
3666
+ setLine(line) {
3667
+ this._lineOverride = line;
3668
+ }
3669
+ getLine() {
3670
+ return this._lineOverride ?? this.metadata.line ?? 0;
3671
+ }
3539
3672
  renderWithData(data, _context) {
3540
3673
  const renderData = {
3541
3674
  displayName: data.model.display_name,
@@ -3596,7 +3729,7 @@ var init_demo_data = __esm({
3596
3729
  // src/cli/commands/quick-config/layout-preview.ts
3597
3730
  async function registerWidgetsFromConfig(registry, config, style, themeName) {
3598
3731
  const themeColors = getThemeByName(themeName).colors;
3599
- const transcriptProvider = new TranscriptProvider();
3732
+ const transcriptProvider = new MockTranscriptProvider();
3600
3733
  const widgetFactory = {
3601
3734
  model: (s) => {
3602
3735
  const w = new ModelWidget(themeColors);
@@ -3624,17 +3757,18 @@ async function registerWidgetsFromConfig(registry, config, style, themeName) {
3624
3757
  return w;
3625
3758
  },
3626
3759
  git: (s) => {
3627
- const w = new GitWidget(void 0, themeColors);
3760
+ const w = new GitWidget((cwd) => new MockGit(cwd), themeColors);
3628
3761
  w.setStyle(s);
3629
3762
  return w;
3630
3763
  },
3631
3764
  "git-tag": (s) => {
3632
- const w = new GitTagWidget(void 0, themeColors);
3765
+ const w = new GitTagWidget((cwd) => new MockGit(cwd), themeColors);
3633
3766
  w.setStyle(s);
3634
3767
  return w;
3635
3768
  },
3636
3769
  "config-count": (s) => {
3637
- const w = new ConfigCountWidget();
3770
+ const mockConfig = new MockConfigProvider();
3771
+ const w = new ConfigCountWidget(mockConfig, themeColors);
3638
3772
  w.setStyle(s);
3639
3773
  return w;
3640
3774
  },
@@ -3685,7 +3819,9 @@ var init_layout_preview = __esm({
3685
3819
  "use strict";
3686
3820
  init_renderer();
3687
3821
  init_widget_registry();
3688
- init_transcript_provider();
3822
+ init_mock_config_provider();
3823
+ init_mock_git();
3824
+ init_mock_transcript_provider();
3689
3825
  init_theme();
3690
3826
  init_active_tools();
3691
3827
  init_cache_metrics();
@@ -6992,7 +7128,108 @@ async function runQuickConfigMenu() {
6992
7128
  // src/cli/commands/quick-config/preview.ts
6993
7129
  init_renderer();
6994
7130
  init_widget_registry();
6995
- init_transcript_provider();
7131
+
7132
+ // src/providers/transcript-provider.ts
7133
+ var import_node_fs2 = require("node:fs");
7134
+ var import_node_readline = require("node:readline");
7135
+ var TranscriptProvider = class {
7136
+ MAX_TOOLS = 20;
7137
+ /**
7138
+ * Parse tools from a JSONL transcript file
7139
+ * @param transcriptPath Path to the transcript file
7140
+ * @returns Array of tool entries, limited to last 20
7141
+ */
7142
+ async parseTools(transcriptPath) {
7143
+ if (!(0, import_node_fs2.existsSync)(transcriptPath)) {
7144
+ return [];
7145
+ }
7146
+ const toolMap = /* @__PURE__ */ new Map();
7147
+ try {
7148
+ const fileStream = (0, import_node_fs2.createReadStream)(transcriptPath, { encoding: "utf-8" });
7149
+ const rl = (0, import_node_readline.createInterface)({
7150
+ input: fileStream,
7151
+ crlfDelay: Infinity
7152
+ });
7153
+ for await (const line of rl) {
7154
+ if (!line.trim()) continue;
7155
+ try {
7156
+ const entry = JSON.parse(line);
7157
+ this.processLine(entry, toolMap);
7158
+ } catch {
7159
+ }
7160
+ }
7161
+ const tools = Array.from(toolMap.values());
7162
+ return tools.slice(-this.MAX_TOOLS);
7163
+ } catch {
7164
+ return [];
7165
+ }
7166
+ }
7167
+ /**
7168
+ * Process a single transcript line and update tool map
7169
+ */
7170
+ processLine(line, toolMap) {
7171
+ const blocks = line.message?.content ?? [];
7172
+ const timestamp = /* @__PURE__ */ new Date();
7173
+ for (const block of blocks) {
7174
+ if (block.type === "tool_use" && block.id && block.name) {
7175
+ const tool = {
7176
+ id: block.id,
7177
+ name: block.name,
7178
+ target: this.extractTarget(block.name, block.input),
7179
+ status: "running",
7180
+ startTime: timestamp
7181
+ };
7182
+ toolMap.set(block.id, tool);
7183
+ }
7184
+ if (block.type === "tool_result" && block.tool_use_id) {
7185
+ const existing = toolMap.get(block.tool_use_id);
7186
+ if (existing) {
7187
+ existing.status = block.is_error ? "error" : "completed";
7188
+ existing.endTime = timestamp;
7189
+ }
7190
+ }
7191
+ }
7192
+ }
7193
+ /**
7194
+ * Extract target from tool input based on tool type
7195
+ */
7196
+ extractTarget(toolName, input) {
7197
+ if (!input) return void 0;
7198
+ switch (toolName) {
7199
+ case "Read":
7200
+ case "Write":
7201
+ case "Edit":
7202
+ return this.asString(input.file_path ?? input.path);
7203
+ case "Glob":
7204
+ return this.asString(input.pattern);
7205
+ case "Grep":
7206
+ return this.asString(input.pattern);
7207
+ case "Bash": {
7208
+ const cmd = this.asString(input.command);
7209
+ return cmd ? this.truncateCommand(cmd) : void 0;
7210
+ }
7211
+ default:
7212
+ return void 0;
7213
+ }
7214
+ }
7215
+ /**
7216
+ * Safely convert value to string
7217
+ */
7218
+ asString(value) {
7219
+ if (typeof value === "string") return value;
7220
+ if (typeof value === "number") return String(value);
7221
+ return void 0;
7222
+ }
7223
+ /**
7224
+ * Truncate long commands to 30 chars
7225
+ */
7226
+ truncateCommand(cmd) {
7227
+ if (cmd.length <= 30) return cmd;
7228
+ return `${cmd.slice(0, 30)}...`;
7229
+ }
7230
+ };
7231
+
7232
+ // src/cli/commands/quick-config/preview.ts
6996
7233
  init_theme();
6997
7234
  init_active_tools();
6998
7235
  init_cache_metrics();
@@ -7253,7 +7490,6 @@ var StdinProvider = class {
7253
7490
  };
7254
7491
 
7255
7492
  // src/index.ts
7256
- init_transcript_provider();
7257
7493
  init_theme();
7258
7494
  init_active_tools();
7259
7495
  init_cache_metrics();
@@ -7275,12 +7511,19 @@ var EmptyLineWidget = class extends StdinDataWidget {
7275
7511
  5
7276
7512
  // Sixth line (0-indexed)
7277
7513
  );
7514
+ _lineOverride;
7278
7515
  /**
7279
7516
  * All styles return the same value (Braille Pattern Blank).
7280
7517
  * This method exists for API consistency with other widgets.
7281
7518
  */
7282
7519
  setStyle(_style) {
7283
7520
  }
7521
+ setLine(line) {
7522
+ this._lineOverride = line;
7523
+ }
7524
+ getLine() {
7525
+ return this._lineOverride ?? this.metadata.line ?? 0;
7526
+ }
7284
7527
  /**
7285
7528
  * Return Braille Pattern Blank to create a visible empty separator line.
7286
7529
  * U+2800 occupies cell width but appears blank, ensuring the line renders.
@@ -7860,6 +8103,7 @@ var PokerWidget = class extends StdinDataWidget {
7860
8103
  THROTTLE_MS = 5e3;
7861
8104
  // 5 seconds
7862
8105
  colors;
8106
+ _lineOverride;
7863
8107
  styleFn = pokerStyles.balanced;
7864
8108
  setStyle(style = DEFAULT_WIDGET_STYLE) {
7865
8109
  const fn = pokerStyles[style];
@@ -7867,6 +8111,12 @@ var PokerWidget = class extends StdinDataWidget {
7867
8111
  this.styleFn = fn;
7868
8112
  }
7869
8113
  }
8114
+ setLine(line) {
8115
+ this._lineOverride = line;
8116
+ }
8117
+ getLine() {
8118
+ return this._lineOverride ?? this.metadata.line ?? 0;
8119
+ }
7870
8120
  constructor(colors2) {
7871
8121
  super();
7872
8122
  this.colors = colors2 ?? DEFAULT_THEME;
@@ -7953,10 +8203,15 @@ async function readStdin() {
7953
8203
  return Buffer.concat(chunks).toString("utf8");
7954
8204
  }
7955
8205
  function applyWidgetConfig(widget, widgetId, config) {
7956
- for (const line of Object.values(config.lines)) {
7957
- const widgetConfig = line.find((w) => w.id === widgetId);
7958
- if (widgetConfig && typeof widget.setStyle === "function" && isValidWidgetStyle(widgetConfig.style)) {
7959
- widget.setStyle(widgetConfig.style);
8206
+ for (const [lineNum, widgets] of Object.entries(config.lines)) {
8207
+ const widgetConfig = widgets.find((w) => w.id === widgetId);
8208
+ if (widgetConfig) {
8209
+ if (typeof widget.setStyle === "function" && isValidWidgetStyle(widgetConfig.style)) {
8210
+ widget.setStyle(widgetConfig.style);
8211
+ }
8212
+ if (typeof widget.setLine === "function") {
8213
+ widget.setLine(parseInt(lineNum, 10));
8214
+ }
7960
8215
  break;
7961
8216
  }
7962
8217
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-scope",
3
- "version": "0.8.3",
3
+ "version": "0.8.5",
4
4
  "description": "Claude Code plugin for session status and analytics",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -19,12 +19,12 @@
19
19
  "build:tsc": "tsc",
20
20
  "build:bundle": "esbuild src/index.ts --bundle --platform=node --target=node18 --outfile=dist/claude-scope.cjs && chmod +x dist/claude-scope.cjs",
21
21
  "prebuild:bundle": "npm run build:tsc",
22
- "test": "tsx --test tests/e2e/stdin-flow.test.ts tests/integration/cli-flow.integration.test.ts tests/integration/five-widgets.integration.test.ts tests/integration/quick-config.integration.test.ts tests/integration/three-stage-config-flow.test.ts tests/unit/cli.test.ts tests/unit/cli/commands/quick-config/*.test.ts tests/unit/types.test.ts tests/unit/config/*.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",
23
- "test:unit": "tsx --test tests/unit/cli.test.ts tests/unit/types.test.ts tests/unit/config/*.test.ts tests/unit/core/*.test.ts tests/unit/data/*.test.ts tests/unit/utils/*.test.ts tests/unit/widgets/*.test.ts",
24
- "test:integration": "tsx --test tests/e2e/stdin-flow.test.ts tests/integration/cli-flow.integration.test.ts tests/integration/five-widgets.integration.test.ts tests/integration/quick-config.integration.test.ts tests/integration/three-stage-config-flow.test.ts",
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/integration/quick-config.integration.test.ts tests/integration/three-stage-config-flow.test.ts tests/unit/cli.test.ts tests/unit/cli/commands/quick-config/*.test.ts tests/unit/types.test.ts tests/unit/config/*.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
- "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/integration/quick-config.integration.test.ts tests/integration/three-stage-config-flow.test.ts tests/unit/cli.test.ts tests/unit/cli/commands/quick-config/*.test.ts tests/unit/types.test.ts tests/unit/config/*.test.ts tests/unit/core/*.test.ts tests/unit/data/*.test.ts tests/unit/utils/*.test.ts tests/unit/widgets/*.test.ts",
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/integration/quick-config.integration.test.ts tests/integration/three-stage-config-flow.test.ts tests/unit/cli.test.ts tests/unit/cli/commands/quick-config/*.test.ts tests/unit/types.test.ts tests/unit/config/*.test.ts tests/unit/core/*.test.ts tests/unit/data/*.test.ts tests/unit/utils/*.test.ts tests/unit/widgets/*.test.ts",
22
+ "test": "tsx --test tests/e2e/stdin-flow.test.ts tests/integration/cli-flow.integration.test.ts tests/integration/config-line-assignment.test.ts tests/integration/five-widgets.integration.test.ts tests/integration/preview-mock-config.test.ts tests/integration/quick-config.integration.test.ts tests/integration/three-stage-config-flow.test.ts tests/unit/cli.test.ts tests/unit/cli/commands/quick-config/*.test.ts tests/unit/providers/*.test.ts tests/unit/types.test.ts tests/unit/config/*.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",
23
+ "test:unit": "tsx --test tests/unit/cli.test.ts tests/unit/providers/*.test.ts tests/unit/types.test.ts tests/unit/config/*.test.ts tests/unit/core/*.test.ts tests/unit/data/*.test.ts tests/unit/utils/*.test.ts tests/unit/widgets/*.test.ts",
24
+ "test:integration": "tsx --test tests/e2e/stdin-flow.test.ts tests/integration/cli-flow.integration.test.ts tests/integration/config-line-assignment.test.ts tests/integration/five-widgets.integration.test.ts tests/integration/preview-mock-config.test.ts tests/integration/quick-config.integration.test.ts tests/integration/three-stage-config-flow.test.ts",
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/config-line-assignment.test.ts tests/integration/five-widgets.integration.test.ts tests/integration/preview-mock-config.test.ts tests/integration/quick-config.integration.test.ts tests/integration/three-stage-config-flow.test.ts tests/unit/cli.test.ts tests/unit/cli/commands/quick-config/*.test.ts tests/unit/providers/*.test.ts tests/unit/types.test.ts tests/unit/config/*.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
+ "test:snapshot": "SNAPSHOT_UPDATE=true tsx --test tests/e2e/stdin-flow.test.ts tests/integration/cli-flow.integration.test.ts tests/integration/config-line-assignment.test.ts tests/integration/five-widgets.integration.test.ts tests/integration/preview-mock-config.test.ts tests/integration/quick-config.integration.test.ts tests/integration/three-stage-config-flow.test.ts tests/unit/cli.test.ts tests/unit/cli/commands/quick-config/*.test.ts tests/unit/providers/*.test.ts tests/unit/types.test.ts tests/unit/config/*.test.ts tests/unit/core/*.test.ts tests/unit/data/*.test.ts tests/unit/utils/*.test.ts tests/unit/widgets/*.test.ts",
27
+ "test:snapshot:verify": "tsx --test tests/e2e/stdin-flow.test.ts tests/integration/cli-flow.integration.test.ts tests/integration/config-line-assignment.test.ts tests/integration/five-widgets.integration.test.ts tests/integration/preview-mock-config.test.ts tests/integration/quick-config.integration.test.ts tests/integration/three-stage-config-flow.test.ts tests/unit/cli.test.ts tests/unit/cli/commands/quick-config/*.test.ts tests/unit/providers/*.test.ts tests/unit/types.test.ts tests/unit/config/*.test.ts tests/unit/core/*.test.ts tests/unit/data/*.test.ts tests/unit/utils/*.test.ts tests/unit/widgets/*.test.ts",
28
28
  "dev": "tsx src/index.ts",
29
29
  "prepare": "husky",
30
30
  "lint": "biome check .",