ccstatusline 1.0.14 → 1.0.15

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
@@ -55,7 +55,7 @@ Once configured, ccstatusline automatically formats your Claude Code status line
55
55
  - **Context Percentage** - Shows percentage of context limit used
56
56
  - **Terminal Width** - Shows detected terminal width (for debugging)
57
57
  - **Custom Text** - Add your own custom text to the status line
58
- - **Custom Command** - Execute shell commands and display their output (refreshes every 5 seconds)
58
+ - **Custom Command** - Execute shell commands and display their output (refreshes whenever the statusline is updated by Claude Code)
59
59
  - **Separator** - Visual divider between items (customizable: |, -, comma, space)
60
60
  - **Flex Separator** - Expands to fill available space
61
61
 
@@ -76,6 +76,9 @@ Once configured, ccstatusline automatically formats your Claude Code status line
76
76
  - **c** - Clear entire line
77
77
  - **r** - Toggle raw value mode (no labels)
78
78
  - **e** - Edit value (for custom-text and custom-command items)
79
+ - **w** - Set max width (for custom-command items)
80
+ - **t** - Set timeout in milliseconds (for custom-command items)
81
+ - **p** - Toggle preserve colors (for custom-command items)
79
82
  - **Space** - Change separator character (for separator items)
80
83
  - **ESC** - Go back
81
84
 
@@ -107,16 +110,34 @@ Add static text to your status line. Perfect for:
107
110
 
108
111
  #### Custom Command Widget
109
112
  Execute shell commands and display their output dynamically:
110
- - Refreshes automatically every 5 seconds
113
+ - Refreshes whenever the statusline is updated by Claude Code
114
+ - Receives the full Claude Code JSON data via stdin (model info, session ID, transcript path, etc.)
111
115
  - Displays command output inline in your status line
116
+ - Configurable timeout (default: 1000ms)
112
117
  - Examples:
113
118
  - `pwd | xargs basename` - Show current directory name
114
119
  - `node -v` - Display Node.js version
115
120
  - `git rev-parse --short HEAD` - Show current commit hash
116
121
  - `date +%H:%M` - Display current time
117
122
  - `curl -s wttr.in?format="%t"` - Show current temperature
123
+ - `npx -y ccusage statusline` - Display Claude usage metrics (set timeout: 5000ms)
118
124
 
