@loompad/cli 0.1.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 (177) hide show
  1. package/ARCHITECTURE.md +211 -0
  2. package/CHANGELOG.md +470 -0
  3. package/LICENSE +21 -0
  4. package/README.md +630 -0
  5. package/dist/adapters/base.d.ts +71 -0
  6. package/dist/adapters/base.js +187 -0
  7. package/dist/adapters/base.js.map +1 -0
  8. package/dist/adapters/bridges/antigravity.d.ts +53 -0
  9. package/dist/adapters/bridges/antigravity.js +106 -0
  10. package/dist/adapters/bridges/antigravity.js.map +1 -0
  11. package/dist/adapters/bridges/cdp.d.ts +82 -0
  12. package/dist/adapters/bridges/cdp.js +222 -0
  13. package/dist/adapters/bridges/cdp.js.map +1 -0
  14. package/dist/adapters/bridges/gui-chat.d.ts +113 -0
  15. package/dist/adapters/bridges/gui-chat.js +498 -0
  16. package/dist/adapters/bridges/gui-chat.js.map +1 -0
  17. package/dist/adapters/bridges/kiro.d.ts +19 -0
  18. package/dist/adapters/bridges/kiro.js +22 -0
  19. package/dist/adapters/bridges/kiro.js.map +1 -0
  20. package/dist/adapters/bridges/profiles.d.ts +82 -0
  21. package/dist/adapters/bridges/profiles.js +125 -0
  22. package/dist/adapters/bridges/profiles.js.map +1 -0
  23. package/dist/adapters/claude-code.d.ts +26 -0
  24. package/dist/adapters/claude-code.js +209 -0
  25. package/dist/adapters/claude-code.js.map +1 -0
  26. package/dist/adapters/codex.d.ts +51 -0
  27. package/dist/adapters/codex.js +291 -0
  28. package/dist/adapters/codex.js.map +1 -0
  29. package/dist/adapters/echo.d.ts +20 -0
  30. package/dist/adapters/echo.js +79 -0
  31. package/dist/adapters/echo.js.map +1 -0
  32. package/dist/adapters/grok.d.ts +61 -0
  33. package/dist/adapters/grok.js +217 -0
  34. package/dist/adapters/grok.js.map +1 -0
  35. package/dist/adapters/index.d.ts +9 -0
  36. package/dist/adapters/index.js +33 -0
  37. package/dist/adapters/index.js.map +1 -0
  38. package/dist/adapters/opencode.d.ts +64 -0
  39. package/dist/adapters/opencode.js +426 -0
  40. package/dist/adapters/opencode.js.map +1 -0
  41. package/dist/cli/common.d.ts +11 -0
  42. package/dist/cli/common.js +40 -0
  43. package/dist/cli/common.js.map +1 -0
  44. package/dist/cli/doctor.d.ts +12 -0
  45. package/dist/cli/doctor.js +181 -0
  46. package/dist/cli/doctor.js.map +1 -0
  47. package/dist/cli/index.d.ts +8 -0
  48. package/dist/cli/index.js +641 -0
  49. package/dist/cli/index.js.map +1 -0
  50. package/dist/cli/tui-model.d.ts +73 -0
  51. package/dist/cli/tui-model.js +312 -0
  52. package/dist/cli/tui-model.js.map +1 -0
  53. package/dist/cli/tui.d.ts +15 -0
  54. package/dist/cli/tui.js +451 -0
  55. package/dist/cli/tui.js.map +1 -0
  56. package/dist/cli/ui.d.ts +10 -0
  57. package/dist/cli/ui.js +135 -0
  58. package/dist/cli/ui.js.map +1 -0
  59. package/dist/core/ades.d.ts +79 -0
  60. package/dist/core/ades.js +130 -0
  61. package/dist/core/ades.js.map +1 -0
  62. package/dist/core/baton.d.ts +37 -0
  63. package/dist/core/baton.js +92 -0
  64. package/dist/core/baton.js.map +1 -0
  65. package/dist/core/brain-extract.d.ts +111 -0
  66. package/dist/core/brain-extract.js +248 -0
  67. package/dist/core/brain-extract.js.map +1 -0
  68. package/dist/core/brain-index.d.ts +128 -0
  69. package/dist/core/brain-index.js +338 -0
  70. package/dist/core/brain-index.js.map +1 -0
  71. package/dist/core/brain.d.ts +214 -0
  72. package/dist/core/brain.js +425 -0
  73. package/dist/core/brain.js.map +1 -0
  74. package/dist/core/claude-cli.d.ts +12 -0
  75. package/dist/core/claude-cli.js +47 -0
  76. package/dist/core/claude-cli.js.map +1 -0
  77. package/dist/core/distill.d.ts +19 -0
  78. package/dist/core/distill.js +88 -0
  79. package/dist/core/distill.js.map +1 -0
  80. package/dist/core/eventlog.d.ts +43 -0
  81. package/dist/core/eventlog.js +211 -0
  82. package/dist/core/eventlog.js.map +1 -0
  83. package/dist/core/git.d.ts +203 -0
  84. package/dist/core/git.js +460 -0
  85. package/dist/core/git.js.map +1 -0
  86. package/dist/core/logbook.d.ts +59 -0
  87. package/dist/core/logbook.js +118 -0
  88. package/dist/core/logbook.js.map +1 -0
  89. package/dist/core/memory.d.ts +34 -0
  90. package/dist/core/memory.js +124 -0
  91. package/dist/core/memory.js.map +1 -0
  92. package/dist/core/notify.d.ts +11 -0
  93. package/dist/core/notify.js +30 -0
  94. package/dist/core/notify.js.map +1 -0
  95. package/dist/core/projection.d.ts +20 -0
  96. package/dist/core/projection.js +96 -0
  97. package/dist/core/projection.js.map +1 -0
  98. package/dist/core/registry.d.ts +76 -0
  99. package/dist/core/registry.js +147 -0
  100. package/dist/core/registry.js.map +1 -0
  101. package/dist/core/router.d.ts +42 -0
  102. package/dist/core/router.js +99 -0
  103. package/dist/core/router.js.map +1 -0
  104. package/dist/core/routes.d.ts +93 -0
  105. package/dist/core/routes.js +399 -0
  106. package/dist/core/routes.js.map +1 -0
  107. package/dist/core/search.d.ts +63 -0
  108. package/dist/core/search.js +0 -0
  109. package/dist/core/search.js.map +1 -0
  110. package/dist/core/setup.d.ts +70 -0
  111. package/dist/core/setup.js +250 -0
  112. package/dist/core/setup.js.map +1 -0
  113. package/dist/core/suggestions.d.ts +15 -0
  114. package/dist/core/suggestions.js +38 -0
  115. package/dist/core/suggestions.js.map +1 -0
  116. package/dist/core/worktree.d.ts +32 -0
  117. package/dist/core/worktree.js +83 -0
  118. package/dist/core/worktree.js.map +1 -0
  119. package/dist/daemon/app-page.d.ts +32 -0
  120. package/dist/daemon/app-page.js +6363 -0
  121. package/dist/daemon/app-page.js.map +1 -0
  122. package/dist/daemon/auth.d.ts +51 -0
  123. package/dist/daemon/auth.js +135 -0
  124. package/dist/daemon/auth.js.map +1 -0
  125. package/dist/daemon/board.d.ts +117 -0
  126. package/dist/daemon/board.js +202 -0
  127. package/dist/daemon/board.js.map +1 -0
  128. package/dist/daemon/brand-icons.d.ts +29 -0
  129. package/dist/daemon/brand-icons.js +44 -0
  130. package/dist/daemon/brand-icons.js.map +1 -0
  131. package/dist/daemon/client.d.ts +127 -0
  132. package/dist/daemon/client.js +237 -0
  133. package/dist/daemon/client.js.map +1 -0
  134. package/dist/daemon/geist-font.d.ts +10 -0
  135. package/dist/daemon/geist-font.js +783 -0
  136. package/dist/daemon/geist-font.js.map +1 -0
  137. package/dist/daemon/guards.d.ts +18 -0
  138. package/dist/daemon/guards.js +40 -0
  139. package/dist/daemon/guards.js.map +1 -0
  140. package/dist/daemon/linear.d.ts +59 -0
  141. package/dist/daemon/linear.js +117 -0
  142. package/dist/daemon/linear.js.map +1 -0
  143. package/dist/daemon/push.d.ts +22 -0
  144. package/dist/daemon/push.js +58 -0
  145. package/dist/daemon/push.js.map +1 -0
  146. package/dist/daemon/runtime.d.ts +299 -0
  147. package/dist/daemon/runtime.js +1016 -0
  148. package/dist/daemon/runtime.js.map +1 -0
  149. package/dist/daemon/server.d.ts +125 -0
  150. package/dist/daemon/server.js +1728 -0
  151. package/dist/daemon/server.js.map +1 -0
  152. package/dist/daemon/tasks.d.ts +153 -0
  153. package/dist/daemon/tasks.js +264 -0
  154. package/dist/daemon/tasks.js.map +1 -0
  155. package/dist/daemon/terminals.d.ts +82 -0
  156. package/dist/daemon/terminals.js +372 -0
  157. package/dist/daemon/terminals.js.map +1 -0
  158. package/dist/index.d.ts +20 -0
  159. package/dist/index.js +20 -0
  160. package/dist/index.js.map +1 -0
  161. package/dist/sdk.d.ts +17 -0
  162. package/dist/sdk.js +17 -0
  163. package/dist/sdk.js.map +1 -0
  164. package/dist/types.d.ts +236 -0
  165. package/dist/types.js +18 -0
  166. package/dist/types.js.map +1 -0
  167. package/docs/SETUP.md +265 -0
  168. package/docs/adapters.md +132 -0
  169. package/docs/design-system.md +245 -0
  170. package/docs/img/board-linear.png +0 -0
  171. package/docs/img/board-projects.png +0 -0
  172. package/docs/img/brain.png +0 -0
  173. package/docs/img/settings.png +0 -0
  174. package/docs/img/workspace.png +0 -0
  175. package/docs/integration-notes.md +76 -0
  176. package/docs/proposals/brain.md +262 -0
  177. package/package.json +84 -0
