@owloops/claude-powerline 1.24.4 → 1.25.1

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 (46) hide show
  1. package/dist/browser.d.ts +676 -0
  2. package/dist/browser.js +3 -0
  3. package/dist/index.mjs +10 -10
  4. package/package.json +9 -1
  5. package/plugin/templates/config-tui-compact.json +4 -4
  6. package/plugin/templates/config-tui-full.json +5 -5
  7. package/plugin/templates/config-tui-standard.json +5 -5
  8. package/src/browser.ts +203 -0
  9. package/src/config/defaults.ts +79 -0
  10. package/src/config/loader.ts +462 -0
  11. package/src/index.ts +90 -0
  12. package/src/powerline.ts +904 -0
  13. package/src/segments/block.ts +31 -0
  14. package/src/segments/context.ts +221 -0
  15. package/src/segments/git.ts +492 -0
  16. package/src/segments/index.ts +25 -0
  17. package/src/segments/metrics.ts +175 -0
  18. package/src/segments/pricing.ts +454 -0
  19. package/src/segments/renderer.ts +796 -0
  20. package/src/segments/session.ts +207 -0
  21. package/src/segments/tmux.ts +35 -0
  22. package/src/segments/today.ts +191 -0
  23. package/src/themes/dark.ts +52 -0
  24. package/src/themes/gruvbox.ts +52 -0
  25. package/src/themes/index.ts +131 -0
  26. package/src/themes/light.ts +52 -0
  27. package/src/themes/nord.ts +52 -0
  28. package/src/themes/rose-pine.ts +52 -0
  29. package/src/themes/tokyo-night.ts +52 -0
  30. package/src/tui/grid.ts +712 -0
  31. package/src/tui/index.ts +4 -0
  32. package/src/tui/layouts.ts +285 -0
  33. package/src/tui/primitives.ts +175 -0
  34. package/src/tui/renderer.ts +206 -0
  35. package/src/tui/sections.ts +1080 -0
  36. package/src/tui/types.ts +181 -0
  37. package/src/utils/budget.ts +47 -0
  38. package/src/utils/cache.ts +247 -0
  39. package/src/utils/claude.ts +489 -0
  40. package/src/utils/color-support.ts +118 -0
  41. package/src/utils/colors.ts +120 -0
  42. package/src/utils/constants.ts +176 -0
  43. package/src/utils/formatters.ts +160 -0
  44. package/src/utils/logger.ts +5 -0
  45. package/src/utils/terminal-width.ts +117 -0
  46. package/src/utils/terminal.ts +11 -0
