@robota-sdk/agent-cli 3.0.0-beta.51 → 3.0.0-beta.53
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.
- package/dist/node/bin.js +1 -1
- package/dist/node/{chunk-7EBJI2DW.js → chunk-CXBD4JNS.js} +458 -366
- package/dist/node/index.cjs +621 -528
- package/dist/node/index.js +1 -1
- package/package.json +6 -6
|
@@ -75,7 +75,14 @@ function getUserSettingsPath() {
|
|
|
75
75
|
}
|
|
76
76
|
function readSettings(path) {
|
|
77
77
|
if (!existsSync(path)) return {};
|
|
78
|
-
|
|
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
|
|
151
|
-
import { Box as
|
|
152
|
-
import { getModelName, createSystemMessage as
|
|
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 =
|
|
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 =
|
|
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 =
|
|
410
|
-
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
|
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
|
|
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";
|
|
@@ -942,16 +1073,17 @@ function CjkTextInput({
|
|
|
942
1073
|
placeholder = "",
|
|
943
1074
|
focus = true,
|
|
944
1075
|
showCursor = true,
|
|
945
|
-
availableWidth
|
|
1076
|
+
availableWidth,
|
|
1077
|
+
cursorHint = null
|
|
946
1078
|
}) {
|
|
947
|
-
const valueRef =
|
|
948
|
-
const cursorRef =
|
|
949
|
-
const [, forceRender] =
|
|
950
|
-
const isPastingRef =
|
|
951
|
-
const pasteBufferRef =
|
|
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("");
|
|
952
1084
|
if (value !== valueRef.current) {
|
|
953
1085
|
valueRef.current = value;
|
|
954
|
-
cursorRef.current = value.length;
|
|
1086
|
+
cursorRef.current = cursorHint != null ? Math.min(cursorHint, value.length) : value.length;
|
|
955
1087
|
}
|
|
956
1088
|
useInput(
|
|
957
1089
|
(input, key) => {
|
|
@@ -973,7 +1105,7 @@ function CjkTextInput({
|
|
|
973
1105
|
isPastingRef.current = false;
|
|
974
1106
|
if (text.length > 0) {
|
|
975
1107
|
if (text.includes("\n") && onPaste) {
|
|
976
|
-
onPaste(text);
|
|
1108
|
+
onPaste(text, cursorRef.current);
|
|
977
1109
|
} else {
|
|
978
1110
|
const printable2 = filterPrintable(text);
|
|
979
1111
|
if (printable2.length > 0) {
|
|
@@ -1012,7 +1144,7 @@ function CjkTextInput({
|
|
|
1012
1144
|
return;
|
|
1013
1145
|
}
|
|
1014
1146
|
if (input.length > 1 && (input.includes("\n") || input.includes("\r")) && onPaste) {
|
|
1015
|
-
onPaste(input.replace(/\r\n?/g, "\n"));
|
|
1147
|
+
onPaste(input.replace(/\r\n?/g, "\n"), cursorRef.current);
|
|
1016
1148
|
return;
|
|
1017
1149
|
}
|
|
1018
1150
|
if (key.leftArrow) {
|
|
@@ -1075,14 +1207,14 @@ function renderWithCursor(value, cursorOffset, placeholder, showCursor) {
|
|
|
1075
1207
|
}
|
|
1076
1208
|
|
|
1077
1209
|
// src/ui/WaveText.tsx
|
|
1078
|
-
import { useState as
|
|
1210
|
+
import { useState as useState4, useEffect as useEffect2 } from "react";
|
|
1079
1211
|
import { Text as Text5 } from "ink";
|
|
1080
1212
|
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
1081
1213
|
var WAVE_COLORS = ["#666666", "#888888", "#aaaaaa", "#888888"];
|
|
1082
1214
|
var INTERVAL_MS = 400;
|
|
1083
1215
|
var CHARS_PER_GROUP = 4;
|
|
1084
1216
|
function WaveText({ text }) {
|
|
1085
|
-
const [tick, setTick] =
|
|
1217
|
+
const [tick, setTick] = useState4(0);
|
|
1086
1218
|
useEffect2(() => {
|
|
1087
1219
|
const timer = setInterval(() => {
|
|
1088
1220
|
setTick((prev) => prev + 1);
|
|
@@ -1143,8 +1275,8 @@ function expandPasteLabels(text, store) {
|
|
|
1143
1275
|
return text.replace(PASTE_LABEL_RE, (_, id) => store.get(Number(id)) ?? "");
|
|
1144
1276
|
}
|
|
1145
1277
|
|
|
1146
|
-
// src/ui/
|
|
1147
|
-
import {
|
|
1278
|
+
// src/ui/hooks/useAutocomplete.ts
|
|
1279
|
+
import React4, { useState as useState5, useMemo as useMemo2 } from "react";
|
|
1148
1280
|
function parseSlashInput(value) {
|
|
1149
1281
|
if (!value.startsWith("/")) return { isSlash: false, parentCommand: "", filter: "" };
|
|
1150
1282
|
const afterSlash = value.slice(1);
|
|
@@ -1155,9 +1287,9 @@ function parseSlashInput(value) {
|
|
|
1155
1287
|
return { isSlash: true, parentCommand: parent, filter: rest };
|
|
1156
1288
|
}
|
|
1157
1289
|
function useAutocomplete(value, registry) {
|
|
1158
|
-
const [selectedIndex, setSelectedIndex] =
|
|
1159
|
-
const [dismissed, setDismissed] =
|
|
1160
|
-
const prevValueRef =
|
|
1290
|
+
const [selectedIndex, setSelectedIndex] = useState5(0);
|
|
1291
|
+
const [dismissed, setDismissed] = useState5(false);
|
|
1292
|
+
const prevValueRef = React4.useRef(value);
|
|
1161
1293
|
if (prevValueRef.current !== value) {
|
|
1162
1294
|
prevValueRef.current = value;
|
|
1163
1295
|
if (dismissed) setDismissed(false);
|
|
@@ -1197,6 +1329,9 @@ function useAutocomplete(value, registry) {
|
|
|
1197
1329
|
}
|
|
1198
1330
|
};
|
|
1199
1331
|
}
|
|
1332
|
+
|
|
1333
|
+
// src/ui/InputArea.tsx
|
|
1334
|
+
import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1200
1335
|
var BORDER_HORIZONTAL = 2;
|
|
1201
1336
|
var PADDING_LEFT = 1;
|
|
1202
1337
|
var PROMPT_WIDTH = 2;
|
|
@@ -1210,12 +1345,13 @@ function InputArea({
|
|
|
1210
1345
|
registry,
|
|
1211
1346
|
sessionName
|
|
1212
1347
|
}) {
|
|
1213
|
-
const [value, setValue] =
|
|
1214
|
-
const
|
|
1348
|
+
const [value, setValue] = useState6("");
|
|
1349
|
+
const [cursorHint, setCursorHint] = useState6(null);
|
|
1350
|
+
const pasteStore = useRef4(/* @__PURE__ */ new Map());
|
|
1215
1351
|
const { stdout } = useStdout();
|
|
1216
1352
|
const terminalColumns = stdout?.columns ?? 80;
|
|
1217
1353
|
const availableWidth = Math.max(1, terminalColumns - INPUT_AREA_OVERHEAD);
|
|
1218
|
-
const pasteIdRef =
|
|
1354
|
+
const pasteIdRef = useRef4(0);
|
|
1219
1355
|
const {
|
|
1220
1356
|
showPopup,
|
|
1221
1357
|
filteredCommands,
|
|
@@ -1224,15 +1360,17 @@ function InputArea({
|
|
|
1224
1360
|
isSubcommandMode,
|
|
1225
1361
|
setShowPopup
|
|
1226
1362
|
} = useAutocomplete(value, registry);
|
|
1227
|
-
const handlePaste =
|
|
1363
|
+
const handlePaste = useCallback4((text, cursorPosition) => {
|
|
1228
1364
|
pasteIdRef.current += 1;
|
|
1229
1365
|
const id = pasteIdRef.current;
|
|
1230
1366
|
pasteStore.current.set(id, text);
|
|
1231
1367
|
const lineCount = text.split("\n").length;
|
|
1232
1368
|
const label = `[Pasted text #${id} +${lineCount} lines]`;
|
|
1233
|
-
|
|
1369
|
+
const newCursorPos = cursorPosition + label.length;
|
|
1370
|
+
setCursorHint(newCursorPos);
|
|
1371
|
+
setValue((prev) => prev.slice(0, cursorPosition) + label + prev.slice(cursorPosition));
|
|
1234
1372
|
}, []);
|
|
1235
|
-
const tabCompleteCommand =
|
|
1373
|
+
const tabCompleteCommand = useCallback4(
|
|
1236
1374
|
(cmd) => {
|
|
1237
1375
|
const parsed = parseSlashInput(value);
|
|
1238
1376
|
if (parsed.parentCommand) {
|
|
@@ -1248,7 +1386,7 @@ function InputArea({
|
|
|
1248
1386
|
},
|
|
1249
1387
|
[value, setSelectedIndex]
|
|
1250
1388
|
);
|
|
1251
|
-
const enterSelectCommand =
|
|
1389
|
+
const enterSelectCommand = useCallback4(
|
|
1252
1390
|
(cmd) => {
|
|
1253
1391
|
const parsed = parseSlashInput(value);
|
|
1254
1392
|
if (parsed.parentCommand) {
|
|
@@ -1267,7 +1405,7 @@ function InputArea({
|
|
|
1267
1405
|
},
|
|
1268
1406
|
[value, onSubmit, setSelectedIndex]
|
|
1269
1407
|
);
|
|
1270
|
-
const handleSubmit =
|
|
1408
|
+
const handleSubmit = useCallback4(
|
|
1271
1409
|
(text) => {
|
|
1272
1410
|
const trimmed = text.trim();
|
|
1273
1411
|
if (trimmed.length === 0) return;
|
|
@@ -1345,11 +1483,15 @@ function InputArea({
|
|
|
1345
1483
|
CjkTextInput,
|
|
1346
1484
|
{
|
|
1347
1485
|
value,
|
|
1348
|
-
onChange:
|
|
1486
|
+
onChange: (v) => {
|
|
1487
|
+
setValue(v);
|
|
1488
|
+
setCursorHint(null);
|
|
1489
|
+
},
|
|
1349
1490
|
onSubmit: handleSubmit,
|
|
1350
1491
|
onPaste: handlePaste,
|
|
1351
1492
|
placeholder: "Type a message or /help",
|
|
1352
|
-
availableWidth
|
|
1493
|
+
availableWidth,
|
|
1494
|
+
cursorHint
|
|
1353
1495
|
}
|
|
1354
1496
|
)
|
|
1355
1497
|
] }) })
|
|
@@ -1357,7 +1499,7 @@ function InputArea({
|
|
|
1357
1499
|
}
|
|
1358
1500
|
|
|
1359
1501
|
// src/ui/ConfirmPrompt.tsx
|
|
1360
|
-
import { useState as
|
|
1502
|
+
import { useState as useState7, useCallback as useCallback5, useRef as useRef5 } from "react";
|
|
1361
1503
|
import { Box as Box6, Text as Text8, useInput as useInput3 } from "ink";
|
|
1362
1504
|
import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1363
1505
|
function ConfirmPrompt({
|
|
@@ -1365,9 +1507,9 @@ function ConfirmPrompt({
|
|
|
1365
1507
|
options = ["Yes", "No"],
|
|
1366
1508
|
onSelect
|
|
1367
1509
|
}) {
|
|
1368
|
-
const [selected, setSelected] =
|
|
1369
|
-
const resolvedRef =
|
|
1370
|
-
const doSelect =
|
|
1510
|
+
const [selected, setSelected] = useState7(0);
|
|
1511
|
+
const resolvedRef = useRef5(false);
|
|
1512
|
+
const doSelect = useCallback5(
|
|
1371
1513
|
(index) => {
|
|
1372
1514
|
if (resolvedRef.current) return;
|
|
1373
1515
|
resolvedRef.current = true;
|
|
@@ -1507,10 +1649,10 @@ function StreamingIndicator({ text, activeTools }) {
|
|
|
1507
1649
|
}
|
|
1508
1650
|
|
|
1509
1651
|
// src/ui/PluginTUI.tsx
|
|
1510
|
-
import { useState as
|
|
1652
|
+
import { useState as useState11, useCallback as useCallback8 } from "react";
|
|
1511
1653
|
|
|
1512
1654
|
// src/ui/MenuSelect.tsx
|
|
1513
|
-
import { useState as
|
|
1655
|
+
import { useState as useState8, useCallback as useCallback6, useRef as useRef6 } from "react";
|
|
1514
1656
|
import { Box as Box9, Text as Text11, useInput as useInput5 } from "ink";
|
|
1515
1657
|
import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1516
1658
|
function MenuSelect({
|
|
@@ -1521,10 +1663,10 @@ function MenuSelect({
|
|
|
1521
1663
|
loading,
|
|
1522
1664
|
error
|
|
1523
1665
|
}) {
|
|
1524
|
-
const [selected, setSelected] =
|
|
1525
|
-
const selectedRef =
|
|
1526
|
-
const resolvedRef =
|
|
1527
|
-
const doSelect =
|
|
1666
|
+
const [selected, setSelected] = useState8(0);
|
|
1667
|
+
const selectedRef = useRef6(0);
|
|
1668
|
+
const resolvedRef = useRef6(false);
|
|
1669
|
+
const doSelect = useCallback6(
|
|
1528
1670
|
(index) => {
|
|
1529
1671
|
if (resolvedRef.current || items.length === 0) return;
|
|
1530
1672
|
resolvedRef.current = true;
|
|
@@ -1574,7 +1716,7 @@ function MenuSelect({
|
|
|
1574
1716
|
}
|
|
1575
1717
|
|
|
1576
1718
|
// src/ui/TextPrompt.tsx
|
|
1577
|
-
import { useState as
|
|
1719
|
+
import { useState as useState9, useRef as useRef7, useCallback as useCallback7 } from "react";
|
|
1578
1720
|
import { Box as Box10, Text as Text12, useInput as useInput6 } from "ink";
|
|
1579
1721
|
import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
1580
1722
|
function TextPrompt({
|
|
@@ -1584,11 +1726,11 @@ function TextPrompt({
|
|
|
1584
1726
|
onCancel,
|
|
1585
1727
|
validate
|
|
1586
1728
|
}) {
|
|
1587
|
-
const [value, setValue] =
|
|
1588
|
-
const [error, setError] =
|
|
1589
|
-
const resolvedRef =
|
|
1590
|
-
const valueRef =
|
|
1591
|
-
const handleSubmit =
|
|
1729
|
+
const [value, setValue] = useState9("");
|
|
1730
|
+
const [error, setError] = useState9();
|
|
1731
|
+
const resolvedRef = useRef7(false);
|
|
1732
|
+
const valueRef = useRef7("");
|
|
1733
|
+
const handleSubmit = useCallback7(() => {
|
|
1592
1734
|
if (resolvedRef.current) return;
|
|
1593
1735
|
const trimmed = valueRef.current.trim();
|
|
1594
1736
|
if (!trimmed) return;
|
|
@@ -1729,61 +1871,16 @@ function handleInstalledActionSelect(value, pluginId, callbacks, nav) {
|
|
|
1729
1871
|
}
|
|
1730
1872
|
}
|
|
1731
1873
|
|
|
1732
|
-
// src/ui/
|
|
1733
|
-
import {
|
|
1734
|
-
function
|
|
1735
|
-
const [
|
|
1736
|
-
const [
|
|
1737
|
-
const [
|
|
1738
|
-
|
|
1739
|
-
const [confirm, setConfirm] = useState8();
|
|
1740
|
-
const [refreshCounter, setRefreshCounter] = useState8(0);
|
|
1741
|
-
const current = stack[stack.length - 1] ?? { screen: "main" };
|
|
1742
|
-
const push = useCallback6((state) => {
|
|
1743
|
-
setStack((prev) => [...prev, state]);
|
|
1744
|
-
setItems([]);
|
|
1745
|
-
setError(void 0);
|
|
1746
|
-
}, []);
|
|
1747
|
-
const pop = useCallback6(() => {
|
|
1748
|
-
setStack((prev) => {
|
|
1749
|
-
if (prev.length <= 1) {
|
|
1750
|
-
onClose();
|
|
1751
|
-
return prev;
|
|
1752
|
-
}
|
|
1753
|
-
return prev.slice(0, -1);
|
|
1754
|
-
});
|
|
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(() => {
|
|
1755
1881
|
setItems([]);
|
|
1756
1882
|
setError(void 0);
|
|
1757
|
-
|
|
1758
|
-
const popN = useCallback6(
|
|
1759
|
-
(n) => {
|
|
1760
|
-
setStack((prev) => {
|
|
1761
|
-
const next = prev.slice(0, Math.max(1, prev.length - n));
|
|
1762
|
-
if (next.length === 0) {
|
|
1763
|
-
onClose();
|
|
1764
|
-
return prev;
|
|
1765
|
-
}
|
|
1766
|
-
return next;
|
|
1767
|
-
});
|
|
1768
|
-
setItems([]);
|
|
1769
|
-
setError(void 0);
|
|
1770
|
-
},
|
|
1771
|
-
[onClose]
|
|
1772
|
-
);
|
|
1773
|
-
const notify = useCallback6(
|
|
1774
|
-
(content) => {
|
|
1775
|
-
addMessage?.({ role: "system", content });
|
|
1776
|
-
},
|
|
1777
|
-
[addMessage]
|
|
1778
|
-
);
|
|
1779
|
-
const refresh = useCallback6(() => {
|
|
1780
|
-
setItems([]);
|
|
1781
|
-
setRefreshCounter((c) => c + 1);
|
|
1782
|
-
}, []);
|
|
1783
|
-
const nav = { push, pop, popN, notify, setConfirm, refresh };
|
|
1784
|
-
useEffect3(() => {
|
|
1785
|
-
const screen2 = current.screen;
|
|
1786
|
-
if (screen2 === "marketplace-list") {
|
|
1883
|
+
if (screen === "marketplace-list") {
|
|
1787
1884
|
setLoading(true);
|
|
1788
1885
|
callbacks.marketplaceList().then((sources) => {
|
|
1789
1886
|
const baseItems = [{ label: "Add Marketplace", value: "__add__" }];
|
|
@@ -1798,10 +1895,10 @@ function PluginTUI({ callbacks, onClose, addMessage }) {
|
|
|
1798
1895
|
setError(err instanceof Error ? err.message : String(err));
|
|
1799
1896
|
setLoading(false);
|
|
1800
1897
|
});
|
|
1801
|
-
} else if (
|
|
1802
|
-
const
|
|
1898
|
+
} else if (screen === "marketplace-browse") {
|
|
1899
|
+
const mp = marketplace ?? "";
|
|
1803
1900
|
setLoading(true);
|
|
1804
|
-
callbacks.listAvailablePlugins(
|
|
1901
|
+
callbacks.listAvailablePlugins(mp).then((plugins) => {
|
|
1805
1902
|
setItems(
|
|
1806
1903
|
plugins.map((p) => ({
|
|
1807
1904
|
label: p.name,
|
|
@@ -1814,7 +1911,7 @@ function PluginTUI({ callbacks, onClose, addMessage }) {
|
|
|
1814
1911
|
setError(err instanceof Error ? err.message : String(err));
|
|
1815
1912
|
setLoading(false);
|
|
1816
1913
|
});
|
|
1817
|
-
} else if (
|
|
1914
|
+
} else if (screen === "installed-list") {
|
|
1818
1915
|
setLoading(true);
|
|
1819
1916
|
callbacks.listInstalled().then((plugins) => {
|
|
1820
1917
|
setItems(
|
|
@@ -1830,8 +1927,60 @@ function PluginTUI({ callbacks, onClose, addMessage }) {
|
|
|
1830
1927
|
setLoading(false);
|
|
1831
1928
|
});
|
|
1832
1929
|
}
|
|
1833
|
-
}, [
|
|
1834
|
-
|
|
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 nav = { push, pop, popN, notify, setConfirm, refresh };
|
|
1976
|
+
const { items, loading, error } = usePluginScreenData(
|
|
1977
|
+
current.screen,
|
|
1978
|
+
current.context?.marketplace,
|
|
1979
|
+
callbacks,
|
|
1980
|
+
refreshCounter,
|
|
1981
|
+
stack.length
|
|
1982
|
+
);
|
|
1983
|
+
const handleSelect = useCallback8(
|
|
1835
1984
|
(value) => {
|
|
1836
1985
|
const screen2 = current.screen;
|
|
1837
1986
|
const ctx = current.context;
|
|
@@ -1849,7 +1998,7 @@ function PluginTUI({ callbacks, onClose, addMessage }) {
|
|
|
1849
1998
|
},
|
|
1850
1999
|
[current, items, callbacks, push, pop, popN, notify, setConfirm, refresh]
|
|
1851
2000
|
);
|
|
1852
|
-
const handleTextSubmit =
|
|
2001
|
+
const handleTextSubmit = useCallback8(
|
|
1853
2002
|
(value) => {
|
|
1854
2003
|
if (current.screen === "marketplace-add") {
|
|
1855
2004
|
callbacks.marketplaceAdd(value).then((name) => {
|
|
@@ -1957,8 +2106,11 @@ function PluginTUI({ callbacks, onClose, addMessage }) {
|
|
|
1957
2106
|
);
|
|
1958
2107
|
}
|
|
1959
2108
|
|
|
2109
|
+
// src/ui/SessionPicker.tsx
|
|
2110
|
+
import { Box as Box12, Text as Text14 } from "ink";
|
|
2111
|
+
|
|
1960
2112
|
// src/ui/ListPicker.tsx
|
|
1961
|
-
import { useState as
|
|
2113
|
+
import { useState as useState12, useRef as useRef8 } from "react";
|
|
1962
2114
|
import { Box as Box11, Text as Text13, useInput as useInput7 } from "ink";
|
|
1963
2115
|
import { jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
1964
2116
|
var DEFAULT_MAX_VISIBLE = 3;
|
|
@@ -1969,10 +2121,10 @@ function ListPicker({
|
|
|
1969
2121
|
onCancel,
|
|
1970
2122
|
maxVisible = DEFAULT_MAX_VISIBLE
|
|
1971
2123
|
}) {
|
|
1972
|
-
const [selectedIndex, setSelectedIndex] =
|
|
1973
|
-
const [scrollOffset, setScrollOffset] =
|
|
1974
|
-
const selectedRef =
|
|
1975
|
-
const resolvedRef =
|
|
2124
|
+
const [selectedIndex, setSelectedIndex] = useState12(0);
|
|
2125
|
+
const [scrollOffset, setScrollOffset] = useState12(0);
|
|
2126
|
+
const selectedRef = useRef8(0);
|
|
2127
|
+
const resolvedRef = useRef8(false);
|
|
1976
2128
|
useInput7((_input, key) => {
|
|
1977
2129
|
if (resolvedRef.current) return;
|
|
1978
2130
|
if (key.escape) {
|
|
@@ -2024,13 +2176,62 @@ function ListPicker({
|
|
|
2024
2176
|
] });
|
|
2025
2177
|
}
|
|
2026
2178
|
|
|
2027
|
-
// src/ui/
|
|
2179
|
+
// src/ui/SessionPicker.tsx
|
|
2028
2180
|
import { Fragment as Fragment4, jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
2029
|
-
var EXIT_DELAY_MS = 500;
|
|
2030
2181
|
var SESSION_ID_DISPLAY_LENGTH = 8;
|
|
2182
|
+
function SessionPicker({
|
|
2183
|
+
sessionStore,
|
|
2184
|
+
cwd,
|
|
2185
|
+
onSelect,
|
|
2186
|
+
onCancel
|
|
2187
|
+
}) {
|
|
2188
|
+
const sessions = (sessionStore?.list() ?? []).filter((s) => s.cwd === cwd);
|
|
2189
|
+
return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", paddingX: 1, marginBottom: 1, children: [
|
|
2190
|
+
/* @__PURE__ */ jsx14(Text14, { bold: true, color: "cyan", children: "Select a session to resume (ESC to cancel):" }),
|
|
2191
|
+
/* @__PURE__ */ jsx14(
|
|
2192
|
+
ListPicker,
|
|
2193
|
+
{
|
|
2194
|
+
items: sessions,
|
|
2195
|
+
renderItem: (session, isSelected) => {
|
|
2196
|
+
const lastMsg = session.messages.slice().reverse().find((m) => {
|
|
2197
|
+
const msg = m;
|
|
2198
|
+
return msg.role === "assistant" && msg.content;
|
|
2199
|
+
});
|
|
2200
|
+
const rawPreview = lastMsg?.content?.replace(/[\n\r]+/g, " ").trim() ?? "";
|
|
2201
|
+
const preview = rawPreview ? rawPreview.slice(0, 60) + (rawPreview.length > 60 ? "..." : "") : "";
|
|
2202
|
+
return /* @__PURE__ */ jsxs12(Text14, { children: [
|
|
2203
|
+
isSelected ? "> " : " ",
|
|
2204
|
+
/* @__PURE__ */ jsx14(Text14, { bold: true, children: session.name ?? session.id.slice(0, SESSION_ID_DISPLAY_LENGTH) }),
|
|
2205
|
+
" ",
|
|
2206
|
+
/* @__PURE__ */ jsx14(Text14, { dimColor: true, children: new Date(session.updatedAt).toLocaleString(void 0, {
|
|
2207
|
+
month: "short",
|
|
2208
|
+
day: "numeric",
|
|
2209
|
+
hour: "2-digit",
|
|
2210
|
+
minute: "2-digit"
|
|
2211
|
+
}) }),
|
|
2212
|
+
" ",
|
|
2213
|
+
/* @__PURE__ */ jsxs12(Text14, { dimColor: true, children: [
|
|
2214
|
+
"msgs: ",
|
|
2215
|
+
session.messages.length
|
|
2216
|
+
] }),
|
|
2217
|
+
preview ? /* @__PURE__ */ jsxs12(Fragment4, { children: [
|
|
2218
|
+
"\n ",
|
|
2219
|
+
/* @__PURE__ */ jsx14(Text14, { color: "gray", children: preview })
|
|
2220
|
+
] }) : null
|
|
2221
|
+
] });
|
|
2222
|
+
},
|
|
2223
|
+
onSelect: (session) => onSelect(session.id),
|
|
2224
|
+
onCancel
|
|
2225
|
+
}
|
|
2226
|
+
)
|
|
2227
|
+
] });
|
|
2228
|
+
}
|
|
2229
|
+
|
|
2230
|
+
// src/ui/App.tsx
|
|
2231
|
+
import { jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
2031
2232
|
function App(props) {
|
|
2032
|
-
const [activeSessionId, setActiveSessionId] =
|
|
2033
|
-
return /* @__PURE__ */
|
|
2233
|
+
const [activeSessionId, setActiveSessionId] = useState13(props.resumeSessionId);
|
|
2234
|
+
return /* @__PURE__ */ jsx15(
|
|
2034
2235
|
AppInner,
|
|
2035
2236
|
{
|
|
2036
2237
|
...props,
|
|
@@ -2041,7 +2242,6 @@ function App(props) {
|
|
|
2041
2242
|
);
|
|
2042
2243
|
}
|
|
2043
2244
|
function AppInner(props) {
|
|
2044
|
-
const { exit } = useApp();
|
|
2045
2245
|
const cwd = props.cwd;
|
|
2046
2246
|
const {
|
|
2047
2247
|
interactiveSession,
|
|
@@ -2069,13 +2269,21 @@ function AppInner(props) {
|
|
|
2069
2269
|
sessionName: props.sessionName
|
|
2070
2270
|
});
|
|
2071
2271
|
const pluginCallbacks = usePluginCallbacks(cwd);
|
|
2072
|
-
const [
|
|
2073
|
-
const
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2272
|
+
const [sessionName, setSessionName] = useState13(props.sessionName);
|
|
2273
|
+
const {
|
|
2274
|
+
handleSubmit,
|
|
2275
|
+
pendingModelId,
|
|
2276
|
+
showPluginTUI,
|
|
2277
|
+
showSessionPicker,
|
|
2278
|
+
setShowPluginTUI,
|
|
2279
|
+
setShowSessionPicker,
|
|
2280
|
+
handleModelConfirm
|
|
2281
|
+
} = useSideEffects({
|
|
2282
|
+
interactiveSession,
|
|
2283
|
+
addEntry,
|
|
2284
|
+
baseHandleSubmit,
|
|
2285
|
+
setSessionName
|
|
2286
|
+
});
|
|
2079
2287
|
useEffect4(() => {
|
|
2080
2288
|
const name = interactiveSession?.getName?.();
|
|
2081
2289
|
if (name && !sessionName) setSessionName(name);
|
|
@@ -2084,68 +2292,9 @@ function AppInner(props) {
|
|
|
2084
2292
|
const title = sessionName ? `Robota \u2014 ${sessionName}` : "Robota";
|
|
2085
2293
|
process.stdout.write(`\x1B]0;${title}\x07`);
|
|
2086
2294
|
}, [sessionName]);
|
|
2087
|
-
const handleSubmit = async (input) => {
|
|
2088
|
-
await baseHandleSubmit(input);
|
|
2089
|
-
const sideEffects = interactiveSession;
|
|
2090
|
-
if (sideEffects._pendingModelId) {
|
|
2091
|
-
const modelId = sideEffects._pendingModelId;
|
|
2092
|
-
delete sideEffects._pendingModelId;
|
|
2093
|
-
pendingModelChangeRef.current = modelId;
|
|
2094
|
-
setPendingModelId(modelId);
|
|
2095
|
-
return;
|
|
2096
|
-
}
|
|
2097
|
-
if (sideEffects._pendingLanguage) {
|
|
2098
|
-
const lang = sideEffects._pendingLanguage;
|
|
2099
|
-
delete sideEffects._pendingLanguage;
|
|
2100
|
-
const settingsPath = getUserSettingsPath();
|
|
2101
|
-
const settings = readSettings(settingsPath);
|
|
2102
|
-
settings.language = lang;
|
|
2103
|
-
writeSettings(settingsPath, settings);
|
|
2104
|
-
addEntry(
|
|
2105
|
-
messageToHistoryEntry2(createSystemMessage2(`Language set to "${lang}". Restarting...`))
|
|
2106
|
-
);
|
|
2107
|
-
setTimeout(() => exit(), EXIT_DELAY_MS);
|
|
2108
|
-
return;
|
|
2109
|
-
}
|
|
2110
|
-
if (sideEffects._resetRequested) {
|
|
2111
|
-
delete sideEffects._resetRequested;
|
|
2112
|
-
const settingsPath = getUserSettingsPath();
|
|
2113
|
-
if (deleteSettings(settingsPath)) {
|
|
2114
|
-
addEntry(messageToHistoryEntry2(createSystemMessage2(`Deleted ${settingsPath}. Exiting...`)));
|
|
2115
|
-
} else {
|
|
2116
|
-
addEntry(messageToHistoryEntry2(createSystemMessage2("No user settings found.")));
|
|
2117
|
-
}
|
|
2118
|
-
setTimeout(() => exit(), EXIT_DELAY_MS);
|
|
2119
|
-
return;
|
|
2120
|
-
}
|
|
2121
|
-
if (sideEffects._exitRequested) {
|
|
2122
|
-
delete sideEffects._exitRequested;
|
|
2123
|
-
setTimeout(() => exit(), EXIT_DELAY_MS);
|
|
2124
|
-
return;
|
|
2125
|
-
}
|
|
2126
|
-
if (sideEffects._triggerPluginTUI) {
|
|
2127
|
-
delete sideEffects._triggerPluginTUI;
|
|
2128
|
-
setShowPluginTUI(true);
|
|
2129
|
-
return;
|
|
2130
|
-
}
|
|
2131
|
-
if (sideEffects._triggerResumePicker) {
|
|
2132
|
-
delete sideEffects._triggerResumePicker;
|
|
2133
|
-
setShowSessionPicker(true);
|
|
2134
|
-
return;
|
|
2135
|
-
}
|
|
2136
|
-
if (sideEffects._sessionName) {
|
|
2137
|
-
const name = sideEffects._sessionName;
|
|
2138
|
-
delete sideEffects._sessionName;
|
|
2139
|
-
interactiveSession.setName(name);
|
|
2140
|
-
setSessionName(name);
|
|
2141
|
-
return;
|
|
2142
|
-
}
|
|
2143
|
-
};
|
|
2144
2295
|
useInput8(
|
|
2145
2296
|
(_input, key) => {
|
|
2146
|
-
if (key.escape && isThinking)
|
|
2147
|
-
handleAbort();
|
|
2148
|
-
}
|
|
2297
|
+
if (key.escape && isThinking) handleAbort();
|
|
2149
2298
|
},
|
|
2150
2299
|
{ isActive: !permissionRequest && !showPluginTUI && !showSessionPicker }
|
|
2151
2300
|
);
|
|
@@ -2157,117 +2306,60 @@ function AppInner(props) {
|
|
|
2157
2306
|
sessionId = session.getSessionId();
|
|
2158
2307
|
} catch {
|
|
2159
2308
|
}
|
|
2160
|
-
return /* @__PURE__ */
|
|
2161
|
-
/* @__PURE__ */
|
|
2162
|
-
/* @__PURE__ */
|
|
2309
|
+
return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", children: [
|
|
2310
|
+
/* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", paddingX: 1, marginBottom: 1, children: [
|
|
2311
|
+
/* @__PURE__ */ jsx15(Text15, { color: "cyan", bold: true, children: `
|
|
2163
2312
|
____ ___ ____ ___ _____ _
|
|
2164
2313
|
| _ \\ / _ \\| __ ) / _ \\_ _|/ \\
|
|
2165
2314
|
| |_) | | | | _ \\| | | || | / _ \\
|
|
2166
2315
|
| _ <| |_| | |_) | |_| || |/ ___ \\
|
|
2167
2316
|
|_| \\_\\\\___/|____/ \\___/ |_/_/ \\_\\
|
|
2168
2317
|
` }),
|
|
2169
|
-
/* @__PURE__ */
|
|
2318
|
+
/* @__PURE__ */ jsxs13(Text15, { dimColor: true, children: [
|
|
2170
2319
|
" v",
|
|
2171
2320
|
props.version ?? "0.0.0"
|
|
2172
2321
|
] })
|
|
2173
2322
|
] }),
|
|
2174
|
-
/* @__PURE__ */
|
|
2175
|
-
/* @__PURE__ */
|
|
2176
|
-
(isThinking || activeTools.length > 0) && /* @__PURE__ */
|
|
2323
|
+
/* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", paddingX: 1, flexGrow: 1, children: [
|
|
2324
|
+
/* @__PURE__ */ jsx15(MessageList, { history }),
|
|
2325
|
+
(isThinking || activeTools.length > 0) && /* @__PURE__ */ jsx15(Box13, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ jsx15(StreamingIndicator, { text: streamingText, activeTools }) })
|
|
2177
2326
|
] }),
|
|
2178
|
-
permissionRequest && /* @__PURE__ */
|
|
2179
|
-
pendingModelId && /* @__PURE__ */
|
|
2327
|
+
permissionRequest && /* @__PURE__ */ jsx15(PermissionPrompt, { request: permissionRequest }),
|
|
2328
|
+
pendingModelId && /* @__PURE__ */ jsx15(
|
|
2180
2329
|
ConfirmPrompt,
|
|
2181
2330
|
{
|
|
2182
|
-
message: `Change model to ${
|
|
2183
|
-
onSelect:
|
|
2184
|
-
setPendingModelId(null);
|
|
2185
|
-
pendingModelChangeRef.current = null;
|
|
2186
|
-
if (index === 0) {
|
|
2187
|
-
try {
|
|
2188
|
-
const settingsPath = getUserSettingsPath();
|
|
2189
|
-
updateModelInSettings(settingsPath, pendingModelId);
|
|
2190
|
-
addEntry(
|
|
2191
|
-
messageToHistoryEntry2(
|
|
2192
|
-
createSystemMessage2(
|
|
2193
|
-
`Model changed to ${getModelName(pendingModelId)}. Restarting...`
|
|
2194
|
-
)
|
|
2195
|
-
)
|
|
2196
|
-
);
|
|
2197
|
-
setTimeout(() => exit(), EXIT_DELAY_MS);
|
|
2198
|
-
} catch (err) {
|
|
2199
|
-
addEntry(
|
|
2200
|
-
messageToHistoryEntry2(
|
|
2201
|
-
createSystemMessage2(
|
|
2202
|
-
`Failed: ${err instanceof Error ? err.message : String(err)}`
|
|
2203
|
-
)
|
|
2204
|
-
)
|
|
2205
|
-
);
|
|
2206
|
-
}
|
|
2207
|
-
} else {
|
|
2208
|
-
addEntry(messageToHistoryEntry2(createSystemMessage2("Model change cancelled.")));
|
|
2209
|
-
}
|
|
2210
|
-
}
|
|
2331
|
+
message: `Change model to ${getModelName2(pendingModelId)}? This will restart the session.`,
|
|
2332
|
+
onSelect: handleModelConfirm
|
|
2211
2333
|
}
|
|
2212
2334
|
),
|
|
2213
|
-
showPluginTUI && /* @__PURE__ */
|
|
2335
|
+
showPluginTUI && /* @__PURE__ */ jsx15(
|
|
2214
2336
|
PluginTUI,
|
|
2215
2337
|
{
|
|
2216
2338
|
callbacks: pluginCallbacks,
|
|
2217
2339
|
onClose: () => setShowPluginTUI(false),
|
|
2218
|
-
addMessage: (msg) => addEntry(
|
|
2340
|
+
addMessage: (msg) => addEntry(messageToHistoryEntry3(createSystemMessage3(msg.content)))
|
|
2219
2341
|
}
|
|
2220
2342
|
),
|
|
2221
|
-
showSessionPicker && /* @__PURE__ */
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
const preview = rawPreview ? rawPreview.slice(0, 60) + (rawPreview.length > 60 ? "..." : "") : "";
|
|
2234
|
-
return /* @__PURE__ */ jsxs12(Text14, { children: [
|
|
2235
|
-
isSelected ? "> " : " ",
|
|
2236
|
-
/* @__PURE__ */ jsx14(Text14, { bold: true, children: session.name ?? session.id.slice(0, SESSION_ID_DISPLAY_LENGTH) }),
|
|
2237
|
-
" ",
|
|
2238
|
-
/* @__PURE__ */ jsx14(Text14, { dimColor: true, children: new Date(session.updatedAt).toLocaleString(void 0, {
|
|
2239
|
-
month: "short",
|
|
2240
|
-
day: "numeric",
|
|
2241
|
-
hour: "2-digit",
|
|
2242
|
-
minute: "2-digit"
|
|
2243
|
-
}) }),
|
|
2244
|
-
" ",
|
|
2245
|
-
/* @__PURE__ */ jsxs12(Text14, { dimColor: true, children: [
|
|
2246
|
-
"msgs: ",
|
|
2247
|
-
session.messages.length
|
|
2248
|
-
] }),
|
|
2249
|
-
preview ? /* @__PURE__ */ jsxs12(Fragment4, { children: [
|
|
2250
|
-
"\n ",
|
|
2251
|
-
/* @__PURE__ */ jsx14(Text14, { color: "gray", children: preview })
|
|
2252
|
-
] }) : null
|
|
2253
|
-
] });
|
|
2254
|
-
},
|
|
2255
|
-
onSelect: (session) => {
|
|
2256
|
-
setShowSessionPicker(false);
|
|
2257
|
-
props.onSessionSwitch(session.id);
|
|
2258
|
-
},
|
|
2259
|
-
onCancel: () => {
|
|
2260
|
-
setShowSessionPicker(false);
|
|
2261
|
-
addEntry(messageToHistoryEntry2(createSystemMessage2("Session resume cancelled.")));
|
|
2262
|
-
}
|
|
2343
|
+
showSessionPicker && /* @__PURE__ */ jsx15(
|
|
2344
|
+
SessionPicker,
|
|
2345
|
+
{
|
|
2346
|
+
sessionStore: props.sessionStore,
|
|
2347
|
+
cwd: props.cwd,
|
|
2348
|
+
onSelect: (id) => {
|
|
2349
|
+
setShowSessionPicker(false);
|
|
2350
|
+
props.onSessionSwitch(id);
|
|
2351
|
+
},
|
|
2352
|
+
onCancel: () => {
|
|
2353
|
+
setShowSessionPicker(false);
|
|
2354
|
+
addEntry(messageToHistoryEntry3(createSystemMessage3("Session resume cancelled.")));
|
|
2263
2355
|
}
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
/* @__PURE__ */
|
|
2356
|
+
}
|
|
2357
|
+
),
|
|
2358
|
+
/* @__PURE__ */ jsx15(
|
|
2267
2359
|
StatusBar,
|
|
2268
2360
|
{
|
|
2269
2361
|
permissionMode,
|
|
2270
|
-
modelName: props.modelId ?
|
|
2362
|
+
modelName: props.modelId ? getModelName2(props.modelId) : "",
|
|
2271
2363
|
sessionId,
|
|
2272
2364
|
messageCount: history.length,
|
|
2273
2365
|
isThinking,
|
|
@@ -2277,7 +2369,7 @@ function AppInner(props) {
|
|
|
2277
2369
|
sessionName
|
|
2278
2370
|
}
|
|
2279
2371
|
),
|
|
2280
|
-
/* @__PURE__ */
|
|
2372
|
+
/* @__PURE__ */ jsx15(
|
|
2281
2373
|
InputArea,
|
|
2282
2374
|
{
|
|
2283
2375
|
onSubmit: handleSubmit,
|
|
@@ -2289,12 +2381,12 @@ function AppInner(props) {
|
|
|
2289
2381
|
sessionName
|
|
2290
2382
|
}
|
|
2291
2383
|
),
|
|
2292
|
-
/* @__PURE__ */
|
|
2384
|
+
/* @__PURE__ */ jsx15(Text15, { children: " " })
|
|
2293
2385
|
] });
|
|
2294
2386
|
}
|
|
2295
2387
|
|
|
2296
2388
|
// src/ui/render.tsx
|
|
2297
|
-
import { jsx as
|
|
2389
|
+
import { jsx as jsx16 } from "react/jsx-runtime";
|
|
2298
2390
|
function renderApp(options) {
|
|
2299
2391
|
process.on("unhandledRejection", (reason) => {
|
|
2300
2392
|
process.stderr.write(`
|
|
@@ -2308,7 +2400,7 @@ function renderApp(options) {
|
|
|
2308
2400
|
if (process.stdin.isTTY && process.stdout.isTTY) {
|
|
2309
2401
|
process.stdout.write("\x1B[?2004h");
|
|
2310
2402
|
}
|
|
2311
|
-
const instance = render(/* @__PURE__ */
|
|
2403
|
+
const instance = render(/* @__PURE__ */ jsx16(App, { ...options }), {
|
|
2312
2404
|
exitOnCtrlC: true
|
|
2313
2405
|
});
|
|
2314
2406
|
instance.waitUntilExit().then(() => {
|