aiblueprint-cli 1.4.12 → 1.4.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/claude-code-config/scripts/.claude/commands/fix-on-my-computer.md +87 -0
- package/claude-code-config/scripts/CLAUDE.md +50 -0
- package/claude-code-config/scripts/{statusline/biome.json → biome.json} +5 -2
- package/claude-code-config/scripts/bun.lockb +0 -0
- package/claude-code-config/scripts/command-validator/CLAUDE.md +112 -0
- package/claude-code-config/scripts/command-validator/src/__tests__/validator.test.ts +62 -111
- package/claude-code-config/scripts/command-validator/src/cli.ts +5 -3
- package/claude-code-config/scripts/command-validator/src/lib/security-rules.ts +3 -4
- package/claude-code-config/scripts/command-validator/src/lib/types.ts +1 -0
- package/claude-code-config/scripts/command-validator/src/lib/validator.ts +47 -317
- package/claude-code-config/scripts/package.json +39 -0
- package/claude-code-config/scripts/statusline/CLAUDE.md +29 -7
- package/claude-code-config/scripts/statusline/README.md +89 -1
- package/claude-code-config/scripts/statusline/__tests__/context.test.ts +229 -0
- package/claude-code-config/scripts/statusline/__tests__/formatters.test.ts +108 -0
- package/claude-code-config/scripts/statusline/__tests__/statusline.test.ts +309 -0
- package/claude-code-config/scripts/statusline/data/.gitignore +8 -0
- package/claude-code-config/scripts/statusline/data/.gitkeep +0 -0
- package/claude-code-config/scripts/statusline/defaults.json +79 -0
- package/claude-code-config/scripts/statusline/docs/ARCHITECTURE.md +166 -0
- package/claude-code-config/scripts/statusline/fixtures/mock-transcript.jsonl +4 -0
- package/claude-code-config/scripts/statusline/fixtures/test-input.json +12 -2
- package/claude-code-config/scripts/statusline/src/index.ts +175 -24
- package/claude-code-config/scripts/statusline/src/lib/config-types.ts +104 -0
- package/claude-code-config/scripts/statusline/src/lib/config.ts +21 -0
- package/claude-code-config/scripts/statusline/src/lib/context.ts +32 -11
- package/claude-code-config/scripts/statusline/src/lib/formatters.ts +360 -22
- package/claude-code-config/scripts/statusline/src/lib/git.ts +100 -0
- package/claude-code-config/scripts/statusline/src/lib/menu-factories.ts +224 -0
- package/claude-code-config/scripts/statusline/src/lib/presets.ts +177 -0
- package/claude-code-config/scripts/statusline/src/lib/render-pure.ts +497 -0
- package/claude-code-config/scripts/statusline/src/lib/types.ts +11 -0
- package/claude-code-config/scripts/statusline/src/lib/utils.ts +15 -0
- package/claude-code-config/scripts/statusline/src/tests/spend-v2.test.ts +306 -0
- package/claude-code-config/scripts/statusline/statusline.config.json +79 -0
- package/claude-code-config/scripts/statusline/test-with-fixtures.ts +37 -0
- package/claude-code-config/scripts/tsconfig.json +27 -0
- package/claude-code-config/skills/claude-memory/SKILL.md +689 -0
- package/claude-code-config/skills/claude-memory/references/comprehensive-example.md +175 -0
- package/claude-code-config/skills/claude-memory/references/project-patterns.md +334 -0
- package/claude-code-config/skills/claude-memory/references/prompting-techniques.md +411 -0
- package/claude-code-config/skills/claude-memory/references/section-templates.md +347 -0
- package/claude-code-config/skills/create-slash-commands/SKILL.md +1110 -0
- package/claude-code-config/skills/create-slash-commands/references/arguments.md +273 -0
- package/claude-code-config/skills/create-slash-commands/references/patterns.md +947 -0
- package/claude-code-config/skills/create-slash-commands/references/prompt-examples.md +656 -0
- package/claude-code-config/skills/create-slash-commands/references/tool-restrictions.md +389 -0
- package/claude-code-config/skills/create-subagents/SKILL.md +425 -0
- package/claude-code-config/skills/create-subagents/references/context-management.md +567 -0
- package/claude-code-config/skills/create-subagents/references/debugging-agents.md +714 -0
- package/claude-code-config/skills/create-subagents/references/error-handling-and-recovery.md +502 -0
- package/claude-code-config/skills/create-subagents/references/evaluation-and-testing.md +374 -0
- package/claude-code-config/skills/create-subagents/references/orchestration-patterns.md +591 -0
- package/claude-code-config/skills/create-subagents/references/subagents.md +599 -0
- package/claude-code-config/skills/create-subagents/references/writing-subagent-prompts.md +513 -0
- package/package.json +1 -1
- package/claude-code-config/commands/apex.md +0 -109
- package/claude-code-config/commands/tasks/run-task.md +0 -220
- package/claude-code-config/commands/utils/watch-ci.md +0 -47
- package/claude-code-config/scripts/command-validator/biome.json +0 -29
- package/claude-code-config/scripts/command-validator/bun.lockb +0 -0
- package/claude-code-config/scripts/command-validator/package.json +0 -27
- package/claude-code-config/scripts/command-validator/vitest.config.ts +0 -7
- package/claude-code-config/scripts/hook-post-file.ts +0 -162
- package/claude-code-config/scripts/statusline/bun.lockb +0 -0
- package/claude-code-config/scripts/statusline/package.json +0 -19
- package/claude-code-config/scripts/statusline/statusline.config.ts +0 -25
- package/claude-code-config/scripts/validate-command.js +0 -712
- package/claude-code-config/scripts/validate-command.readme.md +0 -283
|
@@ -0,0 +1,497 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure statusline renderer - no I/O, no side effects
|
|
3
|
+
*
|
|
4
|
+
* ARCHITECTURE: Raw data in, formatted string out.
|
|
5
|
+
* ALL config decisions happen here, not in data preparation.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { StatuslineConfig } from "./config-types";
|
|
9
|
+
import {
|
|
10
|
+
colors,
|
|
11
|
+
formatCost,
|
|
12
|
+
formatDuration,
|
|
13
|
+
formatPath,
|
|
14
|
+
formatProgressBar,
|
|
15
|
+
formatResetTime,
|
|
16
|
+
formatTokens,
|
|
17
|
+
} from "./formatters";
|
|
18
|
+
|
|
19
|
+
const WEEKLY_HOURS = 168; // 7 days * 24 hours
|
|
20
|
+
const FIVE_HOUR_MINUTES = 300; // 5 hours * 60 minutes
|
|
21
|
+
|
|
22
|
+
// ─────────────────────────────────────────────────────────────
|
|
23
|
+
// RAW DATA TYPES - No pre-formatting, just raw values
|
|
24
|
+
// ─────────────────────────────────────────────────────────────
|
|
25
|
+
|
|
26
|
+
export interface GitChanges {
|
|
27
|
+
files: number;
|
|
28
|
+
added: number;
|
|
29
|
+
deleted: number;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface RawGitData {
|
|
33
|
+
branch: string;
|
|
34
|
+
dirty: boolean;
|
|
35
|
+
staged: GitChanges;
|
|
36
|
+
unstaged: GitChanges;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface UsageLimit {
|
|
40
|
+
utilization: number;
|
|
41
|
+
resets_at: string | null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface RawStatuslineData {
|
|
45
|
+
git: RawGitData | null;
|
|
46
|
+
path: string;
|
|
47
|
+
modelName: string;
|
|
48
|
+
cost: number;
|
|
49
|
+
durationMs: number;
|
|
50
|
+
contextTokens: number | null;
|
|
51
|
+
contextPercentage: number | null;
|
|
52
|
+
usageLimits?: {
|
|
53
|
+
five_hour: UsageLimit | null;
|
|
54
|
+
seven_day: UsageLimit | null;
|
|
55
|
+
};
|
|
56
|
+
periodCost?: number;
|
|
57
|
+
todayCost?: number;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Legacy interface for backwards compatibility
|
|
61
|
+
export interface StatuslineData {
|
|
62
|
+
branch: string;
|
|
63
|
+
dirPath: string;
|
|
64
|
+
modelName: string;
|
|
65
|
+
sessionCost: string;
|
|
66
|
+
sessionDuration: string;
|
|
67
|
+
contextTokens: number | null;
|
|
68
|
+
contextPercentage: number | null;
|
|
69
|
+
usageLimits?: {
|
|
70
|
+
five_hour: UsageLimit | null;
|
|
71
|
+
seven_day: UsageLimit | null;
|
|
72
|
+
};
|
|
73
|
+
periodCost?: number;
|
|
74
|
+
todayCost?: number;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// ─────────────────────────────────────────────────────────────
|
|
78
|
+
// FORMATTING - All config-aware formatting in one place
|
|
79
|
+
// ─────────────────────────────────────────────────────────────
|
|
80
|
+
|
|
81
|
+
function formatGitPart(
|
|
82
|
+
git: RawGitData | null,
|
|
83
|
+
config: StatuslineConfig["git"],
|
|
84
|
+
): string {
|
|
85
|
+
if (!git || !config.enabled) return "";
|
|
86
|
+
|
|
87
|
+
const parts: string[] = [];
|
|
88
|
+
|
|
89
|
+
if (config.showBranch) {
|
|
90
|
+
parts.push(colors.lightGray(git.branch));
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (git.dirty && config.showDirtyIndicator) {
|
|
94
|
+
// Append to branch name without space
|
|
95
|
+
if (parts.length > 0) {
|
|
96
|
+
parts[parts.length - 1] += colors.purple("*");
|
|
97
|
+
} else {
|
|
98
|
+
parts.push(colors.purple("*"));
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const changeParts: string[] = [];
|
|
103
|
+
|
|
104
|
+
if (config.showChanges) {
|
|
105
|
+
const totalAdded = git.staged.added + git.unstaged.added;
|
|
106
|
+
const totalDeleted = git.staged.deleted + git.unstaged.deleted;
|
|
107
|
+
if (totalAdded > 0) changeParts.push(colors.green(`+${totalAdded}`));
|
|
108
|
+
if (totalDeleted > 0) changeParts.push(colors.red(`-${totalDeleted}`));
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (config.showStaged && git.staged.files > 0) {
|
|
112
|
+
changeParts.push(colors.gray(`~${git.staged.files}`));
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (config.showUnstaged && git.unstaged.files > 0) {
|
|
116
|
+
changeParts.push(colors.yellow(`~${git.unstaged.files}`));
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (changeParts.length > 0) {
|
|
120
|
+
parts.push(changeParts.join(" "));
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return parts.join(" ");
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function formatSessionPart(
|
|
127
|
+
cost: number,
|
|
128
|
+
durationMs: number,
|
|
129
|
+
contextTokens: number | null,
|
|
130
|
+
contextPercentage: number | null,
|
|
131
|
+
maxTokens: number,
|
|
132
|
+
config: StatuslineConfig["session"],
|
|
133
|
+
): string {
|
|
134
|
+
// No context data yet - show placeholder
|
|
135
|
+
if (contextTokens === null || contextPercentage === null) {
|
|
136
|
+
return `${colors.gray("S:")} ${colors.gray("-")}`;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const items: string[] = [];
|
|
140
|
+
|
|
141
|
+
if (config.cost.enabled) {
|
|
142
|
+
const formattedCost = formatCost(cost, config.cost.format);
|
|
143
|
+
items.push(`${colors.gray("$")}${colors.dimWhite(formattedCost)}`);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (config.tokens.enabled) {
|
|
147
|
+
const formattedUsed = formatTokens(
|
|
148
|
+
contextTokens,
|
|
149
|
+
config.tokens.showDecimals,
|
|
150
|
+
);
|
|
151
|
+
if (config.tokens.showMax) {
|
|
152
|
+
const formattedMax = formatTokens(maxTokens, config.tokens.showDecimals);
|
|
153
|
+
items.push(`${formattedUsed}${colors.gray("/")}${formattedMax}`);
|
|
154
|
+
} else {
|
|
155
|
+
items.push(formattedUsed);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (config.percentage.enabled) {
|
|
160
|
+
const pctParts: string[] = [];
|
|
161
|
+
|
|
162
|
+
if (config.percentage.progressBar.enabled) {
|
|
163
|
+
pctParts.push(
|
|
164
|
+
formatProgressBar({
|
|
165
|
+
percentage: contextPercentage,
|
|
166
|
+
length: config.percentage.progressBar.length,
|
|
167
|
+
style: config.percentage.progressBar.style,
|
|
168
|
+
colorMode: config.percentage.progressBar.color,
|
|
169
|
+
background: config.percentage.progressBar.background,
|
|
170
|
+
}),
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (config.percentage.showValue) {
|
|
175
|
+
pctParts.push(
|
|
176
|
+
`${colors.lightGray(contextPercentage.toString())}${colors.gray("%")}`,
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (pctParts.length > 0) {
|
|
181
|
+
items.push(pctParts.join(" "));
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (config.duration.enabled) {
|
|
186
|
+
items.push(colors.gray(`(${formatDuration(durationMs)})`));
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (items.length === 0) return "";
|
|
190
|
+
|
|
191
|
+
const sep = config.infoSeparator
|
|
192
|
+
? ` ${colors.gray(config.infoSeparator)} `
|
|
193
|
+
: " ";
|
|
194
|
+
return `${colors.gray("S:")} ${items.join(sep)}`;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function formatLimitsPart(
|
|
198
|
+
fiveHour: UsageLimit | null,
|
|
199
|
+
periodCost: number,
|
|
200
|
+
config: StatuslineConfig["limits"],
|
|
201
|
+
): string {
|
|
202
|
+
if (!config.enabled || !fiveHour) return "";
|
|
203
|
+
|
|
204
|
+
const parts: string[] = [];
|
|
205
|
+
|
|
206
|
+
if (config.cost.enabled && periodCost > 0) {
|
|
207
|
+
parts.push(
|
|
208
|
+
`${colors.gray("$")}${colors.dimWhite(formatCost(periodCost, config.cost.format))}`,
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (config.percentage.enabled) {
|
|
213
|
+
if (config.percentage.progressBar.enabled) {
|
|
214
|
+
parts.push(
|
|
215
|
+
formatProgressBar({
|
|
216
|
+
percentage: fiveHour.utilization,
|
|
217
|
+
length: config.percentage.progressBar.length,
|
|
218
|
+
style: config.percentage.progressBar.style,
|
|
219
|
+
colorMode: config.percentage.progressBar.color,
|
|
220
|
+
background: config.percentage.progressBar.background,
|
|
221
|
+
}),
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
if (config.percentage.showValue) {
|
|
226
|
+
parts.push(
|
|
227
|
+
`${colors.lightGray(fiveHour.utilization.toString())}${colors.gray("%")}`,
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if (config.showPacingDelta && fiveHour.resets_at) {
|
|
233
|
+
const delta = calculateFiveHourDelta(
|
|
234
|
+
fiveHour.utilization,
|
|
235
|
+
fiveHour.resets_at,
|
|
236
|
+
);
|
|
237
|
+
parts.push(
|
|
238
|
+
`${colors.gray("(")}${formatPacingDelta(delta)}${colors.gray(")")}`,
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
if (config.showTimeLeft && fiveHour.resets_at) {
|
|
243
|
+
parts.push(colors.gray(`(${formatResetTime(fiveHour.resets_at)})`));
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return parts.length > 0 ? `${colors.gray("L:")} ${parts.join(" ")}` : "";
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function shouldShowWeekly(
|
|
250
|
+
config: StatuslineConfig["weeklyUsage"],
|
|
251
|
+
fiveHourUtilization: number | null,
|
|
252
|
+
): boolean {
|
|
253
|
+
if (config.enabled === true) return true;
|
|
254
|
+
if (config.enabled === false) return false;
|
|
255
|
+
if (config.enabled === "90%" && fiveHourUtilization !== null) {
|
|
256
|
+
return fiveHourUtilization >= 90;
|
|
257
|
+
}
|
|
258
|
+
return false;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function calculateWeeklyDelta(
|
|
262
|
+
utilization: number,
|
|
263
|
+
resetsAt: string | null,
|
|
264
|
+
): number {
|
|
265
|
+
if (!resetsAt) return 0;
|
|
266
|
+
|
|
267
|
+
const resetDate = new Date(resetsAt);
|
|
268
|
+
const now = new Date();
|
|
269
|
+
const diffMs = resetDate.getTime() - now.getTime();
|
|
270
|
+
const hoursRemaining = Math.max(0, diffMs / 3600000);
|
|
271
|
+
const timeElapsedPercent =
|
|
272
|
+
((WEEKLY_HOURS - hoursRemaining) / WEEKLY_HOURS) * 100;
|
|
273
|
+
|
|
274
|
+
return utilization - timeElapsedPercent;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function formatPacingDelta(delta: number): string {
|
|
278
|
+
const sign = delta >= 0 ? "+" : "";
|
|
279
|
+
const value = `${sign}${delta.toFixed(1)}%`;
|
|
280
|
+
|
|
281
|
+
if (delta > 5) return colors.green(value);
|
|
282
|
+
if (delta > 0) return colors.lightGray(value);
|
|
283
|
+
if (delta > -10) return colors.yellow(value);
|
|
284
|
+
return colors.red(value);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
function calculateFiveHourDelta(
|
|
288
|
+
utilization: number,
|
|
289
|
+
resetsAt: string | null,
|
|
290
|
+
): number {
|
|
291
|
+
if (!resetsAt) return 0;
|
|
292
|
+
|
|
293
|
+
const resetDate = new Date(resetsAt);
|
|
294
|
+
const now = new Date();
|
|
295
|
+
const diffMs = resetDate.getTime() - now.getTime();
|
|
296
|
+
const minutesRemaining = Math.max(0, diffMs / 60000);
|
|
297
|
+
const timeElapsedPercent =
|
|
298
|
+
((FIVE_HOUR_MINUTES - minutesRemaining) / FIVE_HOUR_MINUTES) * 100;
|
|
299
|
+
|
|
300
|
+
return utilization - timeElapsedPercent;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
function formatWeeklyPart(
|
|
304
|
+
sevenDay: UsageLimit | null,
|
|
305
|
+
fiveHourUtilization: number | null,
|
|
306
|
+
periodCost: number,
|
|
307
|
+
config: StatuslineConfig["weeklyUsage"],
|
|
308
|
+
): string {
|
|
309
|
+
if (!shouldShowWeekly(config, fiveHourUtilization) || !sevenDay) return "";
|
|
310
|
+
|
|
311
|
+
const parts: string[] = [];
|
|
312
|
+
|
|
313
|
+
if (config.cost.enabled && periodCost > 0) {
|
|
314
|
+
parts.push(
|
|
315
|
+
`${colors.gray("$")}${colors.dimWhite(formatCost(periodCost, config.cost.format))}`,
|
|
316
|
+
);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
if (config.percentage.enabled) {
|
|
320
|
+
if (config.percentage.progressBar.enabled) {
|
|
321
|
+
parts.push(
|
|
322
|
+
formatProgressBar({
|
|
323
|
+
percentage: sevenDay.utilization,
|
|
324
|
+
length: config.percentage.progressBar.length,
|
|
325
|
+
style: config.percentage.progressBar.style,
|
|
326
|
+
colorMode: config.percentage.progressBar.color,
|
|
327
|
+
background: config.percentage.progressBar.background,
|
|
328
|
+
}),
|
|
329
|
+
);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
if (config.percentage.showValue) {
|
|
333
|
+
parts.push(
|
|
334
|
+
`${colors.lightGray(sevenDay.utilization.toString())}${colors.gray("%")}`,
|
|
335
|
+
);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
if (config.showPacingDelta && sevenDay.resets_at) {
|
|
340
|
+
const delta = calculateWeeklyDelta(
|
|
341
|
+
sevenDay.utilization,
|
|
342
|
+
sevenDay.resets_at,
|
|
343
|
+
);
|
|
344
|
+
parts.push(
|
|
345
|
+
`${colors.gray("(")}${formatPacingDelta(delta)}${colors.gray(")")}`,
|
|
346
|
+
);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
if (config.showTimeLeft && sevenDay.resets_at) {
|
|
350
|
+
parts.push(colors.gray(`(${formatResetTime(sevenDay.resets_at)})`));
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
return parts.length > 0 ? `${colors.gray("W:")} ${parts.join(" ")}` : "";
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
function formatDailyPart(
|
|
357
|
+
todayCost: number,
|
|
358
|
+
config: StatuslineConfig["dailySpend"],
|
|
359
|
+
): string {
|
|
360
|
+
if (!config.cost.enabled || todayCost <= 0) return "";
|
|
361
|
+
return `${colors.gray("D:")} ${colors.gray("$")}${colors.dimWhite(formatCost(todayCost, config.cost.format))}`;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// ─────────────────────────────────────────────────────────────
|
|
365
|
+
// MAIN RENDER FUNCTION - Raw data + config = output
|
|
366
|
+
// ─────────────────────────────────────────────────────────────
|
|
367
|
+
|
|
368
|
+
export function renderStatuslineRaw(
|
|
369
|
+
data: RawStatuslineData,
|
|
370
|
+
config: StatuslineConfig,
|
|
371
|
+
): string {
|
|
372
|
+
const sep = colors.gray(config.separator);
|
|
373
|
+
const sections: string[] = [];
|
|
374
|
+
|
|
375
|
+
// Line 1: Git + Path + Model
|
|
376
|
+
const line1Parts: string[] = [];
|
|
377
|
+
|
|
378
|
+
const gitPart = formatGitPart(data.git, config.git);
|
|
379
|
+
if (gitPart) line1Parts.push(gitPart);
|
|
380
|
+
|
|
381
|
+
const pathPart = formatPath(data.path, config.pathDisplayMode);
|
|
382
|
+
line1Parts.push(colors.gray(pathPart));
|
|
383
|
+
|
|
384
|
+
const isSonnet = data.modelName.toLowerCase().includes("sonnet");
|
|
385
|
+
if (!isSonnet || config.showSonnetModel) {
|
|
386
|
+
line1Parts.push(colors.peach(data.modelName));
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
sections.push(line1Parts.join(` ${sep} `));
|
|
390
|
+
|
|
391
|
+
// Line 2: Session info
|
|
392
|
+
const sessionPart = formatSessionPart(
|
|
393
|
+
data.cost,
|
|
394
|
+
data.durationMs,
|
|
395
|
+
data.contextTokens,
|
|
396
|
+
data.contextPercentage,
|
|
397
|
+
config.context.maxContextTokens,
|
|
398
|
+
config.session,
|
|
399
|
+
);
|
|
400
|
+
if (sessionPart) sections.push(sessionPart);
|
|
401
|
+
|
|
402
|
+
// Limits
|
|
403
|
+
const limitsPart = formatLimitsPart(
|
|
404
|
+
data.usageLimits?.five_hour ?? null,
|
|
405
|
+
data.periodCost ?? 0,
|
|
406
|
+
config.limits,
|
|
407
|
+
);
|
|
408
|
+
if (limitsPart) sections.push(limitsPart);
|
|
409
|
+
|
|
410
|
+
// Weekly
|
|
411
|
+
const weeklyPart = formatWeeklyPart(
|
|
412
|
+
data.usageLimits?.seven_day ?? null,
|
|
413
|
+
data.usageLimits?.five_hour?.utilization ?? null,
|
|
414
|
+
data.periodCost ?? 0,
|
|
415
|
+
config.weeklyUsage,
|
|
416
|
+
);
|
|
417
|
+
if (weeklyPart) sections.push(weeklyPart);
|
|
418
|
+
|
|
419
|
+
// Daily
|
|
420
|
+
const dailyPart = formatDailyPart(data.todayCost ?? 0, config.dailySpend);
|
|
421
|
+
if (dailyPart) sections.push(dailyPart);
|
|
422
|
+
|
|
423
|
+
const output = sections.join(` ${sep} `);
|
|
424
|
+
|
|
425
|
+
if (config.oneLine) return output;
|
|
426
|
+
|
|
427
|
+
// Two-line mode: break after line1
|
|
428
|
+
const line1 = sections[0];
|
|
429
|
+
const rest = sections.slice(1).join(` ${sep} `);
|
|
430
|
+
return rest ? `${line1}\n${rest}` : line1;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// ─────────────────────────────────────────────────────────────
|
|
434
|
+
// LEGACY SUPPORT - For backwards compatibility with old data format
|
|
435
|
+
// ─────────────────────────────────────────────────────────────
|
|
436
|
+
|
|
437
|
+
export function renderStatusline(
|
|
438
|
+
data: StatuslineData,
|
|
439
|
+
config: StatuslineConfig,
|
|
440
|
+
): string {
|
|
441
|
+
// Convert legacy format to raw format
|
|
442
|
+
// Parse pre-formatted values back to raw (best effort)
|
|
443
|
+
const rawData: RawStatuslineData = {
|
|
444
|
+
git: parseGitFromBranch(data.branch),
|
|
445
|
+
path: data.dirPath.startsWith("~") ? data.dirPath : data.dirPath,
|
|
446
|
+
modelName: data.modelName,
|
|
447
|
+
cost: parseFloat(data.sessionCost.replace(/[$,]/g, "")) || 0,
|
|
448
|
+
durationMs: parseDurationToMs(data.sessionDuration),
|
|
449
|
+
contextTokens: data.contextTokens,
|
|
450
|
+
contextPercentage: data.contextPercentage,
|
|
451
|
+
usageLimits: data.usageLimits,
|
|
452
|
+
periodCost: data.periodCost,
|
|
453
|
+
todayCost: data.todayCost,
|
|
454
|
+
};
|
|
455
|
+
|
|
456
|
+
return renderStatuslineRaw(rawData, config);
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
// Helper to parse legacy branch string back to git data
|
|
460
|
+
function parseGitFromBranch(branch: string): RawGitData | null {
|
|
461
|
+
if (!branch) return null;
|
|
462
|
+
|
|
463
|
+
// Parse "main* +10 -5" format
|
|
464
|
+
const dirty = branch.includes("*");
|
|
465
|
+
const branchName =
|
|
466
|
+
branch.replace(/\*.*$/, "").replace(/\*/, "").trim() || "main";
|
|
467
|
+
|
|
468
|
+
const addMatch = branch.match(/\+(\d+)/);
|
|
469
|
+
const delMatch = branch.match(/-(\d+)/);
|
|
470
|
+
const added = addMatch ? parseInt(addMatch[1], 10) : 0;
|
|
471
|
+
const deleted = delMatch ? parseInt(delMatch[1], 10) : 0;
|
|
472
|
+
|
|
473
|
+
return {
|
|
474
|
+
branch: branchName,
|
|
475
|
+
dirty,
|
|
476
|
+
staged: {
|
|
477
|
+
files: 0,
|
|
478
|
+
added: Math.floor(added / 2),
|
|
479
|
+
deleted: Math.floor(deleted / 2),
|
|
480
|
+
},
|
|
481
|
+
unstaged: {
|
|
482
|
+
files: 0,
|
|
483
|
+
added: Math.ceil(added / 2),
|
|
484
|
+
deleted: Math.ceil(deleted / 2),
|
|
485
|
+
},
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
// Helper to parse "12m" or "1h 30m" back to ms
|
|
490
|
+
function parseDurationToMs(duration: string): number {
|
|
491
|
+
let ms = 0;
|
|
492
|
+
const hourMatch = duration.match(/(\d+)h/);
|
|
493
|
+
const minMatch = duration.match(/(\d+)m/);
|
|
494
|
+
if (hourMatch) ms += parseInt(hourMatch[1], 10) * 3600000;
|
|
495
|
+
if (minMatch) ms += parseInt(minMatch[1], 10) * 60000;
|
|
496
|
+
return ms || 720000; // Default 12 minutes
|
|
497
|
+
}
|
|
@@ -21,5 +21,16 @@ export interface HookInput {
|
|
|
21
21
|
total_lines_added: number;
|
|
22
22
|
total_lines_removed: number;
|
|
23
23
|
};
|
|
24
|
+
context_window?: {
|
|
25
|
+
total_input_tokens: number;
|
|
26
|
+
total_output_tokens: number;
|
|
27
|
+
context_window_size: number;
|
|
28
|
+
current_usage?: {
|
|
29
|
+
input_tokens: number;
|
|
30
|
+
output_tokens: number;
|
|
31
|
+
cache_creation_input_tokens?: number;
|
|
32
|
+
cache_read_input_tokens?: number;
|
|
33
|
+
};
|
|
34
|
+
};
|
|
24
35
|
exceeds_200k_tokens?: boolean;
|
|
25
36
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export function normalizeResetsAt(resetsAt: string): string {
|
|
2
|
+
try {
|
|
3
|
+
const date = new Date(resetsAt);
|
|
4
|
+
const minutes = date.getMinutes();
|
|
5
|
+
const roundedMinutes = Math.round(minutes / 5) * 5;
|
|
6
|
+
|
|
7
|
+
date.setMinutes(roundedMinutes);
|
|
8
|
+
date.setSeconds(0);
|
|
9
|
+
date.setMilliseconds(0);
|
|
10
|
+
|
|
11
|
+
return date.toISOString();
|
|
12
|
+
} catch {
|
|
13
|
+
return resetsAt;
|
|
14
|
+
}
|
|
15
|
+
}
|