diff --git a/README.md b/README.md index 8dd7604..799c5c1 100644 --- a/README.md +++ b/README.md @@ -200,7 +200,7 @@ Execute shell commands and display their output dynamically: ### 🔗 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: +[ccusage](https://github.com/ryoppippi/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` @@ -325,6 +325,16 @@ Contributions are welcome! Please feel free to submit a Pull Request. --- +## Star History + + + + + + Star History Chart + + +
### 🌟 Show Your Support diff --git a/package.json b/package.json index 9124387..430ae93 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ccstatusline", - "version": "2.0.0", + "version": "2.0.1", "description": "A customizable status line formatter for Claude Code CLI", "module": "src/ccstatusline.ts", "type": "module", @@ -13,6 +13,7 @@ "statusline": "bun run src/ccstatusline.ts", "patch": "patch-package", "build": "bun run patch && rm -rf dist/* && bun build src/ccstatusline.ts --target=node --outfile=dist/ccstatusline.js --target-version=14", + "postbuild": "bun run scripts/replace-version.ts", "prepublishOnly": "bun run build", "lint": "bun tsc --noEmit; eslint . --config eslint.config.js --max-warnings=999999 --fix" }, diff --git a/scripts/replace-version.ts b/scripts/replace-version.ts new file mode 100755 index 0000000..d28a25b --- /dev/null +++ b/scripts/replace-version.ts @@ -0,0 +1,28 @@ +#!/usr/bin/env bun + +import { + readFileSync, + writeFileSync +} from 'fs'; +import { join } from 'path'; + +interface PackageJson { + version: string; + [key: string]: unknown; +} + +// Read package.json to get version +const packageJson = JSON.parse(readFileSync('package.json', 'utf-8')) as PackageJson; +const version = packageJson.version; + +// Read the bundled file +const bundledFilePath = join('dist', 'ccstatusline.js'); +let bundledContent = readFileSync(bundledFilePath, 'utf-8'); + +// Replace the placeholder with the actual version +bundledContent = bundledContent.replace(/__PACKAGE_VERSION__/g, version); + +// Write back the modified content +writeFileSync(bundledFilePath, bundledContent); + +console.log(`✓ Replaced version placeholder with ${version}`); \ No newline at end of file diff --git a/src/tui/components/ItemsEditor.tsx b/src/tui/components/ItemsEditor.tsx index 31fbfcd..aa5ae9d 100644 --- a/src/tui/components/ItemsEditor.tsx +++ b/src/tui/components/ItemsEditor.tsx @@ -372,8 +372,8 @@ export const ItemsEditor: React.FC = ({ widgets, onUpdate, onB {' '} {lineNumber} {' '} - {moveMode && [MOVE MODE]} + {moveMode && [MOVE MODE]} {(settings.powerline.enabled || Boolean(settings.defaultSeparator)) && ( @@ -412,7 +412,7 @@ export const ItemsEditor: React.FC = ({ widgets, onUpdate, onB return ( - + {isSelected ? (moveMode ? '◆ ' : '▶ ') : ' '} {` ${index + 1}. ${displayText || getWidgetDisplay(widget)}`} diff --git a/src/tui/components/MainMenu.tsx b/src/tui/components/MainMenu.tsx index dca985a..5fe8cf0 100644 --- a/src/tui/components/MainMenu.tsx +++ b/src/tui/components/MainMenu.tsx @@ -30,7 +30,7 @@ export const MainMenu: React.FC = ({ onSelect, isClaudeInstalled, { label: '💻 Terminal Options', value: 'terminalConfig', selectable: true }, { label: '🌐 Global Overrides', value: 'globalOverrides', selectable: true }, { label: '', value: '_gap2', selectable: false }, // Visual gap - { label: isClaudeInstalled ? '🗑️ Uninstall from Claude Code' : '📦 Install to Claude Code', value: 'install', selectable: true } + { label: isClaudeInstalled ? '🔌 Uninstall from Claude Code' : '📦 Install to Claude Code', value: 'install', selectable: true } ]; if (hasChanges) { diff --git a/src/utils/terminal.ts b/src/utils/terminal.ts index 140dab1..394f5e3 100644 --- a/src/utils/terminal.ts +++ b/src/utils/terminal.ts @@ -3,14 +3,33 @@ import * as fs from 'fs'; import * as path from 'path'; // Get package version +// __PACKAGE_VERSION__ will be replaced at build time +const PACKAGE_VERSION = '__PACKAGE_VERSION__'; + export function getPackageVersion(): string { - try { - const packageJsonPath = path.join(__dirname, '..', '..', 'package.json'); - const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8')) as { version?: string }; - return packageJson.version ?? ''; - } catch { - return ''; + // If we have the build-time replaced version, use it (check if it looks like a version) + if (/^\d+\.\d+\.\d+/.test(PACKAGE_VERSION)) { + return PACKAGE_VERSION; } + + // Fallback for development mode + const possiblePaths = [ + path.join(__dirname, '..', '..', 'package.json'), // Development: dist/utils/ -> root + path.join(__dirname, '..', 'package.json') // Production: dist/ -> root (bundled) + ]; + + for (const packageJsonPath of possiblePaths) { + try { + if (fs.existsSync(packageJsonPath)) { + const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8')) as { version?: string }; + return packageJson.version ?? ''; + } + } catch { + // Continue to next path + } + } + + return ''; } // Get terminal width diff --git a/src/widgets/CustomCommand.tsx b/src/widgets/CustomCommand.tsx index a31d018..3dc88e0 100644 --- a/src/widgets/CustomCommand.tsx +++ b/src/widgets/CustomCommand.tsx @@ -60,7 +60,8 @@ export class CustomCommandWidget implements Widget { encoding: 'utf8', input: jsonInput, timeout: timeout, - stdio: ['pipe', 'pipe', 'ignore'] + stdio: ['pipe', 'pipe', 'ignore'], + env: process.env }).trim(); // Strip ANSI codes if preserveColors is false @@ -74,7 +75,26 @@ export class CustomCommandWidget implements Widget { } return output || null; - } catch { + } catch (error) { + // Provide more specific error messages + if (error instanceof Error) { + const execError = error as Error & { + code?: string; + signal?: string; + status?: number; + }; + if (execError.code === 'ENOENT') { + return '[Cmd not found]'; + } else if (execError.code === 'ETIMEDOUT') { + return '[Timeout]'; + } else if (execError.code === 'EACCES') { + return '[Permission denied]'; + } else if (execError.signal) { + return `[Signal: ${execError.signal}]`; + } else if (execError.status !== undefined) { + return `[Exit: ${execError.status}]`; + } + } return '[Error]'; } }