replicas-cli 0.2.39 → 0.2.41

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.
@@ -0,0 +1,2011 @@
1
+ #!/usr/bin/env bun
2
+ import {
3
+ buildGroups,
4
+ createMockWorkspaceRecord,
5
+ createUserMessage,
6
+ filterDisplayMessages,
7
+ getInitialChatId,
8
+ getOrganizationId,
9
+ getValidToken,
10
+ isAgentBackendEvent,
11
+ parseAgentEvents
12
+ } from "./chunk-IO4QHXPW.mjs";
13
+
14
+ // src/interactive/index.tsx
15
+ import { createCliRenderer } from "@opentui/core";
16
+ import { createRoot } from "@opentui/react";
17
+
18
+ // src/interactive/App.tsx
19
+ import { useState as useState5, useEffect as useEffect3, useMemo as useMemo3, useCallback as useCallback5, useRef as useRef3 } from "react";
20
+ import { useKeyboard as useKeyboard3, useRenderer, useTerminalDimensions as useTerminalDimensions3 } from "@opentui/react";
21
+ import { QueryClient } from "@tanstack/react-query";
22
+
23
+ // ../shared/src/hooks/auth-context.ts
24
+ import { createContext, useContext } from "react";
25
+ var ReplicasAuthContext = createContext(null);
26
+ var ReplicasAuthProvider = ReplicasAuthContext.Provider;
27
+ function useReplicasAuth() {
28
+ const ctx = useContext(ReplicasAuthContext);
29
+ if (!ctx) {
30
+ throw new Error("useReplicasAuth must be used within a ReplicasAuthProvider");
31
+ }
32
+ return ctx;
33
+ }
34
+
35
+ // ../shared/src/hooks/fetch.ts
36
+ import { useCallback } from "react";
37
+ var REDIRECT_STATUSES = /* @__PURE__ */ new Set([301, 302, 303, 307, 308]);
38
+ var MAX_REDIRECTS = 5;
39
+ async function fetchWithRedirects(url, init, attempt = 0) {
40
+ const response = await fetch(url, { ...init, redirect: "manual" });
41
+ if (REDIRECT_STATUSES.has(response.status)) {
42
+ if (attempt >= MAX_REDIRECTS) {
43
+ throw new Error("Too many redirects while contacting the Replicas API");
44
+ }
45
+ const location = response.headers.get("location");
46
+ if (!location) {
47
+ throw new Error(`Redirect status ${response.status} missing Location header`);
48
+ }
49
+ const nextUrl = new URL(location, url).toString();
50
+ const shouldForceGet = response.status === 303 && init.method !== void 0 && init.method.toUpperCase() !== "GET";
51
+ const nextInit = {
52
+ ...init,
53
+ method: shouldForceGet ? "GET" : init.method,
54
+ body: shouldForceGet ? void 0 : init.body
55
+ };
56
+ return fetchWithRedirects(nextUrl, nextInit, attempt + 1);
57
+ }
58
+ return response;
59
+ }
60
+ function useOrgFetch() {
61
+ const auth = useReplicasAuth();
62
+ return useCallback(
63
+ async function orgFetch(url, options) {
64
+ const token = await auth.getAccessToken();
65
+ const orgId = auth.getOrganizationId();
66
+ const headers = {
67
+ "Authorization": `Bearer ${token}`,
68
+ "Replicas-Org-Id": orgId,
69
+ "Content-Type": "application/json",
70
+ ...options?.headers || {}
71
+ };
72
+ const absoluteUrl = `${auth.monolithUrl}${url}`;
73
+ const requestInit = {
74
+ ...options,
75
+ headers,
76
+ body: options?.body !== void 0 ? JSON.stringify(options.body) : void 0
77
+ };
78
+ const response = await fetchWithRedirects(absoluteUrl, requestInit);
79
+ if (!response.ok) {
80
+ const error = await response.json().catch(() => ({ error: "Request failed" }));
81
+ throw new Error(error.error || `Request failed with status ${response.status}`);
82
+ }
83
+ return response.json();
84
+ },
85
+ [auth]
86
+ );
87
+ }
88
+ function useRawOrgFetch() {
89
+ const auth = useReplicasAuth();
90
+ return useCallback(
91
+ async function rawOrgFetch(url, options) {
92
+ const token = await auth.getAccessToken();
93
+ const orgId = auth.getOrganizationId();
94
+ const headers = {
95
+ "Authorization": `Bearer ${token}`,
96
+ "Replicas-Org-Id": orgId,
97
+ ...options?.headers || {}
98
+ };
99
+ const absoluteUrl = `${auth.monolithUrl}${url}`;
100
+ return fetch(absoluteUrl, { ...options, headers });
101
+ },
102
+ [auth]
103
+ );
104
+ }
105
+
106
+ // ../shared/src/hooks/useWorkspaces.ts
107
+ import { useQuery, useMutation } from "@tanstack/react-query";
108
+ function useWorkspaces(page = 1, limit = 100, scope = "organization") {
109
+ const auth = useReplicasAuth();
110
+ const orgFetch = useOrgFetch();
111
+ const orgId = auth.getOrganizationId();
112
+ return useQuery({
113
+ queryKey: ["workspaces", orgId, scope, page, limit],
114
+ queryFn: () => orgFetch(`/v1/workspaces?scope=${scope}&page=${page}&limit=${limit}`),
115
+ enabled: !!orgId
116
+ }, auth.queryClient);
117
+ }
118
+ function useCreateWorkspace() {
119
+ const auth = useReplicasAuth();
120
+ const orgFetch = useOrgFetch();
121
+ const orgId = auth.getOrganizationId();
122
+ return useMutation({
123
+ mutationFn: (request) => orgFetch("/v1/workspaces", {
124
+ method: "POST",
125
+ body: request
126
+ }),
127
+ onSettled: () => {
128
+ auth.queryClient.invalidateQueries({ queryKey: ["workspaces", orgId] });
129
+ }
130
+ }, auth.queryClient);
131
+ }
132
+ function useDeleteWorkspace() {
133
+ const auth = useReplicasAuth();
134
+ const orgFetch = useOrgFetch();
135
+ const orgId = auth.getOrganizationId();
136
+ return useMutation({
137
+ mutationFn: (workspaceId) => orgFetch(`/v1/workspaces/${workspaceId}`, {
138
+ method: "DELETE"
139
+ }),
140
+ onSuccess: (_data, deletedWorkspaceId) => {
141
+ auth.queryClient.invalidateQueries({ queryKey: ["workspaces", orgId] });
142
+ auth.queryClient.removeQueries({ queryKey: ["workspace-status", deletedWorkspaceId] });
143
+ auth.queryClient.removeQueries({ queryKey: ["workspace-chats", deletedWorkspaceId] });
144
+ auth.queryClient.removeQueries({ queryKey: ["chat-history", deletedWorkspaceId] });
145
+ auth.queryClient.removeQueries({ queryKey: ["workspace-previews", deletedWorkspaceId] });
146
+ }
147
+ }, auth.queryClient);
148
+ }
149
+ function useWakeWorkspace() {
150
+ const auth = useReplicasAuth();
151
+ const orgFetch = useOrgFetch();
152
+ const orgId = auth.getOrganizationId();
153
+ return useMutation({
154
+ mutationFn: (workspaceId) => orgFetch(`/v1/workspaces/${workspaceId}/wake`, {
155
+ method: "POST"
156
+ }),
157
+ onSuccess: (_data, workspaceId) => {
158
+ auth.queryClient.invalidateQueries({ queryKey: ["workspaces", orgId] });
159
+ auth.queryClient.invalidateQueries({ queryKey: ["workspace-status", workspaceId] });
160
+ auth.queryClient.invalidateQueries({ queryKey: ["workspace-chats", workspaceId] });
161
+ }
162
+ }, auth.queryClient);
163
+ }
164
+ function useGenerateWorkspaceName() {
165
+ const auth = useReplicasAuth();
166
+ const orgFetch = useOrgFetch();
167
+ return useMutation({
168
+ mutationFn: ({ workspaceId, prompt }) => orgFetch(`/v1/workspaces/${workspaceId}/generate-name`, {
169
+ method: "POST",
170
+ body: { prompt }
171
+ }),
172
+ onSuccess: () => {
173
+ auth.queryClient.invalidateQueries({ queryKey: ["workspaces"] });
174
+ }
175
+ }, auth.queryClient);
176
+ }
177
+
178
+ // ../shared/src/hooks/useWorkspaceEngine.ts
179
+ import { useCallback as useCallback2, useEffect, useMemo, useState } from "react";
180
+ import { useQuery as useQuery2, useMutation as useMutation2 } from "@tanstack/react-query";
181
+ function upsertChat(chats, chat) {
182
+ const existingIndex = chats.findIndex((item) => item.id === chat.id);
183
+ if (existingIndex === -1) {
184
+ return [chat, ...chats].sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
185
+ }
186
+ const next = [...chats];
187
+ next[existingIndex] = chat;
188
+ return next.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
189
+ }
190
+ function patchChat(chats, chatId, patch) {
191
+ let changed = false;
192
+ const next = chats.map((chat) => {
193
+ if (chat.id !== chatId) return chat;
194
+ changed = true;
195
+ return { ...chat, ...patch };
196
+ });
197
+ return changed ? next.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt)) : chats;
198
+ }
199
+ function parseSseEvent(raw) {
200
+ for (const line of raw.split("\n")) {
201
+ if (line.startsWith("data: ")) {
202
+ try {
203
+ return JSON.parse(line.slice(6));
204
+ } catch {
205
+ return null;
206
+ }
207
+ }
208
+ }
209
+ return null;
210
+ }
211
+ function getEventSignature(value) {
212
+ const normalize = (input) => {
213
+ if (Array.isArray(input)) return input.map(normalize);
214
+ if (input && typeof input === "object") {
215
+ const entries = Object.entries(input).sort(([a], [b]) => a.localeCompare(b)).map(([key, val]) => [key, normalize(val)]);
216
+ return Object.fromEntries(entries);
217
+ }
218
+ return input;
219
+ };
220
+ try {
221
+ return JSON.stringify(normalize(value));
222
+ } catch {
223
+ return String(value);
224
+ }
225
+ }
226
+ function useWorkspaceChats(workspaceId) {
227
+ const auth = useReplicasAuth();
228
+ const orgFetch = useOrgFetch();
229
+ return useQuery2({
230
+ queryKey: ["workspace-chats", workspaceId],
231
+ queryFn: () => orgFetch(`/v1/workspaces/${workspaceId}/chats`),
232
+ enabled: !!workspaceId,
233
+ staleTime: 6e4
234
+ }, auth.queryClient);
235
+ }
236
+ function useChatHistory(workspaceId, chatId) {
237
+ const auth = useReplicasAuth();
238
+ const orgFetch = useOrgFetch();
239
+ return useQuery2({
240
+ queryKey: ["chat-history", workspaceId, chatId],
241
+ queryFn: () => orgFetch(`/v1/workspaces/${workspaceId}/chats/${chatId}/history`),
242
+ enabled: !!workspaceId && !!chatId
243
+ }, auth.queryClient);
244
+ }
245
+ function useSendChatMessage(workspaceId, chatId) {
246
+ const auth = useReplicasAuth();
247
+ const orgFetch = useOrgFetch();
248
+ return useMutation2({
249
+ mutationFn: (body) => {
250
+ if (!workspaceId || !chatId) throw new Error("No chat selected");
251
+ return orgFetch(
252
+ `/v1/workspaces/${workspaceId}/chats/${chatId}/messages`,
253
+ { method: "POST", body }
254
+ );
255
+ }
256
+ }, auth.queryClient);
257
+ }
258
+ function useInterruptChat(workspaceId, chatId) {
259
+ const auth = useReplicasAuth();
260
+ const orgFetch = useOrgFetch();
261
+ return useMutation2({
262
+ mutationFn: () => {
263
+ if (!workspaceId || !chatId) throw new Error("No chat selected");
264
+ return orgFetch(
265
+ `/v1/workspaces/${workspaceId}/chats/${chatId}/interrupt`,
266
+ { method: "POST" }
267
+ );
268
+ }
269
+ }, auth.queryClient);
270
+ }
271
+ function useWorkspaceStatus(workspaceId, options) {
272
+ const auth = useReplicasAuth();
273
+ const orgFetch = useOrgFetch();
274
+ const includeDiffs = options?.includeDiffs ?? false;
275
+ const enabled = options?.enabled ?? true;
276
+ return useQuery2({
277
+ queryKey: ["workspace-status", workspaceId, includeDiffs],
278
+ queryFn: () => {
279
+ const url = `/v1/workspaces/${workspaceId}/status${includeDiffs ? "?includeDiffs=true" : ""}`;
280
+ return orgFetch(url);
281
+ },
282
+ enabled: !!workspaceId && enabled,
283
+ staleTime: 3e3,
284
+ retry: 2,
285
+ placeholderData: (previousData) => previousData
286
+ }, auth.queryClient);
287
+ }
288
+ function useWorkspacePreviews(workspaceId) {
289
+ const auth = useReplicasAuth();
290
+ const orgFetch = useOrgFetch();
291
+ return useQuery2({
292
+ queryKey: ["workspace-previews", workspaceId],
293
+ queryFn: () => orgFetch(`/v1/workspaces/${workspaceId}/previews`),
294
+ enabled: !!workspaceId,
295
+ staleTime: 5e3,
296
+ retry: 2
297
+ }, auth.queryClient);
298
+ }
299
+ function useWorkspaceEvents(workspaceId, enabled = true) {
300
+ const auth = useReplicasAuth();
301
+ const rawFetch = useRawOrgFetch();
302
+ const qc = auth.queryClient;
303
+ const [connected, setConnected] = useState(false);
304
+ const handleEvent = useCallback2((event) => {
305
+ if (!workspaceId) return;
306
+ const chatsKey = ["workspace-chats", workspaceId];
307
+ if (event.type === "chat.created" || event.type === "chat.updated") {
308
+ qc.setQueryData(chatsKey, (current) => {
309
+ if (!current) return { chats: [event.payload.chat] };
310
+ return { chats: upsertChat(current.chats, event.payload.chat) };
311
+ });
312
+ return;
313
+ }
314
+ if (event.type === "chat.deleted") {
315
+ qc.setQueryData(chatsKey, (current) => {
316
+ if (!current) return current;
317
+ return { chats: current.chats.filter((chat) => chat.id !== event.payload.chatId) };
318
+ });
319
+ qc.removeQueries({ queryKey: ["chat-history", workspaceId, event.payload.chatId] });
320
+ return;
321
+ }
322
+ if (event.type === "chat.turn.accepted") {
323
+ if (!event.payload.queued) {
324
+ qc.setQueryData(chatsKey, (current) => {
325
+ if (!current) return current;
326
+ return { chats: patchChat(current.chats, event.payload.chatId, { processing: true, updatedAt: event.ts }) };
327
+ });
328
+ }
329
+ return;
330
+ }
331
+ if (event.type === "chat.turn.started") {
332
+ qc.setQueryData(chatsKey, (current) => {
333
+ if (!current) return current;
334
+ return { chats: patchChat(current.chats, event.payload.chatId, { processing: true, updatedAt: event.ts }) };
335
+ });
336
+ return;
337
+ }
338
+ if (event.type === "chat.turn.completed" || event.type === "chat.interrupted") {
339
+ qc.setQueryData(chatsKey, (current) => {
340
+ if (!current) return current;
341
+ return { chats: patchChat(current.chats, event.payload.chatId, { processing: false, updatedAt: event.ts }) };
342
+ });
343
+ qc.invalidateQueries({ queryKey: ["chat-history", workspaceId, event.payload.chatId] });
344
+ return;
345
+ }
346
+ if (event.type === "chat.turn.delta") {
347
+ qc.setQueryData(
348
+ ["chat-history", workspaceId, event.payload.chatId],
349
+ (current) => {
350
+ const incomingSignature = getEventSignature(event.payload.event);
351
+ if (!current) {
352
+ return { thread_id: null, events: [event.payload.event] };
353
+ }
354
+ const hasDuplicate = current.events.some((e) => getEventSignature(e) === incomingSignature);
355
+ if (hasDuplicate) return current;
356
+ return { ...current, events: [...current.events, event.payload.event] };
357
+ }
358
+ );
359
+ return;
360
+ }
361
+ if (event.type === "repo.status.changed") {
362
+ qc.setQueryData(["workspace-status", workspaceId, false], (current) => {
363
+ if (!current) return current;
364
+ return { ...current, repoStatuses: event.payload.repos };
365
+ });
366
+ return;
367
+ }
368
+ if (event.type === "preview.changed") {
369
+ qc.setQueryData(["workspace-previews", workspaceId], {
370
+ previews: event.payload.previews
371
+ });
372
+ return;
373
+ }
374
+ }, [qc, workspaceId]);
375
+ useEffect(() => {
376
+ if (!workspaceId || !enabled) return;
377
+ const controller = new AbortController();
378
+ let cancelled = false;
379
+ const connect = async () => {
380
+ try {
381
+ const response = await rawFetch(`/v1/workspaces/${workspaceId}/events`, {
382
+ headers: { "Accept": "text/event-stream" },
383
+ signal: controller.signal
384
+ });
385
+ if (!response.ok || !response.body) {
386
+ setConnected(false);
387
+ return false;
388
+ }
389
+ setConnected(true);
390
+ const reader = response.body.getReader();
391
+ const decoder = new TextDecoder();
392
+ let buffer = "";
393
+ while (!cancelled) {
394
+ const { done, value } = await reader.read();
395
+ if (done) return false;
396
+ buffer += decoder.decode(value, { stream: true });
397
+ const chunks = buffer.split("\n\n");
398
+ buffer = chunks.pop() ?? "";
399
+ for (const chunk of chunks) {
400
+ const event = parseSseEvent(chunk);
401
+ if (event) handleEvent(event);
402
+ }
403
+ }
404
+ return false;
405
+ } catch {
406
+ setConnected(false);
407
+ return false;
408
+ }
409
+ };
410
+ const run = async () => {
411
+ while (!cancelled) {
412
+ await connect();
413
+ if (cancelled) break;
414
+ await new Promise((resolve) => setTimeout(resolve, 1e3));
415
+ }
416
+ };
417
+ run().catch(() => setConnected(false));
418
+ return () => {
419
+ cancelled = true;
420
+ controller.abort();
421
+ setConnected(false);
422
+ };
423
+ }, [enabled, handleEvent, rawFetch, workspaceId]);
424
+ return useMemo(() => ({ connected }), [connected]);
425
+ }
426
+
427
+ // ../shared/src/hooks/useRepositories.ts
428
+ import { useQuery as useQuery3 } from "@tanstack/react-query";
429
+ function useRepositories() {
430
+ const auth = useReplicasAuth();
431
+ const orgFetch = useOrgFetch();
432
+ const orgId = auth.getOrganizationId();
433
+ return useQuery3({
434
+ queryKey: ["repositories", orgId],
435
+ queryFn: () => orgFetch("/v1/repositories"),
436
+ enabled: !!orgId,
437
+ staleTime: 2 * 60 * 1e3
438
+ }, auth.queryClient);
439
+ }
440
+ function useRepositorySets() {
441
+ const auth = useReplicasAuth();
442
+ const orgFetch = useOrgFetch();
443
+ const orgId = auth.getOrganizationId();
444
+ return useQuery3({
445
+ queryKey: ["repository-sets", orgId],
446
+ queryFn: () => orgFetch("/v1/repository-sets"),
447
+ enabled: !!orgId
448
+ }, auth.queryClient);
449
+ }
450
+
451
+ // ../shared/src/hooks/useEnvironment.ts
452
+ import { useQuery as useQuery4 } from "@tanstack/react-query";
453
+ function useEnvironmentSkills() {
454
+ const auth = useReplicasAuth();
455
+ const orgFetch = useOrgFetch();
456
+ const orgId = auth.getOrganizationId();
457
+ return useQuery4({
458
+ queryKey: ["env-skills", orgId],
459
+ queryFn: () => orgFetch("/v1/environment/skills"),
460
+ enabled: !!orgId
461
+ }, auth.queryClient);
462
+ }
463
+ function useEnvironmentVariables() {
464
+ const auth = useReplicasAuth();
465
+ const orgFetch = useOrgFetch();
466
+ const orgId = auth.getOrganizationId();
467
+ return useQuery4({
468
+ queryKey: ["env-variables", orgId],
469
+ queryFn: () => orgFetch("/v1/environment/variables"),
470
+ enabled: !!orgId
471
+ }, auth.queryClient);
472
+ }
473
+ function useEnvironmentFiles() {
474
+ const auth = useReplicasAuth();
475
+ const orgFetch = useOrgFetch();
476
+ const orgId = auth.getOrganizationId();
477
+ return useQuery4({
478
+ queryKey: ["env-files", orgId],
479
+ queryFn: () => orgFetch("/v1/environment/files"),
480
+ enabled: !!orgId
481
+ }, auth.queryClient);
482
+ }
483
+
484
+ // src/interactive/components/StatusBar.tsx
485
+ import { jsx, jsxs } from "@opentui/react/jsx-runtime";
486
+ var KEYBINDS = {
487
+ sidebar: "j/k nav \u21B5 select d del a add w wake",
488
+ "chat-tabs": "\u2190/\u2192 switch tabs",
489
+ "chat-history": "j/k scroll",
490
+ "chat-input": "\u21B5 send \u21E5 plan/build",
491
+ info: "j/k scroll o dashboard w wake"
492
+ };
493
+ function StatusBar({ focusPanel }) {
494
+ return /* @__PURE__ */ jsxs(
495
+ "box",
496
+ {
497
+ height: 1,
498
+ backgroundColor: "#111111",
499
+ paddingX: 1,
500
+ flexDirection: "row",
501
+ justifyContent: "space-between",
502
+ width: "100%",
503
+ children: [
504
+ /* @__PURE__ */ jsx("text", { children: /* @__PURE__ */ jsx("span", { fg: "#66bb6a", children: /* @__PURE__ */ jsx("strong", { children: "Replicas" }) }) }),
505
+ /* @__PURE__ */ jsxs("text", { children: [
506
+ /* @__PURE__ */ jsxs("span", { fg: "#555555", children: [
507
+ KEYBINDS[focusPanel],
508
+ " "
509
+ ] }),
510
+ /* @__PURE__ */ jsxs("span", { fg: "#888888", children: [
511
+ "\u21E7",
512
+ "Tab"
513
+ ] }),
514
+ /* @__PURE__ */ jsx("span", { fg: "#555555", children: " panels " }),
515
+ /* @__PURE__ */ jsx("span", { fg: "#888888", children: "^C" }),
516
+ /* @__PURE__ */ jsx("span", { fg: "#555555", children: " quit" })
517
+ ] })
518
+ ]
519
+ }
520
+ );
521
+ }
522
+
523
+ // src/interactive/components/WorkspaceSidebar.tsx
524
+ import { useState as useState2, useMemo as useMemo2, useCallback as useCallback3 } from "react";
525
+ import { useKeyboard, useTerminalDimensions } from "@opentui/react";
526
+ import { jsx as jsx2, jsxs as jsxs2 } from "@opentui/react/jsx-runtime";
527
+ function flattenGroups(groups, expandedGroups) {
528
+ const items = [];
529
+ for (const group of groups) {
530
+ if (group.id === "__ungrouped__" && group.workspaces.length === 0) continue;
531
+ const expanded = expandedGroups.has(group.id);
532
+ items.push({ type: "group", id: group.id, name: group.name, groupType: group.type, expanded });
533
+ if (expanded) {
534
+ for (const ws of group.workspaces) {
535
+ items.push({
536
+ type: "workspace",
537
+ workspaceId: ws.id,
538
+ name: ws.name,
539
+ status: ws.status,
540
+ isMock: ws.status === "preparing"
541
+ });
542
+ }
543
+ if (group.id !== "__ungrouped__") {
544
+ items.push({ type: "add", groupId: group.id, groupType: group.type });
545
+ }
546
+ }
547
+ }
548
+ return items;
549
+ }
550
+ function truncate(text, maxLen) {
551
+ return text.length > maxLen ? text.slice(0, maxLen - 1) + "\u2026" : text;
552
+ }
553
+ function WorkspaceSidebar({
554
+ groups,
555
+ selectedWorkspaceId,
556
+ mockIds,
557
+ onSelectWorkspace,
558
+ onCreateWorkspace,
559
+ onDeleteWorkspace,
560
+ onWakeWorkspace,
561
+ wakingWorkspaceId,
562
+ focused,
563
+ loading
564
+ }) {
565
+ const { height: termHeight } = useTerminalDimensions();
566
+ const [expandedGroups, setExpandedGroups] = useState2(
567
+ () => new Set(groups.map((g) => g.id))
568
+ );
569
+ const [cursorIndex, setCursorIndex] = useState2(0);
570
+ const [scrollOffset, setScrollOffset] = useState2(0);
571
+ const [confirmDelete, setConfirmDelete] = useState2(null);
572
+ const expandedWithNew = useMemo2(() => {
573
+ const next = new Set(expandedGroups);
574
+ for (const g of groups) {
575
+ if (!next.has(g.id)) next.add(g.id);
576
+ }
577
+ return next;
578
+ }, [expandedGroups, groups]);
579
+ const items = useMemo2(() => flattenGroups(groups, expandedWithNew), [groups, expandedWithNew]);
580
+ const clampedCursor = Math.min(cursorIndex, Math.max(0, items.length - 1));
581
+ if (clampedCursor !== cursorIndex) {
582
+ setCursorIndex(clampedCursor);
583
+ }
584
+ const visibleHeight = Math.max(1, termHeight - 4);
585
+ const adjustScroll = useCallback3(
586
+ (newCursor) => {
587
+ let newOffset = scrollOffset;
588
+ if (newCursor < newOffset) {
589
+ newOffset = newCursor;
590
+ } else if (newCursor >= newOffset + visibleHeight) {
591
+ newOffset = newCursor - visibleHeight + 1;
592
+ }
593
+ setScrollOffset(newOffset);
594
+ },
595
+ [scrollOffset, visibleHeight]
596
+ );
597
+ const moveCursor = useCallback3(
598
+ (next) => {
599
+ const clamped = Math.max(0, Math.min(items.length - 1, next));
600
+ setCursorIndex(clamped);
601
+ adjustScroll(clamped);
602
+ },
603
+ [items.length, adjustScroll]
604
+ );
605
+ const handleAction = useCallback3(
606
+ (item) => {
607
+ if (item.type === "group") {
608
+ setExpandedGroups((prev) => {
609
+ const next = new Set(prev);
610
+ if (next.has(item.id)) {
611
+ next.delete(item.id);
612
+ } else {
613
+ next.add(item.id);
614
+ }
615
+ return next;
616
+ });
617
+ } else if (item.type === "workspace") {
618
+ onSelectWorkspace(item.workspaceId);
619
+ } else if (item.type === "add") {
620
+ onCreateWorkspace(item.groupId, item.groupType);
621
+ }
622
+ },
623
+ [onSelectWorkspace, onCreateWorkspace]
624
+ );
625
+ const handleDelete = useCallback3(() => {
626
+ const item = items[cursorIndex];
627
+ if (!item || item.type !== "workspace") return;
628
+ if (item.isMock) return;
629
+ if (confirmDelete === item.workspaceId) {
630
+ onDeleteWorkspace(item.workspaceId);
631
+ setConfirmDelete(null);
632
+ } else {
633
+ setConfirmDelete(item.workspaceId);
634
+ }
635
+ }, [items, cursorIndex, confirmDelete, onDeleteWorkspace]);
636
+ const moveCursorAndClearConfirm = useCallback3(
637
+ (next) => {
638
+ setConfirmDelete(null);
639
+ moveCursor(next);
640
+ },
641
+ [moveCursor]
642
+ );
643
+ useKeyboard((key) => {
644
+ if (!focused) return;
645
+ if (confirmDelete) {
646
+ if (key.name === "y") {
647
+ onDeleteWorkspace(confirmDelete);
648
+ setConfirmDelete(null);
649
+ return;
650
+ }
651
+ if (key.name === "n" || key.name === "escape") {
652
+ setConfirmDelete(null);
653
+ return;
654
+ }
655
+ return;
656
+ }
657
+ if (key.name === "up" || key.name === "k") {
658
+ moveCursorAndClearConfirm(cursorIndex - 1);
659
+ return;
660
+ }
661
+ if (key.name === "down" || key.name === "j") {
662
+ moveCursorAndClearConfirm(cursorIndex + 1);
663
+ return;
664
+ }
665
+ if (key.name === "g" && !key.shift) {
666
+ moveCursorAndClearConfirm(0);
667
+ return;
668
+ }
669
+ if (key.name === "g" && key.shift) {
670
+ moveCursorAndClearConfirm(items.length - 1);
671
+ return;
672
+ }
673
+ if (key.name === "enter" || key.name === "return" || key.name === "l") {
674
+ const item = items[cursorIndex];
675
+ if (item) handleAction(item);
676
+ return;
677
+ }
678
+ if (key.name === "h") {
679
+ const item = items[cursorIndex];
680
+ if (item?.type === "group" && item.expanded) {
681
+ handleAction(item);
682
+ }
683
+ return;
684
+ }
685
+ if (key.name === "d" || key.name === "x" || key.name === "delete") {
686
+ handleDelete();
687
+ return;
688
+ }
689
+ if (key.name === "a") {
690
+ const item = items[cursorIndex];
691
+ if (item?.type === "add") {
692
+ onCreateWorkspace(item.groupId, item.groupType);
693
+ }
694
+ return;
695
+ }
696
+ if (key.name === "w") {
697
+ const item = items[cursorIndex];
698
+ if (item?.type === "workspace" && item.status === "sleeping" && wakingWorkspaceId !== item.workspaceId) {
699
+ onWakeWorkspace(item.workspaceId);
700
+ }
701
+ return;
702
+ }
703
+ });
704
+ const handleItemClick = useCallback3(
705
+ (globalIndex) => {
706
+ setConfirmDelete(null);
707
+ setCursorIndex(globalIndex);
708
+ const item = items[globalIndex];
709
+ if (item) handleAction(item);
710
+ },
711
+ [items, handleAction]
712
+ );
713
+ const handleDeleteClick = useCallback3(
714
+ (workspaceId, e) => {
715
+ e?.preventDefault?.();
716
+ if (confirmDelete === workspaceId) {
717
+ onDeleteWorkspace(workspaceId);
718
+ setConfirmDelete(null);
719
+ } else {
720
+ setConfirmDelete(workspaceId);
721
+ }
722
+ },
723
+ [confirmDelete, onDeleteWorkspace]
724
+ );
725
+ const visibleItems = items.slice(scrollOffset, scrollOffset + visibleHeight);
726
+ if (loading) {
727
+ return /* @__PURE__ */ jsx2(
728
+ "box",
729
+ {
730
+ width: 28,
731
+ border: true,
732
+ borderStyle: "rounded",
733
+ borderColor: "#333333",
734
+ title: "Workspaces",
735
+ titleAlignment: "center",
736
+ flexDirection: "column",
737
+ backgroundColor: "#000000",
738
+ children: /* @__PURE__ */ jsx2("text", { fg: "#666666", children: "Loading..." })
739
+ }
740
+ );
741
+ }
742
+ return /* @__PURE__ */ jsxs2(
743
+ "box",
744
+ {
745
+ width: 28,
746
+ border: true,
747
+ borderStyle: "rounded",
748
+ borderColor: focused ? "#66bb6a" : "#333333",
749
+ title: "Workspaces",
750
+ titleAlignment: "center",
751
+ flexDirection: "column",
752
+ backgroundColor: "#000000",
753
+ children: [
754
+ items.length === 0 ? /* @__PURE__ */ jsx2("text", { fg: "#666666", paddingX: 1, children: "No repositories" }) : visibleItems.map((item, vi) => {
755
+ const globalIndex = scrollOffset + vi;
756
+ const isCursor = globalIndex === cursorIndex;
757
+ if (item.type === "group") {
758
+ const chevron = item.expanded ? "\u25BE" : "\u25B8";
759
+ const label = truncate(item.name, 20);
760
+ const suffix = item.groupType === "set" ? " Set" : "";
761
+ return /* @__PURE__ */ jsx2(
762
+ "box",
763
+ {
764
+ height: 1,
765
+ backgroundColor: isCursor ? "#1a1a1a" : "#151515",
766
+ paddingX: 1,
767
+ onMouseDown: () => handleItemClick(globalIndex),
768
+ children: /* @__PURE__ */ jsxs2("text", { children: [
769
+ /* @__PURE__ */ jsxs2("span", { fg: isCursor ? "#ffffff" : "#888888", children: [
770
+ chevron,
771
+ " "
772
+ ] }),
773
+ /* @__PURE__ */ jsx2("span", { fg: "#ffffff", children: /* @__PURE__ */ jsx2("strong", { children: label }) }),
774
+ suffix && /* @__PURE__ */ jsxs2("span", { fg: "#555555", children: [
775
+ " ",
776
+ suffix
777
+ ] })
778
+ ] })
779
+ },
780
+ `g-${item.id}`
781
+ );
782
+ }
783
+ if (item.type === "workspace") {
784
+ const isSelected = item.workspaceId === selectedWorkspaceId;
785
+ const isConfirming = confirmDelete === item.workspaceId;
786
+ const dot = item.status === "active" ? "\u25CF" : item.status === "preparing" ? "\u25CC" : "\u25CB";
787
+ const dotColor = item.status === "active" ? "#66bb6a" : item.status === "preparing" ? "#ffaa00" : "#666666";
788
+ const nameColor = isSelected ? "#66bb6a" : "#cccccc";
789
+ const itemBg = isConfirming ? "#331111" : isCursor ? "#1a1a1a" : isSelected ? "#0a1a0a" : "#0a0a0a";
790
+ if (isConfirming) {
791
+ return /* @__PURE__ */ jsxs2("box", { height: 1, backgroundColor: "#331111", paddingX: 1, flexDirection: "row", gap: 1, children: [
792
+ /* @__PURE__ */ jsx2("text", { children: /* @__PURE__ */ jsx2("span", { fg: "#ff4444", children: "Delete?" }) }),
793
+ /* @__PURE__ */ jsx2("box", { onMouseDown: () => {
794
+ onDeleteWorkspace(item.workspaceId);
795
+ setConfirmDelete(null);
796
+ }, children: /* @__PURE__ */ jsx2("text", { children: /* @__PURE__ */ jsx2("span", { fg: "#66bb6a", children: "[y]" }) }) }),
797
+ /* @__PURE__ */ jsx2("box", { onMouseDown: () => setConfirmDelete(null), children: /* @__PURE__ */ jsx2("text", { children: /* @__PURE__ */ jsx2("span", { fg: "#ff4444", children: "[n]" }) }) })
798
+ ] }, `w-${item.workspaceId}`);
799
+ }
800
+ return /* @__PURE__ */ jsxs2(
801
+ "box",
802
+ {
803
+ height: 1,
804
+ backgroundColor: itemBg,
805
+ paddingX: 1,
806
+ flexDirection: "row",
807
+ justifyContent: "space-between",
808
+ onMouseDown: () => handleItemClick(globalIndex),
809
+ children: [
810
+ /* @__PURE__ */ jsxs2("text", { children: [
811
+ /* @__PURE__ */ jsxs2("span", { fg: dotColor, children: [
812
+ " ",
813
+ dot,
814
+ " "
815
+ ] }),
816
+ /* @__PURE__ */ jsx2("span", { fg: nameColor, children: truncate(item.name, 17) })
817
+ ] }),
818
+ !item.isMock && /* @__PURE__ */ jsx2("box", { onMouseDown: (e) => handleDeleteClick(item.workspaceId, e), children: /* @__PURE__ */ jsx2("text", { children: /* @__PURE__ */ jsx2("span", { fg: isCursor ? "#ff4444" : "#222222", children: "\u2717" }) }) })
819
+ ]
820
+ },
821
+ `w-${item.workspaceId}`
822
+ );
823
+ }
824
+ return /* @__PURE__ */ jsx2(
825
+ "box",
826
+ {
827
+ height: 1,
828
+ backgroundColor: isCursor ? "#1a1a1a" : "#0a0a0a",
829
+ paddingX: 1,
830
+ onMouseDown: () => handleItemClick(globalIndex),
831
+ children: /* @__PURE__ */ jsxs2("text", { children: [
832
+ /* @__PURE__ */ jsx2("span", { fg: isCursor ? "#66bb6a" : "#444444", children: " + " }),
833
+ /* @__PURE__ */ jsx2("span", { fg: isCursor ? "#888888" : "#333333", children: "New workspace" })
834
+ ] })
835
+ },
836
+ `a-${item.groupId}`
837
+ );
838
+ }),
839
+ focused && /* @__PURE__ */ jsx2("box", { height: 1, paddingX: 1, backgroundColor: "#111111", children: /* @__PURE__ */ jsxs2("text", { children: [
840
+ /* @__PURE__ */ jsx2("span", { fg: "#555555", children: "j/k:nav " }),
841
+ /* @__PURE__ */ jsx2("span", { fg: "#555555", children: "d:del " }),
842
+ /* @__PURE__ */ jsx2("span", { fg: "#555555", children: "a:add " }),
843
+ /* @__PURE__ */ jsx2("span", { fg: "#555555", children: "w:wake" })
844
+ ] }) })
845
+ ]
846
+ }
847
+ );
848
+ }
849
+
850
+ // src/interactive/components/ChatArea.tsx
851
+ import { useRef, useEffect as useEffect2 } from "react";
852
+ import { useKeyboard as useKeyboard2 } from "@opentui/react";
853
+
854
+ // src/interactive/components/ChatMessage.tsx
855
+ import { useState as useState3 } from "react";
856
+ import { SyntaxStyle } from "@opentui/core";
857
+
858
+ // src/interactive/components/Spinner.tsx
859
+ import "opentui-spinner/react";
860
+ import { createPulse } from "opentui-spinner";
861
+ import { jsx as jsx3, jsxs as jsxs3 } from "@opentui/react/jsx-runtime";
862
+ var thinkingColor = createPulse(["#66bb6a", "#4caf50", "#2e7d32"], 0.5);
863
+ function SpinnerLabel({ color, label }) {
864
+ return /* @__PURE__ */ jsxs3("box", { flexDirection: "row", gap: 1, children: [
865
+ /* @__PURE__ */ jsx3("spinner", { name: "dots", color: color ?? thinkingColor, interval: 80 }),
866
+ label && /* @__PURE__ */ jsx3("text", { children: /* @__PURE__ */ jsx3("span", { fg: color ?? "#66bb6a", children: label }) })
867
+ ] });
868
+ }
869
+
870
+ // src/interactive/components/ChatMessage.tsx
871
+ import { jsx as jsx4, jsxs as jsxs4 } from "@opentui/react/jsx-runtime";
872
+ var sharedSyntaxStyle = null;
873
+ function getSyntaxStyle() {
874
+ if (!sharedSyntaxStyle) {
875
+ sharedSyntaxStyle = SyntaxStyle.fromTheme([
876
+ // Markdown text styling
877
+ { scope: ["markup.heading", "markup.heading.1", "markup.heading.2", "markup.heading.3"], style: { foreground: "#66bb6a", bold: true } },
878
+ { scope: ["markup.bold", "markup.strong"], style: { foreground: "#ffffff", bold: true } },
879
+ { scope: ["markup.italic", "markup.emphasis"], style: { foreground: "#e0e0e0", italic: true } },
880
+ { scope: ["markup.link"], style: { foreground: "#7dcfff", underline: true } },
881
+ { scope: ["markup.link.url"], style: { foreground: "#7dcfff", underline: true } },
882
+ { scope: ["markup.link.text"], style: { foreground: "#66bb6a", underline: true } },
883
+ { scope: ["markup.link.label"], style: { foreground: "#66bb6a", underline: true } },
884
+ { scope: ["markup.list"], style: { foreground: "#66bb6a" } },
885
+ { scope: ["markup.quote"], style: { foreground: "#888888", italic: true } },
886
+ { scope: ["markup.raw", "markup.raw.block"], style: { foreground: "#bb9af7" } },
887
+ { scope: ["markup.raw.inline"], style: { foreground: "#bb9af7", background: "#1a1a2e" } },
888
+ // Code syntax highlighting
889
+ { scope: ["keyword", "keyword.control", "keyword.operator"], style: { foreground: "#bb9af7" } },
890
+ { scope: ["string", "string.quoted"], style: { foreground: "#9ece6a" } },
891
+ { scope: ["comment", "comment.line", "comment.block"], style: { foreground: "#555555", italic: true } },
892
+ { scope: ["constant", "constant.numeric", "constant.language"], style: { foreground: "#ff9e64" } },
893
+ { scope: ["entity.name.function", "support.function"], style: { foreground: "#7aa2f7" } },
894
+ { scope: ["entity.name.type", "support.type"], style: { foreground: "#2ac3de" } },
895
+ { scope: ["variable", "variable.other"], style: { foreground: "#c0caf5" } },
896
+ { scope: ["punctuation"], style: { foreground: "#888888" } }
897
+ ]);
898
+ }
899
+ return sharedSyntaxStyle;
900
+ }
901
+ var AGENT_LABELS = {
902
+ claude: "Claude",
903
+ codex: "Codex"
904
+ };
905
+ function truncate2(text, maxLen) {
906
+ return text.length > maxLen ? text.slice(0, maxLen - 1) + "\u2026" : text;
907
+ }
908
+ function StatusIcon({ status }) {
909
+ if (status === "completed") return /* @__PURE__ */ jsxs4("span", { fg: "#66bb6a", children: [
910
+ " ",
911
+ "\u2713"
912
+ ] });
913
+ if (status === "failed") return /* @__PURE__ */ jsxs4("span", { fg: "#ff4444", children: [
914
+ " ",
915
+ "\u2717"
916
+ ] });
917
+ if (status === "in_progress") return /* @__PURE__ */ jsxs4("span", { fg: "#ffaa00", children: [
918
+ " ",
919
+ "\u2026"
920
+ ] });
921
+ return null;
922
+ }
923
+ function ActionLine({
924
+ label,
925
+ arg,
926
+ status,
927
+ expandable,
928
+ onToggle,
929
+ expanded
930
+ }) {
931
+ return /* @__PURE__ */ jsxs4(
932
+ "box",
933
+ {
934
+ paddingX: 1,
935
+ flexDirection: "row",
936
+ justifyContent: "space-between",
937
+ onMouseDown: onToggle,
938
+ children: [
939
+ /* @__PURE__ */ jsxs4("text", { children: [
940
+ /* @__PURE__ */ jsx4("span", { fg: "#ffffff", children: /* @__PURE__ */ jsx4("strong", { children: label }) }),
941
+ /* @__PURE__ */ jsxs4("span", { fg: "#cccccc", children: [
942
+ " ",
943
+ arg.replace(/\n/g, " ").replace(/\s+/g, " ")
944
+ ] }),
945
+ /* @__PURE__ */ jsx4(StatusIcon, { status })
946
+ ] }),
947
+ expandable && /* @__PURE__ */ jsx4("text", { children: /* @__PURE__ */ jsx4("span", { fg: "#555555", children: expanded ? "\u25BE" : "\u25B8" }) })
948
+ ]
949
+ }
950
+ );
951
+ }
952
+ function ExpandableAction({
953
+ label,
954
+ arg,
955
+ status,
956
+ children
957
+ }) {
958
+ const [expanded, setExpanded] = useState3(false);
959
+ return /* @__PURE__ */ jsxs4("box", { flexDirection: "column", children: [
960
+ /* @__PURE__ */ jsx4(
961
+ ActionLine,
962
+ {
963
+ label,
964
+ arg,
965
+ status,
966
+ expandable: true,
967
+ expanded,
968
+ onToggle: () => setExpanded((e) => !e)
969
+ }
970
+ ),
971
+ expanded && children
972
+ ] });
973
+ }
974
+ function ChatMessage({ message, provider }) {
975
+ switch (message.type) {
976
+ case "user":
977
+ if (message.content === "Request interrupted") {
978
+ return /* @__PURE__ */ jsx4("box", { paddingX: 1, justifyContent: "center", children: /* @__PURE__ */ jsx4("text", { fg: "#555555", children: "--- Request interrupted ---" }) });
979
+ }
980
+ return /* @__PURE__ */ jsxs4(
981
+ "box",
982
+ {
983
+ flexDirection: "column",
984
+ backgroundColor: "#151515",
985
+ paddingX: 2,
986
+ paddingY: 1,
987
+ marginX: 1,
988
+ children: [
989
+ /* @__PURE__ */ jsx4("text", { children: /* @__PURE__ */ jsx4("span", { fg: "#66bb6a", children: /* @__PURE__ */ jsx4("strong", { children: "You" }) }) }),
990
+ /* @__PURE__ */ jsx4("text", { fg: "#ffffff", selectable: true, children: message.content })
991
+ ]
992
+ }
993
+ );
994
+ case "agent":
995
+ return /* @__PURE__ */ jsxs4("box", { flexDirection: "column", paddingX: 1, children: [
996
+ /* @__PURE__ */ jsx4("text", { children: /* @__PURE__ */ jsx4("span", { fg: "#66bb6a", children: /* @__PURE__ */ jsx4("strong", { children: AGENT_LABELS[provider] }) }) }),
997
+ /* @__PURE__ */ jsx4(
998
+ "markdown",
999
+ {
1000
+ content: message.content,
1001
+ streaming: true,
1002
+ syntaxStyle: getSyntaxStyle(),
1003
+ conceal: true,
1004
+ fg: "#ffffff",
1005
+ bg: "#000000"
1006
+ }
1007
+ )
1008
+ ] });
1009
+ case "reasoning":
1010
+ return /* @__PURE__ */ jsx4("box", { paddingX: 1, children: /* @__PURE__ */ jsx4(SpinnerLabel, { color: "#555555", label: "thinking" }) });
1011
+ case "command": {
1012
+ const cmdArg = truncate2(message.command, 60);
1013
+ const exitSuffix = message.exitCode !== void 0 && message.exitCode !== 0 ? ` (exit ${message.exitCode})` : "";
1014
+ if (!message.output) {
1015
+ return /* @__PURE__ */ jsx4(ActionLine, { label: "Command", arg: cmdArg + exitSuffix, status: message.status });
1016
+ }
1017
+ return /* @__PURE__ */ jsx4(ExpandableAction, { label: "Command", arg: cmdArg + exitSuffix, status: message.status, children: /* @__PURE__ */ jsx4("box", { paddingLeft: 2, paddingX: 1, children: /* @__PURE__ */ jsx4("text", { fg: "#888888", selectable: true, children: message.output.length > 500 ? message.output.slice(0, 500) + "\n\u2026" : message.output }) }) });
1018
+ }
1019
+ case "file_change": {
1020
+ const count = message.changes.length;
1021
+ return /* @__PURE__ */ jsx4(ExpandableAction, { label: "Files", arg: `${count}`, status: message.status, children: /* @__PURE__ */ jsx4("box", { flexDirection: "column", paddingLeft: 2, paddingX: 1, children: message.changes.map((change, i) => /* @__PURE__ */ jsxs4("text", { children: [
1022
+ /* @__PURE__ */ jsx4(
1023
+ "span",
1024
+ {
1025
+ fg: change.kind === "add" ? "#66bb6a" : change.kind === "delete" ? "#ff4444" : "#ffaa00",
1026
+ children: change.kind === "add" ? "+" : change.kind === "delete" ? "-" : "~"
1027
+ }
1028
+ ),
1029
+ /* @__PURE__ */ jsxs4("span", { fg: "#cccccc", children: [
1030
+ " ",
1031
+ change.path
1032
+ ] })
1033
+ ] }, i)) }) });
1034
+ }
1035
+ case "tool_call": {
1036
+ const toolArg = truncate2(message.tool, 50);
1037
+ if (message.output || message.input) {
1038
+ return /* @__PURE__ */ jsx4(ExpandableAction, { label: "Tool", arg: toolArg, status: message.status, children: /* @__PURE__ */ jsxs4("box", { flexDirection: "column", paddingLeft: 2, paddingX: 1, children: [
1039
+ message.input && /* @__PURE__ */ jsx4("text", { fg: "#888888", selectable: true, children: typeof message.input === "string" ? truncate2(message.input, 300) : truncate2(JSON.stringify(message.input, null, 2), 300) }),
1040
+ message.output && /* @__PURE__ */ jsx4("text", { fg: "#888888", selectable: true, children: truncate2(message.output, 300) })
1041
+ ] }) });
1042
+ }
1043
+ return /* @__PURE__ */ jsx4(ActionLine, { label: "Tool", arg: toolArg, status: message.status });
1044
+ }
1045
+ case "web_search":
1046
+ return /* @__PURE__ */ jsx4(ActionLine, { label: "Search", arg: truncate2(message.query, 50), status: message.status });
1047
+ case "todo_list": {
1048
+ const completed = message.items.filter((i) => i.completed).length;
1049
+ const total = message.items.length;
1050
+ return /* @__PURE__ */ jsxs4("box", { flexDirection: "column", paddingX: 1, children: [
1051
+ /* @__PURE__ */ jsx4("text", { children: /* @__PURE__ */ jsx4("span", { fg: "#888888", children: /* @__PURE__ */ jsxs4("strong", { children: [
1052
+ "Plan (",
1053
+ completed,
1054
+ "/",
1055
+ total,
1056
+ ")"
1057
+ ] }) }) }),
1058
+ message.items.map((item, i) => /* @__PURE__ */ jsxs4("text", { children: [
1059
+ /* @__PURE__ */ jsx4("span", { fg: item.completed ? "#66bb6a" : "#555555", children: item.completed ? " \u2611" : " \u2610" }),
1060
+ /* @__PURE__ */ jsxs4("span", { fg: "#ffffff", children: [
1061
+ " ",
1062
+ item.text
1063
+ ] })
1064
+ ] }, i))
1065
+ ] });
1066
+ }
1067
+ case "subagent":
1068
+ return /* @__PURE__ */ jsx4(ActionLine, { label: "Agent", arg: truncate2(message.description, 50), status: message.status });
1069
+ case "error":
1070
+ return /* @__PURE__ */ jsx4("box", { paddingX: 1, children: /* @__PURE__ */ jsx4("text", { fg: "#ff4444", selectable: true, children: message.message }) });
1071
+ case "skill":
1072
+ return /* @__PURE__ */ jsx4(ActionLine, { label: "Skill", arg: message.skillName + (message.args ? ` ${message.args}` : ""), status: message.status });
1073
+ case "patch": {
1074
+ const opCount = message.operations.length;
1075
+ return /* @__PURE__ */ jsx4(ExpandableAction, { label: "Patch", arg: `${opCount}`, children: /* @__PURE__ */ jsx4("box", { flexDirection: "column", paddingLeft: 2, paddingX: 1, children: message.operations.map((op, i) => /* @__PURE__ */ jsxs4("text", { children: [
1076
+ /* @__PURE__ */ jsx4(
1077
+ "span",
1078
+ {
1079
+ fg: op.action === "add" ? "#66bb6a" : op.action === "delete" ? "#ff4444" : "#ffaa00",
1080
+ children: op.action === "add" ? "+" : op.action === "delete" ? "-" : "~"
1081
+ }
1082
+ ),
1083
+ /* @__PURE__ */ jsxs4("span", { fg: "#cccccc", children: [
1084
+ " ",
1085
+ op.path
1086
+ ] })
1087
+ ] }, i)) }) });
1088
+ }
1089
+ default:
1090
+ return null;
1091
+ }
1092
+ }
1093
+
1094
+ // src/interactive/components/ChatArea.tsx
1095
+ import { jsx as jsx5, jsxs as jsxs5 } from "@opentui/react/jsx-runtime";
1096
+ var textareaKeyBindings = [
1097
+ { name: "return", action: "submit" },
1098
+ { name: "return", shift: true, action: "newline" },
1099
+ { name: "return", ctrl: true, action: "newline" },
1100
+ { name: "return", meta: true, action: "newline" }
1101
+ ];
1102
+ var MODE_COLORS = {
1103
+ build: { border: "#66bb6a", text: "#66bb6a" },
1104
+ plan: { border: "#d97706", text: "#d97706" }
1105
+ };
1106
+ function ChatArea({
1107
+ chats,
1108
+ selectedChatId,
1109
+ displayMessages,
1110
+ onSelectChat,
1111
+ onSendMessage,
1112
+ onFocus,
1113
+ focusPanel,
1114
+ taskMode,
1115
+ isProcessing,
1116
+ loading
1117
+ }) {
1118
+ const textareaRef = useRef(null);
1119
+ const scrollboxRef = useRef(null);
1120
+ const isAtBottomRef = useRef(true);
1121
+ const onSendMessageRef = useRef(onSendMessage);
1122
+ onSendMessageRef.current = onSendMessage;
1123
+ const inputFocused = focusPanel === "chat-input";
1124
+ const tabsFocused = focusPanel === "chat-tabs";
1125
+ const historyFocused = focusPanel === "chat-history";
1126
+ const anyFocused = inputFocused || tabsFocused || historyFocused;
1127
+ const modeColors = MODE_COLORS[taskMode];
1128
+ useEffect2(() => {
1129
+ const scrollbox = scrollboxRef.current;
1130
+ if (!scrollbox) return;
1131
+ let lastScrollHeight = 0;
1132
+ const tick = () => {
1133
+ const viewportHeight = scrollbox.viewport?.height ?? 0;
1134
+ const atBottom = scrollbox.scrollTop + viewportHeight >= scrollbox.scrollHeight - 3;
1135
+ if (atBottom) {
1136
+ isAtBottomRef.current = true;
1137
+ } else if (scrollbox.scrollHeight === lastScrollHeight) {
1138
+ isAtBottomRef.current = false;
1139
+ }
1140
+ if (isAtBottomRef.current && scrollbox.scrollHeight > lastScrollHeight) {
1141
+ scrollbox.scrollTo(scrollbox.scrollHeight);
1142
+ }
1143
+ lastScrollHeight = scrollbox.scrollHeight;
1144
+ };
1145
+ const interval = setInterval(tick, 100);
1146
+ return () => clearInterval(interval);
1147
+ }, []);
1148
+ useEffect2(() => {
1149
+ const textarea = textareaRef.current;
1150
+ if (!textarea) return;
1151
+ textarea.onSubmit = () => {
1152
+ const message = textarea.plainText.trim();
1153
+ if (message) {
1154
+ isAtBottomRef.current = true;
1155
+ onSendMessageRef.current(message);
1156
+ textarea.setText("");
1157
+ }
1158
+ };
1159
+ return () => {
1160
+ textarea.onSubmit = void 0;
1161
+ };
1162
+ }, []);
1163
+ useKeyboard2((key) => {
1164
+ if (!historyFocused) return;
1165
+ const scrollbox = scrollboxRef.current;
1166
+ if (!scrollbox) return;
1167
+ if (key.name === "j" || key.name === "down") {
1168
+ scrollbox.scrollBy(3);
1169
+ return;
1170
+ }
1171
+ if (key.name === "k" || key.name === "up") {
1172
+ scrollbox.scrollBy(-3);
1173
+ return;
1174
+ }
1175
+ });
1176
+ useKeyboard2((key) => {
1177
+ if (!tabsFocused || chats.length <= 1 || !selectedChatId) return;
1178
+ if (key.name === "left" || key.name === "right" || key.name === "tab" && !key.shift) {
1179
+ const currentIdx = chats.findIndex((c) => c.id === selectedChatId);
1180
+ if (currentIdx >= 0) {
1181
+ const nextIdx = key.name === "left" ? (currentIdx - 1 + chats.length) % chats.length : (currentIdx + 1) % chats.length;
1182
+ onSelectChat(chats[nextIdx].id);
1183
+ }
1184
+ }
1185
+ });
1186
+ return /* @__PURE__ */ jsxs5(
1187
+ "box",
1188
+ {
1189
+ flexGrow: 1,
1190
+ border: true,
1191
+ borderStyle: "rounded",
1192
+ borderColor: anyFocused ? "#66bb6a" : "#333333",
1193
+ title: "Chat",
1194
+ titleAlignment: "center",
1195
+ flexDirection: "column",
1196
+ backgroundColor: "#000000",
1197
+ children: [
1198
+ chats.length > 1 && /* @__PURE__ */ jsx5(
1199
+ "box",
1200
+ {
1201
+ height: 3,
1202
+ paddingX: 1,
1203
+ marginX: 1,
1204
+ border: true,
1205
+ borderColor: tabsFocused ? "#66bb6a" : "#333333",
1206
+ backgroundColor: "#000000",
1207
+ flexDirection: "row",
1208
+ gap: 1,
1209
+ alignItems: "center",
1210
+ onMouseDown: () => onFocus("chat-tabs"),
1211
+ children: chats.map((chat) => {
1212
+ const isSelected = chat.id === selectedChatId;
1213
+ const label = `${chat.title}${chat.processing ? " ..." : ""}`;
1214
+ return /* @__PURE__ */ jsx5(
1215
+ "box",
1216
+ {
1217
+ backgroundColor: isSelected ? "#1a1a1a" : void 0,
1218
+ paddingX: 1,
1219
+ onMouseDown: () => {
1220
+ onSelectChat(chat.id);
1221
+ onFocus("chat-tabs");
1222
+ },
1223
+ children: /* @__PURE__ */ jsx5("text", { children: /* @__PURE__ */ jsx5("span", { fg: isSelected ? modeColors.text : "#666666", children: label }) })
1224
+ },
1225
+ chat.id
1226
+ );
1227
+ })
1228
+ }
1229
+ ),
1230
+ /* @__PURE__ */ jsx5(
1231
+ "scrollbox",
1232
+ {
1233
+ ref: scrollboxRef,
1234
+ flexGrow: 1,
1235
+ focused: historyFocused,
1236
+ paddingX: 1,
1237
+ paddingY: 1,
1238
+ onMouseDown: () => onFocus("chat-history"),
1239
+ children: loading ? /* @__PURE__ */ jsx5("box", { paddingX: 1, children: /* @__PURE__ */ jsx5("text", { fg: "#666666", children: "Loading messages..." }) }) : displayMessages.length === 0 ? /* @__PURE__ */ jsx5("box", { paddingX: 1, children: /* @__PURE__ */ jsx5("text", { fg: "#666666", children: "No messages yet. Send a message to start chatting." }) }) : (() => {
1240
+ const activeChat = chats.find((c) => c.id === selectedChatId);
1241
+ const provider = activeChat?.provider ?? "claude";
1242
+ const primaryTypes = /* @__PURE__ */ new Set(["user", "agent", "todo_list"]);
1243
+ return displayMessages.map((msg, i) => {
1244
+ const prev = i > 0 ? displayMessages[i - 1] : null;
1245
+ const needsSpacing = prev && (primaryTypes.has(msg.type) || primaryTypes.has(prev.type));
1246
+ return /* @__PURE__ */ jsxs5("box", { flexDirection: "column", children: [
1247
+ needsSpacing && /* @__PURE__ */ jsx5("box", { height: 1 }),
1248
+ /* @__PURE__ */ jsx5(ChatMessage, { message: msg, provider })
1249
+ ] }, msg.id || `msg-${i}`);
1250
+ });
1251
+ })()
1252
+ }
1253
+ ),
1254
+ /* @__PURE__ */ jsxs5(
1255
+ "box",
1256
+ {
1257
+ flexDirection: "column",
1258
+ flexShrink: 0,
1259
+ border: true,
1260
+ borderColor: inputFocused ? modeColors.border : "#333333",
1261
+ backgroundColor: "#111111",
1262
+ onMouseDown: () => onFocus("chat-input"),
1263
+ children: [
1264
+ /* @__PURE__ */ jsx5("box", { height: 6, paddingX: 1, children: /* @__PURE__ */ jsx5(
1265
+ "textarea",
1266
+ {
1267
+ ref: textareaRef,
1268
+ keyBindings: textareaKeyBindings,
1269
+ placeholder: "Type a message... (Enter to send, Shift+Enter for newline)",
1270
+ focused: inputFocused,
1271
+ backgroundColor: "#111111",
1272
+ textColor: "#ffffff",
1273
+ focusedBackgroundColor: "#111111",
1274
+ placeholderColor: "#555555",
1275
+ width: "100%",
1276
+ height: 5
1277
+ }
1278
+ ) }),
1279
+ /* @__PURE__ */ jsxs5(
1280
+ "box",
1281
+ {
1282
+ height: 1,
1283
+ paddingX: 1,
1284
+ flexDirection: "row",
1285
+ justifyContent: "space-between",
1286
+ children: [
1287
+ /* @__PURE__ */ jsx5("text", { children: /* @__PURE__ */ jsx5("span", { fg: modeColors.text, children: taskMode === "build" ? "Build" : "Plan" }) }),
1288
+ isProcessing && /* @__PURE__ */ jsx5(SpinnerLabel, { color: "#ffaa00", label: "thinking" })
1289
+ ]
1290
+ }
1291
+ )
1292
+ ]
1293
+ }
1294
+ )
1295
+ ]
1296
+ }
1297
+ );
1298
+ }
1299
+
1300
+ // src/interactive/components/WorkspaceInfo.tsx
1301
+ import React4 from "react";
1302
+ import open from "open";
1303
+ import { Fragment, jsx as jsx6, jsxs as jsxs6 } from "@opentui/react/jsx-runtime";
1304
+ var WEB_APP_URL = process.env.REPLICAS_WEB_URL || "https://replicas.dev";
1305
+ var AUTH_METHOD_LABELS = {
1306
+ oauth: "OAuth",
1307
+ api_key: "API Key",
1308
+ bedrock: "Bedrock",
1309
+ none: null
1310
+ };
1311
+ function StatusDot({ status }) {
1312
+ if (status === true || status === "yes") {
1313
+ return /* @__PURE__ */ jsx6("span", { fg: "#66bb6a", children: "\u2713" });
1314
+ }
1315
+ if (status === false || status === "no") {
1316
+ return /* @__PURE__ */ jsx6("span", { fg: "#ff4444", children: "\u2717" });
1317
+ }
1318
+ return /* @__PURE__ */ jsx6("span", { fg: "#555555", children: "-" });
1319
+ }
1320
+ function SectionLabel({ title }) {
1321
+ return /* @__PURE__ */ jsx6("box", { backgroundColor: "#151515", paddingX: 1, children: /* @__PURE__ */ jsx6("text", { children: /* @__PURE__ */ jsx6("span", { fg: "#666666", children: /* @__PURE__ */ jsx6("strong", { children: title.toUpperCase() }) }) }) });
1322
+ }
1323
+ function Section({ title, children }) {
1324
+ return /* @__PURE__ */ jsxs6("box", { flexDirection: "column", marginBottom: 1, children: [
1325
+ /* @__PURE__ */ jsx6(SectionLabel, { title }),
1326
+ /* @__PURE__ */ jsx6("box", { flexDirection: "column", backgroundColor: "#0a0a0a", children })
1327
+ ] });
1328
+ }
1329
+ function CardItem({ label, status }) {
1330
+ return /* @__PURE__ */ jsxs6(
1331
+ "box",
1332
+ {
1333
+ flexDirection: "row",
1334
+ justifyContent: "space-between",
1335
+ paddingX: 1,
1336
+ backgroundColor: "#111111",
1337
+ marginBottom: 0,
1338
+ children: [
1339
+ /* @__PURE__ */ jsx6("text", { children: /* @__PURE__ */ jsx6("span", { fg: "#cccccc", children: label }) }),
1340
+ /* @__PURE__ */ jsx6("text", { children: /* @__PURE__ */ jsx6(StatusDot, { status }) })
1341
+ ]
1342
+ }
1343
+ );
1344
+ }
1345
+ function DetailList({
1346
+ expected,
1347
+ actual
1348
+ }) {
1349
+ const uniqueExpected = Array.from(new Set(expected)).sort((a, b) => a.localeCompare(b));
1350
+ const actualSet = new Set(actual);
1351
+ const rows = uniqueExpected.length > 0 ? uniqueExpected : Array.from(actualSet).sort((a, b) => a.localeCompare(b));
1352
+ if (rows.length === 0) return null;
1353
+ return /* @__PURE__ */ jsx6(Fragment, { children: rows.map((item, i) => /* @__PURE__ */ jsx6(CardItem, { label: item, status: actualSet.has(item) }, i)) });
1354
+ }
1355
+ function WorkspaceInfo({ status, workspaceName, workspaceId, focused, loading, envConfig, previews, onWakeWorkspace, wakingWorkspaceId }) {
1356
+ const borderColor = focused ? "#66bb6a" : "#333333";
1357
+ if (!workspaceName) {
1358
+ return /* @__PURE__ */ jsx6(
1359
+ "box",
1360
+ {
1361
+ width: 30,
1362
+ border: true,
1363
+ borderStyle: "rounded",
1364
+ borderColor,
1365
+ title: "Info",
1366
+ titleAlignment: "center",
1367
+ flexDirection: "column",
1368
+ paddingX: 1,
1369
+ backgroundColor: "#000000",
1370
+ children: /* @__PURE__ */ jsx6("text", { fg: "#666666", children: "Select a workspace" })
1371
+ }
1372
+ );
1373
+ }
1374
+ if (loading || !status) {
1375
+ return /* @__PURE__ */ jsx6(
1376
+ "box",
1377
+ {
1378
+ width: 30,
1379
+ border: true,
1380
+ borderStyle: "rounded",
1381
+ borderColor,
1382
+ title: "Info",
1383
+ titleAlignment: "center",
1384
+ flexDirection: "column",
1385
+ paddingX: 1,
1386
+ backgroundColor: "#000000",
1387
+ children: /* @__PURE__ */ jsx6("text", { fg: "#666666", children: "Loading..." })
1388
+ }
1389
+ );
1390
+ }
1391
+ const statusColor = status.status === "active" ? "#66bb6a" : status.status === "sleeping" ? "#ffaa00" : "#ff4444";
1392
+ const env = status.environmentDetails;
1393
+ const expectedSkills = (envConfig.skills?.environment_skills ?? []).map((s) => s.source);
1394
+ const expectedGlobalVars = (envConfig.variables?.environment_variables ?? []).filter((v) => v.scope_type === "global").map((v) => v.key);
1395
+ const expectedGlobalFiles = (envConfig.files?.environment_files ?? []).filter((f) => f.scope_type === "global").map((f) => f.path);
1396
+ return /* @__PURE__ */ jsx6(
1397
+ "box",
1398
+ {
1399
+ width: 30,
1400
+ border: true,
1401
+ borderStyle: "rounded",
1402
+ borderColor,
1403
+ title: "Info",
1404
+ titleAlignment: "center",
1405
+ flexDirection: "column",
1406
+ backgroundColor: "#000000",
1407
+ children: /* @__PURE__ */ jsxs6("scrollbox", { focused, flexGrow: 1, children: [
1408
+ /* @__PURE__ */ jsx6("box", { backgroundColor: "#111111", paddingX: 1, marginBottom: 1, children: /* @__PURE__ */ jsxs6("box", { flexDirection: "column", children: [
1409
+ /* @__PURE__ */ jsx6("text", { children: /* @__PURE__ */ jsx6("span", { fg: "#ffffff", children: /* @__PURE__ */ jsx6("strong", { children: workspaceName }) }) }),
1410
+ /* @__PURE__ */ jsxs6("text", { children: [
1411
+ /* @__PURE__ */ jsxs6("span", { fg: statusColor, children: [
1412
+ "\u25CF",
1413
+ " ",
1414
+ status.status
1415
+ ] }),
1416
+ env && /* @__PURE__ */ jsxs6("span", { fg: "#555555", children: [
1417
+ " ",
1418
+ "\u2502",
1419
+ " ",
1420
+ env.engineVersion
1421
+ ] })
1422
+ ] }),
1423
+ workspaceId && /* @__PURE__ */ jsx6(
1424
+ "box",
1425
+ {
1426
+ onMouseDown: () => {
1427
+ open(`${WEB_APP_URL}/dashboard?workspaceId=${workspaceId}`).catch(() => {
1428
+ });
1429
+ },
1430
+ children: /* @__PURE__ */ jsxs6("text", { children: [
1431
+ /* @__PURE__ */ jsxs6("span", { fg: "#7dcfff", children: [
1432
+ "\u2197",
1433
+ " Open in Dashboard"
1434
+ ] }),
1435
+ /* @__PURE__ */ jsx6("span", { fg: "#555555", children: " (o)" })
1436
+ ] })
1437
+ }
1438
+ ),
1439
+ workspaceId && status.status === "sleeping" && /* @__PURE__ */ jsx6(
1440
+ "box",
1441
+ {
1442
+ onMouseDown: () => {
1443
+ if (wakingWorkspaceId !== workspaceId) onWakeWorkspace(workspaceId);
1444
+ },
1445
+ children: /* @__PURE__ */ jsxs6("text", { children: [
1446
+ /* @__PURE__ */ jsx6("span", { fg: wakingWorkspaceId === workspaceId ? "#888888" : "#66bb6a", children: wakingWorkspaceId === workspaceId ? "\u25CC Waking..." : "\u25B6 Wake" }),
1447
+ /* @__PURE__ */ jsx6("span", { fg: "#555555", children: " (w)" })
1448
+ ] })
1449
+ }
1450
+ )
1451
+ ] }) }),
1452
+ (status.isClaudeProcessing || status.isCodexProcessing) && /* @__PURE__ */ jsxs6("box", { backgroundColor: "#1a1500", paddingX: 1, marginX: 1, marginBottom: 1, children: [
1453
+ status.isClaudeProcessing && /* @__PURE__ */ jsx6("text", { children: /* @__PURE__ */ jsxs6("span", { fg: "#ffaa00", children: [
1454
+ "\u25C6",
1455
+ " Claude thinking..."
1456
+ ] }) }),
1457
+ status.isCodexProcessing && /* @__PURE__ */ jsx6("text", { children: /* @__PURE__ */ jsxs6("span", { fg: "#ffaa00", children: [
1458
+ "\u25C6",
1459
+ " Codex thinking..."
1460
+ ] }) })
1461
+ ] }),
1462
+ env && /* @__PURE__ */ jsxs6(Section, { title: "Agents", children: [
1463
+ /* @__PURE__ */ jsxs6("box", { flexDirection: "row", justifyContent: "space-between", paddingX: 1, backgroundColor: "#111111", children: [
1464
+ /* @__PURE__ */ jsx6("text", { children: /* @__PURE__ */ jsx6("span", { fg: "#cccccc", children: "Claude" }) }),
1465
+ /* @__PURE__ */ jsx6("text", { children: AUTH_METHOD_LABELS[env.claudeAuthMethod] ? /* @__PURE__ */ jsx6("span", { fg: "#66bb6a", children: AUTH_METHOD_LABELS[env.claudeAuthMethod] }) : /* @__PURE__ */ jsx6("span", { fg: "#ff4444", children: "\u2717" }) })
1466
+ ] }),
1467
+ /* @__PURE__ */ jsxs6("box", { flexDirection: "row", justifyContent: "space-between", paddingX: 1, backgroundColor: "#111111", children: [
1468
+ /* @__PURE__ */ jsx6("text", { children: /* @__PURE__ */ jsx6("span", { fg: "#cccccc", children: "Codex" }) }),
1469
+ /* @__PURE__ */ jsx6("text", { children: AUTH_METHOD_LABELS[env.codexAuthMethod] ? /* @__PURE__ */ jsx6("span", { fg: "#66bb6a", children: AUTH_METHOD_LABELS[env.codexAuthMethod] }) : /* @__PURE__ */ jsx6("span", { fg: "#ff4444", children: "\u2717" }) })
1470
+ ] })
1471
+ ] }),
1472
+ env && /* @__PURE__ */ jsxs6(Section, { title: "Integrations", children: [
1473
+ /* @__PURE__ */ jsx6(CardItem, { label: "GitHub", status: env.githubAccessConfigured }),
1474
+ /* @__PURE__ */ jsx6(CardItem, { label: "Slack", status: env.slackAccessConfigured }),
1475
+ /* @__PURE__ */ jsx6(CardItem, { label: "Linear", status: env.linearAccessConfigured })
1476
+ ] }),
1477
+ previews.length > 0 && /* @__PURE__ */ jsx6(Section, { title: "Previews", children: previews.map((preview, i) => /* @__PURE__ */ jsxs6("box", { flexDirection: "row", justifyContent: "space-between", paddingX: 1, backgroundColor: "#111111", children: [
1478
+ /* @__PURE__ */ jsx6("text", { children: /* @__PURE__ */ jsxs6("span", { fg: "#cccccc", children: [
1479
+ ":",
1480
+ preview.port
1481
+ ] }) }),
1482
+ /* @__PURE__ */ jsx6("text", { children: /* @__PURE__ */ jsx6("a", { href: preview.publicUrl, children: /* @__PURE__ */ jsx6("span", { fg: "#66bb6a", children: "\u2197" }) }) })
1483
+ ] }, i)) }),
1484
+ status.repoStatuses && status.repoStatuses.length > 0 && /* @__PURE__ */ jsx6(Section, { title: "Repositories", children: status.repoStatuses.map((repo, i) => /* @__PURE__ */ jsxs6(React4.Fragment, { children: [
1485
+ /* @__PURE__ */ jsx6("box", { backgroundColor: "#111111", paddingX: 1, children: /* @__PURE__ */ jsx6("text", { children: /* @__PURE__ */ jsx6("span", { fg: "#ffffff", children: /* @__PURE__ */ jsx6("strong", { children: repo.name }) }) }) }),
1486
+ /* @__PURE__ */ jsx6("box", { backgroundColor: "#111111", paddingX: 1, flexDirection: "row", justifyContent: "space-between", children: /* @__PURE__ */ jsxs6("text", { children: [
1487
+ /* @__PURE__ */ jsxs6("span", { fg: "#555555", children: [
1488
+ "\u2514",
1489
+ " "
1490
+ ] }),
1491
+ /* @__PURE__ */ jsx6("span", { fg: repo.currentBranch !== repo.defaultBranch ? "#ffaa00" : "#66bb6a", children: repo.currentBranch })
1492
+ ] }) }),
1493
+ repo.gitDiff && (repo.gitDiff.added > 0 || repo.gitDiff.removed > 0) && /* @__PURE__ */ jsx6("box", { backgroundColor: "#111111", paddingX: 1, flexDirection: "row", children: /* @__PURE__ */ jsxs6("text", { children: [
1494
+ /* @__PURE__ */ jsx6("span", { fg: "#555555", children: " " }),
1495
+ /* @__PURE__ */ jsxs6("span", { fg: "#66bb6a", children: [
1496
+ "+",
1497
+ repo.gitDiff.added
1498
+ ] }),
1499
+ /* @__PURE__ */ jsx6("span", { fg: "#444444", children: " / " }),
1500
+ /* @__PURE__ */ jsxs6("span", { fg: "#ff4444", children: [
1501
+ "-",
1502
+ repo.gitDiff.removed
1503
+ ] })
1504
+ ] }) }),
1505
+ repo.prUrl && /* @__PURE__ */ jsx6("box", { backgroundColor: "#111111", paddingX: 1, children: /* @__PURE__ */ jsxs6("text", { children: [
1506
+ /* @__PURE__ */ jsx6("span", { fg: "#555555", children: " " }),
1507
+ /* @__PURE__ */ jsx6("a", { href: repo.prUrl, children: /* @__PURE__ */ jsxs6("span", { fg: "#66bb6a", children: [
1508
+ "View PR ",
1509
+ "\u2197"
1510
+ ] }) })
1511
+ ] }) })
1512
+ ] }, i)) }),
1513
+ env && /* @__PURE__ */ jsxs6(Section, { title: "Hooks", children: [
1514
+ /* @__PURE__ */ jsx6(CardItem, { label: "Global warm", status: env.globalWarmHookCompleted.status }),
1515
+ env.repositories.map((repo, i) => /* @__PURE__ */ jsxs6(React4.Fragment, { children: [
1516
+ /* @__PURE__ */ jsx6("box", { backgroundColor: "#111111", paddingX: 1, children: /* @__PURE__ */ jsx6("text", { children: /* @__PURE__ */ jsx6("span", { fg: "#ffffff", children: /* @__PURE__ */ jsx6("strong", { children: repo.repositoryName }) }) }) }),
1517
+ /* @__PURE__ */ jsxs6("box", { flexDirection: "row", justifyContent: "space-between", paddingX: 1, backgroundColor: "#111111", children: [
1518
+ /* @__PURE__ */ jsxs6("text", { children: [
1519
+ /* @__PURE__ */ jsxs6("span", { fg: "#555555", children: [
1520
+ "\u251C",
1521
+ " "
1522
+ ] }),
1523
+ /* @__PURE__ */ jsx6("span", { fg: "#cccccc", children: "warm" })
1524
+ ] }),
1525
+ /* @__PURE__ */ jsx6("text", { children: /* @__PURE__ */ jsx6(StatusDot, { status: repo.warmHookCompleted }) })
1526
+ ] }),
1527
+ /* @__PURE__ */ jsxs6("box", { flexDirection: "row", justifyContent: "space-between", paddingX: 1, backgroundColor: "#111111", children: [
1528
+ /* @__PURE__ */ jsxs6("text", { children: [
1529
+ /* @__PURE__ */ jsxs6("span", { fg: "#555555", children: [
1530
+ "\u2514",
1531
+ " "
1532
+ ] }),
1533
+ /* @__PURE__ */ jsx6("span", { fg: "#cccccc", children: "start" })
1534
+ ] }),
1535
+ /* @__PURE__ */ jsx6("text", { children: /* @__PURE__ */ jsx6(StatusDot, { status: repo.startHookCompleted }) })
1536
+ ] })
1537
+ ] }, i))
1538
+ ] }),
1539
+ env && (expectedSkills.length > 0 || env.skillsInstalled.length > 0) && /* @__PURE__ */ jsx6(Section, { title: "Skills", children: /* @__PURE__ */ jsx6(DetailList, { expected: expectedSkills, actual: env.skillsInstalled }) }),
1540
+ env && (expectedGlobalVars.length > 0 || env.envVarsSet.length > 0) && /* @__PURE__ */ jsx6(Section, { title: "Env Vars", children: /* @__PURE__ */ jsx6(DetailList, { expected: expectedGlobalVars, actual: env.envVarsSet }) }),
1541
+ env && (expectedGlobalFiles.length > 0 || env.filesUploaded.length > 0) && /* @__PURE__ */ jsx6(Section, { title: "Files", children: /* @__PURE__ */ jsx6(DetailList, { expected: expectedGlobalFiles, actual: env.filesUploaded }) })
1542
+ ] })
1543
+ }
1544
+ );
1545
+ }
1546
+
1547
+ // src/interactive/components/Toast.tsx
1548
+ import { useTerminalDimensions as useTerminalDimensions2 } from "@opentui/react";
1549
+
1550
+ // src/interactive/toast-context.tsx
1551
+ import { createContext as createContext2, useContext as useContext2, useState as useState4, useCallback as useCallback4, useRef as useRef2 } from "react";
1552
+ import { jsx as jsx7 } from "@opentui/react/jsx-runtime";
1553
+ var ToastContext = createContext2(null);
1554
+ function useToast() {
1555
+ const ctx = useContext2(ToastContext);
1556
+ if (!ctx) throw new Error("useToast must be used within ToastProvider");
1557
+ return ctx;
1558
+ }
1559
+ function ToastProvider({ children }) {
1560
+ const [current, setCurrent] = useState4(null);
1561
+ const timeoutRef = useRef2(null);
1562
+ const show = useCallback4((options) => {
1563
+ const { message, variant = "info", duration = 3e3 } = options;
1564
+ setCurrent({ message, variant });
1565
+ if (timeoutRef.current) clearTimeout(timeoutRef.current);
1566
+ timeoutRef.current = setTimeout(() => {
1567
+ setCurrent(null);
1568
+ }, duration);
1569
+ }, []);
1570
+ const error = useCallback4((err) => {
1571
+ const message = err instanceof Error ? err.message : "An error occurred";
1572
+ show({ message, variant: "error", duration: 5e3 });
1573
+ }, [show]);
1574
+ return /* @__PURE__ */ jsx7(ToastContext.Provider, { value: { current, show, error }, children });
1575
+ }
1576
+
1577
+ // src/interactive/components/Toast.tsx
1578
+ import { jsx as jsx8 } from "@opentui/react/jsx-runtime";
1579
+ var VARIANT_COLORS = {
1580
+ info: { border: "#66bb6a", bg: "#0a1a0a" },
1581
+ success: { border: "#66bb6a", bg: "#0a1a0a" },
1582
+ warning: { border: "#ffaa00", bg: "#1a1a0a" },
1583
+ error: { border: "#ff4444", bg: "#1a0a0a" }
1584
+ };
1585
+ function Toast() {
1586
+ const { current } = useToast();
1587
+ const { width } = useTerminalDimensions2();
1588
+ if (!current) return null;
1589
+ const colors = VARIANT_COLORS[current.variant] ?? VARIANT_COLORS.info;
1590
+ const toastWidth = Math.min(60, width - 4);
1591
+ return /* @__PURE__ */ jsx8(
1592
+ "box",
1593
+ {
1594
+ position: "absolute",
1595
+ bottom: 2,
1596
+ right: 2,
1597
+ width: toastWidth,
1598
+ paddingX: 2,
1599
+ paddingY: 1,
1600
+ backgroundColor: colors.bg,
1601
+ borderColor: colors.border,
1602
+ border: true,
1603
+ borderStyle: "rounded",
1604
+ zIndex: 100,
1605
+ children: /* @__PURE__ */ jsx8("text", { fg: "#ffffff", selectable: true, children: current.message })
1606
+ }
1607
+ );
1608
+ }
1609
+
1610
+ // src/interactive/App.tsx
1611
+ import { jsx as jsx9, jsxs as jsxs7 } from "@opentui/react/jsx-runtime";
1612
+ var FOCUS_ORDER = ["sidebar", "chat-tabs", "chat-history", "chat-input", "info"];
1613
+ var MOCK_CHATS = [
1614
+ { id: "mock-claude", provider: "claude", title: "Claude Code", createdAt: "", updatedAt: "", processing: false },
1615
+ { id: "mock-codex", provider: "codex", title: "Codex", createdAt: "", updatedAt: "", processing: false }
1616
+ ];
1617
+ var MONOLITH_URL = process.env.REPLICAS_MONOLITH_URL || "https://api.replicas.dev";
1618
+ var queryClient = new QueryClient({
1619
+ defaultOptions: {
1620
+ queries: {
1621
+ retry: 1,
1622
+ refetchOnWindowFocus: false
1623
+ }
1624
+ }
1625
+ });
1626
+ var authValue = {
1627
+ getAccessToken: () => getValidToken(),
1628
+ getOrganizationId: () => getOrganizationId(),
1629
+ monolithUrl: MONOLITH_URL,
1630
+ queryClient
1631
+ };
1632
+ function App() {
1633
+ return /* @__PURE__ */ jsx9(ReplicasAuthProvider, { value: authValue, children: /* @__PURE__ */ jsxs7(ToastProvider, { children: [
1634
+ /* @__PURE__ */ jsx9(AppInner, {}),
1635
+ /* @__PURE__ */ jsx9(Toast, {})
1636
+ ] }) });
1637
+ }
1638
+ function AppInner() {
1639
+ const renderer = useRenderer();
1640
+ const { width } = useTerminalDimensions3();
1641
+ const toast = useToast();
1642
+ useEffect3(() => {
1643
+ const handler = (selection) => {
1644
+ const text = selection.getSelectedText();
1645
+ if (text) {
1646
+ renderer.copyToClipboardOSC52(text);
1647
+ toast.show({ message: "Copied to clipboard", variant: "info", duration: 1500 });
1648
+ }
1649
+ };
1650
+ renderer.on("selection", handler);
1651
+ return () => {
1652
+ renderer.off("selection", handler);
1653
+ };
1654
+ }, [renderer, toast]);
1655
+ const [selectedWorkspaceId, setSelectedWorkspaceId] = useState5(null);
1656
+ const [selectedChatId, setSelectedChatId] = useState5(null);
1657
+ const [focusPanel, setFocusPanel] = useState5("sidebar");
1658
+ const [taskMode, setTaskMode] = useState5("build");
1659
+ const [mockWorkspaces, setMockWorkspaces] = useState5([]);
1660
+ const mockToRealRef = useRef3(/* @__PURE__ */ new Map());
1661
+ const mockGroupRef = useRef3(/* @__PURE__ */ new Map());
1662
+ const [mockMessages, setMockMessages] = useState5([]);
1663
+ const [mockThinking, setMockThinking] = useState5(false);
1664
+ const pendingMessageRef = useRef3(/* @__PURE__ */ new Map());
1665
+ const mockIds = useMemo3(() => new Set(mockWorkspaces.map((m) => m.id)), [mockWorkspaces]);
1666
+ const isMockSelected = selectedWorkspaceId ? mockIds.has(selectedWorkspaceId) : false;
1667
+ const { data: workspacesData, isLoading: loadingWorkspaces } = useWorkspaces(1, 100, "organization");
1668
+ const { data: reposData } = useRepositories();
1669
+ const { data: setsData } = useRepositorySets();
1670
+ const { data: envSkills } = useEnvironmentSkills();
1671
+ const { data: envVariables } = useEnvironmentVariables();
1672
+ const { data: envFiles } = useEnvironmentFiles();
1673
+ const { data: statusData, isLoading: loadingStatus } = useWorkspaceStatus(
1674
+ isMockSelected ? null : selectedWorkspaceId,
1675
+ { includeDiffs: true }
1676
+ );
1677
+ const { data: chatsData } = useWorkspaceChats(isMockSelected ? null : selectedWorkspaceId);
1678
+ const chats = isMockSelected ? MOCK_CHATS : chatsData?.chats ?? [];
1679
+ const resolvedChatId = useMemo3(() => {
1680
+ if (selectedChatId && chats.find((c) => c.id === selectedChatId)) {
1681
+ return selectedChatId;
1682
+ }
1683
+ return getInitialChatId(chats);
1684
+ }, [chats, selectedChatId]);
1685
+ const { data: historyData, isLoading: loadingMessages } = useChatHistory(
1686
+ isMockSelected ? null : selectedWorkspaceId,
1687
+ resolvedChatId?.startsWith("mock-") ? null : resolvedChatId
1688
+ );
1689
+ const { data: previewsData } = useWorkspacePreviews(isMockSelected ? null : selectedWorkspaceId);
1690
+ const { connected: sseConnected } = useWorkspaceEvents(
1691
+ isMockSelected ? null : selectedWorkspaceId
1692
+ );
1693
+ const createWorkspaceMutation = useCreateWorkspace();
1694
+ const deleteWorkspaceMutation = useDeleteWorkspace();
1695
+ const wakeWorkspaceMutation = useWakeWorkspace();
1696
+ const generateNameMutation = useGenerateWorkspaceName();
1697
+ const [wakingWorkspaceId, setWakingWorkspaceId] = useState5(null);
1698
+ const sendMessageMutation = useSendChatMessage(selectedWorkspaceId, resolvedChatId);
1699
+ const interruptMutation = useInterruptChat(selectedWorkspaceId, resolvedChatId);
1700
+ const workspaces = workspacesData?.workspaces ?? [];
1701
+ const repositories = reposData?.repositories ?? [];
1702
+ const repositorySets = setsData?.repository_sets ?? [];
1703
+ const previews = previewsData?.previews ?? [];
1704
+ const allWorkspaces = useMemo3(() => [...mockWorkspaces, ...workspaces], [mockWorkspaces, workspaces]);
1705
+ const groups = useMemo3(
1706
+ () => buildGroups(allWorkspaces, workspacesData, repositories, repositorySets, mockGroupRef.current),
1707
+ [allWorkspaces, workspacesData, repositories, repositorySets]
1708
+ );
1709
+ const selectedWorkspace = allWorkspaces.find((ws) => ws.id === selectedWorkspaceId) ?? null;
1710
+ const selectedChat = chats.find((c) => c.id === resolvedChatId) ?? null;
1711
+ const isProcessing = mockThinking || (selectedChat?.processing ?? false);
1712
+ const displayMessages = useMemo3(() => {
1713
+ if (isMockSelected) return mockMessages;
1714
+ const rawEvents = historyData?.events ?? [];
1715
+ const events = rawEvents.filter(isAgentBackendEvent);
1716
+ if (events.length === 0) return [];
1717
+ const provider = selectedChat?.provider ?? "claude";
1718
+ const parsed = parseAgentEvents(events, provider);
1719
+ return filterDisplayMessages(parsed, provider);
1720
+ }, [isMockSelected, mockMessages, historyData, selectedChat?.provider]);
1721
+ const showInfoPanel = width >= 100;
1722
+ const showWorkspacePanel = width >= 60;
1723
+ useEffect3(() => {
1724
+ if (!selectedWorkspaceId && workspaces.length > 0) {
1725
+ setSelectedWorkspaceId(workspaces[0].id);
1726
+ }
1727
+ }, [workspaces, selectedWorkspaceId]);
1728
+ useEffect3(() => {
1729
+ if (mockWorkspaces.length === 0) return;
1730
+ const realIds = new Set(workspaces.map((w) => w.id));
1731
+ let changed = false;
1732
+ const remaining = mockWorkspaces.filter((mock) => {
1733
+ const realId = mockToRealRef.current.get(mock.id);
1734
+ if (realId && realIds.has(realId)) {
1735
+ if (selectedWorkspaceId === mock.id) {
1736
+ setSelectedWorkspaceId(realId);
1737
+ }
1738
+ const pending = pendingMessageRef.current.get(mock.id);
1739
+ if (pending) {
1740
+ pendingMessageRef.current.delete(mock.id);
1741
+ pendingMessageRef.current.set(realId, pending);
1742
+ }
1743
+ mockToRealRef.current.delete(mock.id);
1744
+ mockGroupRef.current.delete(mock.id);
1745
+ setSelectedChatId(null);
1746
+ setMockMessages([]);
1747
+ setMockThinking(false);
1748
+ changed = true;
1749
+ return false;
1750
+ }
1751
+ return true;
1752
+ });
1753
+ if (changed) setMockWorkspaces(remaining);
1754
+ }, [workspaces, mockWorkspaces, selectedWorkspaceId]);
1755
+ useEffect3(() => {
1756
+ if (!selectedWorkspaceId || isMockSelected) return;
1757
+ if (!resolvedChatId || resolvedChatId.startsWith("mock-")) return;
1758
+ if (statusData?.status !== "active") return;
1759
+ const pending = pendingMessageRef.current.get(selectedWorkspaceId);
1760
+ if (!pending) return;
1761
+ pendingMessageRef.current.delete(selectedWorkspaceId);
1762
+ const noBranches = statusData?.repoStatuses && statusData.repoStatuses.length > 0 && statusData.repoStatuses.every((r) => r.currentBranch === r.defaultBranch);
1763
+ if (noBranches) {
1764
+ generateNameMutation.mutateAsync({ workspaceId: selectedWorkspaceId, prompt: pending }).catch(() => {
1765
+ });
1766
+ }
1767
+ sendMessageMutation.mutate({ message: pending });
1768
+ }, [selectedWorkspaceId, resolvedChatId, isMockSelected, statusData?.status]);
1769
+ useEffect3(() => {
1770
+ if (mockWorkspaces.length === 0) return;
1771
+ const interval = setInterval(() => {
1772
+ queryClient.invalidateQueries({ queryKey: ["workspaces"] });
1773
+ }, 5e3);
1774
+ return () => clearInterval(interval);
1775
+ }, [mockWorkspaces.length]);
1776
+ const handleSelectWorkspace = useCallback5((workspaceId) => {
1777
+ setSelectedWorkspaceId(workspaceId);
1778
+ setSelectedChatId(null);
1779
+ setMockMessages([]);
1780
+ setMockThinking(false);
1781
+ setFocusPanel("chat-input");
1782
+ }, []);
1783
+ const handleFocus = useCallback5((panel) => {
1784
+ setFocusPanel(panel);
1785
+ }, []);
1786
+ const handleCreateWorkspace = useCallback5(
1787
+ async (groupId, groupType) => {
1788
+ const orgId = getOrganizationId() ?? "";
1789
+ const mock = createMockWorkspaceRecord(orgId);
1790
+ mockGroupRef.current.set(mock.id, groupId);
1791
+ setMockWorkspaces((prev) => [mock, ...prev]);
1792
+ setSelectedWorkspaceId(mock.id);
1793
+ try {
1794
+ const request = groupType === "set" ? { repository_set_id: groupId.replace(/^set:/, ""), name: mock.name, placeholder: true } : { repository_ids: [groupId], name: mock.name, placeholder: true };
1795
+ const result = await createWorkspaceMutation.mutateAsync(request);
1796
+ mockToRealRef.current.set(mock.id, result.workspace.id);
1797
+ } catch (err) {
1798
+ mockGroupRef.current.delete(mock.id);
1799
+ setMockWorkspaces((prev) => prev.filter((m) => m.id !== mock.id));
1800
+ toast.error(err);
1801
+ }
1802
+ },
1803
+ [createWorkspaceMutation, toast]
1804
+ );
1805
+ const handleDeleteWorkspace = useCallback5(
1806
+ async (workspaceId) => {
1807
+ if (mockIds.has(workspaceId)) {
1808
+ mockGroupRef.current.delete(workspaceId);
1809
+ mockToRealRef.current.delete(workspaceId);
1810
+ setMockWorkspaces((prev) => prev.filter((m) => m.id !== workspaceId));
1811
+ if (selectedWorkspaceId === workspaceId) setSelectedWorkspaceId(null);
1812
+ return;
1813
+ }
1814
+ try {
1815
+ await deleteWorkspaceMutation.mutateAsync(workspaceId);
1816
+ if (selectedWorkspaceId === workspaceId) setSelectedWorkspaceId(null);
1817
+ toast.show({ message: "Workspace deleted", variant: "info" });
1818
+ } catch (err) {
1819
+ toast.error(err);
1820
+ }
1821
+ },
1822
+ [mockIds, selectedWorkspaceId, deleteWorkspaceMutation]
1823
+ );
1824
+ const handleWakeWorkspace = useCallback5(
1825
+ async (workspaceId) => {
1826
+ setWakingWorkspaceId(workspaceId);
1827
+ try {
1828
+ await wakeWorkspaceMutation.mutateAsync(workspaceId);
1829
+ toast.show({ message: "Workspace waking up...", variant: "info" });
1830
+ } catch (err) {
1831
+ toast.error(err);
1832
+ } finally {
1833
+ setWakingWorkspaceId(null);
1834
+ }
1835
+ },
1836
+ [wakeWorkspaceMutation, toast]
1837
+ );
1838
+ const handleSelectChat = useCallback5((chatId) => {
1839
+ setSelectedChatId(chatId);
1840
+ }, []);
1841
+ useKeyboard3((key) => {
1842
+ if (key.name === "f12") {
1843
+ renderer.console.toggle();
1844
+ return;
1845
+ }
1846
+ if (key.ctrl && key.name === "c") {
1847
+ renderer.destroy();
1848
+ return;
1849
+ }
1850
+ if (key.name === "escape") {
1851
+ if (isProcessing && selectedWorkspaceId && resolvedChatId) {
1852
+ interruptMutation.mutate();
1853
+ }
1854
+ return;
1855
+ }
1856
+ if (key.shift && key.name === "tab") {
1857
+ setFocusPanel((current) => {
1858
+ const availablePanels = FOCUS_ORDER.filter((p) => {
1859
+ if (p === "sidebar" && !showWorkspacePanel) return false;
1860
+ if ((p === "chat-tabs" || p === "chat-history" || p === "chat-input") && (!selectedWorkspaceId || statusData?.status === "sleeping")) return false;
1861
+ if (p === "chat-tabs" && chats.length <= 1) return false;
1862
+ if (p === "info" && !showInfoPanel) return false;
1863
+ return true;
1864
+ });
1865
+ const idx = availablePanels.indexOf(current);
1866
+ return availablePanels[(idx + 1) % availablePanels.length];
1867
+ });
1868
+ return;
1869
+ }
1870
+ if (key.name === "tab" && focusPanel === "chat-input") {
1871
+ setTaskMode((m) => m === "build" ? "plan" : "build");
1872
+ return;
1873
+ }
1874
+ if (key.name === "o" && focusPanel === "info" && selectedWorkspaceId) {
1875
+ const webUrl = process.env.REPLICAS_WEB_URL || "https://replicas.dev";
1876
+ import("open").then((mod) => mod.default(`${webUrl}/dashboard?workspaceId=${selectedWorkspaceId}`)).catch(() => {
1877
+ });
1878
+ return;
1879
+ }
1880
+ if (key.name === "w" && focusPanel !== "sidebar" && selectedWorkspaceId) {
1881
+ if (statusData?.status === "sleeping" && !wakingWorkspaceId) {
1882
+ handleWakeWorkspace(selectedWorkspaceId);
1883
+ }
1884
+ return;
1885
+ }
1886
+ if (focusPanel === "chat-input") return;
1887
+ });
1888
+ const handleSendMessage = useCallback5(
1889
+ async (message) => {
1890
+ if (!selectedWorkspaceId || !resolvedChatId) return;
1891
+ if (isMockSelected) {
1892
+ const { message: userMsg } = createUserMessage(message);
1893
+ setMockMessages((prev) => [...prev, userMsg]);
1894
+ pendingMessageRef.current.set(selectedWorkspaceId, message);
1895
+ setMockThinking(true);
1896
+ return;
1897
+ }
1898
+ sendMessageMutation.mutate({
1899
+ message,
1900
+ permissionMode: taskMode === "plan" ? "read" : void 0
1901
+ });
1902
+ },
1903
+ [selectedWorkspaceId, resolvedChatId, isMockSelected, sendMessageMutation, taskMode]
1904
+ );
1905
+ return /* @__PURE__ */ jsxs7("box", { flexDirection: "column", width: "100%", height: "100%", backgroundColor: "#000000", children: [
1906
+ /* @__PURE__ */ jsxs7("box", { flexDirection: "row", flexGrow: 1, backgroundColor: "#000000", children: [
1907
+ showWorkspacePanel && /* @__PURE__ */ jsx9(
1908
+ WorkspaceSidebar,
1909
+ {
1910
+ groups,
1911
+ selectedWorkspaceId,
1912
+ mockIds,
1913
+ onSelectWorkspace: handleSelectWorkspace,
1914
+ onCreateWorkspace: handleCreateWorkspace,
1915
+ onDeleteWorkspace: handleDeleteWorkspace,
1916
+ onWakeWorkspace: handleWakeWorkspace,
1917
+ wakingWorkspaceId,
1918
+ focused: focusPanel === "sidebar",
1919
+ loading: loadingWorkspaces
1920
+ }
1921
+ ),
1922
+ selectedWorkspaceId && statusData?.status !== "sleeping" ? /* @__PURE__ */ jsx9(
1923
+ ChatArea,
1924
+ {
1925
+ chats,
1926
+ selectedChatId: resolvedChatId,
1927
+ displayMessages,
1928
+ onSelectChat: handleSelectChat,
1929
+ onSendMessage: handleSendMessage,
1930
+ onFocus: handleFocus,
1931
+ focusPanel,
1932
+ taskMode,
1933
+ isProcessing,
1934
+ loading: loadingMessages
1935
+ }
1936
+ ) : selectedWorkspaceId && statusData?.status === "sleeping" ? /* @__PURE__ */ jsxs7(
1937
+ "box",
1938
+ {
1939
+ flexGrow: 1,
1940
+ border: true,
1941
+ borderStyle: "rounded",
1942
+ borderColor: "#333333",
1943
+ backgroundColor: "#000000",
1944
+ justifyContent: "center",
1945
+ alignItems: "center",
1946
+ flexDirection: "column",
1947
+ gap: 1,
1948
+ children: [
1949
+ /* @__PURE__ */ jsxs7("text", { fg: "#888888", children: [
1950
+ "\u263E",
1951
+ " This workspace is sleeping"
1952
+ ] }),
1953
+ /* @__PURE__ */ jsxs7("text", { fg: "#555555", children: [
1954
+ "Press ",
1955
+ /* @__PURE__ */ jsx9("span", { fg: "#66bb6a", children: /* @__PURE__ */ jsx9("strong", { children: "w" }) }),
1956
+ " to wake it up"
1957
+ ] })
1958
+ ]
1959
+ }
1960
+ ) : /* @__PURE__ */ jsx9(
1961
+ "box",
1962
+ {
1963
+ flexGrow: 1,
1964
+ border: true,
1965
+ borderStyle: "rounded",
1966
+ borderColor: "#333333",
1967
+ backgroundColor: "#000000",
1968
+ justifyContent: "center",
1969
+ alignItems: "center",
1970
+ children: /* @__PURE__ */ jsx9("text", { fg: "#555555", children: "Create a workspace to begin building!" })
1971
+ }
1972
+ ),
1973
+ showInfoPanel && /* @__PURE__ */ jsx9(
1974
+ WorkspaceInfo,
1975
+ {
1976
+ status: statusData ?? null,
1977
+ workspaceName: selectedWorkspace?.name ?? null,
1978
+ workspaceId: selectedWorkspaceId,
1979
+ focused: focusPanel === "info",
1980
+ loading: loadingStatus,
1981
+ envConfig: {
1982
+ skills: envSkills ?? null,
1983
+ variables: envVariables ?? null,
1984
+ files: envFiles ?? null
1985
+ },
1986
+ previews,
1987
+ onWakeWorkspace: handleWakeWorkspace,
1988
+ wakingWorkspaceId
1989
+ }
1990
+ )
1991
+ ] }),
1992
+ /* @__PURE__ */ jsx9(
1993
+ StatusBar,
1994
+ {
1995
+ focusPanel
1996
+ }
1997
+ )
1998
+ ] });
1999
+ }
2000
+
2001
+ // src/interactive/index.tsx
2002
+ import { jsx as jsx10 } from "@opentui/react/jsx-runtime";
2003
+ async function launchInteractive() {
2004
+ const renderer = await createCliRenderer({
2005
+ exitOnCtrlC: false
2006
+ });
2007
+ createRoot(renderer).render(/* @__PURE__ */ jsx10(App, {}));
2008
+ }
2009
+ export {
2010
+ launchInteractive
2011
+ };