mirror of
https://github.com/tiennm99/ccstatusline.git
synced 2026-10-14 06:17:28 +00:00
Updated custom commands to receive statusline json via stdin, added configurable timeout to custom commands
This commit is contained in:
1 parent
2f37b389e7
commit
67567e5d5e
6 files changed
+76
-6
No files matched your search
@@ -76,6 +76,9 @@ Once configured, ccstatusline automatically formats your Claude Code status line
|
||||
- **c** - Clear entire line
|
||||
- **r** - Toggle raw value mode (no labels)
|
||||
- **e** - Edit value (for custom-text and custom-command items)
|
||||
- **w** - Set max width (for custom-command items)
|
||||
- **t** - Set timeout in milliseconds (for custom-command items)
|
||||
- **p** - Toggle preserve colors (for custom-command items)
|
||||
- **Space** - Change separator character (for separator items)
|
||||
- **ESC** - Go back
|
||||
|
||||
@@ -108,15 +111,33 @@ Add static text to your status line. Perfect for:
|
||||
#### Custom Command Widget
|
||||
Execute shell commands and display their output dynamically:
|
||||
- Refreshes whenever the statusline is updated by Claude Code
|
||||
- Receives the full Claude Code JSON data via stdin (model info, session ID, transcript path, etc.)
|
||||
- Displays command output inline in your status line
|
||||
- Configurable timeout (default: 1000ms)
|
||||
- Examples:
|
||||
- `pwd | xargs basename` - Show current directory name
|
||||
- `node -v` - Display Node.js version
|
||||
- `git rev-parse --short HEAD` - Show current commit hash
|
||||
- `date +%H:%M` - Display current time
|
||||
- `curl -s wttr.in?format="%t"` - Show current temperature
|
||||
- `npx -y ccusage statusline` - Display Claude usage metrics (set timeout: 5000ms)
|
||||
|
||||
> ⚠️ **Note:** Commands should complete quickly (<1s) to avoid delays. Long-running commands will be killed after timeout.
|
||||
> ⚠️ **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).
|
||||
|
||||
> 💡 **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.
|
||||
|
||||
### 🔗 Integration Example: ccusage
|
||||
|
||||
[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:
|
||||
|
||||
1. Add a Custom Command widget
|
||||
2. Set command: `npx -y ccusage statusline`
|
||||
3. Set timeout: `5000` (5 seconds for initial download)
|
||||
4. Enable "preserve colors" to keep ccusage's color formatting
|
||||
|
||||

|
||||
|
||||
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.
|
||||
|
||||
### ✂️ Smart Truncation
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ccstatusline",
|
||||
"version": "1.0.14",
|
||||
"version": "1.0.15",
|
||||
"description": "A customizable status line formatter for Claude Code CLI",
|
||||
"module": "src/ccstatusline.ts",
|
||||
"type": "module",
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 25 KiB |
+4
-2
@@ -480,11 +480,13 @@ function renderSingleLine(items: StatusItem[], settings: any, data: StatusJSON,
|
||||
case 'custom-command':
|
||||
if (item.commandPath) {
|
||||
try {
|
||||
// Execute the command with a timeout
|
||||
// Execute the command with JSON input via stdin
|
||||
const timeout = item.timeout || 1000; // Default to 1000ms if not specified
|
||||
const output = execSync(item.commandPath, {
|
||||
encoding: 'utf8',
|
||||
input: JSON.stringify(data),
|
||||
stdio: ['pipe', 'pipe', 'ignore'],
|
||||
timeout: 1000 // 1 second timeout
|
||||
timeout: timeout
|
||||
}).trim();
|
||||
|
||||
if (output) {
|
||||
|
||||
@@ -21,6 +21,7 @@ export interface StatusItem {
|
||||
commandPath?: string; // For custom-command type - the command to execute
|
||||
maxWidth?: number; // For custom-command type - max width of output
|
||||
preserveColors?: boolean; // For custom-command type - preserve ANSI colors from command output
|
||||
timeout?: number; // For custom-command type - timeout in milliseconds (default: 1000)
|
||||
}
|
||||
|
||||
export type FlexMode = 'full' | 'full-minus-40' | 'full-until-compact';
|
||||
|
||||
+48
-2
@@ -416,6 +416,8 @@ const ItemsEditor: React.FC<ItemsEditorProps> = ({ items, onUpdate, onBack, line
|
||||
const [commandCursorPos, setCommandCursorPos] = useState(0);
|
||||
const [editingMaxWidth, setEditingMaxWidth] = useState(false);
|
||||
const [maxWidthInput, setMaxWidthInput] = useState('');
|
||||
const [editingTimeout, setEditingTimeout] = useState(false);
|
||||
const [timeoutInput, setTimeoutInput] = useState('');
|
||||
const separatorChars = ['|', '-', ',', ' '];
|
||||
|
||||
useInput((input, key) => {
|
||||
@@ -521,6 +523,34 @@ const ItemsEditor: React.FC<ItemsEditorProps> = ({ items, onUpdate, onBack, line
|
||||
} else if (input && /\d/.test(input)) {
|
||||
setMaxWidthInput(maxWidthInput + input);
|
||||
}
|
||||
} else if (editingTimeout) {
|
||||
// In timeout editing mode
|
||||
if (key.return) {
|
||||
// Save the timeout
|
||||
const currentItem = items[selectedIndex];
|
||||
if (currentItem) {
|
||||
const timeout = parseInt(timeoutInput, 10);
|
||||
const newItems = [...items];
|
||||
if (!isNaN(timeout) && timeout > 0) {
|
||||
newItems[selectedIndex] = { ...currentItem, timeout: timeout };
|
||||
} else {
|
||||
// Remove timeout if invalid (will use default 1000ms)
|
||||
const { timeout: _, ...rest } = currentItem;
|
||||
newItems[selectedIndex] = rest;
|
||||
}
|
||||
onUpdate(newItems);
|
||||
}
|
||||
setEditingTimeout(false);
|
||||
setTimeoutInput('');
|
||||
} else if (key.escape) {
|
||||
// Cancel editing
|
||||
setEditingTimeout(false);
|
||||
setTimeoutInput('');
|
||||
} else if (key.backspace || key.delete) {
|
||||
setTimeoutInput(timeoutInput.slice(0, -1));
|
||||
} else if (input && /\d/.test(input)) {
|
||||
setTimeoutInput(timeoutInput + input);
|
||||
}
|
||||
} else if (moveMode) {
|
||||
// In move mode, use up/down to move the selected item
|
||||
if (key.upArrow && selectedIndex > 0) {
|
||||
@@ -658,6 +688,13 @@ const ItemsEditor: React.FC<ItemsEditorProps> = ({ items, onUpdate, onBack, line
|
||||
setMaxWidthInput(currentItem.maxWidth ? currentItem.maxWidth.toString() : '');
|
||||
setEditingMaxWidth(true);
|
||||
}
|
||||
} else if (input === 't' && items.length > 0) {
|
||||
// Edit timeout for custom command
|
||||
const currentItem = items[selectedIndex];
|
||||
if (currentItem && currentItem.type === 'custom-command') {
|
||||
setTimeoutInput(currentItem.timeout ? currentItem.timeout.toString() : '1000');
|
||||
setEditingTimeout(true);
|
||||
}
|
||||
} else if (input === 'p' && items.length > 0) {
|
||||
// Toggle preserve colors for custom command
|
||||
const currentItem = items[selectedIndex];
|
||||
@@ -764,7 +801,7 @@ const ItemsEditor: React.FC<ItemsEditorProps> = ({ items, onUpdate, onBack, line
|
||||
helpText += ', (e)dit text';
|
||||
}
|
||||
if (isCustomCommand) {
|
||||
helpText += ', (e)dit cmd, (w)idth, (p)reserve colors';
|
||||
helpText += ', (e)dit cmd, (w)idth, (t)imeout, (p)reserve colors';
|
||||
}
|
||||
helpText += ', Enter to move, (a)dd, (i)nsert, (d)elete, (c)lear line';
|
||||
if (canToggleRaw) {
|
||||
@@ -798,6 +835,11 @@ const ItemsEditor: React.FC<ItemsEditorProps> = ({ items, onUpdate, onBack, line
|
||||
<Text>Enter max width (blank for no limit): {maxWidthInput}</Text>
|
||||
<Text dimColor>Press Enter to save, ESC to cancel</Text>
|
||||
</Box>
|
||||
) : editingTimeout ? (
|
||||
<Box flexDirection='column'>
|
||||
<Text>Enter timeout in milliseconds (default 1000): {timeoutInput}</Text>
|
||||
<Text dimColor>Press Enter to save, ESC to cancel</Text>
|
||||
</Box>
|
||||
) : moveMode ? (
|
||||
<Text dimColor>↑↓ to move item, ESC or Enter to exit move mode</Text>
|
||||
) : (
|
||||
@@ -902,7 +944,11 @@ const ColorMenu: React.FC<ColorMenuProps> = ({ items, onUpdate, onBack }) => {
|
||||
case 'terminal-width': return 'Terminal Width';
|
||||
case 'version': return 'Version';
|
||||
case 'custom-text': return `Custom Text (${item.customText || 'Empty'})`;
|
||||
case 'custom-command': return `Custom Command (${item.commandPath ? item.commandPath.substring(0, 20) + (item.commandPath.length > 20 ? '...' : '') : 'No command'})`;
|
||||
case 'custom-command': {
|
||||
const cmd = item.commandPath ? item.commandPath.substring(0, 20) + (item.commandPath.length > 20 ? '...' : '') : 'No command';
|
||||
const timeout = item.timeout ? ` ${item.timeout}ms` : '';
|
||||
return `Custom Command (${cmd}${timeout})`;
|
||||
}
|
||||
default: return item.type;
|
||||
}
|
||||
};
|
||||
|
||||
Reference in new issue
Block a user