agenttop 0.10.7 → 0.11.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/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
+ STATUS_PRIORITY,
3
4
  SecurityEngine,
4
5
  Watcher,
5
6
  archiveSession,
@@ -7,30 +8,34 @@ import {
7
8
  deleteSessionFiles,
8
9
  discoverSessions,
9
10
  getArchived,
11
+ getClaudeProcessesAsync,
10
12
  getNicknames,
11
13
  getProjectsDirs,
12
14
  getTaskDirs,
13
15
  isFirstRun,
14
16
  loadConfig,
17
+ movePinned,
18
+ pinSession,
15
19
  purgeExpiredArchives,
16
20
  resolveAlertLogPath,
17
21
  rotateLogFile,
18
22
  saveConfig,
19
23
  setNickname,
20
24
  startMcpServer,
21
- unarchiveSession
22
- } from "./chunk-CXXCDFJ5.js";
25
+ unarchiveSession,
26
+ unpinSession
27
+ } from "./chunk-LPXME2WB.js";
23
28
 
24
29
  // src/index.tsx
25
30
  import { readFileSync as readFileSync3 } from "fs";
26
31
  import { join as join5, dirname as dirname4 } from "path";
27
32
  import { fileURLToPath as fileURLToPath3 } from "url";
28
- import React18 from "react";
33
+ import React19 from "react";
29
34
  import { render } from "ink";
30
35
 
31
36
  // src/ui/App.tsx
32
- import { useState as useState17, useEffect as useEffect9, useCallback as useCallback7 } from "react";
33
- import { Box as Box17, Text as Text16, useApp, useStdout as useStdout3 } from "ink";
37
+ import { useState as useState18, useEffect as useEffect10, useCallback as useCallback7 } from "react";
38
+ import { Box as Box18, Text as Text17, useApp, useStdout as useStdout4 } from "ink";
34
39
 
35
40
  // src/config/themes.ts
