@hanai/ccusage 18.0.5

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/dist/index.js ADDED
@@ -0,0 +1,3635 @@
1
+ #!/usr/bin/env node
2
+ import { $ as DEFAULT_RECENT_DAYS, B as pushBreakdownRows, D as calculateBurnRate, E as DEFAULT_SESSION_DURATION_HOURS, F as formatDateCompact, G as BLOCKS_COMPACT_WIDTH_THRESHOLD, H as getFileModifiedTime, I as formatModelsDisplayMultiline, K as BLOCKS_DEFAULT_TERMINAL_WIDTH, L as formatNumber, M as addEmptySeparatorRow, N as createUsageReportTable, O as filterRecentBlocks, P as formatCurrency, Q as DEFAULT_LOCALE, R as formatTotalsRow, U as unreachable, V as require_picocolors, W as _usingCtx, Y as CONFIG_FILE_NAME, Z as DEFAULT_CONTEXT_USAGE_THRESHOLDS, _ as loadWeeklyUsageData, at as try_, ct as inspectError, dt as isSuccess, et as DEFAULT_REFRESH_INTERVAL_SECONDS, f as loadDailyUsageData, ft as isFailure, g as loadSessionUsageById, h as loadSessionData, it as unwrap, j as ResponsiveTable, k as projectBlockUsage, lt as inspect, m as loadSessionBlockData, mt as __toESM, n as calculateContextTokens, nt as WEEK_DAYS, ot as pipe, p as loadMonthlyUsageData, pt as toArray, q as BLOCKS_WARNING_THRESHOLD, s as getClaudePaths, st as map$1, ut as succeed, z as formatUsageDataRow } from "./data-loader-C3ndvSiF.js";
3
+ import { D as maxValue, E as integer$1, F as string, I as transform, L as trim, M as parse$1, N as pipe$1, O as minValue, P as safeParse, R as union, T as flatten, d as filterDateSchema, k as number, n as SortOrders, t as CostModes, w as check, y as statuslineHookJsonSchema, z as getTotalTokens } from "./_types-BFL_bTNX.js";
4
+ import { n as createTotalsObject, t as calculateTotals } from "./calculate-cost-CKH-OC_c.js";
5
+ import { a as version, i as name, n as logger, r as description, t as log } from "./logger-PyTier1E.js";
6
+ import { n as printMismatchReport, t as detectMismatches } from "./debug-D_C3L8L7.js";
7
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
8
+ import a from "node:fs/promises";
9
+ import path, { join } from "node:path";
10
+ import process$1 from "node:process";
11
+ import { tmpdir } from "node:os";
12
+ import { fileURLToPath } from "node:url";
13
+ import { stripVTControlCharacters } from "node:util";
14
+ import { spawn } from "node:child_process";
15
+ import { on, once } from "node:events";
16
+ import { pipeline } from "node:stream/promises";
17
+ import * as readline from "node:readline/promises";
18
+ const DEFAULT_LOCALE$1 = "en-US";
19
+ const BUILT_IN_PREFIX = "_";
20
+ const ARG_PREFIX = "arg";
21
+ const BUILT_IN_KEY_SEPARATOR = ":";
22
+ const ANONYMOUS_COMMAND_NAME = "(anonymous)";
23
+ const NOOP = () => {};
24
+ const COMMON_ARGS = {
25
+ help: {
26
+ type: "boolean",
27
+ short: "h",
28
+ description: "Display this help message"
29
+ },
30
+ version: {
31
+ type: "boolean",
32
+ short: "v",
33
+ description: "Display this version"
34
+ }
35
+ };
36
+ const COMMAND_OPTIONS_DEFAULT = {
37
+ name: void 0,
38
+ description: void 0,
39
+ version: void 0,
40
+ cwd: void 0,
41
+ usageSilent: false,
42
+ subCommands: void 0,
43
+ leftMargin: 2,
44
+ middleMargin: 10,
45
+ usageOptionType: false,
46
+ usageOptionValue: true,
47
+ renderHeader: void 0,
48
+ renderUsage: void 0,
49
+ renderValidationErrors: void 0,
50
+ translationAdapterFactory: void 0
51
+ };
52
+ function isLazyCommand(cmd) {
53
+ return typeof cmd === "function" && "commandName" in cmd && !!cmd.commandName;
54
+ }
55
+ async function resolveLazyCommand(cmd, name$1, needRunResolving = false) {
56
+ let command;
57
+ if (isLazyCommand(cmd)) {
58
+ command = Object.assign(create(), {
59
+ name: cmd.commandName,
60
+ description: cmd.description,
61
+ args: cmd.args,
62
+ examples: cmd.examples,
63
+ resource: cmd.resource
64
+ });
65
+ if (needRunResolving) {
66
+ const loaded = await cmd();
67
+ if (typeof loaded === "function") command.run = loaded;
68
+ else if (typeof loaded === "object") {
69
+ if (loaded.run == null) throw new TypeError(`'run' is required in command: ${cmd.name || name$1}`);
70
+ command.run = loaded.run;
71
+ command.name = loaded.name;
72
+ command.description = loaded.description;
73
+ command.args = loaded.args;
74
+ command.examples = loaded.examples;
75
+ command.resource = loaded.resource;
76
+ } else throw new TypeError(`Cannot resolve command: ${cmd.name || name$1}`);
77
+ }
78
+ } else command = Object.assign(create(), cmd);
79
+ if (command.name == null && name$1) command.name = name$1;
80
+ return deepFreeze(command);
81
+ }
82
+ function resolveBuiltInKey(key) {
83
+ return `${BUILT_IN_PREFIX}${BUILT_IN_KEY_SEPARATOR}${key}`;
84
+ }
85
+ function resolveArgKey(key) {
86
+ return `${ARG_PREFIX}${BUILT_IN_KEY_SEPARATOR}${key}`;
87
+ }
88
+ async function resolveExamples(ctx, examples) {
89
+ return typeof examples === "string" ? examples : typeof examples === "function" ? await examples(ctx) : "";
90
+ }
91
+ function mapResourceWithBuiltinKey(resource) {
92
+ return Object.entries(resource).reduce((acc, [key, value$1]) => {
93
+ acc[resolveBuiltInKey(key)] = value$1;
94
+ return acc;
95
+ }, create());
96
+ }
97
+ function create(obj = null) {
98
+ return Object.create(obj);
99
+ }
100
+ function log$1(...args) {
101
+ console.log(...args);
102
+ }
103
+ function deepFreeze(obj) {
104
+ if (obj === null || typeof obj !== "object") return obj;
105
+ for (const key of Object.keys(obj)) {
106
+ const value$1 = obj[key];
107
+ if (typeof value$1 === "object" && value$1 !== null) deepFreeze(value$1);
108
+ }
109
+ return Object.freeze(obj);
110
+ }
111
+ var en_US_default = {
112
+ COMMAND: "COMMAND",
113
+ COMMANDS: "COMMANDS",
114
+ SUBCOMMAND: "SUBCOMMAND",
115
+ USAGE: "USAGE",
116
+ ARGUMENTS: "ARGUMENTS",
117
+ OPTIONS: "OPTIONS",
118
+ EXAMPLES: "EXAMPLES",
119
+ FORMORE: "For more info, run any command with the `--help` flag:",
120
+ NEGATABLE: "Negatable of",
121
+ DEFAULT: "default",
122
+ CHOICES: "choices",
123
+ help: "Display this help message",
124
+ version: "Display this version"
125
+ };
126
+ function createTranslationAdapter(options) {
127
+ return new DefaultTranslation(options);
128
+ }
129
+ var DefaultTranslation = class {
130
+ #resources = /* @__PURE__ */ new Map();
131
+ #options;
132
+ constructor(options) {
133
+ this.#options = options;
134
+ this.#resources.set(options.locale, create());
135
+ if (options.locale !== options.fallbackLocale) this.#resources.set(options.fallbackLocale, create());
136
+ }
137
+ getResource(locale) {
138
+ return this.#resources.get(locale);
139
+ }
140
+ setResource(locale, resource) {
141
+ this.#resources.set(locale, resource);
142
+ }
143
+ getMessage(locale, key) {
144
+ const resource = this.getResource(locale);
145
+ if (resource) return resource[key];
146
+ }
147
+ translate(locale, key, values = create()) {
148
+ let message = this.getMessage(locale, key);
149
+ if (message === void 0 && locale !== this.#options.fallbackLocale) message = this.getMessage(this.#options.fallbackLocale, key);
150
+ if (message === void 0) return;
151
+ return message.replaceAll(/\{\{(\w+)\}\}/g, (_$1, name$1) => {
152
+ return values[name$1] == null ? "" : values[name$1].toString();
153
+ });
154
+ }
155
+ };
156
+ const BUILT_IN_PREFIX_CODE = BUILT_IN_PREFIX.codePointAt(0);
157
+ async function createCommandContext({ args, values, positionals, rest, argv, tokens, command, cliOptions, callMode = "entry", omitted = false }) {
158
+ const _args = Object.entries(args).reduce((acc, [key, value$1]) => {
159
+ acc[key] = Object.assign(create(), value$1);
160
+ return acc;
161
+ }, create());
162
+ const env = Object.assign(create(), COMMAND_OPTIONS_DEFAULT, cliOptions);
163
+ const locale = resolveLocale(cliOptions.locale);
164
+ const localeStr = locale.toString();
165
+ const adapter = (cliOptions.translationAdapterFactory || createTranslationAdapter)({
166
+ locale: localeStr,
167
+ fallbackLocale: DEFAULT_LOCALE$1
168
+ });
169
+ const localeResources = /* @__PURE__ */ new Map();
170
+ let builtInLoadedResources;
171
+ localeResources.set(DEFAULT_LOCALE$1, mapResourceWithBuiltinKey(en_US_default));
172
+ if (DEFAULT_LOCALE$1 !== localeStr) try {
173
+ builtInLoadedResources = (await import(`./locales/${localeStr}.json`, { with: { type: "json" } })).default;
174
+ localeResources.set(localeStr, mapResourceWithBuiltinKey(builtInLoadedResources));
175
+ } catch {}
176
+ function translate(key, values$1 = create()) {
177
+ const strKey = key;
178
+ if (strKey.codePointAt(0) === BUILT_IN_PREFIX_CODE) return (localeResources.get(localeStr) || localeResources.get(DEFAULT_LOCALE$1))[strKey] || strKey;
179
+ else return adapter.translate(locale.toString(), strKey, values$1) || "";
180
+ }
181
+ let cachedCommands;
182
+ async function loadCommands() {
183
+ if (cachedCommands) return cachedCommands;
184
+ const subCommands$1 = [...cliOptions.subCommands || []];
185
+ return cachedCommands = await Promise.all(subCommands$1.map(async ([name$1, cmd]) => await resolveLazyCommand(cmd, name$1)));
186
+ }
187
+ const ctx = deepFreeze(Object.assign(create(), {
188
+ name: getCommandName(command),
189
+ description: command.description,
190
+ omitted,
191
+ callMode,
192
+ locale,
193
+ env,
194
+ args: _args,
195
+ values,
196
+ positionals,
197
+ rest,
198
+ _: argv,
199
+ tokens,
200
+ toKebab: command.toKebab,
201
+ log: cliOptions.usageSilent ? NOOP : log$1,
202
+ loadCommands,
203
+ translate
204
+ }));
205
+ const defaultCommandResource = Object.entries(args).map(([key, arg]) => {
206
+ return [key, arg.description || ""];
207
+ }).reduce((res, [key, value$1]) => {
208
+ res[resolveArgKey(key)] = value$1;
209
+ return res;
210
+ }, create());
211
+ defaultCommandResource.description = command.description || "";
212
+ defaultCommandResource.examples = await resolveExamples(ctx, command.examples);
213
+ adapter.setResource(DEFAULT_LOCALE$1, defaultCommandResource);
214
+ const originalResource = await loadCommandResource(ctx, command);
215
+ if (originalResource) {
216
+ const resource = Object.assign(create(), originalResource, { examples: await resolveExamples(ctx, originalResource.examples) });
217
+ if (builtInLoadedResources) {
218
+ resource.help = builtInLoadedResources.help;
219
+ resource.version = builtInLoadedResources.version;
220
+ }
221
+ adapter.setResource(localeStr, resource);
222
+ }
223
+ return ctx;
224
+ }
225
+ function getCommandName(cmd) {
226
+ if (isLazyCommand(cmd)) return cmd.commandName || cmd.name || ANONYMOUS_COMMAND_NAME;
227
+ else if (typeof cmd === "object") return cmd.name || ANONYMOUS_COMMAND_NAME;
228
+ else return ANONYMOUS_COMMAND_NAME;
229
+ }
230
+ function resolveLocale(locale) {
231
+ return locale instanceof Intl.Locale ? locale : typeof locale === "string" ? new Intl.Locale(locale) : new Intl.Locale(DEFAULT_LOCALE$1);
232
+ }
233
+ async function loadCommandResource(ctx, command) {
234
+ let resource;
235
+ try {
236
+ resource = await command.resource?.(ctx);
237
+ } catch {}
238
+ return resource;
239
+ }
240
+ function define(definition) {
241
+ return definition;
242
+ }
243
+ /**
244
+ * @author kazuya kawaguchi (a.k.a. kazupon)
245
+ * @license MIT
246
+ */
247
+ function kebabnize(str$1) {
248
+ return str$1.replace(/[A-Z]/g, (match, offset) => (offset > 0 ? "-" : "") + match.toLowerCase());
249
+ }
250
+ function renderHeader(ctx) {
251
+ const title = ctx.env.description || ctx.env.name || "";
252
+ return Promise.resolve(title ? `${title} (${ctx.env.name || ""}${ctx.env.version ? ` v${ctx.env.version}` : ""})` : title);
253
+ }
254
+ const COMMON_ARGS_KEYS = Object.keys(COMMON_ARGS);
255
+ async function renderUsage(ctx) {
256
+ const messages = [];
257
+ if (!ctx.omitted) {
258
+ const description$1 = resolveDescription(ctx);
259
+ if (description$1) messages.push(description$1, "");
260
+ }
261
+ messages.push(...await renderUsageSection(ctx), "");
262
+ if (ctx.omitted && await hasCommands(ctx)) messages.push(...await renderCommandsSection(ctx), "");
263
+ if (hasPositionalArgs(ctx)) messages.push(...await renderPositionalArgsSection(ctx), "");
264
+ if (hasOptionalArgs(ctx)) messages.push(...await renderOptionalArgsSection(ctx), "");
265
+ const examples = await renderExamplesSection(ctx);
266
+ if (examples.length > 0) messages.push(...examples, "");
267
+ return messages.join("\n");
268
+ }
269
+ async function renderPositionalArgsSection(ctx) {
270
+ const messages = [];
271
+ messages.push(`${ctx.translate(resolveBuiltInKey("ARGUMENTS"))}:`);
272
+ messages.push(await generatePositionalArgsUsage(ctx));
273
+ return messages;
274
+ }
275
+ async function renderOptionalArgsSection(ctx) {
276
+ const messages = [];
277
+ messages.push(`${ctx.translate(resolveBuiltInKey("OPTIONS"))}:`);
278
+ messages.push(await generateOptionalArgsUsage(ctx, getOptionalArgsPairs(ctx)));
279
+ return messages;
280
+ }
281
+ async function renderExamplesSection(ctx) {
282
+ const messages = [];
283
+ const resolvedExamples = await resolveExamples$1(ctx);
284
+ if (resolvedExamples) {
285
+ const examples = resolvedExamples.split("\n").map((example) => example.padStart(ctx.env.leftMargin + example.length));
286
+ messages.push(`${ctx.translate(resolveBuiltInKey("EXAMPLES"))}:`, ...examples);
287
+ }
288
+ return messages;
289
+ }
290
+ async function renderUsageSection(ctx) {
291
+ const messages = [`${ctx.translate(resolveBuiltInKey("USAGE"))}:`];
292
+ if (ctx.omitted) {
293
+ const defaultCommand = `${resolveEntry(ctx)}${await hasCommands(ctx) ? ` [${resolveSubCommand(ctx)}]` : ""} ${[generateOptionsSymbols(ctx), generatePositionalSymbols(ctx)].filter(Boolean).join(" ")}`;
294
+ messages.push(defaultCommand.padStart(ctx.env.leftMargin + defaultCommand.length));
295
+ if (await hasCommands(ctx)) {
296
+ const commandsUsage = `${resolveEntry(ctx)} <${ctx.translate(resolveBuiltInKey("COMMANDS"))}>`;
297
+ messages.push(commandsUsage.padStart(ctx.env.leftMargin + commandsUsage.length));
298
+ }
299
+ } else {
300
+ const usageStr = `${resolveEntry(ctx)} ${resolveSubCommand(ctx)} ${[generateOptionsSymbols(ctx), generatePositionalSymbols(ctx)].filter(Boolean).join(" ")}`;
301
+ messages.push(usageStr.padStart(ctx.env.leftMargin + usageStr.length));
302
+ }
303
+ return messages;
304
+ }
305
+ async function renderCommandsSection(ctx) {
306
+ const messages = [`${ctx.translate(resolveBuiltInKey("COMMANDS"))}:`];
307
+ const loadedCommands = await ctx.loadCommands();
308
+ const commandMaxLength = Math.max(...loadedCommands.map((cmd) => (cmd.name || "").length));
309
+ const commandsStr = await Promise.all(loadedCommands.map((cmd) => {
310
+ const key = cmd.name || "";
311
+ const desc = cmd.description || "";
312
+ const command = `${key.padEnd(commandMaxLength + ctx.env.middleMargin)}${desc} `;
313
+ return `${command.padStart(ctx.env.leftMargin + command.length)} `;
314
+ }));
315
+ messages.push(...commandsStr, "", ctx.translate(resolveBuiltInKey("FORMORE")));
316
+ messages.push(...loadedCommands.map((cmd) => {
317
+ const commandHelp = `${ctx.env.name} ${cmd.name} --help`;
318
+ return `${commandHelp.padStart(ctx.env.leftMargin + commandHelp.length)}`;
319
+ }));
320
+ return messages;
321
+ }
322
+ function resolveEntry(ctx) {
323
+ return ctx.env.name || ctx.translate(resolveBuiltInKey("COMMAND"));
324
+ }
325
+ function resolveSubCommand(ctx) {
326
+ return ctx.name || ctx.translate(resolveBuiltInKey("SUBCOMMAND"));
327
+ }
328
+ function resolveDescription(ctx) {
329
+ return ctx.translate("description") || ctx.description || "";
330
+ }
331
+ async function resolveExamples$1(ctx) {
332
+ const ret = ctx.translate("examples");
333
+ if (ret) return ret;
334
+ const command = ctx.env.subCommands?.get(ctx.name || "");
335
+ return await resolveExamples(ctx, command?.examples);
336
+ }
337
+ async function hasCommands(ctx) {
338
+ return (await ctx.loadCommands()).length > 1;
339
+ }
340
+ function hasOptionalArgs(ctx) {
341
+ return !!(ctx.args && Object.values(ctx.args).some((arg) => arg.type !== "positional"));
342
+ }
343
+ function hasPositionalArgs(ctx) {
344
+ return !!(ctx.args && Object.values(ctx.args).some((arg) => arg.type === "positional"));
345
+ }
346
+ function hasAllDefaultOptions(ctx) {
347
+ return !!(ctx.args && Object.values(ctx.args).every((arg) => arg.default));
348
+ }
349
+ function generateOptionsSymbols(ctx) {
350
+ return hasOptionalArgs(ctx) ? hasAllDefaultOptions(ctx) ? `[${ctx.translate(resolveBuiltInKey("OPTIONS"))}]` : `<${ctx.translate(resolveBuiltInKey("OPTIONS"))}>` : "";
351
+ }
352
+ function makeShortLongOptionPair(schema, name$1, toKebab) {
353
+ let key = `--${toKebab || schema.toKebab ? kebabnize(name$1) : name$1}`;
354
+ if (schema.short) key = `-${schema.short}, ${key}`;
355
+ return key;
356
+ }
357
+ function getOptionalArgsPairs(ctx) {
358
+ return Object.entries(ctx.args).reduce((acc, [name$1, schema]) => {
359
+ if (schema.type === "positional") return acc;
360
+ let key = makeShortLongOptionPair(schema, name$1, ctx.toKebab);
361
+ if (schema.type !== "boolean") {
362
+ const displayName = ctx.toKebab || schema.toKebab ? kebabnize(name$1) : name$1;
363
+ key = schema.default ? `${key} [${displayName}]` : `${key} <${displayName}>`;
364
+ }
365
+ acc[name$1] = key;
366
+ if (schema.type === "boolean" && schema.negatable && !COMMON_ARGS_KEYS.includes(name$1)) {
367
+ const displayName = ctx.toKebab || schema.toKebab ? kebabnize(name$1) : name$1;
368
+ acc[`no-${name$1}`] = `--no-${displayName}`;
369
+ }
370
+ return acc;
371
+ }, create());
372
+ }
373
+ const resolveNegatableKey = (key) => key.split("no-")[1];
374
+ function resolveNegatableType(key, ctx) {
375
+ return ctx.args[key.startsWith("no-") ? resolveNegatableKey(key) : key].type;
376
+ }
377
+ function generateDefaultDisplayValue(ctx, schema) {
378
+ return `${ctx.translate(resolveBuiltInKey("DEFAULT"))}: ${schema.default}`;
379
+ }
380
+ function resolveDisplayValue(ctx, key) {
381
+ if (COMMON_ARGS_KEYS.includes(key)) return "";
382
+ const schema = ctx.args[key];
383
+ if ((schema.type === "boolean" || schema.type === "number" || schema.type === "string" || schema.type === "custom") && schema.default !== void 0) return `(${generateDefaultDisplayValue(ctx, schema)})`;
384
+ if (schema.type === "enum") {
385
+ const _default = schema.default !== void 0 ? generateDefaultDisplayValue(ctx, schema) : "";
386
+ const choices = `${ctx.translate(resolveBuiltInKey("CHOICES"))}: ${schema.choices.join(" | ")}`;
387
+ return `(${_default ? `${_default}, ${choices}` : choices})`;
388
+ }
389
+ return "";
390
+ }
391
+ async function generateOptionalArgsUsage(ctx, optionsPairs) {
392
+ const optionsMaxLength = Math.max(...Object.entries(optionsPairs).map(([_$1, value$1]) => value$1.length));
393
+ const optionSchemaMaxLength = ctx.env.usageOptionType ? Math.max(...Object.entries(optionsPairs).map(([key]) => resolveNegatableType(key, ctx).length)) : 0;
394
+ return (await Promise.all(Object.entries(optionsPairs).map(([key, value$1]) => {
395
+ let rawDesc = ctx.translate(resolveArgKey(key));
396
+ if (!rawDesc && key.startsWith("no-")) {
397
+ const name$1 = resolveNegatableKey(key);
398
+ const schema = ctx.args[name$1];
399
+ const optionKey = makeShortLongOptionPair(schema, name$1, ctx.toKebab);
400
+ rawDesc = `${ctx.translate(resolveBuiltInKey("NEGATABLE"))} ${optionKey}`;
401
+ }
402
+ const optionsSchema = ctx.env.usageOptionType ? `[${resolveNegatableType(key, ctx)}] ` : "";
403
+ const valueDesc = key.startsWith("no-") ? "" : resolveDisplayValue(ctx, key);
404
+ const desc = `${optionsSchema ? optionsSchema.padEnd(optionSchemaMaxLength + 3) : ""}${rawDesc}`;
405
+ const option = `${value$1.padEnd(optionsMaxLength + ctx.env.middleMargin)}${desc}${valueDesc ? ` ${valueDesc}` : ""}`;
406
+ return `${option.padStart(ctx.env.leftMargin + option.length)}`;
407
+ }))).join("\n");
408
+ }
409
+ function getPositionalArgs(ctx) {
410
+ return Object.entries(ctx.args).filter(([_$1, schema]) => schema.type === "positional");
411
+ }
412
+ async function generatePositionalArgsUsage(ctx) {
413
+ const positionals = getPositionalArgs(ctx);
414
+ const argsMaxLength = Math.max(...positionals.map(([name$1]) => name$1.length));
415
+ return (await Promise.all(positionals.map(([name$1]) => {
416
+ const desc = ctx.translate(resolveArgKey(name$1)) || ctx.args[name$1].description || "";
417
+ const arg = `${name$1.padEnd(argsMaxLength + ctx.env.middleMargin)} ${desc}`;
418
+ return `${arg.padStart(ctx.env.leftMargin + arg.length)}`;
419
+ }))).join("\n");
420
+ }
421
+ function generatePositionalSymbols(ctx) {
422
+ return hasPositionalArgs(ctx) ? getPositionalArgs(ctx).map(([name$1]) => `<${name$1}>`).join(" ") : "";
423
+ }
424
+ function renderValidationErrors(_ctx, error) {
425
+ const messages = [];
426
+ for (const err of error.errors) messages.push(err.message);
427
+ return Promise.resolve(messages.join("\n"));
428
+ }
429
+ const HYPHEN_CHAR = "-";
430
+ const HYPHEN_CODE = HYPHEN_CHAR.codePointAt(0);
431
+ const EQUAL_CHAR = "=";
432
+ const EQUAL_CODE = EQUAL_CHAR.codePointAt(0);
433
+ const TERMINATOR = "--";
434
+ const SHORT_OPTION_PREFIX = HYPHEN_CHAR;
435
+ const LONG_OPTION_PREFIX = "--";
436
+ function parseArgs(args, options = {}) {
437
+ const { allowCompatible = false } = options;
438
+ const tokens = [];
439
+ const remainings = [...args];
440
+ let index = -1;
441
+ let groupCount = 0;
442
+ let hasShortValueSeparator = false;
443
+ while (remainings.length > 0) {
444
+ const arg = remainings.shift();
445
+ if (arg == void 0) break;
446
+ const nextArg = remainings[0];
447
+ if (groupCount > 0) groupCount--;
448
+ else index++;
449
+ if (arg === TERMINATOR) {
450
+ tokens.push({
451
+ kind: "option-terminator",
452
+ index
453
+ });
454
+ const mapped = remainings.map((arg$1) => {
455
+ return {
456
+ kind: "positional",
457
+ index: ++index,
458
+ value: arg$1
459
+ };
460
+ });
461
+ tokens.push(...mapped);
462
+ break;
463
+ }
464
+ if (isShortOption(arg)) {
465
+ const shortOption = arg.charAt(1);
466
+ let value$1;
467
+ let inlineValue;
468
+ if (groupCount) {
469
+ tokens.push({
470
+ kind: "option",
471
+ name: shortOption,
472
+ rawName: arg,
473
+ index,
474
+ value: value$1,
475
+ inlineValue
476
+ });
477
+ if (groupCount === 1 && hasOptionValue(nextArg)) {
478
+ value$1 = remainings.shift();
479
+ if (hasShortValueSeparator) {
480
+ inlineValue = true;
481
+ hasShortValueSeparator = false;
482
+ }
483
+ tokens.push({
484
+ kind: "option",
485
+ index,
486
+ value: value$1,
487
+ inlineValue
488
+ });
489
+ }
490
+ } else tokens.push({
491
+ kind: "option",
492
+ name: shortOption,
493
+ rawName: arg,
494
+ index,
495
+ value: value$1,
496
+ inlineValue
497
+ });
498
+ if (value$1 != null) ++index;
499
+ continue;
500
+ }
501
+ if (isShortOptionGroup(arg)) {
502
+ const expanded = [];
503
+ let shortValue = "";
504
+ for (let i = 1; i < arg.length; i++) {
505
+ const shortableOption = arg.charAt(i);
506
+ if (hasShortValueSeparator) shortValue += shortableOption;
507
+ else if (!allowCompatible && shortableOption.codePointAt(0) === EQUAL_CODE) hasShortValueSeparator = true;
508
+ else expanded.push(`${SHORT_OPTION_PREFIX}${shortableOption}`);
509
+ }
510
+ if (shortValue) expanded.push(shortValue);
511
+ remainings.unshift(...expanded);
512
+ groupCount = expanded.length;
513
+ continue;
514
+ }
515
+ if (isLongOption(arg)) {
516
+ const longOption = arg.slice(2);
517
+ tokens.push({
518
+ kind: "option",
519
+ name: longOption,
520
+ rawName: arg,
521
+ index,
522
+ value: void 0,
523
+ inlineValue: void 0
524
+ });
525
+ continue;
526
+ }
527
+ if (isLongOptionAndValue(arg)) {
528
+ const equalIndex = arg.indexOf(EQUAL_CHAR);
529
+ const longOption = arg.slice(2, equalIndex);
530
+ const value$1 = arg.slice(equalIndex + 1);
531
+ tokens.push({
532
+ kind: "option",
533
+ name: longOption,
534
+ rawName: `${LONG_OPTION_PREFIX}${longOption}`,
535
+ index,
536
+ value: value$1,
537
+ inlineValue: true
538
+ });
539
+ continue;
540
+ }
541
+ tokens.push({
542
+ kind: "positional",
543
+ index,
544
+ value: arg
545
+ });
546
+ }
547
+ return tokens;
548
+ }
549
+ function isShortOption(arg) {
550
+ return arg.length === 2 && arg.codePointAt(0) === HYPHEN_CODE && arg.codePointAt(1) !== HYPHEN_CODE;
551
+ }
552
+ function isShortOptionGroup(arg) {
553
+ if (arg.length <= 2) return false;
554
+ if (arg.codePointAt(0) !== HYPHEN_CODE) return false;
555
+ if (arg.codePointAt(1) === HYPHEN_CODE) return false;
556
+ return true;
557
+ }
558
+ function isLongOption(arg) {
559
+ return hasLongOptionPrefix(arg) && !arg.includes(EQUAL_CHAR, 3);
560
+ }
561
+ function isLongOptionAndValue(arg) {
562
+ return hasLongOptionPrefix(arg) && arg.includes(EQUAL_CHAR, 3);
563
+ }
564
+ function hasLongOptionPrefix(arg) {
565
+ return arg.length > 2 && ~arg.indexOf(LONG_OPTION_PREFIX);
566
+ }
567
+ function hasOptionValue(value$1) {
568
+ return !(value$1 == null) && value$1.codePointAt(0) !== HYPHEN_CODE;
569
+ }
570
+ const SKIP_POSITIONAL_DEFAULT = -1;
571
+ function resolveArgs(args, tokens, { shortGrouping = false, skipPositional = SKIP_POSITIONAL_DEFAULT, toKebab = false } = {}) {
572
+ const skipPositionalIndex = typeof skipPositional === "number" ? Math.max(skipPositional, SKIP_POSITIONAL_DEFAULT) : SKIP_POSITIONAL_DEFAULT;
573
+ const rest = [];
574
+ const optionTokens = [];
575
+ const positionalTokens = [];
576
+ let currentLongOption;
577
+ let currentShortOption;
578
+ const expandableShortOptions = [];
579
+ function toShortValue() {
580
+ if (expandableShortOptions.length === 0) return void 0;
581
+ else {
582
+ const value$1 = expandableShortOptions.map((token) => token.name).join("");
583
+ expandableShortOptions.length = 0;
584
+ return value$1;
585
+ }
586
+ }
587
+ function applyLongOptionValue(value$1 = void 0) {
588
+ if (currentLongOption) {
589
+ currentLongOption.value = value$1;
590
+ optionTokens.push({ ...currentLongOption });
591
+ currentLongOption = void 0;
592
+ }
593
+ }
594
+ function applyShortOptionValue(value$1 = void 0) {
595
+ if (currentShortOption) {
596
+ currentShortOption.value = value$1 || toShortValue();
597
+ optionTokens.push({ ...currentShortOption });
598
+ currentShortOption = void 0;
599
+ }
600
+ }
601
+ const schemas = Object.values(args);
602
+ let terminated = false;
603
+ for (let i = 0; i < tokens.length; i++) {
604
+ const token = tokens[i];
605
+ if (token.kind === "positional") {
606
+ if (terminated && token.value) {
607
+ rest.push(token.value);
608
+ continue;
609
+ }
610
+ if (currentShortOption) {
611
+ if (schemas.find((schema) => schema.short === currentShortOption.name && schema.type === "boolean")) positionalTokens.push({ ...token });
612
+ } else if (currentLongOption) {
613
+ if (args[currentLongOption.name]?.type === "boolean") positionalTokens.push({ ...token });
614
+ } else positionalTokens.push({ ...token });
615
+ applyLongOptionValue(token.value);
616
+ applyShortOptionValue(token.value);
617
+ } else if (token.kind === "option") if (token.rawName) {
618
+ if (hasLongOptionPrefix(token.rawName)) {
619
+ applyLongOptionValue();
620
+ if (token.inlineValue) optionTokens.push({ ...token });
621
+ else currentLongOption = { ...token };
622
+ applyShortOptionValue();
623
+ } else if (isShortOption(token.rawName)) if (currentShortOption) {
624
+ if (currentShortOption.index === token.index) if (shortGrouping) {
625
+ currentShortOption.value = token.value;
626
+ optionTokens.push({ ...currentShortOption });
627
+ currentShortOption = { ...token };
628
+ } else expandableShortOptions.push({ ...token });
629
+ else {
630
+ currentShortOption.value = toShortValue();
631
+ optionTokens.push({ ...currentShortOption });
632
+ currentShortOption = { ...token };
633
+ }
634
+ applyLongOptionValue();
635
+ } else {
636
+ currentShortOption = { ...token };
637
+ applyLongOptionValue();
638
+ }
639
+ } else {
640
+ if (currentShortOption && currentShortOption.index == token.index && token.inlineValue) {
641
+ currentShortOption.value = token.value;
642
+ optionTokens.push({ ...currentShortOption });
643
+ currentShortOption = void 0;
644
+ }
645
+ applyLongOptionValue();
646
+ }
647
+ else {
648
+ if (token.kind === "option-terminator") terminated = true;
649
+ applyLongOptionValue();
650
+ applyShortOptionValue();
651
+ }
652
+ }
653
+ applyLongOptionValue();
654
+ applyShortOptionValue();
655
+ const values = Object.create(null);
656
+ const errors = [];
657
+ function checkTokenName(option, schema, token) {
658
+ return token.name === (schema.type === "boolean" ? schema.negatable && token.name?.startsWith("no-") ? `no-${option}` : option : option);
659
+ }
660
+ const positionalItemCount = tokens.filter((token) => token.kind === "positional").length;
661
+ function getPositionalSkipIndex() {
662
+ return Math.min(skipPositionalIndex, positionalItemCount);
663
+ }
664
+ let positionalsCount = 0;
665
+ for (const [rawArg, schema] of Object.entries(args)) {
666
+ const arg = toKebab || schema.toKebab ? kebabnize(rawArg) : rawArg;
667
+ if (schema.required) {
668
+ if (!optionTokens.find((token) => {
669
+ return schema.short && token.name === schema.short || token.rawName && hasLongOptionPrefix(token.rawName) && token.name === arg;
670
+ })) {
671
+ errors.push(createRequireError(arg, schema));
672
+ continue;
673
+ }
674
+ }
675
+ if (schema.type === "positional") {
676
+ if (skipPositionalIndex > SKIP_POSITIONAL_DEFAULT) while (positionalsCount <= getPositionalSkipIndex()) positionalsCount++;
677
+ const positional = positionalTokens[positionalsCount];
678
+ if (positional != null) values[rawArg] = positional.value;
679
+ else errors.push(createRequireError(arg, schema));
680
+ positionalsCount++;
681
+ continue;
682
+ }
683
+ for (let i = 0; i < optionTokens.length; i++) {
684
+ const token = optionTokens[i];
685
+ if (checkTokenName(arg, schema, token) && token.rawName != void 0 && hasLongOptionPrefix(token.rawName) || schema.short === token.name && token.rawName != void 0 && isShortOption(token.rawName)) {
686
+ const invalid = validateRequire(token, arg, schema);
687
+ if (invalid) {
688
+ errors.push(invalid);
689
+ continue;
690
+ }
691
+ if (schema.type === "boolean") token.value = void 0;
692
+ const [parsedValue, error] = parse(token, arg, schema);
693
+ if (error) errors.push(error);
694
+ else if (schema.multiple) {
695
+ values[rawArg] ||= [];
696
+ values[rawArg].push(parsedValue);
697
+ } else values[rawArg] = parsedValue;
698
+ }
699
+ }
700
+ if (values[rawArg] == null && schema.default != null) values[rawArg] = schema.default;
701
+ }
702
+ return {
703
+ values,
704
+ positionals: positionalTokens.map((token) => token.value),
705
+ rest,
706
+ error: errors.length > 0 ? new AggregateError(errors) : void 0
707
+ };
708
+ }
709
+ function parse(token, option, schema) {
710
+ switch (schema.type) {
711
+ case "string": return typeof token.value === "string" ? [token.value || schema.default, void 0] : [void 0, createTypeError(option, schema)];
712
+ case "boolean": return token.value ? [token.value || schema.default, void 0] : [!(schema.negatable && token.name.startsWith("no-")), void 0];
713
+ case "number":
714
+ if (!isNumeric(token.value)) return [void 0, createTypeError(option, schema)];
715
+ return token.value ? [+token.value, void 0] : [+(schema.default || ""), void 0];
716
+ case "enum":
717
+ if (schema.choices && !schema.choices.includes(token.value)) return [void 0, new ArgResolveError(`Optional argument '--${option}' ${schema.short ? `or '-${schema.short}' ` : ""}should be chosen from '${schema.type}' [${schema.choices.map((c) => JSON.stringify(c)).join(", ")}] values`, option, "type", schema)];
718
+ return [token.value || schema.default, void 0];
719
+ case "custom":
720
+ if (typeof schema.parse !== "function") throw new TypeError(`argument '${option}' should have a 'parse' function`);
721
+ try {
722
+ return [schema.parse(token.value || String(schema.default || "")), void 0];
723
+ } catch (error) {
724
+ return [void 0, error];
725
+ }
726
+ default: throw new Error(`Unsupported argument type '${schema.type}' for option '${option}'`);
727
+ }
728
+ }
729
+ function createRequireError(option, schema) {
730
+ return new ArgResolveError(schema.type === "positional" ? `Positional argument '${option}' is required` : `Optional argument '--${option}' ${schema.short ? `or '-${schema.short}' ` : ""}is required`, option, "required", schema);
731
+ }
732
+ var ArgResolveError = class extends Error {
733
+ name;
734
+ schema;
735
+ type;
736
+ constructor(message, name$1, type, schema) {
737
+ super(message);
738
+ this.name = name$1;
739
+ this.type = type;
740
+ this.schema = schema;
741
+ }
742
+ };
743
+ function validateRequire(token, option, schema) {
744
+ if (schema.required && schema.type !== "boolean" && !token.value) return createRequireError(option, schema);
745
+ }
746
+ function isNumeric(str$1) {
747
+ return str$1.trim() !== "" && !isNaN(str$1);
748
+ }
749
+ function createTypeError(option, schema) {
750
+ return new ArgResolveError(`Optional argument '--${option}' ${schema.short ? `or '-${schema.short}' ` : ""}should be '${schema.type}'`, option, "type", schema);
751
+ }
752
+ async function cli(argv, entry, options = {}) {
753
+ const cliOptions = resolveCliOptions(options, entry);
754
+ const tokens = parseArgs(argv);
755
+ const subCommand = getSubCommand(tokens);
756
+ const { commandName: name$1, command, callMode } = await resolveCommand(subCommand, entry, cliOptions);
757
+ if (!command) throw new Error(`Command not found: ${name$1 || ""}`);
758
+ const args = resolveArguments(getCommandArgs(command));
759
+ const { values, positionals, rest, error } = resolveArgs(args, tokens, {
760
+ shortGrouping: true,
761
+ toKebab: command.toKebab,
762
+ skipPositional: cliOptions.subCommands.size > 0 ? 0 : -1
763
+ });
764
+ const ctx = await createCommandContext({
765
+ args,
766
+ values,
767
+ positionals,
768
+ rest,
769
+ argv,
770
+ tokens,
771
+ omitted: !subCommand,
772
+ callMode,
773
+ command,
774
+ cliOptions
775
+ });
776
+ if (values.version) {
777
+ showVersion(ctx);
778
+ return;
779
+ }
780
+ const usageBuffer = [];
781
+ const header = await showHeader(ctx);
782
+ if (header) usageBuffer.push(header);
783
+ if (values.help) {
784
+ const usage = await showUsage(ctx);
785
+ if (usage) usageBuffer.push(usage);
786
+ return usageBuffer.join("\n");
787
+ }
788
+ if (error) {
789
+ await showValidationErrors(ctx, error);
790
+ return;
791
+ }
792
+ await executeCommand(command, ctx, name$1 || "");
793
+ }
794
+ function getCommandArgs(cmd) {
795
+ if (isLazyCommand(cmd)) return cmd.args || create();
796
+ else if (typeof cmd === "object") return cmd.args || create();
797
+ else return create();
798
+ }
799
+ function resolveArguments(args) {
800
+ return Object.assign(create(), args, COMMON_ARGS);
801
+ }
802
+ function resolveCliOptions(options, entry) {
803
+ const subCommands$1 = new Map(options.subCommands);
804
+ if (options.subCommands) {
805
+ if (isLazyCommand(entry)) subCommands$1.set(entry.commandName, entry);
806
+ else if (typeof entry === "object" && entry.name) subCommands$1.set(entry.name, entry);
807
+ }
808
+ return Object.assign(create(), COMMAND_OPTIONS_DEFAULT, options, { subCommands: subCommands$1 });
809
+ }
810
+ function getSubCommand(tokens) {
811
+ const firstToken = tokens[0];
812
+ return firstToken && firstToken.kind === "positional" && firstToken.index === 0 && firstToken.value ? firstToken.value : "";
813
+ }
814
+ async function showUsage(ctx) {
815
+ if (ctx.env.renderUsage === null) return;
816
+ const usage = await (ctx.env.renderUsage || renderUsage)(ctx);
817
+ if (usage) {
818
+ ctx.log(usage);
819
+ return usage;
820
+ }
821
+ }
822
+ function showVersion(ctx) {
823
+ ctx.log(ctx.env.version);
824
+ }
825
+ async function showHeader(ctx) {
826
+ if (ctx.env.renderHeader === null) return;
827
+ const header = await (ctx.env.renderHeader || renderHeader)(ctx);
828
+ if (header) {
829
+ ctx.log(header);
830
+ ctx.log();
831
+ return header;
832
+ }
833
+ }
834
+ async function showValidationErrors(ctx, error) {
835
+ if (ctx.env.renderValidationErrors === null) return;
836
+ const render = ctx.env.renderValidationErrors || renderValidationErrors;
837
+ ctx.log(await render(ctx, error));
838
+ }
839
+ const CANNOT_RESOLVE_COMMAND = { callMode: "unexpected" };
840
+ async function resolveCommand(sub, entry, options) {
841
+ const omitted = !sub;
842
+ async function doResolveCommand() {
843
+ if (typeof entry === "function") if ("commandName" in entry && entry.commandName) return {
844
+ commandName: entry.commandName,
845
+ command: entry,
846
+ callMode: "entry"
847
+ };
848
+ else return {
849
+ command: { run: entry },
850
+ callMode: "entry"
851
+ };
852
+ else if (typeof entry === "object") return {
853
+ commandName: resolveEntryName(entry),
854
+ command: entry,
855
+ callMode: "entry"
856
+ };
857
+ else return CANNOT_RESOLVE_COMMAND;
858
+ }
859
+ if (omitted || options.subCommands?.size === 0) return doResolveCommand();
860
+ const cmd = options.subCommands?.get(sub);
861
+ if (cmd == null) return {
862
+ commandName: sub,
863
+ callMode: "unexpected"
864
+ };
865
+ if (isLazyCommand(cmd) && cmd.commandName == null) cmd.commandName = sub;
866
+ else if (typeof cmd === "object" && cmd.name == null) cmd.name = sub;
867
+ return {
868
+ commandName: sub,
869
+ command: cmd,
870
+ callMode: "subCommand"
871
+ };
872
+ }
873
+ function resolveEntryName(entry) {
874
+ return entry.name || ANONYMOUS_COMMAND_NAME;
875
+ }
876
+ async function executeCommand(cmd, ctx, name$1) {
877
+ const resolved = isLazyCommand(cmd) ? await resolveLazyCommand(cmd, name$1, true) : cmd;
878
+ if (resolved.run == null) throw new Error(`'run' not found on Command \`${name$1}\``);
879
+ await resolved.run(ctx);
880
+ }
881
+ var import_picocolors$2 = /* @__PURE__ */ __toESM(require_picocolors(), 1);
882
+ function extractExplicitArgs(tokens) {
883
+ const explicit = {};
884
+ for (const token of tokens) if (typeof token === "object" && token !== null) {
885
+ const t = token;
886
+ if (t.kind === "option" && typeof t.name === "string") explicit[t.name] = true;
887
+ }
888
+ return explicit;
889
+ }
890
+ function getConfigSearchPaths() {
891
+ return [join(process$1.cwd(), ".ccusage"), ...toArray(getClaudePaths())].map((dir) => join(dir, CONFIG_FILE_NAME));
892
+ }
893
+ function validateConfigJson(data) {
894
+ if (typeof data !== "object" || data === null) return false;
895
+ const config = data;
896
+ if (config.$schema != null && typeof config.$schema !== "string") return false;
897
+ if (config.defaults != null && (typeof config.defaults !== "object" || config.defaults === null)) return false;
898
+ if (config.commands != null && (typeof config.commands !== "object" || config.commands === null)) return false;
899
+ if (config.modelAliases != null && (typeof config.modelAliases !== "object" || config.modelAliases === null)) return false;
900
+ if (config.modelAliases != null) {
901
+ for (const value$1 of Object.values(config.modelAliases)) if (typeof value$1 !== "string") return false;
902
+ }
903
+ return true;
904
+ }
905
+ function loadConfigFile(filePath, debug = false) {
906
+ if (!existsSync(filePath)) {
907
+ if (debug) logger.info(` • Checking: ${filePath} (not found)`);
908
+ return;
909
+ }
910
+ return pipe(try_({
911
+ try: () => {
912
+ const content = readFileSync(filePath, "utf-8");
913
+ const data = JSON.parse(content);
914
+ if (!validateConfigJson(data)) throw new Error("Invalid configuration structure");
915
+ data.source = filePath;
916
+ return data;
917
+ },
918
+ catch: (error) => error
919
+ })(), inspect(() => {
920
+ logger.debug(`Parsed configuration file: ${filePath}`);
921
+ if (debug) logger.info(` • Checking: ${filePath} (found ✓)`);
922
+ }), inspectError((error) => {
923
+ const errorMessage = error instanceof Error ? error.message : String(error);
924
+ logger.warn(`Error parsing configuration file at ${filePath}: ${errorMessage}`);
925
+ if (debug) logger.info(` • Checking: ${filePath} (error: ${errorMessage})`);
926
+ }), unwrap(void 0));
927
+ }
928
+ function loadConfig(configPath, debug = false) {
929
+ if (debug) logger.info("Debug mode enabled - showing config loading details\n");
930
+ if (configPath != null) {
931
+ if (debug) {
932
+ logger.info("Using specified config file:");
933
+ logger.info(` • Path: ${configPath}`);
934
+ }
935
+ const config = loadConfigFile(configPath, debug);
936
+ if (config == null) logger.warn(`Configuration file not found or invalid: ${configPath}`);
937
+ else if (debug) {
938
+ logger.info("");
939
+ logger.info(`Loaded config from: ${configPath}`);
940
+ logger.info(` • Schema: ${config.$schema ?? "none"}`);
941
+ logger.info(` • Has defaults: ${config.defaults != null ? "yes" : "no"}${config.defaults != null ? ` (${Object.keys(config.defaults).length} options)` : ""}`);
942
+ logger.info(` • Has command configs: ${config.commands != null ? "yes" : "no"}${config.commands != null ? ` (${Object.keys(config.commands).join(", ")})` : ""}`);
943
+ }
944
+ return config;
945
+ }
946
+ if (debug) logger.info("Searching for config files:");
947
+ for (const searchPath of getConfigSearchPaths()) {
948
+ const config = loadConfigFile(searchPath, debug);
949
+ if (config != null) {
950
+ if (debug) {
951
+ logger.info("");
952
+ logger.info(`Loaded config from: ${searchPath}`);
953
+ logger.info(` • Schema: ${config.$schema ?? "none"}`);
954
+ logger.info(` • Has defaults: ${config.defaults != null ? "yes" : "no"}${config.defaults != null ? ` (${Object.keys(config.defaults).length} options)` : ""}`);
955
+ logger.info(` • Has command configs: ${config.commands != null ? "yes" : "no"}${config.commands != null ? ` (${Object.keys(config.commands).join(", ")})` : ""}`);
956
+ }
957
+ return config;
958
+ }
959
+ }
960
+ logger.debug("No valid configuration file found");
961
+ if (debug) {
962
+ logger.info("");
963
+ logger.info("No valid configuration file found");
964
+ }
965
+ }
966
+ function mergeConfigWithArgs(ctx, config, debug = false) {
967
+ if (config == null) {
968
+ if (debug) {
969
+ logger.info("");
970
+ logger.info(`No config file loaded, using CLI args only for '${ctx.name ?? "unknown"}' command`);
971
+ }
972
+ return ctx.values;
973
+ }
974
+ const merged = {};
975
+ const commandName = ctx.name;
976
+ const sources = {};
977
+ if (config.defaults != null) for (const [key, value$1] of Object.entries(config.defaults)) {
978
+ merged[key] = value$1;
979
+ sources[key] = "defaults";
980
+ }
981
+ if (commandName != null && config.commands?.[commandName] != null) for (const [key, value$1] of Object.entries(config.commands[commandName])) {
982
+ merged[key] = value$1;
983
+ sources[key] = "command config";
984
+ }
985
+ const explicit = extractExplicitArgs(ctx.tokens);
986
+ for (const [key, value$1] of Object.entries(ctx.values)) if (value$1 != null && explicit[key] === true) {
987
+ merged[key] = value$1;
988
+ sources[key] = "CLI";
989
+ }
990
+ logger.debug(`Merged config for ${commandName ?? "unknown"}:`, merged);
991
+ if (debug) {
992
+ logger.info("");
993
+ logger.info(`Merging options for '${commandName ?? "unknown"}' command:`);
994
+ const bySource = {
995
+ defaults: [],
996
+ "command config": [],
997
+ CLI: []
998
+ };
999
+ for (const [key, source] of Object.entries(sources)) if (bySource[source] != null) bySource[source].push(`${key}=${JSON.stringify(merged[key])}`);
1000
+ if (bySource.defaults.length > 0) logger.info(` • From defaults: ${bySource.defaults.join(", ")}`);
1001
+ if (bySource["command config"].length > 0) logger.info(` • From command config: ${bySource["command config"].join(", ")}`);
1002
+ if (bySource.CLI.length > 0) logger.info(` • From CLI args: ${bySource.CLI.join(", ")}`);
1003
+ logger.info(" • Final merged options: {");
1004
+ for (const [key, value$1] of Object.entries(merged)) {
1005
+ const source = sources[key] ?? "unknown";
1006
+ logger.info(` ${key}: ${JSON.stringify(value$1)} (from ${source}),`);
1007
+ }
1008
+ logger.info(" }");
1009
+ }
1010
+ return merged;
1011
+ }
1012
+ const getContext = (raw) => ({
1013
+ start: process$1.hrtime.bigint(),
1014
+ command: raw.map((part) => getCommandPart(stripVTControlCharacters(part))).join(" "),
1015
+ state: {
1016
+ stdout: "",
1017
+ stderr: "",
1018
+ output: ""
1019
+ }
1020
+ });
1021
+ const getCommandPart = (part) => /[^\w./-]/.test(part) ? `'${part.replaceAll("'", "'\\''")}'` : part;
1022
+ const getOptions = ({ stdin: stdin$2, stdout: stdout$1, stderr, stdio = [
1023
+ stdin$2,
1024
+ stdout$1,
1025
+ stderr
1026
+ ], env: envOption, preferLocal, cwd: cwdOption = ".", ...options }) => {
1027
+ const cwd = cwdOption instanceof URL ? fileURLToPath(cwdOption) : path.resolve(cwdOption);
1028
+ const env = envOption ? {
1029
+ ...process$1.env,
1030
+ ...envOption
1031
+ } : void 0;
1032
+ const input = stdio[0]?.string;
1033
+ return {
1034
+ ...options,
1035
+ input,
1036
+ stdio: input === void 0 ? stdio : ["pipe", ...stdio.slice(1)],
1037
+ env: preferLocal ? addLocalPath(env ?? process$1.env, cwd) : env,
1038
+ cwd
1039
+ };
1040
+ };
1041
+ const addLocalPath = ({ Path = "", PATH = Path, ...env }, cwd) => {
1042
+ const pathParts = PATH.split(path.delimiter);
1043
+ const localPaths = getLocalPaths([], path.resolve(cwd)).map((localPath) => path.join(localPath, "node_modules/.bin")).filter((localPath) => !pathParts.includes(localPath));
1044
+ return {
1045
+ ...env,
1046
+ PATH: [...localPaths, PATH].filter(Boolean).join(path.delimiter)
1047
+ };
1048
+ };
1049
+ const getLocalPaths = (localPaths, localPath) => localPaths.at(-1) === localPath ? localPaths : getLocalPaths([...localPaths, localPath], path.resolve(localPath, ".."));
1050
+ const applyForceShell = async (file, commandArguments, options) => await shouldForceShell(file, options) ? [
1051
+ escapeFile(file),
1052
+ commandArguments.map((argument) => escapeArgument(argument)),
1053
+ {
1054
+ ...options,
1055
+ shell: true
1056
+ }
1057
+ ] : [
1058
+ file,
1059
+ commandArguments,
1060
+ options
1061
+ ];
1062
+ const shouldForceShell = async (file, { shell, cwd, env = process$1.env }) => process$1.platform === "win32" && !shell && !await isExe(file, cwd, env);
1063
+ const isExe = (file, cwd, { Path = "", PATH = Path }) => exeExtensions.some((extension) => file.toLowerCase().endsWith(extension)) || mIsExe(file, cwd, PATH);
1064
+ const EXE_MEMO = {};
1065
+ const memoize = (function_) => (...arguments_) => EXE_MEMO[arguments_.join("\0")] ??= function_(...arguments_);
1066
+ const access = memoize(a.access);
1067
+ const mIsExe = memoize(async (file, cwd, PATH) => {
1068
+ const parts = PATH.split(path.delimiter).filter(Boolean).map((part) => part.replace(/^"(.*)"$/, "$1"));
1069
+ try {
1070
+ await Promise.any([cwd, ...parts].flatMap((part) => exeExtensions.map((extension) => access(`${path.resolve(part, file)}${extension}`))));
1071
+ } catch {
1072
+ return false;
1073
+ }
1074
+ return true;
1075
+ });
1076
+ const exeExtensions = [".exe", ".com"];
1077
+ const escapeArgument = (argument) => escapeFile(escapeFile(`"${argument.replaceAll(/(\\*)"/g, "$1$1\\\"").replace(/(\\*)$/, "$1$1")}"`));
1078
+ const escapeFile = (file) => file.replaceAll(/([()\][%!^"`<>&|;, *?])/g, "^$1");
1079
+ const getResult = async (nodeChildProcess, { input }, context) => {
1080
+ const instance = await nodeChildProcess;
1081
+ if (input !== void 0) instance.stdin.end(input);
1082
+ const onClose = once(instance, "close");
1083
+ try {
1084
+ await Promise.race([onClose, ...instance.stdio.filter(Boolean).map((stream) => onStreamError(stream))]);
1085
+ checkFailure(context, getErrorOutput(instance));
1086
+ return getOutputs(context);
1087
+ } catch (error) {
1088
+ await Promise.allSettled([onClose]);
1089
+ throw getResultError(error, instance, context);
1090
+ }
1091
+ };
1092
+ const onStreamError = async (stream) => {
1093
+ for await (const [error] of on(stream, "error")) if (!["ERR_STREAM_PREMATURE_CLOSE", "EPIPE"].includes(error?.code)) throw error;
1094
+ };
1095
+ const checkFailure = ({ command }, { exitCode, signalName }) => {
1096
+ if (signalName !== void 0) throw new SubprocessError(`Command was terminated with ${signalName}: ${command}`);
1097
+ if (exitCode !== void 0) throw new SubprocessError(`Command failed with exit code ${exitCode}: ${command}`);
1098
+ };
1099
+ const getResultError = (error, instance, context) => Object.assign(getErrorInstance(error, context), getErrorOutput(instance), getOutputs(context));
1100
+ const getErrorInstance = (error, { command }) => error instanceof SubprocessError ? error : new SubprocessError(`Command failed: ${command}`, { cause: error });
1101
+ var SubprocessError = class extends Error {
1102
+ name = "SubprocessError";
1103
+ };
1104
+ const getErrorOutput = ({ exitCode, signalCode }) => ({
1105
+ ...exitCode < 1 ? {} : { exitCode },
1106
+ ...signalCode === null ? {} : { signalName: signalCode }
1107
+ });
1108
+ const getOutputs = ({ state: { stdout: stdout$1, stderr, output }, command, start }) => ({
1109
+ stdout: getOutput(stdout$1),
1110
+ stderr: getOutput(stderr),
1111
+ output: getOutput(output),
1112
+ command,
1113
+ durationMs: Number(process$1.hrtime.bigint() - start) / 1e6
1114
+ });
1115
+ const getOutput = (output) => output.at(-1) === "\n" ? output.slice(0, output.at(-2) === "\r" ? -2 : -1) : output;
1116
+ const spawnSubprocess = async (file, commandArguments, options, context) => {
1117
+ try {
1118
+ if (["node", "node.exe"].includes(file.toLowerCase())) {
1119
+ file = process$1.execPath;
1120
+ commandArguments = [...process$1.execArgv.filter((flag) => !flag.startsWith("--inspect")), ...commandArguments];
1121
+ }
1122
+ [file, commandArguments, options] = await applyForceShell(file, commandArguments, options);
1123
+ [file, commandArguments, options] = concatenateShell(file, commandArguments, options);
1124
+ const instance = spawn(file, commandArguments, options);
1125
+ bufferOutput(instance.stdout, context, "stdout");
1126
+ bufferOutput(instance.stderr, context, "stderr");
1127
+ instance.once("error", () => {});
1128
+ await once(instance, "spawn");
1129
+ return instance;
1130
+ } catch (error) {
1131
+ throw getResultError(error, {}, context);
1132
+ }
1133
+ };
1134
+ const concatenateShell = (file, commandArguments, options) => options.shell && commandArguments.length > 0 ? [
1135
+ [file, ...commandArguments].join(" "),
1136
+ [],
1137
+ options
1138
+ ] : [
1139
+ file,
1140
+ commandArguments,
1141
+ options
1142
+ ];
1143
+ const bufferOutput = (stream, { state }, streamName) => {
1144
+ if (stream) {
1145
+ stream.setEncoding("utf8");
1146
+ if (!state.isIterating) {
1147
+ state.isIterating = false;
1148
+ stream.on("data", (chunk) => {
1149
+ state[streamName] += chunk;
1150
+ state.output += chunk;
1151
+ });
1152
+ }
1153
+ }
1154
+ };
1155
+ const handlePipe = async (subprocesses) => {
1156
+ const [[from, to]] = await Promise.all([Promise.allSettled(subprocesses), pipeStreams(subprocesses)]);
1157
+ if (to.reason) {
1158
+ to.reason.pipedFrom = from.reason ?? from.value;
1159
+ throw to.reason;
1160
+ }
1161
+ if (from.reason) throw from.reason;
1162
+ return {
1163
+ ...to.value,
1164
+ pipedFrom: from.value
1165
+ };
1166
+ };
1167
+ const pipeStreams = async (subprocesses) => {
1168
+ try {
1169
+ const [{ stdout: stdout$1 }, { stdin: stdin$2 }] = await Promise.all(subprocesses.map(({ nodeChildProcess }) => nodeChildProcess));
1170
+ if (stdin$2 === null) throw new Error("The \"stdin\" option must be set on the first \"spawn()\" call in the pipeline.");
1171
+ if (stdout$1 === null) throw new Error("The \"stdout\" option must be set on the last \"spawn()\" call in the pipeline.");
1172
+ pipeline(stdout$1, stdin$2).catch(() => {});
1173
+ } catch (error) {
1174
+ await Promise.allSettled(subprocesses.map(({ nodeChildProcess }) => closeStdin(nodeChildProcess)));
1175
+ throw error;
1176
+ }
1177
+ };
1178
+ const closeStdin = async (nodeChildProcess) => {
1179
+ const { stdin: stdin$2 } = await nodeChildProcess;
1180
+ stdin$2.end();
1181
+ };
1182
+ const lineIterator = async function* (subprocess, { state }, streamName) {
1183
+ if (state.isIterating === false) throw new Error(`The subprocess must be iterated right away, for example:
1184
+ for await (const line of spawn(...)) { ... }`);
1185
+ state.isIterating = true;
1186
+ try {
1187
+ const { [streamName]: stream } = await subprocess.nodeChildProcess;
1188
+ if (!stream) return;
1189
+ handleErrors(subprocess);
1190
+ yield* readline.createInterface({ input: stream });
1191
+ } finally {
1192
+ await subprocess;
1193
+ }
1194
+ };
1195
+ const handleErrors = async (subprocess) => {
1196
+ try {
1197
+ await subprocess;
1198
+ } catch {}
1199
+ };
1200
+ const combineAsyncIterators = async function* (...iterators) {
1201
+ try {
1202
+ let promises = [];
1203
+ while (iterators.length > 0) {
1204
+ promises = iterators.map((iterator$1, index$1) => promises[index$1] ?? getNext(iterator$1));
1205
+ const [{ value: value$1, done }, index] = await Promise.race(promises.map((promise, index$1) => Promise.all([promise, index$1])));
1206
+ const [iterator] = iterators.splice(index, 1);
1207
+ promises.splice(index, 1);
1208
+ if (!done) {
1209
+ iterators.push(iterator);
1210
+ yield value$1;
1211
+ }
1212
+ }
1213
+ } finally {
1214
+ await Promise.all(iterators.map((iterator) => iterator.return()));
1215
+ }
1216
+ };
1217
+ const getNext = async (iterator) => {
1218
+ try {
1219
+ return await iterator.next();
1220
+ } catch (error) {
1221
+ await iterator.throw(error);
1222
+ }
1223
+ };
1224
+ function spawn$1(file, second, third, previous) {
1225
+ const [commandArguments = [], options = {}] = Array.isArray(second) ? [second, third] : [[], second];
1226
+ const context = getContext([file, ...commandArguments]);
1227
+ const spawnOptions = getOptions(options);
1228
+ const nodeChildProcess = spawnSubprocess(file, commandArguments, spawnOptions, context);
1229
+ let subprocess = getResult(nodeChildProcess, spawnOptions, context);
1230
+ Object.assign(subprocess, { nodeChildProcess });
1231
+ subprocess = previous ? handlePipe([previous, subprocess]) : subprocess;
1232
+ const stdout$1 = lineIterator(subprocess, context, "stdout");
1233
+ const stderr = lineIterator(subprocess, context, "stderr");
1234
+ return Object.assign(subprocess, {
1235
+ nodeChildProcess,
1236
+ stdout: stdout$1,
1237
+ stderr,
1238
+ [Symbol.asyncIterator]: () => combineAsyncIterators(stdout$1, stderr),
1239
+ pipe: (file$1, second$1, third$1) => spawn$1(file$1, second$1, third$1, subprocess)
1240
+ });
1241
+ }
1242
+ async function processWithJq(jsonData, jqCommand) {
1243
+ const jsonString = JSON.stringify(jsonData);
1244
+ return try_({
1245
+ try: async () => {
1246
+ return (await spawn$1("jq", [jqCommand], { stdin: { string: jsonString } })).output.trim();
1247
+ },
1248
+ catch: (error) => {
1249
+ if (error instanceof Error) {
1250
+ if (error.message.includes("ENOENT") || error.message.includes("not found")) return /* @__PURE__ */ new Error("jq command not found. Please install jq to use the --jq option.");
1251
+ return /* @__PURE__ */ new Error(`jq processing failed: ${error.message}`);
1252
+ }
1253
+ return /* @__PURE__ */ new Error("Unknown error during jq processing");
1254
+ }
1255
+ })();
1256
+ }
1257
+ function parseDateArg(value$1) {
1258
+ return parse$1(filterDateSchema, value$1);
1259
+ }
1260
+ const sharedArgs = {
1261
+ since: {
1262
+ type: "custom",
1263
+ short: "s",
1264
+ description: "Filter from date (YYYYMMDD format)",
1265
+ parse: parseDateArg
1266
+ },
1267
+ until: {
1268
+ type: "custom",
1269
+ short: "u",
1270
+ description: "Filter until date (YYYYMMDD format)",
1271
+ parse: parseDateArg
1272
+ },
1273
+ json: {
1274
+ type: "boolean",
1275
+ short: "j",
1276
+ description: "Output in JSON format",
1277
+ default: false
1278
+ },
1279
+ mode: {
1280
+ type: "enum",
1281
+ short: "m",
1282
+ description: "Cost calculation mode: auto (use costUSD if exists, otherwise calculate), calculate (always calculate), display (always use costUSD)",
1283
+ default: "auto",
1284
+ choices: CostModes
1285
+ },
1286
+ debug: {
1287
+ type: "boolean",
1288
+ short: "d",
1289
+ description: "Show pricing mismatch information for debugging",
1290
+ default: false
1291
+ },
1292
+ debugSamples: {
1293
+ type: "number",
1294
+ description: "Number of sample discrepancies to show in debug output (default: 5)",
1295
+ default: 5
1296
+ },
1297
+ order: {
1298
+ type: "enum",
1299
+ short: "o",
1300
+ description: "Sort order: desc (newest first) or asc (oldest first)",
1301
+ default: "asc",
1302
+ choices: SortOrders
1303
+ },
1304
+ breakdown: {
1305
+ type: "boolean",
1306
+ short: "b",
1307
+ description: "Show per-model cost breakdown",
1308
+ default: false
1309
+ },
1310
+ offline: {
1311
+ type: "boolean",
1312
+ negatable: true,
1313
+ short: "O",
1314
+ description: "Use cached pricing data for Claude models instead of fetching from API",
1315
+ default: false
1316
+ },
1317
+ color: {
1318
+ type: "boolean",
1319
+ description: "Enable colored output (default: auto). FORCE_COLOR=1 has the same effect."
1320
+ },
1321
+ noColor: {
1322
+ type: "boolean",
1323
+ description: "Disable colored output (default: auto). NO_COLOR=1 has the same effect."
1324
+ },
1325
+ timezone: {
1326
+ type: "string",
1327
+ short: "z",
1328
+ description: "Timezone for date grouping (e.g., UTC, America/New_York, Asia/Tokyo). Default: system timezone"
1329
+ },
1330
+ locale: {
1331
+ type: "string",
1332
+ short: "l",
1333
+ description: "Locale for date/time formatting (e.g., en-US, ja-JP, de-DE)",
1334
+ default: DEFAULT_LOCALE
1335
+ },
1336
+ jq: {
1337
+ type: "string",
1338
+ short: "q",
1339
+ description: "Process JSON output with jq command (requires jq binary, implies --json)"
1340
+ },
1341
+ config: {
1342
+ type: "string",
1343
+ description: "Path to configuration file (default: auto-discovery)"
1344
+ },
1345
+ compact: {
1346
+ type: "boolean",
1347
+ description: "Force compact mode for narrow displays (better for screenshots)",
1348
+ default: false
1349
+ }
1350
+ };
1351
+ const sharedCommandConfig = {
1352
+ args: sharedArgs,
1353
+ toKebab: true
1354
+ };
1355
+ function formatBlockTime(block, compact = false, locale) {
1356
+ const start = compact ? block.startTime.toLocaleString(locale, {
1357
+ month: "2-digit",
1358
+ day: "2-digit",
1359
+ hour: "2-digit",
1360
+ minute: "2-digit"
1361
+ }) : block.startTime.toLocaleString(locale);
1362
+ if (block.isGap ?? false) {
1363
+ const end = compact ? block.endTime.toLocaleString(locale, {
1364
+ hour: "2-digit",
1365
+ minute: "2-digit"
1366
+ }) : block.endTime.toLocaleString(locale);
1367
+ const duration$1 = Math.round((block.endTime.getTime() - block.startTime.getTime()) / (1e3 * 60 * 60));
1368
+ return compact ? `${start}-${end}\n(${duration$1}h gap)` : `${start} - ${end} (${duration$1}h gap)`;
1369
+ }
1370
+ const duration = block.actualEndTime != null ? Math.round((block.actualEndTime.getTime() - block.startTime.getTime()) / (1e3 * 60)) : 0;
1371
+ if (block.isActive) {
1372
+ const now = /* @__PURE__ */ new Date();
1373
+ const elapsed = Math.round((now.getTime() - block.startTime.getTime()) / (1e3 * 60));
1374
+ const remaining = Math.round((block.endTime.getTime() - now.getTime()) / (1e3 * 60));
1375
+ const elapsedHours = Math.floor(elapsed / 60);
1376
+ const elapsedMins = elapsed % 60;
1377
+ const remainingHours = Math.floor(remaining / 60);
1378
+ const remainingMins = remaining % 60;
1379
+ if (compact) return `${start}\n(${elapsedHours}h${elapsedMins}m/${remainingHours}h${remainingMins}m)`;
1380
+ return `${start} (${elapsedHours}h ${elapsedMins}m elapsed, ${remainingHours}h ${remainingMins}m remaining)`;
1381
+ }
1382
+ const hours = Math.floor(duration / 60);
1383
+ const mins = duration % 60;
1384
+ if (compact) return hours > 0 ? `${start}\n(${hours}h${mins}m)` : `${start}\n(${mins}m)`;
1385
+ if (hours > 0) return `${start} (${hours}h ${mins}m)`;
1386
+ return `${start} (${mins}m)`;
1387
+ }
1388
+ function formatModels(models) {
1389
+ if (models.length === 0) return "-";
1390
+ return formatModelsDisplayMultiline(models);
1391
+ }
1392
+ function parseTokenLimit(value$1, maxFromAll) {
1393
+ if (value$1 == null || value$1 === "" || value$1 === "max") return maxFromAll > 0 ? maxFromAll : void 0;
1394
+ const limit = Number.parseInt(value$1, 10);
1395
+ return Number.isNaN(limit) ? void 0 : limit;
1396
+ }
1397
+ const blocksCommand = define({
1398
+ name: "blocks",
1399
+ description: "Show usage report grouped by session billing blocks",
1400
+ args: {
1401
+ ...sharedCommandConfig.args,
1402
+ active: {
1403
+ type: "boolean",
1404
+ short: "a",
1405
+ description: "Show only active block with projections",
1406
+ default: false
1407
+ },
1408
+ recent: {
1409
+ type: "boolean",
1410
+ short: "r",
1411
+ description: `Show blocks from last ${DEFAULT_RECENT_DAYS} days (including active)`,
1412
+ default: false
1413
+ },
1414
+ tokenLimit: {
1415
+ type: "string",
1416
+ short: "t",
1417
+ description: "Token limit for quota warnings (e.g., 500000 or \"max\")"
1418
+ },
1419
+ sessionLength: {
1420
+ type: "number",
1421
+ short: "n",
1422
+ description: `Session block duration in hours (default: ${DEFAULT_SESSION_DURATION_HOURS})`,
1423
+ default: DEFAULT_SESSION_DURATION_HOURS
1424
+ }
1425
+ },
1426
+ toKebab: true,
1427
+ async run(ctx) {
1428
+ const config = loadConfig(ctx.values.config, ctx.values.debug);
1429
+ const mergedOptions = mergeConfigWithArgs(ctx, config, ctx.values.debug);
1430
+ const useJson = mergedOptions.json || mergedOptions.jq != null;
1431
+ if (useJson) logger.level = 0;
1432
+ if (ctx.values.sessionLength <= 0) {
1433
+ logger.error("Session length must be a positive number");
1434
+ process$1.exit(1);
1435
+ }
1436
+ let blocks = await loadSessionBlockData({
1437
+ since: ctx.values.since,
1438
+ until: ctx.values.until,
1439
+ mode: ctx.values.mode,
1440
+ order: ctx.values.order,
1441
+ offline: ctx.values.offline,
1442
+ sessionDurationHours: ctx.values.sessionLength,
1443
+ timezone: ctx.values.timezone,
1444
+ locale: ctx.values.locale,
1445
+ modelAliases: config?.modelAliases
1446
+ });
1447
+ if (blocks.length === 0) {
1448
+ if (useJson) log(JSON.stringify({ blocks: [] }));
1449
+ else logger.warn("No Claude usage data found.");
1450
+ process$1.exit(0);
1451
+ }
1452
+ let maxTokensFromAll = 0;
1453
+ if (ctx.values.tokenLimit === "max" || ctx.values.tokenLimit == null || ctx.values.tokenLimit === "") {
1454
+ for (const block of blocks) if (!(block.isGap ?? false) && !block.isActive) {
1455
+ const blockTokens = getTotalTokens(block.tokenCounts);
1456
+ if (blockTokens > maxTokensFromAll) maxTokensFromAll = blockTokens;
1457
+ }
1458
+ if (!useJson && maxTokensFromAll > 0) logger.info(`Using max tokens from previous sessions: ${formatNumber(maxTokensFromAll)}`);
1459
+ }
1460
+ if (ctx.values.recent) blocks = filterRecentBlocks(blocks, DEFAULT_RECENT_DAYS);
1461
+ if (ctx.values.active) {
1462
+ blocks = blocks.filter((block) => block.isActive);
1463
+ if (blocks.length === 0) {
1464
+ if (useJson) log(JSON.stringify({
1465
+ blocks: [],
1466
+ message: "No active block"
1467
+ }));
1468
+ else logger.info("No active session block found.");
1469
+ process$1.exit(0);
1470
+ }
1471
+ }
1472
+ if (useJson) {
1473
+ const jsonOutput = { blocks: blocks.map((block) => {
1474
+ const burnRate = block.isActive ? calculateBurnRate(block) : null;
1475
+ const projection = block.isActive ? projectBlockUsage(block) : null;
1476
+ return {
1477
+ id: block.id,
1478
+ startTime: block.startTime.toISOString(),
1479
+ endTime: block.endTime.toISOString(),
1480
+ actualEndTime: block.actualEndTime?.toISOString() ?? null,
1481
+ isActive: block.isActive,
1482
+ isGap: block.isGap ?? false,
1483
+ entries: block.entries.length,
1484
+ tokenCounts: block.tokenCounts,
1485
+ totalTokens: getTotalTokens(block.tokenCounts),
1486
+ costUSD: block.costUSD,
1487
+ models: block.models,
1488
+ burnRate,
1489
+ projection,
1490
+ tokenLimitStatus: projection != null && ctx.values.tokenLimit != null ? (() => {
1491
+ const limit = parseTokenLimit(ctx.values.tokenLimit, maxTokensFromAll);
1492
+ return limit != null ? {
1493
+ limit,
1494
+ projectedUsage: projection.totalTokens,
1495
+ percentUsed: projection.totalTokens / limit * 100,
1496
+ status: projection.totalTokens > limit ? "exceeds" : projection.totalTokens > limit * BLOCKS_WARNING_THRESHOLD ? "warning" : "ok"
1497
+ } : void 0;
1498
+ })() : void 0,
1499
+ usageLimitResetTime: block.usageLimitResetTime
1500
+ };
1501
+ }) };
1502
+ if (ctx.values.jq != null) {
1503
+ const jqResult = await processWithJq(jsonOutput, ctx.values.jq);
1504
+ if (isFailure(jqResult)) {
1505
+ logger.error(jqResult.error.message);
1506
+ process$1.exit(1);
1507
+ }
1508
+ log(jqResult.value);
1509
+ } else log(JSON.stringify(jsonOutput, null, 2));
1510
+ } else if (ctx.values.active && blocks.length === 1) {
1511
+ const block = blocks[0];
1512
+ if (block == null) {
1513
+ logger.warn("No active block found.");
1514
+ process$1.exit(0);
1515
+ }
1516
+ const burnRate = calculateBurnRate(block);
1517
+ const projection = projectBlockUsage(block);
1518
+ logger.box("Current Session Block Status");
1519
+ const now = /* @__PURE__ */ new Date();
1520
+ const elapsed = Math.round((now.getTime() - block.startTime.getTime()) / (1e3 * 60));
1521
+ const remaining = Math.round((block.endTime.getTime() - now.getTime()) / (1e3 * 60));
1522
+ log(`Block Started: ${import_picocolors$2.default.cyan(block.startTime.toLocaleString())} (${import_picocolors$2.default.yellow(`${Math.floor(elapsed / 60)}h ${elapsed % 60}m`)} ago)`);
1523
+ log(`Time Remaining: ${import_picocolors$2.default.green(`${Math.floor(remaining / 60)}h ${remaining % 60}m`)}\n`);
1524
+ log(import_picocolors$2.default.bold("Current Usage:"));
1525
+ log(` Input Tokens: ${formatNumber(block.tokenCounts.inputTokens)}`);
1526
+ log(` Output Tokens: ${formatNumber(block.tokenCounts.outputTokens)}`);
1527
+ log(` Total Cost: ${formatCurrency(block.costUSD)}\n`);
1528
+ if (burnRate != null) {
1529
+ log(import_picocolors$2.default.bold("Burn Rate:"));
1530
+ log(` Tokens/minute: ${formatNumber(burnRate.tokensPerMinute)}`);
1531
+ log(` Cost/hour: ${formatCurrency(burnRate.costPerHour)}\n`);
1532
+ }
1533
+ if (projection != null) {
1534
+ log(import_picocolors$2.default.bold("Projected Usage (if current rate continues):"));
1535
+ log(` Total Tokens: ${formatNumber(projection.totalTokens)}`);
1536
+ log(` Total Cost: ${formatCurrency(projection.totalCost)}\n`);
1537
+ if (ctx.values.tokenLimit != null) {
1538
+ const limit = parseTokenLimit(ctx.values.tokenLimit, maxTokensFromAll);
1539
+ if (limit != null && limit > 0) {
1540
+ const currentTokens = getTotalTokens(block.tokenCounts);
1541
+ const remainingTokens = Math.max(0, limit - currentTokens);
1542
+ const percentUsed = projection.totalTokens / limit * 100;
1543
+ const status = percentUsed > 100 ? import_picocolors$2.default.red("EXCEEDS LIMIT") : percentUsed > BLOCKS_WARNING_THRESHOLD * 100 ? import_picocolors$2.default.yellow("WARNING") : import_picocolors$2.default.green("OK");
1544
+ log(import_picocolors$2.default.bold("Token Limit Status:"));
1545
+ log(` Limit: ${formatNumber(limit)} tokens`);
1546
+ log(` Current Usage: ${formatNumber(currentTokens)} (${(currentTokens / limit * 100).toFixed(1)}%)`);
1547
+ log(` Remaining: ${formatNumber(remainingTokens)} tokens`);
1548
+ log(` Projected Usage: ${percentUsed.toFixed(1)}% ${status}`);
1549
+ }
1550
+ }
1551
+ }
1552
+ } else {
1553
+ logger.box("Claude Code Token Usage Report - Session Blocks");
1554
+ const actualTokenLimit = parseTokenLimit(ctx.values.tokenLimit, maxTokensFromAll);
1555
+ const tableHeaders = [
1556
+ "Block Start",
1557
+ "Duration/Status",
1558
+ "Models",
1559
+ "Tokens"
1560
+ ];
1561
+ const tableAligns = [
1562
+ "left",
1563
+ "left",
1564
+ "left",
1565
+ "right"
1566
+ ];
1567
+ if (actualTokenLimit != null && actualTokenLimit > 0) {
1568
+ tableHeaders.push("%");
1569
+ tableAligns.push("right");
1570
+ }
1571
+ tableHeaders.push("Cost");
1572
+ tableAligns.push("right");
1573
+ const table = new ResponsiveTable({
1574
+ head: tableHeaders,
1575
+ style: { head: ["cyan"] },
1576
+ colAligns: tableAligns
1577
+ });
1578
+ const isNarrowTerminal = (process$1.stdout.columns || BLOCKS_DEFAULT_TERMINAL_WIDTH) < BLOCKS_COMPACT_WIDTH_THRESHOLD;
1579
+ const useCompactFormat = ctx.values.compact || isNarrowTerminal;
1580
+ for (const block of blocks) if (block.isGap ?? false) {
1581
+ const gapRow = [
1582
+ import_picocolors$2.default.gray(formatBlockTime(block, useCompactFormat, ctx.values.locale)),
1583
+ import_picocolors$2.default.gray("(inactive)"),
1584
+ import_picocolors$2.default.gray("-"),
1585
+ import_picocolors$2.default.gray("-")
1586
+ ];
1587
+ if (actualTokenLimit != null && actualTokenLimit > 0) gapRow.push(import_picocolors$2.default.gray("-"));
1588
+ gapRow.push(import_picocolors$2.default.gray("-"));
1589
+ table.push(gapRow);
1590
+ } else {
1591
+ const totalTokens = getTotalTokens(block.tokenCounts);
1592
+ const status = block.isActive ? import_picocolors$2.default.green("ACTIVE") : "";
1593
+ const row = [
1594
+ formatBlockTime(block, useCompactFormat, ctx.values.locale),
1595
+ status,
1596
+ formatModels(block.models),
1597
+ formatNumber(totalTokens)
1598
+ ];
1599
+ if (actualTokenLimit != null && actualTokenLimit > 0) {
1600
+ const percentage = totalTokens / actualTokenLimit * 100;
1601
+ const percentText = `${percentage.toFixed(1)}%`;
1602
+ row.push(percentage > 100 ? import_picocolors$2.default.red(percentText) : percentText);
1603
+ }
1604
+ row.push(formatCurrency(block.costUSD));
1605
+ table.push(row);
1606
+ if (block.isActive) {
1607
+ if (actualTokenLimit != null && actualTokenLimit > 0) {
1608
+ const currentTokens = getTotalTokens(block.tokenCounts);
1609
+ const remainingTokens = Math.max(0, actualTokenLimit - currentTokens);
1610
+ const remainingText = remainingTokens > 0 ? formatNumber(remainingTokens) : import_picocolors$2.default.red("0");
1611
+ const remainingPercent = (actualTokenLimit - currentTokens) / actualTokenLimit * 100;
1612
+ const remainingPercentText = remainingPercent > 0 ? `${remainingPercent.toFixed(1)}%` : import_picocolors$2.default.red("0.0%");
1613
+ const remainingRow = [
1614
+ {
1615
+ content: import_picocolors$2.default.gray(`(assuming ${formatNumber(actualTokenLimit)} token limit)`),
1616
+ hAlign: "right"
1617
+ },
1618
+ import_picocolors$2.default.blue("REMAINING"),
1619
+ "",
1620
+ remainingText,
1621
+ remainingPercentText,
1622
+ ""
1623
+ ];
1624
+ table.push(remainingRow);
1625
+ }
1626
+ const projection = projectBlockUsage(block);
1627
+ if (projection != null) {
1628
+ const projectedTokens = formatNumber(projection.totalTokens);
1629
+ const projectedText = actualTokenLimit != null && actualTokenLimit > 0 && projection.totalTokens > actualTokenLimit ? import_picocolors$2.default.red(projectedTokens) : projectedTokens;
1630
+ const projectedRow = [
1631
+ {
1632
+ content: import_picocolors$2.default.gray("(assuming current burn rate)"),
1633
+ hAlign: "right"
1634
+ },
1635
+ import_picocolors$2.default.yellow("PROJECTED"),
1636
+ "",
1637
+ projectedText
1638
+ ];
1639
+ if (actualTokenLimit != null && actualTokenLimit > 0) {
1640
+ const percentText = `${(projection.totalTokens / actualTokenLimit * 100).toFixed(1)}%`;
1641
+ projectedRow.push(percentText);
1642
+ }
1643
+ projectedRow.push(formatCurrency(projection.totalCost));
1644
+ table.push(projectedRow);
1645
+ }
1646
+ }
1647
+ }
1648
+ log(table.toString());
1649
+ }
1650
+ }
1651
+ });
1652
+ function groupByProject(dailyData) {
1653
+ const projects = {};
1654
+ for (const data of dailyData) {
1655
+ const projectName = data.project ?? "unknown";
1656
+ if (projects[projectName] == null) projects[projectName] = [];
1657
+ projects[projectName].push({
1658
+ date: data.date,
1659
+ inputTokens: data.inputTokens,
1660
+ outputTokens: data.outputTokens,
1661
+ cacheCreationTokens: data.cacheCreationTokens,
1662
+ cacheReadTokens: data.cacheReadTokens,
1663
+ totalTokens: getTotalTokens(data),
1664
+ totalCost: data.totalCost,
1665
+ modelsUsed: data.modelsUsed,
1666
+ modelBreakdowns: data.modelBreakdowns
1667
+ });
1668
+ }
1669
+ return projects;
1670
+ }
1671
+ function groupDataByProject(dailyData) {
1672
+ const projects = {};
1673
+ for (const data of dailyData) {
1674
+ const projectName = data.project ?? "unknown";
1675
+ if (projects[projectName] == null) projects[projectName] = [];
1676
+ projects[projectName].push(data);
1677
+ }
1678
+ return projects;
1679
+ }
1680
+ function parseProjectName(projectName) {
1681
+ if (projectName === "unknown" || projectName === "") return "Unknown Project";
1682
+ let cleaned = projectName;
1683
+ if (cleaned.match(/^[A-Z]:\\Users\\|^\\Users\\/) != null) {
1684
+ const segments = cleaned.split("\\");
1685
+ const userIndex = segments.findIndex((seg) => seg === "Users");
1686
+ if (userIndex !== -1 && userIndex + 3 < segments.length) cleaned = segments.slice(userIndex + 3).join("-");
1687
+ }
1688
+ if (cleaned.startsWith("-Users-") || cleaned.startsWith("/Users/")) {
1689
+ const separator = cleaned.startsWith("-Users-") ? "-" : "/";
1690
+ const segments = cleaned.split(separator).filter((s) => s.length > 0);
1691
+ const userIndex = segments.findIndex((seg) => seg === "Users");
1692
+ if (userIndex !== -1 && userIndex + 3 < segments.length) cleaned = segments.slice(userIndex + 3).join("-");
1693
+ }
1694
+ if (cleaned === projectName) cleaned = projectName.replace(/^[/\\-]+|[/\\-]+$/g, "");
1695
+ if (cleaned.match(/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}/i) != null) {
1696
+ const parts = cleaned.split("-");
1697
+ if (parts.length >= 5) cleaned = parts.slice(-2).join("-");
1698
+ }
1699
+ if (cleaned.includes("--")) {
1700
+ const parts = cleaned.split("--");
1701
+ if (parts.length >= 2 && parts[0] != null) cleaned = parts[0];
1702
+ }
1703
+ if (cleaned.includes("-") && cleaned.length > 20) {
1704
+ const meaningfulSegments = cleaned.split("-").filter((seg) => seg.length > 2 && seg.match(/^(?:dev|development|feat|feature|fix|bug|test|staging|prod|production|main|master|branch)$/i) == null);
1705
+ if (meaningfulSegments.length >= 2) {
1706
+ const lastSegments = meaningfulSegments.slice(-2);
1707
+ if (lastSegments.join("-").length >= 6) cleaned = lastSegments.join("-");
1708
+ else if (meaningfulSegments.length >= 3) cleaned = meaningfulSegments.slice(-3).join("-");
1709
+ }
1710
+ }
1711
+ cleaned = cleaned.replace(/^[/\\-]+|[/\\-]+$/g, "");
1712
+ return cleaned !== "" ? cleaned : projectName !== "" ? projectName : "Unknown Project";
1713
+ }
1714
+ function formatProjectName(projectName, aliases) {
1715
+ if (aliases != null && aliases.has(projectName)) return aliases.get(projectName);
1716
+ const parsed = parseProjectName(projectName);
1717
+ if (aliases != null && aliases.has(parsed)) return aliases.get(parsed);
1718
+ return parsed;
1719
+ }
1720
+ var import_picocolors$1 = /* @__PURE__ */ __toESM(require_picocolors(), 1);
1721
+ const dailyCommand = define({
1722
+ name: "daily",
1723
+ description: "Show usage report grouped by date",
1724
+ ...sharedCommandConfig,
1725
+ args: {
1726
+ ...sharedCommandConfig.args,
1727
+ instances: {
1728
+ type: "boolean",
1729
+ short: "i",
1730
+ description: "Show usage breakdown by project/instance",
1731
+ default: false
1732
+ },
1733
+ project: {
1734
+ type: "string",
1735
+ short: "p",
1736
+ description: "Filter to specific project name"
1737
+ },
1738
+ projectAliases: {
1739
+ type: "string",
1740
+ description: "Comma-separated project aliases (e.g., 'ccusage=Usage Tracker,myproject=My Project')",
1741
+ hidden: true
1742
+ }
1743
+ },
1744
+ async run(ctx) {
1745
+ const config = loadConfig(ctx.values.config, ctx.values.debug);
1746
+ const mergedOptions = mergeConfigWithArgs(ctx, config, ctx.values.debug);
1747
+ let projectAliases;
1748
+ if (mergedOptions.projectAliases != null && typeof mergedOptions.projectAliases === "string") {
1749
+ projectAliases = /* @__PURE__ */ new Map();
1750
+ const pairs$1 = mergedOptions.projectAliases.split(",").map((pair$1) => pair$1.trim()).filter((pair$1) => pair$1 !== "");
1751
+ for (const pair$1 of pairs$1) {
1752
+ const parts = pair$1.split("=").map((s) => s.trim());
1753
+ const rawName = parts[0];
1754
+ const alias = parts[1];
1755
+ if (rawName != null && alias != null && rawName !== "" && alias !== "") projectAliases.set(rawName, alias);
1756
+ }
1757
+ }
1758
+ const useJson = Boolean(mergedOptions.json) || mergedOptions.jq != null;
1759
+ if (useJson) logger.level = 0;
1760
+ const dailyData = await loadDailyUsageData({
1761
+ ...mergedOptions,
1762
+ groupByProject: mergedOptions.instances,
1763
+ modelAliases: config?.modelAliases
1764
+ });
1765
+ if (dailyData.length === 0) {
1766
+ if (useJson) log(JSON.stringify([]));
1767
+ else logger.warn("No Claude usage data found.");
1768
+ process$1.exit(0);
1769
+ }
1770
+ const totals = calculateTotals(dailyData);
1771
+ if (mergedOptions.debug && !useJson) printMismatchReport(await detectMismatches(void 0), mergedOptions.debugSamples);
1772
+ if (useJson) {
1773
+ const jsonOutput = Boolean(mergedOptions.instances) && dailyData.some((d) => d.project != null) ? {
1774
+ projects: groupByProject(dailyData),
1775
+ totals: createTotalsObject(totals)
1776
+ } : {
1777
+ daily: dailyData.map((data) => ({
1778
+ date: data.date,
1779
+ inputTokens: data.inputTokens,
1780
+ outputTokens: data.outputTokens,
1781
+ cacheCreationTokens: data.cacheCreationTokens,
1782
+ cacheReadTokens: data.cacheReadTokens,
1783
+ totalTokens: getTotalTokens(data),
1784
+ totalCost: data.totalCost,
1785
+ modelsUsed: data.modelsUsed,
1786
+ modelBreakdowns: data.modelBreakdowns,
1787
+ ...data.project != null && { project: data.project }
1788
+ })),
1789
+ totals: createTotalsObject(totals)
1790
+ };
1791
+ if (mergedOptions.jq != null) {
1792
+ const jqResult = await processWithJq(jsonOutput, mergedOptions.jq);
1793
+ if (isFailure(jqResult)) {
1794
+ logger.error(jqResult.error.message);
1795
+ process$1.exit(1);
1796
+ }
1797
+ log(jqResult.value);
1798
+ } else log(JSON.stringify(jsonOutput, null, 2));
1799
+ } else {
1800
+ logger.box("Claude Code Token Usage Report - Daily");
1801
+ const table = createUsageReportTable({
1802
+ firstColumnName: "Date",
1803
+ dateFormatter: (dateStr) => formatDateCompact(dateStr, mergedOptions.timezone, mergedOptions.locale ?? void 0),
1804
+ forceCompact: ctx.values.compact
1805
+ });
1806
+ if (Boolean(mergedOptions.instances) && dailyData.some((d) => d.project != null)) {
1807
+ const projectGroups = groupDataByProject(dailyData);
1808
+ let isFirstProject = true;
1809
+ for (const [projectName, projectData] of Object.entries(projectGroups)) {
1810
+ if (!isFirstProject) table.push([
1811
+ "",
1812
+ "",
1813
+ "",
1814
+ "",
1815
+ "",
1816
+ "",
1817
+ "",
1818
+ ""
1819
+ ]);
1820
+ table.push([
1821
+ import_picocolors$1.default.cyan(`Project: ${formatProjectName(projectName, projectAliases)}`),
1822
+ "",
1823
+ "",
1824
+ "",
1825
+ "",
1826
+ "",
1827
+ "",
1828
+ ""
1829
+ ]);
1830
+ for (const data of projectData) {
1831
+ const row = formatUsageDataRow(data.date, {
1832
+ inputTokens: data.inputTokens,
1833
+ outputTokens: data.outputTokens,
1834
+ cacheCreationTokens: data.cacheCreationTokens,
1835
+ cacheReadTokens: data.cacheReadTokens,
1836
+ totalCost: data.totalCost,
1837
+ modelsUsed: data.modelsUsed
1838
+ });
1839
+ table.push(row);
1840
+ if (mergedOptions.breakdown) pushBreakdownRows(table, data.modelBreakdowns);
1841
+ }
1842
+ isFirstProject = false;
1843
+ }
1844
+ } else for (const data of dailyData) {
1845
+ const row = formatUsageDataRow(data.date, {
1846
+ inputTokens: data.inputTokens,
1847
+ outputTokens: data.outputTokens,
1848
+ cacheCreationTokens: data.cacheCreationTokens,
1849
+ cacheReadTokens: data.cacheReadTokens,
1850
+ totalCost: data.totalCost,
1851
+ modelsUsed: data.modelsUsed
1852
+ });
1853
+ table.push(row);
1854
+ if (mergedOptions.breakdown) pushBreakdownRows(table, data.modelBreakdowns);
1855
+ }
1856
+ addEmptySeparatorRow(table, 8);
1857
+ const totalsRow = formatTotalsRow({
1858
+ inputTokens: totals.inputTokens,
1859
+ outputTokens: totals.outputTokens,
1860
+ cacheCreationTokens: totals.cacheCreationTokens,
1861
+ cacheReadTokens: totals.cacheReadTokens,
1862
+ totalCost: totals.totalCost
1863
+ });
1864
+ table.push(totalsRow);
1865
+ log(table.toString());
1866
+ if (table.isCompactMode()) {
1867
+ logger.info("\nRunning in Compact Mode");
1868
+ logger.info("Expand terminal width to see cache metrics and total tokens");
1869
+ }
1870
+ }
1871
+ }
1872
+ });
1873
+ const monthlyCommand = define({
1874
+ name: "monthly",
1875
+ description: "Show usage report grouped by month",
1876
+ ...sharedCommandConfig,
1877
+ async run(ctx) {
1878
+ const config = loadConfig(ctx.values.config, ctx.values.debug);
1879
+ const mergedOptions = mergeConfigWithArgs(ctx, config, ctx.values.debug);
1880
+ const useJson = Boolean(mergedOptions.json) || mergedOptions.jq != null;
1881
+ if (useJson) logger.level = 0;
1882
+ const monthlyData = await loadMonthlyUsageData({
1883
+ ...mergedOptions,
1884
+ modelAliases: config?.modelAliases
1885
+ });
1886
+ if (monthlyData.length === 0) {
1887
+ if (useJson) log(JSON.stringify({
1888
+ monthly: [],
1889
+ totals: {
1890
+ inputTokens: 0,
1891
+ outputTokens: 0,
1892
+ cacheCreationTokens: 0,
1893
+ cacheReadTokens: 0,
1894
+ totalTokens: 0,
1895
+ totalCost: 0
1896
+ }
1897
+ }, null, 2));
1898
+ else logger.warn("No Claude usage data found.");
1899
+ process$1.exit(0);
1900
+ }
1901
+ const totals = calculateTotals(monthlyData);
1902
+ if (mergedOptions.debug && !useJson) printMismatchReport(await detectMismatches(void 0), mergedOptions.debugSamples);
1903
+ if (useJson) {
1904
+ const jsonOutput = {
1905
+ monthly: monthlyData.map((data) => ({
1906
+ month: data.month,
1907
+ inputTokens: data.inputTokens,
1908
+ outputTokens: data.outputTokens,
1909
+ cacheCreationTokens: data.cacheCreationTokens,
1910
+ cacheReadTokens: data.cacheReadTokens,
1911
+ totalTokens: getTotalTokens(data),
1912
+ totalCost: data.totalCost,
1913
+ modelsUsed: data.modelsUsed,
1914
+ modelBreakdowns: data.modelBreakdowns
1915
+ })),
1916
+ totals: createTotalsObject(totals)
1917
+ };
1918
+ if (mergedOptions.jq != null) {
1919
+ const jqResult = await processWithJq(jsonOutput, mergedOptions.jq);
1920
+ if (isFailure(jqResult)) {
1921
+ logger.error(jqResult.error.message);
1922
+ process$1.exit(1);
1923
+ }
1924
+ log(jqResult.value);
1925
+ } else log(JSON.stringify(jsonOutput, null, 2));
1926
+ } else {
1927
+ logger.box("Claude Code Token Usage Report - Monthly");
1928
+ const table = createUsageReportTable({
1929
+ firstColumnName: "Month",
1930
+ dateFormatter: (dateStr) => formatDateCompact(dateStr, mergedOptions.timezone, mergedOptions.locale ?? DEFAULT_LOCALE),
1931
+ forceCompact: ctx.values.compact
1932
+ });
1933
+ for (const data of monthlyData) {
1934
+ const row = formatUsageDataRow(data.month, {
1935
+ inputTokens: data.inputTokens,
1936
+ outputTokens: data.outputTokens,
1937
+ cacheCreationTokens: data.cacheCreationTokens,
1938
+ cacheReadTokens: data.cacheReadTokens,
1939
+ totalCost: data.totalCost,
1940
+ modelsUsed: data.modelsUsed
1941
+ });
1942
+ table.push(row);
1943
+ if (mergedOptions.breakdown) pushBreakdownRows(table, data.modelBreakdowns);
1944
+ }
1945
+ addEmptySeparatorRow(table, 8);
1946
+ const totalsRow = formatTotalsRow({
1947
+ inputTokens: totals.inputTokens,
1948
+ outputTokens: totals.outputTokens,
1949
+ cacheCreationTokens: totals.cacheCreationTokens,
1950
+ cacheReadTokens: totals.cacheReadTokens,
1951
+ totalCost: totals.totalCost
1952
+ });
1953
+ table.push(totalsRow);
1954
+ log(table.toString());
1955
+ if (table.isCompactMode()) {
1956
+ logger.info("\nRunning in Compact Mode");
1957
+ logger.info("Expand terminal width to see cache metrics and total tokens");
1958
+ }
1959
+ }
1960
+ }
1961
+ });
1962
+ async function handleSessionIdLookup(ctx, useJson) {
1963
+ const sessionUsage = await loadSessionUsageById(ctx.values.id, {
1964
+ mode: ctx.values.mode,
1965
+ offline: ctx.values.offline,
1966
+ modelAliases: ctx.values.modelAliases
1967
+ });
1968
+ if (sessionUsage == null) {
1969
+ if (useJson) log(JSON.stringify(null));
1970
+ else logger.warn(`No session found with ID: ${ctx.values.id}`);
1971
+ process$1.exit(0);
1972
+ }
1973
+ if (useJson) {
1974
+ const jsonOutput = {
1975
+ sessionId: ctx.values.id,
1976
+ totalCost: sessionUsage.totalCost,
1977
+ totalTokens: calculateSessionTotalTokens(sessionUsage.entries),
1978
+ entries: sessionUsage.entries.map((entry) => ({
1979
+ timestamp: entry.timestamp,
1980
+ inputTokens: entry.message.usage.input_tokens,
1981
+ outputTokens: entry.message.usage.output_tokens,
1982
+ cacheCreationTokens: entry.message.usage.cache_creation_input_tokens ?? 0,
1983
+ cacheReadTokens: entry.message.usage.cache_read_input_tokens ?? 0,
1984
+ model: entry.message.model ?? "unknown",
1985
+ costUSD: entry.costUSD ?? 0
1986
+ }))
1987
+ };
1988
+ if (ctx.values.jq != null) {
1989
+ const jqResult = await processWithJq(jsonOutput, ctx.values.jq);
1990
+ if (isFailure(jqResult)) {
1991
+ logger.error(jqResult.error.message);
1992
+ process$1.exit(1);
1993
+ }
1994
+ log(jqResult.value);
1995
+ } else log(JSON.stringify(jsonOutput, null, 2));
1996
+ } else {
1997
+ logger.box(`Claude Code Session Usage - ${ctx.values.id}`);
1998
+ const totalTokens = calculateSessionTotalTokens(sessionUsage.entries);
1999
+ log(`Total Cost: ${formatCurrency(sessionUsage.totalCost)}`);
2000
+ log(`Total Tokens: ${formatNumber(totalTokens)}`);
2001
+ log(`Total Entries: ${sessionUsage.entries.length}`);
2002
+ log("");
2003
+ if (sessionUsage.entries.length > 0) {
2004
+ const table = new ResponsiveTable({
2005
+ head: [
2006
+ "Timestamp",
2007
+ "Model",
2008
+ "Input",
2009
+ "Output",
2010
+ "Cache Create",
2011
+ "Cache Read",
2012
+ "Cost (USD)"
2013
+ ],
2014
+ style: { head: ["cyan"] },
2015
+ colAligns: [
2016
+ "left",
2017
+ "left",
2018
+ "right",
2019
+ "right",
2020
+ "right",
2021
+ "right",
2022
+ "right"
2023
+ ]
2024
+ });
2025
+ for (const entry of sessionUsage.entries) table.push([
2026
+ formatDateCompact(entry.timestamp, ctx.values.timezone, ctx.values.locale),
2027
+ entry.message.model ?? "unknown",
2028
+ formatNumber(entry.message.usage.input_tokens),
2029
+ formatNumber(entry.message.usage.output_tokens),
2030
+ formatNumber(entry.message.usage.cache_creation_input_tokens ?? 0),
2031
+ formatNumber(entry.message.usage.cache_read_input_tokens ?? 0),
2032
+ formatCurrency(entry.costUSD ?? 0)
2033
+ ]);
2034
+ log(table.toString());
2035
+ }
2036
+ }
2037
+ }
2038
+ function calculateSessionTotalTokens(entries) {
2039
+ return entries.reduce((sum, entry) => {
2040
+ const usage = entry.message.usage;
2041
+ return sum + usage.input_tokens + usage.output_tokens + (usage.cache_creation_input_tokens ?? 0) + (usage.cache_read_input_tokens ?? 0);
2042
+ }, 0);
2043
+ }
2044
+ const { order: _, ...sharedArgs$1 } = sharedCommandConfig.args;
2045
+ const sessionCommand = define({
2046
+ name: "session",
2047
+ description: "Show usage report grouped by conversation session",
2048
+ ...sharedCommandConfig,
2049
+ args: {
2050
+ ...sharedArgs$1,
2051
+ id: {
2052
+ type: "string",
2053
+ short: "i",
2054
+ description: "Load usage data for a specific session ID"
2055
+ }
2056
+ },
2057
+ toKebab: true,
2058
+ async run(ctx) {
2059
+ const config = loadConfig(ctx.values.config, ctx.values.debug);
2060
+ const mergedOptions = mergeConfigWithArgs(ctx, config, ctx.values.debug);
2061
+ const useJson = mergedOptions.json || mergedOptions.jq != null;
2062
+ if (useJson) logger.level = 0;
2063
+ if (mergedOptions.id != null) return handleSessionIdLookup({ values: {
2064
+ id: mergedOptions.id,
2065
+ mode: mergedOptions.mode,
2066
+ offline: mergedOptions.offline,
2067
+ jq: mergedOptions.jq,
2068
+ timezone: mergedOptions.timezone,
2069
+ locale: mergedOptions.locale ?? DEFAULT_LOCALE,
2070
+ modelAliases: config?.modelAliases
2071
+ } }, useJson);
2072
+ const sessionData = await loadSessionData({
2073
+ since: ctx.values.since,
2074
+ until: ctx.values.until,
2075
+ mode: ctx.values.mode,
2076
+ offline: ctx.values.offline,
2077
+ timezone: ctx.values.timezone,
2078
+ locale: ctx.values.locale,
2079
+ modelAliases: config?.modelAliases
2080
+ });
2081
+ if (sessionData.length === 0) {
2082
+ if (useJson) log(JSON.stringify([]));
2083
+ else logger.warn("No Claude usage data found.");
2084
+ process$1.exit(0);
2085
+ }
2086
+ const totals = calculateTotals(sessionData);
2087
+ if (ctx.values.debug && !useJson) printMismatchReport(await detectMismatches(void 0), ctx.values.debugSamples);
2088
+ if (useJson) {
2089
+ const jsonOutput = {
2090
+ sessions: sessionData.map((data) => ({
2091
+ sessionId: data.sessionId,
2092
+ inputTokens: data.inputTokens,
2093
+ outputTokens: data.outputTokens,
2094
+ cacheCreationTokens: data.cacheCreationTokens,
2095
+ cacheReadTokens: data.cacheReadTokens,
2096
+ totalTokens: getTotalTokens(data),
2097
+ totalCost: data.totalCost,
2098
+ lastActivity: data.lastActivity,
2099
+ modelsUsed: data.modelsUsed,
2100
+ modelBreakdowns: data.modelBreakdowns,
2101
+ projectPath: data.projectPath
2102
+ })),
2103
+ totals: createTotalsObject(totals)
2104
+ };
2105
+ if (ctx.values.jq != null) {
2106
+ const jqResult = await processWithJq(jsonOutput, ctx.values.jq);
2107
+ if (isFailure(jqResult)) {
2108
+ logger.error(jqResult.error.message);
2109
+ process$1.exit(1);
2110
+ }
2111
+ log(jqResult.value);
2112
+ } else log(JSON.stringify(jsonOutput, null, 2));
2113
+ } else {
2114
+ logger.box("Claude Code Token Usage Report - By Session");
2115
+ const table = createUsageReportTable({
2116
+ firstColumnName: "Session",
2117
+ includeLastActivity: true,
2118
+ dateFormatter: (dateStr) => formatDateCompact(dateStr, ctx.values.timezone, ctx.values.locale),
2119
+ forceCompact: ctx.values.compact
2120
+ });
2121
+ let maxSessionLength = 0;
2122
+ for (const data of sessionData) {
2123
+ const sessionDisplay = data.sessionId.split("-").slice(-2).join("-");
2124
+ maxSessionLength = Math.max(maxSessionLength, sessionDisplay.length);
2125
+ const row = formatUsageDataRow(sessionDisplay, {
2126
+ inputTokens: data.inputTokens,
2127
+ outputTokens: data.outputTokens,
2128
+ cacheCreationTokens: data.cacheCreationTokens,
2129
+ cacheReadTokens: data.cacheReadTokens,
2130
+ totalCost: data.totalCost,
2131
+ modelsUsed: data.modelsUsed
2132
+ }, data.lastActivity);
2133
+ table.push(row);
2134
+ if (ctx.values.breakdown) pushBreakdownRows(table, data.modelBreakdowns, 1, 1);
2135
+ }
2136
+ addEmptySeparatorRow(table, 9);
2137
+ const totalsRow = formatTotalsRow({
2138
+ inputTokens: totals.inputTokens,
2139
+ outputTokens: totals.outputTokens,
2140
+ cacheCreationTokens: totals.cacheCreationTokens,
2141
+ cacheReadTokens: totals.cacheReadTokens,
2142
+ totalCost: totals.totalCost
2143
+ }, true);
2144
+ table.push(totalsRow);
2145
+ log(table.toString());
2146
+ if (table.isCompactMode()) {
2147
+ logger.info("\nRunning in Compact Mode");
2148
+ logger.info("Expand terminal width to see cache metrics and total tokens");
2149
+ }
2150
+ }
2151
+ }
2152
+ });
2153
+ function filterInPlace(array, predicate) {
2154
+ let outputIndex = 0;
2155
+ for (const cur of array) {
2156
+ if (!predicate(cur)) continue;
2157
+ array[outputIndex] = cur;
2158
+ outputIndex += 1;
2159
+ }
2160
+ array.splice(outputIndex);
2161
+ return array;
2162
+ }
2163
+ function deepMerge(record, other, options) {
2164
+ return deepMergeInternal(record, other, /* @__PURE__ */ new Set(), options);
2165
+ }
2166
+ function deepMergeInternal(record, other, seen, options) {
2167
+ const result = {};
2168
+ const keys = new Set([...getKeys(record), ...getKeys(other)]);
2169
+ for (const key of keys) {
2170
+ if (key === "__proto__") continue;
2171
+ const a$1 = record[key];
2172
+ if (!Object.hasOwn(other, key)) {
2173
+ result[key] = a$1;
2174
+ continue;
2175
+ }
2176
+ const b$1 = other[key];
2177
+ if (isNonNullObject(a$1) && isNonNullObject(b$1) && !seen.has(a$1) && !seen.has(b$1)) {
2178
+ seen.add(a$1);
2179
+ seen.add(b$1);
2180
+ result[key] = mergeObjects(a$1, b$1, seen, options);
2181
+ continue;
2182
+ }
2183
+ result[key] = b$1;
2184
+ }
2185
+ return result;
2186
+ }
2187
+ function mergeObjects(left, right, seen, options = {
2188
+ arrays: "merge",
2189
+ sets: "merge",
2190
+ maps: "merge"
2191
+ }) {
2192
+ if (isMergeable(left) && isMergeable(right)) return deepMergeInternal(left, right, seen, options);
2193
+ if (isIterable(left) && isIterable(right)) {
2194
+ if (Array.isArray(left) && Array.isArray(right)) {
2195
+ if (options.arrays === "merge") return left.concat(right);
2196
+ return right;
2197
+ }
2198
+ if (left instanceof Map && right instanceof Map) {
2199
+ if (options.maps === "merge") return new Map([...left, ...right]);
2200
+ return right;
2201
+ }
2202
+ if (left instanceof Set && right instanceof Set) {
2203
+ if (options.sets === "merge") return new Set([...left, ...right]);
2204
+ return right;
2205
+ }
2206
+ }
2207
+ return right;
2208
+ }
2209
+ function isMergeable(value$1) {
2210
+ return Object.getPrototypeOf(value$1) === Object.prototype;
2211
+ }
2212
+ function isIterable(value$1) {
2213
+ return typeof value$1[Symbol.iterator] === "function";
2214
+ }
2215
+ function isNonNullObject(value$1) {
2216
+ return value$1 !== null && typeof value$1 === "object";
2217
+ }
2218
+ function getKeys(record) {
2219
+ const result = Object.getOwnPropertySymbols(record);
2220
+ filterInPlace(result, (key) => Object.prototype.propertyIsEnumerable.call(record, key));
2221
+ result.push(...Object.keys(record));
2222
+ return result;
2223
+ }
2224
+ function isLeap(yearNumber) {
2225
+ return yearNumber % 4 === 0 && yearNumber % 100 !== 0 || yearNumber % 400 === 0;
2226
+ }
2227
+ function success(body) {
2228
+ return {
2229
+ ok: true,
2230
+ body
2231
+ };
2232
+ }
2233
+ function failure() {
2234
+ return { ok: false };
2235
+ }
2236
+ function unflat(keys, values = { __proto__: null }) {
2237
+ return keys.reduceRight((acc, key) => ({ [key]: acc }), values);
2238
+ }
2239
+ function or(parsers) {
2240
+ return (scanner) => {
2241
+ for (const parse$2 of parsers) {
2242
+ const result = parse$2(scanner);
2243
+ if (result.ok) return result;
2244
+ }
2245
+ return failure();
2246
+ };
2247
+ }
2248
+ function join$1(parser, separator) {
2249
+ const Separator = character(separator);
2250
+ return (scanner) => {
2251
+ const out = [];
2252
+ const first = parser(scanner);
2253
+ if (!first.ok) return success(out);
2254
+ out.push(first.body);
2255
+ while (!scanner.eof()) {
2256
+ if (!Separator(scanner).ok) break;
2257
+ const result = parser(scanner);
2258
+ if (!result.ok) throw new SyntaxError(`Invalid token after "${separator}"`);
2259
+ out.push(result.body);
2260
+ }
2261
+ return success(out);
2262
+ };
2263
+ }
2264
+ function join1(parser, separator) {
2265
+ const Separator = character(separator);
2266
+ return (scanner) => {
2267
+ const first = parser(scanner);
2268
+ if (!first.ok) return failure();
2269
+ const out = [first.body];
2270
+ while (!scanner.eof()) {
2271
+ if (!Separator(scanner).ok) break;
2272
+ const result = parser(scanner);
2273
+ if (!result.ok) throw new SyntaxError(`Invalid token after "${separator}"`);
2274
+ out.push(result.body);
2275
+ }
2276
+ return success(out);
2277
+ };
2278
+ }
2279
+ function kv(keyParser, separator, valueParser) {
2280
+ const Separator = character(separator);
2281
+ return (scanner) => {
2282
+ const position = scanner.position;
2283
+ const key = keyParser(scanner);
2284
+ if (!key.ok) return failure();
2285
+ if (!Separator(scanner).ok) throw new SyntaxError(`key/value pair doesn't have "${separator}"`);
2286
+ const value$1 = valueParser(scanner);
2287
+ if (!value$1.ok) {
2288
+ const lineEndIndex = scanner.source.indexOf("\n", scanner.position);
2289
+ const endPosition = lineEndIndex > 0 ? lineEndIndex : scanner.source.length;
2290
+ const line = scanner.source.slice(position, endPosition);
2291
+ throw new SyntaxError(`Cannot parse value on line '${line}'`);
2292
+ }
2293
+ return success(unflat(key.body, value$1.body));
2294
+ };
2295
+ }
2296
+ function surround(left, parser, right) {
2297
+ const Left = character(left);
2298
+ const Right = character(right);
2299
+ return (scanner) => {
2300
+ if (!Left(scanner).ok) return failure();
2301
+ const result = parser(scanner);
2302
+ if (!result.ok) throw new SyntaxError(`Invalid token after "${left}"`);
2303
+ if (!Right(scanner).ok) throw new SyntaxError(`Not closed by "${right}" after started with "${left}"`);
2304
+ return success(result.body);
2305
+ };
2306
+ }
2307
+ function character(str$1) {
2308
+ return (scanner) => {
2309
+ scanner.skipWhitespaces();
2310
+ if (!scanner.startsWith(str$1)) return failure();
2311
+ scanner.next(str$1.length);
2312
+ scanner.skipWhitespaces();
2313
+ return success(void 0);
2314
+ };
2315
+ }
2316
+ const BARE_KEY_REGEXP = /[A-Za-z0-9_-]+/y;
2317
+ function bareKey(scanner) {
2318
+ scanner.skipWhitespaces();
2319
+ const key = scanner.match(BARE_KEY_REGEXP)?.[0];
2320
+ if (!key) return failure();
2321
+ scanner.next(key.length);
2322
+ return success(key);
2323
+ }
2324
+ function escapeSequence(scanner) {
2325
+ if (scanner.char() !== "\\") return failure();
2326
+ scanner.next();
2327
+ switch (scanner.char()) {
2328
+ case "b":
2329
+ scanner.next();
2330
+ return success("\b");
2331
+ case "t":
2332
+ scanner.next();
2333
+ return success(" ");
2334
+ case "n":
2335
+ scanner.next();
2336
+ return success("\n");
2337
+ case "f":
2338
+ scanner.next();
2339
+ return success("\f");
2340
+ case "r":
2341
+ scanner.next();
2342
+ return success("\r");
2343
+ case "u":
2344
+ case "U": {
2345
+ const codePointLen = scanner.char() === "u" ? 4 : 6;
2346
+ const codePoint = parseInt("0x" + scanner.slice(1, 1 + codePointLen), 16);
2347
+ const str$1 = String.fromCodePoint(codePoint);
2348
+ scanner.next(codePointLen + 1);
2349
+ return success(str$1);
2350
+ }
2351
+ case "\"":
2352
+ scanner.next();
2353
+ return success("\"");
2354
+ case "\\":
2355
+ scanner.next();
2356
+ return success("\\");
2357
+ default: throw new SyntaxError(`Invalid escape sequence: \\${scanner.char()}`);
2358
+ }
2359
+ }
2360
+ function basicString(scanner) {
2361
+ scanner.skipWhitespaces();
2362
+ if (scanner.char() !== "\"") return failure();
2363
+ scanner.next();
2364
+ const acc = [];
2365
+ while (scanner.char() !== "\"" && !scanner.eof()) {
2366
+ if (scanner.char() === "\n") throw new SyntaxError("Single-line string cannot contain EOL");
2367
+ const escapedChar = escapeSequence(scanner);
2368
+ if (escapedChar.ok) acc.push(escapedChar.body);
2369
+ else {
2370
+ acc.push(scanner.char());
2371
+ scanner.next();
2372
+ }
2373
+ }
2374
+ if (scanner.eof()) throw new SyntaxError(`Single-line string is not closed:\n${acc.join("")}`);
2375
+ scanner.next();
2376
+ return success(acc.join(""));
2377
+ }
2378
+ function literalString(scanner) {
2379
+ scanner.skipWhitespaces();
2380
+ if (scanner.char() !== "'") return failure();
2381
+ scanner.next();
2382
+ const acc = [];
2383
+ while (scanner.char() !== "'" && !scanner.eof()) {
2384
+ if (scanner.char() === "\n") throw new SyntaxError("Single-line string cannot contain EOL");
2385
+ acc.push(scanner.char());
2386
+ scanner.next();
2387
+ }
2388
+ if (scanner.eof()) throw new SyntaxError(`Single-line string is not closed:\n${acc.join("")}`);
2389
+ scanner.next();
2390
+ return success(acc.join(""));
2391
+ }
2392
+ function multilineBasicString(scanner) {
2393
+ scanner.skipWhitespaces();
2394
+ if (!scanner.startsWith("\"\"\"")) return failure();
2395
+ scanner.next(3);
2396
+ if (scanner.char() === "\n") scanner.next();
2397
+ else if (scanner.startsWith("\r\n")) scanner.next(2);
2398
+ const acc = [];
2399
+ while (!scanner.startsWith("\"\"\"") && !scanner.eof()) {
2400
+ if (scanner.startsWith("\\\n")) {
2401
+ scanner.next();
2402
+ scanner.nextUntilChar({ skipComments: false });
2403
+ continue;
2404
+ } else if (scanner.startsWith("\\\r\n")) {
2405
+ scanner.next();
2406
+ scanner.nextUntilChar({ skipComments: false });
2407
+ continue;
2408
+ }
2409
+ const escapedChar = escapeSequence(scanner);
2410
+ if (escapedChar.ok) acc.push(escapedChar.body);
2411
+ else {
2412
+ acc.push(scanner.char());
2413
+ scanner.next();
2414
+ }
2415
+ }
2416
+ if (scanner.eof()) throw new SyntaxError(`Multi-line string is not closed:\n${acc.join("")}`);
2417
+ if (scanner.char(3) === "\"") {
2418
+ acc.push("\"");
2419
+ scanner.next();
2420
+ }
2421
+ scanner.next(3);
2422
+ return success(acc.join(""));
2423
+ }
2424
+ function multilineLiteralString(scanner) {
2425
+ scanner.skipWhitespaces();
2426
+ if (!scanner.startsWith("'''")) return failure();
2427
+ scanner.next(3);
2428
+ if (scanner.char() === "\n") scanner.next();
2429
+ else if (scanner.startsWith("\r\n")) scanner.next(2);
2430
+ const acc = [];
2431
+ while (!scanner.startsWith("'''") && !scanner.eof()) {
2432
+ acc.push(scanner.char());
2433
+ scanner.next();
2434
+ }
2435
+ if (scanner.eof()) throw new SyntaxError(`Multi-line string is not closed:\n${acc.join("")}`);
2436
+ if (scanner.char(3) === "'") {
2437
+ acc.push("'");
2438
+ scanner.next();
2439
+ }
2440
+ scanner.next(3);
2441
+ return success(acc.join(""));
2442
+ }
2443
+ const BOOLEAN_REGEXP = /(?:true|false)\b/y;
2444
+ function boolean(scanner) {
2445
+ scanner.skipWhitespaces();
2446
+ const match = scanner.match(BOOLEAN_REGEXP);
2447
+ if (!match) return failure();
2448
+ const string$1 = match[0];
2449
+ scanner.next(string$1.length);
2450
+ return success(string$1 === "true");
2451
+ }
2452
+ const INFINITY_MAP = new Map([
2453
+ ["inf", Infinity],
2454
+ ["+inf", Infinity],
2455
+ ["-inf", -Infinity]
2456
+ ]);
2457
+ const INFINITY_REGEXP = /[+-]?inf\b/y;
2458
+ function infinity(scanner) {
2459
+ scanner.skipWhitespaces();
2460
+ const match = scanner.match(INFINITY_REGEXP);
2461
+ if (!match) return failure();
2462
+ const string$1 = match[0];
2463
+ scanner.next(string$1.length);
2464
+ return success(INFINITY_MAP.get(string$1));
2465
+ }
2466
+ const NAN_REGEXP = /[+-]?nan\b/y;
2467
+ function nan(scanner) {
2468
+ scanner.skipWhitespaces();
2469
+ const match = scanner.match(NAN_REGEXP);
2470
+ if (!match) return failure();
2471
+ const string$1 = match[0];
2472
+ scanner.next(string$1.length);
2473
+ return success(NaN);
2474
+ }
2475
+ const dottedKey = join1(or([
2476
+ bareKey,
2477
+ basicString,
2478
+ literalString
2479
+ ]), ".");
2480
+ const BINARY_REGEXP = /0b[01]+(?:_[01]+)*\b/y;
2481
+ function binary$1(scanner) {
2482
+ scanner.skipWhitespaces();
2483
+ const match = scanner.match(BINARY_REGEXP)?.[0];
2484
+ if (!match) return failure();
2485
+ scanner.next(match.length);
2486
+ const value$1 = match.slice(2).replaceAll("_", "");
2487
+ const number$1 = parseInt(value$1, 2);
2488
+ return isNaN(number$1) ? failure() : success(number$1);
2489
+ }
2490
+ const OCTAL_REGEXP = /0o[0-7]+(?:_[0-7]+)*\b/y;
2491
+ function octal(scanner) {
2492
+ scanner.skipWhitespaces();
2493
+ const match = scanner.match(OCTAL_REGEXP)?.[0];
2494
+ if (!match) return failure();
2495
+ scanner.next(match.length);
2496
+ const value$1 = match.slice(2).replaceAll("_", "");
2497
+ const number$1 = parseInt(value$1, 8);
2498
+ return isNaN(number$1) ? failure() : success(number$1);
2499
+ }
2500
+ const HEX_REGEXP = /0x[0-9a-f]+(?:_[0-9a-f]+)*\b/iy;
2501
+ function hex(scanner) {
2502
+ scanner.skipWhitespaces();
2503
+ const match = scanner.match(HEX_REGEXP)?.[0];
2504
+ if (!match) return failure();
2505
+ scanner.next(match.length);
2506
+ const value$1 = match.slice(2).replaceAll("_", "");
2507
+ const number$1 = parseInt(value$1, 16);
2508
+ return isNaN(number$1) ? failure() : success(number$1);
2509
+ }
2510
+ const INTEGER_REGEXP = /[+-]?(?:0|[1-9][0-9]*(?:_[0-9]+)*)\b/y;
2511
+ function integer(scanner) {
2512
+ scanner.skipWhitespaces();
2513
+ const match = scanner.match(INTEGER_REGEXP)?.[0];
2514
+ if (!match) return failure();
2515
+ scanner.next(match.length);
2516
+ const value$1 = match.replaceAll("_", "");
2517
+ return success(parseInt(value$1, 10));
2518
+ }
2519
+ const FLOAT_REGEXP = /[+-]?(?:0|[1-9][0-9]*(?:_[0-9]+)*)(?:\.[0-9]+(?:_[0-9]+)*)?(?:e[+-]?[0-9]+(?:_[0-9]+)*)?\b/iy;
2520
+ function float$1(scanner) {
2521
+ scanner.skipWhitespaces();
2522
+ const match = scanner.match(FLOAT_REGEXP)?.[0];
2523
+ if (!match) return failure();
2524
+ scanner.next(match.length);
2525
+ const value$1 = match.replaceAll("_", "");
2526
+ const float$2 = parseFloat(value$1);
2527
+ if (isNaN(float$2)) return failure();
2528
+ return success(float$2);
2529
+ }
2530
+ const DATE_TIME_REGEXP = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})(?:[ 0-9TZ.:+-]+)?\b/y;
2531
+ function dateTime(scanner) {
2532
+ scanner.skipWhitespaces();
2533
+ const match = scanner.match(DATE_TIME_REGEXP);
2534
+ if (!match) return failure();
2535
+ const string$1 = match[0];
2536
+ scanner.next(string$1.length);
2537
+ const groups = match.groups;
2538
+ if (groups.month == "02") {
2539
+ const days = parseInt(groups.day);
2540
+ if (days > 29) throw new SyntaxError(`Invalid date string "${match}"`);
2541
+ const year = parseInt(groups.year);
2542
+ if (days > 28 && !isLeap(year)) throw new SyntaxError(`Invalid date string "${match}"`);
2543
+ }
2544
+ const date = new Date(string$1.trim());
2545
+ if (isNaN(date.getTime())) throw new SyntaxError(`Invalid date string "${match}"`);
2546
+ return success(date);
2547
+ }
2548
+ const LOCAL_TIME_REGEXP = /(\d{2}):(\d{2}):(\d{2})(?:\.[0-9]+)?\b/y;
2549
+ function localTime(scanner) {
2550
+ scanner.skipWhitespaces();
2551
+ const match = scanner.match(LOCAL_TIME_REGEXP)?.[0];
2552
+ if (!match) return failure();
2553
+ scanner.next(match.length);
2554
+ return success(match);
2555
+ }
2556
+ function arrayValue(scanner) {
2557
+ scanner.skipWhitespaces();
2558
+ if (scanner.char() !== "[") return failure();
2559
+ scanner.next();
2560
+ const array = [];
2561
+ while (!scanner.eof()) {
2562
+ scanner.nextUntilChar();
2563
+ const result = value(scanner);
2564
+ if (!result.ok) break;
2565
+ array.push(result.body);
2566
+ scanner.skipWhitespaces();
2567
+ if (scanner.char() !== ",") break;
2568
+ scanner.next();
2569
+ }
2570
+ scanner.nextUntilChar();
2571
+ if (scanner.char() !== "]") throw new SyntaxError("Array is not closed");
2572
+ scanner.next();
2573
+ return success(array);
2574
+ }
2575
+ function inlineTable(scanner) {
2576
+ scanner.nextUntilChar();
2577
+ if (scanner.char(1) === "}") {
2578
+ scanner.next(2);
2579
+ return success({ __proto__: null });
2580
+ }
2581
+ const pairs$1 = surround("{", join$1(pair, ","), "}")(scanner);
2582
+ if (!pairs$1.ok) return failure();
2583
+ let table = { __proto__: null };
2584
+ for (const pair$1 of pairs$1.body) table = deepMerge(table, pair$1);
2585
+ return success(table);
2586
+ }
2587
+ const value = or([
2588
+ multilineBasicString,
2589
+ multilineLiteralString,
2590
+ basicString,
2591
+ literalString,
2592
+ boolean,
2593
+ infinity,
2594
+ nan,
2595
+ dateTime,
2596
+ localTime,
2597
+ binary$1,
2598
+ octal,
2599
+ hex,
2600
+ float$1,
2601
+ integer,
2602
+ arrayValue,
2603
+ inlineTable
2604
+ ]);
2605
+ const pair = kv(dottedKey, "=", value);
2606
+ surround("[", dottedKey, "]");
2607
+ surround("[[", dottedKey, "]]");
2608
+ const BASE64_MAP = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\n\r";
2609
+ function resolveYamlBinary(data) {
2610
+ if (data === null) return false;
2611
+ let code;
2612
+ let bitlen = 0;
2613
+ const max = data.length;
2614
+ const map$2 = BASE64_MAP;
2615
+ for (let idx = 0; idx < max; idx++) {
2616
+ code = map$2.indexOf(data.charAt(idx));
2617
+ if (code > 64) continue;
2618
+ if (code < 0) return false;
2619
+ bitlen += 6;
2620
+ }
2621
+ return bitlen % 8 === 0;
2622
+ }
2623
+ function constructYamlBinary(data) {
2624
+ const input = data.replace(/[\r\n=]/g, "");
2625
+ const max = input.length;
2626
+ const map$2 = BASE64_MAP;
2627
+ const result = [];
2628
+ let bits = 0;
2629
+ for (let idx = 0; idx < max; idx++) {
2630
+ if (idx % 4 === 0 && idx) {
2631
+ result.push(bits >> 16 & 255);
2632
+ result.push(bits >> 8 & 255);
2633
+ result.push(bits & 255);
2634
+ }
2635
+ bits = bits << 6 | map$2.indexOf(input.charAt(idx));
2636
+ }
2637
+ const tailbits = max % 4 * 6;
2638
+ if (tailbits === 0) {
2639
+ result.push(bits >> 16 & 255);
2640
+ result.push(bits >> 8 & 255);
2641
+ result.push(bits & 255);
2642
+ } else if (tailbits === 18) {
2643
+ result.push(bits >> 10 & 255);
2644
+ result.push(bits >> 2 & 255);
2645
+ } else if (tailbits === 12) result.push(bits >> 4 & 255);
2646
+ return new Uint8Array(result);
2647
+ }
2648
+ function representYamlBinary(object) {
2649
+ const max = object.length;
2650
+ const map$2 = BASE64_MAP;
2651
+ let result = "";
2652
+ let bits = 0;
2653
+ for (let idx = 0; idx < max; idx++) {
2654
+ if (idx % 3 === 0 && idx) {
2655
+ result += map$2[bits >> 18 & 63];
2656
+ result += map$2[bits >> 12 & 63];
2657
+ result += map$2[bits >> 6 & 63];
2658
+ result += map$2[bits & 63];
2659
+ }
2660
+ bits = (bits << 8) + object[idx];
2661
+ }
2662
+ const tail = max % 3;
2663
+ if (tail === 0) {
2664
+ result += map$2[bits >> 18 & 63];
2665
+ result += map$2[bits >> 12 & 63];
2666
+ result += map$2[bits >> 6 & 63];
2667
+ result += map$2[bits & 63];
2668
+ } else if (tail === 2) {
2669
+ result += map$2[bits >> 10 & 63];
2670
+ result += map$2[bits >> 4 & 63];
2671
+ result += map$2[bits << 2 & 63];
2672
+ result += map$2[64];
2673
+ } else if (tail === 1) {
2674
+ result += map$2[bits >> 2 & 63];
2675
+ result += map$2[bits << 4 & 63];
2676
+ result += map$2[64];
2677
+ result += map$2[64];
2678
+ }
2679
+ return result;
2680
+ }
2681
+ function isBinary(obj) {
2682
+ return obj instanceof Uint8Array;
2683
+ }
2684
+ const binary = {
2685
+ tag: "tag:yaml.org,2002:binary",
2686
+ construct: constructYamlBinary,
2687
+ kind: "scalar",
2688
+ predicate: isBinary,
2689
+ represent: representYamlBinary,
2690
+ resolve: resolveYamlBinary
2691
+ };
2692
+ const YAML_TRUE_BOOLEANS = [
2693
+ "true",
2694
+ "True",
2695
+ "TRUE"
2696
+ ];
2697
+ const YAML_FALSE_BOOLEANS = [
2698
+ "false",
2699
+ "False",
2700
+ "FALSE"
2701
+ ];
2702
+ const YAML_BOOLEANS = [...YAML_TRUE_BOOLEANS, ...YAML_FALSE_BOOLEANS];
2703
+ const bool = {
2704
+ tag: "tag:yaml.org,2002:bool",
2705
+ kind: "scalar",
2706
+ defaultStyle: "lowercase",
2707
+ predicate: (value$1) => typeof value$1 === "boolean" || value$1 instanceof Boolean,
2708
+ construct: (data) => YAML_TRUE_BOOLEANS.includes(data),
2709
+ resolve: (data) => YAML_BOOLEANS.includes(data),
2710
+ represent: {
2711
+ lowercase: (object) => {
2712
+ return (object instanceof Boolean ? object.valueOf() : object) ? "true" : "false";
2713
+ },
2714
+ uppercase: (object) => {
2715
+ return (object instanceof Boolean ? object.valueOf() : object) ? "TRUE" : "FALSE";
2716
+ },
2717
+ camelcase: (object) => {
2718
+ return (object instanceof Boolean ? object.valueOf() : object) ? "True" : "False";
2719
+ }
2720
+ }
2721
+ };
2722
+ function isNegativeZero(i) {
2723
+ return i === 0 && Number.NEGATIVE_INFINITY === 1 / i;
2724
+ }
2725
+ function isPlainObject(object) {
2726
+ return Object.prototype.toString.call(object) === "[object Object]";
2727
+ }
2728
+ const YAML_FLOAT_PATTERN = /* @__PURE__ */ new RegExp("^(?:[-+]?(?:0|[1-9][0-9_]*)(?:\\.[0-9_]*)?(?:[eE][-+]?[0-9]+)?|\\.[0-9_]+(?:[eE][-+]?[0-9]+)?|[-+]?\\.(?:inf|Inf|INF)|\\.(?:nan|NaN|NAN))$");
2729
+ function resolveYamlFloat(data) {
2730
+ if (!YAML_FLOAT_PATTERN.test(data) || data[data.length - 1] === "_") return false;
2731
+ return true;
2732
+ }
2733
+ function constructYamlFloat(data) {
2734
+ let value$1 = data.replace(/_/g, "").toLowerCase();
2735
+ const sign = value$1[0] === "-" ? -1 : 1;
2736
+ if (value$1[0] && "+-".includes(value$1[0])) value$1 = value$1.slice(1);
2737
+ if (value$1 === ".inf") return sign === 1 ? Number.POSITIVE_INFINITY : Number.NEGATIVE_INFINITY;
2738
+ if (value$1 === ".nan") return NaN;
2739
+ return sign * parseFloat(value$1);
2740
+ }
2741
+ const SCIENTIFIC_WITHOUT_DOT = /^[-+]?[0-9]+e/;
2742
+ function representYamlFloat(object, style) {
2743
+ const value$1 = object instanceof Number ? object.valueOf() : object;
2744
+ if (isNaN(value$1)) switch (style) {
2745
+ case "lowercase": return ".nan";
2746
+ case "uppercase": return ".NAN";
2747
+ case "camelcase": return ".NaN";
2748
+ }
2749
+ else if (Number.POSITIVE_INFINITY === value$1) switch (style) {
2750
+ case "lowercase": return ".inf";
2751
+ case "uppercase": return ".INF";
2752
+ case "camelcase": return ".Inf";
2753
+ }
2754
+ else if (Number.NEGATIVE_INFINITY === value$1) switch (style) {
2755
+ case "lowercase": return "-.inf";
2756
+ case "uppercase": return "-.INF";
2757
+ case "camelcase": return "-.Inf";
2758
+ }
2759
+ else if (isNegativeZero(value$1)) return "-0.0";
2760
+ const res = value$1.toString(10);
2761
+ return SCIENTIFIC_WITHOUT_DOT.test(res) ? res.replace("e", ".e") : res;
2762
+ }
2763
+ function isFloat(object) {
2764
+ if (object instanceof Number) object = object.valueOf();
2765
+ return typeof object === "number" && (object % 1 !== 0 || isNegativeZero(object));
2766
+ }
2767
+ const float = {
2768
+ tag: "tag:yaml.org,2002:float",
2769
+ construct: constructYamlFloat,
2770
+ defaultStyle: "lowercase",
2771
+ kind: "scalar",
2772
+ predicate: isFloat,
2773
+ represent: representYamlFloat,
2774
+ resolve: resolveYamlFloat
2775
+ };
2776
+ function isCharCodeInRange(c, lower, upper) {
2777
+ return lower <= c && c <= upper;
2778
+ }
2779
+ function isHexCode(c) {
2780
+ return isCharCodeInRange(c, 48, 57) || isCharCodeInRange(c, 65, 70) || isCharCodeInRange(c, 97, 102);
2781
+ }
2782
+ function isOctCode(c) {
2783
+ return isCharCodeInRange(c, 48, 55);
2784
+ }
2785
+ function isDecCode(c) {
2786
+ return isCharCodeInRange(c, 48, 57);
2787
+ }
2788
+ function resolveYamlInteger(data) {
2789
+ const max = data.length;
2790
+ let index = 0;
2791
+ let hasDigits = false;
2792
+ if (!max) return false;
2793
+ let ch = data[index];
2794
+ if (ch === "-" || ch === "+") ch = data[++index];
2795
+ if (ch === "0") {
2796
+ if (index + 1 === max) return true;
2797
+ ch = data[++index];
2798
+ if (ch === "b") {
2799
+ index++;
2800
+ for (; index < max; index++) {
2801
+ ch = data[index];
2802
+ if (ch === "_") continue;
2803
+ if (ch !== "0" && ch !== "1") return false;
2804
+ hasDigits = true;
2805
+ }
2806
+ return hasDigits && ch !== "_";
2807
+ }
2808
+ if (ch === "x") {
2809
+ index++;
2810
+ for (; index < max; index++) {
2811
+ ch = data[index];
2812
+ if (ch === "_") continue;
2813
+ if (!isHexCode(data.charCodeAt(index))) return false;
2814
+ hasDigits = true;
2815
+ }
2816
+ return hasDigits && ch !== "_";
2817
+ }
2818
+ for (; index < max; index++) {
2819
+ ch = data[index];
2820
+ if (ch === "_") continue;
2821
+ if (!isOctCode(data.charCodeAt(index))) return false;
2822
+ hasDigits = true;
2823
+ }
2824
+ return hasDigits && ch !== "_";
2825
+ }
2826
+ if (ch === "_") return false;
2827
+ for (; index < max; index++) {
2828
+ ch = data[index];
2829
+ if (ch === "_") continue;
2830
+ if (!isDecCode(data.charCodeAt(index))) return false;
2831
+ hasDigits = true;
2832
+ }
2833
+ if (!hasDigits || ch === "_") return false;
2834
+ return /^(:[0-5]?[0-9])+$/.test(data.slice(index));
2835
+ }
2836
+ function constructYamlInteger(data) {
2837
+ let value$1 = data;
2838
+ if (value$1.includes("_")) value$1 = value$1.replace(/_/g, "");
2839
+ let sign = 1;
2840
+ let ch = value$1[0];
2841
+ if (ch === "-" || ch === "+") {
2842
+ if (ch === "-") sign = -1;
2843
+ value$1 = value$1.slice(1);
2844
+ ch = value$1[0];
2845
+ }
2846
+ if (value$1 === "0") return 0;
2847
+ if (ch === "0") {
2848
+ if (value$1[1] === "b") return sign * parseInt(value$1.slice(2), 2);
2849
+ if (value$1[1] === "x") return sign * parseInt(value$1, 16);
2850
+ return sign * parseInt(value$1, 8);
2851
+ }
2852
+ return sign * parseInt(value$1, 10);
2853
+ }
2854
+ function isInteger(object) {
2855
+ if (object instanceof Number) object = object.valueOf();
2856
+ return typeof object === "number" && object % 1 === 0 && !isNegativeZero(object);
2857
+ }
2858
+ const int = {
2859
+ tag: "tag:yaml.org,2002:int",
2860
+ construct: constructYamlInteger,
2861
+ defaultStyle: "decimal",
2862
+ kind: "scalar",
2863
+ predicate: isInteger,
2864
+ represent: {
2865
+ binary(object) {
2866
+ const value$1 = object instanceof Number ? object.valueOf() : object;
2867
+ return value$1 >= 0 ? `0b${value$1.toString(2)}` : `-0b${value$1.toString(2).slice(1)}`;
2868
+ },
2869
+ octal(object) {
2870
+ const value$1 = object instanceof Number ? object.valueOf() : object;
2871
+ return value$1 >= 0 ? `0${value$1.toString(8)}` : `-0${value$1.toString(8).slice(1)}`;
2872
+ },
2873
+ decimal(object) {
2874
+ return (object instanceof Number ? object.valueOf() : object).toString(10);
2875
+ },
2876
+ hexadecimal(object) {
2877
+ const value$1 = object instanceof Number ? object.valueOf() : object;
2878
+ return value$1 >= 0 ? `0x${value$1.toString(16).toUpperCase()}` : `-0x${value$1.toString(16).toUpperCase().slice(1)}`;
2879
+ }
2880
+ },
2881
+ resolve: resolveYamlInteger
2882
+ };
2883
+ const map = {
2884
+ tag: "tag:yaml.org,2002:map",
2885
+ resolve() {
2886
+ return true;
2887
+ },
2888
+ construct(data) {
2889
+ return data !== null ? data : {};
2890
+ },
2891
+ kind: "mapping"
2892
+ };
2893
+ const merge = {
2894
+ tag: "tag:yaml.org,2002:merge",
2895
+ kind: "scalar",
2896
+ resolve: (data) => data === "<<" || data === null,
2897
+ construct: (data) => data
2898
+ };
2899
+ const nil = {
2900
+ tag: "tag:yaml.org,2002:null",
2901
+ kind: "scalar",
2902
+ defaultStyle: "lowercase",
2903
+ predicate: (object) => object === null,
2904
+ construct: () => null,
2905
+ resolve: (data) => {
2906
+ return data === "~" || data === "null" || data === "Null" || data === "NULL";
2907
+ },
2908
+ represent: {
2909
+ lowercase: () => "null",
2910
+ uppercase: () => "NULL",
2911
+ camelcase: () => "Null"
2912
+ }
2913
+ };
2914
+ function resolveYamlOmap(data) {
2915
+ const objectKeys = /* @__PURE__ */ new Set();
2916
+ for (const object of data) {
2917
+ if (!isPlainObject(object)) return false;
2918
+ const keys = Object.keys(object);
2919
+ if (keys.length !== 1) return false;
2920
+ for (const key of keys) {
2921
+ if (objectKeys.has(key)) return false;
2922
+ objectKeys.add(key);
2923
+ }
2924
+ }
2925
+ return true;
2926
+ }
2927
+ const omap = {
2928
+ tag: "tag:yaml.org,2002:omap",
2929
+ kind: "sequence",
2930
+ resolve: resolveYamlOmap,
2931
+ construct(data) {
2932
+ return data;
2933
+ }
2934
+ };
2935
+ function resolveYamlPairs(data) {
2936
+ if (data === null) return true;
2937
+ return data.every((it) => isPlainObject(it) && Object.keys(it).length === 1);
2938
+ }
2939
+ const pairs = {
2940
+ tag: "tag:yaml.org,2002:pairs",
2941
+ construct(data) {
2942
+ return data?.flatMap(Object.entries) ?? [];
2943
+ },
2944
+ kind: "sequence",
2945
+ resolve: resolveYamlPairs
2946
+ };
2947
+ const REGEXP = /^\/(?<regexp>[\s\S]+)\/(?<modifiers>[gismuy]*)$/;
2948
+ const regexp = {
2949
+ tag: "tag:yaml.org,2002:js/regexp",
2950
+ kind: "scalar",
2951
+ resolve(data) {
2952
+ if (data === null || !data.length) return false;
2953
+ if (data.charAt(0) === "/") {
2954
+ const groups = data.match(REGEXP)?.groups;
2955
+ if (!groups) return false;
2956
+ const modifiers = groups.modifiers ?? "";
2957
+ if (new Set(modifiers).size < modifiers.length) return false;
2958
+ }
2959
+ return true;
2960
+ },
2961
+ construct(data) {
2962
+ const { regexp: regexp$1 = data, modifiers = "" } = data.match(REGEXP)?.groups ?? {};
2963
+ return new RegExp(regexp$1, modifiers);
2964
+ },
2965
+ predicate: (object) => object instanceof RegExp,
2966
+ represent: (object) => object.toString()
2967
+ };
2968
+ const seq = {
2969
+ tag: "tag:yaml.org,2002:seq",
2970
+ kind: "sequence",
2971
+ resolve: () => true,
2972
+ construct: (data) => data !== null ? data : []
2973
+ };
2974
+ const set = {
2975
+ tag: "tag:yaml.org,2002:set",
2976
+ kind: "mapping",
2977
+ construct: (data) => data !== null ? data : {},
2978
+ resolve: (data) => {
2979
+ if (data === null) return true;
2980
+ return Object.values(data).every((it) => it === null);
2981
+ }
2982
+ };
2983
+ const str = {
2984
+ tag: "tag:yaml.org,2002:str",
2985
+ kind: "scalar",
2986
+ resolve: () => true,
2987
+ construct: (data) => data !== null ? data : ""
2988
+ };
2989
+ const YAML_DATE_REGEXP = /* @__PURE__ */ new RegExp("^([0-9][0-9][0-9][0-9])-([0-9][0-9])-([0-9][0-9])$");
2990
+ const YAML_TIMESTAMP_REGEXP = /* @__PURE__ */ new RegExp("^([0-9][0-9][0-9][0-9])-([0-9][0-9]?)-([0-9][0-9]?)(?:[Tt]|[ \\t]+)([0-9][0-9]?):([0-9][0-9]):([0-9][0-9])(?:\\.([0-9]*))?(?:[ \\t]*(Z|([-+])([0-9][0-9]?)(?::([0-9][0-9]))?))?$");
2991
+ function resolveYamlTimestamp(data) {
2992
+ if (data === null) return false;
2993
+ if (YAML_DATE_REGEXP.exec(data) !== null) return true;
2994
+ if (YAML_TIMESTAMP_REGEXP.exec(data) !== null) return true;
2995
+ return false;
2996
+ }
2997
+ function constructYamlTimestamp(data) {
2998
+ let match = YAML_DATE_REGEXP.exec(data);
2999
+ if (match === null) match = YAML_TIMESTAMP_REGEXP.exec(data);
3000
+ if (match === null) throw new Error("Cannot construct YAML timestamp: date resolve error");
3001
+ const year = +match[1];
3002
+ const month = +match[2] - 1;
3003
+ const day = +match[3];
3004
+ if (!match[4]) return new Date(Date.UTC(year, month, day));
3005
+ const hour = +match[4];
3006
+ const minute = +match[5];
3007
+ const second = +match[6];
3008
+ let fraction = 0;
3009
+ if (match[7]) {
3010
+ let partFraction = match[7].slice(0, 3);
3011
+ while (partFraction.length < 3) partFraction += "0";
3012
+ fraction = +partFraction;
3013
+ }
3014
+ let delta = null;
3015
+ if (match[9] && match[10]) {
3016
+ const tzHour = +match[10];
3017
+ const tzMinute = +(match[11] || 0);
3018
+ delta = (tzHour * 60 + tzMinute) * 6e4;
3019
+ if (match[9] === "-") delta = -delta;
3020
+ }
3021
+ const date = new Date(Date.UTC(year, month, day, hour, minute, second, fraction));
3022
+ if (delta) date.setTime(date.getTime() - delta);
3023
+ return date;
3024
+ }
3025
+ function representYamlTimestamp(date) {
3026
+ return date.toISOString();
3027
+ }
3028
+ const timestamp = {
3029
+ tag: "tag:yaml.org,2002:timestamp",
3030
+ construct: constructYamlTimestamp,
3031
+ predicate(object) {
3032
+ return object instanceof Date;
3033
+ },
3034
+ kind: "scalar",
3035
+ represent: representYamlTimestamp,
3036
+ resolve: resolveYamlTimestamp
3037
+ };
3038
+ const undefinedType = {
3039
+ tag: "tag:yaml.org,2002:js/undefined",
3040
+ kind: "scalar",
3041
+ resolve() {
3042
+ return true;
3043
+ },
3044
+ construct() {},
3045
+ predicate(object) {
3046
+ return typeof object === "undefined";
3047
+ },
3048
+ represent() {
3049
+ return "";
3050
+ }
3051
+ };
3052
+ function createTypeMap(implicitTypes, explicitTypes) {
3053
+ const result = {
3054
+ fallback: /* @__PURE__ */ new Map(),
3055
+ mapping: /* @__PURE__ */ new Map(),
3056
+ scalar: /* @__PURE__ */ new Map(),
3057
+ sequence: /* @__PURE__ */ new Map()
3058
+ };
3059
+ const fallbackMap = result.fallback;
3060
+ for (const type of [...implicitTypes, ...explicitTypes]) {
3061
+ result[type.kind].set(type.tag, type);
3062
+ fallbackMap.set(type.tag, type);
3063
+ }
3064
+ return result;
3065
+ }
3066
+ function createSchema({ explicitTypes = [], implicitTypes = [], include }) {
3067
+ if (include) {
3068
+ implicitTypes.push(...include.implicitTypes);
3069
+ explicitTypes.push(...include.explicitTypes);
3070
+ }
3071
+ return {
3072
+ implicitTypes,
3073
+ explicitTypes,
3074
+ typeMap: createTypeMap(implicitTypes, explicitTypes)
3075
+ };
3076
+ }
3077
+ const FAILSAFE_SCHEMA = createSchema({ explicitTypes: [
3078
+ str,
3079
+ seq,
3080
+ map
3081
+ ] });
3082
+ const CORE_SCHEMA = createSchema({ include: createSchema({
3083
+ implicitTypes: [
3084
+ nil,
3085
+ bool,
3086
+ int,
3087
+ float
3088
+ ],
3089
+ include: FAILSAFE_SCHEMA
3090
+ }) });
3091
+ const DEFAULT_SCHEMA = createSchema({
3092
+ explicitTypes: [
3093
+ binary,
3094
+ omap,
3095
+ pairs,
3096
+ set
3097
+ ],
3098
+ implicitTypes: [timestamp, merge],
3099
+ include: CORE_SCHEMA
3100
+ });
3101
+ createSchema({
3102
+ explicitTypes: [regexp, undefinedType],
3103
+ include: DEFAULT_SCHEMA
3104
+ });
3105
+ var _computedKey;
3106
+ function resolveOptions(options) {
3107
+ return {
3108
+ validator: options.validator,
3109
+ allowNoExist: options.allowNoExist ?? true,
3110
+ allowValidatorFailure: options.allowValidatorFailure
3111
+ };
3112
+ }
3113
+ _computedKey = Symbol.dispose;
3114
+ var LimoFile = class {
3115
+ #data;
3116
+ #oldData;
3117
+ #path;
3118
+ #text;
3119
+ #options;
3120
+ #parseOptions;
3121
+ constructor(path$1, options) {
3122
+ this.#options = resolveOptions(options);
3123
+ this.#parseOptions = options.parseOptions;
3124
+ this.#path = path$1;
3125
+ const { data, text } = this._read();
3126
+ this.#data = this.#oldData = data;
3127
+ this.#text = text;
3128
+ }
3129
+ [_computedKey]() {
3130
+ this._write();
3131
+ }
3132
+ get data() {
3133
+ return this.#data;
3134
+ }
3135
+ set data(value$1) {
3136
+ this.#data = value$1;
3137
+ }
3138
+ _read() {
3139
+ if (existsSync(this.#path)) {
3140
+ const text = readFileSync(this.#path, { encoding: "utf8" });
3141
+ if (text.trim() === "") return {
3142
+ data: void 0,
3143
+ text
3144
+ };
3145
+ try {
3146
+ const data = this.#parseOptions.parse(text);
3147
+ const { validator, allowValidatorFailure } = this.#options;
3148
+ if (validator != null && !validator(data)) {
3149
+ if (allowValidatorFailure) return {
3150
+ data: void 0,
3151
+ text
3152
+ };
3153
+ throw new Error(`Invalid data: ${text}`);
3154
+ }
3155
+ return {
3156
+ data,
3157
+ text
3158
+ };
3159
+ } catch (error) {
3160
+ throw new Error(`Failed to parse ${this.#path}: ${error}`);
3161
+ }
3162
+ }
3163
+ if (!this.#options.allowNoExist) throw new Error(`File not found: ${this.#path}`);
3164
+ return {
3165
+ data: void 0,
3166
+ text: void 0
3167
+ };
3168
+ }
3169
+ _write() {
3170
+ if (this.#data == null) return;
3171
+ try {
3172
+ let content;
3173
+ if (this.#parseOptions.preserveFormat && this.#text != null && this.#oldData != null) content = this.#parseOptions.preserveFormat(this.#text, this.#oldData, this.#data);
3174
+ else content = this.#parseOptions.stringify(this.#data);
3175
+ writeFileSync(this.#path, content);
3176
+ } catch (error) {
3177
+ throw new Error(`Failed to write ${this.#path}: ${error}`);
3178
+ }
3179
+ }
3180
+ };
3181
+ function createLimoJson(path$1, options = {}) {
3182
+ return new LimoFile(path$1, {
3183
+ ...options,
3184
+ parseOptions: {
3185
+ parse: (text) => JSON.parse(text),
3186
+ stringify: (data) => JSON.stringify(data, null, 2)
3187
+ }
3188
+ });
3189
+ }
3190
+ const { stdin: stdin$1 } = process;
3191
+ async function getStdin() {
3192
+ let result = "";
3193
+ if (stdin$1.isTTY) return result;
3194
+ stdin$1.setEncoding("utf8");
3195
+ for await (const chunk of stdin$1) result += chunk;
3196
+ return result;
3197
+ }
3198
+ getStdin.buffer = async () => {
3199
+ const result = [];
3200
+ let length = 0;
3201
+ if (stdin$1.isTTY) return Buffer.concat([]);
3202
+ for await (const chunk of stdin$1) {
3203
+ result.push(chunk);
3204
+ length += chunk.length;
3205
+ }
3206
+ return Buffer.concat(result, length);
3207
+ };
3208
+ var import_picocolors = /* @__PURE__ */ __toESM(require_picocolors(), 1);
3209
+ function formatRemainingTime(remaining) {
3210
+ const remainingHours = Math.floor(remaining / 60);
3211
+ const remainingMins = remaining % 60;
3212
+ if (remainingHours > 0) return `${remainingHours}h ${remainingMins}m left`;
3213
+ return `${remainingMins}m left`;
3214
+ }
3215
+ function getSemaphore(sessionId) {
3216
+ const semaphoreDir = join(tmpdir(), "ccusage-semaphore");
3217
+ const semaphorePath = join(semaphoreDir, `${sessionId}.lock`);
3218
+ mkdirSync(semaphoreDir, { recursive: true });
3219
+ return createLimoJson(semaphorePath);
3220
+ }
3221
+ const visualBurnRateChoices = [
3222
+ "off",
3223
+ "emoji",
3224
+ "text",
3225
+ "emoji-text"
3226
+ ];
3227
+ const costSourceChoices = [
3228
+ "auto",
3229
+ "ccusage",
3230
+ "cc",
3231
+ "both"
3232
+ ];
3233
+ const contextThresholdSchema = pipe$1(union([number(), pipe$1(string(), trim(), check((value$1) => /^-?\d+$/u.test(value$1), "Context threshold must be an integer"), transform((value$1) => Number.parseInt(value$1, 10)))]), number("Context threshold must be a number"), integer$1("Context threshold must be an integer"), minValue(0, "Context threshold must be at least 0"), maxValue(100, "Context threshold must be at most 100"));
3234
+ function parseContextThreshold(value$1) {
3235
+ return parse$1(contextThresholdSchema, value$1);
3236
+ }
3237
+ const statuslineCommand = define({
3238
+ name: "statusline",
3239
+ description: "Display compact status line for Claude Code hooks with hybrid time+file caching (Beta)",
3240
+ toKebab: true,
3241
+ args: {
3242
+ offline: {
3243
+ ...sharedArgs.offline,
3244
+ default: true
3245
+ },
3246
+ visualBurnRate: {
3247
+ type: "enum",
3248
+ choices: visualBurnRateChoices,
3249
+ description: "Controls the visualization of the burn rate status",
3250
+ default: "off",
3251
+ short: "B",
3252
+ negatable: false,
3253
+ toKebab: true
3254
+ },
3255
+ costSource: {
3256
+ type: "enum",
3257
+ choices: costSourceChoices,
3258
+ description: "Session cost source: auto (prefer CC then ccusage), ccusage (always calculate), cc (always use Claude Code cost), both (show both costs)",
3259
+ default: "auto",
3260
+ negatable: false,
3261
+ toKebab: true
3262
+ },
3263
+ cache: {
3264
+ type: "boolean",
3265
+ description: "Enable cache for status line output (default: true)",
3266
+ negatable: true,
3267
+ default: true
3268
+ },
3269
+ refreshInterval: {
3270
+ type: "number",
3271
+ description: `Refresh interval in seconds for cache expiry (default: ${DEFAULT_REFRESH_INTERVAL_SECONDS})`,
3272
+ default: DEFAULT_REFRESH_INTERVAL_SECONDS
3273
+ },
3274
+ contextLowThreshold: {
3275
+ type: "custom",
3276
+ description: "Context usage percentage below which status is shown in green (0-100)",
3277
+ parse: (value$1) => parseContextThreshold(value$1),
3278
+ default: DEFAULT_CONTEXT_USAGE_THRESHOLDS.LOW
3279
+ },
3280
+ contextMediumThreshold: {
3281
+ type: "custom",
3282
+ description: "Context usage percentage below which status is shown in yellow (0-100)",
3283
+ parse: (value$1) => parseContextThreshold(value$1),
3284
+ default: DEFAULT_CONTEXT_USAGE_THRESHOLDS.MEDIUM
3285
+ },
3286
+ config: sharedArgs.config,
3287
+ debug: sharedArgs.debug
3288
+ },
3289
+ async run(ctx) {
3290
+ logger.level = 0;
3291
+ if (ctx.values.contextLowThreshold >= ctx.values.contextMediumThreshold) throw new Error(`Context low threshold (${ctx.values.contextLowThreshold}) must be less than medium threshold (${ctx.values.contextMediumThreshold})`);
3292
+ const mergedOptions = mergeConfigWithArgs(ctx, loadConfig(ctx.values.config, ctx.values.debug), ctx.values.debug);
3293
+ const refreshInterval = mergedOptions.refreshInterval;
3294
+ const stdin$2 = await getStdin();
3295
+ if (stdin$2.length === 0) {
3296
+ log("❌ No input provided");
3297
+ process$1.exit(1);
3298
+ }
3299
+ const hookDataParseResult = safeParse(statuslineHookJsonSchema, JSON.parse(stdin$2.trim()));
3300
+ if (!hookDataParseResult.success) {
3301
+ log("❌ Invalid input format:", flatten(hookDataParseResult.issues));
3302
+ process$1.exit(1);
3303
+ }
3304
+ const hookData = hookDataParseResult.output;
3305
+ const sessionId = hookData.session_id;
3306
+ const initialSemaphoreState = pipe(succeed(getSemaphore(sessionId)), map$1((semaphore) => semaphore.data), unwrap(void 0));
3307
+ const currentMtime = await getFileModifiedTime(hookData.transcript_path);
3308
+ if (mergedOptions.cache && initialSemaphoreState != null) {
3309
+ const isExpired = Date.now() - (initialSemaphoreState.lastUpdateTime ?? 0) >= refreshInterval * 1e3;
3310
+ const isFileModified = initialSemaphoreState.transcriptMtime !== currentMtime;
3311
+ if (!isExpired && !isFileModified) {
3312
+ log(initialSemaphoreState.lastOutput);
3313
+ return;
3314
+ }
3315
+ if (initialSemaphoreState.isUpdating === true) {
3316
+ const pid = initialSemaphoreState.pid;
3317
+ let isProcessAlive = false;
3318
+ if (pid != null) try {
3319
+ process$1.kill(pid, 0);
3320
+ isProcessAlive = true;
3321
+ } catch {
3322
+ isProcessAlive = false;
3323
+ }
3324
+ if (isProcessAlive) {
3325
+ log(initialSemaphoreState.lastOutput);
3326
+ return;
3327
+ }
3328
+ }
3329
+ }
3330
+ try {
3331
+ var _usingCtx$1 = _usingCtx();
3332
+ const currentPid = process$1.pid;
3333
+ const semaphore = _usingCtx$1.u(getSemaphore(sessionId));
3334
+ if (semaphore.data != null) semaphore.data = {
3335
+ ...semaphore.data,
3336
+ isUpdating: true,
3337
+ pid: currentPid
3338
+ };
3339
+ else {
3340
+ const currentMtimeForInit = await getFileModifiedTime(hookData.transcript_path);
3341
+ semaphore.data = {
3342
+ date: (/* @__PURE__ */ new Date()).toISOString(),
3343
+ lastOutput: "",
3344
+ lastUpdateTime: 0,
3345
+ transcriptPath: hookData.transcript_path,
3346
+ transcriptMtime: currentMtimeForInit,
3347
+ isUpdating: true,
3348
+ pid: currentPid
3349
+ };
3350
+ }
3351
+ } catch (_$1) {
3352
+ _usingCtx$1.e = _$1;
3353
+ } finally {
3354
+ _usingCtx$1.d();
3355
+ }
3356
+ const mainProcessingResult = pipe(await try_({
3357
+ try: async () => {
3358
+ const { sessionCost, ccCost, ccusageCost } = await (async () => {
3359
+ const costSource = ctx.values.costSource;
3360
+ const getCcusageCost = async () => {
3361
+ return pipe(try_({
3362
+ try: async () => loadSessionUsageById(sessionId, {
3363
+ mode: "auto",
3364
+ offline: mergedOptions.offline
3365
+ }),
3366
+ catch: (error) => error
3367
+ })(), map$1((sessionCost$1) => sessionCost$1?.totalCost), inspectError((error) => logger.error("Failed to load session data:", error)), unwrap(void 0));
3368
+ };
3369
+ if (costSource === "both") return {
3370
+ ccCost: hookData.cost?.total_cost_usd,
3371
+ ccusageCost: await getCcusageCost()
3372
+ };
3373
+ if (costSource === "cc") return { sessionCost: hookData.cost?.total_cost_usd };
3374
+ if (costSource === "ccusage") return { sessionCost: await getCcusageCost() };
3375
+ if (costSource === "auto") {
3376
+ if (hookData.cost?.total_cost_usd != null) return { sessionCost: hookData.cost.total_cost_usd };
3377
+ return { sessionCost: await getCcusageCost() };
3378
+ }
3379
+ unreachable(costSource);
3380
+ return {};
3381
+ })();
3382
+ const todayStr = (/* @__PURE__ */ new Date()).toISOString().split("T")[0]?.replace(/-/g, "") ?? "";
3383
+ const todayCost = await pipe(try_({
3384
+ try: async () => loadDailyUsageData({
3385
+ since: todayStr,
3386
+ until: todayStr,
3387
+ mode: "auto",
3388
+ offline: mergedOptions.offline
3389
+ }),
3390
+ catch: (error) => error
3391
+ })(), map$1((dailyData) => {
3392
+ if (dailyData.length > 0) return calculateTotals(dailyData).totalCost;
3393
+ return 0;
3394
+ }), inspectError((error) => logger.error("Failed to load daily data:", error)), unwrap(0));
3395
+ const { blockInfo, burnRateInfo } = await pipe(try_({
3396
+ try: async () => loadSessionBlockData({
3397
+ mode: "auto",
3398
+ offline: mergedOptions.offline
3399
+ }),
3400
+ catch: (error) => error
3401
+ })(), map$1((blocks) => {
3402
+ if (blocks.length === 0) return {
3403
+ blockInfo: "No active block",
3404
+ burnRateInfo: ""
3405
+ };
3406
+ const activeBlock = blocks.find((block) => {
3407
+ if (!block.isActive) return false;
3408
+ return true;
3409
+ });
3410
+ if (activeBlock != null) {
3411
+ const now = /* @__PURE__ */ new Date();
3412
+ const remaining = Math.round((activeBlock.endTime.getTime() - now.getTime()) / (1e3 * 60));
3413
+ const blockCost = activeBlock.costUSD;
3414
+ const blockInfo$1 = `${formatCurrency(blockCost)} block (${formatRemainingTime(remaining)})`;
3415
+ const burnRate = calculateBurnRate(activeBlock);
3416
+ return {
3417
+ blockInfo: blockInfo$1,
3418
+ burnRateInfo: burnRate != null ? (() => {
3419
+ const renderEmojiStatus = ctx.values.visualBurnRate === "emoji" || ctx.values.visualBurnRate === "emoji-text";
3420
+ const renderTextStatus = ctx.values.visualBurnRate === "text" || ctx.values.visualBurnRate === "emoji-text";
3421
+ const costPerHour = burnRate.costPerHour;
3422
+ const costPerHourStr = `${formatCurrency(costPerHour)}/hr`;
3423
+ const burnStatus = burnRate.tokensPerMinuteForIndicator < 2e3 ? "normal" : burnRate.tokensPerMinuteForIndicator < 5e3 ? "moderate" : "high";
3424
+ const { emoji, textValue, coloredString } = {
3425
+ normal: {
3426
+ emoji: "🟢",
3427
+ textValue: "Normal",
3428
+ coloredString: import_picocolors.default.green
3429
+ },
3430
+ moderate: {
3431
+ emoji: "⚠️",
3432
+ textValue: "Moderate",
3433
+ coloredString: import_picocolors.default.yellow
3434
+ },
3435
+ high: {
3436
+ emoji: "🚨",
3437
+ textValue: "High",
3438
+ coloredString: import_picocolors.default.red
3439
+ }
3440
+ }[burnStatus];
3441
+ const burnRateOutputSegments = [coloredString(costPerHourStr)];
3442
+ if (renderEmojiStatus) burnRateOutputSegments.push(emoji);
3443
+ if (renderTextStatus) burnRateOutputSegments.push(coloredString(`(${textValue})`));
3444
+ return ` | 🔥 ${burnRateOutputSegments.join(" ")}`;
3445
+ })() : ""
3446
+ };
3447
+ }
3448
+ return {
3449
+ blockInfo: "No active block",
3450
+ burnRateInfo: ""
3451
+ };
3452
+ }), inspectError((error) => logger.error("Failed to load block data:", error)), unwrap({
3453
+ blockInfo: "No active block",
3454
+ burnRateInfo: ""
3455
+ }));
3456
+ const formatContextInfo = (inputTokens, contextLimit) => {
3457
+ const percentage = Math.round(inputTokens / contextLimit * 100);
3458
+ const coloredPercentage = (percentage < ctx.values.contextLowThreshold ? import_picocolors.default.green : percentage < ctx.values.contextMediumThreshold ? import_picocolors.default.yellow : import_picocolors.default.red)(`${percentage}%`);
3459
+ return `${inputTokens.toLocaleString()} (${coloredPercentage})`;
3460
+ };
3461
+ const contextInfo = pipe(hookData.context_window != null ? succeed({
3462
+ inputTokens: hookData.context_window.total_input_tokens,
3463
+ contextLimit: hookData.context_window.context_window_size
3464
+ }) : await try_({
3465
+ try: async () => calculateContextTokens(hookData.transcript_path, hookData.model.id, mergedOptions.offline),
3466
+ catch: (error) => error
3467
+ })(), inspectError((error) => logger.debug(`Failed to calculate context tokens: ${error instanceof Error ? error.message : String(error)}`)), map$1((contextResult) => {
3468
+ if (contextResult == null) return;
3469
+ return formatContextInfo(contextResult.inputTokens, contextResult.contextLimit);
3470
+ }), unwrap(void 0));
3471
+ return `🤖 ${hookData.model.display_name} | 💰 ${(() => {
3472
+ if (ccCost != null || ccusageCost != null) return `(${ccCost != null ? formatCurrency(ccCost) : "N/A"} cc / ${ccusageCost != null ? formatCurrency(ccusageCost) : "N/A"} ccusage)`;
3473
+ return sessionCost != null ? formatCurrency(sessionCost) : "N/A";
3474
+ })()} session / ${formatCurrency(todayCost)} today / ${blockInfo}${burnRateInfo} | 🧠 ${contextInfo ?? "N/A"}`;
3475
+ },
3476
+ catch: (error) => error
3477
+ })());
3478
+ if (isSuccess(mainProcessingResult)) try {
3479
+ var _usingCtx3 = _usingCtx();
3480
+ const statusLine = mainProcessingResult.value;
3481
+ log(statusLine);
3482
+ if (!mergedOptions.cache) return;
3483
+ const semaphore = _usingCtx3.u(getSemaphore(sessionId));
3484
+ semaphore.data = {
3485
+ date: (/* @__PURE__ */ new Date()).toISOString(),
3486
+ lastOutput: statusLine,
3487
+ lastUpdateTime: Date.now(),
3488
+ transcriptPath: hookData.transcript_path,
3489
+ transcriptMtime: currentMtime,
3490
+ isUpdating: false,
3491
+ pid: void 0
3492
+ };
3493
+ return;
3494
+ } catch (_$1) {
3495
+ _usingCtx3.e = _$1;
3496
+ } finally {
3497
+ _usingCtx3.d();
3498
+ }
3499
+ if (isFailure(mainProcessingResult)) try {
3500
+ var _usingCtx4 = _usingCtx();
3501
+ if (initialSemaphoreState?.lastOutput != null && initialSemaphoreState.lastOutput !== "") log(initialSemaphoreState.lastOutput);
3502
+ else log("❌ Error generating status");
3503
+ logger.error("Error in statusline command:", mainProcessingResult.error);
3504
+ if (!mergedOptions.cache) return;
3505
+ const semaphore = _usingCtx4.u(getSemaphore(sessionId));
3506
+ if (semaphore.data != null) {
3507
+ semaphore.data.isUpdating = false;
3508
+ semaphore.data.pid = void 0;
3509
+ }
3510
+ } catch (_$1) {
3511
+ _usingCtx4.e = _$1;
3512
+ } finally {
3513
+ _usingCtx4.d();
3514
+ }
3515
+ }
3516
+ });
3517
+ const weeklyCommand = define({
3518
+ name: "weekly",
3519
+ description: "Show usage report grouped by week",
3520
+ args: {
3521
+ ...sharedArgs,
3522
+ startOfWeek: {
3523
+ type: "enum",
3524
+ short: "w",
3525
+ description: "Day to start the week on",
3526
+ default: "sunday",
3527
+ choices: WEEK_DAYS
3528
+ }
3529
+ },
3530
+ toKebab: true,
3531
+ async run(ctx) {
3532
+ const mergedOptions = mergeConfigWithArgs(ctx, loadConfig(ctx.values.config, ctx.values.debug), ctx.values.debug);
3533
+ const useJson = Boolean(mergedOptions.json) || mergedOptions.jq != null;
3534
+ if (useJson) logger.level = 0;
3535
+ const weeklyData = await loadWeeklyUsageData(mergedOptions);
3536
+ if (weeklyData.length === 0) {
3537
+ if (useJson) log(JSON.stringify({
3538
+ weekly: [],
3539
+ totals: {
3540
+ inputTokens: 0,
3541
+ outputTokens: 0,
3542
+ cacheCreationTokens: 0,
3543
+ cacheReadTokens: 0,
3544
+ totalTokens: 0,
3545
+ totalCost: 0
3546
+ }
3547
+ }, null, 2));
3548
+ else logger.warn("No Claude usage data found.");
3549
+ process$1.exit(0);
3550
+ }
3551
+ const totals = calculateTotals(weeklyData);
3552
+ if (mergedOptions.debug && !useJson) printMismatchReport(await detectMismatches(void 0), mergedOptions.debugSamples);
3553
+ if (useJson) {
3554
+ const jsonOutput = {
3555
+ weekly: weeklyData.map((data) => ({
3556
+ week: data.week,
3557
+ inputTokens: data.inputTokens,
3558
+ outputTokens: data.outputTokens,
3559
+ cacheCreationTokens: data.cacheCreationTokens,
3560
+ cacheReadTokens: data.cacheReadTokens,
3561
+ totalTokens: getTotalTokens(data),
3562
+ totalCost: data.totalCost,
3563
+ modelsUsed: data.modelsUsed,
3564
+ modelBreakdowns: data.modelBreakdowns
3565
+ })),
3566
+ totals: createTotalsObject(totals)
3567
+ };
3568
+ if (mergedOptions.jq != null) {
3569
+ const jqResult = await processWithJq(jsonOutput, mergedOptions.jq);
3570
+ if (isFailure(jqResult)) {
3571
+ logger.error(jqResult.error.message);
3572
+ process$1.exit(1);
3573
+ }
3574
+ log(jqResult.value);
3575
+ } else log(JSON.stringify(jsonOutput, null, 2));
3576
+ } else {
3577
+ logger.box("Claude Code Token Usage Report - Weekly");
3578
+ const table = createUsageReportTable({
3579
+ firstColumnName: "Week",
3580
+ dateFormatter: (dateStr) => formatDateCompact(dateStr, mergedOptions.timezone, mergedOptions.locale ?? void 0),
3581
+ forceCompact: ctx.values.compact
3582
+ });
3583
+ for (const data of weeklyData) {
3584
+ const row = formatUsageDataRow(data.week, {
3585
+ inputTokens: data.inputTokens,
3586
+ outputTokens: data.outputTokens,
3587
+ cacheCreationTokens: data.cacheCreationTokens,
3588
+ cacheReadTokens: data.cacheReadTokens,
3589
+ totalCost: data.totalCost,
3590
+ modelsUsed: data.modelsUsed
3591
+ });
3592
+ table.push(row);
3593
+ if (mergedOptions.breakdown) pushBreakdownRows(table, data.modelBreakdowns);
3594
+ }
3595
+ addEmptySeparatorRow(table, 8);
3596
+ const totalsRow = formatTotalsRow({
3597
+ inputTokens: totals.inputTokens,
3598
+ outputTokens: totals.outputTokens,
3599
+ cacheCreationTokens: totals.cacheCreationTokens,
3600
+ cacheReadTokens: totals.cacheReadTokens,
3601
+ totalCost: totals.totalCost
3602
+ });
3603
+ table.push(totalsRow);
3604
+ log(table.toString());
3605
+ if (table.isCompactMode()) {
3606
+ logger.info("\nRunning in Compact Mode");
3607
+ logger.info("Expand terminal width to see cache metrics and total tokens");
3608
+ }
3609
+ }
3610
+ }
3611
+ });
3612
+ const subCommandUnion = [
3613
+ ["daily", dailyCommand],
3614
+ ["monthly", monthlyCommand],
3615
+ ["weekly", weeklyCommand],
3616
+ ["session", sessionCommand],
3617
+ ["blocks", blocksCommand],
3618
+ ["statusline", statuslineCommand]
3619
+ ];
3620
+ const subCommands = /* @__PURE__ */ new Map();
3621
+ for (const [name$1, command] of subCommandUnion) subCommands.set(name$1, command);
3622
+ const mainCommand = dailyCommand;
3623
+ async function run() {
3624
+ let args = process$1.argv.slice(2);
3625
+ if (args[0] === "ccusage") args = args.slice(1);
3626
+ await cli(args, mainCommand, {
3627
+ name,
3628
+ version,
3629
+ description,
3630
+ subCommands,
3631
+ renderHeader: null
3632
+ });
3633
+ }
3634
+ await run();
3635
+ export {};