@robota-sdk/agent-cli 3.0.0-beta.52 → 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-QZUCO4ZK.js → chunk-CXBD4JNS.js} +444 -360
- package/dist/node/index.cjs +607 -522
- 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";
|
|
@@ -945,11 +1076,11 @@ function CjkTextInput({
|
|
|
945
1076
|
availableWidth,
|
|
946
1077
|
cursorHint = null
|
|
947
1078
|
}) {
|
|
948
|
-
const valueRef =
|
|
949
|
-
const cursorRef =
|
|
950
|
-
const [, forceRender] =
|
|
951
|
-
const isPastingRef =
|
|
952
|
-
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("");
|
|
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
|
|
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] =
|
|
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/
|
|
1148
|
-
import {
|
|
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] =
|
|
1160
|
-
const [dismissed, setDismissed] =
|
|
1161
|
-
const prevValueRef =
|
|
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] =
|
|
1215
|
-
const [cursorHint, setCursorHint] =
|
|
1216
|
-
const pasteStore =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
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] =
|
|
1377
|
-
const resolvedRef =
|
|
1378
|
-
const doSelect =
|
|
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
|
|
1652
|
+
import { useState as useState11, useCallback as useCallback8 } from "react";
|
|
1519
1653
|
|
|
1520
1654
|
// src/ui/MenuSelect.tsx
|
|
1521
|
-
import { useState as
|
|
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] =
|
|
1533
|
-
const selectedRef =
|
|
1534
|
-
const resolvedRef =
|
|
1535
|
-
const doSelect =
|
|
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
|
|
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] =
|
|
1596
|
-
const [error, setError] =
|
|
1597
|
-
const resolvedRef =
|
|
1598
|
-
const valueRef =
|
|
1599
|
-
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(() => {
|
|
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/
|
|
1741
|
-
import {
|
|
1742
|
-
function
|
|
1743
|
-
const [
|
|
1744
|
-
const [
|
|
1745
|
-
const [
|
|
1746
|
-
|
|
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
|
-
|
|
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 (
|
|
1810
|
-
const
|
|
1898
|
+
} else if (screen === "marketplace-browse") {
|
|
1899
|
+
const mp = marketplace ?? "";
|
|
1811
1900
|
setLoading(true);
|
|
1812
|
-
callbacks.listAvailablePlugins(
|
|
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 (
|
|
1914
|
+
} else if (screen === "installed-list") {
|
|
1826
1915
|
setLoading(true);
|
|
1827
1916
|
callbacks.listInstalled().then((plugins) => {
|
|
1828
1917
|
setItems(
|
|
@@ -1838,8 +1927,60 @@ function PluginTUI({ callbacks, onClose, addMessage }) {
|
|
|
1838
1927
|
setLoading(false);
|
|
1839
1928
|
});
|
|
1840
1929
|
}
|
|
1841
|
-
}, [
|
|
1842
|
-
|
|
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(
|
|
1843
1984
|
(value) => {
|
|
1844
1985
|
const screen2 = current.screen;
|
|
1845
1986
|
const ctx = current.context;
|
|
@@ -1857,7 +1998,7 @@ function PluginTUI({ callbacks, onClose, addMessage }) {
|
|
|
1857
1998
|
},
|
|
1858
1999
|
[current, items, callbacks, push, pop, popN, notify, setConfirm, refresh]
|
|
1859
2000
|
);
|
|
1860
|
-
const handleTextSubmit =
|
|
2001
|
+
const handleTextSubmit = useCallback8(
|
|
1861
2002
|
(value) => {
|
|
1862
2003
|
if (current.screen === "marketplace-add") {
|
|
1863
2004
|
callbacks.marketplaceAdd(value).then((name) => {
|
|
@@ -1965,8 +2106,11 @@ function PluginTUI({ callbacks, onClose, addMessage }) {
|
|
|
1965
2106
|
);
|
|
1966
2107
|
}
|
|
1967
2108
|
|
|
2109
|
+
// src/ui/SessionPicker.tsx
|
|
2110
|
+
import { Box as Box12, Text as Text14 } from "ink";
|
|
2111
|
+
|
|
1968
2112
|
// src/ui/ListPicker.tsx
|
|
1969
|
-
import { useState as
|
|
2113
|
+
import { useState as useState12, useRef as useRef8 } from "react";
|
|
1970
2114
|
import { Box as Box11, Text as Text13, useInput as useInput7 } from "ink";
|
|
1971
2115
|
import { jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
1972
2116
|
var DEFAULT_MAX_VISIBLE = 3;
|
|
@@ -1977,10 +2121,10 @@ function ListPicker({
|
|
|
1977
2121
|
onCancel,
|
|
1978
2122
|
maxVisible = DEFAULT_MAX_VISIBLE
|
|
1979
2123
|
}) {
|
|
1980
|
-
const [selectedIndex, setSelectedIndex] =
|
|
1981
|
-
const [scrollOffset, setScrollOffset] =
|
|
1982
|
-
const selectedRef =
|
|
1983
|
-
const resolvedRef =
|
|
2124
|
+
const [selectedIndex, setSelectedIndex] = useState12(0);
|
|
2125
|
+
const [scrollOffset, setScrollOffset] = useState12(0);
|
|
2126
|
+
const selectedRef = useRef8(0);
|
|
2127
|
+
const resolvedRef = useRef8(false);
|
|
1984
2128
|
useInput7((_input, key) => {
|
|
1985
2129
|
if (resolvedRef.current) return;
|
|
1986
2130
|
if (key.escape) {
|
|
@@ -2032,13 +2176,62 @@ function ListPicker({
|
|
|
2032
2176
|
] });
|
|
2033
2177
|
}
|
|
2034
2178
|
|
|
2035
|
-
// src/ui/
|
|
2179
|
+
// src/ui/SessionPicker.tsx
|
|
2036
2180
|
import { Fragment as Fragment4, jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
2037
|
-
var EXIT_DELAY_MS = 500;
|
|
2038
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";
|
|
2039
2232
|
function App(props) {
|
|
2040
|
-
const [activeSessionId, setActiveSessionId] =
|
|
2041
|
-
return /* @__PURE__ */
|
|
2233
|
+
const [activeSessionId, setActiveSessionId] = useState13(props.resumeSessionId);
|
|
2234
|
+
return /* @__PURE__ */ jsx15(
|
|
2042
2235
|
AppInner,
|
|
2043
2236
|
{
|
|
2044
2237
|
...props,
|
|
@@ -2049,7 +2242,6 @@ function App(props) {
|
|
|
2049
2242
|
);
|
|
2050
2243
|
}
|
|
2051
2244
|
function AppInner(props) {
|
|
2052
|
-
const { exit } = useApp();
|
|
2053
2245
|
const cwd = props.cwd;
|
|
2054
2246
|
const {
|
|
2055
2247
|
interactiveSession,
|
|
@@ -2077,13 +2269,21 @@ function AppInner(props) {
|
|
|
2077
2269
|
sessionName: props.sessionName
|
|
2078
2270
|
});
|
|
2079
2271
|
const pluginCallbacks = usePluginCallbacks(cwd);
|
|
2080
|
-
const [
|
|
2081
|
-
const
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
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
|
+
});
|
|
2087
2287
|
useEffect4(() => {
|
|
2088
2288
|
const name = interactiveSession?.getName?.();
|
|
2089
2289
|
if (name && !sessionName) setSessionName(name);
|
|
@@ -2092,68 +2292,9 @@ function AppInner(props) {
|
|
|
2092
2292
|
const title = sessionName ? `Robota \u2014 ${sessionName}` : "Robota";
|
|
2093
2293
|
process.stdout.write(`\x1B]0;${title}\x07`);
|
|
2094
2294
|
}, [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
2295
|
useInput8(
|
|
2153
2296
|
(_input, key) => {
|
|
2154
|
-
if (key.escape && isThinking)
|
|
2155
|
-
handleAbort();
|
|
2156
|
-
}
|
|
2297
|
+
if (key.escape && isThinking) handleAbort();
|
|
2157
2298
|
},
|
|
2158
2299
|
{ isActive: !permissionRequest && !showPluginTUI && !showSessionPicker }
|
|
2159
2300
|
);
|
|
@@ -2165,117 +2306,60 @@ function AppInner(props) {
|
|
|
2165
2306
|
sessionId = session.getSessionId();
|
|
2166
2307
|
} catch {
|
|
2167
2308
|
}
|
|
2168
|
-
return /* @__PURE__ */
|
|
2169
|
-
/* @__PURE__ */
|
|
2170
|
-
/* @__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: `
|
|
2171
2312
|
____ ___ ____ ___ _____ _
|
|
2172
2313
|
| _ \\ / _ \\| __ ) / _ \\_ _|/ \\
|
|
2173
2314
|
| |_) | | | | _ \\| | | || | / _ \\
|
|
2174
2315
|
| _ <| |_| | |_) | |_| || |/ ___ \\
|
|
2175
2316
|
|_| \\_\\\\___/|____/ \\___/ |_/_/ \\_\\
|
|
2176
2317
|
` }),
|
|
2177
|
-
/* @__PURE__ */
|
|
2318
|
+
/* @__PURE__ */ jsxs13(Text15, { dimColor: true, children: [
|
|
2178
2319
|
" v",
|
|
2179
2320
|
props.version ?? "0.0.0"
|
|
2180
2321
|
] })
|
|
2181
2322
|
] }),
|
|
2182
|
-
/* @__PURE__ */
|
|
2183
|
-
/* @__PURE__ */
|
|
2184
|
-
(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 }) })
|
|
2185
2326
|
] }),
|
|
2186
|
-
permissionRequest && /* @__PURE__ */
|
|
2187
|
-
pendingModelId && /* @__PURE__ */
|
|
2327
|
+
permissionRequest && /* @__PURE__ */ jsx15(PermissionPrompt, { request: permissionRequest }),
|
|
2328
|
+
pendingModelId && /* @__PURE__ */ jsx15(
|
|
2188
2329
|
ConfirmPrompt,
|
|
2189
2330
|
{
|
|
2190
|
-
message: `Change model to ${
|
|
2191
|
-
onSelect:
|
|
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
|
-
}
|
|
2331
|
+
message: `Change model to ${getModelName2(pendingModelId)}? This will restart the session.`,
|
|
2332
|
+
onSelect: handleModelConfirm
|
|
2219
2333
|
}
|
|
2220
2334
|
),
|
|
2221
|
-
showPluginTUI && /* @__PURE__ */
|
|
2335
|
+
showPluginTUI && /* @__PURE__ */ jsx15(
|
|
2222
2336
|
PluginTUI,
|
|
2223
2337
|
{
|
|
2224
2338
|
callbacks: pluginCallbacks,
|
|
2225
2339
|
onClose: () => setShowPluginTUI(false),
|
|
2226
|
-
addMessage: (msg) => addEntry(
|
|
2340
|
+
addMessage: (msg) => addEntry(messageToHistoryEntry3(createSystemMessage3(msg.content)))
|
|
2227
2341
|
}
|
|
2228
2342
|
),
|
|
2229
|
-
showSessionPicker && /* @__PURE__ */
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
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
|
-
}
|
|
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.")));
|
|
2271
2355
|
}
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
/* @__PURE__ */
|
|
2356
|
+
}
|
|
2357
|
+
),
|
|
2358
|
+
/* @__PURE__ */ jsx15(
|
|
2275
2359
|
StatusBar,
|
|
2276
2360
|
{
|
|
2277
2361
|
permissionMode,
|
|
2278
|
-
modelName: props.modelId ?
|
|
2362
|
+
modelName: props.modelId ? getModelName2(props.modelId) : "",
|
|
2279
2363
|
sessionId,
|
|
2280
2364
|
messageCount: history.length,
|
|
2281
2365
|
isThinking,
|
|
@@ -2285,7 +2369,7 @@ function AppInner(props) {
|
|
|
2285
2369
|
sessionName
|
|
2286
2370
|
}
|
|
2287
2371
|
),
|
|
2288
|
-
/* @__PURE__ */
|
|
2372
|
+
/* @__PURE__ */ jsx15(
|
|
2289
2373
|
InputArea,
|
|
2290
2374
|
{
|
|
2291
2375
|
onSubmit: handleSubmit,
|
|
@@ -2297,12 +2381,12 @@ function AppInner(props) {
|
|
|
2297
2381
|
sessionName
|
|
2298
2382
|
}
|
|
2299
2383
|
),
|
|
2300
|
-
/* @__PURE__ */
|
|
2384
|
+
/* @__PURE__ */ jsx15(Text15, { children: " " })
|
|
2301
2385
|
] });
|
|
2302
2386
|
}
|
|
2303
2387
|
|
|
2304
2388
|
// src/ui/render.tsx
|
|
2305
|
-
import { jsx as
|
|
2389
|
+
import { jsx as jsx16 } from "react/jsx-runtime";
|
|
2306
2390
|
function renderApp(options) {
|
|
2307
2391
|
process.on("unhandledRejection", (reason) => {
|
|
2308
2392
|
process.stderr.write(`
|
|
@@ -2316,7 +2400,7 @@ function renderApp(options) {
|
|
|
2316
2400
|
if (process.stdin.isTTY && process.stdout.isTTY) {
|
|
2317
2401
|
process.stdout.write("\x1B[?2004h");
|
|
2318
2402
|
}
|
|
2319
|
-
const instance = render(/* @__PURE__ */
|
|
2403
|
+
const instance = render(/* @__PURE__ */ jsx16(App, { ...options }), {
|
|
2320
2404
|
exitOnCtrlC: true
|
|
2321
2405
|
});
|
|
2322
2406
|
instance.waitUntilExit().then(() => {
|