claude-statusline 2.1.5 → 2.2.0

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.
Files changed (30) hide show
  1. package/README.md +6 -12
  2. package/dist/core/cache.js +4 -4
  3. package/dist/core/cache.js.map +1 -1
  4. package/dist/env/context.js +3 -3
  5. package/dist/env/context.js.map +1 -1
  6. package/dist/index.bundle.js +9 -9
  7. package/dist/index.bundle.js.map +2 -2
  8. package/dist/index.js +3 -53
  9. package/dist/index.js.map +1 -1
  10. package/dist/metafile.json +12 -12
  11. package/dist/metafile.prod.json +12 -12
  12. package/dist/ui/symbols.js +1 -1
  13. package/dist/ui/symbols.js.map +1 -1
  14. package/dist/ui/width.js +71 -17
  15. package/dist/ui/width.js.map +1 -1
  16. package/docs/README.md +95 -54
  17. package/docs/{MIGRATION.md → guides/MIGRATION.md} +1 -1
  18. package/docs/{guide-02-troubleshooting.md → guides/guide-02-troubleshooting.md} +43 -2
  19. package/docs/{guide-03-performance.md → guides/guide-03-performance.md} +1 -1
  20. package/docs/plans/eval-002-comprehensive-code-review.md +536 -0
  21. package/docs/{FEATURE_COMPARISON.md → ref/FEATURE_COMPARISON.md} +1 -1
  22. package/package.json +1 -1
  23. package/docs/investigation-garbled-output.tmp.md +0 -387
  24. /package/docs/{guide-01-configuration.md → guides/guide-01-configuration.md} +0 -0
  25. /package/docs/{eval-01-terminal-widths.md → plans/eval-001-terminal-widths.md} +0 -0
  26. /package/docs/{prd-01-typescript-perf-optimization.md → plans/prd-01-typescript-perf-optimization.md} +0 -0
  27. /package/docs/{research-01-sandbox-detection.md → plans/research-01-sandbox-detection.md} +0 -0
  28. /package/docs/{research-02-competitive-analysis.md → plans/research-02-competitive-analysis.md} +0 -0
  29. /package/docs/{research-03-platform-analysis.md → plans/research-03-platform-analysis.md} +0 -0
  30. /package/docs/{ARCHITECTURE.md → ref/ARCHITECTURE.md} +0 -0
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  Simple statusline for Claude Code with project-branch, git indicators, and context usage. Optimized for speed with bun. Just the essentials, none of the bloat.
4
4
 
5
5
  ![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)
