@neotx/cli 0.1.0-alpha.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 (38) hide show
  1. package/LICENSE +21 -0
  2. package/dist/agents-Y6LREFXP.js +58 -0
  3. package/dist/agents-Y6LREFXP.js.map +1 -0
  4. package/dist/chunk-CP54H7WA.js +85 -0
  5. package/dist/chunk-CP54H7WA.js.map +1 -0
  6. package/dist/chunk-EZAJLAUF.js +40 -0
  7. package/dist/chunk-EZAJLAUF.js.map +1 -0
  8. package/dist/chunk-TNJOG54I.js +16 -0
  9. package/dist/chunk-TNJOG54I.js.map +1 -0
  10. package/dist/chunk-YQIWMDXL.js +33 -0
  11. package/dist/chunk-YQIWMDXL.js.map +1 -0
  12. package/dist/cost-DNGKT4UC.js +134 -0
  13. package/dist/cost-DNGKT4UC.js.map +1 -0
  14. package/dist/daemon/supervisor-worker.js +28 -0
  15. package/dist/daemon/supervisor-worker.js.map +1 -0
  16. package/dist/daemon/worker.js +95 -0
  17. package/dist/daemon/worker.js.map +1 -0
  18. package/dist/doctor-CPVIT7IP.js +198 -0
  19. package/dist/doctor-CPVIT7IP.js.map +1 -0
  20. package/dist/index.js +24 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/init-YNSPTCA3.js +74 -0
  23. package/dist/init-YNSPTCA3.js.map +1 -0
  24. package/dist/logs-AWNAMMJC.js +200 -0
  25. package/dist/logs-AWNAMMJC.js.map +1 -0
  26. package/dist/mcp-LC5VU65M.js +217 -0
  27. package/dist/mcp-LC5VU65M.js.map +1 -0
  28. package/dist/repos-GI6F72NO.js +111 -0
  29. package/dist/repos-GI6F72NO.js.map +1 -0
  30. package/dist/run-KIU2ZE72.js +231 -0
  31. package/dist/run-KIU2ZE72.js.map +1 -0
  32. package/dist/runs-CHA2JM5K.js +176 -0
  33. package/dist/runs-CHA2JM5K.js.map +1 -0
  34. package/dist/supervise-7ZITWRSL.js +298 -0
  35. package/dist/supervise-7ZITWRSL.js.map +1 -0
  36. package/dist/tui-W2FHMMMN.js +489 -0
  37. package/dist/tui-W2FHMMMN.js.map +1 -0
  38. package/package.json +53 -0
