@oxgeneral/orch 0.2.3 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (145) hide show
  1. package/dist/App-TW35IULR.js +18 -0
  2. package/dist/agent-FRQKL7YI.js +9 -0
  3. package/dist/{orchestrator-VGYKSOZJ.js → chunk-2UC4SVJB.js} +236 -71
  4. package/dist/chunk-2UC4SVJB.js.map +1 -0
  5. package/dist/chunk-5AJ4LYO5.js +8 -0
  6. package/dist/{chunk-45K2XID7.js → chunk-6DWHQPTE.js} +2 -1
  7. package/dist/chunk-6DWHQPTE.js.map +1 -0
  8. package/dist/{chunk-POUC4CPC.js → chunk-6MJ7V6VY.js} +2 -2
  9. package/dist/{chunk-HNKJ4IF7.js → chunk-B4JQM4NU.js} +34 -10
  10. package/dist/chunk-B4JQM4NU.js.map +1 -0
  11. package/dist/{chunk-6HENRUYZ.js → chunk-CDFA4IIQ.js} +2 -2
  12. package/dist/chunk-CHRW4CLD.js +2 -0
  13. package/dist/{chunk-ZU6AY2VU.js → chunk-GZ2Q56YZ.js} +2 -2
  14. package/dist/chunk-HMMPM7MF.js +3 -0
  15. package/dist/{chunk-AELEEEV3.js → chunk-HSBYJ5C5.js} +27 -7
  16. package/dist/chunk-HXOMNULD.js +2 -0
  17. package/dist/{chunk-O5AO5QIR.js → chunk-IQXRQBUK.js} +9 -2
  18. package/dist/chunk-IQXRQBUK.js.map +1 -0
  19. package/dist/chunk-L26TK7Y5.js +2 -0
  20. package/dist/chunk-L3FYR45M.js +2 -0
  21. package/dist/chunk-LXNRCJ22.js +2 -0
  22. package/dist/{chunk-TX7WOFCW.js → chunk-MGFMVPRD.js} +4 -7
  23. package/dist/chunk-MGFMVPRD.js.map +1 -0
  24. package/dist/chunk-MNXU3KCD.js +2 -0
  25. package/dist/{chunk-CHIP7O6V.js → chunk-O2MSGW3V.js} +3 -1
  26. package/dist/chunk-O2MSGW3V.js.map +1 -0
  27. package/dist/chunk-PJ5DKXGR.js +2 -0
  28. package/dist/{chunk-VTA74YWX.js → chunk-QEEM67OA.js} +11 -17
  29. package/dist/chunk-QEEM67OA.js.map +1 -0
  30. package/dist/chunk-UMZEA3JT.js +5 -0
  31. package/dist/{shell-OGTSH4RJ.js → chunk-UW6GUUE6.js} +3 -3
  32. package/dist/chunk-XDVMX2FO.js +8 -0
  33. package/dist/chunk-XDVMX2FO.js.map +1 -0
  34. package/dist/chunk-ZA5Z33GO.js +11 -0
  35. package/dist/claude-E36EGXUV.js +2 -0
  36. package/dist/{chunk-IRN2U2NE.js → claude-RIB3RQS5.js} +5 -2
  37. package/dist/claude-RIB3RQS5.js.map +1 -0
  38. package/dist/cli.js +1 -199
  39. package/dist/clipboard-service-PDTSZIR5.js +25 -0
  40. package/dist/codex-OTZKVESD.js +2 -0
  41. package/dist/{codex-U7LTJTX6.js → codex-VBUSA2GJ.js} +5 -3
  42. package/dist/codex-VBUSA2GJ.js.map +1 -0
  43. package/dist/config-CCSS2P7R.js +2 -0
  44. package/dist/container-OIXLFSX2.js +6 -0
  45. package/dist/context-GSMQHQES.js +7 -0
  46. package/dist/cursor-3DJA6LWS.js +2 -0
  47. package/dist/{cursor-3DI5GKRF.js → cursor-4QIOTDBW.js} +5 -3
  48. package/dist/cursor-4QIOTDBW.js.map +1 -0
  49. package/dist/doctor-KBK5JZBZ.js +2 -0
  50. package/dist/doctor-service-F2SXDWHS.js +91 -0
  51. package/dist/doctor-service-F2SXDWHS.js.map +1 -0
  52. package/dist/doctor-service-PB7YBH3F.js +2 -0
  53. package/dist/goal-RFKFPR7M.js +8 -0
  54. package/dist/index.d.ts +124 -46
  55. package/dist/index.js +1817 -5
  56. package/dist/index.js.map +1 -1
  57. package/dist/init-WRDFAFS2.js +53 -0
  58. package/dist/logs-5QHJWMEG.js +12 -0
  59. package/dist/msg-4SCLBO4K.js +9 -0
  60. package/dist/orchestrator-FGGXK3N3.js +5 -0
  61. package/dist/{orchestrator-TAFBYQQ5.js.map → orchestrator-FGGXK3N3.js.map} +1 -1
  62. package/dist/orchestrator-R7IWZUT6.js +13 -0
  63. package/dist/process-manager-33H27MQF.js +2 -0
  64. package/dist/process-manager-A36Y7LHP.js +3 -0
  65. package/dist/{process-manager-TLZOTO4Y.js.map → process-manager-A36Y7LHP.js.map} +1 -1
  66. package/dist/registry-BO2PPRNG.js +2 -0
  67. package/dist/registry-JXXRLJ5J.js +3 -0
  68. package/dist/{registry-UQAHK77P.js.map → registry-JXXRLJ5J.js.map} +1 -1
  69. package/dist/run-HSHRELOP.js +3 -0
  70. package/dist/shell-EOJBDWTH.js +2 -0
  71. package/dist/{chunk-CIIE6LNG.js → shell-IH2MMTVP.js} +3 -2
  72. package/dist/shell-IH2MMTVP.js.map +1 -0
  73. package/dist/status-DLBNWSWM.js +2 -0
  74. package/dist/task-J6ZN7ALI.js +20 -0
  75. package/dist/team-MSIBKOQC.js +4 -0
  76. package/dist/template-engine-MFL5B677.js +3 -0
  77. package/dist/{template-engine-322SCRR6.js.map → template-engine-MFL5B677.js.map} +1 -1
  78. package/dist/template-engine-ONIDVD4F.js +2 -0
  79. package/dist/tui-G4XUFAIP.js +2 -0
  80. package/dist/update-PC2ENCKU.js +2 -0
  81. package/dist/update-check-HGMBDYHL.js +2 -0
  82. package/dist/workspace-manager-KOOYTO7E.js +3 -0
  83. package/dist/{workspace-manager-47KI7B27.js → workspace-manager-T6AXG7XL.js} +40 -3
  84. package/dist/workspace-manager-T6AXG7XL.js.map +1 -0
  85. package/package.json +2 -1
  86. package/readme.md +5 -4
  87. package/scripts/benchmark.ts +304 -0
  88. package/dist/App-KDZSTAMR.js +0 -4864
  89. package/dist/agent-V5M2C3OC.js +0 -157
  90. package/dist/chunk-2B32FPEB.js +0 -11
  91. package/dist/chunk-2B32FPEB.js.map +0 -1
  92. package/dist/chunk-33QNTNR6.js +0 -46
  93. package/dist/chunk-6GFVB6EK.js +0 -101
  94. package/dist/chunk-6HENRUYZ.js.map +0 -1
  95. package/dist/chunk-AELEEEV3.js.map +0 -1
  96. package/dist/chunk-E3TCKHU6.js +0 -13
  97. package/dist/chunk-E3TCKHU6.js.map +0 -1
  98. package/dist/chunk-ED47GL3F.js +0 -29
  99. package/dist/chunk-HXYAZGLP.js +0 -15
  100. package/dist/chunk-I5WEMARW.js +0 -166
  101. package/dist/chunk-IZYSGYXG.js +0 -2
  102. package/dist/chunk-IZYSGYXG.js.map +0 -1
  103. package/dist/chunk-P6ATSXGL.js +0 -107
  104. package/dist/chunk-PBFE5V3G.js +0 -2
  105. package/dist/chunk-PBFE5V3G.js.map +0 -1
  106. package/dist/chunk-PNE6LQRF.js +0 -5
  107. package/dist/chunk-POUC4CPC.js.map +0 -1
  108. package/dist/chunk-XI4TU6VU.js +0 -50
  109. package/dist/chunk-ZU6AY2VU.js.map +0 -1
  110. package/dist/claude-GH6P2DC5.js +0 -4
  111. package/dist/claude-S47YTIHU.js +0 -2
  112. package/dist/claude-S47YTIHU.js.map +0 -1
  113. package/dist/codex-2CH57B7G.js +0 -2
  114. package/dist/codex-2CH57B7G.js.map +0 -1
  115. package/dist/config-LJFM55LN.js +0 -75
  116. package/dist/container-JV7TAUP5.js +0 -1532
  117. package/dist/context-EPSDCJTU.js +0 -83
  118. package/dist/cursor-QFUNKPCQ.js +0 -2
  119. package/dist/cursor-QFUNKPCQ.js.map +0 -1
  120. package/dist/doctor-IO4PV4D6.js +0 -67
  121. package/dist/doctor-service-A34DHPKI.js +0 -2
  122. package/dist/doctor-service-NTWBWOM2.js +0 -2
  123. package/dist/doctor-service-NTWBWOM2.js.map +0 -1
  124. package/dist/goal-I56QP7HS.js +0 -110
  125. package/dist/init-BE5VKWOM.js +0 -149
  126. package/dist/logs-IAUAS5TX.js +0 -207
  127. package/dist/msg-SQWQLJP6.js +0 -95
  128. package/dist/orchestrator-TAFBYQQ5.js +0 -2
  129. package/dist/process-manager-HUVNAPQV.js +0 -2
  130. package/dist/process-manager-TLZOTO4Y.js +0 -2
  131. package/dist/registry-PQWRVNF2.js +0 -2
  132. package/dist/registry-UQAHK77P.js +0 -2
  133. package/dist/run-PSZURVVL.js +0 -95
  134. package/dist/shell-5ZNXFGXV.js +0 -3
  135. package/dist/shell-OGTSH4RJ.js.map +0 -1
  136. package/dist/status-DTF7D3DV.js +0 -56
  137. package/dist/task-5OJTXW27.js +0 -209
  138. package/dist/team-AISPLEJB.js +0 -97
  139. package/dist/template-engine-322SCRR6.js +0 -2
  140. package/dist/template-engine-3CDRZNMJ.js +0 -3
  141. package/dist/tui-XDJE3IUA.js +0 -225
  142. package/dist/update-72GZMF65.js +0 -64
  143. package/dist/update-check-4RV7Z6WT.js +0 -2
  144. package/dist/workspace-manager-7M46ESUL.js +0 -2
  145. package/dist/workspace-manager-7M46ESUL.js.map +0 -1
