claude-scope 0.1.8 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/dist/claude-scope.cjs +764 -0
  2. package/package.json +9 -6
  3. package/dist/constants.d.ts +0 -56
  4. package/dist/constants.d.ts.map +0 -1
  5. package/dist/constants.js +0 -57
  6. package/dist/constants.js.map +0 -1
  7. package/dist/core/renderer.d.ts +0 -51
  8. package/dist/core/renderer.d.ts.map +0 -1
  9. package/dist/core/renderer.js +0 -75
  10. package/dist/core/renderer.js.map +0 -1
  11. package/dist/core/types.d.ts +0 -56
  12. package/dist/core/types.d.ts.map +0 -1
  13. package/dist/core/types.js +0 -5
  14. package/dist/core/types.js.map +0 -1
  15. package/dist/core/widget-registry.d.ts +0 -40
  16. package/dist/core/widget-registry.d.ts.map +0 -1
  17. package/dist/core/widget-registry.js +0 -75
  18. package/dist/core/widget-registry.js.map +0 -1
  19. package/dist/core/widget-types.d.ts +0 -30
  20. package/dist/core/widget-types.d.ts.map +0 -1
  21. package/dist/core/widget-types.js +0 -30
  22. package/dist/core/widget-types.js.map +0 -1
  23. package/dist/data/stdin-provider.d.ts +0 -44
  24. package/dist/data/stdin-provider.d.ts.map +0 -1
  25. package/dist/data/stdin-provider.js +0 -72
  26. package/dist/data/stdin-provider.js.map +0 -1
  27. package/dist/index.d.ts +0 -10
  28. package/dist/index.d.ts.map +0 -1
  29. package/dist/index.js.map +0 -1
  30. package/dist/providers/git-provider.d.ts +0 -71
  31. package/dist/providers/git-provider.d.ts.map +0 -1
  32. package/dist/providers/git-provider.js +0 -73
  33. package/dist/providers/git-provider.js.map +0 -1
  34. package/dist/schemas/stdin-schema.d.ts +0 -84
  35. package/dist/schemas/stdin-schema.d.ts.map +0 -1
  36. package/dist/schemas/stdin-schema.js +0 -48
  37. package/dist/schemas/stdin-schema.js.map +0 -1
  38. package/dist/types.d.ts +0 -31
  39. package/dist/types.d.ts.map +0 -1
  40. package/dist/types.js +0 -8
  41. package/dist/types.js.map +0 -1
  42. package/dist/ui/utils/colors.d.ts +0 -52
  43. package/dist/ui/utils/colors.d.ts.map +0 -1
  44. package/dist/ui/utils/colors.js +0 -54
  45. package/dist/ui/utils/colors.js.map +0 -1
  46. package/dist/ui/utils/formatters.d.ts +0 -56
  47. package/dist/ui/utils/formatters.d.ts.map +0 -1
  48. package/dist/ui/utils/formatters.js +0 -114
  49. package/dist/ui/utils/formatters.js.map +0 -1
  50. package/dist/validation/combinators.d.ts +0 -10
  51. package/dist/validation/combinators.d.ts.map +0 -1
  52. package/dist/validation/combinators.js +0 -49
  53. package/dist/validation/combinators.js.map +0 -1
  54. package/dist/validation/core.d.ts +0 -30
  55. package/dist/validation/core.d.ts.map +0 -1
  56. package/dist/validation/core.js +0 -2
  57. package/dist/validation/core.js.map +0 -1
  58. package/dist/validation/index.d.ts +0 -4
  59. package/dist/validation/index.d.ts.map +0 -1
  60. package/dist/validation/index.js +0 -4
  61. package/dist/validation/index.js.map +0 -1
  62. package/dist/validation/result.d.ts +0 -5
  63. package/dist/validation/result.d.ts.map +0 -1
  64. package/dist/validation/result.js +0 -11
  65. package/dist/validation/result.js.map +0 -1
  66. package/dist/validation/validators.d.ts +0 -7
  67. package/dist/validation/validators.d.ts.map +0 -1
  68. package/dist/validation/validators.js +0 -41
  69. package/dist/validation/validators.js.map +0 -1
  70. package/dist/widgets/context-widget.d.ts +0 -13
  71. package/dist/widgets/context-widget.d.ts.map +0 -1
  72. package/dist/widgets/context-widget.js +0 -31
  73. package/dist/widgets/context-widget.js.map +0 -1
  74. package/dist/widgets/core/stdin-data-widget.d.ts +0 -93
  75. package/dist/widgets/core/stdin-data-widget.d.ts.map +0 -1
  76. package/dist/widgets/core/stdin-data-widget.js +0 -84
  77. package/dist/widgets/core/stdin-data-widget.js.map +0 -1
  78. package/dist/widgets/cost-widget.d.ts +0 -13
  79. package/dist/widgets/cost-widget.d.ts.map +0 -1
  80. package/dist/widgets/cost-widget.js +0 -18
  81. package/dist/widgets/cost-widget.js.map +0 -1
  82. package/dist/widgets/duration-widget.d.ts +0 -13
  83. package/dist/widgets/duration-widget.d.ts.map +0 -1
  84. package/dist/widgets/duration-widget.js +0 -18
  85. package/dist/widgets/duration-widget.js.map +0 -1
  86. package/dist/widgets/git/git-changes-widget.d.ts +0 -38
  87. package/dist/widgets/git/git-changes-widget.d.ts.map +0 -1
  88. package/dist/widgets/git/git-changes-widget.js +0 -91
  89. package/dist/widgets/git/git-changes-widget.js.map +0 -1
  90. package/dist/widgets/git/git-widget.d.ts +0 -37
  91. package/dist/widgets/git/git-widget.d.ts.map +0 -1
  92. package/dist/widgets/git/git-widget.js +0 -67
  93. package/dist/widgets/git/git-widget.js.map +0 -1
  94. package/dist/widgets/model-widget.d.ts +0 -13
  95. package/dist/widgets/model-widget.d.ts.map +0 -1
  96. package/dist/widgets/model-widget.js +0 -15
  97. package/dist/widgets/model-widget.js.map +0 -1
