@robota-sdk/agent-cli 3.0.0-beta.52 → 3.0.0-beta.54

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.
@@ -75,7 +75,14 @@ function getUserSettingsPath() {
75
75
  }
76
76
  function readSettings(path) {
77
77
  if (!existsSync(path)) return {};
78
- return JSON.parse(readFileSync(path, "utf8"));
78
+ const raw = readFileSync(path, "utf8");
79
+ try {
80
+ return JSON.parse(raw);
81
+ } catch {
82
+ process.stderr.write(`Warning: corrupt settings file at ${path}, resetting to defaults
83
+ `);
84
+ return {};
85
+ }
79
86
  }
80
87
  function writeSettings(path, settings) {
81
88
  mkdirSync(dirname(path), { recursive: true });
@@ -147,12 +154,12 @@ import { createHeadlessTransport } from "@robota-sdk/agent-transport-headless";
147
154
  import { render } from "ink";
148
155
 
149
156
  // src/ui/App.tsx
150
- import { useState as useState10, useRef as useRef8, useEffect as useEffect4 } from "react";
151
- import { Box as Box12, Text as Text14, useApp, useInput as useInput8 } from "ink";
152
- import { getModelName, createSystemMessage as createSystemMessage2, messageToHistoryEntry as messageToHistoryEntry2 } from "@robota-sdk/agent-core";
157
+ import { useState as useState13, useEffect as useEffect4 } from "react";
158
+ import { Box as Box13, Text as Text15, useInput as useInput8 } from "ink";
159
+ import { getModelName as getModelName2, createSystemMessage as createSystemMessage3, messageToHistoryEntry as messageToHistoryEntry3 } from "@robota-sdk/agent-core";
153
160
 
154
161
  // src/ui/hooks/useInteractiveSession.ts
155
- import { useState, useRef, useCallback, useEffect } from "react";
162
+ import { useState, useRef, useCallback as useCallback2, useEffect } from "react";
156
163
  import { homedir as homedir2 } from "os";
157
164
  import { join as join3 } from "path";
158
165
  import {
@@ -161,11 +168,8 @@ import {
161
168
  BuiltinCommandSource,
162
169
  SkillCommandSource,
163
170
  PluginCommandSource,
164
- BundlePluginLoader,
165
- buildSkillPrompt
171
+ BundlePluginLoader
166
172
  } from "@robota-sdk/agent-sdk";
167
- import { createSystemMessage, messageToHistoryEntry } from "@robota-sdk/agent-core";
168
- import { randomUUID } from "crypto";
169
173
 
170
174
  // src/ui/tui-state-manager.ts
171
175
  var MAX_RENDERED_MESSAGES = 100;
@@ -293,6 +297,94 @@ var TuiStateManager = class {
293
297
  }
294
298
  };
295
299
 
300
+ // src/ui/hooks/useSlashRouting.ts
301
+ import { useCallback } from "react";
302
+ import { randomUUID } from "crypto";
303
+ import { buildSkillPrompt } from "@robota-sdk/agent-sdk";
304
+ import { createSystemMessage, messageToHistoryEntry } from "@robota-sdk/agent-core";
305
+ function useSlashRouting(interactiveSession, registry, manager) {
306
+ return useCallback(
307
+ async (input) => {
308
+ if (!input.startsWith("/")) {
309
+ await interactiveSession.submit(input);
310
+ manager.setPendingPrompt(interactiveSession.getPendingPrompt());
311
+ return;
312
+ }
313
+ const parts = input.slice(1).split(/\s+/);
314
+ const cmd = parts[0]?.toLowerCase() ?? "";
315
+ const args = parts.slice(1).join(" ");
316
+ const result = await interactiveSession.executeCommand(cmd, args);
317
+ if (result) {
318
+ manager.addEntry(messageToHistoryEntry(createSystemMessage(result.message)));
319
+ const effects = interactiveSession;
320
+ if (result.data?.modelId) {
321
+ effects._pendingModelId = result.data.modelId;
322
+ return;
323
+ }
324
+ if (result.data?.language) {
325
+ effects._pendingLanguage = result.data.language;
326
+ return;
327
+ }
328
+ if (result.data?.resetRequested) {
329
+ effects._resetRequested = true;
330
+ return;
331
+ }
332
+ if (result.data?.triggerResumePicker) {
333
+ effects._triggerResumePicker = true;
334
+ return;
335
+ }
336
+ if (result.data?.name) {
337
+ effects._sessionName = result.data.name;
338
+ return;
339
+ }
340
+ const ctx = interactiveSession.getContextState();
341
+ manager.setContextState({
342
+ percentage: ctx.usedPercentage,
343
+ usedTokens: ctx.usedTokens,
344
+ maxTokens: ctx.maxTokens
345
+ });
346
+ return;
347
+ }
348
+ const skillCmd = registry.getCommands().find((c) => c.name === cmd && (c.source === "skill" || c.source === "plugin"));
349
+ if (skillCmd) {
350
+ manager.addEntry({
351
+ id: randomUUID(),
352
+ timestamp: /* @__PURE__ */ new Date(),
353
+ category: "event",
354
+ type: "skill-invocation",
355
+ data: {
356
+ skillName: cmd,
357
+ source: skillCmd.source,
358
+ message: `Invoking ${skillCmd.source}: ${cmd}`
359
+ }
360
+ });
361
+ const prompt = await buildSkillPrompt(input, registry);
362
+ if (prompt) {
363
+ const qualifiedName = registry.resolveQualifiedName(cmd);
364
+ const hookInput = qualifiedName ? `/${qualifiedName}${input.slice(1 + cmd.length)}` : input;
365
+ await interactiveSession.submit(prompt, input, hookInput);
366
+ manager.setPendingPrompt(interactiveSession.getPendingPrompt());
367
+ return;
368
+ }
369
+ }
370
+ if (cmd === "exit") {
371
+ interactiveSession._exitRequested = true;
372
+ return;
373
+ }
374
+ if (cmd === "plugin") {
375
+ interactiveSession._triggerPluginTUI = true;
376
+ return;
377
+ }
378
+ manager.addEntry(
379
+ messageToHistoryEntry(
380
+ createSystemMessage(`Unknown command "/${cmd}". Type /help for help.`)
381
+ )
382
+ );
383
+ },
384
+ [interactiveSession, registry, manager]
385
+ );
386
+ }
387
+
296
388
  // src/ui/hooks/useInteractiveSession.ts
297
389
  function initializeSession(props, permissionHandler) {
298
390
  const interactiveSession = new InteractiveSession({
@@ -326,7 +418,7 @@ function useInteractiveSession(props) {
326
418
  const [permissionRequest, setPermissionRequest] = useState(null);
327
419
  const permissionQueueRef = useRef([]);
328
420
  const processingRef = useRef(false);
329
- const processNextPermission = useCallback(() => {
421
+ const processNextPermission = useCallback2(() => {
330
422
  if (processingRef.current) return;
331
423
  const next = permissionQueueRef.current[0];
332
424
  if (!next) {
@@ -346,7 +438,7 @@ function useInteractiveSession(props) {
346
438
  }
347
439
  });
348
440
  }, []);
349
- const permissionHandler = useCallback(
441
+ const permissionHandler = useCallback2(
350
442
  (toolName, toolArgs) => new Promise((resolve) => {
351
443
  permissionQueueRef.current.push({ toolName, toolArgs, resolve });
352
444
  processNextPermission();
@@ -406,91 +498,12 @@ function useInteractiveSession(props) {
406
498
  manager.setPendingPrompt(interactiveSession.getPendingPrompt());
407
499
  }
408
500
  }, [manager.isThinking, interactiveSession, manager]);
409
- const handleSubmit = useCallback(
410
- async (input) => {
411
- if (input.startsWith("/")) {
412
- const parts = input.slice(1).split(/\s+/);
413
- const cmd = parts[0]?.toLowerCase() ?? "";
414
- const args = parts.slice(1).join(" ");
415
- const result = await interactiveSession.executeCommand(cmd, args);
416
- if (result) {
417
- manager.addEntry(messageToHistoryEntry(createSystemMessage(result.message)));
418
- const effects = interactiveSession;
419
- if (result.data?.modelId) {
420
- effects._pendingModelId = result.data.modelId;
421
- return;
422
- }
423
- if (result.data?.language) {
424
- effects._pendingLanguage = result.data.language;
425
- return;
426
- }
427
- if (result.data?.resetRequested) {
428
- effects._resetRequested = true;
429
- return;
430
- }
431
- if (result.data?.triggerResumePicker) {
432
- effects._triggerResumePicker = true;
433
- return;
434
- }
435
- if (result.data?.name) {
436
- effects._sessionName = result.data.name;
437
- return;
438
- }
439
- const ctx = interactiveSession.getContextState();
440
- manager.setContextState({
441
- percentage: ctx.usedPercentage,
442
- usedTokens: ctx.usedTokens,
443
- maxTokens: ctx.maxTokens
444
- });
445
- return;
446
- }
447
- const skillCmd = registry.getCommands().find((c) => c.name === cmd && (c.source === "skill" || c.source === "plugin"));
448
- if (skillCmd) {
449
- manager.addEntry({
450
- id: randomUUID(),
451
- timestamp: /* @__PURE__ */ new Date(),
452
- category: "event",
453
- type: "skill-invocation",
454
- data: {
455
- skillName: cmd,
456
- source: skillCmd.source,
457
- message: `Invoking ${skillCmd.source}: ${cmd}`
458
- }
459
- });
460
- const prompt = await buildSkillPrompt(input, registry);
461
- if (prompt) {
462
- const qualifiedName = registry.resolveQualifiedName(cmd);
463
- const hookInput = qualifiedName ? `/${qualifiedName}${input.slice(1 + cmd.length)}` : input;
464
- await interactiveSession.submit(prompt, input, hookInput);
465
- manager.setPendingPrompt(interactiveSession.getPendingPrompt());
466
- return;
467
- }
468
- }
469
- if (cmd === "exit") {
470
- interactiveSession._exitRequested = true;
471
- return;
472
- }
473
- if (cmd === "plugin") {
474
- interactiveSession._triggerPluginTUI = true;
475
- return;
476
- }
477
- manager.addEntry(
478
- messageToHistoryEntry(
479
- createSystemMessage(`Unknown command "/${cmd}". Type /help for help.`)
480
- )
481
- );
482
- return;
483
- }
484
- await interactiveSession.submit(input);
485
- manager.setPendingPrompt(interactiveSession.getPendingPrompt());
486
- },
487
- [interactiveSession, registry, manager]
488
- );
489
- const handleAbort = useCallback(() => {
501
+ const handleSubmit = useSlashRouting(interactiveSession, registry, manager);
502
+ const handleAbort = useCallback2(() => {
490
503
  manager.setAborting(true);
491
504
  interactiveSession.abort();
492
505
  }, [interactiveSession, manager]);
493
- const handleCancelQueue = useCallback(() => {
506
+ const handleCancelQueue = useCallback2(() => {
494
507
  interactiveSession.cancelQueue();
495
508
  manager.setPendingPrompt(null);
496
509
  }, [interactiveSession, manager]);
@@ -621,8 +634,126 @@ function usePluginCallbacks(cwd) {
621
634
  }, [cwd]);
622
635
  }
623
636
 
637
+ // src/ui/hooks/useSideEffects.ts
638
+ import { useState as useState2, useRef as useRef2, useCallback as useCallback3 } from "react";
639
+ import { useApp } from "ink";
640
+ import { createSystemMessage as createSystemMessage2, messageToHistoryEntry as messageToHistoryEntry2, getModelName } from "@robota-sdk/agent-core";
641
+ var EXIT_DELAY_MS = 500;
642
+ function useSideEffects({
643
+ interactiveSession,
644
+ addEntry,
645
+ baseHandleSubmit,
646
+ setSessionName
647
+ }) {
648
+ const { exit } = useApp();
649
+ const [pendingModelId, setPendingModelId] = useState2(null);
650
+ const pendingModelChangeRef = useRef2(null);
651
+ const [showPluginTUI, setShowPluginTUI] = useState2(false);
652
+ const [showSessionPicker, setShowSessionPicker] = useState2(false);
653
+ const handleSubmit = useCallback3(
654
+ async (input) => {
655
+ await baseHandleSubmit(input);
656
+ const sideEffects = interactiveSession;
657
+ if (sideEffects._pendingModelId) {
658
+ const modelId = sideEffects._pendingModelId;
659
+ delete sideEffects._pendingModelId;
660
+ pendingModelChangeRef.current = modelId;
661
+ setPendingModelId(modelId);
662
+ return;
663
+ }
664
+ if (sideEffects._pendingLanguage) {
665
+ const lang = sideEffects._pendingLanguage;
666
+ delete sideEffects._pendingLanguage;
667
+ const settingsPath = getUserSettingsPath();
668
+ const settings = readSettings(settingsPath);
669
+ settings.language = lang;
670
+ writeSettings(settingsPath, settings);
671
+ addEntry(
672
+ messageToHistoryEntry2(createSystemMessage2(`Language set to "${lang}". Restarting...`))
673
+ );
674
+ setTimeout(() => exit(), EXIT_DELAY_MS);
675
+ return;
676
+ }
677
+ if (sideEffects._resetRequested) {
678
+ delete sideEffects._resetRequested;
679
+ const settingsPath = getUserSettingsPath();
680
+ if (deleteSettings(settingsPath)) {
681
+ addEntry(
682
+ messageToHistoryEntry2(createSystemMessage2(`Deleted ${settingsPath}. Exiting...`))
683
+ );
684
+ } else {
685
+ addEntry(messageToHistoryEntry2(createSystemMessage2("No user settings found.")));
686
+ }
687
+ setTimeout(() => exit(), EXIT_DELAY_MS);
688
+ return;
689
+ }
690
+ if (sideEffects._exitRequested) {
691
+ delete sideEffects._exitRequested;
692
+ setTimeout(() => exit(), EXIT_DELAY_MS);
693
+ return;
694
+ }
695
+ if (sideEffects._triggerPluginTUI) {
696
+ delete sideEffects._triggerPluginTUI;
697
+ setShowPluginTUI(true);
698
+ return;
699
+ }
700
+ if (sideEffects._triggerResumePicker) {
701
+ delete sideEffects._triggerResumePicker;
702
+ setShowSessionPicker(true);
703
+ return;
704
+ }
705
+ if (sideEffects._sessionName) {
706
+ const name = sideEffects._sessionName;
707
+ delete sideEffects._sessionName;
708
+ interactiveSession.setName(name);
709
+ setSessionName(name);
710
+ return;
711
+ }
712
+ },
713
+ [interactiveSession, baseHandleSubmit, addEntry, exit, setSessionName]
714
+ );
715
+ const handleModelConfirm = useCallback3(
716
+ (index) => {
717
+ const modelId = pendingModelChangeRef.current;
718
+ setPendingModelId(null);
719
+ pendingModelChangeRef.current = null;
720
+ if (index === 0 && modelId) {
721
+ try {
722
+ const settingsPath = getUserSettingsPath();
723
+ updateModelInSettings(settingsPath, modelId);
724
+ addEntry(
725
+ messageToHistoryEntry2(
726
+ createSystemMessage2(`Model changed to ${getModelName(modelId)}. Restarting...`)
727
+ )
728
+ );
729
+ setTimeout(() => exit(), EXIT_DELAY_MS);
730
+ } catch (err) {
731
+ addEntry(
732
+ messageToHistoryEntry2(
733
+ createSystemMessage2(`Failed: ${err instanceof Error ? err.message : String(err)}`)
734
+ )
735
+ );
736
+ }
737
+ } else {
738
+ addEntry(messageToHistoryEntry2(createSystemMessage2("Model change cancelled.")));
739
+ }
740
+ },
741
+ [addEntry, exit]
742
+ );
743
+ return {
744
+ handleSubmit,
745
+ pendingModelId,
746
+ showPluginTUI,
747
+ showSessionPicker,
748
+ setPendingModelId,
749
+ setShowPluginTUI,
750
+ setShowSessionPicker,
751
+ handleModelConfirm
752
+ };
753
+ }
754
+
624
755
  // src/ui/MessageList.tsx
625
- import React2 from "react";
756
+ import React from "react";
626
757
  import { Box as Box2, Text as Text2 } from "ink";
627
758
  import { isToolMessage, isAssistantMessage } from "@robota-sdk/agent-core";
628
759
 
@@ -770,7 +901,7 @@ function ToolMessage({ message }) {
770
901
  ] }, i))
771
902
  ] });
772
903
  }
773
- var MessageItem = React2.memo(function MessageItem2({
904
+ var MessageItem = React.memo(function MessageItem2({
774
905
  message
775
906
  }) {
776
907
  if (isToolMessage(message)) {
@@ -894,11 +1025,11 @@ function StatusBar({
894
1025
  }
895
1026
 
896
1027
  // src/ui/InputArea.tsx
897
- import React5, { useState as useState4, useCallback as useCallback2, useMemo as useMemo2, useRef as useRef3 } from "react";
1028
+ import { useState as useState6, useCallback as useCallback4, useRef as useRef4 } from "react";
898
1029
  import { Box as Box5, Text as Text7, useInput as useInput2, useStdout } from "ink";
899
1030
 
900
1031
  // src/ui/CjkTextInput.tsx
901
- import { useRef as useRef2, useState as useState2 } from "react";
1032
+ import { useRef as useRef3, useState as useState3 } from "react";
902
1033
  import { Text as Text4, useInput } from "ink";
903
1034
  import chalk from "chalk";
904
1035
  import stringWidth from "string-width";
@@ -945,11 +1076,11 @@ function CjkTextInput({
945
1076
  availableWidth,
946
1077
  cursorHint = null
947
1078
  }) {
948
- const valueRef = useRef2(value);
949
- const cursorRef = useRef2(value.length);
950
- const [, forceRender] = useState2(0);
951
- const isPastingRef = useRef2(false);
952
- const pasteBufferRef = useRef2("");
1079
+ const valueRef = useRef3(value);
1080
+ const cursorRef = useRef3(value.length);
1081
+ const [, forceRender] = useState3(0);
1082
+ const isPastingRef = useRef3(false);
1083
+ const pasteBufferRef = useRef3("");
953
1084
  if (value !== valueRef.current) {
954
1085
  valueRef.current = value;
955
1086
  cursorRef.current = cursorHint != null ? Math.min(cursorHint, value.length) : value.length;
@@ -1076,14 +1207,14 @@ function renderWithCursor(value, cursorOffset, placeholder, showCursor) {
1076
1207
  }
1077
1208
 
1078
1209
  // src/ui/WaveText.tsx
1079
- import { useState as useState3, useEffect as useEffect2 } from "react";
1210
+ import { useState as useState4, useEffect as useEffect2 } from "react";
1080
1211
  import { Text as Text5 } from "ink";
1081
1212
  import { jsx as jsx4 } from "react/jsx-runtime";
1082
1213
  var WAVE_COLORS = ["#666666", "#888888", "#aaaaaa", "#888888"];
1083
1214
  var INTERVAL_MS = 400;
1084
1215
  var CHARS_PER_GROUP = 4;
1085
1216
  function WaveText({ text }) {
1086
- const [tick, setTick] = useState3(0);
1217
+ const [tick, setTick] = useState4(0);
1087
1218
  useEffect2(() => {
1088
1219
  const timer = setInterval(() => {
1089
1220
  setTick((prev) => prev + 1);
@@ -1144,8 +1275,8 @@ function expandPasteLabels(text, store) {
1144
1275
  return text.replace(PASTE_LABEL_RE, (_, id) => store.get(Number(id)) ?? "");
1145
1276
  }
1146
1277
 
1147
- // src/ui/InputArea.tsx
1148
- import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
1278
+ // src/ui/hooks/useAutocomplete.ts
1279
+ import React4, { useState as useState5, useMemo as useMemo2 } from "react";
1149
1280
  function parseSlashInput(value) {
1150
1281
  if (!value.startsWith("/")) return { isSlash: false, parentCommand: "", filter: "" };
1151
1282
  const afterSlash = value.slice(1);
@@ -1156,9 +1287,9 @@ function parseSlashInput(value) {
1156
1287
  return { isSlash: true, parentCommand: parent, filter: rest };
1157
1288
  }
1158
1289
  function useAutocomplete(value, registry) {
1159
- const [selectedIndex, setSelectedIndex] = useState4(0);
1160
- const [dismissed, setDismissed] = useState4(false);
1161
- const prevValueRef = React5.useRef(value);
1290
+ const [selectedIndex, setSelectedIndex] = useState5(0);
1291
+ const [dismissed, setDismissed] = useState5(false);
1292
+ const prevValueRef = React4.useRef(value);
1162
1293
  if (prevValueRef.current !== value) {
1163
1294
  prevValueRef.current = value;
1164
1295
  if (dismissed) setDismissed(false);
@@ -1198,6 +1329,9 @@ function useAutocomplete(value, registry) {
1198
1329
  }
1199
1330
  };
1200
1331
  }
1332
+
1333
+ // src/ui/InputArea.tsx
1334
+ import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
1201
1335
  var BORDER_HORIZONTAL = 2;
1202
1336
  var PADDING_LEFT = 1;
1203
1337
  var PROMPT_WIDTH = 2;
@@ -1211,13 +1345,13 @@ function InputArea({
1211
1345
  registry,
1212
1346
  sessionName
1213
1347
  }) {
1214
- const [value, setValue] = useState4("");
1215
- const [cursorHint, setCursorHint] = useState4(null);
1216
- const pasteStore = useRef3(/* @__PURE__ */ new Map());
1348
+ const [value, setValue] = useState6("");
1349
+ const [cursorHint, setCursorHint] = useState6(null);
1350
+ const pasteStore = useRef4(/* @__PURE__ */ new Map());
1217
1351
  const { stdout } = useStdout();
1218
1352
  const terminalColumns = stdout?.columns ?? 80;
1219
1353
  const availableWidth = Math.max(1, terminalColumns - INPUT_AREA_OVERHEAD);
1220
- const pasteIdRef = useRef3(0);
1354
+ const pasteIdRef = useRef4(0);
1221
1355
  const {
1222
1356
  showPopup,
1223
1357
  filteredCommands,
@@ -1226,7 +1360,7 @@ function InputArea({
1226
1360
  isSubcommandMode,
1227
1361
  setShowPopup
1228
1362
  } = useAutocomplete(value, registry);
1229
- const handlePaste = useCallback2((text, cursorPosition) => {
1363
+ const handlePaste = useCallback4((text, cursorPosition) => {
1230
1364
  pasteIdRef.current += 1;
1231
1365
  const id = pasteIdRef.current;
1232
1366
  pasteStore.current.set(id, text);
@@ -1236,7 +1370,7 @@ function InputArea({
1236
1370
  setCursorHint(newCursorPos);
1237
1371
  setValue((prev) => prev.slice(0, cursorPosition) + label + prev.slice(cursorPosition));
1238
1372
  }, []);
1239
- const tabCompleteCommand = useCallback2(
1373
+ const tabCompleteCommand = useCallback4(
1240
1374
  (cmd) => {
1241
1375
  const parsed = parseSlashInput(value);
1242
1376
  if (parsed.parentCommand) {
@@ -1252,7 +1386,7 @@ function InputArea({
1252
1386
  },
1253
1387
  [value, setSelectedIndex]
1254
1388
  );
1255
- const enterSelectCommand = useCallback2(
1389
+ const enterSelectCommand = useCallback4(
1256
1390
  (cmd) => {
1257
1391
  const parsed = parseSlashInput(value);
1258
1392
  if (parsed.parentCommand) {
@@ -1271,7 +1405,7 @@ function InputArea({
1271
1405
  },
1272
1406
  [value, onSubmit, setSelectedIndex]
1273
1407
  );
1274
- const handleSubmit = useCallback2(
1408
+ const handleSubmit = useCallback4(
1275
1409
  (text) => {
1276
1410
  const trimmed = text.trim();
1277
1411
  if (trimmed.length === 0) return;
@@ -1365,7 +1499,7 @@ function InputArea({
1365
1499
  }
1366
1500
 
1367
1501
  // src/ui/ConfirmPrompt.tsx
1368
- import { useState as useState5, useCallback as useCallback3, useRef as useRef4 } from "react";
1502
+ import { useState as useState7, useCallback as useCallback5, useRef as useRef5 } from "react";
1369
1503
  import { Box as Box6, Text as Text8, useInput as useInput3 } from "ink";
1370
1504
  import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
1371
1505
  function ConfirmPrompt({
@@ -1373,9 +1507,9 @@ function ConfirmPrompt({
1373
1507
  options = ["Yes", "No"],
1374
1508
  onSelect
1375
1509
  }) {
1376
- const [selected, setSelected] = useState5(0);
1377
- const resolvedRef = useRef4(false);
1378
- const doSelect = useCallback3(
1510
+ const [selected, setSelected] = useState7(0);
1511
+ const resolvedRef = useRef5(false);
1512
+ const doSelect = useCallback5(
1379
1513
  (index) => {
1380
1514
  if (resolvedRef.current) return;
1381
1515
  resolvedRef.current = true;
@@ -1515,10 +1649,10 @@ function StreamingIndicator({ text, activeTools }) {
1515
1649
  }
1516
1650
 
1517
1651
  // src/ui/PluginTUI.tsx
1518
- import { useState as useState8, useEffect as useEffect3, useCallback as useCallback6 } from "react";
1652
+ import { useState as useState11, useCallback as useCallback8 } from "react";
1519
1653
 
1520
1654
  // src/ui/MenuSelect.tsx
1521
- import { useState as useState6, useCallback as useCallback4, useRef as useRef5 } from "react";
1655
+ import { useState as useState8, useCallback as useCallback6, useRef as useRef6 } from "react";
1522
1656
  import { Box as Box9, Text as Text11, useInput as useInput5 } from "ink";
1523
1657
  import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
1524
1658
  function MenuSelect({
@@ -1529,10 +1663,10 @@ function MenuSelect({
1529
1663
  loading,
1530
1664
  error
1531
1665
  }) {
1532
- const [selected, setSelected] = useState6(0);
1533
- const selectedRef = useRef5(0);
1534
- const resolvedRef = useRef5(false);
1535
- const doSelect = useCallback4(
1666
+ const [selected, setSelected] = useState8(0);
1667
+ const selectedRef = useRef6(0);
1668
+ const resolvedRef = useRef6(false);
1669
+ const doSelect = useCallback6(
1536
1670
  (index) => {
1537
1671
  if (resolvedRef.current || items.length === 0) return;
1538
1672
  resolvedRef.current = true;
@@ -1582,7 +1716,7 @@ function MenuSelect({
1582
1716
  }
1583
1717
 
1584
1718
  // src/ui/TextPrompt.tsx
1585
- import { useState as useState7, useRef as useRef6, useCallback as useCallback5 } from "react";
1719
+ import { useState as useState9, useRef as useRef7, useCallback as useCallback7 } from "react";
1586
1720
  import { Box as Box10, Text as Text12, useInput as useInput6 } from "ink";
1587
1721
  import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
1588
1722
  function TextPrompt({
@@ -1592,11 +1726,11 @@ function TextPrompt({
1592
1726
  onCancel,
1593
1727
  validate
1594
1728
  }) {
1595
- const [value, setValue] = useState7("");
1596
- const [error, setError] = useState7();
1597
- const resolvedRef = useRef6(false);
1598
- const valueRef = useRef6("");
1599
- const handleSubmit = useCallback5(() => {
1729
+ const [value, setValue] = useState9("");
1730
+ const [error, setError] = useState9();
1731
+ const resolvedRef = useRef7(false);
1732
+ const valueRef = useRef7("");
1733
+ const handleSubmit = useCallback7(() => {
1600
1734
  if (resolvedRef.current) return;
1601
1735
  const trimmed = valueRef.current.trim();
1602
1736
  if (!trimmed) return;
@@ -1737,61 +1871,16 @@ function handleInstalledActionSelect(value, pluginId, callbacks, nav) {
1737
1871
  }
1738
1872
  }
1739
1873
 
1740
- // src/ui/PluginTUI.tsx
1741
- import { jsx as jsx12 } from "react/jsx-runtime";
1742
- function PluginTUI({ callbacks, onClose, addMessage }) {
1743
- const [stack, setStack] = useState8([{ screen: "main" }]);
1744
- const [items, setItems] = useState8([]);
1745
- const [loading, setLoading] = useState8(false);
1746
- const [error, setError] = useState8();
1747
- const [confirm, setConfirm] = useState8();
1748
- const [refreshCounter, setRefreshCounter] = useState8(0);
1749
- const current = stack[stack.length - 1] ?? { screen: "main" };
1750
- const push = useCallback6((state) => {
1751
- setStack((prev) => [...prev, state]);
1752
- setItems([]);
1753
- setError(void 0);
1754
- }, []);
1755
- const pop = useCallback6(() => {
1756
- setStack((prev) => {
1757
- if (prev.length <= 1) {
1758
- onClose();
1759
- return prev;
1760
- }
1761
- return prev.slice(0, -1);
1762
- });
1874
+ // src/ui/hooks/usePluginScreenData.ts
1875
+ import { useState as useState10, useEffect as useEffect3 } from "react";
1876
+ function usePluginScreenData(screen, marketplace, callbacks, refreshCounter, stackLength) {
1877
+ const [items, setItems] = useState10([]);
1878
+ const [loading, setLoading] = useState10(false);
1879
+ const [error, setError] = useState10();
1880
+ useEffect3(() => {
1763
1881
  setItems([]);
1764
1882
  setError(void 0);
1765
- }, [onClose]);
1766
- const popN = useCallback6(
1767
- (n) => {
1768
- setStack((prev) => {
1769
- const next = prev.slice(0, Math.max(1, prev.length - n));
1770
- if (next.length === 0) {
1771
- onClose();
1772
- return prev;
1773
- }
1774
- return next;
1775
- });
1776
- setItems([]);
1777
- setError(void 0);
1778
- },
1779
- [onClose]
1780
- );
1781
- const notify = useCallback6(
1782
- (content) => {
1783
- addMessage?.({ role: "system", content });
1784
- },
1785
- [addMessage]
1786
- );
1787
- const refresh = useCallback6(() => {
1788
- setItems([]);
1789
- setRefreshCounter((c) => c + 1);
1790
- }, []);
1791
- const nav = { push, pop, popN, notify, setConfirm, refresh };
1792
- useEffect3(() => {
1793
- const screen2 = current.screen;
1794
- if (screen2 === "marketplace-list") {
1883
+ if (screen === "marketplace-list") {
1795
1884
  setLoading(true);
1796
1885
  callbacks.marketplaceList().then((sources) => {
1797
1886
  const baseItems = [{ label: "Add Marketplace", value: "__add__" }];
@@ -1806,10 +1895,10 @@ function PluginTUI({ callbacks, onClose, addMessage }) {
1806
1895
  setError(err instanceof Error ? err.message : String(err));
1807
1896
  setLoading(false);
1808
1897
  });
1809
- } else if (screen2 === "marketplace-browse") {
1810
- const marketplace = current.context?.marketplace ?? "";
1898
+ } else if (screen === "marketplace-browse") {
1899
+ const mp = marketplace ?? "";
1811
1900
  setLoading(true);
1812
- callbacks.listAvailablePlugins(marketplace).then((plugins) => {
1901
+ callbacks.listAvailablePlugins(mp).then((plugins) => {
1813
1902
  setItems(
1814
1903
  plugins.map((p) => ({
1815
1904
  label: p.name,
@@ -1822,7 +1911,7 @@ function PluginTUI({ callbacks, onClose, addMessage }) {
1822
1911
  setError(err instanceof Error ? err.message : String(err));
1823
1912
  setLoading(false);
1824
1913
  });
1825
- } else if (screen2 === "installed-list") {
1914
+ } else if (screen === "installed-list") {
1826
1915
  setLoading(true);
1827
1916
  callbacks.listInstalled().then((plugins) => {
1828
1917
  setItems(
@@ -1838,8 +1927,68 @@ function PluginTUI({ callbacks, onClose, addMessage }) {
1838
1927
  setLoading(false);
1839
1928
  });
1840
1929
  }
1841
- }, [stack.length, current.screen, current.context?.marketplace, callbacks, refreshCounter]);
1842
- const handleSelect = useCallback6(
1930
+ }, [stackLength, screen, marketplace, callbacks, refreshCounter]);
1931
+ return { items, loading, error };
1932
+ }
1933
+
1934
+ // src/ui/PluginTUI.tsx
1935
+ import { jsx as jsx12 } from "react/jsx-runtime";
1936
+ function PluginTUI({ callbacks, onClose, addMessage }) {
1937
+ const [stack, setStack] = useState11([{ screen: "main" }]);
1938
+ const [confirm, setConfirm] = useState11();
1939
+ const [refreshCounter, setRefreshCounter] = useState11(0);
1940
+ const current = stack[stack.length - 1] ?? { screen: "main" };
1941
+ const push = useCallback8((state) => {
1942
+ setStack((prev) => [...prev, state]);
1943
+ }, []);
1944
+ const pop = useCallback8(() => {
1945
+ setStack((prev) => {
1946
+ if (prev.length <= 1) {
1947
+ onClose();
1948
+ return prev;
1949
+ }
1950
+ return prev.slice(0, -1);
1951
+ });
1952
+ }, [onClose]);
1953
+ const popN = useCallback8(
1954
+ (n) => {
1955
+ setStack((prev) => {
1956
+ const next = prev.slice(0, Math.max(1, prev.length - n));
1957
+ if (next.length === 0) {
1958
+ onClose();
1959
+ return prev;
1960
+ }
1961
+ return next;
1962
+ });
1963
+ },
1964
+ [onClose]
1965
+ );
1966
+ const notify = useCallback8(
1967
+ (content) => {
1968
+ addMessage?.({ role: "system", content });
1969
+ },
1970
+ [addMessage]
1971
+ );
1972
+ const refresh = useCallback8(() => {
1973
+ setRefreshCounter((c) => c + 1);
1974
+ }, []);
1975
+ const setConfirmNav = useCallback8(
1976
+ (state) => setConfirm(state),
1977
+ [setConfirm]
1978
+ );
1979
+ const pushNav = useCallback8(
1980
+ (state) => push({ screen: state.screen, context: state.context }),
1981
+ [push]
1982
+ );
1983
+ const nav = { push: pushNav, pop, popN, notify, setConfirm: setConfirmNav, refresh };
1984
+ const { items, loading, error } = usePluginScreenData(
1985
+ current.screen,
1986
+ current.context?.marketplace,
1987
+ callbacks,
1988
+ refreshCounter,
1989
+ stack.length
1990
+ );
1991
+ const handleSelect = useCallback8(
1843
1992
  (value) => {
1844
1993
  const screen2 = current.screen;
1845
1994
  const ctx = current.context;
@@ -1857,7 +2006,7 @@ function PluginTUI({ callbacks, onClose, addMessage }) {
1857
2006
  },
1858
2007
  [current, items, callbacks, push, pop, popN, notify, setConfirm, refresh]
1859
2008
  );
1860
- const handleTextSubmit = useCallback6(
2009
+ const handleTextSubmit = useCallback8(
1861
2010
  (value) => {
1862
2011
  if (current.screen === "marketplace-add") {
1863
2012
  callbacks.marketplaceAdd(value).then((name) => {
@@ -1965,8 +2114,11 @@ function PluginTUI({ callbacks, onClose, addMessage }) {
1965
2114
  );
1966
2115
  }
1967
2116
 
2117
+ // src/ui/SessionPicker.tsx
2118
+ import { Box as Box12, Text as Text14 } from "ink";
2119
+
1968
2120
  // src/ui/ListPicker.tsx
1969
- import { useState as useState9, useRef as useRef7 } from "react";
2121
+ import { useState as useState12, useRef as useRef8 } from "react";
1970
2122
  import { Box as Box11, Text as Text13, useInput as useInput7 } from "ink";
1971
2123
  import { jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
1972
2124
  var DEFAULT_MAX_VISIBLE = 3;
@@ -1977,10 +2129,10 @@ function ListPicker({
1977
2129
  onCancel,
1978
2130
  maxVisible = DEFAULT_MAX_VISIBLE
1979
2131
  }) {
1980
- const [selectedIndex, setSelectedIndex] = useState9(0);
1981
- const [scrollOffset, setScrollOffset] = useState9(0);
1982
- const selectedRef = useRef7(0);
1983
- const resolvedRef = useRef7(false);
2132
+ const [selectedIndex, setSelectedIndex] = useState12(0);
2133
+ const [scrollOffset, setScrollOffset] = useState12(0);
2134
+ const selectedRef = useRef8(0);
2135
+ const resolvedRef = useRef8(false);
1984
2136
  useInput7((_input, key) => {
1985
2137
  if (resolvedRef.current) return;
1986
2138
  if (key.escape) {
@@ -2032,13 +2184,62 @@ function ListPicker({
2032
2184
  ] });
2033
2185
  }
2034
2186
 
2035
- // src/ui/App.tsx
2187
+ // src/ui/SessionPicker.tsx
2036
2188
  import { Fragment as Fragment4, jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
2037
- var EXIT_DELAY_MS = 500;
2038
2189
  var SESSION_ID_DISPLAY_LENGTH = 8;
2190
+ function SessionPicker({
2191
+ sessionStore,
2192
+ cwd,
2193
+ onSelect,
2194
+ onCancel
2195
+ }) {
2196
+ const sessions = (sessionStore?.list() ?? []).filter((s) => s.cwd === cwd);
2197
+ return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", paddingX: 1, marginBottom: 1, children: [
2198
+ /* @__PURE__ */ jsx14(Text14, { bold: true, color: "cyan", children: "Select a session to resume (ESC to cancel):" }),
2199
+ /* @__PURE__ */ jsx14(
2200
+ ListPicker,
2201
+ {
2202
+ items: sessions,
2203
+ renderItem: (session, isSelected) => {
2204
+ const lastMsg = session.messages.slice().reverse().find((m) => {
2205
+ const msg = m;
2206
+ return msg.role === "assistant" && msg.content;
2207
+ });
2208
+ const rawPreview = lastMsg?.content?.replace(/[\n\r]+/g, " ").trim() ?? "";
2209
+ const preview = rawPreview ? rawPreview.slice(0, 60) + (rawPreview.length > 60 ? "..." : "") : "";
2210
+ return /* @__PURE__ */ jsxs12(Text14, { children: [
2211
+ isSelected ? "> " : " ",
2212
+ /* @__PURE__ */ jsx14(Text14, { bold: true, children: session.name ?? session.id.slice(0, SESSION_ID_DISPLAY_LENGTH) }),
2213
+ " ",
2214
+ /* @__PURE__ */ jsx14(Text14, { dimColor: true, children: new Date(session.updatedAt).toLocaleString(void 0, {
2215
+ month: "short",
2216
+ day: "numeric",
2217
+ hour: "2-digit",
2218
+ minute: "2-digit"
2219
+ }) }),
2220
+ " ",
2221
+ /* @__PURE__ */ jsxs12(Text14, { dimColor: true, children: [
2222
+ "msgs: ",
2223
+ session.messages.length
2224
+ ] }),
2225
+ preview ? /* @__PURE__ */ jsxs12(Fragment4, { children: [
2226
+ "\n ",
2227
+ /* @__PURE__ */ jsx14(Text14, { color: "gray", children: preview })
2228
+ ] }) : null
2229
+ ] });
2230
+ },
2231
+ onSelect: (session) => onSelect(session.id),
2232
+ onCancel
2233
+ }
2234
+ )
2235
+ ] });
2236
+ }
2237
+
2238
+ // src/ui/App.tsx
2239
+ import { jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
2039
2240
  function App(props) {
2040
- const [activeSessionId, setActiveSessionId] = useState10(props.resumeSessionId);
2041
- return /* @__PURE__ */ jsx14(
2241
+ const [activeSessionId, setActiveSessionId] = useState13(props.resumeSessionId);
2242
+ return /* @__PURE__ */ jsx15(
2042
2243
  AppInner,
2043
2244
  {
2044
2245
  ...props,
@@ -2049,7 +2250,6 @@ function App(props) {
2049
2250
  );
2050
2251
  }
2051
2252
  function AppInner(props) {
2052
- const { exit } = useApp();
2053
2253
  const cwd = props.cwd;
2054
2254
  const {
2055
2255
  interactiveSession,
@@ -2077,13 +2277,21 @@ function AppInner(props) {
2077
2277
  sessionName: props.sessionName
2078
2278
  });
2079
2279
  const pluginCallbacks = usePluginCallbacks(cwd);
2080
- const [pendingModelId, setPendingModelId] = useState10(null);
2081
- const pendingModelChangeRef = useRef8(null);
2082
- const [showPluginTUI, setShowPluginTUI] = useState10(false);
2083
- const [showSessionPicker, setShowSessionPicker] = useState10(
2084
- props.resumeSessionId === "__picker__"
2085
- );
2086
- const [sessionName, setSessionName] = useState10(props.sessionName);
2280
+ const [sessionName, setSessionName] = useState13(props.sessionName);
2281
+ const {
2282
+ handleSubmit,
2283
+ pendingModelId,
2284
+ showPluginTUI,
2285
+ showSessionPicker,
2286
+ setShowPluginTUI,
2287
+ setShowSessionPicker,
2288
+ handleModelConfirm
2289
+ } = useSideEffects({
2290
+ interactiveSession,
2291
+ addEntry,
2292
+ baseHandleSubmit,
2293
+ setSessionName
2294
+ });
2087
2295
  useEffect4(() => {
2088
2296
  const name = interactiveSession?.getName?.();
2089
2297
  if (name && !sessionName) setSessionName(name);
@@ -2092,68 +2300,9 @@ function AppInner(props) {
2092
2300
  const title = sessionName ? `Robota \u2014 ${sessionName}` : "Robota";
2093
2301
  process.stdout.write(`\x1B]0;${title}\x07`);
2094
2302
  }, [sessionName]);
2095
- const handleSubmit = async (input) => {
2096
- await baseHandleSubmit(input);
2097
- const sideEffects = interactiveSession;
2098
- if (sideEffects._pendingModelId) {
2099
- const modelId = sideEffects._pendingModelId;
2100
- delete sideEffects._pendingModelId;
2101
- pendingModelChangeRef.current = modelId;
2102
- setPendingModelId(modelId);
2103
- return;
2104
- }
2105
- if (sideEffects._pendingLanguage) {
2106
- const lang = sideEffects._pendingLanguage;
2107
- delete sideEffects._pendingLanguage;
2108
- const settingsPath = getUserSettingsPath();
2109
- const settings = readSettings(settingsPath);
2110
- settings.language = lang;
2111
- writeSettings(settingsPath, settings);
2112
- addEntry(
2113
- messageToHistoryEntry2(createSystemMessage2(`Language set to "${lang}". Restarting...`))
2114
- );
2115
- setTimeout(() => exit(), EXIT_DELAY_MS);
2116
- return;
2117
- }
2118
- if (sideEffects._resetRequested) {
2119
- delete sideEffects._resetRequested;
2120
- const settingsPath = getUserSettingsPath();
2121
- if (deleteSettings(settingsPath)) {
2122
- addEntry(messageToHistoryEntry2(createSystemMessage2(`Deleted ${settingsPath}. Exiting...`)));
2123
- } else {
2124
- addEntry(messageToHistoryEntry2(createSystemMessage2("No user settings found.")));
2125
- }
2126
- setTimeout(() => exit(), EXIT_DELAY_MS);
2127
- return;
2128
- }
2129
- if (sideEffects._exitRequested) {
2130
- delete sideEffects._exitRequested;
2131
- setTimeout(() => exit(), EXIT_DELAY_MS);
2132
- return;
2133
- }
2134
- if (sideEffects._triggerPluginTUI) {
2135
- delete sideEffects._triggerPluginTUI;
2136
- setShowPluginTUI(true);
2137
- return;
2138
- }
2139
- if (sideEffects._triggerResumePicker) {
2140
- delete sideEffects._triggerResumePicker;
2141
- setShowSessionPicker(true);
2142
- return;
2143
- }
2144
- if (sideEffects._sessionName) {
2145
- const name = sideEffects._sessionName;
2146
- delete sideEffects._sessionName;
2147
- interactiveSession.setName(name);
2148
- setSessionName(name);
2149
- return;
2150
- }
2151
- };
2152
2303
  useInput8(
2153
2304
  (_input, key) => {
2154
- if (key.escape && isThinking) {
2155
- handleAbort();
2156
- }
2305
+ if (key.escape && isThinking) handleAbort();
2157
2306
  },
2158
2307
  { isActive: !permissionRequest && !showPluginTUI && !showSessionPicker }
2159
2308
  );
@@ -2165,117 +2314,60 @@ function AppInner(props) {
2165
2314
  sessionId = session.getSessionId();
2166
2315
  } catch {
2167
2316
  }
2168
- return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", children: [
2169
- /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", paddingX: 1, marginBottom: 1, children: [
2170
- /* @__PURE__ */ jsx14(Text14, { color: "cyan", bold: true, children: `
2317
+ return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", children: [
2318
+ /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", paddingX: 1, marginBottom: 1, children: [
2319
+ /* @__PURE__ */ jsx15(Text15, { color: "cyan", bold: true, children: `
2171
2320
  ____ ___ ____ ___ _____ _
2172
2321
  | _ \\ / _ \\| __ ) / _ \\_ _|/ \\
2173
2322
  | |_) | | | | _ \\| | | || | / _ \\
2174
2323
  | _ <| |_| | |_) | |_| || |/ ___ \\
2175
2324
  |_| \\_\\\\___/|____/ \\___/ |_/_/ \\_\\
2176
2325
  ` }),
2177
- /* @__PURE__ */ jsxs12(Text14, { dimColor: true, children: [
2326
+ /* @__PURE__ */ jsxs13(Text15, { dimColor: true, children: [
2178
2327
  " v",
2179
2328
  props.version ?? "0.0.0"
2180
2329
  ] })
2181
2330
  ] }),
2182
- /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", paddingX: 1, flexGrow: 1, children: [
2183
- /* @__PURE__ */ jsx14(MessageList, { history }),
2184
- (isThinking || activeTools.length > 0) && /* @__PURE__ */ jsx14(Box12, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ jsx14(StreamingIndicator, { text: streamingText, activeTools }) })
2331
+ /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", paddingX: 1, flexGrow: 1, children: [
2332
+ /* @__PURE__ */ jsx15(MessageList, { history }),
2333
+ (isThinking || activeTools.length > 0) && /* @__PURE__ */ jsx15(Box13, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ jsx15(StreamingIndicator, { text: streamingText, activeTools }) })
2185
2334
  ] }),
2186
- permissionRequest && /* @__PURE__ */ jsx14(PermissionPrompt, { request: permissionRequest }),
2187
- pendingModelId && /* @__PURE__ */ jsx14(
2335
+ permissionRequest && /* @__PURE__ */ jsx15(PermissionPrompt, { request: permissionRequest }),
2336
+ pendingModelId && /* @__PURE__ */ jsx15(
2188
2337
  ConfirmPrompt,
2189
2338
  {
2190
- message: `Change model to ${getModelName(pendingModelId)}? This will restart the session.`,
2191
- onSelect: (index) => {
2192
- setPendingModelId(null);
2193
- pendingModelChangeRef.current = null;
2194
- if (index === 0) {
2195
- try {
2196
- const settingsPath = getUserSettingsPath();
2197
- updateModelInSettings(settingsPath, pendingModelId);
2198
- addEntry(
2199
- messageToHistoryEntry2(
2200
- createSystemMessage2(
2201
- `Model changed to ${getModelName(pendingModelId)}. Restarting...`
2202
- )
2203
- )
2204
- );
2205
- setTimeout(() => exit(), EXIT_DELAY_MS);
2206
- } catch (err) {
2207
- addEntry(
2208
- messageToHistoryEntry2(
2209
- createSystemMessage2(
2210
- `Failed: ${err instanceof Error ? err.message : String(err)}`
2211
- )
2212
- )
2213
- );
2214
- }
2215
- } else {
2216
- addEntry(messageToHistoryEntry2(createSystemMessage2("Model change cancelled.")));
2217
- }
2218
- }
2339
+ message: `Change model to ${getModelName2(pendingModelId)}? This will restart the session.`,
2340
+ onSelect: handleModelConfirm
2219
2341
  }
2220
2342
  ),
2221
- showPluginTUI && /* @__PURE__ */ jsx14(
2343
+ showPluginTUI && /* @__PURE__ */ jsx15(
2222
2344
  PluginTUI,
2223
2345
  {
2224
2346
  callbacks: pluginCallbacks,
2225
2347
  onClose: () => setShowPluginTUI(false),
2226
- addMessage: (msg) => addEntry(messageToHistoryEntry2(createSystemMessage2(msg.content)))
2348
+ addMessage: (msg) => addEntry(messageToHistoryEntry3(createSystemMessage3(msg.content)))
2227
2349
  }
2228
2350
  ),
2229
- showSessionPicker && /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", paddingX: 1, marginBottom: 1, children: [
2230
- /* @__PURE__ */ jsx14(Text14, { bold: true, color: "cyan", children: "Select a session to resume (ESC to cancel):" }),
2231
- /* @__PURE__ */ jsx14(
2232
- ListPicker,
2233
- {
2234
- items: (props.sessionStore?.list() ?? []).filter((s) => s.cwd === props.cwd),
2235
- renderItem: (session, isSelected) => {
2236
- const lastMsg = session.messages.slice().reverse().find((m) => {
2237
- const msg = m;
2238
- return msg.role === "assistant" && msg.content;
2239
- });
2240
- const rawPreview = lastMsg?.content?.replace(/[\n\r]+/g, " ").trim() ?? "";
2241
- const preview = rawPreview ? rawPreview.slice(0, 60) + (rawPreview.length > 60 ? "..." : "") : "";
2242
- return /* @__PURE__ */ jsxs12(Text14, { children: [
2243
- isSelected ? "> " : " ",
2244
- /* @__PURE__ */ jsx14(Text14, { bold: true, children: session.name ?? session.id.slice(0, SESSION_ID_DISPLAY_LENGTH) }),
2245
- " ",
2246
- /* @__PURE__ */ jsx14(Text14, { dimColor: true, children: new Date(session.updatedAt).toLocaleString(void 0, {
2247
- month: "short",
2248
- day: "numeric",
2249
- hour: "2-digit",
2250
- minute: "2-digit"
2251
- }) }),
2252
- " ",
2253
- /* @__PURE__ */ jsxs12(Text14, { dimColor: true, children: [
2254
- "msgs: ",
2255
- session.messages.length
2256
- ] }),
2257
- preview ? /* @__PURE__ */ jsxs12(Fragment4, { children: [
2258
- "\n ",
2259
- /* @__PURE__ */ jsx14(Text14, { color: "gray", children: preview })
2260
- ] }) : null
2261
- ] });
2262
- },
2263
- onSelect: (session) => {
2264
- setShowSessionPicker(false);
2265
- props.onSessionSwitch(session.id);
2266
- },
2267
- onCancel: () => {
2268
- setShowSessionPicker(false);
2269
- addEntry(messageToHistoryEntry2(createSystemMessage2("Session resume cancelled.")));
2270
- }
2351
+ showSessionPicker && /* @__PURE__ */ jsx15(
2352
+ SessionPicker,
2353
+ {
2354
+ sessionStore: props.sessionStore,
2355
+ cwd: props.cwd,
2356
+ onSelect: (id) => {
2357
+ setShowSessionPicker(false);
2358
+ props.onSessionSwitch(id);
2359
+ },
2360
+ onCancel: () => {
2361
+ setShowSessionPicker(false);
2362
+ addEntry(messageToHistoryEntry3(createSystemMessage3("Session resume cancelled.")));
2271
2363
  }
2272
- )
2273
- ] }),
2274
- /* @__PURE__ */ jsx14(
2364
+ }
2365
+ ),
2366
+ /* @__PURE__ */ jsx15(
2275
2367
  StatusBar,
2276
2368
  {
2277
2369
  permissionMode,
2278
- modelName: props.modelId ? getModelName(props.modelId) : "",
2370
+ modelName: props.modelId ? getModelName2(props.modelId) : "",
2279
2371
  sessionId,
2280
2372
  messageCount: history.length,
2281
2373
  isThinking,
@@ -2285,7 +2377,7 @@ function AppInner(props) {
2285
2377
  sessionName
2286
2378
  }
2287
2379
  ),
2288
- /* @__PURE__ */ jsx14(
2380
+ /* @__PURE__ */ jsx15(
2289
2381
  InputArea,
2290
2382
  {
2291
2383
  onSubmit: handleSubmit,
@@ -2297,12 +2389,12 @@ function AppInner(props) {
2297
2389
  sessionName
2298
2390
  }
2299
2391
  ),
2300
- /* @__PURE__ */ jsx14(Text14, { children: " " })
2392
+ /* @__PURE__ */ jsx15(Text15, { children: " " })
2301
2393
  ] });
2302
2394
  }
2303
2395
 
2304
2396
  // src/ui/render.tsx
2305
- import { jsx as jsx15 } from "react/jsx-runtime";
2397
+ import { jsx as jsx16 } from "react/jsx-runtime";
2306
2398
  function renderApp(options) {
2307
2399
  process.on("unhandledRejection", (reason) => {
2308
2400
  process.stderr.write(`
@@ -2316,7 +2408,7 @@ function renderApp(options) {
2316
2408
  if (process.stdin.isTTY && process.stdout.isTTY) {
2317
2409
  process.stdout.write("\x1B[?2004h");
2318
2410
  }
2319
- const instance = render(/* @__PURE__ */ jsx15(App, { ...options }), {
2411
+ const instance = render(/* @__PURE__ */ jsx16(App, { ...options }), {
2320
2412
  exitOnCtrlC: true
2321
2413
  });
2322
2414
  instance.waitUntilExit().then(() => {