ccstatusline 2.0.19 → 2.0.21

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
@@ -125,6 +125,7 @@
125
125
  - **🖥️ Interactive TUI** - Built-in configuration interface using React/Ink
126
126
  - **⚙️ Global Options** - Apply consistent formatting across all widgets (padding, separators, bold, background)
127
127
  - **🚀 Cross-platform** - Works seamlessly with both Bun and Node.js
128
+ - **🔧 Flexible Configuration** - Supports custom Claude Code config directory via `CLAUDE_CONFIG_DIR` environment variable
128
129
  - **📏 Smart Width Detection** - Automatically adapts to terminal width with flex separators
129
130
  - **⚡ Zero Config** - Sensible defaults that work out of the box
130
131
 
@@ -155,6 +156,15 @@ The interactive configuration tool provides a terminal UI where you can:
155
156
 
156
157
  > 💡 **Tip:** Your settings are automatically saved to `~/.config/ccstatusline/settings.json`
157
158
 
159
+ > 🔧 **Custom Claude Config:** If your Claude Code configuration is in a non-standard location, set the `CLAUDE_CONFIG_DIR` environment variable:
160
+ > ```bash
161
+ > # Linux/macOS
162
+ > export CLAUDE_CONFIG_DIR=/custom/path/to/.claude
163
+ >
164
+ > # Windows PowerShell
165
+ > $env:CLAUDE_CONFIG_DIR="C:\custom\path\.claude"
166
+ > ```
167
+
158
168
  ---
159
169
 
160
170
  ## 🪟 Windows Support
@@ -294,7 +304,11 @@ For the best experience, configure Windows Terminal with these recommended setti
294
304
  #### Claude Code Integration
295
305
  Configure ccstatusline in your Claude Code settings:
296
306
 
297
- **For Bun users** (Windows: `%USERPROFILE%\.claude\settings.json`):
307
+ **Settings Location:**
308
+ - Default: `~/.claude/settings.json` (Windows: `%USERPROFILE%\.claude\settings.json`)
309
+ - Custom: Set `CLAUDE_CONFIG_DIR` environment variable to use a different directory
310
+
311
+ **For Bun users**:
298
312
  ```json
299
313
  {
300
314
  "statusLine": "bunx ccstatusline@latest"
@@ -308,6 +322,8 @@ Configure ccstatusline in your Claude Code settings:
308
322
  }
309
323
  ```
310
324
 
325
+ > 💡 **Custom Config Directory:** If you use a non-standard Claude Code configuration directory, set the `CLAUDE_CONFIG_DIR` environment variable before running ccstatusline. The tool will automatically detect and use your custom location.
326
+
311
327
  ### Performance on Windows
312
328
 
313
329
  ccstatusline is optimized for Windows performance:
@@ -561,7 +577,7 @@ ccstatusline/
561
577
  │ │ ├── renderer.ts # Core rendering logic
562
578
  │ │ ├── powerline.ts # Powerline font utilities
563
579
  │ │ ├── colors.ts # Color definitions
564
- │ │ └── claude-settings.ts # Claude Code integration
580
+ │ │ └── claude-settings.ts # Claude Code integration (supports CLAUDE_CONFIG_DIR)
565
581
  │ └── types/ # TypeScript type definitions
566
582
  │ ├── Settings.ts
567
583
  │ ├── Widget.ts
@@ -39283,31 +39283,62 @@ import * as path from "path";
39283
39283
  var readFile = fs2.promises.readFile;
39284
39284
  var writeFile = fs2.promises.writeFile;
39285
39285
  var mkdir = fs2.promises.mkdir;
