@owloops/claude-powerline 1.25.2 → 1.26.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.
- package/README.md +104 -2
- package/dist/browser.d.ts +88 -9
- package/dist/browser.js +3 -3
- package/dist/index.mjs +13 -12
- package/package.json +1 -1
- package/src/browser.ts +5 -0
- package/src/config/defaults.ts +3 -0
- package/src/config/loader.ts +9 -0
- package/src/powerline.ts +148 -5
- package/src/segments/cacheTimer.ts +72 -0
- package/src/segments/index.ts +5 -0
- package/src/segments/renderer.ts +177 -58
- package/src/themes/dark.ts +9 -0
- package/src/themes/gruvbox.ts +9 -0
- package/src/themes/index.ts +27 -0
- package/src/themes/light.ts +9 -0
- package/src/themes/nord.ts +9 -0
- package/src/themes/rose-pine.ts +9 -0
- package/src/themes/tokyo-night.ts +9 -0
- package/src/tui/layouts.ts +56 -20
- package/src/tui/primitives.ts +12 -3
- package/src/tui/sections.ts +555 -124
- package/src/tui/types.ts +8 -0
- package/src/utils/budget.ts +69 -0
- package/src/utils/claude.ts +29 -0
- package/src/utils/constants.ts +6 -0
- package/src/utils/formatters.ts +8 -0
- package/src/utils/icon-visibility.ts +31 -0
package/src/powerline.ts
CHANGED
|
@@ -18,9 +18,13 @@ import type {
|
|
|
18
18
|
SessionIdSegmentConfig,
|
|
19
19
|
EnvSegmentConfig,
|
|
20
20
|
WeeklySegmentConfig,
|
|
21
|
+
AgentSegmentConfig,
|
|
22
|
+
ThinkingSegmentConfig,
|
|
23
|
+
CacheTimerSegmentConfig,
|
|
21
24
|
} from "./segments";
|
|
22
25
|
import type { BlockInfo } from "./segments/block";
|
|
23
26
|
import type { TodayInfo } from "./segments/today";
|
|
27
|
+
import type { CacheTimerInfo } from "./segments/cacheTimer";
|
|
24
28
|
import type { TuiData } from "./tui";
|
|
25
29
|
|
|
26
30
|
import {
|
|
@@ -42,6 +46,7 @@ import {
|
|
|
42
46
|
} from "./segments";
|
|
43
47
|
import { BlockProvider } from "./segments/block";
|
|
44
48
|
import { TodayProvider } from "./segments/today";
|
|
49
|
+
import { CacheTimerProvider } from "./segments/cacheTimer";
|
|
45
50
|
import {
|
|
46
51
|
SYMBOLS,
|
|
47
52
|
TEXT_SYMBOLS,
|
|
@@ -58,6 +63,7 @@ interface RenderedSegment {
|
|
|
58
63
|
text: string;
|
|
59
64
|
bgColor: string;
|
|
60
65
|
fgColor: string;
|
|
66
|
+
bold?: boolean;
|
|
61
67
|
}
|
|
62
68
|
|
|
63
69
|
export class PowerlineRenderer {
|
|
@@ -69,6 +75,7 @@ export class PowerlineRenderer {
|
|
|
69
75
|
private _gitService?: GitService;
|
|
70
76
|
private _tmuxService?: TmuxService;
|
|
71
77
|
private _metricsProvider?: MetricsProvider;
|
|
78
|
+
private _cacheTimerProvider?: CacheTimerProvider;
|
|
72
79
|
private _segmentRenderer?: SegmentRenderer;
|
|
73
80
|
|
|
74
81
|
constructor(private readonly config: PowerlineConfig) {
|
|
@@ -124,6 +131,13 @@ export class PowerlineRenderer {
|
|
|
124
131
|
return this._metricsProvider;
|
|
125
132
|
}
|
|
126
133
|
|
|
134
|
+
private get cacheTimerProvider(): CacheTimerProvider {
|
|
135
|
+
if (!this._cacheTimerProvider) {
|
|
136
|
+
this._cacheTimerProvider = new CacheTimerProvider();
|
|
137
|
+
}
|
|
138
|
+
return this._cacheTimerProvider;
|
|
139
|
+
}
|
|
140
|
+
|
|
127
141
|
private get segmentRenderer(): SegmentRenderer {
|
|
128
142
|
if (!this._segmentRenderer) {
|
|
129
143
|
this._segmentRenderer = new SegmentRenderer(this.config, this.symbols);
|
|
@@ -166,6 +180,10 @@ export class PowerlineRenderer {
|
|
|
166
180
|
? await this.metricsProvider.getMetricsInfo(hookData.session_id, hookData)
|
|
167
181
|
: null;
|
|
168
182
|
|
|
183
|
+
const cacheTimerInfo = this.needsSegmentInfo("cacheTimer")
|
|
184
|
+
? await this.cacheTimerProvider.getCacheTimerInfo(hookData)
|
|
185
|
+
: null;
|
|
186
|
+
|
|
169
187
|
if (this.config.display.autoWrap) {
|
|
170
188
|
return this.generateAutoWrapStatusline(
|
|
171
189
|
hookData,
|
|
@@ -174,6 +192,7 @@ export class PowerlineRenderer {
|
|
|
174
192
|
todayInfo,
|
|
175
193
|
contextInfo,
|
|
176
194
|
metricsInfo,
|
|
195
|
+
cacheTimerInfo,
|
|
177
196
|
);
|
|
178
197
|
}
|
|
179
198
|
|
|
@@ -187,6 +206,7 @@ export class PowerlineRenderer {
|
|
|
187
206
|
todayInfo,
|
|
188
207
|
contextInfo,
|
|
189
208
|
metricsInfo,
|
|
209
|
+
cacheTimerInfo,
|
|
190
210
|
),
|
|
191
211
|
),
|
|
192
212
|
);
|
|
@@ -201,6 +221,7 @@ export class PowerlineRenderer {
|
|
|
201
221
|
todayInfo: TodayInfo | null,
|
|
202
222
|
contextInfo: ContextInfo | null,
|
|
203
223
|
metricsInfo: MetricsInfo | null,
|
|
224
|
+
cacheTimerInfo: CacheTimerInfo | null,
|
|
204
225
|
): Promise<string> {
|
|
205
226
|
const colors = this.getThemeColors();
|
|
206
227
|
const currentDir = hookData.workspace?.current_dir || hookData.cwd || "/";
|
|
@@ -229,6 +250,7 @@ export class PowerlineRenderer {
|
|
|
229
250
|
todayInfo,
|
|
230
251
|
contextInfo,
|
|
231
252
|
metricsInfo,
|
|
253
|
+
cacheTimerInfo,
|
|
232
254
|
colors,
|
|
233
255
|
currentDir,
|
|
234
256
|
);
|
|
@@ -239,6 +261,7 @@ export class PowerlineRenderer {
|
|
|
239
261
|
text: segmentData.text,
|
|
240
262
|
bgColor: segmentData.bgColor,
|
|
241
263
|
fgColor: segmentData.fgColor,
|
|
264
|
+
bold: segmentData.bold,
|
|
242
265
|
});
|
|
243
266
|
}
|
|
244
267
|
}
|
|
@@ -318,6 +341,7 @@ export class PowerlineRenderer {
|
|
|
318
341
|
hookData.workspace?.project_dir,
|
|
319
342
|
),
|
|
320
343
|
this.tmuxService.getSessionId(),
|
|
344
|
+
this.cacheTimerProvider.getCacheTimerInfo(hookData),
|
|
321
345
|
]);
|
|
322
346
|
const val = <T>(r: PromiseSettledResult<T>) =>
|
|
323
347
|
r.status === "fulfilled" ? r.value : null;
|
|
@@ -329,6 +353,7 @@ export class PowerlineRenderer {
|
|
|
329
353
|
metricsInfo,
|
|
330
354
|
gitInfo,
|
|
331
355
|
tmuxSessionId,
|
|
356
|
+
cacheTimerInfo,
|
|
332
357
|
] = [
|
|
333
358
|
val(results[0]!),
|
|
334
359
|
val(results[1]!),
|
|
@@ -337,6 +362,7 @@ export class PowerlineRenderer {
|
|
|
337
362
|
val(results[4]!),
|
|
338
363
|
val(results[5]!),
|
|
339
364
|
val(results[6]!),
|
|
365
|
+
val(results[7]!),
|
|
340
366
|
] as const;
|
|
341
367
|
|
|
342
368
|
const tuiData: TuiData = {
|
|
@@ -347,6 +373,7 @@ export class PowerlineRenderer {
|
|
|
347
373
|
contextInfo,
|
|
348
374
|
metricsInfo,
|
|
349
375
|
gitInfo,
|
|
376
|
+
cacheTimerInfo,
|
|
350
377
|
tmuxSessionId,
|
|
351
378
|
colors,
|
|
352
379
|
};
|
|
@@ -398,12 +425,16 @@ export class PowerlineRenderer {
|
|
|
398
425
|
line += " ";
|
|
399
426
|
}
|
|
400
427
|
|
|
428
|
+
const bold =
|
|
429
|
+
segment.bold ?? this.getSegmentBoldFlag(segment.type, colors);
|
|
430
|
+
|
|
401
431
|
line += this.formatSegment(
|
|
402
432
|
segment.bgColor,
|
|
403
433
|
segment.fgColor,
|
|
404
434
|
segment.text,
|
|
405
435
|
nextSegment?.bgColor,
|
|
406
436
|
colors,
|
|
437
|
+
bold,
|
|
407
438
|
);
|
|
408
439
|
}
|
|
409
440
|
|
|
@@ -418,6 +449,7 @@ export class PowerlineRenderer {
|
|
|
418
449
|
todayInfo: TodayInfo | null,
|
|
419
450
|
contextInfo: ContextInfo | null,
|
|
420
451
|
metricsInfo: MetricsInfo | null,
|
|
452
|
+
cacheTimerInfo: CacheTimerInfo | null,
|
|
421
453
|
): Promise<string> {
|
|
422
454
|
const colors = this.getThemeColors();
|
|
423
455
|
const currentDir = hookData.workspace?.current_dir || hookData.cwd || "/";
|
|
@@ -439,6 +471,7 @@ export class PowerlineRenderer {
|
|
|
439
471
|
todayInfo,
|
|
440
472
|
contextInfo,
|
|
441
473
|
metricsInfo,
|
|
474
|
+
cacheTimerInfo,
|
|
442
475
|
colors,
|
|
443
476
|
currentDir,
|
|
444
477
|
);
|
|
@@ -449,6 +482,7 @@ export class PowerlineRenderer {
|
|
|
449
482
|
text: segmentData.text,
|
|
450
483
|
bgColor: segmentData.bgColor,
|
|
451
484
|
fgColor: segmentData.fgColor,
|
|
485
|
+
bold: segmentData.bold,
|
|
452
486
|
});
|
|
453
487
|
}
|
|
454
488
|
}
|
|
@@ -464,6 +498,7 @@ export class PowerlineRenderer {
|
|
|
464
498
|
todayInfo: TodayInfo | null,
|
|
465
499
|
contextInfo: ContextInfo | null,
|
|
466
500
|
metricsInfo: MetricsInfo | null,
|
|
501
|
+
cacheTimerInfo: CacheTimerInfo | null,
|
|
467
502
|
colors: PowerlineColors,
|
|
468
503
|
currentDir: string,
|
|
469
504
|
) {
|
|
@@ -475,7 +510,7 @@ export class PowerlineRenderer {
|
|
|
475
510
|
);
|
|
476
511
|
}
|
|
477
512
|
if (segment.type === "model") {
|
|
478
|
-
return this.segmentRenderer.renderModel(hookData, colors);
|
|
513
|
+
return this.segmentRenderer.renderModel(hookData, colors, segment.config);
|
|
479
514
|
}
|
|
480
515
|
|
|
481
516
|
if (segment.type === "git") {
|
|
@@ -565,6 +600,31 @@ export class PowerlineRenderer {
|
|
|
565
600
|
);
|
|
566
601
|
}
|
|
567
602
|
|
|
603
|
+
if (segment.type === "agent") {
|
|
604
|
+
return this.segmentRenderer.renderAgent(
|
|
605
|
+
hookData,
|
|
606
|
+
colors,
|
|
607
|
+
segment.config as AgentSegmentConfig,
|
|
608
|
+
);
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
if (segment.type === "thinking") {
|
|
612
|
+
return this.segmentRenderer.renderThinking(
|
|
613
|
+
hookData,
|
|
614
|
+
colors,
|
|
615
|
+
segment.config as ThinkingSegmentConfig,
|
|
616
|
+
);
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
if (segment.type === "cacheTimer") {
|
|
620
|
+
if (!cacheTimerInfo) return null;
|
|
621
|
+
return this.segmentRenderer.renderCacheTimer(
|
|
622
|
+
cacheTimerInfo,
|
|
623
|
+
colors,
|
|
624
|
+
segment.config as CacheTimerSegmentConfig,
|
|
625
|
+
);
|
|
626
|
+
}
|
|
627
|
+
|
|
568
628
|
return null;
|
|
569
629
|
}
|
|
570
630
|
|
|
@@ -644,8 +704,7 @@ export class PowerlineRenderer {
|
|
|
644
704
|
colors: PowerlineColors,
|
|
645
705
|
) {
|
|
646
706
|
if (!todayInfo) return null;
|
|
647
|
-
|
|
648
|
-
return this.segmentRenderer.renderToday(todayInfo, colors, todayType);
|
|
707
|
+
return this.segmentRenderer.renderToday(todayInfo, colors, config);
|
|
649
708
|
}
|
|
650
709
|
|
|
651
710
|
private renderVersionSegment(
|
|
@@ -700,6 +759,9 @@ export class PowerlineRenderer {
|
|
|
700
759
|
env: symbolSet.env,
|
|
701
760
|
session_id: symbolSet.session_id,
|
|
702
761
|
weekly_cost: symbolSet.weekly_cost,
|
|
762
|
+
agent: symbolSet.agent,
|
|
763
|
+
thinking: symbolSet.thinking,
|
|
764
|
+
cache_timer: symbolSet.cache_timer,
|
|
703
765
|
};
|
|
704
766
|
}
|
|
705
767
|
|
|
@@ -753,9 +815,13 @@ export class PowerlineRenderer {
|
|
|
753
815
|
fgHex = colors.bg;
|
|
754
816
|
}
|
|
755
817
|
|
|
818
|
+
const bold =
|
|
819
|
+
colorSupport !== "none" && Boolean(custom?.bold ?? fallback.bold);
|
|
820
|
+
|
|
756
821
|
return {
|
|
757
822
|
bg: convertHex(colors.bg, true),
|
|
758
823
|
fg: convertHex(fgHex, false),
|
|
824
|
+
bold,
|
|
759
825
|
};
|
|
760
826
|
};
|
|
761
827
|
|
|
@@ -773,37 +839,63 @@ export class PowerlineRenderer {
|
|
|
773
839
|
const version = getSegmentColors("version");
|
|
774
840
|
const env = getSegmentColors("env");
|
|
775
841
|
const weekly = getSegmentColors("weekly");
|
|
842
|
+
const agent = getSegmentColors("agent");
|
|
843
|
+
const thinking = getSegmentColors("thinking");
|
|
844
|
+
const cacheTimer = getSegmentColors("cacheTimer");
|
|
776
845
|
|
|
777
846
|
return {
|
|
778
847
|
reset: colorSupport === "none" ? "" : RESET_CODE,
|
|
779
848
|
modeBg: directory.bg,
|
|
780
849
|
modeFg: directory.fg,
|
|
850
|
+
modeBold: directory.bold,
|
|
781
851
|
gitBg: git.bg,
|
|
782
852
|
gitFg: git.fg,
|
|
853
|
+
gitBold: git.bold,
|
|
783
854
|
modelBg: model.bg,
|
|
784
855
|
modelFg: model.fg,
|
|
856
|
+
modelBold: model.bold,
|
|
785
857
|
sessionBg: session.bg,
|
|
786
858
|
sessionFg: session.fg,
|
|
859
|
+
sessionBold: session.bold,
|
|
787
860
|
blockBg: block.bg,
|
|
788
861
|
blockFg: block.fg,
|
|
862
|
+
blockBold: block.bold,
|
|
789
863
|
todayBg: today.bg,
|
|
790
864
|
todayFg: today.fg,
|
|
865
|
+
todayBold: today.bold,
|
|
791
866
|
tmuxBg: tmux.bg,
|
|
792
867
|
tmuxFg: tmux.fg,
|
|
868
|
+
tmuxBold: tmux.bold,
|
|
793
869
|
contextBg: context.bg,
|
|
794
870
|
contextFg: context.fg,
|
|
871
|
+
contextBold: context.bold,
|
|
795
872
|
contextWarningBg: contextWarning.bg,
|
|
796
873
|
contextWarningFg: contextWarning.fg,
|
|
874
|
+
contextWarningBold: contextWarning.bold,
|
|
797
875
|
contextCriticalBg: contextCritical.bg,
|
|
798
876
|
contextCriticalFg: contextCritical.fg,
|
|
877
|
+
contextCriticalBold: contextCritical.bold,
|
|
799
878
|
metricsBg: metrics.bg,
|
|
800
879
|
metricsFg: metrics.fg,
|
|
880
|
+
metricsBold: metrics.bold,
|
|
801
881
|
versionBg: version.bg,
|
|
802
882
|
versionFg: version.fg,
|
|
883
|
+
versionBold: version.bold,
|
|
803
884
|
envBg: env.bg,
|
|
804
885
|
envFg: env.fg,
|
|
886
|
+
envBold: env.bold,
|
|
805
887
|
weeklyBg: weekly.bg,
|
|
806
888
|
weeklyFg: weekly.fg,
|
|
889
|
+
weeklyBold: weekly.bold,
|
|
890
|
+
agentBg: agent.bg,
|
|
891
|
+
agentFg: agent.fg,
|
|
892
|
+
agentBold: agent.bold,
|
|
893
|
+
thinkingBg: thinking.bg,
|
|
894
|
+
thinkingFg: thinking.fg,
|
|
895
|
+
thinkingBold: thinking.bold,
|
|
896
|
+
cacheTimerBg: cacheTimer.bg,
|
|
897
|
+
cacheTimerFg: cacheTimer.fg,
|
|
898
|
+
cacheTimerBold: cacheTimer.bold,
|
|
807
899
|
partFg: theme === "custom" ? this.resolvePartColors(convertHex) : {},
|
|
808
900
|
};
|
|
809
901
|
}
|
|
@@ -855,20 +947,71 @@ export class PowerlineRenderer {
|
|
|
855
947
|
return colors.envBg;
|
|
856
948
|
case "weekly":
|
|
857
949
|
return colors.weeklyBg;
|
|
950
|
+
case "agent":
|
|
951
|
+
return colors.agentBg;
|
|
952
|
+
case "thinking":
|
|
953
|
+
return colors.thinkingBg;
|
|
954
|
+
case "cacheTimer":
|
|
955
|
+
return colors.cacheTimerBg;
|
|
858
956
|
default:
|
|
859
957
|
return colors.modeBg;
|
|
860
958
|
}
|
|
861
959
|
}
|
|
862
960
|
|
|
961
|
+
private getSegmentBoldFlag(
|
|
962
|
+
segmentType: string,
|
|
963
|
+
colors: PowerlineColors,
|
|
964
|
+
): boolean {
|
|
965
|
+
switch (segmentType) {
|
|
966
|
+
case "directory":
|
|
967
|
+
return colors.modeBold;
|
|
968
|
+
case "git":
|
|
969
|
+
return colors.gitBold;
|
|
970
|
+
case "model":
|
|
971
|
+
return colors.modelBold;
|
|
972
|
+
case "session":
|
|
973
|
+
case "sessionId":
|
|
974
|
+
return colors.sessionBold;
|
|
975
|
+
case "block":
|
|
976
|
+
return colors.blockBold;
|
|
977
|
+
case "today":
|
|
978
|
+
return colors.todayBold;
|
|
979
|
+
case "tmux":
|
|
980
|
+
return colors.tmuxBold;
|
|
981
|
+
case "context":
|
|
982
|
+
return colors.contextBold;
|
|
983
|
+
case "metrics":
|
|
984
|
+
return colors.metricsBold;
|
|
985
|
+
case "version":
|
|
986
|
+
return colors.versionBold;
|
|
987
|
+
case "env":
|
|
988
|
+
return colors.envBold;
|
|
989
|
+
case "weekly":
|
|
990
|
+
return colors.weeklyBold;
|
|
991
|
+
case "agent":
|
|
992
|
+
return colors.agentBold;
|
|
993
|
+
case "thinking":
|
|
994
|
+
return colors.thinkingBold;
|
|
995
|
+
case "cacheTimer":
|
|
996
|
+
return colors.cacheTimerBold;
|
|
997
|
+
default:
|
|
998
|
+
return colors.modeBold;
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
|
|
863
1002
|
private formatSegment(
|
|
864
1003
|
bgColor: string,
|
|
865
1004
|
fgColor: string,
|
|
866
1005
|
text: string,
|
|
867
1006
|
nextBgColor: string | undefined,
|
|
868
1007
|
colors: PowerlineColors,
|
|
1008
|
+
bold: boolean,
|
|
869
1009
|
): string {
|
|
870
1010
|
const isCapsuleStyle = this.config.display.style === "capsule";
|
|
871
1011
|
const padding = " ".repeat(this.config.display.padding ?? 1);
|
|
1012
|
+
const useBold = bold && colors.reset !== "";
|
|
1013
|
+
const boldOn = useBold ? "\x1b[1m" : "";
|
|
1014
|
+
const boldOff = useBold ? "\x1b[22m" : "";
|
|
872
1015
|
|
|
873
1016
|
if (isCapsuleStyle) {
|
|
874
1017
|
const colorMode = this.config.display.colorCompatibility || "auto";
|
|
@@ -879,14 +1022,14 @@ export class PowerlineRenderer {
|
|
|
879
1022
|
|
|
880
1023
|
const leftCap = `${capFgColor}${this.symbols.left}${colors.reset}`;
|
|
881
1024
|
|
|
882
|
-
const content = `${bgColor}${fgColor}${padding}${text}${padding}${colors.reset}`;
|
|
1025
|
+
const content = `${bgColor}${fgColor}${boldOn}${padding}${text}${padding}${boldOff}${colors.reset}`;
|
|
883
1026
|
|
|
884
1027
|
const rightCap = `${capFgColor}${this.symbols.right}${colors.reset}`;
|
|
885
1028
|
|
|
886
1029
|
return `${leftCap}${content}${rightCap}`;
|
|
887
1030
|
}
|
|
888
1031
|
|
|
889
|
-
let output = `${bgColor}${fgColor}${padding}${text}${padding}`;
|
|
1032
|
+
let output = `${bgColor}${fgColor}${boldOn}${padding}${text}${padding}${boldOff}`;
|
|
890
1033
|
|
|
891
1034
|
const colorMode = this.config.display.colorCompatibility || "auto";
|
|
892
1035
|
const colorSupport = colorMode === "auto" ? getColorSupport() : colorMode;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { readFile, stat } from "node:fs/promises";
|
|
2
|
+
import { debug } from "../utils/logger";
|
|
3
|
+
import type { ClaudeHookData } from "../utils/claude";
|
|
4
|
+
|
|
5
|
+
export interface CacheTimerInfo {
|
|
6
|
+
elapsedSeconds: number;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
interface TranscriptEntry {
|
|
10
|
+
type?: string;
|
|
11
|
+
timestamp?: string;
|
|
12
|
+
message?: { role?: string; type?: string };
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export class CacheTimerProvider {
|
|
16
|
+
async getCacheTimerInfo(
|
|
17
|
+
hookData: ClaudeHookData,
|
|
18
|
+
): Promise<CacheTimerInfo | null> {
|
|
19
|
+
const path = hookData?.transcript_path;
|
|
20
|
+
if (!path) {
|
|
21
|
+
debug("CacheTimer: no transcript_path in hookData");
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const anchor =
|
|
26
|
+
(await this.lastUserTimestamp(path)) ?? (await this.fileMtime(path));
|
|
27
|
+
if (anchor === null) return null;
|
|
28
|
+
|
|
29
|
+
const elapsedSeconds = Math.max(
|
|
30
|
+
0,
|
|
31
|
+
Math.floor((Date.now() - anchor) / 1000),
|
|
32
|
+
);
|
|
33
|
+
return { elapsedSeconds };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
private async lastUserTimestamp(path: string): Promise<number | null> {
|
|
37
|
+
try {
|
|
38
|
+
const content = await readFile(path, "utf-8");
|
|
39
|
+
const lines = content.split("\n");
|
|
40
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
41
|
+
const line = lines[i]?.trim();
|
|
42
|
+
if (!line) continue;
|
|
43
|
+
try {
|
|
44
|
+
const entry = JSON.parse(line) as TranscriptEntry;
|
|
45
|
+
const messageType =
|
|
46
|
+
entry.type || entry.message?.role || entry.message?.type;
|
|
47
|
+
if (messageType !== "user") continue;
|
|
48
|
+
if (!entry.timestamp) continue;
|
|
49
|
+
const t = Date.parse(entry.timestamp);
|
|
50
|
+
if (Number.isNaN(t)) continue;
|
|
51
|
+
return t;
|
|
52
|
+
} catch {
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return null;
|
|
57
|
+
} catch (error) {
|
|
58
|
+
debug(`CacheTimer: readFile failed for ${path}: ${String(error)}`);
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
private async fileMtime(path: string): Promise<number | null> {
|
|
64
|
+
try {
|
|
65
|
+
const { mtime } = await stat(path);
|
|
66
|
+
return mtime.getTime();
|
|
67
|
+
} catch (error) {
|
|
68
|
+
debug(`CacheTimer: stat failed for ${path}: ${String(error)}`);
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
package/src/segments/index.ts
CHANGED
|
@@ -7,6 +7,8 @@ export { ContextProvider } from "./context";
|
|
|
7
7
|
export type { ContextInfo } from "./context";
|
|
8
8
|
export { MetricsProvider } from "./metrics";
|
|
9
9
|
export type { MetricsInfo } from "./metrics";
|
|
10
|
+
export { CacheTimerProvider } from "./cacheTimer";
|
|
11
|
+
export type { CacheTimerInfo } from "./cacheTimer";
|
|
10
12
|
export { SegmentRenderer } from "./renderer";
|
|
11
13
|
export type {
|
|
12
14
|
PowerlineSymbols,
|
|
@@ -22,4 +24,7 @@ export type {
|
|
|
22
24
|
SessionIdSegmentConfig,
|
|
23
25
|
EnvSegmentConfig,
|
|
24
26
|
WeeklySegmentConfig,
|
|
27
|
+
AgentSegmentConfig,
|
|
28
|
+
ThinkingSegmentConfig,
|
|
29
|
+
CacheTimerSegmentConfig,
|
|
25
30
|
} from "./renderer";
|