6
- ![Version](https://img.shields.io/badge/version-2.1.4-green.svg)
6
+ ![Version](https://img.shields.io/badge/version-2.2.0-green.svg)
7
7
  ![TypeScript](https://img.shields.io/badge/language-TypeScript-3178C6.svg)
8
8
  ![Node](https://img.shields.io/badge/node-%3E%3D22.6.0-brightgreen.svg)
9
9
  ![Bun](https://img.shields.io/badge/runtime-Bun-black.svg)
@@ -136,27 +136,21 @@ bun install -g claude-statusline # Downloads 19KB in <1 second
136
136
 
137
137
  ### Context Window Usage (Beta Feature)
138
138
 
139
- **⚠️ Beta Feature:** Context window usage display is currently in beta. The calculation follows the official Claude Code documentation but may show different values compared to Claude Code's built-in `/context` command.
140
-
141
- Automatically displays context window usage percentage when available (requires Claude Code to send context window data):
139
+ Automatically displays context window remaining percentage when available (requires Claude Code to send context window data):
142
140
 
143
141
  ```
144
- claude-statusline @ main [$!] *Opus #27% (ASCII version)
142
+ claude-statusline @ main [$!] *Opus ≈76% (ASCII version)
145
143
  ```
146
144
 
147
- Shows percentage of context window consumed in the current conversation. The symbol varies by mode:
145
+ Shows percentage of context window remaining in the current conversation. The symbol varies by mode:
148
146
  - **Nerd Font**: ⚡︎ (lightning bolt)
149
- - **ASCII**: # (hash symbol)
147
+ - **ASCII**: (approximately equals)
150
148
 
151
149
  **Important Notes:**
152
- - Calculation follows [official Claude Code documentation](https://code.claude.com/docs/en/statusline#context-window-usage)
153
- - May differ from `/context` command due to different data sources or calculation methods
150
+ - Uses the `remaining_percentage` field provided by Claude Code API (since v2.1.15)
154
151
  - Only shows when Claude Code provides context window data
155
152
  - Can be disabled with `"noContextWindow": true` or `CLAUDE_CODE_STATUSLINE_NO_CONTEXT_WINDOW=1`
156
153
 
157
- **Known Discrepancy:**
158
- Some users have noted differences between the statusline percentage and `/context` command output. This appears to be a difference in how Claude Code internally calculates context usage versus what's provided through the statusline API.
159
-
160
154
  ### Environment Context
161
155
 
162
156
  When enabled with `"envContext": true`, shows development tool versions:
@@ -18,7 +18,7 @@ export class Cache {
18
18
  */
19
19
  async ensureCacheDir() {
20
20
  try {
21
- await mkdir(this.config.cacheDir, { recursive: true });
21
+ await mkdir(this.config.cacheDir, { recursive: true, mode: 0o700 });
22
22
  }
23
23
  catch (error) {
24
24
  // Directory might already exist or we can't create it
@@ -201,10 +201,10 @@ export async function cachedCommand(cache, key, command, args = [], ttl = 300) {
201
201
  return cached;
202
202
  }
203
203
  try {
204
- const { exec } = await import('child_process');
204
+ const { execFile } = await import('child_process');
205
205
  const { promisify } = await import('util');
206
- const execAsync = promisify(exec);
207
- const { stdout } = await execAsync(`${command} ${args.join(' ')}`, {
206
+ const execFileAsync = promisify(execFile);
207
+ const { stdout } = await execFileAsync(command, args, {
208
208
  timeout: 5000, // 5 second timeout
209
209
  encoding: 'utf-8',
210
210
  });
@@ -1 +1 @@
1
- {"version":3,"file":"cache.js","sourceRoot":"","sources":["../../src/core/cache.ts"],"names":[],"mappings":"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","sourcesContent":["import { readFile, writeFile, mkdir } from 'fs/promises';\nimport { existsSync } from 'fs';\nimport { join } from 'path';\nimport { Config } from './config.js';\n\n/**\n * Simple caching system with TTL support\n * Ported from bash implementation with Node.js optimizations\n */\n\n\n/**\n * Cache wrapper that handles TTL and file operations\n */\nexport class Cache {\n private config: Config;\n\n constructor(config: Config) {\n this.config = config;\n }\n\n /**\n * Ensure cache directory exists\n */\n private async ensureCacheDir(): Promise<void> {\n try {\n await mkdir(this.config.cacheDir, { recursive: true });\n } catch (error) {\n // Directory might already exist or we can't create it\n console.warn('[WARNING] Failed to create cache directory:', this.config.cacheDir);\n }\n }\n\n /**\n * Get cache file path for a given key\n */\n private getCachePath(key: string): string {\n return join(this.config.cacheDir, key);\n }\n\n /**\n * Get cache timestamp file path for a given key\n */\n private getTimestampPath(key: string): string {\n return join(this.config.cacheDir, `${key}.time`);\n }\n\n /**\n * Read cached data with TTL validation\n */\n async get<T = string>(key: string, ttl: number = this.config.cacheTTL): Promise<T | null> {\n const cachePath = this.getCachePath(key);\n const timestampPath = this.getTimestampPath(key);\n\n try {\n // Check if cache files exist\n if (!existsSync(cachePath) || !existsSync(timestampPath)) {\n return null;\n }\n\n // Read timestamp and check TTL\n const timestampContent = await readFile(timestampPath, 'utf-8');\n const timestamp = parseInt(timestampContent.trim(), 10);\n\n if (isNaN(timestamp)) {\n return null;\n }\n\n const currentTime = Math.floor(Date.now() / 1000);\n const age = currentTime - timestamp;\n\n // In development, reduce cache TTL to prevent stale data\n const effectiveTTL = process.env.NODE_ENV === 'development' ? Math.min(ttl, 5) : ttl;\n\n if (age >= effectiveTTL) {\n // Cache expired\n return null;\n }\n\n // Read cached data\n const dataContent = await readFile(cachePath, 'utf-8');\n\n // Try to parse as JSON, fallback to string\n try {\n return JSON.parse(dataContent) as T;\n } catch {\n return dataContent as T;\n }\n\n } catch (error) {\n // Any error reading cache should result in cache miss\n console.debug('[DEBUG] Cache read error for key:', key, error instanceof Error ? error.message : String(error));\n return null;\n }\n }\n\n /**\n * Write data to cache with timestamp\n */\n async set<T = string>(key: string, data: T): Promise<boolean> {\n const cachePath = this.getCachePath(key);\n const timestampPath = this.getTimestampPath(key);\n\n try {\n // Ensure cache directory exists\n await this.ensureCacheDir();\n\n // Prepare data for storage\n let dataContent: string;\n if (typeof data === 'string') {\n dataContent = data;\n } else {\n dataContent = JSON.stringify(data);\n }\n\n // Write data and timestamp\n const currentTime = Math.floor(Date.now() / 1000);\n\n await Promise.all([\n writeFile(cachePath, dataContent, 'utf-8'),\n writeFile(timestampPath, currentTime.toString(), 'utf-8'),\n ]);\n\n return true;\n\n } catch (error) {\n console.warn('[WARNING] Failed to write cache for key:', key, error instanceof Error ? error.message : String(error));\n return false;\n }\n }\n\n /**\n * Check if cache entry exists and is valid\n */\n async has(key: string, ttl: number = this.config.cacheTTL): Promise<boolean> {\n const value = await this.get(key, ttl);\n return value !== null;\n }\n\n /**\n * Delete cache entry\n */\n async delete(key: string): Promise<boolean> {\n const cachePath = this.getCachePath(key);\n const timestampPath = this.getTimestampPath(key);\n\n try {\n const { unlink } = await import('fs/promises');\n\n await Promise.allSettled([\n unlink(cachePath),\n unlink(timestampPath),\n ]);\n\n return true;\n } catch (error) {\n console.warn('[WARNING] Failed to delete cache for key:', key, error instanceof Error ? error.message : String(error));\n return false;\n }\n }\n\n /**\n * Clear all cache entries\n */\n async clear(): Promise<boolean> {\n try {\n const { readdir, unlink } = await import('fs/promises');\n const files = await readdir(this.config.cacheDir);\n\n await Promise.allSettled(\n files.map(file => unlink(join(this.config.cacheDir, file)))\n );\n\n return true;\n } catch (error) {\n console.warn('[WARNING] Failed to clear cache:', error instanceof Error ? error.message : String(error));\n return false;\n }\n }\n\n /**\n * Get cache statistics\n */\n async getStats(): Promise<{ total: number; size: number }> {\n try {\n const { readdir, stat } = await import('fs/promises');\n const files = await readdir(this.config.cacheDir);\n\n let totalSize = 0;\n let cacheFiles = 0;\n\n for (const file of files) {\n if (!file.endsWith('.time')) {\n cacheFiles++;\n const filePath = join(this.config.cacheDir, file);\n try {\n const stats = await stat(filePath);\n totalSize += stats.size;\n } catch {\n // Skip files that can't be stat'ed\n }\n }\n }\n\n return { total: cacheFiles, size: totalSize };\n } catch (error) {\n console.warn('[WARNING] Failed to get cache stats:', error instanceof Error ? error.message : String(error));\n return { total: 0, size: 0 };\n }\n }\n}\n\n/**\n * Cache key generators for common use cases\n */\nexport const CacheKeys = {\n NODE_VERSION: 'node_version',\n PYTHON_VERSION: 'python_version',\n PYTHON3_VERSION: 'python3_version',\n DOCKER_VERSION: 'docker_version',\n GIT_REMOTE_URL: (dir: string) => `git_remote_${Buffer.from(dir).toString('base64')}`,\n GIT_BRANCH: (dir: string) => `git_branch_${Buffer.from(dir).toString('base64')}`,\n} as const;\n\n/**\n * Cached command execution helper\n * Runs a command and caches the result\n */\nexport async function cachedCommand(\n cache: Cache,\n key: string,\n command: string,\n args: string[] = [],\n ttl: number = 300\n): Promise<string | null> {\n // Try to get from cache first\n const cached = await cache.get<string>(key, ttl);\n if (cached !== null) {\n return cached;\n }\n\n try {\n const { exec } = await import('child_process');\n const { promisify } = await import('util');\n const execAsync = promisify(exec);\n\n const { stdout } = await execAsync(`${command} ${args.join(' ')}`, {\n timeout: 5000, // 5 second timeout\n encoding: 'utf-8',\n });\n\n const result = stdout.trim();\n if (result) {\n // Cache the successful result\n await cache.set(key, result);\n }\n\n return result;\n\n } catch (error) {\n console.debug('[DEBUG] Command execution failed:', command, error instanceof Error ? error.message : String(error));\n return null;\n }\n}"]}
1
+ {"version":3,"file":"cache.js","sourceRoot":"","sources":["../../src/core/cache.ts"],"names":[],"mappings":"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","sourcesContent":["import { readFile, writeFile, mkdir } from 'fs/promises';\nimport { existsSync } from 'fs';\nimport { join } from 'path';\nimport { Config } from './config.js';\n\n/**\n * Simple caching system with TTL support\n * Ported from bash implementation with Node.js optimizations\n */\n\n\n/**\n * Cache wrapper that handles TTL and file operations\n */\nexport class Cache {\n private config: Config;\n\n constructor(config: Config) {\n this.config = config;\n }\n\n /**\n * Ensure cache directory exists\n */\n private async ensureCacheDir(): Promise<void> {\n try {\n await mkdir(this.config.cacheDir, { recursive: true, mode: 0o700 });\n } catch (error) {\n // Directory might already exist or we can't create it\n console.warn('[WARNING] Failed to create cache directory:', this.config.cacheDir);\n }\n }\n\n /**\n * Get cache file path for a given key\n */\n private getCachePath(key: string): string {\n return join(this.config.cacheDir, key);\n }\n\n /**\n * Get cache timestamp file path for a given key\n */\n private getTimestampPath(key: string): string {\n return join(this.config.cacheDir, `${key}.time`);\n }\n\n /**\n * Read cached data with TTL validation\n */\n async get<T = string>(key: string, ttl: number = this.config.cacheTTL): Promise<T | null> {\n const cachePath = this.getCachePath(key);\n const timestampPath = this.getTimestampPath(key);\n\n try {\n // Check if cache files exist\n if (!existsSync(cachePath) || !existsSync(timestampPath)) {\n return null;\n }\n\n // Read timestamp and check TTL\n const timestampContent = await readFile(timestampPath, 'utf-8');\n const timestamp = parseInt(timestampContent.trim(), 10);\n\n if (isNaN(timestamp)) {\n return null;\n }\n\n const currentTime = Math.floor(Date.now() / 1000);\n const age = currentTime - timestamp;\n\n // In development, reduce cache TTL to prevent stale data\n const effectiveTTL = process.env.NODE_ENV === 'development' ? Math.min(ttl, 5) : ttl;\n\n if (age >= effectiveTTL) {\n // Cache expired\n return null;\n }\n\n // Read cached data\n const dataContent = await readFile(cachePath, 'utf-8');\n\n // Try to parse as JSON, fallback to string\n try {\n return JSON.parse(dataContent) as T;\n } catch {\n return dataContent as T;\n }\n\n } catch (error) {\n // Any error reading cache should result in cache miss\n console.debug('[DEBUG] Cache read error for key:', key, error instanceof Error ? error.message : String(error));\n return null;\n }\n }\n\n /**\n * Write data to cache with timestamp\n */\n async set<T = string>(key: string, data: T): Promise<boolean> {\n const cachePath = this.getCachePath(key);\n const timestampPath = this.getTimestampPath(key);\n\n try {\n // Ensure cache directory exists\n await this.ensureCacheDir();\n\n // Prepare data for storage\n let dataContent: string;\n if (typeof data === 'string') {\n dataContent = data;\n } else {\n dataContent = JSON.stringify(data);\n }\n\n // Write data and timestamp\n const currentTime = Math.floor(Date.now() / 1000);\n\n await Promise.all([\n writeFile(cachePath, dataContent, 'utf-8'),\n writeFile(timestampPath, currentTime.toString(), 'utf-8'),\n ]);\n\n return true;\n\n } catch (error) {\n console.warn('[WARNING] Failed to write cache for key:', key, error instanceof Error ? error.message : String(error));\n return false;\n }\n }\n\n /**\n * Check if cache entry exists and is valid\n */\n async has(key: string, ttl: number = this.config.cacheTTL): Promise<boolean> {\n const value = await this.get(key, ttl);\n return value !== null;\n }\n\n /**\n * Delete cache entry\n */\n async delete(key: string): Promise<boolean> {\n const cachePath = this.getCachePath(key);\n const timestampPath = this.getTimestampPath(key);\n\n try {\n const { unlink } = await import('fs/promises');\n\n await Promise.allSettled([\n unlink(cachePath),\n unlink(timestampPath),\n ]);\n\n return true;\n } catch (error) {\n console.warn('[WARNING] Failed to delete cache for key:', key, error instanceof Error ? error.message : String(error));\n return false;\n }\n }\n\n /**\n * Clear all cache entries\n */\n async clear(): Promise<boolean> {\n try {\n const { readdir, unlink } = await import('fs/promises');\n const files = await readdir(this.config.cacheDir);\n\n await Promise.allSettled(\n files.map(file => unlink(join(this.config.cacheDir, file)))\n );\n\n return true;\n } catch (error) {\n console.warn('[WARNING] Failed to clear cache:', error instanceof Error ? error.message : String(error));\n return false;\n }\n }\n\n /**\n * Get cache statistics\n */\n async getStats(): Promise<{ total: number; size: number }> {\n try {\n const { readdir, stat } = await import('fs/promises');\n const files = await readdir(this.config.cacheDir);\n\n let totalSize = 0;\n let cacheFiles = 0;\n\n for (const file of files) {\n if (!file.endsWith('.time')) {\n cacheFiles++;\n const filePath = join(this.config.cacheDir, file);\n try {\n const stats = await stat(filePath);\n totalSize += stats.size;\n } catch {\n // Skip files that can't be stat'ed\n }\n }\n }\n\n return { total: cacheFiles, size: totalSize };\n } catch (error) {\n console.warn('[WARNING] Failed to get cache stats:', error instanceof Error ? error.message : String(error));\n return { total: 0, size: 0 };\n }\n }\n}\n\n/**\n * Cache key generators for common use cases\n */\nexport const CacheKeys = {\n NODE_VERSION: 'node_version',\n PYTHON_VERSION: 'python_version',\n PYTHON3_VERSION: 'python3_version',\n DOCKER_VERSION: 'docker_version',\n GIT_REMOTE_URL: (dir: string) => `git_remote_${Buffer.from(dir).toString('base64')}`,\n GIT_BRANCH: (dir: string) => `git_branch_${Buffer.from(dir).toString('base64')}`,\n} as const;\n\n/**\n * Cached command execution helper\n * Runs a command and caches the result\n */\nexport async function cachedCommand(\n cache: Cache,\n key: string,\n command: string,\n args: string[] = [],\n ttl: number = 300\n): Promise<string | null> {\n // Try to get from cache first\n const cached = await cache.get<string>(key, ttl);\n if (cached !== null) {\n return cached;\n }\n\n try {\n const { execFile } = await import('child_process');\n const { promisify } = await import('util');\n const execFileAsync = promisify(execFile);\n\n const { stdout } = await execFileAsync(command, args, {\n timeout: 5000, // 5 second timeout\n encoding: 'utf-8' as BufferEncoding,\n });\n\n const result = stdout.trim();\n if (result) {\n // Cache the successful result\n await cache.set(key, result);\n }\n\n return result;\n\n } catch (error) {\n console.debug('[DEBUG] Command execution failed:', command, error instanceof Error ? error.message : String(error));\n return null;\n }\n}"]}
@@ -163,10 +163,10 @@ export class EnvironmentDetector {
163
163
  */
164
164
  async isToolAvailable(tool) {
165
165
  try {
166
- const { exec } = await import('child_process');
166
+ const { execFile } = await import('child_process');
167
167
  const { promisify } = await import('util');
168
- const execAsync = promisify(exec);
169
- await execAsync(`command -v ${tool}`, { timeout: 2000 });
168
+ const execFileAsync = promisify(execFile);
169
+ await execFileAsync('which', [tool], { timeout: 2000 });
170
170
  return true;
171
171
  }
172
172
  catch {
@@ -1 +1 @@
1
- {"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/env/context.ts"],"names":[],"mappings":"AACA,OAAO,EAAS,aAAa,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAYnE;;;GAGG;AACH,MAAM,OAAO,mBAAmB;IACtB,MAAM,CAAS;IACf,KAAK,CAAQ;IAErB,YAAY,MAAc,EAAE,KAAY;QACtC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACrB,CAAC;IAEC;;KAEC;IACH,KAAK,CAAC,kBAAkB;QACtB,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC;YAC5B,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,OAAO,GAAoB,EAAE,CAAC;QAEpC,6DAA6D;QAC7D,MAAM,CAAC,WAAW,EAAE,aAAa,EAAE,aAAa,CAAC,GAAG,MAAM,OAAO,CAAC,UAAU,CAAC;YAC3E,IAAI,CAAC,cAAc,EAAE;YACrB,IAAI,CAAC,gBAAgB,EAAE;YACvB,IAAI,CAAC,gBAAgB,EAAE;SACxB,CAAC,CAAC;QAEH,IAAI,WAAW,CAAC,MAAM,KAAK,WAAW,IAAI,WAAW,CAAC,KAAK,EAAE,CAAC;YAC5D,OAAO,CAAC,IAAI,GAAG,WAAW,CAAC,KAAK,CAAC;QACnC,CAAC;QAED,IAAI,aAAa,CAAC,MAAM,KAAK,WAAW,IAAI,aAAa,CAAC,KAAK,EAAE,CAAC;YAChE,OAAO,CAAC,MAAM,GAAG,aAAa,CAAC,KAAK,CAAC;QACvC,CAAC;QAED,IAAI,aAAa,CAAC,MAAM,KAAK,WAAW,IAAI,aAAa,CAAC,KAAK,EAAE,CAAC;YAChE,OAAO,CAAC,MAAM,GAAG,aAAa,CAAC,KAAK,CAAC;QACvC,CAAC;QAED,oDAAoD;QACpD,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACtC,OAAO,IAAI,CAAC;QACd,CAAC;QAED,OAAO,OAAO,CAAC;IACjB,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,cAAc;QAC1B,MAAM,QAAQ,GAAG,SAAS,CAAC,YAAY,CAAC;QACxC,0EAA0E;QAC1E,wFAAwF;QACxF,MAAM,WAAW,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,GAAG,EAAE,CAAC;QAE9C,kBAAkB;QAClB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAS,QAAQ,EAAE,WAAW,CAAC,CAAC;QACnE,IAAI,MAAM,EAAE,CAAC;YACX,OAAO,MAAM,CAAC;QAChB,CAAC;QAED,IAAI,CAAC;YACH,2BAA2B;YAC3B,IAAI,OAAO,GAAG,MAAM,aAAa,CAC/B,IAAI,CAAC,KAAK,EACV,QAAQ,EACR,MAAM,EACN,CAAC,WAAW,CAAC,EACb,WAAW,CACZ,CAAC;YAEF,IAAI,OAAO,EAAE,CAAC;gBACZ,iCAAiC;gBACjC,OAAO,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;YAC1C,CAAC;YAED,OAAO,IAAI,CAAC;QAEd,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,wCAAwC,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;YAChH,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,gBAAgB;QAC5B,MAAM,UAAU,GAAG,SAAS,CAAC,eAAe,CAAC;QAC7C,MAAM,SAAS,GAAG,SAAS,CAAC,cAAc,CAAC;QAC3C,0EAA0E;QAC1E,wFAAwF;QACxF,MAAM,WAAW,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,GAAG,EAAE,CAAC;QAE9C,oBAAoB;QACpB,IAAI,CAAC;YACH,IAAI,OAAO,GAAG,MAAM,aAAa,CAC/B,IAAI,CAAC,KAAK,EACV,UAAU,EACV,SAAS,EACT,CAAC,WAAW,CAAC,EACb,WAAW,CACZ,CAAC;YAEF,IAAI,OAAO,EAAE,CAAC;gBACZ,oDAAoD;gBACpD,MAAM,YAAY,GAAG,OAAO,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;gBACtD,IAAI,YAAY,EAAE,CAAC;oBACjB,OAAO,YAAY,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;gBACjC,CAAC;YACH,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,oCAAoC;QACtC,CAAC;QAED,qBAAqB;QACrB,IAAI,CAAC;YACH,IAAI,OAAO,GAAG,MAAM,aAAa,CAC/B,IAAI,CAAC,KAAK,EACV,SAAS,EACT,QAAQ,EACR,CAAC,WAAW,CAAC,EACb,WAAW,CACZ,CAAC;YAEF,IAAI,OAAO,EAAE,CAAC;gBACZ,sEAAsE;gBACtE,MAAM,YAAY,GAAG,OAAO,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;gBACtD,IAAI,YAAY,EAAE,CAAC;oBACjB,OAAO,YAAY,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;gBACjC,CAAC;YACH,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,8BAA8B;QAChC,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,gBAAgB;QAC5B,MAAM,QAAQ,GAAG,gBAAgB,CAAC;QAElC,IAAI,CAAC;YACH,IAAI,OAAO,GAAG,MAAM,aAAa,CAC/B,IAAI,CAAC,KAAK,EACV,QAAQ,EACR,QAAQ,EACR,CAAC,WAAW,CAAC,EACb,IAAI,CAAC,MAAM,CAAC,QAAQ,GAAG,EAAE,CAAC,+CAA+C;aAC1E,CAAC;YAEF,IAAI,OAAO,EAAE,CAAC;gBACZ,6DAA6D;gBAC7D,MAAM,YAAY,GAAG,OAAO,CAAC,KAAK,CAAC,gCAAgC,CAAC,CAAC;gBACrE,IAAI,YAAY,EAAE,CAAC;oBACjB,OAAO,YAAY,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;gBACjC,CAAC;YACH,CAAC;YAED,OAAO,IAAI,CAAC;QAEd,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,uCAAuC,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;YAC/G,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED;;OAEG;IACH,qBAAqB,CAAC,OAAwB,EAAE,OAAiD;QAC/F,MAAM,KAAK,GAAa,EAAE,CAAC;QAE3B,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;YACjB,KAAK,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;QAC/C,CAAC;QAED,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;YACnB,KAAK,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;QACnD,CAAC;QAED,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;YACnB,KAAK,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;QACnD,CAAC;QAED,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACzB,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,kBAAkB;QACtB,MAAM,eAAe,GAA8B,EAAE,CAAC;QAEtD,8BAA8B;QAC9B,eAAe;QACf,iBAAiB;QACjB,iBAAiB;QACjB,iBAAiB;QACjB,uBAAuB;QACvB,iBAAiB;QACjB,SAAS;QAET,OAAO,eAAe,CAAC;IACzB,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,eAAe,CAAC,IAAY;QAChC,IAAI,CAAC;YACH,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,MAAM,CAAC,eAAe,CAAC,CAAC;YAC/C,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,CAAC;YAC3C,MAAM,SAAS,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;YAElC,MAAM,SAAS,CAAC,cAAc,IAAI,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;YACzD,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED;;OAEG;IACH,mBAAmB;QACjB,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,KAAK,IAAI,SAAS,CAAC;QAE7C,sDAAsD;QACtD,IAAI,YAAgC,CAAC;QAErC,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;YAC3B,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;QAC1C,CAAC;aAAM,IAAI,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YACjC,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC;QACzC,CAAC;aAAM,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;YAClC,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;QAC1C,CAAC;QAED,OAAO,YAAY,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC;IAC5D,CAAC;IAED;;OAEG;IACH,SAAS;QACP,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;QAClC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;QAC1B,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QAErD,OAAO,OAAO,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IACpE,CAAC;CACF;AAED;;GAEG;AACH,MAAM,OAAO,oBAAoB;IACvB,OAAO,CAA2C;IAE1D,YAAY,OAAiD;QAC3D,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,OAAwB,EAAE,QAA2C,SAAS;QACnF,QAAQ,KAAK,EAAE,CAAC;YACd,KAAK,SAAS;gBACZ,OAAO,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;YACrC,KAAK,SAAS;gBACZ,OAAO,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;YACrC,KAAK,SAAS;gBACZ,OAAO,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;YACrC;gBACE,OAAO,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;QACvC,CAAC;IACH,CAAC;IAED;;OAEG;IACK,aAAa,CAAC,OAAwB;QAC5C,MAAM,KAAK,GAAa,EAAE,CAAC;QAE3B,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;YACjB,KAAK,CAAC,IAAI,CAAC,OAAO,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;QACpC,CAAC;QAED,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;YACnB,KAAK,CAAC,IAAI,CAAC,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;QACpC,CAAC;QAED,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;YACnB,KAAK,CAAC,IAAI,CAAC,SAAS,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;QACxC,CAAC;QAED,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACzB,CAAC;IAED;;OAEG;IACK,aAAa,CAAC,OAAwB;QAC5C,MAAM,KAAK,GAAa,EAAE,CAAC;QAE3B,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;YACjB,KAAK,CAAC,IAAI,CAAC,YAAY,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;QACzC,CAAC;QAED,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;YACnB,KAAK,CAAC,IAAI,CAAC,UAAU,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;QACzC,CAAC;QAED,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;YACnB,KAAK,CAAC,IAAI,CAAC,UAAU,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;QACzC,CAAC;QAED,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC3B,CAAC;IAED;;OAEG;IACK,aAAa,CAAC,OAAwB;QAC5C,MAAM,KAAK,GAAa,EAAE,CAAC;QAE3B,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;YACjB,MAAM,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;YAChD,KAAK,CAAC,IAAI,CAAC,IAAI,YAAY,EAAE,CAAC,CAAC;QACjC,CAAC;QAED,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;YACnB,MAAM,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;YAClD,KAAK,CAAC,IAAI,CAAC,IAAI,YAAY,EAAE,CAAC,CAAC;QACjC,CAAC;QAED,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;YACnB,MAAM,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;YAClD,KAAK,CAAC,IAAI,CAAC,IAAI,YAAY,EAAE,CAAC,CAAC;QACjC,CAAC;QAED,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACzB,CAAC;IAED;;OAEG;IACH,eAAe,CAAC,OAAwB;QACtC,MAAM,KAAK,GAAa,EAAE,CAAC;QAE3B,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;YACjB,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;QACpD,CAAC;QAED,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;YACnB,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;QACxD,CAAC;QAED,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;YACnB,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;QACxD,CAAC;QAED,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACzB,CAAC;CACF","sourcesContent":["import { Config } from '../core/config.js';\nimport { Cache, cachedCommand, CacheKeys } from '../core/cache.js';\nimport { getEnvironmentSymbols } from '../ui/symbols.js';\n\n/**\n * Environment version information interface\n */\nexport interface EnvironmentInfo {\n node?: string;\n python?: string;\n docker?: string;\n}\n\n/**\n * Environment context detection\n * Ported from bash implementation with enhanced TypeScript safety\n */\nexport class EnvironmentDetector {\n private config: Config;\n private cache: Cache;\n\n constructor(config: Config, cache: Cache) {\n this.config = config;\n this.cache = cache;\n }\n\n /**\n * Get environment information if context is enabled\n */\n async getEnvironmentInfo(): Promise<EnvironmentInfo | null> {\n if (!this.config.envContext) {\n return null;\n }\n\n const envInfo: EnvironmentInfo = {};\n\n // Get version information in parallel for better performance\n const [nodeVersion, pythonVersion, dockerVersion] = await Promise.allSettled([\n this.getNodeVersion(),\n this.getPythonVersion(),\n this.getDockerVersion(),\n ]);\n\n if (nodeVersion.status === 'fulfilled' && nodeVersion.value) {\n envInfo.node = nodeVersion.value;\n }\n\n if (pythonVersion.status === 'fulfilled' && pythonVersion.value) {\n envInfo.python = pythonVersion.value;\n }\n\n if (dockerVersion.status === 'fulfilled' && dockerVersion.value) {\n envInfo.docker = dockerVersion.value;\n }\n\n // Return null if no environment versions were found\n if (Object.keys(envInfo).length === 0) {\n return null;\n }\n\n return envInfo;\n }\n\n /**\n * Get Node.js version with caching\n */\n private async getNodeVersion(): Promise<string | null> {\n const cacheKey = CacheKeys.NODE_VERSION;\n // Environment versions change rarely, cache for 8 hours (96x default TTL)\n // Covers a full workday - users rarely update Node/Python/Docker multiple times per day\n const envCacheTTL = this.config.cacheTTL * 96;\n\n // Try cache first\n const cached = await this.cache.get<string>(cacheKey, envCacheTTL);\n if (cached) {\n return cached;\n }\n\n try {\n // Method 1: node --version\n let version = await cachedCommand(\n this.cache,\n cacheKey,\n 'node',\n ['--version'],\n envCacheTTL\n );\n\n if (version) {\n // Remove 'v' prefix and clean up\n return version.replace(/^v/, '').trim();\n }\n\n return null;\n\n } catch (error) {\n console.debug('[DEBUG] Failed to get Node.js version:', error instanceof Error ? error.message : String(error));\n return null;\n }\n }\n\n /**\n * Get Python version with caching (tries python3 first, then python)\n */\n private async getPythonVersion(): Promise<string | null> {\n const python3Key = CacheKeys.PYTHON3_VERSION;\n const pythonKey = CacheKeys.PYTHON_VERSION;\n // Environment versions change rarely, cache for 8 hours (96x default TTL)\n // Covers a full workday - users rarely update Node/Python/Docker multiple times per day\n const envCacheTTL = this.config.cacheTTL * 96;\n\n // Try python3 first\n try {\n let version = await cachedCommand(\n this.cache,\n python3Key,\n 'python3',\n ['--version'],\n envCacheTTL\n );\n\n if (version) {\n // Extract version number from \"Python 3.x.y\" format\n const versionMatch = version.match(/(\\d+\\.\\d+\\.\\d+)/);\n if (versionMatch) {\n return versionMatch[1] || null;\n }\n }\n } catch {\n // python3 not available, try python\n }\n\n // Fallback to python\n try {\n let version = await cachedCommand(\n this.cache,\n pythonKey,\n 'python',\n ['--version'],\n envCacheTTL\n );\n\n if (version) {\n // Extract version number from \"Python 3.x.y\" or \"Python 2.x.y\" format\n const versionMatch = version.match(/(\\d+\\.\\d+\\.\\d+)/);\n if (versionMatch) {\n return versionMatch[1] || null;\n }\n }\n } catch {\n // python not available either\n }\n\n return null;\n }\n\n /**\n * Get Docker version with caching\n */\n private async getDockerVersion(): Promise<string | null> {\n const cacheKey = 'docker_version';\n\n try {\n let version = await cachedCommand(\n this.cache,\n cacheKey,\n 'docker',\n ['--version'],\n this.config.cacheTTL * 96 // Longer TTL for Docker (8 hours vs 5 minutes)\n );\n\n if (version) {\n // Extract version number from \"Docker version 20.x.y\" format\n const versionMatch = version.match(/Docker version (\\d+\\.\\d+\\.\\d+)/);\n if (versionMatch) {\n return versionMatch[1] || null;\n }\n }\n\n return null;\n\n } catch (error) {\n console.debug('[DEBUG] Failed to get Docker version:', error instanceof Error ? error.message : String(error));\n return null;\n }\n }\n\n /**\n * Format environment information for display\n */\n formatEnvironmentInfo(envInfo: EnvironmentInfo, symbols: ReturnType<typeof getEnvironmentSymbols>): string {\n const parts: string[] = [];\n\n if (envInfo.node) {\n parts.push(`${symbols.node}${envInfo.node}`);\n }\n\n if (envInfo.python) {\n parts.push(`${symbols.python}${envInfo.python}`);\n }\n\n if (envInfo.docker) {\n parts.push(`${symbols.docker}${envInfo.docker}`);\n }\n\n return parts.join(' ');\n }\n\n /**\n * Get additional tool versions (for future expansion)\n */\n async getAdditionalTools(): Promise<{ [key: string]: string }> {\n const additionalTools: { [key: string]: string } = {};\n\n // Future tools could include:\n // - Go version\n // - Rust version\n // - Java version\n // - Ruby version\n // - Kubernetes version\n // - Helm version\n // - etc.\n\n return additionalTools;\n }\n\n /**\n * Check if a specific tool is available in the environment\n */\n async isToolAvailable(tool: string): Promise<boolean> {\n try {\n const { exec } = await import('child_process');\n const { promisify } = await import('util');\n const execAsync = promisify(exec);\n\n await execAsync(`command -v ${tool}`, { timeout: 2000 });\n return true;\n } catch {\n return false;\n }\n }\n\n /**\n * Get shell environment information\n */\n getShellEnvironment(): { shell: string; shellVersion?: string } {\n const shell = process.env.SHELL || 'unknown';\n\n // Try to extract shell version (basic implementation)\n let shellVersion: string | undefined;\n\n if (shell.includes('bash')) {\n shellVersion = process.env.BASH_VERSION;\n } else if (shell.includes('zsh')) {\n shellVersion = process.env.ZSH_VERSION;\n } else if (shell.includes('fish')) {\n shellVersion = process.env.FISH_VERSION;\n }\n\n return shellVersion ? { shell, shellVersion } : { shell };\n }\n\n /**\n * Get operating system information\n */\n getOSInfo(): { platform: string; arch: string; release?: string } {\n const platform = process.platform;\n const arch = process.arch;\n const release = process.env.OSTYPE || process.env.OS;\n\n return release ? { platform, arch, release } : { platform, arch };\n }\n}\n\n/**\n * Environment information formatter for different display modes\n */\nexport class EnvironmentFormatter {\n private symbols: ReturnType<typeof getEnvironmentSymbols>;\n\n constructor(symbols: ReturnType<typeof getEnvironmentSymbols>) {\n this.symbols = symbols;\n }\n\n /**\n * Format environment info in different styles\n */\n format(envInfo: EnvironmentInfo, style: 'compact' | 'verbose' | 'minimal' = 'compact'): string {\n switch (style) {\n case 'compact':\n return this.formatCompact(envInfo);\n case 'verbose':\n return this.formatVerbose(envInfo);\n case 'minimal':\n return this.formatMinimal(envInfo);\n default:\n return this.formatCompact(envInfo);\n }\n }\n\n /**\n * Compact format: Node22.17 Py3.13 Docker28.3\n */\n private formatCompact(envInfo: EnvironmentInfo): string {\n const parts: string[] = [];\n\n if (envInfo.node) {\n parts.push(`Node${envInfo.node}`);\n }\n\n if (envInfo.python) {\n parts.push(`Py${envInfo.python}`);\n }\n\n if (envInfo.docker) {\n parts.push(`Docker${envInfo.docker}`);\n }\n\n return parts.join(' ');\n }\n\n /**\n * Verbose format: Node.js v22.17.1 • Python 3.13.5 • Docker 28.3.3\n */\n private formatVerbose(envInfo: EnvironmentInfo): string {\n const parts: string[] = [];\n\n if (envInfo.node) {\n parts.push(`Node.js v${envInfo.node}`);\n }\n\n if (envInfo.python) {\n parts.push(`Python ${envInfo.python}`);\n }\n\n if (envInfo.docker) {\n parts.push(`Docker ${envInfo.docker}`);\n }\n\n return parts.join(' • ');\n }\n\n /**\n * Minimal format: N22 P17 D28 (just major versions)\n */\n private formatMinimal(envInfo: EnvironmentInfo): string {\n const parts: string[] = [];\n\n if (envInfo.node) {\n const majorVersion = envInfo.node.split('.')[0];\n parts.push(`N${majorVersion}`);\n }\n\n if (envInfo.python) {\n const majorVersion = envInfo.python.split('.')[0];\n parts.push(`P${majorVersion}`);\n }\n\n if (envInfo.docker) {\n const majorVersion = envInfo.docker.split('.')[0];\n parts.push(`D${majorVersion}`);\n }\n\n return parts.join(' ');\n }\n\n /**\n * Format with icons: 22.17 3.13 28.3\n */\n formatWithIcons(envInfo: EnvironmentInfo): string {\n const parts: string[] = [];\n\n if (envInfo.node) {\n parts.push(`${this.symbols.node}${envInfo.node}`);\n }\n\n if (envInfo.python) {\n parts.push(`${this.symbols.python}${envInfo.python}`);\n }\n\n if (envInfo.docker) {\n parts.push(`${this.symbols.docker}${envInfo.docker}`);\n }\n\n return parts.join(' ');\n }\n}"]}
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/env/context.ts"],"names":[],"mappings":"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","sourcesContent":["import { Config } from '../core/config.js';\nimport { Cache, cachedCommand, CacheKeys } from '../core/cache.js';\nimport { getEnvironmentSymbols } from '../ui/symbols.js';\n\n/**\n * Environment version information interface\n */\nexport interface EnvironmentInfo {\n node?: string;\n python?: string;\n docker?: string;\n}\n\n/**\n * Environment context detection\n * Ported from bash implementation with enhanced TypeScript safety\n */\nexport class EnvironmentDetector {\n private config: Config;\n private cache: Cache;\n\n constructor(config: Config, cache: Cache) {\n this.config = config;\n this.cache = cache;\n }\n\n /**\n * Get environment information if context is enabled\n */\n async getEnvironmentInfo(): Promise<EnvironmentInfo | null> {\n if (!this.config.envContext) {\n return null;\n }\n\n const envInfo: EnvironmentInfo = {};\n\n // Get version information in parallel for better performance\n const [nodeVersion, pythonVersion, dockerVersion] = await Promise.allSettled([\n this.getNodeVersion(),\n this.getPythonVersion(),\n this.getDockerVersion(),\n ]);\n\n if (nodeVersion.status === 'fulfilled' && nodeVersion.value) {\n envInfo.node = nodeVersion.value;\n }\n\n if (pythonVersion.status === 'fulfilled' && pythonVersion.value) {\n envInfo.python = pythonVersion.value;\n }\n\n if (dockerVersion.status === 'fulfilled' && dockerVersion.value) {\n envInfo.docker = dockerVersion.value;\n }\n\n // Return null if no environment versions were found\n if (Object.keys(envInfo).length === 0) {\n return null;\n }\n\n return envInfo;\n }\n\n /**\n * Get Node.js version with caching\n */\n private async getNodeVersion(): Promise<string | null> {\n const cacheKey = CacheKeys.NODE_VERSION;\n // Environment versions change rarely, cache for 8 hours (96x default TTL)\n // Covers a full workday - users rarely update Node/Python/Docker multiple times per day\n const envCacheTTL = this.config.cacheTTL * 96;\n\n // Try cache first\n const cached = await this.cache.get<string>(cacheKey, envCacheTTL);\n if (cached) {\n return cached;\n }\n\n try {\n // Method 1: node --version\n let version = await cachedCommand(\n this.cache,\n cacheKey,\n 'node',\n ['--version'],\n envCacheTTL\n );\n\n if (version) {\n // Remove 'v' prefix and clean up\n return version.replace(/^v/, '').trim();\n }\n\n return null;\n\n } catch (error) {\n console.debug('[DEBUG] Failed to get Node.js version:', error instanceof Error ? error.message : String(error));\n return null;\n }\n }\n\n /**\n * Get Python version with caching (tries python3 first, then python)\n */\n private async getPythonVersion(): Promise<string | null> {\n const python3Key = CacheKeys.PYTHON3_VERSION;\n const pythonKey = CacheKeys.PYTHON_VERSION;\n // Environment versions change rarely, cache for 8 hours (96x default TTL)\n // Covers a full workday - users rarely update Node/Python/Docker multiple times per day\n const envCacheTTL = this.config.cacheTTL * 96;\n\n // Try python3 first\n try {\n let version = await cachedCommand(\n this.cache,\n python3Key,\n 'python3',\n ['--version'],\n envCacheTTL\n );\n\n if (version) {\n // Extract version number from \"Python 3.x.y\" format\n const versionMatch = version.match(/(\\d+\\.\\d+\\.\\d+)/);\n if (versionMatch) {\n return versionMatch[1] || null;\n }\n }\n } catch {\n // python3 not available, try python\n }\n\n // Fallback to python\n try {\n let version = await cachedCommand(\n this.cache,\n pythonKey,\n 'python',\n ['--version'],\n envCacheTTL\n );\n\n if (version) {\n // Extract version number from \"Python 3.x.y\" or \"Python 2.x.y\" format\n const versionMatch = version.match(/(\\d+\\.\\d+\\.\\d+)/);\n if (versionMatch) {\n return versionMatch[1] || null;\n }\n }\n } catch {\n // python not available either\n }\n\n return null;\n }\n\n /**\n * Get Docker version with caching\n */\n private async getDockerVersion(): Promise<string | null> {\n const cacheKey = 'docker_version';\n\n try {\n let version = await cachedCommand(\n this.cache,\n cacheKey,\n 'docker',\n ['--version'],\n this.config.cacheTTL * 96 // Longer TTL for Docker (8 hours vs 5 minutes)\n );\n\n if (version) {\n // Extract version number from \"Docker version 20.x.y\" format\n const versionMatch = version.match(/Docker version (\\d+\\.\\d+\\.\\d+)/);\n if (versionMatch) {\n return versionMatch[1] || null;\n }\n }\n\n return null;\n\n } catch (error) {\n console.debug('[DEBUG] Failed to get Docker version:', error instanceof Error ? error.message : String(error));\n return null;\n }\n }\n\n /**\n * Format environment information for display\n */\n formatEnvironmentInfo(envInfo: EnvironmentInfo, symbols: ReturnType<typeof getEnvironmentSymbols>): string {\n const parts: string[] = [];\n\n if (envInfo.node) {\n parts.push(`${symbols.node}${envInfo.node}`);\n }\n\n if (envInfo.python) {\n parts.push(`${symbols.python}${envInfo.python}`);\n }\n\n if (envInfo.docker) {\n parts.push(`${symbols.docker}${envInfo.docker}`);\n }\n\n return parts.join(' ');\n }\n\n /**\n * Get additional tool versions (for future expansion)\n */\n async getAdditionalTools(): Promise<{ [key: string]: string }> {\n const additionalTools: { [key: string]: string } = {};\n\n // Future tools could include:\n // - Go version\n // - Rust version\n // - Java version\n // - Ruby version\n // - Kubernetes version\n // - Helm version\n // - etc.\n\n return additionalTools;\n }\n\n /**\n * Check if a specific tool is available in the environment\n */\n async isToolAvailable(tool: string): Promise<boolean> {\n try {\n const { execFile } = await import('child_process');\n const { promisify } = await import('util');\n const execFileAsync = promisify(execFile);\n\n await execFileAsync('which', [tool], { timeout: 2000 });\n return true;\n } catch {\n return false;\n }\n }\n\n /**\n * Get shell environment information\n */\n getShellEnvironment(): { shell: string; shellVersion?: string } {\n const shell = process.env.SHELL || 'unknown';\n\n // Try to extract shell version (basic implementation)\n let shellVersion: string | undefined;\n\n if (shell.includes('bash')) {\n shellVersion = process.env.BASH_VERSION;\n } else if (shell.includes('zsh')) {\n shellVersion = process.env.ZSH_VERSION;\n } else if (shell.includes('fish')) {\n shellVersion = process.env.FISH_VERSION;\n }\n\n return shellVersion ? { shell, shellVersion } : { shell };\n }\n\n /**\n * Get operating system information\n */\n getOSInfo(): { platform: string; arch: string; release?: string } {\n const platform = process.platform;\n const arch = process.arch;\n const release = process.env.OSTYPE || process.env.OS;\n\n return release ? { platform, arch, release } : { platform, arch };\n }\n}\n\n/**\n * Environment information formatter for different display modes\n */\nexport class EnvironmentFormatter {\n private symbols: ReturnType<typeof getEnvironmentSymbols>;\n\n constructor(symbols: ReturnType<typeof getEnvironmentSymbols>) {\n this.symbols = symbols;\n }\n\n /**\n * Format environment info in different styles\n */\n format(envInfo: EnvironmentInfo, style: 'compact' | 'verbose' | 'minimal' = 'compact'): string {\n switch (style) {\n case 'compact':\n return this.formatCompact(envInfo);\n case 'verbose':\n return this.formatVerbose(envInfo);\n case 'minimal':\n return this.formatMinimal(envInfo);\n default:\n return this.formatCompact(envInfo);\n }\n }\n\n /**\n * Compact format: Node22.17 Py3.13 Docker28.3\n */\n private formatCompact(envInfo: EnvironmentInfo): string {\n const parts: string[] = [];\n\n if (envInfo.node) {\n parts.push(`Node${envInfo.node}`);\n }\n\n if (envInfo.python) {\n parts.push(`Py${envInfo.python}`);\n }\n\n if (envInfo.docker) {\n parts.push(`Docker${envInfo.docker}`);\n }\n\n return parts.join(' ');\n }\n\n /**\n * Verbose format: Node.js v22.17.1 • Python 3.13.5 • Docker 28.3.3\n */\n private formatVerbose(envInfo: EnvironmentInfo): string {\n const parts: string[] = [];\n\n if (envInfo.node) {\n parts.push(`Node.js v${envInfo.node}`);\n }\n\n if (envInfo.python) {\n parts.push(`Python ${envInfo.python}`);\n }\n\n if (envInfo.docker) {\n parts.push(`Docker ${envInfo.docker}`);\n }\n\n return parts.join(' • ');\n }\n\n /**\n * Minimal format: N22 P17 D28 (just major versions)\n */\n private formatMinimal(envInfo: EnvironmentInfo): string {\n const parts: string[] = [];\n\n if (envInfo.node) {\n const majorVersion = envInfo.node.split('.')[0];\n parts.push(`N${majorVersion}`);\n }\n\n if (envInfo.python) {\n const majorVersion = envInfo.python.split('.')[0];\n parts.push(`P${majorVersion}`);\n }\n\n if (envInfo.docker) {\n const majorVersion = envInfo.docker.split('.')[0];\n parts.push(`D${majorVersion}`);\n }\n\n return parts.join(' ');\n }\n\n /**\n * Format with icons: 22.17 3.13 28.3\n */\n formatWithIcons(envInfo: EnvironmentInfo): string {\n const parts: string[] = [];\n\n if (envInfo.node) {\n parts.push(`${this.symbols.node}${envInfo.node}`);\n }\n\n if (envInfo.python) {\n parts.push(`${this.symbols.python}${envInfo.python}`);\n }\n\n if (envInfo.docker) {\n parts.push(`${this.symbols.docker}${envInfo.docker}`);\n }\n\n return parts.join(' ');\n }\n}"]}
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
- import{readFileSync as St}from"fs";import{z as u}from"zod";import{readFileSync as q,existsSync as Q}from"fs";import{homedir as tt}from"os";import{join as P,dirname as et}from"path";import{parse as rt}from"yaml";var I=u.object({cacheTTL:u.number().default(300),cacheDir:u.string().default("/tmp/.claude-statusline-cache"),maxLength:u.number().default(1e3),noEmoji:u.boolean().default(!1),noGitStatus:u.boolean().default(!1),noContextWindow:u.boolean().default(!1),envContext:u.boolean().default(!1),truncate:u.boolean().default(!1),softWrap:u.boolean().default(!1),noSoftWrap:u.boolean().default(!1),forceWidth:u.number().optional(),debugWidth:u.boolean().default(!1),rightMargin:u.number().default(15),symbols:u.object({git:u.string().default("\uF418"),model:u.string().default("\u{F06A9}"),contextWindow:u.string().default("\u26A1\uFE0E"),staged:u.string().default("+"),conflict:u.string().default("\xD7"),stashed:u.string().default("\u2691"),ahead:u.string().default("\u21E1"),behind:u.string().default("\u21E3"),diverged:u.string().default("\u21D5"),renamed:u.string().default("\xBB"),deleted:u.string().default("\u2718")}).default({}),asciiSymbols:u.object({git:u.string().default("@"),model:u.string().default("*"),contextWindow:u.string().default("#"),staged:u.string().default("+"),conflict:u.string().default("C"),stashed:u.string().default("$"),ahead:u.string().default("A"),behind:u.string().default("B"),diverged:u.string().default("D"),renamed:u.string().default(">"),deleted:u.string().default("X")}).default({})}),nt=I.parse({}),ot=["claude-statusline.json","claude-statusline.yaml"];function W(n=process.cwd()){let t={...nt};return t={...t,...st(n)},t={...t,...it()},I.parse(t)}function st(n){let t=[n,et(n),P(tt(),".claude")];for(let r of t)for(let e of ot){let o=P(r,e);if(Q(o))try{let s=q(o,"utf-8");if(e.endsWith(".json"))return JSON.parse(s);if(e.endsWith(".yaml"))return rt(s)}catch{}}return{}}function it(){let n={};if(process.env.CLAUDE_CODE_STATUSLINE_NO_EMOJI==="1"&&(n.noEmoji=!0),process.env.CLAUDE_CODE_STATUSLINE_NO_GITSTATUS==="1"&&(n.noGitStatus=!0),process.env.CLAUDE_CODE_STATUSLINE_NO_CONTEXT_WINDOW==="1"&&(n.noContextWindow=!0),process.env.CLAUDE_CODE_STATUSLINE_ENV_CONTEXT==="1"&&(n.envContext=!0),process.env.CLAUDE_CODE_STATUSLINE_TRUNCATE==="1"&&(n.truncate=!0),process.env.CLAUDE_CODE_STATUSLINE_SOFT_WRAP==="1"&&(n.softWrap=!0),process.env.CLAUDE_CODE_STATUSLINE_NO_SOFT_WRAP==="1"&&(n.noSoftWrap=!0),process.env.CLAUDE_CODE_STATUSLINE_FORCE_WIDTH){let t=parseInt(process.env.CLAUDE_CODE_STATUSLINE_FORCE_WIDTH,10);!isNaN(t)&&t>0&&(n.forceWidth=t)}return process.env.CLAUDE_CODE_STATUSLINE_DEBUG_WIDTH==="1"&&(n.debugWidth=!0),process.env.CLAUDE_CODE_STATUSLINE_CACHE_DIR&&(n.cacheDir=process.env.CLAUDE_CODE_STATUSLINE_CACHE_DIR),n}var at=4096,ct=[/\.\./,/\.\.\\/,/\[/,/;/,/&/,/</,/>/,/`/];function O(n,t){if(!n||typeof n!="string")return!1;let r=n.trimEnd();if(r.length===0||r.length>t.maxLength||!r.includes("{")||!r.includes("}"))return!1;let e=(r.match(/"/g)||[]).length;if(e===0||e%2!==0)return!1;try{JSON.parse(r)}catch{return!1}return!0}function lt(n){if(!n||typeof n!="string"||n.length>at)return!1;for(let t of ct)if(t.test(n))return!1;if(n.includes("${")||n.includes("`")||n.includes("$("))return!1;try{let t=n.replace(/\/+/g,"/").replace(/\\+/g,"\\");if(t.includes("../")||t.includes("..\\")||t.startsWith("/")&&!t.startsWith("/home/")&&!t.startsWith("/Users/")&&!t.startsWith("/tmp/")&&!["/home","/Users","/tmp","/var","/opt"].some(o=>t.startsWith(o)))return!1;if(t.includes(":")&&/^[A-Za-z]:/.test(t)){let r=t.charAt(0).toUpperCase();if(r<"C"||r>"Z")return!1}}catch{return!1}return!0}async function R(n){if(!lt(n))return!1;try{let{access:t}=await import("fs/promises"),{constants:r}=await import("fs");return await t(n,r.R_OK),!0}catch{return!1}}import{readFile as $,writeFile as A,mkdir as ut}from"fs/promises";import{existsSync as k}from"fs";import{join as w}from"path";var T=class{config;constructor(t){this.config=t}async ensureCacheDir(){try{await ut(this.config.cacheDir,{recursive:!0})}catch{}}getCachePath(t){return w(this.config.cacheDir,t)}getTimestampPath(t){return w(this.config.cacheDir,`${t}.time`)}async get(t,r=this.config.cacheTTL){let e=this.getCachePath(t),o=this.getTimestampPath(t);try{if(!k(e)||!k(o))return null;let s=await $(o,"utf-8"),c=parseInt(s.trim(),10);if(isNaN(c)||Math.floor(Date.now()/1e3)-c>=r)return null;let d=await $(e,"utf-8");try{return JSON.parse(d)}catch{return d}}catch{return null}}async set(t,r){let e=this.getCachePath(t),o=this.getTimestampPath(t);try{await this.ensureCacheDir();let s;typeof r=="string"?s=r:s=JSON.stringify(r);let c=Math.floor(Date.now()/1e3);return await Promise.all([A(e,s,"utf-8"),A(o,c.toString(),"utf-8")]),!0}catch{return!1}}async has(t,r=this.config.cacheTTL){return await this.get(t,r)!==null}async delete(t){let r=this.getCachePath(t),e=this.getTimestampPath(t);try{let{unlink:o}=await import("fs/promises");return await Promise.allSettled([o(r),o(e)]),!0}catch{return!1}}async clear(){try{let{readdir:t,unlink:r}=await import("fs/promises"),e=await t(this.config.cacheDir);return await Promise.allSettled(e.map(o=>r(w(this.config.cacheDir,o)))),!0}catch{return!1}}async getStats(){try{let{readdir:t,stat:r}=await import("fs/promises"),e=await t(this.config.cacheDir),o=0,s=0;for(let c of e)if(!c.endsWith(".time")){s++;let i=w(this.config.cacheDir,c);try{let a=await r(i);o+=a.size}catch{}}return{total:s,size:o}}catch{return{total:0,size:0}}}},S={NODE_VERSION:"node_version",PYTHON_VERSION:"python_version",PYTHON3_VERSION:"python3_version",DOCKER_VERSION:"docker_version",GIT_REMOTE_URL:n=>`git_remote_${Buffer.from(n).toString("base64")}`,GIT_BRANCH:n=>`git_branch_${Buffer.from(n).toString("base64")}`};async function D(n,t,r,e=[],o=300){let s=await n.get(t,o);if(s!==null)return s;try{let{exec:c}=await import("child_process"),{promisify:i}=await import("util"),a=i(c),{stdout:l}=await a(`${r} ${e.join(" ")}`,{timeout:5e3,encoding:"utf-8"}),d=l.trim();return d&&await n.set(t,d),d}catch{return null}}import{spawn as dt}from"child_process";async function E(n,t={}){return new Promise((r,e)=>{let o=dt("git",n,{cwd:t.cwd||process.cwd(),stdio:["ignore","pipe","pipe"],timeout:t.timeout||5e3}),s="",c="";o.stdout.on("data",i=>{s+=i.toString()}),o.stderr.on("data",i=>{c+=i.toString()}),o.on("close",i=>{i===0?r(s):e(new Error(`Git command failed with code ${i}: ${c||s}`))}),o.on("error",i=>{e(new Error(`Failed to execute git command: ${i.message}`))})})}async function M(n){try{return await E(["rev-parse","--git-dir"],n?{cwd:n}:{}),!0}catch{return!1}}async function U(n){let t=n?{cwd:n}:{};try{let e=(await E(["branch","--show-current"],t)).trim();if(e)return e}catch{}try{let e=(await E(["rev-parse","--abbrev-ref","HEAD"],t)).trim();if(e&&e!=="HEAD")return e}catch{}try{let e=(await E(["branch","--no-color"],t)).split(`
3
- `);for(let o of e)if(o.startsWith("* "))return o.substring(2).trim()}catch{}return null}async function G(n){return E(["status","--porcelain"],n?{cwd:n}:{})}async function L(n){try{return E(["stash","list"],n?{cwd:n}:{})}catch{return""}}async function B(n){try{return(await E(["rev-parse","--abbrev-ref","@{u}"],n?{cwd:n}:{})).trim()}catch{return""}}async function H(n){try{let e=(await E(["rev-list","--count","--left-right","@{u}...HEAD"],n?{cwd:n}:{})).trim().split(" ");if(e.length===2){let o=parseInt(e[0]||"0",10);return{ahead:parseInt(e[1]||"0",10),behind:o}}}catch{}return{ahead:0,behind:0}}var ft={stashed:0,staged:0,modified:0,untracked:0,renamed:0,deleted:0,conflicts:0,ahead:0,behind:0,diverged:!1},v=class{config;cache;constructor(t,r){this.config=t,this.cache=r}async getGitInfo(t){if(this.config.noGitStatus)return null;try{if(!await M(t))return null;let e=await this.getCurrentBranch(t);if(!e)return null;let o=await this.getGitIndicators(t);return{branch:e,indicators:o}}catch{return null}}async getCurrentBranch(t){let r=`${S.GIT_BRANCH(t)}_current`,e=await this.cache.get(r,60);if(e)return e;try{let o=await U(t);return o?(await this.cache.set(r,o),o):null}catch{return null}}async getGitIndicators(t){let r={...ft};try{let o=(await G(t)).split(`
4
- `).map(i=>i.trimEnd()).filter(i=>i.length>0);for(let i of o){if(i.length<2)continue;let a=i.charAt(0),l=i.charAt(1);if(a==="U"||l==="U"||a==="A"&&l==="A"||a==="D"&&l==="D")r.conflicts++;else if(a==="?"&&l==="?")r.untracked++;else{switch(a){case"M":r.staged++;break;case"A":r.staged++;break;case"D":r.deleted++;break;case"R":r.renamed++;break;case"C":r.staged++;break}switch(l){case"M":r.modified++;break;case"D":r.deleted++;break;case"R":r.renamed++;break}}}r.stashed=await this.getStashedCount(t);let{ahead:s,behind:c}=await this.getAheadBehind(t);r.ahead=s,r.behind=c,r.diverged=s>0&&c>0}catch{}return r}async getStashedCount(t){try{return(await L(t)).trim().split(`
5
- `).filter(e=>e.trim().length>0).length}catch{return 0}}async getAheadBehind(t){try{return await B(t)?await H(t):{ahead:0,behind:0}}catch{}return{ahead:0,behind:0}}formatIndicators(t,r){let e=[],o=["stashed","renamed","modified","staged","untracked","deleted","conflicts"];return t.stashed>0&&e.push(r.stashed),t.renamed>0&&e.push(r.renamed),t.modified>0&&e.push("!"),t.staged>0&&e.push(r.staged),t.untracked>0&&e.push("?"),t.deleted>0&&e.push(r.deleted),t.conflicts>0&&e.push(r.conflict),t.diverged?e.push(r.diverged):(t.ahead>0&&e.push(r.ahead),t.behind>0&&e.push(r.behind)),e.join("")}formatGitStatus(t,r){let e=this.formatIndicators(t.indicators,r),o=(this.config.noEmoji,r.git);return e?` ${o} ${t.branch} [${e}]`:` ${o} ${t.branch}`}};var j=new Map,mt=1,V={git:"@",model:"*",contextWindow:"#",staged:"+",conflict:"C",stashed:"$",ahead:"A",behind:"B",diverged:"D",renamed:">",deleted:"X"},ht={git:"\uF418",model:"\u{F06A9}",contextWindow:"\u26A1\uFE0E",staged:"+",conflict:"\xD7",stashed:"\u2691",ahead:"\u21E1",behind:"\u21E3",diverged:"\u21D5",renamed:"\xBB",deleted:"\u2718"};async function z(n){let t=process.env.NERD_FONT+"|"+process.env.TERM_PROGRAM+"|"+process.env.TERM,r=`${mt}:${n.noEmoji?"ascii":"nerd"}:${t}`,e=j.get(r);if(e&&Date.now()-e.timestamp<6e4)return e.symbols;let o;return n.noEmoji?o={...V,...n.symbols,...n.asciiSymbols}:(await gt()).hasNerdFont?o={...ht,...n.symbols}:o={...V,...n.asciiSymbols},j.set(r,{symbols:o,timestamp:Date.now()}),o}async function gt(){let n={hasNerdFont:!1,terminal:"",font:"",method:""};if(process.env.NERD_FONT==="1")return n.hasNerdFont=!0,n.method="NERD_FONT env var",n;let t=process.env.TERM_PROGRAM,r=process.env.TERM;if(t&&(n.terminal=t,n.method="TERM_PROGRAM detection",["vscode","ghostty","wezterm","iterm"].includes(t))||r&&(n.terminal=r,n.method="TERM detection",["alacritty","kitty","wezterm","ghostty","xterm-256color"].includes(r)))return n.hasNerdFont=!0,n;let e=await pt();if(e.hasNerdFont)return n.hasNerdFont=!0,n.font=e.font,n.method="font list detection",n;let o=await yt();return o.hasNerdFont?(n.hasNerdFont=!0,n.font=o.font,n.method="installation detection",n):bt().hasNerdFont?(n.hasNerdFont=!0,n.method="environment detection",n):((await Et()).hasNerdFont&&(n.hasNerdFont=!0,n.method="platform-specific detection"),n)}async function pt(){try{let{exec:n}=await import("child_process"),{promisify:t}=await import("util"),r=t(n),e="",o=process.platform;if(o==="linux"?e="fc-list":o==="darwin"&&(e="system_profiler SPFontsDataType 2>/dev/null || system_profiler SPFontsDataType"),e){let{stdout:s}=await r(e,{timeout:3e3,encoding:"utf-8"}),c=[/nerd font/i,/symbols only/i,/jetbrains mono.*nerd/i,/fira code.*nerd/i,/hack.*nerd/i,/source code pro.*nerd/i,/ubuntu mono.*nerd/i,/anonymous pro.*nerd/i];for(let i of c)if(i.test(s)){let a=s.match(/([^:\n]*)(?=\s*(nerd|symbols))/i);return{hasNerdFont:!0,font:(a?a[1]:"Nerd Font")?.trim()||"Nerd Font"}}}}catch{}return{hasNerdFont:!1,font:""}}async function yt(){try{let{access:n,readdir:t}=await import("fs/promises"),{homedir:r}=await import("os"),e=process.platform,o=[];e==="darwin"?o.push(`${r()}/Library/Fonts`,"/System/Library/Fonts","/Library/Fonts"):e==="linux"&&o.push(`${r()}/.local/share/fonts`,`${r()}/.fonts`,"/usr/share/fonts","/usr/local/share/fonts");let s=["jetbrains-mono-nerd-font","fira-code-nerd-font","hack-nerd-font","source-code-pro-nerd-font","ubuntu-mono-nerd-font","anonymous-pro-nerd-font"];for(let c of o)try{await n(c);let i=await t(c);for(let a of i){let l=a.toLowerCase();for(let d of s)if(l.includes(d))return{hasNerdFont:!0,font:a}}}catch{}}catch{}return{hasNerdFont:!1,font:""}}function bt(){let n=["POWERLINE_COMMAND","NERDFONTS","FONT_FAMILY"];for(let t of n){let r=process.env[t];if(r&&r.toLowerCase().includes("nerd"))return{hasNerdFont:!0}}return process.env.VSCODE_PID||process.env.TERM_PROGRAM==="vscode"||process.env.TERM_PROGRAM==="ghostty"||process.env.TERM_PROGRAM==="wezterm"?{hasNerdFont:!0}:{hasNerdFont:!1}}async function Et(){if(process.platform==="darwin")try{let{exec:t}=await import("child_process"),{promisify:r}=await import("util"),e=r(t),{stdout:o}=await e("brew list | grep -i font",{timeout:2e3,encoding:"utf-8"});if(o.includes("nerd"))return{hasNerdFont:!0}}catch{}return{hasNerdFont:!1}}function K(n){return{node:"\uE718",python:"\uE235",docker:"\uF308",git:n.git,model:n.model}}async function x(n){if(n.forceWidth&&n.forceWidth>0)return n.forceWidth;let t=process.env.COLUMNS;if(t){let i=parseInt(t,10);if(!isNaN(i)&&i>0)return i}if(process.stdout.columns&&process.stdout.columns>0)return process.stdout.columns;let r=await Y("tput",["cols"]);if(r)return r;let e=await J();if(e)return e;let o=process.env.CLAUDE_CODE_TERMINAL_WIDTH;if(o){let i=parseInt(o,10);if(!isNaN(i)&&i>0)return i}let s=process.env.TERM_PROGRAM,c=process.env.TERM;return s==="vscode"&&process.env.VSCODE_PID||["ghostty","wezterm","iterm"].includes(s||"")||c&&["alacritty","kitty","wezterm","ghostty","xterm-256color"].includes(c)||process.env.WT_SESSION||process.env.WT_PROFILE_ID?120:80}async function Y(n,t){try{let{exec:r}=await import("child_process"),{promisify:e}=await import("util"),o=e(r),{stdout:s}=await o(`${n} ${t.join(" ")}`,{timeout:1e3,encoding:"utf-8"}),c=parseInt(s.trim(),10);if(!isNaN(c)&&c>0)return c}catch{}return null}async function J(){try{let{exec:n}=await import("child_process"),{promisify:t}=await import("util"),r=t(n),{stdout:e}=await r("stty size",{timeout:1e3,encoding:"utf-8"}),o=e.trim().split(" ");if(o.length===2){let s=parseInt(o[1]||"0",10);if(!isNaN(s)&&s>0)return s}}catch{}return null}async function X(n){if(!n.debugWidth)return;process.stdout.columns;let t=process.env.COLUMNS,r=await Y("tput",["cols"]),e=await J(),o=await x(n)}function F(n,t){return n.length<=t?n:t<4?"..":`${n.substring(0,t-2)}..`}function Z(n,t,r,e){if(n.length+t.length<=r)return"";let o=r-t.length-2;if(o>=5)return`${n.substring(0,o)}..${t}`;let s="",c=t.match(/\[([^\]]+)\]/);c&&(s=c[1]||"");let i=r-s.length-8;if(i>=8){let a=t.substring(0,Math.min(t.length,i));return`${n.substring(0,4)}..${a}..${s?` [${s}]`:""}`}return`${n.substring(0,r)}..`}function y(n){let t=0;for(let r of n){let e=r.codePointAt(0)??0;e>=4352&&(e>=4352&&e<=4447||e>=11904&&e<=42191||e>=44032&&e<=55203||e>=63744&&e<=64255||e>=65040&&e<=65049||e>=65072&&e<=65135||e>=65280&&e<=65376||e>=65504&&e<=65510||e>=131072&&e<=196605||e>=196608&&e<=262141)||e>=127744&&e<=129535||e>=9728&&e<=10175||e>=65024&&e<=65039||e>=126976&&e<=127023||e>=57344&&e<=63743||e>=983040&&e<=1048573||e>=1048576&&e<=1114109?t+=2:e>=768&&e<=879||e>=7616&&e<=7679||e>=8400&&e<=8447||e>=65056&&e<=65071||(t+=1)}return t}var _=class{config;cache;constructor(t,r){this.config=t,this.cache=r}async getEnvironmentInfo(){if(!this.config.envContext)return null;let t={},[r,e,o]=await Promise.allSettled([this.getNodeVersion(),this.getPythonVersion(),this.getDockerVersion()]);return r.status==="fulfilled"&&r.value&&(t.node=r.value),e.status==="fulfilled"&&e.value&&(t.python=e.value),o.status==="fulfilled"&&o.value&&(t.docker=o.value),Object.keys(t).length===0?null:t}async getNodeVersion(){let t=S.NODE_VERSION,r=this.config.cacheTTL*96,e=await this.cache.get(t,r);if(e)return e;try{let o=await D(this.cache,t,"node",["--version"],r);return o?o.replace(/^v/,"").trim():null}catch{return null}}async getPythonVersion(){let t=S.PYTHON3_VERSION,r=S.PYTHON_VERSION,e=this.config.cacheTTL*96;try{let o=await D(this.cache,t,"python3",["--version"],e);if(o){let s=o.match(/(\d+\.\d+\.\d+)/);if(s)return s[1]||null}}catch{}try{let o=await D(this.cache,r,"python",["--version"],e);if(o){let s=o.match(/(\d+\.\d+\.\d+)/);if(s)return s[1]||null}}catch{}return null}async getDockerVersion(){let t="docker_version";try{let r=await D(this.cache,t,"docker",["--version"],this.config.cacheTTL*96);if(r){let e=r.match(/Docker version (\d+\.\d+\.\d+)/);if(e)return e[1]||null}return null}catch{return null}}formatEnvironmentInfo(t,r){let e=[];return t.node&&e.push(`${r.node}${t.node}`),t.python&&e.push(`${r.python}${t.python}`),t.docker&&e.push(`${r.docker}${t.docker}`),e.join(" ")}async getAdditionalTools(){return{}}async isToolAvailable(t){try{let{exec:r}=await import("child_process"),{promisify:e}=await import("util");return await e(r)(`command -v ${t}`,{timeout:2e3}),!0}catch{return!1}}getShellEnvironment(){let t=process.env.SHELL||"unknown",r;return t.includes("bash")?r=process.env.BASH_VERSION:t.includes("zsh")?r=process.env.ZSH_VERSION:t.includes("fish")&&(r=process.env.FISH_VERSION),r?{shell:t,shellVersion:r}:{shell:t}}getOSInfo(){let t=process.platform,r=process.arch,e=process.env.OSTYPE||process.env.OS;return e?{platform:t,arch:r,release:e}:{platform:t,arch:r}}},N=class{symbols;constructor(t){this.symbols=t}format(t,r="compact"){switch(r){case"compact":return this.formatCompact(t);case"verbose":return this.formatVerbose(t);case"minimal":return this.formatMinimal(t);default:return this.formatCompact(t)}}formatCompact(t){let r=[];return t.node&&r.push(`Node${t.node}`),t.python&&r.push(`Py${t.python}`),t.docker&&r.push(`Docker${t.docker}`),r.join(" ")}formatVerbose(t){let r=[];return t.node&&r.push(`Node.js v${t.node}`),t.python&&r.push(`Python ${t.python}`),t.docker&&r.push(`Docker ${t.docker}`),r.join(" \u2022 ")}formatMinimal(t){let r=[];if(t.node){let e=t.node.split(".")[0];r.push(`N${e}`)}if(t.python){let e=t.python.split(".")[0];r.push(`P${e}`)}if(t.docker){let e=t.docker.split(".")[0];r.push(`D${e}`)}return r.join(" ")}formatWithIcons(t){let r=[];return t.node&&r.push(`${this.symbols.node}${t.node}`),t.python&&r.push(`${this.symbols.python}${t.python}`),t.docker&&r.push(`${this.symbols.docker}${t.docker}`),r.join(" ")}};async function Ct(){try{let n=W(),t=new T(n),r=new v(n,t),e=new _(n,t);await X(n);let o=await Dt();O(JSON.stringify(o),n)||process.exit(1);let{fullDir:s,modelName:c,contextWindow:i}=wt(o);(!s||!c)&&process.exit(1),await R(s)||process.exit(1);let l=[r.getGitInfo(s),e.getEnvironmentInfo(),z(n)],d;n.truncate&&l.push(x(n));let m=await Promise.all(l),[h,p,b]=m;n.truncate&&m.length>3&&(d=m[3]);let f=await Tt({fullDir:s,modelName:c,contextWindow:i,gitInfo:h,envInfo:p,symbols:b,...d&&{terminalWidth:d},config:n,gitOps:r});process.stdout.write(f)}catch{process.exit(1)}}async function Dt(){try{let n=St(0,"utf-8");return JSON.parse(n.trim())}catch(n){throw new Error(`Failed to read or parse input: ${n instanceof Error?n.message:String(n)}`)}}function wt(n){let t=n.workspace?.current_dir||"",r=n.model?.display_name||"Unknown",e=n.context_window;return{fullDir:t,modelName:r,contextWindow:e}}async function Tt(n){let{fullDir:t,modelName:r,contextWindow:e,gitInfo:o,envInfo:s,symbols:c,terminalWidth:i,config:a,gitOps:l}=n,d=t.split("/").pop()||t.split("\\").pop()||"project",m="";o&&(m=l.formatGitStatus(o,c));let h="";if(s){let g=K(c);h=` ${new N(g).formatWithIcons(s)}`}let p="";if(e&&!a.noContextWindow){let g=vt(e);g!==null&&(p=` ${c.contextWindow}${g}%`)}let b=`${c.model}${r}${h}${p}`,f=`${d}${m} ${b}`;return a.truncate&&(i||process.exit(1),f=_t({statusline:f,projectName:d,gitStatus:m,modelString:b,terminalWidth:i,config:a,symbols:c})),f}function vt(n){try{let{total_input_tokens:t,context_window_size:r,used_percentage:e,current_usage:o}=n;if(!r||r===0)return null;if(e!=null&&e>0)return Math.max(0,Math.min(100,Math.round(e)));if(t&&t>0){let s=Math.round(t/r*100);return Math.max(0,Math.min(100,s))}if(o){let s=o.input_tokens+(o.cache_creation_input_tokens||0)+(o.cache_read_input_tokens||0);if(s>0){let c=Math.round(s/r*100);return Math.max(0,Math.min(100,c))}}return 0}catch{return 0}}function _t(n){let{statusline:t,projectName:r,gitStatus:e,modelString:o,terminalWidth:s,config:c}=n,i=Math.max(s-c.rightMargin,30),a=`${r}${e}`;if(y(t)<=i)return t;let d=y(a);if(d+1<=i)if(c.noSoftWrap){let h=i-d-1,p=F(o,h);return`${a} ${p}`}else{let h=i-d-1,p=/^[󰚩*]/,b=y(o);if(p.test(o)&&b>h)return`${a}
6
- ${o}`;let f=Nt(o,h);return`${a} ${f}`}let m=Z(r,e,i,c);return m||F(t,i)}function Nt(n,t){if(y(n)<=t)return n;if(/^[󰚩*]/.test(n))return xt(n,t);let o=!1,s=Array.from(n),c=0,i=s.length,a=-1;for(let f=0;f<s.length;f++){let g=s[f];if(!g)break;g===" "&&(a=f);let C=y(g);if(c+=C,c>t){a>=0?i=a:i=f,o=a>=0;break}}if(i>=s.length)return n;let l=s.length>0?s[0]:"",d=l&&l!==" "&&Buffer.byteLength(l,"utf8")>1;if(!o&&t-c>-3&&!d)return n;if(d&&i<=2&&t>=3){let f=0;for(let g=0;g<s.length;g++){let C=s[g];if(!C)break;if(f+=y(C),f>=3||f>=t){i=g,o=!0;break}}}let m=s.slice(0,i),h=s.slice(i);h.length>0&&h[0]===" "&&h.shift();let p=m.join(""),b=h.join("");return b?`${p}
7
- ${b}`:p}function xt(n,t){if(y(n)<=t)return n;let e=Array.from(n),o=[];for(let a=0;a<e.length;a++)e[a]===" "&&o.push(a);let s=-1;for(let a=0;a<e.length;a++){let l=e[a];if(l==="\u26A1"||l==="#"){s=a;break}}if(s>0){let a=e.findIndex((l,d)=>d<s&&l===" ");if(a>0){let l=e.slice(a+1).join("");if(y(l)<=t)return`${e.slice(0,a).join("")}
8
- ${l}`}}if(o.length>0){let a=o[0];if(a!==void 0&&a>1){let l=e.slice(0,a).join("");if(y(l)<=t){let m=e.slice(a+1).join("");return m?`${l}
9
- ${m}`:l}}}let c=0,i=e.length;for(let a=0;a<e.length;a++){let l=e[a];if(!l)break;let d=y(l);if(c+d>t){i=a;break}c+=d}if(i<e.length&&i>0){let a=e.slice(0,i).join(""),l=e.slice(i).join("");return l?`${a}
10
- ${l}`:a}return n}import.meta.url===`file://${process.argv[1]}`&&Ct();export{Ct as main};
2
+ import{readFileSync as vt}from"fs";import{z as d}from"zod";import{readFileSync as nt,existsSync as ot}from"fs";import{homedir as it}from"os";import{join as A,dirname as st}from"path";import{parse as at}from"yaml";var $=d.object({cacheTTL:d.number().default(300),cacheDir:d.string().default("/tmp/.claude-statusline-cache"),maxLength:d.number().default(1e3),noEmoji:d.boolean().default(!1),noGitStatus:d.boolean().default(!1),noContextWindow:d.boolean().default(!1),envContext:d.boolean().default(!1),truncate:d.boolean().default(!1),softWrap:d.boolean().default(!1),noSoftWrap:d.boolean().default(!1),forceWidth:d.number().optional(),debugWidth:d.boolean().default(!1),rightMargin:d.number().default(15),symbols:d.object({git:d.string().default("\uF418"),model:d.string().default("\u{F06A9}"),contextWindow:d.string().default("\u26A1\uFE0E"),staged:d.string().default("+"),conflict:d.string().default("\xD7"),stashed:d.string().default("\u2691"),ahead:d.string().default("\u21E1"),behind:d.string().default("\u21E3"),diverged:d.string().default("\u21D5"),renamed:d.string().default("\xBB"),deleted:d.string().default("\u2718")}).default({}),asciiSymbols:d.object({git:d.string().default("@"),model:d.string().default("*"),contextWindow:d.string().default("#"),staged:d.string().default("+"),conflict:d.string().default("C"),stashed:d.string().default("$"),ahead:d.string().default("A"),behind:d.string().default("B"),diverged:d.string().default("D"),renamed:d.string().default(">"),deleted:d.string().default("X")}).default({})}),ct=$.parse({}),lt=["claude-statusline.json","claude-statusline.yaml"];function k(n=process.cwd()){let t={...ct};return t={...t,...dt(n)},t={...t,...ut()},$.parse(t)}function dt(n){let t=[n,st(n),A(it(),".claude")];for(let r of t)for(let e of lt){let o=A(r,e);if(ot(o))try{let i=nt(o,"utf-8");if(e.endsWith(".json"))return JSON.parse(i);if(e.endsWith(".yaml"))return at(i)}catch{}}return{}}function ut(){let n={};if(process.env.CLAUDE_CODE_STATUSLINE_NO_EMOJI==="1"&&(n.noEmoji=!0),process.env.CLAUDE_CODE_STATUSLINE_NO_GITSTATUS==="1"&&(n.noGitStatus=!0),process.env.CLAUDE_CODE_STATUSLINE_NO_CONTEXT_WINDOW==="1"&&(n.noContextWindow=!0),process.env.CLAUDE_CODE_STATUSLINE_ENV_CONTEXT==="1"&&(n.envContext=!0),process.env.CLAUDE_CODE_STATUSLINE_TRUNCATE==="1"&&(n.truncate=!0),process.env.CLAUDE_CODE_STATUSLINE_SOFT_WRAP==="1"&&(n.softWrap=!0),process.env.CLAUDE_CODE_STATUSLINE_NO_SOFT_WRAP==="1"&&(n.noSoftWrap=!0),process.env.CLAUDE_CODE_STATUSLINE_FORCE_WIDTH){let t=parseInt(process.env.CLAUDE_CODE_STATUSLINE_FORCE_WIDTH,10);!isNaN(t)&&t>0&&(n.forceWidth=t)}return process.env.CLAUDE_CODE_STATUSLINE_DEBUG_WIDTH==="1"&&(n.debugWidth=!0),process.env.CLAUDE_CODE_STATUSLINE_CACHE_DIR&&(n.cacheDir=process.env.CLAUDE_CODE_STATUSLINE_CACHE_DIR),n}var ft=4096,ht=[/\.\./,/\.\.\\/,/\[/,/;/,/&/,/</,/>/,/`/];function U(n,t){if(!n||typeof n!="string")return!1;let r=n.trimEnd();if(r.length===0||r.length>t.maxLength||!r.includes("{")||!r.includes("}"))return!1;let e=(r.match(/"/g)||[]).length;if(e===0||e%2!==0)return!1;try{JSON.parse(r)}catch{return!1}return!0}function mt(n){if(!n||typeof n!="string"||n.length>ft)return!1;for(let t of ht)if(t.test(n))return!1;if(n.includes("${")||n.includes("`")||n.includes("$("))return!1;try{let t=n.replace(/\/+/g,"/").replace(/\\+/g,"\\");if(t.includes("../")||t.includes("..\\")||t.startsWith("/")&&!t.startsWith("/home/")&&!t.startsWith("/Users/")&&!t.startsWith("/tmp/")&&!["/home","/Users","/tmp","/var","/opt"].some(o=>t.startsWith(o)))return!1;if(t.includes(":")&&/^[A-Za-z]:/.test(t)){let r=t.charAt(0).toUpperCase();if(r<"C"||r>"Z")return!1}}catch{return!1}return!0}async function G(n){if(!mt(n))return!1;try{let{access:t}=await import("fs/promises"),{constants:r}=await import("fs");return await t(n,r.R_OK),!0}catch{return!1}}import{readFile as M,writeFile as L,mkdir as gt}from"fs/promises";import{existsSync as j}from"fs";import{join as N}from"path";var F=class{config;constructor(t){this.config=t}async ensureCacheDir(){try{await gt(this.config.cacheDir,{recursive:!0,mode:448})}catch{}}getCachePath(t){return N(this.config.cacheDir,t)}getTimestampPath(t){return N(this.config.cacheDir,`${t}.time`)}async get(t,r=this.config.cacheTTL){let e=this.getCachePath(t),o=this.getTimestampPath(t);try{if(!j(e)||!j(o))return null;let i=await M(o,"utf-8"),c=parseInt(i.trim(),10);if(isNaN(c)||Math.floor(Date.now()/1e3)-c>=r)return null;let u=await M(e,"utf-8");try{return JSON.parse(u)}catch{return u}}catch{return null}}async set(t,r){let e=this.getCachePath(t),o=this.getTimestampPath(t);try{await this.ensureCacheDir();let i;typeof r=="string"?i=r:i=JSON.stringify(r);let c=Math.floor(Date.now()/1e3);return await Promise.all([L(e,i,"utf-8"),L(o,c.toString(),"utf-8")]),!0}catch{return!1}}async has(t,r=this.config.cacheTTL){return await this.get(t,r)!==null}async delete(t){let r=this.getCachePath(t),e=this.getTimestampPath(t);try{let{unlink:o}=await import("fs/promises");return await Promise.allSettled([o(r),o(e)]),!0}catch{return!1}}async clear(){try{let{readdir:t,unlink:r}=await import("fs/promises"),e=await t(this.config.cacheDir);return await Promise.allSettled(e.map(o=>r(N(this.config.cacheDir,o)))),!0}catch{return!1}}async getStats(){try{let{readdir:t,stat:r}=await import("fs/promises"),e=await t(this.config.cacheDir),o=0,i=0;for(let c of e)if(!c.endsWith(".time")){i++;let s=N(this.config.cacheDir,c);try{let a=await r(s);o+=a.size}catch{}}return{total:i,size:o}}catch{return{total:0,size:0}}}},w={NODE_VERSION:"node_version",PYTHON_VERSION:"python_version",PYTHON3_VERSION:"python3_version",DOCKER_VERSION:"docker_version",GIT_REMOTE_URL:n=>`git_remote_${Buffer.from(n).toString("base64")}`,GIT_BRANCH:n=>`git_branch_${Buffer.from(n).toString("base64")}`};async function v(n,t,r,e=[],o=300){let i=await n.get(t,o);if(i!==null)return i;try{let{execFile:c}=await import("child_process"),{promisify:s}=await import("util"),a=s(c),{stdout:l}=await a(r,e,{timeout:5e3,encoding:"utf-8"}),u=l.trim();return u&&await n.set(t,u),u}catch{return null}}import{spawn as pt}from"child_process";async function C(n,t={}){return new Promise((r,e)=>{let o=pt("git",n,{cwd:t.cwd||process.cwd(),stdio:["ignore","pipe","pipe"],timeout:t.timeout||5e3}),i="",c="";o.stdout.on("data",s=>{i+=s.toString()}),o.stderr.on("data",s=>{c+=s.toString()}),o.on("close",s=>{s===0?r(i):e(new Error(`Git command failed with code ${s}: ${c||i}`))}),o.on("error",s=>{e(new Error(`Failed to execute git command: ${s.message}`))})})}async function B(n){try{return await C(["rev-parse","--git-dir"],n?{cwd:n}:{}),!0}catch{return!1}}async function H(n){let t=n?{cwd:n}:{};try{let e=(await C(["branch","--show-current"],t)).trim();if(e)return e}catch{}try{let e=(await C(["rev-parse","--abbrev-ref","HEAD"],t)).trim();if(e&&e!=="HEAD")return e}catch{}try{let e=(await C(["branch","--no-color"],t)).split(`
3
+ `);for(let o of e)if(o.startsWith("* "))return o.substring(2).trim()}catch{}return null}async function V(n){return C(["status","--porcelain"],n?{cwd:n}:{})}async function z(n){try{return C(["stash","list"],n?{cwd:n}:{})}catch{return""}}async function K(n){try{return(await C(["rev-parse","--abbrev-ref","@{u}"],n?{cwd:n}:{})).trim()}catch{return""}}async function Y(n){try{let e=(await C(["rev-list","--count","--left-right","@{u}...HEAD"],n?{cwd:n}:{})).trim().split(" ");if(e.length===2){let o=parseInt(e[0]||"0",10);return{ahead:parseInt(e[1]||"0",10),behind:o}}}catch{}return{ahead:0,behind:0}}var yt={stashed:0,staged:0,modified:0,untracked:0,renamed:0,deleted:0,conflicts:0,ahead:0,behind:0,diverged:!1},x=class{config;cache;constructor(t,r){this.config=t,this.cache=r}async getGitInfo(t){if(this.config.noGitStatus)return null;try{if(!await B(t))return null;let e=await this.getCurrentBranch(t);if(!e)return null;let o=await this.getGitIndicators(t);return{branch:e,indicators:o}}catch{return null}}async getCurrentBranch(t){let r=`${w.GIT_BRANCH(t)}_current`,e=await this.cache.get(r,60);if(e)return e;try{let o=await H(t);return o?(await this.cache.set(r,o),o):null}catch{return null}}async getGitIndicators(t){let r={...yt};try{let o=(await V(t)).split(`
4
+ `).map(s=>s.trimEnd()).filter(s=>s.length>0);for(let s of o){if(s.length<2)continue;let a=s.charAt(0),l=s.charAt(1);if(a==="U"||l==="U"||a==="A"&&l==="A"||a==="D"&&l==="D")r.conflicts++;else if(a==="?"&&l==="?")r.untracked++;else{switch(a){case"M":r.staged++;break;case"A":r.staged++;break;case"D":r.deleted++;break;case"R":r.renamed++;break;case"C":r.staged++;break}switch(l){case"M":r.modified++;break;case"D":r.deleted++;break;case"R":r.renamed++;break}}}r.stashed=await this.getStashedCount(t);let{ahead:i,behind:c}=await this.getAheadBehind(t);r.ahead=i,r.behind=c,r.diverged=i>0&&c>0}catch{}return r}async getStashedCount(t){try{return(await z(t)).trim().split(`
5
+ `).filter(e=>e.trim().length>0).length}catch{return 0}}async getAheadBehind(t){try{return await K(t)?await Y(t):{ahead:0,behind:0}}catch{}return{ahead:0,behind:0}}formatIndicators(t,r){let e=[],o=["stashed","renamed","modified","staged","untracked","deleted","conflicts"];return t.stashed>0&&e.push(r.stashed),t.renamed>0&&e.push(r.renamed),t.modified>0&&e.push("!"),t.staged>0&&e.push(r.staged),t.untracked>0&&e.push("?"),t.deleted>0&&e.push(r.deleted),t.conflicts>0&&e.push(r.conflict),t.diverged?e.push(r.diverged):(t.ahead>0&&e.push(r.ahead),t.behind>0&&e.push(r.behind)),e.join("")}formatGitStatus(t,r){let e=this.formatIndicators(t.indicators,r),o=(this.config.noEmoji,r.git);return e?` ${o} ${t.branch} [${e}]`:` ${o} ${t.branch}`}};var J=new Map,bt=1,X={git:"@",model:"*",contextWindow:"\u2248",staged:"+",conflict:"C",stashed:"$",ahead:"A",behind:"B",diverged:"D",renamed:">",deleted:"X"},Et={git:"\uF418",model:"\u{F06A9}",contextWindow:"\u26A1\uFE0E",staged:"+",conflict:"\xD7",stashed:"\u2691",ahead:"\u21E1",behind:"\u21E3",diverged:"\u21D5",renamed:"\xBB",deleted:"\u2718"};async function Z(n){let t=process.env.NERD_FONT+"|"+process.env.TERM_PROGRAM+"|"+process.env.TERM,r=`${bt}:${n.noEmoji?"ascii":"nerd"}:${t}`,e=J.get(r);if(e&&Date.now()-e.timestamp<6e4)return e.symbols;let o;return n.noEmoji?o={...X,...n.symbols,...n.asciiSymbols}:(await St()).hasNerdFont?o={...Et,...n.symbols}:o={...X,...n.asciiSymbols},J.set(r,{symbols:o,timestamp:Date.now()}),o}async function St(){let n={hasNerdFont:!1,terminal:"",font:"",method:""};if(process.env.NERD_FONT==="1")return n.hasNerdFont=!0,n.method="NERD_FONT env var",n;let t=process.env.TERM_PROGRAM,r=process.env.TERM;if(t&&(n.terminal=t,n.method="TERM_PROGRAM detection",["vscode","ghostty","wezterm","iterm"].includes(t))||r&&(n.terminal=r,n.method="TERM detection",["alacritty","kitty","wezterm","ghostty","xterm-256color"].includes(r)))return n.hasNerdFont=!0,n;let e=await Ct();if(e.hasNerdFont)return n.hasNerdFont=!0,n.font=e.font,n.method="font list detection",n;let o=await Dt();return o.hasNerdFont?(n.hasNerdFont=!0,n.font=o.font,n.method="installation detection",n):wt().hasNerdFont?(n.hasNerdFont=!0,n.method="environment detection",n):((await Tt()).hasNerdFont&&(n.hasNerdFont=!0,n.method="platform-specific detection"),n)}async function Ct(){try{let{exec:n}=await import("child_process"),{promisify:t}=await import("util"),r=t(n),e="",o=process.platform;if(o==="linux"?e="fc-list":o==="darwin"&&(e="system_profiler SPFontsDataType 2>/dev/null || system_profiler SPFontsDataType"),e){let{stdout:i}=await r(e,{timeout:3e3,encoding:"utf-8"}),c=[/nerd font/i,/symbols only/i,/jetbrains mono.*nerd/i,/fira code.*nerd/i,/hack.*nerd/i,/source code pro.*nerd/i,/ubuntu mono.*nerd/i,/anonymous pro.*nerd/i];for(let s of c)if(s.test(i)){let a=i.match(/([^:\n]*)(?=\s*(nerd|symbols))/i);return{hasNerdFont:!0,font:(a?a[1]:"Nerd Font")?.trim()||"Nerd Font"}}}}catch{}return{hasNerdFont:!1,font:""}}async function Dt(){try{let{access:n,readdir:t}=await import("fs/promises"),{homedir:r}=await import("os"),e=process.platform,o=[];e==="darwin"?o.push(`${r()}/Library/Fonts`,"/System/Library/Fonts","/Library/Fonts"):e==="linux"&&o.push(`${r()}/.local/share/fonts`,`${r()}/.fonts`,"/usr/share/fonts","/usr/local/share/fonts");let i=["jetbrains-mono-nerd-font","fira-code-nerd-font","hack-nerd-font","source-code-pro-nerd-font","ubuntu-mono-nerd-font","anonymous-pro-nerd-font"];for(let c of o)try{await n(c);let s=await t(c);for(let a of s){let l=a.toLowerCase();for(let u of i)if(l.includes(u))return{hasNerdFont:!0,font:a}}}catch{}}catch{}return{hasNerdFont:!1,font:""}}function wt(){let n=["POWERLINE_COMMAND","NERDFONTS","FONT_FAMILY"];for(let t of n){let r=process.env[t];if(r&&r.toLowerCase().includes("nerd"))return{hasNerdFont:!0}}return process.env.VSCODE_PID||process.env.TERM_PROGRAM==="vscode"||process.env.TERM_PROGRAM==="ghostty"||process.env.TERM_PROGRAM==="wezterm"?{hasNerdFont:!0}:{hasNerdFont:!1}}async function Tt(){if(process.platform==="darwin")try{let{exec:t}=await import("child_process"),{promisify:r}=await import("util"),e=r(t),{stdout:o}=await e("brew list | grep -i font",{timeout:2e3,encoding:"utf-8"});if(o.includes("nerd"))return{hasNerdFont:!0}}catch{}return{hasNerdFont:!1}}function q(n){return{node:"\uE718",python:"\uE235",docker:"\uF308",git:n.git,model:n.model}}async function W(n){if(n.forceWidth&&n.forceWidth>0)return n.forceWidth;let t=process.env.COLUMNS;if(t){let s=parseInt(t,10);if(!isNaN(s)&&s>0)return s}if(process.stdout.columns&&process.stdout.columns>0)return process.stdout.columns;let r=await Q("tput",["cols"]);if(r)return r;let e=await tt();if(e)return e;let o=process.env.CLAUDE_CODE_TERMINAL_WIDTH;if(o){let s=parseInt(o,10);if(!isNaN(s)&&s>0)return s}let i=process.env.TERM_PROGRAM,c=process.env.TERM;return i==="vscode"&&process.env.VSCODE_PID||["ghostty","wezterm","iterm"].includes(i||"")||c&&["alacritty","kitty","wezterm","ghostty","xterm-256color"].includes(c)||process.env.WT_SESSION||process.env.WT_PROFILE_ID?120:80}async function Q(n,t){try{let{execFile:r}=await import("child_process"),{promisify:e}=await import("util"),o=e(r),{stdout:i}=await o(n,t,{timeout:1e3,encoding:"utf-8"}),c=parseInt(i.trim(),10);if(!isNaN(c)&&c>0)return c}catch{}return null}async function tt(){try{let{execFile:n}=await import("child_process"),{promisify:t}=await import("util"),r=t(n),{stdout:e}=await r("stty",["size"],{timeout:1e3,encoding:"utf-8"}),o=e.trim().split(" ");if(o.length===2){let i=parseInt(o[1]||"0",10);if(!isNaN(i)&&i>0)return i}}catch{}return null}async function et(n){if(!n.debugWidth)return;process.stdout.columns;let t=process.env.COLUMNS,r=await Q("tput",["cols"]),e=await tt(),o=await W(n)}function P(n,t){if(p(n)<=t)return n;if(t<4)return"..";let r=Array.from(n),e=0,o=0;for(let i=0;i<r.length;i++){let c=p(r[i]);if(e+c>t-2)break;e+=c,o=i+1}return`${r.slice(0,o).join("")}..`}function rt(n,t,r,e){let o=p(n),i=p(t);if(o+i<=r)return"";let c=r-i-2;if(c>=5){let h=Array.from(n),f=0,g=0;for(let E=0;E<h.length;E++){let D=p(h[E]);if(f+D>c)break;f+=D,g=E+1}return`${h.slice(0,g).join("")}..${t}`}let s="",a=t.match(/\[([^\]]+)\]/);a&&(s=a[1]||"");let l=p(s),u=r-l-8;if(u>=8){let h=Array.from(t),f=0,g=0;for(let S=0;S<h.length;S++){let T=p(h[S]);if(f+T>u)break;f+=T,g=S+1}let E=h.slice(0,g).join(""),D=Array.from(n),O=0,R=0;for(let S=0;S<D.length;S++){let T=p(D[S]);if(O+T>4)break;O+=T,R=S+1}return`${D.slice(0,R).join("")}..${E}..${s?` [${s}]`:""}`}let m=Array.from(n),y=0,b=0;for(let h=0;h<m.length;h++){let f=p(m[h]);if(y+f>r)break;y+=f,b=h+1}return`${m.slice(0,b).join("")}..`}function p(n){let t=0;for(let r of n){let e=r.codePointAt(0)??0;e>=4352&&(e>=4352&&e<=4447||e>=11904&&e<=42191||e>=44032&&e<=55203||e>=63744&&e<=64255||e>=65040&&e<=65049||e>=65072&&e<=65135||e>=65280&&e<=65376||e>=65504&&e<=65510||e>=131072&&e<=196605||e>=196608&&e<=262141)||e>=127744&&e<=129535||e>=9728&&e<=10175||e>=65024&&e<=65039||e>=126976&&e<=127023||e>=57344&&e<=63743||e>=983040&&e<=1048573||e>=1048576&&e<=1114109?t+=2:e>=768&&e<=879||e>=7616&&e<=7679||e>=8400&&e<=8447||e>=65056&&e<=65071||(t+=1)}return t}var _=class{config;cache;constructor(t,r){this.config=t,this.cache=r}async getEnvironmentInfo(){if(!this.config.envContext)return null;let t={},[r,e,o]=await Promise.allSettled([this.getNodeVersion(),this.getPythonVersion(),this.getDockerVersion()]);return r.status==="fulfilled"&&r.value&&(t.node=r.value),e.status==="fulfilled"&&e.value&&(t.python=e.value),o.status==="fulfilled"&&o.value&&(t.docker=o.value),Object.keys(t).length===0?null:t}async getNodeVersion(){let t=w.NODE_VERSION,r=this.config.cacheTTL*96,e=await this.cache.get(t,r);if(e)return e;try{let o=await v(this.cache,t,"node",["--version"],r);return o?o.replace(/^v/,"").trim():null}catch{return null}}async getPythonVersion(){let t=w.PYTHON3_VERSION,r=w.PYTHON_VERSION,e=this.config.cacheTTL*96;try{let o=await v(this.cache,t,"python3",["--version"],e);if(o){let i=o.match(/(\d+\.\d+\.\d+)/);if(i)return i[1]||null}}catch{}try{let o=await v(this.cache,r,"python",["--version"],e);if(o){let i=o.match(/(\d+\.\d+\.\d+)/);if(i)return i[1]||null}}catch{}return null}async getDockerVersion(){let t="docker_version";try{let r=await v(this.cache,t,"docker",["--version"],this.config.cacheTTL*96);if(r){let e=r.match(/Docker version (\d+\.\d+\.\d+)/);if(e)return e[1]||null}return null}catch{return null}}formatEnvironmentInfo(t,r){let e=[];return t.node&&e.push(`${r.node}${t.node}`),t.python&&e.push(`${r.python}${t.python}`),t.docker&&e.push(`${r.docker}${t.docker}`),e.join(" ")}async getAdditionalTools(){return{}}async isToolAvailable(t){try{let{execFile:r}=await import("child_process"),{promisify:e}=await import("util");return await e(r)("which",[t],{timeout:2e3}),!0}catch{return!1}}getShellEnvironment(){let t=process.env.SHELL||"unknown",r;return t.includes("bash")?r=process.env.BASH_VERSION:t.includes("zsh")?r=process.env.ZSH_VERSION:t.includes("fish")&&(r=process.env.FISH_VERSION),r?{shell:t,shellVersion:r}:{shell:t}}getOSInfo(){let t=process.platform,r=process.arch,e=process.env.OSTYPE||process.env.OS;return e?{platform:t,arch:r,release:e}:{platform:t,arch:r}}},I=class{symbols;constructor(t){this.symbols=t}format(t,r="compact"){switch(r){case"compact":return this.formatCompact(t);case"verbose":return this.formatVerbose(t);case"minimal":return this.formatMinimal(t);default:return this.formatCompact(t)}}formatCompact(t){let r=[];return t.node&&r.push(`Node${t.node}`),t.python&&r.push(`Py${t.python}`),t.docker&&r.push(`Docker${t.docker}`),r.join(" ")}formatVerbose(t){let r=[];return t.node&&r.push(`Node.js v${t.node}`),t.python&&r.push(`Python ${t.python}`),t.docker&&r.push(`Docker ${t.docker}`),r.join(" \u2022 ")}formatMinimal(t){let r=[];if(t.node){let e=t.node.split(".")[0];r.push(`N${e}`)}if(t.python){let e=t.python.split(".")[0];r.push(`P${e}`)}if(t.docker){let e=t.docker.split(".")[0];r.push(`D${e}`)}return r.join(" ")}formatWithIcons(t){let r=[];return t.node&&r.push(`${this.symbols.node}${t.node}`),t.python&&r.push(`${this.symbols.python}${t.python}`),t.docker&&r.push(`${this.symbols.docker}${t.docker}`),r.join(" ")}};async function Nt(){try{let n=k(),t=new F(n),r=new x(n,t),e=new _(n,t);await et(n);let o=await Ft();U(JSON.stringify(o),n)||process.exit(1);let{fullDir:i,modelName:c,contextWindow:s}=xt(o);(!i||!c)&&process.exit(1),await G(i)||process.exit(1);let l=[r.getGitInfo(i),e.getEnvironmentInfo(),Z(n)],u;n.truncate&&l.push(W(n));let m=await Promise.all(l),[y,b,h]=m;n.truncate&&m.length>3&&(u=m[3]);let f=await _t({fullDir:i,modelName:c,contextWindow:s,gitInfo:y,envInfo:b,symbols:h,...u&&{terminalWidth:u},config:n,gitOps:r});process.stdout.write(f)}catch{process.exit(1)}}async function Ft(){try{let n=vt(0,"utf-8");return JSON.parse(n.trim())}catch(n){throw new Error(`Failed to read or parse input: ${n instanceof Error?n.message:String(n)}`)}}function xt(n){let t=n.workspace?.current_dir||"",r=n.model?.display_name||"Unknown",e=n.context_window;return{fullDir:t,modelName:r,contextWindow:e}}async function _t(n){let{fullDir:t,modelName:r,contextWindow:e,gitInfo:o,envInfo:i,symbols:c,terminalWidth:s,config:a,gitOps:l}=n,u=t.split("/").pop()||t.split("\\").pop()||"project",m="";o&&(m=l.formatGitStatus(o,c));let y="";if(i){let g=q(c);y=` ${new I(g).formatWithIcons(i)}`}let b="";if(e&&!a.noContextWindow){let g=e.remaining_percentage;g!=null&&(b=` ${c.contextWindow}${Math.round(g)}%`)}let h=`${c.model}${r}${y}${b}`,f=`${u}${m} ${h}`;return a.truncate&&(s||process.exit(1),f=It({statusline:f,projectName:u,gitStatus:m,modelString:h,terminalWidth:s,config:a,symbols:c})),f}function It(n){let{statusline:t,projectName:r,gitStatus:e,modelString:o,terminalWidth:i,config:c}=n,s=Math.max(i-c.rightMargin,30),a=`${r}${e}`;if(p(t)<=s)return t;let u=p(a);if(u+1<=s)if(c.noSoftWrap){let y=s-u-1,b=P(o,y);return`${a} ${b}`}else{let y=s-u-1,b=/^[󰚩*]/,h=p(o);if(b.test(o)&&h>y)return`${a}
6
+ ${o}`;let f=Wt(o,y);return`${a} ${f}`}let m=rt(r,e,s,c);return m||P(t,s)}function Wt(n,t){if(p(n)<=t)return n;if(/^[󰚩*]/.test(n))return Pt(n,t);let o=!1,i=Array.from(n),c=0,s=i.length,a=-1;for(let f=0;f<i.length;f++){let g=i[f];if(!g)break;g===" "&&(a=f);let E=p(g);if(c+=E,c>t){a>=0?s=a:s=f,o=a>=0;break}}if(s>=i.length)return n;let l=i.length>0?i[0]:"",u=l&&l!==" "&&Buffer.byteLength(l,"utf8")>1;if(!o&&t-c>-3&&!u)return n;if(u&&s<=2&&t>=3){let f=0;for(let g=0;g<i.length;g++){let E=i[g];if(!E)break;if(f+=p(E),f>=3||f>=t){s=g,o=!0;break}}}let m=i.slice(0,s),y=i.slice(s);y.length>0&&y[0]===" "&&y.shift();let b=m.join(""),h=y.join("");return h?`${b}
7
+ ${h}`:b}function Pt(n,t){if(p(n)<=t)return n;let e=Array.from(n),o=[];for(let a=0;a<e.length;a++)e[a]===" "&&o.push(a);let i=-1;for(let a=0;a<e.length;a++){let l=e[a];if(l==="\u26A1"||l==="#"){i=a;break}}if(i>0){let a=e.findIndex((l,u)=>u<i&&l===" ");if(a>0){let l=e.slice(a+1).join("");if(p(l)<=t)return`${e.slice(0,a).join("")}
8
+ ${l}`}}if(o.length>0){let a=o[0];if(a!==void 0&&a>1){let l=e.slice(0,a).join("");if(p(l)<=t){let m=e.slice(a+1).join("");return m?`${l}
9
+ ${m}`:l}}}let c=0,s=e.length;for(let a=0;a<e.length;a++){let l=e[a];if(!l)break;let u=p(l);if(c+u>t){s=a;break}c+=u}if(s<e.length&&s>0){let a=e.slice(0,s).join(""),l=e.slice(s).join("");return l?`${a}
10
+ ${l}`:a}return n}import.meta.url===`file://${process.argv[1]}`&&Nt();export{Nt as main};