claude-scope 0.5.4 → 0.5.8
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/README.md +26 -1
- package/dist/claude-scope.cjs +645 -196
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -55,7 +55,32 @@ Or for global install:
|
|
|
55
55
|
|
|
56
56
|
## Usage
|
|
57
57
|
|
|
58
|
-
Once configured, claude-scope displays
|
|
58
|
+
Once configured, claude-scope displays real-time session information in your statusline including:
|
|
59
|
+
- Git branch and changes
|
|
60
|
+
- Model information
|
|
61
|
+
- Context usage with progress bar
|
|
62
|
+
- Session duration
|
|
63
|
+
- Cost estimation
|
|
64
|
+
- Lines added/removed
|
|
65
|
+
- Configuration counts
|
|
66
|
+
- Poker hand (entertainment)
|
|
67
|
+
|
|
68
|
+
### Widget Display Styles
|
|
69
|
+
|
|
70
|
+
Each widget supports multiple display styles for customization:
|
|
71
|
+
|
|
72
|
+
| Style | Description |
|
|
73
|
+
|-------|-------------|
|
|
74
|
+
| `balanced` | Default balanced style (minimalism + informativeness) |
|
|
75
|
+
| `compact` | Maximally compact display |
|
|
76
|
+
| `playful` | Fun style with informative emojis |
|
|
77
|
+
| `verbose` | Detailed text descriptions |
|
|
78
|
+
| `technical` | Technical details (model IDs, milliseconds, etc.) |
|
|
79
|
+
| `symbolic` | Symbol-based representation |
|
|
80
|
+
| `labeled` | Prefix labels for clarity |
|
|
81
|
+
| `indicator` | Bullet indicator prefix |
|
|
82
|
+
| `fancy` | Decorative formatting (brackets, quotes) |
|
|
83
|
+
| `compact-verbose` | Compact with K-formatted numbers |
|
|
59
84
|
|
|
60
85
|
**Note:** This is an early release with basic functionality. Additional features (repository status, session analytics, etc.) are planned for future releases.
|
|
61
86
|
|
package/dist/claude-scope.cjs
CHANGED
|
@@ -112,12 +112,6 @@ var TIME = {
|
|
|
112
112
|
/** Seconds per hour */
|
|
113
113
|
SECONDS_PER_HOUR: 3600
|
|
114
114
|
};
|
|
115
|
-
var COST_THRESHOLDS = {
|
|
116
|
-
/** Below this value, show 4 decimal places ($0.0012) */
|
|
117
|
-
SMALL: 0.01,
|
|
118
|
-
/** Above this value, show no decimal places ($123) */
|
|
119
|
-
LARGE: 100
|
|
120
|
-
};
|
|
121
115
|
var DEFAULTS = {
|
|
122
116
|
/** Default separator between widgets */
|
|
123
117
|
SEPARATOR: " ",
|
|
@@ -254,25 +248,23 @@ var NativeGit = class {
|
|
|
254
248
|
const { stdout } = await execFileAsync("git", args, {
|
|
255
249
|
cwd: this.cwd
|
|
256
250
|
});
|
|
251
|
+
const fileMatch = stdout.match(/(\d+)\s+file(s?)\s+changed/);
|
|
257
252
|
const insertionMatch = stdout.match(/(\d+)\s+insertion/);
|
|
258
253
|
const deletionMatch = stdout.match(/(\d+)\s+deletion/);
|
|
254
|
+
const fileCount = fileMatch ? parseInt(fileMatch[1], 10) : 0;
|
|
259
255
|
const insertions = insertionMatch ? parseInt(insertionMatch[1], 10) : 0;
|
|
260
256
|
const deletions = deletionMatch ? parseInt(deletionMatch[1], 10) : 0;
|
|
261
257
|
const files = insertions > 0 || deletions > 0 ? [{ file: "(total)", insertions, deletions }] : [];
|
|
262
|
-
return { files };
|
|
258
|
+
return { fileCount, files };
|
|
263
259
|
} catch {
|
|
264
|
-
return { files: [] };
|
|
260
|
+
return { fileCount: 0, files: [] };
|
|
265
261
|
}
|
|
266
262
|
}
|
|
267
263
|
async latestTag() {
|
|
268
264
|
try {
|
|
269
|
-
const { stdout } = await execFileAsync(
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
{
|
|
273
|
-
cwd: this.cwd
|
|
274
|
-
}
|
|
275
|
-
);
|
|
265
|
+
const { stdout } = await execFileAsync("git", ["describe", "--tags", "--abbrev=0"], {
|
|
266
|
+
cwd: this.cwd
|
|
267
|
+
});
|
|
276
268
|
return stdout.trim();
|
|
277
269
|
} catch {
|
|
278
270
|
return null;
|
|
@@ -283,6 +275,95 @@ function createGit(cwd) {
|
|
|
283
275
|
return new NativeGit(cwd);
|
|
284
276
|
}
|
|
285
277
|
|
|
278
|
+
// src/ui/utils/style-utils.ts
|
|
279
|
+
function withLabel(prefix, value) {
|
|
280
|
+
if (prefix === "") return value;
|
|
281
|
+
return `${prefix}: ${value}`;
|
|
282
|
+
}
|
|
283
|
+
function withIndicator(value) {
|
|
284
|
+
return `\u25CF ${value}`;
|
|
285
|
+
}
|
|
286
|
+
function progressBar(percent, width = 10) {
|
|
287
|
+
const clamped = Math.max(0, Math.min(100, percent));
|
|
288
|
+
const filled = Math.round(clamped / 100 * width);
|
|
289
|
+
const empty = width - filled;
|
|
290
|
+
return "\u2588".repeat(filled) + "\u2591".repeat(empty);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// src/widgets/git/styles.ts
|
|
294
|
+
var gitStyles = {
|
|
295
|
+
minimal: (data) => {
|
|
296
|
+
return data.branch;
|
|
297
|
+
},
|
|
298
|
+
balanced: (data) => {
|
|
299
|
+
if (data.changes && data.changes.files > 0) {
|
|
300
|
+
const parts = [];
|
|
301
|
+
if (data.changes.insertions > 0) parts.push(`+${data.changes.insertions}`);
|
|
302
|
+
if (data.changes.deletions > 0) parts.push(`-${data.changes.deletions}`);
|
|
303
|
+
if (parts.length > 0) {
|
|
304
|
+
return `${data.branch} [${parts.join(" ")}]`;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
return data.branch;
|
|
308
|
+
},
|
|
309
|
+
compact: (data) => {
|
|
310
|
+
if (data.changes && data.changes.files > 0) {
|
|
311
|
+
const parts = [];
|
|
312
|
+
if (data.changes.insertions > 0) parts.push(`+${data.changes.insertions}`);
|
|
313
|
+
if (data.changes.deletions > 0) parts.push(`-${data.changes.deletions}`);
|
|
314
|
+
if (parts.length > 0) {
|
|
315
|
+
return `${data.branch} ${parts.join("/")}`;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
return data.branch;
|
|
319
|
+
},
|
|
320
|
+
playful: (data) => {
|
|
321
|
+
if (data.changes && data.changes.files > 0) {
|
|
322
|
+
const parts = [];
|
|
323
|
+
if (data.changes.insertions > 0) parts.push(`\u2B06${data.changes.insertions}`);
|
|
324
|
+
if (data.changes.deletions > 0) parts.push(`\u2B07${data.changes.deletions}`);
|
|
325
|
+
if (parts.length > 0) {
|
|
326
|
+
return `\u{1F500} ${data.branch} ${parts.join(" ")}`;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
return `\u{1F500} ${data.branch}`;
|
|
330
|
+
},
|
|
331
|
+
verbose: (data) => {
|
|
332
|
+
if (data.changes && data.changes.files > 0) {
|
|
333
|
+
const parts = [];
|
|
334
|
+
if (data.changes.insertions > 0) parts.push(`+${data.changes.insertions} insertions`);
|
|
335
|
+
if (data.changes.deletions > 0) parts.push(`-${data.changes.deletions} deletions`);
|
|
336
|
+
if (parts.length > 0) {
|
|
337
|
+
return `branch: ${data.branch} [${parts.join(", ")}]`;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
return `branch: ${data.branch} (HEAD)`;
|
|
341
|
+
},
|
|
342
|
+
labeled: (data) => {
|
|
343
|
+
if (data.changes && data.changes.files > 0) {
|
|
344
|
+
const parts = [];
|
|
345
|
+
if (data.changes.insertions > 0) parts.push(`+${data.changes.insertions}`);
|
|
346
|
+
if (data.changes.deletions > 0) parts.push(`-${data.changes.deletions}`);
|
|
347
|
+
if (parts.length > 0) {
|
|
348
|
+
const changes = `${data.changes.files} files: ${parts.join("/")}`;
|
|
349
|
+
return `Git: ${data.branch} [${changes}]`;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
return `Git: ${data.branch}`;
|
|
353
|
+
},
|
|
354
|
+
indicator: (data) => {
|
|
355
|
+
if (data.changes && data.changes.files > 0) {
|
|
356
|
+
const parts = [];
|
|
357
|
+
if (data.changes.insertions > 0) parts.push(`+${data.changes.insertions}`);
|
|
358
|
+
if (data.changes.deletions > 0) parts.push(`-${data.changes.deletions}`);
|
|
359
|
+
if (parts.length > 0) {
|
|
360
|
+
return `\u25CF ${data.branch} [${parts.join(" ")}]`;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
return withIndicator(data.branch);
|
|
364
|
+
}
|
|
365
|
+
};
|
|
366
|
+
|
|
286
367
|
// src/widgets/git/git-widget.ts
|
|
287
368
|
var GitWidget = class {
|
|
288
369
|
id = "git";
|
|
@@ -298,6 +379,7 @@ var GitWidget = class {
|
|
|
298
379
|
git = null;
|
|
299
380
|
enabled = true;
|
|
300
381
|
cwd = null;
|
|
382
|
+
styleFn = gitStyles.balanced;
|
|
301
383
|
/**
|
|
302
384
|
* @param gitFactory - Optional factory function for creating IGit instances
|
|
303
385
|
* If not provided, uses default createGit (production)
|
|
@@ -306,6 +388,12 @@ var GitWidget = class {
|
|
|
306
388
|
constructor(gitFactory) {
|
|
307
389
|
this.gitFactory = gitFactory || createGit;
|
|
308
390
|
}
|
|
391
|
+
setStyle(style = "balanced") {
|
|
392
|
+
const fn = gitStyles[style];
|
|
393
|
+
if (fn) {
|
|
394
|
+
this.styleFn = fn;
|
|
395
|
+
}
|
|
396
|
+
}
|
|
309
397
|
async initialize(context) {
|
|
310
398
|
this.enabled = context.config?.enabled !== false;
|
|
311
399
|
}
|
|
@@ -319,7 +407,24 @@ var GitWidget = class {
|
|
|
319
407
|
if (!branch) {
|
|
320
408
|
return null;
|
|
321
409
|
}
|
|
322
|
-
|
|
410
|
+
let changes;
|
|
411
|
+
try {
|
|
412
|
+
const diffSummary = await this.git.diffSummary();
|
|
413
|
+
if (diffSummary.fileCount > 0) {
|
|
414
|
+
let insertions = 0;
|
|
415
|
+
let deletions = 0;
|
|
416
|
+
for (const file of diffSummary.files) {
|
|
417
|
+
insertions += file.insertions || 0;
|
|
418
|
+
deletions += file.deletions || 0;
|
|
419
|
+
}
|
|
420
|
+
if (insertions > 0 || deletions > 0) {
|
|
421
|
+
changes = { files: diffSummary.fileCount, insertions, deletions };
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
} catch {
|
|
425
|
+
}
|
|
426
|
+
const renderData = { branch, changes };
|
|
427
|
+
return this.styleFn(renderData);
|
|
323
428
|
} catch {
|
|
324
429
|
return null;
|
|
325
430
|
}
|
|
@@ -337,13 +442,29 @@ var GitWidget = class {
|
|
|
337
442
|
}
|
|
338
443
|
};
|
|
339
444
|
|
|
340
|
-
// src/
|
|
341
|
-
var
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
445
|
+
// src/widgets/git-tag/styles.ts
|
|
446
|
+
var gitTagStyles = {
|
|
447
|
+
balanced: (data) => {
|
|
448
|
+
return data.tag || "\u2014";
|
|
449
|
+
},
|
|
450
|
+
compact: (data) => {
|
|
451
|
+
if (!data.tag) return "\u2014";
|
|
452
|
+
return data.tag.replace(/^v/, "");
|
|
453
|
+
},
|
|
454
|
+
playful: (data) => {
|
|
455
|
+
return `\u{1F3F7}\uFE0F ${data.tag || "\u2014"}`;
|
|
456
|
+
},
|
|
457
|
+
verbose: (data) => {
|
|
458
|
+
if (!data.tag) return "version: none";
|
|
459
|
+
return `version ${data.tag}`;
|
|
460
|
+
},
|
|
461
|
+
labeled: (data) => {
|
|
462
|
+
return withLabel("Tag", data.tag || "none");
|
|
463
|
+
},
|
|
464
|
+
indicator: (data) => {
|
|
465
|
+
return withIndicator(data.tag || "\u2014");
|
|
466
|
+
}
|
|
467
|
+
};
|
|
347
468
|
|
|
348
469
|
// src/widgets/git/git-tag-widget.ts
|
|
349
470
|
var GitTagWidget = class {
|
|
@@ -360,7 +481,7 @@ var GitTagWidget = class {
|
|
|
360
481
|
git = null;
|
|
361
482
|
enabled = true;
|
|
362
483
|
cwd = null;
|
|
363
|
-
|
|
484
|
+
styleFn = gitTagStyles.balanced;
|
|
364
485
|
/**
|
|
365
486
|
* @param gitFactory - Optional factory function for creating IGit instances
|
|
366
487
|
* If not provided, uses default createGit (production)
|
|
@@ -369,6 +490,12 @@ var GitTagWidget = class {
|
|
|
369
490
|
constructor(gitFactory) {
|
|
370
491
|
this.gitFactory = gitFactory || createGit;
|
|
371
492
|
}
|
|
493
|
+
setStyle(style = "balanced") {
|
|
494
|
+
const fn = gitTagStyles[style];
|
|
495
|
+
if (fn) {
|
|
496
|
+
this.styleFn = fn;
|
|
497
|
+
}
|
|
498
|
+
}
|
|
372
499
|
async initialize(context) {
|
|
373
500
|
this.enabled = context.config?.enabled !== false;
|
|
374
501
|
}
|
|
@@ -377,12 +504,9 @@ var GitTagWidget = class {
|
|
|
377
504
|
return null;
|
|
378
505
|
}
|
|
379
506
|
try {
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
}
|
|
384
|
-
const tagValue = `${green}${this.latestTag}${reset}`;
|
|
385
|
-
return `${gray}Tag:${reset} ${tagValue}`;
|
|
507
|
+
const latestTag = await (this.git.latestTag?.() ?? Promise.resolve(null));
|
|
508
|
+
const renderData = { tag: latestTag };
|
|
509
|
+
return this.styleFn(renderData);
|
|
386
510
|
} catch {
|
|
387
511
|
return null;
|
|
388
512
|
}
|
|
@@ -400,6 +524,34 @@ var GitTagWidget = class {
|
|
|
400
524
|
}
|
|
401
525
|
};
|
|
402
526
|
|
|
527
|
+
// src/widgets/model/styles.ts
|
|
528
|
+
function getShortName(displayName) {
|
|
529
|
+
return displayName.replace(/^Claude\s+/, "");
|
|
530
|
+
}
|
|
531
|
+
var modelStyles = {
|
|
532
|
+
balanced: (data) => {
|
|
533
|
+
return data.displayName;
|
|
534
|
+
},
|
|
535
|
+
compact: (data) => {
|
|
536
|
+
return getShortName(data.displayName);
|
|
537
|
+
},
|
|
538
|
+
playful: (data) => {
|
|
539
|
+
return `\u{1F916} ${getShortName(data.displayName)}`;
|
|
540
|
+
},
|
|
541
|
+
technical: (data) => {
|
|
542
|
+
return data.id;
|
|
543
|
+
},
|
|
544
|
+
symbolic: (data) => {
|
|
545
|
+
return `\u25C6 ${getShortName(data.displayName)}`;
|
|
546
|
+
},
|
|
547
|
+
labeled: (data) => {
|
|
548
|
+
return withLabel("Model", getShortName(data.displayName));
|
|
549
|
+
},
|
|
550
|
+
indicator: (data) => {
|
|
551
|
+
return withIndicator(getShortName(data.displayName));
|
|
552
|
+
}
|
|
553
|
+
};
|
|
554
|
+
|
|
403
555
|
// src/widgets/core/stdin-data-widget.ts
|
|
404
556
|
var StdinDataWidget = class {
|
|
405
557
|
/**
|
|
@@ -469,8 +621,39 @@ var ModelWidget = class extends StdinDataWidget {
|
|
|
469
621
|
0
|
|
470
622
|
// First line
|
|
471
623
|
);
|
|
472
|
-
|
|
473
|
-
|
|
624
|
+
styleFn = modelStyles.balanced;
|
|
625
|
+
setStyle(style = "balanced") {
|
|
626
|
+
const fn = modelStyles[style];
|
|
627
|
+
if (fn) {
|
|
628
|
+
this.styleFn = fn;
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
renderWithData(data, _context) {
|
|
632
|
+
const renderData = {
|
|
633
|
+
displayName: data.model.display_name,
|
|
634
|
+
id: data.model.id
|
|
635
|
+
};
|
|
636
|
+
return this.styleFn(renderData);
|
|
637
|
+
}
|
|
638
|
+
};
|
|
639
|
+
|
|
640
|
+
// src/ui/utils/colors.ts
|
|
641
|
+
var reset = "\x1B[0m";
|
|
642
|
+
var red = "\x1B[31m";
|
|
643
|
+
var gray = "\x1B[90m";
|
|
644
|
+
var lightGray = "\x1B[37m";
|
|
645
|
+
var bold = "\x1B[1m";
|
|
646
|
+
|
|
647
|
+
// src/ui/theme/default-theme.ts
|
|
648
|
+
var DEFAULT_THEME = {
|
|
649
|
+
context: {
|
|
650
|
+
low: gray,
|
|
651
|
+
medium: gray,
|
|
652
|
+
high: gray
|
|
653
|
+
},
|
|
654
|
+
lines: {
|
|
655
|
+
added: gray,
|
|
656
|
+
removed: gray
|
|
474
657
|
}
|
|
475
658
|
};
|
|
476
659
|
|
|
@@ -495,37 +678,42 @@ function formatDuration(ms) {
|
|
|
495
678
|
return parts.join(" ");
|
|
496
679
|
}
|
|
497
680
|
function formatCostUSD(usd) {
|
|
498
|
-
|
|
499
|
-
if (usd < 0) {
|
|
500
|
-
return `$${usd.toFixed(2)}`;
|
|
501
|
-
} else if (absUsd < COST_THRESHOLDS.SMALL) {
|
|
502
|
-
return `$${usd.toFixed(4)}`;
|
|
503
|
-
} else if (absUsd < COST_THRESHOLDS.LARGE) {
|
|
504
|
-
return `$${usd.toFixed(2)}`;
|
|
505
|
-
} else {
|
|
506
|
-
return `$${Math.floor(usd).toFixed(0)}`;
|
|
507
|
-
}
|
|
508
|
-
}
|
|
509
|
-
function progressBar(percent, width = DEFAULTS.PROGRESS_BAR_WIDTH) {
|
|
510
|
-
const clampedPercent = Math.max(0, Math.min(100, percent));
|
|
511
|
-
const filled = Math.round(clampedPercent / 100 * width);
|
|
512
|
-
const empty = width - filled;
|
|
513
|
-
return "\u2588".repeat(filled) + "\u2591".repeat(empty);
|
|
681
|
+
return `$${usd.toFixed(2)}`;
|
|
514
682
|
}
|
|
515
683
|
function colorize(text, color) {
|
|
516
684
|
return `${color}${text}${ANSI_COLORS.RESET}`;
|
|
517
685
|
}
|
|
518
686
|
|
|
519
|
-
// src/
|
|
520
|
-
var
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
high: gray
|
|
687
|
+
// src/widgets/context/styles.ts
|
|
688
|
+
var contextStyles = {
|
|
689
|
+
balanced: (data) => {
|
|
690
|
+
const bar = progressBar(data.percent, 10);
|
|
691
|
+
return `[${bar}] ${data.percent}%`;
|
|
525
692
|
},
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
693
|
+
compact: (data) => {
|
|
694
|
+
return `${data.percent}%`;
|
|
695
|
+
},
|
|
696
|
+
playful: (data) => {
|
|
697
|
+
const bar = progressBar(data.percent, 10);
|
|
698
|
+
return `\u{1F9E0} [${bar}] ${data.percent}%`;
|
|
699
|
+
},
|
|
700
|
+
verbose: (data) => {
|
|
701
|
+
const usedFormatted = data.used.toLocaleString();
|
|
702
|
+
const maxFormatted = data.contextWindowSize.toLocaleString();
|
|
703
|
+
return `${usedFormatted} / ${maxFormatted} tokens (${data.percent}%)`;
|
|
704
|
+
},
|
|
705
|
+
symbolic: (data) => {
|
|
706
|
+
const filled = Math.round(data.percent / 100 * 5);
|
|
707
|
+
const empty = 5 - filled;
|
|
708
|
+
return `${"\u25AE".repeat(filled)}${"\u25AF".repeat(empty)} ${data.percent}%`;
|
|
709
|
+
},
|
|
710
|
+
"compact-verbose": (data) => {
|
|
711
|
+
const usedK = data.used >= 1e3 ? `${Math.floor(data.used / 1e3)}K` : data.used.toString();
|
|
712
|
+
const maxK = data.contextWindowSize >= 1e3 ? `${Math.floor(data.contextWindowSize / 1e3)}K` : data.contextWindowSize.toString();
|
|
713
|
+
return `${data.percent}% (${usedK}/${maxK})`;
|
|
714
|
+
},
|
|
715
|
+
indicator: (data) => {
|
|
716
|
+
return `\u25CF ${data.percent}%`;
|
|
529
717
|
}
|
|
530
718
|
};
|
|
531
719
|
|
|
@@ -541,18 +729,30 @@ var ContextWidget = class extends StdinDataWidget {
|
|
|
541
729
|
// First line
|
|
542
730
|
);
|
|
543
731
|
colors;
|
|
732
|
+
styleFn = contextStyles.balanced;
|
|
544
733
|
constructor(colors) {
|
|
545
734
|
super();
|
|
546
735
|
this.colors = colors ?? DEFAULT_THEME.context;
|
|
547
736
|
}
|
|
548
|
-
|
|
737
|
+
setStyle(style = "balanced") {
|
|
738
|
+
const fn = contextStyles[style];
|
|
739
|
+
if (fn) {
|
|
740
|
+
this.styleFn = fn;
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
renderWithData(data, _context) {
|
|
549
744
|
const { current_usage, context_window_size } = data.context_window;
|
|
550
745
|
if (!current_usage) return null;
|
|
551
746
|
const used = current_usage.input_tokens + current_usage.cache_creation_input_tokens + current_usage.cache_read_input_tokens + current_usage.output_tokens;
|
|
552
747
|
const percent = Math.round(used / context_window_size * 100);
|
|
553
|
-
const
|
|
748
|
+
const renderData = {
|
|
749
|
+
used,
|
|
750
|
+
contextWindowSize: context_window_size,
|
|
751
|
+
percent
|
|
752
|
+
};
|
|
753
|
+
const output = this.styleFn(renderData);
|
|
554
754
|
const color = this.getContextColor(percent);
|
|
555
|
-
return colorize(
|
|
755
|
+
return colorize(output, color);
|
|
556
756
|
}
|
|
557
757
|
getContextColor(percent) {
|
|
558
758
|
const clampedPercent = Math.max(0, Math.min(100, percent));
|
|
@@ -566,6 +766,25 @@ var ContextWidget = class extends StdinDataWidget {
|
|
|
566
766
|
}
|
|
567
767
|
};
|
|
568
768
|
|
|
769
|
+
// src/widgets/cost/styles.ts
|
|
770
|
+
var costStyles = {
|
|
771
|
+
balanced: (data) => {
|
|
772
|
+
return formatCostUSD(data.costUsd);
|
|
773
|
+
},
|
|
774
|
+
compact: (data) => {
|
|
775
|
+
return formatCostUSD(data.costUsd);
|
|
776
|
+
},
|
|
777
|
+
playful: (data) => {
|
|
778
|
+
return `\u{1F4B0} ${formatCostUSD(data.costUsd)}`;
|
|
779
|
+
},
|
|
780
|
+
labeled: (data) => {
|
|
781
|
+
return withLabel("Cost", formatCostUSD(data.costUsd));
|
|
782
|
+
},
|
|
783
|
+
indicator: (data) => {
|
|
784
|
+
return withIndicator(formatCostUSD(data.costUsd));
|
|
785
|
+
}
|
|
786
|
+
};
|
|
787
|
+
|
|
569
788
|
// src/widgets/cost-widget.ts
|
|
570
789
|
var CostWidget = class extends StdinDataWidget {
|
|
571
790
|
id = "cost";
|
|
@@ -577,12 +796,65 @@ var CostWidget = class extends StdinDataWidget {
|
|
|
577
796
|
0
|
|
578
797
|
// First line
|
|
579
798
|
);
|
|
580
|
-
|
|
799
|
+
styleFn = costStyles.balanced;
|
|
800
|
+
setStyle(style = "balanced") {
|
|
801
|
+
const fn = costStyles[style];
|
|
802
|
+
if (fn) {
|
|
803
|
+
this.styleFn = fn;
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
renderWithData(data, _context) {
|
|
581
807
|
if (!data.cost || data.cost.total_cost_usd === void 0) return null;
|
|
582
|
-
|
|
808
|
+
const renderData = {
|
|
809
|
+
costUsd: data.cost.total_cost_usd
|
|
810
|
+
};
|
|
811
|
+
return this.styleFn(renderData);
|
|
583
812
|
}
|
|
584
813
|
};
|
|
585
814
|
|
|
815
|
+
// src/widgets/lines/styles.ts
|
|
816
|
+
function createLinesStyles(colors) {
|
|
817
|
+
return {
|
|
818
|
+
balanced: (data) => {
|
|
819
|
+
const addedStr = colorize(`+${data.added}`, colors.added);
|
|
820
|
+
const removedStr = colorize(`-${data.removed}`, colors.removed);
|
|
821
|
+
return `${addedStr}/${removedStr}`;
|
|
822
|
+
},
|
|
823
|
+
compact: (data) => {
|
|
824
|
+
const addedStr = colorize(`+${data.added}`, colors.added);
|
|
825
|
+
const removedStr = colorize(`-${data.removed}`, colors.removed);
|
|
826
|
+
return `${addedStr}${removedStr}`;
|
|
827
|
+
},
|
|
828
|
+
playful: (data) => {
|
|
829
|
+
const addedStr = colorize(`\u2795${data.added}`, colors.added);
|
|
830
|
+
const removedStr = colorize(`\u2796${data.removed}`, colors.removed);
|
|
831
|
+
return `${addedStr} ${removedStr}`;
|
|
832
|
+
},
|
|
833
|
+
verbose: (data) => {
|
|
834
|
+
const parts = [];
|
|
835
|
+
if (data.added > 0) {
|
|
836
|
+
parts.push(colorize(`+${data.added} added`, colors.added));
|
|
837
|
+
}
|
|
838
|
+
if (data.removed > 0) {
|
|
839
|
+
parts.push(colorize(`-${data.removed} removed`, colors.removed));
|
|
840
|
+
}
|
|
841
|
+
return parts.join(", ");
|
|
842
|
+
},
|
|
843
|
+
labeled: (data) => {
|
|
844
|
+
const addedStr = colorize(`+${data.added}`, colors.added);
|
|
845
|
+
const removedStr = colorize(`-${data.removed}`, colors.removed);
|
|
846
|
+
const lines = `${addedStr}/${removedStr}`;
|
|
847
|
+
return withLabel("Lines", lines);
|
|
848
|
+
},
|
|
849
|
+
indicator: (data) => {
|
|
850
|
+
const addedStr = colorize(`+${data.added}`, colors.added);
|
|
851
|
+
const removedStr = colorize(`-${data.removed}`, colors.removed);
|
|
852
|
+
const lines = `${addedStr}/${removedStr}`;
|
|
853
|
+
return withIndicator(lines);
|
|
854
|
+
}
|
|
855
|
+
};
|
|
856
|
+
}
|
|
857
|
+
|
|
586
858
|
// src/widgets/lines-widget.ts
|
|
587
859
|
var LinesWidget = class extends StdinDataWidget {
|
|
588
860
|
id = "lines";
|
|
@@ -595,16 +867,59 @@ var LinesWidget = class extends StdinDataWidget {
|
|
|
595
867
|
// First line
|
|
596
868
|
);
|
|
597
869
|
colors;
|
|
870
|
+
linesStyles;
|
|
871
|
+
styleFn;
|
|
598
872
|
constructor(colors) {
|
|
599
873
|
super();
|
|
600
874
|
this.colors = colors ?? DEFAULT_THEME.lines;
|
|
875
|
+
this.linesStyles = createLinesStyles(this.colors);
|
|
876
|
+
this.styleFn = this.linesStyles.balanced;
|
|
877
|
+
}
|
|
878
|
+
setStyle(style = "balanced") {
|
|
879
|
+
const fn = this.linesStyles[style];
|
|
880
|
+
if (fn) {
|
|
881
|
+
this.styleFn = fn;
|
|
882
|
+
}
|
|
601
883
|
}
|
|
602
|
-
renderWithData(data,
|
|
884
|
+
renderWithData(data, _context) {
|
|
603
885
|
const added = data.cost?.total_lines_added ?? 0;
|
|
604
886
|
const removed = data.cost?.total_lines_removed ?? 0;
|
|
605
|
-
const
|
|
606
|
-
|
|
607
|
-
|
|
887
|
+
const renderData = { added, removed };
|
|
888
|
+
return this.styleFn(renderData);
|
|
889
|
+
}
|
|
890
|
+
};
|
|
891
|
+
|
|
892
|
+
// src/widgets/duration/styles.ts
|
|
893
|
+
var durationStyles = {
|
|
894
|
+
balanced: (data) => {
|
|
895
|
+
return formatDuration(data.durationMs);
|
|
896
|
+
},
|
|
897
|
+
compact: (data) => {
|
|
898
|
+
const totalSeconds = Math.floor(data.durationMs / 1e3);
|
|
899
|
+
const hours = Math.floor(totalSeconds / 3600);
|
|
900
|
+
const minutes = Math.floor(totalSeconds % 3600 / 60);
|
|
901
|
+
if (hours > 0) {
|
|
902
|
+
return `${hours}h${minutes}m`;
|
|
903
|
+
}
|
|
904
|
+
return `${minutes}m`;
|
|
905
|
+
},
|
|
906
|
+
playful: (data) => {
|
|
907
|
+
const totalSeconds = Math.floor(data.durationMs / 1e3);
|
|
908
|
+
const hours = Math.floor(totalSeconds / 3600);
|
|
909
|
+
const minutes = Math.floor(totalSeconds % 3600 / 60);
|
|
910
|
+
if (hours > 0) {
|
|
911
|
+
return `\u231B ${hours}h ${minutes}m`;
|
|
912
|
+
}
|
|
913
|
+
return `\u231B ${minutes}m`;
|
|
914
|
+
},
|
|
915
|
+
technical: (data) => {
|
|
916
|
+
return `${Math.floor(data.durationMs)}ms`;
|
|
917
|
+
},
|
|
918
|
+
labeled: (data) => {
|
|
919
|
+
return withLabel("Time", formatDuration(data.durationMs));
|
|
920
|
+
},
|
|
921
|
+
indicator: (data) => {
|
|
922
|
+
return withIndicator(formatDuration(data.durationMs));
|
|
608
923
|
}
|
|
609
924
|
};
|
|
610
925
|
|
|
@@ -619,83 +934,19 @@ var DurationWidget = class extends StdinDataWidget {
|
|
|
619
934
|
0
|
|
620
935
|
// First line
|
|
621
936
|
);
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
// src/widgets/git/git-changes-widget.ts
|
|
629
|
-
var GitChangesWidget = class {
|
|
630
|
-
id = "git-changes";
|
|
631
|
-
metadata = createWidgetMetadata(
|
|
632
|
-
"Git Changes",
|
|
633
|
-
"Displays git diff statistics",
|
|
634
|
-
"1.0.0",
|
|
635
|
-
"claude-scope",
|
|
636
|
-
0
|
|
637
|
-
// First line
|
|
638
|
-
);
|
|
639
|
-
gitFactory;
|
|
640
|
-
git = null;
|
|
641
|
-
enabled = true;
|
|
642
|
-
cwd = null;
|
|
643
|
-
/**
|
|
644
|
-
* @param gitFactory - Optional factory function for creating IGit instances
|
|
645
|
-
* If not provided, uses default createGit (production)
|
|
646
|
-
* Tests can inject MockGit factory here
|
|
647
|
-
*/
|
|
648
|
-
constructor(gitFactory) {
|
|
649
|
-
this.gitFactory = gitFactory || createGit;
|
|
650
|
-
}
|
|
651
|
-
async initialize(context) {
|
|
652
|
-
this.enabled = context.config?.enabled !== false;
|
|
653
|
-
}
|
|
654
|
-
async update(data) {
|
|
655
|
-
if (data.cwd !== this.cwd) {
|
|
656
|
-
this.cwd = data.cwd;
|
|
657
|
-
this.git = this.gitFactory(data.cwd);
|
|
658
|
-
}
|
|
659
|
-
}
|
|
660
|
-
async render(context) {
|
|
661
|
-
if (!this.enabled || !this.git || !this.cwd) {
|
|
662
|
-
return null;
|
|
663
|
-
}
|
|
664
|
-
let changes;
|
|
665
|
-
try {
|
|
666
|
-
const summary = await this.git.diffSummary(["--shortstat"]);
|
|
667
|
-
let insertions = 0;
|
|
668
|
-
let deletions = 0;
|
|
669
|
-
if (summary.files && summary.files.length > 0) {
|
|
670
|
-
for (const file of summary.files) {
|
|
671
|
-
if (typeof file.insertions === "number") {
|
|
672
|
-
insertions += file.insertions;
|
|
673
|
-
}
|
|
674
|
-
if (typeof file.deletions === "number") {
|
|
675
|
-
deletions += file.deletions;
|
|
676
|
-
}
|
|
677
|
-
}
|
|
678
|
-
}
|
|
679
|
-
if (insertions === 0 && deletions === 0) {
|
|
680
|
-
return null;
|
|
681
|
-
}
|
|
682
|
-
changes = { insertions, deletions };
|
|
683
|
-
} catch {
|
|
684
|
-
return null;
|
|
937
|
+
styleFn = durationStyles.balanced;
|
|
938
|
+
setStyle(style = "balanced") {
|
|
939
|
+
const fn = durationStyles[style];
|
|
940
|
+
if (fn) {
|
|
941
|
+
this.styleFn = fn;
|
|
685
942
|
}
|
|
686
|
-
if (!changes) return null;
|
|
687
|
-
if (changes.insertions === 0 && changes.deletions === 0) {
|
|
688
|
-
return null;
|
|
689
|
-
}
|
|
690
|
-
const parts = [];
|
|
691
|
-
if (changes.insertions > 0) parts.push(`+${changes.insertions}`);
|
|
692
|
-
if (changes.deletions > 0) parts.push(`-${changes.deletions}`);
|
|
693
|
-
return parts.join(",");
|
|
694
943
|
}
|
|
695
|
-
|
|
696
|
-
return
|
|
697
|
-
|
|
698
|
-
|
|
944
|
+
renderWithData(data, _context) {
|
|
945
|
+
if (!data.cost || data.cost.total_duration_ms === void 0) return null;
|
|
946
|
+
const renderData = {
|
|
947
|
+
durationMs: data.cost.total_duration_ms
|
|
948
|
+
};
|
|
949
|
+
return this.styleFn(renderData);
|
|
699
950
|
}
|
|
700
951
|
};
|
|
701
952
|
|
|
@@ -846,6 +1097,82 @@ var ConfigProvider = class {
|
|
|
846
1097
|
}
|
|
847
1098
|
};
|
|
848
1099
|
|
|
1100
|
+
// src/widgets/config-count/styles.ts
|
|
1101
|
+
var configCountStyles = {
|
|
1102
|
+
balanced: (data) => {
|
|
1103
|
+
const { claudeMdCount, rulesCount, mcpCount, hooksCount } = data;
|
|
1104
|
+
const parts = [];
|
|
1105
|
+
if (claudeMdCount > 0) {
|
|
1106
|
+
parts.push(`CLAUDE.md:${claudeMdCount}`);
|
|
1107
|
+
}
|
|
1108
|
+
if (rulesCount > 0) {
|
|
1109
|
+
parts.push(`rules:${rulesCount}`);
|
|
1110
|
+
}
|
|
1111
|
+
if (mcpCount > 0) {
|
|
1112
|
+
parts.push(`MCPs:${mcpCount}`);
|
|
1113
|
+
}
|
|
1114
|
+
if (hooksCount > 0) {
|
|
1115
|
+
parts.push(`hooks:${hooksCount}`);
|
|
1116
|
+
}
|
|
1117
|
+
return parts.join(" \u2502 ");
|
|
1118
|
+
},
|
|
1119
|
+
compact: (data) => {
|
|
1120
|
+
const { claudeMdCount, rulesCount, mcpCount, hooksCount } = data;
|
|
1121
|
+
const parts = [];
|
|
1122
|
+
if (claudeMdCount > 0) {
|
|
1123
|
+
parts.push(`${claudeMdCount} docs`);
|
|
1124
|
+
}
|
|
1125
|
+
if (rulesCount > 0) {
|
|
1126
|
+
parts.push(`${rulesCount} rules`);
|
|
1127
|
+
}
|
|
1128
|
+
if (mcpCount > 0) {
|
|
1129
|
+
parts.push(`${mcpCount} MCPs`);
|
|
1130
|
+
}
|
|
1131
|
+
if (hooksCount > 0) {
|
|
1132
|
+
const hookLabel = hooksCount === 1 ? "hook" : "hooks";
|
|
1133
|
+
parts.push(`${hooksCount} ${hookLabel}`);
|
|
1134
|
+
}
|
|
1135
|
+
return parts.join(" \u2502 ");
|
|
1136
|
+
},
|
|
1137
|
+
playful: (data) => {
|
|
1138
|
+
const { claudeMdCount, rulesCount, mcpCount, hooksCount } = data;
|
|
1139
|
+
const parts = [];
|
|
1140
|
+
if (claudeMdCount > 0) {
|
|
1141
|
+
parts.push(`\u{1F4C4} CLAUDE.md:${claudeMdCount}`);
|
|
1142
|
+
}
|
|
1143
|
+
if (rulesCount > 0) {
|
|
1144
|
+
parts.push(`\u{1F4DC} rules:${rulesCount}`);
|
|
1145
|
+
}
|
|
1146
|
+
if (mcpCount > 0) {
|
|
1147
|
+
parts.push(`\u{1F50C} MCPs:${mcpCount}`);
|
|
1148
|
+
}
|
|
1149
|
+
if (hooksCount > 0) {
|
|
1150
|
+
parts.push(`\u{1FA9D} hooks:${hooksCount}`);
|
|
1151
|
+
}
|
|
1152
|
+
return parts.join(" \u2502 ");
|
|
1153
|
+
},
|
|
1154
|
+
verbose: (data) => {
|
|
1155
|
+
const { claudeMdCount, rulesCount, mcpCount, hooksCount } = data;
|
|
1156
|
+
const parts = [];
|
|
1157
|
+
if (claudeMdCount > 0) {
|
|
1158
|
+
parts.push(`${claudeMdCount} CLAUDE.md`);
|
|
1159
|
+
}
|
|
1160
|
+
if (rulesCount > 0) {
|
|
1161
|
+
parts.push(`${rulesCount} rules`);
|
|
1162
|
+
}
|
|
1163
|
+
if (mcpCount > 0) {
|
|
1164
|
+
parts.push(`${mcpCount} MCP servers`);
|
|
1165
|
+
}
|
|
1166
|
+
if (hooksCount > 0) {
|
|
1167
|
+
parts.push(`${hooksCount} hook`);
|
|
1168
|
+
}
|
|
1169
|
+
return parts.join(" \u2502 ");
|
|
1170
|
+
}
|
|
1171
|
+
};
|
|
1172
|
+
|
|
1173
|
+
// src/core/style-types.ts
|
|
1174
|
+
var DEFAULT_WIDGET_STYLE = "balanced";
|
|
1175
|
+
|
|
849
1176
|
// src/widgets/config-count-widget.ts
|
|
850
1177
|
var ConfigCountWidget = class {
|
|
851
1178
|
id = "config-count";
|
|
@@ -860,6 +1187,13 @@ var ConfigCountWidget = class {
|
|
|
860
1187
|
configProvider = new ConfigProvider();
|
|
861
1188
|
configs;
|
|
862
1189
|
cwd;
|
|
1190
|
+
styleFn = configCountStyles.balanced;
|
|
1191
|
+
setStyle(style = DEFAULT_WIDGET_STYLE) {
|
|
1192
|
+
const fn = configCountStyles[style];
|
|
1193
|
+
if (fn) {
|
|
1194
|
+
this.styleFn = fn;
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
863
1197
|
async initialize() {
|
|
864
1198
|
}
|
|
865
1199
|
async update(data) {
|
|
@@ -878,20 +1212,13 @@ var ConfigCountWidget = class {
|
|
|
878
1212
|
return null;
|
|
879
1213
|
}
|
|
880
1214
|
const { claudeMdCount, rulesCount, mcpCount, hooksCount } = this.configs;
|
|
881
|
-
const
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
if (mcpCount > 0) {
|
|
889
|
-
parts.push(`\u{1F50C} ${mcpCount} MCPs`);
|
|
890
|
-
}
|
|
891
|
-
if (hooksCount > 0) {
|
|
892
|
-
parts.push(`\u{1FA9D} ${hooksCount} hooks`);
|
|
893
|
-
}
|
|
894
|
-
return parts.join(" \u2502 ") || null;
|
|
1215
|
+
const renderData = {
|
|
1216
|
+
claudeMdCount,
|
|
1217
|
+
rulesCount,
|
|
1218
|
+
mcpCount,
|
|
1219
|
+
hooksCount
|
|
1220
|
+
};
|
|
1221
|
+
return this.styleFn(renderData);
|
|
895
1222
|
}
|
|
896
1223
|
async cleanup() {
|
|
897
1224
|
}
|
|
@@ -942,10 +1269,10 @@ function getRankValue(rank) {
|
|
|
942
1269
|
"8": 8,
|
|
943
1270
|
"9": 9,
|
|
944
1271
|
"10": 10,
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
1272
|
+
J: 11,
|
|
1273
|
+
Q: 12,
|
|
1274
|
+
K: 13,
|
|
1275
|
+
A: 14
|
|
949
1276
|
};
|
|
950
1277
|
return values[rank];
|
|
951
1278
|
}
|
|
@@ -1246,7 +1573,11 @@ function evaluateHand(hole, board) {
|
|
|
1246
1573
|
const sfHighCard = getStraightFlushHighCard(allCards, flushSuit);
|
|
1247
1574
|
if (sfHighCard === 14) {
|
|
1248
1575
|
const participatingCards = getStraightFlushIndices(allCards, 14, flushSuit);
|
|
1249
|
-
return {
|
|
1576
|
+
return {
|
|
1577
|
+
rank: 10 /* RoyalFlush */,
|
|
1578
|
+
...HAND_DISPLAY[10 /* RoyalFlush */],
|
|
1579
|
+
participatingCards
|
|
1580
|
+
};
|
|
1250
1581
|
}
|
|
1251
1582
|
}
|
|
1252
1583
|
if (flush) {
|
|
@@ -1254,13 +1585,21 @@ function evaluateHand(hole, board) {
|
|
|
1254
1585
|
const sfHighCard = getStraightFlushHighCard(allCards, flushSuit);
|
|
1255
1586
|
if (sfHighCard !== null) {
|
|
1256
1587
|
const participatingCards = getStraightFlushIndices(allCards, sfHighCard, flushSuit);
|
|
1257
|
-
return {
|
|
1588
|
+
return {
|
|
1589
|
+
rank: 9 /* StraightFlush */,
|
|
1590
|
+
...HAND_DISPLAY[9 /* StraightFlush */],
|
|
1591
|
+
participatingCards
|
|
1592
|
+
};
|
|
1258
1593
|
}
|
|
1259
1594
|
}
|
|
1260
1595
|
if (maxCount === 4) {
|
|
1261
1596
|
const rank = getMostCommonRank(allCards);
|
|
1262
1597
|
const participatingCards = findCardsOfRank(allCards, rank);
|
|
1263
|
-
return {
|
|
1598
|
+
return {
|
|
1599
|
+
rank: 8 /* FourOfAKind */,
|
|
1600
|
+
...HAND_DISPLAY[8 /* FourOfAKind */],
|
|
1601
|
+
participatingCards
|
|
1602
|
+
};
|
|
1264
1603
|
}
|
|
1265
1604
|
if (maxCount === 3 && pairCount >= 1) {
|
|
1266
1605
|
const participatingCards = getFullHouseIndices(allCards);
|
|
@@ -1279,7 +1618,11 @@ function evaluateHand(hole, board) {
|
|
|
1279
1618
|
if (maxCount === 3) {
|
|
1280
1619
|
const rank = getMostCommonRank(allCards);
|
|
1281
1620
|
const participatingCards = findCardsOfRank(allCards, rank);
|
|
1282
|
-
return {
|
|
1621
|
+
return {
|
|
1622
|
+
rank: 4 /* ThreeOfAKind */,
|
|
1623
|
+
...HAND_DISPLAY[4 /* ThreeOfAKind */],
|
|
1624
|
+
participatingCards
|
|
1625
|
+
};
|
|
1283
1626
|
}
|
|
1284
1627
|
if (pairCount >= 2) {
|
|
1285
1628
|
const [rank1, rank2] = getTwoPairRanks(allCards);
|
|
@@ -1294,8 +1637,100 @@ function evaluateHand(hole, board) {
|
|
|
1294
1637
|
return { rank: 2 /* OnePair */, ...HAND_DISPLAY[2 /* OnePair */], participatingCards };
|
|
1295
1638
|
}
|
|
1296
1639
|
const highestIdx = getHighestCardIndex(allCards);
|
|
1297
|
-
return {
|
|
1640
|
+
return {
|
|
1641
|
+
rank: 1 /* HighCard */,
|
|
1642
|
+
...HAND_DISPLAY[1 /* HighCard */],
|
|
1643
|
+
participatingCards: [highestIdx]
|
|
1644
|
+
};
|
|
1645
|
+
}
|
|
1646
|
+
|
|
1647
|
+
// src/widgets/poker/styles.ts
|
|
1648
|
+
var HAND_ABBREVIATIONS = {
|
|
1649
|
+
"Royal Flush": "RF",
|
|
1650
|
+
"Straight Flush": "SF",
|
|
1651
|
+
"Four of a Kind": "4K",
|
|
1652
|
+
"Full House": "FH",
|
|
1653
|
+
"Flush": "FL",
|
|
1654
|
+
"Straight": "ST",
|
|
1655
|
+
"Three of a Kind": "3K",
|
|
1656
|
+
"Two Pair": "2P",
|
|
1657
|
+
"One Pair": "1P",
|
|
1658
|
+
"High Card": "HC",
|
|
1659
|
+
"Nothing": "\u2014"
|
|
1660
|
+
};
|
|
1661
|
+
function formatCardByParticipation(cardData, isParticipating) {
|
|
1662
|
+
const color = isRedSuit(cardData.card.suit) ? red : gray;
|
|
1663
|
+
const cardText = formatCard(cardData.card);
|
|
1664
|
+
if (isParticipating) {
|
|
1665
|
+
return `${color}${bold}(${cardText})${reset} `;
|
|
1666
|
+
} else {
|
|
1667
|
+
return `${color}${cardText}${reset} `;
|
|
1668
|
+
}
|
|
1669
|
+
}
|
|
1670
|
+
function formatCardCompact(cardData, isParticipating) {
|
|
1671
|
+
const color = isRedSuit(cardData.card.suit) ? red : gray;
|
|
1672
|
+
const cardText = formatCardTextCompact(cardData.card);
|
|
1673
|
+
if (isParticipating) {
|
|
1674
|
+
return `${color}${bold}(${cardText})${reset}`;
|
|
1675
|
+
} else {
|
|
1676
|
+
return `${color}${cardText}${reset}`;
|
|
1677
|
+
}
|
|
1678
|
+
}
|
|
1679
|
+
function formatCardTextCompact(card) {
|
|
1680
|
+
const rankSymbols = {
|
|
1681
|
+
"10": "T",
|
|
1682
|
+
"11": "J",
|
|
1683
|
+
"12": "Q",
|
|
1684
|
+
"13": "K",
|
|
1685
|
+
"14": "A"
|
|
1686
|
+
};
|
|
1687
|
+
const rank = String(card.rank);
|
|
1688
|
+
const rankSymbol = rankSymbols[rank] ?? rank;
|
|
1689
|
+
return `${rankSymbol}${card.suit}`;
|
|
1690
|
+
}
|
|
1691
|
+
function formatHandResult(handResult) {
|
|
1692
|
+
if (!handResult) {
|
|
1693
|
+
return "\u2014";
|
|
1694
|
+
}
|
|
1695
|
+
const playerParticipates = handResult.participatingIndices.some((idx) => idx < 2);
|
|
1696
|
+
if (!playerParticipates) {
|
|
1697
|
+
return `Nothing \u{1F0CF}`;
|
|
1698
|
+
} else {
|
|
1699
|
+
return `${handResult.name}! ${handResult.emoji}`;
|
|
1700
|
+
}
|
|
1701
|
+
}
|
|
1702
|
+
function getHandAbbreviation(handResult) {
|
|
1703
|
+
if (!handResult) {
|
|
1704
|
+
return "\u2014 (\u2014)";
|
|
1705
|
+
}
|
|
1706
|
+
const abbreviation = HAND_ABBREVIATIONS[handResult.name] ?? "\u2014";
|
|
1707
|
+
return `${abbreviation} (${handResult.name})`;
|
|
1298
1708
|
}
|
|
1709
|
+
var pokerStyles = {
|
|
1710
|
+
balanced: (data) => {
|
|
1711
|
+
const { holeCards, boardCards, handResult } = data;
|
|
1712
|
+
const participatingSet = new Set(handResult?.participatingIndices || []);
|
|
1713
|
+
const handStr = holeCards.map((hc, idx) => formatCardByParticipation(hc, participatingSet.has(idx))).join("");
|
|
1714
|
+
const boardStr = boardCards.map((bc, idx) => formatCardByParticipation(bc, participatingSet.has(idx + 2))).join("");
|
|
1715
|
+
const handLabel = colorize("Hand:", lightGray);
|
|
1716
|
+
const boardLabel = colorize("Board:", lightGray);
|
|
1717
|
+
return `${handLabel} ${handStr}| ${boardLabel} ${boardStr}\u2192 ${formatHandResult(handResult)}`;
|
|
1718
|
+
},
|
|
1719
|
+
compact: (data) => {
|
|
1720
|
+
return pokerStyles.balanced(data);
|
|
1721
|
+
},
|
|
1722
|
+
playful: (data) => {
|
|
1723
|
+
return pokerStyles.balanced(data);
|
|
1724
|
+
},
|
|
1725
|
+
"compact-verbose": (data) => {
|
|
1726
|
+
const { holeCards, boardCards, handResult } = data;
|
|
1727
|
+
const participatingSet = new Set(handResult?.participatingIndices || []);
|
|
1728
|
+
const handStr = holeCards.map((hc, idx) => formatCardCompact(hc, participatingSet.has(idx))).join("");
|
|
1729
|
+
const boardStr = boardCards.map((bc, idx) => formatCardCompact(bc, participatingSet.has(idx + 2))).join("");
|
|
1730
|
+
const abbreviation = getHandAbbreviation(handResult);
|
|
1731
|
+
return `${handStr}| ${boardStr}\u2192 ${abbreviation}`;
|
|
1732
|
+
}
|
|
1733
|
+
};
|
|
1299
1734
|
|
|
1300
1735
|
// src/widgets/poker-widget.ts
|
|
1301
1736
|
var PokerWidget = class extends StdinDataWidget {
|
|
@@ -1314,8 +1749,12 @@ var PokerWidget = class extends StdinDataWidget {
|
|
|
1314
1749
|
lastUpdateTimestamp = 0;
|
|
1315
1750
|
THROTTLE_MS = 5e3;
|
|
1316
1751
|
// 5 seconds
|
|
1317
|
-
|
|
1318
|
-
|
|
1752
|
+
styleFn = pokerStyles.balanced;
|
|
1753
|
+
setStyle(style = DEFAULT_WIDGET_STYLE) {
|
|
1754
|
+
const fn = pokerStyles[style];
|
|
1755
|
+
if (fn) {
|
|
1756
|
+
this.styleFn = fn;
|
|
1757
|
+
}
|
|
1319
1758
|
}
|
|
1320
1759
|
/**
|
|
1321
1760
|
* Generate new poker hand on each update
|
|
@@ -1356,30 +1795,37 @@ var PokerWidget = class extends StdinDataWidget {
|
|
|
1356
1795
|
* Format card with appropriate color (red for ♥♦, gray for ♠♣)
|
|
1357
1796
|
*/
|
|
1358
1797
|
formatCardColor(card) {
|
|
1359
|
-
const color = isRedSuit(card.suit) ? red : gray;
|
|
1360
|
-
return
|
|
1361
|
-
}
|
|
1362
|
-
/**
|
|
1363
|
-
* Format card based on participation in best hand
|
|
1364
|
-
* Participating cards: (K♠) with color + BOLD
|
|
1365
|
-
* Non-participating cards: K♠ with color, no brackets
|
|
1366
|
-
*/
|
|
1367
|
-
formatCardByParticipation(cardData, isParticipating) {
|
|
1368
|
-
const color = isRedSuit(cardData.card.suit) ? red : gray;
|
|
1369
|
-
const cardText = formatCard(cardData.card);
|
|
1370
|
-
if (isParticipating) {
|
|
1371
|
-
return `${color}${bold}(${cardText})${reset} `;
|
|
1372
|
-
} else {
|
|
1373
|
-
return `${color}${cardText}${reset} `;
|
|
1374
|
-
}
|
|
1798
|
+
const color = isRedSuit(card.suit) ? "red" : "gray";
|
|
1799
|
+
return formatCard(card);
|
|
1375
1800
|
}
|
|
1376
1801
|
renderWithData(_data, _context) {
|
|
1377
|
-
const
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
const
|
|
1382
|
-
|
|
1802
|
+
const holeCardsData = this.holeCards.map((hc, idx) => ({
|
|
1803
|
+
card: hc.card,
|
|
1804
|
+
isParticipating: (this.handResult?.participatingIndices || []).includes(idx)
|
|
1805
|
+
}));
|
|
1806
|
+
const boardCardsData = this.boardCards.map((bc, idx) => ({
|
|
1807
|
+
card: bc.card,
|
|
1808
|
+
isParticipating: (this.handResult?.participatingIndices || []).includes(idx + 2)
|
|
1809
|
+
}));
|
|
1810
|
+
const handResult = this.handResult ? {
|
|
1811
|
+
name: this.getHandName(this.handResult.text),
|
|
1812
|
+
emoji: this.getHandEmoji(this.handResult.text),
|
|
1813
|
+
participatingIndices: this.handResult.participatingIndices
|
|
1814
|
+
} : null;
|
|
1815
|
+
const renderData = {
|
|
1816
|
+
holeCards: holeCardsData,
|
|
1817
|
+
boardCards: boardCardsData,
|
|
1818
|
+
handResult
|
|
1819
|
+
};
|
|
1820
|
+
return this.styleFn(renderData);
|
|
1821
|
+
}
|
|
1822
|
+
getHandName(text) {
|
|
1823
|
+
const match = text.match(/^([^!]+)/);
|
|
1824
|
+
return match ? match[1].trim() : "Nothing";
|
|
1825
|
+
}
|
|
1826
|
+
getHandEmoji(text) {
|
|
1827
|
+
const match = text.match(/([🃏♠️♥️♦️♣️🎉✨🌟])/);
|
|
1828
|
+
return match ? match[1] : "\u{1F0CF}";
|
|
1383
1829
|
}
|
|
1384
1830
|
};
|
|
1385
1831
|
|
|
@@ -1394,6 +1840,12 @@ var EmptyLineWidget = class extends StdinDataWidget {
|
|
|
1394
1840
|
3
|
|
1395
1841
|
// Fourth line (0-indexed)
|
|
1396
1842
|
);
|
|
1843
|
+
/**
|
|
1844
|
+
* All styles return the same value (Braille Pattern Blank).
|
|
1845
|
+
* This method exists for API consistency with other widgets.
|
|
1846
|
+
*/
|
|
1847
|
+
setStyle(_style) {
|
|
1848
|
+
}
|
|
1397
1849
|
/**
|
|
1398
1850
|
* Return Braille Pattern Blank to create a visible empty separator line.
|
|
1399
1851
|
* U+2800 occupies cell width but appears blank, ensuring the line renders.
|
|
@@ -1558,9 +2010,7 @@ var StdinProvider = class {
|
|
|
1558
2010
|
}
|
|
1559
2011
|
const result = StdinDataSchema.validate(data);
|
|
1560
2012
|
if (!result.success) {
|
|
1561
|
-
throw new StdinValidationError(
|
|
1562
|
-
`Validation failed: ${formatError(result.error)}`
|
|
1563
|
-
);
|
|
2013
|
+
throw new StdinValidationError(`Validation failed: ${formatError(result.error)}`);
|
|
1564
2014
|
}
|
|
1565
2015
|
return result.data;
|
|
1566
2016
|
}
|
|
@@ -1605,7 +2055,6 @@ async function main() {
|
|
|
1605
2055
|
await registry.register(new DurationWidget());
|
|
1606
2056
|
await registry.register(new GitWidget());
|
|
1607
2057
|
await registry.register(new GitTagWidget());
|
|
1608
|
-
await registry.register(new GitChangesWidget());
|
|
1609
2058
|
await registry.register(new ConfigCountWidget());
|
|
1610
2059
|
await registry.register(new PokerWidget());
|
|
1611
2060
|
await registry.register(new EmptyLineWidget());
|
|
@@ -1618,10 +2067,10 @@ async function main() {
|
|
|
1618
2067
|
for (const widget of registry.getAll()) {
|
|
1619
2068
|
await widget.update(stdinData);
|
|
1620
2069
|
}
|
|
1621
|
-
const lines = await renderer.render(
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
);
|
|
2070
|
+
const lines = await renderer.render(registry.getEnabledWidgets(), {
|
|
2071
|
+
width: 80,
|
|
2072
|
+
timestamp: Date.now()
|
|
2073
|
+
});
|
|
1625
2074
|
return lines.join("\n");
|
|
1626
2075
|
} catch (error) {
|
|
1627
2076
|
const fallback = await tryGitFallback();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-scope",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.8",
|
|
4
4
|
"description": "Claude Code plugin for session status and analytics",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
"dev": "tsx src/index.ts"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
|
+
"@biomejs/biome": "^2.3.11",
|
|
31
32
|
"@types/node": "^22.10.2",
|
|
32
33
|
"c8": "^10.1.3",
|
|
33
34
|
"chai": "^6.2.2",
|