@@ -1,4864 +0,0 @@
1
- #!/usr/bin/env node
2
- import { DEFAULT_CONFIG } from './chunk-ED47GL3F.js';
3
- import { GOAL_STATUS_ORDER } from './chunk-HXYAZGLP.js';
4
- import { formatDuration, formatDurationSince } from './chunk-I5WEMARW.js';
5
- import React5, { useState, useEffect, useMemo, useRef, useCallback } from 'react';
6
- import { Box, Text, useApp, useStdout, useInput } from 'ink';
7
- import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
8
-
9
- // src/tui/colors.ts
10
- var tuiColors = {
11
- // Brand
12
- amber: "#ffaf00",
13
- // ansi256(214) — logo, selected cursor, accents
14
- amberDim: "#af8700",
15
- // ansi256(136) — amber at reduced intensity
16
- // Semantic
17
- green: "#5faf87",
18
- // ansi256(72) — running, success, active
19
- red: "#d75f5f",
20
- // ansi256(167) — failed, P1, errors
21
- blue: "#5fafd7",
22
- // ansi256(74) — review, info
23
- yellow: "#d7af00",
24
- // ansi256(178) — retrying, P2, warning
25
- cyan: "#5fd7d7",
26
- // ansi256(80) — timestamps, metadata
27
- purple: "#af87ff",
28
- // ansi256(141) — file paths, labels
29
- // Neutrals (high → low intensity)
30
- white: "#eeeeee",
31
- // ansi256(255) — primary text
32
- silver: "#bcbcbc",
33
- // ansi256(250) — secondary text
34
- gray: "#808080",
35
- // ansi256(244) — tertiary text
36
- dim: "#585858",
37
- // ansi256(240) — subtle text, deemphasized
38
- ghost: "#3a3a3a",
39
- // ansi256(237) — rules, separators
40
- void: "#262626",
41
- // ansi256(235) — deepest background elements
42
- // Semantic backgrounds (for log highlights)
43
- errorBg: "#3d1515",
44
- // deep red background for errors
45
- warnBg: "#3d2e0a",
46
- // deep amber background for warnings
47
- successBg: "#0f2d1f",
48
- // deep green background for success
49
- infoBg: "#1a1a22"};
50
- var HEAVY_RULE = "\u2501";
51
- var LIGHT_RULE = "\u2500";
52
- var DOT = "\xB7";
53
- var LOZENGE = "\u25C8";
54
- var STAR = "\u2605";
55
- var LOOP = "\u27F3";
56
- var DIAMOND = "\u25C6";
57
- var TICK_INTERVAL = 120;
58
- var globalTick = 0;
59
- var timer = null;
60
- var listeners = /* @__PURE__ */ new Set();
61
- function startGlobal() {
62
- if (timer) return;
63
- timer = setInterval(() => {
64
- globalTick++;
65
- for (const fn of listeners) fn(globalTick);
66
- }, TICK_INTERVAL);
67
- }
68
- function stopGlobal() {
69
- if (timer && listeners.size === 0) {
70
- clearInterval(timer);
71
- timer = null;
72
- }
73
- }
74
- function useAnimTick() {
75
- const [tick, setTick] = useState(globalTick);
76
- useEffect(() => {
77
- const listener = (t) => setTick(t);
78
- listeners.add(listener);
79
- startGlobal();
80
- return () => {
81
- listeners.delete(listener);
82
- stopGlobal();
83
- };
84
- }, []);
85
- return tick;
86
- }
87
- var FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
88
- function Spinner({ color }) {
89
- const tick = useAnimTick();
90
- return /* @__PURE__ */ jsx(Text, { color, children: FRAMES[tick % FRAMES.length] });
91
- }
92
- var STATUS_ORDER = {
93
- in_progress: 0,
94
- retrying: 1,
95
- review: 2,
96
- todo: 3,
97
- done: 4,
98
- failed: 5,
99
- cancelled: 6
100
- };
101
- var EMPTY_CIRCLE = "\u25CB";
102
- var CHECK = "\u2713";
103
- var CROSS = "\u2715";
104
- var RETRY = "\u21BB";
105
- var DASH = "\u2500";
106
- var TRIANGLE = "\u25B6";
107
- var chipBg = {
108
- green: "#0f2d1f",
109
- blue: "#0f1f2d",
110
- yellow: "#2d2a0f",
111
- red: "#2d0f0f",
112
- neutral: "#1a1a22"};
113
- var STATUS_CHIP = {
114
- in_progress: { icon: TRIANGLE, label: "RUN", fg: tuiColors.green, bg: chipBg.green, bold: true, spinner: true },
115
- retrying: { icon: RETRY, label: "RETRY", fg: tuiColors.yellow, bg: chipBg.yellow, spinner: true },
116
- review: { icon: LOZENGE, label: "REVIEW", fg: tuiColors.blue, bg: chipBg.blue },
117
- todo: { icon: EMPTY_CIRCLE, label: "TODO", fg: tuiColors.dim, bg: chipBg.neutral },
118
- done: { icon: CHECK, label: "DONE", fg: tuiColors.green, bg: chipBg.green },
119
- failed: { icon: CROSS, label: "FAIL", fg: tuiColors.red, bg: chipBg.red, bold: true },
120
- cancelled: { icon: DASH, label: "OFF", fg: tuiColors.dim, bg: chipBg.neutral }
121
- };
122
- var PRIORITY_CONFIG = {
123
- 1: { color: tuiColors.red, label: "!!!" },
124
- 2: { color: tuiColors.yellow, label: "!!" },
125
- 3: { color: tuiColors.dim, label: "!" },
126
- 4: { color: tuiColors.ghost, label: DOT }
127
- };
128
- var TaskRow = React5.memo(function TaskRow2({ task, selected, width, agentNameMap }) {
129
- const chip = STATUS_CHIP[task.status];
130
- const isRunning = task.status === "in_progress" || task.status === "retrying";
131
- const priConf = PRIORITY_CONFIG[task.priority] ?? { color: tuiColors.ghost, label: DOT };
132
- let timeStr;
133
- let timeColor;
134
- if (task.status === "done") {
135
- timeStr = CHECK;
136
- timeColor = tuiColors.green;
137
- } else if (task.status === "failed") {
138
- timeStr = CROSS;
139
- timeColor = tuiColors.red;
140
- } else if (isRunning) {
141
- const ms = Date.now() - new Date(task.updated_at).getTime();
142
- timeStr = formatDuration(ms);
143
- timeColor = tuiColors.cyan;
144
- } else {
145
- timeStr = "\u2014";
146
- timeColor = void 0;
147
- }
148
- const cursor = selected ? "\u25B8" : " ";
149
- const chipWidth = 10;
150
- const priWidth = 4;
151
- const assigneeWidth = 14;
152
- const timeWidth = 7;
153
- const fixedCols = 2 + chipWidth + priWidth + assigneeWidth + timeWidth;
154
- const titleWidth = width ? Math.max(10, width - fixedCols) : 40;
155
- const assigneeName = task.assignee ? agentNameMap?.get(task.assignee) ?? task.assignee : void 0;
156
- return /* @__PURE__ */ jsxs(Box, { children: [
157
- /* @__PURE__ */ jsxs(Text, { color: selected ? tuiColors.amber : void 0, children: [
158
- cursor,
159
- " "
160
- ] }),
161
- /* @__PURE__ */ jsx(Box, { width: chipWidth, children: /* @__PURE__ */ jsx(Text, { backgroundColor: chip.bg, color: chip.fg, bold: chip.bold, children: chip.spinner ? /* @__PURE__ */ jsxs(Fragment, { children: [
162
- " ",
163
- /* @__PURE__ */ jsx(Spinner, { color: chip.fg }),
164
- " ",
165
- chip.label,
166
- " "
167
- ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
168
- " ",
169
- chip.icon,
170
- " ",
171
- chip.label,
172
- " "
173
- ] }) }) }),
174
- /* @__PURE__ */ jsx(Box, { width: priWidth, children: /* @__PURE__ */ jsx(Text, { color: priConf.color, bold: task.priority <= 2, children: priConf.label }) }),
175
- /* @__PURE__ */ jsx(Box, { width: titleWidth, children: /* @__PURE__ */ jsx(
176
- Text,
177
- {
178
- wrap: "truncate",
179
- bold: selected || isRunning,
180
- color: selected ? tuiColors.white : isRunning ? tuiColors.silver : void 0,
181
- children: task.title.length > titleWidth ? task.title.slice(0, titleWidth - 1) + "\u2026" : task.title
182
- }
183
- ) }),
184
- /* @__PURE__ */ jsx(Box, { width: assigneeWidth, children: assigneeName ? /* @__PURE__ */ jsxs(Text, { backgroundColor: chipBg.green, color: tuiColors.green, wrap: "truncate", children: [
185
- " ",
186
- assigneeName.length > assigneeWidth - 2 ? assigneeName.slice(0, assigneeWidth - 3) + "\u2026" : assigneeName,
187
- " "
188
- ] }) : /* @__PURE__ */ jsx(Text, { color: tuiColors.ghost, children: "\u2014" }) }),
189
- /* @__PURE__ */ jsx(Box, { width: timeWidth, justifyContent: "flex-end", children: /* @__PURE__ */ jsx(Text, { color: timeColor, dimColor: !timeColor, children: timeStr }) })
190
- ] });
191
- });
192
- var CROSS2 = "\u2715";
193
- var EMPTY_CIRCLE2 = "\u25CB";
194
- var TRIANGLE2 = "\u25B6";
195
- var CHECK2 = "\u2713";
196
- var chipBg2 = {
197
- green: "#0f2d1f",
198
- red: "#2d0f0f",
199
- neutral: "#1a1a22",
200
- amber: "#2d1f0a"
201
- };
202
- var STATUS_CHIP2 = {
203
- running: { icon: TRIANGLE2, label: "ACTIVE", fg: tuiColors.green, bg: chipBg2.green, bold: true, spinner: true },
204
- idle: { icon: EMPTY_CIRCLE2, label: "IDLE", fg: tuiColors.dim, bg: chipBg2.neutral },
205
- error: { icon: CROSS2, label: "ERROR", fg: tuiColors.red, bg: chipBg2.red, bold: true },
206
- disabled: { icon: EMPTY_CIRCLE2, label: "OFF", fg: tuiColors.ghost, bg: chipBg2.neutral }
207
- };
208
- var AGENT_STATUS_ORDER = {
209
- running: 0,
210
- idle: 1,
211
- error: 2,
212
- disabled: 3
213
- };
214
- var AgentRow = React5.memo(function AgentRow2({ agent, selected, width, runningEntry, currentTaskTitle, teamName, isLead }) {
215
- const chip = STATUS_CHIP2[agent.status];
216
- const isRunning = agent.status === "running";
217
- let timeStr;
218
- let timeColor;
219
- if (isRunning && runningEntry) {
220
- const ms = Date.now() - new Date(runningEntry.started_at).getTime();
221
- timeStr = formatDuration(ms);
222
- timeColor = tuiColors.cyan;
223
- } else if (agent.stats.total_runs > 0) {
224
- timeStr = `${agent.stats.tasks_completed}/${agent.stats.total_runs}`;
225
- timeColor = agent.stats.tasks_completed > 0 ? tuiColors.green : tuiColors.dim;
226
- } else {
227
- timeStr = "\u2014";
228
- timeColor = void 0;
229
- }
230
- const cursor = selected ? "\u25B8" : " ";
231
- const chipWidth = 11;
232
- const adapterWidth = 10;
233
- const teamColWidth = teamName ? Math.min(teamName.length + 2, 14) : 0;
234
- const roleWidth = Math.max(6, 22 - teamColWidth);
235
- const timeWidth = 7;
236
- const fixedCols = 2 + chipWidth + adapterWidth + teamColWidth + roleWidth + timeWidth;
237
- const nameWidth = width ? Math.max(8, width - fixedCols) : 20;
238
- let roleText;
239
- let roleColor;
240
- let roleBold = false;
241
- if (isRunning && currentTaskTitle) {
242
- roleText = currentTaskTitle;
243
- roleColor = tuiColors.white;
244
- roleBold = true;
245
- } else if (agent.role) {
246
- roleText = agent.role;
247
- roleColor = tuiColors.dim;
248
- } else {
249
- roleText = "\u2014";
250
- roleColor = tuiColors.ghost;
251
- }
252
- const hasHistory = agent.stats.total_runs > 0;
253
- const successRate = hasHistory ? Math.round(agent.stats.tasks_completed / agent.stats.total_runs * 100) : 0;
254
- return /* @__PURE__ */ jsxs(Box, { children: [
255
- /* @__PURE__ */ jsxs(Text, { color: selected ? tuiColors.amber : void 0, children: [
256
- cursor,
257
- " "
258
- ] }),
259
- /* @__PURE__ */ jsx(Box, { width: chipWidth, children: /* @__PURE__ */ jsx(Text, { backgroundColor: chip.bg, color: chip.fg, bold: chip.bold, children: chip.spinner ? /* @__PURE__ */ jsxs(Fragment, { children: [
260
- " ",
261
- /* @__PURE__ */ jsx(Spinner, { color: chip.fg }),
262
- " ",
263
- chip.label,
264
- " "
265
- ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
266
- " ",
267
- chip.icon,
268
- " ",
269
- chip.label,
270
- " "
271
- ] }) }) }),
272
- /* @__PURE__ */ jsx(Box, { width: nameWidth, children: /* @__PURE__ */ jsxs(
273
- Text,
274
- {
275
- wrap: "truncate",
276
- bold: selected || isRunning,
277
- color: selected ? tuiColors.white : isRunning ? tuiColors.green : tuiColors.silver,
278
- children: [
279
- agent.autonomous && /* @__PURE__ */ jsxs(Text, { color: tuiColors.cyan, children: [
280
- LOOP,
281
- " "
282
- ] }),
283
- isLead && /* @__PURE__ */ jsxs(Text, { color: tuiColors.amber, children: [
284
- STAR,
285
- " "
286
- ] }),
287
- agent.name
288
- ]
289
- }
290
- ) }),
291
- /* @__PURE__ */ jsx(Box, { width: adapterWidth, children: /* @__PURE__ */ jsxs(Text, { backgroundColor: chipBg2.neutral, color: tuiColors.dim, children: [
292
- " ",
293
- agent.adapter,
294
- " "
295
- ] }) }),
296
- teamName && /* @__PURE__ */ jsx(Box, { width: teamColWidth, children: /* @__PURE__ */ jsxs(Text, { backgroundColor: chipBg2.amber, color: tuiColors.amber, wrap: "truncate", children: [
297
- " ",
298
- teamName,
299
- " "
300
- ] }) }),
301
- /* @__PURE__ */ jsx(Box, { width: roleWidth, children: /* @__PURE__ */ jsx(Text, { color: roleColor, bold: roleBold, wrap: "truncate", children: roleText }) }),
302
- /* @__PURE__ */ jsx(Box, { width: timeWidth, justifyContent: "flex-end", children: hasHistory && !isRunning ? /* @__PURE__ */ jsxs(Text, { color: successRate >= 80 ? tuiColors.green : successRate >= 50 ? tuiColors.yellow : tuiColors.red, children: [
303
- timeStr,
304
- " ",
305
- CHECK2
306
- ] }) : /* @__PURE__ */ jsx(Text, { color: timeColor, dimColor: !timeColor, children: timeStr }) })
307
- ] });
308
- });
309
- function TeamSectionRow({ teamName, memberCount, leadName, width }) {
310
- const count = `${memberCount} agent${memberCount !== 1 ? "s" : ""}`;
311
- const leadStr = leadName ? ` ${DOT} ${STAR} ${leadName}` : "";
312
- const label = ` ${LOZENGE} ${teamName.toUpperCase()} ${DOT} ${count}${leadStr} `;
313
- const leftLen = 3;
314
- const rightLen = Math.max(0, width - leftLen - label.length - 4);
315
- return /* @__PURE__ */ jsxs(Box, { paddingX: 2, children: [
316
- /* @__PURE__ */ jsx(Text, { color: tuiColors.ghost, children: "\u2500".repeat(leftLen) }),
317
- /* @__PURE__ */ jsx(Text, { backgroundColor: chipBg2.amber, color: tuiColors.amber, bold: true, children: label }),
318
- /* @__PURE__ */ jsx(Text, { color: tuiColors.ghost, children: "\u2500".repeat(rightLen) })
319
- ] });
320
- }
321
- var EMPTY_DIAMOND = "\u25C7";
322
- function UnassignedSectionRow({ memberCount, width }) {
323
- const count = `${memberCount} agent${memberCount !== 1 ? "s" : ""}`;
324
- const label = ` ${EMPTY_DIAMOND} UNASSIGNED ${DOT} ${count} `;
325
- const leftLen = 3;
326
- const rightLen = Math.max(0, width - leftLen - label.length - 4);
327
- return /* @__PURE__ */ jsxs(Box, { paddingX: 2, children: [
328
- /* @__PURE__ */ jsx(Text, { color: tuiColors.ghost, children: "\u2500".repeat(leftLen) }),
329
- /* @__PURE__ */ jsx(Text, { backgroundColor: chipBg2.neutral, color: tuiColors.dim, children: label }),
330
- /* @__PURE__ */ jsx(Text, { color: tuiColors.ghost, children: "\u2500".repeat(rightLen) })
331
- ] });
332
- }
333
- var CHECK3 = "\u2713";
334
- var CROSS3 = "\u2715";
335
- var PAUSE = "\u2016";
336
- var TARGET = "\u25C9";
337
- var chipBg3 = {
338
- green: "#0f2d1f",
339
- amber: "#2d1f0a",
340
- neutral: "#1a1a22"};
341
- var STATUS_CHIP3 = {
342
- active: { icon: TARGET, label: "ACTIVE", fg: tuiColors.green, bg: chipBg3.green, bold: true },
343
- paused: { icon: PAUSE, label: "PAUSED", fg: tuiColors.dim, bg: chipBg3.neutral },
344
- achieved: { icon: CHECK3, label: "DONE", fg: tuiColors.amber, bg: chipBg3.amber, bold: true },
345
- abandoned: { icon: CROSS3, label: "DROP", fg: tuiColors.ghost, bg: chipBg3.neutral }
346
- };
347
- var GoalRow = React5.memo(function GoalRow2({ goal, selected, width, agentNameMap }) {
348
- const chip = STATUS_CHIP3[goal.status];
349
- const cursor = selected ? "\u25B8" : " ";
350
- const chipWidth = 11;
351
- const assigneeWidth = 14;
352
- const timeWidth = 7;
353
- const fixedCols = 2 + chipWidth + assigneeWidth + timeWidth;
354
- const titleWidth = width ? Math.max(10, width - fixedCols) : 40;
355
- const assigneeName = goal.assignee ? agentNameMap?.get(goal.assignee) ?? goal.assignee : void 0;
356
- let timeStr;
357
- let timeColor;
358
- if (goal.status === "achieved") {
359
- timeStr = CHECK3;
360
- timeColor = tuiColors.amber;
361
- } else if (goal.status === "abandoned") {
362
- timeStr = CROSS3;
363
- timeColor = tuiColors.ghost;
364
- } else {
365
- const ms = Date.now() - new Date(goal.created_at).getTime();
366
- const days = Math.floor(ms / 864e5);
367
- timeStr = days > 0 ? `${days}d` : "<1d";
368
- timeColor = tuiColors.dim;
369
- }
370
- return /* @__PURE__ */ jsxs(Box, { children: [
371
- /* @__PURE__ */ jsxs(Text, { color: selected ? tuiColors.amber : void 0, children: [
372
- cursor,
373
- " "
374
- ] }),
375
- /* @__PURE__ */ jsx(Box, { width: chipWidth, children: /* @__PURE__ */ jsxs(Text, { backgroundColor: chip.bg, color: chip.fg, bold: chip.bold, children: [
376
- " ",
377
- chip.icon,
378
- " ",
379
- chip.label,
380
- " "
381
- ] }) }),
382
- /* @__PURE__ */ jsx(Box, { width: titleWidth, children: /* @__PURE__ */ jsx(
383
- Text,
384
- {
385
- wrap: "truncate",
386
- bold: selected || goal.status === "active",
387
- color: selected ? tuiColors.white : goal.status === "active" ? tuiColors.silver : void 0,
388
- children: goal.title.length > titleWidth ? goal.title.slice(0, titleWidth - 1) + "\u2026" : goal.title
389
- }
390
- ) }),
391
- /* @__PURE__ */ jsx(Box, { width: assigneeWidth, children: assigneeName ? /* @__PURE__ */ jsxs(Text, { backgroundColor: chipBg3.green, color: tuiColors.green, wrap: "truncate", children: [
392
- " ",
393
- assigneeName.length > assigneeWidth - 2 ? assigneeName.slice(0, assigneeWidth - 3) + "\u2026" : assigneeName,
394
- " "
395
- ] }) : /* @__PURE__ */ jsx(Text, { color: tuiColors.ghost, children: "\u2014" }) }),
396
- /* @__PURE__ */ jsx(Box, { width: timeWidth, justifyContent: "flex-end", children: /* @__PURE__ */ jsx(Text, { color: timeColor, dimColor: !timeColor, children: timeStr }) })
397
- ] });
398
- });
399
- var MSG_ICONS = {
400
- system: "\u2666",
401
- // ◆
402
- lifecycle: "\u25B6",
403
- // ▶
404
- output: "\u2502",
405
- // │
406
- tool: "\u2699",
407
- // ⚙
408
- result: "\u2190",
409
- // ←
410
- error: "\u2715",
411
- // ✕
412
- file: "\u270E",
413
- // ✎
414
- info: "\u2502"
415
- // │
416
- };
417
- function SectionDivider({ label, width, color }) {
418
- const innerW = width - 4;
419
- const labelWithSpaces = ` ${label} `;
420
- const leftLen = 3;
421
- const rightLen = Math.max(0, innerW - leftLen - labelWithSpaces.length);
422
- return /* @__PURE__ */ jsxs(Text, { color: color ?? tuiColors.ghost, children: [
423
- " ",
424
- "\u2500".repeat(leftLen),
425
- labelWithSpaces,
426
- "\u2500".repeat(rightLen)
427
- ] });
428
- }
429
- function DetailPanel({ task, height, width, taskLogs, agentNameMap }) {
430
- const statusColor = STATUS_DETAIL_COLOR[task.status] ?? tuiColors.dim;
431
- const priColor = task.priority <= 2 ? task.priority === 1 ? tuiColors.red : tuiColors.yellow : void 0;
432
- const col1Width = 24;
433
- const hasDescription = !!task.description?.trim();
434
- const hasSummary = !!task.proof?.agent_summary;
435
- const hasFiles = (task.proof?.files_changed?.length ?? 0) > 0;
436
- const hasLogs = (taskLogs?.length ?? 0) > 0;
437
- const descLines = hasDescription ? task.description.split("\n") : [];
438
- const summaryLines = hasSummary ? task.proof.agent_summary.split("\n") : [];
439
- let usedRows = 3;
440
- if (hasDescription) {
441
- usedRows += 1;
442
- usedRows += Math.min(descLines.length, Math.max(1, Math.ceil((height - 10) * 0.3)));
443
- } else if (!hasSummary) {
444
- usedRows += 2;
445
- }
446
- if (hasSummary) {
447
- usedRows += 2;
448
- }
449
- if (hasFiles) {
450
- usedRows += 1;
451
- }
452
- if (hasLogs) {
453
- usedRows += 2;
454
- }
455
- const remainingRows = Math.max(0, height - usedRows);
456
- let summaryMaxLines = 0;
457
- let logMaxLines = 0;
458
- if (hasSummary && hasLogs) {
459
- summaryMaxLines = Math.max(1, Math.floor(remainingRows * 0.4));
460
- logMaxLines = Math.max(1, remainingRows - summaryMaxLines);
461
- } else if (hasSummary) {
462
- summaryMaxLines = remainingRows;
463
- } else if (hasLogs) {
464
- logMaxLines = remainingRows;
465
- }
466
- const visibleDescLines = hasDescription ? descLines.slice(0, Math.max(1, Math.ceil((height - 10) * 0.3))) : [];
467
- const visibleSummaryLines = summaryLines.slice(0, summaryMaxLines);
468
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingX: 2, children: [
469
- /* @__PURE__ */ jsxs(Box, { children: [
470
- /* @__PURE__ */ jsxs(Box, { width: col1Width, children: [
471
- /* @__PURE__ */ jsx(Text, { color: tuiColors.dim, children: " status " }),
472
- /* @__PURE__ */ jsx(Text, { color: statusColor, children: task.status })
473
- ] }),
474
- /* @__PURE__ */ jsxs(Box, { children: [
475
- /* @__PURE__ */ jsx(Text, { color: tuiColors.dim, children: " assignee " }),
476
- /* @__PURE__ */ jsx(Text, { color: task.assignee ? tuiColors.green : tuiColors.dim, children: task.assignee ? agentNameMap?.get(task.assignee) ?? task.assignee : "\u2014" })
477
- ] })
478
- ] }),
479
- /* @__PURE__ */ jsxs(Box, { children: [
480
- /* @__PURE__ */ jsxs(Box, { width: col1Width, children: [
481
- /* @__PURE__ */ jsx(Text, { color: tuiColors.dim, children: " priority " }),
482
- /* @__PURE__ */ jsxs(Text, { color: priColor, bold: task.priority <= 2, children: [
483
- "P",
484
- task.priority
485
- ] })
486
- ] }),
487
- /* @__PURE__ */ jsxs(Box, { children: [
488
- /* @__PURE__ */ jsx(Text, { color: tuiColors.dim, children: " attempts " }),
489
- /* @__PURE__ */ jsxs(Text, { children: [
490
- task.attempts,
491
- "/",
492
- task.max_attempts
493
- ] })
494
- ] })
495
- ] }),
496
- /* @__PURE__ */ jsxs(Box, { children: [
497
- /* @__PURE__ */ jsxs(Box, { width: col1Width, children: [
498
- /* @__PURE__ */ jsx(Text, { color: tuiColors.dim, children: " labels " }),
499
- /* @__PURE__ */ jsx(Text, { color: tuiColors.purple, children: task.labels.length > 0 ? task.labels.join(", ") : "\u2014" })
500
- ] }),
501
- /* @__PURE__ */ jsxs(Box, { children: [
502
- /* @__PURE__ */ jsx(Text, { color: tuiColors.dim, children: " depends " }),
503
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: task.depends_on.length > 0 ? task.depends_on.join(", ") : "\u2014" })
504
- ] })
505
- ] }),
506
- hasDescription && /* @__PURE__ */ jsxs(Fragment, { children: [
507
- /* @__PURE__ */ jsx(Text, { children: " " }),
508
- visibleDescLines.map((line, i) => /* @__PURE__ */ jsxs(Text, { color: tuiColors.silver, wrap: "truncate", children: [
509
- " ",
510
- line
511
- ] }, `d${i}`))
512
- ] }),
513
- !hasDescription && !hasSummary && /* @__PURE__ */ jsxs(Fragment, { children: [
514
- /* @__PURE__ */ jsx(Text, { children: " " }),
515
- /* @__PURE__ */ jsx(Text, { color: tuiColors.dim, children: " No description." })
516
- ] }),
517
- hasSummary && /* @__PURE__ */ jsxs(Fragment, { children: [
518
- /* @__PURE__ */ jsx(Text, { children: " " }),
519
- /* @__PURE__ */ jsx(SectionDivider, { label: "result", width, color: tuiColors.dim }),
520
- visibleSummaryLines.map((line, i) => /* @__PURE__ */ jsxs(Text, { color: tuiColors.white, wrap: "truncate", children: [
521
- " ",
522
- line
523
- ] }, `r${i}`))
524
- ] }),
525
- hasFiles && /* @__PURE__ */ jsxs(Box, { children: [
526
- /* @__PURE__ */ jsx(Text, { color: tuiColors.dim, children: " files " }),
527
- /* @__PURE__ */ jsx(Text, { color: tuiColors.cyan, children: task.proof.files_changed.length }),
528
- /* @__PURE__ */ jsx(Text, { color: tuiColors.dim, children: " changed" }),
529
- task.proof.branch && /* @__PURE__ */ jsxs(Fragment, { children: [
530
- /* @__PURE__ */ jsxs(Text, { color: tuiColors.dim, children: [
531
- " ",
532
- "\xB7",
533
- " "
534
- ] }),
535
- /* @__PURE__ */ jsx(Text, { color: tuiColors.cyan, children: task.proof.branch })
536
- ] })
537
- ] }),
538
- hasLogs && /* @__PURE__ */ jsxs(Fragment, { children: [
539
- /* @__PURE__ */ jsx(Text, { children: " " }),
540
- /* @__PURE__ */ jsx(SectionDivider, { label: "activity", width, color: tuiColors.dim }),
541
- taskLogs.slice(-logMaxLines).map((log, i) => {
542
- const msgType = log.msgType ?? "info";
543
- const icon = MSG_ICONS[msgType] ?? "\u2502";
544
- let textColor = log.color;
545
- if (msgType === "tool") textColor = tuiColors.cyan;
546
- else if (msgType === "file") textColor = tuiColors.purple;
547
- else if (msgType === "error") textColor = tuiColors.red;
548
- else if (msgType === "lifecycle") textColor = tuiColors.green;
549
- else if (msgType === "system") textColor = tuiColors.dim;
550
- const logTextW = Math.max(10, width - 12);
551
- const logDisplay = log.text.length > logTextW ? log.text.slice(0, logTextW - 1) + "\u2026" : log.text;
552
- return /* @__PURE__ */ jsxs(Box, { children: [
553
- /* @__PURE__ */ jsxs(Text, { color: tuiColors.ghost, children: [
554
- " ",
555
- log.time,
556
- " "
557
- ] }),
558
- /* @__PURE__ */ jsxs(Text, { color: msgType === "error" ? tuiColors.red : tuiColors.dim, children: [
559
- icon,
560
- " "
561
- ] }),
562
- /* @__PURE__ */ jsx(Text, { color: textColor, bold: msgType === "lifecycle", children: logDisplay })
563
- ] }, i);
564
- })
565
- ] })
566
- ] });
567
- }
568
- var STATUS_DETAIL_COLOR = {
569
- in_progress: tuiColors.green,
570
- retrying: tuiColors.yellow,
571
- review: tuiColors.blue,
572
- todo: tuiColors.dim,
573
- done: tuiColors.green,
574
- failed: tuiColors.red,
575
- cancelled: tuiColors.dim
576
- };
577
- var TABS = [
578
- { key: "G", id: "goals", label: "GOALS" },
579
- { key: "T", id: "tasks", label: "TASKS" },
580
- { key: "A", id: "agents", label: "AGENTS" },
581
- { key: "L", id: "logs", label: "ACTIONS" }
582
- ];
583
- var FILLED_CIRCLE = "\u25CF";
584
- var EMPTY_CIRCLE3 = "\u25CB";
585
- var CHECK4 = "\u2713";
586
- var CROSS4 = "\u2715";
587
- var ARROW_UP = "\u2191";
588
- var ARROW_DOWN = "\u2193";
589
- var SIGMA = "\u03A3";
590
- var TRIANGLE_RIGHT = "\u25B6";
591
- var RETRY_ICON = "\u21BB";
592
- var BRAILLE_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
593
- var SPARK_CHARS = [" ", "\u2581", "\u2582", "\u2583", "\u2584", "\u2585", "\u2586", "\u2587", "\u2588"];
594
- var chipBg4 = {
595
- green: "#0f2d1f",
596
- // dark emerald tint
597
- blue: "#0f1f2d",
598
- // dark sky tint
599
- yellow: "#2d2a0f",
600
- // dark amber tint
601
- red: "#2d0f0f",
602
- // dark rose tint
603
- neutral: "#1a1a22",
604
- // dark slate
605
- amber: "#2d1f0a"
606
- // dark warm
607
- };
608
- function MiniSpinner({ color }) {
609
- const tick = useAnimTick();
610
- return /* @__PURE__ */ jsx(Text, { color, children: BRAILLE_FRAMES[tick % BRAILLE_FRAMES.length] });
611
- }
612
- function PulsingDiamond() {
613
- const tick = useAnimTick();
614
- const bright = Math.floor(tick / 10) % 2 === 0;
615
- return /* @__PURE__ */ jsx(Text, { color: bright ? tuiColors.amber : tuiColors.amberDim, bold: true, children: DIAMOND });
616
- }
617
- function ScannerBar({ width, active }) {
618
- const segLen = Math.max(4, Math.floor(width * 0.08));
619
- const step = 2;
620
- const tick = useAnimTick();
621
- const totalFrames = Math.ceil((width + segLen) / step);
622
- const pos = active ? tick % (totalFrames * 2) : 0;
623
- if (!active) {
624
- return /* @__PURE__ */ jsx(Box, { paddingX: 1, children: /* @__PURE__ */ jsx(Text, { color: tuiColors.ghost, children: HEAVY_RULE.repeat(width) }) });
625
- }
626
- const rawPos = pos < totalFrames ? pos * step : (totalFrames * 2 - pos) * step;
627
- const brightStart = Math.max(0, rawPos - segLen);
628
- const brightEnd = Math.min(width, rawPos);
629
- const beforeLen = brightStart;
630
- const brightLen = Math.max(0, brightEnd - brightStart);
631
- const afterLen = Math.max(0, width - brightEnd);
632
- return /* @__PURE__ */ jsxs(Box, { paddingX: 1, children: [
633
- /* @__PURE__ */ jsx(Text, { color: tuiColors.ghost, children: HEAVY_RULE.repeat(beforeLen) }),
634
- /* @__PURE__ */ jsx(Text, { color: tuiColors.amber, children: HEAVY_RULE.repeat(brightLen) }),
635
- /* @__PURE__ */ jsx(Text, { color: tuiColors.ghost, children: HEAVY_RULE.repeat(afterLen) })
636
- ] });
637
- }
638
- function Sparkline({ data, width, color }) {
639
- if (data.length === 0) return null;
640
- const display = data.slice(-width);
641
- const max = Math.max(...display, 1);
642
- const chars = display.map((v) => {
643
- const idx = Math.round(v / max * (SPARK_CHARS.length - 1));
644
- return SPARK_CHARS[idx] ?? SPARK_CHARS[0];
645
- }).join("");
646
- return /* @__PURE__ */ jsx(Text, { color, children: chars });
647
- }
648
- function BrandBar({
649
- projectName,
650
- activeView,
651
- mode,
652
- stats,
653
- uptime,
654
- width
655
- }) {
656
- const isWatching = mode === "watching";
657
- return /* @__PURE__ */ jsxs(Box, { paddingX: 1, justifyContent: "space-between", width, children: [
658
- /* @__PURE__ */ jsxs(Box, { gap: 0, children: [
659
- /* @__PURE__ */ jsx(PulsingDiamond, {}),
660
- /* @__PURE__ */ jsx(Text, { color: tuiColors.amber, bold: true, children: " ORCH" }),
661
- /* @__PURE__ */ jsxs(Text, { color: tuiColors.ghost, children: [
662
- " ",
663
- DOT,
664
- " "
665
- ] }),
666
- /* @__PURE__ */ jsx(Text, { color: tuiColors.silver, children: projectName })
667
- ] }),
668
- /* @__PURE__ */ jsx(Box, { gap: 0, children: TABS.map((tab, i) => {
669
- const isActive = activeView === tab.id;
670
- return /* @__PURE__ */ jsxs(React5.Fragment, { children: [
671
- i > 0 && /* @__PURE__ */ jsx(Text, { children: " " }),
672
- isActive ? (
673
- // Active tab: inverted chip — amber background, dark text
674
- /* @__PURE__ */ jsxs(Text, { backgroundColor: tuiColors.amber, color: "#0a0a0c", bold: true, children: [
675
- " ",
676
- tab.key,
677
- " ",
678
- tab.label,
679
- " "
680
- ] })
681
- ) : (
682
- // Inactive tab: ghost key + dim label
683
- /* @__PURE__ */ jsxs(Box, { gap: 0, children: [
684
- /* @__PURE__ */ jsx(Text, { color: tuiColors.ghost, children: tab.key }),
685
- /* @__PURE__ */ jsxs(Text, { color: tuiColors.dim, children: [
686
- " ",
687
- tab.label.toLowerCase()
688
- ] })
689
- ] })
690
- )
691
- ] }, tab.id);
692
- }) }),
693
- /* @__PURE__ */ jsxs(Box, { gap: 0, children: [
694
- isWatching ? /* @__PURE__ */ jsxs(Text, { backgroundColor: chipBg4.green, color: tuiColors.green, bold: true, children: [
695
- " ",
696
- FILLED_CIRCLE,
697
- " WATCHING",
698
- " "
699
- ] }) : /* @__PURE__ */ jsxs(Text, { backgroundColor: chipBg4.neutral, color: tuiColors.dim, children: [
700
- " ",
701
- EMPTY_CIRCLE3,
702
- " IDLE",
703
- " "
704
- ] }),
705
- stats.running > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
706
- /* @__PURE__ */ jsx(Text, { children: " " }),
707
- /* @__PURE__ */ jsxs(Text, { backgroundColor: chipBg4.green, color: tuiColors.green, children: [
708
- " ",
709
- /* @__PURE__ */ jsx(MiniSpinner, { color: tuiColors.green }),
710
- " ",
711
- stats.running,
712
- " active",
713
- " "
714
- ] })
715
- ] }),
716
- uptime && /* @__PURE__ */ jsxs(Text, { color: tuiColors.ghost, children: [
717
- " ",
718
- uptime
719
- ] })
720
- ] })
721
- ] });
722
- }
723
- function StatsBar({
724
- stats,
725
- tokens,
726
- width,
727
- sparklineData
728
- }) {
729
- const allChips = [
730
- { icon: TRIANGLE_RIGHT, label: "RUN", count: stats.running, fg: tuiColors.green, bg: chipBg4.green, bold: true, spinner: true, show: stats.running > 0 },
731
- { icon: RETRY_ICON, label: "RETRY", count: stats.retrying, fg: tuiColors.yellow, bg: chipBg4.yellow, show: stats.retrying > 0 },
732
- { icon: LOZENGE, label: "REVIEW", count: stats.review, fg: tuiColors.blue, bg: chipBg4.blue, show: stats.review > 0 },
733
- { icon: EMPTY_CIRCLE3, label: "TODO", count: stats.todo, fg: tuiColors.dim, bg: chipBg4.neutral, show: stats.todo > 0 },
734
- { icon: CHECK4, label: "DONE", count: stats.done, fg: tuiColors.green, bg: chipBg4.green, show: stats.done > 0 },
735
- { icon: CROSS4, label: "FAIL", count: stats.failed, fg: tuiColors.red, bg: chipBg4.red, bold: true, show: stats.failed > 0 },
736
- { icon: DIAMOND, label: "TEAMS", count: stats.teams, fg: tuiColors.amber, bg: chipBg4.amber, show: stats.teams > 0 }
737
- ];
738
- const chips = allChips.filter((c) => c.show);
739
- const hasTokens = tokens.total > 0;
740
- const fmtK = (n) => n >= 1e3 ? `${(n / 1e3).toFixed(1)}k` : String(n);
741
- const sparkWidth = sparklineData && sparklineData.length > 0 ? Math.min(16, sparklineData.length) : 0;
742
- return /* @__PURE__ */ jsxs(Box, { paddingX: 1, justifyContent: "space-between", width, children: [
743
- /* @__PURE__ */ jsxs(Box, { gap: 1, children: [
744
- chips.map((chip) => /* @__PURE__ */ jsx(Text, { backgroundColor: chip.bg, color: chip.fg, bold: chip.bold, children: chip.spinner ? /* @__PURE__ */ jsxs(Fragment, { children: [
745
- " ",
746
- /* @__PURE__ */ jsx(MiniSpinner, { color: chip.fg }),
747
- " ",
748
- chip.count,
749
- " ",
750
- chip.label,
751
- " "
752
- ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
753
- " ",
754
- chip.icon,
755
- " ",
756
- chip.count,
757
- " ",
758
- chip.label,
759
- " "
760
- ] }) }, chip.label)),
761
- chips.length === 0 && /* @__PURE__ */ jsxs(Text, { backgroundColor: chipBg4.neutral, color: tuiColors.dim, children: [
762
- " ",
763
- "NO TASKS",
764
- " "
765
- ] })
766
- ] }),
767
- /* @__PURE__ */ jsxs(Box, { gap: 0, children: [
768
- sparkWidth > 0 && sparklineData && /* @__PURE__ */ jsxs(Fragment, { children: [
769
- /* @__PURE__ */ jsx(Sparkline, { data: sparklineData, width: sparkWidth, color: tuiColors.amberDim }),
770
- /* @__PURE__ */ jsx(Text, { children: " " })
771
- ] }),
772
- hasTokens && /* @__PURE__ */ jsxs(Text, { backgroundColor: chipBg4.amber, color: tuiColors.cyan, children: [
773
- " ",
774
- ARROW_UP,
775
- fmtK(tokens.input),
776
- " ",
777
- ARROW_DOWN,
778
- fmtK(tokens.output),
779
- " ",
780
- DOT,
781
- " ",
782
- SIGMA,
783
- fmtK(tokens.total),
784
- " "
785
- ] })
786
- ] })
787
- ] });
788
- }
789
- var Header = React5.memo(function Header2(props) {
790
- const barWidth = Math.max(10, props.width - 2);
791
- const isActive = props.stats.running > 0;
792
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
793
- /* @__PURE__ */ jsx(Box, { height: 1 }),
794
- /* @__PURE__ */ jsx(
795
- BrandBar,
796
- {
797
- projectName: props.projectName,
798
- activeView: props.activeView,
799
- mode: props.mode,
800
- stats: props.stats,
801
- uptime: props.uptime,
802
- width: props.width
803
- }
804
- ),
805
- /* @__PURE__ */ jsx(Box, { height: 1 }),
806
- /* @__PURE__ */ jsx(
807
- StatsBar,
808
- {
809
- stats: props.stats,
810
- tokens: props.tokens,
811
- width: props.width,
812
- sparklineData: props.sparklineData
813
- }
814
- ),
815
- /* @__PURE__ */ jsx(ScannerBar, { width: barWidth, active: isActive })
816
- ] });
817
- });
818
-
819
- // src/tui/commandBar.ts
820
- var COMMAND_REGISTRY = {
821
- goal: { sub: ["add", "list", "show", "status", "delete"], help: "Manage goals" },
822
- task: { sub: ["add", "list", "show", "cancel", "retry", "assign", "approve", "reject", "delete"], help: "Manage tasks" },
823
- agent: { sub: ["add", "list", "disable", "enable", "delete", "autonomous"], help: "Manage agents" },
824
- team: { sub: ["create", "list", "join", "leave", "disband", "set-lead"], help: "Manage teams" },
825
- run: { args: "[id]", help: "Run task (or selected)" },
826
- "run-all": { help: "Run all todo tasks" },
827
- watch: { help: "Start watch mode (auto-dispatch)" },
828
- pause: { help: "Pause watch mode" },
829
- config: { sub: ["activity-filter", "max-concurrent"], help: "TUI settings" },
830
- status: { help: "Show orchestrator status" },
831
- help: { help: "List all commands" },
832
- quit: { help: "Exit the TUI" }
833
- };
834
- function resolveCompletion(input) {
835
- if (!input.startsWith("/")) return null;
836
- const body = input.slice(1);
837
- const spaceIdx = body.indexOf(" ");
838
- if (spaceIdx === -1) {
839
- const prefix = body;
840
- if (!prefix) return null;
841
- const match2 = Object.keys(COMMAND_REGISTRY).find(
842
- (k) => k.startsWith(prefix) && k !== prefix
843
- );
844
- return match2 ? match2.slice(prefix.length) : null;
845
- }
846
- const verb = body.slice(0, spaceIdx);
847
- const spec = COMMAND_REGISTRY[verb];
848
- if (!spec?.sub) return null;
849
- const subPrefix = body.slice(spaceIdx + 1);
850
- if (!subPrefix) return null;
851
- const match = spec.sub.find(
852
- (s) => s.startsWith(subPrefix) && s !== subPrefix
853
- );
854
- return match ? match.slice(subPrefix.length) : null;
855
- }
856
- function resolveSuggestions(input) {
857
- if (!input.startsWith("/")) return [];
858
- const body = input.slice(1);
859
- const spaceIdx = body.indexOf(" ");
860
- if (spaceIdx === -1) {
861
- const prefix = body.toLowerCase();
862
- const results2 = [];
863
- for (const [verb2, spec2] of Object.entries(COMMAND_REGISTRY)) {
864
- if (!prefix || verb2.startsWith(prefix)) {
865
- const argsHint = spec2.args ? ` ${spec2.args}` : "";
866
- results2.push({
867
- cmd: `/${verb2}${argsHint}`,
868
- desc: spec2.help,
869
- subs: spec2.sub?.join(" \xB7 ")
870
- });
871
- if (prefix && verb2 === prefix && spec2.sub) {
872
- for (const sub of spec2.sub) {
873
- results2.push({ cmd: `/${verb2} ${sub}`, desc: `${spec2.help}: ${sub}` });
874
- }
875
- }
876
- }
877
- }
878
- return results2;
879
- }
880
- const verb = body.slice(0, spaceIdx);
881
- const spec = COMMAND_REGISTRY[verb];
882
- if (!spec?.sub) return [];
883
- const subPrefix = body.slice(spaceIdx + 1).toLowerCase();
884
- const results = [];
885
- for (const sub of spec.sub) {
886
- if (!subPrefix || sub.startsWith(subPrefix)) {
887
- results.push({ cmd: `/${verb} ${sub}`, desc: `${spec.help}: ${sub}` });
888
- }
889
- }
890
- return results;
891
- }
892
- var CommandHistory = class {
893
- entries = [];
894
- cursor = 0;
895
- push(cmd) {
896
- if (!cmd) return;
897
- if (this.entries[this.entries.length - 1] !== cmd) {
898
- this.entries.push(cmd);
899
- if (this.entries.length > 100) this.entries.shift();
900
- }
901
- this.cursor = this.entries.length;
902
- }
903
- prev() {
904
- if (this.entries.length === 0) return null;
905
- if (this.cursor > 0) this.cursor--;
906
- return this.entries[this.cursor] ?? null;
907
- }
908
- next() {
909
- if (this.cursor < this.entries.length - 1) {
910
- this.cursor++;
911
- return this.entries[this.cursor] ?? null;
912
- }
913
- this.cursor = this.entries.length;
914
- return null;
915
- }
916
- reset() {
917
- this.cursor = this.entries.length;
918
- }
919
- };
920
- var CURSOR_CHAR = "\u2588";
921
- var CommandBar = React5.memo(function CommandBar2({
922
- mode,
923
- value,
924
- completion,
925
- activeView,
926
- canRun,
927
- canNew,
928
- canApprove,
929
- canReject,
930
- canCancel,
931
- canDelete,
932
- canEdit,
933
- canForceStop,
934
- canToggleAuto,
935
- autoActive,
936
- canPause,
937
- isPaused,
938
- canToggleShowAll,
939
- showAllActive,
940
- hasDetail,
941
- itemCount,
942
- itemLabel,
943
- width,
944
- hasSuggestions
945
- }) {
946
- if (mode === "command") {
947
- const hintText = hasSuggestions ? " \u2191\u2193 select Tab fill Esc \u2715" : " Enter exec \u2191\u2193 history Tab complete Esc \u2715";
948
- const maxLen = Math.max(4, width - 6 - hintText.length - (completion?.length ?? 0) - 1);
949
- const display = value.length > maxLen ? "\u2026" + value.slice(-(maxLen - 1)) : value;
950
- return /* @__PURE__ */ jsx(Box, { paddingX: 2, justifyContent: "space-between", width, children: /* @__PURE__ */ jsxs(Box, { children: [
951
- /* @__PURE__ */ jsx(Text, { color: tuiColors.amber, children: "/ " }),
952
- /* @__PURE__ */ jsx(Text, { color: tuiColors.white, children: display }),
953
- completion && /* @__PURE__ */ jsx(Text, { color: tuiColors.ghost, children: completion }),
954
- /* @__PURE__ */ jsx(Text, { color: tuiColors.amber, children: CURSOR_CHAR }),
955
- /* @__PURE__ */ jsx(Text, { color: tuiColors.dim, children: hintText })
956
- ] }) });
957
- }
958
- return /* @__PURE__ */ jsxs(Box, { paddingX: 2, justifyContent: "space-between", width, children: [
959
- /* @__PURE__ */ jsxs(Text, { color: tuiColors.dim, children: [
960
- /* @__PURE__ */ jsx(Text, { bold: true, color: tuiColors.gray, children: "\u2191\u2193" }),
961
- " ",
962
- /* @__PURE__ */ jsx(Text, { bold: true, color: tuiColors.gray, children: "Tab" }),
963
- "/",
964
- /* @__PURE__ */ jsx(Text, { bold: true, color: tuiColors.gray, children: "\u2190\u2192" }),
965
- " ",
966
- /* @__PURE__ */ jsx(Text, { bold: true, color: tuiColors.gray, children: "/" }),
967
- " cmd",
968
- canNew && /* @__PURE__ */ jsxs(Fragment, { children: [
969
- " ",
970
- /* @__PURE__ */ jsx(Text, { bold: true, color: tuiColors.gray, children: "N" }),
971
- " new"
972
- ] }),
973
- canRun && /* @__PURE__ */ jsxs(Fragment, { children: [
974
- " ",
975
- /* @__PURE__ */ jsx(Text, { bold: true, color: tuiColors.gray, children: "R" }),
976
- " run"
977
- ] }),
978
- canCancel && /* @__PURE__ */ jsxs(Fragment, { children: [
979
- " ",
980
- /* @__PURE__ */ jsx(Text, { bold: true, color: tuiColors.amber, children: "C" }),
981
- " cancel"
982
- ] }),
983
- canApprove && /* @__PURE__ */ jsxs(Fragment, { children: [
984
- " ",
985
- /* @__PURE__ */ jsx(Text, { bold: true, color: tuiColors.green, children: "A" }),
986
- " approve"
987
- ] }),
988
- canReject && /* @__PURE__ */ jsxs(Fragment, { children: [
989
- " ",
990
- /* @__PURE__ */ jsx(Text, { bold: true, color: tuiColors.red, children: "X" }),
991
- " reject"
992
- ] }),
993
- canEdit && /* @__PURE__ */ jsxs(Fragment, { children: [
994
- " ",
995
- /* @__PURE__ */ jsx(Text, { bold: true, color: tuiColors.cyan, children: "E" }),
996
- " edit"
997
- ] }),
998
- canForceStop && /* @__PURE__ */ jsxs(Fragment, { children: [
999
- " ",
1000
- /* @__PURE__ */ jsx(Text, { bold: true, color: tuiColors.red, children: "S" }),
1001
- " stop"
1002
- ] }),
1003
- canPause && /* @__PURE__ */ jsxs(Fragment, { children: [
1004
- " ",
1005
- /* @__PURE__ */ jsx(Text, { bold: true, color: tuiColors.amber, children: "P" }),
1006
- isPaused ? " resume" : " pause"
1007
- ] }),
1008
- canToggleAuto && /* @__PURE__ */ jsxs(Fragment, { children: [
1009
- " ",
1010
- /* @__PURE__ */ jsx(Text, { bold: true, color: tuiColors.cyan, children: "U" }),
1011
- autoActive ? " auto off" : " auto on"
1012
- ] }),
1013
- canToggleShowAll && /* @__PURE__ */ jsxs(Fragment, { children: [
1014
- " ",
1015
- /* @__PURE__ */ jsx(Text, { bold: true, color: tuiColors.gray, children: "S" }),
1016
- showAllActive ? " collapse" : " show all"
1017
- ] }),
1018
- canDelete && !canApprove && /* @__PURE__ */ jsxs(Fragment, { children: [
1019
- " ",
1020
- /* @__PURE__ */ jsx(Text, { bold: true, color: tuiColors.gray, children: "D" }),
1021
- " delete"
1022
- ] }),
1023
- hasDetail && /* @__PURE__ */ jsxs(Fragment, { children: [
1024
- " ",
1025
- /* @__PURE__ */ jsx(Text, { bold: true, color: tuiColors.gray, children: "Esc" }),
1026
- " close"
1027
- ] }),
1028
- !hasDetail && (activeView === "tasks" || activeView === "agents" || activeView === "goals") && /* @__PURE__ */ jsxs(Fragment, { children: [
1029
- " ",
1030
- /* @__PURE__ */ jsx(Text, { bold: true, color: tuiColors.gray, children: "Enter" }),
1031
- " detail"
1032
- ] }),
1033
- " ",
1034
- /* @__PURE__ */ jsx(Text, { bold: true, color: tuiColors.gray, children: "Q" }),
1035
- " quit"
1036
- ] }),
1037
- itemCount > 0 && /* @__PURE__ */ jsxs(Text, { color: tuiColors.dim, children: [
1038
- itemCount,
1039
- " ",
1040
- itemLabel
1041
- ] })
1042
- ] });
1043
- });
1044
- var CURSOR = "\u2588";
1045
- function FormWizard({ title, steps, onComplete, onCancel, width, height }) {
1046
- const [currentStep, setCurrentStep] = useState(0);
1047
- const [values, setValues] = useState({});
1048
- const [textInput, setTextInput] = useState(() => {
1049
- const firstActive = steps.find((s) => !s.skip?.({}));
1050
- return firstActive?.type === "text" && firstActive.defaultValue ? firstActive.defaultValue : "";
1051
- });
1052
- const [cursorPos, setCursorPos] = useState(() => {
1053
- const firstActive = steps.find((s) => !s.skip?.({}));
1054
- return firstActive?.type === "text" && firstActive.defaultValue ? firstActive.defaultValue.length : 0;
1055
- });
1056
- const [taLines, setTaLines] = useState(() => {
1057
- const firstActive = steps.find((s) => !s.skip?.({}));
1058
- if (firstActive?.type === "textarea" && firstActive.defaultValue) {
1059
- return firstActive.defaultValue.split("\n");
1060
- }
1061
- return [""];
1062
- });
1063
- const [taCursorRow, setTaCursorRow] = useState(0);
1064
- const [taCursorCol, setTaCursorCol] = useState(0);
1065
- const [selectIndex, setSelectIndex] = useState(() => {
1066
- const firstActive = steps.find((s) => !s.skip?.({}));
1067
- if (firstActive?.type === "select" && firstActive.defaultValue) {
1068
- const opts = firstActive.getOptions?.({}) ?? firstActive.options ?? [];
1069
- const idx = opts.findIndex((o) => o.value === firstActive.defaultValue);
1070
- return idx >= 0 ? idx : 0;
1071
- }
1072
- return 0;
1073
- });
1074
- const activeSteps = useMemo(() => {
1075
- return steps.filter((s) => !s.skip?.(values));
1076
- }, [steps, values]);
1077
- const step = activeSteps[currentStep];
1078
- const totalSteps = activeSteps.length;
1079
- const [multiSelected, setMultiSelected] = useState(/* @__PURE__ */ new Set());
1080
- const options = useMemo(() => {
1081
- if (!step || step.type !== "select" && step.type !== "multiselect") return [];
1082
- return step.getOptions?.(values) ?? step.options ?? [];
1083
- }, [step, values]);
1084
- const clampedSelectIndex = Math.min(selectIndex, Math.max(0, options.length - 1));
1085
- const goToNextStep = (value) => {
1086
- const newValues = { ...values, [step.id]: value };
1087
- setValues(newValues);
1088
- setTextInput("");
1089
- setCursorPos(0);
1090
- setTaLines([""]);
1091
- setTaCursorRow(0);
1092
- setTaCursorCol(0);
1093
- setSelectIndex(0);
1094
- setMultiSelected(/* @__PURE__ */ new Set());
1095
- const currentStepId = step.id;
1096
- const currentIdx = steps.findIndex((s) => s.id === currentStepId);
1097
- let nextOrigIdx = currentIdx + 1;
1098
- while (nextOrigIdx < steps.length) {
1099
- const candidate = steps[nextOrigIdx];
1100
- if (candidate && !candidate.skip?.(newValues)) break;
1101
- nextOrigIdx++;
1102
- }
1103
- if (nextOrigIdx >= steps.length) {
1104
- onComplete(newValues);
1105
- } else {
1106
- const nextStepId = steps[nextOrigIdx].id;
1107
- const newActiveSteps = steps.filter((s) => !s.skip?.(newValues));
1108
- const nextActiveIdx = newActiveSteps.findIndex((s) => s.id === nextStepId);
1109
- setCurrentStep(nextActiveIdx >= 0 ? nextActiveIdx : 0);
1110
- const nextStep = steps[nextOrigIdx];
1111
- if (nextStep.type === "text") {
1112
- const def = nextStep.defaultValue ?? "";
1113
- setTextInput(def);
1114
- setCursorPos(def.length);
1115
- } else if (nextStep.type === "textarea") {
1116
- const lines = nextStep.defaultValue ? nextStep.defaultValue.split("\n") : [""];
1117
- setTaLines(lines);
1118
- setTaCursorRow(lines.length - 1);
1119
- setTaCursorCol(lines[lines.length - 1].length);
1120
- } else if (nextStep.type === "select") {
1121
- const opts = nextStep.getOptions?.(newValues) ?? nextStep.options ?? [];
1122
- if (nextStep.defaultValue) {
1123
- const idx = opts.findIndex((o) => o.value === nextStep.defaultValue);
1124
- setSelectIndex(idx >= 0 ? idx : 0);
1125
- } else {
1126
- setSelectIndex(0);
1127
- }
1128
- } else if (nextStep.type === "multiselect") {
1129
- setSelectIndex(0);
1130
- if (nextStep.defaultValue) {
1131
- setMultiSelected(new Set(nextStep.defaultValue.split(",")));
1132
- } else {
1133
- setMultiSelected(/* @__PURE__ */ new Set());
1134
- }
1135
- }
1136
- }
1137
- };
1138
- const goToPrevStep = () => {
1139
- if (currentStep === 0) {
1140
- onCancel();
1141
- return;
1142
- }
1143
- const currentStepId = step.id;
1144
- const currentOrigIdx = steps.findIndex((s) => s.id === currentStepId);
1145
- let prevOrigIdx = currentOrigIdx - 1;
1146
- while (prevOrigIdx >= 0) {
1147
- const candidate = steps[prevOrigIdx];
1148
- if (candidate && !candidate.skip?.(values)) break;
1149
- prevOrigIdx--;
1150
- }
1151
- if (prevOrigIdx < 0) {
1152
- onCancel();
1153
- return;
1154
- }
1155
- const prevStepId = steps[prevOrigIdx].id;
1156
- const prevActiveIdx = activeSteps.findIndex((s) => s.id === prevStepId);
1157
- setCurrentStep(prevActiveIdx >= 0 ? prevActiveIdx : 0);
1158
- const prevStep = steps[prevOrigIdx];
1159
- if (prevStep.type === "text") {
1160
- const val = values[prevStep.id] ?? prevStep.defaultValue ?? "";
1161
- setTextInput(val);
1162
- setCursorPos(val.length);
1163
- } else if (prevStep.type === "textarea") {
1164
- const val = values[prevStep.id] ?? prevStep.defaultValue ?? "";
1165
- const lines = val ? val.split("\n") : [""];
1166
- setTaLines(lines);
1167
- setTaCursorRow(lines.length - 1);
1168
- setTaCursorCol(lines[lines.length - 1].length);
1169
- } else if (prevStep.type === "multiselect") {
1170
- setSelectIndex(0);
1171
- const prevVal = values[prevStep.id];
1172
- setMultiSelected(prevVal ? new Set(prevVal.split(",")) : /* @__PURE__ */ new Set());
1173
- } else {
1174
- const prevOptions = prevStep.getOptions?.(values) ?? prevStep.options ?? [];
1175
- const prevVal = values[prevStep.id];
1176
- const idx = prevOptions.findIndex((o) => o.value === prevVal);
1177
- setSelectIndex(idx >= 0 ? idx : 0);
1178
- }
1179
- };
1180
- useInput((input, key) => {
1181
- if (!step) return;
1182
- if (key.escape) {
1183
- if (currentStep === 0) {
1184
- onCancel();
1185
- } else {
1186
- goToPrevStep();
1187
- }
1188
- return;
1189
- }
1190
- if (step.type === "text") {
1191
- if (key.return) {
1192
- const val = textInput.trim();
1193
- if (step.required && !val) return;
1194
- goToNextStep(val);
1195
- return;
1196
- }
1197
- if (key.leftArrow) {
1198
- setCursorPos((p) => Math.max(0, p - 1));
1199
- return;
1200
- }
1201
- if (key.rightArrow) {
1202
- setCursorPos((p) => Math.min(textInput.length, p + 1));
1203
- return;
1204
- }
1205
- if (key.backspace || key.delete) {
1206
- if (cursorPos === 0 && textInput.length === 0 && currentStep > 0) {
1207
- goToPrevStep();
1208
- return;
1209
- }
1210
- if (cursorPos > 0) {
1211
- setTextInput((v) => v.slice(0, cursorPos - 1) + v.slice(cursorPos));
1212
- setCursorPos((p) => p - 1);
1213
- }
1214
- return;
1215
- }
1216
- if (input && !key.ctrl && !key.meta && !key.escape) {
1217
- setTextInput((v) => v.slice(0, cursorPos) + input + v.slice(cursorPos));
1218
- setCursorPos((p) => p + input.length);
1219
- }
1220
- return;
1221
- }
1222
- if (step.type === "textarea") {
1223
- if (key.return && !key.shift) {
1224
- const val = taLines.join("\n").trim();
1225
- if (step.required && !val) return;
1226
- goToNextStep(val);
1227
- return;
1228
- }
1229
- if (key.return && key.shift) {
1230
- setTaLines((lines) => {
1231
- const line = lines[taCursorRow] ?? "";
1232
- const before = line.slice(0, taCursorCol);
1233
- const after = line.slice(taCursorCol);
1234
- const newLines = [...lines];
1235
- newLines.splice(taCursorRow, 1, before, after);
1236
- return newLines;
1237
- });
1238
- setTaCursorRow((r) => r + 1);
1239
- setTaCursorCol(0);
1240
- return;
1241
- }
1242
- if (key.upArrow) {
1243
- if (taCursorRow > 0) {
1244
- setTaCursorRow((r) => r - 1);
1245
- setTaCursorCol((c) => Math.min(c, (taLines[taCursorRow - 1] ?? "").length));
1246
- }
1247
- return;
1248
- }
1249
- if (key.downArrow) {
1250
- if (taCursorRow < taLines.length - 1) {
1251
- setTaCursorRow((r) => r + 1);
1252
- setTaCursorCol((c) => Math.min(c, (taLines[taCursorRow + 1] ?? "").length));
1253
- }
1254
- return;
1255
- }
1256
- if (key.leftArrow) {
1257
- if (taCursorCol > 0) {
1258
- setTaCursorCol((c) => c - 1);
1259
- } else if (taCursorRow > 0) {
1260
- setTaCursorRow((r) => r - 1);
1261
- setTaCursorCol((taLines[taCursorRow - 1] ?? "").length);
1262
- }
1263
- return;
1264
- }
1265
- if (key.rightArrow) {
1266
- const lineLen = (taLines[taCursorRow] ?? "").length;
1267
- if (taCursorCol < lineLen) {
1268
- setTaCursorCol((c) => c + 1);
1269
- } else if (taCursorRow < taLines.length - 1) {
1270
- setTaCursorRow((r) => r + 1);
1271
- setTaCursorCol(0);
1272
- }
1273
- return;
1274
- }
1275
- if (key.backspace || key.delete) {
1276
- if (taCursorCol === 0 && taCursorRow === 0) {
1277
- if (taLines.length === 1 && taLines[0] === "" && currentStep > 0) {
1278
- goToPrevStep();
1279
- }
1280
- return;
1281
- }
1282
- if (taCursorCol > 0) {
1283
- setTaLines((lines) => {
1284
- const newLines = [...lines];
1285
- const line = newLines[taCursorRow] ?? "";
1286
- newLines[taCursorRow] = line.slice(0, taCursorCol - 1) + line.slice(taCursorCol);
1287
- return newLines;
1288
- });
1289
- setTaCursorCol((c) => c - 1);
1290
- } else {
1291
- const mergedCol = (taLines[taCursorRow - 1] ?? "").length;
1292
- setTaLines((lines) => {
1293
- const newLines = [...lines];
1294
- const prevLine = newLines[taCursorRow - 1] ?? "";
1295
- const curLine = newLines[taCursorRow] ?? "";
1296
- newLines.splice(taCursorRow - 1, 2, prevLine + curLine);
1297
- return newLines;
1298
- });
1299
- setTaCursorCol(mergedCol);
1300
- setTaCursorRow((r) => r - 1);
1301
- }
1302
- return;
1303
- }
1304
- if (input && !key.ctrl && !key.meta && !key.escape) {
1305
- setTaLines((lines) => {
1306
- const newLines = [...lines];
1307
- const line = newLines[taCursorRow] ?? "";
1308
- newLines[taCursorRow] = line.slice(0, taCursorCol) + input + line.slice(taCursorCol);
1309
- return newLines;
1310
- });
1311
- setTaCursorCol((c) => c + input.length);
1312
- }
1313
- return;
1314
- }
1315
- if (step.type === "select" || step.type === "multiselect") {
1316
- if (key.upArrow || input === "k") {
1317
- setSelectIndex((i) => Math.max(0, i - 1));
1318
- return;
1319
- }
1320
- if (key.downArrow || input === "j") {
1321
- setSelectIndex((i) => Math.min(options.length - 1, i + 1));
1322
- return;
1323
- }
1324
- if (key.backspace || key.delete) {
1325
- goToPrevStep();
1326
- return;
1327
- }
1328
- if (step.type === "select") {
1329
- if (key.return || key.tab) {
1330
- const selected = options[clampedSelectIndex];
1331
- if (selected) goToNextStep(selected.value);
1332
- return;
1333
- }
1334
- if (input >= "1" && input <= "9") {
1335
- const idx = parseInt(input, 10) - 1;
1336
- if (idx < options.length) {
1337
- const selected = options[idx];
1338
- if (selected) goToNextStep(selected.value);
1339
- }
1340
- return;
1341
- }
1342
- } else {
1343
- if (input === " ") {
1344
- const opt = options[clampedSelectIndex];
1345
- if (opt) {
1346
- setMultiSelected((prev) => {
1347
- const next = new Set(prev);
1348
- if (next.has(opt.value)) {
1349
- next.delete(opt.value);
1350
- } else {
1351
- next.add(opt.value);
1352
- }
1353
- return next;
1354
- });
1355
- }
1356
- return;
1357
- }
1358
- if (key.return) {
1359
- const selected = Array.from(multiSelected).join(",");
1360
- goToNextStep(selected);
1361
- return;
1362
- }
1363
- }
1364
- }
1365
- });
1366
- if (!step) return null;
1367
- const progressText = `${currentStep + 1}/${totalSteps}`;
1368
- const optionsH = Math.max(2, height - 4);
1369
- let optScrollStart = 0;
1370
- if (clampedSelectIndex >= optionsH) {
1371
- optScrollStart = clampedSelectIndex - optionsH + 1;
1372
- }
1373
- const visibleOptions = options.slice(optScrollStart, optScrollStart + optionsH);
1374
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingX: 2, children: [
1375
- /* @__PURE__ */ jsxs(Box, { children: [
1376
- /* @__PURE__ */ jsx(Text, { color: tuiColors.amber, bold: true, children: title }),
1377
- /* @__PURE__ */ jsxs(Text, { color: tuiColors.ghost, children: [
1378
- " ",
1379
- LIGHT_RULE,
1380
- LIGHT_RULE,
1381
- " "
1382
- ] }),
1383
- /* @__PURE__ */ jsxs(Text, { color: tuiColors.dim, children: [
1384
- "step ",
1385
- progressText
1386
- ] })
1387
- ] }),
1388
- /* @__PURE__ */ jsxs(Box, { children: [
1389
- /* @__PURE__ */ jsx(Text, { children: " " }),
1390
- activeSteps.map((s, i) => /* @__PURE__ */ jsxs(Text, { color: i === currentStep ? tuiColors.amber : i < currentStep ? tuiColors.green : tuiColors.ghost, children: [
1391
- i === currentStep ? "\u25CF" : i < currentStep ? "\u2713" : "\u25CB",
1392
- " "
1393
- ] }, s.id))
1394
- ] }),
1395
- /* @__PURE__ */ jsxs(Box, { marginTop: 0, children: [
1396
- /* @__PURE__ */ jsxs(Text, { color: tuiColors.white, bold: true, children: [
1397
- " ",
1398
- step.label
1399
- ] }),
1400
- !step.required && /* @__PURE__ */ jsx(Text, { color: tuiColors.dim, children: " (optional, Enter to skip)" })
1401
- ] }),
1402
- step.type === "text" && /* @__PURE__ */ jsxs(Box, { children: [
1403
- /* @__PURE__ */ jsxs(Text, { color: tuiColors.amber, children: [
1404
- " ",
1405
- ">",
1406
- " "
1407
- ] }),
1408
- textInput.length > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
1409
- /* @__PURE__ */ jsx(Text, { color: tuiColors.white, children: textInput.slice(0, cursorPos) }),
1410
- /* @__PURE__ */ jsx(Text, { color: tuiColors.amber, children: CURSOR }),
1411
- /* @__PURE__ */ jsx(Text, { color: tuiColors.white, children: textInput.slice(cursorPos) })
1412
- ] }) : step.placeholder ? /* @__PURE__ */ jsxs(Fragment, { children: [
1413
- /* @__PURE__ */ jsx(Text, { color: tuiColors.ghost, children: step.placeholder }),
1414
- /* @__PURE__ */ jsx(Text, { color: tuiColors.amber, children: CURSOR })
1415
- ] }) : /* @__PURE__ */ jsx(Text, { color: tuiColors.amber, children: CURSOR })
1416
- ] }),
1417
- step.type === "textarea" && (() => {
1418
- const taVisibleH = Math.max(3, height - 6);
1419
- let taScrollStart = 0;
1420
- if (taCursorRow >= taVisibleH) {
1421
- taScrollStart = taCursorRow - taVisibleH + 1;
1422
- }
1423
- const visibleLines = taLines.slice(taScrollStart, taScrollStart + taVisibleH);
1424
- const lineNumWidth = String(taLines.length).length;
1425
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
1426
- visibleLines.map((line, i) => {
1427
- const realRow = i + taScrollStart;
1428
- const lineNum = String(realRow + 1).padStart(lineNumWidth, " ");
1429
- const isCursorLine = realRow === taCursorRow;
1430
- return /* @__PURE__ */ jsxs(Box, { children: [
1431
- /* @__PURE__ */ jsxs(Text, { color: tuiColors.dim, children: [
1432
- " ",
1433
- lineNum,
1434
- " "
1435
- ] }),
1436
- /* @__PURE__ */ jsxs(Text, { color: tuiColors.ghost, children: [
1437
- "\u2502",
1438
- " "
1439
- ] }),
1440
- isCursorLine ? /* @__PURE__ */ jsxs(Fragment, { children: [
1441
- /* @__PURE__ */ jsx(Text, { color: tuiColors.white, children: line.slice(0, taCursorCol) }),
1442
- /* @__PURE__ */ jsx(Text, { color: tuiColors.amber, children: CURSOR }),
1443
- /* @__PURE__ */ jsx(Text, { color: tuiColors.white, children: line.slice(taCursorCol) })
1444
- ] }) : /* @__PURE__ */ jsx(Text, { color: tuiColors.silver, children: line || " " })
1445
- ] }, realRow);
1446
- }),
1447
- taLines.length === 1 && taLines[0] === "" && step.placeholder && /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsxs(Text, { color: tuiColors.dim, children: [
1448
- " ",
1449
- "".padStart(lineNumWidth, " "),
1450
- " ",
1451
- step.placeholder
1452
- ] }) })
1453
- ] });
1454
- })(),
1455
- step.type === "select" && /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: visibleOptions.map((opt, i) => {
1456
- const realIdx = i + optScrollStart;
1457
- const isSelected = realIdx === clampedSelectIndex;
1458
- const numLabel = realIdx < 9 ? `${realIdx + 1}` : " ";
1459
- return /* @__PURE__ */ jsxs(Box, { children: [
1460
- /* @__PURE__ */ jsx(Text, { color: isSelected ? tuiColors.amber : tuiColors.ghost, children: isSelected ? " \u25B8 " : ` ${numLabel} ` }),
1461
- /* @__PURE__ */ jsx(Text, { color: isSelected ? tuiColors.white : tuiColors.silver, bold: isSelected, children: opt.label }),
1462
- opt.hint && /* @__PURE__ */ jsxs(Text, { color: tuiColors.dim, wrap: "truncate", children: [
1463
- " ",
1464
- LIGHT_RULE,
1465
- " ",
1466
- opt.hint.replace(/\n/g, " ")
1467
- ] })
1468
- ] }, opt.value);
1469
- }) }),
1470
- step.type === "multiselect" && /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
1471
- visibleOptions.map((opt, i) => {
1472
- const realIdx = i + optScrollStart;
1473
- const isSelected = realIdx === clampedSelectIndex;
1474
- const isChecked = multiSelected.has(opt.value);
1475
- return /* @__PURE__ */ jsxs(Box, { children: [
1476
- /* @__PURE__ */ jsx(Text, { color: isSelected ? tuiColors.amber : tuiColors.ghost, children: isSelected ? " \u25B8 " : " " }),
1477
- /* @__PURE__ */ jsx(Text, { color: isChecked ? tuiColors.green : tuiColors.dim, children: isChecked ? "[\u2713]" : "[ ]" }),
1478
- /* @__PURE__ */ jsxs(Text, { color: isSelected ? tuiColors.white : tuiColors.silver, bold: isSelected, children: [
1479
- " ",
1480
- opt.label
1481
- ] }),
1482
- opt.hint && /* @__PURE__ */ jsxs(Text, { color: tuiColors.dim, wrap: "truncate", children: [
1483
- " ",
1484
- LIGHT_RULE,
1485
- " ",
1486
- opt.hint.replace(/\n/g, " ")
1487
- ] })
1488
- ] }, opt.value);
1489
- }),
1490
- multiSelected.size > 0 && /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsxs(Text, { color: tuiColors.dim, children: [
1491
- " ",
1492
- "\u2514",
1493
- " ",
1494
- multiSelected.size,
1495
- " selected"
1496
- ] }) })
1497
- ] }),
1498
- /* @__PURE__ */ jsx(Box, { marginTop: 0, children: /* @__PURE__ */ jsxs(Text, { color: tuiColors.ghost, children: [
1499
- " ",
1500
- step.type === "select" ? "\u2191\u2193 select Enter confirm" : step.type === "multiselect" ? "\u2191\u2193 move Space toggle Enter confirm" : step.type === "textarea" ? "Shift+Enter newline Enter confirm \u2190\u2191\u2192\u2193 navigate" : "\u2190\u2192 move Enter confirm",
1501
- " Esc ",
1502
- currentStep > 0 ? "back" : "cancel"
1503
- ] }) })
1504
- ] });
1505
- }
1506
- var TL = "\u256D";
1507
- var TR = "\u256E";
1508
- var BL = "\u2570";
1509
- var BR = "\u256F";
1510
- var V = "\u2502";
1511
- function Row({ children, cw }) {
1512
- return /* @__PURE__ */ jsxs(Text, { children: [
1513
- /* @__PURE__ */ jsx(Text, { color: tuiColors.ghost, children: V }),
1514
- /* @__PURE__ */ jsxs(Text, { children: [
1515
- " ",
1516
- children.padEnd(cw),
1517
- " "
1518
- ] }),
1519
- /* @__PURE__ */ jsx(Text, { color: tuiColors.ghost, children: V })
1520
- ] });
1521
- }
1522
- function EmptyRow({ cw }) {
1523
- return /* @__PURE__ */ jsx(Row, { cw, children: "" });
1524
- }
1525
- function OnboardingBox({ count, config, width }) {
1526
- if (count >= 3) return null;
1527
- const boxW = Math.min((width ?? 44) - 4, 50);
1528
- const cw = boxW - 6;
1529
- const hLine = LIGHT_RULE.repeat(boxW - 2);
1530
- const topBorder = /* @__PURE__ */ jsxs(Text, { color: tuiColors.ghost, children: [
1531
- TL,
1532
- hLine,
1533
- TR
1534
- ] });
1535
- const botBorder = /* @__PURE__ */ jsxs(Text, { color: tuiColors.ghost, children: [
1536
- BL,
1537
- hLine,
1538
- BR
1539
- ] });
1540
- if (count > 0) {
1541
- const hint = config.hints[0];
1542
- const hintSuffix = hint ? ` ${hint.key} ${hint.label}` : "";
1543
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingX: 2, marginTop: 1, children: [
1544
- topBorder,
1545
- /* @__PURE__ */ jsxs(Text, { children: [
1546
- /* @__PURE__ */ jsxs(Text, { color: tuiColors.ghost, children: [
1547
- V,
1548
- " "
1549
- ] }),
1550
- /* @__PURE__ */ jsx(Text, { color: tuiColors.amber, children: DIAMOND }),
1551
- /* @__PURE__ */ jsxs(Text, { color: tuiColors.silver, children: [
1552
- " ",
1553
- config.nudge.padEnd(cw - 2 - hintSuffix.length)
1554
- ] }),
1555
- hint && /* @__PURE__ */ jsxs(Fragment, { children: [
1556
- /* @__PURE__ */ jsxs(Text, { color: tuiColors.amber, children: [
1557
- " ",
1558
- hint.key
1559
- ] }),
1560
- /* @__PURE__ */ jsxs(Text, { color: tuiColors.gray, children: [
1561
- " ",
1562
- hint.label
1563
- ] })
1564
- ] }),
1565
- /* @__PURE__ */ jsx(Text, { children: " " }),
1566
- /* @__PURE__ */ jsx(Text, { color: tuiColors.ghost, children: V })
1567
- ] }),
1568
- botBorder
1569
- ] });
1570
- }
1571
- const hintsLen = config.hints.reduce((acc, h, i) => acc + h.key.length + 1 + h.label.length + (i > 0 ? 3 : 0), 0);
1572
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingX: 2, marginTop: 1, children: [
1573
- topBorder,
1574
- /* @__PURE__ */ jsx(EmptyRow, { cw }),
1575
- /* @__PURE__ */ jsxs(Text, { children: [
1576
- /* @__PURE__ */ jsxs(Text, { color: tuiColors.ghost, children: [
1577
- V,
1578
- " "
1579
- ] }),
1580
- /* @__PURE__ */ jsx(Text, { color: tuiColors.amber, children: DIAMOND }),
1581
- /* @__PURE__ */ jsxs(Text, { color: tuiColors.white, bold: true, children: [
1582
- " ",
1583
- config.title
1584
- ] }),
1585
- /* @__PURE__ */ jsx(Text, { children: " ".repeat(Math.max(0, cw - config.title.length - 2)) }),
1586
- /* @__PURE__ */ jsx(Text, { children: " " }),
1587
- /* @__PURE__ */ jsx(Text, { color: tuiColors.ghost, children: V })
1588
- ] }),
1589
- /* @__PURE__ */ jsx(EmptyRow, { cw }),
1590
- config.description.map((line, i) => /* @__PURE__ */ jsxs(Text, { children: [
1591
- /* @__PURE__ */ jsxs(Text, { color: tuiColors.ghost, children: [
1592
- V,
1593
- " "
1594
- ] }),
1595
- /* @__PURE__ */ jsx(Text, { color: tuiColors.silver, children: line.padEnd(cw) }),
1596
- /* @__PURE__ */ jsx(Text, { children: " " }),
1597
- /* @__PURE__ */ jsx(Text, { color: tuiColors.ghost, children: V })
1598
- ] }, i)),
1599
- /* @__PURE__ */ jsx(EmptyRow, { cw }),
1600
- /* @__PURE__ */ jsxs(Text, { children: [
1601
- /* @__PURE__ */ jsxs(Text, { color: tuiColors.ghost, children: [
1602
- V,
1603
- " "
1604
- ] }),
1605
- config.hints.map((h, i) => /* @__PURE__ */ jsxs(React5.Fragment, { children: [
1606
- i > 0 && /* @__PURE__ */ jsx(Text, { color: tuiColors.ghost, children: " " }),
1607
- /* @__PURE__ */ jsx(Text, { color: tuiColors.amber, children: h.key }),
1608
- /* @__PURE__ */ jsxs(Text, { color: tuiColors.gray, children: [
1609
- " ",
1610
- h.label
1611
- ] })
1612
- ] }, i)),
1613
- /* @__PURE__ */ jsx(Text, { children: " ".repeat(Math.max(0, cw - hintsLen)) }),
1614
- /* @__PURE__ */ jsx(Text, { children: " " }),
1615
- /* @__PURE__ */ jsx(Text, { color: tuiColors.ghost, children: V })
1616
- ] }),
1617
- /* @__PURE__ */ jsx(EmptyRow, { cw }),
1618
- botBorder
1619
- ] });
1620
- }
1621
-
1622
- // src/tui/wizardConfigs.ts
1623
- var CLAUDE_MODELS = [
1624
- { value: "claude-opus-4-6", label: "Claude Opus 4.6", hint: "most capable" },
1625
- { value: "claude-sonnet-4-6", label: "Claude Sonnet 4.6", hint: "fast, balanced" },
1626
- { value: "claude-haiku-4-6", label: "Claude Haiku 4.6", hint: "fastest, cheapest" },
1627
- { value: "claude-sonnet-4-5-20250929", label: "Claude Sonnet 4.5", hint: "extended thinking" },
1628
- { value: "claude-haiku-4-5-20251001", label: "Claude Haiku 4.5", hint: "legacy" }
1629
- ];
1630
- var CODEX_MODELS = [
1631
- { value: "gpt-5.3-codex", label: "GPT-5.3 Codex", hint: "default, balanced" },
1632
- { value: "gpt-5.4", label: "GPT-5.4", hint: "latest" },
1633
- { value: "gpt-5", label: "GPT-5", hint: "capable" },
1634
- { value: "gpt-5.3-codex-spark", label: "GPT-5.3 Codex Spark", hint: "fast" },
1635
- { value: "o3", label: "o3", hint: "reasoning" },
1636
- { value: "o4-mini", label: "o4-mini", hint: "fast reasoning" },
1637
- { value: "gpt-5-mini", label: "GPT-5 Mini", hint: "light" },
1638
- { value: "gpt-5-nano", label: "GPT-5 Nano", hint: "cheapest" },
1639
- { value: "codex-mini-latest", label: "Codex Mini", hint: "legacy" }
1640
- ];
1641
- var CURSOR_MODELS = [
1642
- { value: "auto", label: "Auto", hint: "let Cursor decide" },
1643
- { value: "composer-1.5", label: "Composer 1.5", hint: "latest agent" },
1644
- { value: "composer-1", label: "Composer 1", hint: "stable agent" },
1645
- { value: "gpt-5.3-codex", label: "GPT-5.3 Codex", hint: "OpenAI" },
1646
- { value: "claude-sonnet-4-6", label: "Claude Sonnet 4.6", hint: "Anthropic" }
1647
- ];
1648
- var SHELL_MODELS = [
1649
- { value: "", label: "Default", hint: "use shell adapter default" }
1650
- ];
1651
- var ADAPTERS = [
1652
- { value: "claude", label: "Claude", hint: "Claude Code CLI" },
1653
- { value: "codex", label: "Codex", hint: "OpenAI Codex CLI" },
1654
- { value: "cursor", label: "Cursor", hint: "Cursor Agent CLI" },
1655
- { value: "shell", label: "Shell", hint: "custom shell command" }
1656
- ];
1657
- var PRIORITY_OPTIONS = [
1658
- { value: "1", label: "P1 Critical", hint: "urgent, do first" },
1659
- { value: "2", label: "P2 High", hint: "important" },
1660
- { value: "3", label: "P3 Medium", hint: "default priority" },
1661
- { value: "4", label: "P4 Low", hint: "nice to have" }
1662
- ];
1663
- var AGENT_HINT_MAX_LEN = 80;
1664
- function mapAgentOptions(agents) {
1665
- return agents.filter((a) => a.status !== "disabled").map((a) => {
1666
- const raw = a.role ?? a.adapter;
1667
- const hint = raw.length > AGENT_HINT_MAX_LEN ? raw.slice(0, AGENT_HINT_MAX_LEN - 1) + "\u2026" : raw;
1668
- return { value: a.id, label: a.name, hint };
1669
- });
1670
- }
1671
- function buildAgentOptions(agents, emptyLabel = "Auto-assign", emptyHint = "orchestrator picks the best agent") {
1672
- return [
1673
- { value: "", label: emptyLabel, hint: emptyHint },
1674
- ...mapAgentOptions(agents)
1675
- ];
1676
- }
1677
- var ROLE_PRESETS = [
1678
- { value: "", label: "Skip", hint: "no role description" },
1679
- { value: "Full-stack developer", label: "Full-stack developer", hint: "general purpose" },
1680
- { value: "Frontend developer", label: "Frontend developer", hint: "React, CSS, UI" },
1681
- { value: "Backend developer", label: "Backend developer", hint: "APIs, databases, services" },
1682
- { value: "DevOps engineer", label: "DevOps engineer", hint: "CI/CD, infra, deploys" },
1683
- { value: "QA / Test engineer", label: "QA / Test engineer", hint: "testing, quality" },
1684
- { value: "Code reviewer", label: "Code reviewer", hint: "review PRs, find bugs" },
1685
- { value: "Technical writer", label: "Technical writer", hint: "docs, READMEs" },
1686
- { value: "__custom__", label: "Custom...", hint: "type your own" }
1687
- ];
1688
- function buildTeamOptions(teams) {
1689
- return [
1690
- { value: "", label: "None", hint: "no team" },
1691
- ...(teams ?? []).filter((t) => t.status === "active").map((t) => ({ value: t.id, label: t.name, hint: `${t.members.length} members` }))
1692
- ];
1693
- }
1694
- function getAgentWizardSteps(teams) {
1695
- const teamOptions = buildTeamOptions(teams);
1696
- return [
1697
- {
1698
- id: "name",
1699
- label: "Agent name",
1700
- type: "text",
1701
- placeholder: "e.g. alpha, frontend-bot, reviewer",
1702
- required: true
1703
- },
1704
- {
1705
- id: "adapter",
1706
- label: "Provider",
1707
- type: "select",
1708
- options: ADAPTERS
1709
- },
1710
- {
1711
- id: "model",
1712
- label: "Model",
1713
- type: "select",
1714
- getOptions: (vals) => {
1715
- if (vals.adapter === "codex") return CODEX_MODELS;
1716
- if (vals.adapter === "cursor") return CURSOR_MODELS;
1717
- if (vals.adapter === "shell") return SHELL_MODELS;
1718
- return CLAUDE_MODELS;
1719
- }
1720
- },
1721
- {
1722
- id: "role",
1723
- label: "Role / specialization",
1724
- type: "select",
1725
- options: ROLE_PRESETS
1726
- },
1727
- {
1728
- id: "role_custom",
1729
- label: "Describe the role",
1730
- type: "textarea",
1731
- placeholder: "e.g. Specialist in React and TypeScript",
1732
- skip: (vals) => vals.role !== "__custom__"
1733
- },
1734
- {
1735
- id: "team",
1736
- label: "Join team",
1737
- type: "select",
1738
- options: teamOptions,
1739
- skip: () => teamOptions.length <= 1
1740
- // Skip if no teams exist
1741
- }
1742
- ];
1743
- }
1744
- function agentWizardToInput(vals) {
1745
- const role = vals.role === "__custom__" ? vals.role_custom || void 0 : vals.role || void 0;
1746
- return {
1747
- name: vals.name,
1748
- adapter: vals.adapter || "claude",
1749
- role,
1750
- model: vals.model || void 0,
1751
- approval_policy: "auto",
1752
- team_id: vals.team || void 0
1753
- };
1754
- }
1755
- function getTeamWizardSteps(agents) {
1756
- const agentOptions = mapAgentOptions(agents);
1757
- return [
1758
- {
1759
- id: "name",
1760
- label: "Team name",
1761
- type: "text",
1762
- placeholder: "e.g. frontend, backend, qa",
1763
- required: true
1764
- },
1765
- {
1766
- id: "lead",
1767
- label: "Team lead",
1768
- type: "select",
1769
- options: agentOptions
1770
- },
1771
- {
1772
- id: "members",
1773
- label: "Team members",
1774
- type: "multiselect",
1775
- getOptions: (vals) => agentOptions.filter((a) => a.value !== vals.lead),
1776
- skip: (vals) => !agentOptions.some((a) => a.value !== vals.lead)
1777
- },
1778
- {
1779
- id: "description",
1780
- label: "Description",
1781
- type: "textarea",
1782
- placeholder: "Optional team purpose..."
1783
- }
1784
- ];
1785
- }
1786
- function teamWizardToInput(vals) {
1787
- const memberIds = vals.members ? vals.members.split(",").filter(Boolean) : [];
1788
- return {
1789
- name: vals.name,
1790
- lead_agent_id: vals.lead,
1791
- member_agent_ids: memberIds.length > 0 ? memberIds : void 0,
1792
- description: vals.description || void 0
1793
- };
1794
- }
1795
- function getTaskWizardSteps(agents) {
1796
- const agentOptions = buildAgentOptions(agents);
1797
- return [
1798
- {
1799
- id: "title",
1800
- label: "Task title",
1801
- type: "text",
1802
- placeholder: "What needs to be done?",
1803
- required: true
1804
- },
1805
- {
1806
- id: "priority",
1807
- label: "Priority",
1808
- type: "select",
1809
- options: PRIORITY_OPTIONS,
1810
- defaultValue: "3"
1811
- },
1812
- {
1813
- id: "assignee",
1814
- label: "Assignee",
1815
- type: "select",
1816
- options: agentOptions,
1817
- skip: () => agentOptions.length <= 1
1818
- // Skip if no agents to choose from
1819
- },
1820
- {
1821
- id: "description",
1822
- label: "Description",
1823
- type: "textarea",
1824
- placeholder: "Optional details, context, acceptance criteria..."
1825
- }
1826
- ];
1827
- }
1828
- function taskWizardToInput(vals) {
1829
- return {
1830
- title: vals.title,
1831
- priority: vals.priority ? parseInt(vals.priority, 10) : void 0,
1832
- assignee: vals.assignee || void 0,
1833
- description: vals.description || void 0
1834
- };
1835
- }
1836
- function getEditTaskWizardSteps(task, agents) {
1837
- const agentOptions = buildAgentOptions(agents, "None / Auto", "remove assignee");
1838
- return [
1839
- {
1840
- id: "title",
1841
- label: "Task title",
1842
- type: "text",
1843
- defaultValue: task.title,
1844
- required: true
1845
- },
1846
- {
1847
- id: "priority",
1848
- label: "Priority",
1849
- type: "select",
1850
- options: PRIORITY_OPTIONS,
1851
- defaultValue: String(task.priority)
1852
- },
1853
- {
1854
- id: "assignee",
1855
- label: "Assignee",
1856
- type: "select",
1857
- options: agentOptions,
1858
- defaultValue: task.assignee ?? "",
1859
- skip: () => agentOptions.length <= 1
1860
- },
1861
- {
1862
- id: "description",
1863
- label: "Description",
1864
- type: "textarea",
1865
- defaultValue: task.description || "",
1866
- placeholder: "Optional details..."
1867
- }
1868
- ];
1869
- }
1870
- function editTaskWizardToFields(vals) {
1871
- return {
1872
- title: vals.title,
1873
- priority: vals.priority ? parseInt(vals.priority, 10) : void 0,
1874
- assignee: vals.assignee || void 0,
1875
- description: vals.description ?? ""
1876
- };
1877
- }
1878
- function getEditAgentWizardSteps(agent, teams) {
1879
- const currentRoleInPresets = ROLE_PRESETS.find((r) => r.value === agent.role);
1880
- const roleDefault = currentRoleInPresets ? agent.role : agent.role ? "__custom__" : "";
1881
- const modelOptions = agent.adapter === "codex" ? CODEX_MODELS : agent.adapter === "cursor" ? CURSOR_MODELS : agent.adapter === "shell" ? SHELL_MODELS : CLAUDE_MODELS;
1882
- const teamOptions = buildTeamOptions(teams);
1883
- const currentTeamId = teams?.find((t) => t.members.some((m) => m.agent_id === agent.id))?.id;
1884
- return [
1885
- {
1886
- id: "name",
1887
- label: "Agent name",
1888
- type: "text",
1889
- defaultValue: agent.name,
1890
- required: true
1891
- },
1892
- {
1893
- id: "model",
1894
- label: "Model",
1895
- type: "select",
1896
- options: modelOptions,
1897
- defaultValue: agent.config.model ?? ""
1898
- },
1899
- {
1900
- id: "role",
1901
- label: "Role / specialization",
1902
- type: "select",
1903
- options: ROLE_PRESETS,
1904
- defaultValue: roleDefault
1905
- },
1906
- {
1907
- id: "role_custom",
1908
- label: "Describe the role",
1909
- type: "textarea",
1910
- defaultValue: agent.role && !currentRoleInPresets ? agent.role : "",
1911
- placeholder: "e.g. Specialist in React and TypeScript",
1912
- skip: (vals) => vals.role !== "__custom__"
1913
- },
1914
- {
1915
- id: "team",
1916
- label: "Team",
1917
- type: "select",
1918
- options: teamOptions,
1919
- defaultValue: currentTeamId ?? "",
1920
- skip: () => teamOptions.length <= 1
1921
- }
1922
- ];
1923
- }
1924
- var MAX_CONCURRENT_OPTIONS = [
1925
- { value: "1", label: "1 agent", hint: "~0.5 GB RAM, 1 subprocess" },
1926
- { value: "2", label: "2 agents", hint: "~1 GB RAM, 2 subprocesses" },
1927
- { value: "3", label: "3 agents", hint: "~1.5 GB RAM, 3 subprocesses" },
1928
- { value: "4", label: "4 agents", hint: "~2 GB RAM, 4 subprocesses" },
1929
- { value: "6", label: "6 agents", hint: "~3 GB RAM, 6 subprocesses" },
1930
- { value: "8", label: "8 agents", hint: "~4 GB RAM, 8 subprocesses" },
1931
- { value: "10", label: "10 agents", hint: "~5 GB RAM, 10 subprocesses" }
1932
- ];
1933
- var ACTIVITY_FILTER_OPTIONS = [
1934
- { value: "all", label: "All", hint: "show everything" },
1935
- { value: "text", label: "Text", hint: "agent output only" },
1936
- { value: "tools", label: "Tools", hint: "tool calls, results, files" },
1937
- { value: "errors", label: "Errors", hint: "errors only" },
1938
- { value: "events", label: "Events", hint: "lifecycle, system events" }
1939
- ];
1940
- function getConfigWizardSteps(currentFilter, currentMaxConcurrent) {
1941
- return [
1942
- {
1943
- id: "setting",
1944
- label: "Setting",
1945
- type: "select",
1946
- options: [
1947
- { value: "activity_filter", label: "Activity filter", hint: `current: ${currentFilter}` },
1948
- { value: "max_concurrent", label: "Max concurrent agents", hint: `current: ${currentMaxConcurrent}` }
1949
- ]
1950
- },
1951
- {
1952
- id: "activity_filter",
1953
- label: "Activity filter preset",
1954
- type: "select",
1955
- options: ACTIVITY_FILTER_OPTIONS,
1956
- defaultValue: currentFilter,
1957
- skip: (vals) => vals.setting !== "activity_filter"
1958
- },
1959
- {
1960
- id: "max_concurrent",
1961
- label: "Max concurrent agents",
1962
- type: "select",
1963
- options: MAX_CONCURRENT_OPTIONS,
1964
- defaultValue: String(currentMaxConcurrent),
1965
- skip: (vals) => vals.setting !== "max_concurrent"
1966
- }
1967
- ];
1968
- }
1969
- function editAgentWizardToFields(vals) {
1970
- const role = vals.role === "__custom__" ? vals.role_custom || void 0 : vals.role || void 0;
1971
- return {
1972
- name: vals.name,
1973
- role,
1974
- model: vals.model || void 0,
1975
- team_id: vals.team || void 0
1976
- };
1977
- }
1978
- function getGoalWizardSteps(agents) {
1979
- const agentOptions = buildAgentOptions(agents, "Any agent", "auto-assign to autonomous agents");
1980
- return [
1981
- {
1982
- id: "title",
1983
- label: "Goal title",
1984
- type: "text",
1985
- placeholder: "What should be achieved?",
1986
- required: true
1987
- },
1988
- {
1989
- id: "assignee",
1990
- label: "Assignee",
1991
- type: "select",
1992
- options: agentOptions,
1993
- skip: () => agentOptions.length <= 1
1994
- },
1995
- {
1996
- id: "description",
1997
- label: "Description",
1998
- type: "textarea",
1999
- placeholder: "Detailed goal description, success criteria..."
2000
- }
2001
- ];
2002
- }
2003
- function goalWizardToInput(vals) {
2004
- return {
2005
- title: vals.title,
2006
- assignee: vals.assignee || void 0,
2007
- description: vals.description || void 0
2008
- };
2009
- }
2010
- function getEditGoalWizardSteps(goal, agents) {
2011
- const agentOptions = buildAgentOptions(agents, "Any agent", "auto-assign");
2012
- return [
2013
- {
2014
- id: "title",
2015
- label: "Goal title",
2016
- type: "text",
2017
- defaultValue: goal.title,
2018
- required: true
2019
- },
2020
- {
2021
- id: "assignee",
2022
- label: "Assignee",
2023
- type: "select",
2024
- options: agentOptions,
2025
- defaultValue: goal.assignee ?? "",
2026
- skip: () => agentOptions.length <= 1
2027
- },
2028
- {
2029
- id: "description",
2030
- label: "Description",
2031
- type: "textarea",
2032
- defaultValue: goal.description || "",
2033
- placeholder: "Detailed goal description..."
2034
- }
2035
- ];
2036
- }
2037
- function editGoalWizardToFields(vals) {
2038
- return {
2039
- title: vals.title,
2040
- assignee: vals.assignee || void 0,
2041
- description: vals.description ?? ""
2042
- };
2043
- }
2044
- var TASK_LIST_LIMIT = 10;
2045
- var MAX_RUN_MAP_SIZE = 500;
2046
- var MAX_DETAIL_LEN = 2048;
2047
- var MAX_MESSAGES = 200;
2048
- var RUNNABLE = /* @__PURE__ */ new Set(["todo", "failed", "cancelled"]);
2049
- var ONBOARDING_GOALS = {
2050
- title: "Goals",
2051
- description: [
2052
- "Define what your team should achieve.",
2053
- "The orchestrator breaks goals into tasks",
2054
- "and assigns them to agents automatically."
2055
- ],
2056
- hints: [{ key: "N", label: "new goal" }, { key: "/", label: "commands" }],
2057
- nudge: "Add more goals to keep your team focused."
2058
- };
2059
- var ONBOARDING_TASKS = {
2060
- title: "Tasks",
2061
- description: [
2062
- "Units of work dispatched to agents.",
2063
- "Create them manually or let goals",
2064
- "generate them automatically."
2065
- ],
2066
- hints: [{ key: "N", label: "new task" }, { key: "W", label: "start orchestrator" }],
2067
- nudge: "Add more tasks to keep agents busy."
2068
- };
2069
- var ONBOARDING_AGENTS = {
2070
- title: "Agents",
2071
- description: [
2072
- "AI workers that execute your tasks.",
2073
- "Each agent uses an adapter (claude, codex,",
2074
- "cursor, shell) and has its own role."
2075
- ],
2076
- hints: [{ key: "N", label: "new agent" }, { key: "W", label: "start orchestrator" }],
2077
- nudge: "Add more agents to increase parallelism."
2078
- };
2079
- var LIFECYCLE_TAG_RE = /^\[[\w_]+\]$/;
2080
- function classifyAgentSummary(summary) {
2081
- if (LIFECYCLE_TAG_RE.test(summary)) return { msgType: "lifecycle", color: tuiColors.dim };
2082
- if (summary.startsWith("\u2699")) return { msgType: "tool", color: tuiColors.dim };
2083
- if (summary.startsWith("\u2190")) return { msgType: "result", color: tuiColors.dim };
2084
- if (summary.startsWith("\u2713")) return { msgType: "lifecycle", color: tuiColors.dim };
2085
- if (summary.startsWith("\u23F3")) return { msgType: "info", color: tuiColors.silver };
2086
- return { msgType: "output", color: tuiColors.silver };
2087
- }
2088
- var AGENT_COLORS = [
2089
- "#5faf87",
2090
- // green
2091
- "#5fafd7",
2092
- // blue
2093
- "#af87ff",
2094
- // purple
2095
- "#d7af00",
2096
- // yellow
2097
- "#5fd7d7",
2098
- // cyan
2099
- "#d787af",
2100
- // pink
2101
- "#afaf5f",
2102
- // olive
2103
- "#d7875f"
2104
- // orange
2105
- ];
2106
- function getAgentColor(agentId, agents) {
2107
- const idx = agents.findIndex((a) => a.id === agentId);
2108
- return AGENT_COLORS[idx >= 0 ? idx % AGENT_COLORS.length : 0];
2109
- }
2110
- var AGENT_INDENT = " ".repeat(9);
2111
- var MSG_ICONS2 = {
2112
- system: "\u2666",
2113
- // ◆
2114
- lifecycle: "\u25B6",
2115
- // ▶
2116
- output: "\u2502",
2117
- // │
2118
- tool: "\u2699",
2119
- // ⚙
2120
- result: "\u2190",
2121
- // ←
2122
- error: "\u2715",
2123
- // ✕
2124
- file: "\u270E",
2125
- // ✎
2126
- info: "\u2502"
2127
- // │
2128
- };
2129
- var ALL_MSG_TYPES = ["system", "lifecycle", "output", "tool", "result", "error", "file", "info"];
2130
- var ACTIVITY_PRESETS = [
2131
- { label: "all", types: ALL_MSG_TYPES },
2132
- { label: "text", types: ["output"] },
2133
- { label: "tools", types: ["tool", "result", "file"] },
2134
- { label: "errors", types: ["error"] },
2135
- { label: "events", types: ["lifecycle", "system"] }
2136
- ];
2137
- function cyclePreset(current) {
2138
- const curIdx = ACTIVITY_PRESETS.findIndex((p) => p.types.length === current.size && p.types.every((t) => current.has(t)));
2139
- const nextIdx = (curIdx + 1) % ACTIVITY_PRESETS.length;
2140
- return ACTIVITY_PRESETS[nextIdx];
2141
- }
2142
- function App({
2143
- projectName,
2144
- tasks: initialTasks,
2145
- agents: initialAgents = [],
2146
- state: initialState,
2147
- onRunTask,
2148
- onCreateTask,
2149
- onCancelTask,
2150
- onRetryTask,
2151
- onAssignTask,
2152
- onRunAll,
2153
- onDisableAgent,
2154
- onEnableAgent,
2155
- onSubscribeEvents,
2156
- onRefreshTasks,
2157
- onRefreshAgents,
2158
- onRefreshState,
2159
- onLoadHistory,
2160
- onAddAgent,
2161
- onDeleteAgent,
2162
- onApproveTask,
2163
- onRejectTask,
2164
- onDeleteTask,
2165
- onUpdateTask,
2166
- onUpdateAgent,
2167
- onForceStopAgent,
2168
- onCreateTeam,
2169
- onListTeams,
2170
- onJoinTeam,
2171
- onLeaveTeam,
2172
- onDisbandTeam,
2173
- onSetTeamLead,
2174
- onStartWatch,
2175
- onStopWatch,
2176
- onToggleAutonomous,
2177
- onRefreshGoals,
2178
- onCreateGoal,
2179
- onUpdateGoal,
2180
- onUpdateGoalStatus,
2181
- onDeleteGoal,
2182
- initialWatchActive,
2183
- watchError,
2184
- messageBatchMs = process.env.VITEST ? 0 : 80,
2185
- initialActivityFilter = "all",
2186
- onSaveActivityFilter,
2187
- initialMaxConcurrent = DEFAULT_CONFIG.scheduling.max_concurrent_agents,
2188
- onSaveMaxConcurrent
2189
- }) {
2190
- const { exit } = useApp();
2191
- const { stdout } = useStdout();
2192
- const [termSize, setTermSize] = useState({ w: stdout?.columns ?? 80, h: stdout?.rows ?? 24 });
2193
- useEffect(() => {
2194
- if (!stdout) return;
2195
- const onResize = () => setTermSize({ w: stdout.columns, h: stdout.rows });
2196
- stdout.on("resize", onResize);
2197
- return () => {
2198
- stdout.off("resize", onResize);
2199
- };
2200
- }, [stdout]);
2201
- const W = termSize.w;
2202
- const H = termSize.h;
2203
- const [liveTasks, setLiveTasks] = useState(initialTasks);
2204
- const [liveAgents, setLiveAgents] = useState(initialAgents);
2205
- const [liveState, setLiveState] = useState(initialState);
2206
- const [watchActive, setWatchActive] = useState(initialWatchActive ?? !!initialState.pid);
2207
- const [liveGoals, setLiveGoals] = useState([]);
2208
- const [activeView, setActiveView] = useState("tasks");
2209
- const [taskSelectedIndex, setTaskSelectedIndex] = useState(0);
2210
- const [agentSelectedIndex, setAgentSelectedIndex] = useState(0);
2211
- const [goalSelectedIndex, setGoalSelectedIndex] = useState(0);
2212
- const [detailOpen, setDetailOpen] = useState(false);
2213
- const [messages, setMessages] = useState([]);
2214
- const [inputMode, setInputMode] = useState("none");
2215
- const [inputValue, setInputValue] = useState("");
2216
- const [wizardConfig, setWizardConfig] = useState(null);
2217
- const [logFilter, setLogFilter] = useState(0);
2218
- const [logTypeFilter, setLogTypeFilter] = useState(() => new Set(ALL_MSG_TYPES));
2219
- const [logSelectedIndex, setLogSelectedIndex] = useState(-1);
2220
- const [logScrollOffset, setLogScrollOffset] = useState(0);
2221
- const [activityFilter, setActivityFilter] = useState(() => {
2222
- const preset = ACTIVITY_PRESETS.find((p) => p.label === initialActivityFilter);
2223
- return new Set(preset?.types ?? ALL_MSG_TYPES);
2224
- });
2225
- const activityFilterLabel = useMemo(() => {
2226
- const preset = ACTIVITY_PRESETS.find((p) => p.types.length === activityFilter.size && p.types.every((t) => activityFilter.has(t)));
2227
- return preset?.label ?? "all";
2228
- }, [activityFilter]);
2229
- const activityFilteredMessages = useMemo(() => {
2230
- if (activityFilter.size >= ALL_MSG_TYPES.length) return messages;
2231
- return messages.filter((m) => activityFilter.has(m.msgType ?? "info"));
2232
- }, [messages, activityFilter]);
2233
- const [maxConcurrent, setMaxConcurrent] = useState(initialMaxConcurrent);
2234
- const cmdHistory = React5.useRef(new CommandHistory()).current;
2235
- const [taskScrollOffset, setTaskScrollOffset] = useState(0);
2236
- const [showAllTasks, setShowAllTasks] = useState(false);
2237
- const [agentScrollOffset, setAgentScrollOffset] = useState(0);
2238
- const [goalScrollOffset, setGoalScrollOffset] = useState(0);
2239
- const [suggestionIndex, setSuggestionIndex] = useState(0);
2240
- const [liveTeams, setLiveTeams] = useState([]);
2241
- const liveTeamsRef = useRef(liveTeams);
2242
- liveTeamsRef.current = liveTeams;
2243
- const refreshAll = useCallback(async (opts) => {
2244
- const [t, a, s, teams, goals] = await Promise.all([
2245
- onRefreshTasks?.() ?? Promise.resolve(liveTasks),
2246
- onRefreshAgents?.() ?? Promise.resolve(liveAgents),
2247
- onRefreshState?.() ?? Promise.resolve(liveState),
2248
- opts?.includeTeams ? onListTeams?.() ?? Promise.resolve(liveTeamsRef.current) : Promise.resolve(null),
2249
- onRefreshGoals?.() ?? Promise.resolve(liveGoals)
2250
- ]);
2251
- setLiveTasks(t);
2252
- setLiveAgents(a);
2253
- setLiveState(s);
2254
- if (teams !== null) setLiveTeams(teams);
2255
- setLiveGoals(goals);
2256
- if (initialWatchActive) {
2257
- setWatchActive(!!s.pid);
2258
- }
2259
- }, [onRefreshTasks, onRefreshAgents, onRefreshState, onListTeams, onRefreshGoals, initialWatchActive]);
2260
- const sortedTasks = useMemo(
2261
- () => [...liveTasks].sort((a, b) => (STATUS_ORDER[a.status] ?? 9) - (STATUS_ORDER[b.status] ?? 9)),
2262
- [liveTasks]
2263
- );
2264
- const visibleTasks = showAllTasks ? sortedTasks : sortedTasks.slice(0, TASK_LIST_LIMIT);
2265
- const hiddenTaskCount = sortedTasks.length - visibleTasks.length;
2266
- const selectedTask = sortedTasks[taskSelectedIndex];
2267
- const taskTitleMap = useMemo(() => {
2268
- const map = /* @__PURE__ */ new Map();
2269
- for (const t of liveTasks) map.set(t.id, t.title);
2270
- return map;
2271
- }, [liveTasks]);
2272
- const agentNameMap = useMemo(() => {
2273
- const map = /* @__PURE__ */ new Map();
2274
- for (const a of liveAgents) map.set(a.id, a.name);
2275
- return map;
2276
- }, [liveAgents]);
2277
- const { agentTeamMap, activeTeamCount, teamLeadSet } = useMemo(() => {
2278
- const map = /* @__PURE__ */ new Map();
2279
- const leads = /* @__PURE__ */ new Set();
2280
- let count = 0;
2281
- for (const team of liveTeams) {
2282
- if (team.status !== "active") continue;
2283
- count++;
2284
- leads.add(team.lead_agent_id);
2285
- for (const member of team.members) {
2286
- map.set(member.agent_id, team.name);
2287
- }
2288
- }
2289
- return { agentTeamMap: map, activeTeamCount: count, teamLeadSet: leads };
2290
- }, [liveTeams]);
2291
- const sortedAgents = useMemo(() => {
2292
- const agents = [...liveAgents];
2293
- agents.sort((a, b) => {
2294
- const teamA = agentTeamMap.get(a.id);
2295
- const teamB = agentTeamMap.get(b.id);
2296
- if (teamA && !teamB) return -1;
2297
- if (!teamA && teamB) return 1;
2298
- if (teamA && teamB && teamA !== teamB) return teamA.localeCompare(teamB);
2299
- return (AGENT_STATUS_ORDER[a.status] ?? 9) - (AGENT_STATUS_ORDER[b.status] ?? 9);
2300
- });
2301
- return agents;
2302
- }, [liveAgents, agentTeamMap]);
2303
- const selectedAgent = sortedAgents[agentSelectedIndex];
2304
- const sortedGoals = useMemo(
2305
- () => [...liveGoals].sort((a, b) => (GOAL_STATUS_ORDER[a.status] ?? 9) - (GOAL_STATUS_ORDER[b.status] ?? 9)),
2306
- [liveGoals]
2307
- );
2308
- const selectedGoal = sortedGoals[goalSelectedIndex];
2309
- const runIdToAgentId = useRef(/* @__PURE__ */ new Map());
2310
- const runIdToTaskId = useRef(/* @__PURE__ */ new Map());
2311
- useEffect(() => {
2312
- for (const [taskId, entry] of Object.entries(liveState.running)) {
2313
- runIdToAgentId.current.set(entry.run_id, entry.agent_id);
2314
- runIdToTaskId.current.set(entry.run_id, taskId);
2315
- }
2316
- if (runIdToAgentId.current.size > MAX_RUN_MAP_SIZE) {
2317
- const excess = runIdToAgentId.current.size - MAX_RUN_MAP_SIZE;
2318
- let i = 0;
2319
- for (const key of runIdToAgentId.current.keys()) {
2320
- if (i++ >= excess) break;
2321
- runIdToAgentId.current.delete(key);
2322
- runIdToTaskId.current.delete(key);
2323
- }
2324
- }
2325
- }, [liveState.running]);
2326
- const pendingMessages = useRef([]);
2327
- const flushTimer = useRef(null);
2328
- const flushMessages = useCallback(() => {
2329
- flushTimer.current = null;
2330
- if (pendingMessages.current.length === 0) return;
2331
- const batch = pendingMessages.current;
2332
- pendingMessages.current = [];
2333
- setMessages((prev) => {
2334
- if (batch.length >= MAX_MESSAGES) return batch.slice(-MAX_MESSAGES);
2335
- const keep = MAX_MESSAGES - batch.length;
2336
- const trimmed = prev.length > keep ? prev.slice(-keep) : prev;
2337
- return trimmed.concat(batch);
2338
- });
2339
- }, []);
2340
- useEffect(() => {
2341
- return () => {
2342
- if (flushTimer.current) clearTimeout(flushTimer.current);
2343
- };
2344
- }, []);
2345
- const addMessage = useCallback((text, color, opts) => {
2346
- const now = /* @__PURE__ */ new Date();
2347
- const time = now.toLocaleTimeString("en-US", {
2348
- hour12: false,
2349
- hour: "2-digit",
2350
- minute: "2-digit",
2351
- second: "2-digit"
2352
- });
2353
- const detail = opts?.detail && opts.detail.length > MAX_DETAIL_LEN ? opts.detail.slice(0, MAX_DETAIL_LEN) + "\u2026[truncated]" : opts?.detail;
2354
- pendingMessages.current.push({ text, color, time, ts: now.getTime(), ...opts, detail });
2355
- if (pendingMessages.current.length > MAX_MESSAGES * 2) {
2356
- pendingMessages.current = pendingMessages.current.slice(-MAX_MESSAGES);
2357
- }
2358
- if (messageBatchMs === 0) {
2359
- flushMessages();
2360
- } else if (!flushTimer.current) {
2361
- flushTimer.current = setTimeout(flushMessages, messageBatchMs);
2362
- }
2363
- }, [flushMessages]);
2364
- useEffect(() => {
2365
- if (watchError) {
2366
- addMessage(`Watch mode failed: ${watchError}. Tasks will not auto-dispatch.`, tuiColors.red);
2367
- }
2368
- }, []);
2369
- useEffect(() => {
2370
- if (!onLoadHistory) return;
2371
- const historyEntryToMsg = (entry) => {
2372
- const time = new Date(entry.timestamp).toLocaleTimeString("en-US", {
2373
- hour12: false,
2374
- hour: "2-digit",
2375
- minute: "2-digit",
2376
- second: "2-digit"
2377
- });
2378
- const raw = typeof entry.data === "string" ? entry.data : JSON.stringify(entry.data);
2379
- let text;
2380
- let color = tuiColors.silver;
2381
- let msgType = "output";
2382
- if (entry.type === "error") {
2383
- text = typeof entry.data === "string" ? entry.data : JSON.stringify(entry.data);
2384
- text = text.slice(0, 200);
2385
- color = tuiColors.red;
2386
- msgType = "error";
2387
- } else if (entry.type === "file_changed") {
2388
- text = String(entry.data);
2389
- color = tuiColors.purple;
2390
- msgType = "file";
2391
- } else if (entry.type === "done") {
2392
- text = "Completed";
2393
- color = tuiColors.green;
2394
- msgType = "lifecycle";
2395
- } else if (entry.type === "tool_call") {
2396
- const d = entry.data;
2397
- text = `\u2699 ${d?.name ?? "tool"}()`;
2398
- color = tuiColors.cyan;
2399
- msgType = "tool";
2400
- } else {
2401
- const { summary } = formatAgentOutput(raw);
2402
- text = summary;
2403
- const cls = classifyAgentSummary(text);
2404
- msgType = cls.msgType;
2405
- color = cls.color;
2406
- }
2407
- return { text, color, time, ts: new Date(entry.timestamp).getTime(), agentId: entry.agentId, taskId: entry.taskId, msgType };
2408
- };
2409
- onLoadHistory((batch) => {
2410
- if (batch.length === 0) return;
2411
- const histMsgs = batch.map(historyEntryToMsg);
2412
- setMessages((prev) => {
2413
- const combined = [...histMsgs, ...prev];
2414
- return combined.length > MAX_MESSAGES ? combined.slice(-MAX_MESSAGES) : combined;
2415
- });
2416
- }).catch(() => {
2417
- });
2418
- }, []);
2419
- useEffect(() => {
2420
- onListTeams?.().then(setLiveTeams).catch(() => {
2421
- });
2422
- onRefreshGoals?.().then(setLiveGoals).catch(() => {
2423
- });
2424
- }, []);
2425
- const launchAgentWizard = useCallback(() => {
2426
- setWizardConfig({
2427
- title: "NEW AGENT",
2428
- steps: getAgentWizardSteps(liveTeamsRef.current),
2429
- kind: "agent"
2430
- });
2431
- setInputMode("wizard");
2432
- }, []);
2433
- const launchTaskWizard = useCallback(() => {
2434
- setWizardConfig({
2435
- title: "NEW TASK",
2436
- steps: getTaskWizardSteps(liveAgents),
2437
- kind: "task"
2438
- });
2439
- setInputMode("wizard");
2440
- }, [liveAgents]);
2441
- const launchEditTaskWizard = useCallback((task) => {
2442
- setWizardConfig({
2443
- title: "EDIT TASK",
2444
- steps: getEditTaskWizardSteps(task, liveAgents),
2445
- kind: "edit_task",
2446
- targetId: task.id
2447
- });
2448
- setInputMode("wizard");
2449
- }, [liveAgents]);
2450
- const launchTeamWizard = useCallback(() => {
2451
- setWizardConfig({
2452
- title: "NEW TEAM",
2453
- steps: getTeamWizardSteps(liveAgents),
2454
- kind: "team"
2455
- });
2456
- setInputMode("wizard");
2457
- }, [liveAgents]);
2458
- const launchEditAgentWizard = useCallback((agent) => {
2459
- setWizardConfig({
2460
- title: "EDIT AGENT",
2461
- steps: getEditAgentWizardSteps(agent, liveTeams),
2462
- kind: "edit_agent",
2463
- targetId: agent.id
2464
- });
2465
- setInputMode("wizard");
2466
- }, [liveTeams]);
2467
- const launchConfigWizard = useCallback(() => {
2468
- setWizardConfig({
2469
- title: "SETTINGS",
2470
- steps: getConfigWizardSteps(activityFilterLabel, maxConcurrent),
2471
- kind: "config"
2472
- });
2473
- setInputMode("wizard");
2474
- }, [activityFilterLabel, maxConcurrent]);
2475
- const handleWizardComplete = useCallback((values) => {
2476
- setInputMode("none");
2477
- const kind = wizardConfig?.kind;
2478
- const targetId = wizardConfig?.targetId;
2479
- setWizardConfig(null);
2480
- if (kind === "agent" && onAddAgent) {
2481
- const input = agentWizardToInput(values);
2482
- addMessage(`Creating agent "${input.name}"...`, tuiColors.amber);
2483
- onAddAgent(input.name, input.adapter, {
2484
- model: input.model,
2485
- role: input.role,
2486
- approval_policy: input.approval_policy
2487
- }).then(
2488
- (agent) => {
2489
- addMessage(`\u2713 Created agent "${agent.name}" (${agent.id}, ${agent.adapter})`, tuiColors.green);
2490
- if (input.team_id && onJoinTeam) {
2491
- onJoinTeam(input.team_id, agent.id).then(
2492
- (t) => {
2493
- addMessage(`\u2713 Joined team "${t.name}"`, tuiColors.green);
2494
- refreshAll({ includeTeams: true });
2495
- },
2496
- (err) => addMessage(`Failed to join team: ${err instanceof Error ? err.message : String(err)}`, tuiColors.red)
2497
- );
2498
- } else {
2499
- refreshAll();
2500
- }
2501
- },
2502
- (err) => addMessage(`Failed: ${err instanceof Error ? err.message : String(err)}`, tuiColors.red)
2503
- );
2504
- } else if (kind === "team" && onCreateTeam) {
2505
- const input = teamWizardToInput(values);
2506
- addMessage(`Creating team "${input.name}"...`, tuiColors.amber);
2507
- onCreateTeam(input).then(
2508
- (team) => {
2509
- addMessage(`\u2713 Created team "${team.name}" (${team.id}, ${team.members.length} members)`, tuiColors.green);
2510
- refreshAll({ includeTeams: true });
2511
- },
2512
- (err) => addMessage(`Failed: ${err instanceof Error ? err.message : String(err)}`, tuiColors.red)
2513
- );
2514
- } else if (kind === "task" && onCreateTask) {
2515
- const input = taskWizardToInput(values);
2516
- addMessage(`Creating "${input.title}"...`, tuiColors.amber);
2517
- onCreateTask(input.title, {
2518
- priority: input.priority,
2519
- description: input.description
2520
- }).then(
2521
- (task) => {
2522
- addMessage(`\u2713 Created "${task.title}" (${task.id})`, tuiColors.green);
2523
- if (input.assignee && onAssignTask) {
2524
- onAssignTask(task.id, input.assignee).catch(() => {
2525
- });
2526
- }
2527
- refreshAll();
2528
- },
2529
- (err) => addMessage(`Failed: ${err instanceof Error ? err.message : String(err)}`, tuiColors.red)
2530
- );
2531
- } else if (kind === "edit_task" && targetId && onUpdateTask) {
2532
- const fields = editTaskWizardToFields(values);
2533
- addMessage(`Updating task...`, tuiColors.amber);
2534
- onUpdateTask(targetId, fields).then(
2535
- (task) => {
2536
- addMessage(`\u2713 Updated "${task.title}"`, tuiColors.green);
2537
- if (fields.assignee && onAssignTask) {
2538
- onAssignTask(targetId, fields.assignee).catch(() => {
2539
- });
2540
- }
2541
- refreshAll();
2542
- },
2543
- (err) => addMessage(`Failed: ${err instanceof Error ? err.message : String(err)}`, tuiColors.red)
2544
- );
2545
- } else if (kind === "edit_agent" && targetId && onUpdateAgent) {
2546
- const fields = editAgentWizardToFields(values);
2547
- const newTeamId = fields.team_id ?? "";
2548
- const oldTeamId = liveTeams.find((t) => t.members.some((m) => m.agent_id === targetId))?.id ?? "";
2549
- addMessage(`Updating agent...`, tuiColors.amber);
2550
- onUpdateAgent(targetId, { name: fields.name, role: fields.role, model: fields.model }).then(
2551
- (agent) => {
2552
- addMessage(`\u2713 Updated agent "${agent.name}"`, tuiColors.green);
2553
- const teamOps = [];
2554
- if (oldTeamId && oldTeamId !== newTeamId && onLeaveTeam) {
2555
- teamOps.push(
2556
- onLeaveTeam(oldTeamId, targetId).then(
2557
- (t) => addMessage(`\u2713 Left team "${t.name}"`, tuiColors.green),
2558
- (err) => addMessage(`Failed to leave team: ${err instanceof Error ? err.message : String(err)}`, tuiColors.red)
2559
- )
2560
- );
2561
- }
2562
- if (newTeamId && newTeamId !== oldTeamId && onJoinTeam) {
2563
- teamOps.push(
2564
- onJoinTeam(newTeamId, targetId).then(
2565
- (t) => addMessage(`\u2713 Joined team "${t.name}"`, tuiColors.green),
2566
- (err) => addMessage(`Failed to join team: ${err instanceof Error ? err.message : String(err)}`, tuiColors.red)
2567
- )
2568
- );
2569
- }
2570
- Promise.all(teamOps).then(() => refreshAll({ includeTeams: teamOps.length > 0 }));
2571
- },
2572
- (err) => addMessage(`Failed: ${err instanceof Error ? err.message : String(err)}`, tuiColors.red)
2573
- );
2574
- } else if (kind === "config") {
2575
- if (values.setting === "activity_filter" && values.activity_filter) {
2576
- const preset = ACTIVITY_PRESETS.find((p) => p.label === values.activity_filter);
2577
- if (preset) {
2578
- setActivityFilter(new Set(preset.types));
2579
- onSaveActivityFilter?.(preset.label);
2580
- addMessage(`Activity filter: ${preset.label}`, tuiColors.amber);
2581
- }
2582
- } else if (values.setting === "max_concurrent" && values.max_concurrent) {
2583
- const num = parseInt(values.max_concurrent, 10);
2584
- if (num > 0) {
2585
- setMaxConcurrent(num);
2586
- onSaveMaxConcurrent?.(num);
2587
- addMessage(`Max concurrent agents: ${num}`, tuiColors.amber);
2588
- }
2589
- }
2590
- } else if (kind === "goal" && onCreateGoal) {
2591
- const input = goalWizardToInput(values);
2592
- addMessage(`Creating goal "${input.title}"...`, tuiColors.amber);
2593
- onCreateGoal(input).then(
2594
- (goal) => {
2595
- addMessage(`\u2713 Created goal "${goal.title}" (${goal.id})`, tuiColors.green);
2596
- refreshAll();
2597
- },
2598
- (err) => addMessage(`Failed: ${err instanceof Error ? err.message : String(err)}`, tuiColors.red)
2599
- );
2600
- } else if (kind === "edit_goal" && targetId && onUpdateGoal) {
2601
- const fields = editGoalWizardToFields(values);
2602
- addMessage(`Updating goal...`, tuiColors.amber);
2603
- onUpdateGoal(targetId, fields).then(
2604
- (goal) => {
2605
- addMessage(`\u2713 Updated goal "${goal.title}"`, tuiColors.green);
2606
- refreshAll();
2607
- },
2608
- (err) => addMessage(`Failed: ${err instanceof Error ? err.message : String(err)}`, tuiColors.red)
2609
- );
2610
- }
2611
- }, [wizardConfig, onAddAgent, onCreateTask, onCreateTeam, onJoinTeam, onLeaveTeam, onAssignTask, onUpdateTask, onUpdateAgent, onToggleAutonomous, onCreateGoal, onUpdateGoal, addMessage, refreshAll, onSaveActivityFilter, onSaveMaxConcurrent, liveTeams]);
2612
- const handleWizardCancel = useCallback(() => {
2613
- setInputMode("none");
2614
- setWizardConfig(null);
2615
- }, []);
2616
- useEffect(() => {
2617
- if (!onSubscribeEvents) return;
2618
- let refreshTimer = null;
2619
- const scheduleRefresh = () => {
2620
- if (refreshTimer) return;
2621
- refreshTimer = setTimeout(() => {
2622
- refreshTimer = null;
2623
- refreshAll().catch(() => {
2624
- });
2625
- }, 150);
2626
- };
2627
- const unsubscribe = onSubscribeEvents((event) => {
2628
- if (event.type === "agent:started") {
2629
- runIdToAgentId.current.set(event.runId, event.agentId);
2630
- runIdToTaskId.current.set(event.runId, event.taskId);
2631
- }
2632
- formatEvent(event, addMessage, runIdToAgentId.current, runIdToTaskId.current);
2633
- if (event.type === "task:status_changed" || event.type === "task:created" || event.type === "task:assigned" || event.type === "agent:started" || event.type === "agent:completed" || event.type === "run:retry" || event.type === "goal:created" || event.type === "goal:status_changed" || event.type === "goal:updated" || event.type === "goal:deleted") {
2634
- scheduleRefresh();
2635
- }
2636
- });
2637
- return () => {
2638
- unsubscribe();
2639
- if (refreshTimer) clearTimeout(refreshTimer);
2640
- };
2641
- }, [onSubscribeEvents, addMessage, refreshAll]);
2642
- const mode = watchActive ? "watching" : "idle";
2643
- const uptime = liveState.started_at ? formatDurationSince(liveState.started_at) : void 0;
2644
- const totalTokens = liveState.stats.total_tokens.total;
2645
- const headerStats = useMemo(() => {
2646
- const counts = { running: 0, retrying: 0, review: 0, todo: 0, done: 0, failed: 0, cancelled: 0 };
2647
- for (const t of liveTasks) {
2648
- if (t.status === "in_progress") counts.running++;
2649
- else if (t.status === "retrying") counts.retrying++;
2650
- else if (t.status === "review") counts.review++;
2651
- else if (t.status === "todo") counts.todo++;
2652
- else if (t.status === "done") counts.done++;
2653
- else if (t.status === "failed") counts.failed++;
2654
- else if (t.status === "cancelled") counts.cancelled++;
2655
- }
2656
- return { ...counts, teams: activeTeamCount };
2657
- }, [liveTasks, activeTeamCount]);
2658
- headerStats.running;
2659
- const headerTokens = {
2660
- input: liveState.stats.total_tokens.input ?? 0,
2661
- output: liveState.stats.total_tokens.output ?? 0,
2662
- total: totalTokens
2663
- };
2664
- const fixedRows = 9;
2665
- const contentH = Math.max(4, H - fixedRows);
2666
- const teamAgentCount = agentTeamMap.size;
2667
- const hasUnassigned = liveAgents.length > teamAgentCount;
2668
- const agentSectionRows = activeTeamCount > 0 ? activeTeamCount + (hasUnassigned ? 1 : 0) : 0;
2669
- const listItemCount = activeView === "goals" ? sortedGoals.length + 1 : activeView === "tasks" ? visibleTasks.length + 1 + (hiddenTaskCount > 0 ? 1 : 0) : (
2670
- // +1 for "+ add" row, +1 for "show all" row
2671
- activeView === "agents" ? liveAgents.length + 1 + agentSectionRows : 0
2672
- );
2673
- const minListH = Math.min(listItemCount + 1, Math.ceil(contentH * 0.5));
2674
- const mainH = activeView === "logs" ? contentH : Math.max(2, Math.min(minListH, contentH - 4));
2675
- const feedH = Math.max(1, contentH - mainH);
2676
- const ruleW = Math.max(10, W - 2);
2677
- const suggestions = useMemo(
2678
- () => inputMode === "command" ? resolveSuggestions(inputValue) : [],
2679
- [inputMode, inputValue]
2680
- );
2681
- useEffect(() => {
2682
- setTaskScrollOffset((o) => Math.min(o, Math.max(0, visibleTasks.length - mainH)));
2683
- }, [visibleTasks.length, mainH]);
2684
- useEffect(() => {
2685
- setAgentScrollOffset((o) => Math.min(o, Math.max(0, sortedAgents.length - mainH)));
2686
- }, [sortedAgents.length, mainH]);
2687
- useEffect(() => {
2688
- setGoalScrollOffset((o) => Math.min(o, Math.max(0, sortedGoals.length - mainH)));
2689
- }, [sortedGoals.length, mainH]);
2690
- const executeCommand = useCallback((raw) => {
2691
- const stripped = raw.trim().replace(/^\//, "");
2692
- const parts = stripped.split(/\s+/);
2693
- const cmd = parts[0]?.toLowerCase();
2694
- if (!cmd) return;
2695
- const errMsg = (err) => err instanceof Error ? err.message : String(err);
2696
- switch (cmd) {
2697
- // ── Legacy shortcuts (backward compat) ──
2698
- case "cancel": {
2699
- if (!selectedTask) {
2700
- addMessage("No task selected", tuiColors.yellow);
2701
- return;
2702
- }
2703
- if (!onCancelTask) return;
2704
- addMessage(`Cancelling "${selectedTask.title}"...`, tuiColors.amber);
2705
- onCancelTask(selectedTask.id).then(
2706
- () => {
2707
- addMessage(`\u2713 Cancelled "${selectedTask.title}"`, tuiColors.green);
2708
- refreshAll();
2709
- },
2710
- (err) => addMessage(`Failed: ${errMsg(err)}`, tuiColors.red)
2711
- );
2712
- return;
2713
- }
2714
- case "retry": {
2715
- if (!selectedTask) {
2716
- addMessage("No task selected", tuiColors.yellow);
2717
- return;
2718
- }
2719
- if (!onRetryTask) return;
2720
- addMessage(`Retrying "${selectedTask.title}"...`, tuiColors.amber);
2721
- onRetryTask(selectedTask.id).then(
2722
- () => {
2723
- addMessage(`\u2713 Retried "${selectedTask.title}"`, tuiColors.green);
2724
- refreshAll();
2725
- },
2726
- (err) => addMessage(`Failed: ${errMsg(err)}`, tuiColors.red)
2727
- );
2728
- return;
2729
- }
2730
- case "assign": {
2731
- if (!selectedTask) {
2732
- addMessage("No task selected", tuiColors.yellow);
2733
- return;
2734
- }
2735
- if (!onAssignTask || !parts[1]) {
2736
- addMessage("Usage: assign <agent>", tuiColors.yellow);
2737
- return;
2738
- }
2739
- addMessage(`Assigning "${selectedTask.title}" to ${parts[1]}...`, tuiColors.amber);
2740
- onAssignTask(selectedTask.id, parts[1]).then(
2741
- () => {
2742
- addMessage(`\u2713 Assigned "${selectedTask.title}" to ${parts[1]}`, tuiColors.green);
2743
- refreshAll();
2744
- },
2745
- (err) => addMessage(`Failed: ${errMsg(err)}`, tuiColors.red)
2746
- );
2747
- return;
2748
- }
2749
- // ── /task group ──
2750
- case "task": {
2751
- const sub = parts[1]?.toLowerCase();
2752
- if (sub === "add") {
2753
- const title = parts.slice(2).join(" ");
2754
- if (!title) {
2755
- launchTaskWizard();
2756
- return;
2757
- }
2758
- if (!onCreateTask) {
2759
- addMessage("Create not available", tuiColors.yellow);
2760
- return;
2761
- }
2762
- addMessage(`Creating "${title}"...`, tuiColors.amber);
2763
- onCreateTask(title).then(
2764
- (task) => {
2765
- addMessage(`\u2713 Created "${task.title}" (${task.id})`, tuiColors.green);
2766
- refreshAll();
2767
- },
2768
- (err) => addMessage(`Failed: ${errMsg(err)}`, tuiColors.red)
2769
- );
2770
- } else if (sub === "list") {
2771
- const lines = sortedTasks.map((t) => ` ${t.id} ${t.status.padEnd(11)} ${t.title}`);
2772
- if (lines.length === 0) addMessage("No tasks", tuiColors.dim);
2773
- else for (const line of lines) addMessage(line, tuiColors.cyan);
2774
- } else if (sub === "show") {
2775
- const t = parts[2] ? sortedTasks.find((x) => x.id === parts[2]) : selectedTask;
2776
- if (!t) {
2777
- addMessage("No task selected or id given", tuiColors.yellow);
2778
- return;
2779
- }
2780
- addMessage(`${t.id} ${t.status} P${t.priority} "${t.title}"`, tuiColors.cyan);
2781
- if (t.assignee) addMessage(` agent: ${t.assignee}`, tuiColors.dim);
2782
- if (t.description) addMessage(` ${t.description.slice(0, 100)}`, tuiColors.dim);
2783
- } else if (sub === "cancel") {
2784
- const t = parts[2] ? sortedTasks.find((x) => x.id === parts[2]) : selectedTask;
2785
- if (!t) {
2786
- addMessage("No task selected or id given", tuiColors.yellow);
2787
- return;
2788
- }
2789
- if (!onCancelTask) return;
2790
- addMessage(`Cancelling "${t.title}"...`, tuiColors.amber);
2791
- onCancelTask(t.id).then(
2792
- () => {
2793
- addMessage(`\u2713 Cancelled "${t.title}"`, tuiColors.green);
2794
- refreshAll();
2795
- },
2796
- (err) => addMessage(`Failed: ${errMsg(err)}`, tuiColors.red)
2797
- );
2798
- } else if (sub === "retry") {
2799
- const t = parts[2] ? sortedTasks.find((x) => x.id === parts[2]) : selectedTask;
2800
- if (!t) {
2801
- addMessage("No task selected or id given", tuiColors.yellow);
2802
- return;
2803
- }
2804
- if (!onRetryTask) return;
2805
- addMessage(`Retrying "${t.title}"...`, tuiColors.amber);
2806
- onRetryTask(t.id).then(
2807
- () => {
2808
- addMessage(`\u2713 Retried "${t.title}"`, tuiColors.green);
2809
- refreshAll();
2810
- },
2811
- (err) => addMessage(`Failed: ${errMsg(err)}`, tuiColors.red)
2812
- );
2813
- } else if (sub === "assign") {
2814
- const foundByParts2 = parts[2] ? sortedTasks.find((x) => x.id === parts[2]) : void 0;
2815
- const t = foundByParts2 ?? selectedTask;
2816
- const agentArg = foundByParts2 ? parts[3] : parts[2];
2817
- if (!t) {
2818
- addMessage("No task selected or id given", tuiColors.yellow);
2819
- return;
2820
- }
2821
- if (!agentArg) {
2822
- addMessage("Usage: /task assign [id] <agent>", tuiColors.yellow);
2823
- return;
2824
- }
2825
- if (!onAssignTask) return;
2826
- addMessage(`Assigning "${t.title}" to ${agentArg}...`, tuiColors.amber);
2827
- onAssignTask(t.id, agentArg).then(
2828
- () => {
2829
- addMessage(`\u2713 Assigned "${t.title}" to ${agentArg}`, tuiColors.green);
2830
- refreshAll();
2831
- },
2832
- (err) => addMessage(`Failed: ${errMsg(err)}`, tuiColors.red)
2833
- );
2834
- } else if (sub === "approve") {
2835
- const t = parts[2] ? sortedTasks.find((x) => x.id === parts[2]) : selectedTask;
2836
- if (!t) {
2837
- addMessage("No task selected or id given", tuiColors.yellow);
2838
- return;
2839
- }
2840
- if (t.status !== "review") {
2841
- addMessage(`Cannot approve \u2014 status is ${t.status}`, tuiColors.yellow);
2842
- return;
2843
- }
2844
- if (!onApproveTask) return;
2845
- addMessage(`Approving "${t.title}"...`, tuiColors.amber);
2846
- onApproveTask(t.id).then(
2847
- () => {
2848
- addMessage(`\u2713 Approved "${t.title}"`, tuiColors.green);
2849
- refreshAll();
2850
- },
2851
- (err) => addMessage(`Failed: ${errMsg(err)}`, tuiColors.red)
2852
- );
2853
- } else if (sub === "reject") {
2854
- const t = parts[2] ? sortedTasks.find((x) => x.id === parts[2]) : selectedTask;
2855
- if (!t) {
2856
- addMessage("No task selected or id given", tuiColors.yellow);
2857
- return;
2858
- }
2859
- if (t.status !== "review") {
2860
- addMessage(`Cannot reject \u2014 status is ${t.status}`, tuiColors.yellow);
2861
- return;
2862
- }
2863
- if (!onRejectTask) return;
2864
- const feedback = parts.slice(parts[2] && sortedTasks.find((x) => x.id === parts[2]) ? 3 : 2).join(" ").trim() || void 0;
2865
- addMessage(`Rejecting "${t.title}"${feedback ? " with feedback" : ""}...`, tuiColors.amber);
2866
- onRejectTask(t.id, feedback).then(
2867
- () => {
2868
- addMessage(`\u2713 Rejected "${t.title}" \u2192 todo`, tuiColors.green);
2869
- refreshAll();
2870
- },
2871
- (err) => addMessage(`Failed: ${errMsg(err)}`, tuiColors.red)
2872
- );
2873
- } else if (sub === "delete") {
2874
- const t = parts[2] ? sortedTasks.find((x) => x.id === parts[2]) : selectedTask;
2875
- if (!t) {
2876
- addMessage("No task selected or id given", tuiColors.yellow);
2877
- return;
2878
- }
2879
- if (t.status === "in_progress") {
2880
- addMessage(`Cannot delete \u2014 task is running`, tuiColors.yellow);
2881
- return;
2882
- }
2883
- if (!onDeleteTask) return;
2884
- addMessage(`Deleting "${t.title}"...`, tuiColors.amber);
2885
- onDeleteTask(t.id).then(
2886
- () => {
2887
- addMessage(`\u2713 Deleted "${t.title}"`, tuiColors.green);
2888
- refreshAll();
2889
- },
2890
- (err) => addMessage(`Failed: ${errMsg(err)}`, tuiColors.red)
2891
- );
2892
- } else {
2893
- addMessage("Usage: /task add|list|show|cancel|retry|assign|approve|reject|delete", tuiColors.yellow);
2894
- }
2895
- return;
2896
- }
2897
- // ── /agent group ──
2898
- case "agent": {
2899
- const sub = parts[1]?.toLowerCase();
2900
- if (sub === "add") {
2901
- const name = parts[2];
2902
- if (!name) {
2903
- launchAgentWizard();
2904
- return;
2905
- }
2906
- if (!onAddAgent) {
2907
- addMessage("Agent creation not available", tuiColors.yellow);
2908
- return;
2909
- }
2910
- const adapter = parts[3];
2911
- addMessage(`Creating agent "${name}"...`, tuiColors.amber);
2912
- onAddAgent(name, adapter).then(
2913
- (agent) => {
2914
- addMessage(`\u2713 Created agent "${agent.name}" (${agent.id}, ${agent.adapter})`, tuiColors.green);
2915
- refreshAll();
2916
- },
2917
- (err) => addMessage(`Failed: ${errMsg(err)}`, tuiColors.red)
2918
- );
2919
- } else if (sub === "list") {
2920
- const lines = sortedAgents.map((a) => ` ${a.id} ${a.status.padEnd(8)} ${a.name} (${a.adapter})`);
2921
- if (lines.length === 0) addMessage("No agents", tuiColors.dim);
2922
- else for (const line of lines) addMessage(line, tuiColors.cyan);
2923
- } else if (sub === "disable") {
2924
- const a = parts[2] ? sortedAgents.find((x) => x.id === parts[2] || x.name === parts[2]) : selectedAgent;
2925
- if (!a) {
2926
- addMessage("No agent selected or id given", tuiColors.yellow);
2927
- return;
2928
- }
2929
- if (!onDisableAgent) return;
2930
- addMessage(`Disabling ${a.name}...`, tuiColors.amber);
2931
- onDisableAgent(a.id).then(
2932
- () => {
2933
- addMessage(`\u2713 Disabled ${a.name}`, tuiColors.green);
2934
- refreshAll();
2935
- },
2936
- (err) => addMessage(`Failed: ${errMsg(err)}`, tuiColors.red)
2937
- );
2938
- } else if (sub === "enable") {
2939
- const a = parts[2] ? sortedAgents.find((x) => x.id === parts[2] || x.name === parts[2]) : selectedAgent;
2940
- if (!a) {
2941
- addMessage("No agent selected or id given", tuiColors.yellow);
2942
- return;
2943
- }
2944
- if (!onEnableAgent) return;
2945
- addMessage(`Enabling ${a.name}...`, tuiColors.amber);
2946
- onEnableAgent(a.id).then(
2947
- () => {
2948
- addMessage(`\u2713 Enabled ${a.name}`, tuiColors.green);
2949
- refreshAll();
2950
- },
2951
- (err) => addMessage(`Failed: ${errMsg(err)}`, tuiColors.red)
2952
- );
2953
- } else if (sub === "delete" || sub === "remove") {
2954
- const a = parts[2] ? sortedAgents.find((x) => x.id === parts[2] || x.name === parts[2]) : selectedAgent;
2955
- if (!a) {
2956
- addMessage("No agent selected or id given", tuiColors.yellow);
2957
- return;
2958
- }
2959
- if (a.status === "running") {
2960
- addMessage("Cannot delete \u2014 agent is running", tuiColors.yellow);
2961
- return;
2962
- }
2963
- if (!onDeleteAgent) {
2964
- addMessage("Agent deletion not available", tuiColors.yellow);
2965
- return;
2966
- }
2967
- addMessage(`Deleting agent "${a.name}"...`, tuiColors.amber);
2968
- onDeleteAgent(a.id).then(
2969
- () => {
2970
- addMessage(`\u2713 Deleted agent "${a.name}"`, tuiColors.green);
2971
- refreshAll();
2972
- },
2973
- (err) => addMessage(`Failed: ${errMsg(err)}`, tuiColors.red)
2974
- );
2975
- } else if (sub === "autonomous" || sub === "auto") {
2976
- const a = parts[2] ? sortedAgents.find((x) => x.id === parts[2] || x.name === parts[2]) : selectedAgent;
2977
- if (!a) {
2978
- addMessage("No agent selected or id given", tuiColors.yellow);
2979
- return;
2980
- }
2981
- if (!onToggleAutonomous) {
2982
- addMessage("Autonomous toggle not available", tuiColors.yellow);
2983
- return;
2984
- }
2985
- if (a.autonomous) {
2986
- addMessage(`Disabling autonomous mode for "${a.name}"...`, tuiColors.amber);
2987
- onToggleAutonomous(a.id, false).then(
2988
- () => {
2989
- addMessage(`${LOOP} ${a.name} autonomous OFF`, tuiColors.cyan);
2990
- refreshAll();
2991
- },
2992
- (err) => addMessage(`Failed: ${errMsg(err)}`, tuiColors.red)
2993
- );
2994
- } else {
2995
- addMessage(`Enabling autonomous mode for "${a.name}"...`, tuiColors.amber);
2996
- onToggleAutonomous(a.id, true).then(
2997
- () => {
2998
- addMessage(`${LOOP} ${a.name} autonomous ON`, tuiColors.cyan);
2999
- refreshAll();
3000
- },
3001
- (err) => addMessage(`Failed: ${errMsg(err)}`, tuiColors.red)
3002
- );
3003
- }
3004
- } else {
3005
- addMessage("Usage: /agent add|list|disable|enable|delete|autonomous", tuiColors.yellow);
3006
- }
3007
- return;
3008
- }
3009
- // ── /team group ──
3010
- case "team": {
3011
- const sub = parts[1]?.toLowerCase();
3012
- if (sub === "create" || sub === "add") {
3013
- launchTeamWizard();
3014
- } else if (sub === "list") {
3015
- const teams = liveTeamsRef.current;
3016
- if (teams.length === 0) {
3017
- addMessage("No teams", tuiColors.dim);
3018
- } else for (const t of teams) {
3019
- addMessage(` ${t.id} ${t.status.padEnd(8)} ${t.name} (${t.members.length} members)`, tuiColors.cyan);
3020
- }
3021
- } else if (sub === "join") {
3022
- if (!onJoinTeam) {
3023
- addMessage("Join not available", tuiColors.yellow);
3024
- return;
3025
- }
3026
- const teamArg = parts[2];
3027
- const agentArg = parts[3] ?? selectedAgent?.id;
3028
- if (!teamArg || !agentArg) {
3029
- addMessage("Usage: /team join <teamId> [agentId]", tuiColors.yellow);
3030
- return;
3031
- }
3032
- addMessage(`Joining team ${teamArg}...`, tuiColors.amber);
3033
- onJoinTeam(teamArg, agentArg).then(
3034
- (t) => {
3035
- addMessage(`\u2713 Agent joined team "${t.name}"`, tuiColors.green);
3036
- refreshAll({ includeTeams: true });
3037
- },
3038
- (err) => addMessage(`Failed: ${errMsg(err)}`, tuiColors.red)
3039
- );
3040
- } else if (sub === "leave") {
3041
- if (!onLeaveTeam) {
3042
- addMessage("Leave not available", tuiColors.yellow);
3043
- return;
3044
- }
3045
- const teamArg = parts[2];
3046
- const agentArg = parts[3] ?? selectedAgent?.id;
3047
- if (!teamArg || !agentArg) {
3048
- addMessage("Usage: /team leave <teamId> [agentId]", tuiColors.yellow);
3049
- return;
3050
- }
3051
- addMessage(`Leaving team ${teamArg}...`, tuiColors.amber);
3052
- onLeaveTeam(teamArg, agentArg).then(
3053
- (t) => {
3054
- addMessage(`\u2713 Agent left team "${t.name}"`, tuiColors.green);
3055
- refreshAll({ includeTeams: true });
3056
- },
3057
- (err) => addMessage(`Failed: ${errMsg(err)}`, tuiColors.red)
3058
- );
3059
- } else if (sub === "disband") {
3060
- if (!onDisbandTeam) {
3061
- addMessage("Disband not available", tuiColors.yellow);
3062
- return;
3063
- }
3064
- const teamArg = parts[2];
3065
- if (!teamArg) {
3066
- addMessage("Usage: /team disband <teamId>", tuiColors.yellow);
3067
- return;
3068
- }
3069
- addMessage(`Disbanding team ${teamArg}...`, tuiColors.amber);
3070
- onDisbandTeam(teamArg).then(
3071
- () => {
3072
- addMessage(`\u2713 Team disbanded`, tuiColors.green);
3073
- refreshAll({ includeTeams: true });
3074
- },
3075
- (err) => addMessage(`Failed: ${errMsg(err)}`, tuiColors.red)
3076
- );
3077
- } else if (sub === "set-lead") {
3078
- if (!onSetTeamLead) {
3079
- addMessage("Set-lead not available", tuiColors.yellow);
3080
- return;
3081
- }
3082
- const teamArg = parts[2];
3083
- const agentArg = parts[3];
3084
- if (!teamArg || !agentArg) {
3085
- addMessage("Usage: /team set-lead <teamId> <agentId>", tuiColors.yellow);
3086
- return;
3087
- }
3088
- addMessage(`Setting lead for team ${teamArg}...`, tuiColors.amber);
3089
- onSetTeamLead(teamArg, agentArg).then(
3090
- (t) => {
3091
- addMessage(`\u2713 New lead for team "${t.name}"`, tuiColors.green);
3092
- refreshAll({ includeTeams: true });
3093
- },
3094
- (err) => addMessage(`Failed: ${errMsg(err)}`, tuiColors.red)
3095
- );
3096
- } else {
3097
- addMessage("Usage: /team create|list|join|leave|disband|set-lead", tuiColors.yellow);
3098
- }
3099
- return;
3100
- }
3101
- // ── /run, /run-all ──
3102
- case "run": {
3103
- const idArg = parts[1] ?? selectedTask?.id;
3104
- if (!idArg) {
3105
- addMessage("No task selected or id given", tuiColors.yellow);
3106
- return;
3107
- }
3108
- if (!onRunTask) {
3109
- addMessage("Run not available", tuiColors.yellow);
3110
- return;
3111
- }
3112
- const t = sortedTasks.find((x) => x.id === idArg);
3113
- if (t && !RUNNABLE.has(t.status)) {
3114
- addMessage(`Cannot run \u2014 status is ${t.status}`, tuiColors.yellow);
3115
- return;
3116
- }
3117
- addMessage(`Running ${idArg}...`, tuiColors.amber);
3118
- onRunTask(idArg).then(
3119
- () => {
3120
- addMessage(`\u2713 Dispatched ${idArg}`, tuiColors.green);
3121
- refreshAll();
3122
- },
3123
- (err) => addMessage(`Failed: ${errMsg(err)}`, tuiColors.red)
3124
- );
3125
- return;
3126
- }
3127
- case "run-all": {
3128
- if (!onRunAll) {
3129
- addMessage("Run-all not available", tuiColors.yellow);
3130
- return;
3131
- }
3132
- addMessage("Running all todo tasks...", tuiColors.amber);
3133
- onRunAll().then(
3134
- () => {
3135
- addMessage("\u2713 Dispatched all todo tasks", tuiColors.green);
3136
- refreshAll();
3137
- },
3138
- (err) => addMessage(`Failed: ${errMsg(err)}`, tuiColors.red)
3139
- );
3140
- return;
3141
- }
3142
- // ── /watch, /pause ──
3143
- case "watch": {
3144
- if (watchActive) {
3145
- addMessage("Watch mode already active", tuiColors.yellow);
3146
- return;
3147
- }
3148
- if (!onStartWatch) {
3149
- addMessage("Watch not available", tuiColors.yellow);
3150
- return;
3151
- }
3152
- addMessage("Starting watch mode...", tuiColors.amber);
3153
- onStartWatch().then(
3154
- () => {
3155
- setWatchActive(true);
3156
- addMessage("\u2713 Watch mode started", tuiColors.green);
3157
- },
3158
- (err) => addMessage(`Failed: ${errMsg(err)}`, tuiColors.red)
3159
- );
3160
- return;
3161
- }
3162
- case "pause": {
3163
- if (!watchActive) {
3164
- addMessage("Watch mode not active", tuiColors.yellow);
3165
- return;
3166
- }
3167
- if (!onStopWatch) {
3168
- addMessage("Pause not available", tuiColors.yellow);
3169
- return;
3170
- }
3171
- addMessage("Pausing watch mode...", tuiColors.amber);
3172
- onStopWatch().then(
3173
- () => {
3174
- setWatchActive(false);
3175
- addMessage("\u2713 Watch mode paused", tuiColors.green);
3176
- },
3177
- (err) => addMessage(`Failed: ${errMsg(err)}`, tuiColors.red)
3178
- );
3179
- return;
3180
- }
3181
- // ── /config ──
3182
- case "config": {
3183
- const sub = parts[1]?.toLowerCase();
3184
- if (sub === "activity-filter") {
3185
- setActivityFilter((prev) => {
3186
- const next = cyclePreset(prev);
3187
- onSaveActivityFilter?.(next.label);
3188
- addMessage(`Activity filter: ${next.label}`, tuiColors.amber);
3189
- return new Set(next.types);
3190
- });
3191
- } else {
3192
- launchConfigWizard();
3193
- }
3194
- return;
3195
- }
3196
- // ── /status ──
3197
- case "status": {
3198
- const running = liveTasks.filter((t) => t.status === "in_progress").length;
3199
- addMessage(`${mode} ${running} running ${liveTasks.length} tasks ${sortedAgents.length} agents`, tuiColors.cyan);
3200
- return;
3201
- }
3202
- // ── /help ──
3203
- case "help": {
3204
- for (const [verb, spec] of Object.entries(COMMAND_REGISTRY)) {
3205
- const subs = spec.sub ? " " + spec.sub.join("|") : spec.args ? " " + spec.args : "";
3206
- addMessage(` /${verb}${subs} \u2014 ${spec.help}`, tuiColors.silver);
3207
- }
3208
- return;
3209
- }
3210
- // ── /quit ──
3211
- case "quit": {
3212
- exit();
3213
- return;
3214
- }
3215
- // ── /disable, /enable (agent shortcuts) ──
3216
- case "disable": {
3217
- if (!selectedAgent) {
3218
- addMessage("No agent selected", tuiColors.yellow);
3219
- return;
3220
- }
3221
- if (!onDisableAgent) return;
3222
- addMessage(`Disabling ${selectedAgent.name}...`, tuiColors.amber);
3223
- onDisableAgent(selectedAgent.id).then(
3224
- () => {
3225
- addMessage(`\u2713 Disabled ${selectedAgent.name}`, tuiColors.green);
3226
- refreshAll();
3227
- },
3228
- (err) => addMessage(`Failed: ${errMsg(err)}`, tuiColors.red)
3229
- );
3230
- return;
3231
- }
3232
- case "enable": {
3233
- if (!selectedAgent) {
3234
- addMessage("No agent selected", tuiColors.yellow);
3235
- return;
3236
- }
3237
- if (!onEnableAgent) return;
3238
- addMessage(`Enabling ${selectedAgent.name}...`, tuiColors.amber);
3239
- onEnableAgent(selectedAgent.id).then(
3240
- () => {
3241
- addMessage(`\u2713 Enabled ${selectedAgent.name}`, tuiColors.green);
3242
- refreshAll();
3243
- },
3244
- (err) => addMessage(`Failed: ${errMsg(err)}`, tuiColors.red)
3245
- );
3246
- return;
3247
- }
3248
- default:
3249
- addMessage(`Unknown: ${cmd}. Type /help for commands`, tuiColors.yellow);
3250
- }
3251
- }, [
3252
- selectedTask,
3253
- selectedAgent,
3254
- sortedTasks,
3255
- sortedAgents,
3256
- liveTasks,
3257
- mode,
3258
- watchActive,
3259
- onCancelTask,
3260
- onRetryTask,
3261
- onAssignTask,
3262
- onRunAll,
3263
- onRunTask,
3264
- onCreateTask,
3265
- onDisableAgent,
3266
- onEnableAgent,
3267
- onAddAgent,
3268
- onApproveTask,
3269
- onRejectTask,
3270
- onDeleteTask,
3271
- onJoinTeam,
3272
- onLeaveTeam,
3273
- onDisbandTeam,
3274
- onSetTeamLead,
3275
- onStartWatch,
3276
- onStopWatch,
3277
- addMessage,
3278
- exit,
3279
- refreshAll,
3280
- launchTaskWizard,
3281
- launchAgentWizard,
3282
- launchTeamWizard,
3283
- launchConfigWizard
3284
- ]);
3285
- useInput((input, key) => {
3286
- if (inputMode !== "none") {
3287
- if (key.escape) {
3288
- setInputMode("none");
3289
- setInputValue("");
3290
- cmdHistory.reset();
3291
- return;
3292
- }
3293
- if (key.return) {
3294
- const value = inputValue.trim();
3295
- if (!value) return;
3296
- if (inputMode === "new_task") {
3297
- if (!onCreateTask) return;
3298
- setInputMode("none");
3299
- setInputValue("");
3300
- addMessage(`Creating "${value}"...`, tuiColors.amber);
3301
- onCreateTask(value).then(
3302
- (task) => {
3303
- addMessage(`\u2713 Created "${task.title}" (${task.id})`, tuiColors.green);
3304
- refreshAll();
3305
- },
3306
- (err) => addMessage(`Failed to create: ${err instanceof Error ? err.message : String(err)}`, tuiColors.red)
3307
- );
3308
- } else if (inputMode === "command") {
3309
- let cmdToRun = value;
3310
- if (suggestions.length > 0 && suggestions[suggestionIndex]) {
3311
- const sel = suggestions[suggestionIndex];
3312
- const cmdPart = sel.cmd.replace(/\s+\[.*\]$/, "");
3313
- if (cmdPart.startsWith(value) || value === "/") {
3314
- cmdToRun = cmdPart;
3315
- }
3316
- if (sel.subs && !cmdPart.includes(" ")) {
3317
- setInputValue(cmdPart + " ");
3318
- setSuggestionIndex(0);
3319
- return;
3320
- }
3321
- }
3322
- setInputMode("none");
3323
- setInputValue("");
3324
- setSuggestionIndex(0);
3325
- cmdHistory.push(cmdToRun);
3326
- executeCommand(cmdToRun);
3327
- }
3328
- return;
3329
- }
3330
- if (key.tab && inputMode === "command") {
3331
- if (suggestions.length > 0) {
3332
- const sel = suggestions[suggestionIndex];
3333
- if (sel) {
3334
- const cmdPart = sel.cmd.replace(/\s+\[.*\]$/, "");
3335
- setInputValue(cmdPart + (sel.subs ? " " : ""));
3336
- setSuggestionIndex(0);
3337
- }
3338
- } else {
3339
- const suffix = resolveCompletion(inputValue);
3340
- if (suffix) setInputValue((v) => v + suffix);
3341
- }
3342
- return;
3343
- }
3344
- if (key.upArrow && inputMode === "command") {
3345
- if (suggestions.length > 0) {
3346
- setSuggestionIndex((i) => Math.max(0, i - 1));
3347
- } else {
3348
- const prev = cmdHistory.prev();
3349
- if (prev !== null) setInputValue(prev);
3350
- }
3351
- return;
3352
- }
3353
- if (key.downArrow && inputMode === "command") {
3354
- if (suggestions.length > 0) {
3355
- setSuggestionIndex((i) => Math.min(suggestions.length - 1, i + 1));
3356
- } else {
3357
- const next = cmdHistory.next();
3358
- setInputValue(next ?? "");
3359
- }
3360
- return;
3361
- }
3362
- if (key.backspace || key.delete) {
3363
- setInputValue((v) => v.slice(0, -1));
3364
- setSuggestionIndex(0);
3365
- return;
3366
- }
3367
- if (input && !key.ctrl && !key.meta) {
3368
- setInputValue((v) => v + input);
3369
- setSuggestionIndex(0);
3370
- }
3371
- return;
3372
- }
3373
- if (input.toLowerCase() === "q") {
3374
- exit();
3375
- return;
3376
- }
3377
- if (key.escape) {
3378
- if (detailOpen) {
3379
- setDetailOpen(false);
3380
- return;
3381
- }
3382
- if (activeView === "logs" && logSelectedIndex >= 0) {
3383
- setLogSelectedIndex(-1);
3384
- setLogScrollOffset(0);
3385
- return;
3386
- }
3387
- return;
3388
- }
3389
- if (input === "/" && !detailOpen) {
3390
- setInputMode("command");
3391
- setInputValue("/");
3392
- setSuggestionIndex(0);
3393
- return;
3394
- }
3395
- if (activeView === "logs" && !detailOpen && input >= "0" && input <= "9") {
3396
- setLogFilter(parseInt(input, 10));
3397
- return;
3398
- }
3399
- if ((input === "f" || input === "F") && activeView === "logs" && !detailOpen) {
3400
- setLogTypeFilter((prev) => new Set(cyclePreset(prev).types));
3401
- return;
3402
- }
3403
- if ((input === "f" || input === "F") && (activeView === "tasks" || activeView === "agents" || activeView === "goals") && !detailOpen) {
3404
- setActivityFilter((prev) => {
3405
- const next = cyclePreset(prev);
3406
- onSaveActivityFilter?.(next.label);
3407
- return new Set(next.types);
3408
- });
3409
- return;
3410
- }
3411
- if ((input === "n" || input === "N") && activeView === "tasks" && !detailOpen && onCreateTask) {
3412
- launchTaskWizard();
3413
- return;
3414
- }
3415
- if ((input === "n" || input === "N") && activeView === "agents" && !detailOpen && onAddAgent) {
3416
- launchAgentWizard();
3417
- return;
3418
- }
3419
- if ((input === "n" || input === "N") && activeView === "goals" && !detailOpen && onCreateGoal) {
3420
- const steps = getGoalWizardSteps(liveAgents);
3421
- setWizardConfig({ title: "New Goal", steps, kind: "goal" });
3422
- setInputMode("wizard");
3423
- return;
3424
- }
3425
- if ((input === "e" || input === "E") && activeView === "goals" && selectedGoal && onUpdateGoal) {
3426
- const steps = getEditGoalWizardSteps(selectedGoal, liveAgents);
3427
- setWizardConfig({ title: `Edit Goal: ${selectedGoal.title}`, steps, kind: "edit_goal", targetId: selectedGoal.id });
3428
- setInputMode("wizard");
3429
- return;
3430
- }
3431
- if ((input === "d" || input === "D") && activeView === "goals" && selectedGoal && onDeleteGoal) {
3432
- addMessage(`Deleting goal "${selectedGoal.title}"...`, tuiColors.amber);
3433
- onDeleteGoal(selectedGoal.id).then(
3434
- () => {
3435
- addMessage(`\u2713 Deleted goal "${selectedGoal.title}"`, tuiColors.green);
3436
- refreshAll();
3437
- },
3438
- (err) => addMessage(`Failed: ${err instanceof Error ? err.message : String(err)}`, tuiColors.red)
3439
- );
3440
- return;
3441
- }
3442
- if ((input === "c" || input === "C") && activeView === "goals" && selectedGoal && onUpdateGoalStatus) {
3443
- if (selectedGoal.status === "active" || selectedGoal.status === "paused") {
3444
- addMessage(`Marking goal "${selectedGoal.title}" as achieved...`, tuiColors.amber);
3445
- onUpdateGoalStatus(selectedGoal.id, "achieved").then(
3446
- () => {
3447
- addMessage(`\u2713 Goal "${selectedGoal.title}" achieved`, tuiColors.green);
3448
- refreshAll();
3449
- },
3450
- (err) => addMessage(`Failed: ${err instanceof Error ? err.message : String(err)}`, tuiColors.red)
3451
- );
3452
- }
3453
- return;
3454
- }
3455
- if ((input === "x" || input === "X") && activeView === "goals" && selectedGoal && onUpdateGoalStatus) {
3456
- if (selectedGoal.status === "active" || selectedGoal.status === "paused") {
3457
- addMessage(`Abandoning goal "${selectedGoal.title}"...`, tuiColors.amber);
3458
- onUpdateGoalStatus(selectedGoal.id, "abandoned").then(
3459
- () => {
3460
- addMessage(`\u2713 Goal "${selectedGoal.title}" abandoned`, tuiColors.dim);
3461
- refreshAll();
3462
- },
3463
- (err) => addMessage(`Failed: ${err instanceof Error ? err.message : String(err)}`, tuiColors.red)
3464
- );
3465
- }
3466
- return;
3467
- }
3468
- if ((input === "p" || input === "P") && activeView === "goals" && selectedGoal && onUpdateGoalStatus) {
3469
- const newStatus = selectedGoal.status === "paused" ? "active" : "paused";
3470
- if (selectedGoal.status === "active" || selectedGoal.status === "paused") {
3471
- onUpdateGoalStatus(selectedGoal.id, newStatus).then(
3472
- () => {
3473
- addMessage(`Goal "${selectedGoal.title}" ${newStatus}`, tuiColors.cyan);
3474
- refreshAll();
3475
- },
3476
- (err) => addMessage(`Failed: ${err instanceof Error ? err.message : String(err)}`, tuiColors.red)
3477
- );
3478
- }
3479
- return;
3480
- }
3481
- if ((input === "a" || input === "A") && activeView === "tasks" && selectedTask?.status === "review" && onApproveTask) {
3482
- addMessage(`Approving "${selectedTask.title}"...`, tuiColors.amber);
3483
- onApproveTask(selectedTask.id).then(
3484
- () => {
3485
- addMessage(`\u2713 Approved "${selectedTask.title}"`, tuiColors.green);
3486
- refreshAll();
3487
- },
3488
- (err) => addMessage(`Failed: ${err instanceof Error ? err.message : String(err)}`, tuiColors.red)
3489
- );
3490
- return;
3491
- }
3492
- if ((input === "x" || input === "X") && activeView === "tasks" && selectedTask?.status === "review" && onRejectTask) {
3493
- addMessage(`Rejecting "${selectedTask.title}"...`, tuiColors.amber);
3494
- onRejectTask(selectedTask.id).then(
3495
- () => {
3496
- addMessage(`\u2713 Rejected "${selectedTask.title}" \u2192 todo`, tuiColors.green);
3497
- refreshAll();
3498
- },
3499
- (err) => addMessage(`Failed: ${err instanceof Error ? err.message : String(err)}`, tuiColors.red)
3500
- );
3501
- return;
3502
- }
3503
- if ((input === "c" || input === "C") && activeView === "tasks" && selectedTask && onCancelTask) {
3504
- if (selectedTask.status === "done" || selectedTask.status === "failed" || selectedTask.status === "cancelled") {
3505
- addMessage(`Cannot cancel \u2014 status is ${selectedTask.status}`, tuiColors.yellow);
3506
- return;
3507
- }
3508
- addMessage(`Cancelling "${selectedTask.title}"...`, tuiColors.amber);
3509
- onCancelTask(selectedTask.id).then(
3510
- () => {
3511
- addMessage(`\u2713 Cancelled "${selectedTask.title}"`, tuiColors.green);
3512
- refreshAll();
3513
- },
3514
- (err) => addMessage(`Failed: ${err instanceof Error ? err.message : String(err)}`, tuiColors.red)
3515
- );
3516
- return;
3517
- }
3518
- if ((input === "e" || input === "E") && activeView === "tasks" && selectedTask && onUpdateTask) {
3519
- launchEditTaskWizard(selectedTask);
3520
- return;
3521
- }
3522
- if ((input === "e" || input === "E") && activeView === "agents" && selectedAgent && onUpdateAgent) {
3523
- launchEditAgentWizard(selectedAgent);
3524
- return;
3525
- }
3526
- if ((input === "s" || input === "S") && activeView === "tasks") {
3527
- setShowAllTasks((v) => !v);
3528
- setTaskSelectedIndex(0);
3529
- setTaskScrollOffset(0);
3530
- return;
3531
- }
3532
- if ((input === "s" || input === "S") && activeView === "agents" && selectedAgent && onForceStopAgent) {
3533
- const isActuallyRunning = Object.values(liveState.running).some((e) => e.agent_id === selectedAgent.id);
3534
- if (!isActuallyRunning && selectedAgent.status !== "running") {
3535
- addMessage(`Agent "${selectedAgent.name}" is not running`, tuiColors.yellow);
3536
- return;
3537
- }
3538
- addMessage(`Force-stopping agent "${selectedAgent.name}"...`, tuiColors.amber);
3539
- onForceStopAgent(selectedAgent.id).then(
3540
- () => {
3541
- addMessage(`\u2713 Stopped agent "${selectedAgent.name}"`, tuiColors.green);
3542
- refreshAll();
3543
- },
3544
- (err) => addMessage(`Failed: ${err instanceof Error ? err.message : String(err)}`, tuiColors.red)
3545
- );
3546
- return;
3547
- }
3548
- if ((input === "d" || input === "D") && activeView === "tasks" && selectedTask && selectedTask.status !== "in_progress" && onDeleteTask) {
3549
- addMessage(`Deleting "${selectedTask.title}"...`, tuiColors.amber);
3550
- onDeleteTask(selectedTask.id).then(
3551
- () => {
3552
- addMessage(`\u2713 Deleted "${selectedTask.title}"`, tuiColors.green);
3553
- refreshAll();
3554
- },
3555
- (err) => addMessage(`Failed: ${err instanceof Error ? err.message : String(err)}`, tuiColors.red)
3556
- );
3557
- return;
3558
- }
3559
- if ((input === "d" || input === "D") && activeView === "agents" && selectedAgent && onDeleteAgent) {
3560
- const isActuallyRunning = Object.values(liveState.running).some((e) => e.agent_id === selectedAgent.id);
3561
- if (isActuallyRunning) {
3562
- if (onForceStopAgent) {
3563
- addMessage(`Stopping & deleting agent "${selectedAgent.name}"...`, tuiColors.amber);
3564
- onForceStopAgent(selectedAgent.id).then(
3565
- () => onDeleteAgent(selectedAgent.id)
3566
- ).then(
3567
- () => {
3568
- addMessage(`\u2713 Deleted agent "${selectedAgent.name}"`, tuiColors.green);
3569
- refreshAll();
3570
- },
3571
- (err) => addMessage(`Failed: ${err instanceof Error ? err.message : String(err)}`, tuiColors.red)
3572
- );
3573
- } else {
3574
- addMessage(`Cannot delete \u2014 agent "${selectedAgent.name}" is running. Press S to stop first.`, tuiColors.yellow);
3575
- }
3576
- return;
3577
- }
3578
- addMessage(`Deleting agent "${selectedAgent.name}"...`, tuiColors.amber);
3579
- onDeleteAgent(selectedAgent.id).then(
3580
- () => {
3581
- addMessage(`\u2713 Deleted agent "${selectedAgent.name}"`, tuiColors.green);
3582
- refreshAll();
3583
- },
3584
- (err) => addMessage(`Failed: ${err instanceof Error ? err.message : String(err)}`, tuiColors.red)
3585
- );
3586
- return;
3587
- }
3588
- if ((input === "u" || input === "U") && activeView === "agents" && selectedAgent && onToggleAutonomous) {
3589
- const newAuto = !selectedAgent.autonomous;
3590
- addMessage(`${newAuto ? "Enabling" : "Disabling"} autonomous mode for "${selectedAgent.name}"...`, tuiColors.amber);
3591
- onToggleAutonomous(selectedAgent.id, newAuto).then(
3592
- () => {
3593
- addMessage(`${LOOP} ${selectedAgent.name} autonomous ${newAuto ? "ON" : "OFF"}`, tuiColors.cyan);
3594
- refreshAll();
3595
- },
3596
- (err) => addMessage(`Failed: ${err instanceof Error ? err.message : String(err)}`, tuiColors.red)
3597
- );
3598
- return;
3599
- }
3600
- if (!detailOpen) {
3601
- if (input === "g" || input === "G") {
3602
- setActiveView("goals");
3603
- return;
3604
- }
3605
- if (input === "t" || input === "T") {
3606
- setActiveView("tasks");
3607
- return;
3608
- }
3609
- if (input === "a" || input === "A") {
3610
- setActiveView("agents");
3611
- return;
3612
- }
3613
- if (input === "l" || input === "L") {
3614
- setActiveView("logs");
3615
- return;
3616
- }
3617
- }
3618
- if (!detailOpen) {
3619
- const viewOrder = TABS.map((t) => t.id);
3620
- const idx = viewOrder.indexOf(activeView);
3621
- if (key.tab || key.rightArrow) {
3622
- setActiveView(viewOrder[(idx + 1) % viewOrder.length]);
3623
- return;
3624
- }
3625
- if (key.leftArrow) {
3626
- setActiveView(viewOrder[(idx + viewOrder.length - 1) % viewOrder.length]);
3627
- return;
3628
- }
3629
- }
3630
- if (key.return) {
3631
- const showAllRowIdx = hiddenTaskCount > 0 ? visibleTasks.length : -1;
3632
- if (activeView === "tasks" && taskSelectedIndex === showAllRowIdx) {
3633
- setShowAllTasks((v) => !v);
3634
- setTaskSelectedIndex(0);
3635
- setTaskScrollOffset(0);
3636
- return;
3637
- }
3638
- const addRowIdx = visibleTasks.length + (hiddenTaskCount > 0 ? 1 : 0);
3639
- if (activeView === "tasks" && taskSelectedIndex === addRowIdx && onCreateTask) {
3640
- launchTaskWizard();
3641
- return;
3642
- }
3643
- if (activeView === "goals" && goalSelectedIndex === sortedGoals.length && onCreateGoal) {
3644
- const steps = getGoalWizardSteps(liveAgents);
3645
- setWizardConfig({ title: "New Goal", steps, kind: "goal" });
3646
- setInputMode("wizard");
3647
- return;
3648
- }
3649
- if (activeView === "agents" && agentSelectedIndex === sortedAgents.length && onAddAgent) {
3650
- launchAgentWizard();
3651
- return;
3652
- }
3653
- if (activeView === "goals" && selectedGoal) {
3654
- setDetailOpen((prev) => !prev);
3655
- return;
3656
- }
3657
- if (activeView === "tasks" && selectedTask) {
3658
- setDetailOpen((prev) => !prev);
3659
- return;
3660
- }
3661
- if (activeView === "agents" && selectedAgent) {
3662
- setDetailOpen((prev) => !prev);
3663
- return;
3664
- }
3665
- if (activeView === "logs" && logSelectedIndex >= 0) {
3666
- setDetailOpen((prev) => !prev);
3667
- return;
3668
- }
3669
- }
3670
- if ((input === "r" || input === "R") && activeView === "tasks" && selectedTask && onRunTask) {
3671
- if (!RUNNABLE.has(selectedTask.status)) {
3672
- addMessage(`Cannot run "${selectedTask.title}" \u2014 status is ${selectedTask.status}`, tuiColors.yellow);
3673
- return;
3674
- }
3675
- addMessage(`Running "${selectedTask.title}"...`, tuiColors.green);
3676
- onRunTask(selectedTask.id).then(
3677
- () => {
3678
- addMessage(`Dispatched "${selectedTask.title}"`, tuiColors.green);
3679
- refreshAll();
3680
- },
3681
- (err) => addMessage(`Failed to run: ${err instanceof Error ? err.message : String(err)}`, tuiColors.red)
3682
- );
3683
- return;
3684
- }
3685
- if (key.upArrow || input === "k") {
3686
- if (activeView === "goals") {
3687
- setGoalSelectedIndex((i) => {
3688
- const next = Math.max(0, i - 1);
3689
- setGoalScrollOffset((o) => next < o ? next : o);
3690
- return next;
3691
- });
3692
- } else if (activeView === "tasks") {
3693
- setTaskSelectedIndex((i) => {
3694
- const next = Math.max(0, i - 1);
3695
- setTaskScrollOffset((o) => next < o ? next : o);
3696
- return next;
3697
- });
3698
- } else if (activeView === "agents") {
3699
- setAgentSelectedIndex((i) => {
3700
- const next = Math.max(0, i - 1);
3701
- setAgentScrollOffset((o) => next < o ? next : o);
3702
- return next;
3703
- });
3704
- } else if (activeView === "logs") {
3705
- setLogSelectedIndex((i) => {
3706
- if (i === -1) {
3707
- const last = messages.length - 1;
3708
- setLogScrollOffset(Math.max(0, last - mainH + 2));
3709
- return Math.max(0, last);
3710
- }
3711
- const next = Math.max(0, i - 1);
3712
- setLogScrollOffset((o) => next < o ? next : o);
3713
- return next;
3714
- });
3715
- }
3716
- }
3717
- if (key.downArrow || input === "j") {
3718
- if (activeView === "goals") {
3719
- const maxIdx = sortedGoals.length + (onCreateGoal ? 1 : 0) - 1;
3720
- setGoalSelectedIndex((i) => {
3721
- const next = Math.min(Math.max(0, maxIdx), i + 1);
3722
- setGoalScrollOffset((o) => next >= o + mainH ? next - mainH + 1 : o);
3723
- return next;
3724
- });
3725
- } else if (activeView === "tasks") {
3726
- const maxIdx = visibleTasks.length + (onCreateTask ? 1 : 0) + (hiddenTaskCount > 0 ? 1 : 0) - 1;
3727
- setTaskSelectedIndex((i) => {
3728
- const next = Math.min(Math.max(0, maxIdx), i + 1);
3729
- setTaskScrollOffset((o) => next >= o + mainH ? next - mainH + 1 : o);
3730
- return next;
3731
- });
3732
- } else if (activeView === "agents") {
3733
- const maxIdx = sortedAgents.length + (onAddAgent ? 1 : 0) - 1;
3734
- setAgentSelectedIndex((i) => {
3735
- const next = Math.min(Math.max(0, maxIdx), i + 1);
3736
- setAgentScrollOffset((o) => next >= o + mainH ? next - mainH + 1 : o);
3737
- return next;
3738
- });
3739
- } else if (activeView === "logs") {
3740
- setLogSelectedIndex((i) => {
3741
- if (i === -1) return -1;
3742
- const maxIdx = messages.length - 1;
3743
- if (i >= maxIdx) {
3744
- setLogScrollOffset(0);
3745
- return -1;
3746
- }
3747
- const next = i + 1;
3748
- setLogScrollOffset((o) => next >= o + mainH - 1 ? next - mainH + 2 : o);
3749
- return next;
3750
- });
3751
- }
3752
- }
3753
- });
3754
- const inInput = inputMode !== "none";
3755
- const selectedLog = logSelectedIndex >= 0 ? messages[logSelectedIndex] : void 0;
3756
- const showTaskDetail = !inInput && detailOpen && activeView === "tasks" && selectedTask;
3757
- const showAgentDetail = !inInput && detailOpen && activeView === "agents" && selectedAgent;
3758
- const showGoalDetail = !inInput && detailOpen && activeView === "goals" && selectedGoal;
3759
- const showLogDetail = !inInput && detailOpen && activeView === "logs" && selectedLog;
3760
- const canRun = !inInput && activeView === "tasks" && selectedTask && RUNNABLE.has(selectedTask.status) && !!onRunTask;
3761
- const canNew = !inInput && !detailOpen && (activeView === "goals" && !!onCreateGoal || activeView === "tasks" && !!onCreateTask || activeView === "agents" && !!onAddAgent);
3762
- const canApprove = !inInput && activeView === "tasks" && selectedTask?.status === "review" && !!onApproveTask;
3763
- const canReject = !inInput && activeView === "tasks" && selectedTask?.status === "review" && !!onRejectTask;
3764
- const agentActuallyRunning = selectedAgent ? Object.values(liveState.running).some((e) => e.agent_id === selectedAgent.id) : false;
3765
- const canDelete = !inInput && (activeView === "goals" && selectedGoal && !!onDeleteGoal || activeView === "tasks" && selectedTask && selectedTask.status !== "in_progress" && !!onDeleteTask || activeView === "agents" && selectedAgent && !!onDeleteAgent);
3766
- const canEdit = !inInput && !detailOpen && (activeView === "goals" && !!selectedGoal && !!onUpdateGoal || activeView === "tasks" && !!selectedTask && !!onUpdateTask || activeView === "agents" && !!selectedAgent && !!onUpdateAgent);
3767
- const canForceStop = !inInput && activeView === "agents" && selectedAgent && (agentActuallyRunning || selectedAgent.status === "running") && !!onForceStopAgent;
3768
- const canToggleAuto = !inInput && activeView === "agents" && !!selectedAgent && !!onToggleAutonomous;
3769
- const canPause = !inInput && activeView === "goals" && !!selectedGoal && (selectedGoal.status === "active" || selectedGoal.status === "paused") && !!onUpdateGoalStatus;
3770
- const showSuggestions = inputMode === "command" && suggestions.length > 0;
3771
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", width: W, height: H, children: [
3772
- /* @__PURE__ */ jsx(
3773
- Header,
3774
- {
3775
- projectName,
3776
- activeView,
3777
- mode,
3778
- stats: headerStats,
3779
- tokens: headerTokens,
3780
- uptime,
3781
- width: W
3782
- }
3783
- ),
3784
- /* @__PURE__ */ jsx(Box, { height: 1 }),
3785
- activeView === "goals" && /* @__PURE__ */ jsx(
3786
- GoalsContent,
3787
- {
3788
- goals: sortedGoals,
3789
- selectedIndex: goalSelectedIndex,
3790
- scrollOffset: goalScrollOffset,
3791
- height: mainH,
3792
- width: ruleW,
3793
- showAddRow: !!onCreateGoal,
3794
- agentNameMap
3795
- }
3796
- ),
3797
- activeView === "tasks" && /* @__PURE__ */ jsx(
3798
- TasksContent,
3799
- {
3800
- tasks: visibleTasks,
3801
- selectedIndex: taskSelectedIndex,
3802
- scrollOffset: taskScrollOffset,
3803
- height: mainH,
3804
- width: ruleW,
3805
- showAddRow: !!onCreateTask,
3806
- agentNameMap,
3807
- hiddenCount: hiddenTaskCount
3808
- }
3809
- ),
3810
- activeView === "agents" && /* @__PURE__ */ jsx(
3811
- AgentsContent,
3812
- {
3813
- agents: sortedAgents,
3814
- selectedIndex: agentSelectedIndex,
3815
- scrollOffset: agentScrollOffset,
3816
- height: mainH,
3817
- width: ruleW,
3818
- state: liveState,
3819
- taskTitleMap,
3820
- showAddRow: !!onAddAgent,
3821
- agentTeamMap,
3822
- teamLeadSet,
3823
- activeTeamCount
3824
- }
3825
- ),
3826
- activeView === "logs" && /* @__PURE__ */ jsx(
3827
- LogsContent,
3828
- {
3829
- messages,
3830
- height: mainH,
3831
- agents: sortedAgents,
3832
- logFilter,
3833
- logTypeFilter,
3834
- selectedIndex: logSelectedIndex,
3835
- scrollOffset: logScrollOffset,
3836
- agentNameMap,
3837
- taskTitleMap,
3838
- width: ruleW
3839
- }
3840
- ),
3841
- /* @__PURE__ */ jsx(Box, { height: 1 }),
3842
- inputMode === "wizard" && wizardConfig ? /* @__PURE__ */ jsx(
3843
- FormWizard,
3844
- {
3845
- title: wizardConfig.title,
3846
- steps: wizardConfig.steps,
3847
- onComplete: handleWizardComplete,
3848
- onCancel: handleWizardCancel,
3849
- width: ruleW,
3850
- height: feedH
3851
- }
3852
- ) : showSuggestions ? /* @__PURE__ */ jsxs(Fragment, { children: [
3853
- /* @__PURE__ */ jsx(SectionLabel, { label: "COMMANDS", width: ruleW }),
3854
- /* @__PURE__ */ jsx(
3855
- SuggestionsPanel,
3856
- {
3857
- suggestions,
3858
- selectedIndex: suggestionIndex,
3859
- height: Math.min(suggestions.length, feedH),
3860
- width: ruleW
3861
- }
3862
- )
3863
- ] }) : inputMode === "new_task" ? /* @__PURE__ */ jsxs(Fragment, { children: [
3864
- /* @__PURE__ */ jsx(InputSectionLabel, { mode: inputMode, width: ruleW }),
3865
- /* @__PURE__ */ jsx(InputPanel, { mode: inputMode, value: inputValue, width: ruleW })
3866
- ] }) : showTaskDetail ? /* @__PURE__ */ jsxs(Fragment, { children: [
3867
- /* @__PURE__ */ jsx(DetailSectionLabel, { task: selectedTask, width: ruleW }),
3868
- /* @__PURE__ */ jsx(
3869
- DetailPanel,
3870
- {
3871
- task: selectedTask,
3872
- height: feedH,
3873
- width: ruleW,
3874
- taskLogs: messages.filter((m) => m.taskId === selectedTask.id),
3875
- agentNameMap
3876
- }
3877
- )
3878
- ] }) : showGoalDetail ? /* @__PURE__ */ jsxs(Fragment, { children: [
3879
- /* @__PURE__ */ jsx(SectionLabel, { label: `GOAL: ${selectedGoal.title}`, width: ruleW }),
3880
- /* @__PURE__ */ jsx(GoalDetailPanel, { goal: selectedGoal, height: feedH, width: ruleW, agentNameMap })
3881
- ] }) : showAgentDetail ? /* @__PURE__ */ jsxs(Fragment, { children: [
3882
- /* @__PURE__ */ jsx(AgentDetailSectionLabel, { agent: selectedAgent, width: ruleW }),
3883
- /* @__PURE__ */ jsx(AgentDetailPanel, { agent: selectedAgent, height: feedH, state: liveState, taskTitleMap, teamName: agentTeamMap.get(selectedAgent.id) })
3884
- ] }) : showLogDetail ? /* @__PURE__ */ jsxs(Fragment, { children: [
3885
- /* @__PURE__ */ jsx(SectionLabel, { label: "LOG", width: ruleW }),
3886
- /* @__PURE__ */ jsx(LogDetailPanel, { message: selectedLog, height: feedH, width: ruleW, agents: sortedAgents, agentNameMap, taskTitleMap })
3887
- ] }) : messages.length > 0 && activeView !== "logs" ? /* @__PURE__ */ jsxs(Fragment, { children: [
3888
- (() => {
3889
- const suffixText = ` F:${activityFilterLabel.toUpperCase()} \u2502 ${activityFilteredMessages.length}/${messages.length}`;
3890
- return /* @__PURE__ */ jsx(SectionLabel, { label: "ACTIVITY", width: ruleW, suffixLen: suffixText.length, suffix: /* @__PURE__ */ jsxs(Fragment, { children: [
3891
- /* @__PURE__ */ jsx(Text, { color: tuiColors.dim, children: " F:" }),
3892
- /* @__PURE__ */ jsx(Text, { color: tuiColors.amber, children: activityFilterLabel.toUpperCase() }),
3893
- /* @__PURE__ */ jsxs(Text, { color: tuiColors.ghost, children: [
3894
- " ",
3895
- "\u2502",
3896
- " ",
3897
- activityFilteredMessages.length,
3898
- "/",
3899
- messages.length
3900
- ] })
3901
- ] }) });
3902
- })(),
3903
- /* @__PURE__ */ jsx(
3904
- ActivityFeed,
3905
- {
3906
- messages: activityFilteredMessages,
3907
- height: Math.max(1, feedH - 1),
3908
- width: ruleW,
3909
- agents: sortedAgents,
3910
- agentNameMap
3911
- }
3912
- )
3913
- ] }) : activeView === "goals" ? /* @__PURE__ */ jsx(OnboardingBox, { count: sortedGoals.length, config: ONBOARDING_GOALS, width: ruleW }) : activeView === "tasks" ? /* @__PURE__ */ jsx(OnboardingBox, { count: sortedTasks.length, config: ONBOARDING_TASKS, width: ruleW }) : activeView === "agents" ? /* @__PURE__ */ jsx(OnboardingBox, { count: sortedAgents.length, config: ONBOARDING_AGENTS, width: ruleW }) : null,
3914
- /* @__PURE__ */ jsx(Box, { flexGrow: 1 }),
3915
- /* @__PURE__ */ jsx(
3916
- CommandBar,
3917
- {
3918
- mode: inputMode === "command" ? "command" : "navigate",
3919
- value: inputMode === "command" ? inputValue : "",
3920
- completion: inputMode === "command" ? resolveCompletion(inputValue) : null,
3921
- activeView,
3922
- canRun: !!canRun,
3923
- canNew: !!canNew,
3924
- canApprove: !!canApprove,
3925
- canReject: !!canReject,
3926
- canCancel: activeView === "tasks" && !!selectedTask && selectedTask.status === "in_progress" && !!onCancelTask,
3927
- canDelete: !!canDelete,
3928
- canEdit: !!canEdit,
3929
- canForceStop: !!canForceStop,
3930
- canToggleAuto: !!canToggleAuto,
3931
- autoActive: selectedAgent?.autonomous,
3932
- canPause: !!canPause,
3933
- isPaused: selectedGoal?.status === "paused",
3934
- canToggleShowAll: activeView === "tasks" && sortedTasks.length > TASK_LIST_LIMIT,
3935
- showAllActive: showAllTasks,
3936
- hasDetail: !!(showTaskDetail || showAgentDetail || showGoalDetail),
3937
- itemCount: activeView === "goals" ? sortedGoals.length : activeView === "tasks" ? sortedTasks.length : activeView === "agents" ? liveAgents.length : messages.length,
3938
- itemLabel: activeView === "goals" ? "goals" : activeView === "tasks" ? "tasks" : activeView === "agents" ? "agents" : "events",
3939
- width: W,
3940
- hasSuggestions: showSuggestions
3941
- }
3942
- )
3943
- ] });
3944
- }
3945
- function SuggestionsPanel({ suggestions, selectedIndex, height, width }) {
3946
- const maxVisible = height;
3947
- let scrollStart = 0;
3948
- if (selectedIndex >= maxVisible) {
3949
- scrollStart = selectedIndex - maxVisible + 1;
3950
- }
3951
- const visible = suggestions.slice(scrollStart, scrollStart + maxVisible);
3952
- return /* @__PURE__ */ jsx(Box, { flexDirection: "column", paddingX: 2, children: visible.map((sug, i) => {
3953
- const realIndex = i + scrollStart;
3954
- const isSelected = realIndex === selectedIndex;
3955
- const marker = isSelected ? "\u25B6" : " ";
3956
- const cmdPad = Math.min(20, Math.max(14, ...suggestions.map((s) => s.cmd.length + 1)));
3957
- const cmdText = sug.cmd.padEnd(cmdPad);
3958
- const subsText = sug.subs ? ` ${sug.subs}` : "";
3959
- const maxDescLen = Math.max(4, width - cmdPad - subsText.length - 8);
3960
- const desc = sug.desc.length > maxDescLen ? sug.desc.slice(0, maxDescLen - 1) + "\u2026" : sug.desc;
3961
- return /* @__PURE__ */ jsxs(Text, { wrap: "truncate", children: [
3962
- /* @__PURE__ */ jsx(Text, { color: isSelected ? tuiColors.amber : tuiColors.ghost, children: ` ${marker} ` }),
3963
- /* @__PURE__ */ jsx(Text, { color: isSelected ? tuiColors.white : tuiColors.silver, bold: isSelected, children: cmdText }),
3964
- /* @__PURE__ */ jsx(Text, { color: tuiColors.dim, children: desc }),
3965
- subsText && /* @__PURE__ */ jsx(Text, { color: tuiColors.ghost, children: subsText })
3966
- ] }, realIndex);
3967
- }) });
3968
- }
3969
- function GoalsContent({ goals, selectedIndex, scrollOffset = 0, height, width, showAddRow, agentNameMap }) {
3970
- const addRowIndex = goals.length;
3971
- const visible = goals.slice(scrollOffset, scrollOffset + height);
3972
- const addRowVisible = showAddRow && addRowIndex >= scrollOffset && addRowIndex < scrollOffset + height;
3973
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", height, children: [
3974
- visible.map((goal, i) => /* @__PURE__ */ jsx(Box, { paddingX: 2, children: /* @__PURE__ */ jsx(GoalRow, { goal, selected: i + scrollOffset === selectedIndex, width: width - 2, agentNameMap }) }, goal.id)),
3975
- addRowVisible && /* @__PURE__ */ jsx(Box, { paddingX: 2, children: /* @__PURE__ */ jsxs(Text, { color: selectedIndex === addRowIndex ? tuiColors.amber : tuiColors.ghost, children: [
3976
- selectedIndex === addRowIndex ? " \u25B8 " : " ",
3977
- /* @__PURE__ */ jsx(Text, { color: selectedIndex === addRowIndex ? tuiColors.amber : tuiColors.dim, children: "+ add goal..." })
3978
- ] }) }, "__add__")
3979
- ] });
3980
- }
3981
- function GoalDetailPanel({ goal, height, width, agentNameMap }) {
3982
- const assigneeName = goal.assignee ? agentNameMap?.get(goal.assignee) ?? goal.assignee : "any";
3983
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", height, paddingX: 2, children: [
3984
- /* @__PURE__ */ jsxs(Text, { children: [
3985
- /* @__PURE__ */ jsx(Text, { color: tuiColors.dim, children: "Status: " }),
3986
- /* @__PURE__ */ jsx(Text, { color: goal.status === "active" ? tuiColors.green : goal.status === "achieved" ? tuiColors.amber : tuiColors.dim, bold: true, children: goal.status.toUpperCase() }),
3987
- /* @__PURE__ */ jsxs(Text, { color: tuiColors.ghost, children: [
3988
- " ",
3989
- "\u2502",
3990
- " "
3991
- ] }),
3992
- /* @__PURE__ */ jsx(Text, { color: tuiColors.dim, children: "Assignee: " }),
3993
- /* @__PURE__ */ jsx(Text, { color: tuiColors.silver, children: assigneeName }),
3994
- /* @__PURE__ */ jsxs(Text, { color: tuiColors.ghost, children: [
3995
- " ",
3996
- "\u2502",
3997
- " "
3998
- ] }),
3999
- /* @__PURE__ */ jsx(Text, { color: tuiColors.dim, children: "Created: " }),
4000
- /* @__PURE__ */ jsx(Text, { color: tuiColors.silver, children: goal.created_at.slice(0, 10) })
4001
- ] }),
4002
- goal.description ? /* @__PURE__ */ jsx(Box, { flexDirection: "column", marginTop: 1, children: goal.description.split("\n").slice(0, Math.max(1, height - 2)).map((line, i) => /* @__PURE__ */ jsx(Text, { color: tuiColors.silver, wrap: "truncate", children: line }, i)) }) : /* @__PURE__ */ jsx(Text, { color: tuiColors.ghost, dimColor: true, children: "No description" })
4003
- ] });
4004
- }
4005
- function TasksContent({ tasks, selectedIndex, scrollOffset = 0, height, width, showAddRow, agentNameMap, hiddenCount = 0 }) {
4006
- const hasShowAll = hiddenCount > 0;
4007
- const showAllIndex = hasShowAll ? tasks.length : -1;
4008
- const addRowIndex = tasks.length + (hasShowAll ? 1 : 0);
4009
- const visible = tasks.slice(scrollOffset, scrollOffset + height);
4010
- const showAllVisible = hasShowAll && showAllIndex >= scrollOffset && showAllIndex < scrollOffset + height;
4011
- const addRowVisible = showAddRow && addRowIndex >= scrollOffset && addRowIndex < scrollOffset + height;
4012
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", height, children: [
4013
- visible.map((task, i) => /* @__PURE__ */ jsx(Box, { paddingX: 2, children: /* @__PURE__ */ jsx(TaskRow, { task, selected: i + scrollOffset === selectedIndex, width: width - 2, agentNameMap }) }, task.id)),
4014
- showAllVisible && /* @__PURE__ */ jsx(Box, { paddingX: 2, children: /* @__PURE__ */ jsxs(Text, { color: selectedIndex === showAllIndex ? tuiColors.amber : tuiColors.ghost, children: [
4015
- selectedIndex === showAllIndex ? " \u25B8 " : " ",
4016
- /* @__PURE__ */ jsxs(Text, { color: selectedIndex === showAllIndex ? tuiColors.amber : tuiColors.dim, children: [
4017
- "\u25BC",
4018
- " Show all (",
4019
- hiddenCount,
4020
- " more) \u2014 press ",
4021
- /* @__PURE__ */ jsx(Text, { bold: true, color: tuiColors.gray, children: "S" })
4022
- ] })
4023
- ] }) }, "__show_all__"),
4024
- addRowVisible && /* @__PURE__ */ jsx(Box, { paddingX: 2, children: /* @__PURE__ */ jsxs(Text, { color: selectedIndex === addRowIndex ? tuiColors.amber : tuiColors.ghost, children: [
4025
- selectedIndex === addRowIndex ? " \u25B8 " : " ",
4026
- /* @__PURE__ */ jsx(Text, { color: selectedIndex === addRowIndex ? tuiColors.amber : tuiColors.dim, children: "+ add task..." })
4027
- ] }) }, "__add__")
4028
- ] });
4029
- }
4030
- function AgentsContent({ agents, selectedIndex, scrollOffset = 0, height, width, state, taskTitleMap, showAddRow, agentTeamMap, teamLeadSet, activeTeamCount }) {
4031
- const runningByAgent = /* @__PURE__ */ new Map();
4032
- for (const entry of Object.values(state.running)) {
4033
- runningByAgent.set(entry.agent_id, entry);
4034
- }
4035
- const teamMemberCounts = /* @__PURE__ */ new Map();
4036
- if (activeTeamCount && activeTeamCount > 0) {
4037
- for (const a of agents) {
4038
- const t = agentTeamMap?.get(a.id);
4039
- if (t) teamMemberCounts.set(t, (teamMemberCounts.get(t) ?? 0) + 1);
4040
- }
4041
- }
4042
- const addRowIndex = agents.length;
4043
- const visible = agents.slice(scrollOffset, scrollOffset + height);
4044
- const addRowVisible = showAddRow && addRowIndex >= scrollOffset && addRowIndex < scrollOffset + height;
4045
- const hasTeams = activeTeamCount != null && activeTeamCount > 0;
4046
- const teamLeadNameMap = /* @__PURE__ */ new Map();
4047
- if (hasTeams && teamLeadSet && agentTeamMap) {
4048
- for (const a of agents) {
4049
- if (teamLeadSet.has(a.id)) {
4050
- const t = agentTeamMap.get(a.id);
4051
- if (t) teamLeadNameMap.set(t, a.name);
4052
- }
4053
- }
4054
- }
4055
- let teamMemberTotal = 0;
4056
- for (const c of teamMemberCounts.values()) teamMemberTotal += c;
4057
- const unassignedCount = agents.length - teamMemberTotal;
4058
- const rows = [];
4059
- let prevTeam = scrollOffset > 0 ? agentTeamMap?.get(agents[scrollOffset - 1]?.id ?? "") : void 0;
4060
- for (let i = 0; i < visible.length && rows.length < height; i++) {
4061
- const agent = visible[i];
4062
- const team = agentTeamMap?.get(agent.id);
4063
- if (hasTeams && team && team !== prevTeam) {
4064
- rows.push(
4065
- /* @__PURE__ */ jsx(
4066
- TeamSectionRow,
4067
- {
4068
- teamName: team,
4069
- memberCount: teamMemberCounts.get(team) ?? 0,
4070
- leadName: teamLeadNameMap.get(team),
4071
- width
4072
- },
4073
- `ts-${team}`
4074
- )
4075
- );
4076
- if (rows.length >= height) break;
4077
- }
4078
- if (hasTeams && !team && prevTeam) {
4079
- rows.push(
4080
- /* @__PURE__ */ jsx(UnassignedSectionRow, { memberCount: unassignedCount, width }, "ts-unassigned")
4081
- );
4082
- if (rows.length >= height) break;
4083
- }
4084
- prevTeam = team;
4085
- rows.push(
4086
- /* @__PURE__ */ jsx(Box, { paddingX: 2, children: /* @__PURE__ */ jsx(
4087
- AgentRow,
4088
- {
4089
- agent,
4090
- selected: i + scrollOffset === selectedIndex,
4091
- width: width - 2,
4092
- runningEntry: runningByAgent.get(agent.id),
4093
- currentTaskTitle: agent.current_task ? taskTitleMap.get(agent.current_task) : void 0,
4094
- teamName: team,
4095
- isLead: teamLeadSet?.has(agent.id)
4096
- }
4097
- ) }, agent.id)
4098
- );
4099
- }
4100
- if (addRowVisible && rows.length < height) {
4101
- rows.push(
4102
- /* @__PURE__ */ jsx(Box, { paddingX: 2, children: /* @__PURE__ */ jsxs(Text, { color: selectedIndex === addRowIndex ? tuiColors.amber : tuiColors.ghost, children: [
4103
- selectedIndex === addRowIndex ? " \u25B8 " : " ",
4104
- /* @__PURE__ */ jsx(Text, { color: selectedIndex === addRowIndex ? tuiColors.amber : tuiColors.dim, children: "+ add agent..." })
4105
- ] }) }, "__add__")
4106
- );
4107
- }
4108
- return /* @__PURE__ */ jsx(Box, { flexDirection: "column", height, children: rows });
4109
- }
4110
- function useNow(interval = 5e3) {
4111
- const [now, setNow] = useState(Date.now());
4112
- useEffect(() => {
4113
- const timer2 = setInterval(() => setNow(Date.now()), interval);
4114
- return () => clearInterval(timer2);
4115
- }, [interval]);
4116
- return now;
4117
- }
4118
- function relativeTime(ts, now) {
4119
- const diff = Math.max(0, now - ts);
4120
- if (diff < 3e3) return "now";
4121
- if (diff < 6e4) return `${Math.floor(diff / 1e3)}s`;
4122
- if (diff < 36e5) return `${Math.floor(diff / 6e4)}m`;
4123
- return `${Math.floor(diff / 36e5)}h`;
4124
- }
4125
- var SPARK_CHARS2 = " \u2581\u2582\u2583\u2584\u2585\u2586\u2587\u2588";
4126
- function buildSparkline(messages, buckets, bucketMs, now) {
4127
- const counts = new Array(buckets).fill(0);
4128
- const windowStart = now - buckets * bucketMs;
4129
- for (const m of messages) {
4130
- if (m.ts < windowStart) continue;
4131
- const idx = Math.min(buckets - 1, Math.floor((m.ts - windowStart) / bucketMs));
4132
- counts[idx]++;
4133
- }
4134
- const max = Math.max(1, ...counts);
4135
- return counts.map((c) => SPARK_CHARS2[Math.round(c / max * 8)]).join("");
4136
- }
4137
- function getMsgBg(msgType) {
4138
- switch (msgType) {
4139
- case "error":
4140
- return tuiColors.errorBg;
4141
- default:
4142
- return void 0;
4143
- }
4144
- }
4145
- function getMsgTextColor(msgType, fallback) {
4146
- switch (msgType) {
4147
- case "output":
4148
- return tuiColors.white;
4149
- case "tool":
4150
- return tuiColors.dim;
4151
- case "result":
4152
- return tuiColors.dim;
4153
- case "file":
4154
- return tuiColors.gray;
4155
- case "error":
4156
- return tuiColors.red;
4157
- case "lifecycle":
4158
- return tuiColors.dim;
4159
- case "system":
4160
- return tuiColors.dim;
4161
- default:
4162
- return fallback;
4163
- }
4164
- }
4165
- function LogsContent({ messages, height, agents, logFilter, logTypeFilter, selectedIndex, scrollOffset, agentNameMap, taskTitleMap, width }) {
4166
- const now = useNow();
4167
- const filteredMessages = useMemo(() => {
4168
- return messages.filter((m) => {
4169
- if (logFilter !== 0) {
4170
- const agent = agents[logFilter - 1];
4171
- if (agent && m.agentId !== agent.id) return false;
4172
- }
4173
- const mt = m.msgType ?? "info";
4174
- return logTypeFilter.has(mt);
4175
- });
4176
- }, [messages, agents, logFilter, logTypeFilter]);
4177
- useMemo(() => {
4178
- const counts = {};
4179
- for (const m of messages) {
4180
- const mt = m.msgType ?? "info";
4181
- counts[mt] = (counts[mt] ?? 0) + 1;
4182
- }
4183
- return counts;
4184
- }, [messages]);
4185
- const agentMsgCounts = useMemo(() => {
4186
- const counts = /* @__PURE__ */ new Map();
4187
- for (const m of messages) {
4188
- if (m.agentId) counts.set(m.agentId, (counts.get(m.agentId) ?? 0) + 1);
4189
- }
4190
- return counts;
4191
- }, [messages]);
4192
- const sparkline = useMemo(() => buildSparkline(filteredMessages, 30, 1e4, now), [filteredMessages, now]);
4193
- const typeFilterLabel = logTypeFilter.size >= 8 ? "all" : logTypeFilter.size === 1 && logTypeFilter.has("output") ? "text" : logTypeFilter.size === 1 && logTypeFilter.has("error") ? "errors" : logTypeFilter.has("tool") && !logTypeFilter.has("output") ? "tools" : logTypeFilter.has("lifecycle") && !logTypeFilter.has("output") ? "events" : `${logTypeFilter.size} types`;
4194
- const viewH = height - 3;
4195
- const visible = selectedIndex === -1 ? filteredMessages.slice(-viewH) : filteredMessages.slice(scrollOffset, scrollOffset + viewH);
4196
- const highlightIdx = selectedIndex === -1 ? -1 : selectedIndex - scrollOffset;
4197
- const agentColW = Math.min(10, Math.max(6, ...agents.map((a) => a.name.length)));
4198
- const prefixW = 11 + agentColW;
4199
- const isSessionStart = (i) => {
4200
- if (i === 0) return true;
4201
- const curr = visible[i];
4202
- const prev = visible[i - 1];
4203
- if (curr.agentId !== prev.agentId) return true;
4204
- if (!curr.agentId) return false;
4205
- return curr.ts - prev.ts > 3e4;
4206
- };
4207
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingX: 1, children: [
4208
- /* @__PURE__ */ jsxs(Box, { gap: 0, children: [
4209
- logFilter === 0 ? /* @__PURE__ */ jsx(Text, { backgroundColor: tuiColors.infoBg, color: tuiColors.silver, bold: true, children: " \u25CF ALL " }) : /* @__PURE__ */ jsx(Text, { color: tuiColors.ghost, children: " \u25CB all " }),
4210
- /* @__PURE__ */ jsx(Text, { color: tuiColors.ghost, children: " " }),
4211
- agents.slice(0, 9).map((a, i) => {
4212
- const ac = getAgentColor(a.id, agents);
4213
- const active = logFilter === i + 1;
4214
- const count = agentMsgCounts.get(a.id) ?? 0;
4215
- const countStr = count > 0 ? `\xB7${count}` : "";
4216
- return active ? /* @__PURE__ */ jsxs(Text, { backgroundColor: tuiColors.successBg, color: ac, bold: true, children: [
4217
- " ",
4218
- i + 1,
4219
- ":",
4220
- a.name,
4221
- countStr,
4222
- " "
4223
- ] }, a.id) : /* @__PURE__ */ jsxs(Text, { color: count > 0 ? tuiColors.dim : tuiColors.ghost, children: [
4224
- " ",
4225
- i + 1,
4226
- ":",
4227
- a.name,
4228
- countStr
4229
- ] }, a.id);
4230
- })
4231
- ] }),
4232
- /* @__PURE__ */ jsxs(Box, { gap: 0, children: [
4233
- typeFilterLabel === "all" ? /* @__PURE__ */ jsx(Text, { color: tuiColors.dim, children: " F:all" }) : /* @__PURE__ */ jsxs(Text, { backgroundColor: tuiColors.warnBg, color: tuiColors.amber, bold: true, children: [
4234
- " F:",
4235
- typeFilterLabel.toUpperCase(),
4236
- " "
4237
- ] }),
4238
- /* @__PURE__ */ jsxs(Text, { color: tuiColors.ghost, children: [
4239
- " ",
4240
- "\u2502",
4241
- " "
4242
- ] }),
4243
- /* @__PURE__ */ jsxs(Text, { color: tuiColors.dim, children: [
4244
- filteredMessages.length,
4245
- " events"
4246
- ] }),
4247
- /* @__PURE__ */ jsxs(Text, { color: tuiColors.ghost, children: [
4248
- " ",
4249
- "\u2502",
4250
- " "
4251
- ] }),
4252
- selectedIndex === -1 ? /* @__PURE__ */ jsxs(Box, { gap: 0, children: [
4253
- /* @__PURE__ */ jsx(Text, { backgroundColor: tuiColors.successBg, color: tuiColors.green, children: " " }),
4254
- /* @__PURE__ */ jsx(Text, { backgroundColor: tuiColors.successBg, color: tuiColors.green, children: /* @__PURE__ */ jsx(Spinner, { color: tuiColors.green }) }),
4255
- /* @__PURE__ */ jsx(Text, { backgroundColor: tuiColors.successBg, color: tuiColors.green, children: " LIVE " })
4256
- ] }) : /* @__PURE__ */ jsxs(Text, { backgroundColor: tuiColors.warnBg, color: tuiColors.amber, children: [
4257
- " \u2191\u2193 ",
4258
- selectedIndex + 1,
4259
- "/",
4260
- filteredMessages.length,
4261
- " "
4262
- ] }),
4263
- /* @__PURE__ */ jsx(Text, { color: tuiColors.ghost, children: " " }),
4264
- /* @__PURE__ */ jsx(Text, { color: tuiColors.amberDim, children: sparkline }),
4265
- /* @__PURE__ */ jsx(Text, { color: tuiColors.ghost, children: " 5m" })
4266
- ] }),
4267
- visible.length === 0 ? /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingX: 2, paddingTop: 1, children: [
4268
- /* @__PURE__ */ jsx(Text, { color: tuiColors.dim, children: messages.length === 0 ? " \u256D\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E" : "No events for current filter." }),
4269
- messages.length === 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
4270
- /* @__PURE__ */ jsx(Text, { color: tuiColors.dim, children: " \u2502 \u2502" }),
4271
- /* @__PURE__ */ jsxs(Text, { color: tuiColors.dim, children: [
4272
- " \u2502 ",
4273
- /* @__PURE__ */ jsx(Text, { color: tuiColors.ghost, children: "\u25C7" }),
4274
- /* @__PURE__ */ jsx(Text, { color: tuiColors.gray, children: " Waiting for activity " }),
4275
- "\u2502"
4276
- ] }),
4277
- /* @__PURE__ */ jsxs(Text, { color: tuiColors.dim, children: [
4278
- " \u2502 ",
4279
- /* @__PURE__ */ jsx(Text, { color: tuiColors.ghost, children: "\u2502" }),
4280
- /* @__PURE__ */ jsx(Text, { color: tuiColors.dim, children: " Run tasks or start " }),
4281
- "\u2502"
4282
- ] }),
4283
- /* @__PURE__ */ jsxs(Text, { color: tuiColors.dim, children: [
4284
- " \u2502 ",
4285
- /* @__PURE__ */ jsx(Text, { color: tuiColors.ghost, children: "\u2502" }),
4286
- /* @__PURE__ */ jsx(Text, { color: tuiColors.dim, children: " the orchestrator " }),
4287
- "\u2502"
4288
- ] }),
4289
- /* @__PURE__ */ jsxs(Text, { color: tuiColors.dim, children: [
4290
- " \u2502 ",
4291
- /* @__PURE__ */ jsx(Text, { color: tuiColors.ghost, children: "\u25C7" }),
4292
- /* @__PURE__ */ jsx(Text, { color: tuiColors.dim, children: " " }),
4293
- "\u2502"
4294
- ] }),
4295
- /* @__PURE__ */ jsx(Text, { color: tuiColors.dim, children: " \u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F" })
4296
- ] })
4297
- ] }) : visible.map((msg, i) => {
4298
- const isSelected = i === highlightIdx;
4299
- const msgType = msg.msgType ?? "info";
4300
- const icon = MSG_ICONS2[msgType] ?? "\u2502";
4301
- const agentName = msg.agentId ? agentNameMap.get(msg.agentId) ?? msg.agentId.slice(0, 8) : void 0;
4302
- const agentColor = msg.agentId ? getAgentColor(msg.agentId, agents) : void 0;
4303
- const sessionStart = isSessionStart(i);
4304
- const prevMsg = i > 0 ? visible[i - 1] : void 0;
4305
- const isContinuation = prevMsg?.agentId === msg.agentId && !!msg.agentId;
4306
- const showAgentBadge = !isContinuation && !!agentName;
4307
- const showConnector = isContinuation && !!agentName;
4308
- const textColor = getMsgTextColor(msgType, msg.color);
4309
- const rowBg = isSelected ? tuiColors.infoBg : msgType === "error" ? tuiColors.errorBg : void 0;
4310
- const taskTitle = msg.taskId ? taskTitleMap.get(msg.taskId) : void 0;
4311
- const relTs = relativeTime(msg.ts, now);
4312
- const badgeLabel = taskTitle && width > 80 ? `#${taskTitle.slice(0, 20)}` : "";
4313
- const badgeW = badgeLabel ? badgeLabel.length + 3 : 0;
4314
- const textW = Math.max(10, width - 2 - prefixW - badgeW);
4315
- const displayText = msg.text.length > textW ? msg.text.slice(0, textW - 1) + "\u2026" : msg.text;
4316
- return /* @__PURE__ */ jsxs(Box, { backgroundColor: rowBg, children: [
4317
- /* @__PURE__ */ jsx(Text, { color: agentColor ?? tuiColors.ghost, children: sessionStart && showAgentBadge ? "\u250C" : showConnector ? "\u2502" : " " }),
4318
- /* @__PURE__ */ jsx(Text, { color: isSelected ? tuiColors.amber : void 0, children: isSelected ? "\u25B8" : " " }),
4319
- /* @__PURE__ */ jsx(Box, { width: 5, children: /* @__PURE__ */ jsx(Text, { color: relTs === "now" ? tuiColors.green : isSelected ? tuiColors.silver : tuiColors.ghost, children: relTs.padStart(4) }) }),
4320
- /* @__PURE__ */ jsx(Box, { width: agentColW + 1, children: showAgentBadge ? /* @__PURE__ */ jsxs(Text, { color: agentColor, bold: true, children: [
4321
- " ",
4322
- agentName.slice(0, agentColW).padEnd(agentColW)
4323
- ] }) : showConnector ? /* @__PURE__ */ jsxs(Text, { color: agentColor ?? tuiColors.ghost, children: [
4324
- " ",
4325
- "\xB7".padEnd(agentColW)
4326
- ] }) : /* @__PURE__ */ jsxs(Text, { color: tuiColors.ghost, children: [
4327
- " ",
4328
- " ".padEnd(agentColW)
4329
- ] }) }),
4330
- /* @__PURE__ */ jsxs(Text, { color: msgType === "error" ? tuiColors.red : agentColor ?? tuiColors.dim, children: [
4331
- " ",
4332
- icon,
4333
- " "
4334
- ] }),
4335
- /* @__PURE__ */ jsx(
4336
- Text,
4337
- {
4338
- color: isSelected ? tuiColors.white : textColor,
4339
- bold: isSelected || msgType === "lifecycle",
4340
- children: displayText
4341
- }
4342
- ),
4343
- badgeLabel && /* @__PURE__ */ jsxs(Text, { color: tuiColors.ghost, children: [
4344
- " ",
4345
- /* @__PURE__ */ jsx(Text, { color: tuiColors.dim, backgroundColor: tuiColors.void, children: ` ${badgeLabel} ` })
4346
- ] })
4347
- ] }, i);
4348
- })
4349
- ] });
4350
- }
4351
- function ActivityFeed({ messages, height, width, agents, agentNameMap }) {
4352
- const now = useNow();
4353
- const visible = messages.slice(-height);
4354
- const textW = Math.max(10, width - 2 - 17);
4355
- const padRows = Math.max(0, height - visible.length);
4356
- let groupIdx = 0;
4357
- const groupIndices = [];
4358
- for (let j = 0; j < visible.length; j++) {
4359
- if (j > 0 && visible[j].agentId !== visible[j - 1].agentId) groupIdx++;
4360
- groupIndices.push(groupIdx);
4361
- }
4362
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingX: 1, children: [
4363
- padRows > 0 && /* @__PURE__ */ jsx(Box, { height: padRows }),
4364
- visible.map((msg, i) => {
4365
- const agentName = msg.agentId ? agentNameMap.get(msg.agentId) ?? msg.agentId.slice(0, 8) : void 0;
4366
- const agentColor = msg.agentId ? getAgentColor(msg.agentId, agents) : void 0;
4367
- const msgType = msg.msgType ?? "info";
4368
- const icon = MSG_ICONS2[msgType] ?? "\u2502";
4369
- const textColor = getMsgTextColor(msgType, msg.color);
4370
- const prevMsg = i > 0 ? visible[i - 1] : void 0;
4371
- const isContinuation = prevMsg?.agentId === msg.agentId && !!msg.agentId;
4372
- const isOddGroup = (groupIndices[i] & 1) === 1;
4373
- const rowBg = getMsgBg(msgType) ?? (isOddGroup ? "#1a1a1a" : void 0);
4374
- const relTs = relativeTime(msg.ts, now);
4375
- const displayText = msg.text.length > textW ? msg.text.slice(0, textW - 1) + "\u2026" : msg.text;
4376
- return /* @__PURE__ */ jsxs(Box, { backgroundColor: rowBg, children: [
4377
- /* @__PURE__ */ jsx(Text, { color: agentColor ?? tuiColors.ghost, children: !isContinuation && agentName ? "\u258D" : isContinuation ? "\u258F" : " " }),
4378
- /* @__PURE__ */ jsx(Box, { width: 5, children: /* @__PURE__ */ jsx(Text, { color: isContinuation ? tuiColors.ghost : relTs === "now" ? tuiColors.green : tuiColors.dim, children: isContinuation ? " " : relTs.padStart(4) }) }),
4379
- /* @__PURE__ */ jsx(Box, { width: 9, children: agentName && !isContinuation ? /* @__PURE__ */ jsxs(Text, { color: agentColor, bold: true, children: [
4380
- " ",
4381
- agentName.slice(0, 8)
4382
- ] }) : /* @__PURE__ */ jsx(Text, { color: tuiColors.ghost, children: AGENT_INDENT }) }),
4383
- /* @__PURE__ */ jsxs(Text, { color: msgType === "error" ? tuiColors.red : isContinuation ? tuiColors.ghost : agentColor ?? tuiColors.dim, children: [
4384
- icon,
4385
- " "
4386
- ] }),
4387
- /* @__PURE__ */ jsx(Text, { color: textColor, children: displayText })
4388
- ] }, i);
4389
- })
4390
- ] });
4391
- }
4392
- function LogDetailPanel({ message, height, width, agents, agentNameMap, taskTitleMap }) {
4393
- const content = message.detail ?? message.text;
4394
- const msgType = message.msgType ?? "info";
4395
- const agentName = message.agentId ? agentNameMap.get(message.agentId) ?? message.agentId.slice(0, 8) : void 0;
4396
- const agentColor = message.agentId ? getAgentColor(message.agentId, agents) : tuiColors.dim;
4397
- const taskTitle = message.taskId ? taskTitleMap.get(message.taskId) : void 0;
4398
- let display;
4399
- let isJson = false;
4400
- try {
4401
- const parsed = JSON.parse(content);
4402
- display = JSON.stringify(parsed, null, 2);
4403
- isJson = true;
4404
- } catch {
4405
- display = content;
4406
- }
4407
- const maxW = Math.max(4, width - 6);
4408
- const bodyHeight = Math.max(1, height - 4);
4409
- const lines = display.split("\n").slice(0, bodyHeight);
4410
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingX: 1, children: [
4411
- /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsxs(Text, { color: tuiColors.ghost, children: [
4412
- "\u256D",
4413
- "\u2500".repeat(maxW + 2),
4414
- "\u256E"
4415
- ] }) }),
4416
- /* @__PURE__ */ jsxs(Box, { children: [
4417
- /* @__PURE__ */ jsx(Text, { color: tuiColors.ghost, children: "\u2502 " }),
4418
- /* @__PURE__ */ jsx(Text, { color: tuiColors.dim, children: message.time }),
4419
- /* @__PURE__ */ jsx(Text, { color: tuiColors.ghost, children: " \u2502 " }),
4420
- agentName && /* @__PURE__ */ jsx(Text, { color: agentColor, bold: true, children: agentName }),
4421
- agentName && /* @__PURE__ */ jsx(Text, { color: tuiColors.ghost, children: " \u2502 " }),
4422
- /* @__PURE__ */ jsxs(Text, { color: MSG_ICONS2[msgType] ? msgType === "error" ? tuiColors.red : tuiColors.dim : tuiColors.dim, children: [
4423
- MSG_ICONS2[msgType] ?? "\u2502",
4424
- " ",
4425
- msgType
4426
- ] }),
4427
- taskTitle && /* @__PURE__ */ jsxs(Fragment, { children: [
4428
- /* @__PURE__ */ jsx(Text, { color: tuiColors.ghost, children: " \u2502 " }),
4429
- /* @__PURE__ */ jsxs(Text, { color: tuiColors.dim, children: [
4430
- "#",
4431
- taskTitle.slice(0, 30)
4432
- ] })
4433
- ] })
4434
- ] }),
4435
- /* @__PURE__ */ jsxs(Box, { children: [
4436
- /* @__PURE__ */ jsx(Text, { color: tuiColors.ghost, children: "\u2502 " }),
4437
- /* @__PURE__ */ jsx(Text, { color: message.color, bold: true, wrap: "truncate", children: message.text.slice(0, maxW) })
4438
- ] }),
4439
- /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsxs(Text, { color: tuiColors.ghost, children: [
4440
- "\u251C",
4441
- "\u2500".repeat(maxW + 2),
4442
- "\u2524"
4443
- ] }) }),
4444
- lines.map((line, i) => /* @__PURE__ */ jsxs(Box, { children: [
4445
- /* @__PURE__ */ jsx(Text, { color: tuiColors.ghost, children: "\u2502 " }),
4446
- isJson && /* @__PURE__ */ jsxs(Text, { color: tuiColors.ghost, children: [
4447
- String(i + 1).padStart(3),
4448
- " "
4449
- ] }),
4450
- /* @__PURE__ */ jsx(
4451
- Text,
4452
- {
4453
- wrap: "truncate",
4454
- color: isJson && line.includes('"') ? tuiColors.cyan : isJson && /^\s*[}\]]/.test(line) ? tuiColors.ghost : line.startsWith("error") || line.startsWith("Error") ? tuiColors.red : tuiColors.silver,
4455
- children: line.slice(0, isJson ? maxW - 4 : maxW)
4456
- }
4457
- )
4458
- ] }, i)),
4459
- /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsxs(Text, { color: tuiColors.ghost, children: [
4460
- "\u2570",
4461
- "\u2500".repeat(maxW + 2),
4462
- "\u256F"
4463
- ] }) })
4464
- ] });
4465
- }
4466
- function SectionLabel({ label, width, suffix, suffixLen = 0 }) {
4467
- const chipText = ` ${label} `;
4468
- const leftRuleLen = 3;
4469
- const usedLen = leftRuleLen + chipText.length + 2;
4470
- if (!suffix) {
4471
- const rightRuleLen = Math.max(0, width - usedLen);
4472
- return /* @__PURE__ */ jsxs(Box, { paddingX: 1, children: [
4473
- /* @__PURE__ */ jsx(Text, { color: tuiColors.ghost, children: HEAVY_RULE.repeat(leftRuleLen) }),
4474
- /* @__PURE__ */ jsx(Text, { backgroundColor: "#1a1a22", color: tuiColors.dim, bold: true, children: chipText }),
4475
- /* @__PURE__ */ jsx(Text, { color: tuiColors.ghost, children: HEAVY_RULE.repeat(rightRuleLen) })
4476
- ] });
4477
- }
4478
- const gapLen = 2;
4479
- const trailLen = Math.max(0, width - usedLen - gapLen - suffixLen);
4480
- return /* @__PURE__ */ jsxs(Box, { paddingX: 1, children: [
4481
- /* @__PURE__ */ jsx(Text, { color: tuiColors.ghost, children: HEAVY_RULE.repeat(leftRuleLen) }),
4482
- /* @__PURE__ */ jsx(Text, { backgroundColor: "#1a1a22", color: tuiColors.dim, bold: true, children: chipText }),
4483
- /* @__PURE__ */ jsx(Text, { color: tuiColors.ghost, children: HEAVY_RULE.repeat(gapLen) }),
4484
- suffix,
4485
- /* @__PURE__ */ jsx(Text, { color: tuiColors.ghost, children: HEAVY_RULE.repeat(trailLen) })
4486
- ] });
4487
- }
4488
- function DetailSectionLabel({ task, width }) {
4489
- const chipText = " DETAIL ";
4490
- const maxTitleLen = width - chipText.length - 10;
4491
- const titleTrunc = task.title.length > maxTitleLen ? task.title.slice(0, maxTitleLen - 3) + "..." : task.title;
4492
- const rightRuleLen = Math.max(0, width - 3 - chipText.length - titleTrunc.length - 4);
4493
- return /* @__PURE__ */ jsxs(Box, { paddingX: 1, children: [
4494
- /* @__PURE__ */ jsx(Text, { color: tuiColors.ghost, children: HEAVY_RULE.repeat(3) }),
4495
- /* @__PURE__ */ jsx(Text, { backgroundColor: "#2d1f0a", color: tuiColors.amber, bold: true, children: chipText }),
4496
- /* @__PURE__ */ jsxs(Text, { color: tuiColors.ghost, children: [
4497
- HEAVY_RULE,
4498
- " "
4499
- ] }),
4500
- /* @__PURE__ */ jsx(Text, { color: tuiColors.white, bold: true, children: titleTrunc }),
4501
- /* @__PURE__ */ jsxs(Text, { color: tuiColors.ghost, children: [
4502
- " ",
4503
- HEAVY_RULE.repeat(Math.max(0, rightRuleLen))
4504
- ] })
4505
- ] });
4506
- }
4507
- function AgentDetailSectionLabel({ agent, width }) {
4508
- const chipText = " AGENT ";
4509
- const maxNameLen = width - chipText.length - 10;
4510
- const nameTrunc = agent.name.length > maxNameLen ? agent.name.slice(0, maxNameLen - 3) + "..." : agent.name;
4511
- const rightRuleLen = Math.max(0, width - 3 - chipText.length - nameTrunc.length - 4);
4512
- return /* @__PURE__ */ jsxs(Box, { paddingX: 1, children: [
4513
- /* @__PURE__ */ jsx(Text, { color: tuiColors.ghost, children: HEAVY_RULE.repeat(3) }),
4514
- /* @__PURE__ */ jsx(Text, { backgroundColor: "#0f2d1f", color: tuiColors.green, bold: true, children: chipText }),
4515
- /* @__PURE__ */ jsxs(Text, { color: tuiColors.ghost, children: [
4516
- HEAVY_RULE,
4517
- " "
4518
- ] }),
4519
- /* @__PURE__ */ jsx(Text, { color: tuiColors.green, bold: true, children: nameTrunc }),
4520
- /* @__PURE__ */ jsxs(Text, { color: tuiColors.ghost, children: [
4521
- " ",
4522
- HEAVY_RULE.repeat(Math.max(0, rightRuleLen))
4523
- ] })
4524
- ] });
4525
- }
4526
- function AgentDetailPanel({ agent, height, state, taskTitleMap, teamName }) {
4527
- const statusColor = STATUS_DETAIL_COLOR2[agent.status] ?? tuiColors.dim;
4528
- Object.values(state.running).find((e) => e.agent_id === agent.id);
4529
- const taskTitle = agent.current_task ? taskTitleMap.get(agent.current_task) : void 0;
4530
- const col1Width = 24;
4531
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingX: 2, children: [
4532
- /* @__PURE__ */ jsxs(Box, { children: [
4533
- /* @__PURE__ */ jsxs(Box, { width: col1Width, children: [
4534
- /* @__PURE__ */ jsx(Text, { color: tuiColors.dim, children: " status " }),
4535
- /* @__PURE__ */ jsx(Text, { color: statusColor, children: agent.status })
4536
- ] }),
4537
- /* @__PURE__ */ jsxs(Box, { children: [
4538
- /* @__PURE__ */ jsx(Text, { color: tuiColors.dim, children: " adapter " }),
4539
- /* @__PURE__ */ jsx(Text, { color: tuiColors.cyan, children: agent.adapter })
4540
- ] })
4541
- ] }),
4542
- /* @__PURE__ */ jsxs(Box, { children: [
4543
- /* @__PURE__ */ jsxs(Box, { width: col1Width, children: [
4544
- /* @__PURE__ */ jsx(Text, { color: tuiColors.dim, children: " model " }),
4545
- /* @__PURE__ */ jsx(Text, { children: agent.config.model ?? "\u2014" })
4546
- ] }),
4547
- /* @__PURE__ */ jsxs(Box, { children: [
4548
- /* @__PURE__ */ jsx(Text, { color: tuiColors.dim, children: " task " }),
4549
- /* @__PURE__ */ jsx(Text, { color: taskTitle ? tuiColors.white : tuiColors.dim, children: taskTitle ?? "\u2014" })
4550
- ] })
4551
- ] }),
4552
- /* @__PURE__ */ jsxs(Box, { children: [
4553
- /* @__PURE__ */ jsxs(Box, { width: col1Width, children: [
4554
- /* @__PURE__ */ jsx(Text, { color: tuiColors.dim, children: " runs " }),
4555
- /* @__PURE__ */ jsx(Text, { children: agent.stats.total_runs }),
4556
- /* @__PURE__ */ jsx(Text, { color: tuiColors.dim, children: " (" }),
4557
- /* @__PURE__ */ jsx(Text, { color: tuiColors.green, children: agent.stats.tasks_completed }),
4558
- /* @__PURE__ */ jsx(Text, { color: tuiColors.dim, children: "/" }),
4559
- /* @__PURE__ */ jsx(Text, { color: agent.stats.tasks_failed > 0 ? tuiColors.red : tuiColors.dim, children: agent.stats.tasks_failed }),
4560
- /* @__PURE__ */ jsx(Text, { color: tuiColors.dim, children: ")" })
4561
- ] }),
4562
- /* @__PURE__ */ jsxs(Box, { children: [
4563
- /* @__PURE__ */ jsx(Text, { color: tuiColors.dim, children: " team " }),
4564
- /* @__PURE__ */ jsx(Text, { color: teamName ? tuiColors.amber : tuiColors.dim, children: teamName ?? "\u2014" })
4565
- ] })
4566
- ] }),
4567
- agent.autonomous && /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsxs(Box, { width: col1Width, children: [
4568
- /* @__PURE__ */ jsx(Text, { color: tuiColors.dim, children: " auto " }),
4569
- /* @__PURE__ */ jsxs(Text, { color: tuiColors.cyan, children: [
4570
- LOOP,
4571
- " ON"
4572
- ] })
4573
- ] }) }),
4574
- /* @__PURE__ */ jsx(Text, { children: " " }),
4575
- agent.role ? agent.role.split("\n").slice(0, Math.max(1, height - 4)).map((line, i) => /* @__PURE__ */ jsxs(Text, { color: tuiColors.silver, wrap: "truncate", children: [
4576
- " ",
4577
- line
4578
- ] }, i)) : /* @__PURE__ */ jsx(Text, { color: tuiColors.dim, children: " No role description." })
4579
- ] });
4580
- }
4581
- var STATUS_DETAIL_COLOR2 = {
4582
- idle: tuiColors.dim,
4583
- running: tuiColors.green,
4584
- error: tuiColors.red,
4585
- disabled: tuiColors.ghost
4586
- };
4587
- var CURSOR_CHAR2 = "\u2588";
4588
- function InputSectionLabel({ mode, width }) {
4589
- const label = mode === "command" ? "COMMAND" : "NEW TASK";
4590
- const chipText = ` ${label} `;
4591
- const rightRuleLen = Math.max(0, width - 3 - chipText.length - 2);
4592
- return /* @__PURE__ */ jsxs(Box, { paddingX: 1, children: [
4593
- /* @__PURE__ */ jsx(Text, { color: tuiColors.ghost, children: HEAVY_RULE.repeat(3) }),
4594
- /* @__PURE__ */ jsx(Text, { backgroundColor: "#2d1f0a", color: tuiColors.amber, bold: true, children: chipText }),
4595
- /* @__PURE__ */ jsx(Text, { color: tuiColors.ghost, children: HEAVY_RULE.repeat(rightRuleLen) })
4596
- ] });
4597
- }
4598
- function InputPanel({ mode, value, width }) {
4599
- const prefix = mode === "command" ? "/" : "\u25B8";
4600
- const maxLen = Math.max(10, width - 8);
4601
- const displayValue = value.length > maxLen ? value.slice(-maxLen) : value;
4602
- return /* @__PURE__ */ jsxs(Box, { paddingX: 2, children: [
4603
- /* @__PURE__ */ jsxs(Text, { color: tuiColors.amber, children: [
4604
- prefix,
4605
- " "
4606
- ] }),
4607
- /* @__PURE__ */ jsx(Text, { color: tuiColors.white, children: displayValue }),
4608
- /* @__PURE__ */ jsx(Text, { color: tuiColors.amber, children: CURSOR_CHAR2 })
4609
- ] });
4610
- }
4611
- function formatToolInput(name, input) {
4612
- if (!input || typeof input !== "object") return "";
4613
- const inp = input;
4614
- if (inp.file_path && typeof inp.file_path === "string") {
4615
- const parts = inp.file_path.split("/");
4616
- return parts.slice(-2).join("/");
4617
- }
4618
- if (inp.command && typeof inp.command === "string") {
4619
- return inp.command.slice(0, 60);
4620
- }
4621
- if (inp.pattern && typeof inp.pattern === "string") {
4622
- return `"${inp.pattern.slice(0, 40)}"`;
4623
- }
4624
- if (inp.glob && typeof inp.glob === "string") {
4625
- return inp.glob.slice(0, 40);
4626
- }
4627
- const s = JSON.stringify(inp);
4628
- return s.length > 80 ? s.slice(0, 77) + "..." : s;
4629
- }
4630
- function extractTextFromContent(content, maxLen = 200) {
4631
- if (typeof content === "string") return content.slice(0, maxLen);
4632
- if (!Array.isArray(content)) return null;
4633
- const parts = [];
4634
- let totalLen = 0;
4635
- for (const block of content) {
4636
- if (totalLen >= maxLen) break;
4637
- if (block?.type === "text" && typeof block.text === "string") {
4638
- const firstLine = block.text.split("\n").find((l) => l.trim().length > 0) ?? "";
4639
- parts.push(firstLine.slice(0, maxLen - totalLen));
4640
- totalLen += firstLine.length;
4641
- } else if (block?.type === "tool_use") {
4642
- const hint = formatToolInput(block.name ?? "tool", block.input);
4643
- const s = `\u2699 ${block.name ?? "tool"}(${hint})`;
4644
- parts.push(s);
4645
- totalLen += s.length;
4646
- } else if (block?.type === "tool_result") {
4647
- parts.push(`\u2190 (result)`);
4648
- totalLen += 10;
4649
- } else if (block?.type === "thinking" && typeof block.thinking === "string") {
4650
- const s = block.thinking.slice(0, 60).split("\n")[0] ?? "";
4651
- parts.push(`\u{1F4AD} ${s}`);
4652
- totalLen += s.length + 3;
4653
- }
4654
- }
4655
- return parts.length > 0 ? parts.join(" ") : null;
4656
- }
4657
- function summarizeToolResult(content) {
4658
- if (typeof content === "string") {
4659
- const lines = content.split("\n").length;
4660
- const firstLine = content.split("\n").find((l) => l.trim().length > 0) ?? "";
4661
- if (lines > 3) return `${firstLine.slice(0, 80)}... (${lines} lines)`;
4662
- return firstLine.slice(0, 120);
4663
- }
4664
- if (!Array.isArray(content)) return "(result)";
4665
- const summaries = [];
4666
- for (const block of content) {
4667
- if (block?.type === "tool_result") {
4668
- block.tool_use_id ? block.tool_use_id.slice(0, 8) : "";
4669
- const isError = block.is_error;
4670
- const inner = typeof block.content === "string" ? block.content : "";
4671
- const lines = inner.split("\n").length;
4672
- if (isError) {
4673
- summaries.push(`\u2715 error: ${inner.slice(0, 60)}`);
4674
- } else if (lines > 3) {
4675
- summaries.push(`\u2713 ${lines} lines`);
4676
- } else {
4677
- summaries.push(`\u2713 ${inner.slice(0, 80)}`);
4678
- }
4679
- } else if (block?.type === "text" && typeof block.text === "string") {
4680
- summaries.push(block.text.split("\n")[0]?.slice(0, 80) ?? "");
4681
- }
4682
- }
4683
- return summaries.join(" ") || "(result)";
4684
- }
4685
- function formatAgentOutput(raw) {
4686
- const detail = raw.length > MAX_DETAIL_LEN ? raw.slice(0, MAX_DETAIL_LEN) + "\u2026" : raw;
4687
- if (LIFECYCLE_TAG_RE.test(raw.trim())) {
4688
- return { summary: raw.trim(), detail };
4689
- }
4690
- try {
4691
- const parsed = JSON.parse(raw);
4692
- if (parsed.type === "message" && parsed.role === "assistant") {
4693
- const text = extractTextFromContent(parsed.content);
4694
- if (text) return { summary: text.slice(0, 200), detail };
4695
- return { summary: "\u{1F4AC} (assistant message)", detail };
4696
- }
4697
- if (parsed.type === "assistant" || parsed.role === "assistant") {
4698
- const content = parsed.message?.content ?? parsed.content;
4699
- const text = extractTextFromContent(content);
4700
- if (text) return { summary: text.slice(0, 200), detail };
4701
- return { summary: "\u{1F4AC} (assistant)", detail };
4702
- }
4703
- if (parsed.type === "user" || parsed.role === "user") {
4704
- const content = parsed.message?.content ?? parsed.content;
4705
- const summary = summarizeToolResult(content);
4706
- return { summary: `\u2190 ${summary.slice(0, 180)}`, detail };
4707
- }
4708
- if (parsed.type === "tool_use") {
4709
- const name = parsed.name ?? "tool";
4710
- const hint = formatToolInput(name, parsed.input);
4711
- return { summary: `\u2699 ${name}(${hint})`, detail };
4712
- }
4713
- if (parsed.type === "tool_result") {
4714
- const summary = summarizeToolResult(parsed.content);
4715
- return { summary: `\u2190 ${summary.slice(0, 180)}`, detail };
4716
- }
4717
- if (parsed.type === "result") {
4718
- const text = typeof parsed.result === "string" ? parsed.result : null;
4719
- return { summary: text ? `\u2713 ${text.slice(0, 180)}` : "\u2713 Agent finished", detail };
4720
- }
4721
- if (parsed.type === "rate_limit_event") {
4722
- return { summary: `\u23F3 Rate limited (${parsed.rate_limit_info?.rateLimitType ?? "unknown"})`, detail };
4723
- }
4724
- if (parsed.subtype) {
4725
- if (parsed.message) {
4726
- const content = parsed.message.content ?? parsed.message;
4727
- const text = extractTextFromContent(content);
4728
- if (text) return { summary: text.slice(0, 200), detail };
4729
- }
4730
- return { summary: `[${parsed.subtype}]`, detail };
4731
- }
4732
- if (parsed.content) {
4733
- const text = extractTextFromContent(parsed.content);
4734
- if (text) return { summary: text.slice(0, 200), detail };
4735
- }
4736
- if (parsed.type) return { summary: `[${parsed.type}]`, detail };
4737
- return { summary: raw.slice(0, 150), detail };
4738
- } catch {
4739
- return { summary: extractSummaryFromTruncated(raw), detail };
4740
- }
4741
- }
4742
- function extractSummaryFromTruncated(raw) {
4743
- const subtypeMatch = raw.match(/"subtype"\s*:\s*"([^"]+)"/);
4744
- if (subtypeMatch) return `[${subtypeMatch[1]}]`;
4745
- const typeMatch = raw.match(/"type"\s*:\s*"([^"]+)"/);
4746
- const roleMatch = raw.match(/"role"\s*:\s*"([^"]+)"/);
4747
- const type = typeMatch?.[1];
4748
- const role = roleMatch?.[1];
4749
- if (!type && !role) return raw.slice(0, 200);
4750
- if (type === "assistant" || type === "message" || role === "assistant") {
4751
- const textMatch = raw.match(/"text"\s*:\s*"((?:[^"\\]|\\.)*)"/);
4752
- if (textMatch) {
4753
- try {
4754
- return JSON.parse(`"${textMatch[1]}"`).slice(0, 200);
4755
- } catch {
4756
- }
4757
- }
4758
- return "\u{1F4AC} (assistant)";
4759
- }
4760
- if (type === "user" || type === "tool_result" || role === "user") {
4761
- return "\u2190 (tool result)";
4762
- }
4763
- if (type === "tool_use") {
4764
- const nameMatch = raw.match(/"name"\s*:\s*"([^"]+)"/);
4765
- return `\u2699 ${nameMatch?.[1] ?? "tool"}()`;
4766
- }
4767
- if (type === "result") {
4768
- const resultMatch = raw.match(/"result"\s*:\s*"((?:[^"\\]|\\.)*)"/);
4769
- if (resultMatch) {
4770
- try {
4771
- return `\u2713 ${JSON.parse(`"${resultMatch[1]}"`).slice(0, 180)}`;
4772
- } catch {
4773
- }
4774
- }
4775
- return "\u2713 Agent finished";
4776
- }
4777
- if (type === "rate_limit_event") return "\u23F3 Rate limited";
4778
- return `[${type ?? role}]`;
4779
- }
4780
- function formatEvent(event, addMsg, runIdToAgentId, runIdToTaskId) {
4781
- const resolveTask = (runId) => runIdToTaskId?.get(runId);
4782
- switch (event.type) {
4783
- case "agent:started":
4784
- addMsg(
4785
- `Started task`,
4786
- tuiColors.green,
4787
- { agentId: event.agentId, taskId: event.taskId, msgType: "lifecycle" }
4788
- );
4789
- break;
4790
- case "agent:output": {
4791
- const { summary, detail } = formatAgentOutput(event.data);
4792
- const cls = classifyAgentSummary(summary);
4793
- addMsg(
4794
- summary,
4795
- cls.color,
4796
- { agentId: event.agentId, taskId: resolveTask(event.runId), detail, msgType: cls.msgType }
4797
- );
4798
- break;
4799
- }
4800
- case "agent:file_changed":
4801
- addMsg(
4802
- `${event.path}`,
4803
- tuiColors.purple,
4804
- { agentId: event.agentId, taskId: resolveTask(event.runId), msgType: "file" }
4805
- );
4806
- break;
4807
- case "agent:completed":
4808
- addMsg(
4809
- event.success ? "Completed successfully" : "Failed",
4810
- event.success ? tuiColors.green : tuiColors.red,
4811
- { agentId: event.agentId, taskId: resolveTask(event.runId), msgType: "lifecycle" }
4812
- );
4813
- break;
4814
- case "agent:error":
4815
- addMsg(
4816
- `${event.error.slice(0, 150)}`,
4817
- tuiColors.red,
4818
- { agentId: event.agentId, taskId: resolveTask(event.runId), detail: event.error, msgType: "error" }
4819
- );
4820
- break;
4821
- case "task:status_changed":
4822
- addMsg(
4823
- `${event.from} \u2192 ${event.to}`,
4824
- tuiColors.cyan,
4825
- { taskId: event.taskId, msgType: "system" }
4826
- );
4827
- break;
4828
- case "task:assigned":
4829
- addMsg(
4830
- `Assigned \u2192 ${event.agentId}`,
4831
- tuiColors.cyan,
4832
- { taskId: event.taskId, msgType: "system" }
4833
- );
4834
- break;
4835
- case "task:created":
4836
- addMsg(
4837
- `Created: ${event.task.title}`,
4838
- tuiColors.amber,
4839
- { taskId: event.task.id, msgType: "system" }
4840
- );
4841
- break;
4842
- case "run:retry":
4843
- addMsg(
4844
- `Retry #${event.attempt} (${Math.round(event.delay_ms / 1e3)}s delay)`,
4845
- tuiColors.yellow,
4846
- { agentId: runIdToAgentId?.get(event.runId), taskId: resolveTask(event.runId), msgType: "lifecycle" }
4847
- );
4848
- break;
4849
- case "orchestrator:tick":
4850
- if (event.running > 0 || event.queued > 0) {
4851
- addMsg(`${event.running} running \xB7 ${event.queued} queued`, tuiColors.ghost, { msgType: "system" });
4852
- }
4853
- break;
4854
- case "orchestrator:stall_detected":
4855
- addMsg(
4856
- `Stall detected`,
4857
- tuiColors.yellow,
4858
- { agentId: runIdToAgentId?.get(event.runId), taskId: resolveTask(event.runId), msgType: "error" }
4859
- );
4860
- break;
4861
- }
4862
- }
4863
-
4864
- export { App };