claude-statusline 2.1.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.
Files changed (48) hide show
  1. package/LICENSE +203 -0
  2. package/README.md +362 -0
  3. package/bin/claude-statusline +22 -0
  4. package/dist/core/cache.d.ts +67 -0
  5. package/dist/core/cache.js +223 -0
  6. package/dist/core/cache.js.map +1 -0
  7. package/dist/core/config.d.ts +190 -0
  8. package/dist/core/config.js +192 -0
  9. package/dist/core/config.js.map +1 -0
  10. package/dist/core/security.d.ts +27 -0
  11. package/dist/core/security.js +154 -0
  12. package/dist/core/security.js.map +1 -0
  13. package/dist/env/context.d.ts +92 -0
  14. package/dist/env/context.js +295 -0
  15. package/dist/env/context.js.map +1 -0
  16. package/dist/git/native.d.ts +35 -0
  17. package/dist/git/native.js +141 -0
  18. package/dist/git/native.js.map +1 -0
  19. package/dist/git/status.d.ts +65 -0
  20. package/dist/git/status.js +256 -0
  21. package/dist/git/status.js.map +1 -0
  22. package/dist/index.bundle.js +11 -0
  23. package/dist/index.d.ts +9 -0
  24. package/dist/index.js +396 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/metafile.prod.json +473 -0
  27. package/dist/ui/symbols.d.ts +31 -0
  28. package/dist/ui/symbols.js +308 -0
  29. package/dist/ui/symbols.js.map +1 -0
  30. package/dist/ui/width.d.ts +29 -0
  31. package/dist/ui/width.js +261 -0
  32. package/dist/ui/width.js.map +1 -0
  33. package/dist/utils/runtime.d.ts +31 -0
  34. package/dist/utils/runtime.js +82 -0
  35. package/dist/utils/runtime.js.map +1 -0
  36. package/docs/ARCHITECTURE.md +336 -0
  37. package/docs/FEATURE_COMPARISON.md +178 -0
  38. package/docs/MIGRATION.md +354 -0
  39. package/docs/README.md +101 -0
  40. package/docs/eval-01-terminal-widths.md +519 -0
  41. package/docs/guide-01-configuration.md +277 -0
  42. package/docs/guide-02-troubleshooting.md +416 -0
  43. package/docs/guide-03-performance.md +183 -0
  44. package/docs/prd-01-typescript-perf-optimization.md +480 -0
  45. package/docs/research-01-sandbox-detection.md +169 -0
  46. package/docs/research-02-competitive-analysis.md +226 -0
  47. package/docs/research-03-platform-analysis.md +142 -0
  48. package/package.json +89 -0
