@robota-sdk/agent-cli 3.0.0-beta.52 → 3.0.0-beta.54
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/node/bin.js +1 -1
- package/dist/node/{chunk-QZUCO4ZK.js → chunk-ASBCJDLC.js} +452 -360
- package/dist/node/index.cjs +615 -522
- package/dist/node/index.js +1 -1
- package/package.json +8 -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,68 @@ 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 setConfirmNav = useCallback8(
|
|
1976
|
+
(state) => setConfirm(state),
|
|
1977
|
+
[setConfirm]
|
|
1978
|
+
);
|
|
1979
|
+
const pushNav = useCallback8(
|
|
1980
|
+
(state) => push({ screen: state.screen, context: state.context }),
|
|
1981
|
+
[push]
|
|
1982
|
+
);
|
|
1983
|
+
const nav = { push: pushNav, pop, popN, notify, setConfirm: setConfirmNav, refresh };
|
|
1984
|
+
const { items, loading, error } = usePluginScreenData(
|
|
1985
|
+
current.screen,
|
|
1986
|
+
current.context?.marketplace,
|
|
1987
|
+
callbacks,
|
|
1988
|
+
refreshCounter,
|
|
1989
|
+
stack.length
|
|
1990
|
+
);
|
|
1991
|
+
const handleSelect = useCallback8(
|
|
1843
1992
|
(value) => {
|
|
1844
1993
|
const screen2 = current.screen;
|
|
1845
1994
|
const ctx = current.context;
|
|
@@ -1857,7 +2006,7 @@ function PluginTUI({ callbacks, onClose, addMessage }) {
|
|
|
1857
2006
|
},
|
|
1858
2007
|
[current, items, callbacks, push, pop, popN, notify, setConfirm, refresh]
|
|
1859
2008
|
);
|
|
1860
|
-
const handleTextSubmit =
|
|
2009
|
+
const handleTextSubmit = useCallback8(
|
|
1861
2010
|
(value) => {
|
|
1862
2011
|
if (current.screen === "marketplace-add") {
|
|
1863
2012
|
callbacks.marketplaceAdd(value).then((name) => {
|
|
@@ -1965,8 +2114,11 @@ function PluginTUI({ callbacks, onClose, addMessage }) {
|
|
|
1965
2114
|
);
|
|
1966
2115
|
}
|
|
1967
2116
|
|
|
2117
|
+
// src/ui/SessionPicker.tsx
|
|
2118
|
+
import { Box as Box12, Text as Text14 } from "ink";
|
|
2119
|
+
|
|
1968
2120
|
// src/ui/ListPicker.tsx
|
|
1969
|
-
import { useState as
|
|
2121
|
+
import { useState as useState12, useRef as useRef8 } from "react";
|
|
1970
2122
|
import { Box as Box11, Text as Text13, useInput as useInput7 } from "ink";
|
|
1971
2123
|
import { jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
1972
2124
|
var DEFAULT_MAX_VISIBLE = 3;
|
|
@@ -1977,10 +2129,10 @@ function ListPicker({
|
|
|
1977
2129
|
onCancel,
|
|
1978
2130
|
maxVisible = DEFAULT_MAX_VISIBLE
|
|
1979
2131
|
}) {
|
|
1980
|
-
const [selectedIndex, setSelectedIndex] =
|
|
1981
|
-
const [scrollOffset, setScrollOffset] =
|
|
1982
|
-
const selectedRef =
|
|
1983
|
-
const resolvedRef =
|
|
2132
|
+
const [selectedIndex, setSelectedIndex] = useState12(0);
|
|
2133
|
+
const [scrollOffset, setScrollOffset] = useState12(0);
|
|
2134
|
+
const selectedRef = useRef8(0);
|
|
2135
|
+
const resolvedRef = useRef8(false);
|
|
1984
2136
|
useInput7((_input, key) => {
|
|
1985
2137
|
if (resolvedRef.current) return;
|
|
1986
2138
|
if (key.escape) {
|
|
@@ -2032,13 +2184,62 @@ function ListPicker({
|
|
|
2032
2184
|
] });
|
|
2033
2185
|
}
|
|
2034
2186
|
|
|
2035
|
-
// src/ui/
|
|
2187
|
+
// src/ui/SessionPicker.tsx
|
|
2036
2188
|
import { Fragment as Fragment4, jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
2037
|
-
var EXIT_DELAY_MS = 500;
|
|
2038
2189
|
var SESSION_ID_DISPLAY_LENGTH = 8;
|
|
2190
|
+
function SessionPicker({
|
|
2191
|
+
sessionStore,
|
|
2192
|
+
cwd,
|
|
2193
|
+
onSelect,
|
|
2194
|
+
onCancel
|
|
2195
|
+
}) {
|
|
2196
|
+
const sessions = (sessionStore?.list() ?? []).filter((s) => s.cwd === cwd);
|
|
2197
|
+
return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", paddingX: 1, marginBottom: 1, children: [
|
|
2198
|
+
/* @__PURE__ */ jsx14(Text14, { bold: true, color: "cyan", children: "Select a session to resume (ESC to cancel):" }),
|
|
2199
|
+
/* @__PURE__ */ jsx14(
|
|
2200
|
+
ListPicker,
|
|
2201
|
+
{
|
|
2202
|
+
items: sessions,
|
|
2203
|
+
renderItem: (session, isSelected) => {
|
|
2204
|
+
const lastMsg = session.messages.slice().reverse().find((m) => {
|
|
2205
|
+
const msg = m;
|
|
2206
|
+
return msg.role === "assistant" && msg.content;
|
|
2207
|
+
});
|
|
2208
|
+
const rawPreview = lastMsg?.content?.replace(/[\n\r]+/g, " ").trim() ?? "";
|
|
2209
|
+
const preview = rawPreview ? rawPreview.slice(0, 60) + (rawPreview.length > 60 ? "..." : "") : "";
|
|
2210
|
+
return /* @__PURE__ */ jsxs12(Text14, { children: [
|
|
2211
|
+
isSelected ? "> " : " ",
|
|
2212
|
+
/* @__PURE__ */ jsx14(Text14, { bold: true, children: session.name ?? session.id.slice(0, SESSION_ID_DISPLAY_LENGTH) }),
|
|
2213
|
+
" ",
|
|
2214
|
+
/* @__PURE__ */ jsx14(Text14, { dimColor: true, children: new Date(session.updatedAt).toLocaleString(void 0, {
|
|
2215
|
+
month: "short",
|
|
2216
|
+
day: "numeric",
|
|
2217
|
+
hour: "2-digit",
|
|
2218
|
+
minute: "2-digit"
|
|
2219
|
+
}) }),
|
|
2220
|
+
" ",
|
|
2221
|
+
/* @__PURE__ */ jsxs12(Text14, { dimColor: true, children: [
|
|
2222
|
+
"msgs: ",
|
|
2223
|
+
session.messages.length
|
|
2224
|
+
] }),
|
|
2225
|
+
preview ? /* @__PURE__ */ jsxs12(Fragment4, { children: [
|
|
2226
|
+
"\n ",
|
|
2227
|
+
/* @__PURE__ */ jsx14(Text14, { color: "gray", children: preview })
|
|
2228
|
+
] }) : null
|
|
2229
|
+
] });
|
|
2230
|
+
},
|
|
2231
|
+
onSelect: (session) => onSelect(session.id),
|
|
2232
|
+
onCancel
|
|
2233
|
+
}
|
|
2234
|
+
)
|
|
2235
|
+
] });
|
|
2236
|
+
}
|
|
2237
|
+
|
|
2238
|
+
// src/ui/App.tsx
|
|
2239
|
+
import { jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
2039
2240
|
function App(props) {
|
|
2040
|
-
const [activeSessionId, setActiveSessionId] =
|
|
2041
|
-
return /* @__PURE__ */
|
|
2241
|
+
const [activeSessionId, setActiveSessionId] = useState13(props.resumeSessionId);
|
|
2242
|
+
return /* @__PURE__ */ jsx15(
|
|
2042
2243
|
AppInner,
|
|
2043
2244
|
{
|
|
2044
2245
|
...props,
|
|
@@ -2049,7 +2250,6 @@ function App(props) {
|
|
|
2049
2250
|
);
|
|
2050
2251
|
}
|
|
2051
2252
|
function AppInner(props) {
|
|
2052
|
-
const { exit } = useApp();
|
|
2053
2253
|
const cwd = props.cwd;
|
|
2054
2254
|
const {
|
|
2055
2255
|
interactiveSession,
|
|
@@ -2077,13 +2277,21 @@ function AppInner(props) {
|
|
|
2077
2277
|
sessionName: props.sessionName
|
|
2078
2278
|
});
|
|
2079
2279
|
const pluginCallbacks = usePluginCallbacks(cwd);
|
|
2080
|
-
const [
|
|
2081
|
-
const
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2280
|
+
const [sessionName, setSessionName] = useState13(props.sessionName);
|
|
2281
|
+
const {
|
|
2282
|
+
handleSubmit,
|
|
2283
|
+
pendingModelId,
|
|
2284
|
+
showPluginTUI,
|
|
2285
|
+
showSessionPicker,
|
|
2286
|
+
setShowPluginTUI,
|
|
2287
|
+
setShowSessionPicker,
|
|
2288
|
+
handleModelConfirm
|
|
2289
|
+
} = useSideEffects({
|
|
2290
|
+
interactiveSession,
|
|
2291
|
+
addEntry,
|
|
2292
|
+
baseHandleSubmit,
|
|
2293
|
+
setSessionName
|
|
2294
|
+
});
|
|
2087
2295
|
useEffect4(() => {
|
|
2088
2296
|
const name = interactiveSession?.getName?.();
|
|
2089
2297
|
if (name && !sessionName) setSessionName(name);
|
|
@@ -2092,68 +2300,9 @@ function AppInner(props) {
|
|
|
2092
2300
|
const title = sessionName ? `Robota \u2014 ${sessionName}` : "Robota";
|
|
2093
2301
|
process.stdout.write(`\x1B]0;${title}\x07`);
|
|
2094
2302
|
}, [sessionName]);
|
|
2095
|
-
const handleSubmit = async (input) => {
|
|
2096
|
-
await baseHandleSubmit(input);
|
|
2097
|
-
const sideEffects = interactiveSession;
|
|
2098
|
-
if (sideEffects._pendingModelId) {
|
|
2099
|
-
const modelId = sideEffects._pendingModelId;
|
|
2100
|
-
delete sideEffects._pendingModelId;
|
|
2101
|
-
pendingModelChangeRef.current = modelId;
|
|
2102
|
-
setPendingModelId(modelId);
|
|
2103
|
-
return;
|
|
2104
|
-
}
|
|
2105
|
-
if (sideEffects._pendingLanguage) {
|
|
2106
|
-
const lang = sideEffects._pendingLanguage;
|
|
2107
|
-
delete sideEffects._pendingLanguage;
|
|
2108
|
-
const settingsPath = getUserSettingsPath();
|
|
2109
|
-
const settings = readSettings(settingsPath);
|
|
2110
|
-
settings.language = lang;
|
|
2111
|
-
writeSettings(settingsPath, settings);
|
|
2112
|
-
addEntry(
|
|
2113
|
-
messageToHistoryEntry2(createSystemMessage2(`Language set to "${lang}". Restarting...`))
|
|
2114
|
-
);
|
|
2115
|
-
setTimeout(() => exit(), EXIT_DELAY_MS);
|
|
2116
|
-
return;
|
|
2117
|
-
}
|
|
2118
|
-
if (sideEffects._resetRequested) {
|
|
2119
|
-
delete sideEffects._resetRequested;
|
|
2120
|
-
const settingsPath = getUserSettingsPath();
|
|
2121
|
-
if (deleteSettings(settingsPath)) {
|
|
2122
|
-
addEntry(messageToHistoryEntry2(createSystemMessage2(`Deleted ${settingsPath}. Exiting...`)));
|
|
2123
|
-
} else {
|
|
2124
|
-
addEntry(messageToHistoryEntry2(createSystemMessage2("No user settings found.")));
|
|
2125
|
-
}
|
|
2126
|
-
setTimeout(() => exit(), EXIT_DELAY_MS);
|
|
2127
|
-
return;
|
|
2128
|
-
}
|
|
2129
|
-
if (sideEffects._exitRequested) {
|
|
2130
|
-
delete sideEffects._exitRequested;
|
|
2131
|
-
setTimeout(() => exit(), EXIT_DELAY_MS);
|
|
2132
|
-
return;
|
|
2133
|
-
}
|
|
2134
|
-
if (sideEffects._triggerPluginTUI) {
|
|
2135
|
-
delete sideEffects._triggerPluginTUI;
|
|
2136
|
-
setShowPluginTUI(true);
|
|
2137
|
-
return;
|
|
2138
|
-
}
|
|
2139
|
-
if (sideEffects._triggerResumePicker) {
|
|
2140
|
-
delete sideEffects._triggerResumePicker;
|
|
2141
|
-
setShowSessionPicker(true);
|
|
2142
|
-
return;
|
|
2143
|
-
}
|
|
2144
|
-
if (sideEffects._sessionName) {
|
|
2145
|
-
const name = sideEffects._sessionName;
|
|
2146
|
-
delete sideEffects._sessionName;
|
|
2147
|
-
interactiveSession.setName(name);
|
|
2148
|
-
setSessionName(name);
|
|
2149
|
-
return;
|
|
2150
|
-
}
|
|
2151
|
-
};
|
|
2152
2303
|
useInput8(
|
|
2153
2304
|
(_input, key) => {
|
|
2154
|
-
if (key.escape && isThinking)
|
|
2155
|
-
handleAbort();
|
|
2156
|
-
}
|
|
2305
|
+
if (key.escape && isThinking) handleAbort();
|
|
2157
2306
|
},
|
|
2158
2307
|
{ isActive: !permissionRequest && !showPluginTUI && !showSessionPicker }
|
|
2159
2308
|
);
|
|
@@ -2165,117 +2314,60 @@ function AppInner(props) {
|
|
|
2165
2314
|
sessionId = session.getSessionId();
|
|
2166
2315
|
} catch {
|
|
2167
2316
|
}
|
|
2168
|
-
return /* @__PURE__ */
|
|
2169
|
-
/* @__PURE__ */
|
|
2170
|
-
/* @__PURE__ */
|
|
2317
|
+
return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", children: [
|
|
2318
|
+
/* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", paddingX: 1, marginBottom: 1, children: [
|
|
2319
|
+
/* @__PURE__ */ jsx15(Text15, { color: "cyan", bold: true, children: `
|
|
2171
2320
|
____ ___ ____ ___ _____ _
|
|
2172
2321
|
| _ \\ / _ \\| __ ) / _ \\_ _|/ \\
|
|
2173
2322
|
| |_) | | | | _ \\| | | || | / _ \\
|
|
2174
2323
|
| _ <| |_| | |_) | |_| || |/ ___ \\
|
|
2175
2324
|
|_| \\_\\\\___/|____/ \\___/ |_/_/ \\_\\
|
|
2176
2325
|
` }),
|
|
2177
|
-
/* @__PURE__ */
|
|
2326
|
+
/* @__PURE__ */ jsxs13(Text15, { dimColor: true, children: [
|
|
2178
2327
|
" v",
|
|
2179
2328
|
props.version ?? "0.0.0"
|
|
2180
2329
|
] })
|
|
2181
2330
|
] }),
|
|
2182
|
-
/* @__PURE__ */
|
|
2183
|
-
/* @__PURE__ */
|
|
2184
|
-
(isThinking || activeTools.length > 0) && /* @__PURE__ */
|
|
2331
|
+
/* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", paddingX: 1, flexGrow: 1, children: [
|
|
2332
|
+
/* @__PURE__ */ jsx15(MessageList, { history }),
|
|
2333
|
+
(isThinking || activeTools.length > 0) && /* @__PURE__ */ jsx15(Box13, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ jsx15(StreamingIndicator, { text: streamingText, activeTools }) })
|
|
2185
2334
|
] }),
|
|
2186
|
-
permissionRequest && /* @__PURE__ */
|
|
2187
|
-
pendingModelId && /* @__PURE__ */
|
|
2335
|
+
permissionRequest && /* @__PURE__ */ jsx15(PermissionPrompt, { request: permissionRequest }),
|
|
2336
|
+
pendingModelId && /* @__PURE__ */ jsx15(
|
|
2188
2337
|
ConfirmPrompt,
|
|
2189
2338
|
{
|
|
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
|
-
}
|
|
2339
|
+
message: `Change model to ${getModelName2(pendingModelId)}? This will restart the session.`,
|
|
2340
|
+
onSelect: handleModelConfirm
|
|
2219
2341
|
}
|
|
2220
2342
|
),
|
|
2221
|
-
showPluginTUI && /* @__PURE__ */
|
|
2343
|
+
showPluginTUI && /* @__PURE__ */ jsx15(
|
|
2222
2344
|
PluginTUI,
|
|
2223
2345
|
{
|
|
2224
2346
|
callbacks: pluginCallbacks,
|
|
2225
2347
|
onClose: () => setShowPluginTUI(false),
|
|
2226
|
-
addMessage: (msg) => addEntry(
|
|
2348
|
+
addMessage: (msg) => addEntry(messageToHistoryEntry3(createSystemMessage3(msg.content)))
|
|
2227
2349
|
}
|
|
2228
2350
|
),
|
|
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
|
-
}
|
|
2351
|
+
showSessionPicker && /* @__PURE__ */ jsx15(
|
|
2352
|
+
SessionPicker,
|
|
2353
|
+
{
|
|
2354
|
+
sessionStore: props.sessionStore,
|
|
2355
|
+
cwd: props.cwd,
|
|
2356
|
+
onSelect: (id) => {
|
|
2357
|
+
setShowSessionPicker(false);
|
|
2358
|
+
props.onSessionSwitch(id);
|
|
2359
|
+
},
|
|
2360
|
+
onCancel: () => {
|
|
2361
|
+
setShowSessionPicker(false);
|
|
2362
|
+
addEntry(messageToHistoryEntry3(createSystemMessage3("Session resume cancelled.")));
|
|
2271
2363
|
}
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
/* @__PURE__ */
|
|
2364
|
+
}
|
|
2365
|
+
),
|
|
2366
|
+
/* @__PURE__ */ jsx15(
|
|
2275
2367
|
StatusBar,
|
|
2276
2368
|
{
|
|
2277
2369
|
permissionMode,
|
|
2278
|
-
modelName: props.modelId ?
|
|
2370
|
+
modelName: props.modelId ? getModelName2(props.modelId) : "",
|
|
2279
2371
|
sessionId,
|
|
2280
2372
|
messageCount: history.length,
|
|
2281
2373
|
isThinking,
|
|
@@ -2285,7 +2377,7 @@ function AppInner(props) {
|
|
|
2285
2377
|
sessionName
|
|
2286
2378
|
}
|
|
2287
2379
|
),
|
|
2288
|
-
/* @__PURE__ */
|
|
2380
|
+
/* @__PURE__ */ jsx15(
|
|
2289
2381
|
InputArea,
|
|
2290
2382
|
{
|
|
2291
2383
|
onSubmit: handleSubmit,
|
|
@@ -2297,12 +2389,12 @@ function AppInner(props) {
|
|
|
2297
2389
|
sessionName
|
|
2298
2390
|
}
|
|
2299
2391
|
),
|
|
2300
|
-
/* @__PURE__ */
|
|
2392
|
+
/* @__PURE__ */ jsx15(Text15, { children: " " })
|
|
2301
2393
|
] });
|
|
2302
2394
|
}
|
|
2303
2395
|
|
|
2304
2396
|
// src/ui/render.tsx
|
|
2305
|
-
import { jsx as
|
|
2397
|
+
import { jsx as jsx16 } from "react/jsx-runtime";
|
|
2306
2398
|
function renderApp(options) {
|
|
2307
2399
|
process.on("unhandledRejection", (reason) => {
|
|
2308
2400
|
process.stderr.write(`
|
|
@@ -2316,7 +2408,7 @@ function renderApp(options) {
|
|
|
2316
2408
|
if (process.stdin.isTTY && process.stdout.isTTY) {
|
|
2317
2409
|
process.stdout.write("\x1B[?2004h");
|
|
2318
2410
|
}
|
|
2319
|
-
const instance = render(/* @__PURE__ */
|
|
2411
|
+
const instance = render(/* @__PURE__ */ jsx16(App, { ...options }), {
|
|
2320
2412
|
exitOnCtrlC: true
|
|
2321
2413
|
});
|
|
2322
2414
|
instance.waitUntilExit().then(() => {
|