119
- > ⚠️ **Note:** Commands should complete quickly (<1s) to avoid delays. Long-running commands will be killed after timeout.
125
+ > ⚠️ **Note:** Commands should complete quickly to avoid delays. Long-running commands will be killed after the configured timeout. If you're not seeing output from your custom command, try increasing the timeout value (press 't' in the editor).
126
+
127
+ > 💡 **Tip:** Custom commands can be other Claude Code compatible status line formatters! They receive the same JSON via stdin that ccstatusline receives from Claude Code, allowing you to chain or combine multiple status line tools.
128
+
129
+ ### 🔗 Integration Example: ccusage
130
+
131
+ [ccusage](https://github.com/samuelint/ccusage) is a tool that tracks and displays Claude Code usage metrics. You can integrate it directly into your status line:
132
+
133
+ 1. Add a Custom Command widget
134
+ 2. Set command: `npx -y ccusage statusline`
135
+ 3. Set timeout: `5000` (5 seconds for initial download)
136
+ 4. Enable "preserve colors" to keep ccusage's color formatting
137
+
138
+ ![ccusage integration](https://raw.githubusercontent.com/sirmalloc/ccstatusline/main/screenshots/ccusage.png)
139
+
140
+ The command receives Claude Code's JSON data via stdin, allowing ccusage to access session information, model details, and transcript data for accurate usage tracking.
120
141
 
121
142
  ### ✂️ Smart Truncation
122
143
 
@@ -182,7 +182,7 @@ async function getExistingStatusLine() {
182
182
  // src/tui.tsx
183
183
  import * as fs3 from "fs";
184
184
  import * as path3 from "path";
185
- var __dirname = "/Users/sirmalloc/Desktop/ccstatusline/src";
185
+ var __dirname = "/Users/sirmalloc/Projects/Personal/ccstatusline/src";
186
186
  import { jsxDEV, Fragment } from "react/jsx-dev-runtime";
187
187
  function getPackageVersion() {
188
188
  try {
@@ -529,6 +529,8 @@ var ItemsEditor = ({ items, onUpdate, onBack, lineNumber }) => {
529
529
  const [commandCursorPos, setCommandCursorPos] = useState(0);
530
530
  const [editingMaxWidth, setEditingMaxWidth] = useState(false);
531
531
  const [maxWidthInput, setMaxWidthInput] = useState("");
532
+ const [editingTimeout, setEditingTimeout] = useState(false);
533
+ const [timeoutInput, setTimeoutInput] = useState("");
532
534
  const separatorChars = ["|", "-", ",", " "];
533
535
  useInput((input, key) => {
534
536
  if (editingText) {
@@ -619,6 +621,30 @@ var ItemsEditor = ({ items, onUpdate, onBack, lineNumber }) => {
619
621
  } else if (input && /\d/.test(input)) {
620
622
  setMaxWidthInput(maxWidthInput + input);
621
623
  }
624
+ } else if (editingTimeout) {
625
+ if (key.return) {
626
+ const currentItem2 = items[selectedIndex];
627
+ if (currentItem2) {
628
+ const timeout = parseInt(timeoutInput, 10);
629
+ const newItems = [...items];
630
+ if (!isNaN(timeout) && timeout > 0) {
631
+ newItems[selectedIndex] = { ...currentItem2, timeout };
632
+ } else {
633
+ const { timeout: _, ...rest } = currentItem2;
634
+ newItems[selectedIndex] = rest;
635
+ }
636
+ onUpdate(newItems);
637
+ }
638
+ setEditingTimeout(false);
639
+ setTimeoutInput("");
640
+ } else if (key.escape) {
641
+ setEditingTimeout(false);
642
+ setTimeoutInput("");
643
+ } else if (key.backspace || key.delete) {
644
+ setTimeoutInput(timeoutInput.slice(0, -1));
645
+ } else if (input && /\d/.test(input)) {
646
+ setTimeoutInput(timeoutInput + input);
647
+ }
622
648
  } else if (moveMode) {
623
649
  if (key.upArrow && selectedIndex > 0) {
624
650
  const newItems = [...items];
@@ -771,6 +797,12 @@ var ItemsEditor = ({ items, onUpdate, onBack, lineNumber }) => {
771
797
  setMaxWidthInput(currentItem2.maxWidth ? currentItem2.maxWidth.toString() : "");
772
798
  setEditingMaxWidth(true);
773
799
  }
800
+ } else if (input === "t" && items.length > 0) {
801
+ const currentItem2 = items[selectedIndex];
802
+ if (currentItem2 && currentItem2.type === "custom-command") {
803
+ setTimeoutInput(currentItem2.timeout ? currentItem2.timeout.toString() : "1000");
804
+ setEditingTimeout(true);
805
+ }
774
806
  } else if (input === "p" && items.length > 0) {
775
807
  const currentItem2 = items[selectedIndex];
776
808
  if (currentItem2 && currentItem2.type === "custom-command") {
@@ -881,7 +913,7 @@ var ItemsEditor = ({ items, onUpdate, onBack, lineNumber }) => {
881
913
  helpText += ", (e)dit text";
882
914
  }
883
915
  if (isCustomCommand) {
884
- helpText += ", (e)dit cmd, (w)idth, (p)reserve colors";
916
+ helpText += ", (e)dit cmd, (w)idth, (t)imeout, (p)reserve colors";
885
917
  }
886
918
  helpText += ", Enter to move, (a)dd, (i)nsert, (d)elete, (c)lear line";
887
919
  if (canToggleRaw) {
@@ -957,6 +989,20 @@ var ItemsEditor = ({ items, onUpdate, onBack, lineNumber }) => {
957
989
  children: "Press Enter to save, ESC to cancel"
958
990
  }, undefined, false, undefined, this)
959
991
  ]
992
+ }, undefined, true, undefined, this) : editingTimeout ? /* @__PURE__ */ jsxDEV(Box, {
993
+ flexDirection: "column",
994
+ children: [
995
+ /* @__PURE__ */ jsxDEV(Text, {
996
+ children: [
997
+ "Enter timeout in milliseconds (default 1000): ",
998
+ timeoutInput
999
+ ]
1000
+ }, undefined, true, undefined, this),
1001
+ /* @__PURE__ */ jsxDEV(Text, {
1002
+ dimColor: true,
1003
+ children: "Press Enter to save, ESC to cancel"
1004
+ }, undefined, false, undefined, this)
1005
+ ]
960
1006
  }, undefined, true, undefined, this) : moveMode ? /* @__PURE__ */ jsxDEV(Text, {
961
1007
  dimColor: true,
962
1008
  children: "↑↓ to move item, ESC or Enter to exit move mode"
@@ -1118,8 +1164,11 @@ var ColorMenu = ({ items, onUpdate, onBack }) => {
1118
1164
  return "Version";
1119
1165
  case "custom-text":
1120
1166
  return `Custom Text (${item.customText || "Empty"})`;
1121
- case "custom-command":
1122
- return `Custom Command (${item.commandPath ? item.commandPath.substring(0, 20) + (item.commandPath.length > 20 ? "..." : "") : "No command"})`;
1167
+ case "custom-command": {
1168
+ const cmd = item.commandPath ? item.commandPath.substring(0, 20) + (item.commandPath.length > 20 ? "..." : "") : "No command";
1169
+ const timeout = item.timeout ? ` ${item.timeout}ms` : "";
1170
+ return `Custom Command (${cmd}${timeout})`;
1171
+ }
1123
1172
  default:
1124
1173
  return item.type;
1125
1174
  }
@@ -1930,10 +1979,12 @@ function renderSingleLine2(items, settings, data, tokenMetrics, sessionDuration)
1930
1979
  case "custom-command":
1931
1980
  if (item.commandPath) {
1932
1981
  try {
1982
+ const timeout = item.timeout || 1000;
1933
1983
  const output = execSync2(item.commandPath, {
1934
1984
  encoding: "utf8",
1985
+ input: JSON.stringify(data),
1935
1986
  stdio: ["pipe", "pipe", "ignore"],
1936
- timeout: 1000
1987
+ timeout
1937
1988
  }).trim();
1938
1989
  if (output) {
1939
1990
  let finalOutput = output;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccstatusline",
3
- "version": "1.0.14",
3
+ "version": "1.0.15",
4
4
  "description": "A customizable status line formatter for Claude Code CLI",
5
5
  "module": "src/ccstatusline.ts",
6
6
  "type": "module",