@@ -0,0 +1,764 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+
21
+ // src/index.ts
22
+ var index_exports = {};
23
+ __export(index_exports, {
24
+ main: () => main
25
+ });
26
+ module.exports = __toCommonJS(index_exports);
27
+
28
+ // src/core/widget-registry.ts
29
+ var WidgetRegistry = class {
30
+ widgets = /* @__PURE__ */ new Map();
31
+ /**
32
+ * Register a widget
33
+ */
34
+ async register(widget, context) {
35
+ if (this.widgets.has(widget.id)) {
36
+ throw new Error(`Widget with id '${widget.id}' already registered`);
37
+ }
38
+ if (context) {
39
+ await widget.initialize(context);
40
+ }
41
+ this.widgets.set(widget.id, widget);
42
+ }
43
+ /**
44
+ * Unregister a widget
45
+ */
46
+ async unregister(id) {
47
+ const widget = this.widgets.get(id);
48
+ if (!widget) {
49
+ return;
50
+ }
51
+ try {
52
+ if (widget.cleanup) {
53
+ await widget.cleanup();
54
+ }
55
+ } finally {
56
+ this.widgets.delete(id);
57
+ }
58
+ }
59
+ /**
60
+ * Get a widget by id
61
+ */
62
+ get(id) {
63
+ return this.widgets.get(id);
64
+ }
65
+ /**
66
+ * Check if widget is registered
67
+ */
68
+ has(id) {
69
+ return this.widgets.has(id);
70
+ }
71
+ /**
72
+ * Get all registered widgets
73
+ */
74
+ getAll() {
75
+ return Array.from(this.widgets.values());
76
+ }
77
+ /**
78
+ * Get only enabled widgets
79
+ */
80
+ getEnabledWidgets() {
81
+ return this.getAll().filter((w) => w.isEnabled());
82
+ }
83
+ /**
84
+ * Clear all widgets
85
+ */
86
+ async clear() {
87
+ for (const widget of this.widgets.values()) {
88
+ if (widget.cleanup) {
89
+ await widget.cleanup();
90
+ }
91
+ }
92
+ this.widgets.clear();
93
+ }
94
+ };
95
+
96
+ // src/constants.ts
97
+ var TIME = {
98
+ /** Milliseconds per second */
99
+ MS_PER_SECOND: 1e3,
100
+ /** Seconds per minute */
101
+ SECONDS_PER_MINUTE: 60,
102
+ /** Seconds per hour */
103
+ SECONDS_PER_HOUR: 3600
104
+ };
105
+ var COST_THRESHOLDS = {
106
+ /** Below this value, show 4 decimal places ($0.0012) */
107
+ SMALL: 0.01,
108
+ /** Above this value, show no decimal places ($123) */
109
+ LARGE: 100
110
+ };
111
+ var CONTEXT_THRESHOLDS = {
112
+ /** Below this: green (low usage) */
113
+ LOW_MEDIUM: 50,
114
+ /** Below this: yellow (medium usage), above: red (high usage) */
115
+ MEDIUM_HIGH: 80
116
+ };
117
+ var DEFAULTS = {
118
+ /** Default separator between widgets */
119
+ SEPARATOR: " ",
120
+ /** Default width for progress bars in characters */
121
+ PROGRESS_BAR_WIDTH: 20
122
+ };
123
+ var ANSI_COLORS = {
124
+ /** Green color */
125
+ GREEN: "\x1B[32m",
126
+ /** Yellow color */
127
+ YELLOW: "\x1B[33m",
128
+ /** Red color */
129
+ RED: "\x1B[31m",
130
+ /** Reset color */
131
+ RESET: "\x1B[0m"
132
+ };
133
+ var DEFAULT_PROGRESS_BAR_WIDTH = DEFAULTS.PROGRESS_BAR_WIDTH;
134
+
135
+ // src/core/renderer.ts
136
+ var Renderer = class {
137
+ separator;
138
+ onError;
139
+ showErrors;
140
+ constructor(options = {}) {
141
+ this.separator = options.separator ?? DEFAULTS.SEPARATOR;
142
+ this.onError = options.onError;
143
+ this.showErrors = options.showErrors ?? false;
144
+ }
145
+ /**
146
+ * Render widgets into a single line with error boundaries
147
+ *
148
+ * Widgets that throw errors are logged (via onError callback) and skipped,
149
+ * allowing other widgets to continue rendering.
150
+ *
151
+ * @param widgets - Array of widgets to render
152
+ * @param context - Render context with width and timestamp
153
+ * @returns Combined widget outputs separated by separator
154
+ */
155
+ async render(widgets, context) {
156
+ const outputs = [];
157
+ for (const widget of widgets) {
158
+ if (!widget.isEnabled()) {
159
+ continue;
160
+ }
161
+ try {
162
+ const output = await widget.render(context);
163
+ if (output !== null) {
164
+ outputs.push(output);
165
+ }
166
+ } catch (error) {
167
+ this.handleError(error, widget);
168
+ if (this.showErrors) {
169
+ outputs.push(`${widget.id}:<err>`);
170
+ }
171
+ }
172
+ }
173
+ return outputs.join(this.separator);
174
+ }
175
+ /**
176
+ * Set custom separator
177
+ */
178
+ setSeparator(separator) {
179
+ this.separator = separator;
180
+ }
181
+ /**
182
+ * Handle widget render errors
183
+ *
184
+ * Calls the onError callback if provided, otherwise logs to console.warn
185
+ */
186
+ handleError(error, widget) {
187
+ if (this.onError) {
188
+ this.onError(error, widget);
189
+ } else {
190
+ console.warn(`[Widget ${widget.id}] ${error.message}`);
191
+ }
192
+ }
193
+ };
194
+
195
+ // src/core/widget-types.ts
196
+ function createWidgetMetadata(name, description, version = "1.0.0", author = "claude-scope") {
197
+ return {
198
+ name,
199
+ description,
200
+ version,
201
+ author
202
+ };
203
+ }
204
+
205
+ // src/providers/git-provider.ts
206
+ var import_node_child_process = require("node:child_process");
207
+ var import_node_util = require("node:util");
208
+ var execFileAsync = (0, import_node_util.promisify)(import_node_child_process.execFile);
209
+ var NativeGit = class {
210
+ cwd;
211
+ constructor(cwd) {
212
+ this.cwd = cwd;
213
+ }
214
+ async status() {
215
+ try {
216
+ const { stdout } = await execFileAsync("git", ["status", "--branch", "--short"], {
217
+ cwd: this.cwd
218
+ });
219
+ const match = stdout.match(/^##\s+(\S+)/m);
220
+ const current = match ? match[1] : null;
221
+ return { current };
222
+ } catch {
223
+ return { current: null };
224
+ }
225
+ }
226
+ async diffSummary(options) {
227
+ const args = ["diff", "--shortstat"];
228
+ if (options) {
229
+ args.push(...options);
230
+ }
231
+ try {
232
+ const { stdout } = await execFileAsync("git", args, {
233
+ cwd: this.cwd
234
+ });
235
+ const insertionMatch = stdout.match(/(\d+)\s+insertion/);
236
+ const deletionMatch = stdout.match(/(\d+)\s+deletion/);
237
+ const insertions = insertionMatch ? parseInt(insertionMatch[1], 10) : 0;
238
+ const deletions = deletionMatch ? parseInt(deletionMatch[1], 10) : 0;
239
+ const files = insertions > 0 || deletions > 0 ? [{ file: "(total)", insertions, deletions }] : [];
240
+ return { files };
241
+ } catch {
242
+ return { files: [] };
243
+ }
244
+ }
245
+ };
246
+ function createGit(cwd) {
247
+ return new NativeGit(cwd);
248
+ }
249
+
250
+ // src/widgets/git/git-widget.ts
251
+ var GitWidget = class {
252
+ id = "git";
253
+ metadata = createWidgetMetadata(
254
+ "Git Widget",
255
+ "Displays current git branch"
256
+ );
257
+ gitFactory;
258
+ git = null;
259
+ enabled = true;
260
+ cwd = null;
261
+ /**
262
+ * @param gitFactory - Optional factory function for creating IGit instances
263
+ * If not provided, uses default createGit (production)
264
+ * Tests can inject MockGit factory here
265
+ */
266
+ constructor(gitFactory) {
267
+ this.gitFactory = gitFactory || createGit;
268
+ }
269
+ async initialize(context) {
270
+ this.enabled = context.config?.enabled !== false;
271
+ }
272
+ async render(context) {
273
+ if (!this.enabled || !this.git || !this.cwd) {
274
+ return null;
275
+ }
276
+ try {
277
+ const status = await this.git.status();
278
+ const branch = status.current || null;
279
+ if (!branch) {
280
+ return null;
281
+ }
282
+ return ` ${branch}`;
283
+ } catch {
284
+ return null;
285
+ }
286
+ }
287
+ async update(data) {
288
+ if (data.cwd !== this.cwd) {
289
+ this.cwd = data.cwd;
290
+ this.git = this.gitFactory(data.cwd);
291
+ }
292
+ }
293
+ isEnabled() {
294
+ return this.enabled;
295
+ }
296
+ async cleanup() {
297
+ }
298
+ };
299
+
300
+ // src/widgets/core/stdin-data-widget.ts
301
+ var StdinDataWidget = class {
302
+ /**
303
+ * Stored stdin data from last update
304
+ */
305
+ data = null;
306
+ /**
307
+ * Widget enabled state
308
+ */
309
+ enabled = true;
310
+ /**
311
+ * Initialize widget with context
312
+ * @param context - Widget initialization context
313
+ */
314
+ async initialize(context) {
315
+ this.enabled = context.config?.enabled !== false;
316
+ }
317
+ /**
318
+ * Update widget with new stdin data
319
+ * @param data - Stdin data from Claude Code
320
+ */
321
+ async update(data) {
322
+ this.data = data;
323
+ }
324
+ /**
325
+ * Get stored stdin data
326
+ * @returns Stored stdin data
327
+ * @throws Error if data has not been initialized (update not called)
328
+ */
329
+ getData() {
330
+ if (!this.data) {
331
+ throw new Error(`Widget ${this.id} data not initialized. Call update() first.`);
332
+ }
333
+ return this.data;
334
+ }
335
+ /**
336
+ * Check if widget is enabled
337
+ * @returns true if widget should render
338
+ */
339
+ isEnabled() {
340
+ return this.enabled;
341
+ }
342
+ /**
343
+ * Template method - final, subclasses implement renderWithData()
344
+ *
345
+ * Handles null data checks and calls renderWithData() hook.
346
+ *
347
+ * @param context - Render context
348
+ * @returns Rendered string, or null if widget should not display
349
+ */
350
+ async render(context) {
351
+ if (!this.data || !this.enabled) {
352
+ return null;
353
+ }
354
+ return this.renderWithData(this.data, context);
355
+ }
356
+ };
357
+
358
+ // src/widgets/model-widget.ts
359
+ var ModelWidget = class extends StdinDataWidget {
360
+ id = "model";
361
+ metadata = createWidgetMetadata(
362
+ "Model",
363
+ "Displays the current Claude model name"
364
+ );
365
+ renderWithData(data, context) {
366
+ return data.model.display_name;
367
+ }
368
+ };
369
+
370
+ // src/ui/utils/formatters.ts
371
+ function formatDuration(ms) {
372
+ if (ms <= 0) return "0s";
373
+ const seconds = Math.floor(ms / TIME.MS_PER_SECOND);
374
+ const hours = Math.floor(seconds / TIME.SECONDS_PER_HOUR);
375
+ const minutes = Math.floor(seconds % TIME.SECONDS_PER_HOUR / TIME.SECONDS_PER_MINUTE);
376
+ const secs = seconds % TIME.SECONDS_PER_MINUTE;
377
+ const parts = [];
378
+ if (hours > 0) {
379
+ parts.push(`${hours}h`);
380
+ parts.push(`${minutes}m`);
381
+ parts.push(`${secs}s`);
382
+ } else if (minutes > 0) {
383
+ parts.push(`${minutes}m`);
384
+ parts.push(`${secs}s`);
385
+ } else {
386
+ parts.push(`${secs}s`);
387
+ }
388
+ return parts.join(" ");
389
+ }
390
+ function formatCostUSD(usd) {
391
+ const absUsd = Math.abs(usd);
392
+ if (usd < 0) {
393
+ return `$${usd.toFixed(2)}`;
394
+ } else if (absUsd < COST_THRESHOLDS.SMALL) {
395
+ return `$${usd.toFixed(4)}`;
396
+ } else if (absUsd < COST_THRESHOLDS.LARGE) {
397
+ return `$${usd.toFixed(2)}`;
398
+ } else {
399
+ return `$${Math.floor(usd).toFixed(0)}`;
400
+ }
401
+ }
402
+ function progressBar(percent, width = DEFAULTS.PROGRESS_BAR_WIDTH) {
403
+ const clampedPercent = Math.max(0, Math.min(100, percent));
404
+ const filled = Math.round(clampedPercent / 100 * width);
405
+ const empty = width - filled;
406
+ return "\u2588".repeat(filled) + "\u2591".repeat(empty);
407
+ }
408
+ function getContextColor(percent) {
409
+ const clampedPercent = Math.max(0, Math.min(100, percent));
410
+ if (clampedPercent < CONTEXT_THRESHOLDS.LOW_MEDIUM) {
411
+ return ANSI_COLORS.GREEN;
412
+ } else if (clampedPercent < CONTEXT_THRESHOLDS.MEDIUM_HIGH) {
413
+ return ANSI_COLORS.YELLOW;
414
+ } else {
415
+ return ANSI_COLORS.RED;
416
+ }
417
+ }
418
+ function colorize(text, color) {
419
+ return `${color}${text}${ANSI_COLORS.RESET}`;
420
+ }
421
+
422
+ // src/widgets/context-widget.ts
423
+ var ContextWidget = class extends StdinDataWidget {
424
+ id = "context";
425
+ metadata = createWidgetMetadata(
426
+ "Context",
427
+ "Displays context window usage with progress bar"
428
+ );
429
+ renderWithData(data, context) {
430
+ const { current_usage, context_window_size } = data.context_window;
431
+ if (!current_usage) return null;
432
+ const used = current_usage.input_tokens + current_usage.cache_creation_input_tokens + current_usage.output_tokens;
433
+ const percent = Math.round(used / context_window_size * 100);
434
+ const bar = progressBar(percent, DEFAULTS.PROGRESS_BAR_WIDTH);
435
+ const color = getContextColor(percent);
436
+ return colorize(`[${bar}] ${percent}%`, color);
437
+ }
438
+ };
439
+
440
+ // src/widgets/cost-widget.ts
441
+ var CostWidget = class extends StdinDataWidget {
442
+ id = "cost";
443
+ metadata = createWidgetMetadata(
444
+ "Cost",
445
+ "Displays session cost in USD"
446
+ );
447
+ renderWithData(data, context) {
448
+ if (!data.cost || data.cost.total_cost_usd === void 0) return null;
449
+ return formatCostUSD(data.cost.total_cost_usd);
450
+ }
451
+ };
452
+
453
+ // src/widgets/duration-widget.ts
454
+ var DurationWidget = class extends StdinDataWidget {
455
+ id = "duration";
456
+ metadata = createWidgetMetadata(
457
+ "Duration",
458
+ "Displays elapsed session time"
459
+ );
460
+ renderWithData(data, context) {
461
+ if (!data.cost || data.cost.total_duration_ms === void 0) return null;
462
+ return formatDuration(data.cost.total_duration_ms);
463
+ }
464
+ };
465
+
466
+ // src/widgets/git/git-changes-widget.ts
467
+ var GitChangesWidget = class {
468
+ id = "git-changes";
469
+ metadata = createWidgetMetadata(
470
+ "Git Changes",
471
+ "Displays git diff statistics"
472
+ );
473
+ gitFactory;
474
+ git = null;
475
+ enabled = true;
476
+ cwd = null;
477
+ /**
478
+ * @param gitFactory - Optional factory function for creating IGit instances
479
+ * If not provided, uses default createGit (production)
480
+ * Tests can inject MockGit factory here
481
+ */
482
+ constructor(gitFactory) {
483
+ this.gitFactory = gitFactory || createGit;
484
+ }
485
+ async initialize(context) {
486
+ this.enabled = context.config?.enabled !== false;
487
+ }
488
+ async update(data) {
489
+ if (data.cwd !== this.cwd) {
490
+ this.cwd = data.cwd;
491
+ this.git = this.gitFactory(data.cwd);
492
+ }
493
+ }
494
+ async render(context) {
495
+ if (!this.enabled || !this.git || !this.cwd) {
496
+ return null;
497
+ }
498
+ let changes;
499
+ try {
500
+ const summary = await this.git.diffSummary(["--shortstat"]);
501
+ let insertions = 0;
502
+ let deletions = 0;
503
+ if (summary.files && summary.files.length > 0) {
504
+ for (const file of summary.files) {
505
+ if (typeof file.insertions === "number") {
506
+ insertions += file.insertions;
507
+ }
508
+ if (typeof file.deletions === "number") {
509
+ deletions += file.deletions;
510
+ }
511
+ }
512
+ }
513
+ if (insertions === 0 && deletions === 0) {
514
+ return null;
515
+ }
516
+ changes = { insertions, deletions };
517
+ } catch {
518
+ return null;
519
+ }
520
+ if (!changes) return null;
521
+ if (changes.insertions === 0 && changes.deletions === 0) {
522
+ return null;
523
+ }
524
+ const parts = [];
525
+ if (changes.insertions > 0) parts.push(`+${changes.insertions}`);
526
+ if (changes.deletions > 0) parts.push(`-${changes.deletions}`);
527
+ return parts.join(",");
528
+ }
529
+ isEnabled() {
530
+ return this.enabled;
531
+ }
532
+ async cleanup() {
533
+ }
534
+ };
535
+
536
+ // src/validation/result.ts
537
+ function success(data) {
538
+ return { success: true, data };
539
+ }
540
+ function failure(path, message, value) {
541
+ return { success: false, error: { path, message, value } };
542
+ }
543
+ function formatError(error) {
544
+ const path = error.path.length > 0 ? error.path.join(".") : "root";
545
+ return `${path}: ${error.message}`;
546
+ }
547
+
548
+ // src/validation/validators.ts
549
+ function string() {
550
+ return {
551
+ validate(value) {
552
+ if (typeof value === "string") return success(value);
553
+ return failure([], "Expected string", value);
554
+ }
555
+ };
556
+ }
557
+ function number() {
558
+ return {
559
+ validate(value) {
560
+ if (typeof value === "number" && !Number.isNaN(value)) return success(value);
561
+ return failure([], "Expected number", value);
562
+ }
563
+ };
564
+ }
565
+ function literal(expected) {
566
+ return {
567
+ validate(value) {
568
+ if (value === expected) return success(expected);
569
+ return failure([], `Expected '${expected}'`, value);
570
+ }
571
+ };
572
+ }
573
+
574
+ // src/validation/combinators.ts
575
+ function object(shape) {
576
+ return {
577
+ validate(value) {
578
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
579
+ return failure([], "Expected object", value);
580
+ }
581
+ const result = {};
582
+ for (const [key, validator] of Object.entries(shape)) {
583
+ const fieldValue = value[key];
584
+ const validationResult = validator.validate(fieldValue);
585
+ if (!validationResult.success) {
586
+ return {
587
+ success: false,
588
+ error: { ...validationResult.error, path: [key, ...validationResult.error.path] }
589
+ };
590
+ }
591
+ result[key] = validationResult.data;
592
+ }
593
+ return success(result);
594
+ }
595
+ };
596
+ }
597
+ function optional(validator) {
598
+ return {
599
+ validate(value) {
600
+ if (value === void 0) return success(void 0);
601
+ return validator.validate(value);
602
+ }
603
+ };
604
+ }
605
+ function nullable(validator) {
606
+ return {
607
+ validate(value) {
608
+ if (value === null) return success(null);
609
+ return validator.validate(value);
610
+ }
611
+ };
612
+ }
613
+
614
+ // src/schemas/stdin-schema.ts
615
+ var ContextUsageSchema = object({
616
+ input_tokens: number(),
617
+ output_tokens: number(),
618
+ cache_creation_input_tokens: number(),
619
+ cache_read_input_tokens: number()
620
+ });
621
+ var CostInfoSchema = object({
622
+ total_cost_usd: optional(number()),
623
+ total_duration_ms: optional(number()),
624
+ total_api_duration_ms: optional(number()),
625
+ total_lines_added: optional(number()),
626
+ total_lines_removed: optional(number())
627
+ });
628
+ var ContextWindowSchema = object({
629
+ total_input_tokens: number(),
630
+ total_output_tokens: number(),
631
+ context_window_size: number(),
632
+ current_usage: nullable(ContextUsageSchema)
633
+ });
634
+ var ModelInfoSchema = object({
635
+ id: string(),
636
+ display_name: string()
637
+ });
638
+ var WorkspaceSchema = object({
639
+ current_dir: string(),
640
+ project_dir: string()
641
+ });
642
+ var OutputStyleSchema = object({
643
+ name: string()
644
+ });
645
+ var StdinDataSchema = object({
646
+ hook_event_name: literal("Status"),
647
+ session_id: string(),
648
+ transcript_path: string(),
649
+ cwd: string(),
650
+ model: ModelInfoSchema,
651
+ workspace: WorkspaceSchema,
652
+ version: string(),
653
+ output_style: OutputStyleSchema,
654
+ cost: optional(CostInfoSchema),
655
+ context_window: ContextWindowSchema
656
+ });
657
+
658
+ // src/data/stdin-provider.ts
659
+ var StdinParseError = class extends Error {
660
+ constructor(message) {
661
+ super(message);
662
+ this.name = "StdinParseError";
663
+ }
664
+ };
665
+ var StdinValidationError = class extends Error {
666
+ constructor(message) {
667
+ super(message);
668
+ this.name = "StdinValidationError";
669
+ }
670
+ };
671
+ var StdinProvider = class {
672
+ /**
673
+ * Parse and validate JSON string from stdin
674
+ * @param input JSON string to parse
675
+ * @returns Validated StdinData object
676
+ * @throws StdinParseError if JSON is malformed
677
+ * @throws StdinValidationError if data doesn't match schema
678
+ */
679
+ async parse(input) {
680
+ if (!input || input.trim().length === 0) {
681
+ throw new StdinParseError("stdin data is empty");
682
+ }
683
+ let data;
684
+ try {
685
+ data = JSON.parse(input);
686
+ } catch (error) {
687
+ throw new StdinParseError(`Invalid JSON: ${error.message}`);
688
+ }
689
+ const result = StdinDataSchema.validate(data);
690
+ if (!result.success) {
691
+ throw new StdinValidationError(
692
+ `Validation failed: ${formatError(result.error)}`
693
+ );
694
+ }
695
+ return result.data;
696
+ }
697
+ /**
698
+ * Safe parse that returns result instead of throwing
699
+ * Useful for testing and optional validation
700
+ * @param input JSON string to parse
701
+ * @returns Result object with success flag
702
+ */
703
+ async safeParse(input) {
704
+ try {
705
+ const data = await this.parse(input);
706
+ return { success: true, data };
707
+ } catch (error) {
708
+ return { success: false, error: error.message };
709
+ }
710
+ }
711
+ };
712
+
713
+ // src/index.ts
714
+ async function readStdin() {
715
+ const chunks = [];
716
+ for await (const chunk of process.stdin) {
717
+ chunks.push(chunk);
718
+ }
719
+ return Buffer.concat(chunks).toString("utf8");
720
+ }
721
+ async function main() {
722
+ try {
723
+ const stdin = await readStdin();
724
+ if (!stdin || stdin.trim().length === 0) {
725
+ return "";
726
+ }
727
+ const provider = new StdinProvider();
728
+ const stdinData = await provider.parse(stdin);
729
+ const registry = new WidgetRegistry();
730
+ await registry.register(new ModelWidget());
731
+ await registry.register(new ContextWidget());
732
+ await registry.register(new CostWidget());
733
+ await registry.register(new DurationWidget());
734
+ await registry.register(new GitWidget());
735
+ await registry.register(new GitChangesWidget());
736
+ const renderer = new Renderer({
737
+ separator: " \u2502 ",
738
+ onError: (error, widget) => {
739
+ },
740
+ showErrors: false
741
+ });
742
+ for (const widget of registry.getAll()) {
743
+ await widget.update(stdinData);
744
+ }
745
+ const output = await renderer.render(
746
+ registry.getEnabledWidgets(),
747
+ { width: 80, timestamp: Date.now() }
748
+ );
749
+ return output || "";
750
+ } catch (error) {
751
+ return "";
752
+ }
753
+ }
754
+ main().then((output) => {
755
+ if (output) {
756
+ console.log(output);
757
+ }
758
+ }).catch(() => {
759
+ process.exit(0);
760
+ });
761
+ // Annotate the CommonJS export names for ESM import in node:
762
+ 0 && (module.exports = {
763
+ main
764
+ });