@chongdashu/cc-statusline 1.2.7 โ 1.3.2
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/CHANGELOG.md +157 -110
- package/CLAUDE.md +76 -61
- package/CONTRIBUTING.md +207 -207
- package/LICENSE +20 -20
- package/README.md +373 -305
- package/dist/index.js +206 -22
- package/dist/index.js.map +1 -1
- package/package.json +57 -57
- package/test/test-concurrent-locking.sh +54 -54
- package/test/test-installation.sh +335 -335
- package/test/test-statusline-with-lock.sh +67 -67
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../node_modules/tsup/assets/esm_shims.js","../src/utils/tester.ts","../src/cli/preview.ts","../src/index.ts","../src/cli/commands.ts","../src/cli/prompts.ts","../src/generators/bash-generator.ts","../src/features/colors.ts","../src/features/git.ts","../src/features/usage.ts","../src/utils/validator.ts","../src/utils/installer.ts"],"sourcesContent":["// Shim globals in esm bundle\nimport path from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\nconst getFilename = () => fileURLToPath(import.meta.url)\nconst getDirname = () => path.dirname(getFilename())\n\nexport const __dirname = /* @__PURE__ */ getDirname()\nexport const __filename = /* @__PURE__ */ getFilename()\n","import { StatuslineConfig } from '../cli/prompts.js'\nimport { spawn } from 'child_process'\nimport { promises as fs } from 'fs'\nimport path from 'path'\n\nexport interface TestResult {\n success: boolean\n output: string\n error?: string\n executionTime: number\n}\n\nexport async function testStatuslineScript(script: string, mockData?: any): Promise<TestResult> {\n const startTime = Date.now()\n \n try {\n // Create temporary script file\n const tempDir = '/tmp'\n const scriptPath = path.join(tempDir, `statusline-test-${Date.now()}.sh`)\n \n await fs.writeFile(scriptPath, script, { mode: 0o755 })\n \n // Generate mock input if not provided\n const input = mockData || generateMockClaudeInput()\n \n // Execute script\n const result = await executeScript(scriptPath, JSON.stringify(input))\n \n // Cleanup\n await fs.unlink(scriptPath).catch(() => {}) // Ignore cleanup errors\n \n const executionTime = Date.now() - startTime\n \n return {\n success: result.success,\n output: result.output,\n error: result.error,\n executionTime\n }\n \n } catch (error) {\n return {\n success: false,\n output: '',\n error: error instanceof Error ? error.message : String(error),\n executionTime: Date.now() - startTime\n }\n }\n}\n\nexport function generateMockClaudeInput(config?: Partial<StatuslineConfig>): any {\n return {\n session_id: \"test-session-123\",\n transcript_path: \"/home/user/.claude/conversations/test.jsonl\",\n cwd: \"/home/user/projects/my-project\",\n workspace: {\n current_dir: \"/home/user/projects/my-project\"\n },\n model: {\n id: \"claude-opus-4-1-20250805\",\n display_name: \"Opus 4.1\",\n version: \"20250805\"\n }\n }\n}\n\nexport function generateMockCcusageOutput(): any {\n return {\n blocks: [\n {\n id: \"2025-08-13T08:00:00.000Z\",\n startTime: \"2025-08-13T08:00:00.000Z\",\n endTime: \"2025-08-13T13:00:00.000Z\",\n usageLimitResetTime: \"2025-08-13T13:00:00.000Z\",\n actualEndTime: \"2025-08-13T09:30:34.698Z\",\n isActive: true,\n isGap: false,\n entries: 12,\n tokenCounts: {\n inputTokens: 1250,\n outputTokens: 2830,\n cacheCreationInputTokens: 15000,\n cacheReadInputTokens: 45000\n },\n totalTokens: 64080,\n costUSD: 3.42,\n models: [\"claude-opus-4-1-20250805\"],\n burnRate: {\n tokensPerMinute: 850.5,\n tokensPerMinuteForIndicator: 850,\n costPerHour: 12.45\n },\n projection: {\n totalTokens: 128000,\n totalCost: 6.84,\n remainingMinutes: 210\n }\n }\n ]\n }\n}\n\nasync function executeScript(scriptPath: string, input: string): Promise<{ success: boolean, output: string, error?: string }> {\n return new Promise((resolve) => {\n const process = spawn('bash', [scriptPath], {\n stdio: ['pipe', 'pipe', 'pipe']\n })\n \n let stdout = ''\n let stderr = ''\n \n process.stdout.on('data', (data) => {\n stdout += data.toString()\n })\n \n process.stderr.on('data', (data) => {\n stderr += data.toString()\n })\n \n process.on('close', (code) => {\n resolve({\n success: code === 0,\n output: stdout.trim(),\n error: stderr.trim() || undefined\n })\n })\n \n process.on('error', (err) => {\n resolve({\n success: false,\n output: '',\n error: err.message\n })\n })\n \n // Send input and close stdin\n process.stdin.write(input)\n process.stdin.end()\n \n // Timeout after 5 seconds\n setTimeout(() => {\n process.kill()\n resolve({\n success: false,\n output: stdout,\n error: 'Script execution timed out (5s)'\n })\n }, 5000)\n })\n}\n\nexport function analyzeTestResult(result: TestResult, config: StatuslineConfig): {\n performance: 'excellent' | 'good' | 'slow' | 'timeout'\n hasRequiredFeatures: boolean\n issues: string[]\n suggestions: string[]\n} {\n const issues: string[] = []\n const suggestions: string[] = []\n \n // Performance analysis\n let performance: 'excellent' | 'good' | 'slow' | 'timeout'\n if (result.executionTime > 1000) {\n performance = 'timeout'\n issues.push('Script execution is very slow (>1s)')\n } else if (result.executionTime > 500) {\n performance = 'slow'\n issues.push('Script execution is slow (>500ms)')\n } else if (result.executionTime > 100) {\n performance = 'good'\n } else {\n performance = 'excellent'\n }\n \n // Feature validation\n let hasRequiredFeatures = true\n \n if (config.features.includes('directory') && !result.output.includes('projects')) {\n hasRequiredFeatures = false\n issues.push('Directory feature not working properly')\n }\n \n if (config.features.includes('model') && !result.output.includes('Opus')) {\n hasRequiredFeatures = false\n issues.push('Model feature not working properly')\n }\n \n if (config.features.includes('git') && config.ccusageIntegration && !result.output.includes('git')) {\n suggestions.push('Git integration may require actual git repository')\n }\n \n // Error analysis\n if (result.error) {\n issues.push(`Script errors: ${result.error}`)\n }\n \n if (!result.success) {\n issues.push('Script failed to execute successfully')\n }\n \n // Performance suggestions\n if (config.features.length > 6) {\n suggestions.push('Consider reducing number of features for better performance')\n }\n \n if (config.ccusageIntegration && result.executionTime > 200) {\n suggestions.push('ccusage integration may slow down statusline - consider caching')\n }\n \n return {\n performance,\n hasRequiredFeatures,\n issues,\n suggestions\n }\n}","import { StatuslineConfig } from './prompts.js'\nimport { generateBashStatusline } from '../generators/bash-generator.js'\nimport { testStatuslineScript, generateMockClaudeInput, analyzeTestResult } from '../utils/tester.js'\nimport { promises as fs } from 'fs'\nimport chalk from 'chalk'\nimport ora from 'ora'\n\nexport async function previewCommand(scriptPath: string): Promise<void> {\n console.log(chalk.cyan('๐ Statusline Preview Mode\\n'))\n \n let script: string\n \n // Load existing statusline script\n try {\n const spinner = ora(`Loading statusline script from ${scriptPath}...`).start()\n script = await fs.readFile(scriptPath, 'utf-8')\n spinner.succeed('Script loaded!')\n \n // Try to extract config info from the script header\n const headerMatch = script.match(/# Theme: (\\w+) \\| Colors: (\\w+) \\| Features: ([^\\n]+)/i)\n if (headerMatch) {\n console.log(chalk.yellow('Detected Configuration:'))\n console.log(` Theme: ${headerMatch[1]}`)\n console.log(` Colors: ${headerMatch[2]}`) \n console.log(` Features: ${headerMatch[3]}\\n`)\n }\n \n // Extract generation info if available\n const generationMatch = script.match(/# Generated by cc-statusline.*\\n# Custom Claude Code statusline - Created: ([^\\n]+)/i)\n if (generationMatch) {\n console.log(chalk.gray(`Generated: ${generationMatch[1]}\\n`))\n }\n \n } catch (error) {\n console.error(chalk.red(`โ Failed to load script: ${error instanceof Error ? error.message : String(error)}`))\n return\n }\n \n // Test the script\n const testSpinner = ora('Testing statusline with mock data...').start()\n const mockInput = generateMockClaudeInput()\n \n console.log(chalk.gray('\\nMock Claude Code Input:'))\n console.log(chalk.gray(JSON.stringify(mockInput, null, 2)))\n \n const testResult = await testStatuslineScript(script, mockInput)\n \n if (testResult.success) {\n testSpinner.succeed(`Test completed in ${testResult.executionTime}ms`)\n \n console.log(chalk.green('\\nโ
Statusline Output:'))\n console.log(chalk.white('โ'.repeat(60)))\n console.log(testResult.output)\n console.log(chalk.white('โ'.repeat(60)))\n \n // Basic performance analysis\n console.log(chalk.cyan(`\\n๐ Performance: ${getPerformanceEmoji(getPerformanceLevel(testResult.executionTime))} ${getPerformanceLevel(testResult.executionTime)} (${testResult.executionTime}ms)`))\n \n // Basic output validation\n if (testResult.output.includes('๐') || testResult.output.includes('๐ฟ') || testResult.output.includes('๐ค')) {\n console.log(chalk.green('โ
Statusline features appear to be working'))\n } else {\n console.log(chalk.yellow('โ ๏ธ Basic features may not be displaying correctly'))\n }\n \n } else {\n testSpinner.fail('Test failed')\n console.error(chalk.red(`\\nโ Error: ${testResult.error}`))\n if (testResult.output) {\n console.log(chalk.gray('\\nPartial output:'))\n console.log(testResult.output)\n }\n }\n \n console.log(chalk.green('\\nโจ Preview complete! Use `cc-statusline init` to generate a new statusline.'))\n}\n\nfunction getPerformanceEmoji(performance: string): string {\n switch (performance) {\n case 'excellent': return '๐'\n case 'good': return 'โ
'\n case 'slow': return 'โ ๏ธ'\n case 'timeout': return '๐'\n default: return 'โ'\n }\n}\n\nfunction getPerformanceLevel(executionTime: number): string {\n if (executionTime > 1000) return 'timeout'\n if (executionTime > 500) return 'slow'\n if (executionTime > 100) return 'good'\n return 'excellent'\n}","import { Command } from 'commander'\nimport { initCommand } from './cli/commands.js'\nimport chalk from 'chalk'\n\nconst program = new Command()\n\nprogram\n .name('cc-statusline')\n .description('Interactive CLI tool for generating custom Claude Code statuslines')\n .version('1.2.6')\n\nprogram\n .command('init')\n .description('Create a custom statusline with interactive prompts')\n .option('-o, --output <path>', 'Output path for statusline.sh', './.claude/statusline.sh')\n .option('--no-install', 'Don\\'t automatically install to .claude/statusline.sh')\n .action(initCommand)\n\nprogram\n .command('preview')\n .description('Preview existing statusline.sh with mock data')\n .argument('<script-path>', 'Path to statusline.sh file to preview')\n .action(async (scriptPath) => {\n const { previewCommand } = await import('./cli/preview.js')\n await previewCommand(scriptPath)\n })\n\nprogram\n .command('test')\n .description('Test statusline with real Claude Code JSON input')\n .option('-c, --config <path>', 'Configuration file to test')\n .action(() => {\n console.log(chalk.yellow('Test command coming soon!'))\n })\n\n// Show help if no command provided\nif (!process.argv.slice(2).length) {\n program.outputHelp()\n}\n\nprogram.parse(process.argv)","import { collectConfiguration, displayConfigSummary } from './prompts.js'\nimport { generateBashStatusline } from '../generators/bash-generator.js'\nimport { validateConfig } from '../utils/validator.js'\nimport { installStatusline } from '../utils/installer.js'\nimport chalk from 'chalk'\nimport ora from 'ora'\nimport path from 'path'\nimport os from 'os'\n\ninterface InitOptions {\n output?: string\n install?: boolean\n}\n\nexport async function initCommand(options: InitOptions): Promise<void> {\n try {\n const spinner = ora('Initializing statusline generator...').start()\n await new Promise(resolve => setTimeout(resolve, 500)) // Brief pause for UX\n spinner.stop()\n\n // Collect user configuration\n const config = await collectConfiguration()\n \n // Validate configuration\n const validation = validateConfig(config)\n if (!validation.isValid) {\n console.error(chalk.red('โ Configuration validation failed:'))\n validation.errors.forEach(error => console.error(chalk.red(` โข ${error}`)))\n process.exit(1)\n }\n\n // Generate statusline script\n const generationSpinner = ora('Generating statusline script...').start()\n \n const script = generateBashStatusline(config)\n const filename = 'statusline.sh'\n \n generationSpinner.succeed('Statusline script generated!')\n\n // Show preview of what it will look like\n console.log(chalk.cyan('\\nโจ Your statusline will look like:'))\n console.log(chalk.white('โ'.repeat(60)))\n \n // Generate preview using the test function\n const { testStatuslineScript, generateMockClaudeInput } = await import('../utils/tester.js')\n const mockInput = generateMockClaudeInput()\n const testResult = await testStatuslineScript(script, mockInput)\n \n if (testResult.success) {\n console.log(testResult.output)\n } else {\n console.log(chalk.gray('๐ ~/projects/my-app ๐ฟ main ๐ค Claude ๐ต $2.48 ($12.50/h)'))\n console.log(chalk.gray('(Preview unavailable - will work when Claude Code runs it)'))\n }\n \n console.log(chalk.white('โ'.repeat(60)))\n\n // Determine output path based on installation location\n const isGlobal = config.installLocation === 'global'\n const baseDir = isGlobal ? os.homedir() : '.'\n const outputPath = options.output || path.join(baseDir, '.claude', filename)\n const resolvedPath = path.resolve(outputPath)\n\n // Install the statusline\n if (options.install !== false) {\n console.log(chalk.cyan('\\n๐ฆ Installing statusline...'))\n \n try {\n await installStatusline(script, resolvedPath, config)\n \n console.log(chalk.green('\\nโ
Statusline installed!'))\n console.log(chalk.green('\\n๐ Success! Your custom statusline is ready!'))\n console.log(chalk.cyan(`\\n๐ ${isGlobal ? 'Global' : 'Project'} installation complete: ${chalk.white(resolvedPath)}`))\n console.log(chalk.cyan('\\nNext steps:'))\n console.log(chalk.white(' 1. Restart Claude Code to see your new statusline'))\n console.log(chalk.white(' 2. Usage statistics work via: npx ccusage@latest'))\n \n } catch (error) {\n console.log(chalk.red('\\nโ Failed to install statusline'))\n \n if (error instanceof Error && error.message === 'USER_CANCELLED_OVERWRITE') {\n console.log(chalk.yellow('\\nโ ๏ธ Installation cancelled. Existing statusline.sh was not overwritten.'))\n } else if (error instanceof Error && error.message === 'SETTINGS_UPDATE_FAILED') {\n const commandPath = isGlobal ? '~/.claude/statusline.sh' : '.claude/statusline.sh'\n console.log(chalk.yellow('\\nโ ๏ธ Settings.json could not be updated automatically.'))\n console.log(chalk.cyan('\\nManual Configuration Required:'))\n console.log(chalk.white(`Add this to your ${isGlobal ? '~/.claude' : '.claude'}/settings.json file:`))\n console.log(chalk.gray('\\n{'))\n console.log(chalk.gray(' \"statusLine\": {'))\n console.log(chalk.gray(' \"type\": \"command\",'))\n console.log(chalk.gray(` \"command\": \"${commandPath}\",`))\n console.log(chalk.gray(' \"padding\": 0'))\n console.log(chalk.gray(' }'))\n console.log(chalk.gray('}'))\n console.log(chalk.cyan(`\\n๐ Statusline script saved to: ${chalk.white(resolvedPath)}`))\n } else {\n console.error(chalk.red(`Error: ${error instanceof Error ? error.message : String(error)}`))\n console.log(chalk.cyan(`\\n๐ You can manually save the script to: ${chalk.white(resolvedPath)}`))\n }\n }\n } else {\n // Just display where to save it\n console.log(chalk.green('\\nโ
Statusline generated successfully!'))\n console.log(chalk.cyan(`\\n๐ Save this script to: ${chalk.white(resolvedPath)}`))\n console.log(chalk.cyan('\\nThen restart Claude Code to see your new statusline.'))\n }\n\n } catch (error) {\n console.error(chalk.red('โ An error occurred:'))\n console.error(chalk.red(error instanceof Error ? error.message : String(error)))\n process.exit(1)\n }\n}","import inquirer from 'inquirer'\n\nexport interface StatuslineConfig {\n features: string[]\n runtime: 'bash' | 'python' | 'node'\n colors: boolean\n theme: 'minimal' | 'detailed' | 'compact'\n ccusageIntegration: boolean\n logging: boolean\n customEmojis: boolean\n installLocation?: 'global' | 'project'\n}\n\nexport async function collectConfiguration(): Promise<StatuslineConfig> {\n console.log('๐ Welcome to cc-statusline! Let\\'s create your custom Claude Code statusline.\\n')\n console.log('โจ All features are enabled by default. Use โ/โ arrows to navigate, SPACE to toggle, ENTER to continue.\\n')\n \n const config = await inquirer.prompt([\n {\n type: 'checkbox',\n name: 'features',\n message: 'Select statusline features (scroll down for more options):',\n choices: [\n { name: '๐ Working Directory', value: 'directory', checked: true },\n { name: '๐ฟ Git Branch', value: 'git', checked: true },\n { name: '๐ค Model Name & Version', value: 'model', checked: true },\n { name: '๐ง Context Remaining', value: 'context', checked: true },\n { name: '๐ต Usage & Cost', value: 'usage', checked: true },\n { name: 'โ Session Time Remaining', value: 'session', checked: true },\n { name: '๐ Token Statistics', value: 'tokens', checked: true },\n { name: 'โก Burn Rate (tokens/min)', value: 'burnrate', checked: true }\n ],\n validate: (answer: string[]) => {\n if (answer.length < 1) {\n return 'You must choose at least one feature.'\n }\n return true\n },\n pageSize: 10\n },\n {\n type: 'confirm',\n name: 'colors',\n message: '\\n๐จ Enable modern color scheme and emojis?',\n default: true\n },\n {\n type: 'confirm',\n name: 'logging',\n message: '\\n๐ Enable debug logging to .claude/statusline.log?',\n default: false\n },\n {\n type: 'list',\n name: 'installLocation',\n message: '\\n๐ Where would you like to install the statusline?',\n choices: [\n { name: '๐ Global (~/.claude) - Use across all projects', value: 'global' },\n { name: '๐ Project (./.claude) - Only for this project', value: 'project' }\n ],\n default: 'project'\n }\n ])\n\n // Set intelligent defaults\n return {\n features: config.features,\n runtime: 'bash',\n colors: config.colors,\n theme: 'detailed',\n ccusageIntegration: true, // Always enabled since npx works\n logging: config.logging,\n customEmojis: false,\n installLocation: config.installLocation\n } as StatuslineConfig\n}\n\nexport function displayConfigSummary(config: StatuslineConfig): void {\n console.log('\\nโ
Configuration Summary:')\n console.log(` Runtime: ${config.runtime}`)\n console.log(` Theme: ${config.theme}`)\n console.log(` Colors: ${config.colors ? 'โ
' : 'โ'}`)\n console.log(` Features: ${config.features.join(', ')}`)\n \n if (config.ccusageIntegration) {\n console.log(' ๐ ccusage integration enabled')\n }\n \n if (config.logging) {\n console.log(' ๐ Debug logging enabled')\n }\n \n console.log('')\n}","import { StatuslineConfig } from '../cli/prompts.js'\nimport { generateColorBashCode, generateBasicColors } from '../features/colors.js'\nimport { generateGitBashCode, generateGitDisplayCode, generateGitUtilities } from '../features/git.js'\nimport { generateUsageBashCode, generateUsageDisplayCode, generateUsageUtilities } from '../features/usage.js'\n\nexport function generateBashStatusline(config: StatuslineConfig): string {\n const hasGit = config.features.includes('git')\n const hasUsage = config.features.some(f => ['usage', 'session', 'tokens', 'burnrate'].includes(f))\n const hasDirectory = config.features.includes('directory')\n const hasModel = config.features.includes('model')\n const hasContext = config.features.includes('context')\n\n // Build usage feature config\n const usageConfig = {\n enabled: hasUsage && config.ccusageIntegration,\n showCost: config.features.includes('usage'),\n showTokens: config.features.includes('tokens'),\n showBurnRate: config.features.includes('burnrate'),\n showSession: config.features.includes('session'),\n showProgressBar: config.theme !== 'minimal' && config.features.includes('session')\n }\n\n // Build git feature config\n const gitConfig = {\n enabled: hasGit,\n showBranch: hasGit,\n showChanges: false, // Removed delta changes per user request\n compactMode: config.theme === 'compact'\n }\n\n const timestamp = new Date().toISOString()\n const script = `#!/bin/bash\n# Generated by cc-statusline (https://www.npmjs.com/package/@chongdashu/cc-statusline)\n# Custom Claude Code statusline - Created: ${timestamp}\n# Theme: ${config.theme} | Colors: ${config.colors} | Features: ${config.features.join(', ')}\n\ninput=$(cat)\n${config.logging ? generateLoggingCode() : ''}\n${generateColorBashCode({ enabled: config.colors, theme: config.theme })}\n${config.colors ? generateBasicColors() : ''}\n${hasUsage ? generateUsageUtilities() : ''}\n${hasGit ? generateGitUtilities() : ''}\n${generateBasicDataExtraction(hasDirectory, hasModel, hasContext)}\n${hasGit ? generateGitBashCode(gitConfig, config.colors) : ''}\n${hasContext ? generateContextBashCode(config.colors) : ''}\n${hasUsage ? generateUsageBashCode(usageConfig, config.colors) : ''}\n${config.logging ? generateLoggingOutput() : ''}\n${generateDisplaySection(config, gitConfig, usageConfig)}\n`\n\n return script.replace(/\\n\\n\\n+/g, '\\n\\n').trim() + '\\n'\n}\n\nfunction generateLoggingCode(): string {\n return `\nLOG_FILE=\"\\${HOME}/.claude/statusline.log\"\nTIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')\n\n# ---- logging ----\n{\n echo \"[$TIMESTAMP] Status line triggered with input:\"\n (echo \"$input\" | jq . 2>/dev/null) || echo \"$input\"\n echo \"---\"\n} >> \"$LOG_FILE\" 2>/dev/null\n`\n}\n\nfunction generateBasicDataExtraction(hasDirectory: boolean, hasModel: boolean, hasContext: boolean): string {\n return `\n# ---- basics ----\nif command -v jq >/dev/null 2>&1; then${hasDirectory ? `\n current_dir=$(echo \"$input\" | jq -r '.workspace.current_dir // .cwd // \"unknown\"' 2>/dev/null | sed \"s|^$HOME|~|g\")` : ''}${hasModel ? `\n model_name=$(echo \"$input\" | jq -r '.model.display_name // \"Claude\"' 2>/dev/null)\n model_version=$(echo \"$input\" | jq -r '.model.version // \"\"' 2>/dev/null)` : ''}${hasContext ? `\n session_id=$(echo \"$input\" | jq -r '.session_id // \"\"' 2>/dev/null)` : ''}\n cc_version=$(echo \"$input\" | jq -r '.version // \"\"' 2>/dev/null)\n output_style=$(echo \"$input\" | jq -r '.output_style.name // \"\"' 2>/dev/null)\nelse${hasDirectory ? `\n current_dir=\"unknown\"` : ''}${hasModel ? `\n model_name=\"Claude\"; model_version=\"\"` : ''}${hasContext ? `\n session_id=\"\"` : ''}\n cc_version=\"\"\n output_style=\"\"\nfi\n`\n}\n\nfunction generateContextBashCode(colors: boolean): string {\n return `\n# ---- context window calculation ----\ncontext_pct=\"\"\ncontext_color() { if [ \"$use_color\" -eq 1 ]; then printf '\\\\033[1;37m'; fi; } # default white\n\n# Determine max context based on model\nget_max_context() {\n local model_name=\"$1\"\n case \"$model_name\" in\n *\"Opus 4\"*|*\"opus 4\"*|*\"Opus\"*|*\"opus\"*)\n echo \"200000\" # 200K for all Opus versions\n ;;\n *\"Sonnet 4\"*|*\"sonnet 4\"*|*\"Sonnet 3.5\"*|*\"sonnet 3.5\"*|*\"Sonnet\"*|*\"sonnet\"*)\n echo \"200000\" # 200K for Sonnet 3.5+ and 4.x\n ;;\n *\"Haiku 3.5\"*|*\"haiku 3.5\"*|*\"Haiku 4\"*|*\"haiku 4\"*|*\"Haiku\"*|*\"haiku\"*)\n echo \"200000\" # 200K for modern Haiku\n ;;\n *\"Claude 3 Haiku\"*|*\"claude 3 haiku\"*)\n echo \"100000\" # 100K for original Claude 3 Haiku\n ;;\n *)\n echo \"200000\" # Default to 200K\n ;;\n esac\n}\n\nif [ -n \"$session_id\" ] && command -v jq >/dev/null 2>&1; then\n MAX_CONTEXT=$(get_max_context \"$model_name\")\n \n # Convert current dir to session file path\n project_dir=$(echo \"$current_dir\" | sed \"s|~|$HOME|g\" | sed 's|/|-|g' | sed 's|^-||')\n session_file=\"$HOME/.claude/projects/-\\${project_dir}/\\${session_id}.jsonl\"\n \n if [ -f \"$session_file\" ]; then\n # Get the latest input token count from the session file\n latest_tokens=$(tail -20 \"$session_file\" | jq -r 'select(.message.usage) | .message.usage | ((.input_tokens // 0) + (.cache_read_input_tokens // 0))' 2>/dev/null | tail -1)\n \n if [ -n \"$latest_tokens\" ] && [ \"$latest_tokens\" -gt 0 ]; then\n context_used_pct=$(( latest_tokens * 100 / MAX_CONTEXT ))\n context_remaining_pct=$(( 100 - context_used_pct ))\n \n # Set color based on remaining percentage\n if [ \"$context_remaining_pct\" -le 20 ]; then\n context_color() { if [ \"$use_color\" -eq 1 ]; then printf '\\\\033[38;5;203m'; fi; } # coral red\n elif [ \"$context_remaining_pct\" -le 40 ]; then\n context_color() { if [ \"$use_color\" -eq 1 ]; then printf '\\\\033[38;5;215m'; fi; } # peach\n else\n context_color() { if [ \"$use_color\" -eq 1 ]; then printf '\\\\033[38;5;158m'; fi; } # mint green\n fi\n \n context_pct=\"\\${context_remaining_pct}%\"\n fi\n fi\nfi\n`\n}\n\nfunction generateLoggingOutput(): string {\n return `\n# ---- log extracted data ----\n{\n echo \"[\\$TIMESTAMP] Extracted: dir=\\${current_dir:-}, model=\\${model_name:-}, version=\\${model_version:-}, git=\\${git_branch:-}, context=\\${context_pct:-}, cost=\\${cost_usd:-}, cost_ph=\\${cost_per_hour:-}, tokens=\\${tot_tokens:-}, tpm=\\${tpm:-}, session_pct=\\${session_pct:-}\"\n} >> \"$LOG_FILE\" 2>/dev/null\n`\n}\n\nfunction generateDisplaySection(config: StatuslineConfig, gitConfig: any, usageConfig: any): string {\n const emojis = config.colors && !config.customEmojis\n\n return `\n# ---- render statusline ----\n# Line 1: Core info (directory, git, model, claude code version, output style)\n${config.features.includes('directory') ? `printf '๐ %s%s%s' \"$(dir_color)\" \"$current_dir\" \"$(rst)\"` : ''}${gitConfig.enabled ? `\nif [ -n \"$git_branch\" ]; then\n printf ' ๐ฟ %s%s%s' \"$(git_color)\" \"$git_branch\" \"$(rst)\"\nfi` : ''}${config.features.includes('model') ? `\nprintf ' ๐ค %s%s%s' \"$(model_color)\" \"$model_name\" \"$(rst)\"\nif [ -n \"$model_version\" ] && [ \"$model_version\" != \"null\" ]; then\n printf ' ๐ท๏ธ %s%s%s' \"$(version_color)\" \"$model_version\" \"$(rst)\"\nfi` : ''}\nif [ -n \"$cc_version\" ] && [ \"$cc_version\" != \"null\" ]; then\n printf ' ๐ %sv%s%s' \"$(cc_version_color)\" \"$cc_version\" \"$(rst)\"\nfi\nif [ -n \"$output_style\" ] && [ \"$output_style\" != \"null\" ]; then\n printf ' ๐จ %s%s%s' \"$(style_color)\" \"$output_style\" \"$(rst)\"\nfi\n\n# Line 2: Context and session time\nline2=\"\"${config.features.includes('context') ? `\nif [ -n \"$context_pct\" ]; then\n context_bar=$(progress_bar \"$context_remaining_pct\" 10)\n line2=\"๐ง $(context_color)Context Remaining: \\${context_pct} [\\${context_bar}]$(rst)\"\nfi` : ''}${usageConfig.showSession ? `\nif [ -n \"$session_txt\" ]; then\n if [ -n \"$line2\" ]; then\n line2=\"$line2 โ $(session_color)\\${session_txt}$(rst) $(session_color)[\\${session_bar}]$(rst)\"\n else\n line2=\"โ $(session_color)\\${session_txt}$(rst) $(session_color)[\\${session_bar}]$(rst)\"\n fi\nfi` : ''}${config.features.includes('context') ? `\nif [ -z \"$line2\" ] && [ -z \"$context_pct\" ]; then\n line2=\"๐ง $(context_color)Context Remaining: TBD$(rst)\"\nfi` : ''}\n\n# Line 3: Cost and usage analytics\nline3=\"\"${usageConfig.showCost ? `\nif [ -n \"$cost_usd\" ] && [[ \"$cost_usd\" =~ ^[0-9.]+$ ]]; then${usageConfig.showBurnRate ? `\n if [ -n \"$cost_per_hour\" ] && [[ \"$cost_per_hour\" =~ ^[0-9.]+$ ]]; then\n cost_per_hour_formatted=$(printf '%.2f' \"$cost_per_hour\")\n line3=\"๐ฐ $(cost_color)\\\\$$(printf '%.2f' \"$cost_usd\")$(rst) ($(burn_color)\\\\$\\${cost_per_hour_formatted}/h$(rst))\"\n else\n line3=\"๐ฐ $(cost_color)\\\\$$(printf '%.2f' \"$cost_usd\")$(rst)\"\n fi` : `\n line3=\"๐ฐ $(cost_color)\\\\$$(printf '%.2f' \"$cost_usd\")$(rst)\"`}\nfi` : ''}${usageConfig.showTokens ? `\nif [ -n \"$tot_tokens\" ] && [[ \"$tot_tokens\" =~ ^[0-9]+$ ]]; then${usageConfig.showBurnRate ? `\n if [ -n \"$tpm\" ] && [[ \"$tpm\" =~ ^[0-9.]+$ ]]; then\n tpm_formatted=$(printf '%.0f' \"$tpm\")\n if [ -n \"$line3\" ]; then\n line3=\"$line3 ๐ $(usage_color)\\${tot_tokens} tok (\\${tpm_formatted} tpm)$(rst)\"\n else\n line3=\"๐ $(usage_color)\\${tot_tokens} tok (\\${tpm_formatted} tpm)$(rst)\"\n fi\n else\n if [ -n \"$line3\" ]; then\n line3=\"$line3 ๐ $(usage_color)\\${tot_tokens} tok$(rst)\"\n else\n line3=\"๐ $(usage_color)\\${tot_tokens} tok$(rst)\"\n fi\n fi` : `\n if [ -n \"$line3\" ]; then\n line3=\"$line3 ๐ $(usage_color)\\${tot_tokens} tok$(rst)\"\n else\n line3=\"๐ $(usage_color)\\${tot_tokens} tok$(rst)\"\n fi`}\nfi` : ''}\n\n# Print lines\nif [ -n \"$line2\" ]; then\n printf '\\\\n%s' \"$line2\"\nfi\nif [ -n \"$line3\" ]; then\n printf '\\\\n%s' \"$line3\"\nfi\nprintf '\\\\n'`\n}","export interface ColorConfig {\n enabled: boolean\n theme: 'minimal' | 'detailed' | 'compact'\n}\n\nexport function generateColorBashCode(config: ColorConfig): string {\n if (!config.enabled) {\n return `\n# ---- color helpers (disabled) ----\nuse_color=0\nC() { :; }\nRST() { :; }\n`\n }\n\n return `\n# ---- color helpers (force colors for Claude Code) ----\nuse_color=1\n[ -n \"$NO_COLOR\" ] && use_color=0\n\nC() { if [ \"$use_color\" -eq 1 ]; then printf '\\\\033[%sm' \"$1\"; fi; }\nRST() { if [ \"$use_color\" -eq 1 ]; then printf '\\\\033[0m'; fi; }\n`\n}\n\nexport function generateBasicColors(): string {\n return `\n# ---- modern sleek colors ----\ndir_color() { if [ \"$use_color\" -eq 1 ]; then printf '\\\\033[38;5;117m'; fi; } # sky blue\nmodel_color() { if [ \"$use_color\" -eq 1 ]; then printf '\\\\033[38;5;147m'; fi; } # light purple \nversion_color() { if [ \"$use_color\" -eq 1 ]; then printf '\\\\033[38;5;180m'; fi; } # soft yellow\ncc_version_color() { if [ \"$use_color\" -eq 1 ]; then printf '\\\\033[38;5;249m'; fi; } # light gray\nstyle_color() { if [ \"$use_color\" -eq 1 ]; then printf '\\\\033[38;5;245m'; fi; } # gray\nrst() { if [ \"$use_color\" -eq 1 ]; then printf '\\\\033[0m'; fi; }\n`\n}\n\nexport const COLOR_CODES = {\n // Basic colors\n BLACK: '30',\n RED: '31', \n GREEN: '32',\n YELLOW: '33',\n BLUE: '34',\n MAGENTA: '35',\n CYAN: '36',\n WHITE: '37',\n \n // Bright colors (bold)\n BRIGHT_BLACK: '1;30',\n BRIGHT_RED: '1;31',\n BRIGHT_GREEN: '1;32', \n BRIGHT_YELLOW: '1;33',\n BRIGHT_BLUE: '1;34',\n BRIGHT_MAGENTA: '1;35',\n BRIGHT_CYAN: '1;36',\n BRIGHT_WHITE: '1;37',\n \n // Reset\n RESET: '0'\n} as const\n\nexport function getThemeColors(theme: 'minimal' | 'detailed' | 'compact') {\n switch (theme) {\n case 'minimal':\n return {\n directory: COLOR_CODES.CYAN,\n git: COLOR_CODES.GREEN,\n model: COLOR_CODES.MAGENTA,\n usage: COLOR_CODES.YELLOW,\n session: COLOR_CODES.BLUE\n }\n case 'detailed':\n return {\n directory: COLOR_CODES.BRIGHT_CYAN,\n git: COLOR_CODES.BRIGHT_GREEN,\n model: COLOR_CODES.BRIGHT_MAGENTA,\n usage: COLOR_CODES.BRIGHT_YELLOW,\n session: COLOR_CODES.BRIGHT_BLUE\n }\n case 'compact':\n return {\n directory: COLOR_CODES.CYAN,\n git: COLOR_CODES.GREEN,\n model: COLOR_CODES.BLUE,\n usage: COLOR_CODES.YELLOW,\n session: COLOR_CODES.RED\n }\n }\n}","export interface GitFeature {\n enabled: boolean\n showBranch: boolean\n showChanges: boolean\n compactMode: boolean\n}\n\nexport function generateGitBashCode(config: GitFeature, colors: boolean): string {\n if (!config.enabled) return ''\n\n const colorCode = colors ? `\n# ---- git colors ----\ngit_color() { if [ \"$use_color\" -eq 1 ]; then printf '\\\\033[38;5;150m'; fi; } # soft green\nrst() { if [ \"$use_color\" -eq 1 ]; then printf '\\\\033[0m'; fi; }\n` : `\ngit_color() { :; }\nrst() { :; }\n`\n\n return `${colorCode}\n# ---- git ----\ngit_branch=\"\"\nif git rev-parse --git-dir >/dev/null 2>&1; then\n git_branch=$(git branch --show-current 2>/dev/null || git rev-parse --short HEAD 2>/dev/null)\nfi`\n}\n\nexport function generateGitDisplayCode(config: GitFeature, colors: boolean, emojis: boolean): string {\n if (!config.enabled) return ''\n\n const branchEmoji = emojis ? '๐ฟ' : 'git:'\n\n let displayCode = `\n# git display\nif [ -n \"$git_branch\" ]; then\n printf ' ${branchEmoji} %s%s%s' \"$(git_color)\" \"$git_branch\" \"$(rst)\"\nfi`\n\n return displayCode\n}\n\nexport function generateGitUtilities(): string {\n return `\n# git utilities\nnum_or_zero() { v=\"$1\"; [[ \"$v\" =~ ^[0-9]+$ ]] && echo \"$v\" || echo 0; }`\n}","export interface UsageFeature {\n enabled: boolean\n showCost: boolean\n showTokens: boolean\n showBurnRate: boolean\n showSession: boolean\n showProgressBar: boolean\n}\n\nexport function generateUsageBashCode(config: UsageFeature, colors: boolean): string {\n if (!config.enabled) return ''\n\n const colorCode = colors ? `\n# ---- usage colors ----\nusage_color() { if [ \"$use_color\" -eq 1 ]; then printf '\\\\033[38;5;189m'; fi; } # lavender\ncost_color() { if [ \"$use_color\" -eq 1 ]; then printf '\\\\033[38;5;222m'; fi; } # light gold\nburn_color() { if [ \"$use_color\" -eq 1 ]; then printf '\\\\033[38;5;220m'; fi; } # bright gold\nsession_color() { \n rem_pct=$(( 100 - session_pct ))\n if (( rem_pct <= 10 )); then SCLR='38;5;210' # light pink\n elif (( rem_pct <= 25 )); then SCLR='38;5;228' # light yellow \n else SCLR='38;5;194'; fi # light green\n if [ \"$use_color\" -eq 1 ]; then printf '\\\\033[%sm' \"$SCLR\"; fi\n}\n` : `\nusage_color() { :; }\ncost_color() { :; }\nburn_color() { :; }\nsession_color() { :; }\n`\n\n return `${colorCode}\n# ---- ccusage integration ----\nsession_txt=\"\"; session_pct=0; session_bar=\"\"\ncost_usd=\"\"; cost_per_hour=\"\"; tpm=\"\"; tot_tokens=\"\"\n\nif command -v jq >/dev/null 2>&1; then\n # Run ccusage directly (it has its own internal caching)\n blocks_output=\"\"\n \n # Try ccusage with timeout\n if command -v timeout >/dev/null 2>&1; then\n blocks_output=$(timeout 5s ccusage blocks --json 2>/dev/null)\n elif command -v gtimeout >/dev/null 2>&1; then\n # macOS with coreutils installed\n blocks_output=$(gtimeout 5s ccusage blocks --json 2>/dev/null)\n else\n # No timeout available, run directly (ccusage should be fast)\n blocks_output=$(ccusage blocks --json 2>/dev/null)\n fi\n if [ -n \"$blocks_output\" ]; then\n active_block=$(echo \"$blocks_output\" | jq -c '.blocks[] | select(.isActive == true)' 2>/dev/null | head -n1)\n if [ -n \"$active_block\" ]; then${config.showCost ? `\n cost_usd=$(echo \"$active_block\" | jq -r '.costUSD // empty')\n cost_per_hour=$(echo \"$active_block\" | jq -r '.burnRate.costPerHour // empty')` : ''}${config.showTokens ? `\n tot_tokens=$(echo \"$active_block\" | jq -r '.totalTokens // empty')` : ''}${config.showBurnRate ? `\n tpm=$(echo \"$active_block\" | jq -r '.burnRate.tokensPerMinute // empty')` : ''}${config.showSession || config.showProgressBar ? `\n \n # Session time calculation\n reset_time_str=$(echo \"$active_block\" | jq -r '.usageLimitResetTime // .endTime // empty')\n start_time_str=$(echo \"$active_block\" | jq -r '.startTime // empty')\n \n if [ -n \"$reset_time_str\" ] && [ -n \"$start_time_str\" ]; then\n start_sec=$(to_epoch \"$start_time_str\"); end_sec=$(to_epoch \"$reset_time_str\"); now_sec=$(date +%s)\n total=$(( end_sec - start_sec )); (( total<1 )) && total=1\n elapsed=$(( now_sec - start_sec )); (( elapsed<0 ))&&elapsed=0; (( elapsed>total ))&&elapsed=$total\n session_pct=$(( elapsed * 100 / total ))\n remaining=$(( end_sec - now_sec )); (( remaining<0 )) && remaining=0\n rh=$(( remaining / 3600 )); rm=$(( (remaining % 3600) / 60 ))\n end_hm=$(fmt_time_hm \"$end_sec\")${config.showSession ? `\n session_txt=\"$(printf '%dh %dm until reset at %s (%d%%)' \"$rh\" \"$rm\" \"$end_hm\" \"$session_pct\")\"` : ''}${config.showProgressBar ? `\n session_bar=$(progress_bar \"$session_pct\" 10)` : ''}\n fi` : ''}\n fi\n fi\nfi`\n}\n\nexport function generateUsageUtilities(): string {\n return `\n# ---- time helpers ----\nto_epoch() {\n ts=\"$1\"\n if command -v gdate >/dev/null 2>&1; then gdate -d \"$ts\" +%s 2>/dev/null && return; fi\n date -u -j -f \"%Y-%m-%dT%H:%M:%S%z\" \"\\${ts/Z/+0000}\" +%s 2>/dev/null && return\n python3 - \"$ts\" <<'PY' 2>/dev/null\nimport sys, datetime\ns=sys.argv[1].replace('Z','+00:00')\nprint(int(datetime.datetime.fromisoformat(s).timestamp()))\nPY\n}\n\nfmt_time_hm() {\n epoch=\"$1\"\n if date -r 0 +%s >/dev/null 2>&1; then date -r \"$epoch\" +\"%H:%M\"; else date -d \"@$epoch\" +\"%H:%M\"; fi\n}\n\nprogress_bar() {\n pct=\"\\${1:-0}\"; width=\"\\${2:-10}\"\n [[ \"$pct\" =~ ^[0-9]+$ ]] || pct=0; ((pct<0))&&pct=0; ((pct>100))&&pct=100\n filled=$(( pct * width / 100 )); empty=$(( width - filled ))\n printf '%*s' \"$filled\" '' | tr ' ' '='\n printf '%*s' \"$empty\" '' | tr ' ' '-'\n}`\n}\n\nexport function generateUsageDisplayCode(config: UsageFeature, colors: boolean, emojis: boolean): string {\n if (!config.enabled) return ''\n\n let displayCode = ''\n\n if (config.showSession) {\n const sessionEmoji = emojis ? 'โ' : 'session:'\n displayCode += `\n# session time\nif [ -n \"$session_txt\" ]; then\n printf ' ${sessionEmoji} %s%s%s' \"$(session_color)\" \"$session_txt\" \"$(rst)\"${config.showProgressBar ? `\n printf ' %s[%s]%s' \"$(session_color)\" \"$session_bar\" \"$(rst)\"` : ''}\nfi`\n }\n\n if (config.showCost) {\n const costEmoji = emojis ? '๐ต' : '$'\n displayCode += `\n# cost\nif [ -n \"$cost_usd\" ] && [[ \"$cost_usd\" =~ ^[0-9.]+$ ]]; then\n if [ -n \"$cost_per_hour\" ] && [[ \"$cost_per_hour\" =~ ^[0-9.]+$ ]]; then\n printf ' ${costEmoji} %s$%.2f ($%.2f/h)%s' \"$(cost_color)\" \"$cost_usd\" \"$cost_per_hour\" \"$(rst)\"\n else\n printf ' ${costEmoji} %s$%.2f%s' \"$(cost_color)\" \"$cost_usd\" \"$(rst)\"\n fi\nfi`\n }\n\n if (config.showTokens) {\n const tokenEmoji = emojis ? '๐' : 'tok:'\n displayCode += `\n# tokens\nif [ -n \"$tot_tokens\" ] && [[ \"$tot_tokens\" =~ ^[0-9]+$ ]]; then\n if [ -n \"$tpm\" ] && [[ \"$tpm\" =~ ^[0-9.]+$ ]] && ${config.showBurnRate ? 'true' : 'false'}; then\n printf ' ${tokenEmoji} %s%s tok (%.0f tpm)%s' \"$(usage_color)\" \"$tot_tokens\" \"$tpm\" \"$(rst)\"\n else\n printf ' ${tokenEmoji} %s%s tok%s' \"$(usage_color)\" \"$tot_tokens\" \"$(rst)\"\n fi\nfi`\n }\n\n return displayCode\n}","import { StatuslineConfig } from '../cli/prompts.js'\n\nexport interface ValidationResult {\n isValid: boolean\n errors: string[]\n warnings: string[]\n}\n\nexport function validateConfig(config: StatuslineConfig): ValidationResult {\n const errors: string[] = []\n const warnings: string[] = []\n\n // Validate features\n if (!config.features || config.features.length === 0) {\n errors.push('At least one display feature must be selected')\n }\n\n // Validate runtime\n if (!['bash', 'python', 'node'].includes(config.runtime)) {\n errors.push(`Invalid runtime: ${config.runtime}`)\n }\n\n // Validate theme\n if (!['minimal', 'detailed', 'compact'].includes(config.theme)) {\n errors.push(`Invalid theme: ${config.theme}`)\n }\n\n // Check for usage features without ccusage integration\n const usageFeatures = ['usage', 'session', 'tokens', 'burnrate']\n const hasUsageFeatures = config.features.some(f => usageFeatures.includes(f))\n \n if (hasUsageFeatures && !config.ccusageIntegration) {\n warnings.push('Usage features selected but ccusage integration is disabled. Some features may not work properly.')\n }\n\n // Warn about performance with many features\n if (config.features.length > 5) {\n warnings.push('Many features selected. This may impact statusline performance.')\n }\n\n // Validate color/emoji consistency\n if (config.customEmojis && !config.colors) {\n warnings.push('Custom emojis enabled but colors disabled. Visual distinction may be limited.')\n }\n\n return {\n isValid: errors.length === 0,\n errors,\n warnings\n }\n}\n\nexport function validateDependencies(): {\n jq: boolean\n git: boolean\n ccusage: boolean\n python?: boolean\n node?: boolean\n} {\n // This would check system dependencies\n // For now, return placeholder\n return {\n jq: true, // Would check: command -v jq >/dev/null 2>&1\n git: true, // Would check: command -v git >/dev/null 2>&1\n ccusage: false, // Would check: command -v ccusage >/dev/null 2>&1\n python: true, // Would check: command -v python3 >/dev/null 2>&1\n node: true // Would check: command -v node >/dev/null 2>&1\n }\n}","import { StatuslineConfig } from '../cli/prompts.js'\nimport { promises as fs } from 'fs'\nimport path from 'path'\nimport os from 'os'\nimport inquirer from 'inquirer'\n\nexport async function installStatusline(\n script: string,\n outputPath: string,\n config: StatuslineConfig\n): Promise<void> {\n try {\n // Determine the target directory based on install location\n const isGlobal = config.installLocation === 'global'\n const claudeDir = isGlobal ? path.join(os.homedir(), '.claude') : './.claude'\n const scriptPath = path.join(claudeDir, 'statusline.sh')\n \n // Ensure the directory exists\n await fs.mkdir(claudeDir, { recursive: true })\n\n // Check if statusline.sh already exists\n let shouldWrite = true\n try {\n await fs.access(scriptPath)\n // File exists, ask for confirmation\n const { confirmOverwrite } = await inquirer.prompt([{\n type: 'confirm',\n name: 'confirmOverwrite',\n message: `โ ๏ธ ${isGlobal ? 'Global' : 'Project'} statusline.sh already exists. Overwrite?`,\n default: false\n }])\n shouldWrite = confirmOverwrite\n } catch {\n // File doesn't exist, proceed\n }\n\n if (shouldWrite) {\n // Write the script\n await fs.writeFile(scriptPath, script, { mode: 0o755 })\n } else {\n throw new Error('USER_CANCELLED_OVERWRITE')\n }\n\n // Update settings.json safely\n await updateSettingsJson(claudeDir, 'statusline.sh', isGlobal)\n\n // Note: statusline-config.json removed per user feedback - not needed\n // The statusline script contains all necessary configuration info\n\n } catch (error) {\n throw new Error(`Failed to install statusline: ${error instanceof Error ? error.message : String(error)}`)\n }\n}\n\nasync function updateSettingsJson(claudeDir: string, scriptName: string, isGlobal: boolean): Promise<void> {\n const settingsPath = path.join(claudeDir, 'settings.json')\n \n try {\n let settings: any = {}\n let existingStatusLine: any = null\n \n // Try to read existing settings\n try {\n const settingsContent = await fs.readFile(settingsPath, 'utf-8')\n settings = JSON.parse(settingsContent)\n existingStatusLine = settings.statusLine\n } catch {\n // File doesn't exist or invalid JSON, start fresh\n }\n\n // Check if statusLine already exists\n if (existingStatusLine && existingStatusLine.command) {\n // Only update if it's a statusline.sh command or user confirms\n const isOurStatusline = existingStatusLine.command?.includes('statusline.sh')\n \n if (!isOurStatusline) {\n // There's a different statusline configured, ask user\n const { confirmReplace } = await inquirer.prompt([{\n type: 'confirm',\n name: 'confirmReplace',\n message: `โ ๏ธ ${isGlobal ? 'Global' : 'Project'} settings.json already has a statusLine configured (${existingStatusLine.command}). Replace it?`,\n default: false\n }])\n \n if (!confirmReplace) {\n console.warn('\\nโ ๏ธ Statusline script was saved but settings.json was not updated.')\n console.warn(' Your existing statusLine configuration was preserved.')\n return\n }\n }\n }\n\n // Update statusLine configuration\n const commandPath = isGlobal ? `~/.claude/${scriptName}` : `.claude/${scriptName}`\n settings.statusLine = {\n type: 'command',\n command: commandPath,\n padding: 0\n }\n\n // Write updated settings\n await fs.writeFile(settingsPath, JSON.stringify(settings, null, 2))\n \n } catch (error) {\n // Settings update failed, but don't fail the entire installation\n console.warn(`Warning: Could not update settings.json: ${error instanceof Error ? error.message : String(error)}`)\n throw new Error('SETTINGS_UPDATE_FAILED') // Signal that manual config is needed\n }\n}\n\nexport async function checkClaudeCodeSetup(): Promise<{\n hasClaudeDir: boolean\n hasSettings: boolean\n currentStatusline?: string\n}> {\n const claudeDir = './.claude'\n const settingsPath = path.join(claudeDir, 'settings.json')\n \n try {\n const dirExists = await fs.access(claudeDir).then(() => true).catch(() => false)\n const settingsExists = await fs.access(settingsPath).then(() => true).catch(() => false)\n \n let currentStatusline: string | undefined\n \n if (settingsExists) {\n try {\n const settings = JSON.parse(await fs.readFile(settingsPath, 'utf-8'))\n currentStatusline = settings.statusLine?.command\n } catch {\n // Ignore JSON parse errors\n }\n }\n \n return {\n hasClaudeDir: dirExists,\n hasSettings: settingsExists,\n currentStatusline\n }\n } catch {\n return {\n hasClaudeDir: false,\n hasSettings: false\n }\n }\n}"],"mappings":"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","names":["fs","path","process","fs","chalk","ora","path","inquirer","path","os","testStatuslineScript","generateMockClaudeInput","chalk","previewCommand"]}
|
|
1
|
+
{"version":3,"sources":["../node_modules/tsup/assets/esm_shims.js","../src/utils/tester.ts","../src/cli/preview.ts","../src/index.ts","../src/cli/commands.ts","../src/cli/prompts.ts","../src/generators/bash-generator.ts","../src/features/colors.ts","../src/features/git.ts","../src/features/usage.ts","../src/utils/validator.ts","../src/utils/installer.ts"],"sourcesContent":["// Shim globals in esm bundle\nimport path from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\nconst getFilename = () => fileURLToPath(import.meta.url)\nconst getDirname = () => path.dirname(getFilename())\n\nexport const __dirname = /* @__PURE__ */ getDirname()\nexport const __filename = /* @__PURE__ */ getFilename()\n","import { StatuslineConfig } from '../cli/prompts.js'\r\nimport { spawn } from 'child_process'\r\nimport { promises as fs } from 'fs'\r\nimport path from 'path'\r\n\r\nexport interface TestResult {\r\n success: boolean\r\n output: string\r\n error?: string\r\n executionTime: number\r\n}\r\n\r\nexport async function testStatuslineScript(script: string, mockData?: any): Promise<TestResult> {\r\n const startTime = Date.now()\r\n \r\n try {\r\n // Create temporary script file\r\n const tempDir = '/tmp'\r\n const scriptPath = path.join(tempDir, `statusline-test-${Date.now()}.sh`)\r\n \r\n await fs.writeFile(scriptPath, script, { mode: 0o755 })\r\n \r\n // Generate mock input if not provided\r\n const input = mockData || generateMockClaudeInput()\r\n \r\n // Execute script\r\n const result = await executeScript(scriptPath, JSON.stringify(input))\r\n \r\n // Cleanup\r\n await fs.unlink(scriptPath).catch(() => {}) // Ignore cleanup errors\r\n \r\n const executionTime = Date.now() - startTime\r\n \r\n return {\r\n success: result.success,\r\n output: result.output,\r\n error: result.error,\r\n executionTime\r\n }\r\n \r\n } catch (error) {\r\n return {\r\n success: false,\r\n output: '',\r\n error: error instanceof Error ? error.message : String(error),\r\n executionTime: Date.now() - startTime\r\n }\r\n }\r\n}\r\n\r\nexport function generateMockClaudeInput(config?: Partial<StatuslineConfig>): any {\r\n return {\r\n session_id: \"test-session-123\",\r\n transcript_path: \"/home/user/.claude/conversations/test.jsonl\",\r\n cwd: \"/home/user/projects/my-project\",\r\n workspace: {\r\n current_dir: \"/home/user/projects/my-project\"\r\n },\r\n model: {\r\n id: \"claude-opus-4-1-20250805\",\r\n display_name: \"Opus 4.1\",\r\n version: \"20250805\"\r\n }\r\n }\r\n}\r\n\r\nexport function generateMockCcusageOutput(): any {\r\n return {\r\n blocks: [\r\n {\r\n id: \"2025-08-13T08:00:00.000Z\",\r\n startTime: \"2025-08-13T08:00:00.000Z\",\r\n endTime: \"2025-08-13T13:00:00.000Z\",\r\n usageLimitResetTime: \"2025-08-13T13:00:00.000Z\",\r\n actualEndTime: \"2025-08-13T09:30:34.698Z\",\r\n isActive: true,\r\n isGap: false,\r\n entries: 12,\r\n tokenCounts: {\r\n inputTokens: 1250,\r\n outputTokens: 2830,\r\n cacheCreationInputTokens: 15000,\r\n cacheReadInputTokens: 45000\r\n },\r\n totalTokens: 64080,\r\n costUSD: 3.42,\r\n models: [\"claude-opus-4-1-20250805\"],\r\n burnRate: {\r\n tokensPerMinute: 850.5,\r\n tokensPerMinuteForIndicator: 850,\r\n costPerHour: 12.45\r\n },\r\n projection: {\r\n totalTokens: 128000,\r\n totalCost: 6.84,\r\n remainingMinutes: 210\r\n }\r\n }\r\n ]\r\n }\r\n}\r\n\r\nasync function executeScript(scriptPath: string, input: string): Promise<{ success: boolean, output: string, error?: string }> {\r\n return new Promise((resolve) => {\r\n const process = spawn('bash', [scriptPath], {\r\n stdio: ['pipe', 'pipe', 'pipe']\r\n })\r\n \r\n let stdout = ''\r\n let stderr = ''\r\n \r\n process.stdout.on('data', (data) => {\r\n stdout += data.toString()\r\n })\r\n \r\n process.stderr.on('data', (data) => {\r\n stderr += data.toString()\r\n })\r\n \r\n process.on('close', (code) => {\r\n resolve({\r\n success: code === 0,\r\n output: stdout.trim(),\r\n error: stderr.trim() || undefined\r\n })\r\n })\r\n \r\n process.on('error', (err) => {\r\n resolve({\r\n success: false,\r\n output: '',\r\n error: err.message\r\n })\r\n })\r\n \r\n // Send input and close stdin\r\n process.stdin.write(input)\r\n process.stdin.end()\r\n \r\n // Timeout after 5 seconds\r\n setTimeout(() => {\r\n process.kill()\r\n resolve({\r\n success: false,\r\n output: stdout,\r\n error: 'Script execution timed out (5s)'\r\n })\r\n }, 5000)\r\n })\r\n}\r\n\r\nexport function analyzeTestResult(result: TestResult, config: StatuslineConfig): {\r\n performance: 'excellent' | 'good' | 'slow' | 'timeout'\r\n hasRequiredFeatures: boolean\r\n issues: string[]\r\n suggestions: string[]\r\n} {\r\n const issues: string[] = []\r\n const suggestions: string[] = []\r\n \r\n // Performance analysis\r\n let performance: 'excellent' | 'good' | 'slow' | 'timeout'\r\n if (result.executionTime > 1000) {\r\n performance = 'timeout'\r\n issues.push('Script execution is very slow (>1s)')\r\n } else if (result.executionTime > 500) {\r\n performance = 'slow'\r\n issues.push('Script execution is slow (>500ms)')\r\n } else if (result.executionTime > 100) {\r\n performance = 'good'\r\n } else {\r\n performance = 'excellent'\r\n }\r\n \r\n // Feature validation\r\n let hasRequiredFeatures = true\r\n \r\n if (config.features.includes('directory') && !result.output.includes('projects')) {\r\n hasRequiredFeatures = false\r\n issues.push('Directory feature not working properly')\r\n }\r\n \r\n if (config.features.includes('model') && !result.output.includes('Opus')) {\r\n hasRequiredFeatures = false\r\n issues.push('Model feature not working properly')\r\n }\r\n \r\n if (config.features.includes('git') && config.ccusageIntegration && !result.output.includes('git')) {\r\n suggestions.push('Git integration may require actual git repository')\r\n }\r\n \r\n // Error analysis\r\n if (result.error) {\r\n issues.push(`Script errors: ${result.error}`)\r\n }\r\n \r\n if (!result.success) {\r\n issues.push('Script failed to execute successfully')\r\n }\r\n \r\n // Performance suggestions\r\n if (config.features.length > 6) {\r\n suggestions.push('Consider reducing number of features for better performance')\r\n }\r\n \r\n if (config.ccusageIntegration && result.executionTime > 200) {\r\n suggestions.push('ccusage integration may slow down statusline - consider caching')\r\n }\r\n \r\n return {\r\n performance,\r\n hasRequiredFeatures,\r\n issues,\r\n suggestions\r\n }\r\n}","import { StatuslineConfig } from './prompts.js'\r\nimport { generateBashStatusline } from '../generators/bash-generator.js'\r\nimport { testStatuslineScript, generateMockClaudeInput, analyzeTestResult } from '../utils/tester.js'\r\nimport { promises as fs } from 'fs'\r\nimport chalk from 'chalk'\r\nimport ora from 'ora'\r\n\r\nexport async function previewCommand(scriptPath: string): Promise<void> {\r\n console.log(chalk.cyan('๐ Statusline Preview Mode\\n'))\r\n \r\n let script: string\r\n \r\n // Load existing statusline script\r\n try {\r\n const spinner = ora(`Loading statusline script from ${scriptPath}...`).start()\r\n script = await fs.readFile(scriptPath, 'utf-8')\r\n spinner.succeed('Script loaded!')\r\n \r\n // Try to extract config info from the script header\r\n const headerMatch = script.match(/# Theme: (\\w+) \\| Colors: (\\w+) \\| Features: ([^\\n]+)/i)\r\n if (headerMatch) {\r\n console.log(chalk.yellow('Detected Configuration:'))\r\n console.log(` Theme: ${headerMatch[1]}`)\r\n console.log(` Colors: ${headerMatch[2]}`) \r\n console.log(` Features: ${headerMatch[3]}\\n`)\r\n }\r\n \r\n // Extract generation info if available\r\n const generationMatch = script.match(/# Generated by cc-statusline.*\\n# Custom Claude Code statusline - Created: ([^\\n]+)/i)\r\n if (generationMatch) {\r\n console.log(chalk.gray(`Generated: ${generationMatch[1]}\\n`))\r\n }\r\n \r\n } catch (error) {\r\n console.error(chalk.red(`โ Failed to load script: ${error instanceof Error ? error.message : String(error)}`))\r\n return\r\n }\r\n \r\n // Test the script\r\n const testSpinner = ora('Testing statusline with mock data...').start()\r\n const mockInput = generateMockClaudeInput()\r\n \r\n console.log(chalk.gray('\\nMock Claude Code Input:'))\r\n console.log(chalk.gray(JSON.stringify(mockInput, null, 2)))\r\n \r\n const testResult = await testStatuslineScript(script, mockInput)\r\n \r\n if (testResult.success) {\r\n testSpinner.succeed(`Test completed in ${testResult.executionTime}ms`)\r\n \r\n console.log(chalk.green('\\nโ
Statusline Output:'))\r\n console.log(chalk.white('โ'.repeat(60)))\r\n console.log(testResult.output)\r\n console.log(chalk.white('โ'.repeat(60)))\r\n \r\n // Basic performance analysis\r\n console.log(chalk.cyan(`\\n๐ Performance: ${getPerformanceEmoji(getPerformanceLevel(testResult.executionTime))} ${getPerformanceLevel(testResult.executionTime)} (${testResult.executionTime}ms)`))\r\n \r\n // Basic output validation\r\n if (testResult.output.includes('๐') || testResult.output.includes('๐ฟ') || testResult.output.includes('๐ค')) {\r\n console.log(chalk.green('โ
Statusline features appear to be working'))\r\n } else {\r\n console.log(chalk.yellow('โ ๏ธ Basic features may not be displaying correctly'))\r\n }\r\n \r\n } else {\r\n testSpinner.fail('Test failed')\r\n console.error(chalk.red(`\\nโ Error: ${testResult.error}`))\r\n if (testResult.output) {\r\n console.log(chalk.gray('\\nPartial output:'))\r\n console.log(testResult.output)\r\n }\r\n }\r\n \r\n console.log(chalk.green('\\nโจ Preview complete! Use `cc-statusline init` to generate a new statusline.'))\r\n}\r\n\r\nfunction getPerformanceEmoji(performance: string): string {\r\n switch (performance) {\r\n case 'excellent': return '๐'\r\n case 'good': return 'โ
'\r\n case 'slow': return 'โ ๏ธ'\r\n case 'timeout': return '๐'\r\n default: return 'โ'\r\n }\r\n}\r\n\r\nfunction getPerformanceLevel(executionTime: number): string {\r\n if (executionTime > 1000) return 'timeout'\r\n if (executionTime > 500) return 'slow'\r\n if (executionTime > 100) return 'good'\r\n return 'excellent'\r\n}","import { Command } from 'commander'\r\nimport { initCommand } from './cli/commands.js'\r\nimport chalk from 'chalk'\r\n\r\n// Hard-code version to avoid bundling issues\r\nconst VERSION = '1.3.2'\r\n\r\nconst program = new Command()\r\n\r\nprogram\r\n .name('cc-statusline')\r\n .description('Interactive CLI tool for generating custom Claude Code statuslines')\r\n .version(VERSION)\r\n\r\nprogram\r\n .command('init')\r\n .description('Create a custom statusline with interactive prompts')\r\n .option('-o, --output <path>', 'Output path for statusline.sh', './.claude/statusline.sh')\r\n .option('--no-install', 'Don\\'t automatically install to .claude/statusline.sh')\r\n .action(initCommand)\r\n\r\nprogram\r\n .command('preview')\r\n .description('Preview existing statusline.sh with mock data')\r\n .argument('<script-path>', 'Path to statusline.sh file to preview')\r\n .action(async (scriptPath) => {\r\n const { previewCommand } = await import('./cli/preview.js')\r\n await previewCommand(scriptPath)\r\n })\r\n\r\nprogram\r\n .command('test')\r\n .description('Test statusline with real Claude Code JSON input')\r\n .option('-c, --config <path>', 'Configuration file to test')\r\n .action(() => {\r\n console.log(chalk.yellow('Test command coming soon!'))\r\n })\r\n\r\n// Show help if no command provided\r\nif (!process.argv.slice(2).length) {\r\n program.outputHelp()\r\n}\r\n\r\nprogram.parse(process.argv)","import { collectConfiguration, displayConfigSummary } from './prompts.js'\r\nimport { generateBashStatusline } from '../generators/bash-generator.js'\r\nimport { validateConfig } from '../utils/validator.js'\r\nimport { installStatusline } from '../utils/installer.js'\r\nimport chalk from 'chalk'\r\nimport ora from 'ora'\r\nimport path from 'path'\r\nimport os from 'os'\r\nimport { execSync } from 'child_process'\r\n\r\ninterface InitOptions {\r\n output?: string\r\n install?: boolean\r\n}\r\n\r\nfunction checkJqInstallation(): boolean {\r\n try {\r\n execSync('command -v jq', { stdio: 'ignore' })\r\n return true\r\n } catch {\r\n return false\r\n }\r\n}\r\n\r\nfunction getJqInstallInstructions(): string {\r\n const platform = process.platform\r\n \r\n if (platform === 'darwin') {\r\n return `\r\n${chalk.cyan('๐ฆ Install jq for better performance and reliability:')}\r\n\r\n${chalk.green('Using Homebrew (recommended):')}\r\n brew install jq\r\n\r\n${chalk.green('Using MacPorts:')}\r\n sudo port install jq\r\n\r\n${chalk.green('Or download directly:')}\r\n https://github.com/jqlang/jq/releases\r\n`\r\n } else if (platform === 'linux') {\r\n return `\r\n${chalk.cyan('๐ฆ Install jq for better performance and reliability:')}\r\n\r\n${chalk.green('Ubuntu/Debian:')}\r\n sudo apt-get install jq\r\n\r\n${chalk.green('CentOS/RHEL/Fedora:')}\r\n sudo yum install jq\r\n\r\n${chalk.green('Arch Linux:')}\r\n sudo pacman -S jq\r\n\r\n${chalk.green('Or download directly:')}\r\n https://github.com/jqlang/jq/releases\r\n`\r\n } else if (platform === 'win32') {\r\n return `\r\n${chalk.cyan('๐ฆ Install jq for better performance and reliability:')}\r\n\r\n${chalk.green('Option 1: Using Package Manager')}\r\n ${chalk.dim('Chocolatey:')} choco install jq\r\n ${chalk.dim('Scoop:')} scoop install jq\r\n\r\n${chalk.green('Option 2: Manual Download')}\r\n 1. Download from: https://github.com/jqlang/jq/releases/latest\r\n 2. Choose file:\r\n ${chalk.dim('โข 64-bit Windows:')} jq-windows-amd64.exe\r\n ${chalk.dim('โข 32-bit Windows:')} jq-windows-i386.exe\r\n 3. Rename to: jq.exe\r\n 4. Move to: C:\\\\Windows\\\\System32\\\\ ${chalk.dim('(or add to PATH)')}\r\n 5. Test: Open new terminal and run: jq --version\r\n`\r\n } else {\r\n return `\r\n${chalk.cyan('๐ฆ Install jq for better performance and reliability:')}\r\n\r\n${chalk.green('Download for your platform:')}\r\n https://github.com/jqlang/jq/releases\r\n`\r\n }\r\n}\r\n\r\nexport async function initCommand(options: InitOptions): Promise<void> {\r\n try {\r\n const spinner = ora('Initializing statusline generator...').start()\r\n await new Promise(resolve => setTimeout(resolve, 500)) // Brief pause for UX\r\n spinner.stop()\r\n\r\n // Check for jq installation\r\n const hasJq = checkJqInstallation()\r\n if (!hasJq) {\r\n console.log(chalk.yellow('\\nโ ๏ธ jq is not installed'))\r\n console.log(chalk.dim('Your statusline will work without jq, but with limited functionality:'))\r\n console.log(chalk.dim(' โข Context remaining percentage won\\'t be displayed'))\r\n console.log(chalk.dim(' โข Token statistics may not work'))\r\n console.log(chalk.dim(' โข Performance will be slower'))\r\n console.log(getJqInstallInstructions())\r\n \r\n // Ask if they want to continue without jq\r\n const inquirer = (await import('inquirer')).default\r\n const { continueWithoutJq } = await inquirer.prompt([{\r\n type: 'confirm',\r\n name: 'continueWithoutJq',\r\n message: 'Continue without jq?',\r\n default: true\r\n }])\r\n \r\n if (!continueWithoutJq) {\r\n console.log(chalk.cyan('\\n๐ Install jq and run this command again'))\r\n process.exit(0)\r\n }\r\n }\r\n\r\n // Collect user configuration\r\n const config = await collectConfiguration()\r\n \r\n // Validate configuration\r\n const validation = validateConfig(config)\r\n if (!validation.isValid) {\r\n console.error(chalk.red('โ Configuration validation failed:'))\r\n validation.errors.forEach(error => console.error(chalk.red(` โข ${error}`)))\r\n process.exit(1)\r\n }\r\n\r\n // Generate statusline script\r\n const generationSpinner = ora('Generating statusline script...').start()\r\n \r\n const script = generateBashStatusline(config)\r\n const filename = 'statusline.sh'\r\n \r\n generationSpinner.succeed('Statusline script generated!')\r\n\r\n // Show preview of what it will look like\r\n console.log(chalk.cyan('\\nโจ Your statusline will look like:'))\r\n console.log(chalk.white('โ'.repeat(60)))\r\n \r\n // Generate preview using the test function\r\n const { testStatuslineScript, generateMockClaudeInput } = await import('../utils/tester.js')\r\n const mockInput = generateMockClaudeInput()\r\n const testResult = await testStatuslineScript(script, mockInput)\r\n \r\n if (testResult.success) {\r\n console.log(testResult.output)\r\n } else {\r\n console.log(chalk.gray('๐ ~/projects/my-app ๐ฟ main ๐ค Claude ๐ต $2.48 ($12.50/h)'))\r\n console.log(chalk.gray('(Preview unavailable - will work when Claude Code runs it)'))\r\n }\r\n \r\n console.log(chalk.white('โ'.repeat(60)))\r\n\r\n // Determine output path based on installation location\r\n const isGlobal = config.installLocation === 'global'\r\n const baseDir = isGlobal ? os.homedir() : '.'\r\n const outputPath = options.output || path.join(baseDir, '.claude', filename)\r\n const resolvedPath = path.resolve(outputPath)\r\n\r\n // Install the statusline\r\n if (options.install !== false) {\r\n console.log(chalk.cyan('\\n๐ฆ Installing statusline...'))\r\n \r\n try {\r\n await installStatusline(script, resolvedPath, config)\r\n \r\n console.log(chalk.green('\\nโ
Statusline installed!'))\r\n console.log(chalk.green('\\n๐ Success! Your custom statusline is ready!'))\r\n console.log(chalk.cyan(`\\n๐ ${isGlobal ? 'Global' : 'Project'} installation complete: ${chalk.white(resolvedPath)}`))\r\n console.log(chalk.cyan('\\nNext steps:'))\r\n console.log(chalk.white(' 1. Restart Claude Code to see your new statusline'))\r\n console.log(chalk.white(' 2. Usage statistics work via: npx ccusage@latest'))\r\n \r\n } catch (error) {\r\n console.log(chalk.red('\\nโ Failed to install statusline'))\r\n \r\n if (error instanceof Error && error.message === 'USER_CANCELLED_OVERWRITE') {\r\n console.log(chalk.yellow('\\nโ ๏ธ Installation cancelled. Existing statusline.sh was not overwritten.'))\r\n } else if (error instanceof Error && error.message === 'SETTINGS_UPDATE_FAILED') {\r\n const commandPath = isGlobal ? '~/.claude/statusline.sh' : '.claude/statusline.sh'\r\n console.log(chalk.yellow('\\nโ ๏ธ Settings.json could not be updated automatically.'))\r\n console.log(chalk.cyan('\\nManual Configuration Required:'))\r\n console.log(chalk.white(`Add this to your ${isGlobal ? '~/.claude' : '.claude'}/settings.json file:`))\r\n console.log(chalk.gray('\\n{'))\r\n console.log(chalk.gray(' \"statusLine\": {'))\r\n console.log(chalk.gray(' \"type\": \"command\",'))\r\n console.log(chalk.gray(` \"command\": \"${commandPath}\",`))\r\n console.log(chalk.gray(' \"padding\": 0'))\r\n console.log(chalk.gray(' }'))\r\n console.log(chalk.gray('}'))\r\n console.log(chalk.cyan(`\\n๐ Statusline script saved to: ${chalk.white(resolvedPath)}`))\r\n } else {\r\n console.error(chalk.red(`Error: ${error instanceof Error ? error.message : String(error)}`))\r\n console.log(chalk.cyan(`\\n๐ You can manually save the script to: ${chalk.white(resolvedPath)}`))\r\n }\r\n }\r\n } else {\r\n // Just display where to save it\r\n console.log(chalk.green('\\nโ
Statusline generated successfully!'))\r\n console.log(chalk.cyan(`\\n๐ Save this script to: ${chalk.white(resolvedPath)}`))\r\n console.log(chalk.cyan('\\nThen restart Claude Code to see your new statusline.'))\r\n }\r\n\r\n } catch (error) {\r\n console.error(chalk.red('โ An error occurred:'))\r\n console.error(chalk.red(error instanceof Error ? error.message : String(error)))\r\n process.exit(1)\r\n }\r\n}","import inquirer from 'inquirer'\r\n\r\nexport interface StatuslineConfig {\r\n features: string[]\r\n runtime: 'bash' | 'python' | 'node'\r\n colors: boolean\r\n theme: 'minimal' | 'detailed' | 'compact'\r\n ccusageIntegration: boolean\r\n logging: boolean\r\n customEmojis: boolean\r\n installLocation?: 'global' | 'project'\r\n}\r\n\r\nexport async function collectConfiguration(): Promise<StatuslineConfig> {\r\n console.log('๐ Welcome to cc-statusline! Let\\'s create your custom Claude Code statusline.\\n')\r\n console.log('โจ All features are enabled by default. Use โ/โ arrows to navigate, SPACE to toggle, ENTER to continue.\\n')\r\n \r\n const config = await inquirer.prompt([\r\n {\r\n type: 'checkbox',\r\n name: 'features',\r\n message: 'Select statusline features (scroll down for more options):',\r\n choices: [\r\n { name: '๐ Working Directory', value: 'directory', checked: true },\r\n { name: '๐ฟ Git Branch', value: 'git', checked: true },\r\n { name: '๐ค Model Name & Version', value: 'model', checked: true },\r\n { name: '๐ง Context Remaining', value: 'context', checked: true },\r\n { name: '๐ต Usage & Cost', value: 'usage', checked: true },\r\n { name: 'โ Session Time Remaining', value: 'session', checked: true },\r\n { name: '๐ Token Statistics', value: 'tokens', checked: true },\r\n { name: 'โก Burn Rate (tokens/min)', value: 'burnrate', checked: true }\r\n ],\r\n validate: (answer: string[]) => {\r\n if (answer.length < 1) {\r\n return 'You must choose at least one feature.'\r\n }\r\n return true\r\n },\r\n pageSize: 10\r\n },\r\n {\r\n type: 'confirm',\r\n name: 'colors',\r\n message: '\\n๐จ Enable modern color scheme and emojis?',\r\n default: true\r\n },\r\n {\r\n type: 'confirm',\r\n name: 'logging',\r\n message: '\\n๐ Enable debug logging to .claude/statusline.log?',\r\n default: false\r\n },\r\n {\r\n type: 'list',\r\n name: 'installLocation',\r\n message: '\\n๐ Where would you like to install the statusline?',\r\n choices: [\r\n { name: '๐ Global (~/.claude) - Use across all projects', value: 'global' },\r\n { name: '๐ Project (./.claude) - Only for this project', value: 'project' }\r\n ],\r\n default: 'project'\r\n }\r\n ])\r\n\r\n // Set intelligent defaults\r\n return {\r\n features: config.features,\r\n runtime: 'bash',\r\n colors: config.colors,\r\n theme: 'detailed',\r\n ccusageIntegration: true, // Always enabled since npx works\r\n logging: config.logging,\r\n customEmojis: false,\r\n installLocation: config.installLocation\r\n } as StatuslineConfig\r\n}\r\n\r\nexport function displayConfigSummary(config: StatuslineConfig): void {\r\n console.log('\\nโ
Configuration Summary:')\r\n console.log(` Runtime: ${config.runtime}`)\r\n console.log(` Theme: ${config.theme}`)\r\n console.log(` Colors: ${config.colors ? 'โ
' : 'โ'}`)\r\n console.log(` Features: ${config.features.join(', ')}`)\r\n \r\n if (config.ccusageIntegration) {\r\n console.log(' ๐ ccusage integration enabled')\r\n }\r\n \r\n if (config.logging) {\r\n console.log(' ๐ Debug logging enabled')\r\n }\r\n \r\n console.log('')\r\n}","import { StatuslineConfig } from '../cli/prompts.js'\r\nimport { generateColorBashCode, generateBasicColors } from '../features/colors.js'\r\nimport { generateGitBashCode, generateGitDisplayCode, generateGitUtilities } from '../features/git.js'\r\nimport { generateUsageBashCode, generateUsageDisplayCode, generateUsageUtilities } from '../features/usage.js'\r\n\r\n// Version will be updated when releasing\r\nconst VERSION = '1.3.2'\r\n\r\nexport function generateBashStatusline(config: StatuslineConfig): string {\r\n const hasGit = config.features.includes('git')\r\n const hasUsage = config.features.some(f => ['usage', 'session', 'tokens', 'burnrate'].includes(f))\r\n const hasDirectory = config.features.includes('directory')\r\n const hasModel = config.features.includes('model')\r\n const hasContext = config.features.includes('context')\r\n\r\n // Build usage feature config\r\n const usageConfig = {\r\n enabled: hasUsage && config.ccusageIntegration,\r\n showCost: config.features.includes('usage'),\r\n showTokens: config.features.includes('tokens'),\r\n showBurnRate: config.features.includes('burnrate'),\r\n showSession: config.features.includes('session'),\r\n showProgressBar: config.theme !== 'minimal' && config.features.includes('session')\r\n }\r\n\r\n // Build git feature config\r\n const gitConfig = {\r\n enabled: hasGit,\r\n showBranch: hasGit,\r\n showChanges: false, // Removed delta changes per user request\r\n compactMode: config.theme === 'compact'\r\n }\r\n\r\n const timestamp = new Date().toISOString()\r\n const script = `#!/bin/bash\r\n# Generated by cc-statusline v${VERSION} (https://www.npmjs.com/package/@chongdashu/cc-statusline)\r\n# Custom Claude Code statusline - Created: ${timestamp}\r\n# Theme: ${config.theme} | Colors: ${config.colors} | Features: ${config.features.join(', ')}\r\nSTATUSLINE_VERSION=\"${VERSION}\"\r\n\r\ninput=$(cat)\r\n${config.logging ? generateLoggingCode() : ''}\r\n${generateColorBashCode({ enabled: config.colors, theme: config.theme })}\r\n${config.colors ? generateBasicColors() : ''}\r\n${hasUsage ? generateUsageUtilities() : ''}\r\n${hasGit ? generateGitUtilities() : ''}\r\n${generateBasicDataExtraction(hasDirectory, hasModel, hasContext)}\r\n${hasGit ? generateGitBashCode(gitConfig, config.colors) : ''}\r\n${hasContext ? generateContextBashCode(config.colors) : ''}\r\n${hasUsage ? generateUsageBashCode(usageConfig, config.colors) : ''}\r\n${config.logging ? generateLoggingOutput() : ''}\r\n${generateDisplaySection(config, gitConfig, usageConfig)}\r\n`\r\n\r\n return script.replace(/\\n\\n\\n+/g, '\\n\\n').trim() + '\\n'\r\n}\r\n\r\nfunction generateLoggingCode(): string {\r\n return `\r\n# Get the directory where this statusline script is located\r\nSCRIPT_DIR=\"$(cd \"$(dirname \"\\${BASH_SOURCE[0]}\")\" && pwd)\"\r\nLOG_FILE=\"\\${SCRIPT_DIR}/statusline.log\"\r\nTIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')\r\n\r\n# ---- check jq availability ----\r\nHAS_JQ=0\r\nif command -v jq >/dev/null 2>&1; then\r\n HAS_JQ=1\r\nfi\r\n\r\n# ---- logging ----\r\n{\r\n echo \"[$TIMESTAMP] Status line triggered (cc-statusline v\\${STATUSLINE_VERSION})\"\r\n echo \"[$TIMESTAMP] Input:\"\r\n if [ \"$HAS_JQ\" -eq 1 ]; then\r\n echo \"$input\" | jq . 2>/dev/null || echo \"$input\"\r\n echo \"[$TIMESTAMP] Using jq for JSON parsing\"\r\n else\r\n echo \"$input\"\r\n echo \"[$TIMESTAMP] WARNING: jq not found, using bash fallback for JSON parsing\"\r\n fi\r\n echo \"---\"\r\n} >> \"$LOG_FILE\" 2>/dev/null\r\n`\r\n}\r\n\r\nfunction generateJsonExtractorCode(): string {\r\n return `\r\n# ---- JSON extraction utilities ----\r\n# Pure bash JSON value extractor (fallback when jq not available)\r\nextract_json_string() {\r\n local json=\"$1\"\r\n local key=\"$2\"\r\n local default=\"\\${3:-}\"\r\n \r\n # For nested keys like workspace.current_dir, get the last part\r\n local field=\"\\${key##*.}\"\r\n field=\"\\${field%% *}\" # Remove any jq operators\r\n \r\n # Try to extract string value (quoted)\r\n local value=$(echo \"$json\" | grep -o \"\\\\\"\\\\$\\{field}\\\\\"[[:space:]]*:[[:space:]]*\\\\\"[^\\\\\"]*\\\\\"\" | head -1 | sed 's/.*:[[:space:]]*\"\\\\([^\"]*\\\\)\".*/\\\\1/')\r\n \r\n # Convert escaped backslashes to forward slashes for Windows paths\r\n if [ -n \"$value\" ]; then\r\n value=$(echo \"$value\" | sed 's/\\\\\\\\\\\\\\\\/\\\\//g')\r\n fi\r\n \r\n # If no string value found, try to extract number value (unquoted)\r\n if [ -z \"$value\" ] || [ \"$value\" = \"null\" ]; then\r\n value=$(echo \"$json\" | grep -o \"\\\\\"\\\\$\\{field}\\\\\"[[:space:]]*:[[:space:]]*[0-9.]\\\\+\" | head -1 | sed 's/.*:[[:space:]]*\\\\([0-9.]\\\\+\\\\).*/\\\\1/')\r\n fi\r\n \r\n # Return value or default\r\n if [ -n \"$value\" ] && [ \"$value\" != \"null\" ]; then\r\n echo \"$value\"\r\n else\r\n echo \"$default\"\r\n fi\r\n}\r\n`\r\n}\r\n\r\nfunction generateBasicDataExtraction(hasDirectory: boolean, hasModel: boolean, hasContext: boolean): string {\r\n return `\r\n${generateJsonExtractorCode()}\r\n# ---- basics ----\r\nif [ \"$HAS_JQ\" -eq 1 ]; then${hasDirectory ? `\r\n current_dir=$(echo \"$input\" | jq -r '.workspace.current_dir // .cwd // \"unknown\"' 2>/dev/null | sed \"s|^$HOME|~|g\")` : ''}${hasModel ? `\r\n model_name=$(echo \"$input\" | jq -r '.model.display_name // \"Claude\"' 2>/dev/null)\r\n model_version=$(echo \"$input\" | jq -r '.model.version // \"\"' 2>/dev/null)` : ''}${hasContext ? `\r\n session_id=$(echo \"$input\" | jq -r '.session_id // \"\"' 2>/dev/null)` : ''}\r\n cc_version=$(echo \"$input\" | jq -r '.version // \"\"' 2>/dev/null)\r\n output_style=$(echo \"$input\" | jq -r '.output_style.name // \"\"' 2>/dev/null)\r\nelse${hasDirectory ? `\r\n # Bash fallback for JSON extraction\r\n # Extract current_dir from workspace object - look for the pattern workspace\":{\"current_dir\":\"...\"}\r\n current_dir=$(echo \"$input\" | grep -o '\"workspace\"[[:space:]]*:[[:space:]]*{[^}]*\"current_dir\"[[:space:]]*:[[:space:]]*\"[^\"]*\"' | sed 's/.*\"current_dir\"[[:space:]]*:[[:space:]]*\"\\\\([^\"]*\\\\)\".*/\\\\1/' | sed 's/\\\\\\\\\\\\\\\\/\\\\//g')\r\n \r\n # Fall back to cwd if workspace extraction failed\r\n if [ -z \"$current_dir\" ] || [ \"$current_dir\" = \"null\" ]; then\r\n current_dir=$(echo \"$input\" | grep -o '\"cwd\"[[:space:]]*:[[:space:]]*\"[^\"]*\"' | sed 's/.*\"cwd\"[[:space:]]*:[[:space:]]*\"\\\\([^\"]*\\\\)\".*/\\\\1/' | sed 's/\\\\\\\\\\\\\\\\/\\\\//g')\r\n fi\r\n \r\n # Fallback to unknown if all extraction failed\r\n [ -z \"$current_dir\" ] && current_dir=\"unknown\"\r\n current_dir=$(echo \"$current_dir\" | sed \"s|^$HOME|~|g\")` : ''}${hasModel ? `\r\n \r\n # Extract model name from nested model object\r\n model_name=$(echo \"$input\" | grep -o '\"model\"[[:space:]]*:[[:space:]]*{[^}]*\"display_name\"[[:space:]]*:[[:space:]]*\"[^\"]*\"' | sed 's/.*\"display_name\"[[:space:]]*:[[:space:]]*\"\\\\([^\"]*\\\\)\".*/\\\\1/')\r\n [ -z \"$model_name\" ] && model_name=\"Claude\"\r\n # Model version is in the model ID, not a separate field \r\n model_version=\"\" # Not available in Claude Code JSON` : ''}${hasContext ? `\r\n session_id=$(extract_json_string \"$input\" \"session_id\" \"\")` : ''}\r\n # CC version is at the root level\r\n cc_version=$(echo \"$input\" | grep -o '\"version\"[[:space:]]*:[[:space:]]*\"[^\"]*\"' | head -1 | sed 's/.*\"version\"[[:space:]]*:[[:space:]]*\"\\\\([^\"]*\\\\)\".*/\\\\1/')\r\n # Output style is nested\r\n output_style=$(echo \"$input\" | grep -o '\"output_style\"[[:space:]]*:[[:space:]]*{[^}]*\"name\"[[:space:]]*:[[:space:]]*\"[^\"]*\"' | sed 's/.*\"name\"[[:space:]]*:[[:space:]]*\"\\\\([^\"]*\\\\)\".*/\\\\1/')\r\nfi\r\n`\r\n}\r\n\r\nfunction generateContextBashCode(colors: boolean): string {\r\n return `\r\n# ---- context window calculation ----\r\ncontext_pct=\"\"\r\ncontext_color() { if [ \"$use_color\" -eq 1 ]; then printf '\\\\033[1;37m'; fi; } # default white\r\n\r\n# Determine max context based on model\r\nget_max_context() {\r\n local model_name=\"$1\"\r\n case \"$model_name\" in\r\n *\"Opus 4\"*|*\"opus 4\"*|*\"Opus\"*|*\"opus\"*)\r\n echo \"200000\" # 200K for all Opus versions\r\n ;;\r\n *\"Sonnet 4\"*|*\"sonnet 4\"*|*\"Sonnet 3.5\"*|*\"sonnet 3.5\"*|*\"Sonnet\"*|*\"sonnet\"*)\r\n echo \"200000\" # 200K for Sonnet 3.5+ and 4.x\r\n ;;\r\n *\"Haiku 3.5\"*|*\"haiku 3.5\"*|*\"Haiku 4\"*|*\"haiku 4\"*|*\"Haiku\"*|*\"haiku\"*)\r\n echo \"200000\" # 200K for modern Haiku\r\n ;;\r\n *\"Claude 3 Haiku\"*|*\"claude 3 haiku\"*)\r\n echo \"100000\" # 100K for original Claude 3 Haiku\r\n ;;\r\n *)\r\n echo \"200000\" # Default to 200K\r\n ;;\r\n esac\r\n}\r\n\r\nif [ -n \"$session_id\" ] && [ \"$HAS_JQ\" -eq 1 ]; then\r\n MAX_CONTEXT=$(get_max_context \"$model_name\")\r\n \r\n # Convert current dir to session file path\r\n project_dir=$(echo \"$current_dir\" | sed \"s|~|$HOME|g\" | sed 's|/|-|g' | sed 's|^-||')\r\n session_file=\"$HOME/.claude/projects/-\\${project_dir}/\\${session_id}.jsonl\"\r\n \r\n if [ -f \"$session_file\" ]; then\r\n # Get the latest input token count from the session file\r\n latest_tokens=$(tail -20 \"$session_file\" | jq -r 'select(.message.usage) | .message.usage | ((.input_tokens // 0) + (.cache_read_input_tokens // 0))' 2>/dev/null | tail -1)\r\n \r\n if [ -n \"$latest_tokens\" ] && [ \"$latest_tokens\" -gt 0 ]; then\r\n context_used_pct=$(( latest_tokens * 100 / MAX_CONTEXT ))\r\n context_remaining_pct=$(( 100 - context_used_pct ))\r\n \r\n # Set color based on remaining percentage\r\n if [ \"$context_remaining_pct\" -le 20 ]; then\r\n context_color() { if [ \"$use_color\" -eq 1 ]; then printf '\\\\033[38;5;203m'; fi; } # coral red\r\n elif [ \"$context_remaining_pct\" -le 40 ]; then\r\n context_color() { if [ \"$use_color\" -eq 1 ]; then printf '\\\\033[38;5;215m'; fi; } # peach\r\n else\r\n context_color() { if [ \"$use_color\" -eq 1 ]; then printf '\\\\033[38;5;158m'; fi; } # mint green\r\n fi\r\n \r\n context_pct=\"\\${context_remaining_pct}%\"\r\n fi\r\n fi\r\nfi\r\n`\r\n}\r\n\r\nfunction generateLoggingOutput(): string {\r\n return `\r\n# ---- log extracted data ----\r\n{\r\n echo \"[\\$TIMESTAMP] Extracted: dir=\\${current_dir:-}, model=\\${model_name:-}, version=\\${model_version:-}, git=\\${git_branch:-}, context=\\${context_pct:-}, cost=\\${cost_usd:-}, cost_ph=\\${cost_per_hour:-}, tokens=\\${tot_tokens:-}, tpm=\\${tpm:-}, session_pct=\\${session_pct:-}\"\r\n if [ \"$HAS_JQ\" -eq 0 ]; then\r\n echo \"[\\$TIMESTAMP] Note: Context, tokens, and session info require jq for full functionality\"\r\n fi\r\n} >> \"$LOG_FILE\" 2>/dev/null\r\n`\r\n}\r\n\r\nfunction generateDisplaySection(config: StatuslineConfig, gitConfig: any, usageConfig: any): string {\r\n const emojis = config.colors && !config.customEmojis\r\n\r\n return `\r\n# ---- render statusline ----\r\n# Line 1: Core info (directory, git, model, claude code version, output style)\r\n${config.features.includes('directory') ? `printf '๐ %s%s%s' \"$(dir_color)\" \"$current_dir\" \"$(rst)\"` : ''}${gitConfig.enabled ? `\r\nif [ -n \"$git_branch\" ]; then\r\n printf ' ๐ฟ %s%s%s' \"$(git_color)\" \"$git_branch\" \"$(rst)\"\r\nfi` : ''}${config.features.includes('model') ? `\r\nprintf ' ๐ค %s%s%s' \"$(model_color)\" \"$model_name\" \"$(rst)\"\r\nif [ -n \"$model_version\" ] && [ \"$model_version\" != \"null\" ]; then\r\n printf ' ๐ท๏ธ %s%s%s' \"$(version_color)\" \"$model_version\" \"$(rst)\"\r\nfi` : ''}\r\nif [ -n \"$cc_version\" ] && [ \"$cc_version\" != \"null\" ]; then\r\n printf ' ๐ %sv%s%s' \"$(cc_version_color)\" \"$cc_version\" \"$(rst)\"\r\nfi\r\nif [ -n \"$output_style\" ] && [ \"$output_style\" != \"null\" ]; then\r\n printf ' ๐จ %s%s%s' \"$(style_color)\" \"$output_style\" \"$(rst)\"\r\nfi\r\n\r\n# Line 2: Context and session time\r\nline2=\"\"${config.features.includes('context') ? `\r\nif [ -n \"$context_pct\" ]; then\r\n context_bar=$(progress_bar \"$context_remaining_pct\" 10)\r\n line2=\"๐ง $(context_color)Context Remaining: \\${context_pct} [\\${context_bar}]$(rst)\"\r\nfi` : ''}${usageConfig.showSession ? `\r\nif [ -n \"$session_txt\" ]; then\r\n if [ -n \"$line2\" ]; then\r\n line2=\"$line2 โ $(session_color)\\${session_txt}$(rst) $(session_color)[\\${session_bar}]$(rst)\"\r\n else\r\n line2=\"โ $(session_color)\\${session_txt}$(rst) $(session_color)[\\${session_bar}]$(rst)\"\r\n fi\r\nfi` : ''}${config.features.includes('context') ? `\r\nif [ -z \"$line2\" ] && [ -z \"$context_pct\" ]; then\r\n line2=\"๐ง $(context_color)Context Remaining: TBD$(rst)\"\r\nfi` : ''}\r\n\r\n# Line 3: Cost and usage analytics\r\nline3=\"\"${usageConfig.showCost ? `\r\nif [ -n \"$cost_usd\" ] && [[ \"$cost_usd\" =~ ^[0-9.]+$ ]]; then${usageConfig.showBurnRate ? `\r\n if [ -n \"$cost_per_hour\" ] && [[ \"$cost_per_hour\" =~ ^[0-9.]+$ ]]; then\r\n cost_per_hour_formatted=$(printf '%.2f' \"$cost_per_hour\")\r\n line3=\"๐ฐ $(cost_color)\\\\$$(printf '%.2f' \"$cost_usd\")$(rst) ($(burn_color)\\\\$\\${cost_per_hour_formatted}/h$(rst))\"\r\n else\r\n line3=\"๐ฐ $(cost_color)\\\\$$(printf '%.2f' \"$cost_usd\")$(rst)\"\r\n fi` : `\r\n line3=\"๐ฐ $(cost_color)\\\\$$(printf '%.2f' \"$cost_usd\")$(rst)\"`}\r\nfi` : ''}${usageConfig.showTokens ? `\r\nif [ -n \"$tot_tokens\" ] && [[ \"$tot_tokens\" =~ ^[0-9]+$ ]]; then${usageConfig.showBurnRate ? `\r\n if [ -n \"$tpm\" ] && [[ \"$tpm\" =~ ^[0-9.]+$ ]]; then\r\n tpm_formatted=$(printf '%.0f' \"$tpm\")\r\n if [ -n \"$line3\" ]; then\r\n line3=\"$line3 ๐ $(usage_color)\\${tot_tokens} tok (\\${tpm_formatted} tpm)$(rst)\"\r\n else\r\n line3=\"๐ $(usage_color)\\${tot_tokens} tok (\\${tpm_formatted} tpm)$(rst)\"\r\n fi\r\n else\r\n if [ -n \"$line3\" ]; then\r\n line3=\"$line3 ๐ $(usage_color)\\${tot_tokens} tok$(rst)\"\r\n else\r\n line3=\"๐ $(usage_color)\\${tot_tokens} tok$(rst)\"\r\n fi\r\n fi` : `\r\n if [ -n \"$line3\" ]; then\r\n line3=\"$line3 ๐ $(usage_color)\\${tot_tokens} tok$(rst)\"\r\n else\r\n line3=\"๐ $(usage_color)\\${tot_tokens} tok$(rst)\"\r\n fi`}\r\nfi` : ''}\r\n\r\n# Print lines\r\nif [ -n \"$line2\" ]; then\r\n printf '\\\\n%s' \"$line2\"\r\nfi\r\nif [ -n \"$line3\" ]; then\r\n printf '\\\\n%s' \"$line3\"\r\nfi\r\nprintf '\\\\n'`\r\n}","export interface ColorConfig {\r\n enabled: boolean\r\n theme: 'minimal' | 'detailed' | 'compact'\r\n}\r\n\r\nexport function generateColorBashCode(config: ColorConfig): string {\r\n if (!config.enabled) {\r\n return `\r\n# ---- color helpers (disabled) ----\r\nuse_color=0\r\nC() { :; }\r\nRST() { :; }\r\n`\r\n }\r\n\r\n return `\r\n# ---- color helpers (force colors for Claude Code) ----\r\nuse_color=1\r\n[ -n \"$NO_COLOR\" ] && use_color=0\r\n\r\nC() { if [ \"$use_color\" -eq 1 ]; then printf '\\\\033[%sm' \"$1\"; fi; }\r\nRST() { if [ \"$use_color\" -eq 1 ]; then printf '\\\\033[0m'; fi; }\r\n`\r\n}\r\n\r\nexport function generateBasicColors(): string {\r\n return `\r\n# ---- modern sleek colors ----\r\ndir_color() { if [ \"$use_color\" -eq 1 ]; then printf '\\\\033[38;5;117m'; fi; } # sky blue\r\nmodel_color() { if [ \"$use_color\" -eq 1 ]; then printf '\\\\033[38;5;147m'; fi; } # light purple \r\nversion_color() { if [ \"$use_color\" -eq 1 ]; then printf '\\\\033[38;5;180m'; fi; } # soft yellow\r\ncc_version_color() { if [ \"$use_color\" -eq 1 ]; then printf '\\\\033[38;5;249m'; fi; } # light gray\r\nstyle_color() { if [ \"$use_color\" -eq 1 ]; then printf '\\\\033[38;5;245m'; fi; } # gray\r\nrst() { if [ \"$use_color\" -eq 1 ]; then printf '\\\\033[0m'; fi; }\r\n`\r\n}\r\n\r\nexport const COLOR_CODES = {\r\n // Basic colors\r\n BLACK: '30',\r\n RED: '31', \r\n GREEN: '32',\r\n YELLOW: '33',\r\n BLUE: '34',\r\n MAGENTA: '35',\r\n CYAN: '36',\r\n WHITE: '37',\r\n \r\n // Bright colors (bold)\r\n BRIGHT_BLACK: '1;30',\r\n BRIGHT_RED: '1;31',\r\n BRIGHT_GREEN: '1;32', \r\n BRIGHT_YELLOW: '1;33',\r\n BRIGHT_BLUE: '1;34',\r\n BRIGHT_MAGENTA: '1;35',\r\n BRIGHT_CYAN: '1;36',\r\n BRIGHT_WHITE: '1;37',\r\n \r\n // Reset\r\n RESET: '0'\r\n} as const\r\n\r\nexport function getThemeColors(theme: 'minimal' | 'detailed' | 'compact') {\r\n switch (theme) {\r\n case 'minimal':\r\n return {\r\n directory: COLOR_CODES.CYAN,\r\n git: COLOR_CODES.GREEN,\r\n model: COLOR_CODES.MAGENTA,\r\n usage: COLOR_CODES.YELLOW,\r\n session: COLOR_CODES.BLUE\r\n }\r\n case 'detailed':\r\n return {\r\n directory: COLOR_CODES.BRIGHT_CYAN,\r\n git: COLOR_CODES.BRIGHT_GREEN,\r\n model: COLOR_CODES.BRIGHT_MAGENTA,\r\n usage: COLOR_CODES.BRIGHT_YELLOW,\r\n session: COLOR_CODES.BRIGHT_BLUE\r\n }\r\n case 'compact':\r\n return {\r\n directory: COLOR_CODES.CYAN,\r\n git: COLOR_CODES.GREEN,\r\n model: COLOR_CODES.BLUE,\r\n usage: COLOR_CODES.YELLOW,\r\n session: COLOR_CODES.RED\r\n }\r\n }\r\n}","export interface GitFeature {\r\n enabled: boolean\r\n showBranch: boolean\r\n showChanges: boolean\r\n compactMode: boolean\r\n}\r\n\r\nexport function generateGitBashCode(config: GitFeature, colors: boolean): string {\r\n if (!config.enabled) return ''\r\n\r\n const colorCode = colors ? `\r\n# ---- git colors ----\r\ngit_color() { if [ \"$use_color\" -eq 1 ]; then printf '\\\\033[38;5;150m'; fi; } # soft green\r\nrst() { if [ \"$use_color\" -eq 1 ]; then printf '\\\\033[0m'; fi; }\r\n` : `\r\ngit_color() { :; }\r\nrst() { :; }\r\n`\r\n\r\n return `${colorCode}\r\n# ---- git ----\r\ngit_branch=\"\"\r\nif git rev-parse --git-dir >/dev/null 2>&1; then\r\n git_branch=$(git branch --show-current 2>/dev/null || git rev-parse --short HEAD 2>/dev/null)\r\nfi`\r\n}\r\n\r\nexport function generateGitDisplayCode(config: GitFeature, colors: boolean, emojis: boolean): string {\r\n if (!config.enabled) return ''\r\n\r\n const branchEmoji = emojis ? '๐ฟ' : 'git:'\r\n\r\n let displayCode = `\r\n# git display\r\nif [ -n \"$git_branch\" ]; then\r\n printf ' ${branchEmoji} %s%s%s' \"$(git_color)\" \"$git_branch\" \"$(rst)\"\r\nfi`\r\n\r\n return displayCode\r\n}\r\n\r\nexport function generateGitUtilities(): string {\r\n return `\r\n# git utilities\r\nnum_or_zero() { v=\"$1\"; [[ \"$v\" =~ ^[0-9]+$ ]] && echo \"$v\" || echo 0; }`\r\n}","export interface UsageFeature {\r\n enabled: boolean\r\n showCost: boolean\r\n showTokens: boolean\r\n showBurnRate: boolean\r\n showSession: boolean\r\n showProgressBar: boolean\r\n}\r\n\r\nexport function generateUsageBashCode(config: UsageFeature, colors: boolean): string {\r\n if (!config.enabled) return ''\r\n\r\n const colorCode = colors ? `\r\n# ---- usage colors ----\r\nusage_color() { if [ \"$use_color\" -eq 1 ]; then printf '\\\\033[38;5;189m'; fi; } # lavender\r\ncost_color() { if [ \"$use_color\" -eq 1 ]; then printf '\\\\033[38;5;222m'; fi; } # light gold\r\nburn_color() { if [ \"$use_color\" -eq 1 ]; then printf '\\\\033[38;5;220m'; fi; } # bright gold\r\nsession_color() { \r\n rem_pct=$(( 100 - session_pct ))\r\n if (( rem_pct <= 10 )); then SCLR='38;5;210' # light pink\r\n elif (( rem_pct <= 25 )); then SCLR='38;5;228' # light yellow \r\n else SCLR='38;5;194'; fi # light green\r\n if [ \"$use_color\" -eq 1 ]; then printf '\\\\033[%sm' \"$SCLR\"; fi\r\n}\r\n` : `\r\nusage_color() { :; }\r\ncost_color() { :; }\r\nburn_color() { :; }\r\nsession_color() { :; }\r\n`\r\n\r\n return `${colorCode}\r\n# ---- cost and usage extraction ----\r\nsession_txt=\"\"; session_pct=0; session_bar=\"\"\r\ncost_usd=\"\"; cost_per_hour=\"\"; tpm=\"\"; tot_tokens=\"\"\r\n\r\n# Extract cost data from Claude Code input\r\nif [ \"$HAS_JQ\" -eq 1 ]; then\r\n # Get cost data from Claude Code's input\r\n cost_usd=$(echo \"$input\" | jq -r '.cost.total_cost_usd // empty' 2>/dev/null)\r\n total_duration_ms=$(echo \"$input\" | jq -r '.cost.total_duration_ms // empty' 2>/dev/null)\r\n \r\n # Calculate burn rate ($/hour) from cost and duration\r\n if [ -n \"$cost_usd\" ] && [ -n \"$total_duration_ms\" ] && [ \"$total_duration_ms\" -gt 0 ]; then\r\n # Convert ms to hours and calculate rate\r\n cost_per_hour=$(echo \"$cost_usd $total_duration_ms\" | awk '{printf \"%.2f\", $1 * 3600000 / $2}')\r\n fi\r\nelse\r\n # Bash fallback for cost extraction\r\n cost_usd=$(echo \"$input\" | grep -o '\"total_cost_usd\"[[:space:]]*:[[:space:]]*[0-9.]*' | sed 's/.*:[[:space:]]*\\\\([0-9.]*\\\\).*/\\\\1/')\r\n total_duration_ms=$(echo \"$input\" | grep -o '\"total_duration_ms\"[[:space:]]*:[[:space:]]*[0-9]*' | sed 's/.*:[[:space:]]*\\\\([0-9]*\\\\).*/\\\\1/') \r\n \r\n # Calculate burn rate ($/hour) from cost and duration\r\n if [ -n \"$cost_usd\" ] && [ -n \"$total_duration_ms\" ] && [ \"$total_duration_ms\" -gt 0 ]; then\r\n # Convert ms to hours and calculate rate\r\n cost_per_hour=$(echo \"$cost_usd $total_duration_ms\" | awk '{printf \"%.2f\", $1 * 3600000 / $2}')\r\n fi\r\nfi\r\n\r\n# Get token data and session info from ccusage if available\r\nif command -v ccusage >/dev/null 2>&1 && [ \"$HAS_JQ\" -eq 1 ]; then\r\n blocks_output=\"\"\r\n \r\n # Try ccusage with timeout for token data and session info\r\n if command -v timeout >/dev/null 2>&1; then\r\n blocks_output=$(timeout 5s ccusage blocks --json 2>/dev/null)\r\n elif command -v gtimeout >/dev/null 2>&1; then\r\n # macOS with coreutils installed\r\n blocks_output=$(gtimeout 5s ccusage blocks --json 2>/dev/null)\r\n else\r\n # No timeout available, run directly (ccusage should be fast)\r\n blocks_output=$(ccusage blocks --json 2>/dev/null)\r\n fi\r\n if [ -n \"$blocks_output\" ]; then\r\n active_block=$(echo \"$blocks_output\" | jq -c '.blocks[] | select(.isActive == true)' 2>/dev/null | head -n1)\r\n if [ -n \"$active_block\" ]; then${config.showTokens ? `\r\n # Get token count from ccusage\r\n tot_tokens=$(echo \"$active_block\" | jq -r '.totalTokens // empty')` : ''}${config.showBurnRate && config.showTokens ? `\r\n # Get tokens per minute from ccusage\r\n tpm=$(echo \"$active_block\" | jq -r '.burnRate.tokensPerMinute // empty')` : ''}${config.showSession || config.showProgressBar ? `\r\n \r\n # Session time calculation from ccusage\r\n reset_time_str=$(echo \"$active_block\" | jq -r '.usageLimitResetTime // .endTime // empty')\r\n start_time_str=$(echo \"$active_block\" | jq -r '.startTime // empty')\r\n \r\n if [ -n \"$reset_time_str\" ] && [ -n \"$start_time_str\" ]; then\r\n start_sec=$(to_epoch \"$start_time_str\"); end_sec=$(to_epoch \"$reset_time_str\"); now_sec=$(date +%s)\r\n total=$(( end_sec - start_sec )); (( total<1 )) && total=1\r\n elapsed=$(( now_sec - start_sec )); (( elapsed<0 ))&&elapsed=0; (( elapsed>total ))&&elapsed=$total\r\n session_pct=$(( elapsed * 100 / total ))\r\n remaining=$(( end_sec - now_sec )); (( remaining<0 )) && remaining=0\r\n rh=$(( remaining / 3600 )); rm=$(( (remaining % 3600) / 60 ))\r\n end_hm=$(fmt_time_hm \"$end_sec\")${config.showSession ? `\r\n session_txt=\"$(printf '%dh %dm until reset at %s (%d%%)' \"$rh\" \"$rm\" \"$end_hm\" \"$session_pct\")\"` : ''}${config.showProgressBar ? `\r\n session_bar=$(progress_bar \"$session_pct\" 10)` : ''}\r\n fi` : ''}\r\n fi\r\n fi\r\nfi`\r\n}\r\n\r\nexport function generateUsageUtilities(): string {\r\n return `\r\n# ---- time helpers ----\r\nto_epoch() {\r\n ts=\"$1\"\r\n if command -v gdate >/dev/null 2>&1; then gdate -d \"$ts\" +%s 2>/dev/null && return; fi\r\n date -u -j -f \"%Y-%m-%dT%H:%M:%S%z\" \"\\${ts/Z/+0000}\" +%s 2>/dev/null && return\r\n python3 - \"$ts\" <<'PY' 2>/dev/null\r\nimport sys, datetime\r\ns=sys.argv[1].replace('Z','+00:00')\r\nprint(int(datetime.datetime.fromisoformat(s).timestamp()))\r\nPY\r\n}\r\n\r\nfmt_time_hm() {\r\n epoch=\"$1\"\r\n if date -r 0 +%s >/dev/null 2>&1; then date -r \"$epoch\" +\"%H:%M\"; else date -d \"@$epoch\" +\"%H:%M\"; fi\r\n}\r\n\r\nprogress_bar() {\r\n pct=\"\\${1:-0}\"; width=\"\\${2:-10}\"\r\n [[ \"$pct\" =~ ^[0-9]+$ ]] || pct=0; ((pct<0))&&pct=0; ((pct>100))&&pct=100\r\n filled=$(( pct * width / 100 )); empty=$(( width - filled ))\r\n printf '%*s' \"$filled\" '' | tr ' ' '='\r\n printf '%*s' \"$empty\" '' | tr ' ' '-'\r\n}`\r\n}\r\n\r\nexport function generateUsageDisplayCode(config: UsageFeature, colors: boolean, emojis: boolean): string {\r\n if (!config.enabled) return ''\r\n\r\n let displayCode = ''\r\n\r\n if (config.showSession) {\r\n const sessionEmoji = emojis ? 'โ' : 'session:'\r\n displayCode += `\r\n# session time\r\nif [ -n \"$session_txt\" ]; then\r\n printf ' ${sessionEmoji} %s%s%s' \"$(session_color)\" \"$session_txt\" \"$(rst)\"${config.showProgressBar ? `\r\n printf ' %s[%s]%s' \"$(session_color)\" \"$session_bar\" \"$(rst)\"` : ''}\r\nfi`\r\n }\r\n\r\n if (config.showCost) {\r\n const costEmoji = emojis ? '๐ต' : '$'\r\n displayCode += `\r\n# cost\r\nif [ -n \"$cost_usd\" ] && [[ \"$cost_usd\" =~ ^[0-9.]+$ ]]; then\r\n if [ -n \"$cost_per_hour\" ] && [[ \"$cost_per_hour\" =~ ^[0-9.]+$ ]]; then\r\n printf ' ${costEmoji} %s$%.2f ($%.2f/h)%s' \"$(cost_color)\" \"$cost_usd\" \"$cost_per_hour\" \"$(rst)\"\r\n else\r\n printf ' ${costEmoji} %s$%.2f%s' \"$(cost_color)\" \"$cost_usd\" \"$(rst)\"\r\n fi\r\nfi`\r\n }\r\n\r\n if (config.showTokens) {\r\n const tokenEmoji = emojis ? '๐' : 'tok:'\r\n displayCode += `\r\n# tokens\r\nif [ -n \"$tot_tokens\" ] && [[ \"$tot_tokens\" =~ ^[0-9]+$ ]]; then\r\n if [ -n \"$tpm\" ] && [[ \"$tpm\" =~ ^[0-9.]+$ ]] && ${config.showBurnRate ? 'true' : 'false'}; then\r\n printf ' ${tokenEmoji} %s%s tok (%.0f tpm)%s' \"$(usage_color)\" \"$tot_tokens\" \"$tpm\" \"$(rst)\"\r\n else\r\n printf ' ${tokenEmoji} %s%s tok%s' \"$(usage_color)\" \"$tot_tokens\" \"$(rst)\"\r\n fi\r\nfi`\r\n }\r\n\r\n return displayCode\r\n}","import { StatuslineConfig } from '../cli/prompts.js'\r\n\r\nexport interface ValidationResult {\r\n isValid: boolean\r\n errors: string[]\r\n warnings: string[]\r\n}\r\n\r\nexport function validateConfig(config: StatuslineConfig): ValidationResult {\r\n const errors: string[] = []\r\n const warnings: string[] = []\r\n\r\n // Validate features\r\n if (!config.features || config.features.length === 0) {\r\n errors.push('At least one display feature must be selected')\r\n }\r\n\r\n // Validate runtime\r\n if (!['bash', 'python', 'node'].includes(config.runtime)) {\r\n errors.push(`Invalid runtime: ${config.runtime}`)\r\n }\r\n\r\n // Validate theme\r\n if (!['minimal', 'detailed', 'compact'].includes(config.theme)) {\r\n errors.push(`Invalid theme: ${config.theme}`)\r\n }\r\n\r\n // Check for usage features without ccusage integration\r\n const usageFeatures = ['usage', 'session', 'tokens', 'burnrate']\r\n const hasUsageFeatures = config.features.some(f => usageFeatures.includes(f))\r\n \r\n if (hasUsageFeatures && !config.ccusageIntegration) {\r\n warnings.push('Usage features selected but ccusage integration is disabled. Some features may not work properly.')\r\n }\r\n\r\n // Warn about performance with many features\r\n if (config.features.length > 5) {\r\n warnings.push('Many features selected. This may impact statusline performance.')\r\n }\r\n\r\n // Validate color/emoji consistency\r\n if (config.customEmojis && !config.colors) {\r\n warnings.push('Custom emojis enabled but colors disabled. Visual distinction may be limited.')\r\n }\r\n\r\n return {\r\n isValid: errors.length === 0,\r\n errors,\r\n warnings\r\n }\r\n}\r\n\r\nexport function validateDependencies(): {\r\n jq: boolean\r\n git: boolean\r\n ccusage: boolean\r\n python?: boolean\r\n node?: boolean\r\n} {\r\n // This would check system dependencies\r\n // For now, return placeholder\r\n return {\r\n jq: true, // Would check: command -v jq >/dev/null 2>&1\r\n git: true, // Would check: command -v git >/dev/null 2>&1\r\n ccusage: false, // Would check: command -v ccusage >/dev/null 2>&1\r\n python: true, // Would check: command -v python3 >/dev/null 2>&1\r\n node: true // Would check: command -v node >/dev/null 2>&1\r\n }\r\n}","import { StatuslineConfig } from '../cli/prompts.js'\r\nimport { promises as fs } from 'fs'\r\nimport path from 'path'\r\nimport os from 'os'\r\nimport inquirer from 'inquirer'\r\n\r\nexport async function installStatusline(\r\n script: string,\r\n outputPath: string,\r\n config: StatuslineConfig\r\n): Promise<void> {\r\n try {\r\n // Determine the target directory based on install location\r\n const isGlobal = config.installLocation === 'global'\r\n const claudeDir = isGlobal ? path.join(os.homedir(), '.claude') : './.claude'\r\n const scriptPath = path.join(claudeDir, 'statusline.sh')\r\n \r\n // Ensure the directory exists\r\n await fs.mkdir(claudeDir, { recursive: true })\r\n\r\n // Check if statusline.sh already exists\r\n let shouldWrite = true\r\n try {\r\n await fs.access(scriptPath)\r\n // File exists, ask for confirmation\r\n const { confirmOverwrite } = await inquirer.prompt([{\r\n type: 'confirm',\r\n name: 'confirmOverwrite',\r\n message: `โ ๏ธ ${isGlobal ? 'Global' : 'Project'} statusline.sh already exists. Overwrite?`,\r\n default: false\r\n }])\r\n shouldWrite = confirmOverwrite\r\n } catch {\r\n // File doesn't exist, proceed\r\n }\r\n\r\n if (shouldWrite) {\r\n // Write the script\r\n await fs.writeFile(scriptPath, script, { mode: 0o755 })\r\n } else {\r\n throw new Error('USER_CANCELLED_OVERWRITE')\r\n }\r\n\r\n // Update settings.json safely\r\n await updateSettingsJson(claudeDir, 'statusline.sh', isGlobal)\r\n\r\n // Note: statusline-config.json removed per user feedback - not needed\r\n // The statusline script contains all necessary configuration info\r\n\r\n } catch (error) {\r\n throw new Error(`Failed to install statusline: ${error instanceof Error ? error.message : String(error)}`)\r\n }\r\n}\r\n\r\nasync function updateSettingsJson(claudeDir: string, scriptName: string, isGlobal: boolean): Promise<void> {\r\n const settingsPath = path.join(claudeDir, 'settings.json')\r\n \r\n try {\r\n let settings: any = {}\r\n let existingStatusLine: any = null\r\n \r\n // Try to read existing settings\r\n try {\r\n const settingsContent = await fs.readFile(settingsPath, 'utf-8')\r\n settings = JSON.parse(settingsContent)\r\n existingStatusLine = settings.statusLine\r\n } catch {\r\n // File doesn't exist or invalid JSON, start fresh\r\n }\r\n\r\n // Check if statusLine already exists\r\n if (existingStatusLine && existingStatusLine.command) {\r\n // Only update if it's a statusline.sh command or user confirms\r\n const isOurStatusline = existingStatusLine.command?.includes('statusline.sh')\r\n \r\n if (!isOurStatusline) {\r\n // There's a different statusline configured, ask user\r\n const { confirmReplace } = await inquirer.prompt([{\r\n type: 'confirm',\r\n name: 'confirmReplace',\r\n message: `โ ๏ธ ${isGlobal ? 'Global' : 'Project'} settings.json already has a statusLine configured (${existingStatusLine.command}). Replace it?`,\r\n default: false\r\n }])\r\n \r\n if (!confirmReplace) {\r\n console.warn('\\nโ ๏ธ Statusline script was saved but settings.json was not updated.')\r\n console.warn(' Your existing statusLine configuration was preserved.')\r\n return\r\n }\r\n }\r\n }\r\n\r\n // Update statusLine configuration - Windows needs explicit bash command\r\n const commandPath = process.platform === 'win32'\r\n ? `bash ${isGlobal ? '.claude' : '.claude'}/${scriptName}`\r\n : (isGlobal ? `~/.claude/${scriptName}` : `.claude/${scriptName}`)\r\n \r\n settings.statusLine = {\r\n type: 'command',\r\n command: commandPath,\r\n padding: 0\r\n }\r\n\r\n // Write updated settings\r\n await fs.writeFile(settingsPath, JSON.stringify(settings, null, 2))\r\n \r\n } catch (error) {\r\n // Settings update failed, but don't fail the entire installation\r\n console.warn(`Warning: Could not update settings.json: ${error instanceof Error ? error.message : String(error)}`)\r\n throw new Error('SETTINGS_UPDATE_FAILED') // Signal that manual config is needed\r\n }\r\n}\r\n\r\nexport async function checkClaudeCodeSetup(): Promise<{\r\n hasClaudeDir: boolean\r\n hasSettings: boolean\r\n currentStatusline?: string\r\n}> {\r\n const claudeDir = './.claude'\r\n const settingsPath = path.join(claudeDir, 'settings.json')\r\n \r\n try {\r\n const dirExists = await fs.access(claudeDir).then(() => true).catch(() => false)\r\n const settingsExists = await fs.access(settingsPath).then(() => true).catch(() => false)\r\n \r\n let currentStatusline: string | undefined\r\n \r\n if (settingsExists) {\r\n try {\r\n const settings = JSON.parse(await fs.readFile(settingsPath, 'utf-8'))\r\n currentStatusline = settings.statusLine?.command\r\n } catch {\r\n // Ignore JSON parse errors\r\n }\r\n }\r\n \r\n return {\r\n hasClaudeDir: dirExists,\r\n hasSettings: settingsExists,\r\n currentStatusline\r\n }\r\n } catch {\r\n return {\r\n hasClaudeDir: false,\r\n hasSettings: false\r\n }\r\n }\r\n}"],"mappings":";;;;;;;;;;;;AACA,OAAO,UAAU;AACjB,SAAS,qBAAqB;AAF9B;AAAA;AAAA;AAAA;AAAA;;;ACAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA,SAAS,aAAa;AACtB,SAAS,YAAYA,WAAU;AAC/B,OAAOC,WAAU;AASjB,eAAsB,qBAAqB,QAAgB,UAAqC;AAC9F,QAAM,YAAY,KAAK,IAAI;AAE3B,MAAI;AAEF,UAAM,UAAU;AAChB,UAAM,aAAaA,MAAK,KAAK,SAAS,mBAAmB,KAAK,IAAI,CAAC,KAAK;AAExE,UAAMD,IAAG,UAAU,YAAY,QAAQ,EAAE,MAAM,IAAM,CAAC;AAGtD,UAAM,QAAQ,YAAY,wBAAwB;AAGlD,UAAM,SAAS,MAAM,cAAc,YAAY,KAAK,UAAU,KAAK,CAAC;AAGpE,UAAMA,IAAG,OAAO,UAAU,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AAE1C,UAAM,gBAAgB,KAAK,IAAI,IAAI;AAEnC,WAAO;AAAA,MACL,SAAS,OAAO;AAAA,MAChB,QAAQ,OAAO;AAAA,MACf,OAAO,OAAO;AAAA,MACd;AAAA,IACF;AAAA,EAEF,SAAS,OAAO;AACd,WAAO;AAAA,MACL,SAAS;AAAA,MACT,QAAQ;AAAA,MACR,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAAA,MAC5D,eAAe,KAAK,IAAI,IAAI;AAAA,IAC9B;AAAA,EACF;AACF;AAEO,SAAS,wBAAwB,QAAyC;AAC/E,SAAO;AAAA,IACL,YAAY;AAAA,IACZ,iBAAiB;AAAA,IACjB,KAAK;AAAA,IACL,WAAW;AAAA,MACT,aAAa;AAAA,IACf;AAAA,IACA,OAAO;AAAA,MACL,IAAI;AAAA,MACJ,cAAc;AAAA,MACd,SAAS;AAAA,IACX;AAAA,EACF;AACF;AAEO,SAAS,4BAAiC;AAC/C,SAAO;AAAA,IACL,QAAQ;AAAA,MACN;AAAA,QACE,IAAI;AAAA,QACJ,WAAW;AAAA,QACX,SAAS;AAAA,QACT,qBAAqB;AAAA,QACrB,eAAe;AAAA,QACf,UAAU;AAAA,QACV,OAAO;AAAA,QACP,SAAS;AAAA,QACT,aAAa;AAAA,UACX,aAAa;AAAA,UACb,cAAc;AAAA,UACd,0BAA0B;AAAA,UAC1B,sBAAsB;AAAA,QACxB;AAAA,QACA,aAAa;AAAA,QACb,SAAS;AAAA,QACT,QAAQ,CAAC,0BAA0B;AAAA,QACnC,UAAU;AAAA,UACR,iBAAiB;AAAA,UACjB,6BAA6B;AAAA,UAC7B,aAAa;AAAA,QACf;AAAA,QACA,YAAY;AAAA,UACV,aAAa;AAAA,UACb,WAAW;AAAA,UACX,kBAAkB;AAAA,QACpB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAEA,eAAe,cAAc,YAAoB,OAA8E;AAC7H,SAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,UAAME,WAAU,MAAM,QAAQ,CAAC,UAAU,GAAG;AAAA,MAC1C,OAAO,CAAC,QAAQ,QAAQ,MAAM;AAAA,IAChC,CAAC;AAED,QAAI,SAAS;AACb,QAAI,SAAS;AAEb,IAAAA,SAAQ,OAAO,GAAG,QAAQ,CAAC,SAAS;AAClC,gBAAU,KAAK,SAAS;AAAA,IAC1B,CAAC;AAED,IAAAA,SAAQ,OAAO,GAAG,QAAQ,CAAC,SAAS;AAClC,gBAAU,KAAK,SAAS;AAAA,IAC1B,CAAC;AAED,IAAAA,SAAQ,GAAG,SAAS,CAAC,SAAS;AAC5B,cAAQ;AAAA,QACN,SAAS,SAAS;AAAA,QAClB,QAAQ,OAAO,KAAK;AAAA,QACpB,OAAO,OAAO,KAAK,KAAK;AAAA,MAC1B,CAAC;AAAA,IACH,CAAC;AAED,IAAAA,SAAQ,GAAG,SAAS,CAAC,QAAQ;AAC3B,cAAQ;AAAA,QACN,SAAS;AAAA,QACT,QAAQ;AAAA,QACR,OAAO,IAAI;AAAA,MACb,CAAC;AAAA,IACH,CAAC;AAGD,IAAAA,SAAQ,MAAM,MAAM,KAAK;AACzB,IAAAA,SAAQ,MAAM,IAAI;AAGlB,eAAW,MAAM;AACf,MAAAA,SAAQ,KAAK;AACb,cAAQ;AAAA,QACN,SAAS;AAAA,QACT,QAAQ;AAAA,QACR,OAAO;AAAA,MACT,CAAC;AAAA,IACH,GAAG,GAAI;AAAA,EACT,CAAC;AACH;AAEO,SAAS,kBAAkB,QAAoB,QAKpD;AACA,QAAM,SAAmB,CAAC;AAC1B,QAAM,cAAwB,CAAC;AAG/B,MAAI;AACJ,MAAI,OAAO,gBAAgB,KAAM;AAC/B,kBAAc;AACd,WAAO,KAAK,qCAAqC;AAAA,EACnD,WAAW,OAAO,gBAAgB,KAAK;AACrC,kBAAc;AACd,WAAO,KAAK,mCAAmC;AAAA,EACjD,WAAW,OAAO,gBAAgB,KAAK;AACrC,kBAAc;AAAA,EAChB,OAAO;AACL,kBAAc;AAAA,EAChB;AAGA,MAAI,sBAAsB;AAE1B,MAAI,OAAO,SAAS,SAAS,WAAW,KAAK,CAAC,OAAO,OAAO,SAAS,UAAU,GAAG;AAChF,0BAAsB;AACtB,WAAO,KAAK,wCAAwC;AAAA,EACtD;AAEA,MAAI,OAAO,SAAS,SAAS,OAAO,KAAK,CAAC,OAAO,OAAO,SAAS,MAAM,GAAG;AACxE,0BAAsB;AACtB,WAAO,KAAK,oCAAoC;AAAA,EAClD;AAEA,MAAI,OAAO,SAAS,SAAS,KAAK,KAAK,OAAO,sBAAsB,CAAC,OAAO,OAAO,SAAS,KAAK,GAAG;AAClG,gBAAY,KAAK,mDAAmD;AAAA,EACtE;AAGA,MAAI,OAAO,OAAO;AAChB,WAAO,KAAK,kBAAkB,OAAO,KAAK,EAAE;AAAA,EAC9C;AAEA,MAAI,CAAC,OAAO,SAAS;AACnB,WAAO,KAAK,uCAAuC;AAAA,EACrD;AAGA,MAAI,OAAO,SAAS,SAAS,GAAG;AAC9B,gBAAY,KAAK,6DAA6D;AAAA,EAChF;AAEA,MAAI,OAAO,sBAAsB,OAAO,gBAAgB,KAAK;AAC3D,gBAAY,KAAK,iEAAiE;AAAA,EACpF;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAvNA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA;AAAA;AAAA;AAAA;AAGA,SAAS,YAAYC,WAAU;AAC/B,OAAOC,YAAW;AAClB,OAAOC,UAAS;AAEhB,eAAsB,eAAe,YAAmC;AACtE,UAAQ,IAAID,OAAM,KAAK,qCAA8B,CAAC;AAEtD,MAAI;AAGJ,MAAI;AACF,UAAM,UAAUC,KAAI,kCAAkC,UAAU,KAAK,EAAE,MAAM;AAC7E,aAAS,MAAMF,IAAG,SAAS,YAAY,OAAO;AAC9C,YAAQ,QAAQ,gBAAgB;AAGhC,UAAM,cAAc,OAAO,MAAM,wDAAwD;AACzF,QAAI,aAAa;AACf,cAAQ,IAAIC,OAAM,OAAO,yBAAyB,CAAC;AACnD,cAAQ,IAAI,aAAa,YAAY,CAAC,CAAC,EAAE;AACzC,cAAQ,IAAI,cAAc,YAAY,CAAC,CAAC,EAAE;AAC1C,cAAQ,IAAI,gBAAgB,YAAY,CAAC,CAAC;AAAA,CAAI;AAAA,IAChD;AAGA,UAAM,kBAAkB,OAAO,MAAM,sFAAsF;AAC3H,QAAI,iBAAiB;AACnB,cAAQ,IAAIA,OAAM,KAAK,cAAc,gBAAgB,CAAC,CAAC;AAAA,CAAI,CAAC;AAAA,IAC9D;AAAA,EAEF,SAAS,OAAO;AACd,YAAQ,MAAMA,OAAM,IAAI,iCAA4B,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC,EAAE,CAAC;AAC7G;AAAA,EACF;AAGA,QAAM,cAAcC,KAAI,sCAAsC,EAAE,MAAM;AACtE,QAAM,YAAY,wBAAwB;AAE1C,UAAQ,IAAID,OAAM,KAAK,2BAA2B,CAAC;AACnD,UAAQ,IAAIA,OAAM,KAAK,KAAK,UAAU,WAAW,MAAM,CAAC,CAAC,CAAC;AAE1D,QAAM,aAAa,MAAM,qBAAqB,QAAQ,SAAS;AAE/D,MAAI,WAAW,SAAS;AACtB,gBAAY,QAAQ,qBAAqB,WAAW,aAAa,IAAI;AAErE,YAAQ,IAAIA,OAAM,MAAM,6BAAwB,CAAC;AACjD,YAAQ,IAAIA,OAAM,MAAM,SAAI,OAAO,EAAE,CAAC,CAAC;AACvC,YAAQ,IAAI,WAAW,MAAM;AAC7B,YAAQ,IAAIA,OAAM,MAAM,SAAI,OAAO,EAAE,CAAC,CAAC;AAGvC,YAAQ,IAAIA,OAAM,KAAK;AAAA,yBAAqB,oBAAoB,oBAAoB,WAAW,aAAa,CAAC,CAAC,IAAI,oBAAoB,WAAW,aAAa,CAAC,KAAK,WAAW,aAAa,KAAK,CAAC;AAGlM,QAAI,WAAW,OAAO,SAAS,WAAI,KAAK,WAAW,OAAO,SAAS,WAAI,KAAK,WAAW,OAAO,SAAS,WAAI,GAAG;AAC5G,cAAQ,IAAIA,OAAM,MAAM,iDAA4C,CAAC;AAAA,IACvE,OAAO;AACL,cAAQ,IAAIA,OAAM,OAAO,8DAAoD,CAAC;AAAA,IAChF;AAAA,EAEF,OAAO;AACL,gBAAY,KAAK,aAAa;AAC9B,YAAQ,MAAMA,OAAM,IAAI;AAAA,gBAAc,WAAW,KAAK,EAAE,CAAC;AACzD,QAAI,WAAW,QAAQ;AACrB,cAAQ,IAAIA,OAAM,KAAK,mBAAmB,CAAC;AAC3C,cAAQ,IAAI,WAAW,MAAM;AAAA,IAC/B;AAAA,EACF;AAEA,UAAQ,IAAIA,OAAM,MAAM,mFAA8E,CAAC;AACzG;AAEA,SAAS,oBAAoB,aAA6B;AACxD,UAAQ,aAAa;AAAA,IACnB,KAAK;AAAa,aAAO;AAAA,IACzB,KAAK;AAAQ,aAAO;AAAA,IACpB,KAAK;AAAQ,aAAO;AAAA,IACpB,KAAK;AAAW,aAAO;AAAA,IACvB;AAAS,aAAO;AAAA,EAClB;AACF;AAEA,SAAS,oBAAoB,eAA+B;AAC1D,MAAI,gBAAgB,IAAM,QAAO;AACjC,MAAI,gBAAgB,IAAK,QAAO;AAChC,MAAI,gBAAgB,IAAK,QAAO;AAChC,SAAO;AACT;AA5FA;AAAA;AAAA;AAAA;AAEA;AAAA;AAAA;;;ACFA;AAAA,SAAS,eAAe;;;ACAxB;;;ACAA;AAAA,OAAO,cAAc;AAarB,eAAsB,uBAAkD;AACtE,UAAQ,IAAI,wFAAkF;AAC9F,UAAQ,IAAI,yHAA0G;AAEtH,QAAM,SAAS,MAAM,SAAS,OAAO;AAAA,IACnC;AAAA,MACE,MAAM;AAAA,MACN,MAAM;AAAA,MACN,SAAS;AAAA,MACT,SAAS;AAAA,QACP,EAAE,MAAM,+BAAwB,OAAO,aAAa,SAAS,KAAK;AAAA,QAClE,EAAE,MAAM,wBAAiB,OAAO,OAAO,SAAS,KAAK;AAAA,QACrD,EAAE,MAAM,kCAA2B,OAAO,SAAS,SAAS,KAAK;AAAA,QACjE,EAAE,MAAM,+BAAwB,OAAO,WAAW,SAAS,KAAK;AAAA,QAChE,EAAE,MAAM,0BAAmB,OAAO,SAAS,SAAS,KAAK;AAAA,QACzD,EAAE,MAAM,iCAA4B,OAAO,WAAW,SAAS,KAAK;AAAA,QACpE,EAAE,MAAM,8BAAuB,OAAO,UAAU,SAAS,KAAK;AAAA,QAC9D,EAAE,MAAM,iCAA4B,OAAO,YAAY,SAAS,KAAK;AAAA,MACvE;AAAA,MACA,UAAU,CAAC,WAAqB;AAC9B,YAAI,OAAO,SAAS,GAAG;AACrB,iBAAO;AAAA,QACT;AACA,eAAO;AAAA,MACT;AAAA,MACA,UAAU;AAAA,IACZ;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,MAAM;AAAA,MACN,SAAS;AAAA,MACT,SAAS;AAAA,IACX;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,MAAM;AAAA,MACN,SAAS;AAAA,MACT,SAAS;AAAA,IACX;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,MAAM;AAAA,MACN,SAAS;AAAA,MACT,SAAS;AAAA,QACP,EAAE,MAAM,0DAAmD,OAAO,SAAS;AAAA,QAC3E,EAAE,MAAM,yDAAkD,OAAO,UAAU;AAAA,MAC7E;AAAA,MACA,SAAS;AAAA,IACX;AAAA,EACF,CAAC;AAGD,SAAO;AAAA,IACL,UAAU,OAAO;AAAA,IACjB,SAAS;AAAA,IACT,QAAQ,OAAO;AAAA,IACf,OAAO;AAAA,IACP,oBAAoB;AAAA;AAAA,IACpB,SAAS,OAAO;AAAA,IAChB,cAAc;AAAA,IACd,iBAAiB,OAAO;AAAA,EAC1B;AACF;;;AC3EA;;;ACAA;AAKO,SAAS,sBAAsB,QAA6B;AACjE,MAAI,CAAC,OAAO,SAAS;AACnB,WAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMT;AAEA,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAQT;AAEO,SAAS,sBAA8B;AAC5C,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAST;;;ACnCA;AAOO,SAAS,oBAAoB,QAAoB,QAAyB;AAC/E,MAAI,CAAC,OAAO,QAAS,QAAO;AAE5B,QAAM,YAAY,SAAS;AAAA;AAAA;AAAA;AAAA,IAIzB;AAAA;AAAA;AAAA;AAKF,SAAO,GAAG,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAMrB;AAgBO,SAAS,uBAA+B;AAC7C,SAAO;AAAA;AAAA;AAGT;;;AC7CA;AASO,SAAS,sBAAsB,QAAsB,QAAyB;AACnF,MAAI,CAAC,OAAO,QAAS,QAAO;AAE5B,QAAM,YAAY,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAYzB;AAAA;AAAA;AAAA;AAAA;AAAA;AAOF,SAAO,GAAG,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qCA4CgB,OAAO,aAAa;AAAA;AAAA,4EAEmB,EAAE,GAAG,OAAO,gBAAgB,OAAO,aAAa;AAAA;AAAA,kFAE1C,EAAE,GAAG,OAAO,eAAe,OAAO,kBAAkB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,0CAa5F,OAAO,cAAc;AAAA,2GAC4C,EAAE,GAAG,OAAO,kBAAkB;AAAA,yDAChF,EAAE;AAAA,YAC/C,EAAE;AAAA;AAAA;AAAA;AAId;AAEO,SAAS,yBAAiC;AAC/C,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAyBT;;;AHzHA,IAAM,UAAU;AAET,SAAS,uBAAuB,QAAkC;AACvE,QAAM,SAAS,OAAO,SAAS,SAAS,KAAK;AAC7C,QAAM,WAAW,OAAO,SAAS,KAAK,OAAK,CAAC,SAAS,WAAW,UAAU,UAAU,EAAE,SAAS,CAAC,CAAC;AACjG,QAAM,eAAe,OAAO,SAAS,SAAS,WAAW;AACzD,QAAM,WAAW,OAAO,SAAS,SAAS,OAAO;AACjD,QAAM,aAAa,OAAO,SAAS,SAAS,SAAS;AAGrD,QAAM,cAAc;AAAA,IAClB,SAAS,YAAY,OAAO;AAAA,IAC5B,UAAU,OAAO,SAAS,SAAS,OAAO;AAAA,IAC1C,YAAY,OAAO,SAAS,SAAS,QAAQ;AAAA,IAC7C,cAAc,OAAO,SAAS,SAAS,UAAU;AAAA,IACjD,aAAa,OAAO,SAAS,SAAS,SAAS;AAAA,IAC/C,iBAAiB,OAAO,UAAU,aAAa,OAAO,SAAS,SAAS,SAAS;AAAA,EACnF;AAGA,QAAM,YAAY;AAAA,IAChB,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,aAAa;AAAA;AAAA,IACb,aAAa,OAAO,UAAU;AAAA,EAChC;AAEA,QAAM,aAAY,oBAAI,KAAK,GAAE,YAAY;AACzC,QAAM,SAAS;AAAA,gCACe,OAAO;AAAA,6CACM,SAAS;AAAA,WAC3C,OAAO,KAAK,cAAc,OAAO,MAAM,gBAAgB,OAAO,SAAS,KAAK,IAAI,CAAC;AAAA,sBACtE,OAAO;AAAA;AAAA;AAAA,EAG3B,OAAO,UAAU,oBAAoB,IAAI,EAAE;AAAA,EAC3C,sBAAsB,EAAE,SAAS,OAAO,QAAQ,OAAO,OAAO,MAAM,CAAC,CAAC;AAAA,EACtE,OAAO,SAAS,oBAAoB,IAAI,EAAE;AAAA,EAC1C,WAAW,uBAAuB,IAAI,EAAE;AAAA,EACxC,SAAS,qBAAqB,IAAI,EAAE;AAAA,EACpC,4BAA4B,cAAc,UAAU,UAAU,CAAC;AAAA,EAC/D,SAAS,oBAAoB,WAAW,OAAO,MAAM,IAAI,EAAE;AAAA,EAC3D,aAAa,wBAAwB,OAAO,MAAM,IAAI,EAAE;AAAA,EACxD,WAAW,sBAAsB,aAAa,OAAO,MAAM,IAAI,EAAE;AAAA,EACjE,OAAO,UAAU,sBAAsB,IAAI,EAAE;AAAA,EAC7C,uBAAuB,QAAQ,WAAW,WAAW,CAAC;AAAA;AAGtD,SAAO,OAAO,QAAQ,YAAY,MAAM,EAAE,KAAK,IAAI;AACrD;AAEA,SAAS,sBAA8B;AACrC,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA0BT;AAEA,SAAS,4BAAoC;AAC3C,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAiCT;AAEA,SAAS,4BAA4B,cAAuB,UAAmB,YAA6B;AAC1G,SAAO;AAAA,EACP,0BAA0B,CAAC;AAAA;AAAA,8BAEC,eAAe;AAAA,yHAC4E,EAAE,GAAG,WAAW;AAAA;AAAA,+EAE1D,EAAE,GAAG,aAAa;AAAA,yEACxB,EAAE;AAAA;AAAA;AAAA,MAGrE,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,6DAYwC,EAAE,GAAG,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,2DAMlB,EAAE,GAAG,aAAa;AAAA,gEACb,EAAE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAOlE;AAEA,SAAS,wBAAwB,QAAyB;AACxD,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAwDT;AAEA,SAAS,wBAAgC;AACvC,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAST;AAEA,SAAS,uBAAuB,QAA0B,WAAgB,aAA0B;AAClG,QAAM,SAAS,OAAO,UAAU,CAAC,OAAO;AAExC,SAAO;AAAA;AAAA;AAAA,EAGP,OAAO,SAAS,SAAS,WAAW,IAAI,qEAA8D,EAAE,GAAG,UAAU,UAAU;AAAA;AAAA;AAAA,MAG3H,EAAE,GAAG,OAAO,SAAS,SAAS,OAAO,IAAI;AAAA;AAAA;AAAA;AAAA,MAIzC,EAAE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,UASE,OAAO,SAAS,SAAS,SAAS,IAAI;AAAA;AAAA;AAAA;AAAA,MAI1C,EAAE,GAAG,YAAY,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAO/B,EAAE,GAAG,OAAO,SAAS,SAAS,SAAS,IAAI;AAAA;AAAA;AAAA,MAG3C,EAAE;AAAA;AAAA;AAAA,UAGE,YAAY,WAAW;AAAA,+DAC8B,YAAY,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAMlF;AAAA,uEACwD;AAAA,MAC1D,EAAE,GAAG,YAAY,aAAa;AAAA,kEAC8B,YAAY,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAcrF;AAAA;AAAA;AAAA;AAAA;AAAA,KAKH;AAAA,MACC,EAAE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAUR;;;AIvTA;AAQO,SAAS,eAAe,QAA4C;AACzE,QAAM,SAAmB,CAAC;AAC1B,QAAM,WAAqB,CAAC;AAG5B,MAAI,CAAC,OAAO,YAAY,OAAO,SAAS,WAAW,GAAG;AACpD,WAAO,KAAK,+CAA+C;AAAA,EAC7D;AAGA,MAAI,CAAC,CAAC,QAAQ,UAAU,MAAM,EAAE,SAAS,OAAO,OAAO,GAAG;AACxD,WAAO,KAAK,oBAAoB,OAAO,OAAO,EAAE;AAAA,EAClD;AAGA,MAAI,CAAC,CAAC,WAAW,YAAY,SAAS,EAAE,SAAS,OAAO,KAAK,GAAG;AAC9D,WAAO,KAAK,kBAAkB,OAAO,KAAK,EAAE;AAAA,EAC9C;AAGA,QAAM,gBAAgB,CAAC,SAAS,WAAW,UAAU,UAAU;AAC/D,QAAM,mBAAmB,OAAO,SAAS,KAAK,OAAK,cAAc,SAAS,CAAC,CAAC;AAE5E,MAAI,oBAAoB,CAAC,OAAO,oBAAoB;AAClD,aAAS,KAAK,mGAAmG;AAAA,EACnH;AAGA,MAAI,OAAO,SAAS,SAAS,GAAG;AAC9B,aAAS,KAAK,iEAAiE;AAAA,EACjF;AAGA,MAAI,OAAO,gBAAgB,CAAC,OAAO,QAAQ;AACzC,aAAS,KAAK,+EAA+E;AAAA,EAC/F;AAEA,SAAO;AAAA,IACL,SAAS,OAAO,WAAW;AAAA,IAC3B;AAAA,IACA;AAAA,EACF;AACF;;;AClDA;AACA,SAAS,YAAY,UAAU;AAC/B,OAAOE,WAAU;AACjB,OAAO,QAAQ;AACf,OAAOC,eAAc;AAErB,eAAsB,kBACpB,QACA,YACA,QACe;AACf,MAAI;AAEF,UAAM,WAAW,OAAO,oBAAoB;AAC5C,UAAM,YAAY,WAAWD,MAAK,KAAK,GAAG,QAAQ,GAAG,SAAS,IAAI;AAClE,UAAM,aAAaA,MAAK,KAAK,WAAW,eAAe;AAGvD,UAAM,GAAG,MAAM,WAAW,EAAE,WAAW,KAAK,CAAC;AAG7C,QAAI,cAAc;AAClB,QAAI;AACF,YAAM,GAAG,OAAO,UAAU;AAE1B,YAAM,EAAE,iBAAiB,IAAI,MAAMC,UAAS,OAAO,CAAC;AAAA,QAClD,MAAM;AAAA,QACN,MAAM;AAAA,QACN,SAAS,iBAAO,WAAW,WAAW,SAAS;AAAA,QAC/C,SAAS;AAAA,MACX,CAAC,CAAC;AACF,oBAAc;AAAA,IAChB,QAAQ;AAAA,IAER;AAEA,QAAI,aAAa;AAEf,YAAM,GAAG,UAAU,YAAY,QAAQ,EAAE,MAAM,IAAM,CAAC;AAAA,IACxD,OAAO;AACL,YAAM,IAAI,MAAM,0BAA0B;AAAA,IAC5C;AAGA,UAAM,mBAAmB,WAAW,iBAAiB,QAAQ;AAAA,EAK/D,SAAS,OAAO;AACd,UAAM,IAAI,MAAM,iCAAiC,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC,EAAE;AAAA,EAC3G;AACF;AAEA,eAAe,mBAAmB,WAAmB,YAAoB,UAAkC;AAtD3G;AAuDE,QAAM,eAAeD,MAAK,KAAK,WAAW,eAAe;AAEzD,MAAI;AACF,QAAI,WAAgB,CAAC;AACrB,QAAI,qBAA0B;AAG9B,QAAI;AACF,YAAM,kBAAkB,MAAM,GAAG,SAAS,cAAc,OAAO;AAC/D,iBAAW,KAAK,MAAM,eAAe;AACrC,2BAAqB,SAAS;AAAA,IAChC,QAAQ;AAAA,IAER;AAGA,QAAI,sBAAsB,mBAAmB,SAAS;AAEpD,YAAM,mBAAkB,wBAAmB,YAAnB,mBAA4B,SAAS;AAE7D,UAAI,CAAC,iBAAiB;AAEpB,cAAM,EAAE,eAAe,IAAI,MAAMC,UAAS,OAAO,CAAC;AAAA,UAChD,MAAM;AAAA,UACN,MAAM;AAAA,UACN,SAAS,iBAAO,WAAW,WAAW,SAAS,uDAAuD,mBAAmB,OAAO;AAAA,UAChI,SAAS;AAAA,QACX,CAAC,CAAC;AAEF,YAAI,CAAC,gBAAgB;AACnB,kBAAQ,KAAK,gFAAsE;AACnF,kBAAQ,KAAK,0DAA0D;AACvE;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAGA,UAAM,cAAc,QAAQ,aAAa,UACrC,QAAQ,WAAW,YAAY,SAAS,IAAI,UAAU,KACrD,WAAW,aAAa,UAAU,KAAK,WAAW,UAAU;AAEjE,aAAS,aAAa;AAAA,MACpB,MAAM;AAAA,MACN,SAAS;AAAA,MACT,SAAS;AAAA,IACX;AAGA,UAAM,GAAG,UAAU,cAAc,KAAK,UAAU,UAAU,MAAM,CAAC,CAAC;AAAA,EAEpE,SAAS,OAAO;AAEd,YAAQ,KAAK,4CAA4C,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC,EAAE;AACjH,UAAM,IAAI,MAAM,wBAAwB;AAAA,EAC1C;AACF;;;AP3GA,OAAO,WAAW;AAClB,OAAO,SAAS;AAChB,OAAOC,WAAU;AACjB,OAAOC,SAAQ;AACf,SAAS,gBAAgB;AAOzB,SAAS,sBAA+B;AACtC,MAAI;AACF,aAAS,iBAAiB,EAAE,OAAO,SAAS,CAAC;AAC7C,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,2BAAmC;AAC1C,QAAM,WAAW,QAAQ;AAEzB,MAAI,aAAa,UAAU;AACzB,WAAO;AAAA,EACT,MAAM,KAAK,8DAAuD,CAAC;AAAA;AAAA,EAEnE,MAAM,MAAM,+BAA+B,CAAC;AAAA;AAAA;AAAA,EAG5C,MAAM,MAAM,iBAAiB,CAAC;AAAA;AAAA;AAAA,EAG9B,MAAM,MAAM,uBAAuB,CAAC;AAAA;AAAA;AAAA,EAGpC,WAAW,aAAa,SAAS;AAC/B,WAAO;AAAA,EACT,MAAM,KAAK,8DAAuD,CAAC;AAAA;AAAA,EAEnE,MAAM,MAAM,gBAAgB,CAAC;AAAA;AAAA;AAAA,EAG7B,MAAM,MAAM,qBAAqB,CAAC;AAAA;AAAA;AAAA,EAGlC,MAAM,MAAM,aAAa,CAAC;AAAA;AAAA;AAAA,EAG1B,MAAM,MAAM,uBAAuB,CAAC;AAAA;AAAA;AAAA,EAGpC,WAAW,aAAa,SAAS;AAC/B,WAAO;AAAA,EACT,MAAM,KAAK,8DAAuD,CAAC;AAAA;AAAA,EAEnE,MAAM,MAAM,iCAAiC,CAAC;AAAA,IAC5C,MAAM,IAAI,aAAa,CAAC;AAAA,IACxB,MAAM,IAAI,QAAQ,CAAC;AAAA;AAAA,EAErB,MAAM,MAAM,2BAA2B,CAAC;AAAA;AAAA;AAAA,OAGnC,MAAM,IAAI,wBAAmB,CAAC;AAAA,OAC9B,MAAM,IAAI,wBAAmB,CAAC;AAAA;AAAA,wCAEG,MAAM,IAAI,kBAAkB,CAAC;AAAA;AAAA;AAAA,EAGnE,OAAO;AACL,WAAO;AAAA,EACT,MAAM,KAAK,8DAAuD,CAAC;AAAA;AAAA,EAEnE,MAAM,MAAM,6BAA6B,CAAC;AAAA;AAAA;AAAA,EAG1C;AACF;AAEA,eAAsB,YAAY,SAAqC;AACrE,MAAI;AACF,UAAM,UAAU,IAAI,sCAAsC,EAAE,MAAM;AAClE,UAAM,IAAI,QAAQ,aAAW,WAAW,SAAS,GAAG,CAAC;AACrD,YAAQ,KAAK;AAGb,UAAM,QAAQ,oBAAoB;AAClC,QAAI,CAAC,OAAO;AACV,cAAQ,IAAI,MAAM,OAAO,qCAA2B,CAAC;AACrD,cAAQ,IAAI,MAAM,IAAI,uEAAuE,CAAC;AAC9F,cAAQ,IAAI,MAAM,IAAI,0DAAsD,CAAC;AAC7E,cAAQ,IAAI,MAAM,IAAI,wCAAmC,CAAC;AAC1D,cAAQ,IAAI,MAAM,IAAI,qCAAgC,CAAC;AACvD,cAAQ,IAAI,yBAAyB,CAAC;AAGtC,YAAMC,aAAY,MAAM,OAAO,UAAU,GAAG;AAC5C,YAAM,EAAE,kBAAkB,IAAI,MAAMA,UAAS,OAAO,CAAC;AAAA,QACnD,MAAM;AAAA,QACN,MAAM;AAAA,QACN,SAAS;AAAA,QACT,SAAS;AAAA,MACX,CAAC,CAAC;AAEF,UAAI,CAAC,mBAAmB;AACtB,gBAAQ,IAAI,MAAM,KAAK,mDAA4C,CAAC;AACpE,gBAAQ,KAAK,CAAC;AAAA,MAChB;AAAA,IACF;AAGA,UAAM,SAAS,MAAM,qBAAqB;AAG1C,UAAM,aAAa,eAAe,MAAM;AACxC,QAAI,CAAC,WAAW,SAAS;AACvB,cAAQ,MAAM,MAAM,IAAI,yCAAoC,CAAC;AAC7D,iBAAW,OAAO,QAAQ,WAAS,QAAQ,MAAM,MAAM,IAAI,aAAQ,KAAK,EAAE,CAAC,CAAC;AAC5E,cAAQ,KAAK,CAAC;AAAA,IAChB;AAGA,UAAM,oBAAoB,IAAI,iCAAiC,EAAE,MAAM;AAEvE,UAAM,SAAS,uBAAuB,MAAM;AAC5C,UAAM,WAAW;AAEjB,sBAAkB,QAAQ,8BAA8B;AAGxD,YAAQ,IAAI,MAAM,KAAK,0CAAqC,CAAC;AAC7D,YAAQ,IAAI,MAAM,MAAM,SAAI,OAAO,EAAE,CAAC,CAAC;AAGvC,UAAM,EAAE,sBAAAC,uBAAsB,yBAAAC,yBAAwB,IAAI,MAAM;AAChE,UAAM,YAAYA,yBAAwB;AAC1C,UAAM,aAAa,MAAMD,sBAAqB,QAAQ,SAAS;AAE/D,QAAI,WAAW,SAAS;AACtB,cAAQ,IAAI,WAAW,MAAM;AAAA,IAC/B,OAAO;AACL,cAAQ,IAAI,MAAM,KAAK,2FAA+D,CAAC;AACvF,cAAQ,IAAI,MAAM,KAAK,4DAA4D,CAAC;AAAA,IACtF;AAEA,YAAQ,IAAI,MAAM,MAAM,SAAI,OAAO,EAAE,CAAC,CAAC;AAGvC,UAAM,WAAW,OAAO,oBAAoB;AAC5C,UAAM,UAAU,WAAWF,IAAG,QAAQ,IAAI;AAC1C,UAAM,aAAa,QAAQ,UAAUD,MAAK,KAAK,SAAS,WAAW,QAAQ;AAC3E,UAAM,eAAeA,MAAK,QAAQ,UAAU;AAG5C,QAAI,QAAQ,YAAY,OAAO;AAC7B,cAAQ,IAAI,MAAM,KAAK,sCAA+B,CAAC;AAEvD,UAAI;AACF,cAAM,kBAAkB,QAAQ,cAAc,MAAM;AAEpD,gBAAQ,IAAI,MAAM,MAAM,gCAA2B,CAAC;AACpD,gBAAQ,IAAI,MAAM,MAAM,uDAAgD,CAAC;AACzE,gBAAQ,IAAI,MAAM,KAAK;AAAA,YAAQ,WAAW,WAAW,SAAS,2BAA2B,MAAM,MAAM,YAAY,CAAC,EAAE,CAAC;AACrH,gBAAQ,IAAI,MAAM,KAAK,eAAe,CAAC;AACvC,gBAAQ,IAAI,MAAM,MAAM,sDAAsD,CAAC;AAC/E,gBAAQ,IAAI,MAAM,MAAM,qDAAqD,CAAC;AAAA,MAEhF,SAAS,OAAO;AACd,gBAAQ,IAAI,MAAM,IAAI,uCAAkC,CAAC;AAEzD,YAAI,iBAAiB,SAAS,MAAM,YAAY,4BAA4B;AAC1E,kBAAQ,IAAI,MAAM,OAAO,qFAA2E,CAAC;AAAA,QACvG,WAAW,iBAAiB,SAAS,MAAM,YAAY,0BAA0B;AAC/E,gBAAM,cAAc,WAAW,4BAA4B;AAC3D,kBAAQ,IAAI,MAAM,OAAO,mEAAyD,CAAC;AACnF,kBAAQ,IAAI,MAAM,KAAK,kCAAkC,CAAC;AAC1D,kBAAQ,IAAI,MAAM,MAAM,oBAAoB,WAAW,cAAc,SAAS,sBAAsB,CAAC;AACrG,kBAAQ,IAAI,MAAM,KAAK,KAAK,CAAC;AAC7B,kBAAQ,IAAI,MAAM,KAAK,mBAAmB,CAAC;AAC3C,kBAAQ,IAAI,MAAM,KAAK,wBAAwB,CAAC;AAChD,kBAAQ,IAAI,MAAM,KAAK,mBAAmB,WAAW,IAAI,CAAC;AAC1D,kBAAQ,IAAI,MAAM,KAAK,kBAAkB,CAAC;AAC1C,kBAAQ,IAAI,MAAM,KAAK,KAAK,CAAC;AAC7B,kBAAQ,IAAI,MAAM,KAAK,GAAG,CAAC;AAC3B,kBAAQ,IAAI,MAAM,KAAK;AAAA,wCAAoC,MAAM,MAAM,YAAY,CAAC,EAAE,CAAC;AAAA,QACzF,OAAO;AACL,kBAAQ,MAAM,MAAM,IAAI,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC,EAAE,CAAC;AAC3F,kBAAQ,IAAI,MAAM,KAAK;AAAA,iDAA6C,MAAM,MAAM,YAAY,CAAC,EAAE,CAAC;AAAA,QAClG;AAAA,MACF;AAAA,IACF,OAAO;AAEL,cAAQ,IAAI,MAAM,MAAM,6CAAwC,CAAC;AACjE,cAAQ,IAAI,MAAM,KAAK;AAAA,iCAA6B,MAAM,MAAM,YAAY,CAAC,EAAE,CAAC;AAChF,cAAQ,IAAI,MAAM,KAAK,wDAAwD,CAAC;AAAA,IAClF;AAAA,EAEF,SAAS,OAAO;AACd,YAAQ,MAAM,MAAM,IAAI,2BAAsB,CAAC;AAC/C,YAAQ,MAAM,MAAM,IAAI,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC,CAAC;AAC/E,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;;;AD5MA,OAAOK,YAAW;AAGlB,IAAMC,WAAU;AAEhB,IAAM,UAAU,IAAI,QAAQ;AAE5B,QACG,KAAK,eAAe,EACpB,YAAY,oEAAoE,EAChF,QAAQA,QAAO;AAElB,QACG,QAAQ,MAAM,EACd,YAAY,qDAAqD,EACjE,OAAO,uBAAuB,iCAAiC,yBAAyB,EACxF,OAAO,gBAAgB,sDAAuD,EAC9E,OAAO,WAAW;AAErB,QACG,QAAQ,SAAS,EACjB,YAAY,+CAA+C,EAC3D,SAAS,iBAAiB,uCAAuC,EACjE,OAAO,OAAO,eAAe;AAC5B,QAAM,EAAE,gBAAAC,gBAAe,IAAI,MAAM;AACjC,QAAMA,gBAAe,UAAU;AACjC,CAAC;AAEH,QACG,QAAQ,MAAM,EACd,YAAY,kDAAkD,EAC9D,OAAO,uBAAuB,4BAA4B,EAC1D,OAAO,MAAM;AACZ,UAAQ,IAAIF,OAAM,OAAO,2BAA2B,CAAC;AACvD,CAAC;AAGH,IAAI,CAAC,QAAQ,KAAK,MAAM,CAAC,EAAE,QAAQ;AACjC,UAAQ,WAAW;AACrB;AAEA,QAAQ,MAAM,QAAQ,IAAI;","names":["fs","path","process","fs","chalk","ora","path","inquirer","path","os","inquirer","testStatuslineScript","generateMockClaudeInput","chalk","VERSION","previewCommand"]}
|
package/package.json
CHANGED
|
@@ -1,58 +1,58 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@chongdashu/cc-statusline",
|
|
3
|
-
"version": "1.2
|
|
4
|
-
"description": "Interactive CLI tool for generating custom Claude Code statuslines",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"main": "dist/index.js",
|
|
7
|
-
"bin": {
|
|
8
|
-
"cc-statusline": "./dist/index.js"
|
|
9
|
-
},
|
|
10
|
-
"scripts": {
|
|
11
|
-
"build": "tsup",
|
|
12
|
-
"dev": "tsup --watch",
|
|
13
|
-
"start": "node dist/index.js",
|
|
14
|
-
"test": "npm run build && node dist/test.js",
|
|
15
|
-
"prepublishOnly": "npm run build"
|
|
16
|
-
},
|
|
17
|
-
"keywords": [
|
|
18
|
-
"claude-code",
|
|
19
|
-
"claude",
|
|
20
|
-
"statusline",
|
|
21
|
-
"status-line",
|
|
22
|
-
"cli",
|
|
23
|
-
"terminal",
|
|
24
|
-
"productivity",
|
|
25
|
-
"development",
|
|
26
|
-
"anthropic",
|
|
27
|
-
"ai-tools"
|
|
28
|
-
],
|
|
29
|
-
"author": {
|
|
30
|
-
"name": "Chong-U",
|
|
31
|
-
"email": "chong-u@aioriented.dev",
|
|
32
|
-
"url": "https://github.com/chongdashu"
|
|
33
|
-
},
|
|
34
|
-
"license": "MIT",
|
|
35
|
-
"dependencies": {
|
|
36
|
-
"commander": "^11.1.0",
|
|
37
|
-
"inquirer": "^9.2.12",
|
|
38
|
-
"chalk": "^5.3.0",
|
|
39
|
-
"ora": "^7.0.1"
|
|
40
|
-
},
|
|
41
|
-
"devDependencies": {
|
|
42
|
-
"@types/inquirer": "^9.0.7",
|
|
43
|
-
"@types/node": "^20.10.5",
|
|
44
|
-
"tsup": "^8.0.1",
|
|
45
|
-
"typescript": "^5.3.3"
|
|
46
|
-
},
|
|
47
|
-
"engines": {
|
|
48
|
-
"node": ">=16.0.0"
|
|
49
|
-
},
|
|
50
|
-
"repository": {
|
|
51
|
-
"type": "git",
|
|
52
|
-
"url": "git+https://github.com/chongdashu/cc-statusline.git"
|
|
53
|
-
},
|
|
54
|
-
"bugs": {
|
|
55
|
-
"url": "https://github.com/chongdashu/cc-statusline/issues"
|
|
56
|
-
},
|
|
57
|
-
"homepage": "https://github.com/chongdashu/cc-statusline#readme"
|
|
1
|
+
{
|
|
2
|
+
"name": "@chongdashu/cc-statusline",
|
|
3
|
+
"version": "1.3.2",
|
|
4
|
+
"description": "Interactive CLI tool for generating custom Claude Code statuslines",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"cc-statusline": "./dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsup",
|
|
12
|
+
"dev": "tsup --watch",
|
|
13
|
+
"start": "node dist/index.js",
|
|
14
|
+
"test": "npm run build && node dist/test.js",
|
|
15
|
+
"prepublishOnly": "npm run build"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"claude-code",
|
|
19
|
+
"claude",
|
|
20
|
+
"statusline",
|
|
21
|
+
"status-line",
|
|
22
|
+
"cli",
|
|
23
|
+
"terminal",
|
|
24
|
+
"productivity",
|
|
25
|
+
"development",
|
|
26
|
+
"anthropic",
|
|
27
|
+
"ai-tools"
|
|
28
|
+
],
|
|
29
|
+
"author": {
|
|
30
|
+
"name": "Chong-U",
|
|
31
|
+
"email": "chong-u@aioriented.dev",
|
|
32
|
+
"url": "https://github.com/chongdashu"
|
|
33
|
+
},
|
|
34
|
+
"license": "MIT",
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"commander": "^11.1.0",
|
|
37
|
+
"inquirer": "^9.2.12",
|
|
38
|
+
"chalk": "^5.3.0",
|
|
39
|
+
"ora": "^7.0.1"
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"@types/inquirer": "^9.0.7",
|
|
43
|
+
"@types/node": "^20.10.5",
|
|
44
|
+
"tsup": "^8.0.1",
|
|
45
|
+
"typescript": "^5.3.3"
|
|
46
|
+
},
|
|
47
|
+
"engines": {
|
|
48
|
+
"node": ">=16.0.0"
|
|
49
|
+
},
|
|
50
|
+
"repository": {
|
|
51
|
+
"type": "git",
|
|
52
|
+
"url": "git+https://github.com/chongdashu/cc-statusline.git"
|
|
53
|
+
},
|
|
54
|
+
"bugs": {
|
|
55
|
+
"url": "https://github.com/chongdashu/cc-statusline/issues"
|
|
56
|
+
},
|
|
57
|
+
"homepage": "https://github.com/chongdashu/cc-statusline#readme"
|
|
58
58
|
}
|
|
@@ -1,55 +1,55 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# Comprehensive test for concurrent ccusage locking mechanism
|
|
3
|
-
#
|
|
4
|
-
# This script tests the locking mechanism under various scenarios:
|
|
5
|
-
# 1. Concurrent execution test
|
|
6
|
-
# 2. Stale lock cleanup test
|
|
7
|
-
# 3. Performance verification
|
|
8
|
-
#
|
|
9
|
-
# Usage: ./test/test-concurrent-locking.sh
|
|
10
|
-
|
|
11
|
-
set -euo pipefail
|
|
12
|
-
|
|
13
|
-
TEST_DIR="$(dirname "$0")"
|
|
14
|
-
STATUSLINE_TEST="$TEST_DIR/test-statusline-with-lock.sh"
|
|
15
|
-
|
|
16
|
-
echo "๐งช Testing ccusage locking mechanism..."
|
|
17
|
-
echo
|
|
18
|
-
|
|
19
|
-
# Test 1: Concurrent execution
|
|
20
|
-
echo "๐ Test 1: Concurrent execution (10 processes)"
|
|
21
|
-
echo "Expected: Only 1 process runs ccusage, others skip gracefully"
|
|
22
|
-
echo
|
|
23
|
-
|
|
24
|
-
start_time=$(date +%s)
|
|
25
|
-
for i in {1..10}; do
|
|
26
|
-
echo '{}' | "$STATUSLINE_TEST" &
|
|
27
|
-
done
|
|
28
|
-
wait
|
|
29
|
-
end_time=$(date +%s)
|
|
30
|
-
duration=$((end_time - start_time))
|
|
31
|
-
|
|
32
|
-
echo
|
|
33
|
-
echo "โ
Test 1 completed in ${duration}s"
|
|
34
|
-
echo
|
|
35
|
-
|
|
36
|
-
# Test 2: Lock cleanup verification
|
|
37
|
-
echo "๐ Test 2: Verifying lock cleanup"
|
|
38
|
-
LOCK_EXISTS=$(ls /tmp/ccusage_statusline.lock 2>/dev/null || echo "")
|
|
39
|
-
PID_EXISTS=$(ls /tmp/ccusage_statusline.pid 2>/dev/null || echo "")
|
|
40
|
-
|
|
41
|
-
if [ -z "$LOCK_EXISTS" ] && [ -z "$PID_EXISTS" ]; then
|
|
42
|
-
echo "โ
Lock files properly cleaned up"
|
|
43
|
-
else
|
|
44
|
-
echo "โ Lock files still exist:"
|
|
45
|
-
[ -n "$LOCK_EXISTS" ] && echo " - Lock directory: $LOCK_EXISTS"
|
|
46
|
-
[ -n "$PID_EXISTS" ] && echo " - PID file: $PID_EXISTS"
|
|
47
|
-
fi
|
|
48
|
-
|
|
49
|
-
echo
|
|
50
|
-
echo "๐ All tests completed!"
|
|
51
|
-
echo
|
|
52
|
-
echo "๐ก Manual verification:"
|
|
53
|
-
echo " 1. Check that only 1 'Running ccusage...' message appeared in stderr"
|
|
54
|
-
echo " 2. Verify multiple 'Skipped - lock held by' messages appeared"
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Comprehensive test for concurrent ccusage locking mechanism
|
|
3
|
+
#
|
|
4
|
+
# This script tests the locking mechanism under various scenarios:
|
|
5
|
+
# 1. Concurrent execution test
|
|
6
|
+
# 2. Stale lock cleanup test
|
|
7
|
+
# 3. Performance verification
|
|
8
|
+
#
|
|
9
|
+
# Usage: ./test/test-concurrent-locking.sh
|
|
10
|
+
|
|
11
|
+
set -euo pipefail
|
|
12
|
+
|
|
13
|
+
TEST_DIR="$(dirname "$0")"
|
|
14
|
+
STATUSLINE_TEST="$TEST_DIR/test-statusline-with-lock.sh"
|
|
15
|
+
|
|
16
|
+
echo "๐งช Testing ccusage locking mechanism..."
|
|
17
|
+
echo
|
|
18
|
+
|
|
19
|
+
# Test 1: Concurrent execution
|
|
20
|
+
echo "๐ Test 1: Concurrent execution (10 processes)"
|
|
21
|
+
echo "Expected: Only 1 process runs ccusage, others skip gracefully"
|
|
22
|
+
echo
|
|
23
|
+
|
|
24
|
+
start_time=$(date +%s)
|
|
25
|
+
for i in {1..10}; do
|
|
26
|
+
echo '{}' | "$STATUSLINE_TEST" &
|
|
27
|
+
done
|
|
28
|
+
wait
|
|
29
|
+
end_time=$(date +%s)
|
|
30
|
+
duration=$((end_time - start_time))
|
|
31
|
+
|
|
32
|
+
echo
|
|
33
|
+
echo "โ
Test 1 completed in ${duration}s"
|
|
34
|
+
echo
|
|
35
|
+
|
|
36
|
+
# Test 2: Lock cleanup verification
|
|
37
|
+
echo "๐ Test 2: Verifying lock cleanup"
|
|
38
|
+
LOCK_EXISTS=$(ls /tmp/ccusage_statusline.lock 2>/dev/null || echo "")
|
|
39
|
+
PID_EXISTS=$(ls /tmp/ccusage_statusline.pid 2>/dev/null || echo "")
|
|
40
|
+
|
|
41
|
+
if [ -z "$LOCK_EXISTS" ] && [ -z "$PID_EXISTS" ]; then
|
|
42
|
+
echo "โ
Lock files properly cleaned up"
|
|
43
|
+
else
|
|
44
|
+
echo "โ Lock files still exist:"
|
|
45
|
+
[ -n "$LOCK_EXISTS" ] && echo " - Lock directory: $LOCK_EXISTS"
|
|
46
|
+
[ -n "$PID_EXISTS" ] && echo " - PID file: $PID_EXISTS"
|
|
47
|
+
fi
|
|
48
|
+
|
|
49
|
+
echo
|
|
50
|
+
echo "๐ All tests completed!"
|
|
51
|
+
echo
|
|
52
|
+
echo "๐ก Manual verification:"
|
|
53
|
+
echo " 1. Check that only 1 'Running ccusage...' message appeared in stderr"
|
|
54
|
+
echo " 2. Verify multiple 'Skipped - lock held by' messages appeared"
|
|
55
55
|
echo " 3. Confirm no hanging processes with: ps aux | grep ccusage"
|