ccstatusline-usage 2.1.2 โ†’ 2.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/README.md CHANGED
@@ -46,6 +46,21 @@
46
46
 
47
47
  ## ๐Ÿ†• Recent Updates
48
48
 
49
+ ### [v2.1.4](https://github.com/pcvelz/ccstatusline-usage/releases/tag/v2.1.4) - Compact mode widget-boundary wrapping
50
+
51
+ - [pcvelz/ccstatusline-usage](https://github.com/pcvelz/ccstatusline-usage): **Widget-boundary wrapping** โ€” compact mode now wraps at widget boundaries instead of mid-widget, using a dedicated compact renderer (`compact-renderer.ts`). Widgets that don't fit on the current line cleanly wrap to the next:
52
+ ```
53
+ S: [โ–‘โ–‘โ–‘โ–‘] 2.0% | W: [โ–ˆโ–ˆโ–‘โ–‘] 43.0% | 4:36 hr
54
+ M: o4.6 โ–Œโ–Œโ–Œ | S: 5647528d | C: [โ–ˆโ–ˆโ–‘โ–‘] 119k/200k
55
+ ```
56
+ - [pcvelz/ccstatusline-usage](https://github.com/pcvelz/ccstatusline-usage): **Terminal-agnostic compact detection** โ€” compact mode now triggers on any narrow terminal (< 80 columns), not just tmux
57
+
58
+ ### [v2.1.3](https://github.com/pcvelz/ccstatusline-usage/releases/tag/v2.1.3) - Mobile/compact mode and thinking effort bars
59
+
60
+ - [pcvelz/ccstatusline-usage](https://github.com/pcvelz/ccstatusline-usage): **Mobile/compact mode** โ€” in narrow terminals (< 80 effective columns, e.g. tmux panes), widgets auto-switch to compact rendering: `M: o4.6` instead of `Model: claude-opus-4-6`, session ID truncated to 8 chars, and multiple status lines merge into one
61
+ - [pcvelz/ccstatusline-usage](https://github.com/pcvelz/ccstatusline-usage): **Thinking effort bars** โ€” Model widget shows `โ–Œโ–Œโ–Œ` bars indicating Claude Code's effort level (1=low, 2=medium, 3=high). Reads from `CLAUDE_CODE_EFFORT_LEVEL` env var or `~/.claude/settings.json`
62
+ - [pcvelz/ccstatusline-usage](https://github.com/pcvelz/ccstatusline-usage): **Tmux pane width detection** โ€” correctly detects actual pane width via `tmux display-message` instead of falling back to default 80 columns
63
+
49
64
  ### [v2.1.2](https://github.com/pcvelz/ccstatusline-usage/releases/tag/v2.1.2) - Token auth fallback and 401 handling
50
65
 
51
66
  - [pcvelz/ccstatusline-usage](https://github.com/pcvelz/ccstatusline-usage): macOS file credential fallback โ€” reads `~/.claude/.credentials.json` when Keychain entry is missing
@@ -355,7 +370,7 @@ Once configured, ccstatusline automatically formats your Claude Code status line
355
370
 
356
371
  ### ๐Ÿ“Š Available Widgets
357
372
 
358
- - **Model Name** - Shows the current Claude model (e.g., "Claude 3.5 Sonnet")
373
+ - **Model Name** - Shows the current Claude model with thinking effort bars (`โ–Œโ–Œโ–Œ`) indicating low/medium/high effort
359
374
  - **Git Branch** - Displays current git branch name
360
375
  - **Git Changes** - Shows uncommitted insertions/deletions (e.g., "+42,-10")
361
376
  - **Git Worktree** - Shows the name of the current git worktree
@@ -370,8 +385,8 @@ Once configured, ccstatusline automatically formats your Claude Code status line
370
385
  - **Tokens Cached** - Shows cached tokens used
371
386
  - **Tokens Total** - Shows total tokens used
372
387
  - **Context Length** - Shows current context length in tokens
373
- - **Context Percentage** - Shows percentage of context limit used (dynamic: 1M for Sonnet 4.5 with `[1m]` suffix, 200k otherwise)
374
- - **Context Percentage (usable)** - Shows percentage of usable context (dynamic: 800k for Sonnet 4.5 with `[1m]` suffix, 160k otherwise, accounting for auto-compact at 80%)
388
+ - **Context Percentage** - Shows percentage of context limit used (dynamic: 1M for Sonnet 4.5/Opus 4.6 with `[1m]` suffix, 200k otherwise)
389
+ - **Context Percentage (usable)** - Shows percentage of usable context (dynamic: 800k for Sonnet 4.5/Opus 4.6 with `[1m]` suffix, 160k otherwise, accounting for auto-compact at 80%)
375
390
  - **Terminal Width** - Shows detected terminal width (for debugging)
376
391
  - **Custom Text** - Add your own custom text to the status line
377
392
  - **Custom Command** - Execute shell commands and display their output (refreshes whenever the statusline is updated by Claude Code)
@@ -441,7 +456,7 @@ The Block Timer widget helps you track your progress through Claude Code's 5-hou
441
456
  ### ๐Ÿ”ค Raw Value Mode
442
457
 
443
458
  Some widgets support "raw value" mode which displays just the value without a label:
444
- - Normal: `Model: Claude 3.5 Sonnet` โ†’ Raw: `Claude 3.5 Sonnet`
459
+ - Normal: `Model: Claude Opus 4.6` โ†’ Raw: `Claude Opus 4.6`
445
460
  - Normal: `Session: 2hr 15m` โ†’ Raw: `2hr 15m`
446
461
  - Normal: `Block: 3hr 45m` โ†’ Raw: `3hr 45m`
447
462
  - Normal: `Ctx: 18.6k` โ†’ Raw: `18.6k`
@@ -618,6 +633,11 @@ Contributions are welcome! Please feel free to submit a Pull Request.
618
633
 
619
634
  - GitHub: [@sirmalloc](https://github.com/sirmalloc)
620
635
 
636
+ **Fork maintained by Peter van Velzen**
637
+
638
+ - GitHub: [@pcvelz](https://github.com/pcvelz)
639
+ - Fork: [pcvelz/ccstatusline-usage](https://github.com/pcvelz/ccstatusline-usage)
640
+
621
641
  ---
622
642
 
623
643
  ## ๐Ÿ”— Related Projects
@@ -637,11 +657,11 @@ Contributions are welcome! Please feel free to submit a Pull Request.
637
657
 
638
658
  ## Star History
639
659
 
640
- <a href="https://www.star-history.com/#sirmalloc/ccstatusline&Timeline">
660
+ <a href="https://www.star-history.com/#pcvelz/ccstatusline-usage&Timeline">
641
661
  <picture>
642
- <source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=sirmalloc/ccstatusline&type=Timeline&theme=dark" />
643
- <source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=sirmalloc/ccstatusline&type=Timeline" />
644
- <img alt="Star History Chart" src="https://api.star-history.com/svg?repos=sirmalloc/ccstatusline&type=Timeline" />
662
+ <source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=pcvelz/ccstatusline-usage&type=Timeline&theme=dark" />
663
+ <source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=pcvelz/ccstatusline-usage&type=Timeline" />
664
+ <img alt="Star History Chart" src="https://api.star-history.com/svg?repos=pcvelz/ccstatusline-usage&type=Timeline" />
645
665
  </picture>
646
666
  </a>
647
667
 
@@ -651,21 +671,21 @@ Contributions are welcome! Please feel free to submit a Pull Request.
651
671
 
652
672
  Give a โญ if this project helped you!
653
673
 
654
- [![GitHub stars](https://img.shields.io/github/stars/sirmalloc/ccstatusline?style=social)](https://github.com/sirmalloc/ccstatusline/stargazers)
655
- [![GitHub forks](https://img.shields.io/github/forks/sirmalloc/ccstatusline?style=social)](https://github.com/sirmalloc/ccstatusline/network/members)
656
- [![GitHub watchers](https://img.shields.io/github/watchers/sirmalloc/ccstatusline?style=social)](https://github.com/sirmalloc/ccstatusline/watchers)
674
+ [![GitHub stars](https://img.shields.io/github/stars/pcvelz/ccstatusline-usage?style=social)](https://github.com/pcvelz/ccstatusline-usage/stargazers)
675
+ [![GitHub forks](https://img.shields.io/github/forks/pcvelz/ccstatusline-usage?style=social)](https://github.com/pcvelz/ccstatusline-usage/network/members)
676
+ [![GitHub watchers](https://img.shields.io/github/watchers/pcvelz/ccstatusline-usage?style=social)](https://github.com/pcvelz/ccstatusline-usage/watchers)
657
677
 
658
- [![npm version](https://img.shields.io/npm/v/ccstatusline.svg)](https://www.npmjs.com/package/ccstatusline)
659
- [![npm downloads](https://img.shields.io/npm/dm/ccstatusline.svg)](https://www.npmjs.com/package/ccstatusline)
660
- [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/sirmalloc/ccstatusline/blob/main/LICENSE)
678
+ [![npm version](https://img.shields.io/npm/v/ccstatusline-usage.svg)](https://www.npmjs.com/package/ccstatusline-usage)
679
+ [![npm downloads](https://img.shields.io/npm/dm/ccstatusline-usage.svg)](https://www.npmjs.com/package/ccstatusline-usage)
680
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/pcvelz/ccstatusline-usage/blob/main/LICENSE)
661
681
  [![Made with Bun](https://img.shields.io/badge/Made%20with-Bun-000000.svg?logo=bun)](https://bun.sh)
662
682
 
663
- [![Issues](https://img.shields.io/github/issues/sirmalloc/ccstatusline)](https://github.com/sirmalloc/ccstatusline/issues)
664
- [![Pull Requests](https://img.shields.io/github/issues-pr/sirmalloc/ccstatusline)](https://github.com/sirmalloc/ccstatusline/pulls)
665
- [![Contributors](https://img.shields.io/github/contributors/sirmalloc/ccstatusline)](https://github.com/sirmalloc/ccstatusline/graphs/contributors)
683
+ [![Issues](https://img.shields.io/github/issues/pcvelz/ccstatusline-usage)](https://github.com/pcvelz/ccstatusline-usage/issues)
684
+ [![Pull Requests](https://img.shields.io/github/issues-pr/pcvelz/ccstatusline-usage)](https://github.com/pcvelz/ccstatusline-usage/pulls)
685
+ [![Contributors](https://img.shields.io/github/contributors/pcvelz/ccstatusline-usage)](https://github.com/pcvelz/ccstatusline-usage/graphs/contributors)
666
686
 
667
687
  ### ๐Ÿ’ฌ Connect
668
688
 
669
- [Report Bug](https://github.com/sirmalloc/ccstatusline/issues) ยท [Request Feature](https://github.com/sirmalloc/ccstatusline/issues) ยท [Discussions](https://github.com/sirmalloc/ccstatusline/discussions)
689
+ [Report Bug](https://github.com/pcvelz/ccstatusline-usage/issues) ยท [Request Feature](https://github.com/pcvelz/ccstatusline-usage/issues) ยท [Discussions](https://github.com/pcvelz/ccstatusline-usage/discussions)
670
690
 
671
691
  </div>
@@ -32383,8 +32383,8 @@ var require_utils = __commonJS((exports) => {
32383
32383
  }
32384
32384
  return output;
32385
32385
  };
32386
- exports.basename = (path6, { windows } = {}) => {
32387
- const segs = path6.split(windows ? /[\\/]/ : "/");
32386
+ exports.basename = (path7, { windows } = {}) => {
32387
+ const segs = path7.split(windows ? /[\\/]/ : "/");
32388
32388
  const last = segs[segs.length - 1];
32389
32389
  if (last === "") {
32390
32390
  return segs[segs.length - 2];
@@ -50988,7 +50988,7 @@ var SettingsSchema = exports_external.object({
50988
50988
  { id: "sep2", type: "separator" },
50989
50989
  { id: "reset-timer", type: "reset-timer", color: "brightBlue" },
50990
50990
  { id: "sep3", type: "separator" },
50991
- { id: "model", type: "model", color: "magenta" },
50991
+ { id: "model", type: "model", color: "ansi256:124" },
50992
50992
  { id: "sep4", type: "separator" },
50993
50993
  { id: "session-id", type: "claude-session-id", color: "cyan" }
50994
50994
  ],
@@ -51450,7 +51450,7 @@ import { execSync as execSync3 } from "child_process";
51450
51450
  import * as fs5 from "fs";
51451
51451
  import * as path4 from "path";
51452
51452
  var __dirname = "/Users/peter/Documents/Code/ccstatusline-usage/src/utils";
51453
- var PACKAGE_VERSION = "2.1.2";
51453
+ var PACKAGE_VERSION = "2.1.4";
51454
51454
  function getPackageVersion() {
51455
51455
  if (/^\d+\.\d+\.\d+/.test(PACKAGE_VERSION)) {
51456
51456
  return PACKAGE_VERSION;
@@ -51470,6 +51470,14 @@ function getPackageVersion() {
51470
51470
  return "";
51471
51471
  }
51472
51472
  function getTerminalWidth() {
51473
+ if (process.env.TMUX) {
51474
+ try {
51475
+ const output = execSync3("tmux display-message -p '#{pane_width}'", { encoding: "utf8", stdio: ["pipe", "pipe", "ignore"], timeout: 2000 }).trim();
51476
+ const parsed = parseInt(output, 10);
51477
+ if (!isNaN(parsed) && parsed > 0)
51478
+ return parsed;
51479
+ } catch {}
51480
+ }
51473
51481
  try {
51474
51482
  const tty2 = execSync3("ps -o tty= -p $(ps -o ppid= -p $$)", {
51475
51483
  encoding: "utf8",
@@ -52280,9 +52288,61 @@ function getDefaultPowerlineTheme() {
52280
52288
  }
52281
52289
 
52282
52290
  // src/widgets/Model.ts
52291
+ import * as fs6 from "fs";
52292
+ import { homedir as homedir4 } from "os";
52293
+ import * as path5 from "path";
52294
+ var MOBILE_THRESHOLD = 80;
52295
+ function compactModelName(name) {
52296
+ const stripped = name.replace(/^claude-/, "");
52297
+ const match = /^([a-z]+)-(\d+)-(\d+)/.exec(stripped);
52298
+ if (match) {
52299
+ const letter = match[1]?.charAt(0) ?? "";
52300
+ return `${letter}${match[2]}.${match[3]}`;
52301
+ }
52302
+ return stripped;
52303
+ }
52304
+ function getEffortLevel() {
52305
+ const envLevel = process.env.CLAUDE_CODE_EFFORT_LEVEL;
52306
+ if (envLevel)
52307
+ return envLevel.toLowerCase();
52308
+ try {
52309
+ const configDir = process.env.CLAUDE_CONFIG_DIR ?? path5.join(homedir4(), ".claude");
52310
+ const settingsPath = path5.join(configDir, "settings.json");
52311
+ const content = fs6.readFileSync(settingsPath, "utf-8");
52312
+ const settings = JSON.parse(content);
52313
+ if (settings.effortLevel)
52314
+ return settings.effortLevel.toLowerCase();
52315
+ } catch {}
52316
+ return "high";
52317
+ }
52318
+ function effortToLevel(effort) {
52319
+ switch (effort) {
52320
+ case "low":
52321
+ return 1;
52322
+ case "medium":
52323
+ return 2;
52324
+ default:
52325
+ return 3;
52326
+ }
52327
+ }
52328
+ function renderThinkingBars(level, settings) {
52329
+ if (level <= 0)
52330
+ return "";
52331
+ const colorLevel = getColorLevelString(settings.colorLevel);
52332
+ const activeChalk = getChalkColor("red", colorLevel);
52333
+ const dimChalk = getChalkColor("brightBlack", colorLevel);
52334
+ const bars = ["โ–Œ", "โ–Œ", "โ–Œ"];
52335
+ return " " + bars.map((bar, i) => {
52336
+ if (i < level) {
52337
+ return activeChalk ? activeChalk(bar) : bar;
52338
+ }
52339
+ return dimChalk ? dimChalk(bar) : bar;
52340
+ }).join("");
52341
+ }
52342
+
52283
52343
  class ModelWidget {
52284
52344
  getDefaultColor() {
52285
- return "cyan";
52345
+ return "ansi256:124";
52286
52346
  }
52287
52347
  getDescription() {
52288
52348
  return "Displays the Claude model name (e.g., Claude 3.5 Sonnet)";
@@ -52295,14 +52355,21 @@ class ModelWidget {
52295
52355
  }
52296
52356
  render(item, context, settings) {
52297
52357
  if (context.isPreview) {
52298
- return item.rawValue ? "Claude" : "Model: Claude";
52358
+ const bars2 = renderThinkingBars(3, settings);
52359
+ return item.rawValue ? `Claude${bars2}` : `Model: Claude${bars2}`;
52299
52360
  }
52300
52361
  const model = context.data?.model;
52362
+ const modelId = typeof model === "string" ? model : model?.id;
52301
52363
  const modelDisplayName = typeof model === "string" ? model : model?.display_name ?? model?.id;
52302
- if (modelDisplayName) {
52303
- return item.rawValue ? modelDisplayName : `Model: ${modelDisplayName}`;
52364
+ if (!modelDisplayName)
52365
+ return null;
52366
+ const level = effortToLevel(getEffortLevel());
52367
+ const bars = renderThinkingBars(level, settings);
52368
+ const mobile = (context.terminalWidth ?? 0) > 0 && (context.terminalWidth ?? 0) < MOBILE_THRESHOLD;
52369
+ if (mobile && modelId) {
52370
+ return `M: ${compactModelName(modelId)}${bars}`;
52304
52371
  }
52305
- return null;
52372
+ return item.rawValue ? `${modelDisplayName}${bars}` : `Model: ${modelDisplayName}${bars}`;
52306
52373
  }
52307
52374
  supportsRawValue() {
52308
52375
  return true;
@@ -54272,13 +54339,13 @@ class CurrentWorkingDirWidget {
54272
54339
  supportsColors(item) {
54273
54340
  return true;
54274
54341
  }
54275
- abbreviatePath(path5) {
54342
+ abbreviatePath(path6) {
54276
54343
  const homeDir = os5.homedir();
54277
- const useBackslash = path5.includes("\\") && !path5.includes("/");
54344
+ const useBackslash = path6.includes("\\") && !path6.includes("/");
54278
54345
  const sep = useBackslash ? "\\" : "/";
54279
- let normalizedPath = path5;
54280
- if (path5.startsWith(homeDir)) {
54281
- normalizedPath = "~" + path5.slice(homeDir.length);
54346
+ let normalizedPath = path6;
54347
+ if (path6.startsWith(homeDir)) {
54348
+ normalizedPath = "~" + path6.slice(homeDir.length);
54282
54349
  }
54283
54350
  const parts = normalizedPath.split(/[\\/]+/).filter((part) => part !== "");
54284
54351
  const abbreviated = parts.map((part, index) => {
@@ -54376,13 +54443,16 @@ class ClaudeSessionIdWidget {
54376
54443
  render(item, context, settings) {
54377
54444
  if (context.isPreview) {
54378
54445
  return item.rawValue ? "preview-session-id" : "Session ID: preview-session-id";
54379
- } else {
54380
- const sessionId = context.data?.session_id;
54381
- if (!sessionId) {
54382
- return null;
54383
- }
54384
- return item.rawValue ? sessionId : `Session ID: ${sessionId}`;
54385
54446
  }
54447
+ const sessionId = context.data?.session_id;
54448
+ if (!sessionId) {
54449
+ return null;
54450
+ }
54451
+ const mobile = (context.terminalWidth ?? 0) > 0 && (context.terminalWidth ?? 0) < 80;
54452
+ if (mobile) {
54453
+ return `S: ${sessionId.slice(0, 8)}`;
54454
+ }
54455
+ return item.rawValue ? sessionId : `Session ID: ${sessionId}`;
54386
54456
  }
54387
54457
  supportsRawValue() {
54388
54458
  return true;
@@ -54396,10 +54466,10 @@ import {
54396
54466
  execSync as execSync8,
54397
54467
  spawnSync
54398
54468
  } from "child_process";
54399
- import * as fs6 from "fs";
54400
- import * as path5 from "path";
54401
- var CACHE_FILE = path5.join(process.env.HOME ?? "", ".cache", "ccstatusline-api.json");
54402
- var LOCK_FILE = path5.join(process.env.HOME ?? "", ".cache", "ccstatusline-api.lock");
54469
+ import * as fs7 from "fs";
54470
+ import * as path6 from "path";
54471
+ var CACHE_FILE = path6.join(process.env.HOME ?? "", ".cache", "ccstatusline-api.json");
54472
+ var LOCK_FILE = path6.join(process.env.HOME ?? "", ".cache", "ccstatusline-api.lock");
54403
54473
  var CACHE_MAX_AGE = 180;
54404
54474
  var LOCK_MAX_AGE = 30;
54405
54475
  var TOKEN_CACHE_MAX_AGE = 3600;
@@ -54407,10 +54477,10 @@ var cachedData = null;
54407
54477
  var cacheTime = 0;
54408
54478
  var cachedToken = null;
54409
54479
  var tokenCacheTime = 0;
54410
- var CRED_FILE = path5.join(process.env.HOME ?? "", ".claude", ".credentials.json");
54480
+ var CRED_FILE = path6.join(process.env.HOME ?? "", ".claude", ".credentials.json");
54411
54481
  function readTokenFromFile() {
54412
54482
  try {
54413
- const creds = JSON.parse(fs6.readFileSync(CRED_FILE, "utf8"));
54483
+ const creds = JSON.parse(fs7.readFileSync(CRED_FILE, "utf8"));
54414
54484
  return creds?.claudeAiOauth?.accessToken ?? null;
54415
54485
  } catch {
54416
54486
  return null;
@@ -54447,7 +54517,7 @@ function getToken() {
54447
54517
  }
54448
54518
  function readStaleCache() {
54449
54519
  try {
54450
- return JSON.parse(fs6.readFileSync(CACHE_FILE, "utf8"));
54520
+ return JSON.parse(fs7.readFileSync(CACHE_FILE, "utf8"));
54451
54521
  } catch {
54452
54522
  return null;
54453
54523
  }
@@ -54505,10 +54575,10 @@ function fetchApiData() {
54505
54575
  return cachedData;
54506
54576
  }
54507
54577
  try {
54508
- const stat = fs6.statSync(CACHE_FILE);
54578
+ const stat = fs7.statSync(CACHE_FILE);
54509
54579
  const fileAge = now - Math.floor(stat.mtimeMs / 1000);
54510
54580
  if (fileAge < CACHE_MAX_AGE) {
54511
- const fileData = JSON.parse(fs6.readFileSync(CACHE_FILE, "utf8"));
54581
+ const fileData = JSON.parse(fs7.readFileSync(CACHE_FILE, "utf8"));
54512
54582
  if (!fileData.error) {
54513
54583
  cachedData = fileData;
54514
54584
  cacheTime = now;
@@ -54517,7 +54587,7 @@ function fetchApiData() {
54517
54587
  }
54518
54588
  } catch {}
54519
54589
  try {
54520
- const lockStat = fs6.statSync(LOCK_FILE);
54590
+ const lockStat = fs7.statSync(LOCK_FILE);
54521
54591
  const lockAge = now - Math.floor(lockStat.mtimeMs / 1000);
54522
54592
  if (lockAge < LOCK_MAX_AGE) {
54523
54593
  const stale = readStaleCache();
@@ -54527,11 +54597,11 @@ function fetchApiData() {
54527
54597
  }
54528
54598
  } catch {}
54529
54599
  try {
54530
- const lockDir = path5.dirname(LOCK_FILE);
54531
- if (!fs6.existsSync(lockDir)) {
54532
- fs6.mkdirSync(lockDir, { recursive: true });
54600
+ const lockDir = path6.dirname(LOCK_FILE);
54601
+ if (!fs7.existsSync(lockDir)) {
54602
+ fs7.mkdirSync(lockDir, { recursive: true });
54533
54603
  }
54534
- fs6.writeFileSync(LOCK_FILE, "");
54604
+ fs7.writeFileSync(LOCK_FILE, "");
54535
54605
  } catch {}
54536
54606
  const token = getToken();
54537
54607
  if (!token) {
@@ -54577,11 +54647,11 @@ function fetchApiData() {
54577
54647
  return { error: "parse-error" };
54578
54648
  }
54579
54649
  try {
54580
- const cacheDir = path5.dirname(CACHE_FILE);
54581
- if (!fs6.existsSync(cacheDir)) {
54582
- fs6.mkdirSync(cacheDir, { recursive: true });
54650
+ const cacheDir = path6.dirname(CACHE_FILE);
54651
+ if (!fs7.existsSync(cacheDir)) {
54652
+ fs7.mkdirSync(cacheDir, { recursive: true });
54583
54653
  }
54584
- fs6.writeFileSync(CACHE_FILE, JSON.stringify(apiData));
54654
+ fs7.writeFileSync(CACHE_FILE, JSON.stringify(apiData));
54585
54655
  } catch {}
54586
54656
  cachedData = apiData;
54587
54657
  cacheTime = now;
@@ -54605,11 +54675,11 @@ function getErrorMessage(error43) {
54605
54675
  return "[Parse Error]";
54606
54676
  }
54607
54677
  }
54608
- var MOBILE_THRESHOLD = 80;
54678
+ var MOBILE_THRESHOLD2 = 80;
54609
54679
  var MOBILE_BAR_WIDTH = 4;
54610
54680
  var DEFAULT_BAR_WIDTH = 15;
54611
54681
  function isMobileWidth(context) {
54612
- return (context.terminalWidth ?? 0) > 0 && (context.terminalWidth ?? 0) < MOBILE_THRESHOLD;
54682
+ return (context.terminalWidth ?? 0) > 0 && (context.terminalWidth ?? 0) < MOBILE_THRESHOLD2;
54613
54683
  }
54614
54684
  function makeProgressBar(percent, width = DEFAULT_BAR_WIDTH) {
54615
54685
  const filled = Math.round(percent / 100 * width);
@@ -58680,43 +58750,100 @@ var StatusJSONSchema = exports_external.looseObject({
58680
58750
  }).nullable().optional()
58681
58751
  });
58682
58752
 
58753
+ // src/utils/compact-renderer.ts
58754
+ function renderCompactOutput(preRenderedLines, settings, maxWidth) {
58755
+ const colorLevel = getColorLevelString(settings.colorLevel);
58756
+ const sep = settings.defaultSeparator ?? "|";
58757
+ const separatorText = sep === "|" ? " | " : ` ${sep} `;
58758
+ const separatorWidth = separatorText.length;
58759
+ const coloredWidgets = [];
58760
+ for (const line of preRenderedLines) {
58761
+ for (const pw of line) {
58762
+ if (pw.widget.type === "separator" || pw.widget.type === "flex-separator")
58763
+ continue;
58764
+ if (pw.plainLength === 0)
58765
+ continue;
58766
+ let fgColor = pw.widget.color;
58767
+ if (!fgColor) {
58768
+ const impl = getWidget(pw.widget.type);
58769
+ fgColor = impl ? impl.getDefaultColor() : "white";
58770
+ }
58771
+ let bgColor = pw.widget.backgroundColor;
58772
+ if (settings.overrideForegroundColor && settings.overrideForegroundColor !== "none")
58773
+ fgColor = settings.overrideForegroundColor;
58774
+ if (settings.overrideBackgroundColor && settings.overrideBackgroundColor !== "none")
58775
+ bgColor = settings.overrideBackgroundColor;
58776
+ const bold = settings.globalBold || pw.widget.bold;
58777
+ const colored = applyColors(pw.content, fgColor, bgColor, bold, colorLevel);
58778
+ coloredWidgets.push({ colored, width: pw.plainLength });
58779
+ }
58780
+ }
58781
+ if (coloredWidgets.length === 0)
58782
+ return;
58783
+ const outputLines = [];
58784
+ let currentLine = "";
58785
+ let currentWidth = 0;
58786
+ for (const cw of coloredWidgets) {
58787
+ const needed = currentWidth === 0 ? cw.width : separatorWidth + cw.width;
58788
+ if (currentWidth > 0 && currentWidth + needed > maxWidth) {
58789
+ outputLines.push(currentLine);
58790
+ currentLine = cw.colored;
58791
+ currentWidth = cw.width;
58792
+ } else {
58793
+ if (currentWidth > 0) {
58794
+ currentLine += source_default.gray(separatorText);
58795
+ currentWidth += separatorWidth;
58796
+ }
58797
+ currentLine += cw.colored;
58798
+ currentWidth += cw.width;
58799
+ }
58800
+ }
58801
+ if (currentLine)
58802
+ outputLines.push(currentLine);
58803
+ for (const line of outputLines) {
58804
+ let outputLine = line.replace(/ /g, "ย ");
58805
+ outputLine = "\x1B[0m" + outputLine;
58806
+ console.log(outputLine);
58807
+ }
58808
+ }
58809
+
58683
58810
  // src/utils/jsonl.ts
58684
- import * as fs7 from "fs";
58685
- import path7 from "node:path";
58811
+ import * as fs8 from "fs";
58812
+ import path8 from "node:path";
58686
58813
 
58687
58814
  // node_modules/tinyglobby/dist/index.mjs
58688
- import path6, { posix } from "path";
58815
+ import path7, { posix } from "path";
58689
58816
 
58690
58817
  // node_modules/fdir/dist/index.mjs
58691
58818
  import { createRequire as createRequire2 } from "module";
58692
58819
  import { basename as basename2, dirname as dirname3, normalize, relative, resolve as resolve2, sep } from "path";
58693
58820
  import * as nativeFs from "fs";
58694
58821
  var __require2 = /* @__PURE__ */ createRequire2(import.meta.url);
58695
- function cleanPath(path6) {
58696
- let normalized = normalize(path6);
58822
+ function cleanPath(path7) {
58823
+ let normalized = normalize(path7);
58697
58824
  if (normalized.length > 1 && normalized[normalized.length - 1] === sep)
58698
58825
  normalized = normalized.substring(0, normalized.length - 1);
58699
58826
  return normalized;
58700
58827
  }
58701
58828
  var SLASHES_REGEX = /[\\/]/g;
58702
- function convertSlashes(path6, separator) {
58703
- return path6.replace(SLASHES_REGEX, separator);
58829
+ function convertSlashes(path7, separator) {
58830
+ return path7.replace(SLASHES_REGEX, separator);
58704
58831
  }
58705
58832
  var WINDOWS_ROOT_DIR_REGEX = /^[a-z]:[\\/]$/i;
58706
- function isRootDirectory(path6) {
58707
- return path6 === "/" || WINDOWS_ROOT_DIR_REGEX.test(path6);
58833
+ function isRootDirectory(path7) {
58834
+ return path7 === "/" || WINDOWS_ROOT_DIR_REGEX.test(path7);
58708
58835
  }
58709
- function normalizePath(path6, options) {
58836
+ function normalizePath(path7, options) {
58710
58837
  const { resolvePaths, normalizePath: normalizePath$1, pathSeparator } = options;
58711
- const pathNeedsCleaning = process.platform === "win32" && path6.includes("/") || path6.startsWith(".");
58838
+ const pathNeedsCleaning = process.platform === "win32" && path7.includes("/") || path7.startsWith(".");
58712
58839
  if (resolvePaths)
58713
- path6 = resolve2(path6);
58840
+ path7 = resolve2(path7);
58714
58841
  if (normalizePath$1 || pathNeedsCleaning)
58715
- path6 = cleanPath(path6);
58716
- if (path6 === ".")
58842
+ path7 = cleanPath(path7);
58843
+ if (path7 === ".")
58717
58844
  return "";
58718
- const needsSeperator = path6[path6.length - 1] !== pathSeparator;
58719
- return convertSlashes(needsSeperator ? path6 + pathSeparator : path6, pathSeparator);
58845
+ const needsSeperator = path7[path7.length - 1] !== pathSeparator;
58846
+ return convertSlashes(needsSeperator ? path7 + pathSeparator : path7, pathSeparator);
58720
58847
  }
58721
58848
  function joinPathWithBasePath(filename, directoryPath) {
58722
58849
  return directoryPath + filename;
@@ -58756,9 +58883,9 @@ var pushDirectory = (directoryPath, paths) => {
58756
58883
  paths.push(directoryPath || ".");
58757
58884
  };
58758
58885
  var pushDirectoryFilter = (directoryPath, paths, filters) => {
58759
- const path6 = directoryPath || ".";
58760
- if (filters.every((filter) => filter(path6, true)))
58761
- paths.push(path6);
58886
+ const path7 = directoryPath || ".";
58887
+ if (filters.every((filter) => filter(path7, true)))
58888
+ paths.push(path7);
58762
58889
  };
58763
58890
  var empty$2 = () => {};
58764
58891
  function build$6(root, options) {
@@ -58815,29 +58942,29 @@ var empty = () => {};
58815
58942
  function build$3(options) {
58816
58943
  return options.group ? groupFiles : empty;
58817
58944
  }
58818
- var resolveSymlinksAsync = function(path6, state, callback$1) {
58819
- const { queue, fs: fs7, options: { suppressErrors } } = state;
58945
+ var resolveSymlinksAsync = function(path7, state, callback$1) {
58946
+ const { queue, fs: fs8, options: { suppressErrors } } = state;
58820
58947
  queue.enqueue();
58821
- fs7.realpath(path6, (error43, resolvedPath) => {
58948
+ fs8.realpath(path7, (error43, resolvedPath) => {
58822
58949
  if (error43)
58823
58950
  return queue.dequeue(suppressErrors ? null : error43, state);
58824
- fs7.stat(resolvedPath, (error$1, stat) => {
58951
+ fs8.stat(resolvedPath, (error$1, stat) => {
58825
58952
  if (error$1)
58826
58953
  return queue.dequeue(suppressErrors ? null : error$1, state);
58827
- if (stat.isDirectory() && isRecursive(path6, resolvedPath, state))
58954
+ if (stat.isDirectory() && isRecursive(path7, resolvedPath, state))
58828
58955
  return queue.dequeue(null, state);
58829
58956
  callback$1(stat, resolvedPath);
58830
58957
  queue.dequeue(null, state);
58831
58958
  });
58832
58959
  });
58833
58960
  };
58834
- var resolveSymlinks = function(path6, state, callback$1) {
58835
- const { queue, fs: fs7, options: { suppressErrors } } = state;
58961
+ var resolveSymlinks = function(path7, state, callback$1) {
58962
+ const { queue, fs: fs8, options: { suppressErrors } } = state;
58836
58963
  queue.enqueue();
58837
58964
  try {
58838
- const resolvedPath = fs7.realpathSync(path6);
58839
- const stat = fs7.statSync(resolvedPath);
58840
- if (stat.isDirectory() && isRecursive(path6, resolvedPath, state))
58965
+ const resolvedPath = fs8.realpathSync(path7);
58966
+ const stat = fs8.statSync(resolvedPath);
58967
+ if (stat.isDirectory() && isRecursive(path7, resolvedPath, state))
58841
58968
  return;
58842
58969
  callback$1(stat, resolvedPath);
58843
58970
  } catch (e) {
@@ -58850,10 +58977,10 @@ function build$2(options, isSynchronous) {
58850
58977
  return null;
58851
58978
  return isSynchronous ? resolveSymlinks : resolveSymlinksAsync;
58852
58979
  }
58853
- function isRecursive(path6, resolved, state) {
58980
+ function isRecursive(path7, resolved, state) {
58854
58981
  if (state.options.useRealPaths)
58855
58982
  return isRecursiveUsingRealPaths(resolved, state);
58856
- let parent = dirname3(path6);
58983
+ let parent = dirname3(path7);
58857
58984
  let depth = 1;
58858
58985
  while (parent !== state.root && depth < 2) {
58859
58986
  const resolvedPath = state.symlinks.get(parent);
@@ -58863,7 +58990,7 @@ function isRecursive(path6, resolved, state) {
58863
58990
  else
58864
58991
  parent = dirname3(parent);
58865
58992
  }
58866
- state.symlinks.set(path6, resolved);
58993
+ state.symlinks.set(path7, resolved);
58867
58994
  return depth > 1;
58868
58995
  }
58869
58996
  function isRecursiveUsingRealPaths(resolved, state) {
@@ -58919,23 +59046,23 @@ var walkAsync = (state, crawlPath, directoryPath, currentDepth, callback$1) => {
58919
59046
  state.queue.enqueue();
58920
59047
  if (currentDepth < 0)
58921
59048
  return state.queue.dequeue(null, state);
58922
- const { fs: fs7 } = state;
59049
+ const { fs: fs8 } = state;
58923
59050
  state.visited.push(crawlPath);
58924
59051
  state.counts.directories++;
58925
- fs7.readdir(crawlPath || ".", readdirOpts, (error43, entries = []) => {
59052
+ fs8.readdir(crawlPath || ".", readdirOpts, (error43, entries = []) => {
58926
59053
  callback$1(entries, directoryPath, currentDepth);
58927
59054
  state.queue.dequeue(state.options.suppressErrors ? null : error43, state);
58928
59055
  });
58929
59056
  };
58930
59057
  var walkSync = (state, crawlPath, directoryPath, currentDepth, callback$1) => {
58931
- const { fs: fs7 } = state;
59058
+ const { fs: fs8 } = state;
58932
59059
  if (currentDepth < 0)
58933
59060
  return;
58934
59061
  state.visited.push(crawlPath);
58935
59062
  state.counts.directories++;
58936
59063
  let entries = [];
58937
59064
  try {
58938
- entries = fs7.readdirSync(crawlPath || ".", readdirOpts);
59065
+ entries = fs8.readdirSync(crawlPath || ".", readdirOpts);
58939
59066
  } catch (e) {
58940
59067
  if (!state.options.suppressErrors)
58941
59068
  throw e;
@@ -59041,21 +59168,21 @@ var Walker = class {
59041
59168
  const filename = this.joinPath(entry.name, directoryPath);
59042
59169
  this.pushFile(filename, files, this.state.counts, filters);
59043
59170
  } else if (entry.isDirectory()) {
59044
- let path6 = joinDirectoryPath(entry.name, directoryPath, this.state.options.pathSeparator);
59045
- if (exclude && exclude(entry.name, path6))
59171
+ let path7 = joinDirectoryPath(entry.name, directoryPath, this.state.options.pathSeparator);
59172
+ if (exclude && exclude(entry.name, path7))
59046
59173
  continue;
59047
- this.pushDirectory(path6, paths, filters);
59048
- this.walkDirectory(this.state, path6, path6, depth - 1, this.walk);
59174
+ this.pushDirectory(path7, paths, filters);
59175
+ this.walkDirectory(this.state, path7, path7, depth - 1, this.walk);
59049
59176
  } else if (this.resolveSymlink && entry.isSymbolicLink()) {
59050
- let path6 = joinPathWithBasePath(entry.name, directoryPath);
59051
- this.resolveSymlink(path6, this.state, (stat, resolvedPath) => {
59177
+ let path7 = joinPathWithBasePath(entry.name, directoryPath);
59178
+ this.resolveSymlink(path7, this.state, (stat, resolvedPath) => {
59052
59179
  if (stat.isDirectory()) {
59053
59180
  resolvedPath = normalizePath(resolvedPath, this.state.options);
59054
- if (exclude && exclude(entry.name, useRealPaths ? resolvedPath : path6 + pathSeparator))
59181
+ if (exclude && exclude(entry.name, useRealPaths ? resolvedPath : path7 + pathSeparator))
59055
59182
  return;
59056
- this.walkDirectory(this.state, resolvedPath, useRealPaths ? resolvedPath : path6 + pathSeparator, depth - 1, this.walk);
59183
+ this.walkDirectory(this.state, resolvedPath, useRealPaths ? resolvedPath : path7 + pathSeparator, depth - 1, this.walk);
59057
59184
  } else {
59058
- resolvedPath = useRealPaths ? resolvedPath : path6;
59185
+ resolvedPath = useRealPaths ? resolvedPath : path7;
59059
59186
  const filename = basename2(resolvedPath);
59060
59187
  const directoryPath$1 = normalizePath(dirname3(resolvedPath), this.state.options);
59061
59188
  resolvedPath = this.joinPath(filename, directoryPath$1);
@@ -59215,7 +59342,7 @@ var Builder = class {
59215
59342
  isMatch = globFn(patterns, ...options);
59216
59343
  this.globCache[patterns.join("\x00")] = isMatch;
59217
59344
  }
59218
- this.options.filters.push((path6) => isMatch(path6));
59345
+ this.options.filters.push((path7) => isMatch(path7));
59219
59346
  return this;
59220
59347
  }
59221
59348
  };
@@ -59294,7 +59421,7 @@ function normalizePattern(pattern, expandDirectories, cwd2, props, isIgnore) {
59294
59421
  if (!result.endsWith("*") && expandDirectories)
59295
59422
  result += "/**";
59296
59423
  const escapedCwd = escapePath(cwd2);
59297
- if (path6.isAbsolute(result.replace(ESCAPING_BACKSLASHES, "")))
59424
+ if (path7.isAbsolute(result.replace(ESCAPING_BACKSLASHES, "")))
59298
59425
  result = posix.relative(escapedCwd, result);
59299
59426
  else
59300
59427
  result = posix.normalize(result);
@@ -59331,7 +59458,7 @@ function normalizePattern(pattern, expandDirectories, cwd2, props, isIgnore) {
59331
59458
  }
59332
59459
  props.depthOffset = newCommonPath.length;
59333
59460
  props.commonPath = newCommonPath;
59334
- props.root = newCommonPath.length > 0 ? path6.posix.join(cwd2, ...newCommonPath) : cwd2;
59461
+ props.root = newCommonPath.length > 0 ? path7.posix.join(cwd2, ...newCommonPath) : cwd2;
59335
59462
  }
59336
59463
  return result;
59337
59464
  }
@@ -59464,18 +59591,18 @@ function globSync(patternsOrOptions, options) {
59464
59591
  ...options,
59465
59592
  patterns: patternsOrOptions
59466
59593
  } : patternsOrOptions;
59467
- const cwd2 = opts.cwd ? path6.resolve(opts.cwd).replace(BACKSLASHES, "/") : process.cwd().replace(BACKSLASHES, "/");
59594
+ const cwd2 = opts.cwd ? path7.resolve(opts.cwd).replace(BACKSLASHES, "/") : process.cwd().replace(BACKSLASHES, "/");
59468
59595
  return crawl(opts, cwd2, true);
59469
59596
  }
59470
59597
 
59471
59598
  // src/utils/jsonl.ts
59472
59599
  import { promisify } from "util";
59473
- var readFile4 = promisify(fs7.readFile);
59474
- var readFileSync5 = fs7.readFileSync;
59475
- var statSync5 = fs7.statSync;
59600
+ var readFile4 = promisify(fs8.readFile);
59601
+ var readFileSync6 = fs8.readFileSync;
59602
+ var statSync5 = fs8.statSync;
59476
59603
  async function getSessionDuration(transcriptPath) {
59477
59604
  try {
59478
- if (!fs7.existsSync(transcriptPath)) {
59605
+ if (!fs8.existsSync(transcriptPath)) {
59479
59606
  return null;
59480
59607
  }
59481
59608
  const content = await readFile4(transcriptPath, "utf-8");
@@ -59527,7 +59654,7 @@ async function getSessionDuration(transcriptPath) {
59527
59654
  }
59528
59655
  async function getTokenMetrics(transcriptPath) {
59529
59656
  try {
59530
- if (!fs7.existsSync(transcriptPath)) {
59657
+ if (!fs8.existsSync(transcriptPath)) {
59531
59658
  return { inputTokens: 0, outputTokens: 0, cachedTokens: 0, totalTokens: 0, contextLength: 0 };
59532
59659
  }
59533
59660
  const content = await readFile4(transcriptPath, "utf-8");
@@ -59580,7 +59707,7 @@ function getBlockMetrics() {
59580
59707
  function findMostRecentBlockStartTime(rootDir, sessionDurationHours = 5) {
59581
59708
  const sessionDurationMs = sessionDurationHours * 60 * 60 * 1000;
59582
59709
  const now = new Date;
59583
- const pattern = path7.posix.join(rootDir.replace(/\\/g, "/"), "projects", "**", "*.jsonl");
59710
+ const pattern = path8.posix.join(rootDir.replace(/\\/g, "/"), "projects", "**", "*.jsonl");
59584
59711
  const files = globSync([pattern], {
59585
59712
  absolute: true,
59586
59713
  cwd: rootDir
@@ -59674,7 +59801,7 @@ function findMostRecentBlockStartTime(rootDir, sessionDurationHours = 5) {
59674
59801
  function getAllTimestampsFromFile(filePath) {
59675
59802
  const timestamps = [];
59676
59803
  try {
59677
- const content = readFileSync5(filePath, "utf-8");
59804
+ const content = readFileSync6(filePath, "utf-8");
59678
59805
  const lines = content.trim().split(`
59679
59806
  `).filter((line) => line.length > 0);
59680
59807
  for (const line of lines) {
@@ -59711,6 +59838,10 @@ function floorToHour(timestamp) {
59711
59838
  }
59712
59839
 
59713
59840
  // src/ccstatusline.ts
59841
+ var COMPACT_THRESHOLD = 80;
59842
+ function isCompactWidth(width) {
59843
+ return width !== null && width > 0 && width < COMPACT_THRESHOLD;
59844
+ }
59714
59845
  async function readStdin() {
59715
59846
  if (process.stdin.isTTY) {
59716
59847
  return null;
@@ -59753,30 +59884,37 @@ async function renderMultipleLines(data) {
59753
59884
  if (hasBlockTimer) {
59754
59885
  blockMetrics = getBlockMetrics();
59755
59886
  }
59887
+ const terminalWidth = getTerminalWidth();
59756
59888
  const context = {
59757
59889
  data,
59758
59890
  tokenMetrics,
59759
59891
  sessionDuration,
59760
59892
  blockMetrics,
59893
+ terminalWidth,
59761
59894
  isPreview: false
59762
59895
  };
59763
59896
  const preRenderedLines = preRenderAllWidgets(lines, settings, context);
59764
- const preCalculatedMaxWidths = calculateMaxWidthsFromPreRendered(preRenderedLines, settings);
59765
- let globalSeparatorIndex = 0;
59766
- for (let i = 0;i < lines.length; i++) {
59767
- const lineItems = lines[i];
59768
- if (lineItems && lineItems.length > 0) {
59769
- const lineContext = { ...context, lineIndex: i, globalSeparatorIndex };
59770
- const preRenderedWidgets = preRenderedLines[i] ?? [];
59771
- const line = renderStatusLine(lineItems, settings, lineContext, preRenderedWidgets, preCalculatedMaxWidths);
59772
- const strippedLine = line.replace(/\x1b\[[0-9;]*m/g, "").trim();
59773
- if (strippedLine.length > 0) {
59774
- const nonMergedWidgets = lineItems.filter((_, idx) => idx === lineItems.length - 1 || !lineItems[idx]?.merge);
59775
- if (nonMergedWidgets.length > 1)
59776
- globalSeparatorIndex += nonMergedWidgets.length - 1;
59777
- let outputLine = line.replace(/ /g, "ย ");
59778
- outputLine = "\x1B[0m" + outputLine;
59779
- console.log(outputLine);
59897
+ const compact = isCompactWidth(terminalWidth);
59898
+ if (compact && terminalWidth) {
59899
+ renderCompactOutput(preRenderedLines, settings, terminalWidth - 6);
59900
+ } else {
59901
+ const preCalculatedMaxWidths = calculateMaxWidthsFromPreRendered(preRenderedLines, settings);
59902
+ let globalSeparatorIndex = 0;
59903
+ for (let i = 0;i < lines.length; i++) {
59904
+ const lineItems = lines[i];
59905
+ if (lineItems && lineItems.length > 0) {
59906
+ const lineContext = { ...context, lineIndex: i, globalSeparatorIndex };
59907
+ const preRenderedWidgets = preRenderedLines[i] ?? [];
59908
+ const line = renderStatusLine(lineItems, settings, lineContext, preRenderedWidgets, preCalculatedMaxWidths);
59909
+ const strippedLine = line.replace(/\x1b\[[0-9;]*m/g, "").trim();
59910
+ if (strippedLine.length > 0) {
59911
+ const nonMergedWidgets = lineItems.filter((_, idx) => idx === lineItems.length - 1 || !lineItems[idx]?.merge);
59912
+ if (nonMergedWidgets.length > 1)
59913
+ globalSeparatorIndex += nonMergedWidgets.length - 1;
59914
+ let outputLine = line.replace(/ /g, "ย ");
59915
+ outputLine = "\x1B[0m" + outputLine;
59916
+ console.log(outputLine);
59917
+ }
59780
59918
  }
59781
59919
  }
59782
59920
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccstatusline-usage",
3
- "version": "2.1.2",
3
+ "version": "2.1.4",
4
4
  "description": "A customizable status line formatter for Claude Code CLI",
5
5
  "module": "src/ccstatusline.ts",
6
6
  "type": "module",