36
41
  var COLOR_KEYS = [
@@ -45,7 +50,9 @@ var COLOR_KEYS = [
45
50
  "bright",
46
51
  "border",
47
52
  "selected",
48
- "header"
53
+ "header",
54
+ "waiting",
55
+ "stale"
49
56
  ];
50
57
  var TOOL_COLOR_KEYS = [
51
58
  "Bash",
@@ -82,11 +89,28 @@ var fromTuple = ([
82
89
  bright,
83
90
  border,
84
91
  selected,
85
- header
92
+ header,
93
+ waiting,
94
+ stale
86
95
  ]) => ({
87
96
  name,
88
97
  builtin: true,
89
- colors: { primary, secondary, accent, warning, error, critical, muted, text, bright, border, selected, header },
98
+ colors: {
99
+ primary,
100
+ secondary,
101
+ accent,
102
+ warning,
103
+ error,
104
+ critical,
105
+ muted,
106
+ text,
107
+ bright,
108
+ border,
109
+ selected,
110
+ header,
111
+ waiting,
112
+ stale
113
+ },
90
114
  toolColors: deriveToolColors({
91
115
  primary,
92
116
  secondary,
@@ -99,7 +123,9 @@ var fromTuple = ([
99
123
  bright,
100
124
  border,
101
125
  selected,
102
- header
126
+ header,
127
+ waiting,
128
+ stale
103
129
  })
104
130
  });
105
131
  var PRESETS = [
@@ -116,7 +142,9 @@ var PRESETS = [
116
142
  "#FFFFFF",
117
143
  "#3E4451",
118
144
  "#2C313A",
119
- "#61AFEF"
145
+ "#61AFEF",
146
+ "#E5C07B",
147
+ "#D19A66"
120
148
  ],
121
149
  [
122
150
  "dracula",
@@ -131,7 +159,9 @@ var PRESETS = [
131
159
  "#FFFFFF",
132
160
  "#44475A",
133
161
  "#383A59",
134
- "#BD93F9"
162
+ "#BD93F9",
163
+ "#F1FA8C",
164
+ "#FFB86C"
135
165
  ],
136
166
  [
137
167
  "monokai-pro",
@@ -146,7 +176,9 @@ var PRESETS = [
146
176
  "#FFFFFF",
147
177
  "#403E41",
148
178
  "#2D2A2E",
149
- "#78DCE8"
179
+ "#78DCE8",
180
+ "#FFD866",
181
+ "#FC9867"
150
182
  ],
151
183
  [
152
184
  "solarized-dark",
@@ -161,7 +193,9 @@ var PRESETS = [
161
193
  "#FDF6E3",
162
194
  "#073642",
163
195
  "#002B36",
164
- "#268BD2"
196
+ "#268BD2",
197
+ "#B58900",
198
+ "#CB4B16"
165
199
  ],
166
200
  [
167
201
  "solarized-light",
@@ -176,7 +210,9 @@ var PRESETS = [
176
210
  "#002B36",
177
211
  "#EEE8D5",
178
212
  "#FDF6E3",
179
- "#268BD2"
213
+ "#268BD2",
214
+ "#B58900",
215
+ "#CB4B16"
180
216
  ],
181
217
  [
182
218
  "nord",
@@ -191,7 +227,9 @@ var PRESETS = [
191
227
  "#ECEFF4",
192
228
  "#3B4252",
193
229
  "#2E3440",
194
- "#88C0D0"
230
+ "#88C0D0",
231
+ "#EBCB8B",
232
+ "#D08770"
195
233
  ],
196
234
  [
197
235
  "gruvbox-dark",
@@ -206,7 +244,9 @@ var PRESETS = [
206
244
  "#FBF1C7",
207
245
  "#3C3836",
208
246
  "#282828",
209
- "#83A598"
247
+ "#83A598",
248
+ "#FABD2F",
249
+ "#FE8019"
210
250
  ],
211
251
  [
212
252
  "tokyo-night",
@@ -221,7 +261,9 @@ var PRESETS = [
221
261
  "#C0CAF5",
222
262
  "#292E42",
223
263
  "#1A1B26",
224
- "#7AA2F7"
264
+ "#7AA2F7",
265
+ "#E0AF68",
266
+ "#FF9E64"
225
267
  ],
226
268
  [
227
269
  "catppuccin-mocha",
@@ -236,7 +278,9 @@ var PRESETS = [
236
278
  "#FFFFFF",
237
279
  "#313244",
238
280
  "#1E1E2E",
239
- "#89B4FA"
281
+ "#89B4FA",
282
+ "#F9E2AF",
283
+ "#FAB387"
240
284
  ],
241
285
  [
242
286
  "catppuccin-latte",
@@ -251,7 +295,9 @@ var PRESETS = [
251
295
  "#11111B",
252
296
  "#E6E9EF",
253
297
  "#EFF1F5",
254
- "#1E66F5"
298
+ "#1E66F5",
299
+ "#DF8E1D",
300
+ "#FE640B"
255
301
  ],
256
302
  [
257
303
  "rose-pine",
@@ -266,7 +312,9 @@ var PRESETS = [
266
312
  "#E0DEF4",
267
313
  "#26233A",
268
314
  "#191724",
269
- "#9CCFD8"
315
+ "#9CCFD8",
316
+ "#F6C177",
317
+ "#EA9D34"
270
318
  ],
271
319
  [
272
320
  "rose-pine-moon",
@@ -281,7 +329,9 @@ var PRESETS = [
281
329
  "#E0DEF4",
282
330
  "#2A273F",
283
331
  "#232136",
284
- "#9CCFD8"
332
+ "#9CCFD8",
333
+ "#F6C177",
334
+ "#EA9D34"
285
335
  ],
286
336
  [
287
337
  "pastel-dark",
@@ -296,7 +346,9 @@ var PRESETS = [
296
346
  "#FFFFFF",
297
347
  "#3A3A4A",
298
348
  "#2B2B3A",
299
- "#89CFF0"
349
+ "#89CFF0",
350
+ "#FFD580",
351
+ "#FFAA5E"
300
352
  ],
301
353
  [
302
354
  "kanagawa",
@@ -311,7 +363,9 @@ var PRESETS = [
311
363
  "#FFFFFF",
312
364
  "#2A2A37",
313
365
  "#1F1F28",
314
- "#7E9CD8"
366
+ "#7E9CD8",
367
+ "#E6C384",
368
+ "#FFA066"
315
369
  ],
316
370
  [
317
371
  "everforest",
@@ -326,7 +380,213 @@ var PRESETS = [
326
380
  "#FFFFFF",
327
381
  "#374145",
328
382
  "#2D353B",
329
- "#7FBBB3"
383
+ "#7FBBB3",
384
+ "#DBBC7F",
385
+ "#E69875"
386
+ ],
387
+ [
388
+ "hi-feline",
389
+ "#FF6B8A",
390
+ "#FFB3C6",
391
+ "#FF1744",
392
+ "#FFE082",
393
+ "#FF5252",
394
+ "#FF0000",
395
+ "#C48B9F",
396
+ "#FFE4EC",
397
+ "#FFFFFF",
398
+ "#4A2030",
399
+ "#3A1525",
400
+ "#FF6B8A",
401
+ "#FFE082",
402
+ "#FFA726"
403
+ ],
404
+ [
405
+ "plumber-bros",
406
+ "#4CAF50",
407
+ "#F44336",
408
+ "#2196F3",
409
+ "#FFD700",
410
+ "#FF5722",
411
+ "#FF0000",
412
+ "#795548",
413
+ "#EFEBE9",
414
+ "#FFFFFF",
415
+ "#1B5E20",
416
+ "#0D3010",
417
+ "#4CAF50",
418
+ "#FFD700",
419
+ "#FF9800"
420
+ ],
421
+ [
422
+ "hedgehog-speed",
423
+ "#1565C0",
424
+ "#FFD700",
425
+ "#4CAF50",
426
+ "#FFEB3B",
427
+ "#F44336",
428
+ "#FF0000",
429
+ "#5C6BC0",
430
+ "#E8EAF6",
431
+ "#FFFFFF",
432
+ "#0D47A1",
433
+ "#0A2E6E",
434
+ "#1565C0",
435
+ "#FFEB3B",
436
+ "#FF9800"
437
+ ],
438
+ [
439
+ "block-craft",
440
+ "#4CAF50",
441
+ "#8D6E63",
442
+ "#795548",
443
+ "#FFEB3B",
444
+ "#F44336",
445
+ "#FF0000",
446
+ "#78909C",
447
+ "#BCAAA4",
448
+ "#FFFFFF",
449
+ "#33691E",
450
+ "#1B4400",
451
+ "#4CAF50",
452
+ "#FFEB3B",
453
+ "#FF9800"
454
+ ],
455
+ [
456
+ "galaxy-conflicts",
457
+ "#F44336",
458
+ "#2196F3",
459
+ "#9C27B0",
460
+ "#FFD54F",
461
+ "#FF1744",
462
+ "#FF0000",
463
+ "#546E7A",
464
+ "#ECEFF1",
465
+ "#FFFFFF",
466
+ "#1A1A2E",
467
+ "#0D0D1A",
468
+ "#F44336",
469
+ "#FFD54F",
470
+ "#FF6D00"
471
+ ],
472
+ [
473
+ "pocket-creatures",
474
+ "#F44336",
475
+ "#FFFFFF",
476
+ "#FFEB3B",
477
+ "#FFC107",
478
+ "#E53935",
479
+ "#FF0000",
480
+ "#90A4AE",
481
+ "#ECEFF1",
482
+ "#FFFFFF",
483
+ "#B71C1C",
484
+ "#7F0000",
485
+ "#F44336",
486
+ "#FFC107",
487
+ "#FF9800"
488
+ ],
489
+ [
490
+ "brick-wizard",
491
+ "#7B1FA2",
492
+ "#FFD700",
493
+ "#880E4F",
494
+ "#FFC107",
495
+ "#F44336",
496
+ "#FF0000",
497
+ "#6A1B9A",
498
+ "#E1BEE7",
499
+ "#FFFFFF",
500
+ "#311B92",
501
+ "#1A0A52",
502
+ "#7B1FA2",
503
+ "#FFC107",
504
+ "#FF9800"
505
+ ],
506
+ [
507
+ "caped-knight",
508
+ "#616161",
509
+ "#FDD835",
510
+ "#424242",
511
+ "#FFC107",
512
+ "#F44336",
513
+ "#FF0000",
514
+ "#757575",
515
+ "#E0E0E0",
516
+ "#FFFFFF",
517
+ "#212121",
518
+ "#0A0A0A",
519
+ "#616161",
520
+ "#FFC107",
521
+ "#FF9800"
522
+ ],
523
+ [
524
+ "web-crawler",
525
+ "#F44336",
526
+ "#1565C0",
527
+ "#FFFFFF",
528
+ "#FFEB3B",
529
+ "#D50000",
530
+ "#FF0000",
531
+ "#78909C",
532
+ "#E3F2FD",
533
+ "#FFFFFF",
534
+ "#B71C1C",
535
+ "#7F0000",
536
+ "#F44336",
537
+ "#FFEB3B",
538
+ "#FF9800"
539
+ ],
540
+ [
541
+ "frozen-kingdom",
542
+ "#81D4FA",
543
+ "#CE93D8",
544
+ "#B3E5FC",
545
+ "#FFF9C4",
546
+ "#EF9A9A",
547
+ "#FF0000",
548
+ "#90CAF9",
549
+ "#E1F5FE",
550
+ "#FFFFFF",
551
+ "#1A237E",
552
+ "#0D1252",
553
+ "#81D4FA",
554
+ "#FFF9C4",
555
+ "#FFCC80"
556
+ ],
557
+ [
558
+ "coral-reef",
559
+ "#FF7043",
560
+ "#29B6F6",
561
+ "#AB47BC",
562
+ "#FFEE58",
563
+ "#EF5350",
564
+ "#FF0000",
565
+ "#4DB6AC",
566
+ "#E0F7FA",
567
+ "#FFFFFF",
568
+ "#BF360C",
569
+ "#7F2008",
570
+ "#FF7043",
571
+ "#FFEE58",
572
+ "#FFA726"
573
+ ],
574
+ [
575
+ "toy-ranch",
576
+ "#8D6E63",
577
+ "#7CB342",
578
+ "#7E57C2",
579
+ "#FFEE58",
580
+ "#EF5350",
581
+ "#FF0000",
582
+ "#90A4AE",
583
+ "#EFEBE9",
584
+ "#FFFFFF",
585
+ "#4E342E",
586
+ "#2E1C15",
587
+ "#8D6E63",
588
+ "#FFEE58",
589
+ "#FFA726"
330
590
  ]
331
591
  ];
332
592
  var BUILTIN_THEMES = PRESETS.map(fromTuple);
@@ -350,7 +610,7 @@ var deriveSeverityColors = (c) => ({
350
610
  });
351
611
 
352
612
  // src/updates.ts
353
- import { execFile, exec, spawn } from "child_process";
613
+ import { execFile, spawn } from "child_process";
354
614
  import { readFile } from "fs/promises";
355
615
  import { join, dirname } from "path";
356
616
  import { fileURLToPath } from "url";
@@ -366,7 +626,7 @@ var getPackageVersion = async () => {
366
626
  };
367
627
  var getNpmPath = () => {
368
628
  const nodeDir = dirname(process.execPath);
369
- return join(nodeDir, "npm");
629
+ return join(nodeDir, process.platform === "win32" ? "npm.cmd" : "npm");
370
630
  };
371
631
  var checkForUpdate = () => new Promise((resolve) => {
372
632
  const npm = getNpmPath();
@@ -388,7 +648,7 @@ var checkForUpdate = () => new Promise((resolve) => {
388
648
  var installUpdate = () => {
389
649
  const npm = getNpmPath();
390
650
  return new Promise((resolve, reject) => {
391
- exec(`${npm} install -g agenttop@latest`, { timeout: 6e4 }, (err, stdout) => {
651
+ execFile(npm, ["install", "-g", "agenttop@latest"], { timeout: 6e4 }, (err, stdout) => {
392
652
  if (err) {
393
653
  reject(err);
394
654
  } else {
@@ -429,12 +689,15 @@ var colors = {
429
689
  warning: "#E5C07B",
430
690
  error: "#E06C75",
431
691
  critical: "#FF0000",
692
+ success: "#98C379",
432
693
  muted: "#5C6370",
433
694
  text: "#ABB2BF",
434
695
  bright: "#FFFFFF",
435
696
  border: "#3E4451",
436
697
  selected: "#2C313A",
437
- header: "#61AFEF"
698
+ header: "#61AFEF",
699
+ waiting: "#E5C07B",
700
+ stale: "#D19A66"
438
701
  };
439
702
  var severityColors = {
440
703
  info: colors.muted,
@@ -456,6 +719,7 @@ var toolColors = {
456
719
  var getToolColor = (toolName) => toolColors[toolName] || colors.text;
457
720
  var applyTheme = (theme) => {
458
721
  Object.assign(colors, theme.colors);
722
+ colors.success = theme.colors.secondary;
459
723
  Object.assign(toolColors, theme.toolColors);
460
724
  Object.assign(severityColors, deriveSeverityColors(theme.colors));
461
725
  };
@@ -496,18 +760,32 @@ var StatusBar = React.memo(({ sessionCount, alertCount, version, updateInfo }) =
496
760
  // src/ui/components/SessionList.tsx
497
761
  import React2 from "react";
498
762
  import { Box as Box2, Text as Text2 } from "ink";
499
- import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
763
+
764
+ // src/ui/format.ts
765
+ var formatTokens = (n) => {
766
+ if (n >= 1e6) return (n / 1e6).toFixed(1) + "M";
767
+ if (n >= 1e3) return (n / 1e3).toFixed(1) + "k";
768
+ return String(n);
769
+ };
770
+ var formatTime = (ts) => {
771
+ const d = new Date(ts);
772
+ return d.toLocaleTimeString("en-GB", { hour12: false });
773
+ };
500
774
  var formatModel = (model) => {
775
+ if (model.includes("opus")) return "opus";
776
+ if (model.includes("sonnet")) return "sonnet";
777
+ if (model.includes("haiku")) return "haiku";
778
+ return model.slice(0, 4);
779
+ };
780
+ var formatModelShort = (model) => {
501
781
  if (model.includes("opus")) return "opus";
502
782
  if (model.includes("sonnet")) return "son";
503
783
  if (model.includes("haiku")) return "hai";
504
784
  return model.slice(0, 4);
505
785
  };
506
- var formatTokens = (n) => {
507
- if (n >= 1e6) return (n / 1e6).toFixed(1) + "M";
508
- if (n >= 1e3) return (n / 1e3).toFixed(1) + "k";
509
- return String(n);
510
- };
786
+
787
+ // src/ui/components/SessionList.tsx
788
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
511
789
  var truncate = (s, max) => s.length > max ? s.slice(0, max - 1) + "\u2026" : s;
512
790
  var getDisplayName = (s) => {
513
791
  if (s.nickname) return s.nickname;
@@ -567,11 +845,14 @@ var SessionList = React2.memo(
567
845
  if (item.type === "group") {
568
846
  const g = item.group;
569
847
  const arrow = g.expanded ? "\u25BE" : "\u25B8";
570
- const dotColor2 = g.isActive ? colors.success : colors.muted;
571
- const statusDot2 = g.isActive ? "\u25CF" : "\u25CB";
572
- const nameColor2 = isSelected ? colors.bright : g.isActive ? colors.secondary : colors.text;
573
- const label2 = truncate(`${g.key} (${g.sessions.length})`, INNER_WIDTH - 4);
574
- const model2 = formatModel(g.latestModel);
848
+ const dotColor2 = g.status === "waiting" ? colors.waiting : g.status === "stale" ? colors.stale : g.status === "active" ? colors.success : colors.muted;
849
+ const statusDot2 = g.status === "inactive" ? "\u25CB" : "\u25CF";
850
+ const nameColor2 = isSelected ? colors.bright : g.status !== "inactive" ? colors.secondary : colors.text;
851
+ const groupPinned = g.sessions.some((s) => s.pinned);
852
+ const pinMarker2 = groupPinned ? "* " : " ";
853
+ const statusTag2 = g.status === "waiting" ? " [waiting]" : g.status === "stale" ? " [stale]" : "";
854
+ const label2 = truncate(`${g.key} (${g.sessions.length})${statusTag2}`, INNER_WIDTH - 4);
855
+ const model2 = formatModelShort(g.latestModel);
575
856
  return /* @__PURE__ */ jsxs2(
576
857
  Box2,
577
858
  {
@@ -584,6 +865,7 @@ var SessionList = React2.memo(
584
865
  " ",
585
866
  /* @__PURE__ */ jsx2(Text2, { color: dotColor2, children: statusDot2 }),
586
867
  " ",
868
+ pinMarker2,
587
869
  label2
588
870
  ] }),
589
871
  /* @__PURE__ */ jsxs2(Text2, { color: isSelected ? colors.text : colors.muted, wrap: "truncate", children: [
@@ -602,14 +884,15 @@ var SessionList = React2.memo(
602
884
  );
603
885
  }
604
886
  const session = item.type === "session" ? item.session : item.session;
605
- const isActive = session.pid !== null;
606
- const statusDot = isActive ? "\u25CF" : "\u25CB";
607
- const dotColor = isActive ? colors.success : colors.muted;
887
+ const dotColor = session.status === "waiting" ? colors.waiting : session.status === "stale" ? colors.stale : session.status === "active" ? colors.success : colors.muted;
888
+ const statusDot = session.status === "inactive" ? "\u25CB" : "\u25CF";
889
+ const pinMarker = session.pinned ? "* " : " ";
890
+ const statusTag = session.status === "waiting" ? " [waiting]" : session.status === "stale" ? " [stale]" : "";
608
891
  const totalIn = session.usage.inputTokens + session.usage.cacheReadTokens;
609
- const model = formatModel(session.model);
892
+ const model = formatModelShort(session.model);
610
893
  if (item.type === "session") {
611
- const nameColor2 = isSelected ? colors.bright : isActive ? colors.secondary : colors.muted;
612
- const displayName2 = truncate(session.nickname || session.slug, INNER_WIDTH - 6);
894
+ const nameColor2 = isSelected ? colors.bright : session.status !== "inactive" ? colors.secondary : colors.muted;
895
+ const displayName2 = truncate(`${session.nickname || session.slug}${statusTag}`, INNER_WIDTH - 6);
613
896
  return /* @__PURE__ */ jsxs2(
614
897
  Box2,
615
898
  {
@@ -622,6 +905,7 @@ var SessionList = React2.memo(
622
905
  " ",
623
906
  /* @__PURE__ */ jsx2(Text2, { color: dotColor, children: statusDot }),
624
907
  " ",
908
+ pinMarker,
625
909
  displayName2
626
910
  ] }),
627
911
  /* @__PURE__ */ jsxs2(Text2, { color: isSelected ? colors.text : colors.muted, wrap: "truncate", children: [
@@ -640,8 +924,8 @@ var SessionList = React2.memo(
640
924
  );
641
925
  }
642
926
  const indicator = isSelected ? "\u25B8" : " ";
643
- const nameColor = isSelected ? colors.bright : isActive ? colors.secondary : colors.text;
644
- const displayName = truncate(getDisplayName(session), INNER_WIDTH - 4);
927
+ const nameColor = isSelected ? colors.bright : session.status !== "inactive" ? colors.secondary : colors.text;
928
+ const displayName = truncate(`${getDisplayName(session)}${statusTag}`, INNER_WIDTH - 4);
645
929
  return /* @__PURE__ */ jsxs2(
646
930
  Box2,
647
931
  {
@@ -654,6 +938,7 @@ var SessionList = React2.memo(
654
938
  " ",
655
939
  /* @__PURE__ */ jsx2(Text2, { color: dotColor, children: statusDot }),
656
940
  " ",
941
+ pinMarker,
657
942
  displayName
658
943
  ] }),
659
944
  /* @__PURE__ */ jsxs2(Text2, { color: isSelected ? colors.text : colors.muted, wrap: "truncate", children: [
@@ -682,10 +967,6 @@ import React3 from "react";
682
967
  import { Box as Box3, Text as Text3 } from "ink";
683
968
  import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
684
969
  var TAG_COLORS = ["#61AFEF", "#98C379", "#C678DD", "#E5C07B", "#E06C75", "#56B6C2", "#D19A66", "#BE5046"];
685
- var formatTime = (ts) => {
686
- const d = new Date(ts);
687
- return d.toLocaleTimeString("en-GB", { hour12: false });
688
- };
689
970
  var summarizeInput = (call) => {
690
971
  const input = call.toolInput;
691
972
  switch (call.toolName) {
@@ -713,7 +994,7 @@ var ActivityFeed = React3.memo(
713
994
  events,
714
995
  sessionSlug,
715
996
  sessionId,
716
- isActive,
997
+ status,
717
998
  focused,
718
999
  height,
719
1000
  scrollOffset,
@@ -790,7 +1071,7 @@ var ActivityFeed = React3.memo(
790
1071
  ] }) }, `${call.timestamp}-${i}`);
791
1072
  }),
792
1073
  focused && canScroll && !isAtTop && visible.length > 0 && /* @__PURE__ */ jsx3(Box3, { paddingX: 1, justifyContent: "flex-end", children: /* @__PURE__ */ jsx3(Text3, { color: colors.muted, children: isAtBottom ? "" : "G:bottom " }) }),
793
- !merged && sessionId && isActive === false && /* @__PURE__ */ jsxs3(Box3, { paddingX: 1, flexDirection: "column", children: [
1074
+ !merged && sessionId && status === "inactive" && /* @__PURE__ */ jsxs3(Box3, { paddingX: 1, flexDirection: "column", children: [
794
1075
  /* @__PURE__ */ jsx3(Text3, { color: colors.muted, children: " " }),
795
1076
  /* @__PURE__ */ jsxs3(Text3, { color: colors.muted, children: [
796
1077
  "resume: ",
@@ -815,10 +1096,6 @@ var ActivityFeed = React3.memo(
815
1096
  import React4 from "react";
816
1097
  import { Box as Box4, Text as Text4 } from "ink";
817
1098
  import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
818
- var formatTime2 = (ts) => {
819
- const d = new Date(ts);
820
- return d.toLocaleTimeString("en-GB", { hour12: false });
821
- };
822
1099
  var severityIcon = {
823
1100
  info: "i",
824
1101
  warn: "!",
@@ -840,7 +1117,7 @@ var AlertBar = React4.memo(({ alerts, maxVisible = 4 }) => {
840
1117
  ] }),
841
1118
  /* @__PURE__ */ jsxs4(Text4, { color: colors.muted, children: [
842
1119
  " ",
843
- formatTime2(alert.timestamp),
1120
+ formatTime(alert.timestamp),
844
1121
  " "
845
1122
  ] }),
846
1123
  /* @__PURE__ */ jsxs4(Text4, { color: colors.warning, children: [
@@ -856,11 +1133,6 @@ var AlertBar = React4.memo(({ alerts, maxVisible = 4 }) => {
856
1133
  import React5 from "react";
857
1134
  import { Box as Box5, Text as Text5 } from "ink";
858
1135
  import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
859
- var formatTokens2 = (n) => {
860
- if (n >= 1e6) return (n / 1e6).toFixed(1) + "M";
861
- if (n >= 1e3) return (n / 1e3).toFixed(1) + "k";
862
- return String(n);
863
- };
864
1136
  var formatUptime = (startTime) => {
865
1137
  const ms = Date.now() - startTime;
866
1138
  const secs = Math.floor(ms / 1e3);
@@ -870,12 +1142,6 @@ var formatUptime = (startTime) => {
870
1142
  if (mins > 0) return `${mins}m ${secs % 60}s`;
871
1143
  return `${secs}s`;
872
1144
  };
873
- var formatModel2 = (model) => {
874
- if (model.includes("opus")) return "opus";
875
- if (model.includes("sonnet")) return "sonnet";
876
- if (model.includes("haiku")) return "haiku";
877
- return model.slice(0, 20);
878
- };
879
1145
  var SessionDetail = React5.memo(({ session, focused }) => {
880
1146
  const totalInput = session.usage.inputTokens + session.usage.cacheCreationTokens + session.usage.cacheReadTokens;
881
1147
  const totalTokens = totalInput + session.usage.outputTokens;
@@ -903,7 +1169,7 @@ var SessionDetail = React5.memo(({ session, focused }) => {
903
1169
  ] }),
904
1170
  /* @__PURE__ */ jsxs5(Box5, { children: [
905
1171
  /* @__PURE__ */ jsx5(Text5, { color: colors.muted, children: "model: " }),
906
- /* @__PURE__ */ jsx5(Text5, { color: colors.text, children: formatModel2(session.model) })
1172
+ /* @__PURE__ */ jsx5(Text5, { color: colors.text, children: formatModel(session.model) })
907
1173
  ] }),
908
1174
  /* @__PURE__ */ jsxs5(Box5, { children: [
909
1175
  /* @__PURE__ */ jsx5(Text5, { color: colors.muted, children: "cwd: " }),
@@ -946,19 +1212,19 @@ var SessionDetail = React5.memo(({ session, focused }) => {
946
1212
  /* @__PURE__ */ jsx5(Box5, { marginTop: 1, children: /* @__PURE__ */ jsx5(Text5, { color: colors.header, bold: true, children: "Token usage" }) }),
947
1213
  /* @__PURE__ */ jsxs5(Box5, { children: [
948
1214
  /* @__PURE__ */ jsx5(Text5, { color: colors.muted, children: " input: " }),
949
- /* @__PURE__ */ jsx5(Text5, { color: colors.text, children: formatTokens2(session.usage.inputTokens) })
1215
+ /* @__PURE__ */ jsx5(Text5, { color: colors.text, children: formatTokens(session.usage.inputTokens) })
950
1216
  ] }),
951
1217
  /* @__PURE__ */ jsxs5(Box5, { children: [
952
1218
  /* @__PURE__ */ jsx5(Text5, { color: colors.muted, children: " output: " }),
953
- /* @__PURE__ */ jsx5(Text5, { color: colors.text, children: formatTokens2(session.usage.outputTokens) })
1219
+ /* @__PURE__ */ jsx5(Text5, { color: colors.text, children: formatTokens(session.usage.outputTokens) })
954
1220
  ] }),
955
1221
  /* @__PURE__ */ jsxs5(Box5, { children: [
956
1222
  /* @__PURE__ */ jsx5(Text5, { color: colors.muted, children: " cache write: " }),
957
- /* @__PURE__ */ jsx5(Text5, { color: colors.text, children: formatTokens2(session.usage.cacheCreationTokens) })
1223
+ /* @__PURE__ */ jsx5(Text5, { color: colors.text, children: formatTokens(session.usage.cacheCreationTokens) })
958
1224
  ] }),
959
1225
  /* @__PURE__ */ jsxs5(Box5, { children: [
960
1226
  /* @__PURE__ */ jsx5(Text5, { color: colors.muted, children: " cache read: " }),
961
- /* @__PURE__ */ jsx5(Text5, { color: colors.text, children: formatTokens2(session.usage.cacheReadTokens) })
1227
+ /* @__PURE__ */ jsx5(Text5, { color: colors.text, children: formatTokens(session.usage.cacheReadTokens) })
962
1228
  ] }),
963
1229
  /* @__PURE__ */ jsxs5(Box5, { children: [
964
1230
  /* @__PURE__ */ jsx5(Text5, { color: colors.muted, children: " cache hit: " }),
@@ -969,7 +1235,7 @@ var SessionDetail = React5.memo(({ session, focused }) => {
969
1235
  ] }),
970
1236
  /* @__PURE__ */ jsxs5(Box5, { children: [
971
1237
  /* @__PURE__ */ jsx5(Text5, { color: colors.muted, children: " total: " }),
972
- /* @__PURE__ */ jsx5(Text5, { color: colors.bright, bold: true, children: formatTokens2(totalTokens) })
1238
+ /* @__PURE__ */ jsx5(Text5, { color: colors.bright, bold: true, children: formatTokens(totalTokens) })
973
1239
  ] })
974
1240
  ] })
975
1241
  ]
@@ -1109,10 +1375,6 @@ var FooterBar = React7.memo(
1109
1375
  import React8, { useState as useState3 } from "react";
1110
1376
  import { Box as Box8, Text as Text8, useInput as useInput2 } from "ink";
1111
1377
  import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
1112
- var formatTime3 = (ts) => {
1113
- const d = new Date(ts);
1114
- return d.toLocaleTimeString("en-GB", { hour12: false });
1115
- };
1116
1378
  var renderBash = (event) => {
1117
1379
  const lines = [];
1118
1380
  const cmd = String(event.call.toolInput.command || "");
@@ -1286,7 +1548,7 @@ var ToolCallDetail = React8.memo(({ event, focused, height }) => {
1286
1548
  /* @__PURE__ */ jsx8(Text8, { color: getToolColor(event.call.toolName), bold: true, children: event.call.toolName }),
1287
1549
  /* @__PURE__ */ jsxs8(Text8, { color: colors.muted, children: [
1288
1550
  " ",
1289
- formatTime3(event.call.timestamp)
1551
+ formatTime(event.call.timestamp)
1290
1552
  ] }),
1291
1553
  /* @__PURE__ */ jsxs8(Text8, { color: colors.muted, children: [
1292
1554
  " ",
@@ -1970,23 +2232,451 @@ var ThemeMenu = React11.memo(({ config, onClose }) => {
1970
2232
  /* @__PURE__ */ jsx11(Text11, { color: theme.colors.error, children: "\u2588" })
1971
2233
  ] }, theme.name);
1972
2234
  }),
1973
- toast && /* @__PURE__ */ jsx11(Box11, { marginTop: 1, paddingX: 2, children: /* @__PURE__ */ jsx11(Text11, { color: colors.warning, children: toast }) })
2235
+ toast && /* @__PURE__ */ jsx11(Box11, { marginTop: 1, paddingX: 2, children: /* @__PURE__ */ jsx11(Text11, { color: colors.warning, children: toast }) }),
2236
+ /* @__PURE__ */ jsx11(Box11, { paddingX: 1, marginTop: 1, children: /* @__PURE__ */ jsx11(Text11, { color: colors.muted, dimColor: true, italic: true, wrap: "truncate", children: "Some themes were inspired by pop culture. Names have been changed to protect the innocent (and our legal team)." }) })
1974
2237
  ]
1975
2238
  }
1976
2239
  ) });
1977
2240
  });
1978
2241
 
1979
- // src/ui/components/ThemePickerModal.tsx
1980
- import React12, { useState as useState7, useEffect as useEffect4 } from "react";
1981
- import { Box as Box12, Text as Text12, useInput as useInput6 } from "ink";
2242
+ // src/ui/components/AlertRulesMenu.tsx
2243
+ import React12, { useState as useState7, useRef as useRef3, useEffect as useEffect4 } from "react";
2244
+ import { Box as Box12, Text as Text12, useInput as useInput6, useStdout as useStdout3 } from "ink";
1982
2245
  import { jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
1983
- var ThemePickerModal = React12.memo(({ onSelect, onSkip, onDismiss }) => {
1984
- const [selectedIndex, setSelectedIndex] = useState7(0);
2246
+ var MATCH_OPTIONS = ["input", "output", "toolName", "all"];
2247
+ var SEVERITY_OPTIONS2 = ["info", "warn", "high", "critical"];
2248
+ var RULE_LABELS2 = {
2249
+ network: "Network detection",
2250
+ exfiltration: "Exfiltration detection",
2251
+ sensitiveFiles: "Sensitive files",
2252
+ shellEscape: "Shell escape",
2253
+ injection: "Prompt injection"
2254
+ };
2255
+ var BUILTIN_RULE_KEYS = Object.keys(RULE_LABELS2);
2256
+ var FORM_FIELDS = ["name", "pattern", "match", "severity", "message"];
2257
+ var FORM_LABELS = {
2258
+ name: "Name",
2259
+ pattern: "Pattern (regex)",
2260
+ match: "Match target",
2261
+ severity: "Severity",
2262
+ message: "Message"
2263
+ };
2264
+ var emptyForm = () => ({
2265
+ name: "",
2266
+ pattern: "",
2267
+ match: "all",
2268
+ severity: "warn",
2269
+ message: ""
2270
+ });
2271
+ var formFromRule = (rule) => ({
2272
+ name: rule.name,
2273
+ pattern: rule.pattern,
2274
+ match: rule.match,
2275
+ severity: rule.severity,
2276
+ message: rule.message
2277
+ });
2278
+ var validatePattern = (pattern) => {
2279
+ if (!pattern.trim()) return "Pattern cannot be empty";
2280
+ try {
2281
+ new RegExp(pattern);
2282
+ return null;
2283
+ } catch {
2284
+ return "Invalid regex";
2285
+ }
2286
+ };
2287
+ var AlertRulesMenu = React12.memo(({ config, onClose, onSave }) => {
2288
+ const { stdout } = useStdout3();
2289
+ const termHeight = stdout?.rows ?? 40;
2290
+ const [localConfig, setLocalConfig] = useState7(() => JSON.parse(JSON.stringify(config)));
2291
+ const [selectedIdx, setSelectedIdx] = useState7(0);
2292
+ const [view, setView] = useState7("list");
2293
+ const [toast, setToast] = useState7("");
2294
+ const toastTimer = useRef3(null);
2295
+ const [formData, setFormData] = useState7(emptyForm());
2296
+ const [formField, setFormField] = useState7(0);
2297
+ const [formError, setFormError] = useState7("");
2298
+ const [editingIndex, setEditingIndex] = useState7(null);
2299
+ useEffect4(
2300
+ () => () => {
2301
+ if (toastTimer.current) clearTimeout(toastTimer.current);
2302
+ },
2303
+ []
2304
+ );
2305
+ const showToast = (msg) => {
2306
+ if (toastTimer.current) clearTimeout(toastTimer.current);
2307
+ setToast(msg);
2308
+ toastTimer.current = setTimeout(() => setToast(""), 2500);
2309
+ };
2310
+ const customRules = localConfig.alerts.custom || [];
2311
+ const totalItems = 1 + BUILTIN_RULE_KEYS.length + customRules.length;
2312
+ const isStaleRow = (idx) => idx === 0;
2313
+ const isBuiltinRow = (idx) => idx >= 1 && idx <= BUILTIN_RULE_KEYS.length;
2314
+ const isCustomRow = (idx) => idx > BUILTIN_RULE_KEYS.length;
2315
+ const getBuiltinKey = (idx) => BUILTIN_RULE_KEYS[idx - 1];
2316
+ const getCustomIndex = (idx) => idx - BUILTIN_RULE_KEYS.length - 1;
2317
+ useInput6((input, key) => {
2318
+ if (view === "form") {
2319
+ const currentField = FORM_FIELDS[formField];
2320
+ if (key.escape) {
2321
+ setView("list");
2322
+ setFormError("");
2323
+ return;
2324
+ }
2325
+ if (currentField === "match") {
2326
+ if (key.return || input === " ") {
2327
+ const idx = MATCH_OPTIONS.indexOf(formData.match);
2328
+ setFormData((f) => ({ ...f, match: MATCH_OPTIONS[(idx + 1) % MATCH_OPTIONS.length] }));
2329
+ return;
2330
+ }
2331
+ if (key.downArrow) {
2332
+ setFormField((f) => Math.min(f + 1, FORM_FIELDS.length - 1));
2333
+ return;
2334
+ }
2335
+ if (key.upArrow) {
2336
+ setFormField((f) => Math.max(f - 1, 0));
2337
+ return;
2338
+ }
2339
+ if (key.tab) {
2340
+ if (formField < FORM_FIELDS.length - 1) {
2341
+ setFormField((f) => f + 1);
2342
+ }
2343
+ return;
2344
+ }
2345
+ return;
2346
+ }
2347
+ if (currentField === "severity") {
2348
+ if (key.return || input === " ") {
2349
+ const idx = SEVERITY_OPTIONS2.indexOf(formData.severity);
2350
+ setFormData((f) => ({ ...f, severity: SEVERITY_OPTIONS2[(idx + 1) % SEVERITY_OPTIONS2.length] }));
2351
+ return;
2352
+ }
2353
+ if (key.downArrow) {
2354
+ setFormField((f) => Math.min(f + 1, FORM_FIELDS.length - 1));
2355
+ return;
2356
+ }
2357
+ if (key.upArrow) {
2358
+ setFormField((f) => Math.max(f - 1, 0));
2359
+ return;
2360
+ }
2361
+ if (key.tab) {
2362
+ if (formField < FORM_FIELDS.length - 1) {
2363
+ setFormField((f) => f + 1);
2364
+ }
2365
+ return;
2366
+ }
2367
+ return;
2368
+ }
2369
+ if (key.return) {
2370
+ if (currentField === "pattern") {
2371
+ const err = validatePattern(formData.pattern);
2372
+ if (err) {
2373
+ setFormError(err);
2374
+ return;
2375
+ }
2376
+ setFormError("");
2377
+ }
2378
+ if (formField < FORM_FIELDS.length - 1) {
2379
+ setFormField((f) => f + 1);
2380
+ return;
2381
+ }
2382
+ if (!formData.name.trim()) {
2383
+ setFormError("Name cannot be empty");
2384
+ return;
2385
+ }
2386
+ const patErr = validatePattern(formData.pattern);
2387
+ if (patErr) {
2388
+ setFormError(patErr);
2389
+ return;
2390
+ }
2391
+ const newRule = {
2392
+ name: formData.name.trim(),
2393
+ pattern: formData.pattern,
2394
+ match: formData.match,
2395
+ severity: formData.severity,
2396
+ message: formData.message.trim(),
2397
+ enabled: true
2398
+ };
2399
+ setLocalConfig((c) => {
2400
+ const customs = [...c.alerts.custom || []];
2401
+ if (editingIndex !== null) {
2402
+ newRule.enabled = customs[editingIndex].enabled;
2403
+ customs[editingIndex] = newRule;
2404
+ } else {
2405
+ customs.push(newRule);
2406
+ }
2407
+ const updated = { ...c, alerts: { ...c.alerts, custom: customs } };
2408
+ onSave(updated);
2409
+ return updated;
2410
+ });
2411
+ showToast(editingIndex !== null ? `Updated '${newRule.name}'` : `Added '${newRule.name}'`);
2412
+ setView("list");
2413
+ setFormError("");
2414
+ return;
2415
+ }
2416
+ if (key.backspace || key.delete) {
2417
+ setFormData((f) => ({ ...f, [currentField]: f[currentField].slice(0, -1) }));
2418
+ setFormError("");
2419
+ return;
2420
+ }
2421
+ if (key.upArrow) {
2422
+ setFormField((f) => Math.max(f - 1, 0));
2423
+ return;
2424
+ }
2425
+ if (key.downArrow || key.tab) {
2426
+ if (currentField === "pattern") {
2427
+ const err = validatePattern(formData.pattern);
2428
+ if (err && formData.pattern.trim()) {
2429
+ setFormError(err);
2430
+ return;
2431
+ }
2432
+ }
2433
+ setFormField((f) => Math.min(f + 1, FORM_FIELDS.length - 1));
2434
+ return;
2435
+ }
2436
+ if (input && input.length === 1) {
2437
+ setFormData((f) => ({ ...f, [currentField]: f[currentField] + input }));
2438
+ setFormError("");
2439
+ return;
2440
+ }
2441
+ return;
2442
+ }
2443
+ if (key.escape) {
2444
+ onSave(localConfig);
2445
+ onClose();
2446
+ return;
2447
+ }
2448
+ if (key.upArrow) {
2449
+ setSelectedIdx((i) => Math.max(0, i - 1));
2450
+ return;
2451
+ }
2452
+ if (key.downArrow) {
2453
+ setSelectedIdx((i) => Math.min(totalItems - 1, i + 1));
2454
+ return;
2455
+ }
2456
+ if (input === " ") {
2457
+ if (isStaleRow(selectedIdx)) return;
2458
+ if (isBuiltinRow(selectedIdx)) {
2459
+ const ruleKey = getBuiltinKey(selectedIdx);
2460
+ setLocalConfig((c) => {
2461
+ const updated = {
2462
+ ...c,
2463
+ security: { ...c.security, rules: { ...c.security.rules, [ruleKey]: !c.security.rules[ruleKey] } }
2464
+ };
2465
+ onSave(updated);
2466
+ return updated;
2467
+ });
2468
+ return;
2469
+ }
2470
+ if (isCustomRow(selectedIdx)) {
2471
+ const ci = getCustomIndex(selectedIdx);
2472
+ setLocalConfig((c) => {
2473
+ const customs = [...c.alerts.custom || []];
2474
+ customs[ci] = { ...customs[ci], enabled: !customs[ci].enabled };
2475
+ const updated = { ...c, alerts: { ...c.alerts, custom: customs } };
2476
+ onSave(updated);
2477
+ return updated;
2478
+ });
2479
+ return;
2480
+ }
2481
+ return;
2482
+ }
2483
+ if (isStaleRow(selectedIdx)) {
2484
+ if (input === "+" || input === "=") {
2485
+ setLocalConfig((c) => {
2486
+ const updated = { ...c, alerts: { ...c.alerts, staleTimeout: c.alerts.staleTimeout + 15 } };
2487
+ onSave(updated);
2488
+ return updated;
2489
+ });
2490
+ return;
2491
+ }
2492
+ if (input === "-" || input === "_") {
2493
+ setLocalConfig((c) => {
2494
+ const newTimeout = Math.max(15, c.alerts.staleTimeout - 15);
2495
+ const updated = { ...c, alerts: { ...c.alerts, staleTimeout: newTimeout } };
2496
+ onSave(updated);
2497
+ return updated;
2498
+ });
2499
+ return;
2500
+ }
2501
+ }
2502
+ if (input === "n") {
2503
+ setFormData(emptyForm());
2504
+ setFormField(0);
2505
+ setEditingIndex(null);
2506
+ setFormError("");
2507
+ setView("form");
2508
+ return;
2509
+ }
2510
+ if (input === "e" && isCustomRow(selectedIdx)) {
2511
+ const ci = getCustomIndex(selectedIdx);
2512
+ const rule = customRules[ci];
2513
+ setFormData(formFromRule(rule));
2514
+ setFormField(0);
2515
+ setEditingIndex(ci);
2516
+ setFormError("");
2517
+ setView("form");
2518
+ return;
2519
+ }
2520
+ if (input === "d" && isCustomRow(selectedIdx)) {
2521
+ const ci = getCustomIndex(selectedIdx);
2522
+ const name = customRules[ci].name;
2523
+ setLocalConfig((c) => {
2524
+ const customs = [...c.alerts.custom || []];
2525
+ customs.splice(ci, 1);
2526
+ const updated = { ...c, alerts: { ...c.alerts, custom: customs } };
2527
+ onSave(updated);
2528
+ return updated;
2529
+ });
2530
+ setSelectedIdx((i) => Math.min(i, totalItems - 2));
2531
+ showToast(`Deleted '${name}'`);
2532
+ return;
2533
+ }
2534
+ });
2535
+ if (view === "form") {
2536
+ const currentField = FORM_FIELDS[formField];
2537
+ return /* @__PURE__ */ jsx12(Box12, { flexDirection: "column", height: termHeight, children: /* @__PURE__ */ jsxs12(
2538
+ Box12,
2539
+ {
2540
+ borderStyle: "round",
2541
+ borderColor: colors.primary,
2542
+ flexDirection: "column",
2543
+ paddingX: 2,
2544
+ paddingY: 1,
2545
+ height: termHeight,
2546
+ children: [
2547
+ /* @__PURE__ */ jsxs12(Box12, { justifyContent: "space-between", marginBottom: 1, children: [
2548
+ /* @__PURE__ */ jsx12(Text12, { color: colors.header, bold: true, children: editingIndex !== null ? "EDIT RULE" : "NEW RULE" }),
2549
+ /* @__PURE__ */ jsx12(Text12, { color: colors.muted, children: "enter:next/save esc:cancel" })
2550
+ ] }),
2551
+ FORM_FIELDS.map((field, fi) => {
2552
+ const isCurrent = fi === formField;
2553
+ const value = formData[field];
2554
+ const isTextInput = field === "name" || field === "pattern" || field === "message";
2555
+ const displayValue = isTextInput ? isCurrent ? `${value}_` : value || "(empty)" : field === "match" ? `${value} (space to cycle)` : `${value} (space to cycle)`;
2556
+ return /* @__PURE__ */ jsxs12(Box12, { children: [
2557
+ /* @__PURE__ */ jsxs12(Text12, { color: isCurrent ? colors.primary : colors.text, children: [
2558
+ isCurrent ? "> " : " ",
2559
+ FORM_LABELS[field],
2560
+ ":",
2561
+ " "
2562
+ ] }),
2563
+ /* @__PURE__ */ jsx12(Text12, { color: isCurrent ? colors.bright : colors.muted, children: displayValue })
2564
+ ] }, field);
2565
+ }),
2566
+ formError && /* @__PURE__ */ jsx12(Box12, { marginTop: 1, paddingX: 2, children: /* @__PURE__ */ jsx12(Text12, { color: colors.error, children: formError }) }),
2567
+ /* @__PURE__ */ jsx12(Box12, { marginTop: 1, paddingX: 2, children: /* @__PURE__ */ jsx12(Text12, { color: colors.muted, children: currentField === "match" || currentField === "severity" ? "space/enter: cycle options | up/down: move fields" : "type to edit | enter: next field | up/down: move fields" }) })
2568
+ ]
2569
+ }
2570
+ ) });
2571
+ }
2572
+ const contentHeight = termHeight - 6;
2573
+ const halfView = Math.floor(contentHeight / 2);
2574
+ const scrollStart = Math.max(0, Math.min(selectedIdx - halfView, totalItems - contentHeight));
2575
+ const visibleStart = Math.max(0, scrollStart);
2576
+ const visibleEnd = Math.min(totalItems, visibleStart + contentHeight);
2577
+ return /* @__PURE__ */ jsx12(Box12, { flexDirection: "column", height: termHeight, children: /* @__PURE__ */ jsxs12(
2578
+ Box12,
2579
+ {
2580
+ borderStyle: "round",
2581
+ borderColor: colors.primary,
2582
+ flexDirection: "column",
2583
+ paddingX: 2,
2584
+ paddingY: 1,
2585
+ height: termHeight,
2586
+ children: [
2587
+ /* @__PURE__ */ jsxs12(Box12, { justifyContent: "space-between", marginBottom: 1, children: [
2588
+ /* @__PURE__ */ jsx12(Text12, { color: colors.header, bold: true, children: "ALERT RULES" }),
2589
+ /* @__PURE__ */ jsx12(Text12, { color: colors.muted, children: "space:toggle n:new e:edit d:delete esc:close" })
2590
+ ] }),
2591
+ visibleStart === 0 && /* @__PURE__ */ jsx12(Box12, { marginBottom: 1, children: /* @__PURE__ */ jsx12(Box12, { children: /* @__PURE__ */ jsxs12(Text12, { color: colors.accent, bold: true, children: [
2592
+ " ",
2593
+ "STALE SESSION"
2594
+ ] }) }) }),
2595
+ Array.from({ length: visibleEnd - visibleStart }, (_, vi) => {
2596
+ const idx = visibleStart + vi;
2597
+ const isSelected = idx === selectedIdx;
2598
+ if (isStaleRow(idx)) {
2599
+ const timeout = localConfig.alerts.staleTimeout;
2600
+ return /* @__PURE__ */ jsxs12(Box12, { children: [
2601
+ /* @__PURE__ */ jsxs12(Text12, { color: isSelected ? colors.primary : colors.text, children: [
2602
+ isSelected ? "> " : " ",
2603
+ " Stale timeout ",
2604
+ "............ "
2605
+ ] }),
2606
+ /* @__PURE__ */ jsxs12(Text12, { color: colors.bright, children: [
2607
+ timeout,
2608
+ "s"
2609
+ ] }),
2610
+ /* @__PURE__ */ jsx12(Text12, { color: colors.muted, children: " (+/-)" })
2611
+ ] }, "stale-timeout");
2612
+ }
2613
+ if (isBuiltinRow(idx)) {
2614
+ const ruleKey = getBuiltinKey(idx);
2615
+ const label2 = RULE_LABELS2[ruleKey];
2616
+ const enabled = localConfig.security.rules[ruleKey];
2617
+ const showHeader = idx === 1 && visibleStart <= 1;
2618
+ return /* @__PURE__ */ jsxs12(React12.Fragment, { children: [
2619
+ showHeader && /* @__PURE__ */ jsx12(Box12, { marginTop: 1, children: /* @__PURE__ */ jsxs12(Text12, { color: colors.accent, bold: true, children: [
2620
+ " ",
2621
+ "BUILT-IN RULES"
2622
+ ] }) }),
2623
+ /* @__PURE__ */ jsxs12(Box12, { children: [
2624
+ /* @__PURE__ */ jsxs12(Text12, { color: isSelected ? colors.primary : colors.text, children: [
2625
+ isSelected ? "> " : " ",
2626
+ " ",
2627
+ label2,
2628
+ " ",
2629
+ ".".repeat(Math.max(2, 28 - label2.length)),
2630
+ " "
2631
+ ] }),
2632
+ /* @__PURE__ */ jsx12(Text12, { color: enabled ? colors.secondary : colors.error, children: enabled ? "ON" : "OFF" })
2633
+ ] })
2634
+ ] }, `builtin-${ruleKey}`);
2635
+ }
2636
+ if (isCustomRow(idx)) {
2637
+ const ci = getCustomIndex(idx);
2638
+ const rule = customRules[ci];
2639
+ const showHeader = ci === 0;
2640
+ return /* @__PURE__ */ jsxs12(React12.Fragment, { children: [
2641
+ showHeader && /* @__PURE__ */ jsx12(Box12, { marginTop: 1, children: /* @__PURE__ */ jsxs12(Text12, { color: colors.accent, bold: true, children: [
2642
+ " ",
2643
+ "CUSTOM RULES"
2644
+ ] }) }),
2645
+ /* @__PURE__ */ jsxs12(Box12, { children: [
2646
+ /* @__PURE__ */ jsxs12(Text12, { color: isSelected ? colors.primary : colors.text, children: [
2647
+ isSelected ? "> " : " ",
2648
+ " ",
2649
+ rule.name,
2650
+ " "
2651
+ ] }),
2652
+ /* @__PURE__ */ jsxs12(Text12, { color: colors.muted, children: [
2653
+ "/",
2654
+ rule.pattern,
2655
+ "/ "
2656
+ ] }),
2657
+ /* @__PURE__ */ jsx12(Text12, { color: rule.enabled ? colors.secondary : colors.error, children: rule.enabled ? "ON" : "OFF" })
2658
+ ] })
2659
+ ] }, `custom-${ci}`);
2660
+ }
2661
+ return null;
2662
+ }),
2663
+ toast && /* @__PURE__ */ jsx12(Box12, { marginTop: 1, paddingX: 2, children: /* @__PURE__ */ jsx12(Text12, { color: colors.warning, children: toast }) })
2664
+ ]
2665
+ }
2666
+ ) });
2667
+ });
2668
+
2669
+ // src/ui/components/ThemePickerModal.tsx
2670
+ import React13, { useState as useState8, useEffect as useEffect5 } from "react";
2671
+ import { Box as Box13, Text as Text13, useInput as useInput7 } from "ink";
2672
+ import { jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
2673
+ var ThemePickerModal = React13.memo(({ onSelect, onSkip, onDismiss }) => {
2674
+ const [selectedIndex, setSelectedIndex] = useState8(0);
1985
2675
  const themes = BUILTIN_THEMES;
1986
- useEffect4(() => {
2676
+ useEffect5(() => {
1987
2677
  applyTheme(themes[selectedIndex]);
1988
2678
  }, [selectedIndex]);
1989
- useInput6((input, key) => {
2679
+ useInput7((input, key) => {
1990
2680
  if (key.upArrow) setSelectedIndex((i) => Math.max(0, i - 1));
1991
2681
  if (key.downArrow) setSelectedIndex((i) => Math.min(themes.length - 1, i + 1));
1992
2682
  if (key.return) onSelect(themes[selectedIndex].name);
@@ -1995,33 +2685,33 @@ var ThemePickerModal = React12.memo(({ onSelect, onSkip, onDismiss }) => {
1995
2685
  });
1996
2686
  const renderSwatch = (theme) => {
1997
2687
  const c = theme.colors;
1998
- return /* @__PURE__ */ jsxs12(Text12, { children: [
1999
- /* @__PURE__ */ jsx12(Text12, { color: c.primary, children: "\u2588" }),
2000
- /* @__PURE__ */ jsx12(Text12, { color: c.secondary, children: "\u2588" }),
2001
- /* @__PURE__ */ jsx12(Text12, { color: c.accent, children: "\u2588" }),
2002
- /* @__PURE__ */ jsx12(Text12, { color: c.warning, children: "\u2588" }),
2003
- /* @__PURE__ */ jsx12(Text12, { color: c.error, children: "\u2588" })
2688
+ return /* @__PURE__ */ jsxs13(Text13, { children: [
2689
+ /* @__PURE__ */ jsx13(Text13, { color: c.primary, children: "\u2588" }),
2690
+ /* @__PURE__ */ jsx13(Text13, { color: c.secondary, children: "\u2588" }),
2691
+ /* @__PURE__ */ jsx13(Text13, { color: c.accent, children: "\u2588" }),
2692
+ /* @__PURE__ */ jsx13(Text13, { color: c.warning, children: "\u2588" }),
2693
+ /* @__PURE__ */ jsx13(Text13, { color: c.error, children: "\u2588" })
2004
2694
  ] });
2005
2695
  };
2006
- return /* @__PURE__ */ jsx12(Box12, { flexDirection: "column", paddingX: 4, paddingY: 1, children: /* @__PURE__ */ jsxs12(Box12, { borderStyle: "round", borderColor: colors.primary, flexDirection: "column", paddingX: 3, paddingY: 1, children: [
2007
- /* @__PURE__ */ jsx12(Box12, { marginBottom: 1, children: /* @__PURE__ */ jsx12(Text12, { color: colors.header, bold: true, children: "Choose a theme" }) }),
2008
- /* @__PURE__ */ jsx12(Box12, { marginBottom: 1, children: /* @__PURE__ */ jsx12(Text12, { color: colors.text, children: "Select a theme to get started. You can change this later in settings (s)." }) }),
2009
- themes.map((theme, i) => /* @__PURE__ */ jsxs12(Box12, { children: [
2010
- /* @__PURE__ */ jsx12(Text12, { color: i === selectedIndex ? colors.primary : colors.muted, children: i === selectedIndex ? "> " : " " }),
2696
+ return /* @__PURE__ */ jsx13(Box13, { flexDirection: "column", paddingX: 4, paddingY: 1, children: /* @__PURE__ */ jsxs13(Box13, { borderStyle: "round", borderColor: colors.primary, flexDirection: "column", paddingX: 3, paddingY: 1, children: [
2697
+ /* @__PURE__ */ jsx13(Box13, { marginBottom: 1, children: /* @__PURE__ */ jsx13(Text13, { color: colors.header, bold: true, children: "Choose a theme" }) }),
2698
+ /* @__PURE__ */ jsx13(Box13, { marginBottom: 1, children: /* @__PURE__ */ jsx13(Text13, { color: colors.text, children: "Select a theme to get started. You can change this later in settings (s)." }) }),
2699
+ themes.map((theme, i) => /* @__PURE__ */ jsxs13(Box13, { children: [
2700
+ /* @__PURE__ */ jsx13(Text13, { color: i === selectedIndex ? colors.primary : colors.muted, children: i === selectedIndex ? "> " : " " }),
2011
2701
  renderSwatch(theme),
2012
- /* @__PURE__ */ jsxs12(Text12, { color: i === selectedIndex ? colors.bright : colors.text, children: [
2702
+ /* @__PURE__ */ jsxs13(Text13, { color: i === selectedIndex ? colors.bright : colors.text, children: [
2013
2703
  " ",
2014
2704
  theme.name
2015
2705
  ] })
2016
2706
  ] }, theme.name)),
2017
- /* @__PURE__ */ jsx12(Box12, { marginTop: 1, flexDirection: "column", children: /* @__PURE__ */ jsx12(Text12, { color: colors.muted, children: "Enter = select | n = not now | d = don't ask again" }) })
2707
+ /* @__PURE__ */ jsx13(Box13, { marginTop: 1, flexDirection: "column", children: /* @__PURE__ */ jsx13(Text13, { color: colors.muted, children: "Enter = select | n = not now | d = don't ask again" }) })
2018
2708
  ] }) });
2019
2709
  });
2020
2710
 
2021
2711
  // src/ui/components/GuidedTour.tsx
2022
- import React13, { useState as useState8 } from "react";
2023
- import { Box as Box13, Text as Text13, useInput as useInput7 } from "ink";
2024
- import { jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
2712
+ import React14, { useState as useState9 } from "react";
2713
+ import { Box as Box14, Text as Text14, useInput as useInput8 } from "ink";
2714
+ import { jsx as jsx14, jsxs as jsxs14 } from "react/jsx-runtime";
2025
2715
  var STEPS = [
2026
2716
  {
2027
2717
  title: "Session list",
@@ -2052,11 +2742,11 @@ var STEPS = [
2052
2742
  body: "Press s to open settings. Customise keybindings, manage themes, and configure updates."
2053
2743
  }
2054
2744
  ];
2055
- var GuidedTour = React13.memo(({ onComplete, onSkip }) => {
2056
- const [stepIndex, setStepIndex] = useState8(0);
2745
+ var GuidedTour = React14.memo(({ onComplete, onSkip }) => {
2746
+ const [stepIndex, setStepIndex] = useState9(0);
2057
2747
  const step = STEPS[stepIndex];
2058
2748
  const isLast = stepIndex === STEPS.length - 1;
2059
- useInput7((input, key) => {
2749
+ useInput8((input, key) => {
2060
2750
  if (key.return || key.rightArrow) {
2061
2751
  if (isLast) onComplete();
2062
2752
  else setStepIndex((i) => i + 1);
@@ -2064,17 +2754,17 @@ var GuidedTour = React13.memo(({ onComplete, onSkip }) => {
2064
2754
  if (key.leftArrow && stepIndex > 0) setStepIndex((i) => i - 1);
2065
2755
  if (input === "q" || key.escape) onSkip();
2066
2756
  });
2067
- return /* @__PURE__ */ jsx13(Box13, { flexDirection: "column", paddingX: 4, paddingY: 1, children: /* @__PURE__ */ jsxs13(Box13, { borderStyle: "round", borderColor: colors.primary, flexDirection: "column", paddingX: 3, paddingY: 1, children: [
2068
- /* @__PURE__ */ jsx13(Box13, { marginBottom: 1, children: /* @__PURE__ */ jsxs13(Text13, { color: colors.header, bold: true, children: [
2757
+ return /* @__PURE__ */ jsx14(Box14, { flexDirection: "column", paddingX: 4, paddingY: 1, children: /* @__PURE__ */ jsxs14(Box14, { borderStyle: "round", borderColor: colors.primary, flexDirection: "column", paddingX: 3, paddingY: 1, children: [
2758
+ /* @__PURE__ */ jsx14(Box14, { marginBottom: 1, children: /* @__PURE__ */ jsxs14(Text14, { color: colors.header, bold: true, children: [
2069
2759
  "Quick tour (",
2070
2760
  stepIndex + 1,
2071
2761
  "/",
2072
2762
  STEPS.length,
2073
2763
  ")"
2074
2764
  ] }) }),
2075
- /* @__PURE__ */ jsx13(Box13, { marginBottom: 1, children: /* @__PURE__ */ jsx13(Text13, { color: colors.bright, bold: true, children: step.title }) }),
2076
- /* @__PURE__ */ jsx13(Box13, { marginBottom: 1, children: /* @__PURE__ */ jsx13(Text13, { color: colors.text, children: step.body }) }),
2077
- /* @__PURE__ */ jsxs13(Text13, { color: colors.muted, children: [
2765
+ /* @__PURE__ */ jsx14(Box14, { marginBottom: 1, children: /* @__PURE__ */ jsx14(Text14, { color: colors.bright, bold: true, children: step.title }) }),
2766
+ /* @__PURE__ */ jsx14(Box14, { marginBottom: 1, children: /* @__PURE__ */ jsx14(Text14, { color: colors.text, children: step.body }) }),
2767
+ /* @__PURE__ */ jsxs14(Text14, { color: colors.muted, children: [
2078
2768
  isLast ? "Enter = finish" : "Enter/\u2192 = next",
2079
2769
  " | ",
2080
2770
  stepIndex > 0 ? "\u2190 = back | " : "",
@@ -2084,19 +2774,19 @@ var GuidedTour = React13.memo(({ onComplete, onSkip }) => {
2084
2774
  });
2085
2775
 
2086
2776
  // src/ui/components/ConfirmModal.tsx
2087
- import React14 from "react";
2088
- import { Box as Box14, Text as Text14, useInput as useInput8 } from "ink";
2089
- import { jsx as jsx14, jsxs as jsxs14 } from "react/jsx-runtime";
2090
- var ConfirmModal = React14.memo(({ title, message, onConfirm, onCancel }) => {
2091
- useInput8((input, key) => {
2777
+ import React15 from "react";
2778
+ import { Box as Box15, Text as Text15, useInput as useInput9 } from "ink";
2779
+ import { jsx as jsx15, jsxs as jsxs15 } from "react/jsx-runtime";
2780
+ var ConfirmModal = React15.memo(({ title, message, onConfirm, onCancel }) => {
2781
+ useInput9((input, key) => {
2092
2782
  if (input === "y" || input === "Y") {
2093
2783
  onConfirm();
2094
2784
  } else if (input === "n" || input === "N" || key.escape) {
2095
2785
  onCancel();
2096
2786
  }
2097
2787
  });
2098
- return /* @__PURE__ */ jsxs14(
2099
- Box14,
2788
+ return /* @__PURE__ */ jsxs15(
2789
+ Box15,
2100
2790
  {
2101
2791
  borderStyle: "round",
2102
2792
  borderColor: colors.warning,
@@ -2105,27 +2795,27 @@ var ConfirmModal = React14.memo(({ title, message, onConfirm, onCancel }) => {
2105
2795
  paddingY: 1,
2106
2796
  alignSelf: "center",
2107
2797
  children: [
2108
- /* @__PURE__ */ jsx14(Text14, { color: colors.warning, bold: true, children: title }),
2109
- /* @__PURE__ */ jsx14(Text14, { color: colors.text, children: message }),
2110
- /* @__PURE__ */ jsx14(Box14, { marginTop: 1, children: /* @__PURE__ */ jsx14(Text14, { color: colors.muted, children: "[y] confirm [n/esc] cancel" }) })
2798
+ /* @__PURE__ */ jsx15(Text15, { color: colors.warning, bold: true, children: title }),
2799
+ /* @__PURE__ */ jsx15(Text15, { color: colors.text, children: message }),
2800
+ /* @__PURE__ */ jsx15(Box15, { marginTop: 1, children: /* @__PURE__ */ jsx15(Text15, { color: colors.muted, children: "[y] confirm [n/esc] cancel" }) })
2111
2801
  ]
2112
2802
  }
2113
2803
  );
2114
2804
  });
2115
2805
 
2116
2806
  // src/ui/components/UpdateModal.tsx
2117
- import React15, { useState as useState9, useCallback as useCallback2 } from "react";
2118
- import { Box as Box15, Text as Text15, useInput as useInput9 } from "ink";
2119
- import { Fragment as Fragment2, jsx as jsx15, jsxs as jsxs15 } from "react/jsx-runtime";
2807
+ import React16, { useState as useState10, useCallback as useCallback2 } from "react";
2808
+ import { Box as Box16, Text as Text16, useInput as useInput10 } from "ink";
2809
+ import { Fragment as Fragment2, jsx as jsx16, jsxs as jsxs16 } from "react/jsx-runtime";
2120
2810
  var options = [
2121
2811
  { key: "now", label: "Update now" },
2122
2812
  { key: "later", label: "Not now" },
2123
2813
  { key: "never", label: "Don't ask again" }
2124
2814
  ];
2125
- var UpdateModal = React15.memo(({ current, latest, onNotNow, onDontAskAgain }) => {
2126
- const [selected, setSelected] = useState9(0);
2127
- const [status, setStatus] = useState9("idle");
2128
- const [errorMsg, setErrorMsg] = useState9("");
2815
+ var UpdateModal = React16.memo(({ current, latest, onNotNow, onDontAskAgain }) => {
2816
+ const [selected, setSelected] = useState10(0);
2817
+ const [status, setStatus] = useState10("idle");
2818
+ const [errorMsg, setErrorMsg] = useState10("");
2129
2819
  const doUpdate = useCallback2(() => {
2130
2820
  setStatus("updating");
2131
2821
  installUpdate().then(() => {
@@ -2136,7 +2826,7 @@ var UpdateModal = React15.memo(({ current, latest, onNotNow, onDontAskAgain }) =
2136
2826
  setStatus("error");
2137
2827
  });
2138
2828
  }, []);
2139
- useInput9((input, key) => {
2829
+ useInput10((input, key) => {
2140
2830
  if (status === "updating" || status === "restarting") return;
2141
2831
  if (status === "error") {
2142
2832
  if (key.escape || key.return) {
@@ -2168,8 +2858,8 @@ var UpdateModal = React15.memo(({ current, latest, onNotNow, onDontAskAgain }) =
2168
2858
  }
2169
2859
  }
2170
2860
  });
2171
- return /* @__PURE__ */ jsxs15(
2172
- Box15,
2861
+ return /* @__PURE__ */ jsxs16(
2862
+ Box16,
2173
2863
  {
2174
2864
  borderStyle: "round",
2175
2865
  borderColor: colors.primary,
@@ -2178,8 +2868,8 @@ var UpdateModal = React15.memo(({ current, latest, onNotNow, onDontAskAgain }) =
2178
2868
  paddingY: 1,
2179
2869
  alignSelf: "center",
2180
2870
  children: [
2181
- /* @__PURE__ */ jsx15(Text15, { color: colors.primary, bold: true, children: "Update available" }),
2182
- /* @__PURE__ */ jsxs15(Text15, { color: colors.text, children: [
2871
+ /* @__PURE__ */ jsx16(Text16, { color: colors.primary, bold: true, children: "Update available" }),
2872
+ /* @__PURE__ */ jsxs16(Text16, { color: colors.text, children: [
2183
2873
  "v",
2184
2874
  current,
2185
2875
  " ",
@@ -2187,10 +2877,10 @@ var UpdateModal = React15.memo(({ current, latest, onNotNow, onDontAskAgain }) =
2187
2877
  " v",
2188
2878
  latest
2189
2879
  ] }),
2190
- /* @__PURE__ */ jsx15(Box15, { marginTop: 1, flexDirection: "column", children: status === "updating" ? /* @__PURE__ */ jsx15(Text15, { color: colors.warning, children: "updating..." }) : status === "restarting" ? /* @__PURE__ */ jsx15(Text15, { color: colors.success, children: "restarting..." }) : status === "error" ? /* @__PURE__ */ jsxs15(Fragment2, { children: [
2191
- /* @__PURE__ */ jsx15(Text15, { color: colors.error, children: errorMsg }),
2192
- /* @__PURE__ */ jsx15(Text15, { color: colors.muted, children: "press enter to continue" })
2193
- ] }) : options.map((opt, i) => /* @__PURE__ */ jsxs15(Text15, { color: i === selected ? colors.primary : colors.muted, children: [
2880
+ /* @__PURE__ */ jsx16(Box16, { marginTop: 1, flexDirection: "column", children: status === "updating" ? /* @__PURE__ */ jsx16(Text16, { color: colors.warning, children: "updating..." }) : status === "restarting" ? /* @__PURE__ */ jsx16(Text16, { color: colors.secondary, children: "restarting..." }) : status === "error" ? /* @__PURE__ */ jsxs16(Fragment2, { children: [
2881
+ /* @__PURE__ */ jsx16(Text16, { color: colors.error, children: errorMsg }),
2882
+ /* @__PURE__ */ jsx16(Text16, { color: colors.muted, children: "press enter to continue" })
2883
+ ] }) : options.map((opt, i) => /* @__PURE__ */ jsxs16(Text16, { color: i === selected ? colors.primary : colors.muted, children: [
2194
2884
  i === selected ? "> " : " ",
2195
2885
  opt.label
2196
2886
  ] }, opt.key)) })
@@ -2200,10 +2890,10 @@ var UpdateModal = React15.memo(({ current, latest, onNotNow, onDontAskAgain }) =
2200
2890
  });
2201
2891
 
2202
2892
  // src/ui/components/SplitPanel.tsx
2203
- import React16 from "react";
2204
- import { Box as Box16 } from "ink";
2205
- import { jsx as jsx16, jsxs as jsxs16 } from "react/jsx-runtime";
2206
- var SplitPanel = React16.memo(
2893
+ import React17 from "react";
2894
+ import { Box as Box17 } from "ink";
2895
+ import { jsx as jsx17, jsxs as jsxs17 } from "react/jsx-runtime";
2896
+ var SplitPanel = React17.memo(
2207
2897
  ({
2208
2898
  activePanel,
2209
2899
  leftSession,
@@ -2218,35 +2908,35 @@ var SplitPanel = React16.memo(
2218
2908
  rightShowDetail,
2219
2909
  height
2220
2910
  }) => {
2221
- const left = leftShowDetail && leftSession ? /* @__PURE__ */ jsx16(SessionDetail, { session: leftSession, focused: activePanel === "left", height }) : /* @__PURE__ */ jsx16(
2911
+ const left = leftShowDetail && leftSession ? /* @__PURE__ */ jsx17(SessionDetail, { session: leftSession, focused: activePanel === "left", height }) : /* @__PURE__ */ jsx17(
2222
2912
  ActivityFeed,
2223
2913
  {
2224
2914
  events: leftEvents,
2225
2915
  sessionSlug: leftSession?.slug ?? null,
2226
2916
  sessionId: leftSession?.sessionId,
2227
- isActive: leftSession ? leftSession.pid !== null : void 0,
2917
+ status: leftSession ? leftSession.status : void 0,
2228
2918
  focused: activePanel === "left",
2229
2919
  height,
2230
2920
  scrollOffset: leftScroll,
2231
2921
  filter: leftFilter || void 0
2232
2922
  }
2233
2923
  );
2234
- const right = rightShowDetail && rightSession ? /* @__PURE__ */ jsx16(SessionDetail, { session: rightSession, focused: activePanel === "right", height }) : /* @__PURE__ */ jsx16(
2924
+ const right = rightShowDetail && rightSession ? /* @__PURE__ */ jsx17(SessionDetail, { session: rightSession, focused: activePanel === "right", height }) : /* @__PURE__ */ jsx17(
2235
2925
  ActivityFeed,
2236
2926
  {
2237
2927
  events: rightEvents,
2238
2928
  sessionSlug: rightSession?.slug ?? null,
2239
2929
  sessionId: rightSession?.sessionId,
2240
- isActive: rightSession ? rightSession.pid !== null : void 0,
2930
+ status: rightSession ? rightSession.status : void 0,
2241
2931
  focused: activePanel === "right",
2242
2932
  height,
2243
2933
  scrollOffset: rightScroll,
2244
2934
  filter: rightFilter || void 0
2245
2935
  }
2246
2936
  );
2247
- return /* @__PURE__ */ jsxs16(Box16, { flexDirection: "row", flexGrow: 1, children: [
2248
- /* @__PURE__ */ jsx16(
2249
- Box16,
2937
+ return /* @__PURE__ */ jsxs17(Box17, { flexDirection: "row", flexGrow: 1, children: [
2938
+ /* @__PURE__ */ jsx17(
2939
+ Box17,
2250
2940
  {
2251
2941
  flexGrow: 1,
2252
2942
  borderStyle: "single",
@@ -2264,10 +2954,10 @@ var SplitPanel = React16.memo(
2264
2954
  );
2265
2955
 
2266
2956
  // src/ui/hooks/useSessions.ts
2267
- import { useState as useState10, useEffect as useEffect5, useCallback as useCallback3, useRef as useRef3, useMemo as useMemo2 } from "react";
2957
+ import { useState as useState11, useEffect as useEffect6, useCallback as useCallback3, useRef as useRef4, useMemo as useMemo2 } from "react";
2268
2958
 
2269
2959
  // src/discovery/sessionsAsync.ts
2270
- import { readdir, stat as stat2 } from "fs/promises";
2960
+ import { readdir, stat as stat2, open as open2 } from "fs/promises";
2271
2961
  import { join as join2, basename } from "path";
2272
2962
 
2273
2963
  // src/discovery/cache.ts
@@ -2306,44 +2996,9 @@ var pruneFileMetaCache = (validPaths) => {
2306
2996
  };
2307
2997
 
2308
2998
  // src/discovery/asyncHelpers.ts
2309
- import { execFile as execFile2 } from "child_process";
2310
- import { open, stat, readlink } from "fs/promises";
2311
- var normalisePath = (p) => p.replace(/\/+$/, "");
2312
- var getClaudeProcessesAsync = async () => {
2313
- const stdout = await new Promise((resolve) => {
2314
- execFile2("ps", ["aux"], { encoding: "utf-8", timeout: 5e3, maxBuffer: 4 * 1024 * 1024 }, (err, out) => {
2315
- resolve(err || !out ? "" : out);
2316
- });
2317
- });
2318
- if (!stdout) return [];
2319
- const procs = [];
2320
- for (const line of stdout.split("\n")) {
2321
- if (!line.includes("/claude") || line.includes("grep") || line.includes("agenttop")) continue;
2322
- const parts = line.trim().split(/\s+/);
2323
- const pid = parseInt(parts[1], 10);
2324
- if (isNaN(pid)) continue;
2325
- const command = parts.slice(10).join(" ");
2326
- if (command.startsWith("sudo")) continue;
2327
- procs.push({
2328
- pid,
2329
- cpu: parseFloat(parts[2]) || 0,
2330
- mem: parseFloat(parts[3]) || 0,
2331
- memKB: parseInt(parts[5], 10) || 0,
2332
- startTime: parts[8] || "",
2333
- command,
2334
- cwd: ""
2335
- });
2336
- }
2337
- await Promise.all(
2338
- procs.map(async (p) => {
2339
- try {
2340
- p.cwd = await readlink(`/proc/${p.pid}/cwd`);
2341
- } catch {
2342
- }
2343
- })
2344
- );
2345
- return procs;
2346
- };
2999
+ import { open, stat } from "fs/promises";
3000
+ import { normalize } from "path";
3001
+ var normalisePath = (p) => normalize(p).replace(/[\\/]+$/, "");
2347
3002
  var readFirstLinesAsync = async (filePath, bytes) => {
2348
3003
  let fh;
2349
3004
  try {
@@ -2461,7 +3116,47 @@ var findModelAndUsageAsync = async (filePath) => {
2461
3116
  };
2462
3117
 
2463
3118
  // src/discovery/sessionsAsync.ts
2464
- var discoverFromProjectsAsync = async (allUsers, processes, sessionMap, seenFiles) => {
3119
+ var readTailBytesAsync = async (filePath, bytes) => {
3120
+ let fh;
3121
+ try {
3122
+ fh = await open2(filePath, "r");
3123
+ const fstat = await stat2(filePath);
3124
+ const start = Math.max(0, fstat.size - bytes);
3125
+ const readSize = Math.min(bytes, fstat.size);
3126
+ const buf = Buffer.alloc(readSize);
3127
+ await fh.read(buf, 0, readSize, start);
3128
+ return buf.toString("utf-8");
3129
+ } catch {
3130
+ return "";
3131
+ } finally {
3132
+ await fh?.close();
3133
+ }
3134
+ };
3135
+ var detectStatusAsync = async (filePath, hasPid, lastActivity, staleTimeout) => {
3136
+ if (!hasPid) return "inactive";
3137
+ const tail = await readTailBytesAsync(filePath, 4096);
3138
+ const lines = tail.split("\n").filter(Boolean);
3139
+ if (lines.length > 0) {
3140
+ try {
3141
+ const lastEvent = JSON.parse(lines[lines.length - 1]);
3142
+ if (lastEvent.type === "assistant") {
3143
+ const content = lastEvent.message?.content;
3144
+ if (Array.isArray(content)) {
3145
+ const hasAskUser = content.some(
3146
+ (b) => b.type === "tool_use" && b.name === "AskUserQuestion"
3147
+ );
3148
+ if (hasAskUser) return "waiting";
3149
+ const hasToolUse = content.some((b) => b.type === "tool_use");
3150
+ if (!hasToolUse) return "waiting";
3151
+ }
3152
+ }
3153
+ } catch {
3154
+ }
3155
+ }
3156
+ if (Date.now() - lastActivity > staleTimeout * 1e3) return "stale";
3157
+ return "active";
3158
+ };
3159
+ var discoverFromProjectsAsync = async (allUsers, processes, sessionMap, seenFiles, staleTimeout, pinnedOrder) => {
2465
3160
  const projectsDirs = getProjectsDirs(allUsers);
2466
3161
  for (const projectsDir of projectsDirs) {
2467
3162
  let projectNames;
@@ -2516,14 +3211,16 @@ var discoverFromProjectsAsync = async (allUsers, processes, sessionMap, seenFile
2516
3211
  outputFiles: [filePath],
2517
3212
  startTime: fstat.birthtimeMs || fstat.ctimeMs,
2518
3213
  lastActivity: fstat.mtimeMs,
2519
- usage: meta.usage
3214
+ usage: meta.usage,
3215
+ status: await detectStatusAsync(filePath, matchingProcess !== void 0, fstat.mtimeMs, staleTimeout),
3216
+ pinned: pinnedOrder.includes(meta.sessionId)
2520
3217
  };
2521
3218
  sessionMap.set(meta.sessionId, session);
2522
3219
  }
2523
3220
  }
2524
3221
  }
2525
3222
  };
2526
- var discoverFromTmpAsync = async (allUsers, processes, sessionMap, seenFiles) => {
3223
+ var discoverFromTmpAsync = async (allUsers, processes, sessionMap, seenFiles, staleTimeout, pinnedOrder) => {
2527
3224
  const taskDirs = getTaskDirs(allUsers);
2528
3225
  for (const taskDir of taskDirs) {
2529
3226
  let projectDirs;
@@ -2613,6 +3310,13 @@ var discoverFromTmpAsync = async (allUsers, processes, sessionMap, seenFiles) =>
2613
3310
  if (sessionMap.has(sessionId || projectName)) continue;
2614
3311
  const normCwd = normalisePath(cwd);
2615
3312
  const matchingProcess = processes.find((p) => p.cwd && normalisePath(p.cwd) === normCwd);
3313
+ let latestFile = outputFiles[0];
3314
+ for (const f of outputFiles) {
3315
+ try {
3316
+ if ((await stat2(f)).mtimeMs > (await stat2(latestFile)).mtimeMs) latestFile = f;
3317
+ } catch {
3318
+ }
3319
+ }
2616
3320
  const session = {
2617
3321
  sessionId,
2618
3322
  slug: slug || sessionId.slice(0, 12),
@@ -2630,7 +3334,9 @@ var discoverFromTmpAsync = async (allUsers, processes, sessionMap, seenFiles) =>
2630
3334
  outputFiles,
2631
3335
  startTime: startTime === Infinity ? Date.now() : startTime,
2632
3336
  lastActivity,
2633
- usage: totalUsage
3337
+ usage: totalUsage,
3338
+ status: await detectStatusAsync(latestFile, matchingProcess !== void 0, lastActivity, staleTimeout),
3339
+ pinned: pinnedOrder.includes(sessionId)
2634
3340
  };
2635
3341
  sessionMap.set(sessionId || projectName, session);
2636
3342
  }
@@ -2638,16 +3344,26 @@ var discoverFromTmpAsync = async (allUsers, processes, sessionMap, seenFiles) =>
2638
3344
  }
2639
3345
  };
2640
3346
  var discoverSessionsAsync = async (allUsers) => {
3347
+ const config = loadConfig();
3348
+ const staleTimeout = config.alerts.staleTimeout ?? 60;
3349
+ const pinnedOrder = config.pinnedSessions ?? [];
2641
3350
  const processes = await getClaudeProcessesAsync();
2642
3351
  const sessionMap = /* @__PURE__ */ new Map();
2643
3352
  const seenFiles = /* @__PURE__ */ new Set();
2644
- await discoverFromProjectsAsync(allUsers, processes, sessionMap, seenFiles);
2645
- await discoverFromTmpAsync(allUsers, processes, sessionMap, seenFiles);
3353
+ await discoverFromProjectsAsync(allUsers, processes, sessionMap, seenFiles, staleTimeout, pinnedOrder);
3354
+ await discoverFromTmpAsync(allUsers, processes, sessionMap, seenFiles, staleTimeout, pinnedOrder);
2646
3355
  pruneFileMetaCache(seenFiles);
2647
3356
  return Array.from(sessionMap.values()).sort((a, b) => {
2648
- const aActive = a.pid !== null ? 1 : 0;
2649
- const bActive = b.pid !== null ? 1 : 0;
2650
- if (aActive !== bActive) return bActive - aActive;
3357
+ const aPin = pinnedOrder.indexOf(a.sessionId);
3358
+ const bPin = pinnedOrder.indexOf(b.sessionId);
3359
+ const aIsPinned = aPin !== -1;
3360
+ const bIsPinned = bPin !== -1;
3361
+ if (aIsPinned && !bIsPinned) return -1;
3362
+ if (!aIsPinned && bIsPinned) return 1;
3363
+ if (aIsPinned && bIsPinned) return aPin - bPin;
3364
+ const aPri = STATUS_PRIORITY[a.status];
3365
+ const bPri = STATUS_PRIORITY[b.status];
3366
+ if (aPri !== bPri) return aPri - bPri;
2651
3367
  return b.lastActivity - a.lastActivity;
2652
3368
  });
2653
3369
  };
@@ -2692,7 +3408,9 @@ var buildGroups = (sessions2, expandedKeys) => {
2692
3408
  totalInputTokens: totalIn,
2693
3409
  totalOutputTokens: totalOut,
2694
3410
  latestModel: list[0].model,
2695
- isActive: list.some((s) => s.pid !== null),
3411
+ status: list.reduce((best, s) => {
3412
+ return (STATUS_PRIORITY[s.status] ?? 3) < (STATUS_PRIORITY[best] ?? 3) ? s.status : best;
3413
+ }, "inactive"),
2696
3414
  latestActivity: list[0].lastActivity,
2697
3415
  earliestStart: Math.min(...list.map((s) => s.startTime))
2698
3416
  });
@@ -2749,17 +3467,17 @@ var enrichAndFilter = (found, usageOverrides, filter, archivedIds, viewingArchiv
2749
3467
  return enriched;
2750
3468
  };
2751
3469
  var useSessions = (allUsers, filter, archivedIds, viewingArchive) => {
2752
- const [sessions2, setSessions] = useState10([]);
2753
- const [selectedIndex, setSelectedIndex] = useState10(0);
2754
- const [expandedKeys, setExpandedKeys] = useState10(/* @__PURE__ */ new Set());
2755
- const usageOverrides = useRef3(/* @__PURE__ */ new Map());
3470
+ const [sessions2, setSessions] = useState11([]);
3471
+ const [selectedIndex, setSelectedIndex] = useState11(0);
3472
+ const [expandedKeys, setExpandedKeys] = useState11(/* @__PURE__ */ new Set());
3473
+ const usageOverrides = useRef4(/* @__PURE__ */ new Map());
2756
3474
  const refresh = useCallback3(() => {
2757
3475
  const found = getCachedSessions();
2758
3476
  const filtered = enrichAndFilter(found, usageOverrides.current, filter, archivedIds, viewingArchive);
2759
3477
  setSessions(filtered);
2760
3478
  triggerRefresh(allUsers);
2761
3479
  }, [allUsers, filter, archivedIds, viewingArchive]);
2762
- useEffect5(() => {
3480
+ useEffect6(() => {
2763
3481
  const unsubscribe = subscribe(() => {
2764
3482
  const found = getCachedSessions();
2765
3483
  const filtered = enrichAndFilter(found, usageOverrides.current, filter, archivedIds, viewingArchive);
@@ -2767,7 +3485,7 @@ var useSessions = (allUsers, filter, archivedIds, viewingArchive) => {
2767
3485
  });
2768
3486
  return unsubscribe;
2769
3487
  }, [filter, archivedIds, viewingArchive]);
2770
- useEffect5(() => {
3488
+ useEffect6(() => {
2771
3489
  refresh();
2772
3490
  const pollMs = sessions2.length > 0 ? ACTIVE_POLL_MS : IDLE_POLL_MS;
2773
3491
  const interval = setInterval(() => triggerRefresh(allUsers), pollMs);
@@ -2775,7 +3493,7 @@ var useSessions = (allUsers, filter, archivedIds, viewingArchive) => {
2775
3493
  }, [refresh, sessions2.length > 0]);
2776
3494
  const groups = useMemo2(() => buildGroups(sessions2, expandedKeys), [sessions2, expandedKeys]);
2777
3495
  const visibleItems = useMemo2(() => buildVisibleItems(groups), [groups]);
2778
- const itemCountRef = useRef3(visibleItems.length);
3496
+ const itemCountRef = useRef4(visibleItems.length);
2779
3497
  itemCountRef.current = visibleItems.length;
2780
3498
  const selectedItem = visibleItems[selectedIndex] ?? null;
2781
3499
  const selectedSession = selectedItem?.type === "ungrouped" ? selectedItem.session : selectedItem?.type === "session" ? selectedItem.session : null;
@@ -2827,16 +3545,16 @@ var useSessions = (allUsers, filter, archivedIds, viewingArchive) => {
2827
3545
  };
2828
3546
 
2829
3547
  // src/ui/hooks/useActivityStream.ts
2830
- import { useState as useState11, useEffect as useEffect6, useRef as useRef4, useMemo as useMemo3 } from "react";
3548
+ import { useState as useState12, useEffect as useEffect7, useRef as useRef5, useMemo as useMemo3 } from "react";
2831
3549
  var MAX_EVENTS = 200;
2832
3550
  var useActivityStream = (session, allUsers) => {
2833
- const [calls, setCalls] = useState11([]);
2834
- const [results, setResults] = useState11([]);
2835
- const watcherRef = useRef4(null);
3551
+ const [calls, setCalls] = useState12([]);
3552
+ const [results, setResults] = useState12([]);
3553
+ const watcherRef = useRef5(null);
2836
3554
  const sessions2 = session === null ? [] : Array.isArray(session) ? session : [session];
2837
3555
  const sessionKey = sessions2.map((s) => s.sessionId).sort().join(",");
2838
3556
  const sessionIdSet = new Set(sessions2.map((s) => s.sessionId));
2839
- useEffect6(() => {
3557
+ useEffect7(() => {
2840
3558
  setCalls([]);
2841
3559
  setResults([]);
2842
3560
  if (sessions2.length === 0) return;
@@ -2870,7 +3588,7 @@ var useActivityStream = (session, allUsers) => {
2870
3588
  const resultHandler = (newResults) => {
2871
3589
  const matched = newResults.filter((r) => sessionIdSet.has(r.sessionId));
2872
3590
  if (matched.length === 0) return;
2873
- setResults((prev) => [...prev, ...matched]);
3591
+ setResults((prev) => [...prev, ...matched].slice(-MAX_EVENTS * 2));
2874
3592
  };
2875
3593
  const watcher = new Watcher(callHandler, allUsers, void 0, void 0, resultHandler);
2876
3594
  watcherRef.current = watcher;
@@ -2905,10 +3623,10 @@ var applyFilter = (events, filter) => {
2905
3623
  var useFilteredEvents = (rawEvents, filter) => useMemo4(() => applyFilter(rawEvents, filter), [rawEvents, filter]);
2906
3624
 
2907
3625
  // src/ui/hooks/useAlerts.ts
2908
- import { useState as useState12, useEffect as useEffect7, useRef as useRef5 } from "react";
3626
+ import { useState as useState13, useEffect as useEffect8, useRef as useRef6 } from "react";
2909
3627
 
2910
3628
  // src/notifications.ts
2911
- import { exec as exec2 } from "child_process";
3629
+ import { execFile as execFile2 } from "child_process";
2912
3630
  import { platform } from "os";
2913
3631
  var SEVERITY_ORDER = {
2914
3632
  info: 0,
@@ -2931,11 +3649,23 @@ var notify = (alert, config) => {
2931
3649
  const body = `${alert.sessionSlug}: ${alert.message.slice(0, 100)}`;
2932
3650
  const os = platform();
2933
3651
  if (os === "linux") {
2934
- exec2(`notify-send ${JSON.stringify(title)} ${JSON.stringify(body)}`, { timeout: 3e3 });
3652
+ execFile2("notify-send", [title, body], { timeout: 3e3 });
2935
3653
  } else if (os === "darwin") {
2936
- exec2(`osascript -e 'display notification ${JSON.stringify(body)} with title ${JSON.stringify(title)}'`, {
2937
- timeout: 3e3
2938
- });
3654
+ execFile2(
3655
+ "osascript",
3656
+ ["-e", `display notification ${JSON.stringify(body)} with title ${JSON.stringify(title)}`],
3657
+ { timeout: 3e3 }
3658
+ );
3659
+ } else if (os === "win32") {
3660
+ execFile2(
3661
+ "powershell",
3662
+ [
3663
+ "-NoProfile",
3664
+ "-Command",
3665
+ `Add-Type -AssemblyName System.Windows.Forms; $n = New-Object System.Windows.Forms.NotifyIcon; $n.Icon = [System.Drawing.SystemIcons]::Information; $n.Visible = $true; $n.ShowBalloonTip(5000, ${JSON.stringify(title)}, ${JSON.stringify(body)}, 'Warning'); Start-Sleep -Seconds 6; $n.Dispose()`
3666
+ ],
3667
+ { timeout: 1e4 }
3668
+ );
2939
3669
  }
2940
3670
  }
2941
3671
  };
@@ -2973,11 +3703,11 @@ var AlertLogger = class {
2973
3703
  // src/ui/hooks/useAlerts.ts
2974
3704
  var MAX_ALERTS = 100;
2975
3705
  var useAlerts = (enabled, alertLevel, allUsers, config) => {
2976
- const [alerts, setAlerts] = useState12([]);
2977
- const engineRef = useRef5(new SecurityEngine(alertLevel));
2978
- const watcherRef = useRef5(null);
2979
- const loggerRef = useRef5(null);
2980
- useEffect7(() => {
3706
+ const [alerts, setAlerts] = useState13([]);
3707
+ const engineRef = useRef6(new SecurityEngine(alertLevel));
3708
+ const watcherRef = useRef6(null);
3709
+ const loggerRef = useRef6(null);
3710
+ useEffect8(() => {
2981
3711
  if (!enabled) return;
2982
3712
  engineRef.current = new SecurityEngine(alertLevel);
2983
3713
  if (config?.alerts.enabled) {
@@ -3007,16 +3737,16 @@ var useAlerts = (enabled, alertLevel, allUsers, config) => {
3007
3737
  watcherRef.current = null;
3008
3738
  loggerRef.current = null;
3009
3739
  };
3010
- }, [enabled, alertLevel, allUsers]);
3740
+ }, [enabled, alertLevel, allUsers, config]);
3011
3741
  const clearAlerts = () => setAlerts([]);
3012
3742
  return { alerts, clearAlerts };
3013
3743
  };
3014
3744
 
3015
3745
  // src/ui/hooks/useTextInput.ts
3016
- import { useState as useState13, useCallback as useCallback4 } from "react";
3746
+ import { useState as useState14, useCallback as useCallback4 } from "react";
3017
3747
  var useTextInput = (onConfirm, onCancel) => {
3018
- const [value, setValue] = useState13("");
3019
- const [isActive, setIsActive] = useState13(false);
3748
+ const [value, setValue] = useState14("");
3749
+ const [isActive, setIsActive] = useState14(false);
3020
3750
  const start = useCallback4((initial = "") => {
3021
3751
  setValue(initial);
3022
3752
  setIsActive(true);
@@ -3066,16 +3796,17 @@ var useTextInput = (onConfirm, onCancel) => {
3066
3796
  };
3067
3797
 
3068
3798
  // src/ui/hooks/useKeyHandler.ts
3069
- import { useInput as useInput10 } from "ink";
3799
+ import { useInput as useInput11 } from "ink";
3070
3800
  var matchKey = (binding, input, key) => {
3071
3801
  if (binding === "tab") return Boolean(key.tab);
3072
3802
  if (binding === "shift+tab") return Boolean(key.shift && key.tab);
3073
3803
  if (binding === "enter") return Boolean(key.return);
3804
+ if (binding.startsWith("ctrl+")) return Boolean(key.ctrl) && input === binding.slice(5);
3074
3805
  return input === binding;
3075
3806
  };
3076
3807
  var useKeyHandler = (deps) => {
3077
3808
  const d = deps;
3078
- useInput10((input, key) => {
3809
+ useInput11((input, key) => {
3079
3810
  if (d.showSetup || d.showSettings || d.confirmAction || d.showUpdateModal) return;
3080
3811
  if (d.inputMode === "nickname") {
3081
3812
  d.nicknameInput.handleInput(input, key);
@@ -3246,10 +3977,26 @@ var useKeyHandler = (deps) => {
3246
3977
  d.setShowSettings(true);
3247
3978
  return;
3248
3979
  }
3980
+ if (matchKey(d.kb.alertRules, input, key)) {
3981
+ d.setShowAlertRules(true);
3982
+ return;
3983
+ }
3249
3984
  if (matchKey(d.kb.viewArchive, input, key)) {
3250
3985
  d.setViewingArchive((v) => !v);
3251
3986
  return;
3252
3987
  }
3988
+ if (matchKey(d.kb.pin, input, key) && d.selectedSession) {
3989
+ d.onTogglePin(d.selectedSession.sessionId);
3990
+ return;
3991
+ }
3992
+ if (matchKey(d.kb.pinMoveUp, input, key) && d.selectedSession?.pinned) {
3993
+ d.onMovePinned(d.selectedSession.sessionId, "up");
3994
+ return;
3995
+ }
3996
+ if (matchKey(d.kb.pinMoveDown, input, key) && d.selectedSession?.pinned) {
3997
+ d.onMovePinned(d.selectedSession.sessionId, "down");
3998
+ return;
3999
+ }
3253
4000
  if (matchKey(d.kb.archive, input, key) && d.selectedSession) {
3254
4001
  if (d.viewingArchive) d.onUnarchive(d.selectedSession.sessionId);
3255
4002
  else d.onArchive(d.selectedSession.sessionId);
@@ -3308,10 +4055,10 @@ var useKeyHandler = (deps) => {
3308
4055
  };
3309
4056
 
3310
4057
  // src/ui/hooks/useUpdateChecker.ts
3311
- import { useState as useState14, useEffect as useEffect8 } from "react";
4058
+ import { useState as useState15, useEffect as useEffect9 } from "react";
3312
4059
  var useUpdateChecker = (disabled, checkOnLaunch, checkInterval) => {
3313
- const [updateInfo, setUpdateInfo] = useState14(null);
3314
- useEffect8(() => {
4060
+ const [updateInfo, setUpdateInfo] = useState15(null);
4061
+ useEffect9(() => {
3315
4062
  if (disabled || !checkOnLaunch) return;
3316
4063
  let cancelled = false;
3317
4064
  const check = () => {
@@ -3326,12 +4073,12 @@ var useUpdateChecker = (disabled, checkOnLaunch, checkInterval) => {
3326
4073
  cancelled = true;
3327
4074
  clearInterval(iv);
3328
4075
  };
3329
- }, []);
4076
+ }, [disabled, checkOnLaunch, checkInterval]);
3330
4077
  return updateInfo;
3331
4078
  };
3332
4079
 
3333
4080
  // src/ui/hooks/useSetupFlow.ts
3334
- import { useState as useState15, useCallback as useCallback5 } from "react";
4081
+ import { useState as useState16, useCallback as useCallback5 } from "react";
3335
4082
 
3336
4083
  // src/hooks/installer.ts
3337
4084
  import { existsSync, readFileSync, writeFileSync, copyFileSync, mkdirSync as mkdirSync2, chmodSync } from "fs";
@@ -3451,12 +4198,12 @@ var installMcpConfig = () => {
3451
4198
 
3452
4199
  // src/ui/hooks/useSetupFlow.ts
3453
4200
  var useSetupFlow = (initialConfig, firstRun) => {
3454
- const [liveConfig, setLiveConfig] = useState15(initialConfig);
3455
- const [showSetup, setShowSetup] = useState15(firstRun);
3456
- const [showThemePicker, setShowThemePicker] = useState15(false);
3457
- const [showTour, setShowTour] = useState15(false);
3458
- const [showSettings, setShowSettings] = useState15(false);
3459
- const [showThemeMenu, setShowThemeMenu] = useState15(false);
4201
+ const [liveConfig, setLiveConfig] = useState16(initialConfig);
4202
+ const [showSetup, setShowSetup] = useState16(firstRun);
4203
+ const [showThemePicker, setShowThemePicker] = useState16(false);
4204
+ const [showTour, setShowTour] = useState16(false);
4205
+ const [showSettings, setShowSettings] = useState16(false);
4206
+ const [showThemeMenu, setShowThemeMenu] = useState16(false);
3460
4207
  const handleSettingsClose = useCallback5((c) => {
3461
4208
  setLiveConfig(c);
3462
4209
  saveConfig(c);
@@ -3547,17 +4294,17 @@ var useSetupFlow = (initialConfig, firstRun) => {
3547
4294
  };
3548
4295
 
3549
4296
  // src/ui/hooks/useSplitPanel.ts
3550
- import { useState as useState16, useCallback as useCallback6 } from "react";
4297
+ import { useState as useState17, useCallback as useCallback6 } from "react";
3551
4298
  var useSplitPanel = () => {
3552
- const [splitMode, setSplitMode] = useState16(false);
3553
- const [leftSession, setLeftSession] = useState16(null);
3554
- const [rightSession, setRightSession] = useState16(null);
3555
- const [leftScroll, setLeftScroll] = useState16(0);
3556
- const [rightScroll, setRightScroll] = useState16(0);
3557
- const [leftFilter, setLeftFilter] = useState16("");
3558
- const [rightFilter, setRightFilter] = useState16("");
3559
- const [leftShowDetail, setLeftShowDetail] = useState16(false);
3560
- const [rightShowDetail, setRightShowDetail] = useState16(false);
4299
+ const [splitMode, setSplitMode] = useState17(false);
4300
+ const [leftSession, setLeftSession] = useState17(null);
4301
+ const [rightSession, setRightSession] = useState17(null);
4302
+ const [leftScroll, setLeftScroll] = useState17(0);
4303
+ const [rightScroll, setRightScroll] = useState17(0);
4304
+ const [leftFilter, setLeftFilter] = useState17("");
4305
+ const [rightFilter, setRightFilter] = useState17("");
4306
+ const [leftShowDetail, setLeftShowDetail] = useState17(false);
4307
+ const [rightShowDetail, setRightShowDetail] = useState17(false);
3561
4308
  const clearSplitState = useCallback6(() => {
3562
4309
  setSplitMode(false);
3563
4310
  setLeftSession(null);
@@ -3623,30 +4370,31 @@ var useSplitPanel = () => {
3623
4370
  };
3624
4371
 
3625
4372
  // src/ui/App.tsx
3626
- import { jsx as jsx17, jsxs as jsxs17 } from "react/jsx-runtime";
4373
+ import { jsx as jsx18, jsxs as jsxs18 } from "react/jsx-runtime";
3627
4374
  var App = ({ options: options2, config: initialConfig, version, firstRun }) => {
3628
4375
  const { exit } = useApp();
3629
- const { stdout } = useStdout3();
4376
+ const { stdout } = useStdout4();
3630
4377
  const termHeight = stdout?.rows ?? 40;
3631
4378
  const setup = useSetupFlow(initialConfig, firstRun);
3632
4379
  const split = useSplitPanel();
3633
4380
  const kb = setup.liveConfig.keybindings;
3634
- const [activePanel, setActivePanel] = useState17("sessions");
3635
- const [activityScroll, setActivityScroll] = useState17(0);
3636
- const [inputMode, setInputMode] = useState17("normal");
3637
- const [filter, setFilter] = useState17("");
3638
- const [activityFilter, setActivityFilter] = useState17("");
3639
- const [updateStatus, setUpdateStatus] = useState17("");
3640
- const [showDetail, setShowDetail] = useState17(false);
3641
- const [viewingArchive, setViewingArchive] = useState17(false);
3642
- const [confirmAction, setConfirmAction] = useState17(
4381
+ const [activePanel, setActivePanel] = useState18("sessions");
4382
+ const [activityScroll, setActivityScroll] = useState18(0);
4383
+ const [inputMode, setInputMode] = useState18("normal");
4384
+ const [filter, setFilter] = useState18("");
4385
+ const [activityFilter, setActivityFilter] = useState18("");
4386
+ const [updateStatus, setUpdateStatus] = useState18("");
4387
+ const [showDetail, setShowDetail] = useState18(false);
4388
+ const [viewingArchive, setViewingArchive] = useState18(false);
4389
+ const [confirmAction, setConfirmAction] = useState18(
3643
4390
  null
3644
4391
  );
3645
- const [archivedIds, setArchivedIds] = useState17(() => new Set(Object.keys(getArchived())));
3646
- const [sidebarWidth, setSidebarWidth] = useState17(() => initialConfig.sidebarWidth ?? 30);
3647
- const [selectedEventIndex, setSelectedEventIndex] = useState17(0);
3648
- const [showEventDetail, setShowEventDetail] = useState17(false);
3649
- const [showUpdateModal, setShowUpdateModal] = useState17(false);
4392
+ const [archivedIds, setArchivedIds] = useState18(() => new Set(Object.keys(getArchived())));
4393
+ const [sidebarWidth, setSidebarWidth] = useState18(() => initialConfig.sidebarWidth ?? 30);
4394
+ const [selectedEventIndex, setSelectedEventIndex] = useState18(0);
4395
+ const [showEventDetail, setShowEventDetail] = useState18(false);
4396
+ const [showUpdateModal, setShowUpdateModal] = useState18(false);
4397
+ const [showAlertRules, setShowAlertRules] = useState18(false);
3650
4398
  const refreshArchived = useCallback7(() => setArchivedIds(new Set(Object.keys(getArchived()))), []);
3651
4399
  const persistSidebarWidth = useCallback7((v) => {
3652
4400
  setSidebarWidth((prev) => {
@@ -3664,10 +4412,10 @@ var App = ({ options: options2, config: initialConfig, version, firstRun }) => {
3664
4412
  setup.liveConfig.updates.checkOnLaunch,
3665
4413
  setup.liveConfig.updates.checkInterval
3666
4414
  );
3667
- useEffect9(() => {
4415
+ useEffect10(() => {
3668
4416
  applyTheme(resolveTheme(setup.liveConfig.theme, setup.liveConfig.customThemes));
3669
4417
  }, [setup.liveConfig.theme, setup.liveConfig.customThemes]);
3670
- useEffect9(() => {
4418
+ useEffect10(() => {
3671
4419
  if (updateInfo?.available && setup.liveConfig.prompts.autoUpdate === "pending") {
3672
4420
  setShowUpdateModal(true);
3673
4421
  }
@@ -3717,16 +4465,16 @@ var App = ({ options: options2, config: initialConfig, version, firstRun }) => {
3717
4465
  setInputMode("normal");
3718
4466
  }
3719
4467
  );
3720
- useEffect9(() => {
4468
+ useEffect10(() => {
3721
4469
  purgeExpiredArchives();
3722
4470
  refreshArchived();
3723
4471
  }, []);
3724
- useEffect9(() => {
4472
+ useEffect10(() => {
3725
4473
  setActivityScroll(0);
3726
4474
  setSelectedEventIndex(0);
3727
4475
  setShowEventDetail(false);
3728
4476
  }, [selectedSession?.sessionId, selectedGroup?.key]);
3729
- useEffect9(() => {
4477
+ useEffect10(() => {
3730
4478
  const totalEvents = events.length;
3731
4479
  const vRows = termHeight - 3 - (options2.noSecurity ? 0 : 6) - 1 - (inputMode !== "normal" ? 1 : 0) - 2;
3732
4480
  if (vRows <= 0 || totalEvents === 0) return;
@@ -3738,7 +4486,7 @@ var App = ({ options: options2, config: initialConfig, version, firstRun }) => {
3738
4486
  setActivityScroll(totalEvents - vRows - (selectedEventIndex - vRows + 1));
3739
4487
  }
3740
4488
  }, [selectedEventIndex, events.length]);
3741
- useEffect9(() => {
4489
+ useEffect10(() => {
3742
4490
  if (events.length > 0 && selectedEventIndex >= events.length) {
3743
4491
  setSelectedEventIndex(events.length - 1);
3744
4492
  }
@@ -3760,13 +4508,32 @@ var App = ({ options: options2, config: initialConfig, version, firstRun }) => {
3760
4508
  if (activePanel === "right") return split.rightFilter;
3761
4509
  return activityFilter;
3762
4510
  }, [activePanel, filter, split.leftFilter, split.rightFilter, activityFilter]);
4511
+ const handleTogglePin = useCallback7(
4512
+ (sessionId) => {
4513
+ const cfg = loadConfig();
4514
+ if (cfg.pinnedSessions.includes(sessionId)) {
4515
+ unpinSession(sessionId);
4516
+ } else {
4517
+ pinSession(sessionId);
4518
+ }
4519
+ refresh();
4520
+ },
4521
+ [refresh]
4522
+ );
4523
+ const handleMovePinned = useCallback7(
4524
+ (sessionId, dir) => {
4525
+ movePinned(sessionId, dir);
4526
+ refresh();
4527
+ },
4528
+ [refresh]
4529
+ );
3763
4530
  useKeyHandler({
3764
4531
  kb,
3765
4532
  activePanel,
3766
4533
  splitMode: split.splitMode,
3767
4534
  inputMode,
3768
4535
  showSetup: setup.showSetup,
3769
- showSettings: setup.showSettings || setup.showThemeMenu || setup.showThemePicker || setup.showTour,
4536
+ showSettings: setup.showSettings || setup.showThemeMenu || setup.showThemePicker || setup.showTour || showAlertRules,
3770
4537
  showDetail,
3771
4538
  showEventDetail,
3772
4539
  leftShowDetail: split.leftShowDetail,
@@ -3810,6 +4577,8 @@ var App = ({ options: options2, config: initialConfig, version, firstRun }) => {
3810
4577
  setLeftShowDetail: split.setLeftShowDetail,
3811
4578
  setRightShowDetail: split.setRightShowDetail,
3812
4579
  setShowSettings: setup.setShowSettings,
4580
+ showAlertRules,
4581
+ setShowAlertRules,
3813
4582
  setViewingArchive,
3814
4583
  setSplitMode: split.setSplitMode,
3815
4584
  setLeftSession: split.setLeftSession,
@@ -3824,14 +4593,12 @@ var App = ({ options: options2, config: initialConfig, version, firstRun }) => {
3824
4593
  setSidebarWidth: persistSidebarWidth,
3825
4594
  nicknameInput,
3826
4595
  filterInput,
3827
- onNickname: (id) => {
3828
- clearNickname(id);
3829
- refresh();
3830
- },
3831
4596
  onClearNickname: (id) => {
3832
4597
  clearNickname(id);
3833
4598
  refresh();
3834
4599
  },
4600
+ onTogglePin: handleTogglePin,
4601
+ onMovePinned: handleMovePinned,
3835
4602
  onArchive: (id) => {
3836
4603
  archiveSession(id);
3837
4604
  refreshArchived();
@@ -3883,10 +4650,10 @@ var App = ({ options: options2, config: initialConfig, version, firstRun }) => {
3883
4650
  setup.setShowSetup(false);
3884
4651
  return null;
3885
4652
  }
3886
- return /* @__PURE__ */ jsx17(SetupModal, { steps, onComplete: setup.handleSetupComplete });
4653
+ return /* @__PURE__ */ jsx18(SetupModal, { steps, onComplete: setup.handleSetupComplete });
3887
4654
  }
3888
4655
  if (setup.showThemePicker)
3889
- return /* @__PURE__ */ jsx17(
4656
+ return /* @__PURE__ */ jsx18(
3890
4657
  ThemePickerModal,
3891
4658
  {
3892
4659
  onSelect: setup.handleThemePickerSelect,
@@ -3894,10 +4661,22 @@ var App = ({ options: options2, config: initialConfig, version, firstRun }) => {
3894
4661
  onDismiss: setup.handleThemePickerDismiss
3895
4662
  }
3896
4663
  );
3897
- if (setup.showTour) return /* @__PURE__ */ jsx17(GuidedTour, { onComplete: setup.handleTourComplete, onSkip: setup.handleTourSkip });
3898
- if (setup.showThemeMenu) return /* @__PURE__ */ jsx17(ThemeMenu, { config: setup.liveConfig, onClose: setup.handleThemeMenuClose });
4664
+ if (setup.showTour) return /* @__PURE__ */ jsx18(GuidedTour, { onComplete: setup.handleTourComplete, onSkip: setup.handleTourSkip });
4665
+ if (setup.showThemeMenu) return /* @__PURE__ */ jsx18(ThemeMenu, { config: setup.liveConfig, onClose: setup.handleThemeMenuClose });
4666
+ if (showAlertRules)
4667
+ return /* @__PURE__ */ jsx18(
4668
+ AlertRulesMenu,
4669
+ {
4670
+ config: setup.liveConfig,
4671
+ onClose: () => setShowAlertRules(false),
4672
+ onSave: (newConfig) => {
4673
+ saveConfig(newConfig);
4674
+ setup.setLiveConfig(newConfig);
4675
+ }
4676
+ }
4677
+ );
3899
4678
  if (setup.showSettings)
3900
- return /* @__PURE__ */ jsx17(
4679
+ return /* @__PURE__ */ jsx18(
3901
4680
  SettingsMenu,
3902
4681
  {
3903
4682
  config: setup.liveConfig,
@@ -3906,7 +4685,7 @@ var App = ({ options: options2, config: initialConfig, version, firstRun }) => {
3906
4685
  }
3907
4686
  );
3908
4687
  if (showUpdateModal && updateInfo?.available) {
3909
- return /* @__PURE__ */ jsx17(Box17, { flexDirection: "column", height: termHeight, justifyContent: "center", alignItems: "center", children: /* @__PURE__ */ jsx17(
4688
+ return /* @__PURE__ */ jsx18(Box18, { flexDirection: "column", height: termHeight, justifyContent: "center", alignItems: "center", children: /* @__PURE__ */ jsx18(
3910
4689
  UpdateModal,
3911
4690
  {
3912
4691
  current: updateInfo.current,
@@ -3923,7 +4702,7 @@ var App = ({ options: options2, config: initialConfig, version, firstRun }) => {
3923
4702
  ) });
3924
4703
  }
3925
4704
  if (confirmAction) {
3926
- return /* @__PURE__ */ jsx17(Box17, { flexDirection: "column", height: termHeight, justifyContent: "center", alignItems: "center", children: /* @__PURE__ */ jsx17(
4705
+ return /* @__PURE__ */ jsx18(Box18, { flexDirection: "column", height: termHeight, justifyContent: "center", alignItems: "center", children: /* @__PURE__ */ jsx18(
3927
4706
  ConfirmModal,
3928
4707
  {
3929
4708
  title: confirmAction.title,
@@ -3936,7 +4715,7 @@ var App = ({ options: options2, config: initialConfig, version, firstRun }) => {
3936
4715
  const activitySlug = selectedGroup ? selectedGroup.key : selectedSession?.slug ?? null;
3937
4716
  const isMerged = selectedGroup !== null;
3938
4717
  const selectedEvent = events[selectedEventIndex];
3939
- const rightPanel = split.splitMode ? /* @__PURE__ */ jsx17(
4718
+ const rightPanel = split.splitMode ? /* @__PURE__ */ jsx18(
3940
4719
  SplitPanel,
3941
4720
  {
3942
4721
  activePanel,
@@ -3952,13 +4731,13 @@ var App = ({ options: options2, config: initialConfig, version, firstRun }) => {
3952
4731
  rightShowDetail: split.rightShowDetail,
3953
4732
  height: mainHeight
3954
4733
  }
3955
- ) : showEventDetail && selectedEvent ? /* @__PURE__ */ jsx17(ToolCallDetail, { event: selectedEvent, focused: activePanel === "activity", height: mainHeight }) : showDetail && selectedSession ? /* @__PURE__ */ jsx17(SessionDetail, { session: selectedSession, focused: activePanel === "activity", height: mainHeight }) : /* @__PURE__ */ jsx17(
4734
+ ) : showEventDetail && selectedEvent ? /* @__PURE__ */ jsx18(ToolCallDetail, { event: selectedEvent, focused: activePanel === "activity", height: mainHeight }) : showDetail && selectedSession ? /* @__PURE__ */ jsx18(SessionDetail, { session: selectedSession, focused: activePanel === "activity", height: mainHeight }) : /* @__PURE__ */ jsx18(
3956
4735
  ActivityFeed,
3957
4736
  {
3958
4737
  events,
3959
4738
  sessionSlug: activitySlug,
3960
4739
  sessionId: selectedSession?.sessionId,
3961
- isActive: selectedGroup ? selectedGroup.isActive : selectedSession ? selectedSession.pid !== null : void 0,
4740
+ status: selectedGroup ? selectedGroup.status : selectedSession ? selectedSession.status : void 0,
3962
4741
  focused: activePanel === "activity",
3963
4742
  height: mainHeight,
3964
4743
  scrollOffset: activityScroll,
@@ -3968,10 +4747,10 @@ var App = ({ options: options2, config: initialConfig, version, firstRun }) => {
3968
4747
  selectedEventIndex
3969
4748
  }
3970
4749
  );
3971
- return /* @__PURE__ */ jsxs17(Box17, { flexDirection: "column", height: termHeight, children: [
3972
- /* @__PURE__ */ jsx17(StatusBar, { sessionCount: sessions2.length, alertCount: alerts.length, version, updateInfo }),
3973
- /* @__PURE__ */ jsxs17(Box17, { flexGrow: 1, height: mainHeight, children: [
3974
- /* @__PURE__ */ jsx17(
4750
+ return /* @__PURE__ */ jsxs18(Box18, { flexDirection: "column", height: termHeight, children: [
4751
+ /* @__PURE__ */ jsx18(StatusBar, { sessionCount: sessions2.length, alertCount: alerts.length, version, updateInfo }),
4752
+ /* @__PURE__ */ jsxs18(Box18, { flexGrow: 1, height: mainHeight, children: [
4753
+ /* @__PURE__ */ jsx18(
3975
4754
  SessionList,
3976
4755
  {
3977
4756
  visibleItems,
@@ -3984,21 +4763,21 @@ var App = ({ options: options2, config: initialConfig, version, firstRun }) => {
3984
4763
  sidebarWidth
3985
4764
  }
3986
4765
  ),
3987
- /* @__PURE__ */ jsx17(Box17, { flexGrow: 1, flexShrink: 1, minWidth: 0, overflow: "hidden", children: rightPanel })
4766
+ /* @__PURE__ */ jsx18(Box18, { flexGrow: 1, flexShrink: 1, minWidth: 0, overflow: "hidden", children: rightPanel })
3988
4767
  ] }),
3989
- !options2.noSecurity && /* @__PURE__ */ jsx17(AlertBar, { alerts }),
3990
- inputMode === "nickname" && /* @__PURE__ */ jsxs17(Box17, { paddingX: 1, children: [
3991
- /* @__PURE__ */ jsx17(Text16, { color: colors.primary, children: "nickname: " }),
3992
- /* @__PURE__ */ jsx17(Text16, { color: colors.bright, children: nicknameInput.value }),
3993
- /* @__PURE__ */ jsx17(Text16, { color: colors.muted, children: "_" })
4768
+ !options2.noSecurity && /* @__PURE__ */ jsx18(AlertBar, { alerts }),
4769
+ inputMode === "nickname" && /* @__PURE__ */ jsxs18(Box18, { paddingX: 1, children: [
4770
+ /* @__PURE__ */ jsx18(Text17, { color: colors.primary, children: "nickname: " }),
4771
+ /* @__PURE__ */ jsx18(Text17, { color: colors.bright, children: nicknameInput.value }),
4772
+ /* @__PURE__ */ jsx18(Text17, { color: colors.muted, children: "_" })
3994
4773
  ] }),
3995
- inputMode === "filter" && /* @__PURE__ */ jsxs17(Box17, { paddingX: 1, children: [
3996
- /* @__PURE__ */ jsx17(Text16, { color: colors.muted, children: activePanel === "sessions" ? "sessions" : activePanel === "left" ? "left" : activePanel === "right" ? "right" : "activity" }),
3997
- /* @__PURE__ */ jsx17(Text16, { color: colors.primary, children: "/" }),
3998
- /* @__PURE__ */ jsx17(Text16, { color: colors.bright, children: filterInput.value }),
3999
- /* @__PURE__ */ jsx17(Text16, { color: colors.muted, children: "_" })
4774
+ inputMode === "filter" && /* @__PURE__ */ jsxs18(Box18, { paddingX: 1, children: [
4775
+ /* @__PURE__ */ jsx18(Text17, { color: colors.muted, children: activePanel === "sessions" ? "sessions" : activePanel === "left" ? "left" : activePanel === "right" ? "right" : "activity" }),
4776
+ /* @__PURE__ */ jsx18(Text17, { color: colors.primary, children: "/" }),
4777
+ /* @__PURE__ */ jsx18(Text17, { color: colors.bright, children: filterInput.value }),
4778
+ /* @__PURE__ */ jsx18(Text17, { color: colors.muted, children: "_" })
4000
4779
  ] }),
4001
- inputMode === "normal" && /* @__PURE__ */ jsx17(
4780
+ inputMode === "normal" && /* @__PURE__ */ jsx18(
4002
4781
  FooterBar,
4003
4782
  {
4004
4783
  keybindings: kb,
@@ -4014,11 +4793,6 @@ var App = ({ options: options2, config: initialConfig, version, firstRun }) => {
4014
4793
  var write = (msg) => {
4015
4794
  process.stdout.write(msg + "\n");
4016
4795
  };
4017
- var formatTokens3 = (n) => {
4018
- if (n >= 1e6) return (n / 1e6).toFixed(1) + "M";
4019
- if (n >= 1e3) return (n / 1e3).toFixed(1) + "k";
4020
- return String(n);
4021
- };
4022
4796
  var runStreamMode = (options2, isJson) => {
4023
4797
  const engine = options2.noSecurity ? null : new SecurityEngine(options2.alertLevel);
4024
4798
  const sessions2 = discoverSessions(options2.allUsers);
@@ -4027,7 +4801,7 @@ var runStreamMode = (options2, isJson) => {
4027
4801
  } else {
4028
4802
  for (const s of sessions2) {
4029
4803
  write(
4030
- `SESSION ${s.slug} | ${s.model} | ${s.cwd} | CPU ${s.cpu}% | ${s.memMB}MB | ${formatTokens3(s.usage.inputTokens)} in / ${formatTokens3(s.usage.outputTokens)} out`
4804
+ `SESSION ${s.slug} | ${s.status} | ${s.model} | ${s.cwd} | CPU ${s.cpu}% | ${s.memMB}MB | ${formatTokens(s.usage.inputTokens)} in / ${formatTokens(s.usage.outputTokens)} out`
4031
4805
  );
4032
4806
  }
4033
4807
  }
@@ -4060,7 +4834,7 @@ var runStreamMode = (options2, isJson) => {
4060
4834
  write(JSON.stringify({ type: "usage", data: { sessionId, usage } }));
4061
4835
  } else {
4062
4836
  write(
4063
- `USAGE ${sessionId.slice(0, 12)} +${formatTokens3(usage.inputTokens)} in / +${formatTokens3(usage.outputTokens)} out`
4837
+ `USAGE ${sessionId.slice(0, 12)} +${formatTokens(usage.inputTokens)} in / +${formatTokens(usage.outputTokens)} out`
4064
4838
  );
4065
4839
  }
4066
4840
  };
@@ -4244,7 +5018,7 @@ var main = () => {
4244
5018
  if (options2.noUpdates) config.updates.checkOnLaunch = false;
4245
5019
  if (options2.noSecurity) config.security.enabled = false;
4246
5020
  if (firstRun) saveConfig(config);
4247
- render(React18.createElement(App, { options: options2, config, version: VERSION, firstRun }));
5021
+ render(React19.createElement(App, { options: options2, config, version: VERSION, firstRun }));
4248
5022
  };
4249
5023
  main();
4250
5024
  //# sourceMappingURL=index.js.map