@robota-sdk/agent-transport-tui 3.0.0-beta.63

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,4041 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ TuiTransport: () => TuiTransport
34
+ });
35
+ module.exports = __toCommonJS(index_exports);
36
+
37
+ // src/render.tsx
38
+ var import_ink25 = require("ink");
39
+
40
+ // src/App.tsx
41
+ var import_react23 = require("react");
42
+ var import_ink24 = require("ink");
43
+ var import_agent_sdk4 = require("@robota-sdk/agent-sdk");
44
+ var import_agent_core10 = require("@robota-sdk/agent-core");
45
+
46
+ // src/hooks/useInteractiveSession.ts
47
+ var import_react2 = require("react");
48
+ var import_agent_sdk = require("@robota-sdk/agent-sdk");
49
+ var import_agent_core2 = require("@robota-sdk/agent-core");
50
+
51
+ // src/tui-state-manager.ts
52
+ var MAX_RENDERED_MESSAGES = 100;
53
+ var STREAMING_DEBOUNCE_MS = 300;
54
+ function createDebouncedNotify(notify, ms) {
55
+ let timer = null;
56
+ return {
57
+ schedule() {
58
+ if (!timer) {
59
+ timer = setTimeout(() => {
60
+ timer = null;
61
+ notify();
62
+ }, ms);
63
+ }
64
+ },
65
+ flush() {
66
+ if (timer) {
67
+ clearTimeout(timer);
68
+ timer = null;
69
+ }
70
+ }
71
+ };
72
+ }
73
+ var TuiStateManager = class {
74
+ // ── Rendering state ───────────────────────────────────────────
75
+ history = [];
76
+ streamingText = "";
77
+ activeTools = [];
78
+ isThinking = false;
79
+ isAborting = false;
80
+ pendingPrompt = null;
81
+ contextState = { percentage: 0, usedTokens: 0, maxTokens: 0 };
82
+ executionWorkspaceSnapshot = null;
83
+ selectedExecutionEntryId;
84
+ /** Called after any state change. React hook sets this to trigger re-render. */
85
+ onChange = null;
86
+ // ── Internal ──────────────────────────────────────────────────
87
+ streamBuf = "";
88
+ debouncedStreamNotify = createDebouncedNotify(() => this.notify(), STREAMING_DEBOUNCE_MS);
89
+ notify() {
90
+ this.onChange?.();
91
+ }
92
+ // ── Event handlers (InteractiveSession → state) ───────────────
93
+ onTextDelta = (delta) => {
94
+ this.streamBuf += delta;
95
+ this.streamingText = this.streamBuf;
96
+ this.debouncedStreamNotify.schedule();
97
+ };
98
+ onToolStart = (state) => {
99
+ this.activeTools = [...this.activeTools, state];
100
+ this.notify();
101
+ };
102
+ onToolEnd = (state) => {
103
+ const idx = this.activeTools.findLastIndex((t) => t.toolName === state.toolName && t.isRunning);
104
+ if (idx !== -1) {
105
+ const updated = [...this.activeTools];
106
+ updated[idx] = state;
107
+ this.activeTools = updated;
108
+ }
109
+ this.notify();
110
+ };
111
+ onThinking = (thinking) => {
112
+ this.isThinking = thinking;
113
+ if (thinking) {
114
+ this.debouncedStreamNotify.flush();
115
+ this.streamBuf = "";
116
+ this.streamingText = "";
117
+ this.activeTools = [];
118
+ } else {
119
+ this.isAborting = false;
120
+ }
121
+ this.notify();
122
+ };
123
+ onComplete = (result) => {
124
+ this.debouncedStreamNotify.flush();
125
+ this.streamBuf = "";
126
+ this.streamingText = "";
127
+ this.activeTools = [];
128
+ this.contextState = {
129
+ percentage: result.contextState.usedPercentage,
130
+ usedTokens: result.contextState.usedTokens,
131
+ maxTokens: result.contextState.maxTokens
132
+ };
133
+ this.notify();
134
+ };
135
+ onInterrupted = () => {
136
+ this.debouncedStreamNotify.flush();
137
+ this.streamBuf = "";
138
+ this.streamingText = "";
139
+ this.activeTools = [];
140
+ this.notify();
141
+ };
142
+ onError = () => {
143
+ this.debouncedStreamNotify.flush();
144
+ this.streamBuf = "";
145
+ this.streamingText = "";
146
+ this.activeTools = [];
147
+ this.notify();
148
+ };
149
+ onContextUpdate = (state) => {
150
+ this.setContextState({
151
+ percentage: state.usedPercentage,
152
+ usedTokens: state.usedTokens,
153
+ maxTokens: state.maxTokens
154
+ });
155
+ };
156
+ // ── State updates from external sources ───────────────────────
157
+ /** Sync history from InteractiveSession */
158
+ syncHistory(entries) {
159
+ if (entries.length === 0) return;
160
+ this.history = entries.length > MAX_RENDERED_MESSAGES ? entries.slice(-MAX_RENDERED_MESSAGES) : [...entries];
161
+ this.notify();
162
+ }
163
+ /** Add a single history entry */
164
+ addEntry(entry) {
165
+ const updated = [...this.history, entry];
166
+ this.history = updated.length > MAX_RENDERED_MESSAGES ? updated.slice(-MAX_RENDERED_MESSAGES) : updated;
167
+ this.notify();
168
+ }
169
+ clearHistory() {
170
+ this.history = [];
171
+ this.debouncedStreamNotify.flush();
172
+ this.streamBuf = "";
173
+ this.streamingText = "";
174
+ this.activeTools = [];
175
+ this.notify();
176
+ }
177
+ /** Update pending prompt state */
178
+ setPendingPrompt(prompt) {
179
+ this.pendingPrompt = prompt;
180
+ this.notify();
181
+ }
182
+ /** Set aborting flag */
183
+ setAborting(aborting) {
184
+ this.isAborting = aborting;
185
+ this.notify();
186
+ }
187
+ /** Update context state */
188
+ setContextState(state) {
189
+ this.contextState = state;
190
+ this.notify();
191
+ }
192
+ syncExecutionWorkspaceSnapshot(snapshot) {
193
+ const currentSelection = this.selectedExecutionEntryId;
194
+ const hasCurrentSelection = currentSelection !== void 0 && snapshot.entries.some((entry) => entry.id === currentSelection);
195
+ const selectedExecutionEntryId = hasCurrentSelection ? currentSelection : snapshot.selectedEntryId ?? snapshot.entries[0]?.id;
196
+ this.executionWorkspaceSnapshot = {
197
+ ...snapshot,
198
+ ...selectedExecutionEntryId ? { selectedEntryId: selectedExecutionEntryId } : {}
199
+ };
200
+ this.selectedExecutionEntryId = selectedExecutionEntryId;
201
+ this.notify();
202
+ }
203
+ selectExecutionWorkspaceEntry(entryId) {
204
+ if (!this.executionWorkspaceSnapshot?.entries.some((entry) => entry.id === entryId)) return;
205
+ this.selectedExecutionEntryId = entryId;
206
+ this.executionWorkspaceSnapshot = {
207
+ ...this.executionWorkspaceSnapshot,
208
+ selectedEntryId: entryId
209
+ };
210
+ this.notify();
211
+ }
212
+ };
213
+
214
+ // src/hooks/useSlashRouting.ts
215
+ var import_react = require("react");
216
+ var import_agent_core = require("@robota-sdk/agent-core");
217
+ function useSlashRouting(interactiveSession, registry, manager, commandEffectQueue, reloadPluginCommandSource) {
218
+ return (0, import_react.useCallback)(
219
+ async (input) => {
220
+ if (!input.startsWith("/")) {
221
+ await interactiveSession.submit(input);
222
+ manager.setPendingPrompt(interactiveSession.getPendingPrompt());
223
+ return;
224
+ }
225
+ const parts = input.slice(1).split(/\s+/);
226
+ const cmd = parts[0]?.toLowerCase() ?? "";
227
+ const args = parts.slice(1).join(" ");
228
+ const result = await interactiveSession.executeCommand(cmd, args);
229
+ if (result) {
230
+ if (result.effects?.some((effect) => effect.type === "session-execution-started")) {
231
+ manager.setPendingPrompt(interactiveSession.getPendingPrompt());
232
+ return;
233
+ }
234
+ applySystemCommandResult(
235
+ result,
236
+ interactiveSession,
237
+ registry,
238
+ manager,
239
+ commandEffectQueue,
240
+ reloadPluginCommandSource
241
+ );
242
+ return;
243
+ }
244
+ manager.addEntry(
245
+ (0, import_agent_core.messageToHistoryEntry)(
246
+ (0, import_agent_core.createSystemMessage)(`Unknown command "/${cmd}". Type /help for help.`)
247
+ )
248
+ );
249
+ },
250
+ [interactiveSession, registry, manager, commandEffectQueue, reloadPluginCommandSource]
251
+ );
252
+ }
253
+ function applySystemCommandResult(result, interactiveSession, registry, manager, commandEffectQueue, reloadPluginCommandSource) {
254
+ const pendingEffects = applyImmediateCommandEffects(
255
+ result.effects,
256
+ registry,
257
+ manager,
258
+ reloadPluginCommandSource
259
+ );
260
+ manager.addEntry((0, import_agent_core.messageToHistoryEntry)((0, import_agent_core.createSystemMessage)(result.message)));
261
+ if (result.interaction !== void 0) {
262
+ commandEffectQueue.enqueueInteraction(result.interaction);
263
+ }
264
+ if (pendingEffects.length > 0) {
265
+ commandEffectQueue.enqueueEffects(pendingEffects);
266
+ }
267
+ if (interactiveSession.isInitialized) {
268
+ const ctx = interactiveSession.getContextState();
269
+ manager.setContextState({
270
+ percentage: ctx.usedPercentage,
271
+ usedTokens: ctx.usedTokens,
272
+ maxTokens: ctx.maxTokens
273
+ });
274
+ }
275
+ }
276
+ function applyImmediateCommandEffects(effects, registry, manager, reloadPluginCommandSource) {
277
+ if (effects === void 0 || effects.length === 0) return [];
278
+ const pendingEffects = [];
279
+ for (const effect of effects) {
280
+ if (effect.type === "conversation-history-cleared") {
281
+ manager.clearHistory();
282
+ continue;
283
+ }
284
+ if (effect.type === "plugin-registry-reload-requested") {
285
+ reloadPluginCommandSource?.(registry);
286
+ continue;
287
+ }
288
+ pendingEffects.push(effect);
289
+ }
290
+ return pendingEffects;
291
+ }
292
+
293
+ // src/hooks/command-effect-queue.ts
294
+ var CommandEffectQueue = class {
295
+ queue = [];
296
+ enqueueInteraction(interaction) {
297
+ this.queue.push({ type: "interaction", interaction });
298
+ }
299
+ enqueueEffects(effects) {
300
+ if (effects.length === 0) return;
301
+ this.queue.push({ type: "effects", effects: [...effects] });
302
+ }
303
+ drain() {
304
+ return this.queue.shift();
305
+ }
306
+ clear() {
307
+ this.queue.length = 0;
308
+ }
309
+ };
310
+
311
+ // src/hooks/useInteractiveSession.ts
312
+ var SESSION_INIT_POLL_MS = 200;
313
+ function applyCompactEventToManager(interactiveSession, manager) {
314
+ manager.syncHistory(interactiveSession.getFullHistory());
315
+ }
316
+ function applySkillActivationEventToManager(interactiveSession, manager) {
317
+ manager.syncHistory(interactiveSession.getFullHistory());
318
+ }
319
+ function syncExecutionWorkspaceFromSession(interactiveSession, manager) {
320
+ try {
321
+ manager.syncExecutionWorkspaceSnapshot(
322
+ interactiveSession.getExecutionWorkspaceSnapshot({
323
+ selectedEntryId: manager.selectedExecutionEntryId
324
+ })
325
+ );
326
+ } catch {
327
+ }
328
+ }
329
+ function initializeSession(props, permissionHandler) {
330
+ const interactiveSession = new import_agent_sdk.InteractiveSession({
331
+ cwd: props.cwd,
332
+ provider: props.provider,
333
+ permissionMode: props.permissionMode,
334
+ maxTurns: props.maxTurns,
335
+ permissionHandler,
336
+ sessionStore: props.sessionStore,
337
+ resumeSessionId: props.resumeSessionId,
338
+ forkSession: props.forkSession,
339
+ sessionName: props.sessionName,
340
+ backgroundTaskRunners: props.backgroundTaskRunners,
341
+ subagentRunnerFactory: props.subagentRunnerFactory,
342
+ commandModules: props.commandModules,
343
+ commandHostAdapters: props.commandHostAdapters,
344
+ language: props.language
345
+ });
346
+ const registry = new import_agent_sdk.CommandRegistry();
347
+ for (const module2 of props.commandModules ?? []) {
348
+ registry.addModule(module2);
349
+ }
350
+ props.reloadPluginCommandSource?.(registry);
351
+ const manager = new TuiStateManager();
352
+ const commandEffectQueue = new CommandEffectQueue();
353
+ return { interactiveSession, registry, manager, commandEffectQueue };
354
+ }
355
+ function useInteractiveSession(props) {
356
+ const [, forceRender] = (0, import_react2.useState)(0);
357
+ const [permissionRequest, setPermissionRequest] = (0, import_react2.useState)(null);
358
+ const [isShuttingDown, setIsShuttingDown] = (0, import_react2.useState)(false);
359
+ const permissionQueueRef = (0, import_react2.useRef)([]);
360
+ const processingRef = (0, import_react2.useRef)(false);
361
+ const processNextPermission = (0, import_react2.useCallback)(() => {
362
+ if (processingRef.current) return;
363
+ const next = permissionQueueRef.current[0];
364
+ if (!next) {
365
+ setPermissionRequest(null);
366
+ return;
367
+ }
368
+ processingRef.current = true;
369
+ setPermissionRequest({
370
+ toolName: next.toolName,
371
+ toolArgs: next.toolArgs,
372
+ resolve: (result) => {
373
+ permissionQueueRef.current.shift();
374
+ processingRef.current = false;
375
+ setPermissionRequest(null);
376
+ next.resolve(result);
377
+ setTimeout(() => processNextPermission(), 0);
378
+ }
379
+ });
380
+ }, []);
381
+ const permissionHandler = (0, import_react2.useCallback)(
382
+ (toolName, toolArgs) => new Promise((resolve) => {
383
+ permissionQueueRef.current.push({ toolName, toolArgs, resolve });
384
+ processNextPermission();
385
+ }),
386
+ [processNextPermission]
387
+ );
388
+ const [initState] = (0, import_react2.useState)(() => initializeSession(props, permissionHandler));
389
+ const { interactiveSession, registry, manager, commandEffectQueue } = initState;
390
+ manager.onChange = () => forceRender((n) => n + 1);
391
+ if (manager.history.length === 0) {
392
+ const restored = interactiveSession.getFullHistory();
393
+ if (restored.length > 0) {
394
+ manager.syncHistory(restored);
395
+ }
396
+ }
397
+ (0, import_react2.useEffect)(() => {
398
+ if (!props.transportRegistry) return;
399
+ const reg = props.transportRegistry;
400
+ reg.startAll(interactiveSession).catch(() => void 0);
401
+ return () => {
402
+ reg.stopAll().catch(() => void 0);
403
+ };
404
+ }, [interactiveSession, props.transportRegistry]);
405
+ (0, import_react2.useEffect)(() => {
406
+ const onCompact = () => applyCompactEventToManager(interactiveSession, manager);
407
+ const onSkillActivation = () => applySkillActivationEventToManager(interactiveSession, manager);
408
+ const onExecutionWorkspaceEvent = (event) => manager.syncExecutionWorkspaceSnapshot(event.snapshot);
409
+ interactiveSession.on("text_delta", manager.onTextDelta);
410
+ interactiveSession.on("tool_start", manager.onToolStart);
411
+ interactiveSession.on("tool_end", manager.onToolEnd);
412
+ interactiveSession.on("thinking", manager.onThinking);
413
+ interactiveSession.on("complete", manager.onComplete);
414
+ interactiveSession.on("interrupted", manager.onInterrupted);
415
+ interactiveSession.on("error", manager.onError);
416
+ interactiveSession.on("context_update", manager.onContextUpdate);
417
+ interactiveSession.on("compact", onCompact);
418
+ interactiveSession.on("skill_activation", onSkillActivation);
419
+ interactiveSession.on("execution_workspace_event", onExecutionWorkspaceEvent);
420
+ const initCheck = setInterval(() => {
421
+ try {
422
+ const ctx = interactiveSession.getContextState();
423
+ manager.setContextState({
424
+ percentage: ctx.usedPercentage,
425
+ usedTokens: ctx.usedTokens,
426
+ maxTokens: ctx.maxTokens
427
+ });
428
+ const restored = interactiveSession.getFullHistory();
429
+ if (restored.length > 0) {
430
+ manager.syncHistory(restored);
431
+ }
432
+ syncExecutionWorkspaceFromSession(interactiveSession, manager);
433
+ clearInterval(initCheck);
434
+ } catch {
435
+ }
436
+ }, SESSION_INIT_POLL_MS);
437
+ return () => {
438
+ clearInterval(initCheck);
439
+ interactiveSession.off("text_delta", manager.onTextDelta);
440
+ interactiveSession.off("tool_start", manager.onToolStart);
441
+ interactiveSession.off("tool_end", manager.onToolEnd);
442
+ interactiveSession.off("thinking", manager.onThinking);
443
+ interactiveSession.off("complete", manager.onComplete);
444
+ interactiveSession.off("interrupted", manager.onInterrupted);
445
+ interactiveSession.off("error", manager.onError);
446
+ interactiveSession.off("context_update", manager.onContextUpdate);
447
+ interactiveSession.off("compact", onCompact);
448
+ interactiveSession.off("skill_activation", onSkillActivation);
449
+ interactiveSession.off("execution_workspace_event", onExecutionWorkspaceEvent);
450
+ };
451
+ }, [interactiveSession, manager]);
452
+ (0, import_react2.useEffect)(() => {
453
+ manager.syncHistory(interactiveSession.getFullHistory());
454
+ syncExecutionWorkspaceFromSession(interactiveSession, manager);
455
+ if (!manager.isThinking) {
456
+ manager.setPendingPrompt(interactiveSession.getPendingPrompt());
457
+ }
458
+ }, [manager.isThinking, interactiveSession, manager]);
459
+ const handleSubmit = useSlashRouting(
460
+ interactiveSession,
461
+ registry,
462
+ manager,
463
+ commandEffectQueue,
464
+ props.reloadPluginCommandSource
465
+ );
466
+ const handleAbort = (0, import_react2.useCallback)(() => {
467
+ manager.setAborting(true);
468
+ interactiveSession.abort();
469
+ }, [interactiveSession, manager]);
470
+ const handleCancelQueue = (0, import_react2.useCallback)(() => {
471
+ interactiveSession.cancelQueue();
472
+ manager.setPendingPrompt(null);
473
+ }, [interactiveSession, manager]);
474
+ const handleShutdown = (0, import_react2.useCallback)(
475
+ async (reason = "prompt_input_exit") => {
476
+ if (isShuttingDown) return;
477
+ setIsShuttingDown(true);
478
+ manager.addEntry((0, import_agent_core2.messageToHistoryEntry)((0, import_agent_core2.createSystemMessage)("Shutting down...")));
479
+ await interactiveSession.shutdown({ reason, message: "CLI shutdown" });
480
+ },
481
+ [interactiveSession, manager, isShuttingDown]
482
+ );
483
+ const selectExecutionWorkspaceEntry = (0, import_react2.useCallback)(
484
+ (entryId) => manager.selectExecutionWorkspaceEntry(entryId),
485
+ [manager]
486
+ );
487
+ const readExecutionWorkspaceDetail = (0, import_react2.useCallback)(
488
+ (entryId) => interactiveSession.readExecutionWorkspaceDetail(entryId),
489
+ [interactiveSession]
490
+ );
491
+ return {
492
+ interactiveSession,
493
+ registry,
494
+ commandEffectQueue,
495
+ history: manager.history,
496
+ addEntry: (entry) => manager.addEntry(entry),
497
+ streamingText: manager.streamingText,
498
+ activeTools: manager.activeTools,
499
+ isThinking: manager.isThinking,
500
+ isAborting: manager.isAborting,
501
+ isShuttingDown,
502
+ pendingPrompt: manager.pendingPrompt,
503
+ executionWorkspaceSnapshot: manager.executionWorkspaceSnapshot,
504
+ selectedExecutionEntryId: manager.selectedExecutionEntryId,
505
+ permissionRequest,
506
+ contextState: manager.contextState,
507
+ handleSubmit,
508
+ handleAbort,
509
+ handleCancelQueue,
510
+ handleShutdown,
511
+ selectExecutionWorkspaceEntry,
512
+ readExecutionWorkspaceDetail
513
+ };
514
+ }
515
+
516
+ // src/hooks/usePluginCallbacks.ts
517
+ var import_react3 = require("react");
518
+ function createNoOpPluginAdapter() {
519
+ return {
520
+ listInstalled: async () => [],
521
+ listAvailablePlugins: async () => [],
522
+ install: async () => void 0,
523
+ uninstall: async () => void 0,
524
+ enable: async () => void 0,
525
+ disable: async () => void 0,
526
+ marketplaceAdd: async () => "",
527
+ marketplaceRemove: async () => void 0,
528
+ marketplaceUpdate: async () => void 0,
529
+ marketplaceList: async () => [],
530
+ reloadPlugins: async () => ({ loadedPluginCount: 0 })
531
+ };
532
+ }
533
+ function usePluginCallbacks(_cwd) {
534
+ return (0, import_react3.useMemo)(() => createNoOpPluginAdapter(), []);
535
+ }
536
+
537
+ // src/hooks/useSideEffects.ts
538
+ var import_react5 = require("react");
539
+ var import_ink = require("ink");
540
+ var import_agent_core5 = require("@robota-sdk/agent-core");
541
+
542
+ // src/hooks/command-effect-handler.ts
543
+ var import_agent_sdk2 = require("@robota-sdk/agent-sdk");
544
+ var import_agent_core3 = require("@robota-sdk/agent-core");
545
+ function applyCommandEffects(effects, deps) {
546
+ for (const effect of effects) {
547
+ if (effect.type === "model-change-requested") {
548
+ deps.requestModelChange(effect.modelId);
549
+ return true;
550
+ }
551
+ if (effect.type === "language-change-requested") {
552
+ applyLanguageEffect(effect.language, deps);
553
+ return true;
554
+ }
555
+ if (effect.type === "settings-reset-requested") {
556
+ applySettingsResetEffect(deps);
557
+ return true;
558
+ }
559
+ if (effect.type === "session-exit-requested") {
560
+ deps.requestShutdown(
561
+ effect.reason ?? "prompt_input_exit",
562
+ effect.message ?? "User requested exit"
563
+ );
564
+ return true;
565
+ }
566
+ if (effect.type === "session-restart-requested") {
567
+ deps.requestShutdown(effect.reason, effect.message);
568
+ return true;
569
+ }
570
+ if (effect.type === "plugin-tui-requested") {
571
+ deps.openPluginTUI();
572
+ return true;
573
+ }
574
+ if (effect.type === "settings-tui-requested") {
575
+ deps.openTransportTUI();
576
+ return true;
577
+ }
578
+ if (effect.type === "session-picker-requested") {
579
+ deps.openSessionPicker();
580
+ return true;
581
+ }
582
+ if (effect.type === "session-renamed") {
583
+ deps.renameSession(effect.name);
584
+ return true;
585
+ }
586
+ if (effect.type === "statusline-settings-patch") {
587
+ if ((0, import_agent_sdk2.isStatusLineCommandSettingsPatch)(effect.patch)) {
588
+ return deps.applyStatusLinePatch(effect.patch);
589
+ }
590
+ }
591
+ }
592
+ return false;
593
+ }
594
+ function applyLanguageEffect(language, deps) {
595
+ const settingsPath = deps.cliAdapter.getUserSettingsPath();
596
+ const settings = deps.cliAdapter.readSettings(settingsPath);
597
+ settings.language = language;
598
+ deps.cliAdapter.writeSettings(settingsPath, settings);
599
+ deps.addEntry(
600
+ (0, import_agent_core3.messageToHistoryEntry)((0, import_agent_core3.createSystemMessage)(`Language set to "${language}". Restarting...`))
601
+ );
602
+ deps.requestShutdown("other", "Language change restart");
603
+ }
604
+ function applySettingsResetEffect(deps) {
605
+ const settingsPath = deps.cliAdapter.getUserSettingsPath();
606
+ if (deps.cliAdapter.deleteSettings(settingsPath)) {
607
+ deps.addEntry(
608
+ (0, import_agent_core3.messageToHistoryEntry)((0, import_agent_core3.createSystemMessage)(`Deleted ${settingsPath}. Exiting...`))
609
+ );
610
+ } else {
611
+ deps.addEntry((0, import_agent_core3.messageToHistoryEntry)((0, import_agent_core3.createSystemMessage)("No user settings found.")));
612
+ }
613
+ deps.requestShutdown("other", "Reset settings restart");
614
+ }
615
+
616
+ // src/tui-cli-adapter-context.tsx
617
+ var import_react4 = require("react");
618
+ var TuiCliAdapterContext = (0, import_react4.createContext)(null);
619
+ var TuiCliAdapterProvider = TuiCliAdapterContext.Provider;
620
+ function useTuiCliAdapter() {
621
+ const adapter = (0, import_react4.useContext)(TuiCliAdapterContext);
622
+ if (!adapter) throw new Error("TuiCliAdapterContext not provided");
623
+ return adapter;
624
+ }
625
+
626
+ // src/hooks/model-change-side-effect.ts
627
+ var import_agent_core4 = require("@robota-sdk/agent-core");
628
+ function formatModelChangeConfirmationMessage(modelId) {
629
+ return `Change model to ${(0, import_agent_core4.getModelName)(modelId)}? This will exit the current session so the next session uses it.`;
630
+ }
631
+ function formatModelChangeExitMessage(modelId) {
632
+ return `Model changed to ${(0, import_agent_core4.getModelName)(modelId)}. Exiting so the next session uses it.`;
633
+ }
634
+ function applyConfirmedModelChange(deps) {
635
+ const applyModelChange = deps.applyModelChange;
636
+ if (!applyModelChange) {
637
+ deps.addEntry(
638
+ (0, import_agent_core4.messageToHistoryEntry)((0, import_agent_core4.createSystemMessage)("Model change unavailable: no adapter provided."))
639
+ );
640
+ return;
641
+ }
642
+ const options = deps.providerOverride !== void 0 ? { providerOverride: deps.providerOverride } : void 0;
643
+ try {
644
+ applyModelChange(deps.cwd, deps.modelId, options);
645
+ deps.addEntry(
646
+ (0, import_agent_core4.messageToHistoryEntry)((0, import_agent_core4.createSystemMessage)(formatModelChangeExitMessage(deps.modelId)))
647
+ );
648
+ deps.requestShutdown("other", "Model change applied");
649
+ } catch (error) {
650
+ deps.addEntry(
651
+ (0, import_agent_core4.messageToHistoryEntry)(
652
+ (0, import_agent_core4.createSystemMessage)(`Failed: ${error instanceof Error ? error.message : String(error)}`)
653
+ )
654
+ );
655
+ }
656
+ }
657
+ function addModelChangeCancelledMessage(addEntry) {
658
+ addEntry((0, import_agent_core4.messageToHistoryEntry)((0, import_agent_core4.createSystemMessage)("Model change cancelled.")));
659
+ }
660
+
661
+ // src/hooks/useSideEffects.ts
662
+ var EXIT_DELAY_MS = 500;
663
+ function useSideEffects({
664
+ cwd,
665
+ providerOverride,
666
+ interactiveSession,
667
+ commandEffectQueue,
668
+ addEntry,
669
+ baseHandleSubmit,
670
+ setSessionName,
671
+ setStatusLineSettings,
672
+ showSessionPickerOnStart
673
+ }) {
674
+ const { exit } = (0, import_ink.useApp)();
675
+ const cliAdapter = useTuiCliAdapter();
676
+ const [pendingModelId, setPendingModelId] = (0, import_react5.useState)(null);
677
+ const pendingModelChangeRef = (0, import_react5.useRef)(null);
678
+ const [pendingInteractionPrompt, setPendingInteractionPrompt] = (0, import_react5.useState)(null);
679
+ const commandInteractionRef = (0, import_react5.useRef)(null);
680
+ const [showPluginTUI, setShowPluginTUI] = (0, import_react5.useState)(false);
681
+ const [showSessionPicker, setShowSessionPicker] = (0, import_react5.useState)(showSessionPickerOnStart ?? false);
682
+ const [showTransportTUI, setShowTransportTUI] = (0, import_react5.useState)(false);
683
+ const requestShutdown = (0, import_react5.useCallback)(
684
+ (reason, message) => {
685
+ addEntry((0, import_agent_core5.messageToHistoryEntry)((0, import_agent_core5.createSystemMessage)("Shutting down...")));
686
+ setTimeout(() => {
687
+ void interactiveSession.shutdown({ reason, message }).finally(() => exit());
688
+ }, EXIT_DELAY_MS);
689
+ },
690
+ [interactiveSession, addEntry, exit]
691
+ );
692
+ const applyEffects = (0, import_react5.useCallback)(
693
+ (effects) => applyCommandEffects(effects, {
694
+ addEntry,
695
+ requestShutdown,
696
+ requestModelChange: (modelId) => {
697
+ pendingModelChangeRef.current = modelId;
698
+ setPendingModelId(modelId);
699
+ },
700
+ openPluginTUI: () => setShowPluginTUI(true),
701
+ openSessionPicker: () => setShowSessionPicker(true),
702
+ openTransportTUI: () => setShowTransportTUI(true),
703
+ renameSession: (name) => {
704
+ interactiveSession.setName(name);
705
+ setSessionName(name);
706
+ },
707
+ applyStatusLinePatch: (patch) => {
708
+ setStatusLineSettings(
709
+ cliAdapter.applyStatusLineSettings(cliAdapter.getUserSettingsPath(), patch)
710
+ );
711
+ return true;
712
+ },
713
+ cliAdapter
714
+ }),
715
+ [
716
+ addEntry,
717
+ cliAdapter,
718
+ interactiveSession,
719
+ requestShutdown,
720
+ setSessionName,
721
+ setStatusLineSettings
722
+ ]
723
+ );
724
+ const applyCommandResult = (0, import_react5.useCallback)(
725
+ (result) => {
726
+ if (result.message.length > 0) {
727
+ addEntry((0, import_agent_core5.messageToHistoryEntry)((0, import_agent_core5.createSystemMessage)(result.message)));
728
+ }
729
+ if (result.interaction !== void 0) {
730
+ commandInteractionRef.current = result.interaction;
731
+ setPendingInteractionPrompt(result.interaction.prompt);
732
+ return;
733
+ }
734
+ commandInteractionRef.current = null;
735
+ setPendingInteractionPrompt(null);
736
+ if (result.effects !== void 0 && result.effects.length > 0) {
737
+ applyEffects(result.effects);
738
+ }
739
+ },
740
+ [addEntry, applyEffects]
741
+ );
742
+ const applyQueuedCommandState = (0, import_react5.useCallback)(() => {
743
+ const queued = commandEffectQueue.drain();
744
+ if (queued === void 0) {
745
+ return false;
746
+ }
747
+ if (queued.type === "interaction") {
748
+ const { interaction } = queued;
749
+ commandInteractionRef.current = interaction;
750
+ setPendingInteractionPrompt(interaction.prompt);
751
+ return true;
752
+ }
753
+ return applyEffects(queued.effects);
754
+ }, [applyEffects, commandEffectQueue]);
755
+ const handleSubmit = (0, import_react5.useCallback)(
756
+ async (input) => {
757
+ await baseHandleSubmit(input);
758
+ applyQueuedCommandState();
759
+ },
760
+ [baseHandleSubmit, applyQueuedCommandState]
761
+ );
762
+ const handleModelConfirm = (0, import_react5.useCallback)(
763
+ (index) => {
764
+ const modelId = pendingModelChangeRef.current;
765
+ setPendingModelId(null);
766
+ pendingModelChangeRef.current = null;
767
+ if (index === 0 && modelId) {
768
+ applyConfirmedModelChange({
769
+ cwd,
770
+ modelId,
771
+ providerOverride,
772
+ addEntry,
773
+ requestShutdown,
774
+ applyModelChange: (c, m, opts) => {
775
+ cliAdapter.applyActiveModelChange(c, m, opts);
776
+ return { applied: true };
777
+ }
778
+ });
779
+ } else {
780
+ addModelChangeCancelledMessage(addEntry);
781
+ }
782
+ },
783
+ [cwd, providerOverride, addEntry, cliAdapter, requestShutdown]
784
+ );
785
+ const handleInteractionSubmit = (0, import_react5.useCallback)(
786
+ async (value) => {
787
+ const interaction = commandInteractionRef.current;
788
+ if (interaction === null) {
789
+ setPendingInteractionPrompt(null);
790
+ return;
791
+ }
792
+ try {
793
+ const result = await interaction.submit(value);
794
+ applyCommandResult(result);
795
+ } catch (err) {
796
+ commandInteractionRef.current = null;
797
+ setPendingInteractionPrompt(null);
798
+ addEntry(
799
+ (0, import_agent_core5.messageToHistoryEntry)(
800
+ (0, import_agent_core5.createSystemMessage)(`Failed: ${err instanceof Error ? err.message : String(err)}`)
801
+ )
802
+ );
803
+ }
804
+ },
805
+ [addEntry, applyCommandResult]
806
+ );
807
+ const handleInteractionCancel = (0, import_react5.useCallback)(() => {
808
+ const interaction = commandInteractionRef.current;
809
+ commandInteractionRef.current = null;
810
+ setPendingInteractionPrompt(null);
811
+ if (interaction?.cancel === void 0) {
812
+ addEntry((0, import_agent_core5.messageToHistoryEntry)((0, import_agent_core5.createSystemMessage)("Command interaction cancelled.")));
813
+ return;
814
+ }
815
+ Promise.resolve(interaction.cancel()).then((result) => applyCommandResult(result)).catch((err) => {
816
+ addEntry(
817
+ (0, import_agent_core5.messageToHistoryEntry)(
818
+ (0, import_agent_core5.createSystemMessage)(`Failed: ${err instanceof Error ? err.message : String(err)}`)
819
+ )
820
+ );
821
+ });
822
+ }, [addEntry, applyCommandResult]);
823
+ return {
824
+ handleSubmit,
825
+ pendingModelId,
826
+ pendingInteractionPrompt,
827
+ showPluginTUI,
828
+ showSessionPicker,
829
+ showTransportTUI,
830
+ setPendingModelId,
831
+ setShowPluginTUI,
832
+ setShowSessionPicker,
833
+ setShowTransportTUI,
834
+ handleModelConfirm,
835
+ handleInteractionSubmit,
836
+ handleInteractionCancel
837
+ };
838
+ }
839
+
840
+ // src/hooks/useStatusLineSettings.ts
841
+ var import_react6 = require("react");
842
+ var import_agent_sdk3 = require("@robota-sdk/agent-sdk");
843
+ function readStatusLineSettings(settings) {
844
+ const defaults = { ...import_agent_sdk3.DEFAULT_STATUS_LINE_COMMAND_SETTINGS };
845
+ const raw = settings.statusline;
846
+ if (!isRecord(raw)) {
847
+ return defaults;
848
+ }
849
+ return {
850
+ enabled: typeof raw.enabled === "boolean" ? raw.enabled : defaults.enabled,
851
+ gitBranch: typeof raw.gitBranch === "boolean" ? raw.gitBranch : defaults.gitBranch
852
+ };
853
+ }
854
+ function isRecord(value) {
855
+ return value !== null && typeof value === "object" && !Array.isArray(value) && !(value instanceof Date);
856
+ }
857
+ function useStatusLineSettings() {
858
+ const cliAdapter = useTuiCliAdapter();
859
+ return (0, import_react6.useState)(
860
+ () => readStatusLineSettings(cliAdapter.readSettings(cliAdapter.getUserSettingsPath()))
861
+ );
862
+ }
863
+
864
+ // src/MessageList.tsx
865
+ var import_react7 = __toESM(require("react"), 1);
866
+ var import_ink5 = require("ink");
867
+ var import_agent_core7 = require("@robota-sdk/agent-core");
868
+
869
+ // src/render-markdown.ts
870
+ var import_marked = require("marked");
871
+ var import_marked_terminal = __toESM(require("marked-terminal"), 1);
872
+ var ANSI_LIGHT_RED = "\x1B[38;5;210m";
873
+ var ANSI_LIGHT_GREEN = "\x1B[38;5;120m";
874
+ var ANSI_CYAN = "\x1B[36m";
875
+ var ANSI_DIM = "\x1B[2m";
876
+ var ANSI_DARK_RED_BACKGROUND = "\x1B[48;5;52m";
877
+ var ANSI_DARK_GREEN_BACKGROUND = "\x1B[48;5;22m";
878
+ var ANSI_RESET = "\x1B[0m";
879
+ var CODE_BLOCK_INDENT = " ";
880
+ var ZERO_COLOR = "0";
881
+ var TerminalRendererConstructor = import_marked_terminal.default;
882
+ function shouldUseColor(option) {
883
+ if (option !== void 0) {
884
+ return option;
885
+ }
886
+ if (process.env.NO_COLOR || process.env.FORCE_COLOR === ZERO_COLOR) {
887
+ return false;
888
+ }
889
+ if (process.env.FORCE_COLOR) {
890
+ return true;
891
+ }
892
+ return Boolean(process.stdout.isTTY);
893
+ }
894
+ function isDiffLanguage(language) {
895
+ return language?.trim().toLowerCase() === "diff";
896
+ }
897
+ function styleAddedOrRemovedDiffRow(line, rowWidth, color) {
898
+ const row = `${CODE_BLOCK_INDENT}${line}`.padEnd(rowWidth);
899
+ if (!color) {
900
+ return row.trimEnd();
901
+ }
902
+ if (line.startsWith("+")) {
903
+ return `${ANSI_DARK_GREEN_BACKGROUND}${ANSI_LIGHT_GREEN}${row}${ANSI_RESET}`;
904
+ }
905
+ if (line.startsWith("-")) {
906
+ return `${ANSI_DARK_RED_BACKGROUND}${ANSI_LIGHT_RED}${row}${ANSI_RESET}`;
907
+ }
908
+ return row.trimEnd();
909
+ }
910
+ function colorizeDiffLine(line, color, rowWidth) {
911
+ if (line.startsWith("+") || line.startsWith("-")) {
912
+ return styleAddedOrRemovedDiffRow(line, rowWidth, color);
913
+ }
914
+ const row = `${CODE_BLOCK_INDENT}${line}`;
915
+ if (!color) {
916
+ return row;
917
+ }
918
+ if (line.startsWith("@@")) {
919
+ return `${ANSI_CYAN}${row}${ANSI_RESET}`;
920
+ }
921
+ if (line.startsWith("diff ") || line.startsWith("index ")) {
922
+ return `${ANSI_DIM}${row}${ANSI_RESET}`;
923
+ }
924
+ return row;
925
+ }
926
+ function resolveDiffRowWidth(lines, requestedWidth) {
927
+ const minimumWidth = lines.reduce(
928
+ (maxWidth, line) => Math.max(maxWidth, CODE_BLOCK_INDENT.length + line.length),
929
+ 0
930
+ );
931
+ if (requestedWidth === void 0) {
932
+ return minimumWidth;
933
+ }
934
+ return Math.max(minimumWidth, requestedWidth);
935
+ }
936
+ function renderDiffCodeBlock(code, color, codeBlockWidth) {
937
+ const lines = code.split("\n");
938
+ const rowWidth = resolveDiffRowWidth(lines, codeBlockWidth);
939
+ const body = lines.map((line) => colorizeDiffLine(line, color, rowWidth)).join("\n");
940
+ return `${body}
941
+
942
+ `;
943
+ }
944
+ function createTerminalRenderer(color, codeBlockWidth) {
945
+ const renderer = new TerminalRendererConstructor(void 0, { ignoreIllegals: true });
946
+ const renderCode = renderer.code.bind(renderer);
947
+ renderer.code = (code, language, escaped) => {
948
+ if (isDiffLanguage(language)) {
949
+ return renderDiffCodeBlock(code, color, codeBlockWidth);
950
+ }
951
+ return renderCode(code, language, escaped);
952
+ };
953
+ return renderer;
954
+ }
955
+ function renderMarkdown(md, options = {}) {
956
+ const result = import_marked.marked.parse(md, {
957
+ renderer: createTerminalRenderer(shouldUseColor(options.color), options.codeBlockWidth)
958
+ });
959
+ return typeof result === "string" ? result.trimEnd() : md;
960
+ }
961
+
962
+ // src/ToolDiffBlock.tsx
963
+ var import_ink2 = require("ink");
964
+
965
+ // src/utils/tool-diff-summary.ts
966
+ var MAX_DIFF_LINES = 12;
967
+ var TRUNCATED_SHOW = 10;
968
+ function buildToolDiffSummary(input) {
969
+ const visibleLines = input.lines.length > MAX_DIFF_LINES ? selectVisibleDiffLines(input.lines) : input.lines;
970
+ const lineNumberWidth = Math.max(...visibleLines.map((line) => line.lineNumber), 0).toString().length;
971
+ const body = visibleLines.map((line) => formatDiffLine(line, lineNumberWidth));
972
+ const truncated = input.lines.length > MAX_DIFF_LINES;
973
+ return {
974
+ file: input.file,
975
+ markdown: ["```diff", ...body, "```"].join("\n"),
976
+ truncated,
977
+ remainingLineCount: truncated ? input.lines.length - visibleLines.length : 0
978
+ };
979
+ }
980
+ function formatDiffLine(line, lineNumberWidth) {
981
+ if (line.type === "hunk") return line.text;
982
+ const lineNumber = line.lineNumber.toString().padStart(lineNumberWidth, " ");
983
+ if (line.type === "remove") return `- ${lineNumber} | ${line.text}`;
984
+ if (line.type === "add") return `+ ${lineNumber} | ${line.text}`;
985
+ return ` ${lineNumber} | ${line.text}`;
986
+ }
987
+ function selectVisibleDiffLines(lines) {
988
+ const groups = groupByHunk(lines);
989
+ const visible = [];
990
+ for (const group of groups) {
991
+ if (visible.length === 0 && group.length > TRUNCATED_SHOW) {
992
+ return group.slice(0, TRUNCATED_SHOW);
993
+ }
994
+ if (visible.length + group.length > TRUNCATED_SHOW) break;
995
+ visible.push(...group);
996
+ }
997
+ return visible.length > 0 ? visible : lines.slice(0, TRUNCATED_SHOW);
998
+ }
999
+ function groupByHunk(lines) {
1000
+ const groups = [];
1001
+ let current = [];
1002
+ for (const line of lines) {
1003
+ if (line.type === "hunk" && current.length > 0) {
1004
+ groups.push(current);
1005
+ current = [line];
1006
+ continue;
1007
+ }
1008
+ current.push(line);
1009
+ }
1010
+ if (current.length > 0) {
1011
+ groups.push(current);
1012
+ }
1013
+ return groups;
1014
+ }
1015
+
1016
+ // src/ToolDiffBlock.tsx
1017
+ var import_jsx_runtime = require("react/jsx-runtime");
1018
+ function ToolDiffBlock({ file, lines }) {
1019
+ const summary = buildToolDiffSummary({ file, lines });
1020
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_ink2.Box, { flexDirection: "column", marginLeft: 4, children: [
1021
+ summary.file && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_ink2.Text, { color: "white", dimColor: true, children: [
1022
+ "\u2502 ",
1023
+ summary.file
1024
+ ] }),
1025
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ink2.Text, { children: renderMarkdown(summary.markdown) }),
1026
+ summary.truncated && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_ink2.Text, { color: "white", dimColor: true, children: [
1027
+ "\u2502 ... and ",
1028
+ summary.remainingLineCount,
1029
+ " more lines"
1030
+ ] })
1031
+ ] });
1032
+ }
1033
+
1034
+ // src/UsageSummaryEntry.tsx
1035
+ var import_ink3 = require("ink");
1036
+ var import_agent_core6 = require("@robota-sdk/agent-core");
1037
+ var import_jsx_runtime2 = require("react/jsx-runtime");
1038
+ var TOKEN_COMPACT_THRESHOLD = 1e3;
1039
+ function UsageSummaryEntry({ entry }) {
1040
+ const usage = entry.data;
1041
+ if (!usage) return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, {});
1042
+ const prompt = usage.promptTokens !== void 0 ? formatUsageTokenCount(usage.promptTokens) : "?";
1043
+ const completion = usage.completionTokens !== void 0 ? formatUsageTokenCount(usage.completionTokens) : "?";
1044
+ const total = formatUsageTokenCount(usage.totalTokens);
1045
+ const context = `${Math.round(usage.contextUsedPercentage)}% (${(0, import_agent_core6.formatTokenCount)(
1046
+ usage.contextUsedTokens
1047
+ )}/${(0, import_agent_core6.formatTokenCount)(usage.contextMaxTokens)})`;
1048
+ const costLabel = usage.costStatus === "unknown" ? "cost unknown" : `cost ${usage.costStatus}`;
1049
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ink3.Box, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink3.Box, { children: [
1050
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink3.Text, { color: "white", bold: true, children: [
1051
+ "Usage:",
1052
+ " "
1053
+ ] }),
1054
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ink3.Text, { dimColor: true, children: [
1055
+ usage.kind,
1056
+ " ",
1057
+ total,
1058
+ " tokens (in ",
1059
+ prompt,
1060
+ " / out ",
1061
+ completion,
1062
+ ") \xB7 Context ",
1063
+ context,
1064
+ " \xB7",
1065
+ " ",
1066
+ costLabel
1067
+ ] })
1068
+ ] }) });
1069
+ }
1070
+ function formatUsageTokenCount(tokens) {
1071
+ return tokens < TOKEN_COMPACT_THRESHOLD ? tokens.toLocaleString() : (0, import_agent_core6.formatTokenCount)(tokens);
1072
+ }
1073
+
1074
+ // src/command-output-summary.ts
1075
+ var MAX_PREVIEW_LINES = 4;
1076
+ var SUCCESS_EXIT_CODE = 0;
1077
+ var COMMAND_TOOL_NAMES = /* @__PURE__ */ new Set(["Bash", "BackgroundProcess"]);
1078
+ function formatCommandOutputSummary(tool) {
1079
+ if (!COMMAND_TOOL_NAMES.has(tool.toolName) || !tool.toolResultData) return void 0;
1080
+ const parsed = parseToolResultData(tool.toolResultData);
1081
+ const exitCode = getNumberValue(parsed, "exitCode");
1082
+ const successValue = getBooleanValue(parsed, "success");
1083
+ const output = buildOutputText(tool.toolResultData, parsed);
1084
+ const lines = trimTrailingBlankLines(splitOutputLines(output));
1085
+ const previewLines = lines.slice(0, MAX_PREVIEW_LINES);
1086
+ const omittedLineCount = Math.max(0, lines.length - previewLines.length);
1087
+ const isFailed = tool.result === "error" || successValue === false || exitCode !== void 0 && exitCode !== SUCCESS_EXIT_CODE;
1088
+ return {
1089
+ status: isFailed ? "error" : "success",
1090
+ statusLabel: formatStatusLabel(isFailed, exitCode),
1091
+ previewLines,
1092
+ omittedLineCount,
1093
+ transcriptHint: omittedLineCount > 0 ? `... +${omittedLineCount} lines (full output in session transcript)` : void 0
1094
+ };
1095
+ }
1096
+ function parseToolResultData(value) {
1097
+ try {
1098
+ return JSON.parse(value);
1099
+ } catch {
1100
+ return value;
1101
+ }
1102
+ }
1103
+ function buildOutputText(raw, parsed) {
1104
+ if (!isUniversalObject(parsed)) return raw;
1105
+ const output = getStringValue(parsed, "output");
1106
+ if (output !== void 0) return output;
1107
+ const stdout = getStringValue(parsed, "stdout");
1108
+ const stderr = getStringValue(parsed, "stderr");
1109
+ const error = getStringValue(parsed, "error");
1110
+ const lines = [];
1111
+ if (stdout) lines.push(stdout);
1112
+ if (stderr) lines.push(prefixLines(stderr, "[stderr] "));
1113
+ if (!stdout && !stderr && error) lines.push(error);
1114
+ return lines.join("\n");
1115
+ }
1116
+ function formatStatusLabel(isFailed, exitCode) {
1117
+ if (exitCode !== void 0 && exitCode !== SUCCESS_EXIT_CODE) return `exit ${exitCode}`;
1118
+ return isFailed ? "error" : "ok";
1119
+ }
1120
+ function splitOutputLines(output) {
1121
+ if (!output) return [];
1122
+ return output.replace(/\r\n/g, "\n").split("\n");
1123
+ }
1124
+ function trimTrailingBlankLines(lines) {
1125
+ let end = lines.length;
1126
+ while (end > 0 && lines[end - 1].trim().length === 0) {
1127
+ end -= 1;
1128
+ }
1129
+ return lines.slice(0, end);
1130
+ }
1131
+ function prefixLines(value, prefix) {
1132
+ return splitOutputLines(value).map((line) => `${prefix}${line}`).join("\n");
1133
+ }
1134
+ function isUniversalObject(value) {
1135
+ return typeof value === "object" && value !== null && !Array.isArray(value) && !(value instanceof Date);
1136
+ }
1137
+ function getStringValue(source, key) {
1138
+ if (!isUniversalObject(source)) return void 0;
1139
+ const value = source[key];
1140
+ return typeof value === "string" ? value : void 0;
1141
+ }
1142
+ function getNumberValue(source, key) {
1143
+ if (!isUniversalObject(source)) return void 0;
1144
+ const value = source[key];
1145
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
1146
+ }
1147
+ function getBooleanValue(source, key) {
1148
+ if (!isUniversalObject(source)) return void 0;
1149
+ const value = source[key];
1150
+ return typeof value === "boolean" ? value : void 0;
1151
+ }
1152
+
1153
+ // src/ToolCommandOutput.tsx
1154
+ var import_ink4 = require("ink");
1155
+ var import_jsx_runtime3 = require("react/jsx-runtime");
1156
+ function ToolCommandOutput({ tool }) {
1157
+ const summary = formatCommandOutputSummary(tool);
1158
+ if (!summary) return null;
1159
+ if (summary.statusLabel === "ok" && summary.previewLines.length === 0 && !summary.transcriptHint) {
1160
+ return null;
1161
+ }
1162
+ const color = summary.status === "error" ? "red" : "white";
1163
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_ink4.Box, { flexDirection: "column", marginLeft: 4, children: [
1164
+ summary.statusLabel !== "ok" && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ink4.Text, { color, dimColor: true, children: summary.statusLabel }),
1165
+ summary.previewLines.map((line, index) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ink4.Text, { color, dimColor: true, children: line }, `${line}-${index}`)),
1166
+ summary.transcriptHint && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ink4.Text, { dimColor: true, children: summary.transcriptHint })
1167
+ ] });
1168
+ }
1169
+
1170
+ // src/MessageList.tsx
1171
+ var import_jsx_runtime4 = require("react/jsx-runtime");
1172
+ function getToolSummaryStatus(tool) {
1173
+ if (formatCommandOutputSummary(tool)?.status === "error") return "\u2717";
1174
+ if (tool.isRunning) return "\u27F3";
1175
+ if (tool.result === "error") return "\u2717";
1176
+ if (tool.result === "denied") return "\u2298";
1177
+ return "\u2713";
1178
+ }
1179
+ function getToolSummaryColor(tool) {
1180
+ if (formatCommandOutputSummary(tool)?.status === "error" || tool.result === "error") return "red";
1181
+ if (tool.isRunning || tool.result === "denied") return "yellow";
1182
+ return "green";
1183
+ }
1184
+ function getToolSummaryLabel(tool) {
1185
+ return `${getToolSummaryStatus(tool)} ${tool.toolName}${tool.firstArg ? `(${tool.firstArg})` : ""}`;
1186
+ }
1187
+ function RoleLabel({ role }) {
1188
+ switch (role) {
1189
+ case "user":
1190
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_ink5.Text, { color: "green", bold: true, children: [
1191
+ "You:",
1192
+ " "
1193
+ ] });
1194
+ case "assistant":
1195
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_ink5.Text, { color: "cyan", bold: true, children: [
1196
+ "Robota:",
1197
+ " "
1198
+ ] });
1199
+ case "system":
1200
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_ink5.Text, { color: "yellow", bold: true, children: [
1201
+ "System:",
1202
+ " "
1203
+ ] });
1204
+ case "tool":
1205
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_ink5.Text, { color: "white", bold: true, children: [
1206
+ "Tool:",
1207
+ " "
1208
+ ] });
1209
+ }
1210
+ }
1211
+ function ToolMessage({ message }) {
1212
+ if (!(0, import_agent_core7.isToolMessage)(message)) {
1213
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, {});
1214
+ }
1215
+ const toolName = message.name;
1216
+ const content = message.content;
1217
+ let summaries = null;
1218
+ try {
1219
+ const parsed = JSON.parse(content);
1220
+ if (Array.isArray(parsed) && parsed.length > 0 && typeof parsed[0].line === "string") {
1221
+ summaries = parsed;
1222
+ }
1223
+ } catch {
1224
+ }
1225
+ if (summaries) {
1226
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_ink5.Box, { flexDirection: "column", marginBottom: 1, children: [
1227
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_ink5.Box, { children: [
1228
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_ink5.Text, { color: "white", bold: true, children: [
1229
+ "Tool:",
1230
+ " "
1231
+ ] }),
1232
+ toolName && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_ink5.Text, { color: "white", dimColor: true, children: [
1233
+ "[",
1234
+ toolName,
1235
+ "]"
1236
+ ] })
1237
+ ] }),
1238
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ink5.Text, { children: " " }),
1239
+ summaries.map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_ink5.Box, { flexDirection: "column", children: [
1240
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_ink5.Text, { color: "green", children: [
1241
+ " ",
1242
+ "\u2713",
1243
+ " ",
1244
+ s.line
1245
+ ] }),
1246
+ s.diffLines && s.diffLines.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ToolDiffBlock, { file: s.diffFile, lines: s.diffLines })
1247
+ ] }, i))
1248
+ ] });
1249
+ }
1250
+ const lines = content.split("\n").filter((l) => l.trim());
1251
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_ink5.Box, { flexDirection: "column", marginBottom: 1, children: [
1252
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_ink5.Box, { children: [
1253
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_ink5.Text, { color: "white", bold: true, children: [
1254
+ "Tool:",
1255
+ " "
1256
+ ] }),
1257
+ toolName && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_ink5.Text, { color: "white", dimColor: true, children: [
1258
+ "[",
1259
+ toolName,
1260
+ "]"
1261
+ ] })
1262
+ ] }),
1263
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ink5.Text, { children: " " }),
1264
+ lines.map((line, i) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_ink5.Text, { color: "green", children: [
1265
+ " ",
1266
+ "\u2713",
1267
+ " ",
1268
+ line
1269
+ ] }, i))
1270
+ ] });
1271
+ }
1272
+ var MessageItem = import_react7.default.memo(function MessageItem2({
1273
+ message
1274
+ }) {
1275
+ if ((0, import_agent_core7.isToolMessage)(message)) {
1276
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ToolMessage, { message });
1277
+ }
1278
+ const content = message.content ?? "";
1279
+ const isInterrupted = message.state === "interrupted";
1280
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_ink5.Box, { flexDirection: "column", marginBottom: 1, children: [
1281
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ink5.Box, { children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(RoleLabel, { role: message.role }) }),
1282
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ink5.Text, { children: " " }),
1283
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ink5.Box, { marginLeft: 2, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ink5.Text, { wrap: "wrap", children: (0, import_agent_core7.isAssistantMessage)(message) ? renderMarkdown(content + (isInterrupted ? "\n\n_(interrupted)_" : "")) : content }) })
1284
+ ] });
1285
+ });
1286
+ function ToolSummaryEntry({ entry }) {
1287
+ const data = entry.data;
1288
+ const tools = data?.tools;
1289
+ const lines = data?.summary?.split("\n") ?? [];
1290
+ if (tools && tools.length > 0) {
1291
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_ink5.Box, { flexDirection: "column", marginBottom: 1, children: [
1292
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ink5.Box, { children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_ink5.Text, { color: "white", bold: true, children: [
1293
+ "Tool:",
1294
+ " "
1295
+ ] }) }),
1296
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ink5.Text, { children: " " }),
1297
+ tools.map((tool, i) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_ink5.Box, { flexDirection: "column", children: [
1298
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_ink5.Text, { color: getToolSummaryColor(tool), children: [
1299
+ " ",
1300
+ getToolSummaryLabel(tool)
1301
+ ] }),
1302
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ToolCommandOutput, { tool }),
1303
+ tool.diffLines && tool.diffLines.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ToolDiffBlock, { file: tool.diffFile, lines: tool.diffLines })
1304
+ ] }, i))
1305
+ ] });
1306
+ }
1307
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_ink5.Box, { flexDirection: "column", marginBottom: 1, children: [
1308
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ink5.Box, { children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_ink5.Text, { color: "white", bold: true, children: [
1309
+ "Tool:",
1310
+ " "
1311
+ ] }) }),
1312
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ink5.Text, { children: " " }),
1313
+ lines.map((line, i) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_ink5.Text, { color: "green", children: [
1314
+ " ",
1315
+ line
1316
+ ] }, i))
1317
+ ] });
1318
+ }
1319
+ function EventEntry({ entry }) {
1320
+ const eventData = entry.data;
1321
+ const eventMessage = typeof eventData?.message === "string" ? eventData.message : typeof eventData?.content === "string" ? eventData.content : entry.type;
1322
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_ink5.Box, { flexDirection: "column", marginBottom: 1, children: [
1323
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ink5.Box, { children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_ink5.Text, { color: "yellow", bold: true, children: [
1324
+ "System:",
1325
+ " "
1326
+ ] }) }),
1327
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ink5.Text, { children: " " }),
1328
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ink5.Box, { marginLeft: 2, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ink5.Text, { wrap: "wrap", children: eventMessage }) })
1329
+ ] });
1330
+ }
1331
+ function EntryItem({ entry }) {
1332
+ if (entry.category === "chat") {
1333
+ const message = entry.data;
1334
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(MessageItem, { message });
1335
+ }
1336
+ if (entry.type === "tool-summary") {
1337
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ToolSummaryEntry, { entry });
1338
+ }
1339
+ if (entry.type === "usage-summary") {
1340
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(UsageSummaryEntry, { entry });
1341
+ }
1342
+ if (entry.type === "tool-start" || entry.type === "tool-end") {
1343
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, {});
1344
+ }
1345
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(EventEntry, { entry });
1346
+ }
1347
+ function MessageList({ history }) {
1348
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ink5.Box, { flexDirection: "column", children: history.map((entry) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(EntryItem, { entry }, entry.id)) });
1349
+ }
1350
+
1351
+ // src/SessionStatusBar.tsx
1352
+ var import_react8 = require("react");
1353
+ var import_agent_core9 = require("@robota-sdk/agent-core");
1354
+
1355
+ // src/StatusBar.tsx
1356
+ var import_ink6 = require("ink");
1357
+ var import_agent_core8 = require("@robota-sdk/agent-core");
1358
+
1359
+ // src/status-activity.ts
1360
+ var NO_ACTIVE_ITEMS = 0;
1361
+ function formatStatusActivity(input) {
1362
+ const base = getPrimaryActivity(input);
1363
+ const segments = input.hasPendingPrompt && base.kind !== "queued" ? ["queued"] : [];
1364
+ const text = [base.label, ...segments].join(" \xB7 ");
1365
+ return { ...base, segments, text };
1366
+ }
1367
+ function getPrimaryActivity(input) {
1368
+ if (input.activeToolCount > NO_ACTIVE_ITEMS) {
1369
+ return {
1370
+ kind: "tools",
1371
+ label: `Tools x${input.activeToolCount}`,
1372
+ color: "cyan"
1373
+ };
1374
+ }
1375
+ if (input.isThinking) {
1376
+ return {
1377
+ kind: "thinking",
1378
+ label: "Thinking",
1379
+ color: "yellow"
1380
+ };
1381
+ }
1382
+ if (input.activeBackgroundTaskCount > NO_ACTIVE_ITEMS) {
1383
+ return {
1384
+ kind: "background",
1385
+ label: `Background x${input.activeBackgroundTaskCount}`,
1386
+ color: "cyan"
1387
+ };
1388
+ }
1389
+ if (input.hasPendingPrompt) {
1390
+ return {
1391
+ kind: "queued",
1392
+ label: "Queued",
1393
+ color: "yellow"
1394
+ };
1395
+ }
1396
+ return {
1397
+ kind: "idle",
1398
+ label: "Idle",
1399
+ color: "gray"
1400
+ };
1401
+ }
1402
+
1403
+ // src/StatusBar.tsx
1404
+ var import_jsx_runtime5 = require("react/jsx-runtime");
1405
+ var CONTEXT_YELLOW_THRESHOLD = 70;
1406
+ var CONTEXT_RED_THRESHOLD = 90;
1407
+ function getContextColor(percentage) {
1408
+ if (percentage >= CONTEXT_RED_THRESHOLD) return "red";
1409
+ if (percentage >= CONTEXT_YELLOW_THRESHOLD) return "yellow";
1410
+ return "green";
1411
+ }
1412
+ function StatusActivityText({
1413
+ isThinking,
1414
+ activeToolCount,
1415
+ activeBackgroundTaskCount,
1416
+ hasPendingPrompt
1417
+ }) {
1418
+ const activity = formatStatusActivity({
1419
+ isThinking,
1420
+ activeToolCount,
1421
+ activeBackgroundTaskCount,
1422
+ hasPendingPrompt
1423
+ });
1424
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ink6.Text, { color: activity.color, bold: activity.kind !== "idle", children: activity.text });
1425
+ }
1426
+ function ContextText({
1427
+ percentage,
1428
+ usedTokens,
1429
+ maxTokens
1430
+ }) {
1431
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_ink6.Text, { color: getContextColor(percentage), children: [
1432
+ "Context: ",
1433
+ Math.round(percentage),
1434
+ "% (",
1435
+ (0, import_agent_core8.formatTokenCount)(usedTokens),
1436
+ "/",
1437
+ (0, import_agent_core8.formatTokenCount)(maxTokens),
1438
+ ")"
1439
+ ] });
1440
+ }
1441
+ function ModeText({ permissionMode }) {
1442
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
1443
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ink6.Text, { color: "cyan", bold: true, children: "Mode:" }),
1444
+ " ",
1445
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ink6.Text, { children: permissionMode })
1446
+ ] });
1447
+ }
1448
+ function shouldShowPermissionMode(permissionMode) {
1449
+ return permissionMode !== "default";
1450
+ }
1451
+ function ProviderText({
1452
+ modelName,
1453
+ providerProfileName,
1454
+ providerType
1455
+ }) {
1456
+ if (providerProfileName !== void 0 && providerType !== void 0) {
1457
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_ink6.Text, { dimColor: true, children: [
1458
+ providerProfileName,
1459
+ " (",
1460
+ providerType,
1461
+ ") ",
1462
+ modelName
1463
+ ] });
1464
+ }
1465
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ink6.Text, { dimColor: true, children: modelName });
1466
+ }
1467
+ function StatusLeft(props) {
1468
+ const shouldShowGitBranch = props.showGitBranch && props.gitBranch !== void 0 && props.gitBranch.length > 0;
1469
+ const showPermissionMode = shouldShowPermissionMode(props.permissionMode);
1470
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_ink6.Text, { children: [
1471
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1472
+ StatusActivityText,
1473
+ {
1474
+ isThinking: props.isThinking,
1475
+ activeToolCount: props.activeToolCount,
1476
+ activeBackgroundTaskCount: props.activeBackgroundTaskCount,
1477
+ hasPendingPrompt: props.hasPendingPrompt
1478
+ }
1479
+ ),
1480
+ showPermissionMode && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
1481
+ " | ",
1482
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ModeText, { permissionMode: props.permissionMode })
1483
+ ] }),
1484
+ props.sessionName && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
1485
+ " | ",
1486
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ink6.Text, { color: "magenta", children: props.sessionName })
1487
+ ] }),
1488
+ shouldShowGitBranch && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
1489
+ " | ",
1490
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_ink6.Text, { dimColor: true, children: [
1491
+ "git: ",
1492
+ props.gitBranch
1493
+ ] })
1494
+ ] }),
1495
+ " | ",
1496
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1497
+ ProviderText,
1498
+ {
1499
+ modelName: props.modelName,
1500
+ providerProfileName: props.providerProfileName,
1501
+ providerType: props.providerType
1502
+ }
1503
+ ),
1504
+ " | ",
1505
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1506
+ ContextText,
1507
+ {
1508
+ percentage: props.contextPercentage,
1509
+ usedTokens: props.contextUsedTokens,
1510
+ maxTokens: props.contextMaxTokens
1511
+ }
1512
+ )
1513
+ ] });
1514
+ }
1515
+ function StatusBar({
1516
+ permissionMode,
1517
+ modelName,
1518
+ providerProfileName,
1519
+ providerType,
1520
+ sessionId: _sessionId,
1521
+ isThinking,
1522
+ activeToolCount = 0,
1523
+ activeBackgroundTaskCount = 0,
1524
+ hasPendingPrompt = false,
1525
+ contextPercentage,
1526
+ contextUsedTokens,
1527
+ contextMaxTokens,
1528
+ sessionName,
1529
+ gitBranch,
1530
+ showGitBranch = true
1531
+ }) {
1532
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1533
+ import_ink6.Box,
1534
+ {
1535
+ borderStyle: "single",
1536
+ borderColor: "gray",
1537
+ paddingLeft: 1,
1538
+ paddingRight: 1,
1539
+ justifyContent: "space-between",
1540
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1541
+ StatusLeft,
1542
+ {
1543
+ permissionMode,
1544
+ modelName,
1545
+ providerProfileName,
1546
+ providerType,
1547
+ isThinking,
1548
+ activeToolCount,
1549
+ activeBackgroundTaskCount,
1550
+ hasPendingPrompt,
1551
+ contextPercentage,
1552
+ contextUsedTokens,
1553
+ contextMaxTokens,
1554
+ sessionName,
1555
+ gitBranch,
1556
+ showGitBranch
1557
+ }
1558
+ )
1559
+ }
1560
+ );
1561
+ }
1562
+
1563
+ // src/SessionStatusBar.tsx
1564
+ var import_jsx_runtime6 = require("react/jsx-runtime");
1565
+ function SessionStatusBar({
1566
+ cwd,
1567
+ permissionMode,
1568
+ modelId,
1569
+ providerProfileName,
1570
+ providerType,
1571
+ sessionId,
1572
+ isThinking,
1573
+ activeToolCount,
1574
+ activeBackgroundTaskCount,
1575
+ hasPendingPrompt,
1576
+ contextState,
1577
+ sessionName,
1578
+ settings
1579
+ }) {
1580
+ const cliAdapter = useTuiCliAdapter();
1581
+ const gitBranch = (0, import_react8.useMemo)(() => cliAdapter.getGitBranch(cwd), [cliAdapter, cwd]);
1582
+ if (!settings.enabled) return null;
1583
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1584
+ StatusBar,
1585
+ {
1586
+ permissionMode,
1587
+ modelName: modelId ? (0, import_agent_core9.getModelName)(modelId) : "",
1588
+ providerProfileName,
1589
+ providerType,
1590
+ sessionId,
1591
+ isThinking,
1592
+ activeToolCount,
1593
+ activeBackgroundTaskCount,
1594
+ hasPendingPrompt,
1595
+ contextPercentage: contextState.percentage,
1596
+ contextUsedTokens: contextState.usedTokens,
1597
+ contextMaxTokens: contextState.maxTokens,
1598
+ sessionName,
1599
+ gitBranch,
1600
+ showGitBranch: settings.gitBranch
1601
+ }
1602
+ );
1603
+ }
1604
+
1605
+ // src/InputArea.tsx
1606
+ var import_react13 = require("react");
1607
+ var import_ink10 = require("ink");
1608
+
1609
+ // src/CjkTextInput.tsx
1610
+ var import_react9 = require("react");
1611
+ var import_ink7 = require("ink");
1612
+ var import_chalk = __toESM(require("chalk"), 1);
1613
+
1614
+ // src/flows/cjk-text-input-flow.ts
1615
+ var import_string_width = __toESM(require("string-width"), 1);
1616
+ var PASTE_START = "[200~";
1617
+ var PASTE_END = "[201~";
1618
+ var LAST_ASCII_CONTROL_CODE = 31;
1619
+ var DELETE_CONTROL_CODE = 127;
1620
+ function createCjkTextInputFlowState(value) {
1621
+ return { value, cursor: value.length, isPasting: false, pasteBuffer: "" };
1622
+ }
1623
+ function syncCjkTextInputFlowState(state, value, cursorHint) {
1624
+ if (value === state.value) {
1625
+ return state;
1626
+ }
1627
+ return {
1628
+ ...state,
1629
+ value,
1630
+ cursor: cursorHint != null ? Math.min(cursorHint, value.length) : value.length
1631
+ };
1632
+ }
1633
+ function applyCjkTextInput(state, input, key, options) {
1634
+ const pasteResult = applyPasteBoundaryInput(state, input, options);
1635
+ if (pasteResult !== void 0) return pasteResult;
1636
+ const controlResult = applyControlInput(state, input, key, options);
1637
+ if (controlResult !== void 0) return controlResult;
1638
+ const cursorResult = applyCursorInput(state, key, options);
1639
+ if (cursorResult !== void 0) return cursorResult;
1640
+ return insertPrintableInput(state, input);
1641
+ }
1642
+ function applyCjkTextPaste(state, text, options) {
1643
+ const normalizedText = text.replace(/\r\n?/g, "\n");
1644
+ if (normalizedText.length === 0) {
1645
+ return { state, effect: { type: "none" } };
1646
+ }
1647
+ if (normalizedText.includes("\n") && options.canPaste) {
1648
+ return { state, effect: { type: "paste", text: normalizedText, cursor: state.cursor } };
1649
+ }
1650
+ return insertPrintableInput(state, normalizedText);
1651
+ }
1652
+ function applyPasteBoundaryInput(state, input, options) {
1653
+ if (input === PASTE_START || input.startsWith(PASTE_START)) {
1654
+ return startBracketedPaste(state, input);
1655
+ }
1656
+ if (state.isPasting) {
1657
+ return continueBracketedPaste(state, input, options);
1658
+ }
1659
+ return void 0;
1660
+ }
1661
+ function applyControlInput(state, input, key, options) {
1662
+ if (key.ctrl === true && input === "c" || key.tab === true) {
1663
+ return { state, effect: { type: "none" } };
1664
+ }
1665
+ if (key.return === true) {
1666
+ return { state, effect: { type: "submit", value: state.value } };
1667
+ }
1668
+ if (input.length > 1 && (input.includes("\n") || input.includes("\r")) && options.canPaste) {
1669
+ return {
1670
+ state,
1671
+ effect: { type: "paste", text: input.replace(/\r\n?/g, "\n"), cursor: state.cursor }
1672
+ };
1673
+ }
1674
+ return void 0;
1675
+ }
1676
+ function applyCursorInput(state, key, options) {
1677
+ if (key.upArrow === true || key.downArrow === true) {
1678
+ if (options.enableVerticalNavigation === false) {
1679
+ return { state, effect: { type: "none" } };
1680
+ }
1681
+ return moveCursorVertically(
1682
+ state,
1683
+ key.upArrow === true ? "up" : "down",
1684
+ options.availableWidth
1685
+ );
1686
+ }
1687
+ if (key.leftArrow === true) {
1688
+ return moveCursorHorizontally(state, "left");
1689
+ }
1690
+ if (key.rightArrow === true) {
1691
+ return moveCursorHorizontally(state, "right");
1692
+ }
1693
+ if (key.backspace === true || key.delete === true) {
1694
+ return deleteBeforeCursor(state);
1695
+ }
1696
+ return void 0;
1697
+ }
1698
+ function filterPrintable(input) {
1699
+ if (!input || input.length === 0) return "";
1700
+ let output = "";
1701
+ for (const char of input) {
1702
+ const code = char.charCodeAt(0);
1703
+ if (code > LAST_ASCII_CONTROL_CODE && code !== DELETE_CONTROL_CODE) {
1704
+ output += char;
1705
+ }
1706
+ }
1707
+ return output;
1708
+ }
1709
+ function insertAtCursor(value, cursor, input) {
1710
+ const next = value.slice(0, cursor) + input + value.slice(cursor);
1711
+ return { value: next, cursor: cursor + input.length };
1712
+ }
1713
+ function displayOffset(chars, charIndex, width) {
1714
+ let offset = 0;
1715
+ for (let i = 0; i < charIndex && i < chars.length; i++) {
1716
+ const w = (0, import_string_width.default)(chars[i]);
1717
+ const col = offset % width;
1718
+ if (col + w > width) offset += width - col;
1719
+ offset += w;
1720
+ }
1721
+ return offset;
1722
+ }
1723
+ function charIndexAtDisplayOffset(chars, target, width) {
1724
+ let offset = 0;
1725
+ for (let i = 0; i < chars.length; i++) {
1726
+ if (offset >= target) return i;
1727
+ const w = (0, import_string_width.default)(chars[i]);
1728
+ const col = offset % width;
1729
+ if (col + w > width) offset += width - col;
1730
+ offset += w;
1731
+ }
1732
+ return chars.length;
1733
+ }
1734
+ function startBracketedPaste(state, input) {
1735
+ return {
1736
+ state: { ...state, isPasting: true, pasteBuffer: input.slice(PASTE_START.length) },
1737
+ effect: { type: "none" }
1738
+ };
1739
+ }
1740
+ function continueBracketedPaste(state, input, options) {
1741
+ if (input !== PASTE_END && !input.includes(PASTE_END)) {
1742
+ return {
1743
+ state: { ...state, pasteBuffer: state.pasteBuffer + input },
1744
+ effect: { type: "none" }
1745
+ };
1746
+ }
1747
+ const beforeMarker = input.split(PASTE_END)[0] ?? "";
1748
+ const nextState = { ...state, isPasting: false, pasteBuffer: "" };
1749
+ return applyCjkTextPaste(nextState, state.pasteBuffer + beforeMarker, options);
1750
+ }
1751
+ function moveCursorVertically(state, direction, availableWidth) {
1752
+ if (!availableWidth || availableWidth <= 0) {
1753
+ return { state, effect: { type: "none" } };
1754
+ }
1755
+ const chars = [...state.value];
1756
+ const offset = displayOffset(chars, state.cursor, availableWidth);
1757
+ const target = direction === "up" ? offset - availableWidth : offset + availableWidth;
1758
+ if (target < 0) {
1759
+ return { state, effect: { type: "none" } };
1760
+ }
1761
+ const cursor = charIndexAtDisplayOffset(chars, target, availableWidth);
1762
+ if (cursor === state.cursor) {
1763
+ return { state, effect: { type: "none" } };
1764
+ }
1765
+ return { state: { ...state, cursor }, effect: { type: "render" } };
1766
+ }
1767
+ function moveCursorHorizontally(state, direction) {
1768
+ if (direction === "left" && state.cursor > 0) {
1769
+ return { state: { ...state, cursor: state.cursor - 1 }, effect: { type: "render" } };
1770
+ }
1771
+ if (direction === "right" && state.cursor < state.value.length) {
1772
+ return { state: { ...state, cursor: state.cursor + 1 }, effect: { type: "render" } };
1773
+ }
1774
+ return { state, effect: { type: "none" } };
1775
+ }
1776
+ function deleteBeforeCursor(state) {
1777
+ if (state.cursor === 0) {
1778
+ return { state, effect: { type: "none" } };
1779
+ }
1780
+ const value = state.value.slice(0, state.cursor - 1) + state.value.slice(state.cursor);
1781
+ return {
1782
+ state: { ...state, value, cursor: state.cursor - 1 },
1783
+ effect: { type: "change", value }
1784
+ };
1785
+ }
1786
+ function insertPrintableInput(state, input) {
1787
+ const printable = filterPrintable(input);
1788
+ if (printable.length === 0) {
1789
+ return { state, effect: { type: "none" } };
1790
+ }
1791
+ const result = insertAtCursor(state.value, state.cursor, printable);
1792
+ return {
1793
+ state: { ...state, value: result.value, cursor: result.cursor },
1794
+ effect: { type: "change", value: result.value }
1795
+ };
1796
+ }
1797
+
1798
+ // src/CjkTextInput.tsx
1799
+ var import_jsx_runtime7 = require("react/jsx-runtime");
1800
+ function CjkTextInput({
1801
+ value,
1802
+ onChange,
1803
+ onSubmit,
1804
+ onPaste,
1805
+ placeholder = "",
1806
+ focus = true,
1807
+ showCursor = true,
1808
+ availableWidth,
1809
+ cursorHint = null,
1810
+ enableVerticalNavigation = true
1811
+ }) {
1812
+ const stateRef = (0, import_react9.useRef)(createCjkTextInputFlowState(value));
1813
+ const [, forceRender] = (0, import_react9.useState)(0);
1814
+ stateRef.current = syncCjkTextInputFlowState(stateRef.current, value, cursorHint);
1815
+ useCjkTextInputHandlers({
1816
+ stateRef,
1817
+ onChange,
1818
+ onSubmit,
1819
+ onPaste,
1820
+ availableWidth,
1821
+ focus,
1822
+ enableVerticalNavigation,
1823
+ forceRender
1824
+ });
1825
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ink7.Text, { children: renderWithCursor(
1826
+ stateRef.current.value,
1827
+ stateRef.current.cursor,
1828
+ placeholder,
1829
+ showCursor && focus
1830
+ ) });
1831
+ }
1832
+ function useCjkTextInputHandlers(options) {
1833
+ (0, import_ink7.usePaste)(
1834
+ (text) => {
1835
+ applyCjkFlowSafely(
1836
+ options,
1837
+ () => applyCjkTextPaste(options.stateRef.current, text, createFlowOptions(options))
1838
+ );
1839
+ },
1840
+ { isActive: options.focus }
1841
+ );
1842
+ (0, import_ink7.useInput)(
1843
+ (input, key) => {
1844
+ applyCjkFlowSafely(
1845
+ options,
1846
+ () => applyCjkTextInput(options.stateRef.current, input, key, createFlowOptions(options))
1847
+ );
1848
+ },
1849
+ { isActive: options.focus }
1850
+ );
1851
+ }
1852
+ function createFlowOptions(options) {
1853
+ return {
1854
+ availableWidth: options.availableWidth,
1855
+ canPaste: options.onPaste !== void 0,
1856
+ enableVerticalNavigation: options.enableVerticalNavigation
1857
+ };
1858
+ }
1859
+ function applyCjkFlowSafely(options, run) {
1860
+ try {
1861
+ const result = run();
1862
+ options.stateRef.current = result.state;
1863
+ applyCjkTextInputEffect(
1864
+ result.effect,
1865
+ options.onChange,
1866
+ options.onSubmit,
1867
+ options.onPaste,
1868
+ options.forceRender
1869
+ );
1870
+ } catch {
1871
+ }
1872
+ }
1873
+ function applyCjkTextInputEffect(effect, onChange, onSubmit, onPaste, forceRender) {
1874
+ if (effect.type === "change") {
1875
+ onChange(effect.value);
1876
+ } else if (effect.type === "submit") {
1877
+ onSubmit?.(effect.value);
1878
+ } else if (effect.type === "paste") {
1879
+ onPaste?.(effect.text, effect.cursor);
1880
+ } else if (effect.type === "render") {
1881
+ forceRender((n) => n + 1);
1882
+ }
1883
+ }
1884
+ function renderWithCursor(value, cursorOffset, placeholder, showCursor) {
1885
+ if (!showCursor) {
1886
+ return value.length > 0 ? value : placeholder ? import_chalk.default.gray(placeholder) : "";
1887
+ }
1888
+ if (value.length === 0) {
1889
+ if (placeholder.length > 0) {
1890
+ return import_chalk.default.inverse(placeholder[0]) + import_chalk.default.gray(placeholder.slice(1));
1891
+ }
1892
+ return import_chalk.default.inverse(" ");
1893
+ }
1894
+ const chars = [...value];
1895
+ let rendered = "";
1896
+ for (let i = 0; i < chars.length; i++) {
1897
+ const char = chars[i] ?? "";
1898
+ rendered += i === cursorOffset ? import_chalk.default.inverse(char) : char;
1899
+ }
1900
+ if (cursorOffset >= chars.length) {
1901
+ rendered += import_chalk.default.inverse(" ");
1902
+ }
1903
+ return rendered;
1904
+ }
1905
+
1906
+ // src/WaveText.tsx
1907
+ var import_react10 = require("react");
1908
+ var import_ink8 = require("ink");
1909
+ var import_jsx_runtime8 = require("react/jsx-runtime");
1910
+ var WAVE_COLORS = ["#666666", "#888888", "#aaaaaa", "#888888"];
1911
+ var INTERVAL_MS = 400;
1912
+ var CHARS_PER_GROUP = 4;
1913
+ function WaveText({ text }) {
1914
+ const [tick, setTick] = (0, import_react10.useState)(0);
1915
+ (0, import_react10.useEffect)(() => {
1916
+ const timer = setInterval(() => {
1917
+ setTick((prev) => prev + 1);
1918
+ }, INTERVAL_MS);
1919
+ return () => clearInterval(timer);
1920
+ }, []);
1921
+ const chars = [...text];
1922
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_ink8.Text, { children: chars.map((char, i) => {
1923
+ const group = Math.floor(i / CHARS_PER_GROUP);
1924
+ const colorIndex = (tick + group) % WAVE_COLORS.length;
1925
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_ink8.Text, { color: WAVE_COLORS[colorIndex], children: char }, i);
1926
+ }) });
1927
+ }
1928
+
1929
+ // src/SlashAutocomplete.tsx
1930
+ var import_react11 = require("react");
1931
+ var import_ink9 = require("ink");
1932
+ var import_jsx_runtime9 = require("react/jsx-runtime");
1933
+ var MAX_VISIBLE = 8;
1934
+ var OUTER_CHROME = 4;
1935
+ var MIN_ROW_WIDTH = 40;
1936
+ var NAME_COL_MAX = 20;
1937
+ function useRowWidth() {
1938
+ const { stdout } = (0, import_ink9.useStdout)();
1939
+ const measure = () => Math.max(MIN_ROW_WIDTH, (stdout.columns ?? 80) - OUTER_CHROME);
1940
+ const [width, setWidth] = (0, import_react11.useState)(measure);
1941
+ (0, import_react11.useEffect)(() => {
1942
+ const onResize = () => setWidth(measure());
1943
+ stdout.on("resize", onResize);
1944
+ return () => {
1945
+ stdout.off("resize", onResize);
1946
+ };
1947
+ }, [stdout]);
1948
+ return width;
1949
+ }
1950
+ function capName(name, colWidth) {
1951
+ return name.length > colWidth ? `${name.slice(0, colWidth - 1)}\u2026` : name.padEnd(colWidth);
1952
+ }
1953
+ function CommandRow(props) {
1954
+ const { cmd, isSelected, showSlash, rowWidth, nameColWidth } = props;
1955
+ const indicator = isSelected ? "\u25B8 " : " ";
1956
+ const nameColor = isSelected ? "cyan" : void 0;
1957
+ const dimmed = !isSelected;
1958
+ const namePart = capName(cmd.name, nameColWidth);
1959
+ const text = showSlash ? `${indicator}/${namePart} ${cmd.description ?? ""}` : `${indicator}${namePart} ${cmd.description ?? ""}`;
1960
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink9.Box, { width: rowWidth, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink9.Text, { color: nameColor, dimColor: dimmed, wrap: "truncate-end", children: text }) });
1961
+ }
1962
+ function SlashAutocomplete({
1963
+ commands,
1964
+ selectedIndex,
1965
+ visible,
1966
+ isSubcommandMode
1967
+ }) {
1968
+ const rowWidth = useRowWidth();
1969
+ if (!visible || commands.length === 0) return null;
1970
+ const scrollOffset = computeScrollOffset(selectedIndex, commands.length);
1971
+ const visibleCommands = commands.slice(scrollOffset, scrollOffset + MAX_VISIBLE);
1972
+ const nameColWidth = Math.min(
1973
+ NAME_COL_MAX,
1974
+ Math.max(...visibleCommands.map((c) => c.name.length))
1975
+ );
1976
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ink9.Box, { flexDirection: "column", borderStyle: "round", borderColor: "gray", paddingX: 1, children: visibleCommands.map((cmd, i) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1977
+ CommandRow,
1978
+ {
1979
+ cmd,
1980
+ isSelected: scrollOffset + i === selectedIndex,
1981
+ showSlash: !isSubcommandMode,
1982
+ rowWidth,
1983
+ nameColWidth
1984
+ },
1985
+ cmd.name
1986
+ )) });
1987
+ }
1988
+ function computeScrollOffset(selectedIndex, total) {
1989
+ if (total <= MAX_VISIBLE) return 0;
1990
+ if (selectedIndex < MAX_VISIBLE) return 0;
1991
+ const maxOffset = total - MAX_VISIBLE;
1992
+ return Math.min(selectedIndex - MAX_VISIBLE + 1, maxOffset);
1993
+ }
1994
+
1995
+ // src/utils/paste-labels.ts
1996
+ var PASTE_LABEL_RE = /\[Pasted text #(\d+)(?: \+\d+ lines)?\]/g;
1997
+ function expandPasteLabels(text, store) {
1998
+ return text.replace(PASTE_LABEL_RE, (_, id) => store.get(Number(id)) ?? "");
1999
+ }
2000
+
2001
+ // src/hooks/useAutocomplete.ts
2002
+ var import_react12 = __toESM(require("react"), 1);
2003
+ function parseSlashInput(value) {
2004
+ if (!value.startsWith("/")) return { isSlash: false, parentCommand: "", filter: "" };
2005
+ const afterSlash = value.slice(1);
2006
+ const spaceIndex = afterSlash.indexOf(" ");
2007
+ if (spaceIndex === -1) return { isSlash: true, parentCommand: "", filter: afterSlash };
2008
+ const parent = afterSlash.slice(0, spaceIndex);
2009
+ const rest = afterSlash.slice(spaceIndex + 1);
2010
+ return { isSlash: true, parentCommand: parent, filter: rest };
2011
+ }
2012
+ function useAutocomplete(value, registry) {
2013
+ const [selectedIndex, setSelectedIndex] = (0, import_react12.useState)(0);
2014
+ const [dismissed, setDismissed] = (0, import_react12.useState)(false);
2015
+ const prevValueRef = import_react12.default.useRef(value);
2016
+ if (prevValueRef.current !== value) {
2017
+ prevValueRef.current = value;
2018
+ if (dismissed) setDismissed(false);
2019
+ }
2020
+ const parsed = parseSlashInput(value);
2021
+ const isSubcommandMode = parsed.isSlash && parsed.parentCommand.length > 0;
2022
+ const filteredCommands = (0, import_react12.useMemo)(() => {
2023
+ if (!registry || !parsed.isSlash || dismissed) return [];
2024
+ if (isSubcommandMode) {
2025
+ const subs = registry.getSubcommands(parsed.parentCommand);
2026
+ if (subs.length === 0) return [];
2027
+ if (!parsed.filter) return subs;
2028
+ const lower = parsed.filter.toLowerCase();
2029
+ return subs.filter((c) => c.name.toLowerCase().startsWith(lower));
2030
+ }
2031
+ return registry.getCommands(parsed.filter);
2032
+ }, [registry, parsed.isSlash, parsed.parentCommand, parsed.filter, dismissed, isSubcommandMode]);
2033
+ const showPopup = parsed.isSlash && filteredCommands.length > 0 && !dismissed;
2034
+ if (selectedIndex >= filteredCommands.length && filteredCommands.length > 0) {
2035
+ setSelectedIndex(filteredCommands.length - 1);
2036
+ }
2037
+ return {
2038
+ showPopup,
2039
+ filteredCommands,
2040
+ selectedIndex,
2041
+ setSelectedIndex,
2042
+ isSubcommandMode,
2043
+ setShowPopup: (val) => {
2044
+ if (typeof val === "function") {
2045
+ setDismissed((prev) => {
2046
+ const nextVal = val(!prev);
2047
+ return !nextVal;
2048
+ });
2049
+ } else {
2050
+ setDismissed(!val);
2051
+ }
2052
+ }
2053
+ };
2054
+ }
2055
+
2056
+ // src/flows/input-area-flow.ts
2057
+ function getAutocompletePopupAction(key) {
2058
+ if (key.upArrow === true) return "previous";
2059
+ if (key.downArrow === true) return "next";
2060
+ if (key.escape === true) return "close";
2061
+ if (key.tab === true) return "complete";
2062
+ return void 0;
2063
+ }
2064
+ function getPendingPromptInputAction(key) {
2065
+ if (key.backspace === true || key.delete === true) {
2066
+ return "cancelQueue";
2067
+ }
2068
+ return void 0;
2069
+ }
2070
+ function getPromptHistoryInputAction(key) {
2071
+ if (key.upArrow === true) return "previous";
2072
+ if (key.downArrow === true) return "next";
2073
+ return void 0;
2074
+ }
2075
+ function createPromptHistoryNavigationState() {
2076
+ return { selectedIndex: null, draft: "" };
2077
+ }
2078
+ function navigatePromptHistory(value, history, state, action) {
2079
+ if (history.length === 0) {
2080
+ return { value, cursorHint: value.length, state };
2081
+ }
2082
+ if (action === "previous") {
2083
+ const selectedIndex = state.selectedIndex === null ? history.length - 1 : Math.max(0, state.selectedIndex - 1);
2084
+ const nextValue = history[selectedIndex] ?? value;
2085
+ return {
2086
+ value: nextValue,
2087
+ cursorHint: nextValue.length,
2088
+ state: { selectedIndex, draft: state.selectedIndex === null ? value : state.draft }
2089
+ };
2090
+ }
2091
+ if (state.selectedIndex === null) {
2092
+ return { value, cursorHint: value.length, state };
2093
+ }
2094
+ if (state.selectedIndex < history.length - 1) {
2095
+ const selectedIndex = state.selectedIndex + 1;
2096
+ const nextValue = history[selectedIndex] ?? value;
2097
+ return {
2098
+ value: nextValue,
2099
+ cursorHint: nextValue.length,
2100
+ state: { ...state, selectedIndex }
2101
+ };
2102
+ }
2103
+ return {
2104
+ value: state.draft,
2105
+ cursorHint: state.draft.length,
2106
+ state: createPromptHistoryNavigationState()
2107
+ };
2108
+ }
2109
+ function appendPromptHistory(history, value) {
2110
+ const prompt = value.trim();
2111
+ if (prompt.length === 0) return [...history];
2112
+ if (history[history.length - 1] === prompt) return [...history];
2113
+ return [...history, prompt];
2114
+ }
2115
+ function extractPromptHistory(entries) {
2116
+ let prompts = [];
2117
+ for (const entry of entries) {
2118
+ if (entry.category !== "chat" || entry.type !== "user") continue;
2119
+ const data = entry.data;
2120
+ if (typeof data?.content !== "string") continue;
2121
+ prompts = appendPromptHistory(prompts, data.content);
2122
+ }
2123
+ return prompts;
2124
+ }
2125
+ function moveAutocompleteSelection(selectedIndex, commandCount, direction) {
2126
+ if (commandCount === 0) return 0;
2127
+ if (direction === "previous") {
2128
+ return selectedIndex > 0 ? selectedIndex - 1 : commandCount - 1;
2129
+ }
2130
+ return selectedIndex < commandCount - 1 ? selectedIndex + 1 : 0;
2131
+ }
2132
+ function resolveTabCompletion(value, command) {
2133
+ const parsed = parseSlashInput(value);
2134
+ if (parsed.parentCommand) {
2135
+ return { type: "insert", value: `/${parsed.parentCommand} ${command.name} ` };
2136
+ }
2137
+ if (command.subcommands && command.subcommands.length > 0) {
2138
+ return { type: "insert", value: `/${command.name} `, selectedIndex: 0 };
2139
+ }
2140
+ return { type: "insert", value: `/${command.name} ` };
2141
+ }
2142
+ function resolveEnterCommandSelection(value, command) {
2143
+ const parsed = parseSlashInput(value);
2144
+ if (parsed.parentCommand) {
2145
+ return { type: "submit", value: `/${parsed.parentCommand} ${command.name}` };
2146
+ }
2147
+ if (command.subcommands && command.subcommands.length > 0) {
2148
+ return { type: "insert", value: `/${command.name} `, selectedIndex: 0 };
2149
+ }
2150
+ return { type: "submit", value: `/${command.name}` };
2151
+ }
2152
+ function createPasteLabelChange(value, cursorPosition, pasteId, text) {
2153
+ const lineCount = text.split("\n").length;
2154
+ const label = `[Pasted text #${pasteId} +${lineCount} lines]`;
2155
+ return {
2156
+ value: value.slice(0, cursorPosition) + label + value.slice(cursorPosition),
2157
+ cursorHint: cursorPosition + label.length,
2158
+ label,
2159
+ lineCount
2160
+ };
2161
+ }
2162
+ function shouldSubmitInput(text) {
2163
+ return text.trim().length > 0;
2164
+ }
2165
+
2166
+ // src/InputArea.tsx
2167
+ var import_jsx_runtime10 = require("react/jsx-runtime");
2168
+ var PENDING_PROMPT_DISPLAY_MAX = 50;
2169
+ var PENDING_PROMPT_TAIL_KEEP = 47;
2170
+ var BORDER_HORIZONTAL = 2;
2171
+ var PADDING_LEFT = 1;
2172
+ var PROMPT_WIDTH = 2;
2173
+ var INPUT_AREA_OVERHEAD = BORDER_HORIZONTAL + PADDING_LEFT + PROMPT_WIDTH;
2174
+ var DEFAULT_TERMINAL_COLUMNS = 80;
2175
+ function InputArea({
2176
+ onSubmit,
2177
+ onCancelQueue,
2178
+ isDisabled,
2179
+ isAborting,
2180
+ pendingPrompt,
2181
+ registry,
2182
+ sessionName,
2183
+ history
2184
+ }) {
2185
+ const [value, setValue] = (0, import_react13.useState)("");
2186
+ const [cursorHint, setCursorHint] = (0, import_react13.useState)(null);
2187
+ const [historyState, setHistoryState] = (0, import_react13.useState)(createPromptHistoryNavigationState);
2188
+ const [localPromptHistory, setLocalPromptHistory] = (0, import_react13.useState)([]);
2189
+ const restoredPromptHistory = (0, import_react13.useMemo)(() => extractPromptHistory(history ?? []), [history]);
2190
+ const promptHistory = (0, import_react13.useMemo)(
2191
+ () => localPromptHistory.reduce(
2192
+ (prompts, prompt) => appendPromptHistory(prompts, prompt),
2193
+ restoredPromptHistory
2194
+ ),
2195
+ [restoredPromptHistory, localPromptHistory]
2196
+ );
2197
+ const pasteStore = (0, import_react13.useRef)(/* @__PURE__ */ new Map());
2198
+ const { columns } = (0, import_ink10.useWindowSize)();
2199
+ const terminalColumns = columns > 0 ? columns : DEFAULT_TERMINAL_COLUMNS;
2200
+ const availableWidth = Math.max(1, terminalColumns - INPUT_AREA_OVERHEAD);
2201
+ const pasteIdRef = (0, import_react13.useRef)(0);
2202
+ const {
2203
+ showPopup,
2204
+ filteredCommands,
2205
+ selectedIndex,
2206
+ setSelectedIndex,
2207
+ isSubcommandMode,
2208
+ setShowPopup
2209
+ } = useAutocomplete(value, registry);
2210
+ const handlePaste = (0, import_react13.useCallback)((text, cursorPosition) => {
2211
+ pasteIdRef.current += 1;
2212
+ const id = pasteIdRef.current;
2213
+ pasteStore.current.set(id, text);
2214
+ setValue((prev) => {
2215
+ const change = createPasteLabelChange(prev, cursorPosition, id, text);
2216
+ setCursorHint(change.cursorHint);
2217
+ return change.value;
2218
+ });
2219
+ }, []);
2220
+ const resetHistoryNavigation = (0, import_react13.useCallback)(() => {
2221
+ setHistoryState(createPromptHistoryNavigationState());
2222
+ }, []);
2223
+ const recordPromptHistory = (0, import_react13.useCallback)((prompt) => {
2224
+ setLocalPromptHistory((prev) => appendPromptHistory(prev, prompt));
2225
+ }, []);
2226
+ const submitPrompt = (0, import_react13.useCallback)(
2227
+ (prompt) => {
2228
+ recordPromptHistory(prompt);
2229
+ resetHistoryNavigation();
2230
+ onSubmit(prompt);
2231
+ },
2232
+ [onSubmit, recordPromptHistory, resetHistoryNavigation]
2233
+ );
2234
+ const tabCompleteCommand = (0, import_react13.useCallback)(
2235
+ (cmd) => {
2236
+ const result = resolveTabCompletion(value, cmd);
2237
+ if (result.type === "insert") {
2238
+ setValue(result.value);
2239
+ if (result.selectedIndex !== void 0) {
2240
+ setSelectedIndex(result.selectedIndex);
2241
+ }
2242
+ }
2243
+ },
2244
+ [value, setSelectedIndex]
2245
+ );
2246
+ const enterSelectCommand = (0, import_react13.useCallback)(
2247
+ (cmd) => {
2248
+ const result = resolveEnterCommandSelection(value, cmd);
2249
+ if (result.type === "insert") {
2250
+ setValue(result.value);
2251
+ if (result.selectedIndex !== void 0) {
2252
+ setSelectedIndex(result.selectedIndex);
2253
+ }
2254
+ return;
2255
+ }
2256
+ setValue("");
2257
+ submitPrompt(result.value);
2258
+ },
2259
+ [value, submitPrompt, setSelectedIndex]
2260
+ );
2261
+ const handleSubmit = (0, import_react13.useCallback)(
2262
+ (text) => {
2263
+ if (!shouldSubmitInput(text)) return;
2264
+ if (showPopup && filteredCommands[selectedIndex]) {
2265
+ enterSelectCommand(filteredCommands[selectedIndex]);
2266
+ return;
2267
+ }
2268
+ const expanded = expandPasteLabels(text.trim(), pasteStore.current);
2269
+ setValue("");
2270
+ pasteStore.current.clear();
2271
+ pasteIdRef.current = 0;
2272
+ submitPrompt(expanded);
2273
+ },
2274
+ [showPopup, filteredCommands, selectedIndex, enterSelectCommand, submitPrompt]
2275
+ );
2276
+ (0, import_ink10.useInput)(
2277
+ (_input, key) => {
2278
+ if (!showPopup) return;
2279
+ const action = getAutocompletePopupAction(key);
2280
+ if (action === "previous" || action === "next") {
2281
+ setSelectedIndex(
2282
+ (prev) => moveAutocompleteSelection(prev, filteredCommands.length, action)
2283
+ );
2284
+ } else if (action === "close") {
2285
+ setShowPopup(false);
2286
+ } else if (action === "complete") {
2287
+ const cmd = filteredCommands[selectedIndex];
2288
+ if (cmd) tabCompleteCommand(cmd);
2289
+ }
2290
+ },
2291
+ { isActive: showPopup && !isDisabled }
2292
+ );
2293
+ (0, import_ink10.useInput)(
2294
+ (_input, key) => {
2295
+ const action = getPromptHistoryInputAction(key);
2296
+ if (!action) return;
2297
+ const result = navigatePromptHistory(value, promptHistory, historyState, action);
2298
+ setValue(result.value);
2299
+ setCursorHint(result.cursorHint);
2300
+ setHistoryState(result.state);
2301
+ },
2302
+ { isActive: !showPopup && !isDisabled && !pendingPrompt }
2303
+ );
2304
+ (0, import_ink10.useInput)(
2305
+ (_input, key) => {
2306
+ if (getPendingPromptInputAction(key) === "cancelQueue" && pendingPrompt) {
2307
+ onCancelQueue?.();
2308
+ }
2309
+ },
2310
+ { isActive: !!pendingPrompt }
2311
+ );
2312
+ const borderColor = isAborting ? "yellow" : pendingPrompt ? "cyan" : isDisabled ? "gray" : "green";
2313
+ const innerWidth = Math.max(1, terminalColumns - BORDER_HORIZONTAL);
2314
+ const topBorder = (() => {
2315
+ if (sessionName) {
2316
+ const label = ` "${sessionName}" `;
2317
+ const rightPad = 2;
2318
+ const leftLen = Math.max(0, innerWidth - label.length - rightPad);
2319
+ return { left: "\u250C" + "\u2500".repeat(leftLen), label, right: "\u2500".repeat(rightPad) + "\u2510" };
2320
+ }
2321
+ return { left: "\u250C" + "\u2500".repeat(innerWidth), label: "", right: "\u2510" };
2322
+ })();
2323
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_ink10.Box, { flexDirection: "column", children: [
2324
+ showPopup && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2325
+ SlashAutocomplete,
2326
+ {
2327
+ commands: filteredCommands,
2328
+ selectedIndex,
2329
+ visible: showPopup,
2330
+ isSubcommandMode
2331
+ }
2332
+ ),
2333
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_ink10.Text, { color: borderColor, children: [
2334
+ topBorder.left,
2335
+ topBorder.label ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_ink10.Text, { backgroundColor: borderColor, color: "black", bold: true, children: topBorder.label }) : null,
2336
+ topBorder.right
2337
+ ] }),
2338
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_ink10.Box, { borderStyle: "single", borderTop: false, borderColor, paddingLeft: 1, children: isAborting ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_ink10.Text, { color: "yellow", children: " Interrupting..." }) : pendingPrompt ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_ink10.Text, { color: "cyan", children: [
2339
+ " ",
2340
+ "Queued:",
2341
+ " ",
2342
+ pendingPrompt.length > PENDING_PROMPT_DISPLAY_MAX ? pendingPrompt.slice(0, PENDING_PROMPT_TAIL_KEEP) + "..." : pendingPrompt,
2343
+ " ",
2344
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_ink10.Text, { dimColor: true, children: "(Backspace to cancel)" })
2345
+ ] }) : isDisabled ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(WaveText, { text: " Waiting for response... (ESC to interrupt)" }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_ink10.Box, { children: [
2346
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_ink10.Text, { color: "green", bold: true, children: "> " }),
2347
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2348
+ CjkTextInput,
2349
+ {
2350
+ value,
2351
+ onChange: (v) => {
2352
+ setValue(v);
2353
+ resetHistoryNavigation();
2354
+ setCursorHint(null);
2355
+ },
2356
+ onSubmit: handleSubmit,
2357
+ onPaste: handlePaste,
2358
+ placeholder: "Type a message or /help",
2359
+ availableWidth,
2360
+ cursorHint,
2361
+ enableVerticalNavigation: false
2362
+ }
2363
+ )
2364
+ ] }) })
2365
+ ] });
2366
+ }
2367
+
2368
+ // src/ConfirmPrompt.tsx
2369
+ var import_react14 = require("react");
2370
+ var import_ink11 = require("ink");
2371
+
2372
+ // src/flows/selection-flow.ts
2373
+ function createSelectionFlowState() {
2374
+ return { selectedIndex: 0, scrollOffset: 0, resolved: false };
2375
+ }
2376
+ function getVerticalSelectionInputAction(key) {
2377
+ if (key.escape === true) return "cancel";
2378
+ if (key.upArrow === true) return "previous";
2379
+ if (key.downArrow === true) return "next";
2380
+ if (key.return === true) return "select";
2381
+ return void 0;
2382
+ }
2383
+ function getDirectionalSelectionInputAction(key) {
2384
+ if (key.escape === true) return "cancel";
2385
+ if (key.leftArrow === true || key.upArrow === true) return "previous";
2386
+ if (key.rightArrow === true || key.downArrow === true) return "next";
2387
+ if (key.return === true) return "select";
2388
+ return void 0;
2389
+ }
2390
+ function applySelectionInput(state, action, options) {
2391
+ if (state.resolved) {
2392
+ return { state, effect: { type: "none" } };
2393
+ }
2394
+ if (action === "cancel") {
2395
+ return { state: { ...state, resolved: true }, effect: { type: "cancel" } };
2396
+ }
2397
+ if (options.enabled === false || options.itemCount === 0) {
2398
+ return { state, effect: { type: "none" } };
2399
+ }
2400
+ if (action === "select") {
2401
+ const index = clampIndex(state.selectedIndex, options.itemCount);
2402
+ return {
2403
+ state: { ...state, selectedIndex: index, resolved: true },
2404
+ effect: { type: "select", index }
2405
+ };
2406
+ }
2407
+ const selectedIndex = moveSelection(state.selectedIndex, action, options);
2408
+ const scrollOffset = resolveScrollOffset(selectedIndex, state.scrollOffset, options);
2409
+ return { state: { ...state, selectedIndex, scrollOffset }, effect: { type: "none" } };
2410
+ }
2411
+ function normalizeSelectionState(state, options) {
2412
+ if (options.itemCount === 0) {
2413
+ return { ...state, selectedIndex: 0, scrollOffset: 0 };
2414
+ }
2415
+ const selectedIndex = clampIndex(state.selectedIndex, options.itemCount);
2416
+ const scrollOffset = resolveScrollOffset(selectedIndex, state.scrollOffset, options);
2417
+ if (selectedIndex === state.selectedIndex && scrollOffset === state.scrollOffset) {
2418
+ return state;
2419
+ }
2420
+ return {
2421
+ ...state,
2422
+ selectedIndex,
2423
+ scrollOffset
2424
+ };
2425
+ }
2426
+ function moveSelection(selectedIndex, action, options) {
2427
+ if (action === "previous") {
2428
+ if (options.wrap === true && selectedIndex === 0) return options.itemCount - 1;
2429
+ return Math.max(0, selectedIndex - 1);
2430
+ }
2431
+ if (options.wrap === true && selectedIndex === options.itemCount - 1) return 0;
2432
+ return Math.min(options.itemCount - 1, selectedIndex + 1);
2433
+ }
2434
+ function resolveScrollOffset(selectedIndex, scrollOffset, options) {
2435
+ const maxVisible = options.maxVisible ?? options.itemCount;
2436
+ if (maxVisible <= 0) return 0;
2437
+ if (selectedIndex < scrollOffset) return selectedIndex;
2438
+ if (selectedIndex >= scrollOffset + maxVisible) return selectedIndex - maxVisible + 1;
2439
+ return Math.max(0, scrollOffset);
2440
+ }
2441
+ function clampIndex(index, itemCount) {
2442
+ return Math.min(Math.max(index, 0), itemCount - 1);
2443
+ }
2444
+
2445
+ // src/flows/confirm-prompt-flow.ts
2446
+ function getConfirmPromptInputAction(input, key, optionCount) {
2447
+ const action = getDirectionalSelectionInputAction({ ...key, escape: false });
2448
+ if (action !== void 0) {
2449
+ return action;
2450
+ }
2451
+ if (optionCount === 2 && input === "y") {
2452
+ return { type: "shortcut", index: 0 };
2453
+ }
2454
+ if (optionCount === 2 && input === "n") {
2455
+ return { type: "shortcut", index: 1 };
2456
+ }
2457
+ return void 0;
2458
+ }
2459
+ function applyConfirmPromptInput(state, action, optionCount) {
2460
+ if (state.resolved) {
2461
+ return { state, effect: { type: "none" } };
2462
+ }
2463
+ if (typeof action !== "string") {
2464
+ return {
2465
+ state: { ...state, selectedIndex: action.index, resolved: true },
2466
+ effect: { type: "select", index: action.index }
2467
+ };
2468
+ }
2469
+ return applySelectionInput(state, action, { itemCount: optionCount });
2470
+ }
2471
+
2472
+ // src/ConfirmPrompt.tsx
2473
+ var import_jsx_runtime11 = require("react/jsx-runtime");
2474
+ function ConfirmPrompt({
2475
+ message,
2476
+ options = ["Yes", "No"],
2477
+ onSelect
2478
+ }) {
2479
+ const [state, setState] = (0, import_react14.useState)(() => createSelectionFlowState());
2480
+ const stateRef = (0, import_react14.useRef)(state);
2481
+ const applyAction = (0, import_react14.useCallback)(
2482
+ (action) => {
2483
+ const result = applyConfirmPromptInput(stateRef.current, action, options.length);
2484
+ stateRef.current = result.state;
2485
+ setState(result.state);
2486
+ if (result.effect.type === "select") {
2487
+ onSelect(result.effect.index);
2488
+ }
2489
+ },
2490
+ [onSelect, options.length]
2491
+ );
2492
+ (0, import_ink11.useInput)((input, key) => {
2493
+ const action = getConfirmPromptInputAction(input, key, options.length);
2494
+ if (action !== void 0) {
2495
+ applyAction(action);
2496
+ }
2497
+ });
2498
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_ink11.Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
2499
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ink11.Text, { color: "yellow", children: message }),
2500
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ink11.Box, { marginTop: 1, children: options.map((opt, i) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ink11.Box, { marginRight: 2, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
2501
+ import_ink11.Text,
2502
+ {
2503
+ color: i === state.selectedIndex ? "cyan" : void 0,
2504
+ bold: i === state.selectedIndex,
2505
+ children: [
2506
+ i === state.selectedIndex ? "> " : " ",
2507
+ opt
2508
+ ]
2509
+ }
2510
+ ) }, opt)) }),
2511
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_ink11.Text, { dimColor: true, children: " arrow keys to select, Enter to confirm" })
2512
+ ] });
2513
+ }
2514
+
2515
+ // src/InteractivePrompt.tsx
2516
+ var import_ink14 = require("ink");
2517
+
2518
+ // src/ListPicker.tsx
2519
+ var import_react15 = require("react");
2520
+ var import_ink12 = require("ink");
2521
+ var import_jsx_runtime12 = require("react/jsx-runtime");
2522
+ var DEFAULT_MAX_VISIBLE = 3;
2523
+ function ListPicker({
2524
+ items,
2525
+ renderItem,
2526
+ onSelect,
2527
+ onCancel,
2528
+ maxVisible = DEFAULT_MAX_VISIBLE
2529
+ }) {
2530
+ const [state, setState] = (0, import_react15.useState)(() => createSelectionFlowState());
2531
+ const stateRef = (0, import_react15.useRef)(state);
2532
+ const applyAction = (0, import_react15.useCallback)(
2533
+ (action) => {
2534
+ const result = applySelectionInput(stateRef.current, action, {
2535
+ itemCount: items.length,
2536
+ maxVisible
2537
+ });
2538
+ stateRef.current = result.state;
2539
+ setState(result.state);
2540
+ if (result.effect.type === "cancel") {
2541
+ onCancel();
2542
+ } else if (result.effect.type === "select") {
2543
+ const item = items[result.effect.index];
2544
+ if (item !== void 0) {
2545
+ onSelect(item);
2546
+ }
2547
+ }
2548
+ },
2549
+ [items, maxVisible, onCancel, onSelect]
2550
+ );
2551
+ (0, import_ink12.useInput)((_input, key) => {
2552
+ const action = getVerticalSelectionInputAction(key);
2553
+ if (action !== void 0) {
2554
+ applyAction(action);
2555
+ }
2556
+ });
2557
+ if (items.length === 0) {
2558
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_ink12.Box, {});
2559
+ }
2560
+ const normalizedState = normalizeSelectionState(state, { itemCount: items.length, maxVisible });
2561
+ if (normalizedState !== state) {
2562
+ stateRef.current = normalizedState;
2563
+ }
2564
+ const { selectedIndex, scrollOffset } = normalizedState;
2565
+ const visibleItems = items.slice(scrollOffset, scrollOffset + maxVisible);
2566
+ const hasMore = scrollOffset + maxVisible < items.length;
2567
+ const hasLess = scrollOffset > 0;
2568
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_ink12.Box, { flexDirection: "column", children: [
2569
+ hasLess && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_ink12.Text, { dimColor: true, children: [
2570
+ " \u2191 ",
2571
+ scrollOffset,
2572
+ " more above"
2573
+ ] }),
2574
+ visibleItems.map((item, index) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_ink12.Box, { marginBottom: 1, children: renderItem(item, scrollOffset + index === selectedIndex) }, scrollOffset + index)),
2575
+ hasMore && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_ink12.Text, { dimColor: true, children: [
2576
+ " \u2193 ",
2577
+ items.length - scrollOffset - maxVisible,
2578
+ " more below"
2579
+ ] })
2580
+ ] });
2581
+ }
2582
+
2583
+ // src/TextPrompt.tsx
2584
+ var import_react16 = require("react");
2585
+ var import_ink13 = require("ink");
2586
+
2587
+ // src/flows/text-prompt-flow.ts
2588
+ function createTextPromptFlowState() {
2589
+ return { value: "", resolved: false };
2590
+ }
2591
+ function getTextPromptInputAction(input, key) {
2592
+ if (key.escape === true) {
2593
+ return { type: "cancel" };
2594
+ }
2595
+ if (key.return === true) {
2596
+ return { type: "submit" };
2597
+ }
2598
+ if (key.backspace === true || key.delete === true) {
2599
+ return { type: "delete" };
2600
+ }
2601
+ if (input && key.ctrl !== true && key.meta !== true) {
2602
+ return { type: "insert", value: input };
2603
+ }
2604
+ return void 0;
2605
+ }
2606
+ function applyTextPromptInput(state, action, options) {
2607
+ if (state.resolved) {
2608
+ return { state, effect: { type: "none" } };
2609
+ }
2610
+ if (action.type === "cancel") {
2611
+ return { state: { ...state, resolved: true }, effect: { type: "cancel" } };
2612
+ }
2613
+ if (action.type === "delete") {
2614
+ return {
2615
+ state: { ...state, value: state.value.slice(0, -1), error: void 0 },
2616
+ effect: { type: "none" }
2617
+ };
2618
+ }
2619
+ if (action.type === "insert") {
2620
+ return {
2621
+ state: { ...state, value: state.value + action.value, error: void 0 },
2622
+ effect: { type: "none" }
2623
+ };
2624
+ }
2625
+ return submitTextPromptValue(state, options);
2626
+ }
2627
+ function submitTextPromptValue(state, options) {
2628
+ const trimmed = state.value.trim();
2629
+ if (!trimmed && !options.allowEmpty) {
2630
+ const emptyError = options.validate?.(trimmed);
2631
+ return {
2632
+ state: emptyError ? { ...state, error: emptyError } : state,
2633
+ effect: { type: "none" }
2634
+ };
2635
+ }
2636
+ const error = options.validate?.(trimmed);
2637
+ if (error !== void 0) {
2638
+ return { state: { ...state, error }, effect: { type: "none" } };
2639
+ }
2640
+ return { state: { ...state, resolved: true }, effect: { type: "submit", value: trimmed } };
2641
+ }
2642
+
2643
+ // src/TextPrompt.tsx
2644
+ var import_jsx_runtime13 = require("react/jsx-runtime");
2645
+ function TextPrompt({
2646
+ title,
2647
+ description,
2648
+ placeholder,
2649
+ onSubmit,
2650
+ onCancel,
2651
+ validate,
2652
+ allowEmpty = false,
2653
+ masked = false
2654
+ }) {
2655
+ const [state, setState] = (0, import_react16.useState)(() => createTextPromptFlowState());
2656
+ const stateRef = (0, import_react16.useRef)(state);
2657
+ const applyAction = (0, import_react16.useCallback)(
2658
+ (action) => {
2659
+ const result = applyTextPromptInput(stateRef.current, action, { allowEmpty, validate });
2660
+ stateRef.current = result.state;
2661
+ setState(result.state);
2662
+ if (result.effect.type === "cancel") {
2663
+ onCancel();
2664
+ } else if (result.effect.type === "submit") {
2665
+ onSubmit(result.effect.value);
2666
+ }
2667
+ },
2668
+ [allowEmpty, validate, onCancel, onSubmit]
2669
+ );
2670
+ (0, import_ink13.useInput)((input, key) => {
2671
+ const action = getTextPromptInputAction(input, key);
2672
+ if (action !== void 0) applyAction(action);
2673
+ });
2674
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_ink13.Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
2675
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_ink13.Text, { color: "yellow", bold: true, children: title }),
2676
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PromptDescription, { description }),
2677
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_ink13.Box, { marginTop: 1, children: [
2678
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_ink13.Text, { color: "cyan", children: "> " }),
2679
+ state.value ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_ink13.Text, { children: masked ? "*".repeat(state.value.length) : state.value }) : placeholder ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_ink13.Text, { dimColor: true, children: placeholder }) : null,
2680
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_ink13.Text, { color: "cyan", children: "\u2588" })
2681
+ ] }),
2682
+ state.error && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_ink13.Text, { color: "red", children: state.error }),
2683
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_ink13.Text, { dimColor: true, children: " Enter Submit Esc Cancel" })
2684
+ ] });
2685
+ }
2686
+ function PromptDescription({ description }) {
2687
+ if (description === void 0 || description.length === 0) {
2688
+ return null;
2689
+ }
2690
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_ink13.Text, { dimColor: true, children: description });
2691
+ }
2692
+
2693
+ // src/InteractivePrompt.tsx
2694
+ var import_jsx_runtime14 = require("react/jsx-runtime");
2695
+ function InteractivePrompt({
2696
+ prompt,
2697
+ onSubmit,
2698
+ onCancel
2699
+ }) {
2700
+ if (prompt.kind === "text") {
2701
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2702
+ TextPrompt,
2703
+ {
2704
+ title: prompt.title,
2705
+ description: prompt.description,
2706
+ placeholder: prompt.placeholder,
2707
+ allowEmpty: prompt.allowEmpty,
2708
+ masked: prompt.masked,
2709
+ validate: prompt.validate,
2710
+ onSubmit,
2711
+ onCancel
2712
+ },
2713
+ `text:${prompt.title}`
2714
+ );
2715
+ }
2716
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_ink14.Box, { flexDirection: "column", children: [
2717
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_ink14.Text, { bold: true, children: prompt.title }),
2718
+ prompt.description !== void 0 && prompt.description.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_ink14.Text, { dimColor: true, children: prompt.description }),
2719
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2720
+ ListPicker,
2721
+ {
2722
+ items: [...prompt.options],
2723
+ maxVisible: prompt.maxVisible,
2724
+ renderItem: (option, isSelected) => /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_ink14.Text, { color: isSelected ? "cyan" : void 0, children: [
2725
+ isSelected ? "> " : " ",
2726
+ option.label
2727
+ ] }),
2728
+ onSelect: (option) => onSubmit(option.value),
2729
+ onCancel
2730
+ }
2731
+ )
2732
+ ] });
2733
+ }
2734
+
2735
+ // src/PermissionPrompt.tsx
2736
+ var import_react17 = __toESM(require("react"), 1);
2737
+ var import_ink15 = require("ink");
2738
+
2739
+ // src/flows/permission-prompt-flow.ts
2740
+ var PERMISSION_PROMPT_OPTIONS = ["Allow", "Allow always (this session)", "Deny"];
2741
+ function getPermissionPromptInputAction(input, key) {
2742
+ const action = getDirectionalSelectionInputAction({ ...key, escape: false });
2743
+ if (action !== void 0) {
2744
+ return action;
2745
+ }
2746
+ if (input === "y" || input === "1") {
2747
+ return { type: "shortcut", index: 0 };
2748
+ }
2749
+ if (input === "a" || input === "2") {
2750
+ return { type: "shortcut", index: 1 };
2751
+ }
2752
+ if (input === "n" || input === "d" || input === "3") {
2753
+ return { type: "shortcut", index: 2 };
2754
+ }
2755
+ return void 0;
2756
+ }
2757
+ function applyPermissionPromptInput(state, action) {
2758
+ if (state.resolved) {
2759
+ return { state, effect: { type: "none" } };
2760
+ }
2761
+ if (typeof action !== "string") {
2762
+ return resolvePermissionIndex(state, action.index);
2763
+ }
2764
+ const result = applySelectionInput(state, action, {
2765
+ itemCount: PERMISSION_PROMPT_OPTIONS.length
2766
+ });
2767
+ if (result.effect.type !== "select") {
2768
+ return { state: result.state, effect: { type: "none" } };
2769
+ }
2770
+ return {
2771
+ state: result.state,
2772
+ effect: { type: "resolve", decision: getPermissionDecision(result.effect.index) }
2773
+ };
2774
+ }
2775
+ function getPermissionDecision(index) {
2776
+ if (index === 0) return true;
2777
+ if (index === 1) return "allow-session";
2778
+ return false;
2779
+ }
2780
+ function resolvePermissionIndex(state, index) {
2781
+ return {
2782
+ state: { ...state, selectedIndex: index, resolved: true },
2783
+ effect: { type: "resolve", decision: getPermissionDecision(index) }
2784
+ };
2785
+ }
2786
+
2787
+ // src/PermissionPrompt.tsx
2788
+ var import_jsx_runtime15 = require("react/jsx-runtime");
2789
+ function formatArgs(args) {
2790
+ const entries = Object.entries(args);
2791
+ if (entries.length === 0) return "(no arguments)";
2792
+ return entries.map(([k, v]) => `${k}: ${typeof v === "string" ? v : JSON.stringify(v)}`).join(", ");
2793
+ }
2794
+ function PermissionPrompt({ request }) {
2795
+ const [state, setState] = import_react17.default.useState(() => createSelectionFlowState());
2796
+ const stateRef = import_react17.default.useRef(state);
2797
+ const prevRequestRef = import_react17.default.useRef(request);
2798
+ if (prevRequestRef.current !== request) {
2799
+ prevRequestRef.current = request;
2800
+ const nextState = createSelectionFlowState();
2801
+ stateRef.current = nextState;
2802
+ setState(nextState);
2803
+ }
2804
+ const applyAction = import_react17.default.useCallback(
2805
+ (action) => {
2806
+ const result = applyPermissionPromptInput(stateRef.current, action);
2807
+ stateRef.current = result.state;
2808
+ setState(result.state);
2809
+ if (result.effect.type === "resolve") {
2810
+ request.resolve(result.effect.decision);
2811
+ }
2812
+ },
2813
+ [request]
2814
+ );
2815
+ (0, import_ink15.useInput)((input, key) => {
2816
+ const action = getPermissionPromptInputAction(input, key);
2817
+ if (action !== void 0) {
2818
+ applyAction(action);
2819
+ }
2820
+ });
2821
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_ink15.Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
2822
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_ink15.Text, { color: "yellow", bold: true, children: "[Permission Required]" }),
2823
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_ink15.Text, { children: [
2824
+ "Tool:",
2825
+ " ",
2826
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_ink15.Text, { color: "cyan", bold: true, children: request.toolName })
2827
+ ] }),
2828
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_ink15.Text, { dimColor: true, children: [
2829
+ " ",
2830
+ formatArgs(request.toolArgs)
2831
+ ] }),
2832
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_ink15.Box, { marginTop: 1, children: PERMISSION_PROMPT_OPTIONS.map((opt, i) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_ink15.Box, { marginRight: 2, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
2833
+ import_ink15.Text,
2834
+ {
2835
+ color: i === state.selectedIndex ? "cyan" : void 0,
2836
+ bold: i === state.selectedIndex,
2837
+ children: [
2838
+ i === state.selectedIndex ? "> " : " ",
2839
+ opt
2840
+ ]
2841
+ }
2842
+ ) }, opt)) }),
2843
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_ink15.Text, { dimColor: true, children: " left/right to select, Enter to confirm" })
2844
+ ] });
2845
+ }
2846
+
2847
+ // src/StreamingIndicator.tsx
2848
+ var import_ink16 = require("ink");
2849
+ var import_jsx_runtime16 = require("react/jsx-runtime");
2850
+ function getToolStyle(t) {
2851
+ if (t.isRunning) return { color: "yellow", icon: "\u27F3", strikethrough: false };
2852
+ if (t.result === "error") return { color: "red", icon: "\u2717", strikethrough: true };
2853
+ if (t.result === "denied") return { color: "yellowBright", icon: "\u2298", strikethrough: true };
2854
+ return { color: "green", icon: "\u2713", strikethrough: false };
2855
+ }
2856
+ function renderThinkingFallback(isThinking) {
2857
+ if (!isThinking) return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_jsx_runtime16.Fragment, {});
2858
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_ink16.Box, { marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_ink16.Text, { color: "yellow", children: "Thinking..." }) });
2859
+ }
2860
+ function renderTools(activeTools) {
2861
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_ink16.Box, { flexDirection: "column", marginBottom: 1, children: [
2862
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_ink16.Text, { color: "white", bold: true, children: "Tools:" }),
2863
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_ink16.Text, { children: " " }),
2864
+ activeTools.map((t, i) => {
2865
+ const { color, icon, strikethrough } = getToolStyle(t);
2866
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_ink16.Box, { flexDirection: "column", children: [
2867
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_ink16.Text, { color, strikethrough, children: [
2868
+ " ",
2869
+ icon,
2870
+ " ",
2871
+ t.toolName,
2872
+ "(",
2873
+ t.firstArg,
2874
+ ")"
2875
+ ] }),
2876
+ t.diffLines && t.diffLines.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(ToolDiffBlock, { file: t.diffFile, lines: t.diffLines })
2877
+ ] }, `${t.toolName}-${i}`);
2878
+ })
2879
+ ] });
2880
+ }
2881
+ function StreamingIndicator({
2882
+ text,
2883
+ activeTools,
2884
+ isThinking = false
2885
+ }) {
2886
+ const hasTools = activeTools.length > 0;
2887
+ const hasText = text.length > 0;
2888
+ if (!hasTools && !hasText) {
2889
+ return renderThinkingFallback(isThinking);
2890
+ }
2891
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_ink16.Box, { flexDirection: "column", children: [
2892
+ hasTools && renderTools(activeTools),
2893
+ hasText && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_ink16.Box, { flexDirection: "column", marginBottom: 1, children: [
2894
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_ink16.Text, { color: "cyan", bold: true, children: "Robota:" }),
2895
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_ink16.Text, { children: " " }),
2896
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_ink16.Box, { marginLeft: 2, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_ink16.Text, { wrap: "wrap", children: renderMarkdown(text) }) })
2897
+ ] })
2898
+ ] });
2899
+ }
2900
+
2901
+ // src/PluginTUI.tsx
2902
+ var import_react20 = require("react");
2903
+
2904
+ // src/MenuSelect.tsx
2905
+ var import_react18 = require("react");
2906
+ var import_ink17 = require("ink");
2907
+ var import_jsx_runtime17 = require("react/jsx-runtime");
2908
+ function MenuSelect({
2909
+ title,
2910
+ items,
2911
+ onSelect,
2912
+ onBack,
2913
+ loading,
2914
+ error
2915
+ }) {
2916
+ const [state, setState] = (0, import_react18.useState)(() => createSelectionFlowState());
2917
+ const stateRef = (0, import_react18.useRef)(state);
2918
+ const isEnabled = !loading && !error;
2919
+ const applyAction = (0, import_react18.useCallback)(
2920
+ (action) => {
2921
+ const result = applySelectionInput(stateRef.current, action, {
2922
+ itemCount: items.length,
2923
+ enabled: isEnabled
2924
+ });
2925
+ stateRef.current = result.state;
2926
+ setState(result.state);
2927
+ if (result.effect.type === "cancel") {
2928
+ onBack();
2929
+ } else if (result.effect.type === "select") {
2930
+ const item = items[result.effect.index];
2931
+ if (item !== void 0) {
2932
+ onSelect(item.value);
2933
+ }
2934
+ }
2935
+ },
2936
+ [isEnabled, items, onBack, onSelect]
2937
+ );
2938
+ (0, import_ink17.useInput)((input, key) => {
2939
+ const action = getVerticalSelectionInputAction(key);
2940
+ if (action !== void 0) {
2941
+ applyAction(action);
2942
+ }
2943
+ });
2944
+ const normalizedState = normalizeSelectionState(state, { itemCount: items.length });
2945
+ if (normalizedState !== state) {
2946
+ stateRef.current = normalizedState;
2947
+ }
2948
+ const selected = normalizedState.selectedIndex;
2949
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_ink17.Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
2950
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_ink17.Text, { color: "yellow", bold: true, children: title }),
2951
+ loading && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_ink17.Box, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_ink17.Text, { dimColor: true, children: "Loading..." }) }),
2952
+ error && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_ink17.Box, { marginTop: 1, flexDirection: "column", children: [
2953
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_ink17.Text, { color: "red", children: error }),
2954
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_ink17.Text, { dimColor: true, children: "Press Esc to go back" })
2955
+ ] }),
2956
+ !loading && !error && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_ink17.Box, { flexDirection: "column", marginTop: 1, children: items.map((item, i) => /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_ink17.Box, { children: [
2957
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_ink17.Text, { color: i === selected ? "cyan" : void 0, bold: i === selected, children: [
2958
+ i === selected ? "> " : " ",
2959
+ item.label
2960
+ ] }),
2961
+ item.hint && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_ink17.Text, { dimColor: true, children: [
2962
+ " ",
2963
+ item.hint
2964
+ ] })
2965
+ ] }, item.value)) }),
2966
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_ink17.Text, { dimColor: true, children: loading || error ? "" : " \u2191\u2193 Navigate Enter Select Esc Back" })
2967
+ ] });
2968
+ }
2969
+
2970
+ // src/plugin-tui-handlers.ts
2971
+ function handleMainSelect(value, nav) {
2972
+ if (value === "marketplace") {
2973
+ nav.push({ screen: "marketplace-list" });
2974
+ } else if (value === "installed") {
2975
+ nav.push({ screen: "installed-list" });
2976
+ }
2977
+ }
2978
+ function handleMarketplaceListSelect(value, nav) {
2979
+ if (value === "__add__") {
2980
+ nav.push({ screen: "marketplace-add" });
2981
+ } else {
2982
+ nav.push({ screen: "marketplace-action", context: { marketplace: value } });
2983
+ }
2984
+ }
2985
+ function handleMarketplaceActionSelect(value, marketplace, callbacks, nav) {
2986
+ if (value === "browse") {
2987
+ nav.push({ screen: "marketplace-browse", context: { marketplace } });
2988
+ } else if (value === "update") {
2989
+ callbacks.marketplaceUpdate(marketplace).then(() => {
2990
+ nav.notify(`Updated marketplace "${marketplace}".`);
2991
+ nav.pop();
2992
+ }).catch((err) => {
2993
+ nav.notify(`Error: ${err instanceof Error ? err.message : String(err)}`);
2994
+ });
2995
+ } else if (value === "remove") {
2996
+ nav.setConfirm({
2997
+ message: `Remove marketplace "${marketplace}" and all its plugins?`,
2998
+ onConfirm: () => {
2999
+ nav.setConfirm(void 0);
3000
+ callbacks.marketplaceRemove(marketplace).then(() => {
3001
+ nav.notify(`Removed marketplace "${marketplace}".`);
3002
+ nav.popN(2);
3003
+ }).catch((err) => {
3004
+ nav.notify(`Error: ${err instanceof Error ? err.message : String(err)}`);
3005
+ });
3006
+ },
3007
+ onCancel: () => nav.setConfirm(void 0)
3008
+ });
3009
+ }
3010
+ }
3011
+ function handleMarketplaceBrowseSelect(value, marketplace, items, nav) {
3012
+ const fullId = `${value}@${marketplace}`;
3013
+ const item = items.find((i) => i.value === value);
3014
+ if (item?.hint === "installed") {
3015
+ nav.push({ screen: "installed-action", context: { pluginId: fullId } });
3016
+ } else {
3017
+ nav.push({ screen: "marketplace-install-scope", context: { marketplace, pluginId: fullId } });
3018
+ }
3019
+ }
3020
+ function handleInstallScopeSelect(value, pluginId, callbacks, nav) {
3021
+ const scope = value;
3022
+ callbacks.install(pluginId, scope).then(() => {
3023
+ nav.notify(`Installed plugin "${pluginId}" (${scope} scope).`);
3024
+ nav.popN(2);
3025
+ }).catch((err) => {
3026
+ nav.notify(`Error: ${err instanceof Error ? err.message : String(err)}`);
3027
+ });
3028
+ }
3029
+ function handleInstalledListSelect(value, callbacks, nav) {
3030
+ nav.setConfirm({
3031
+ message: `Uninstall plugin "${value}"?`,
3032
+ onConfirm: () => {
3033
+ nav.setConfirm(void 0);
3034
+ callbacks.uninstall(value).then(() => {
3035
+ nav.notify(`Uninstalled plugin "${value}".`);
3036
+ nav.refresh();
3037
+ }).catch((err) => {
3038
+ nav.notify(`Error: ${err instanceof Error ? err.message : String(err)}`);
3039
+ });
3040
+ },
3041
+ onCancel: () => nav.setConfirm(void 0)
3042
+ });
3043
+ }
3044
+ function handleInstalledActionSelect(value, pluginId, callbacks, nav) {
3045
+ if (value === "uninstall") {
3046
+ nav.setConfirm({
3047
+ message: `Uninstall plugin "${pluginId}"?`,
3048
+ onConfirm: () => {
3049
+ nav.setConfirm(void 0);
3050
+ callbacks.uninstall(pluginId).then(() => {
3051
+ nav.notify(`Uninstalled plugin "${pluginId}".`);
3052
+ nav.popN(2);
3053
+ }).catch((err) => {
3054
+ nav.notify(`Error: ${err instanceof Error ? err.message : String(err)}`);
3055
+ });
3056
+ },
3057
+ onCancel: () => nav.setConfirm(void 0)
3058
+ });
3059
+ }
3060
+ }
3061
+
3062
+ // src/hooks/usePluginScreenData.ts
3063
+ var import_react19 = require("react");
3064
+ function usePluginScreenData(screen, marketplace, callbacks, refreshCounter, stackLength) {
3065
+ const [items, setItems] = (0, import_react19.useState)([]);
3066
+ const [loading, setLoading] = (0, import_react19.useState)(false);
3067
+ const [error, setError] = (0, import_react19.useState)();
3068
+ (0, import_react19.useEffect)(() => {
3069
+ setItems([]);
3070
+ setError(void 0);
3071
+ if (screen === "marketplace-list") {
3072
+ setLoading(true);
3073
+ callbacks.marketplaceList().then((sources) => {
3074
+ const baseItems = [{ label: "Add Marketplace", value: "__add__" }];
3075
+ const sourceItems = sources.map((s) => ({
3076
+ label: s.name,
3077
+ value: s.name,
3078
+ hint: s.type
3079
+ }));
3080
+ setItems([...baseItems, ...sourceItems]);
3081
+ setLoading(false);
3082
+ }).catch((err) => {
3083
+ setError(err instanceof Error ? err.message : String(err));
3084
+ setLoading(false);
3085
+ });
3086
+ } else if (screen === "marketplace-browse") {
3087
+ const mp = marketplace ?? "";
3088
+ setLoading(true);
3089
+ callbacks.listAvailablePlugins(mp).then((plugins) => {
3090
+ setItems(
3091
+ plugins.map((p) => ({
3092
+ label: p.name,
3093
+ value: p.name,
3094
+ hint: p.installed ? "installed" : p.description
3095
+ }))
3096
+ );
3097
+ setLoading(false);
3098
+ }).catch((err) => {
3099
+ setError(err instanceof Error ? err.message : String(err));
3100
+ setLoading(false);
3101
+ });
3102
+ } else if (screen === "installed-list") {
3103
+ setLoading(true);
3104
+ callbacks.listInstalled().then((plugins) => {
3105
+ setItems(
3106
+ plugins.map((p) => ({
3107
+ label: p.name,
3108
+ value: p.name,
3109
+ hint: p.description
3110
+ }))
3111
+ );
3112
+ setLoading(false);
3113
+ }).catch((err) => {
3114
+ setError(err instanceof Error ? err.message : String(err));
3115
+ setLoading(false);
3116
+ });
3117
+ }
3118
+ }, [stackLength, screen, marketplace, callbacks, refreshCounter]);
3119
+ return { items, loading, error };
3120
+ }
3121
+
3122
+ // src/PluginTUI.tsx
3123
+ var import_jsx_runtime18 = require("react/jsx-runtime");
3124
+ function PluginTUI({ callbacks, onClose, addMessage }) {
3125
+ const [stack, setStack] = (0, import_react20.useState)([{ screen: "main" }]);
3126
+ const [confirm, setConfirm] = (0, import_react20.useState)();
3127
+ const [refreshCounter, setRefreshCounter] = (0, import_react20.useState)(0);
3128
+ const current = stack[stack.length - 1] ?? { screen: "main" };
3129
+ const push = (0, import_react20.useCallback)((state) => {
3130
+ setStack((prev) => [...prev, state]);
3131
+ }, []);
3132
+ const pop = (0, import_react20.useCallback)(() => {
3133
+ setStack((prev) => {
3134
+ if (prev.length <= 1) {
3135
+ onClose();
3136
+ return prev;
3137
+ }
3138
+ return prev.slice(0, -1);
3139
+ });
3140
+ }, [onClose]);
3141
+ const popN = (0, import_react20.useCallback)(
3142
+ (n) => {
3143
+ setStack((prev) => {
3144
+ const next = prev.slice(0, Math.max(1, prev.length - n));
3145
+ if (next.length === 0) {
3146
+ onClose();
3147
+ return prev;
3148
+ }
3149
+ return next;
3150
+ });
3151
+ },
3152
+ [onClose]
3153
+ );
3154
+ const notify = (0, import_react20.useCallback)(
3155
+ (content) => {
3156
+ addMessage?.({ role: "system", content });
3157
+ },
3158
+ [addMessage]
3159
+ );
3160
+ const refresh = (0, import_react20.useCallback)(() => {
3161
+ setRefreshCounter((c) => c + 1);
3162
+ }, []);
3163
+ const setConfirmNav = (0, import_react20.useCallback)(
3164
+ (state) => setConfirm(state),
3165
+ [setConfirm]
3166
+ );
3167
+ const pushNav = (0, import_react20.useCallback)(
3168
+ (state) => push({ screen: state.screen, context: state.context }),
3169
+ [push]
3170
+ );
3171
+ const nav = { push: pushNav, pop, popN, notify, setConfirm: setConfirmNav, refresh };
3172
+ const { items, loading, error } = usePluginScreenData(
3173
+ current.screen,
3174
+ current.context?.marketplace,
3175
+ callbacks,
3176
+ refreshCounter,
3177
+ stack.length
3178
+ );
3179
+ const handleSelect = (0, import_react20.useCallback)(
3180
+ (value) => {
3181
+ const screen2 = current.screen;
3182
+ const ctx = current.context;
3183
+ if (screen2 === "main") handleMainSelect(value, nav);
3184
+ else if (screen2 === "marketplace-list") handleMarketplaceListSelect(value, nav);
3185
+ else if (screen2 === "marketplace-action")
3186
+ handleMarketplaceActionSelect(value, ctx?.marketplace ?? "", callbacks, nav);
3187
+ else if (screen2 === "marketplace-browse")
3188
+ handleMarketplaceBrowseSelect(value, ctx?.marketplace ?? "", items, nav);
3189
+ else if (screen2 === "marketplace-install-scope")
3190
+ handleInstallScopeSelect(value, ctx?.pluginId ?? "", callbacks, nav);
3191
+ else if (screen2 === "installed-list") handleInstalledListSelect(value, callbacks, nav);
3192
+ else if (screen2 === "installed-action")
3193
+ handleInstalledActionSelect(value, ctx?.pluginId ?? "", callbacks, nav);
3194
+ },
3195
+ [current, items, callbacks, push, pop, popN, notify, setConfirm, refresh]
3196
+ );
3197
+ const handleTextSubmit = (0, import_react20.useCallback)(
3198
+ (value) => {
3199
+ if (current.screen === "marketplace-add") {
3200
+ callbacks.marketplaceAdd(value).then((name) => {
3201
+ notify(`Added marketplace "${name}" from ${value}.`);
3202
+ pop();
3203
+ }).catch((err) => {
3204
+ notify(`Error: ${err instanceof Error ? err.message : String(err)}`);
3205
+ pop();
3206
+ });
3207
+ }
3208
+ },
3209
+ [current.screen, callbacks, notify, pop]
3210
+ );
3211
+ if (confirm) {
3212
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
3213
+ ConfirmPrompt,
3214
+ {
3215
+ message: confirm.message,
3216
+ onSelect: (index) => {
3217
+ if (index === 0) confirm.onConfirm();
3218
+ else confirm.onCancel();
3219
+ }
3220
+ }
3221
+ );
3222
+ }
3223
+ const screen = current.screen;
3224
+ if (screen === "marketplace-add") {
3225
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
3226
+ TextPrompt,
3227
+ {
3228
+ title: "Add Marketplace Source",
3229
+ placeholder: "owner/repo or git URL",
3230
+ onSubmit: handleTextSubmit,
3231
+ onCancel: pop,
3232
+ validate: (v) => !v.includes("/") ? "Must be owner/repo or a git URL" : void 0
3233
+ }
3234
+ );
3235
+ }
3236
+ if (screen === "marketplace-action") {
3237
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
3238
+ MenuSelect,
3239
+ {
3240
+ title: `Marketplace: ${current.context?.marketplace ?? ""}`,
3241
+ items: [
3242
+ { label: "Browse plugins", value: "browse" },
3243
+ { label: "Update", value: "update" },
3244
+ { label: "Remove", value: "remove" }
3245
+ ],
3246
+ onSelect: handleSelect,
3247
+ onBack: pop
3248
+ },
3249
+ stack.length
3250
+ );
3251
+ }
3252
+ if (screen === "marketplace-install-scope") {
3253
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
3254
+ MenuSelect,
3255
+ {
3256
+ title: `Install scope for "${current.context?.pluginId ?? ""}"`,
3257
+ items: [
3258
+ { label: "User scope", value: "user" },
3259
+ { label: "Project scope", value: "project" }
3260
+ ],
3261
+ onSelect: handleSelect,
3262
+ onBack: pop
3263
+ },
3264
+ stack.length
3265
+ );
3266
+ }
3267
+ if (screen === "installed-action") {
3268
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
3269
+ MenuSelect,
3270
+ {
3271
+ title: `Plugin: ${current.context?.pluginId ?? ""}`,
3272
+ items: [{ label: "Uninstall", value: "uninstall" }],
3273
+ onSelect: handleSelect,
3274
+ onBack: pop
3275
+ },
3276
+ stack.length
3277
+ );
3278
+ }
3279
+ const titleMap = {
3280
+ main: "Plugin Management",
3281
+ "marketplace-list": "Marketplace",
3282
+ "marketplace-browse": `Browse: ${current.context?.marketplace ?? ""}`,
3283
+ "installed-list": "Installed Plugins"
3284
+ };
3285
+ const staticItemsMap = {
3286
+ main: [
3287
+ { label: "Marketplace", value: "marketplace" },
3288
+ { label: "Installed Plugins", value: "installed" }
3289
+ ]
3290
+ };
3291
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
3292
+ MenuSelect,
3293
+ {
3294
+ title: titleMap[screen] ?? "Plugin Management",
3295
+ items: staticItemsMap[screen] ?? items,
3296
+ onSelect: handleSelect,
3297
+ onBack: pop,
3298
+ loading,
3299
+ error
3300
+ },
3301
+ `${screen}-${stack.length}-${refreshCounter}`
3302
+ );
3303
+ }
3304
+
3305
+ // src/TransportTUI.tsx
3306
+ var import_react21 = require("react");
3307
+ var import_ink18 = require("ink");
3308
+ var import_jsx_runtime19 = require("react/jsx-runtime");
3309
+ var TRANSPORT_NAME_WIDTH = 18;
3310
+ function TransportEntryRow({ entry, selected }) {
3311
+ const enabled = entry.config.enabled;
3312
+ const dot = enabled ? "\u25CF" : "\u25CB";
3313
+ const badge = enabled ? "[enabled] " : "[disabled]";
3314
+ const portOpt = entry.config.options?.port;
3315
+ const portHint = typeof portOpt === "number" ? `port: ${portOpt}` : "";
3316
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_ink18.Box, { children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_ink18.Text, { color: selected ? "cyan" : void 0, bold: selected, children: `${dot} ${entry.transport.name.padEnd(TRANSPORT_NAME_WIDTH)} ${badge} ${portHint}` }) });
3317
+ }
3318
+ function useTransportInput(entries, cursor, saving, registry, setCursor, setSaving, onClose, refresh) {
3319
+ (0, import_ink18.useInput)(
3320
+ (0, import_react21.useCallback)(
3321
+ (_input, key) => {
3322
+ if (saving) return;
3323
+ if (key.upArrow) {
3324
+ setCursor((c) => Math.max(0, c - 1));
3325
+ return;
3326
+ }
3327
+ if (key.downArrow) {
3328
+ setCursor((c) => Math.min(entries.length - 1, c + 1));
3329
+ return;
3330
+ }
3331
+ if (key.escape || key.return) {
3332
+ onClose();
3333
+ return;
3334
+ }
3335
+ if (_input === " ") {
3336
+ const entry = entries[cursor];
3337
+ if (!entry) return;
3338
+ setSaving(true);
3339
+ registry.setEnabled(entry.transport.name, !entry.config.enabled).then(() => {
3340
+ refresh();
3341
+ setSaving(false);
3342
+ }).catch(() => setSaving(false));
3343
+ }
3344
+ },
3345
+ [saving, entries, cursor, registry, onClose, refresh, setCursor, setSaving]
3346
+ )
3347
+ );
3348
+ }
3349
+ function TransportTUI({ registry, onClose }) {
3350
+ const [entries, setEntries] = (0, import_react21.useState)(() => registry.getAll());
3351
+ const [cursor, setCursor] = (0, import_react21.useState)(0);
3352
+ const [saving, setSaving] = (0, import_react21.useState)(false);
3353
+ const refresh = (0, import_react21.useCallback)(() => {
3354
+ setEntries(registry.getAll());
3355
+ }, [registry]);
3356
+ useTransportInput(entries, cursor, saving, registry, setCursor, setSaving, onClose, refresh);
3357
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_ink18.Box, { flexDirection: "column", paddingX: 2, paddingY: 1, children: [
3358
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_ink18.Text, { bold: true, children: "Settings \u203A Transports" }),
3359
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_ink18.Box, { marginTop: 1, flexDirection: "column", children: entries.map((entry, i) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(TransportEntryRow, { entry, selected: i === cursor }, entry.transport.name)) }),
3360
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_ink18.Box, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_ink18.Text, { dimColor: true, children: "\u2191\u2193 select space toggle enter/esc close" }) }),
3361
+ saving && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_ink18.Box, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_ink18.Text, { color: "yellow", children: "Saving\u2026" }) })
3362
+ ] });
3363
+ }
3364
+
3365
+ // src/SessionPicker.tsx
3366
+ var import_ink19 = require("ink");
3367
+ var import_jsx_runtime20 = require("react/jsx-runtime");
3368
+ var SESSION_ID_DISPLAY_LENGTH = 8;
3369
+ var SESSION_PREVIEW_DISPLAY_LENGTH = 60;
3370
+ function SessionPicker({
3371
+ sessions,
3372
+ onSelect,
3373
+ onCancel
3374
+ }) {
3375
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_ink19.Box, { flexDirection: "column", paddingX: 1, marginBottom: 1, children: [
3376
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_ink19.Text, { bold: true, color: "cyan", children: "Select a session to resume (ESC to cancel):" }),
3377
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
3378
+ ListPicker,
3379
+ {
3380
+ items: [...sessions],
3381
+ renderItem: (session, isSelected) => {
3382
+ const preview = session.preview ? session.preview.slice(0, SESSION_PREVIEW_DISPLAY_LENGTH) + (session.preview.length > SESSION_PREVIEW_DISPLAY_LENGTH ? "..." : "") : "";
3383
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_ink19.Text, { children: [
3384
+ isSelected ? "> " : " ",
3385
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_ink19.Text, { bold: true, children: session.name ?? session.id.slice(0, SESSION_ID_DISPLAY_LENGTH) }),
3386
+ " ",
3387
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_ink19.Text, { dimColor: true, children: new Date(session.updatedAt).toLocaleString(void 0, {
3388
+ month: "short",
3389
+ day: "numeric",
3390
+ hour: "2-digit",
3391
+ minute: "2-digit"
3392
+ }) }),
3393
+ " ",
3394
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_ink19.Text, { dimColor: true, children: [
3395
+ "msgs: ",
3396
+ session.messageCount
3397
+ ] }),
3398
+ preview ? /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_jsx_runtime20.Fragment, { children: [
3399
+ "\n ",
3400
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_ink19.Text, { color: "gray", children: preview })
3401
+ ] }) : null
3402
+ ] });
3403
+ },
3404
+ onSelect: (session) => onSelect(session.id),
3405
+ onCancel
3406
+ }
3407
+ )
3408
+ ] });
3409
+ }
3410
+
3411
+ // src/BackgroundTaskPanel.tsx
3412
+ var import_ink20 = require("ink");
3413
+
3414
+ // src/execution-workspace-view-model.ts
3415
+ var ACTIVE_STATUSES = [
3416
+ "active",
3417
+ "queued",
3418
+ "running",
3419
+ "waiting_permission"
3420
+ ];
3421
+ var DETAIL_RECORD_TEXT_LIMIT = 160;
3422
+ var PREVIEW_WHITESPACE = /\s+/g;
3423
+ var PREVIEW_SEPARATOR = " ";
3424
+ function getDefaultBackgroundWorkspaceEntries(snapshot) {
3425
+ return (snapshot?.entries ?? []).filter(
3426
+ (entry) => entry.kind === "background_task" && entry.visibility === "default"
3427
+ );
3428
+ }
3429
+ function countActiveBackgroundWorkspaceEntries(snapshot) {
3430
+ return getDefaultBackgroundWorkspaceEntries(snapshot).filter(
3431
+ (entry) => ACTIVE_STATUSES.includes(entry.status)
3432
+ ).length;
3433
+ }
3434
+ function formatExecutionWorkspaceEntryRow(entry, options = {}) {
3435
+ const isSelected = entry.id === options.selectedEntryId;
3436
+ const row = {
3437
+ id: entry.id,
3438
+ radio: isSelected ? "\u25CF" : "\u25CB",
3439
+ title: formatEntryTitle(entry),
3440
+ subtitle: formatEntrySubtitle(entry),
3441
+ statusLabel: formatStatusLabel2(entry.status),
3442
+ preview: trimPreview(entry.preview ?? entry.currentAction),
3443
+ color: getEntryColor(entry),
3444
+ isSelected
3445
+ };
3446
+ return { ...row, accessibleText: formatAccessibleText(row) };
3447
+ }
3448
+ function formatExecutionDetailRecord(record) {
3449
+ const text = record.text.trim().replace(PREVIEW_WHITESPACE, PREVIEW_SEPARATOR);
3450
+ if (!text) return record.kind;
3451
+ return text.length > DETAIL_RECORD_TEXT_LIMIT ? `${text.slice(0, DETAIL_RECORD_TEXT_LIMIT)}...` : text;
3452
+ }
3453
+ function formatEntryTitle(entry) {
3454
+ if (entry.kind === "main_thread") return entry.title;
3455
+ if (entry.kind === "background_group") return `${entry.title} group`;
3456
+ if (entry.taskKind === "agent") return `${entry.title} agent`;
3457
+ if (entry.taskKind === "process") return entry.title || "Process";
3458
+ return entry.title;
3459
+ }
3460
+ function formatEntrySubtitle(entry) {
3461
+ if (entry.kind === "main_thread") return entry.subtitle;
3462
+ const parts = [
3463
+ entry.taskKind,
3464
+ entry.subtitle,
3465
+ entry.attention === "none" ? void 0 : entry.attention
3466
+ ];
3467
+ return parts.filter((part) => typeof part === "string" && part.length > 0).join(" \xB7 ") || void 0;
3468
+ }
3469
+ function formatStatusLabel2(status) {
3470
+ return status.replace(/_/g, " ");
3471
+ }
3472
+ function getEntryColor(entry) {
3473
+ if (entry.attention === "failed" || entry.status === "failed") return "red";
3474
+ if (entry.attention === "permission" || entry.status === "waiting_permission") return "yellow";
3475
+ if (entry.status === "completed") return "green";
3476
+ if (entry.status === "cancelled") return "yellow";
3477
+ if (ACTIVE_STATUSES.includes(entry.status)) return "cyan";
3478
+ return "white";
3479
+ }
3480
+ function trimPreview(value) {
3481
+ const preview = value?.trim().replace(PREVIEW_WHITESPACE, PREVIEW_SEPARATOR);
3482
+ return preview || void 0;
3483
+ }
3484
+ function formatAccessibleText(row) {
3485
+ const parts = [row.radio, row.title, row.statusLabel, row.subtitle, row.preview];
3486
+ return parts.filter((part) => typeof part === "string" && part.length > 0).join(" \xB7 ");
3487
+ }
3488
+
3489
+ // src/background-task-row-format.ts
3490
+ function formatBackgroundTaskRow(entry, options = {}) {
3491
+ const row = formatExecutionWorkspaceEntryRow(entry);
3492
+ const marker = isActiveEntry(entry) ? "\u25A1" : "\u25A0";
3493
+ const segments = [row.statusLabel, row.subtitle].filter(
3494
+ (segment) => typeof segment === "string" && segment.length > 0
3495
+ );
3496
+ return {
3497
+ connector: options.isLast === false ? "\u251C" : "\u2514",
3498
+ marker,
3499
+ color: row.color,
3500
+ label: row.title,
3501
+ segments,
3502
+ preview: row.preview,
3503
+ accessibleText: [
3504
+ `${options.isLast === false ? "\u251C" : "\u2514"} ${marker} ${row.title}`,
3505
+ ...segments,
3506
+ row.preview
3507
+ ].filter((part) => typeof part === "string" && part.length > 0).join(" \xB7 ")
3508
+ };
3509
+ }
3510
+ function isActiveEntry(entry) {
3511
+ return entry.status === "active" || entry.status === "queued" || entry.status === "running" || entry.status === "waiting_permission";
3512
+ }
3513
+
3514
+ // src/BackgroundTaskPanel.tsx
3515
+ var import_jsx_runtime21 = require("react/jsx-runtime");
3516
+ function BackgroundTaskPanel({ entries }) {
3517
+ if (entries.length === 0) return null;
3518
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_ink20.Box, { flexDirection: "column", marginBottom: 1, children: [
3519
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_ink20.Text, { color: "cyan", bold: true, children: "Background work" }),
3520
+ entries.map((entry, index) => {
3521
+ const row = formatBackgroundTaskRow(entry, { isLast: index === entries.length - 1 });
3522
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_ink20.Text, { children: [
3523
+ `${row.connector} `,
3524
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_ink20.Text, { color: row.color, children: row.marker }),
3525
+ ` ${row.label}`,
3526
+ row.segments.map((segment, segmentIndex) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_ink20.Text, { dimColor: true, children: ` \xB7 ${segment}` }, `${segment}-${segmentIndex}`)),
3527
+ row.preview ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_ink20.Text, { dimColor: true, children: ` \xB7 ${row.preview}` }) : null
3528
+ ] }, entry.id);
3529
+ })
3530
+ ] });
3531
+ }
3532
+
3533
+ // src/ExecutionWorkspaceSwitcher.tsx
3534
+ var import_react22 = require("react");
3535
+ var import_ink21 = require("ink");
3536
+ var import_jsx_runtime22 = require("react/jsx-runtime");
3537
+ var MAX_VISIBLE_WORKSPACE_ENTRIES = 8;
3538
+ function ExecutionWorkspaceSwitcher({
3539
+ snapshot,
3540
+ selectedEntryId,
3541
+ onSelect,
3542
+ onClose
3543
+ }) {
3544
+ const entries = [...snapshot?.entries ?? []];
3545
+ const { normalized, visibleEntries, applyAction } = useWorkspaceSwitcherSelection({
3546
+ entries,
3547
+ selectedEntryId,
3548
+ onSelect,
3549
+ onClose
3550
+ });
3551
+ (0, import_ink21.useInput)((_input, key) => {
3552
+ const action = getVerticalSelectionInputAction(key);
3553
+ if (action !== void 0) applyAction(action);
3554
+ });
3555
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_ink21.Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, children: [
3556
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_ink21.Text, { color: "cyan", bold: true, children: "Execution workspace" }),
3557
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_ink21.Box, { flexDirection: "column", marginTop: 1, children: visibleEntries.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_ink21.Text, { dimColor: true, children: "No workspace entries" }) : visibleEntries.map((entry, index) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
3558
+ ExecutionWorkspaceSwitcherRow,
3559
+ {
3560
+ entry,
3561
+ isFocused: normalized.scrollOffset + index === normalized.selectedIndex,
3562
+ selectedEntryId
3563
+ },
3564
+ entry.id
3565
+ )) }),
3566
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_ink21.Text, { dimColor: true, children: "Ctrl+B Close \u2191\u2193 Navigate Enter Switch Esc Close" })
3567
+ ] });
3568
+ }
3569
+ function useWorkspaceSwitcherSelection({
3570
+ entries,
3571
+ selectedEntryId,
3572
+ onSelect,
3573
+ onClose
3574
+ }) {
3575
+ const [state, setState] = (0, import_react22.useState)(() => createSelectionFlowState());
3576
+ const stateRef = (0, import_react22.useRef)(state);
3577
+ (0, import_react22.useEffect)(() => {
3578
+ const selectedIndex = Math.max(
3579
+ 0,
3580
+ entries.findIndex((entry) => entry.id === selectedEntryId)
3581
+ );
3582
+ const nextState = createNormalizedSelection({ selectedIndex, itemCount: entries.length });
3583
+ stateRef.current = nextState;
3584
+ setState(nextState);
3585
+ }, [entries.length, selectedEntryId]);
3586
+ const normalized = createNormalizedSelection({
3587
+ selectedIndex: state.selectedIndex,
3588
+ scrollOffset: state.scrollOffset,
3589
+ itemCount: entries.length
3590
+ });
3591
+ if (normalized !== state) stateRef.current = normalized;
3592
+ return {
3593
+ normalized,
3594
+ visibleEntries: entries.slice(
3595
+ normalized.scrollOffset,
3596
+ normalized.scrollOffset + MAX_VISIBLE_WORKSPACE_ENTRIES
3597
+ ),
3598
+ applyAction: createApplyAction({ entries, stateRef, setState, onSelect, onClose })
3599
+ };
3600
+ }
3601
+ function createApplyAction({
3602
+ entries,
3603
+ stateRef,
3604
+ setState,
3605
+ onSelect,
3606
+ onClose
3607
+ }) {
3608
+ return (action) => {
3609
+ const result = applySelectionInput(stateRef.current, action, {
3610
+ itemCount: entries.length,
3611
+ maxVisible: MAX_VISIBLE_WORKSPACE_ENTRIES
3612
+ });
3613
+ const nextState = result.effect.type === "select" || result.effect.type === "cancel" ? { ...result.state, resolved: false } : result.state;
3614
+ stateRef.current = nextState;
3615
+ setState(nextState);
3616
+ if (result.effect.type === "cancel") {
3617
+ onClose();
3618
+ } else if (result.effect.type === "select") {
3619
+ const entry = entries[result.effect.index];
3620
+ if (entry) onSelect(entry.id);
3621
+ }
3622
+ };
3623
+ }
3624
+ function createNormalizedSelection(input) {
3625
+ return normalizeSelectionState(
3626
+ {
3627
+ selectedIndex: input.selectedIndex,
3628
+ scrollOffset: input.scrollOffset ?? 0,
3629
+ resolved: false
3630
+ },
3631
+ { itemCount: input.itemCount, maxVisible: MAX_VISIBLE_WORKSPACE_ENTRIES }
3632
+ );
3633
+ }
3634
+ function ExecutionWorkspaceSwitcherRow({
3635
+ entry,
3636
+ isFocused,
3637
+ selectedEntryId
3638
+ }) {
3639
+ const row = formatExecutionWorkspaceEntryRow(entry, { selectedEntryId });
3640
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_ink21.Text, { children: [
3641
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_ink21.Text, { color: isFocused ? "cyan" : void 0, bold: isFocused, children: isFocused ? "> " : " " }),
3642
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_ink21.Text, { color: row.color, children: row.radio }),
3643
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_ink21.Text, { color: isFocused ? "cyan" : void 0, bold: isFocused, children: ` ${row.title}` }),
3644
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_ink21.Text, { dimColor: true, children: ` \xB7 ${row.statusLabel}` }),
3645
+ row.subtitle ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_ink21.Text, { dimColor: true, children: ` \xB7 ${row.subtitle}` }) : null,
3646
+ row.preview ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_ink21.Text, { dimColor: true, children: ` \xB7 ${row.preview}` }) : null
3647
+ ] });
3648
+ }
3649
+
3650
+ // src/ExecutionWorkspaceDetailPane.tsx
3651
+ var import_ink22 = require("ink");
3652
+ var import_jsx_runtime23 = require("react/jsx-runtime");
3653
+ var MAX_VISIBLE_DETAIL_RECORDS = 12;
3654
+ function ExecutionWorkspaceDetailPane({
3655
+ entry,
3656
+ page,
3657
+ loading,
3658
+ error
3659
+ }) {
3660
+ const row = formatExecutionWorkspaceEntryRow(entry, { selectedEntryId: entry.id });
3661
+ const records = page?.records.slice(-MAX_VISIBLE_DETAIL_RECORDS) ?? [];
3662
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_ink22.Box, { flexDirection: "column", marginBottom: 1, children: [
3663
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_ink22.Text, { color: "cyan", bold: true, children: `Viewing ${row.title}` }),
3664
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_ink22.Text, { dimColor: true, children: [
3665
+ row.statusLabel,
3666
+ row.subtitle ? ` \xB7 ${row.subtitle}` : "",
3667
+ row.preview ? ` \xB7 ${row.preview}` : ""
3668
+ ] }),
3669
+ loading ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_ink22.Text, { dimColor: true, children: "Loading workspace detail..." }) : null,
3670
+ error ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_ink22.Text, { color: "red", children: error }) : null,
3671
+ !loading && !error && records.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_ink22.Text, { dimColor: true, children: "No detail yet" }) : null,
3672
+ !loading && !error && records.map((record) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_ink22.Text, { color: getDetailRecordColor(record.kind), children: formatExecutionDetailRecord(record) }, record.id)),
3673
+ page?.nextCursor ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_ink22.Text, { dimColor: true, children: "... more detail available" }) : null
3674
+ ] });
3675
+ }
3676
+ function getDetailRecordColor(kind) {
3677
+ if (kind === "error") return "red";
3678
+ if (kind === "result") return "green";
3679
+ if (kind === "process_output") return "white";
3680
+ if (kind === "group_summary") return "cyan";
3681
+ return void 0;
3682
+ }
3683
+
3684
+ // src/UpdateNotice.tsx
3685
+ var import_ink23 = require("ink");
3686
+ var import_jsx_runtime24 = require("react/jsx-runtime");
3687
+ function UpdateNotice({ message }) {
3688
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_ink23.Box, { paddingX: 1, marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_ink23.Text, { color: "yellow", children: message }) });
3689
+ }
3690
+
3691
+ // src/App.tsx
3692
+ var import_jsx_runtime25 = require("react/jsx-runtime");
3693
+ function App(props) {
3694
+ const [activeSessionId, setActiveSessionId] = (0, import_react23.useState)(props.resumeSessionId);
3695
+ const [showInitialSessionPicker, setShowInitialSessionPicker] = (0, import_react23.useState)(
3696
+ props.showSessionPickerOnStart ?? false
3697
+ );
3698
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(TuiCliAdapterProvider, { value: props.cliAdapter, children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
3699
+ AppInner,
3700
+ {
3701
+ ...props,
3702
+ showSessionPickerOnStart: showInitialSessionPicker,
3703
+ resumeSessionId: activeSessionId,
3704
+ onSessionSwitch: (sessionId) => {
3705
+ setShowInitialSessionPicker(false);
3706
+ setActiveSessionId(sessionId);
3707
+ }
3708
+ },
3709
+ activeSessionId ?? "__new__"
3710
+ ) });
3711
+ }
3712
+ function AppInner(props) {
3713
+ const cwd = props.cwd;
3714
+ const {
3715
+ interactiveSession,
3716
+ registry,
3717
+ commandEffectQueue,
3718
+ history,
3719
+ addEntry,
3720
+ streamingText,
3721
+ activeTools,
3722
+ isThinking,
3723
+ isAborting,
3724
+ isShuttingDown,
3725
+ pendingPrompt,
3726
+ executionWorkspaceSnapshot,
3727
+ selectedExecutionEntryId,
3728
+ selectExecutionWorkspaceEntry,
3729
+ readExecutionWorkspaceDetail,
3730
+ permissionRequest,
3731
+ contextState,
3732
+ handleSubmit: baseHandleSubmit,
3733
+ handleAbort,
3734
+ handleCancelQueue,
3735
+ handleShutdown
3736
+ } = useInteractiveSession({
3737
+ cwd,
3738
+ provider: props.provider,
3739
+ permissionMode: props.permissionMode,
3740
+ maxTurns: props.maxTurns,
3741
+ sessionStore: props.sessionStore,
3742
+ resumeSessionId: props.resumeSessionId,
3743
+ forkSession: props.forkSession,
3744
+ sessionName: props.sessionName,
3745
+ backgroundTaskRunners: props.backgroundTaskRunners,
3746
+ subagentRunnerFactory: props.subagentRunnerFactory,
3747
+ commandModules: props.commandModules,
3748
+ commandHostAdapters: props.commandHostAdapters,
3749
+ transportRegistry: props.transportRegistry,
3750
+ language: props.language,
3751
+ reloadPluginCommandSource: props.reloadPluginCommandSource
3752
+ });
3753
+ const fallbackPluginCallbacks = usePluginCallbacks(cwd);
3754
+ const pluginCallbacks = props.commandHostAdapters?.plugin ?? fallbackPluginCallbacks;
3755
+ const { exit } = (0, import_ink24.useApp)();
3756
+ const [sessionName, setSessionName] = (0, import_react23.useState)(props.sessionName);
3757
+ const [updateNotice, setUpdateNotice] = (0, import_react23.useState)();
3758
+ const [showExecutionWorkspaceSwitcher, setShowExecutionWorkspaceSwitcher] = (0, import_react23.useState)(false);
3759
+ const [executionDetailPage, setExecutionDetailPage] = (0, import_react23.useState)(null);
3760
+ const [executionDetailError, setExecutionDetailError] = (0, import_react23.useState)();
3761
+ const [isExecutionDetailLoading, setIsExecutionDetailLoading] = (0, import_react23.useState)(false);
3762
+ const [statusLineSettings, setStatusLineSettings] = useStatusLineSettings();
3763
+ const backgroundWorkspaceEntries = (0, import_react23.useMemo)(
3764
+ () => getDefaultBackgroundWorkspaceEntries(executionWorkspaceSnapshot),
3765
+ [executionWorkspaceSnapshot]
3766
+ );
3767
+ const activeBackgroundTaskCount = countActiveBackgroundWorkspaceEntries(
3768
+ executionWorkspaceSnapshot
3769
+ );
3770
+ const selectedExecutionEntry = (0, import_react23.useMemo)(
3771
+ () => executionWorkspaceSnapshot?.entries.find((entry) => entry.id === selectedExecutionEntryId),
3772
+ [executionWorkspaceSnapshot, selectedExecutionEntryId]
3773
+ );
3774
+ const {
3775
+ handleSubmit,
3776
+ pendingModelId,
3777
+ pendingInteractionPrompt,
3778
+ showPluginTUI,
3779
+ showSessionPicker,
3780
+ showTransportTUI,
3781
+ setShowPluginTUI,
3782
+ setShowSessionPicker,
3783
+ setShowTransportTUI,
3784
+ handleModelConfirm,
3785
+ handleInteractionSubmit,
3786
+ handleInteractionCancel
3787
+ } = useSideEffects({
3788
+ cwd,
3789
+ providerOverride: props.providerOverride,
3790
+ interactiveSession,
3791
+ commandEffectQueue,
3792
+ addEntry,
3793
+ baseHandleSubmit,
3794
+ setSessionName,
3795
+ setStatusLineSettings,
3796
+ showSessionPickerOnStart: props.showSessionPickerOnStart
3797
+ });
3798
+ (0, import_react23.useEffect)(() => {
3799
+ const name = interactiveSession?.getName?.();
3800
+ if (name && !sessionName) setSessionName(name);
3801
+ }, [interactiveSession, sessionName]);
3802
+ (0, import_react23.useEffect)(() => {
3803
+ let isMounted = true;
3804
+ props.startupUpdateNotice?.then((notice) => {
3805
+ if (isMounted && notice !== void 0) {
3806
+ setUpdateNotice(notice);
3807
+ }
3808
+ }).catch(() => {
3809
+ });
3810
+ return () => {
3811
+ isMounted = false;
3812
+ };
3813
+ }, [props.startupUpdateNotice]);
3814
+ (0, import_react23.useEffect)(() => {
3815
+ const title = sessionName ? `Robota \u2014 ${sessionName}` : "Robota";
3816
+ process.stdout.write(`\x1B]0;${title}\x07`);
3817
+ }, [sessionName]);
3818
+ (0, import_ink24.useInput)((_input, key) => {
3819
+ if (!key.escape || !isThinking) return;
3820
+ if (permissionRequest || showPluginTUI || showTransportTUI || showSessionPicker || showExecutionWorkspaceSwitcher) {
3821
+ return;
3822
+ }
3823
+ handleAbort();
3824
+ });
3825
+ (0, import_ink24.useInput)((input, key) => {
3826
+ if (!key.ctrl || input !== "b") return;
3827
+ if (permissionRequest || showPluginTUI || showSessionPicker || isShuttingDown) return;
3828
+ setShowExecutionWorkspaceSwitcher((shown) => !shown);
3829
+ });
3830
+ (0, import_ink24.useInput)((input, key) => {
3831
+ if (!key.ctrl || input !== "c" || isShuttingDown) return;
3832
+ void handleShutdown("prompt_input_exit").finally(() => exit());
3833
+ });
3834
+ (0, import_react23.useEffect)(() => {
3835
+ const onSigterm = () => {
3836
+ if (isShuttingDown) return;
3837
+ void handleShutdown("other").finally(() => exit());
3838
+ };
3839
+ process.once("SIGINT", onSigterm);
3840
+ process.once("SIGTERM", onSigterm);
3841
+ return () => {
3842
+ process.off("SIGINT", onSigterm);
3843
+ process.off("SIGTERM", onSigterm);
3844
+ };
3845
+ }, [handleShutdown, exit, isShuttingDown]);
3846
+ (0, import_react23.useEffect)(() => {
3847
+ if (!selectedExecutionEntry || selectedExecutionEntry.kind === "main_thread") {
3848
+ setExecutionDetailPage(null);
3849
+ setExecutionDetailError(void 0);
3850
+ setIsExecutionDetailLoading(false);
3851
+ return;
3852
+ }
3853
+ let isCurrent = true;
3854
+ setIsExecutionDetailLoading(true);
3855
+ setExecutionDetailError(void 0);
3856
+ readExecutionWorkspaceDetail(selectedExecutionEntry.id).then((page) => {
3857
+ if (!isCurrent) return;
3858
+ setExecutionDetailPage(page);
3859
+ setIsExecutionDetailLoading(false);
3860
+ }).catch((error) => {
3861
+ if (!isCurrent) return;
3862
+ setExecutionDetailError(error.message);
3863
+ setIsExecutionDetailLoading(false);
3864
+ });
3865
+ return () => {
3866
+ isCurrent = false;
3867
+ };
3868
+ }, [executionWorkspaceSnapshot, readExecutionWorkspaceDetail, selectedExecutionEntry]);
3869
+ let permissionMode = props.permissionMode ?? "default";
3870
+ let sessionId = "";
3871
+ try {
3872
+ const session = interactiveSession.getSession();
3873
+ permissionMode = session.getPermissionMode();
3874
+ sessionId = session.getSessionId();
3875
+ } catch {
3876
+ }
3877
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_ink24.Box, { flexDirection: "column", children: [
3878
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_ink24.Box, { flexDirection: "column", paddingX: 1, marginBottom: 1, children: [
3879
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_ink24.Text, { color: "cyan", bold: true, children: `
3880
+ ____ ___ ____ ___ _____ _
3881
+ | _ \\ / _ \\| __ ) / _ \\_ _|/ \\
3882
+ | |_) | | | | _ \\| | | || | / _ \\
3883
+ | _ <| |_| | |_) | |_| || |/ ___ \\
3884
+ |_| \\_\\\\___/|____/ \\___/ |_/_/ \\_\\
3885
+ ` }),
3886
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_ink24.Text, { dimColor: true, children: [
3887
+ " v",
3888
+ props.version ?? "0.0.0"
3889
+ ] })
3890
+ ] }),
3891
+ updateNotice && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(UpdateNotice, { message: updateNotice }),
3892
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_ink24.Box, { flexDirection: "column", paddingX: 1, flexGrow: 1, children: [
3893
+ selectedExecutionEntry && selectedExecutionEntry.kind !== "main_thread" ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
3894
+ ExecutionWorkspaceDetailPane,
3895
+ {
3896
+ entry: selectedExecutionEntry,
3897
+ page: executionDetailPage,
3898
+ loading: isExecutionDetailLoading,
3899
+ error: executionDetailError
3900
+ }
3901
+ ) : /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(MessageList, { history }),
3902
+ isShuttingDown && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_ink24.Box, { marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_ink24.Text, { color: "yellow", children: "Shutting down..." }) }),
3903
+ (isThinking || activeTools.length > 0) && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_ink24.Box, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
3904
+ StreamingIndicator,
3905
+ {
3906
+ text: streamingText,
3907
+ activeTools,
3908
+ isThinking
3909
+ }
3910
+ ) }),
3911
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(BackgroundTaskPanel, { entries: backgroundWorkspaceEntries })
3912
+ ] }),
3913
+ showExecutionWorkspaceSwitcher && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
3914
+ ExecutionWorkspaceSwitcher,
3915
+ {
3916
+ snapshot: executionWorkspaceSnapshot,
3917
+ selectedEntryId: selectedExecutionEntryId,
3918
+ onSelect: selectExecutionWorkspaceEntry,
3919
+ onClose: () => setShowExecutionWorkspaceSwitcher(false)
3920
+ }
3921
+ ),
3922
+ permissionRequest && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(PermissionPrompt, { request: permissionRequest }),
3923
+ pendingModelId && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
3924
+ ConfirmPrompt,
3925
+ {
3926
+ message: formatModelChangeConfirmationMessage(pendingModelId),
3927
+ onSelect: handleModelConfirm
3928
+ }
3929
+ ),
3930
+ pendingInteractionPrompt && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
3931
+ InteractivePrompt,
3932
+ {
3933
+ prompt: pendingInteractionPrompt,
3934
+ onSubmit: handleInteractionSubmit,
3935
+ onCancel: handleInteractionCancel
3936
+ }
3937
+ ),
3938
+ showPluginTUI && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
3939
+ PluginTUI,
3940
+ {
3941
+ callbacks: pluginCallbacks,
3942
+ onClose: () => setShowPluginTUI(false),
3943
+ addMessage: (msg) => addEntry((0, import_agent_core10.messageToHistoryEntry)((0, import_agent_core10.createSystemMessage)(msg.content)))
3944
+ }
3945
+ ),
3946
+ showTransportTUI && props.transportRegistry && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
3947
+ TransportTUI,
3948
+ {
3949
+ registry: props.transportRegistry,
3950
+ onClose: () => setShowTransportTUI(false)
3951
+ }
3952
+ ),
3953
+ showSessionPicker && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
3954
+ SessionPicker,
3955
+ {
3956
+ sessions: (0, import_agent_sdk4.listResumableSessionSummaries)(props.sessionStore, props.cwd),
3957
+ onSelect: (id) => {
3958
+ setShowSessionPicker(false);
3959
+ props.onSessionSwitch(id);
3960
+ },
3961
+ onCancel: () => {
3962
+ setShowSessionPicker(false);
3963
+ addEntry((0, import_agent_core10.messageToHistoryEntry)((0, import_agent_core10.createSystemMessage)("Session resume cancelled.")));
3964
+ }
3965
+ }
3966
+ ),
3967
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
3968
+ SessionStatusBar,
3969
+ {
3970
+ cwd,
3971
+ permissionMode,
3972
+ modelId: props.modelId,
3973
+ providerProfileName: props.providerProfileName,
3974
+ providerType: props.providerType,
3975
+ sessionId,
3976
+ isThinking,
3977
+ activeToolCount: activeTools.length,
3978
+ activeBackgroundTaskCount,
3979
+ hasPendingPrompt: pendingPrompt !== null,
3980
+ contextState,
3981
+ sessionName,
3982
+ settings: statusLineSettings
3983
+ }
3984
+ ),
3985
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
3986
+ InputArea,
3987
+ {
3988
+ onSubmit: handleSubmit,
3989
+ onCancelQueue: handleCancelQueue,
3990
+ isDisabled: !!permissionRequest || showPluginTUI || showTransportTUI || showSessionPicker || showExecutionWorkspaceSwitcher || isShuttingDown || pendingInteractionPrompt !== null || isThinking && !!pendingPrompt,
3991
+ isAborting,
3992
+ pendingPrompt,
3993
+ registry,
3994
+ sessionName,
3995
+ history
3996
+ }
3997
+ ),
3998
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_ink24.Text, { children: " " })
3999
+ ] });
4000
+ }
4001
+
4002
+ // src/render.tsx
4003
+ var import_jsx_runtime26 = require("react/jsx-runtime");
4004
+ async function renderApp(options) {
4005
+ process.on("unhandledRejection", (reason) => {
4006
+ process.stderr.write(`
4007
+ [UNHANDLED REJECTION] ${reason}
4008
+ `);
4009
+ if (reason instanceof Error) {
4010
+ process.stderr.write(`${reason.stack}
4011
+ `);
4012
+ }
4013
+ });
4014
+ const instance = (0, import_ink25.render)(/* @__PURE__ */ (0, import_jsx_runtime26.jsx)(App, { ...options }), { exitOnCtrlC: false });
4015
+ await instance.waitUntilExit();
4016
+ }
4017
+
4018
+ // src/tui-transport.ts
4019
+ var TuiTransport = class {
4020
+ name = "tui";
4021
+ defaultEnabled = true;
4022
+ optionsSchema = {};
4023
+ options;
4024
+ constructor(options) {
4025
+ this.options = options;
4026
+ }
4027
+ attach(_session) {
4028
+ }
4029
+ async start() {
4030
+ await renderApp(this.options);
4031
+ }
4032
+ async stop() {
4033
+ }
4034
+ validateOptions(_options) {
4035
+ return true;
4036
+ }
4037
+ };
4038
+ // Annotate the CommonJS export names for ESM import in node:
4039
+ 0 && (module.exports = {
4040
+ TuiTransport
4041
+ });