@@ -0,0 +1,451 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * The Loom TUI — the default face of `loom`. A tabbed, full-screen workspace
4
+ * over every agent in the project:
5
+ *
6
+ * Thread — the conversation, streamed; tab shifts the active agent, the
7
+ * handoff happens when you send.
8
+ * Board — agents, your cards, issues and PRs, in four columns.
9
+ * Brain — the memory the project has learned, grouped by kind.
10
+ * Diff — the working tree: changed files and a colourised patch.
11
+ *
12
+ * shift+tab cycles the views; ctrl+p is the command palette; /route runs a
13
+ * pipeline across agents; /pair drops a QR to pair your phone — all without
14
+ * leaving the screen.
15
+ */
16
+ import { useApp, useInput, useStdout, render, Box, Text } from "ink";
17
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
18
+ import pc from "picocolors";
19
+ import qrcode from "qrcode-terminal";
20
+ import { readProjectConfig } from "../core/registry.js";
21
+ import { DaemonError, ensureDaemon } from "../daemon/client.js";
22
+ import { resolveCurrentProject, NoProjectError, currentProjectDir } from "./common.js";
23
+ import { HELP_LINES, SPINNER_FRAMES, cycleAgent, cycleView, filterPalette, formatBoard, formatBrain, formatDiff, logoLines, paletteItems, parseSlash, renderInput, renderTabs, switchableAgents, VIEWS, } from "./tui-model.js";
24
+ import { formatAgentRow, formatEvent } from "./ui.js";
25
+ const PLACEHOLDER = 'Ask anything… "/route ship: add dark mode"';
26
+ function App({ client, initial }) {
27
+ const { exit } = useApp();
28
+ const { stdout } = useStdout();
29
+ // Terminal size, kept live so the viewport tracks a resize.
30
+ const [dims, setDims] = useState({ cols: stdout?.columns ?? 80, rows: stdout?.rows ?? 24 });
31
+ useEffect(() => {
32
+ if (!stdout)
33
+ return;
34
+ const onResize = () => setDims({ cols: stdout.columns ?? 80, rows: stdout.rows ?? 24 });
35
+ stdout.on("resize", onResize);
36
+ return () => void stdout.off("resize", onResize);
37
+ }, [stdout]);
38
+ const { cols, rows } = dims;
39
+ const [view, setView] = useState("thread");
40
+ const [thread, setThread] = useState(() => [...logoLines(cols), ""]);
41
+ const [board, setBoard] = useState(null);
42
+ const [brain, setBrain] = useState(null);
43
+ const [diff, setDiff] = useState(null);
44
+ const [viewErr, setViewErr] = useState(null);
45
+ const [scroll, setScroll] = useState(0);
46
+ const [project, setProject] = useState(initial);
47
+ const [selected, setSelected] = useState(initial.holder ?? switchableAgents(initial.agents)[0]?.id ?? null);
48
+ const [value, setValue] = useState("");
49
+ const [cursor, setCursor] = useState(0);
50
+ const [notice, setNotice] = useState(null);
51
+ const [frame, setFrame] = useState(0);
52
+ const [palette, setPalette] = useState({
53
+ open: false,
54
+ query: "",
55
+ index: 0,
56
+ });
57
+ const lastId = useRef(0);
58
+ const history = useRef([]);
59
+ const histIdx = useRef(-1);
60
+ const routeNames = useRef([]);
61
+ useEffect(() => {
62
+ const dir = currentProjectDir();
63
+ routeNames.current = Object.keys((dir && readProjectConfig(dir)?.routes) || {});
64
+ }, []);
65
+ const push = useCallback((...added) => {
66
+ const real = added.filter((l) => l !== null && l !== undefined);
67
+ if (real.length)
68
+ setThread((prev) => [...prev, ...real]);
69
+ }, []);
70
+ // Recent history, then live events over the websocket.
71
+ useEffect(() => {
72
+ let closed = false;
73
+ void client
74
+ .events(project.id, undefined, 30)
75
+ .then(({ events }) => {
76
+ if (closed)
77
+ return;
78
+ lastId.current = events[events.length - 1]?.id ?? 0;
79
+ push(...events.map(formatEvent));
80
+ })
81
+ .catch(() => { });
82
+ const unsubscribe = client.subscribe((pid, e) => {
83
+ if (pid !== project.id || e.id <= lastId.current)
84
+ return;
85
+ lastId.current = e.id;
86
+ push(formatEvent(e));
87
+ }, project.id);
88
+ return () => {
89
+ closed = true;
90
+ unsubscribe();
91
+ };
92
+ // eslint-disable-next-line react-hooks/exhaustive-deps
93
+ }, []);
94
+ // Light status poll: holder, busy flags, route progress.
95
+ useEffect(() => {
96
+ const timer = setInterval(() => {
97
+ void client
98
+ .project(project.id)
99
+ .then(({ project: fresh }) => setProject(fresh))
100
+ .catch(() => { });
101
+ }, 1500);
102
+ return () => clearInterval(timer);
103
+ // eslint-disable-next-line react-hooks/exhaustive-deps
104
+ }, []);
105
+ // Load the active view's data (and keep it fresh) — the thread streams itself.
106
+ useEffect(() => {
107
+ if (view === "thread")
108
+ return;
109
+ let alive = true;
110
+ const load = () => {
111
+ const done = () => alive && setViewErr(null);
112
+ const fail = (e) => alive && setViewErr(e instanceof Error ? e.message : String(e));
113
+ if (view === "board")
114
+ client.board(project.id).then((b) => { if (alive) {
115
+ setBoard(b);
116
+ done();
117
+ } }).catch(fail);
118
+ else if (view === "brain")
119
+ client.brain(project.id, { limit: 200 }).then((r) => { if (alive) {
120
+ setBrain(r);
121
+ done();
122
+ } }).catch(fail);
123
+ else
124
+ client.tree(project.id).then((r) => { if (alive) {
125
+ setDiff(r.tree);
126
+ done();
127
+ } }).catch(fail);
128
+ };
129
+ load();
130
+ const timer = setInterval(load, 2500);
131
+ return () => {
132
+ alive = false;
133
+ clearInterval(timer);
134
+ };
135
+ }, [view, project.id, client]);
136
+ // A fresh view starts at its natural edge (thread at the newest, others at the top).
137
+ useEffect(() => setScroll(0), [view]);
138
+ const busyAgent = project.agents.find((a) => a.busy);
139
+ // Spinner while anyone is working.
140
+ useEffect(() => {
141
+ if (!busyAgent)
142
+ return;
143
+ const timer = setInterval(() => setFrame((f) => f + 1), 120);
144
+ return () => clearInterval(timer);
145
+ }, [Boolean(busyAgent)]);
146
+ // Smoke hook: render one real frame, then leave (used by CI/smoke tests).
147
+ useEffect(() => {
148
+ if (!process.env.LOOM_TUI_SMOKE)
149
+ return;
150
+ const timer = setTimeout(() => exit(), 900);
151
+ return () => clearTimeout(timer);
152
+ // eslint-disable-next-line react-hooks/exhaustive-deps
153
+ }, []);
154
+ const runCommand = useCallback(async (text) => {
155
+ const slash = parseSlash(text);
156
+ if (!slash)
157
+ return;
158
+ const { cmd, args, rest } = slash;
159
+ // Local view jumps never touch the daemon.
160
+ if (VIEWS.includes(cmd)) {
161
+ setView(cmd);
162
+ return;
163
+ }
164
+ // Everything else produces thread output or a thread effect — show it.
165
+ setView("thread");
166
+ try {
167
+ switch (cmd) {
168
+ case "help":
169
+ push("", ...HELP_LINES, "");
170
+ break;
171
+ case "agents": {
172
+ const { project: fresh } = await client.project(project.id);
173
+ push("", ...fresh.agents.map(formatAgentRow), "");
174
+ break;
175
+ }
176
+ case "handoff": {
177
+ if (!args[0])
178
+ return setNotice("usage: /handoff <agent>");
179
+ await client.handoff(project.id, args[0]);
180
+ setSelected(args[0]);
181
+ break;
182
+ }
183
+ case "route": {
184
+ if (args.length < 2)
185
+ return setNotice("usage: /route <name|a,b,c> <task>");
186
+ const [spec, ...taskWords] = args;
187
+ await client.startRoute(project.id, taskWords.join(" "), spec);
188
+ break;
189
+ }
190
+ case "routes": {
191
+ const dir = currentProjectDir();
192
+ const routes = (dir && readProjectConfig(dir)?.routes) || {};
193
+ const entries = Object.entries(routes);
194
+ push("", ...(entries.length
195
+ ? entries.map(([name, steps]) => ` ${pc.cyan(pc.bold(name))} ${steps
196
+ .map((s) => (typeof s === "string" ? s : s.step))
197
+ .join(" → ")}`)
198
+ : [pc.dim(" no named routes — add them under \"routes\" in .loom/config.json")]), "");
199
+ break;
200
+ }
201
+ case "abort":
202
+ await client.abortRoute(project.id);
203
+ break;
204
+ case "decision": {
205
+ if (!rest)
206
+ return setNotice("usage: /decision <text>");
207
+ await client.decision(project.id, rest);
208
+ setNotice("pinned to shared memory");
209
+ break;
210
+ }
211
+ case "interrupt": {
212
+ const { interrupted } = await client.interrupt(project.id);
213
+ setNotice(interrupted ? `interrupted ${interrupted}` : "nothing running");
214
+ break;
215
+ }
216
+ case "pair": {
217
+ const { token, url } = await client.newPairingToken();
218
+ const link = `${url}/app#pair=${token}`;
219
+ qrcode.generate(link, { small: true }, (qr) => {
220
+ const warn = /127\.0\.0\.1|localhost/.test(url)
221
+ ? [pc.yellow(" ⚠ daemon is on localhost — phones can't reach this QR. Run: loom up --restart --tailnet")]
222
+ : [];
223
+ push("", ...qr.split("\n"), pc.bold(` ${link}`), pc.dim(" scan with your phone camera · single use · 10 min"), ...warn, "");
224
+ });
225
+ break;
226
+ }
227
+ case "quit":
228
+ case "exit":
229
+ exit();
230
+ break;
231
+ default:
232
+ setNotice(`unknown command /${cmd} — try /help`);
233
+ }
234
+ }
235
+ catch (err) {
236
+ setNotice(err instanceof Error ? err.message : String(err));
237
+ }
238
+ }, [client, project.id, push, exit]);
239
+ const onSubmit = useCallback(async () => {
240
+ const text = value.trim();
241
+ setValue("");
242
+ setCursor(0);
243
+ setNotice(null);
244
+ if (!text)
245
+ return;
246
+ history.current.push(text);
247
+ histIdx.current = history.current.length;
248
+ if (text.startsWith("/"))
249
+ return runCommand(text);
250
+ setView("thread"); // a reply is coming — watch it land
251
+ try {
252
+ if (selected && selected !== project.holder) {
253
+ push(pc.dim(` ⟶ shifting baton to ${selected}`));
254
+ await client.handoff(project.id, selected);
255
+ }
256
+ await client.send(project.id, text, selected ?? undefined);
257
+ }
258
+ catch (err) {
259
+ if (err instanceof DaemonError && err.status === 409) {
260
+ setNotice(String(err.body?.message ?? err.message));
261
+ }
262
+ else {
263
+ setNotice(err instanceof Error ? err.message : String(err));
264
+ }
265
+ }
266
+ }, [value, selected, project.holder, project.id, client, push, runCommand]);
267
+ const runPaletteItem = useCallback((item) => {
268
+ setPalette({ open: false, query: "", index: 0 });
269
+ if (!item)
270
+ return;
271
+ const action = item.action;
272
+ if (action.type === "shift") {
273
+ setSelected(action.agentId);
274
+ }
275
+ else if (action.type === "insert") {
276
+ setValue(action.text);
277
+ setCursor(action.text.length);
278
+ }
279
+ else if (action.type === "view") {
280
+ setView(action.view);
281
+ }
282
+ else {
283
+ void runCommand(`/${action.cmd}`);
284
+ }
285
+ }, [runCommand]);
286
+ // Viewport geometry: chrome is the tab bar + input box + hints + status.
287
+ const CHROME = 9;
288
+ const vh = Math.max(6, rows - CHROME);
289
+ const viewLines = useMemo(() => {
290
+ if (view === "thread")
291
+ return thread;
292
+ if (viewErr)
293
+ return ["", pc.red(` ${viewErr}`)];
294
+ if (view === "board")
295
+ return board ? formatBoard(board, cols) : [pc.dim(" loading board…")];
296
+ if (view === "brain")
297
+ return brain ? formatBrain(brain.memories, brain.stats, cols) : [pc.dim(" loading brain…")];
298
+ return diff ? formatDiff(diff, cols) : [pc.dim(" loading diff…")];
299
+ }, [view, thread, board, brain, diff, viewErr, cols]);
300
+ const total = viewLines.length;
301
+ const maxScroll = Math.max(0, total - vh);
302
+ const clamped = Math.min(Math.max(0, scroll), maxScroll);
303
+ // Thread hangs off the bottom (newest visible); the others read from the top.
304
+ const visible = view === "thread"
305
+ ? viewLines.slice(Math.max(0, total - vh - clamped), total - clamped)
306
+ : viewLines.slice(clamped, clamped + vh);
307
+ const hiddenAbove = view === "thread" ? Math.max(0, total - vh - clamped) : clamped;
308
+ const hiddenBelow = view === "thread" ? clamped : Math.max(0, total - vh - clamped);
309
+ useInput((input, key) => {
310
+ if (key.ctrl && input === "c")
311
+ return exit();
312
+ // ctrl+p — command palette
313
+ if (key.ctrl && input === "p") {
314
+ setPalette((p) => ({ open: !p.open, query: "", index: 0 }));
315
+ return;
316
+ }
317
+ if (palette.open) {
318
+ const matches = filterPalette(paletteItems(project.agents, routeNames.current, selected), palette.query);
319
+ if (key.escape)
320
+ return setPalette({ open: false, query: "", index: 0 });
321
+ if (key.return)
322
+ return runPaletteItem(matches[Math.min(palette.index, matches.length - 1)]);
323
+ if (key.upArrow)
324
+ return setPalette((p) => ({ ...p, index: Math.max(0, p.index - 1) }));
325
+ if (key.downArrow)
326
+ return setPalette((p) => ({ ...p, index: Math.min(Math.max(matches.length - 1, 0), p.index + 1) }));
327
+ if (key.tab)
328
+ return;
329
+ if (key.backspace || key.delete) {
330
+ setPalette((p) => ({ ...p, query: p.query.slice(0, -1), index: 0 }));
331
+ return;
332
+ }
333
+ if (input && !key.ctrl && !key.meta) {
334
+ setPalette((p) => ({ ...p, query: p.query + input, index: 0 }));
335
+ }
336
+ return;
337
+ }
338
+ // shift+tab cycles views; tab cycles the active agent.
339
+ if (key.tab && key.shift)
340
+ return setView((v) => cycleView(v, 1));
341
+ if (key.tab) {
342
+ setSelected((s) => cycleAgent(project.agents, s, 1) ?? s);
343
+ return;
344
+ }
345
+ // Scroll the viewport.
346
+ if (key.pageUp)
347
+ return setScroll((s) => Math.min(maxScroll, s + Math.max(1, vh - 2)));
348
+ if (key.pageDown)
349
+ return setScroll((s) => Math.max(0, s - Math.max(1, vh - 2)));
350
+ // esc — leave a side view first, otherwise interrupt the running turn.
351
+ if (key.escape) {
352
+ if (view !== "thread")
353
+ return setView("thread");
354
+ void client
355
+ .interrupt(project.id)
356
+ .then(({ interrupted }) => setNotice(interrupted ? `interrupted ${interrupted}` : "nothing running"))
357
+ .catch(() => { });
358
+ return;
359
+ }
360
+ if (key.return)
361
+ return void onSubmit();
362
+ if (key.upArrow) {
363
+ if (!history.current.length)
364
+ return;
365
+ histIdx.current = Math.max(0, histIdx.current - 1);
366
+ const recalled = history.current[histIdx.current] ?? "";
367
+ setValue(recalled);
368
+ setCursor(recalled.length);
369
+ return;
370
+ }
371
+ if (key.downArrow) {
372
+ histIdx.current = Math.min(history.current.length, histIdx.current + 1);
373
+ const recalled = history.current[histIdx.current] ?? "";
374
+ setValue(recalled);
375
+ setCursor(recalled.length);
376
+ return;
377
+ }
378
+ if (key.leftArrow)
379
+ return setCursor((c) => Math.max(0, c - 1));
380
+ if (key.rightArrow)
381
+ return setCursor((c) => Math.min(value.length, c + 1));
382
+ if (key.ctrl && input === "a")
383
+ return setCursor(0);
384
+ if (key.ctrl && input === "e")
385
+ return setCursor(value.length);
386
+ if (key.ctrl && input === "u") {
387
+ setValue("");
388
+ setCursor(0);
389
+ return;
390
+ }
391
+ if (key.backspace || key.delete) {
392
+ if (cursor > 0) {
393
+ setValue((v) => v.slice(0, cursor - 1) + v.slice(cursor));
394
+ setCursor((c) => c - 1);
395
+ }
396
+ return;
397
+ }
398
+ if (input && !key.ctrl && !key.meta) {
399
+ setValue((v) => v.slice(0, cursor) + input + v.slice(cursor));
400
+ setCursor((c) => c + input.length);
401
+ }
402
+ });
403
+ const selectedStatus = project.agents.find((a) => a.id === selected);
404
+ const holdsBaton = selected !== null && project.holder === selected;
405
+ const route = project.route;
406
+ const routeActive = route && (route.status === "running" || route.status === "waiting_human");
407
+ const routeBadge = routeActive
408
+ ? ` ${pc.cyan(`➤ ${route.name ?? "route"} ${route.current + 1}/${route.steps.length}${route.status === "waiting_human" ? " ⏸" : ""}`)}`
409
+ : "";
410
+ const spinner = busyAgent
411
+ ? `${pc.yellow(SPINNER_FRAMES[frame % SPINNER_FRAMES.length])} ${pc.dim(`${busyAgent.id} working…`)}`
412
+ : "";
413
+ const paletteMatches = palette.open
414
+ ? filterPalette(paletteItems(project.agents, routeNames.current, selected), palette.query)
415
+ : [];
416
+ const scrollHint = hiddenAbove > 0 || hiddenBelow > 0
417
+ ? pc.dim(`${hiddenAbove > 0 ? `↑${hiddenAbove}` : " "} ${hiddenBelow > 0 ? `↓${hiddenBelow}` : ""} pgup/pgdn`)
418
+ : "";
419
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { justifyContent: "space-between", children: [_jsx(Text, { children: renderTabs(view) }), _jsx(Text, { children: scrollHint || pc.dim("loom 0.1.0 ") })] }), _jsx(Box, { height: vh, flexDirection: "column", children: visible.map((line, i) => (_jsx(Text, { wrap: "truncate-end", children: line || " " }, i))) }), notice ? _jsx(Text, { color: "yellow", children: ` ${notice}` }) : null, palette.open ? (_jsxs(Box, { borderStyle: "round", borderColor: "cyan", paddingX: 1, flexDirection: "column", children: [_jsxs(Text, { children: [pc.cyan("⌘ "), renderInput(palette.query, palette.query.length, "type to filter commands…")] }), paletteMatches.slice(0, 8).map((item, i) => {
420
+ const active = i === Math.min(palette.index, paletteMatches.length - 1);
421
+ const label = `${item.label}${item.hint ? pc.dim(` ${item.hint}`) : ""}`;
422
+ return _jsx(Text, { children: active ? pc.cyan(`› ${label}`) : ` ${label}` }, item.id);
423
+ }), !paletteMatches.length ? _jsx(Text, { dimColor: true, children: " no matches" }) : null] })) : null, _jsxs(Box, { borderStyle: "round", borderDimColor: true, paddingX: 1, flexDirection: "column", children: [_jsxs(Text, { children: [pc.cyan("› "), renderInput(value, cursor, PLACEHOLDER)] }), _jsxs(Text, { dimColor: true, children: [selected ?? "no agent", selectedStatus ? ` · ${selectedStatus.role}` : "", holdsBaton ? pc.magenta(" ⟵ baton") : pc.dim(" (send = shift baton)"), spinner ? ` ${spinner}` : ""] })] }), _jsx(Box, { justifyContent: "space-between", children: _jsx(Text, { dimColor: true, children: " shift+tab view · tab agent · ctrl+p palette · esc back/interrupt · ctrl+c quit" }) }), _jsxs(Box, { justifyContent: "space-between", children: [_jsxs(Text, { children: [_jsx(Text, { dimColor: true, children: ` ~ ${project.name} · baton ${project.holder ?? "—"}` +
424
+ (project.costUsd && project.costUsd > 0
425
+ ? ` · ${project.costUsd >= 0.01 ? `$${project.costUsd.toFixed(2)}` : `$${project.costUsd.toFixed(4)}`}`
426
+ : "") }), _jsx(Text, { children: routeBadge })] }), _jsx(Text, { dimColor: true, children: project.needsInput ? pc.yellow("● needs input ") : "" })] })] }));
427
+ }
428
+ export async function runTui() {
429
+ const client = await ensureDaemon();
430
+ let project;
431
+ try {
432
+ project = await resolveCurrentProject(client);
433
+ }
434
+ catch (err) {
435
+ if (err instanceof NoProjectError) {
436
+ for (const line of logoLines(process.stdout.columns ?? 80))
437
+ console.log(line);
438
+ console.log();
439
+ console.log(pc.dim(" no Loom project here — run ") + pc.bold("loom init") + pc.dim(" first"));
440
+ process.exitCode = 1;
441
+ return;
442
+ }
443
+ throw err;
444
+ }
445
+ const { waitUntilExit } = render(_jsx(App, { client: client, initial: project }), {
446
+ exitOnCtrlC: false,
447
+ });
448
+ await waitUntilExit();
449
+ process.exit(0);
450
+ }
451
+ //# sourceMappingURL=tui.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tui.js","sourceRoot":"","sources":["../../src/cli/tui.tsx"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AACrE,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAC1E,OAAO,EAAE,MAAM,YAAY,CAAC;AAC5B,OAAO,MAAM,MAAM,iBAAiB,CAAC;AAIrC,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAgB,WAAW,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAC9E,OAAO,EAAE,qBAAqB,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACvF,OAAO,EACL,UAAU,EACV,cAAc,EACd,UAAU,EACV,SAAS,EACT,aAAa,EACb,WAAW,EACX,WAAW,EACX,UAAU,EACV,SAAS,EACT,YAAY,EACZ,UAAU,EACV,WAAW,EACX,UAAU,EACV,gBAAgB,EAChB,KAAK,GAGN,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAEtD,MAAM,WAAW,GAAG,4CAA4C,CAAC;AAejE,SAAS,GAAG,CAAC,EAAE,MAAM,EAAE,OAAO,EAAY;IACxC,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,EAAE,CAAC;IAC1B,MAAM,EAAE,MAAM,EAAE,GAAG,SAAS,EAAE,CAAC;IAE/B,4DAA4D;IAC5D,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,IAAI,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,IAAI,EAAE,EAAE,CAAC,CAAC;IAC5F,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,MAAM;YAAE,OAAO;QACpB,MAAM,QAAQ,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,OAAO,IAAI,EAAE,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,IAAI,EAAE,EAAE,CAAC,CAAC;QACxF,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;QAC9B,OAAO,GAAG,EAAE,CAAC,KAAK,MAAM,CAAC,GAAG,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IACnD,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;IACb,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC;IAE5B,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAW,QAAQ,CAAC,CAAC;IACrD,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,QAAQ,CAAW,GAAG,EAAE,CAAC,CAAC,GAAG,SAAS,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;IAC/E,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAmB,IAAI,CAAC,CAAC;IAC3D,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAA0F,IAAI,CAAC,CAAC;IAClI,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAmB,IAAI,CAAC,CAAC;IACzD,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAgB,IAAI,CAAC,CAAC;IAC5D,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;IAExC,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;IAChD,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,QAAQ,CACtC,OAAO,CAAC,MAAM,IAAI,gBAAgB,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,IAAI,IAAI,CAClE,CAAC;IACF,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAC;IACvC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;IACxC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,QAAQ,CAAgB,IAAI,CAAC,CAAC;IAC1D,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;IACtC,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAkD;QACtF,IAAI,EAAE,KAAK;QACX,KAAK,EAAE,EAAE;QACT,KAAK,EAAE,CAAC;KACT,CAAC,CAAC;IACH,MAAM,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IACzB,MAAM,OAAO,GAAG,MAAM,CAAW,EAAE,CAAC,CAAC;IACrC,MAAM,OAAO,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3B,MAAM,UAAU,GAAG,MAAM,CAAW,EAAE,CAAC,CAAC;IAExC,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,GAAG,GAAG,iBAAiB,EAAE,CAAC;QAChC,UAAU,CAAC,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,iBAAiB,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;IAClF,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,MAAM,IAAI,GAAG,WAAW,CAAC,CAAC,GAAG,KAA2B,EAAE,EAAE;QAC1D,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAe,EAAE,CAAC,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,SAAS,CAAC,CAAC;QAC7E,IAAI,IAAI,CAAC,MAAM;YAAE,SAAS,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC;IAC3D,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,uDAAuD;IACvD,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,MAAM,GAAG,KAAK,CAAC;QACnB,KAAK,MAAM;aACR,MAAM,CAAC,OAAO,CAAC,EAAE,EAAE,SAAS,EAAE,EAAE,CAAC;aACjC,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE;YACnB,IAAI,MAAM;gBAAE,OAAO;YACnB,MAAM,CAAC,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;YACpD,IAAI,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC;QACnC,CAAC,CAAC;aACD,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QACnB,MAAM,WAAW,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE;YAC9C,IAAI,GAAG,KAAK,OAAO,CAAC,EAAE,IAAI,CAAC,CAAC,EAAE,IAAI,MAAM,CAAC,OAAO;gBAAE,OAAO;YACzD,MAAM,CAAC,OAAO,GAAG,CAAC,CAAC,EAAE,CAAC;YACtB,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;QACvB,CAAC,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC;QACf,OAAO,GAAG,EAAE;YACV,MAAM,GAAG,IAAI,CAAC;YACd,WAAW,EAAE,CAAC;QAChB,CAAC,CAAC;QACF,uDAAuD;IACzD,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,yDAAyD;IACzD,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE;YAC7B,KAAK,MAAM;iBACR,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;iBACnB,IAAI,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;iBAC/C,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QACrB,CAAC,EAAE,IAAI,CAAC,CAAC;QACT,OAAO,GAAG,EAAE,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;QAClC,uDAAuD;IACzD,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,+EAA+E;IAC/E,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,IAAI,KAAK,QAAQ;YAAE,OAAO;QAC9B,IAAI,KAAK,GAAG,IAAI,CAAC;QACjB,MAAM,IAAI,GAAG,GAAG,EAAE;YAChB,MAAM,IAAI,GAAG,GAAG,EAAE,CAAC,KAAK,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC;YAC7C,MAAM,IAAI,GAAG,CAAC,CAAU,EAAE,EAAE,CAAC,KAAK,IAAI,UAAU,CAAC,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;YAC7F,IAAI,IAAI,KAAK,OAAO;gBAAE,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,GAAG,IAAI,KAAK,EAAE,CAAC;oBAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;oBAAC,IAAI,EAAE,CAAC;gBAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;iBAC3G,IAAI,IAAI,KAAK,OAAO;gBAAE,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,GAAG,IAAI,KAAK,EAAE,CAAC;oBAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;oBAAC,IAAI,EAAE,CAAC;gBAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;;gBAChI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,GAAG,IAAI,KAAK,EAAE,CAAC;oBAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;oBAAC,IAAI,EAAE,CAAC;gBAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACpG,CAAC,CAAC;QACF,IAAI,EAAE,CAAC;QACP,MAAM,KAAK,GAAG,WAAW,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QACtC,OAAO,GAAG,EAAE;YACV,KAAK,GAAG,KAAK,CAAC;YACd,aAAa,CAAC,KAAK,CAAC,CAAC;QACvB,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC;IAE/B,qFAAqF;IACrF,SAAS,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;IAEtC,MAAM,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IAErD,mCAAmC;IACnC,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,SAAS;YAAE,OAAO;QACvB,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;QAC7D,OAAO,GAAG,EAAE,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;IACpC,CAAC,EAAE,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;IAEzB,0EAA0E;IAC1E,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc;YAAE,OAAO;QACxC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,IAAI,EAAE,EAAE,GAAG,CAAC,CAAC;QAC5C,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;QACjC,uDAAuD;IACzD,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,MAAM,UAAU,GAAG,WAAW,CAC5B,KAAK,EAAE,IAAY,EAAE,EAAE;QACrB,MAAM,KAAK,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;QAC/B,IAAI,CAAC,KAAK;YAAE,OAAO;QACnB,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,KAAK,CAAC;QAClC,2CAA2C;QAC3C,IAAK,KAAkB,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YACtC,OAAO,CAAC,GAAe,CAAC,CAAC;YACzB,OAAO;QACT,CAAC;QACD,uEAAuE;QACvE,OAAO,CAAC,QAAQ,CAAC,CAAC;QAClB,IAAI,CAAC;YACH,QAAQ,GAAG,EAAE,CAAC;gBACZ,KAAK,MAAM;oBACT,IAAI,CAAC,EAAE,EAAE,GAAG,UAAU,EAAE,EAAE,CAAC,CAAC;oBAC5B,MAAM;gBACR,KAAK,QAAQ,CAAC,CAAC,CAAC;oBACd,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;oBAC5D,IAAI,CAAC,EAAE,EAAE,GAAG,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,cAAc,CAAC,EAAE,EAAE,CAAC,CAAC;oBAClD,MAAM;gBACR,CAAC;gBACD,KAAK,SAAS,CAAC,CAAC,CAAC;oBACf,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;wBAAE,OAAO,SAAS,CAAC,yBAAyB,CAAC,CAAC;oBAC1D,MAAM,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;oBAC1C,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;oBACrB,MAAM;gBACR,CAAC;gBACD,KAAK,OAAO,CAAC,CAAC,CAAC;oBACb,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC;wBAAE,OAAO,SAAS,CAAC,mCAAmC,CAAC,CAAC;oBAC3E,MAAM,CAAC,IAAI,EAAE,GAAG,SAAS,CAAC,GAAG,IAAI,CAAC;oBAClC,MAAM,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,EAAE,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,IAAK,CAAC,CAAC;oBAChE,MAAM;gBACR,CAAC;gBACD,KAAK,QAAQ,CAAC,CAAC,CAAC;oBACd,MAAM,GAAG,GAAG,iBAAiB,EAAE,CAAC;oBAChC,MAAM,MAAM,GAAG,CAAC,GAAG,IAAI,iBAAiB,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC;oBAC7D,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;oBACvC,IAAI,CACF,EAAE,EACF,GAAG,CAAC,OAAO,CAAC,MAAM;wBAChB,CAAC,CAAC,OAAO,CAAC,GAAG,CACT,CAAC,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,EAAE,CAChB,KAAK,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,KAAK;6BAClC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;6BAChD,IAAI,CAAC,KAAK,CAAC,EAAE,CACnB;wBACH,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,oEAAoE,CAAC,CAAC,CAAC,EACnF,EAAE,CACH,CAAC;oBACF,MAAM;gBACR,CAAC;gBACD,KAAK,OAAO;oBACV,MAAM,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;oBACpC,MAAM;gBACR,KAAK,UAAU,CAAC,CAAC,CAAC;oBAChB,IAAI,CAAC,IAAI;wBAAE,OAAO,SAAS,CAAC,yBAAyB,CAAC,CAAC;oBACvD,MAAM,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;oBACxC,SAAS,CAAC,yBAAyB,CAAC,CAAC;oBACrC,MAAM;gBACR,CAAC;gBACD,KAAK,WAAW,CAAC,CAAC,CAAC;oBACjB,MAAM,EAAE,WAAW,EAAE,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;oBAC3D,SAAS,CAAC,WAAW,CAAC,CAAC,CAAC,eAAe,WAAW,EAAE,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC;oBAC1E,MAAM;gBACR,CAAC;gBACD,KAAK,MAAM,CAAC,CAAC,CAAC;oBACZ,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,MAAM,MAAM,CAAC,eAAe,EAAE,CAAC;oBACtD,MAAM,IAAI,GAAG,GAAG,GAAG,aAAa,KAAK,EAAE,CAAC;oBACxC,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE;wBAC5C,MAAM,IAAI,GAAG,wBAAwB,CAAC,IAAI,CAAC,GAAG,CAAC;4BAC7C,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,2FAA2F,CAAC,CAAC;4BAC1G,CAAC,CAAC,EAAE,CAAC;wBACP,IAAI,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,qDAAqD,CAAC,EAAE,GAAG,IAAI,EAAE,EAAE,CAAC,CAAC;oBAChI,CAAC,CAAC,CAAC;oBACH,MAAM;gBACR,CAAC;gBACD,KAAK,MAAM,CAAC;gBACZ,KAAK,MAAM;oBACT,IAAI,EAAE,CAAC;oBACP,MAAM;gBACR;oBACE,SAAS,CAAC,oBAAoB,GAAG,cAAc,CAAC,CAAC;YACrD,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,SAAS,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QAC9D,CAAC;IACH,CAAC,EACD,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,CACjC,CAAC;IAEF,MAAM,QAAQ,GAAG,WAAW,CAAC,KAAK,IAAI,EAAE;QACtC,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;QAC1B,QAAQ,CAAC,EAAE,CAAC,CAAC;QACb,SAAS,CAAC,CAAC,CAAC,CAAC;QACb,SAAS,CAAC,IAAI,CAAC,CAAC;QAChB,IAAI,CAAC,IAAI;YAAE,OAAO;QAClB,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC3B,OAAO,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC;QACzC,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,OAAO,UAAU,CAAC,IAAI,CAAC,CAAC;QAClD,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,oCAAoC;QACvD,IAAI,CAAC;YACH,IAAI,QAAQ,IAAI,QAAQ,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC;gBAC5C,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,yBAAyB,QAAQ,EAAE,CAAC,CAAC,CAAC;gBAClD,MAAM,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC;YAC7C,CAAC;YACD,MAAM,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,EAAE,IAAI,EAAE,QAAQ,IAAI,SAAS,CAAC,CAAC;QAC7D,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,GAAG,YAAY,WAAW,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;gBACrD,SAAS,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,OAAO,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC;YACtD,CAAC;iBAAM,CAAC;gBACN,SAAS,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;YAC9D,CAAC;QACH,CAAC;IACH,CAAC,EAAE,CAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC;IAE5E,MAAM,cAAc,GAAG,WAAW,CAChC,CAAC,IAA6B,EAAE,EAAE;QAChC,UAAU,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;QACjD,IAAI,CAAC,IAAI;YAAE,OAAO;QAClB,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;QAC3B,IAAI,MAAM,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;YAC5B,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAC9B,CAAC;aAAM,IAAI,MAAM,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YACpC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YACtB,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAChC,CAAC;aAAM,IAAI,MAAM,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YAClC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACvB,CAAC;aAAM,CAAC;YACN,KAAK,UAAU,CAAC,IAAI,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC;QACpC,CAAC;IACH,CAAC,EACD,CAAC,UAAU,CAAC,CACb,CAAC;IAEF,yEAAyE;IACzE,MAAM,MAAM,GAAG,CAAC,CAAC;IACjB,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,GAAG,MAAM,CAAC,CAAC;IAEtC,MAAM,SAAS,GAAa,OAAO,CAAC,GAAG,EAAE;QACvC,IAAI,IAAI,KAAK,QAAQ;YAAE,OAAO,MAAM,CAAC;QACrC,IAAI,OAAO;YAAE,OAAO,CAAC,EAAE,EAAE,EAAE,CAAC,GAAG,CAAC,KAAK,OAAO,EAAE,CAAC,CAAC,CAAC;QACjD,IAAI,IAAI,KAAK,OAAO;YAAE,OAAO,KAAK,CAAC,CAAC,CAAC,WAAW,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC,CAAC;QAC7F,IAAI,IAAI,KAAK,OAAO;YAAE,OAAO,KAAK,CAAC,CAAC,CAAC,WAAW,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC,CAAC;QACnH,OAAO,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC,CAAC;IACrE,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC;IAEtD,MAAM,KAAK,GAAG,SAAS,CAAC,MAAM,CAAC;IAC/B,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,GAAG,EAAE,CAAC,CAAC;IAC1C,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,EAAE,SAAS,CAAC,CAAC;IACzD,8EAA8E;IAC9E,MAAM,OAAO,GACX,IAAI,KAAK,QAAQ;QACf,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,GAAG,EAAE,GAAG,OAAO,CAAC,EAAE,KAAK,GAAG,OAAO,CAAC;QACrE,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,OAAO,EAAE,OAAO,GAAG,EAAE,CAAC,CAAC;IAC7C,MAAM,WAAW,GAAG,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,GAAG,EAAE,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;IACpF,MAAM,WAAW,GAAG,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,GAAG,EAAE,GAAG,OAAO,CAAC,CAAC;IAEpF,QAAQ,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;QACtB,IAAI,GAAG,CAAC,IAAI,IAAI,KAAK,KAAK,GAAG;YAAE,OAAO,IAAI,EAAE,CAAC;QAE7C,2BAA2B;QAC3B,IAAI,GAAG,CAAC,IAAI,IAAI,KAAK,KAAK,GAAG,EAAE,CAAC;YAC9B,UAAU,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;YAC5D,OAAO;QACT,CAAC;QACD,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;YACjB,MAAM,OAAO,GAAG,aAAa,CAC3B,YAAY,CAAC,OAAO,CAAC,MAAM,EAAE,UAAU,CAAC,OAAO,EAAE,QAAQ,CAAC,EAC1D,OAAO,CAAC,KAAK,CACd,CAAC;YACF,IAAI,GAAG,CAAC,MAAM;gBAAE,OAAO,UAAU,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;YACxE,IAAI,GAAG,CAAC,MAAM;gBAAE,OAAO,cAAc,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;YAC5F,IAAI,GAAG,CAAC,OAAO;gBAAE,OAAO,UAAU,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;YACvF,IAAI,GAAG,CAAC,SAAS;gBACf,OAAO,UAAU,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;YACtG,IAAI,GAAG,CAAC,GAAG;gBAAE,OAAO;YACpB,IAAI,GAAG,CAAC,SAAS,IAAI,GAAG,CAAC,MAAM,EAAE,CAAC;gBAChC,UAAU,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;gBACrE,OAAO;YACT,CAAC;YACD,IAAI,KAAK,IAAI,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;gBACpC,UAAU,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,GAAG,KAAK,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;YAClE,CAAC;YACD,OAAO;QACT,CAAC;QAED,uDAAuD;QACvD,IAAI,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK;YAAE,OAAO,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QACjE,IAAI,GAAG,CAAC,GAAG,EAAE,CAAC;YACZ,WAAW,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;YAC1D,OAAO;QACT,CAAC;QAED,uBAAuB;QACvB,IAAI,GAAG,CAAC,MAAM;YAAE,OAAO,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QACtF,IAAI,GAAG,CAAC,QAAQ;YAAE,OAAO,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QAEhF,uEAAuE;QACvE,IAAI,GAAG,CAAC,MAAM,EAAE,CAAC;YACf,IAAI,IAAI,KAAK,QAAQ;gBAAE,OAAO,OAAO,CAAC,QAAQ,CAAC,CAAC;YAChD,KAAK,MAAM;iBACR,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC;iBACrB,IAAI,CAAC,CAAC,EAAE,WAAW,EAAE,EAAE,EAAE,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC,CAAC,eAAe,WAAW,EAAE,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC;iBACpG,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;YACnB,OAAO;QACT,CAAC;QAED,IAAI,GAAG,CAAC,MAAM;YAAE,OAAO,KAAK,QAAQ,EAAE,CAAC;QACvC,IAAI,GAAG,CAAC,OAAO,EAAE,CAAC;YAChB,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM;gBAAE,OAAO;YACpC,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC;YACnD,MAAM,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;YACxD,QAAQ,CAAC,QAAQ,CAAC,CAAC;YACnB,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;YAC3B,OAAO;QACT,CAAC;QACD,IAAI,GAAG,CAAC,SAAS,EAAE,CAAC;YAClB,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC;YACxE,MAAM,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;YACxD,QAAQ,CAAC,QAAQ,CAAC,CAAC;YACnB,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;YAC3B,OAAO;QACT,CAAC;QACD,IAAI,GAAG,CAAC,SAAS;YAAE,OAAO,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QAC/D,IAAI,GAAG,CAAC,UAAU;YAAE,OAAO,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QAC3E,IAAI,GAAG,CAAC,IAAI,IAAI,KAAK,KAAK,GAAG;YAAE,OAAO,SAAS,CAAC,CAAC,CAAC,CAAC;QACnD,IAAI,GAAG,CAAC,IAAI,IAAI,KAAK,KAAK,GAAG;YAAE,OAAO,SAAS,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAC9D,IAAI,GAAG,CAAC,IAAI,IAAI,KAAK,KAAK,GAAG,EAAE,CAAC;YAC9B,QAAQ,CAAC,EAAE,CAAC,CAAC;YACb,SAAS,CAAC,CAAC,CAAC,CAAC;YACb,OAAO;QACT,CAAC;QACD,IAAI,GAAG,CAAC,SAAS,IAAI,GAAG,CAAC,MAAM,EAAE,CAAC;YAChC,IAAI,MAAM,GAAG,CAAC,EAAE,CAAC;gBACf,QAAQ,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;gBAC1D,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;YAC1B,CAAC;YACD,OAAO;QACT,CAAC;QACD,IAAI,KAAK,IAAI,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;YACpC,QAAQ,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,GAAG,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;YAC9D,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC;QACrC,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,MAAM,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,QAAQ,CAAC,CAAC;IACrE,MAAM,UAAU,GAAG,QAAQ,KAAK,IAAI,IAAI,OAAO,CAAC,MAAM,KAAK,QAAQ,CAAC;IACpE,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;IAC5B,MAAM,WAAW,GAAG,KAAK,IAAI,CAAC,KAAK,CAAC,MAAM,KAAK,SAAS,IAAI,KAAK,CAAC,MAAM,KAAK,eAAe,CAAC,CAAC;IAC9F,MAAM,UAAU,GAAG,WAAW;QAC5B,CAAC,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,KAAK,KAAK,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,CAAC,OAAO,GAAG,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,KAAK,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE;QACxI,CAAC,CAAC,EAAE,CAAC;IACP,MAAM,OAAO,GAAG,SAAS;QACvB,CAAC,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,cAAc,CAAC,KAAK,GAAG,cAAc,CAAC,MAAM,CAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC,EAAE,WAAW,CAAC,EAAE;QACtG,CAAC,CAAC,EAAE,CAAC;IAEP,MAAM,cAAc,GAAG,OAAO,CAAC,IAAI;QACjC,CAAC,CAAC,aAAa,CAAC,YAAY,CAAC,OAAO,CAAC,MAAM,EAAE,UAAU,CAAC,OAAO,EAAE,QAAQ,CAAC,EAAE,OAAO,CAAC,KAAK,CAAC;QAC1F,CAAC,CAAC,EAAE,CAAC;IAEP,MAAM,UAAU,GACd,WAAW,GAAG,CAAC,IAAI,WAAW,GAAG,CAAC;QAChC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,WAAW,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,WAAW,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,WAAW,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,aAAa,CAAC;QAChH,CAAC,CAAC,EAAE,CAAC;IAET,OAAO,CACL,MAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,aACzB,MAAC,GAAG,IAAC,cAAc,EAAC,eAAe,aACjC,KAAC,IAAI,cAAE,UAAU,CAAC,IAAI,CAAC,GAAQ,EAC/B,KAAC,IAAI,cAAE,UAAU,IAAI,EAAE,CAAC,GAAG,CAAC,aAAa,CAAC,GAAQ,IAC9C,EACN,KAAC,GAAG,IAAC,MAAM,EAAE,EAAE,EAAE,aAAa,EAAC,QAAQ,YACpC,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,CACxB,KAAC,IAAI,IAAS,IAAI,EAAC,cAAc,YAC9B,IAAI,IAAI,GAAG,IADH,CAAC,CAEL,CACR,CAAC,GACE,EACL,MAAM,CAAC,CAAC,CAAC,KAAC,IAAI,IAAC,KAAK,EAAC,QAAQ,YAAE,KAAK,MAAM,EAAE,GAAQ,CAAC,CAAC,CAAC,IAAI,EAC3D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CACd,MAAC,GAAG,IAAC,WAAW,EAAC,OAAO,EAAC,WAAW,EAAC,MAAM,EAAC,QAAQ,EAAE,CAAC,EAAE,aAAa,EAAC,QAAQ,aAC7E,MAAC,IAAI,eACF,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,EACb,WAAW,CAAC,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,0BAA0B,CAAC,IACxE,EACN,cAAc,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE;wBAC1C,MAAM,MAAM,GAAG,CAAC,KAAK,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,cAAc,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;wBACxE,MAAM,KAAK,GAAG,GAAG,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,KAAK,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;wBAC1E,OAAO,KAAC,IAAI,cAAgB,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,KAAK,EAAE,IAAvD,IAAI,CAAC,EAAE,CAAwD,CAAC;oBACpF,CAAC,CAAC,EACD,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC,KAAC,IAAI,IAAC,QAAQ,kBAAE,cAAc,GAAQ,CAAC,CAAC,CAAC,IAAI,IACnE,CACP,CAAC,CAAC,CAAC,IAAI,EACR,MAAC,GAAG,IAAC,WAAW,EAAC,OAAO,EAAC,cAAc,QAAC,QAAQ,EAAE,CAAC,EAAE,aAAa,EAAC,QAAQ,aACzE,MAAC,IAAI,eACF,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,EACb,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,CAAC,IACnC,EACP,MAAC,IAAI,IAAC,QAAQ,mBACX,QAAQ,IAAI,UAAU,EACtB,cAAc,CAAC,CAAC,CAAC,MAAM,cAAc,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,EACjD,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,wBAAwB,CAAC,EACtE,OAAO,CAAC,CAAC,CAAC,MAAM,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,IAC1B,IACH,EACN,KAAC,GAAG,IAAC,cAAc,EAAC,eAAe,YACjC,KAAC,IAAI,IAAC,QAAQ,kBAAE,kFAAkF,GAAQ,GACtG,EACN,MAAC,GAAG,IAAC,cAAc,EAAC,eAAe,aACjC,MAAC,IAAI,eACH,KAAC,IAAI,IAAC,QAAQ,kBACX,OAAO,OAAO,CAAC,IAAI,YAAY,OAAO,CAAC,MAAM,IAAI,GAAG,EAAE;oCACrD,CAAC,OAAO,CAAC,OAAO,IAAI,OAAO,CAAC,OAAO,GAAG,CAAC;wCACrC,CAAC,CAAC,MAAM,OAAO,CAAC,OAAO,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE;wCACvG,CAAC,CAAC,EAAE,CAAC,GACJ,EACP,KAAC,IAAI,cAAE,UAAU,GAAQ,IACpB,EACP,KAAC,IAAI,IAAC,QAAQ,kBAAE,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,EAAE,GAAQ,IACzE,IACF,CACP,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,MAAM;IAC1B,MAAM,MAAM,GAAG,MAAM,YAAY,EAAE,CAAC;IACpC,IAAI,OAAsB,CAAC;IAC3B,IAAI,CAAC;QACH,OAAO,GAAG,MAAM,qBAAqB,CAAC,MAAM,CAAC,CAAC;IAChD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,GAAG,YAAY,cAAc,EAAE,CAAC;YAClC,KAAK,MAAM,IAAI,IAAI,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC;gBAAE,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAC9E,OAAO,CAAC,GAAG,EAAE,CAAC;YACd,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,+BAA+B,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC;YAC/F,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;YACrB,OAAO;QACT,CAAC;QACD,MAAM,GAAG,CAAC;IACZ,CAAC;IACD,MAAM,EAAE,aAAa,EAAE,GAAG,MAAM,CAAC,KAAC,GAAG,IAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAI,EAAE;QAC1E,WAAW,EAAE,KAAK;KACnB,CAAC,CAAC;IACH,MAAM,aAAa,EAAE,CAAC;IACtB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * CLI rendering — one place that turns log events into terminal lines.
3
+ */
4
+ import type { LoomEvent, ProjectStatus } from "../types.js";
5
+ export declare function agentColor(id: string): (s: string) => string;
6
+ export declare function timeShort(ts: number): string;
7
+ export declare function fmtUsd(usd: number): string;
8
+ export declare function formatEvent(e: LoomEvent): string | null;
9
+ export declare function formatProjectRow(p: ProjectStatus): string;
10
+ export declare function formatAgentRow(a: ProjectStatus["agents"][number]): string;
package/dist/cli/ui.js ADDED
@@ -0,0 +1,135 @@
1
+ /**
2
+ * CLI rendering — one place that turns log events into terminal lines.
3
+ */
4
+ import pc from "picocolors";
5
+ const AGENT_COLORS = [pc.cyan, pc.green, pc.yellow, pc.blue, pc.magenta];
6
+ const colorCache = new Map();
7
+ export function agentColor(id) {
8
+ let fn = colorCache.get(id);
9
+ if (!fn) {
10
+ fn = AGENT_COLORS[colorCache.size % AGENT_COLORS.length];
11
+ colorCache.set(id, fn);
12
+ }
13
+ return fn;
14
+ }
15
+ export function timeShort(ts) {
16
+ return new Date(ts).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
17
+ }
18
+ export function fmtUsd(usd) {
19
+ if (usd >= 0.01)
20
+ return `$${usd.toFixed(2)}`;
21
+ return `$${usd.toFixed(4)}`;
22
+ }
23
+ export function formatEvent(e) {
24
+ const who = e.agentId ?? String(e.payload.author ?? "");
25
+ const paint = agentColor(who || "system");
26
+ switch (e.kind) {
27
+ case "message": {
28
+ const text = String(e.payload.text ?? "");
29
+ if (!e.agentId) {
30
+ const author = String(e.payload.author ?? "you");
31
+ if (author === "loom") {
32
+ // Route-generated instructions: show compactly, not as the human.
33
+ return pc.dim(`${timeShort(e.ts)} loom ⟶ ${text.split("\n")[0] ?? ""}`);
34
+ }
35
+ return `${pc.dim(timeShort(e.ts))} ${pc.bold("you")} ${text}`;
36
+ }
37
+ return `${pc.dim(timeShort(e.ts))} ${paint(pc.bold(who))} ${text}`;
38
+ }
39
+ case "tool_call":
40
+ return pc.dim(` ⚙ ${String(e.payload.summary ?? e.payload.tool ?? "tool")}`);
41
+ case "file_edit":
42
+ return pc.dim(` ✎ ${String(e.payload.path ?? "")}`);
43
+ case "turn_diff": {
44
+ const files = e.payload.files ?? [];
45
+ const added = Number(e.payload.added ?? 0);
46
+ const removed = Number(e.payload.removed ?? 0);
47
+ const names = files.slice(0, 3).map((f) => f.path).join(", ");
48
+ const more = files.length > 3 ? ` +${files.length - 3} more` : "";
49
+ return pc.dim(` ✎ this prompt changed ${files.length} file${files.length === 1 ? "" : "s"} (+${added} −${removed}): ${names}${more}`);
50
+ }
51
+ case "handoff": {
52
+ const from = e.payload.from ?? "—";
53
+ const to = e.payload.to ?? "—";
54
+ const dirty = e.payload.dirty ? pc.dim(" · uncommitted changes in tree") : "";
55
+ return pc.magenta(` ⟶ baton: ${from} → ${to}`) + dirty;
56
+ }
57
+ case "suggestion":
58
+ return pc.yellow(` 💡 ${String(e.payload.reason ?? "handoff suggested")} ${pc.dim(`(/handoff ${String(e.payload.to)})`)}`);
59
+ case "needs_input":
60
+ return pc.yellow(` ⏸ ${who} needs input: ${String(e.payload.question ?? "")}`);
61
+ case "run_complete": {
62
+ const ms = Number(e.payload.durationMs ?? 0);
63
+ const cost = e.payload.costUsd !== undefined ? ` · $${Number(e.payload.costUsd).toFixed(4)}` : "";
64
+ return pc.dim(` ✓ ${who} done (${(ms / 1000).toFixed(1)}s${cost})`);
65
+ }
66
+ case "decision":
67
+ return pc.blue(` ★ decision: ${String(e.payload.text ?? "")}`);
68
+ case "memory_import":
69
+ return pc.blue(` 🧠 memory: imported ${String(e.payload.file ?? "")} from ${who} into the shared brain`);
70
+ case "route_started": {
71
+ const steps = e.payload.steps ?? [];
72
+ const name = e.payload.name ? ` "${String(e.payload.name)}"` : "";
73
+ return pc.cyan(` ➤ route${name} started: ${steps.join(" → ")}`);
74
+ }
75
+ case "route_step": {
76
+ const n = Number(e.payload.step) + 1;
77
+ const of = e.payload.of ? `/${Number(e.payload.of)}` : "";
78
+ const reason = e.payload.reason ? pc.dim(` (${String(e.payload.reason)})`) : "";
79
+ return pc.cyan(` ➤ ${e.payload.of ? "step" : "hop"} ${n}${of} → ${String(e.payload.agent)}`) + reason;
80
+ }
81
+ case "route_paused":
82
+ return pc.yellow(` ⏸ route paused — ${String(e.payload.agent)} asks: ${String(e.payload.question ?? "")}` +
83
+ pc.dim(" (answer with loom send / chat; route resumes automatically)"));
84
+ case "route_resumed":
85
+ return pc.cyan(` ➤ route resumed`);
86
+ case "route_completed": {
87
+ const cost = Number(e.payload.costUsd ?? 0);
88
+ const costNote = cost > 0 ? pc.dim(` · ${fmtUsd(cost)}`) : "";
89
+ return pc.green(` ✔ route completed (${Number(e.payload.steps)} steps)`) + costNote;
90
+ }
91
+ case "route_failed":
92
+ return e.payload.aborted
93
+ ? pc.yellow(` ⊘ route stopped: ${String(e.payload.reason ?? "")}`)
94
+ : pc.red(` ✗ route failed: ${String(e.payload.reason ?? "")}`);
95
+ case "error":
96
+ return pc.red(` ✗ ${who}: ${String(e.payload.message ?? "error")}`);
97
+ case "status": {
98
+ const state = String(e.payload.state ?? "");
99
+ if (["interrupted", "unreachable", "wait_failed"].includes(state)) {
100
+ return pc.dim(` · ${who} ${state}`);
101
+ }
102
+ if (state === "turn_cost") {
103
+ return pc.dim(` · cost $${Number(e.payload.costUsd ?? 0).toFixed(4)}`);
104
+ }
105
+ if (state === "projection") {
106
+ return pc.dim(` · shared memory distilled by llm (${Number(e.payload.ms ?? 0)}ms)`);
107
+ }
108
+ return null; // lifecycle noise stays out of the chat
109
+ }
110
+ default:
111
+ return null;
112
+ }
113
+ }
114
+ export function formatProjectRow(p) {
115
+ const flag = p.needsInput ? pc.yellow("● needs input") : pc.dim("○ idle");
116
+ const holder = p.holder ? agentColor(p.holder)(p.holder) : pc.dim("—");
117
+ const last = p.lastEvent
118
+ ? pc.dim(`${timeShort(p.lastEvent.ts)} ${p.lastEvent.kind}`)
119
+ : pc.dim("no activity");
120
+ const route = p.route && (p.route.status === "running" || p.route.status === "waiting_human")
121
+ ? pc.cyan(` ➤ ${p.route.name ?? "route"} ${p.route.current + 1}/${p.route.steps.length}` +
122
+ (p.route.status === "waiting_human" ? " ⏸" : ""))
123
+ : "";
124
+ const cost = p.costUsd && p.costUsd > 0 ? pc.dim(` ${fmtUsd(p.costUsd)}`) : "";
125
+ return `${flag} ${pc.bold(p.name)} ${pc.dim(`(${p.id})`)} baton: ${holder}${route}${cost} ${last}`;
126
+ }
127
+ export function formatAgentRow(a) {
128
+ const paint = agentColor(a.id);
129
+ const baton = a.holdsBaton ? pc.magenta(" ⟵ baton") : "";
130
+ const avail = a.available ? pc.green("●") : pc.red("○");
131
+ const busy = a.busy ? pc.yellow(" (busy)") : "";
132
+ const tier = a.tier === "bridge" ? pc.dim(" [bridge]") : "";
133
+ return ` ${avail} ${paint(pc.bold(a.id))} ${pc.dim(a.kind)} — ${a.role}${tier}${busy}${baton}`;
134
+ }
135
+ //# sourceMappingURL=ui.js.map