@@ -0,0 +1,141 @@
1
+ import { spawn } from 'child_process';
2
+ /**
3
+ * Execute a git command using child_process.spawn
4
+ * Provides native git execution without external dependencies
5
+ */
6
+ export async function executeGitCommand(args, options = {}) {
7
+ return new Promise((resolve, reject) => {
8
+ const git = spawn('git', args, {
9
+ cwd: options.cwd || process.cwd(),
10
+ stdio: ['ignore', 'pipe', 'pipe'],
11
+ timeout: options.timeout || 5000,
12
+ });
13
+ let stdout = '';
14
+ let stderr = '';
15
+ git.stdout.on('data', (data) => {
16
+ stdout += data.toString();
17
+ });
18
+ git.stderr.on('data', (data) => {
19
+ stderr += data.toString();
20
+ });
21
+ git.on('close', (code) => {
22
+ if (code === 0) {
23
+ resolve(stdout);
24
+ }
25
+ else {
26
+ reject(new Error(`Git command failed with code ${code}: ${stderr || stdout}`));
27
+ }
28
+ });
29
+ git.on('error', (error) => {
30
+ reject(new Error(`Failed to execute git command: ${error.message}`));
31
+ });
32
+ });
33
+ }
34
+ /**
35
+ * Check if directory is a git repository
36
+ */
37
+ export async function checkIsRepo(cwd) {
38
+ try {
39
+ const options = cwd ? { cwd } : {};
40
+ await executeGitCommand(['rev-parse', '--git-dir'], options);
41
+ return true;
42
+ }
43
+ catch {
44
+ return false;
45
+ }
46
+ }
47
+ /**
48
+ * Get current branch name using multiple fallback methods
49
+ */
50
+ export async function getCurrentBranch(cwd) {
51
+ const options = cwd ? { cwd } : {};
52
+ // Method 1: git branch --show-current (most reliable)
53
+ try {
54
+ const result = await executeGitCommand(['branch', '--show-current'], options);
55
+ const branch = result.trim();
56
+ if (branch) {
57
+ return branch;
58
+ }
59
+ }
60
+ catch {
61
+ // Continue to next method
62
+ }
63
+ // Method 2: git rev-parse --abbrev-ref HEAD
64
+ try {
65
+ const result = await executeGitCommand(['rev-parse', '--abbrev-ref', 'HEAD'], options);
66
+ const branch = result.trim();
67
+ // Filter out HEAD if not on a branch
68
+ if (branch && branch !== 'HEAD') {
69
+ return branch;
70
+ }
71
+ }
72
+ catch {
73
+ // Continue to next method
74
+ }
75
+ // Method 3: Parse git branch output for current branch
76
+ try {
77
+ const result = await executeGitCommand(['branch', '--no-color'], options);
78
+ const lines = result.split('\n');
79
+ for (const line of lines) {
80
+ if (line.startsWith('* ')) {
81
+ return line.substring(2).trim();
82
+ }
83
+ }
84
+ }
85
+ catch {
86
+ // All methods failed
87
+ }
88
+ return null;
89
+ }
90
+ /**
91
+ * Get git status in porcelain format
92
+ */
93
+ export async function getPorcelainStatus(cwd) {
94
+ const options = cwd ? { cwd } : {};
95
+ return executeGitCommand(['status', '--porcelain'], options);
96
+ }
97
+ /**
98
+ * Get stash list
99
+ */
100
+ export async function getStashList(cwd) {
101
+ try {
102
+ const options = cwd ? { cwd } : {};
103
+ return executeGitCommand(['stash', 'list'], options);
104
+ }
105
+ catch {
106
+ return '';
107
+ }
108
+ }
109
+ /**
110
+ * Get upstream branch reference
111
+ */
112
+ export async function getUpstreamRef(cwd) {
113
+ try {
114
+ const options = cwd ? { cwd } : {};
115
+ const result = await executeGitCommand(['rev-parse', '--abbrev-ref', '@{u}'], options);
116
+ return result.trim();
117
+ }
118
+ catch {
119
+ return '';
120
+ }
121
+ }
122
+ /**
123
+ * Get ahead/behind counts
124
+ */
125
+ export async function getAheadBehind(cwd) {
126
+ try {
127
+ const options = cwd ? { cwd } : {};
128
+ const result = await executeGitCommand(['rev-list', '--count', '--left-right', '@{u}...HEAD'], options);
129
+ const counts = result.trim().split('\t');
130
+ if (counts.length === 2) {
131
+ const behind = parseInt(counts[0] || '0', 10);
132
+ const ahead = parseInt(counts[1] || '0', 10);
133
+ return { ahead, behind };
134
+ }
135
+ }
136
+ catch {
137
+ // No upstream or other error
138
+ }
139
+ return { ahead: 0, behind: 0 };
140
+ }
141
+ //# sourceMappingURL=native.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"native.js","sourceRoot":"","sources":["../../src/git/native.ts"],"names":[],"mappings":"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","sourcesContent":["import { spawn } from 'child_process';\n\n/**\n * Execute a git command using child_process.spawn\n * Provides native git execution without external dependencies\n */\nexport async function executeGitCommand(\n args: string[],\n options: {\n cwd?: string;\n timeout?: number;\n } = {}\n): Promise<string> {\n return new Promise((resolve, reject) => {\n const git = spawn('git', args, {\n cwd: options.cwd || process.cwd(),\n stdio: ['ignore', 'pipe', 'pipe'],\n timeout: options.timeout || 5000,\n });\n\n let stdout = '';\n let stderr = '';\n\n git.stdout.on('data', (data) => {\n stdout += data.toString();\n });\n\n git.stderr.on('data', (data) => {\n stderr += data.toString();\n });\n\n git.on('close', (code) => {\n if (code === 0) {\n resolve(stdout);\n } else {\n reject(new Error(`Git command failed with code ${code}: ${stderr || stdout}`));\n }\n });\n\n git.on('error', (error) => {\n reject(new Error(`Failed to execute git command: ${error.message}`));\n });\n });\n}\n\n/**\n * Check if directory is a git repository\n */\nexport async function checkIsRepo(cwd?: string): Promise<boolean> {\n try {\n const options = cwd ? { cwd } : {};\n await executeGitCommand(['rev-parse', '--git-dir'], options);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Get current branch name using multiple fallback methods\n */\nexport async function getCurrentBranch(cwd?: string): Promise<string | null> {\n const options = cwd ? { cwd } : {};\n\n // Method 1: git branch --show-current (most reliable)\n try {\n const result = await executeGitCommand(['branch', '--show-current'], options);\n const branch = result.trim();\n if (branch) {\n return branch;\n }\n } catch {\n // Continue to next method\n }\n\n // Method 2: git rev-parse --abbrev-ref HEAD\n try {\n const result = await executeGitCommand(['rev-parse', '--abbrev-ref', 'HEAD'], options);\n const branch = result.trim();\n // Filter out HEAD if not on a branch\n if (branch && branch !== 'HEAD') {\n return branch;\n }\n } catch {\n // Continue to next method\n }\n\n // Method 3: Parse git branch output for current branch\n try {\n const result = await executeGitCommand(['branch', '--no-color'], options);\n const lines = result.split('\\n');\n for (const line of lines) {\n if (line.startsWith('* ')) {\n return line.substring(2).trim();\n }\n }\n } catch {\n // All methods failed\n }\n\n return null;\n}\n\n/**\n * Get git status in porcelain format\n */\nexport async function getPorcelainStatus(cwd?: string): Promise<string> {\n const options = cwd ? { cwd } : {};\n return executeGitCommand(['status', '--porcelain'], options);\n}\n\n/**\n * Get stash list\n */\nexport async function getStashList(cwd?: string): Promise<string> {\n try {\n const options = cwd ? { cwd } : {};\n return executeGitCommand(['stash', 'list'], options);\n } catch {\n return '';\n }\n}\n\n/**\n * Get upstream branch reference\n */\nexport async function getUpstreamRef(cwd?: string): Promise<string> {\n try {\n const options = cwd ? { cwd } : {};\n const result = await executeGitCommand(['rev-parse', '--abbrev-ref', '@{u}'], options);\n return result.trim();\n } catch {\n return '';\n }\n}\n\n/**\n * Get ahead/behind counts\n */\nexport async function getAheadBehind(cwd?: string): Promise<{ ahead: number; behind: number }> {\n try {\n const options = cwd ? { cwd } : {};\n const result = await executeGitCommand(['rev-list', '--count', '--left-right', '@{u}...HEAD'], options);\n const counts = result.trim().split('\\t');\n\n if (counts.length === 2) {\n const behind = parseInt(counts[0] || '0', 10);\n const ahead = parseInt(counts[1] || '0', 10);\n return { ahead, behind };\n }\n } catch {\n // No upstream or other error\n }\n\n return { ahead: 0, behind: 0 };\n}"]}
@@ -0,0 +1,65 @@
1
+ import { Config } from '../core/config.js';
2
+ import { Cache } from '../core/cache.js';
3
+ /**
4
+ * Git status information interface
5
+ */
6
+ export interface GitInfo {
7
+ branch: string;
8
+ indicators: GitIndicators;
9
+ }
10
+ /**
11
+ * Git status indicators
12
+ */
13
+ export interface GitIndicators {
14
+ stashed: number;
15
+ staged: number;
16
+ modified: number;
17
+ untracked: number;
18
+ renamed: number;
19
+ deleted: number;
20
+ conflicts: number;
21
+ ahead: number;
22
+ behind: number;
23
+ diverged: boolean;
24
+ }
25
+ /**
26
+ * Empty git indicators (no changes)
27
+ */
28
+ export declare const EMPTY_INDICATORS: GitIndicators;
29
+ /**
30
+ * Git operations and status parsing
31
+ * Ported from bash implementation with enhanced TypeScript safety
32
+ */
33
+ export declare class GitOperations {
34
+ private config;
35
+ private cache;
36
+ constructor(config: Config, cache: Cache);
37
+ /**
38
+ * Get git information for a directory
39
+ */
40
+ getGitInfo(directory: string): Promise<GitInfo | null>;
41
+ /**
42
+ * Get current branch name with caching
43
+ */
44
+ private getCurrentBranch;
45
+ /**
46
+ * Get comprehensive git status indicators
47
+ */
48
+ private getGitIndicators;
49
+ /**
50
+ * Get number of stashed changes
51
+ */
52
+ private getStashedCount;
53
+ /**
54
+ * Get ahead/behind count for tracking branch
55
+ */
56
+ private getAheadBehind;
57
+ /**
58
+ * Format git indicators into display string
59
+ */
60
+ formatIndicators(indicators: GitIndicators, symbols: Config['symbols']): string;
61
+ /**
62
+ * Get git status string for display
63
+ */
64
+ formatGitStatus(gitInfo: GitInfo, symbols: Config['symbols']): string;
65
+ }
@@ -0,0 +1,256 @@
1
+ import { CacheKeys } from '../core/cache.js';
2
+ import { checkIsRepo, getCurrentBranch, getPorcelainStatus, getStashList, getUpstreamRef, getAheadBehind, } from './native.js';
3
+ /**
4
+ * Empty git indicators (no changes)
5
+ */
6
+ export const EMPTY_INDICATORS = {
7
+ stashed: 0,
8
+ staged: 0,
9
+ modified: 0,
10
+ untracked: 0,
11
+ renamed: 0,
12
+ deleted: 0,
13
+ conflicts: 0,
14
+ ahead: 0,
15
+ behind: 0,
16
+ diverged: false,
17
+ };
18
+ /**
19
+ * Git operations and status parsing
20
+ * Ported from bash implementation with enhanced TypeScript safety
21
+ */
22
+ export class GitOperations {
23
+ config;
24
+ cache;
25
+ constructor(config, cache) {
26
+ this.config = config;
27
+ this.cache = cache;
28
+ }
29
+ /**
30
+ * Get git information for a directory
31
+ */
32
+ async getGitInfo(directory) {
33
+ if (this.config.noGitStatus) {
34
+ return null;
35
+ }
36
+ try {
37
+ // Check if this is a git repository
38
+ const isRepo = await checkIsRepo(directory);
39
+ if (!isRepo) {
40
+ return null;
41
+ }
42
+ // Get current branch
43
+ const branch = await this.getCurrentBranch(directory);
44
+ if (!branch) {
45
+ return null;
46
+ }
47
+ // Get status indicators
48
+ const indicators = await this.getGitIndicators(directory);
49
+ return { branch, indicators };
50
+ }
51
+ catch (error) {
52
+ console.debug('[DEBUG] Git operation failed:', error instanceof Error ? error.message : String(error));
53
+ return null;
54
+ }
55
+ }
56
+ /**
57
+ * Get current branch name with caching
58
+ */
59
+ async getCurrentBranch(directory) {
60
+ const cacheKey = `${CacheKeys.GIT_BRANCH(directory)}_current`;
61
+ // Try cache first
62
+ const cached = await this.cache.get(cacheKey, 60); // 1 minute TTL for branch
63
+ if (cached) {
64
+ return cached;
65
+ }
66
+ try {
67
+ const branch = await getCurrentBranch(directory);
68
+ if (branch) {
69
+ await this.cache.set(cacheKey, branch);
70
+ return branch;
71
+ }
72
+ return null;
73
+ }
74
+ catch (error) {
75
+ console.debug('[DEBUG] Failed to get current branch:', error instanceof Error ? error.message : String(error));
76
+ return null;
77
+ }
78
+ }
79
+ /**
80
+ * Get comprehensive git status indicators
81
+ */
82
+ async getGitIndicators(directory) {
83
+ const indicators = { ...EMPTY_INDICATORS };
84
+ try {
85
+ // Get porcelain status for parsing
86
+ const statusResult = await getPorcelainStatus(directory);
87
+ const statusLines = statusResult.split('\n')
88
+ .map(line => line.trimEnd()) // Only trim trailing whitespace, not leading!
89
+ .filter(line => line.length > 0);
90
+ // Parse each status line
91
+ for (const line of statusLines) {
92
+ if (line.length < 2)
93
+ continue;
94
+ const stagedChar = line.charAt(0);
95
+ const unstagedChar = line.charAt(1);
96
+ // Check for conflicts (U = unmerged)
97
+ if (stagedChar === 'U' || unstagedChar === 'U' ||
98
+ (stagedChar === 'A' && unstagedChar === 'A') ||
99
+ (stagedChar === 'D' && unstagedChar === 'D')) {
100
+ indicators.conflicts++;
101
+ }
102
+ // Check for untracked files
103
+ else if (stagedChar === '?' && unstagedChar === '?') {
104
+ indicators.untracked++;
105
+ }
106
+ else {
107
+ // Parse staged changes (first character)
108
+ switch (stagedChar) {
109
+ case 'M':
110
+ indicators.staged++; // Modified
111
+ break;
112
+ case 'A':
113
+ indicators.staged++; // Added
114
+ break;
115
+ case 'D':
116
+ indicators.deleted++; // Deleted (staged)
117
+ break;
118
+ case 'R':
119
+ indicators.renamed++; // Renamed (staged)
120
+ break;
121
+ case 'C':
122
+ indicators.staged++; // Copied (staged)
123
+ break;
124
+ }
125
+ // Parse unstaged changes (second character)
126
+ switch (unstagedChar) {
127
+ case 'M':
128
+ indicators.modified++; // Modified
129
+ break;
130
+ case 'D':
131
+ indicators.deleted++; // Deleted (unstaged)
132
+ break;
133
+ case 'R':
134
+ indicators.renamed++; // Renamed (unstaged)
135
+ break;
136
+ }
137
+ }
138
+ }
139
+ // Get stashed changes count
140
+ indicators.stashed = await this.getStashedCount(directory);
141
+ // Get ahead/behind information
142
+ const { ahead, behind } = await this.getAheadBehind(directory);
143
+ indicators.ahead = ahead;
144
+ indicators.behind = behind;
145
+ indicators.diverged = ahead > 0 && behind > 0;
146
+ }
147
+ catch (error) {
148
+ console.debug('[DEBUG] Failed to parse git status:', error instanceof Error ? error.message : String(error));
149
+ }
150
+ return indicators;
151
+ }
152
+ /**
153
+ * Get number of stashed changes
154
+ */
155
+ async getStashedCount(directory) {
156
+ try {
157
+ const stashList = await getStashList(directory);
158
+ return stashList.trim().split('\n').filter(line => line.trim().length > 0).length;
159
+ }
160
+ catch {
161
+ return 0;
162
+ }
163
+ }
164
+ /**
165
+ * Get ahead/behind count for tracking branch
166
+ */
167
+ async getAheadBehind(directory) {
168
+ try {
169
+ // Check if we have an upstream branch
170
+ const upstream = await getUpstreamRef(directory);
171
+ if (!upstream) {
172
+ return { ahead: 0, behind: 0 };
173
+ }
174
+ // Get ahead/behind count
175
+ return await getAheadBehind(directory);
176
+ }
177
+ catch {
178
+ // No upstream or other error
179
+ }
180
+ return { ahead: 0, behind: 0 };
181
+ }
182
+ /**
183
+ * Format git indicators into display string
184
+ */
185
+ formatIndicators(indicators, symbols) {
186
+ const indicatorChars = [];
187
+ // Custom order matching user preference: ⚑»!+?✘×⇕⇡⇣
188
+ // IMPORTANT: Do NOT change this order without updating documentation
189
+ const expectedOrder = ['stashed', 'renamed', 'modified', 'staged', 'untracked', 'deleted', 'conflicts'];
190
+ // Debug logging for order validation (only in development)
191
+ if (process.env.NODE_ENV !== 'production') {
192
+ const actualOrder = [];
193
+ if (indicators.stashed > 0)
194
+ actualOrder.push('stashed');
195
+ if (indicators.renamed > 0)
196
+ actualOrder.push('renamed');
197
+ if (indicators.modified > 0)
198
+ actualOrder.push('modified');
199
+ if (indicators.staged > 0)
200
+ actualOrder.push('staged');
201
+ if (indicators.untracked > 0)
202
+ actualOrder.push('untracked');
203
+ if (indicators.deleted > 0)
204
+ actualOrder.push('deleted');
205
+ if (indicators.conflicts > 0)
206
+ actualOrder.push('conflicts');
207
+ // Validate order consistency
208
+ for (let i = 0; i < actualOrder.length - 1; i++) {
209
+ const currentIndex = expectedOrder.indexOf(actualOrder[i]);
210
+ const nextIndex = expectedOrder.indexOf(actualOrder[i + 1]);
211
+ if (currentIndex !== -1 && nextIndex !== -1 && currentIndex > nextIndex) {
212
+ console.warn(`[WARN] Indicator order violation: ${actualOrder[i]} should not come before ${actualOrder[i + 1]}`);
213
+ }
214
+ }
215
+ }
216
+ if (indicators.stashed > 0)
217
+ indicatorChars.push(symbols.stashed);
218
+ if (indicators.renamed > 0)
219
+ indicatorChars.push(symbols.renamed);
220
+ if (indicators.modified > 0)
221
+ indicatorChars.push('!');
222
+ if (indicators.staged > 0)
223
+ indicatorChars.push(symbols.staged);
224
+ if (indicators.untracked > 0)
225
+ indicatorChars.push('?');
226
+ if (indicators.deleted > 0)
227
+ indicatorChars.push(symbols.deleted);
228
+ if (indicators.conflicts > 0)
229
+ indicatorChars.push(symbols.conflict);
230
+ // Ahead/behind status
231
+ if (indicators.diverged) {
232
+ indicatorChars.push(symbols.diverged);
233
+ }
234
+ else {
235
+ if (indicators.ahead > 0)
236
+ indicatorChars.push(symbols.ahead);
237
+ if (indicators.behind > 0)
238
+ indicatorChars.push(symbols.behind);
239
+ }
240
+ return indicatorChars.join('');
241
+ }
242
+ /**
243
+ * Get git status string for display
244
+ */
245
+ formatGitStatus(gitInfo, symbols) {
246
+ const indicators = this.formatIndicators(gitInfo.indicators, symbols);
247
+ const gitSymbol = this.config.noEmoji ? symbols.git : symbols.git;
248
+ if (indicators) {
249
+ return ` ${gitSymbol} ${gitInfo.branch} [${indicators}]`;
250
+ }
251
+ else {
252
+ return ` ${gitSymbol} ${gitInfo.branch}`;
253
+ }
254
+ }
255
+ }
256
+ //# sourceMappingURL=status.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/git/status.ts"],"names":[],"mappings":"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","sourcesContent":["import { Config } from '../core/config.js';\nimport { Cache, CacheKeys } from '../core/cache.js';\nimport {\n checkIsRepo,\n getCurrentBranch,\n getPorcelainStatus,\n getStashList,\n getUpstreamRef,\n getAheadBehind,\n} from './native.js';\n\n/**\n * Git status information interface\n */\nexport interface GitInfo {\n branch: string;\n indicators: GitIndicators;\n}\n\n/**\n * Git status indicators\n */\nexport interface GitIndicators {\n stashed: number;\n staged: number;\n modified: number;\n untracked: number;\n renamed: number;\n deleted: number;\n conflicts: number;\n ahead: number;\n behind: number;\n diverged: boolean;\n}\n\n/**\n * Empty git indicators (no changes)\n */\nexport const EMPTY_INDICATORS: GitIndicators = {\n stashed: 0,\n staged: 0,\n modified: 0,\n untracked: 0,\n renamed: 0,\n deleted: 0,\n conflicts: 0,\n ahead: 0,\n behind: 0,\n diverged: false,\n};\n\n/**\n * Git operations and status parsing\n * Ported from bash implementation with enhanced TypeScript safety\n */\nexport class GitOperations {\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 git information for a directory\n */\n async getGitInfo(directory: string): Promise<GitInfo | null> {\n if (this.config.noGitStatus) {\n return null;\n }\n\n try {\n // Check if this is a git repository\n const isRepo = await checkIsRepo(directory);\n if (!isRepo) {\n return null;\n }\n\n // Get current branch\n const branch = await this.getCurrentBranch(directory);\n if (!branch) {\n return null;\n }\n\n // Get status indicators\n const indicators = await this.getGitIndicators(directory);\n\n return { branch, indicators };\n\n } catch (error) {\n console.debug('[DEBUG] Git operation failed:', error instanceof Error ? error.message : String(error));\n return null;\n }\n }\n\n /**\n * Get current branch name with caching\n */\n private async getCurrentBranch(directory: string): Promise<string | null> {\n const cacheKey = `${CacheKeys.GIT_BRANCH(directory)}_current`;\n\n // Try cache first\n const cached = await this.cache.get<string>(cacheKey, 60); // 1 minute TTL for branch\n if (cached) {\n return cached;\n }\n\n try {\n const branch = await getCurrentBranch(directory);\n\n if (branch) {\n await this.cache.set(cacheKey, branch);\n return branch;\n }\n\n return null;\n\n } catch (error) {\n console.debug('[DEBUG] Failed to get current branch:', error instanceof Error ? error.message : String(error));\n return null;\n }\n }\n\n /**\n * Get comprehensive git status indicators\n */\n private async getGitIndicators(directory: string): Promise<GitIndicators> {\n const indicators = { ...EMPTY_INDICATORS };\n\n try {\n // Get porcelain status for parsing\n const statusResult = await getPorcelainStatus(directory);\n const statusLines = statusResult.split('\\n')\n .map(line => line.trimEnd()) // Only trim trailing whitespace, not leading!\n .filter(line => line.length > 0);\n\n // Parse each status line\n for (const line of statusLines) {\n if (line.length < 2) continue;\n\n const stagedChar = line.charAt(0);\n const unstagedChar = line.charAt(1);\n\n // Check for conflicts (U = unmerged)\n if (stagedChar === 'U' || unstagedChar === 'U' ||\n (stagedChar === 'A' && unstagedChar === 'A') ||\n (stagedChar === 'D' && unstagedChar === 'D')) {\n indicators.conflicts++;\n }\n // Check for untracked files\n else if (stagedChar === '?' && unstagedChar === '?') {\n indicators.untracked++;\n } else {\n // Parse staged changes (first character)\n switch (stagedChar) {\n case 'M':\n indicators.staged++; // Modified\n break;\n case 'A':\n indicators.staged++; // Added\n break;\n case 'D':\n indicators.deleted++; // Deleted (staged)\n break;\n case 'R':\n indicators.renamed++; // Renamed (staged)\n break;\n case 'C':\n indicators.staged++; // Copied (staged)\n break;\n }\n\n // Parse unstaged changes (second character)\n switch (unstagedChar) {\n case 'M':\n indicators.modified++; // Modified\n break;\n case 'D':\n indicators.deleted++; // Deleted (unstaged)\n break;\n case 'R':\n indicators.renamed++; // Renamed (unstaged)\n break;\n }\n }\n }\n\n // Get stashed changes count\n indicators.stashed = await this.getStashedCount(directory);\n\n // Get ahead/behind information\n const { ahead, behind } = await this.getAheadBehind(directory);\n indicators.ahead = ahead;\n indicators.behind = behind;\n indicators.diverged = ahead > 0 && behind > 0;\n\n } catch (error) {\n console.debug('[DEBUG] Failed to parse git status:', error instanceof Error ? error.message : String(error));\n }\n\n return indicators;\n }\n\n /**\n * Get number of stashed changes\n */\n private async getStashedCount(directory: string): Promise<number> {\n try {\n const stashList = await getStashList(directory);\n return stashList.trim().split('\\n').filter(line => line.trim().length > 0).length;\n } catch {\n return 0;\n }\n }\n\n /**\n * Get ahead/behind count for tracking branch\n */\n private async getAheadBehind(directory: string): Promise<{ ahead: number; behind: number }> {\n try {\n // Check if we have an upstream branch\n const upstream = await getUpstreamRef(directory);\n if (!upstream) {\n return { ahead: 0, behind: 0 };\n }\n\n // Get ahead/behind count\n return await getAheadBehind(directory);\n\n } catch {\n // No upstream or other error\n }\n\n return { ahead: 0, behind: 0 };\n }\n\n /**\n * Format git indicators into display string\n */\n formatIndicators(indicators: GitIndicators, symbols: Config['symbols']): string {\n const indicatorChars: string[] = [];\n\n // Custom order matching user preference: ⚑»!+?✘×⇕⇡⇣\n // IMPORTANT: Do NOT change this order without updating documentation\n const expectedOrder = ['stashed', 'renamed', 'modified', 'staged', 'untracked', 'deleted', 'conflicts'];\n\n // Debug logging for order validation (only in development)\n if (process.env.NODE_ENV !== 'production') {\n const actualOrder: string[] = [];\n if (indicators.stashed > 0) actualOrder.push('stashed');\n if (indicators.renamed > 0) actualOrder.push('renamed');\n if (indicators.modified > 0) actualOrder.push('modified');\n if (indicators.staged > 0) actualOrder.push('staged');\n if (indicators.untracked > 0) actualOrder.push('untracked');\n if (indicators.deleted > 0) actualOrder.push('deleted');\n if (indicators.conflicts > 0) actualOrder.push('conflicts');\n\n // Validate order consistency\n for (let i = 0; i < actualOrder.length - 1; i++) {\n const currentIndex = expectedOrder.indexOf(actualOrder[i]!);\n const nextIndex = expectedOrder.indexOf(actualOrder[i + 1]!);\n if (currentIndex !== -1 && nextIndex !== -1 && currentIndex > nextIndex) {\n console.warn(`[WARN] Indicator order violation: ${actualOrder[i]!} should not come before ${actualOrder[i + 1]!}`);\n }\n }\n }\n\n if (indicators.stashed > 0) indicatorChars.push(symbols.stashed);\n if (indicators.renamed > 0) indicatorChars.push(symbols.renamed);\n if (indicators.modified > 0) indicatorChars.push('!');\n if (indicators.staged > 0) indicatorChars.push(symbols.staged);\n if (indicators.untracked > 0) indicatorChars.push('?');\n if (indicators.deleted > 0) indicatorChars.push(symbols.deleted);\n if (indicators.conflicts > 0) indicatorChars.push(symbols.conflict);\n\n // Ahead/behind status\n if (indicators.diverged) {\n indicatorChars.push(symbols.diverged);\n } else {\n if (indicators.ahead > 0) indicatorChars.push(symbols.ahead);\n if (indicators.behind > 0) indicatorChars.push(symbols.behind);\n }\n\n return indicatorChars.join('');\n }\n\n /**\n * Get git status string for display\n */\n formatGitStatus(gitInfo: GitInfo, symbols: Config['symbols']): string {\n const indicators = this.formatIndicators(gitInfo.indicators, symbols);\n const gitSymbol = this.config.noEmoji ? symbols.git : symbols.git;\n\n if (indicators) {\n return ` ${gitSymbol} ${gitInfo.branch} [${indicators}]`;\n } else {\n return ` ${gitSymbol} ${gitInfo.branch}`;\n }\n }\n}"]}
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env node
2
+ import{readFileSync as bt}from"fs";import{z as l}from"zod";import{readFileSync as X,existsSync as Z}from"fs";import{homedir as q}from"os";import{join as P,dirname as Q}from"path";import{parse as tt}from"yaml";var I=l.object({cacheTTL:l.number().default(300),cacheDir:l.string().default("/tmp/.claude-statusline-cache"),maxLength:l.number().default(1e3),noEmoji:l.boolean().default(!1),noGitStatus:l.boolean().default(!1),noContextWindow:l.boolean().default(!1),envContext:l.boolean().default(!1),truncate:l.boolean().default(!1),softWrap:l.boolean().default(!1),noSoftWrap:l.boolean().default(!1),forceWidth:l.number().optional(),debugWidth:l.boolean().default(!1),rightMargin:l.number().default(15),symbols:l.object({git:l.string().default("\uF418"),model:l.string().default("\u{F06A9}"),contextWindow:l.string().default("\u26A1\uFE0E"),staged:l.string().default("+"),conflict:l.string().default("\xD7"),stashed:l.string().default("\u2691"),ahead:l.string().default("\u21E1"),behind:l.string().default("\u21E3"),diverged:l.string().default("\u21D5"),renamed:l.string().default("\xBB"),deleted:l.string().default("\u2718")}).default({}),asciiSymbols:l.object({git:l.string().default("@"),model:l.string().default("*"),contextWindow:l.string().default("#"),staged:l.string().default("+"),conflict:l.string().default("C"),stashed:l.string().default("$"),ahead:l.string().default("A"),behind:l.string().default("B"),diverged:l.string().default("D"),renamed:l.string().default(">"),deleted:l.string().default("X")}).default({})}),et=I.parse({}),rt=["claude-statusline.json","claude-statusline.yaml"];function O(r=process.cwd()){let t={...et};return t={...t,...nt(r)},t={...t,...ot()},I.parse(t)}function nt(r){let t=[r,Q(r),P(q(),".claude")];for(let e of t)for(let n of rt){let o=P(e,n);if(Z(o))try{let a=X(o,"utf-8");if(n.endsWith(".json"))return JSON.parse(a);if(n.endsWith(".yaml"))return tt(a)}catch{}}return{}}function ot(){let r={};if(process.env.CLAUDE_CODE_STATUSLINE_NO_EMOJI==="1"&&(r.noEmoji=!0),process.env.CLAUDE_CODE_STATUSLINE_NO_GITSTATUS==="1"&&(r.noGitStatus=!0),process.env.CLAUDE_CODE_STATUSLINE_NO_CONTEXT_WINDOW==="1"&&(r.noContextWindow=!0),process.env.CLAUDE_CODE_STATUSLINE_ENV_CONTEXT==="1"&&(r.envContext=!0),process.env.CLAUDE_CODE_STATUSLINE_TRUNCATE==="1"&&(r.truncate=!0),process.env.CLAUDE_CODE_STATUSLINE_SOFT_WRAP==="1"&&(r.softWrap=!0),process.env.CLAUDE_CODE_STATUSLINE_NO_SOFT_WRAP==="1"&&(r.noSoftWrap=!0),process.env.CLAUDE_CODE_STATUSLINE_FORCE_WIDTH){let t=parseInt(process.env.CLAUDE_CODE_STATUSLINE_FORCE_WIDTH,10);!isNaN(t)&&t>0&&(r.forceWidth=t)}return process.env.CLAUDE_CODE_STATUSLINE_DEBUG_WIDTH==="1"&&(r.debugWidth=!0),process.env.CLAUDE_CODE_STATUSLINE_CACHE_DIR&&(r.cacheDir=process.env.CLAUDE_CODE_STATUSLINE_CACHE_DIR),r}var st=4096,it=[/\.\./,/\.\.\\/,/\[/,/;/,/&/,/</,/>/,/`/];function R(r,t){if(!r||typeof r!="string")return!1;let e=r.trimEnd();if(e.length===0||e.length>t.maxLength||!e.includes("{")||!e.includes("}"))return!1;let n=(e.match(/"/g)||[]).length;if(n===0||n%2!==0)return!1;try{JSON.parse(e)}catch{return!1}return!0}function at(r){if(!r||typeof r!="string"||r.length>st)return!1;for(let t of it)if(t.test(r))return!1;if(r.includes("${")||r.includes("`")||r.includes("$("))return!1;try{let t=r.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 e=t.charAt(0).toUpperCase();if(e<"C"||e>"Z")return!1}}catch{return!1}return!0}async function W(r){if(!at(r))return!1;try{let{access:t}=await import("fs/promises"),{constants:e}=await import("fs");return await t(r,e.R_OK),!0}catch{return!1}}import{readFile as $,writeFile as x,mkdir as ct}from"fs/promises";import{existsSync as A}from"fs";import{join as C}from"path";var w=class{config;constructor(t){this.config=t}async ensureCacheDir(){try{await ct(this.config.cacheDir,{recursive:!0})}catch{}}getCachePath(t){return C(this.config.cacheDir,t)}getTimestampPath(t){return C(this.config.cacheDir,`${t}.time`)}async get(t,e=this.config.cacheTTL){let n=this.getCachePath(t),o=this.getTimestampPath(t);try{if(!A(n)||!A(o))return null;let a=await $(o,"utf-8"),s=parseInt(a.trim(),10);if(isNaN(s)||Math.floor(Date.now()/1e3)-s>=e)return null;let d=await $(n,"utf-8");try{return JSON.parse(d)}catch{return d}}catch{return null}}async set(t,e){let n=this.getCachePath(t),o=this.getTimestampPath(t);try{await this.ensureCacheDir();let a;typeof e=="string"?a=e:a=JSON.stringify(e);let s=Math.floor(Date.now()/1e3);return await Promise.all([x(n,a,"utf-8"),x(o,s.toString(),"utf-8")]),!0}catch{return!1}}async has(t,e=this.config.cacheTTL){return await this.get(t,e)!==null}async delete(t){let e=this.getCachePath(t),n=this.getTimestampPath(t);try{let{unlink:o}=await import("fs/promises");return await Promise.allSettled([o(e),o(n)]),!0}catch{return!1}}async clear(){try{let{readdir:t,unlink:e}=await import("fs/promises"),n=await t(this.config.cacheDir);return await Promise.allSettled(n.map(o=>e(C(this.config.cacheDir,o)))),!0}catch{return!1}}async getStats(){try{let{readdir:t,stat:e}=await import("fs/promises"),n=await t(this.config.cacheDir),o=0,a=0;for(let s of n)if(!s.endsWith(".time")){a++;let i=C(this.config.cacheDir,s);try{let c=await e(i);o+=c.size}catch{}}return{total:a,size:o}}catch{return{total:0,size:0}}}},b={NODE_VERSION:"node_version",PYTHON_VERSION:"python_version",PYTHON3_VERSION:"python3_version",DOCKER_VERSION:"docker_version",GIT_REMOTE_URL:r=>`git_remote_${Buffer.from(r).toString("base64")}`,GIT_BRANCH:r=>`git_branch_${Buffer.from(r).toString("base64")}`};async function E(r,t,e,n=[],o=300){let a=await r.get(t,o);if(a!==null)return a;try{let{exec:s}=await import("child_process"),{promisify:i}=await import("util"),c=i(s),{stdout:u}=await c(`${e} ${n.join(" ")}`,{timeout:5e3,encoding:"utf-8"}),d=u.trim();return d&&await r.set(t,d),d}catch{return null}}import{spawn as lt}from"child_process";async function g(r,t={}){return new Promise((e,n)=>{let o=lt("git",r,{cwd:t.cwd||process.cwd(),stdio:["ignore","pipe","pipe"],timeout:t.timeout||5e3}),a="",s="";o.stdout.on("data",i=>{a+=i.toString()}),o.stderr.on("data",i=>{s+=i.toString()}),o.on("close",i=>{i===0?e(a):n(new Error(`Git command failed with code ${i}: ${s||a}`))}),o.on("error",i=>{n(new Error(`Failed to execute git command: ${i.message}`))})})}async function F(r){try{return await g(["rev-parse","--git-dir"],r?{cwd:r}:{}),!0}catch{return!1}}async function k(r){let t=r?{cwd:r}:{};try{let n=(await g(["branch","--show-current"],t)).trim();if(n)return n}catch{}try{let n=(await g(["rev-parse","--abbrev-ref","HEAD"],t)).trim();if(n&&n!=="HEAD")return n}catch{}try{let n=(await g(["branch","--no-color"],t)).split(`
3
+ `);for(let o of n)if(o.startsWith("* "))return o.substring(2).trim()}catch{}return null}async function M(r){return g(["status","--porcelain"],r?{cwd:r}:{})}async function U(r){try{return g(["stash","list"],r?{cwd:r}:{})}catch{return""}}async function G(r){try{return(await g(["rev-parse","--abbrev-ref","@{u}"],r?{cwd:r}:{})).trim()}catch{return""}}async function L(r){try{let n=(await g(["rev-list","--count","--left-right","@{u}...HEAD"],r?{cwd:r}:{})).trim().split(" ");if(n.length===2){let o=parseInt(n[0]||"0",10);return{ahead:parseInt(n[1]||"0",10),behind:o}}}catch{}return{ahead:0,behind:0}}var ut={stashed:0,staged:0,modified:0,untracked:0,renamed:0,deleted:0,conflicts:0,ahead:0,behind:0,diverged:!1},T=class{config;cache;constructor(t,e){this.config=t,this.cache=e}async getGitInfo(t){if(this.config.noGitStatus)return null;try{if(!await F(t))return null;let n=await this.getCurrentBranch(t);if(!n)return null;let o=await this.getGitIndicators(t);return{branch:n,indicators:o}}catch{return null}}async getCurrentBranch(t){let e=`${b.GIT_BRANCH(t)}_current`,n=await this.cache.get(e,60);if(n)return n;try{let o=await k(t);return o?(await this.cache.set(e,o),o):null}catch{return null}}async getGitIndicators(t){let e={...ut};try{let o=(await M(t)).split(`
4
+ `).map(i=>i.trimEnd()).filter(i=>i.length>0);for(let i of o){if(i.length<2)continue;let c=i.charAt(0),u=i.charAt(1);if(c==="U"||u==="U"||c==="A"&&u==="A"||c==="D"&&u==="D")e.conflicts++;else if(c==="?"&&u==="?")e.untracked++;else{switch(c){case"M":e.staged++;break;case"A":e.staged++;break;case"D":e.deleted++;break;case"R":e.renamed++;break;case"C":e.staged++;break}switch(u){case"M":e.modified++;break;case"D":e.deleted++;break;case"R":e.renamed++;break}}}e.stashed=await this.getStashedCount(t);let{ahead:a,behind:s}=await this.getAheadBehind(t);e.ahead=a,e.behind=s,e.diverged=a>0&&s>0}catch{}return e}async getStashedCount(t){try{return(await U(t)).trim().split(`
5
+ `).filter(n=>n.trim().length>0).length}catch{return 0}}async getAheadBehind(t){try{return await G(t)?await L(t):{ahead:0,behind:0}}catch{}return{ahead:0,behind:0}}formatIndicators(t,e){let n=[],o=["stashed","renamed","modified","staged","untracked","deleted","conflicts"];return t.stashed>0&&n.push(e.stashed),t.renamed>0&&n.push(e.renamed),t.modified>0&&n.push("!"),t.staged>0&&n.push(e.staged),t.untracked>0&&n.push("?"),t.deleted>0&&n.push(e.deleted),t.conflicts>0&&n.push(e.conflict),t.diverged?n.push(e.diverged):(t.ahead>0&&n.push(e.ahead),t.behind>0&&n.push(e.behind)),n.join("")}formatGitStatus(t,e){let n=this.formatIndicators(t.indicators,e),o=(this.config.noEmoji,e.git);return n?` ${o} ${t.branch} [${n}]`:` ${o} ${t.branch}`}};var B=new Map,dt=1,H={git:"@",model:"*",contextWindow:"#",staged:"+",conflict:"C",stashed:"$",ahead:"A",behind:"B",diverged:"D",renamed:">",deleted:"X"},ft={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 j(r){let t=process.env.NERD_FONT+"|"+process.env.TERM_PROGRAM+"|"+process.env.TERM,e=`${dt}:${r.noEmoji?"ascii":"nerd"}:${t}`,n=B.get(e);if(n&&Date.now()-n.timestamp<6e4)return n.symbols;let o;return r.noEmoji?o={...H,...r.symbols,...r.asciiSymbols}:(await mt()).hasNerdFont?o={...ft,...r.symbols}:o={...H,...r.asciiSymbols},B.set(e,{symbols:o,timestamp:Date.now()}),o}async function mt(){let r={hasNerdFont:!1,terminal:"",font:"",method:""};if(process.env.NERD_FONT==="1")return r.hasNerdFont=!0,r.method="NERD_FONT env var",r;let t=process.env.TERM_PROGRAM,e=process.env.TERM;if(t&&(r.terminal=t,r.method="TERM_PROGRAM detection",["vscode","ghostty","wezterm","iterm"].includes(t))||e&&(r.terminal=e,r.method="TERM detection",["alacritty","kitty","wezterm","ghostty","xterm-256color"].includes(e)))return r.hasNerdFont=!0,r;let n=await ht();if(n.hasNerdFont)return r.hasNerdFont=!0,r.font=n.font,r.method="font list detection",r;let o=await gt();return o.hasNerdFont?(r.hasNerdFont=!0,r.font=o.font,r.method="installation detection",r):pt().hasNerdFont?(r.hasNerdFont=!0,r.method="environment detection",r):((await yt()).hasNerdFont&&(r.hasNerdFont=!0,r.method="platform-specific detection"),r)}async function ht(){try{let{exec:r}=await import("child_process"),{promisify:t}=await import("util"),e=t(r),n="",o=process.platform;if(o==="linux"?n="fc-list":o==="darwin"&&(n="system_profiler SPFontsDataType 2>/dev/null || system_profiler SPFontsDataType"),n){let{stdout:a}=await e(n,{timeout:3e3,encoding:"utf-8"}),s=[/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 s)if(i.test(a)){let c=a.match(/([^:\n]*)(?=\s*(nerd|symbols))/i);return{hasNerdFont:!0,font:(c?c[1]:"Nerd Font")?.trim()||"Nerd Font"}}}}catch{}return{hasNerdFont:!1,font:""}}async function gt(){try{let{access:r,readdir:t}=await import("fs/promises"),{homedir:e}=await import("os"),n=process.platform,o=[];n==="darwin"?o.push(`${e()}/Library/Fonts`,"/System/Library/Fonts","/Library/Fonts"):n==="linux"&&o.push(`${e()}/.local/share/fonts`,`${e()}/.fonts`,"/usr/share/fonts","/usr/local/share/fonts");let a=["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 s of o)try{await r(s);let i=await t(s);for(let c of i){let u=c.toLowerCase();for(let d of a)if(u.includes(d))return{hasNerdFont:!0,font:c}}}catch{}}catch{}return{hasNerdFont:!1,font:""}}function pt(){let r=["POWERLINE_COMMAND","NERDFONTS","FONT_FAMILY"];for(let t of r){let e=process.env[t];if(e&&e.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 yt(){if(process.platform==="darwin")try{let{exec:t}=await import("child_process"),{promisify:e}=await import("util"),n=e(t),{stdout:o}=await n("brew list | grep -i font",{timeout:2e3,encoding:"utf-8"});if(o.includes("nerd"))return{hasNerdFont:!0}}catch{}return{hasNerdFont:!1}}function V(r){return{node:"\uE718",python:"\uE235",docker:"\uF308",git:r.git,model:r.model}}async function _(r){if(r.forceWidth&&r.forceWidth>0)return r.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 e=await z("tput",["cols"]);if(e)return e;let n=await K();if(n)return n;let o=process.env.CLAUDE_CODE_TERMINAL_WIDTH;if(o){let i=parseInt(o,10);if(!isNaN(i)&&i>0)return i}let a=process.env.TERM_PROGRAM,s=process.env.TERM;return a==="vscode"&&process.env.VSCODE_PID||["ghostty","wezterm","iterm"].includes(a||"")||s&&["alacritty","kitty","wezterm","ghostty","xterm-256color"].includes(s)||process.env.WT_SESSION||process.env.WT_PROFILE_ID?120:80}async function z(r,t){try{let{exec:e}=await import("child_process"),{promisify:n}=await import("util"),o=n(e),{stdout:a}=await o(`${r} ${t.join(" ")}`,{timeout:1e3,encoding:"utf-8"}),s=parseInt(a.trim(),10);if(!isNaN(s)&&s>0)return s}catch{}return null}async function K(){try{let{exec:r}=await import("child_process"),{promisify:t}=await import("util"),e=t(r),{stdout:n}=await e("stty size",{timeout:1e3,encoding:"utf-8"}),o=n.trim().split(" ");if(o.length===2){let a=parseInt(o[1]||"0",10);if(!isNaN(a)&&a>0)return a}}catch{}return null}async function Y(r){if(!r.debugWidth)return;process.stdout.columns;let t=process.env.COLUMNS,e=await z("tput",["cols"]),n=await K(),o=await _(r)}function D(r,t){return r.length<=t?r:t<4?"..":`${r.substring(0,t-2)}..`}function J(r,t,e,n){if(r.length+t.length<=e)return"";let o=e-t.length-2;if(o>=5)return`${r.substring(0,o)}..${t}`;let a="",s=t.match(/\[([^\]]+)\]/);s&&(a=s[1]||"");let i=e-a.length-8;if(i>=8){let c=t.substring(0,Math.min(t.length,i));return`${r.substring(0,4)}..${c}..${a?` [${a}]`:""}`}return`${r.substring(0,e)}..`}var v=class{config;cache;constructor(t,e){this.config=t,this.cache=e}async getEnvironmentInfo(){if(!this.config.envContext)return null;let t={},[e,n,o]=await Promise.allSettled([this.getNodeVersion(),this.getPythonVersion(),this.getDockerVersion()]);return e.status==="fulfilled"&&e.value&&(t.node=e.value),n.status==="fulfilled"&&n.value&&(t.python=n.value),o.status==="fulfilled"&&o.value&&(t.docker=o.value),Object.keys(t).length===0?null:t}async getNodeVersion(){let t=b.NODE_VERSION,e=this.config.cacheTTL*96,n=await this.cache.get(t,e);if(n)return n;try{let o=await E(this.cache,t,"node",["--version"],e);return o?o.replace(/^v/,"").trim():null}catch{return null}}async getPythonVersion(){let t=b.PYTHON3_VERSION,e=b.PYTHON_VERSION,n=this.config.cacheTTL*96;try{let o=await E(this.cache,t,"python3",["--version"],n);if(o){let a=o.match(/(\d+\.\d+\.\d+)/);if(a)return a[1]||null}}catch{}try{let o=await E(this.cache,e,"python",["--version"],n);if(o){let a=o.match(/(\d+\.\d+\.\d+)/);if(a)return a[1]||null}}catch{}return null}async getDockerVersion(){let t="docker_version";try{let e=await E(this.cache,t,"docker",["--version"],this.config.cacheTTL*96);if(e){let n=e.match(/Docker version (\d+\.\d+\.\d+)/);if(n)return n[1]||null}return null}catch{return null}}formatEnvironmentInfo(t,e){let n=[];return t.node&&n.push(`${e.node}${t.node}`),t.python&&n.push(`${e.python}${t.python}`),t.docker&&n.push(`${e.docker}${t.docker}`),n.join(" ")}async getAdditionalTools(){return{}}async isToolAvailable(t){try{let{exec:e}=await import("child_process"),{promisify:n}=await import("util");return await n(e)(`command -v ${t}`,{timeout:2e3}),!0}catch{return!1}}getShellEnvironment(){let t=process.env.SHELL||"unknown",e;return t.includes("bash")?e=process.env.BASH_VERSION:t.includes("zsh")?e=process.env.ZSH_VERSION:t.includes("fish")&&(e=process.env.FISH_VERSION),e?{shell:t,shellVersion:e}:{shell:t}}getOSInfo(){let t=process.platform,e=process.arch,n=process.env.OSTYPE||process.env.OS;return n?{platform:t,arch:e,release:n}:{platform:t,arch:e}}},N=class{symbols;constructor(t){this.symbols=t}format(t,e="compact"){switch(e){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 e=[];return t.node&&e.push(`Node${t.node}`),t.python&&e.push(`Py${t.python}`),t.docker&&e.push(`Docker${t.docker}`),e.join(" ")}formatVerbose(t){let e=[];return t.node&&e.push(`Node.js v${t.node}`),t.python&&e.push(`Python ${t.python}`),t.docker&&e.push(`Docker ${t.docker}`),e.join(" \u2022 ")}formatMinimal(t){let e=[];if(t.node){let n=t.node.split(".")[0];e.push(`N${n}`)}if(t.python){let n=t.python.split(".")[0];e.push(`P${n}`)}if(t.docker){let n=t.docker.split(".")[0];e.push(`D${n}`)}return e.join(" ")}formatWithIcons(t){let e=[];return t.node&&e.push(`${this.symbols.node}${t.node}`),t.python&&e.push(`${this.symbols.python}${t.python}`),t.docker&&e.push(`${this.symbols.docker}${t.docker}`),e.join(" ")}};async function Et(){try{let r=O(),t=new w(r),e=new T(r,t),n=new v(r,t);await Y(r);let o=await St();R(JSON.stringify(o),r)||process.exit(1);let{fullDir:a,modelName:s,contextWindow:i}=Ct(o);(!a||!s)&&process.exit(1),await W(a)||process.exit(1);let u=[e.getGitInfo(a),n.getEnvironmentInfo(),j(r)],d;r.truncate&&u.push(_(r));let f=await Promise.all(u),[h,p,m]=f;r.truncate&&f.length>3&&(d=f[3]);let y=await wt({fullDir:a,modelName:s,contextWindow:i,gitInfo:h,envInfo:p,symbols:m,...d&&{terminalWidth:d},config:r,gitOps:e});process.stdout.write(y)}catch{process.exit(1)}}async function St(){try{let r=bt(0,"utf-8");return JSON.parse(r.trim())}catch(r){throw new Error(`Failed to read or parse input: ${r instanceof Error?r.message:String(r)}`)}}function Ct(r){let t=r.workspace?.current_dir||"",e=r.model?.display_name||"Unknown",n=r.context_window;return{fullDir:t,modelName:e,contextWindow:n}}async function wt(r){let{fullDir:t,modelName:e,contextWindow:n,gitInfo:o,envInfo:a,symbols:s,terminalWidth:i,config:c,gitOps:u}=r,d=t.split("/").pop()||t.split("\\").pop()||"project",f="";o&&(f=u.formatGitStatus(o,s));let h="";if(a){let S=V(s);h=` ${new N(S).formatWithIcons(a)}`}let p="";if(n&&!c.noContextWindow){let S=Tt(n);S!==null&&(p=` ${s.contextWindow}${S}%`)}let m=`${s.model}${e}${h}${p}`,y=`${d}${f} ${m}`;return c.truncate&&(i||process.exit(1),y=vt({statusline:y,projectName:d,gitStatus:f,modelString:m,terminalWidth:i,config:c,symbols:s})),y}function Tt(r){try{let{current_usage:t,context_window_size:e}=r;if(!e||e===0)return null;if(!t)return 0;let n=t.input_tokens+(t.cache_creation_input_tokens||0)+(t.cache_read_input_tokens||0),o=Math.round(n/e*100);return Math.max(0,Math.min(100,o))}catch{return 0}}function vt(r){let{statusline:t,projectName:e,gitStatus:n,modelString:o,terminalWidth:a,config:s}=r,i=Math.max(a-s.rightMargin,30),c=`${e}${n}`;if(t.length<=i)return t;if(c.length+1<=i)if(s.noSoftWrap){let d=i-c.length-1,f=D(o,d);return`${c} ${f}`}else{let d=i-c.length-1;if(/^[󰚩*]/.test(o)&&o.length>d)return`${c}
6
+ ${o}`;let h=Nt(o,d);return`${c} ${h}`}let u=J(e,n,i,s);return u||D(t,i)}function Nt(r,t){if(r.length<=t)return r;if(/^[󰚩*]/.test(r))return _t(r,t);let n=!1,o=Array.from(r),a=0,s=o.length,i=-1;for(let m=0;m<o.length;m++)if(o[m]===" "&&(i=m),a++,a>t){i>=0?s=i:s=m,n=i>=0;break}if(s>=o.length)return r;let c=o.length>0?o[0]:"",u=c&&c!==" "&&Buffer.byteLength(c,"utf8")>1;if(!n&&t-a>-3&&!u)return r;if(u&&s<=2&&t>=3)for(let m=2;m<Math.min(o.length,t);m++){s=m,n=!0;break}let d=o.slice(0,s),f=o.slice(s);f.length>0&&f[0]===" "&&f.shift();let h=d.join(""),p=f.join("");return p?`${h}
7
+ ${p}`:h}function _t(r,t){if(r.length<=t)return r;let e=Array.from(r),n=[];for(let s=0;s<e.length;s++)e[s]===" "&&n.push(s);let o=-1;for(let s=0;s<e.length;s++){let i=e[s];if(i==="\u26A1"||i==="#"){o=s;break}}if(o>0){let s=e.findIndex((i,c)=>c<o&&i===" ");if(s>0){let i=e.slice(s+1).join("");if(i.length<=t)return`${e.slice(0,s).join("")}
8
+ ${i}`}}if(n.length>0){let s=n[0];if(s!==void 0&&s>1){let i=e.slice(0,s).join("");if(i.length<=t){let c=e.slice(s+1).join("");return c?`${i}
9
+ ${c}`:i}}}let a=Math.min(5,t);if(a>=3&&e.length>a){let s=Math.min(t,e.length-1),i=e.slice(0,s).join(""),c=e.slice(s).join("");return c?`${i}
10
+ ${c}`:i}if(t>=3){let s=e.slice(0,t).join(""),i=e.slice(t).join("");return i?`${s}
11
+ ${i}`:s}return r}import.meta.url===`file://${process.argv[1]}`&&Et();export{Et as main};
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Claude Statusline - TypeScript v2.0
4
+ * Main entry point
5
+ */
6
+ /**
7
+ * Main execution function
8
+ */
9
+ export declare function main(): Promise<void>;