agenttop 0.10.6 → 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-24HX2MSZ.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 React17 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 useState16, useEffect as useEffect9, useCallback as useCallback6 } from "react";
33
- import { Box as Box16, Text as Text15, 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 } 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 {
@@ -397,6 +657,14 @@ var installUpdate = () => {
397
657
  });
398
658
  });
399
659
  };
660
+ var restartProcess = () => {
661
+ const child = spawn(process.argv[0], process.argv.slice(1), {
662
+ detached: true,
663
+ stdio: "inherit"
664
+ });
665
+ child.unref();
666
+ process.exit(0);
667
+ };
400
668
  var compareVersions = (a, b) => {
401
669
  const pa = a.split(".").map(Number);
402
670
  const pb = b.split(".").map(Number);
@@ -421,12 +689,15 @@ var colors = {
421
689
  warning: "#E5C07B",
422
690
  error: "#E06C75",
423
691
  critical: "#FF0000",
692
+ success: "#98C379",
424
693
  muted: "#5C6370",
425
694
  text: "#ABB2BF",
426
695
  bright: "#FFFFFF",
427
696
  border: "#3E4451",
428
697
  selected: "#2C313A",
429
- header: "#61AFEF"
698
+ header: "#61AFEF",
699
+ waiting: "#E5C07B",
700
+ stale: "#D19A66"
430
701
  };
431
702
  var severityColors = {
432
703
  info: colors.muted,
@@ -448,6 +719,7 @@ var toolColors = {
448
719
  var getToolColor = (toolName) => toolColors[toolName] || colors.text;
449
720
  var applyTheme = (theme) => {
450
721
  Object.assign(colors, theme.colors);
722
+ colors.success = theme.colors.secondary;
451
723
  Object.assign(toolColors, theme.toolColors);
452
724
  Object.assign(severityColors, deriveSeverityColors(theme.colors));
453
725
  };
@@ -488,18 +760,32 @@ var StatusBar = React.memo(({ sessionCount, alertCount, version, updateInfo }) =
488
760
  // src/ui/components/SessionList.tsx
489
761
  import React2 from "react";
490
762
  import { Box as Box2, Text as Text2 } from "ink";
491
- 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
+ };
492
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) => {
493
781
  if (model.includes("opus")) return "opus";
494
782
  if (model.includes("sonnet")) return "son";
495
783
  if (model.includes("haiku")) return "hai";
496
784
  return model.slice(0, 4);
497
785
  };
498
- var formatTokens = (n) => {
499
- if (n >= 1e6) return (n / 1e6).toFixed(1) + "M";
500
- if (n >= 1e3) return (n / 1e3).toFixed(1) + "k";
501
- return String(n);
502
- };
786
+
787
+ // src/ui/components/SessionList.tsx
788
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
503
789
  var truncate = (s, max) => s.length > max ? s.slice(0, max - 1) + "\u2026" : s;
504
790
  var getDisplayName = (s) => {
505
791
  if (s.nickname) return s.nickname;
@@ -559,11 +845,14 @@ var SessionList = React2.memo(
559
845
  if (item.type === "group") {
560
846
  const g = item.group;
561
847
  const arrow = g.expanded ? "\u25BE" : "\u25B8";
562
- const dotColor2 = g.isActive ? colors.success : colors.muted;
563
- const statusDot2 = g.isActive ? "\u25CF" : "\u25CB";
564
- const nameColor2 = isSelected ? colors.bright : g.isActive ? colors.secondary : colors.text;
565
- const label2 = truncate(`${g.key} (${g.sessions.length})`, INNER_WIDTH - 4);
566
- 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);
567
856
  return /* @__PURE__ */ jsxs2(
568
857
  Box2,
569
858
  {
@@ -576,6 +865,7 @@ var SessionList = React2.memo(
576
865
  " ",
577
866
  /* @__PURE__ */ jsx2(Text2, { color: dotColor2, children: statusDot2 }),
578
867
  " ",
868
+ pinMarker2,
579
869
  label2
580
870
  ] }),
581
871
  /* @__PURE__ */ jsxs2(Text2, { color: isSelected ? colors.text : colors.muted, wrap: "truncate", children: [
@@ -594,14 +884,15 @@ var SessionList = React2.memo(
594
884
  );
595
885
  }
596
886
  const session = item.type === "session" ? item.session : item.session;
597
- const isActive = session.pid !== null;
598
- const statusDot = isActive ? "\u25CF" : "\u25CB";
599
- 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]" : "";
600
891
  const totalIn = session.usage.inputTokens + session.usage.cacheReadTokens;
601
- const model = formatModel(session.model);
892
+ const model = formatModelShort(session.model);
602
893
  if (item.type === "session") {
603
- const nameColor2 = isSelected ? colors.bright : isActive ? colors.secondary : colors.muted;
604
- 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);
605
896
  return /* @__PURE__ */ jsxs2(
606
897
  Box2,
607
898
  {
@@ -614,6 +905,7 @@ var SessionList = React2.memo(
614
905
  " ",
615
906
  /* @__PURE__ */ jsx2(Text2, { color: dotColor, children: statusDot }),
616
907
  " ",
908
+ pinMarker,
617
909
  displayName2
618
910
  ] }),
619
911
  /* @__PURE__ */ jsxs2(Text2, { color: isSelected ? colors.text : colors.muted, wrap: "truncate", children: [
@@ -632,8 +924,8 @@ var SessionList = React2.memo(
632
924
  );
633
925
  }
634
926
  const indicator = isSelected ? "\u25B8" : " ";
635
- const nameColor = isSelected ? colors.bright : isActive ? colors.secondary : colors.text;
636
- 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);
637
929
  return /* @__PURE__ */ jsxs2(
638
930
  Box2,
639
931
  {
@@ -646,6 +938,7 @@ var SessionList = React2.memo(
646
938
  " ",
647
939
  /* @__PURE__ */ jsx2(Text2, { color: dotColor, children: statusDot }),
648
940
  " ",
941
+ pinMarker,
649
942
  displayName
650
943
  ] }),
651
944
  /* @__PURE__ */ jsxs2(Text2, { color: isSelected ? colors.text : colors.muted, wrap: "truncate", children: [
@@ -674,10 +967,6 @@ import React3 from "react";
674
967
  import { Box as Box3, Text as Text3 } from "ink";
675
968
  import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
676
969
  var TAG_COLORS = ["#61AFEF", "#98C379", "#C678DD", "#E5C07B", "#E06C75", "#56B6C2", "#D19A66", "#BE5046"];
677
- var formatTime = (ts) => {
678
- const d = new Date(ts);
679
- return d.toLocaleTimeString("en-GB", { hour12: false });
680
- };
681
970
  var summarizeInput = (call) => {
682
971
  const input = call.toolInput;
683
972
  switch (call.toolName) {
@@ -705,7 +994,7 @@ var ActivityFeed = React3.memo(
705
994
  events,
706
995
  sessionSlug,
707
996
  sessionId,
708
- isActive,
997
+ status,
709
998
  focused,
710
999
  height,
711
1000
  scrollOffset,
@@ -782,7 +1071,7 @@ var ActivityFeed = React3.memo(
782
1071
  ] }) }, `${call.timestamp}-${i}`);
783
1072
  }),
784
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 " }) }),
785
- !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: [
786
1075
  /* @__PURE__ */ jsx3(Text3, { color: colors.muted, children: " " }),
787
1076
  /* @__PURE__ */ jsxs3(Text3, { color: colors.muted, children: [
788
1077
  "resume: ",
@@ -807,10 +1096,6 @@ var ActivityFeed = React3.memo(
807
1096
  import React4 from "react";
808
1097
  import { Box as Box4, Text as Text4 } from "ink";
809
1098
  import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
810
- var formatTime2 = (ts) => {
811
- const d = new Date(ts);
812
- return d.toLocaleTimeString("en-GB", { hour12: false });
813
- };
814
1099
  var severityIcon = {
815
1100
  info: "i",
816
1101
  warn: "!",
@@ -832,7 +1117,7 @@ var AlertBar = React4.memo(({ alerts, maxVisible = 4 }) => {
832
1117
  ] }),
833
1118
  /* @__PURE__ */ jsxs4(Text4, { color: colors.muted, children: [
834
1119
  " ",
835
- formatTime2(alert.timestamp),
1120
+ formatTime(alert.timestamp),
836
1121
  " "
837
1122
  ] }),
838
1123
  /* @__PURE__ */ jsxs4(Text4, { color: colors.warning, children: [
@@ -848,11 +1133,6 @@ var AlertBar = React4.memo(({ alerts, maxVisible = 4 }) => {
848
1133
  import React5 from "react";
849
1134
  import { Box as Box5, Text as Text5 } from "ink";
850
1135
  import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
851
- var formatTokens2 = (n) => {
852
- if (n >= 1e6) return (n / 1e6).toFixed(1) + "M";
853
- if (n >= 1e3) return (n / 1e3).toFixed(1) + "k";
854
- return String(n);
855
- };
856
1136
  var formatUptime = (startTime) => {
857
1137
  const ms = Date.now() - startTime;
858
1138
  const secs = Math.floor(ms / 1e3);
@@ -862,12 +1142,6 @@ var formatUptime = (startTime) => {
862
1142
  if (mins > 0) return `${mins}m ${secs % 60}s`;
863
1143
  return `${secs}s`;
864
1144
  };
865
- var formatModel2 = (model) => {
866
- if (model.includes("opus")) return "opus";
867
- if (model.includes("sonnet")) return "sonnet";
868
- if (model.includes("haiku")) return "haiku";
869
- return model.slice(0, 20);
870
- };
871
1145
  var SessionDetail = React5.memo(({ session, focused }) => {
872
1146
  const totalInput = session.usage.inputTokens + session.usage.cacheCreationTokens + session.usage.cacheReadTokens;
873
1147
  const totalTokens = totalInput + session.usage.outputTokens;
@@ -895,7 +1169,7 @@ var SessionDetail = React5.memo(({ session, focused }) => {
895
1169
  ] }),
896
1170
  /* @__PURE__ */ jsxs5(Box5, { children: [
897
1171
  /* @__PURE__ */ jsx5(Text5, { color: colors.muted, children: "model: " }),
898
- /* @__PURE__ */ jsx5(Text5, { color: colors.text, children: formatModel2(session.model) })
1172
+ /* @__PURE__ */ jsx5(Text5, { color: colors.text, children: formatModel(session.model) })
899
1173
  ] }),
900
1174
  /* @__PURE__ */ jsxs5(Box5, { children: [
901
1175
  /* @__PURE__ */ jsx5(Text5, { color: colors.muted, children: "cwd: " }),
@@ -938,19 +1212,19 @@ var SessionDetail = React5.memo(({ session, focused }) => {
938
1212
  /* @__PURE__ */ jsx5(Box5, { marginTop: 1, children: /* @__PURE__ */ jsx5(Text5, { color: colors.header, bold: true, children: "Token usage" }) }),
939
1213
  /* @__PURE__ */ jsxs5(Box5, { children: [
940
1214
  /* @__PURE__ */ jsx5(Text5, { color: colors.muted, children: " input: " }),
941
- /* @__PURE__ */ jsx5(Text5, { color: colors.text, children: formatTokens2(session.usage.inputTokens) })
1215
+ /* @__PURE__ */ jsx5(Text5, { color: colors.text, children: formatTokens(session.usage.inputTokens) })
942
1216
  ] }),
943
1217
  /* @__PURE__ */ jsxs5(Box5, { children: [
944
1218
  /* @__PURE__ */ jsx5(Text5, { color: colors.muted, children: " output: " }),
945
- /* @__PURE__ */ jsx5(Text5, { color: colors.text, children: formatTokens2(session.usage.outputTokens) })
1219
+ /* @__PURE__ */ jsx5(Text5, { color: colors.text, children: formatTokens(session.usage.outputTokens) })
946
1220
  ] }),
947
1221
  /* @__PURE__ */ jsxs5(Box5, { children: [
948
1222
  /* @__PURE__ */ jsx5(Text5, { color: colors.muted, children: " cache write: " }),
949
- /* @__PURE__ */ jsx5(Text5, { color: colors.text, children: formatTokens2(session.usage.cacheCreationTokens) })
1223
+ /* @__PURE__ */ jsx5(Text5, { color: colors.text, children: formatTokens(session.usage.cacheCreationTokens) })
950
1224
  ] }),
951
1225
  /* @__PURE__ */ jsxs5(Box5, { children: [
952
1226
  /* @__PURE__ */ jsx5(Text5, { color: colors.muted, children: " cache read: " }),
953
- /* @__PURE__ */ jsx5(Text5, { color: colors.text, children: formatTokens2(session.usage.cacheReadTokens) })
1227
+ /* @__PURE__ */ jsx5(Text5, { color: colors.text, children: formatTokens(session.usage.cacheReadTokens) })
954
1228
  ] }),
955
1229
  /* @__PURE__ */ jsxs5(Box5, { children: [
956
1230
  /* @__PURE__ */ jsx5(Text5, { color: colors.muted, children: " cache hit: " }),
@@ -961,7 +1235,7 @@ var SessionDetail = React5.memo(({ session, focused }) => {
961
1235
  ] }),
962
1236
  /* @__PURE__ */ jsxs5(Box5, { children: [
963
1237
  /* @__PURE__ */ jsx5(Text5, { color: colors.muted, children: " total: " }),
964
- /* @__PURE__ */ jsx5(Text5, { color: colors.bright, bold: true, children: formatTokens2(totalTokens) })
1238
+ /* @__PURE__ */ jsx5(Text5, { color: colors.bright, bold: true, children: formatTokens(totalTokens) })
965
1239
  ] })
966
1240
  ] })
967
1241
  ]
@@ -1101,10 +1375,6 @@ var FooterBar = React7.memo(
1101
1375
  import React8, { useState as useState3 } from "react";
1102
1376
  import { Box as Box8, Text as Text8, useInput as useInput2 } from "ink";
1103
1377
  import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
1104
- var formatTime3 = (ts) => {
1105
- const d = new Date(ts);
1106
- return d.toLocaleTimeString("en-GB", { hour12: false });
1107
- };
1108
1378
  var renderBash = (event) => {
1109
1379
  const lines = [];
1110
1380
  const cmd = String(event.call.toolInput.command || "");
@@ -1278,7 +1548,7 @@ var ToolCallDetail = React8.memo(({ event, focused, height }) => {
1278
1548
  /* @__PURE__ */ jsx8(Text8, { color: getToolColor(event.call.toolName), bold: true, children: event.call.toolName }),
1279
1549
  /* @__PURE__ */ jsxs8(Text8, { color: colors.muted, children: [
1280
1550
  " ",
1281
- formatTime3(event.call.timestamp)
1551
+ formatTime(event.call.timestamp)
1282
1552
  ] }),
1283
1553
  /* @__PURE__ */ jsxs8(Text8, { color: colors.muted, children: [
1284
1554
  " ",
@@ -1962,23 +2232,451 @@ var ThemeMenu = React11.memo(({ config, onClose }) => {
1962
2232
  /* @__PURE__ */ jsx11(Text11, { color: theme.colors.error, children: "\u2588" })
1963
2233
  ] }, theme.name);
1964
2234
  }),
1965
- 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)." }) })
1966
2237
  ]
1967
2238
  }
1968
2239
  ) });
1969
2240
  });
1970
2241
 
1971
- // src/ui/components/ThemePickerModal.tsx
1972
- import React12, { useState as useState7, useEffect as useEffect4 } from "react";
1973
- 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";
1974
2245
  import { jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
1975
- var ThemePickerModal = React12.memo(({ onSelect, onSkip, onDismiss }) => {
1976
- 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);
1977
2675
  const themes = BUILTIN_THEMES;
1978
- useEffect4(() => {
2676
+ useEffect5(() => {
1979
2677
  applyTheme(themes[selectedIndex]);
1980
2678
  }, [selectedIndex]);
1981
- useInput6((input, key) => {
2679
+ useInput7((input, key) => {
1982
2680
  if (key.upArrow) setSelectedIndex((i) => Math.max(0, i - 1));
1983
2681
  if (key.downArrow) setSelectedIndex((i) => Math.min(themes.length - 1, i + 1));
1984
2682
  if (key.return) onSelect(themes[selectedIndex].name);
@@ -1987,33 +2685,33 @@ var ThemePickerModal = React12.memo(({ onSelect, onSkip, onDismiss }) => {
1987
2685
  });
1988
2686
  const renderSwatch = (theme) => {
1989
2687
  const c = theme.colors;
1990
- return /* @__PURE__ */ jsxs12(Text12, { children: [
1991
- /* @__PURE__ */ jsx12(Text12, { color: c.primary, children: "\u2588" }),
1992
- /* @__PURE__ */ jsx12(Text12, { color: c.secondary, children: "\u2588" }),
1993
- /* @__PURE__ */ jsx12(Text12, { color: c.accent, children: "\u2588" }),
1994
- /* @__PURE__ */ jsx12(Text12, { color: c.warning, children: "\u2588" }),
1995
- /* @__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" })
1996
2694
  ] });
1997
2695
  };
1998
- 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: [
1999
- /* @__PURE__ */ jsx12(Box12, { marginBottom: 1, children: /* @__PURE__ */ jsx12(Text12, { color: colors.header, bold: true, children: "Choose a theme" }) }),
2000
- /* @__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)." }) }),
2001
- themes.map((theme, i) => /* @__PURE__ */ jsxs12(Box12, { children: [
2002
- /* @__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 ? "> " : " " }),
2003
2701
  renderSwatch(theme),
2004
- /* @__PURE__ */ jsxs12(Text12, { color: i === selectedIndex ? colors.bright : colors.text, children: [
2702
+ /* @__PURE__ */ jsxs13(Text13, { color: i === selectedIndex ? colors.bright : colors.text, children: [
2005
2703
  " ",
2006
2704
  theme.name
2007
2705
  ] })
2008
2706
  ] }, theme.name)),
2009
- /* @__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" }) })
2010
2708
  ] }) });
2011
2709
  });
2012
2710
 
2013
2711
  // src/ui/components/GuidedTour.tsx
2014
- import React13, { useState as useState8 } from "react";
2015
- import { Box as Box13, Text as Text13, useInput as useInput7 } from "ink";
2016
- 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";
2017
2715
  var STEPS = [
2018
2716
  {
2019
2717
  title: "Session list",
@@ -2044,11 +2742,11 @@ var STEPS = [
2044
2742
  body: "Press s to open settings. Customise keybindings, manage themes, and configure updates."
2045
2743
  }
2046
2744
  ];
2047
- var GuidedTour = React13.memo(({ onComplete, onSkip }) => {
2048
- const [stepIndex, setStepIndex] = useState8(0);
2745
+ var GuidedTour = React14.memo(({ onComplete, onSkip }) => {
2746
+ const [stepIndex, setStepIndex] = useState9(0);
2049
2747
  const step = STEPS[stepIndex];
2050
2748
  const isLast = stepIndex === STEPS.length - 1;
2051
- useInput7((input, key) => {
2749
+ useInput8((input, key) => {
2052
2750
  if (key.return || key.rightArrow) {
2053
2751
  if (isLast) onComplete();
2054
2752
  else setStepIndex((i) => i + 1);
@@ -2056,17 +2754,17 @@ var GuidedTour = React13.memo(({ onComplete, onSkip }) => {
2056
2754
  if (key.leftArrow && stepIndex > 0) setStepIndex((i) => i - 1);
2057
2755
  if (input === "q" || key.escape) onSkip();
2058
2756
  });
2059
- 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: [
2060
- /* @__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: [
2061
2759
  "Quick tour (",
2062
2760
  stepIndex + 1,
2063
2761
  "/",
2064
2762
  STEPS.length,
2065
2763
  ")"
2066
2764
  ] }) }),
2067
- /* @__PURE__ */ jsx13(Box13, { marginBottom: 1, children: /* @__PURE__ */ jsx13(Text13, { color: colors.bright, bold: true, children: step.title }) }),
2068
- /* @__PURE__ */ jsx13(Box13, { marginBottom: 1, children: /* @__PURE__ */ jsx13(Text13, { color: colors.text, children: step.body }) }),
2069
- /* @__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: [
2070
2768
  isLast ? "Enter = finish" : "Enter/\u2192 = next",
2071
2769
  " | ",
2072
2770
  stepIndex > 0 ? "\u2190 = back | " : "",
@@ -2076,19 +2774,19 @@ var GuidedTour = React13.memo(({ onComplete, onSkip }) => {
2076
2774
  });
2077
2775
 
2078
2776
  // src/ui/components/ConfirmModal.tsx
2079
- import React14 from "react";
2080
- import { Box as Box14, Text as Text14, useInput as useInput8 } from "ink";
2081
- import { jsx as jsx14, jsxs as jsxs14 } from "react/jsx-runtime";
2082
- var ConfirmModal = React14.memo(({ title, message, onConfirm, onCancel }) => {
2083
- 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) => {
2084
2782
  if (input === "y" || input === "Y") {
2085
2783
  onConfirm();
2086
2784
  } else if (input === "n" || input === "N" || key.escape) {
2087
2785
  onCancel();
2088
2786
  }
2089
2787
  });
2090
- return /* @__PURE__ */ jsxs14(
2091
- Box14,
2788
+ return /* @__PURE__ */ jsxs15(
2789
+ Box15,
2092
2790
  {
2093
2791
  borderStyle: "round",
2094
2792
  borderColor: colors.warning,
@@ -2097,19 +2795,105 @@ var ConfirmModal = React14.memo(({ title, message, onConfirm, onCancel }) => {
2097
2795
  paddingY: 1,
2098
2796
  alignSelf: "center",
2099
2797
  children: [
2100
- /* @__PURE__ */ jsx14(Text14, { color: colors.warning, bold: true, children: title }),
2101
- /* @__PURE__ */ jsx14(Text14, { color: colors.text, children: message }),
2102
- /* @__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" }) })
2801
+ ]
2802
+ }
2803
+ );
2804
+ });
2805
+
2806
+ // src/ui/components/UpdateModal.tsx
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";
2810
+ var options = [
2811
+ { key: "now", label: "Update now" },
2812
+ { key: "later", label: "Not now" },
2813
+ { key: "never", label: "Don't ask again" }
2814
+ ];
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("");
2819
+ const doUpdate = useCallback2(() => {
2820
+ setStatus("updating");
2821
+ installUpdate().then(() => {
2822
+ setStatus("restarting");
2823
+ setTimeout(() => restartProcess(), 500);
2824
+ }).catch((err) => {
2825
+ setErrorMsg(err.message || "update failed");
2826
+ setStatus("error");
2827
+ });
2828
+ }, []);
2829
+ useInput10((input, key) => {
2830
+ if (status === "updating" || status === "restarting") return;
2831
+ if (status === "error") {
2832
+ if (key.escape || key.return) {
2833
+ setStatus("idle");
2834
+ setErrorMsg("");
2835
+ }
2836
+ return;
2837
+ }
2838
+ if (key.upArrow || input === "k") {
2839
+ setSelected((s) => s > 0 ? s - 1 : options.length - 1);
2840
+ return;
2841
+ }
2842
+ if (key.downArrow || input === "j") {
2843
+ setSelected((s) => s < options.length - 1 ? s + 1 : 0);
2844
+ return;
2845
+ }
2846
+ if (key.escape) {
2847
+ onNotNow();
2848
+ return;
2849
+ }
2850
+ if (key.return) {
2851
+ const choice = options[selected].key;
2852
+ if (choice === "now") {
2853
+ doUpdate();
2854
+ } else if (choice === "later") {
2855
+ onNotNow();
2856
+ } else {
2857
+ onDontAskAgain();
2858
+ }
2859
+ }
2860
+ });
2861
+ return /* @__PURE__ */ jsxs16(
2862
+ Box16,
2863
+ {
2864
+ borderStyle: "round",
2865
+ borderColor: colors.primary,
2866
+ flexDirection: "column",
2867
+ paddingX: 2,
2868
+ paddingY: 1,
2869
+ alignSelf: "center",
2870
+ children: [
2871
+ /* @__PURE__ */ jsx16(Text16, { color: colors.primary, bold: true, children: "Update available" }),
2872
+ /* @__PURE__ */ jsxs16(Text16, { color: colors.text, children: [
2873
+ "v",
2874
+ current,
2875
+ " ",
2876
+ "->",
2877
+ " v",
2878
+ latest
2879
+ ] }),
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: [
2884
+ i === selected ? "> " : " ",
2885
+ opt.label
2886
+ ] }, opt.key)) })
2103
2887
  ]
2104
2888
  }
2105
2889
  );
2106
2890
  });
2107
2891
 
2108
2892
  // src/ui/components/SplitPanel.tsx
2109
- import React15 from "react";
2110
- import { Box as Box15 } from "ink";
2111
- import { jsx as jsx15, jsxs as jsxs15 } from "react/jsx-runtime";
2112
- var SplitPanel = React15.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(
2113
2897
  ({
2114
2898
  activePanel,
2115
2899
  leftSession,
@@ -2124,35 +2908,35 @@ var SplitPanel = React15.memo(
2124
2908
  rightShowDetail,
2125
2909
  height
2126
2910
  }) => {
2127
- const left = leftShowDetail && leftSession ? /* @__PURE__ */ jsx15(SessionDetail, { session: leftSession, focused: activePanel === "left", height }) : /* @__PURE__ */ jsx15(
2911
+ const left = leftShowDetail && leftSession ? /* @__PURE__ */ jsx17(SessionDetail, { session: leftSession, focused: activePanel === "left", height }) : /* @__PURE__ */ jsx17(
2128
2912
  ActivityFeed,
2129
2913
  {
2130
2914
  events: leftEvents,
2131
2915
  sessionSlug: leftSession?.slug ?? null,
2132
2916
  sessionId: leftSession?.sessionId,
2133
- isActive: leftSession ? leftSession.pid !== null : void 0,
2917
+ status: leftSession ? leftSession.status : void 0,
2134
2918
  focused: activePanel === "left",
2135
2919
  height,
2136
2920
  scrollOffset: leftScroll,
2137
2921
  filter: leftFilter || void 0
2138
2922
  }
2139
2923
  );
2140
- const right = rightShowDetail && rightSession ? /* @__PURE__ */ jsx15(SessionDetail, { session: rightSession, focused: activePanel === "right", height }) : /* @__PURE__ */ jsx15(
2924
+ const right = rightShowDetail && rightSession ? /* @__PURE__ */ jsx17(SessionDetail, { session: rightSession, focused: activePanel === "right", height }) : /* @__PURE__ */ jsx17(
2141
2925
  ActivityFeed,
2142
2926
  {
2143
2927
  events: rightEvents,
2144
2928
  sessionSlug: rightSession?.slug ?? null,
2145
2929
  sessionId: rightSession?.sessionId,
2146
- isActive: rightSession ? rightSession.pid !== null : void 0,
2930
+ status: rightSession ? rightSession.status : void 0,
2147
2931
  focused: activePanel === "right",
2148
2932
  height,
2149
2933
  scrollOffset: rightScroll,
2150
2934
  filter: rightFilter || void 0
2151
2935
  }
2152
2936
  );
2153
- return /* @__PURE__ */ jsxs15(Box15, { flexDirection: "row", flexGrow: 1, children: [
2154
- /* @__PURE__ */ jsx15(
2155
- Box15,
2937
+ return /* @__PURE__ */ jsxs17(Box17, { flexDirection: "row", flexGrow: 1, children: [
2938
+ /* @__PURE__ */ jsx17(
2939
+ Box17,
2156
2940
  {
2157
2941
  flexGrow: 1,
2158
2942
  borderStyle: "single",
@@ -2170,10 +2954,10 @@ var SplitPanel = React15.memo(
2170
2954
  );
2171
2955
 
2172
2956
  // src/ui/hooks/useSessions.ts
2173
- import { useState as useState9, useEffect as useEffect5, useCallback as useCallback2, 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";
2174
2958
 
2175
2959
  // src/discovery/sessionsAsync.ts
2176
- import { readdir, stat as stat2 } from "fs/promises";
2960
+ import { readdir, stat as stat2, open as open2 } from "fs/promises";
2177
2961
  import { join as join2, basename } from "path";
2178
2962
 
2179
2963
  // src/discovery/cache.ts
@@ -2212,44 +2996,9 @@ var pruneFileMetaCache = (validPaths) => {
2212
2996
  };
2213
2997
 
2214
2998
  // src/discovery/asyncHelpers.ts
2215
- import { execFile as execFile2 } from "child_process";
2216
- import { open, stat, readlink } from "fs/promises";
2217
- var normalisePath = (p) => p.replace(/\/+$/, "");
2218
- var getClaudeProcessesAsync = async () => {
2219
- const stdout = await new Promise((resolve) => {
2220
- execFile2("ps", ["aux"], { encoding: "utf-8", timeout: 5e3, maxBuffer: 4 * 1024 * 1024 }, (err, out) => {
2221
- resolve(err || !out ? "" : out);
2222
- });
2223
- });
2224
- if (!stdout) return [];
2225
- const procs = [];
2226
- for (const line of stdout.split("\n")) {
2227
- if (!line.includes("/claude") || line.includes("grep") || line.includes("agenttop")) continue;
2228
- const parts = line.trim().split(/\s+/);
2229
- const pid = parseInt(parts[1], 10);
2230
- if (isNaN(pid)) continue;
2231
- const command = parts.slice(10).join(" ");
2232
- if (command.startsWith("sudo")) continue;
2233
- procs.push({
2234
- pid,
2235
- cpu: parseFloat(parts[2]) || 0,
2236
- mem: parseFloat(parts[3]) || 0,
2237
- memKB: parseInt(parts[5], 10) || 0,
2238
- startTime: parts[8] || "",
2239
- command,
2240
- cwd: ""
2241
- });
2242
- }
2243
- await Promise.all(
2244
- procs.map(async (p) => {
2245
- try {
2246
- p.cwd = await readlink(`/proc/${p.pid}/cwd`);
2247
- } catch {
2248
- }
2249
- })
2250
- );
2251
- return procs;
2252
- };
2999
+ import { open, stat } from "fs/promises";
3000
+ import { normalize } from "path";
3001
+ var normalisePath = (p) => normalize(p).replace(/[\\/]+$/, "");
2253
3002
  var readFirstLinesAsync = async (filePath, bytes) => {
2254
3003
  let fh;
2255
3004
  try {
@@ -2367,7 +3116,47 @@ var findModelAndUsageAsync = async (filePath) => {
2367
3116
  };
2368
3117
 
2369
3118
  // src/discovery/sessionsAsync.ts
2370
- 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) => {
2371
3160
  const projectsDirs = getProjectsDirs(allUsers);
2372
3161
  for (const projectsDir of projectsDirs) {
2373
3162
  let projectNames;
@@ -2422,14 +3211,16 @@ var discoverFromProjectsAsync = async (allUsers, processes, sessionMap, seenFile
2422
3211
  outputFiles: [filePath],
2423
3212
  startTime: fstat.birthtimeMs || fstat.ctimeMs,
2424
3213
  lastActivity: fstat.mtimeMs,
2425
- usage: meta.usage
3214
+ usage: meta.usage,
3215
+ status: await detectStatusAsync(filePath, matchingProcess !== void 0, fstat.mtimeMs, staleTimeout),
3216
+ pinned: pinnedOrder.includes(meta.sessionId)
2426
3217
  };
2427
3218
  sessionMap.set(meta.sessionId, session);
2428
3219
  }
2429
3220
  }
2430
3221
  }
2431
3222
  };
2432
- var discoverFromTmpAsync = async (allUsers, processes, sessionMap, seenFiles) => {
3223
+ var discoverFromTmpAsync = async (allUsers, processes, sessionMap, seenFiles, staleTimeout, pinnedOrder) => {
2433
3224
  const taskDirs = getTaskDirs(allUsers);
2434
3225
  for (const taskDir of taskDirs) {
2435
3226
  let projectDirs;
@@ -2519,6 +3310,13 @@ var discoverFromTmpAsync = async (allUsers, processes, sessionMap, seenFiles) =>
2519
3310
  if (sessionMap.has(sessionId || projectName)) continue;
2520
3311
  const normCwd = normalisePath(cwd);
2521
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
+ }
2522
3320
  const session = {
2523
3321
  sessionId,
2524
3322
  slug: slug || sessionId.slice(0, 12),
@@ -2536,7 +3334,9 @@ var discoverFromTmpAsync = async (allUsers, processes, sessionMap, seenFiles) =>
2536
3334
  outputFiles,
2537
3335
  startTime: startTime === Infinity ? Date.now() : startTime,
2538
3336
  lastActivity,
2539
- usage: totalUsage
3337
+ usage: totalUsage,
3338
+ status: await detectStatusAsync(latestFile, matchingProcess !== void 0, lastActivity, staleTimeout),
3339
+ pinned: pinnedOrder.includes(sessionId)
2540
3340
  };
2541
3341
  sessionMap.set(sessionId || projectName, session);
2542
3342
  }
@@ -2544,16 +3344,26 @@ var discoverFromTmpAsync = async (allUsers, processes, sessionMap, seenFiles) =>
2544
3344
  }
2545
3345
  };
2546
3346
  var discoverSessionsAsync = async (allUsers) => {
3347
+ const config = loadConfig();
3348
+ const staleTimeout = config.alerts.staleTimeout ?? 60;
3349
+ const pinnedOrder = config.pinnedSessions ?? [];
2547
3350
  const processes = await getClaudeProcessesAsync();
2548
3351
  const sessionMap = /* @__PURE__ */ new Map();
2549
3352
  const seenFiles = /* @__PURE__ */ new Set();
2550
- await discoverFromProjectsAsync(allUsers, processes, sessionMap, seenFiles);
2551
- await discoverFromTmpAsync(allUsers, processes, sessionMap, seenFiles);
3353
+ await discoverFromProjectsAsync(allUsers, processes, sessionMap, seenFiles, staleTimeout, pinnedOrder);
3354
+ await discoverFromTmpAsync(allUsers, processes, sessionMap, seenFiles, staleTimeout, pinnedOrder);
2552
3355
  pruneFileMetaCache(seenFiles);
2553
3356
  return Array.from(sessionMap.values()).sort((a, b) => {
2554
- const aActive = a.pid !== null ? 1 : 0;
2555
- const bActive = b.pid !== null ? 1 : 0;
2556
- 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;
2557
3367
  return b.lastActivity - a.lastActivity;
2558
3368
  });
2559
3369
  };
@@ -2598,7 +3408,9 @@ var buildGroups = (sessions2, expandedKeys) => {
2598
3408
  totalInputTokens: totalIn,
2599
3409
  totalOutputTokens: totalOut,
2600
3410
  latestModel: list[0].model,
2601
- 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"),
2602
3414
  latestActivity: list[0].lastActivity,
2603
3415
  earliestStart: Math.min(...list.map((s) => s.startTime))
2604
3416
  });
@@ -2655,17 +3467,17 @@ var enrichAndFilter = (found, usageOverrides, filter, archivedIds, viewingArchiv
2655
3467
  return enriched;
2656
3468
  };
2657
3469
  var useSessions = (allUsers, filter, archivedIds, viewingArchive) => {
2658
- const [sessions2, setSessions] = useState9([]);
2659
- const [selectedIndex, setSelectedIndex] = useState9(0);
2660
- const [expandedKeys, setExpandedKeys] = useState9(/* @__PURE__ */ new Set());
2661
- const usageOverrides = useRef3(/* @__PURE__ */ new Map());
2662
- const refresh = useCallback2(() => {
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());
3474
+ const refresh = useCallback3(() => {
2663
3475
  const found = getCachedSessions();
2664
3476
  const filtered = enrichAndFilter(found, usageOverrides.current, filter, archivedIds, viewingArchive);
2665
3477
  setSessions(filtered);
2666
3478
  triggerRefresh(allUsers);
2667
3479
  }, [allUsers, filter, archivedIds, viewingArchive]);
2668
- useEffect5(() => {
3480
+ useEffect6(() => {
2669
3481
  const unsubscribe = subscribe(() => {
2670
3482
  const found = getCachedSessions();
2671
3483
  const filtered = enrichAndFilter(found, usageOverrides.current, filter, archivedIds, viewingArchive);
@@ -2673,7 +3485,7 @@ var useSessions = (allUsers, filter, archivedIds, viewingArchive) => {
2673
3485
  });
2674
3486
  return unsubscribe;
2675
3487
  }, [filter, archivedIds, viewingArchive]);
2676
- useEffect5(() => {
3488
+ useEffect6(() => {
2677
3489
  refresh();
2678
3490
  const pollMs = sessions2.length > 0 ? ACTIVE_POLL_MS : IDLE_POLL_MS;
2679
3491
  const interval = setInterval(() => triggerRefresh(allUsers), pollMs);
@@ -2681,21 +3493,21 @@ var useSessions = (allUsers, filter, archivedIds, viewingArchive) => {
2681
3493
  }, [refresh, sessions2.length > 0]);
2682
3494
  const groups = useMemo2(() => buildGroups(sessions2, expandedKeys), [sessions2, expandedKeys]);
2683
3495
  const visibleItems = useMemo2(() => buildVisibleItems(groups), [groups]);
2684
- const itemCountRef = useRef3(visibleItems.length);
3496
+ const itemCountRef = useRef4(visibleItems.length);
2685
3497
  itemCountRef.current = visibleItems.length;
2686
3498
  const selectedItem = visibleItems[selectedIndex] ?? null;
2687
3499
  const selectedSession = selectedItem?.type === "ungrouped" ? selectedItem.session : selectedItem?.type === "session" ? selectedItem.session : null;
2688
3500
  const selectedGroup = selectedItem?.type === "group" ? selectedItem.group : null;
2689
- const selectNext = useCallback2(() => {
3501
+ const selectNext = useCallback3(() => {
2690
3502
  setSelectedIndex((i) => Math.min(i + 1, Math.max(0, itemCountRef.current - 1)));
2691
3503
  }, []);
2692
- const selectPrev = useCallback2(() => {
3504
+ const selectPrev = useCallback3(() => {
2693
3505
  setSelectedIndex((i) => Math.max(i - 1, 0));
2694
3506
  }, []);
2695
- const selectIndex = useCallback2((i) => {
3507
+ const selectIndex = useCallback3((i) => {
2696
3508
  setSelectedIndex(i);
2697
3509
  }, []);
2698
- const toggleExpand = useCallback2((groupKey) => {
3510
+ const toggleExpand = useCallback3((groupKey) => {
2699
3511
  setExpandedKeys((prev) => {
2700
3512
  const next = new Set(prev);
2701
3513
  if (next.has(groupKey)) next.delete(groupKey);
@@ -2703,7 +3515,7 @@ var useSessions = (allUsers, filter, archivedIds, viewingArchive) => {
2703
3515
  return next;
2704
3516
  });
2705
3517
  }, []);
2706
- const addUsage = useCallback2((sessionId, usage) => {
3518
+ const addUsage = useCallback3((sessionId, usage) => {
2707
3519
  const existing = usageOverrides.current.get(sessionId);
2708
3520
  if (existing) {
2709
3521
  usageOverrides.current.set(sessionId, {
@@ -2733,16 +3545,16 @@ var useSessions = (allUsers, filter, archivedIds, viewingArchive) => {
2733
3545
  };
2734
3546
 
2735
3547
  // src/ui/hooks/useActivityStream.ts
2736
- import { useState as useState10, 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";
2737
3549
  var MAX_EVENTS = 200;
2738
3550
  var useActivityStream = (session, allUsers) => {
2739
- const [calls, setCalls] = useState10([]);
2740
- const [results, setResults] = useState10([]);
2741
- const watcherRef = useRef4(null);
3551
+ const [calls, setCalls] = useState12([]);
3552
+ const [results, setResults] = useState12([]);
3553
+ const watcherRef = useRef5(null);
2742
3554
  const sessions2 = session === null ? [] : Array.isArray(session) ? session : [session];
2743
3555
  const sessionKey = sessions2.map((s) => s.sessionId).sort().join(",");
2744
3556
  const sessionIdSet = new Set(sessions2.map((s) => s.sessionId));
2745
- useEffect6(() => {
3557
+ useEffect7(() => {
2746
3558
  setCalls([]);
2747
3559
  setResults([]);
2748
3560
  if (sessions2.length === 0) return;
@@ -2776,7 +3588,7 @@ var useActivityStream = (session, allUsers) => {
2776
3588
  const resultHandler = (newResults) => {
2777
3589
  const matched = newResults.filter((r) => sessionIdSet.has(r.sessionId));
2778
3590
  if (matched.length === 0) return;
2779
- setResults((prev) => [...prev, ...matched]);
3591
+ setResults((prev) => [...prev, ...matched].slice(-MAX_EVENTS * 2));
2780
3592
  };
2781
3593
  const watcher = new Watcher(callHandler, allUsers, void 0, void 0, resultHandler);
2782
3594
  watcherRef.current = watcher;
@@ -2811,10 +3623,10 @@ var applyFilter = (events, filter) => {
2811
3623
  var useFilteredEvents = (rawEvents, filter) => useMemo4(() => applyFilter(rawEvents, filter), [rawEvents, filter]);
2812
3624
 
2813
3625
  // src/ui/hooks/useAlerts.ts
2814
- import { useState as useState11, useEffect as useEffect7, useRef as useRef5 } from "react";
3626
+ import { useState as useState13, useEffect as useEffect8, useRef as useRef6 } from "react";
2815
3627
 
2816
3628
  // src/notifications.ts
2817
- import { exec as exec2 } from "child_process";
3629
+ import { execFile as execFile2 } from "child_process";
2818
3630
  import { platform } from "os";
2819
3631
  var SEVERITY_ORDER = {
2820
3632
  info: 0,
@@ -2837,11 +3649,23 @@ var notify = (alert, config) => {
2837
3649
  const body = `${alert.sessionSlug}: ${alert.message.slice(0, 100)}`;
2838
3650
  const os = platform();
2839
3651
  if (os === "linux") {
2840
- exec2(`notify-send ${JSON.stringify(title)} ${JSON.stringify(body)}`, { timeout: 3e3 });
3652
+ execFile2("notify-send", [title, body], { timeout: 3e3 });
2841
3653
  } else if (os === "darwin") {
2842
- exec2(`osascript -e 'display notification ${JSON.stringify(body)} with title ${JSON.stringify(title)}'`, {
2843
- timeout: 3e3
2844
- });
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
+ );
2845
3669
  }
2846
3670
  }
2847
3671
  };
@@ -2879,11 +3703,11 @@ var AlertLogger = class {
2879
3703
  // src/ui/hooks/useAlerts.ts
2880
3704
  var MAX_ALERTS = 100;
2881
3705
  var useAlerts = (enabled, alertLevel, allUsers, config) => {
2882
- const [alerts, setAlerts] = useState11([]);
2883
- const engineRef = useRef5(new SecurityEngine(alertLevel));
2884
- const watcherRef = useRef5(null);
2885
- const loggerRef = useRef5(null);
2886
- 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(() => {
2887
3711
  if (!enabled) return;
2888
3712
  engineRef.current = new SecurityEngine(alertLevel);
2889
3713
  if (config?.alerts.enabled) {
@@ -2913,33 +3737,33 @@ var useAlerts = (enabled, alertLevel, allUsers, config) => {
2913
3737
  watcherRef.current = null;
2914
3738
  loggerRef.current = null;
2915
3739
  };
2916
- }, [enabled, alertLevel, allUsers]);
3740
+ }, [enabled, alertLevel, allUsers, config]);
2917
3741
  const clearAlerts = () => setAlerts([]);
2918
3742
  return { alerts, clearAlerts };
2919
3743
  };
2920
3744
 
2921
3745
  // src/ui/hooks/useTextInput.ts
2922
- import { useState as useState12, useCallback as useCallback3 } from "react";
3746
+ import { useState as useState14, useCallback as useCallback4 } from "react";
2923
3747
  var useTextInput = (onConfirm, onCancel) => {
2924
- const [value, setValue] = useState12("");
2925
- const [isActive, setIsActive] = useState12(false);
2926
- const start = useCallback3((initial = "") => {
3748
+ const [value, setValue] = useState14("");
3749
+ const [isActive, setIsActive] = useState14(false);
3750
+ const start = useCallback4((initial = "") => {
2927
3751
  setValue(initial);
2928
3752
  setIsActive(true);
2929
3753
  }, []);
2930
- const cancel = useCallback3(() => {
3754
+ const cancel = useCallback4(() => {
2931
3755
  setValue("");
2932
3756
  setIsActive(false);
2933
3757
  onCancel?.();
2934
3758
  }, [onCancel]);
2935
- const confirm = useCallback3(() => {
3759
+ const confirm = useCallback4(() => {
2936
3760
  const result = value;
2937
3761
  setIsActive(false);
2938
3762
  setValue("");
2939
3763
  onConfirm?.(result);
2940
3764
  return result;
2941
3765
  }, [value, onConfirm]);
2942
- const handleInput = useCallback3(
3766
+ const handleInput = useCallback4(
2943
3767
  (input, key) => {
2944
3768
  if (!isActive) return false;
2945
3769
  if (key.escape) {
@@ -2972,17 +3796,18 @@ var useTextInput = (onConfirm, onCancel) => {
2972
3796
  };
2973
3797
 
2974
3798
  // src/ui/hooks/useKeyHandler.ts
2975
- import { useInput as useInput9 } from "ink";
3799
+ import { useInput as useInput11 } from "ink";
2976
3800
  var matchKey = (binding, input, key) => {
2977
3801
  if (binding === "tab") return Boolean(key.tab);
2978
3802
  if (binding === "shift+tab") return Boolean(key.shift && key.tab);
2979
3803
  if (binding === "enter") return Boolean(key.return);
3804
+ if (binding.startsWith("ctrl+")) return Boolean(key.ctrl) && input === binding.slice(5);
2980
3805
  return input === binding;
2981
3806
  };
2982
3807
  var useKeyHandler = (deps) => {
2983
3808
  const d = deps;
2984
- useInput9((input, key) => {
2985
- if (d.showSetup || d.showSettings || d.confirmAction) return;
3809
+ useInput11((input, key) => {
3810
+ if (d.showSetup || d.showSettings || d.confirmAction || d.showUpdateModal) return;
2986
3811
  if (d.inputMode === "nickname") {
2987
3812
  d.nicknameInput.handleInput(input, key);
2988
3813
  return;
@@ -3152,10 +3977,26 @@ var useKeyHandler = (deps) => {
3152
3977
  d.setShowSettings(true);
3153
3978
  return;
3154
3979
  }
3980
+ if (matchKey(d.kb.alertRules, input, key)) {
3981
+ d.setShowAlertRules(true);
3982
+ return;
3983
+ }
3155
3984
  if (matchKey(d.kb.viewArchive, input, key)) {
3156
3985
  d.setViewingArchive((v) => !v);
3157
3986
  return;
3158
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
+ }
3159
4000
  if (matchKey(d.kb.archive, input, key) && d.selectedSession) {
3160
4001
  if (d.viewingArchive) d.onUnarchive(d.selectedSession.sessionId);
3161
4002
  else d.onArchive(d.selectedSession.sessionId);
@@ -3214,10 +4055,10 @@ var useKeyHandler = (deps) => {
3214
4055
  };
3215
4056
 
3216
4057
  // src/ui/hooks/useUpdateChecker.ts
3217
- import { useState as useState13, useEffect as useEffect8 } from "react";
4058
+ import { useState as useState15, useEffect as useEffect9 } from "react";
3218
4059
  var useUpdateChecker = (disabled, checkOnLaunch, checkInterval) => {
3219
- const [updateInfo, setUpdateInfo] = useState13(null);
3220
- useEffect8(() => {
4060
+ const [updateInfo, setUpdateInfo] = useState15(null);
4061
+ useEffect9(() => {
3221
4062
  if (disabled || !checkOnLaunch) return;
3222
4063
  let cancelled = false;
3223
4064
  const check = () => {
@@ -3232,12 +4073,12 @@ var useUpdateChecker = (disabled, checkOnLaunch, checkInterval) => {
3232
4073
  cancelled = true;
3233
4074
  clearInterval(iv);
3234
4075
  };
3235
- }, []);
4076
+ }, [disabled, checkOnLaunch, checkInterval]);
3236
4077
  return updateInfo;
3237
4078
  };
3238
4079
 
3239
4080
  // src/ui/hooks/useSetupFlow.ts
3240
- import { useState as useState14, useCallback as useCallback4 } from "react";
4081
+ import { useState as useState16, useCallback as useCallback5 } from "react";
3241
4082
 
3242
4083
  // src/hooks/installer.ts
3243
4084
  import { existsSync, readFileSync, writeFileSync, copyFileSync, mkdirSync as mkdirSync2, chmodSync } from "fs";
@@ -3357,25 +4198,25 @@ var installMcpConfig = () => {
3357
4198
 
3358
4199
  // src/ui/hooks/useSetupFlow.ts
3359
4200
  var useSetupFlow = (initialConfig, firstRun) => {
3360
- const [liveConfig, setLiveConfig] = useState14(initialConfig);
3361
- const [showSetup, setShowSetup] = useState14(firstRun);
3362
- const [showThemePicker, setShowThemePicker] = useState14(false);
3363
- const [showTour, setShowTour] = useState14(false);
3364
- const [showSettings, setShowSettings] = useState14(false);
3365
- const [showThemeMenu, setShowThemeMenu] = useState14(false);
3366
- const handleSettingsClose = useCallback4((c) => {
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);
4207
+ const handleSettingsClose = useCallback5((c) => {
3367
4208
  setLiveConfig(c);
3368
4209
  saveConfig(c);
3369
4210
  setShowSettings(false);
3370
4211
  }, []);
3371
- const handleThemeMenuClose = useCallback4((c) => {
4212
+ const handleThemeMenuClose = useCallback5((c) => {
3372
4213
  setLiveConfig(c);
3373
4214
  saveConfig(c);
3374
4215
  setShowThemeMenu(false);
3375
4216
  setShowSettings(true);
3376
4217
  }, []);
3377
- const handleOpenThemeMenu = useCallback4(() => setShowThemeMenu(true), []);
3378
- const handleSetupComplete = useCallback4(
4218
+ const handleOpenThemeMenu = useCallback5(() => setShowThemeMenu(true), []);
4219
+ const handleSetupComplete = useCallback5(
3379
4220
  (results) => {
3380
4221
  const nc = { ...liveConfig };
3381
4222
  const [hc, mc] = results;
@@ -3400,7 +4241,7 @@ var useSetupFlow = (initialConfig, firstRun) => {
3400
4241
  },
3401
4242
  [liveConfig]
3402
4243
  );
3403
- const handleThemePickerSelect = useCallback4(
4244
+ const handleThemePickerSelect = useCallback5(
3404
4245
  (themeName) => {
3405
4246
  const nc = { ...liveConfig, theme: themeName, prompts: { ...liveConfig.prompts, theme: "done" } };
3406
4247
  setLiveConfig(nc);
@@ -3410,24 +4251,24 @@ var useSetupFlow = (initialConfig, firstRun) => {
3410
4251
  },
3411
4252
  [liveConfig]
3412
4253
  );
3413
- const handleThemePickerSkip = useCallback4(() => {
4254
+ const handleThemePickerSkip = useCallback5(() => {
3414
4255
  setShowThemePicker(false);
3415
4256
  if (liveConfig.prompts.tour === "pending") setShowTour(true);
3416
4257
  }, [liveConfig]);
3417
- const handleThemePickerDismiss = useCallback4(() => {
4258
+ const handleThemePickerDismiss = useCallback5(() => {
3418
4259
  const nc = { ...liveConfig, prompts: { ...liveConfig.prompts, theme: "dismissed" } };
3419
4260
  setLiveConfig(nc);
3420
4261
  saveConfig(nc);
3421
4262
  setShowThemePicker(false);
3422
4263
  if (nc.prompts.tour === "pending") setShowTour(true);
3423
4264
  }, [liveConfig]);
3424
- const handleTourComplete = useCallback4(() => {
4265
+ const handleTourComplete = useCallback5(() => {
3425
4266
  const nc = { ...liveConfig, prompts: { ...liveConfig.prompts, tour: "done" } };
3426
4267
  setLiveConfig(nc);
3427
4268
  saveConfig(nc);
3428
4269
  setShowTour(false);
3429
4270
  }, [liveConfig]);
3430
- const handleTourSkip = useCallback4(() => {
4271
+ const handleTourSkip = useCallback5(() => {
3431
4272
  setShowTour(false);
3432
4273
  }, []);
3433
4274
  return {
@@ -3453,18 +4294,18 @@ var useSetupFlow = (initialConfig, firstRun) => {
3453
4294
  };
3454
4295
 
3455
4296
  // src/ui/hooks/useSplitPanel.ts
3456
- import { useState as useState15, useCallback as useCallback5 } from "react";
4297
+ import { useState as useState17, useCallback as useCallback6 } from "react";
3457
4298
  var useSplitPanel = () => {
3458
- const [splitMode, setSplitMode] = useState15(false);
3459
- const [leftSession, setLeftSession] = useState15(null);
3460
- const [rightSession, setRightSession] = useState15(null);
3461
- const [leftScroll, setLeftScroll] = useState15(0);
3462
- const [rightScroll, setRightScroll] = useState15(0);
3463
- const [leftFilter, setLeftFilter] = useState15("");
3464
- const [rightFilter, setRightFilter] = useState15("");
3465
- const [leftShowDetail, setLeftShowDetail] = useState15(false);
3466
- const [rightShowDetail, setRightShowDetail] = useState15(false);
3467
- const clearSplitState = useCallback5(() => {
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);
4308
+ const clearSplitState = useCallback6(() => {
3468
4309
  setSplitMode(false);
3469
4310
  setLeftSession(null);
3470
4311
  setRightSession(null);
@@ -3475,7 +4316,7 @@ var useSplitPanel = () => {
3475
4316
  setLeftShowDetail(false);
3476
4317
  setRightShowDetail(false);
3477
4318
  }, []);
3478
- const resetPanel = useCallback5((side) => {
4319
+ const resetPanel = useCallback6((side) => {
3479
4320
  if (side === "left") {
3480
4321
  setLeftSession(null);
3481
4322
  setLeftScroll(0);
@@ -3488,7 +4329,7 @@ var useSplitPanel = () => {
3488
4329
  setRightShowDetail(false);
3489
4330
  }
3490
4331
  }, []);
3491
- const switchPanel = useCallback5(
4332
+ const switchPanel = useCallback6(
3492
4333
  (dir, setActivePanel) => {
3493
4334
  if (splitMode) {
3494
4335
  const order = ["sessions", "left", "right"];
@@ -3529,31 +4370,33 @@ var useSplitPanel = () => {
3529
4370
  };
3530
4371
 
3531
4372
  // src/ui/App.tsx
3532
- import { jsx as jsx16, jsxs as jsxs16 } from "react/jsx-runtime";
3533
- var App = ({ options, config: initialConfig, version, firstRun }) => {
4373
+ import { jsx as jsx18, jsxs as jsxs18 } from "react/jsx-runtime";
4374
+ var App = ({ options: options2, config: initialConfig, version, firstRun }) => {
3534
4375
  const { exit } = useApp();
3535
- const { stdout } = useStdout3();
4376
+ const { stdout } = useStdout4();
3536
4377
  const termHeight = stdout?.rows ?? 40;
3537
4378
  const setup = useSetupFlow(initialConfig, firstRun);
3538
4379
  const split = useSplitPanel();
3539
4380
  const kb = setup.liveConfig.keybindings;
3540
- const [activePanel, setActivePanel] = useState16("sessions");
3541
- const [activityScroll, setActivityScroll] = useState16(0);
3542
- const [inputMode, setInputMode] = useState16("normal");
3543
- const [filter, setFilter] = useState16("");
3544
- const [activityFilter, setActivityFilter] = useState16("");
3545
- const [updateStatus, setUpdateStatus] = useState16("");
3546
- const [showDetail, setShowDetail] = useState16(false);
3547
- const [viewingArchive, setViewingArchive] = useState16(false);
3548
- const [confirmAction, setConfirmAction] = useState16(
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(
3549
4390
  null
3550
4391
  );
3551
- const [archivedIds, setArchivedIds] = useState16(() => new Set(Object.keys(getArchived())));
3552
- const [sidebarWidth, setSidebarWidth] = useState16(() => initialConfig.sidebarWidth ?? 30);
3553
- const [selectedEventIndex, setSelectedEventIndex] = useState16(0);
3554
- const [showEventDetail, setShowEventDetail] = useState16(false);
3555
- const refreshArchived = useCallback6(() => setArchivedIds(new Set(Object.keys(getArchived()))), []);
3556
- const persistSidebarWidth = useCallback6((v) => {
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);
4398
+ const refreshArchived = useCallback7(() => setArchivedIds(new Set(Object.keys(getArchived()))), []);
4399
+ const persistSidebarWidth = useCallback7((v) => {
3557
4400
  setSidebarWidth((prev) => {
3558
4401
  const next = typeof v === "function" ? v(prev) : v;
3559
4402
  if (next !== prev) {
@@ -3565,13 +4408,18 @@ var App = ({ options, config: initialConfig, version, firstRun }) => {
3565
4408
  });
3566
4409
  }, []);
3567
4410
  const updateInfo = useUpdateChecker(
3568
- options.noUpdates,
4411
+ options2.noUpdates,
3569
4412
  setup.liveConfig.updates.checkOnLaunch,
3570
4413
  setup.liveConfig.updates.checkInterval
3571
4414
  );
3572
- useEffect9(() => {
4415
+ useEffect10(() => {
3573
4416
  applyTheme(resolveTheme(setup.liveConfig.theme, setup.liveConfig.customThemes));
3574
4417
  }, [setup.liveConfig.theme, setup.liveConfig.customThemes]);
4418
+ useEffect10(() => {
4419
+ if (updateInfo?.available && setup.liveConfig.prompts.autoUpdate === "pending") {
4420
+ setShowUpdateModal(true);
4421
+ }
4422
+ }, [updateInfo?.available, setup.liveConfig.prompts.autoUpdate]);
3575
4423
  const {
3576
4424
  sessions: sessions2,
3577
4425
  visibleItems,
@@ -3582,15 +4430,15 @@ var App = ({ options, config: initialConfig, version, firstRun }) => {
3582
4430
  selectPrev,
3583
4431
  toggleExpand,
3584
4432
  refresh
3585
- } = useSessions(options.allUsers, filter || void 0, archivedIds, viewingArchive);
4433
+ } = useSessions(options2.allUsers, filter || void 0, archivedIds, viewingArchive);
3586
4434
  const activityTarget = split.splitMode ? null : selectedGroup ? selectedGroup.sessions : selectedSession;
3587
- const rawEvents = useActivityStream(activityTarget, options.allUsers);
3588
- const leftRawEvents = useActivityStream(split.splitMode ? split.leftSession : null, options.allUsers);
3589
- const rightRawEvents = useActivityStream(split.splitMode ? split.rightSession : null, options.allUsers);
4435
+ const rawEvents = useActivityStream(activityTarget, options2.allUsers);
4436
+ const leftRawEvents = useActivityStream(split.splitMode ? split.leftSession : null, options2.allUsers);
4437
+ const rightRawEvents = useActivityStream(split.splitMode ? split.rightSession : null, options2.allUsers);
3590
4438
  const events = useFilteredEvents(rawEvents, activityFilter);
3591
4439
  const leftEvents = useFilteredEvents(leftRawEvents, split.leftFilter);
3592
4440
  const rightEvents = useFilteredEvents(rightRawEvents, split.rightFilter);
3593
- const { alerts } = useAlerts(!options.noSecurity, options.alertLevel, options.allUsers, setup.liveConfig);
4441
+ const { alerts } = useAlerts(!options2.noSecurity, options2.alertLevel, options2.allUsers, setup.liveConfig);
3594
4442
  const nicknameInput = useTextInput(
3595
4443
  (value) => {
3596
4444
  if (selectedSession && value.trim()) {
@@ -3617,18 +4465,18 @@ var App = ({ options, config: initialConfig, version, firstRun }) => {
3617
4465
  setInputMode("normal");
3618
4466
  }
3619
4467
  );
3620
- useEffect9(() => {
4468
+ useEffect10(() => {
3621
4469
  purgeExpiredArchives();
3622
4470
  refreshArchived();
3623
4471
  }, []);
3624
- useEffect9(() => {
4472
+ useEffect10(() => {
3625
4473
  setActivityScroll(0);
3626
4474
  setSelectedEventIndex(0);
3627
4475
  setShowEventDetail(false);
3628
4476
  }, [selectedSession?.sessionId, selectedGroup?.key]);
3629
- useEffect9(() => {
4477
+ useEffect10(() => {
3630
4478
  const totalEvents = events.length;
3631
- const vRows = termHeight - 3 - (options.noSecurity ? 0 : 6) - 1 - (inputMode !== "normal" ? 1 : 0) - 2;
4479
+ const vRows = termHeight - 3 - (options2.noSecurity ? 0 : 6) - 1 - (inputMode !== "normal" ? 1 : 0) - 2;
3632
4480
  if (vRows <= 0 || totalEvents === 0) return;
3633
4481
  const start = Math.max(0, totalEvents - vRows - activityScroll);
3634
4482
  const end = start + vRows;
@@ -3638,40 +4486,60 @@ var App = ({ options, config: initialConfig, version, firstRun }) => {
3638
4486
  setActivityScroll(totalEvents - vRows - (selectedEventIndex - vRows + 1));
3639
4487
  }
3640
4488
  }, [selectedEventIndex, events.length]);
3641
- useEffect9(() => {
4489
+ useEffect10(() => {
3642
4490
  if (events.length > 0 && selectedEventIndex >= events.length) {
3643
4491
  setSelectedEventIndex(events.length - 1);
3644
4492
  }
3645
4493
  }, [events.length]);
3646
- const alertHeight = options.noSecurity ? 0 : 6;
4494
+ const alertHeight = options2.noSecurity ? 0 : 6;
3647
4495
  const mainHeight = termHeight - 3 - alertHeight - 1 - (inputMode !== "normal" ? 1 : 0);
3648
4496
  const viewportRows = mainHeight - 2;
3649
- const switchPanel = useCallback6(
4497
+ const switchPanel = useCallback7(
3650
4498
  (dir) => split.switchPanel(dir, setActivePanel),
3651
4499
  [split.switchPanel]
3652
4500
  );
3653
- const clearSplitState = useCallback6(() => {
4501
+ const clearSplitState = useCallback7(() => {
3654
4502
  split.clearSplitState();
3655
4503
  setActivePanel("sessions");
3656
4504
  }, [split.clearSplitState]);
3657
- const getActiveFilter = useCallback6(() => {
4505
+ const getActiveFilter = useCallback7(() => {
3658
4506
  if (activePanel === "sessions") return filter;
3659
4507
  if (activePanel === "left") return split.leftFilter;
3660
4508
  if (activePanel === "right") return split.rightFilter;
3661
4509
  return activityFilter;
3662
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
+ );
3663
4530
  useKeyHandler({
3664
4531
  kb,
3665
4532
  activePanel,
3666
4533
  splitMode: split.splitMode,
3667
4534
  inputMode,
3668
4535
  showSetup: setup.showSetup,
3669
- showSettings: setup.showSettings || setup.showThemeMenu || setup.showThemePicker || setup.showTour,
4536
+ showSettings: setup.showSettings || setup.showThemeMenu || setup.showThemePicker || setup.showTour || showAlertRules,
3670
4537
  showDetail,
3671
4538
  showEventDetail,
3672
4539
  leftShowDetail: split.leftShowDetail,
3673
4540
  rightShowDetail: split.rightShowDetail,
3674
4541
  confirmAction,
4542
+ showUpdateModal,
3675
4543
  selectedSession,
3676
4544
  selectedGroup,
3677
4545
  toggleExpand,
@@ -3709,6 +4577,8 @@ var App = ({ options, config: initialConfig, version, firstRun }) => {
3709
4577
  setLeftShowDetail: split.setLeftShowDetail,
3710
4578
  setRightShowDetail: split.setRightShowDetail,
3711
4579
  setShowSettings: setup.setShowSettings,
4580
+ showAlertRules,
4581
+ setShowAlertRules,
3712
4582
  setViewingArchive,
3713
4583
  setSplitMode: split.setSplitMode,
3714
4584
  setLeftSession: split.setLeftSession,
@@ -3723,14 +4593,12 @@ var App = ({ options, config: initialConfig, version, firstRun }) => {
3723
4593
  setSidebarWidth: persistSidebarWidth,
3724
4594
  nicknameInput,
3725
4595
  filterInput,
3726
- onNickname: (id) => {
3727
- clearNickname(id);
3728
- refresh();
3729
- },
3730
4596
  onClearNickname: (id) => {
3731
4597
  clearNickname(id);
3732
4598
  refresh();
3733
4599
  },
4600
+ onTogglePin: handleTogglePin,
4601
+ onMovePinned: handleMovePinned,
3734
4602
  onArchive: (id) => {
3735
4603
  archiveSession(id);
3736
4604
  refreshArchived();
@@ -3757,7 +4625,10 @@ var App = ({ options, config: initialConfig, version, firstRun }) => {
3757
4625
  }),
3758
4626
  onUpdate: () => {
3759
4627
  setUpdateStatus("updating...");
3760
- installUpdate().then(() => setUpdateStatus(`updated to v${updateInfo?.latest} \u2014 restart to apply`)).catch(() => setUpdateStatus("update failed"));
4628
+ installUpdate().then(() => {
4629
+ setUpdateStatus(`updated to v${updateInfo?.latest} \u2014 restarting...`);
4630
+ setTimeout(() => restartProcess(), 500);
4631
+ }).catch(() => setUpdateStatus("update failed"));
3761
4632
  }
3762
4633
  });
3763
4634
  if (setup.showSetup) {
@@ -3779,10 +4650,10 @@ var App = ({ options, config: initialConfig, version, firstRun }) => {
3779
4650
  setup.setShowSetup(false);
3780
4651
  return null;
3781
4652
  }
3782
- return /* @__PURE__ */ jsx16(SetupModal, { steps, onComplete: setup.handleSetupComplete });
4653
+ return /* @__PURE__ */ jsx18(SetupModal, { steps, onComplete: setup.handleSetupComplete });
3783
4654
  }
3784
4655
  if (setup.showThemePicker)
3785
- return /* @__PURE__ */ jsx16(
4656
+ return /* @__PURE__ */ jsx18(
3786
4657
  ThemePickerModal,
3787
4658
  {
3788
4659
  onSelect: setup.handleThemePickerSelect,
@@ -3790,10 +4661,22 @@ var App = ({ options, config: initialConfig, version, firstRun }) => {
3790
4661
  onDismiss: setup.handleThemePickerDismiss
3791
4662
  }
3792
4663
  );
3793
- if (setup.showTour) return /* @__PURE__ */ jsx16(GuidedTour, { onComplete: setup.handleTourComplete, onSkip: setup.handleTourSkip });
3794
- if (setup.showThemeMenu) return /* @__PURE__ */ jsx16(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
+ );
3795
4678
  if (setup.showSettings)
3796
- return /* @__PURE__ */ jsx16(
4679
+ return /* @__PURE__ */ jsx18(
3797
4680
  SettingsMenu,
3798
4681
  {
3799
4682
  config: setup.liveConfig,
@@ -3801,8 +4684,25 @@ var App = ({ options, config: initialConfig, version, firstRun }) => {
3801
4684
  onOpenThemeMenu: setup.handleOpenThemeMenu
3802
4685
  }
3803
4686
  );
4687
+ if (showUpdateModal && updateInfo?.available) {
4688
+ return /* @__PURE__ */ jsx18(Box18, { flexDirection: "column", height: termHeight, justifyContent: "center", alignItems: "center", children: /* @__PURE__ */ jsx18(
4689
+ UpdateModal,
4690
+ {
4691
+ current: updateInfo.current,
4692
+ latest: updateInfo.latest,
4693
+ onNotNow: () => setShowUpdateModal(false),
4694
+ onDontAskAgain: () => {
4695
+ const cfg = loadConfig();
4696
+ cfg.prompts.autoUpdate = "dismissed";
4697
+ saveConfig(cfg);
4698
+ setup.setLiveConfig(cfg);
4699
+ setShowUpdateModal(false);
4700
+ }
4701
+ }
4702
+ ) });
4703
+ }
3804
4704
  if (confirmAction) {
3805
- return /* @__PURE__ */ jsx16(Box16, { flexDirection: "column", height: termHeight, justifyContent: "center", alignItems: "center", children: /* @__PURE__ */ jsx16(
4705
+ return /* @__PURE__ */ jsx18(Box18, { flexDirection: "column", height: termHeight, justifyContent: "center", alignItems: "center", children: /* @__PURE__ */ jsx18(
3806
4706
  ConfirmModal,
3807
4707
  {
3808
4708
  title: confirmAction.title,
@@ -3815,7 +4715,7 @@ var App = ({ options, config: initialConfig, version, firstRun }) => {
3815
4715
  const activitySlug = selectedGroup ? selectedGroup.key : selectedSession?.slug ?? null;
3816
4716
  const isMerged = selectedGroup !== null;
3817
4717
  const selectedEvent = events[selectedEventIndex];
3818
- const rightPanel = split.splitMode ? /* @__PURE__ */ jsx16(
4718
+ const rightPanel = split.splitMode ? /* @__PURE__ */ jsx18(
3819
4719
  SplitPanel,
3820
4720
  {
3821
4721
  activePanel,
@@ -3831,13 +4731,13 @@ var App = ({ options, config: initialConfig, version, firstRun }) => {
3831
4731
  rightShowDetail: split.rightShowDetail,
3832
4732
  height: mainHeight
3833
4733
  }
3834
- ) : showEventDetail && selectedEvent ? /* @__PURE__ */ jsx16(ToolCallDetail, { event: selectedEvent, focused: activePanel === "activity", height: mainHeight }) : showDetail && selectedSession ? /* @__PURE__ */ jsx16(SessionDetail, { session: selectedSession, focused: activePanel === "activity", height: mainHeight }) : /* @__PURE__ */ jsx16(
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(
3835
4735
  ActivityFeed,
3836
4736
  {
3837
4737
  events,
3838
4738
  sessionSlug: activitySlug,
3839
4739
  sessionId: selectedSession?.sessionId,
3840
- isActive: selectedGroup ? selectedGroup.isActive : selectedSession ? selectedSession.pid !== null : void 0,
4740
+ status: selectedGroup ? selectedGroup.status : selectedSession ? selectedSession.status : void 0,
3841
4741
  focused: activePanel === "activity",
3842
4742
  height: mainHeight,
3843
4743
  scrollOffset: activityScroll,
@@ -3847,10 +4747,10 @@ var App = ({ options, config: initialConfig, version, firstRun }) => {
3847
4747
  selectedEventIndex
3848
4748
  }
3849
4749
  );
3850
- return /* @__PURE__ */ jsxs16(Box16, { flexDirection: "column", height: termHeight, children: [
3851
- /* @__PURE__ */ jsx16(StatusBar, { sessionCount: sessions2.length, alertCount: alerts.length, version, updateInfo }),
3852
- /* @__PURE__ */ jsxs16(Box16, { flexGrow: 1, height: mainHeight, children: [
3853
- /* @__PURE__ */ jsx16(
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(
3854
4754
  SessionList,
3855
4755
  {
3856
4756
  visibleItems,
@@ -3863,21 +4763,21 @@ var App = ({ options, config: initialConfig, version, firstRun }) => {
3863
4763
  sidebarWidth
3864
4764
  }
3865
4765
  ),
3866
- /* @__PURE__ */ jsx16(Box16, { flexGrow: 1, flexShrink: 1, minWidth: 0, overflow: "hidden", children: rightPanel })
4766
+ /* @__PURE__ */ jsx18(Box18, { flexGrow: 1, flexShrink: 1, minWidth: 0, overflow: "hidden", children: rightPanel })
3867
4767
  ] }),
3868
- !options.noSecurity && /* @__PURE__ */ jsx16(AlertBar, { alerts }),
3869
- inputMode === "nickname" && /* @__PURE__ */ jsxs16(Box16, { paddingX: 1, children: [
3870
- /* @__PURE__ */ jsx16(Text15, { color: colors.primary, children: "nickname: " }),
3871
- /* @__PURE__ */ jsx16(Text15, { color: colors.bright, children: nicknameInput.value }),
3872
- /* @__PURE__ */ jsx16(Text15, { 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: "_" })
3873
4773
  ] }),
3874
- inputMode === "filter" && /* @__PURE__ */ jsxs16(Box16, { paddingX: 1, children: [
3875
- /* @__PURE__ */ jsx16(Text15, { color: colors.muted, children: activePanel === "sessions" ? "sessions" : activePanel === "left" ? "left" : activePanel === "right" ? "right" : "activity" }),
3876
- /* @__PURE__ */ jsx16(Text15, { color: colors.primary, children: "/" }),
3877
- /* @__PURE__ */ jsx16(Text15, { color: colors.bright, children: filterInput.value }),
3878
- /* @__PURE__ */ jsx16(Text15, { 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: "_" })
3879
4779
  ] }),
3880
- inputMode === "normal" && /* @__PURE__ */ jsx16(
4780
+ inputMode === "normal" && /* @__PURE__ */ jsx18(
3881
4781
  FooterBar,
3882
4782
  {
3883
4783
  keybindings: kb,
@@ -3893,20 +4793,15 @@ var App = ({ options, config: initialConfig, version, firstRun }) => {
3893
4793
  var write = (msg) => {
3894
4794
  process.stdout.write(msg + "\n");
3895
4795
  };
3896
- var formatTokens3 = (n) => {
3897
- if (n >= 1e6) return (n / 1e6).toFixed(1) + "M";
3898
- if (n >= 1e3) return (n / 1e3).toFixed(1) + "k";
3899
- return String(n);
3900
- };
3901
- var runStreamMode = (options, isJson) => {
3902
- const engine = options.noSecurity ? null : new SecurityEngine(options.alertLevel);
3903
- const sessions2 = discoverSessions(options.allUsers);
4796
+ var runStreamMode = (options2, isJson) => {
4797
+ const engine = options2.noSecurity ? null : new SecurityEngine(options2.alertLevel);
4798
+ const sessions2 = discoverSessions(options2.allUsers);
3904
4799
  if (isJson) {
3905
4800
  write(JSON.stringify({ type: "sessions", data: sessions2 }));
3906
4801
  } else {
3907
4802
  for (const s of sessions2) {
3908
4803
  write(
3909
- `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`
3910
4805
  );
3911
4806
  }
3912
4807
  }
@@ -3939,11 +4834,11 @@ var runStreamMode = (options, isJson) => {
3939
4834
  write(JSON.stringify({ type: "usage", data: { sessionId, usage } }));
3940
4835
  } else {
3941
4836
  write(
3942
- `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`
3943
4838
  );
3944
4839
  }
3945
4840
  };
3946
- const watcher = new Watcher(handler, options.allUsers, securityHandler, usageHandler);
4841
+ const watcher = new Watcher(handler, options2.allUsers, securityHandler, usageHandler);
3947
4842
  watcher.start();
3948
4843
  process.on("SIGINT", () => {
3949
4844
  watcher.stop();
@@ -4004,7 +4899,7 @@ var write2 = (msg) => {
4004
4899
  };
4005
4900
  var parseArgs = (argv) => {
4006
4901
  const args = argv.slice(2);
4007
- const options = {
4902
+ const options2 = {
4008
4903
  allUsers: false,
4009
4904
  noSecurity: false,
4010
4905
  json: false,
@@ -4024,106 +4919,106 @@ var parseArgs = (argv) => {
4024
4919
  for (let i = 0; i < args.length; i++) {
4025
4920
  switch (args[i]) {
4026
4921
  case "--all-users":
4027
- options.allUsers = true;
4922
+ options2.allUsers = true;
4028
4923
  break;
4029
4924
  case "--no-security":
4030
- options.noSecurity = true;
4925
+ options2.noSecurity = true;
4031
4926
  break;
4032
4927
  case "--json":
4033
- options.json = true;
4928
+ options2.json = true;
4034
4929
  break;
4035
4930
  case "--plain":
4036
- options.plain = true;
4931
+ options2.plain = true;
4037
4932
  break;
4038
4933
  case "--alert-level":
4039
4934
  i++;
4040
4935
  if (["info", "warn", "high", "critical"].includes(args[i])) {
4041
- options.alertLevel = args[i];
4936
+ options2.alertLevel = args[i];
4042
4937
  }
4043
4938
  break;
4044
4939
  case "--no-notify":
4045
- options.noNotify = true;
4940
+ options2.noNotify = true;
4046
4941
  break;
4047
4942
  case "--no-alert-log":
4048
- options.noAlertLog = true;
4943
+ options2.noAlertLog = true;
4049
4944
  break;
4050
4945
  case "--no-updates":
4051
- options.noUpdates = true;
4946
+ options2.noUpdates = true;
4052
4947
  break;
4053
4948
  case "--poll-interval":
4054
4949
  i++;
4055
4950
  {
4056
4951
  const n = parseInt(args[i], 10);
4057
- if (n > 0) options.pollInterval = n;
4952
+ if (n > 0) options2.pollInterval = n;
4058
4953
  }
4059
4954
  break;
4060
4955
  case "--mcp":
4061
- options.mcp = true;
4956
+ options2.mcp = true;
4062
4957
  break;
4063
4958
  case "--install-mcp":
4064
- options.installMcp = true;
4959
+ options2.installMcp = true;
4065
4960
  break;
4066
4961
  case "--install-hooks":
4067
- options.installHooks = true;
4962
+ options2.installHooks = true;
4068
4963
  break;
4069
4964
  case "--uninstall-hooks":
4070
- options.uninstallHooks = true;
4965
+ options2.uninstallHooks = true;
4071
4966
  break;
4072
4967
  case "--version":
4073
- options.version = true;
4968
+ options2.version = true;
4074
4969
  break;
4075
4970
  case "--help":
4076
- options.help = true;
4971
+ options2.help = true;
4077
4972
  break;
4078
4973
  }
4079
4974
  }
4080
- return options;
4975
+ return options2;
4081
4976
  };
4082
4977
  var main = () => {
4083
- const options = parseArgs(process.argv);
4084
- if (options.version) {
4978
+ const options2 = parseArgs(process.argv);
4979
+ if (options2.version) {
4085
4980
  write2(`agenttop v${VERSION}`);
4086
4981
  process.exit(0);
4087
4982
  }
4088
- if (options.help) {
4983
+ if (options2.help) {
4089
4984
  write2(HELP);
4090
4985
  process.exit(0);
4091
4986
  }
4092
- if (options.installHooks) {
4987
+ if (options2.installHooks) {
4093
4988
  installHooks();
4094
4989
  process.exit(0);
4095
4990
  }
4096
- if (options.uninstallHooks) {
4991
+ if (options2.uninstallHooks) {
4097
4992
  uninstallHooks();
4098
4993
  process.exit(0);
4099
4994
  }
4100
- if (options.installMcp) {
4995
+ if (options2.installMcp) {
4101
4996
  installMcpConfig();
4102
4997
  process.exit(0);
4103
4998
  }
4104
- if (options.mcp) {
4105
- startMcpServer(options.allUsers, options.noSecurity).catch((err) => {
4999
+ if (options2.mcp) {
5000
+ startMcpServer(options2.allUsers, options2.noSecurity).catch((err) => {
4106
5001
  process.stderr.write(`mcp server error: ${err}
4107
5002
  `);
4108
5003
  process.exit(1);
4109
5004
  });
4110
5005
  return;
4111
5006
  }
4112
- if (options.json || options.plain) {
4113
- runStreamMode(options, options.json);
5007
+ if (options2.json || options2.plain) {
5008
+ runStreamMode(options2, options2.json);
4114
5009
  return;
4115
5010
  }
4116
5011
  const config = loadConfig();
4117
5012
  const firstRun = isFirstRun();
4118
- if (options.noNotify) {
5013
+ if (options2.noNotify) {
4119
5014
  config.notifications.bell = false;
4120
5015
  config.notifications.desktop = false;
4121
5016
  }
4122
- if (options.noAlertLog) config.alerts.enabled = false;
4123
- if (options.noUpdates) config.updates.checkOnLaunch = false;
4124
- if (options.noSecurity) config.security.enabled = false;
5017
+ if (options2.noAlertLog) config.alerts.enabled = false;
5018
+ if (options2.noUpdates) config.updates.checkOnLaunch = false;
5019
+ if (options2.noSecurity) config.security.enabled = false;
4125
5020
  if (firstRun) saveConfig(config);
4126
- render(React17.createElement(App, { options, config, version: VERSION, firstRun }));
5021
+ render(React19.createElement(App, { options: options2, config, version: VERSION, firstRun }));
4127
5022
  };
4128
5023
  main();
4129
5024
  //# sourceMappingURL=index.js.map