@@ -0,0 +1,489 @@
1
+ // src/tui/index.ts
2
+ import { render } from "ink";
3
+ import React from "react";
4
+
5
+ // src/tui/supervisor-tui.tsx
6
+ import { randomUUID } from "crypto";
7
+ import { appendFile, readFile } from "fs/promises";
8
+ import {
9
+ getSupervisorActivityPath,
10
+ getSupervisorInboxPath,
11
+ getSupervisorStatePath
12
+ } from "@neotx/core";
13
+ import { Box, Text, useApp, useInput, useStdout } from "ink";
14
+ import TextInput from "ink-text-input";
15
+ import { useCallback, useEffect, useState } from "react";
16
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
17
+ var MAX_VISIBLE_ENTRIES = 24;
18
+ var POLL_INTERVAL_MS = 1500;
19
+ var ANIMATION_TICK_MS = 400;
20
+ var SPARK_CHARS = ["\u2581", "\u2582", "\u2583", "\u2584", "\u2585", "\u2586", "\u2587", "\u2588"];
21
+ var BLOCK_FULL = "\u2588";
22
+ var BLOCK_EMPTY = "\u2591";
23
+ var PULSE_FRAMES = ["\u25C9", "\u25CE", "\u25CB", "\u25CE"];
24
+ var IDLE_FRAMES = ["\u25CC", "\u25CC", "\u25CC", "\u25CC"];
25
+ var TYPE_ICONS = {
26
+ heartbeat: "\u2665",
27
+ decision: "\u2605",
28
+ action: "\u26A1",
29
+ error: "\u2716",
30
+ event: "\u25C6",
31
+ message: "\u2709",
32
+ thinking: "\u25C7",
33
+ plan: "\u25B8",
34
+ dispatch: "\u2197",
35
+ tool_use: "\u2298"
36
+ };
37
+ var TYPE_COLORS = {
38
+ heartbeat: "#6ee7b7",
39
+ decision: "#fbbf24",
40
+ action: "#60a5fa",
41
+ error: "#f87171",
42
+ event: "#c084fc",
43
+ message: "#67e8f9",
44
+ thinking: "#a78bfa",
45
+ plan: "#34d399",
46
+ dispatch: "#f472b6",
47
+ tool_use: "#38bdf8"
48
+ };
49
+ var TYPE_LABELS = {
50
+ heartbeat: "BEAT",
51
+ decision: "DECIDE",
52
+ action: "ACTION",
53
+ error: "ERROR",
54
+ event: "EVENT",
55
+ message: "MSG",
56
+ thinking: "THINK",
57
+ plan: "PLAN",
58
+ dispatch: "SEND",
59
+ tool_use: "TOOL"
60
+ };
61
+ function formatTime(timestamp) {
62
+ return timestamp.slice(11, 19);
63
+ }
64
+ function formatUptime(startedAt) {
65
+ const ms = Date.now() - new Date(startedAt).getTime();
66
+ const seconds = Math.floor(ms / 1e3);
67
+ const minutes = Math.floor(seconds / 60);
68
+ const hours = Math.floor(minutes / 60);
69
+ const days = Math.floor(hours / 24);
70
+ if (days > 0) return `${days}d ${hours % 24}h`;
71
+ if (hours > 0) return `${hours}h ${minutes % 60}m`;
72
+ if (minutes > 0) return `${minutes}m ${seconds % 60}s`;
73
+ return `${seconds}s`;
74
+ }
75
+ function formatTimeAgo(timestamp) {
76
+ const ms = Date.now() - new Date(timestamp).getTime();
77
+ const seconds = Math.floor(ms / 1e3);
78
+ if (seconds < 60) return `${seconds}s ago`;
79
+ const minutes = Math.floor(seconds / 60);
80
+ if (minutes < 60) return `${minutes}m ago`;
81
+ const hours = Math.floor(minutes / 60);
82
+ return `${hours}h ago`;
83
+ }
84
+ function buildProgressBar(ratio, width) {
85
+ const clamped = Math.max(0, Math.min(1, ratio));
86
+ const filledCount = Math.round(clamped * width);
87
+ return {
88
+ filled: BLOCK_FULL.repeat(filledCount),
89
+ empty: BLOCK_EMPTY.repeat(width - filledCount)
90
+ };
91
+ }
92
+ function buildSparkline(values, width) {
93
+ if (values.length === 0) return "\u2581".repeat(width);
94
+ const recent = values.slice(-width);
95
+ const max = Math.max(...recent, 1e-3);
96
+ return recent.map((v) => {
97
+ const idx = Math.min(
98
+ Math.floor(v / max * (SPARK_CHARS.length - 1)),
99
+ SPARK_CHARS.length - 1
100
+ );
101
+ return SPARK_CHARS[idx];
102
+ }).join("");
103
+ }
104
+ function extractCostHistory(entries) {
105
+ return entries.filter((e) => e.type === "heartbeat" && e.summary.includes("complete")).map((e) => {
106
+ const detail = e.detail;
107
+ return typeof detail?.costUsd === "number" ? detail.costUsd : 0;
108
+ });
109
+ }
110
+ function useAnimationFrame() {
111
+ const [frame, setFrame] = useState(0);
112
+ useEffect(() => {
113
+ const interval = setInterval(() => setFrame((f) => f + 1), ANIMATION_TICK_MS);
114
+ return () => clearInterval(interval);
115
+ }, []);
116
+ return frame;
117
+ }
118
+ function useClock() {
119
+ const [time, setTime] = useState(() => (/* @__PURE__ */ new Date()).toLocaleTimeString());
120
+ useEffect(() => {
121
+ const interval = setInterval(() => setTime((/* @__PURE__ */ new Date()).toLocaleTimeString()), 1e3);
122
+ return () => clearInterval(interval);
123
+ }, []);
124
+ return time;
125
+ }
126
+ function Logo() {
127
+ return /* @__PURE__ */ jsxs(Box, { paddingX: 1, gap: 1, children: [
128
+ /* @__PURE__ */ jsx(Text, { color: "#c084fc", bold: true, children: "\u25C6" }),
129
+ /* @__PURE__ */ jsxs(Text, { bold: true, children: [
130
+ /* @__PURE__ */ jsx(Text, { color: "#c084fc", children: "N" }),
131
+ /* @__PURE__ */ jsx(Text, { color: "#a78bfa", children: "E" }),
132
+ /* @__PURE__ */ jsx(Text, { color: "#818cf8", children: "O" })
133
+ ] }),
134
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "SUPERVISOR" })
135
+ ] });
136
+ }
137
+ function LiveIndicator({ frame, isRunning }) {
138
+ const frames = isRunning ? PULSE_FRAMES : IDLE_FRAMES;
139
+ const dot = frames[frame % frames.length];
140
+ return /* @__PURE__ */ jsxs(Box, { paddingX: 1, children: [
141
+ /* @__PURE__ */ jsx(Text, { color: isRunning ? "#4ade80" : "#6b7280", bold: true, children: dot }),
142
+ /* @__PURE__ */ jsxs(Text, { color: isRunning ? "#4ade80" : "#6b7280", bold: true, children: [
143
+ " ",
144
+ isRunning ? "LIVE" : "IDLE"
145
+ ] })
146
+ ] });
147
+ }
148
+ function HeaderBar({
149
+ state,
150
+ name,
151
+ frame,
152
+ clock
153
+ }) {
154
+ if (!state) {
155
+ return /* @__PURE__ */ jsxs(Box, { borderStyle: "round", borderColor: "#6b7280", paddingX: 1, flexDirection: "column", children: [
156
+ /* @__PURE__ */ jsxs(Box, { justifyContent: "space-between", children: [
157
+ /* @__PURE__ */ jsx(Logo, {}),
158
+ /* @__PURE__ */ jsx(Box, { paddingX: 1, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: clock }) })
159
+ ] }),
160
+ /* @__PURE__ */ jsx(Box, { paddingX: 1, children: /* @__PURE__ */ jsxs(Text, { color: "#fbbf24", children: [
161
+ '\u27F3 Connecting to "',
162
+ name,
163
+ '"...'
164
+ ] }) })
165
+ ] });
166
+ }
167
+ const isRunning = state.status === "running";
168
+ return /* @__PURE__ */ jsxs(
169
+ Box,
170
+ {
171
+ borderStyle: "round",
172
+ borderColor: isRunning ? "#6ee7b7" : "#f87171",
173
+ paddingX: 0,
174
+ flexDirection: "column",
175
+ children: [
176
+ /* @__PURE__ */ jsxs(Box, { justifyContent: "space-between", children: [
177
+ /* @__PURE__ */ jsx(Logo, {}),
178
+ /* @__PURE__ */ jsxs(Box, { gap: 2, children: [
179
+ /* @__PURE__ */ jsx(LiveIndicator, { frame, isRunning }),
180
+ /* @__PURE__ */ jsx(Box, { paddingX: 1, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: clock }) })
181
+ ] })
182
+ ] }),
183
+ /* @__PURE__ */ jsxs(Box, { paddingX: 1, gap: 1, children: [
184
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }),
185
+ /* @__PURE__ */ jsxs(Text, { children: [
186
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "pid" }),
187
+ " ",
188
+ /* @__PURE__ */ jsx(Text, { bold: true, children: state.pid })
189
+ ] }),
190
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\xB7" }),
191
+ /* @__PURE__ */ jsxs(Text, { children: [
192
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "port" }),
193
+ " ",
194
+ /* @__PURE__ */ jsxs(Text, { bold: true, children: [
195
+ ":",
196
+ state.port
197
+ ] })
198
+ ] }),
199
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\xB7" }),
200
+ /* @__PURE__ */ jsxs(Text, { children: [
201
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "beats" }),
202
+ " ",
203
+ /* @__PURE__ */ jsxs(Text, { bold: true, color: "#6ee7b7", children: [
204
+ "\u25B2",
205
+ state.heartbeatCount
206
+ ] })
207
+ ] }),
208
+ state.lastHeartbeat && /* @__PURE__ */ jsxs(Fragment, { children: [
209
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\xB7" }),
210
+ /* @__PURE__ */ jsxs(Text, { children: [
211
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "last" }),
212
+ " ",
213
+ /* @__PURE__ */ jsx(Text, { children: formatTimeAgo(state.lastHeartbeat) })
214
+ ] })
215
+ ] }),
216
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\xB7" }),
217
+ /* @__PURE__ */ jsxs(Text, { children: [
218
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "up" }),
219
+ " ",
220
+ /* @__PURE__ */ jsx(Text, { children: formatUptime(state.startedAt) })
221
+ ] })
222
+ ] })
223
+ ]
224
+ }
225
+ );
226
+ }
227
+ function BudgetPanel({
228
+ state,
229
+ dailyCap,
230
+ costHistory
231
+ }) {
232
+ if (!state) return null;
233
+ const todayCost = state.todayCostUsd ?? 0;
234
+ const totalCost = state.totalCostUsd ?? 0;
235
+ const ratio = dailyCap > 0 ? todayCost / dailyCap : 0;
236
+ const barWidth = 20;
237
+ const bar = buildProgressBar(ratio, barWidth);
238
+ const pct = Math.round(ratio * 100);
239
+ const barColor = pct < 50 ? "#4ade80" : pct < 80 ? "#fbbf24" : "#f87171";
240
+ const sparkline = buildSparkline(costHistory, 12);
241
+ return /* @__PURE__ */ jsxs(Box, { paddingX: 2, gap: 2, children: [
242
+ /* @__PURE__ */ jsxs(Box, { gap: 1, children: [
243
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "budget" }),
244
+ /* @__PURE__ */ jsx(Text, { color: barColor, children: bar.filled }),
245
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: bar.empty }),
246
+ /* @__PURE__ */ jsxs(Text, { bold: true, color: barColor, children: [
247
+ pct,
248
+ "%"
249
+ ] }),
250
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
251
+ "($",
252
+ todayCost.toFixed(2),
253
+ "/$",
254
+ dailyCap,
255
+ ")"
256
+ ] })
257
+ ] }),
258
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }),
259
+ /* @__PURE__ */ jsxs(Box, { gap: 1, children: [
260
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "total" }),
261
+ /* @__PURE__ */ jsxs(Text, { bold: true, children: [
262
+ "$",
263
+ totalCost.toFixed(2)
264
+ ] })
265
+ ] }),
266
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }),
267
+ /* @__PURE__ */ jsxs(Box, { gap: 1, children: [
268
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "cost/beat" }),
269
+ /* @__PURE__ */ jsx(Text, { color: "#818cf8", children: sparkline })
270
+ ] })
271
+ ] });
272
+ }
273
+ function ActivityRow({
274
+ entry,
275
+ isLatest,
276
+ isOld
277
+ }) {
278
+ const icon = TYPE_ICONS[entry.type] ?? "\xB7";
279
+ const color = TYPE_COLORS[entry.type] ?? "#9ca3af";
280
+ const label = (TYPE_LABELS[entry.type] ?? entry.type.toUpperCase()).padEnd(7);
281
+ return /* @__PURE__ */ jsxs(Box, { gap: 1, paddingX: 2, children: [
282
+ /* @__PURE__ */ jsx(Text, { dimColor: isOld, children: isLatest ? "\u2502" : "\u2502" }),
283
+ /* @__PURE__ */ jsx(Text, { dimColor: isOld, children: formatTime(entry.timestamp) }),
284
+ /* @__PURE__ */ jsx(Text, { color, dimColor: isOld, bold: isLatest, children: icon }),
285
+ /* @__PURE__ */ jsx(Text, { color, dimColor: isOld, bold: true, children: label }),
286
+ /* @__PURE__ */ jsx(Text, { dimColor: isOld, bold: isLatest, wrap: "truncate", children: entry.summary })
287
+ ] });
288
+ }
289
+ function ThinkingPanel({ entries }) {
290
+ const latest = [...entries].reverse().find((e) => {
291
+ const type = e.type;
292
+ return type === "thinking" || type === "plan";
293
+ });
294
+ if (!latest) return null;
295
+ const icon = TYPE_ICONS[latest.type] ?? "\xB7";
296
+ const color = TYPE_COLORS[latest.type] ?? "#9ca3af";
297
+ const label = latest.type === "thinking" ? "THINKING" : "PLANNING";
298
+ const text = latest.summary.length > 200 ? `${latest.summary.slice(0, 200)}...` : latest.summary;
299
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
300
+ /* @__PURE__ */ jsxs(Box, { paddingX: 2, gap: 1, children: [
301
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u251C" }),
302
+ /* @__PURE__ */ jsxs(Text, { color, bold: true, children: [
303
+ icon,
304
+ " ",
305
+ label
306
+ ] }),
307
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2500".repeat(36) })
308
+ ] }),
309
+ /* @__PURE__ */ jsxs(Box, { paddingX: 2, children: [
310
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502 " }),
311
+ /* @__PURE__ */ jsx(Text, { color, wrap: "truncate-end", children: text })
312
+ ] }),
313
+ /* @__PURE__ */ jsx(Box, { paddingX: 2, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }) })
314
+ ] });
315
+ }
316
+ function ActivityPanel({ entries, termHeight }) {
317
+ const maxVisible = Math.max(5, Math.min(MAX_VISIBLE_ENTRIES, termHeight - 14));
318
+ const visible = entries.slice(-maxVisible);
319
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
320
+ /* @__PURE__ */ jsxs(Box, { paddingX: 2, gap: 1, children: [
321
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u251C" }),
322
+ /* @__PURE__ */ jsx(Text, { dimColor: true, bold: true, children: "ACTIVITY" }),
323
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2500".repeat(40) })
324
+ ] }),
325
+ visible.length === 0 ? /* @__PURE__ */ jsx(Box, { paddingX: 2, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502 Waiting for heartbeats..." }) }) : visible.map((entry, idx) => /* @__PURE__ */ jsx(
326
+ ActivityRow,
327
+ {
328
+ entry,
329
+ isLatest: idx === visible.length - 1,
330
+ isOld: idx < visible.length - 5
331
+ },
332
+ entry.id
333
+ )),
334
+ /* @__PURE__ */ jsx(Box, { paddingX: 2, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }) })
335
+ ] });
336
+ }
337
+ function InputPanel({
338
+ value,
339
+ onChange,
340
+ onSubmit,
341
+ lastSent
342
+ }) {
343
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
344
+ /* @__PURE__ */ jsxs(Box, { paddingX: 2, gap: 1, children: [
345
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2514" }),
346
+ /* @__PURE__ */ jsx(Text, { bold: true, color: "#60a5fa", children: "\u276F" }),
347
+ /* @__PURE__ */ jsx(
348
+ TextInput,
349
+ {
350
+ value,
351
+ onChange,
352
+ onSubmit,
353
+ placeholder: "message the supervisor..."
354
+ }
355
+ )
356
+ ] }),
357
+ /* @__PURE__ */ jsxs(Box, { paddingX: 2, gap: 1, children: [
358
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: " " }),
359
+ lastSent ? /* @__PURE__ */ jsxs(Text, { color: "#6b7280", children: [
360
+ '\u2713 "',
361
+ lastSent,
362
+ '"'
363
+ ] }) : null
364
+ ] })
365
+ ] });
366
+ }
367
+ function Footer() {
368
+ return /* @__PURE__ */ jsxs(Box, { paddingX: 2, gap: 1, justifyContent: "center", children: [
369
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
370
+ /* @__PURE__ */ jsx(Text, { bold: true, children: "esc" }),
371
+ " quit"
372
+ ] }),
373
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\xB7" }),
374
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
375
+ /* @__PURE__ */ jsx(Text, { bold: true, children: "enter" }),
376
+ " send"
377
+ ] }),
378
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\xB7" }),
379
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "daemon keeps running" })
380
+ ] });
381
+ }
382
+ async function readState(name) {
383
+ try {
384
+ const raw = await readFile(getSupervisorStatePath(name), "utf-8");
385
+ return JSON.parse(raw);
386
+ } catch {
387
+ return null;
388
+ }
389
+ }
390
+ async function readActivity(name, maxEntries) {
391
+ try {
392
+ const content = await readFile(getSupervisorActivityPath(name), "utf-8");
393
+ const lines = content.trim().split("\n").filter(Boolean);
394
+ const lastLines = lines.slice(-maxEntries);
395
+ const entries = [];
396
+ for (const line of lastLines) {
397
+ try {
398
+ entries.push(JSON.parse(line));
399
+ } catch {
400
+ }
401
+ }
402
+ return entries;
403
+ } catch {
404
+ return [];
405
+ }
406
+ }
407
+ async function sendMessage(name, text) {
408
+ const message = {
409
+ id: randomUUID(),
410
+ from: "tui",
411
+ text,
412
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
413
+ };
414
+ const inboxPath = getSupervisorInboxPath(name);
415
+ await appendFile(inboxPath, `${JSON.stringify(message)}
416
+ `, "utf-8");
417
+ }
418
+ function SupervisorTui({ name }) {
419
+ const { exit } = useApp();
420
+ const { stdout } = useStdout();
421
+ const frame = useAnimationFrame();
422
+ const clock = useClock();
423
+ const [state, setState] = useState(null);
424
+ const [entries, setEntries] = useState([]);
425
+ const [input, setInput] = useState("");
426
+ const [lastSent, setLastSent] = useState("");
427
+ const [termHeight, setTermHeight] = useState(stdout?.rows ?? 30);
428
+ useEffect(() => {
429
+ function onResize() {
430
+ if (stdout) setTermHeight(stdout.rows);
431
+ }
432
+ stdout?.on("resize", onResize);
433
+ return () => {
434
+ stdout?.off("resize", onResize);
435
+ };
436
+ }, [stdout]);
437
+ useEffect(() => {
438
+ let active = true;
439
+ async function poll() {
440
+ if (!active) return;
441
+ const [newState, newEntries] = await Promise.all([
442
+ readState(name),
443
+ readActivity(name, MAX_VISIBLE_ENTRIES)
444
+ ]);
445
+ if (!active) return;
446
+ setState(newState);
447
+ setEntries(newEntries);
448
+ }
449
+ poll();
450
+ const interval = setInterval(poll, POLL_INTERVAL_MS);
451
+ return () => {
452
+ active = false;
453
+ clearInterval(interval);
454
+ };
455
+ }, [name]);
456
+ useInput((_input, key) => {
457
+ if (key.escape) {
458
+ exit();
459
+ }
460
+ });
461
+ const handleSubmit = useCallback(
462
+ (text) => {
463
+ if (!text.trim()) return;
464
+ sendMessage(name, text.trim());
465
+ setLastSent(text.trim());
466
+ setInput("");
467
+ },
468
+ [name]
469
+ );
470
+ const costHistory = extractCostHistory(entries);
471
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
472
+ /* @__PURE__ */ jsx(HeaderBar, { state, name, frame, clock }),
473
+ /* @__PURE__ */ jsx(BudgetPanel, { state, dailyCap: 50, costHistory }),
474
+ /* @__PURE__ */ jsx(ThinkingPanel, { entries }),
475
+ /* @__PURE__ */ jsx(ActivityPanel, { entries, termHeight }),
476
+ /* @__PURE__ */ jsx(InputPanel, { value: input, onChange: setInput, onSubmit: handleSubmit, lastSent }),
477
+ /* @__PURE__ */ jsx(Footer, {})
478
+ ] });
479
+ }
480
+
481
+ // src/tui/index.ts
482
+ async function renderSupervisorTui(name) {
483
+ const { waitUntilExit } = render(React.createElement(SupervisorTui, { name }));
484
+ await waitUntilExit();
485
+ }
486
+ export {
487
+ renderSupervisorTui
488
+ };
489
+ //# sourceMappingURL=tui-W2FHMMMN.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/tui/index.ts","../src/tui/supervisor-tui.tsx"],"sourcesContent":["import { render } from \"ink\";\nimport React from \"react\";\nimport { SupervisorTui } from \"./supervisor-tui.js\";\n\n/**\n * Render the supervisor TUI. Returns a promise that resolves when the user exits.\n */\nexport async function renderSupervisorTui(name: string): Promise<void> {\n const { waitUntilExit } = render(React.createElement(SupervisorTui, { name }));\n await waitUntilExit();\n}\n","import { randomUUID } from \"node:crypto\";\nimport { appendFile, readFile } from \"node:fs/promises\";\nimport type { ActivityEntry, SupervisorDaemonState } from \"@neotx/core\";\nimport {\n getSupervisorActivityPath,\n getSupervisorInboxPath,\n getSupervisorStatePath,\n} from \"@neotx/core\";\nimport { Box, Text, useApp, useInput, useStdout } from \"ink\";\nimport TextInput from \"ink-text-input\";\nimport { useCallback, useEffect, useState } from \"react\";\n\n// ─── Constants ───────────────────────────────────────────\n\nconst MAX_VISIBLE_ENTRIES = 24;\nconst POLL_INTERVAL_MS = 1_500;\nconst ANIMATION_TICK_MS = 400;\n\n// ─── Unicode Visual Elements ─────────────────────────────\n\nconst SPARK_CHARS = [\"▁\", \"▂\", \"▃\", \"▄\", \"▅\", \"▆\", \"▇\", \"█\"];\nconst BLOCK_FULL = \"█\";\nconst BLOCK_EMPTY = \"░\";\nconst PULSE_FRAMES = [\"◉\", \"◎\", \"○\", \"◎\"];\nconst IDLE_FRAMES = [\"◌\", \"◌\", \"◌\", \"◌\"];\n\nconst TYPE_ICONS: Record<string, string> = {\n heartbeat: \"♥\",\n decision: \"★\",\n action: \"⚡\",\n error: \"✖\",\n event: \"◆\",\n message: \"✉\",\n thinking: \"◇\",\n plan: \"▸\",\n dispatch: \"↗\",\n tool_use: \"⊘\",\n};\n\nconst TYPE_COLORS: Record<string, string> = {\n heartbeat: \"#6ee7b7\",\n decision: \"#fbbf24\",\n action: \"#60a5fa\",\n error: \"#f87171\",\n event: \"#c084fc\",\n message: \"#67e8f9\",\n thinking: \"#a78bfa\",\n plan: \"#34d399\",\n dispatch: \"#f472b6\",\n tool_use: \"#38bdf8\",\n};\n\nconst TYPE_LABELS: Record<string, string> = {\n heartbeat: \"BEAT\",\n decision: \"DECIDE\",\n action: \"ACTION\",\n error: \"ERROR\",\n event: \"EVENT\",\n message: \"MSG\",\n thinking: \"THINK\",\n plan: \"PLAN\",\n dispatch: \"SEND\",\n tool_use: \"TOOL\",\n};\n\n// ─── Helpers ─────────────────────────────────────────────\n\nfunction formatTime(timestamp: string): string {\n return timestamp.slice(11, 19);\n}\n\nfunction formatUptime(startedAt: string): string {\n const ms = Date.now() - new Date(startedAt).getTime();\n const seconds = Math.floor(ms / 1000);\n const minutes = Math.floor(seconds / 60);\n const hours = Math.floor(minutes / 60);\n const days = Math.floor(hours / 24);\n if (days > 0) return `${days}d ${hours % 24}h`;\n if (hours > 0) return `${hours}h ${minutes % 60}m`;\n if (minutes > 0) return `${minutes}m ${seconds % 60}s`;\n return `${seconds}s`;\n}\n\nfunction formatTimeAgo(timestamp: string): string {\n const ms = Date.now() - new Date(timestamp).getTime();\n const seconds = Math.floor(ms / 1000);\n if (seconds < 60) return `${seconds}s ago`;\n const minutes = Math.floor(seconds / 60);\n if (minutes < 60) return `${minutes}m ago`;\n const hours = Math.floor(minutes / 60);\n return `${hours}h ago`;\n}\n\nfunction buildProgressBar(ratio: number, width: number): { filled: string; empty: string } {\n const clamped = Math.max(0, Math.min(1, ratio));\n const filledCount = Math.round(clamped * width);\n return {\n filled: BLOCK_FULL.repeat(filledCount),\n empty: BLOCK_EMPTY.repeat(width - filledCount),\n };\n}\n\nfunction buildSparkline(values: number[], width: number): string {\n if (values.length === 0) return \"▁\".repeat(width);\n const recent = values.slice(-width);\n const max = Math.max(...recent, 0.001);\n return recent\n .map((v) => {\n const idx = Math.min(\n Math.floor((v / max) * (SPARK_CHARS.length - 1)),\n SPARK_CHARS.length - 1,\n );\n return SPARK_CHARS[idx];\n })\n .join(\"\");\n}\n\nfunction extractCostHistory(entries: ActivityEntry[]): number[] {\n return entries\n .filter((e) => e.type === \"heartbeat\" && e.summary.includes(\"complete\"))\n .map((e) => {\n const detail = e.detail as Record<string, unknown> | undefined;\n return typeof detail?.costUsd === \"number\" ? detail.costUsd : 0;\n });\n}\n\n// ─── Animated Hooks ──────────────────────────────────────\n\nfunction useAnimationFrame(): number {\n const [frame, setFrame] = useState(0);\n useEffect(() => {\n const interval = setInterval(() => setFrame((f) => f + 1), ANIMATION_TICK_MS);\n return () => clearInterval(interval);\n }, []);\n return frame;\n}\n\nfunction useClock(): string {\n const [time, setTime] = useState(() => new Date().toLocaleTimeString());\n useEffect(() => {\n const interval = setInterval(() => setTime(new Date().toLocaleTimeString()), 1000);\n return () => clearInterval(interval);\n }, []);\n return time;\n}\n\n// ─── Components ──────────────────────────────────────────\n\nfunction Logo() {\n return (\n <Box paddingX={1} gap={1}>\n <Text color=\"#c084fc\" bold>\n ◆\n </Text>\n <Text bold>\n <Text color=\"#c084fc\">N</Text>\n <Text color=\"#a78bfa\">E</Text>\n <Text color=\"#818cf8\">O</Text>\n </Text>\n <Text dimColor>SUPERVISOR</Text>\n </Box>\n );\n}\n\nfunction LiveIndicator({ frame, isRunning }: { frame: number; isRunning: boolean }) {\n const frames = isRunning ? PULSE_FRAMES : IDLE_FRAMES;\n const dot = frames[frame % frames.length];\n return (\n <Box paddingX={1}>\n <Text color={isRunning ? \"#4ade80\" : \"#6b7280\"} bold>\n {dot}\n </Text>\n <Text color={isRunning ? \"#4ade80\" : \"#6b7280\"} bold>\n {\" \"}\n {isRunning ? \"LIVE\" : \"IDLE\"}\n </Text>\n </Box>\n );\n}\n\nfunction HeaderBar({\n state,\n name,\n frame,\n clock,\n}: {\n state: SupervisorDaemonState | null;\n name: string;\n frame: number;\n clock: string;\n}) {\n if (!state) {\n return (\n <Box borderStyle=\"round\" borderColor=\"#6b7280\" paddingX={1} flexDirection=\"column\">\n <Box justifyContent=\"space-between\">\n <Logo />\n <Box paddingX={1}>\n <Text dimColor>{clock}</Text>\n </Box>\n </Box>\n <Box paddingX={1}>\n <Text color=\"#fbbf24\">⟳ Connecting to \"{name}\"...</Text>\n </Box>\n </Box>\n );\n }\n\n const isRunning = state.status === \"running\";\n\n return (\n <Box\n borderStyle=\"round\"\n borderColor={isRunning ? \"#6ee7b7\" : \"#f87171\"}\n paddingX={0}\n flexDirection=\"column\"\n >\n <Box justifyContent=\"space-between\">\n <Logo />\n <Box gap={2}>\n <LiveIndicator frame={frame} isRunning={isRunning} />\n <Box paddingX={1}>\n <Text dimColor>{clock}</Text>\n </Box>\n </Box>\n </Box>\n\n <Box paddingX={1} gap={1}>\n <Text dimColor>│</Text>\n <Text>\n <Text dimColor>pid</Text> <Text bold>{state.pid}</Text>\n </Text>\n <Text dimColor>·</Text>\n <Text>\n <Text dimColor>port</Text> <Text bold>:{state.port}</Text>\n </Text>\n <Text dimColor>·</Text>\n <Text>\n <Text dimColor>beats</Text>{\" \"}\n <Text bold color=\"#6ee7b7\">\n ▲{state.heartbeatCount}\n </Text>\n </Text>\n {state.lastHeartbeat && (\n <>\n <Text dimColor>·</Text>\n <Text>\n <Text dimColor>last</Text> <Text>{formatTimeAgo(state.lastHeartbeat)}</Text>\n </Text>\n </>\n )}\n <Text dimColor>·</Text>\n <Text>\n <Text dimColor>up</Text> <Text>{formatUptime(state.startedAt)}</Text>\n </Text>\n </Box>\n </Box>\n );\n}\n\nfunction BudgetPanel({\n state,\n dailyCap,\n costHistory,\n}: {\n state: SupervisorDaemonState | null;\n dailyCap: number;\n costHistory: number[];\n}) {\n if (!state) return null;\n\n const todayCost = state.todayCostUsd ?? 0;\n const totalCost = state.totalCostUsd ?? 0;\n const ratio = dailyCap > 0 ? todayCost / dailyCap : 0;\n const barWidth = 20;\n const bar = buildProgressBar(ratio, barWidth);\n const pct = Math.round(ratio * 100);\n\n const barColor = pct < 50 ? \"#4ade80\" : pct < 80 ? \"#fbbf24\" : \"#f87171\";\n\n const sparkline = buildSparkline(costHistory, 12);\n\n return (\n <Box paddingX={2} gap={2}>\n <Box gap={1}>\n <Text dimColor>budget</Text>\n <Text color={barColor}>{bar.filled}</Text>\n <Text dimColor>{bar.empty}</Text>\n <Text bold color={barColor}>\n {pct}%\n </Text>\n <Text dimColor>\n (${todayCost.toFixed(2)}/${dailyCap})\n </Text>\n </Box>\n <Text dimColor>│</Text>\n <Box gap={1}>\n <Text dimColor>total</Text>\n <Text bold>${totalCost.toFixed(2)}</Text>\n </Box>\n <Text dimColor>│</Text>\n <Box gap={1}>\n <Text dimColor>cost/beat</Text>\n <Text color=\"#818cf8\">{sparkline}</Text>\n </Box>\n </Box>\n );\n}\n\nfunction ActivityRow({\n entry,\n isLatest,\n isOld,\n}: {\n entry: ActivityEntry;\n isLatest: boolean;\n isOld: boolean;\n}) {\n const icon = TYPE_ICONS[entry.type] ?? \"·\";\n const color = TYPE_COLORS[entry.type] ?? \"#9ca3af\";\n const label = (TYPE_LABELS[entry.type] ?? (entry.type as string).toUpperCase()).padEnd(7);\n\n return (\n <Box gap={1} paddingX={2}>\n <Text dimColor={isOld}>{isLatest ? \"│\" : \"│\"}</Text>\n <Text dimColor={isOld}>{formatTime(entry.timestamp)}</Text>\n <Text color={color} dimColor={isOld} bold={isLatest}>\n {icon}\n </Text>\n <Text color={color} dimColor={isOld} bold>\n {label}\n </Text>\n <Text dimColor={isOld} bold={isLatest} wrap=\"truncate\">\n {entry.summary}\n </Text>\n </Box>\n );\n}\n\nfunction ThinkingPanel({ entries }: { entries: ActivityEntry[] }) {\n // Find the latest thinking entry\n const latest = [...entries].reverse().find((e) => {\n const type = e.type as string;\n return type === \"thinking\" || type === \"plan\";\n });\n if (!latest) return null;\n\n const icon = TYPE_ICONS[latest.type] ?? \"·\";\n const color = TYPE_COLORS[latest.type] ?? \"#9ca3af\";\n const label = (latest.type as string) === \"thinking\" ? \"THINKING\" : \"PLANNING\";\n\n // Truncate to ~3 lines worth\n const text = latest.summary.length > 200 ? `${latest.summary.slice(0, 200)}...` : latest.summary;\n\n return (\n <Box flexDirection=\"column\">\n <Box paddingX={2} gap={1}>\n <Text dimColor>├</Text>\n <Text color={color} bold>\n {icon} {label}\n </Text>\n <Text dimColor>{\"─\".repeat(36)}</Text>\n </Box>\n <Box paddingX={2}>\n <Text dimColor>│ </Text>\n <Text color={color} wrap=\"truncate-end\">\n {text}\n </Text>\n </Box>\n <Box paddingX={2}>\n <Text dimColor>│</Text>\n </Box>\n </Box>\n );\n}\n\nfunction ActivityPanel({ entries, termHeight }: { entries: ActivityEntry[]; termHeight: number }) {\n // Reserve lines for header (5), budget (1), thinking (4), separator (1), input (2), footer (1) = 14\n const maxVisible = Math.max(5, Math.min(MAX_VISIBLE_ENTRIES, termHeight - 14));\n const visible = entries.slice(-maxVisible);\n\n return (\n <Box flexDirection=\"column\">\n <Box paddingX={2} gap={1}>\n <Text dimColor>├</Text>\n <Text dimColor bold>\n ACTIVITY\n </Text>\n <Text dimColor>{\"─\".repeat(40)}</Text>\n </Box>\n\n {visible.length === 0 ? (\n <Box paddingX={2}>\n <Text dimColor>│ Waiting for heartbeats...</Text>\n </Box>\n ) : (\n visible.map((entry, idx) => (\n <ActivityRow\n key={entry.id}\n entry={entry}\n isLatest={idx === visible.length - 1}\n isOld={idx < visible.length - 5}\n />\n ))\n )}\n\n <Box paddingX={2}>\n <Text dimColor>│</Text>\n </Box>\n </Box>\n );\n}\n\nfunction InputPanel({\n value,\n onChange,\n onSubmit,\n lastSent,\n}: {\n value: string;\n onChange: (v: string) => void;\n onSubmit: (v: string) => void;\n lastSent: string;\n}) {\n return (\n <Box flexDirection=\"column\">\n <Box paddingX={2} gap={1}>\n <Text dimColor>└</Text>\n <Text bold color=\"#60a5fa\">\n ❯\n </Text>\n <TextInput\n value={value}\n onChange={onChange}\n onSubmit={onSubmit}\n placeholder=\"message the supervisor...\"\n />\n </Box>\n <Box paddingX={2} gap={1}>\n <Text dimColor> </Text>\n {lastSent ? <Text color=\"#6b7280\">✓ \"{lastSent}\"</Text> : null}\n </Box>\n </Box>\n );\n}\n\nfunction Footer() {\n return (\n <Box paddingX={2} gap={1} justifyContent=\"center\">\n <Text dimColor>\n <Text bold>esc</Text> quit\n </Text>\n <Text dimColor>·</Text>\n <Text dimColor>\n <Text bold>enter</Text> send\n </Text>\n <Text dimColor>·</Text>\n <Text dimColor>daemon keeps running</Text>\n </Box>\n );\n}\n\n// ─── Data Fetching ───────────────────────────────────────\n\nasync function readState(name: string): Promise<SupervisorDaemonState | null> {\n try {\n const raw = await readFile(getSupervisorStatePath(name), \"utf-8\");\n return JSON.parse(raw) as SupervisorDaemonState;\n } catch {\n return null;\n }\n}\n\nasync function readActivity(name: string, maxEntries: number): Promise<ActivityEntry[]> {\n try {\n const content = await readFile(getSupervisorActivityPath(name), \"utf-8\");\n const lines = content.trim().split(\"\\n\").filter(Boolean);\n const lastLines = lines.slice(-maxEntries);\n const entries: ActivityEntry[] = [];\n for (const line of lastLines) {\n try {\n entries.push(JSON.parse(line) as ActivityEntry);\n } catch {\n // Skip malformed\n }\n }\n return entries;\n } catch {\n return [];\n }\n}\n\nasync function sendMessage(name: string, text: string): Promise<void> {\n const message = {\n id: randomUUID(),\n from: \"tui\" as const,\n text,\n timestamp: new Date().toISOString(),\n };\n const inboxPath = getSupervisorInboxPath(name);\n await appendFile(inboxPath, `${JSON.stringify(message)}\\n`, \"utf-8\");\n}\n\n// ─── Main Component ──────────────────────────────────────\n\nexport function SupervisorTui({ name }: { name: string }) {\n const { exit } = useApp();\n const { stdout } = useStdout();\n const frame = useAnimationFrame();\n const clock = useClock();\n\n const [state, setState] = useState<SupervisorDaemonState | null>(null);\n const [entries, setEntries] = useState<ActivityEntry[]>([]);\n const [input, setInput] = useState(\"\");\n const [lastSent, setLastSent] = useState(\"\");\n const [termHeight, setTermHeight] = useState(stdout?.rows ?? 30);\n\n // Track terminal resize\n useEffect(() => {\n function onResize() {\n if (stdout) setTermHeight(stdout.rows);\n }\n stdout?.on(\"resize\", onResize);\n return () => {\n stdout?.off(\"resize\", onResize);\n };\n }, [stdout]);\n\n // Poll state and activity\n useEffect(() => {\n let active = true;\n\n async function poll() {\n if (!active) return;\n const [newState, newEntries] = await Promise.all([\n readState(name),\n readActivity(name, MAX_VISIBLE_ENTRIES),\n ]);\n if (!active) return;\n setState(newState);\n setEntries(newEntries);\n }\n\n poll();\n const interval = setInterval(poll, POLL_INTERVAL_MS);\n return () => {\n active = false;\n clearInterval(interval);\n };\n }, [name]);\n\n useInput((_input, key) => {\n if (key.escape) {\n exit();\n }\n });\n\n const handleSubmit = useCallback(\n (text: string) => {\n if (!text.trim()) return;\n sendMessage(name, text.trim());\n setLastSent(text.trim());\n setInput(\"\");\n },\n [name],\n );\n\n const costHistory = extractCostHistory(entries);\n\n return (\n <Box flexDirection=\"column\">\n <HeaderBar state={state} name={name} frame={frame} clock={clock} />\n <BudgetPanel state={state} dailyCap={50} costHistory={costHistory} />\n <ThinkingPanel entries={entries} />\n <ActivityPanel entries={entries} termHeight={termHeight} />\n <InputPanel value={input} onChange={setInput} onSubmit={handleSubmit} lastSent={lastSent} />\n <Footer />\n </Box>\n );\n}\n"],"mappings":"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","names":[]}
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "@neotx/cli",
3
+ "version": "0.1.0-alpha.0",
4
+ "description": "CLI for the Neo orchestration framework",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/voltaire-network/neo.git",
10
+ "directory": "packages/cli"
11
+ },
12
+ "bin": {
13
+ "neo": "dist/index.js"
14
+ },
15
+ "files": [
16
+ "dist"
17
+ ],
18
+ "publishConfig": {
19
+ "access": "public"
20
+ },
21
+ "keywords": [
22
+ "ai-agents",
23
+ "claude",
24
+ "cli",
25
+ "orchestration",
26
+ "autonomous-agents",
27
+ "developer-tools"
28
+ ],
29
+ "engines": {
30
+ "node": ">=22"
31
+ },
32
+ "dependencies": {
33
+ "citty": "^0.2.1",
34
+ "ink": "^6.8.0",
35
+ "ink-text-input": "^6.0.0",
36
+ "react": "^19.2.4",
37
+ "yaml": "^2.8.2",
38
+ "@neotx/agents": "0.1.0-alpha.0",
39
+ "@neotx/core": "0.1.0-alpha.0"
40
+ },
41
+ "devDependencies": {
42
+ "@anthropic-ai/claude-agent-sdk": "^0.1.0",
43
+ "@types/node": "^25.5.0",
44
+ "@types/react": "^19.2.14",
45
+ "tsup": "^8.5.1"
46
+ },
47
+ "scripts": {
48
+ "build": "tsup",
49
+ "typecheck": "tsc --noEmit",
50
+ "lint": "biome check src/",
51
+ "test": "vitest run"
52
+ }
53
+ }