39286
- var CLAUDE_SETTINGS_PATH = path.join(os2.homedir(), ".claude", "settings.json");
39286
+ var CCSTATUSLINE_COMMANDS = {
39287
+ NPM: "npx -y ccstatusline@latest",
39288
+ BUNX: "bunx -y ccstatusline@latest",
39289
+ SELF_MANAGED: "ccstatusline"
39290
+ };
39291
+ function getClaudeConfigDir() {
39292
+ const envConfigDir = process.env.CLAUDE_CONFIG_DIR;
39293
+ if (envConfigDir) {
39294
+ try {
39295
+ const resolvedPath = path.resolve(envConfigDir);
39296
+ if (fs2.existsSync(resolvedPath)) {
39297
+ const stats = fs2.statSync(resolvedPath);
39298
+ if (stats.isDirectory()) {
39299
+ return resolvedPath;
39300
+ }
39301
+ } else {
39302
+ return resolvedPath;
39303
+ }
39304
+ } catch {}
39305
+ }
39306
+ return path.join(os2.homedir(), ".claude");
39307
+ }
39308
+ function getClaudeSettingsPath() {
39309
+ return path.join(getClaudeConfigDir(), "settings.json");
39310
+ }
39287
39311
  async function loadClaudeSettings() {
39288
39312
  try {
39289
- if (!fs2.existsSync(CLAUDE_SETTINGS_PATH)) {
39313
+ const settingsPath = getClaudeSettingsPath();
39314
+ if (!fs2.existsSync(settingsPath)) {
39290
39315
  return {};
39291
39316
  }
39292
- const content = await readFile(CLAUDE_SETTINGS_PATH, "utf-8");
39317
+ const content = await readFile(settingsPath, "utf-8");
39293
39318
  return JSON.parse(content);
39294
39319
  } catch {
39295
39320
  return {};
39296
39321
  }
39297
39322
  }
39298
39323
  async function saveClaudeSettings(settings) {
39299
- const dir = path.dirname(CLAUDE_SETTINGS_PATH);
39324
+ const settingsPath = getClaudeSettingsPath();
39325
+ const dir = path.dirname(settingsPath);
39300
39326
  await mkdir(dir, { recursive: true });
39301
- await writeFile(CLAUDE_SETTINGS_PATH, JSON.stringify(settings, null, 2), "utf-8");
39327
+ await writeFile(settingsPath, JSON.stringify(settings, null, 2), "utf-8");
39302
39328
  }
39303
39329
  async function isInstalled() {
39304
39330
  const settings = await loadClaudeSettings();
39305
- const validCommands = ["npx -y ccstatusline@latest", "bunx -y ccstatusline@latest"];
39331
+ const validCommands = [
39332
+ CCSTATUSLINE_COMMANDS.NPM,
39333
+ CCSTATUSLINE_COMMANDS.BUNX,
39334
+ CCSTATUSLINE_COMMANDS.SELF_MANAGED
39335
+ ];
39306
39336
  return validCommands.includes(settings.statusLine?.command ?? "") && (settings.statusLine?.padding === 0 || settings.statusLine?.padding === undefined);
39307
39337
  }
39308
39338
  function isBunxAvailable() {
39309
39339
  try {
39310
- execSync("which bunx", { stdio: "ignore" });
39340
+ const command = process.platform === "win32" ? "where bunx" : "which bunx";
39341
+ execSync(command, { stdio: "ignore" });
39311
39342
  return true;
39312
39343
  } catch {
39313
39344
  return false;
@@ -39317,7 +39348,7 @@ async function installStatusLine(useBunx = false) {
39317
39348
  const settings = await loadClaudeSettings();
39318
39349
  settings.statusLine = {
39319
39350
  type: "command",
39320
- command: useBunx ? "bunx -y ccstatusline@latest" : "npx -y ccstatusline@latest",
39351
+ command: useBunx ? CCSTATUSLINE_COMMANDS.BUNX : CCSTATUSLINE_COMMANDS.NPM,
39321
39352
  padding: 0
39322
39353
  };
39323
39354
  await saveClaudeSettings(settings);
@@ -51374,7 +51405,7 @@ import { execSync as execSync3 } from "child_process";
51374
51405
  import * as fs5 from "fs";
51375
51406
  import * as path4 from "path";
51376
51407
  var __dirname = "/Users/sirmalloc/Projects/Personal/ccstatusline/src/utils";
51377
- var PACKAGE_VERSION = "2.0.19";
51408
+ var PACKAGE_VERSION = "2.0.21";
51378
51409
  function getPackageVersion() {
51379
51410
  if (/^\d+\.\d+\.\d+/.test(PACKAGE_VERSION)) {
51380
51411
  return PACKAGE_VERSION;
@@ -55377,8 +55408,12 @@ var InstallMenu = ({
55377
55408
  marginTop: 2,
55378
55409
  children: /* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Text, {
55379
55410
  dimColor: true,
55380
- children: "The selected command will be written to ~/.claude/settings.json"
55381
- }, undefined, false, undefined, this)
55411
+ children: [
55412
+ "The selected command will be written to",
55413
+ " ",
55414
+ getClaudeSettingsPath()
55415
+ ]
55416
+ }, undefined, true, undefined, this)
55382
55417
  }, undefined, false, undefined, this),
55383
55418
  /* @__PURE__ */ jsx_dev_runtime7.jsxDEV(Box_default, {
55384
55419
  marginTop: 1,
@@ -57741,6 +57776,41 @@ var App2 = () => {
57741
57776
  })();
57742
57777
  }
57743
57778
  });
57779
+ const handleInstallSelection = import_react45.useCallback((command, displayName, useBunx) => {
57780
+ getExistingStatusLine().then((existing) => {
57781
+ const isAlreadyInstalled = [CCSTATUSLINE_COMMANDS.NPM, CCSTATUSLINE_COMMANDS.BUNX, CCSTATUSLINE_COMMANDS.SELF_MANAGED].includes(existing ?? "");
57782
+ let message;
57783
+ if (existing && !isAlreadyInstalled) {
57784
+ message = `This will modify ${getClaudeSettingsPath()}
57785
+
57786
+ A status line is already configured: "${existing}"
57787
+ Replace it with ${command}?`;
57788
+ } else if (isAlreadyInstalled) {
57789
+ message = `ccstatusline is already installed in ${getClaudeSettingsPath()}
57790
+ Update it with ${command}?`;
57791
+ } else {
57792
+ message = `This will modify ${getClaudeSettingsPath()} to add ccstatusline with ${displayName}.
57793
+ Continue?`;
57794
+ }
57795
+ setConfirmDialog({
57796
+ message,
57797
+ action: async () => {
57798
+ await installStatusLine(useBunx);
57799
+ setIsClaudeInstalled(true);
57800
+ setExistingStatusLine(command);
57801
+ setScreen("main");
57802
+ setConfirmDialog(null);
57803
+ }
57804
+ });
57805
+ setScreen("confirm");
57806
+ });
57807
+ }, []);
57808
+ const handleNpxInstall = import_react45.useCallback(() => {
57809
+ handleInstallSelection(CCSTATUSLINE_COMMANDS.NPM, "npx", false);
57810
+ }, [handleInstallSelection]);
57811
+ const handleBunxInstall = import_react45.useCallback(() => {
57812
+ handleInstallSelection(CCSTATUSLINE_COMMANDS.BUNX, "bunx", true);
57813
+ }, [handleInstallSelection]);
57744
57814
  if (!settings) {
57745
57815
  return /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Text, {
57746
57816
  children: "Loading settings..."
@@ -57749,7 +57819,7 @@ var App2 = () => {
57749
57819
  const handleInstallUninstall = () => {
57750
57820
  if (isClaudeInstalled) {
57751
57821
  setConfirmDialog({
57752
- message: "This will remove ccstatusline from ~/.claude/settings.json. Continue?",
57822
+ message: `This will remove ccstatusline from ${getClaudeSettingsPath()}. Continue?`,
57753
57823
  action: async () => {
57754
57824
  await uninstallStatusLine();
57755
57825
  setIsClaudeInstalled(false);
@@ -57963,64 +58033,8 @@ var App2 = () => {
57963
58033
  screen === "install" && /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(InstallMenu, {
57964
58034
  bunxAvailable: isBunxAvailable(),
57965
58035
  existingStatusLine,
57966
- onSelectNpx: () => {
57967
- getExistingStatusLine().then((existing) => {
57968
- const isAlreadyInstalled = ["npx -y ccstatusline@latest", "bunx -y ccstatusline@latest"].includes(existing ?? "");
57969
- let message;
57970
- if (existing && !isAlreadyInstalled) {
57971
- message = `This will modify ~/.claude/settings.json
57972
-
57973
- A status line is already configured: "${existing}"
57974
- Replace it with npx -y ccstatusline@latest?`;
57975
- } else if (isAlreadyInstalled) {
57976
- message = `ccstatusline is already installed in ~/.claude/settings.json
57977
- Update it with npx -y ccstatusline@latest?`;
57978
- } else {
57979
- message = `This will modify ~/.claude/settings.json to add ccstatusline with npx.
57980
- Continue?`;
57981
- }
57982
- setConfirmDialog({
57983
- message,
57984
- action: async () => {
57985
- await installStatusLine(false);
57986
- setIsClaudeInstalled(true);
57987
- setExistingStatusLine("npx -y ccstatusline@latest");
57988
- setScreen("main");
57989
- setConfirmDialog(null);
57990
- }
57991
- });
57992
- setScreen("confirm");
57993
- });
57994
- },
57995
- onSelectBunx: () => {
57996
- getExistingStatusLine().then((existing) => {
57997
- const isAlreadyInstalled = ["npx -y ccstatusline@latest", "bunx -y ccstatusline@latest"].includes(existing ?? "");
57998
- let message;
57999
- if (existing && !isAlreadyInstalled) {
58000
- message = `This will modify ~/.claude/settings.json
58001
-
58002
- A status line is already configured: "${existing}"
58003
- Replace it with bunx -y ccstatusline@latest?`;
58004
- } else if (isAlreadyInstalled) {
58005
- message = `ccstatusline is already installed in ~/.claude/settings.json
58006
- Update it with bunx -y ccstatusline@latest?`;
58007
- } else {
58008
- message = `This will modify ~/.claude/settings.json to add ccstatusline with bunx.
58009
- Continue?`;
58010
- }
58011
- setConfirmDialog({
58012
- message,
58013
- action: async () => {
58014
- await installStatusLine(true);
58015
- setIsClaudeInstalled(true);
58016
- setExistingStatusLine("bunx -y ccstatusline@latest");
58017
- setScreen("main");
58018
- setConfirmDialog(null);
58019
- }
58020
- });
58021
- setScreen("confirm");
58022
- });
58023
- },
58036
+ onSelectNpx: handleNpxInstall,
58037
+ onSelectBunx: handleBunxInstall,
58024
58038
  onCancel: () => {
58025
58039
  setScreen("main");
58026
58040
  }
@@ -58095,7 +58109,7 @@ import path5, { posix } from "path";
58095
58109
 
58096
58110
  // node_modules/fdir/dist/index.mjs
58097
58111
  import { createRequire as createRequire2 } from "module";
58098
- import { basename as basename2, dirname as dirname2, normalize, relative, resolve, sep } from "path";
58112
+ import { basename as basename2, dirname as dirname2, normalize, relative, resolve as resolve2, sep } from "path";
58099
58113
  import * as nativeFs from "fs";
58100
58114
  var __require2 = /* @__PURE__ */ createRequire2(import.meta.url);
58101
58115
  function cleanPath(path5) {
@@ -58116,7 +58130,7 @@ function normalizePath(path5, options) {
58116
58130
  const { resolvePaths, normalizePath: normalizePath$1, pathSeparator } = options;
58117
58131
  const pathNeedsCleaning = process.platform === "win32" && path5.includes("/") || path5.startsWith(".");
58118
58132
  if (resolvePaths)
58119
- path5 = resolve(path5);
58133
+ path5 = resolve2(path5);
58120
58134
  if (normalizePath$1 || pathNeedsCleaning)
58121
58135
  path5 = cleanPath(path5);
58122
58136
  if (path5 === ".")
@@ -58878,7 +58892,7 @@ function globSync(patternsOrOptions, options) {
58878
58892
  import { promisify } from "util";
58879
58893
  var readFile4 = promisify(fs6.readFile);
58880
58894
  var readFileSync4 = fs6.readFileSync;
58881
- var statSync3 = fs6.statSync;
58895
+ var statSync4 = fs6.statSync;
58882
58896
  async function getSessionDuration(transcriptPath) {
58883
58897
  try {
58884
58898
  if (!fs6.existsSync(transcriptPath)) {
@@ -58953,7 +58967,7 @@ async function getTokenMetrics(transcriptPath) {
58953
58967
  outputTokens += data.message.usage.output_tokens || 0;
58954
58968
  cachedTokens += data.message.usage.cache_read_input_tokens ?? 0;
58955
58969
  cachedTokens += data.message.usage.cache_creation_input_tokens ?? 0;
58956
- if (data.isSidechain !== true && data.timestamp) {
58970
+ if (data.isSidechain !== true && data.timestamp && !data.isApiErrorMessage) {
58957
58971
  const entryTime = new Date(data.timestamp);
58958
58972
  if (!mostRecentTimestamp || entryTime > mostRecentTimestamp) {
58959
58973
  mostRecentTimestamp = entryTime;
@@ -59022,7 +59036,7 @@ function findMostRecentBlockStartTime(rootDir, sessionDurationHours = 5) {
59022
59036
  if (files.length === 0)
59023
59037
  return null;
59024
59038
  const filesWithStats = files.map((file2) => {
59025
- const stats = statSync3(file2);
59039
+ const stats = statSync4(file2);
59026
59040
  return { file: file2, mtime: stats.mtime };
59027
59041
  });
59028
59042
  filesWithStats.sort((a, b) => b.mtime.getTime() - a.mtime.getTime());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccstatusline",
3
- "version": "2.0.19",
3
+ "version": "2.0.21",
4
4
  "description": "A customizable status line formatter for Claude Code CLI",
5
5
  "module": "src/ccstatusline.ts",
6
6
  "type": "module",
@@ -12,7 +12,7 @@
12
12
  ],
13
13
  "scripts": {
14
14
  "start": "bun run src/ccstatusline.ts",
15
- "build": "rm -rf dist/* && bun build src/ccstatusline.ts --target=node --outfile=dist/ccstatusline.js --target-version=14",
15
+ "build": "rm -rf dist/* ; bun build src/ccstatusline.ts --target=node --outfile=dist/ccstatusline.js --target-version=14",
16
16
  "postbuild": "bun run scripts/replace-version.ts",
17
17
  "example": "cat scripts/payload.example.json | bun start",
18
18
  "prepublishOnly": "bun run build",
@@ -70,4 +70,4 @@
70
70
  "patchedDependencies": {
71
71
  "ink@6.2.0": "patches/ink@6.2.0.patch"
72
72
  }
73
- }
73
+ }