@@ -0,0 +1,31 @@
1
+ import { debug } from "../utils/logger";
2
+ import { minutesUntilReset } from "../utils/formatters";
3
+ import type { ClaudeHookData } from "../utils/claude";
4
+
5
+ export interface BlockInfo {
6
+ nativeUtilization: number;
7
+ timeRemaining: number;
8
+ }
9
+
10
+ export class BlockProvider {
11
+ async getActiveBlockInfo(
12
+ hookData?: ClaudeHookData,
13
+ ): Promise<BlockInfo | null> {
14
+ const fiveHour = hookData?.rate_limits?.five_hour;
15
+ if (!fiveHour) {
16
+ debug("Block segment: No native rate_limits data available");
17
+ return null;
18
+ }
19
+
20
+ const timeRemaining = minutesUntilReset(fiveHour.resets_at);
21
+
22
+ debug(
23
+ `Block segment: Using native rate_limits: ${fiveHour.used_percentage}%, resets in ${timeRemaining}m`,
24
+ );
25
+
26
+ return {
27
+ nativeUtilization: fiveHour.used_percentage,
28
+ timeRemaining,
29
+ };
30
+ }
31
+ }
@@ -0,0 +1,221 @@
1
+ import type { ParsedEntry, ClaudeHookData } from "../utils/claude";
2
+ import type { PowerlineConfig } from "../config/loader";
3
+
4
+ import { debug } from "../utils/logger";
5
+ import { parseJsonlFile } from "../utils/claude";
6
+
7
+ export interface ContextInfo {
8
+ totalTokens: number;
9
+ percentage: number;
10
+ usablePercentage: number;
11
+ contextLeftPercentage: number;
12
+ maxTokens: number;
13
+ usableTokens: number;
14
+ }
15
+
16
+ interface ContextUsageThresholds {
17
+ LOW: number;
18
+ MEDIUM: number;
19
+ }
20
+
21
+ export class ContextProvider {
22
+ private readonly thresholds: ContextUsageThresholds = {
23
+ LOW: 50,
24
+ MEDIUM: 80,
25
+ };
26
+ private readonly config: PowerlineConfig;
27
+
28
+ constructor(config: PowerlineConfig) {
29
+ this.config = config;
30
+ }
31
+
32
+ getContextUsageThresholds(): ContextUsageThresholds {
33
+ return this.thresholds;
34
+ }
35
+
36
+ private getContextLimit(modelId: string): number {
37
+ const modelLimits = this.config.modelContextLimits || { default: 200000 };
38
+ const modelType = this.getModelType(modelId);
39
+ return modelLimits[modelType] || modelLimits.default || 200000;
40
+ }
41
+
42
+ private getModelType(modelId: string): string {
43
+ const id = modelId.toLowerCase();
44
+
45
+ if (id.includes("sonnet")) {
46
+ return "sonnet";
47
+ }
48
+ if (id.includes("opus")) {
49
+ return "opus";
50
+ }
51
+
52
+ return "default";
53
+ }
54
+
55
+ private calculatePercentages(
56
+ totalTokens: number,
57
+ contextLimit: number,
58
+ autocompactBuffer: number = 33000,
59
+ ): Pick<
60
+ ContextInfo,
61
+ "percentage" | "usablePercentage" | "contextLeftPercentage" | "usableTokens"
62
+ > {
63
+ const percentage = Math.min(
64
+ 100,
65
+ Math.max(0, Math.round((totalTokens / contextLimit) * 100)),
66
+ );
67
+
68
+ const usableLimit = Math.max(1, contextLimit - autocompactBuffer);
69
+ const usablePercentage = Math.min(
70
+ 100,
71
+ Math.max(0, Math.round((totalTokens / usableLimit) * 100)),
72
+ );
73
+
74
+ const contextLeftPercentage = Math.max(0, 100 - usablePercentage);
75
+
76
+ return {
77
+ percentage,
78
+ usablePercentage,
79
+ contextLeftPercentage,
80
+ usableTokens: usableLimit,
81
+ };
82
+ }
83
+
84
+ /**
85
+ * Calculate context info from native Claude Code context_window data (preferred).
86
+ * Requires Claude Code 2.0.70+ with current_usage field.
87
+ */
88
+ calculateContextFromHookData(
89
+ hookData: ClaudeHookData,
90
+ autocompactBuffer: number = 33000,
91
+ ): ContextInfo | null {
92
+ const currentUsage = hookData.context_window?.current_usage;
93
+ if (!currentUsage) {
94
+ debug(
95
+ "No current_usage in hook data, falling back to transcript parsing",
96
+ );
97
+ return null;
98
+ }
99
+
100
+ const contextLimit = hookData.context_window?.context_window_size || 200000;
101
+ const totalTokens =
102
+ (currentUsage.input_tokens || 0) +
103
+ (currentUsage.cache_creation_input_tokens || 0) +
104
+ (currentUsage.cache_read_input_tokens || 0);
105
+
106
+ debug(
107
+ `Native current_usage: input=${currentUsage.input_tokens}, cache_create=${currentUsage.cache_creation_input_tokens}, cache_read=${currentUsage.cache_read_input_tokens}, total=${totalTokens} (limit: ${contextLimit})`,
108
+ );
109
+
110
+ const nativePct = hookData.context_window?.used_percentage;
111
+ const percentages = this.calculatePercentages(
112
+ totalTokens,
113
+ contextLimit,
114
+ autocompactBuffer,
115
+ );
116
+
117
+ if (nativePct != null) {
118
+ percentages.percentage = Math.round(nativePct);
119
+ debug(`Using native used_percentage: ${nativePct}%`);
120
+ }
121
+
122
+ return {
123
+ totalTokens,
124
+ maxTokens: contextLimit,
125
+ ...percentages,
126
+ };
127
+ }
128
+
129
+ /**
130
+ * Calculate context tokens by parsing the transcript file (fallback).
131
+ * Used for older Claude Code versions that don't provide context_window.
132
+ */
133
+ async calculateContextTokensFromTranscript(
134
+ transcriptPath: string,
135
+ modelId?: string,
136
+ autocompactBuffer: number = 33000,
137
+ ): Promise<ContextInfo | null> {
138
+ try {
139
+ debug(`Calculating context tokens from transcript: ${transcriptPath}`);
140
+
141
+ const parsedEntries = await parseJsonlFile(transcriptPath);
142
+
143
+ if (parsedEntries.length === 0) {
144
+ debug("No entries in transcript");
145
+ return null;
146
+ }
147
+
148
+ let mostRecentEntry: ParsedEntry | null = null;
149
+
150
+ for (let i = parsedEntries.length - 1; i >= 0; i--) {
151
+ const entry = parsedEntries[i];
152
+ if (!entry) continue;
153
+
154
+ if (!entry.message?.usage?.input_tokens) continue;
155
+ if (entry.isSidechain === true) continue;
156
+
157
+ mostRecentEntry = entry;
158
+ debug(
159
+ `Context segment: Found most recent entry at ${entry.timestamp.toISOString()}, stopping search`,
160
+ );
161
+ break;
162
+ }
163
+
164
+ if (mostRecentEntry?.message?.usage) {
165
+ const usage = mostRecentEntry.message.usage;
166
+ const totalTokens =
167
+ (usage.input_tokens || 0) +
168
+ (usage.cache_read_input_tokens || 0) +
169
+ (usage.cache_creation_input_tokens || 0);
170
+
171
+ const contextLimit = modelId ? this.getContextLimit(modelId) : 200000;
172
+
173
+ debug(
174
+ `Most recent main chain context: ${totalTokens} tokens (limit: ${contextLimit})`,
175
+ );
176
+
177
+ const percentages = this.calculatePercentages(
178
+ totalTokens,
179
+ contextLimit,
180
+ autocompactBuffer,
181
+ );
182
+
183
+ return {
184
+ totalTokens,
185
+ maxTokens: contextLimit,
186
+ ...percentages,
187
+ };
188
+ }
189
+
190
+ debug("No main chain entries with usage data found");
191
+ return null;
192
+ } catch (error) {
193
+ debug(
194
+ `Error reading transcript: ${error instanceof Error ? error.message : String(error)}`,
195
+ );
196
+ return null;
197
+ }
198
+ }
199
+
200
+ /**
201
+ * Get context info using native data if available, falling back to transcript parsing.
202
+ */
203
+ async getContextInfo(
204
+ hookData: ClaudeHookData,
205
+ autocompactBuffer: number = 33000,
206
+ ): Promise<ContextInfo | null> {
207
+ const nativeContext = this.calculateContextFromHookData(
208
+ hookData,
209
+ autocompactBuffer,
210
+ );
211
+ if (nativeContext) {
212
+ return nativeContext;
213
+ }
214
+
215
+ return this.calculateContextTokensFromTranscript(
216
+ hookData.transcript_path,
217
+ hookData.model?.id,
218
+ autocompactBuffer,
219
+ );
220
+ }
221
+ }