xibecode 0.9.6 → 0.9.7
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/commands/chat.d.ts.map +1 -1
- package/dist/commands/chat.js +26 -14
- package/dist/commands/chat.js.map +1 -1
- package/dist/commands/run-pr.d.ts.map +1 -1
- package/dist/commands/run-pr.js +22 -11
- package/dist/commands/run-pr.js.map +1 -1
- package/dist/commands/run.d.ts.map +1 -1
- package/dist/commands/run.js +24 -5
- package/dist/commands/run.js.map +1 -1
- package/dist/commands/skills.d.ts +6 -0
- package/dist/commands/skills.d.ts.map +1 -0
- package/dist/commands/skills.js +109 -0
- package/dist/commands/skills.js.map +1 -0
- package/dist/core/agent.d.ts +5 -1
- package/dist/core/agent.d.ts.map +1 -1
- package/dist/core/agent.js +35 -11
- package/dist/core/agent.js.map +1 -1
- package/dist/core/skills.d.ts +10 -0
- package/dist/core/skills.d.ts.map +1 -1
- package/dist/core/skills.js +37 -0
- package/dist/core/skills.js.map +1 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/ink.d.ts +1 -1
- package/dist/ink.d.ts.map +1 -1
- package/dist/ink.js +1 -1
- package/dist/ink.js.map +1 -1
- package/dist/ui/claude-style-chat.d.ts.map +1 -1
- package/dist/ui/claude-style-chat.js +196 -80
- package/dist/ui/claude-style-chat.js.map +1 -1
- package/dist/ui/enhanced-tui.d.ts +0 -4
- package/dist/ui/enhanced-tui.d.ts.map +1 -1
- package/dist/ui/enhanced-tui.js +3 -8
- package/dist/ui/enhanced-tui.js.map +1 -1
- package/dist/utils/at-references.d.ts +14 -0
- package/dist/utils/at-references.d.ts.map +1 -0
- package/dist/utils/at-references.js +47 -0
- package/dist/utils/at-references.js.map +1 -0
- package/dist/utils/image-attachments.d.ts +12 -0
- package/dist/utils/image-attachments.d.ts.map +1 -0
- package/dist/utils/image-attachments.js +30 -0
- package/dist/utils/image-attachments.js.map +1 -0
- package/dist/webui/server.d.ts.map +1 -1
- package/dist/webui/server.js +32 -2
- package/dist/webui/server.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
3
3
|
import TextInput from 'ink-text-input';
|
|
4
|
-
import { Box, Text, createRoot, useApp, useInput } from '../ink.js';
|
|
4
|
+
import { Box, Static, Text, createRoot, useApp, useInput } from '../ink.js';
|
|
5
5
|
import * as fs from 'fs/promises';
|
|
6
6
|
import * as path from 'path';
|
|
7
7
|
import { ConfigManager, PROVIDER_CONFIGS } from '../utils/config.js';
|
|
@@ -14,7 +14,9 @@ import { renderAndRun } from '../interactiveHelpers.js';
|
|
|
14
14
|
import { AssistantMarkdown } from '../components/AssistantMarkdown.js';
|
|
15
15
|
import { formatToolArgs, formatToolOutcome, formatRunSwarmDetailLines } from '../utils/tool-display.js';
|
|
16
16
|
import { SPINNER_VERBS } from '../constants/spinnerVerbs.js';
|
|
17
|
-
|
|
17
|
+
import { extractAtReferences, splitAtReferences } from '../utils/at-references.js';
|
|
18
|
+
import { loadImageAttachment, mimeFromExtension } from '../utils/image-attachments.js';
|
|
19
|
+
const APP_VERSION = '0.9.7';
|
|
18
20
|
const HERO_LOGO = [
|
|
19
21
|
'██╗ ██╗██╗██████╗ ███████╗',
|
|
20
22
|
'╚██╗██╔╝██║██╔══██╗██╔════╝',
|
|
@@ -58,6 +60,14 @@ function isAnthropicWireFormat(requestFormat, provider, customProviderFormat) {
|
|
|
58
60
|
}
|
|
59
61
|
return false;
|
|
60
62
|
}
|
|
63
|
+
function computeWindowStart(total, selected, windowSize) {
|
|
64
|
+
if (total <= windowSize)
|
|
65
|
+
return 0;
|
|
66
|
+
const clampedSelected = Math.max(0, Math.min(selected, total - 1));
|
|
67
|
+
const maxStart = total - windowSize;
|
|
68
|
+
const start = clampedSelected - Math.floor(windowSize / 2);
|
|
69
|
+
return Math.max(0, Math.min(start, maxStart));
|
|
70
|
+
}
|
|
61
71
|
function lineColorKey(type) {
|
|
62
72
|
switch (type) {
|
|
63
73
|
case 'user':
|
|
@@ -117,10 +127,8 @@ function XibeCodeChatApp(props) {
|
|
|
117
127
|
const { exit } = useApp();
|
|
118
128
|
const [input, setInput] = useState('');
|
|
119
129
|
const [isRunning, setIsRunning] = useState(false);
|
|
120
|
-
const [paused, setPaused] = useState(false);
|
|
121
130
|
const [runStartedAt, setRunStartedAt] = useState(null);
|
|
122
131
|
const [runElapsedMs, setRunElapsedMs] = useState(0);
|
|
123
|
-
const [liveStats, setLiveStats] = useState(null);
|
|
124
132
|
const [wireFormat, setWireFormat] = useState(props.initialRequestFormat);
|
|
125
133
|
const [activeModel, setActiveModel] = useState(props.model);
|
|
126
134
|
const [activeMode, setActiveMode] = useState(props.initialMode);
|
|
@@ -156,33 +164,28 @@ function XibeCodeChatApp(props) {
|
|
|
156
164
|
const [configCostModeIndex, setConfigCostModeIndex] = useState(0);
|
|
157
165
|
const [workSpinnerFrame, setWorkSpinnerFrame] = useState(0);
|
|
158
166
|
const [workVerbIndex, setWorkVerbIndex] = useState(0);
|
|
167
|
+
const nextLineIdRef = useRef(1);
|
|
159
168
|
const [lines, setLines] = useState([
|
|
160
169
|
{
|
|
170
|
+
kind: 'line',
|
|
171
|
+
id: nextLineIdRef.current++,
|
|
161
172
|
type: 'info',
|
|
162
173
|
text: 'XibeCode interactive session. Type /exit to quit, /clear to reset the transcript.',
|
|
163
174
|
},
|
|
164
|
-
{ type: 'info', text: '
|
|
175
|
+
{ kind: 'line', id: nextLineIdRef.current++, type: 'info', text: 'Type /help for shortcuts.' },
|
|
165
176
|
]);
|
|
166
|
-
const pausedBuffer = useRef([]);
|
|
167
177
|
const pushLine = useCallback((line) => {
|
|
178
|
+
const withId = { ...line, kind: 'line', id: nextLineIdRef.current++ };
|
|
168
179
|
props.onUiLine?.(line);
|
|
169
|
-
if (paused) {
|
|
170
|
-
pausedBuffer.current.push(line);
|
|
171
|
-
return;
|
|
172
|
-
}
|
|
173
180
|
// Keep a much larger in-memory transcript so context doesn't vanish quickly.
|
|
174
|
-
setLines((prev) => [...prev.slice(-5000),
|
|
175
|
-
}, [
|
|
176
|
-
const
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
}
|
|
183
|
-
return next;
|
|
184
|
-
});
|
|
185
|
-
}, []);
|
|
181
|
+
setLines((prev) => [...prev.slice(-5000), withId]);
|
|
182
|
+
}, [props]);
|
|
183
|
+
const abortControllerRef = useRef(null);
|
|
184
|
+
const abortReasonRef = useRef('none');
|
|
185
|
+
const queuedPromptRef = useRef(null);
|
|
186
|
+
const lastVisibleOutputAtRef = useRef(Date.now());
|
|
187
|
+
const currentPromptRef = useRef(null);
|
|
188
|
+
const restartAttemptsRef = useRef(0);
|
|
186
189
|
const lastBgLineByTask = useRef(new Map());
|
|
187
190
|
useEffect(() => {
|
|
188
191
|
if (!isRunning)
|
|
@@ -226,9 +229,27 @@ function XibeCodeChatApp(props) {
|
|
|
226
229
|
}
|
|
227
230
|
const id = setInterval(() => {
|
|
228
231
|
setWorkSpinnerFrame((f) => (f + 1) % WORK_SPINNER_FRAMES.length);
|
|
229
|
-
},
|
|
232
|
+
}, 120);
|
|
230
233
|
return () => clearInterval(id);
|
|
231
234
|
}, [isRunning]);
|
|
235
|
+
useEffect(() => {
|
|
236
|
+
if (!isRunning)
|
|
237
|
+
return;
|
|
238
|
+
const id = setInterval(() => {
|
|
239
|
+
// Silent means: no visible output (tool_call/tool_result/stream_text/response) for 90s.
|
|
240
|
+
if (Date.now() - lastVisibleOutputAtRef.current > 90_000) {
|
|
241
|
+
if (abortControllerRef.current && abortReasonRef.current === 'none') {
|
|
242
|
+
abortReasonRef.current = 'watchdog';
|
|
243
|
+
pushLine({
|
|
244
|
+
type: 'info',
|
|
245
|
+
text: `No output for 90s — restarting (attempt ${Math.min(restartAttemptsRef.current + 1, 2)}/2)…`,
|
|
246
|
+
});
|
|
247
|
+
abortControllerRef.current.abort();
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}, 1000);
|
|
251
|
+
return () => clearInterval(id);
|
|
252
|
+
}, [isRunning, pushLine]);
|
|
232
253
|
useEffect(() => {
|
|
233
254
|
if (!isRunning) {
|
|
234
255
|
setRunStartedAt(null);
|
|
@@ -242,19 +263,6 @@ function XibeCodeChatApp(props) {
|
|
|
242
263
|
}, 250);
|
|
243
264
|
return () => clearInterval(id);
|
|
244
265
|
}, [isRunning]);
|
|
245
|
-
useEffect(() => {
|
|
246
|
-
if (!isRunning || !props.getLiveStats)
|
|
247
|
-
return;
|
|
248
|
-
const id = setInterval(() => {
|
|
249
|
-
try {
|
|
250
|
-
setLiveStats(props.getLiveStats());
|
|
251
|
-
}
|
|
252
|
-
catch {
|
|
253
|
-
// ignore
|
|
254
|
-
}
|
|
255
|
-
}, 1000);
|
|
256
|
-
return () => clearInterval(id);
|
|
257
|
-
}, [isRunning, props]);
|
|
258
266
|
useEffect(() => {
|
|
259
267
|
if (!isRunning)
|
|
260
268
|
return;
|
|
@@ -359,8 +367,14 @@ function XibeCodeChatApp(props) {
|
|
|
359
367
|
}, [activeMode, props, pushLine]);
|
|
360
368
|
const onSubmit = useCallback(async (value) => {
|
|
361
369
|
const trimmed = value.trim();
|
|
362
|
-
if (!trimmed
|
|
370
|
+
if (!trimmed)
|
|
371
|
+
return;
|
|
372
|
+
if (isRunning) {
|
|
373
|
+
queuedPromptRef.current = trimmed;
|
|
374
|
+
setInput('');
|
|
375
|
+
pushLine({ type: 'info', text: 'Queued message — will run next.' });
|
|
363
376
|
return;
|
|
377
|
+
}
|
|
364
378
|
const commandMatches = CHAT_COMMANDS.filter((command) => command.name.toLowerCase().startsWith(trimmed.toLowerCase()));
|
|
365
379
|
const exactMatch = CHAT_COMMANDS.some((command) => command.name.toLowerCase() === trimmed.toLowerCase());
|
|
366
380
|
const resolvedInput = trimmed.startsWith('/') && !exactMatch && commandMatches[selectedCommandIndex]
|
|
@@ -464,14 +478,21 @@ function XibeCodeChatApp(props) {
|
|
|
464
478
|
return;
|
|
465
479
|
}
|
|
466
480
|
if (resolvedInput === '/clear') {
|
|
467
|
-
|
|
481
|
+
pushLine({ type: 'info', text: '──────────── transcript cleared ────────────' });
|
|
468
482
|
return;
|
|
469
483
|
}
|
|
470
484
|
if (resolvedInput === '/help') {
|
|
471
485
|
setLines((prev) => [
|
|
472
486
|
...prev,
|
|
473
|
-
{ type: 'info', text: `Shortcuts: ${QUICK_HELP.join(' · ')}` },
|
|
474
487
|
{
|
|
488
|
+
kind: 'line',
|
|
489
|
+
id: nextLineIdRef.current++,
|
|
490
|
+
type: 'info',
|
|
491
|
+
text: `Shortcuts: ${QUICK_HELP.join(' · ')}`,
|
|
492
|
+
},
|
|
493
|
+
{
|
|
494
|
+
kind: 'line',
|
|
495
|
+
id: nextLineIdRef.current++,
|
|
475
496
|
type: 'info',
|
|
476
497
|
text: 'Press Ctrl+C to quit. Type any prompt and XibeCode will run agent mode.',
|
|
477
498
|
},
|
|
@@ -579,26 +600,94 @@ function XibeCodeChatApp(props) {
|
|
|
579
600
|
}
|
|
580
601
|
return;
|
|
581
602
|
}
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
603
|
+
const runOne = async (prompt) => {
|
|
604
|
+
currentPromptRef.current = prompt;
|
|
605
|
+
abortReasonRef.current = 'none';
|
|
606
|
+
lastVisibleOutputAtRef.current = Date.now();
|
|
607
|
+
abortControllerRef.current = new AbortController();
|
|
608
|
+
pushLine({ type: 'user', text: prompt });
|
|
609
|
+
setIsRunning(true);
|
|
610
|
+
try {
|
|
611
|
+
const startedAt = Date.now();
|
|
612
|
+
const refs = extractAtReferences(prompt, process.cwd());
|
|
613
|
+
const { image: imageRefs } = splitAtReferences(refs);
|
|
614
|
+
const images = [];
|
|
615
|
+
for (const ref of imageRefs) {
|
|
616
|
+
try {
|
|
617
|
+
const mime = mimeFromExtension(ref.extension);
|
|
618
|
+
if (!mime)
|
|
619
|
+
continue;
|
|
620
|
+
const attachment = await loadImageAttachment(ref.resolvedPath, { mime });
|
|
621
|
+
images.push(attachment);
|
|
622
|
+
}
|
|
623
|
+
catch (err) {
|
|
624
|
+
const message = err instanceof Error ? err.message : 'Failed to load image';
|
|
625
|
+
pushLine({ type: 'error', text: message });
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
const stats = await props.runPrompt(prompt, pushLine, {
|
|
629
|
+
images: images.length ? images : undefined,
|
|
630
|
+
signal: abortControllerRef.current.signal,
|
|
631
|
+
onVisibleOutput: () => {
|
|
632
|
+
lastVisibleOutputAtRef.current = Date.now();
|
|
633
|
+
},
|
|
634
|
+
});
|
|
635
|
+
const elapsedMs = Date.now() - startedAt;
|
|
636
|
+
const seconds = (elapsedMs / 1000).toFixed(1);
|
|
637
|
+
pushLine({
|
|
638
|
+
type: 'info',
|
|
639
|
+
text: `Done in ${seconds}s` + (stats.costLabel ? ` · cost ${stats.costLabel}` : ''),
|
|
640
|
+
});
|
|
641
|
+
}
|
|
642
|
+
finally {
|
|
643
|
+
abortControllerRef.current = null;
|
|
644
|
+
currentPromptRef.current = null;
|
|
645
|
+
setIsRunning(false);
|
|
646
|
+
}
|
|
647
|
+
};
|
|
648
|
+
const isAbortError = (err) => {
|
|
649
|
+
if (!err || typeof err !== 'object')
|
|
650
|
+
return false;
|
|
651
|
+
const anyErr = err;
|
|
652
|
+
return anyErr.name === 'AbortError' || String(anyErr.message || '').toLowerCase().includes('aborted');
|
|
653
|
+
};
|
|
654
|
+
// Watchdog auto-restart loop (up to 2 restarts)
|
|
655
|
+
restartAttemptsRef.current = 0;
|
|
656
|
+
let promptToRun = resolvedInput;
|
|
657
|
+
while (promptToRun) {
|
|
658
|
+
try {
|
|
659
|
+
await runOne(promptToRun);
|
|
660
|
+
break;
|
|
661
|
+
}
|
|
662
|
+
catch (err) {
|
|
663
|
+
if (isAbortError(err) && abortReasonRef.current === 'watchdog') {
|
|
664
|
+
restartAttemptsRef.current += 1;
|
|
665
|
+
if (restartAttemptsRef.current <= 2) {
|
|
666
|
+
// Restart same prompt
|
|
667
|
+
continue;
|
|
668
|
+
}
|
|
669
|
+
pushLine({ type: 'error', text: 'Restart limit reached (2). Stopping.' });
|
|
670
|
+
break;
|
|
671
|
+
}
|
|
672
|
+
if (isAbortError(err) && abortReasonRef.current === 'user') {
|
|
673
|
+
pushLine({ type: 'info', text: 'Cancelled.' });
|
|
674
|
+
break;
|
|
675
|
+
}
|
|
676
|
+
const message = err instanceof Error ? err.message : 'Unknown error';
|
|
677
|
+
pushLine({ type: 'error', text: message });
|
|
678
|
+
break;
|
|
679
|
+
}
|
|
680
|
+
finally {
|
|
681
|
+
// If a message was queued during this run, send it next.
|
|
682
|
+
if (!isRunning && queuedPromptRef.current) {
|
|
683
|
+
const q = queuedPromptRef.current;
|
|
684
|
+
queuedPromptRef.current = null;
|
|
685
|
+
promptToRun = q;
|
|
686
|
+
}
|
|
687
|
+
else {
|
|
688
|
+
promptToRun = null;
|
|
689
|
+
}
|
|
690
|
+
}
|
|
602
691
|
}
|
|
603
692
|
}, [
|
|
604
693
|
applyModel,
|
|
@@ -617,6 +706,7 @@ function XibeCodeChatApp(props) {
|
|
|
617
706
|
wireFormat,
|
|
618
707
|
applyMode,
|
|
619
708
|
startSetupWizard,
|
|
709
|
+
isRunning,
|
|
620
710
|
]);
|
|
621
711
|
const normalizedInput = input.trim().toLowerCase();
|
|
622
712
|
const isSlashMode = input.startsWith('/');
|
|
@@ -631,13 +721,21 @@ function XibeCodeChatApp(props) {
|
|
|
631
721
|
mode.label.toLowerCase().includes(modeFilter) ||
|
|
632
722
|
mode.description.toLowerCase().includes(modeFilter));
|
|
633
723
|
const filteredCommands = CHAT_COMMANDS.filter((command) => command.name.toLowerCase().startsWith(normalizedInput || '/'));
|
|
724
|
+
const MODEL_PICKER_WINDOW = 14;
|
|
725
|
+
const modelPickerStart = computeWindowStart(filteredModels.length, selectedModelIndex, MODEL_PICKER_WINDOW);
|
|
726
|
+
const visibleModelOptions = filteredModels.slice(modelPickerStart, modelPickerStart + MODEL_PICKER_WINDOW);
|
|
727
|
+
const setupModelPickerStart = computeWindowStart(setupModels.length, setupSelectedModelIndex, MODEL_PICKER_WINDOW);
|
|
728
|
+
const visibleSetupModelOptions = setupModels.slice(setupModelPickerStart, setupModelPickerStart + MODEL_PICKER_WINDOW);
|
|
634
729
|
useInput((inputKey, key) => {
|
|
635
730
|
if (key.ctrl && inputKey === 'c') {
|
|
636
731
|
exit();
|
|
637
732
|
return;
|
|
638
733
|
}
|
|
639
|
-
if (
|
|
640
|
-
|
|
734
|
+
if (isRunning && key.escape) {
|
|
735
|
+
if (abortControllerRef.current && abortReasonRef.current === 'none') {
|
|
736
|
+
abortReasonRef.current = 'user';
|
|
737
|
+
abortControllerRef.current.abort();
|
|
738
|
+
}
|
|
641
739
|
return;
|
|
642
740
|
}
|
|
643
741
|
if (configMenuOpen && !isRunning) {
|
|
@@ -817,9 +915,8 @@ function XibeCodeChatApp(props) {
|
|
|
817
915
|
const picked = setupModels[setupSelectedModelIndex];
|
|
818
916
|
if (!picked)
|
|
819
917
|
return;
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
pushLine({ type: 'info', text: `Model set to: ${picked}` });
|
|
918
|
+
// Use the same path as /model so the live status line updates immediately.
|
|
919
|
+
void applyModel(picked);
|
|
823
920
|
setSetupModelPickerOpen(false);
|
|
824
921
|
setSetupStep('idle');
|
|
825
922
|
pushLine({ type: 'info', text: 'Setup complete. Type a prompt to start.' });
|
|
@@ -907,12 +1004,8 @@ function XibeCodeChatApp(props) {
|
|
|
907
1004
|
? `${workVerbPhrase.slice(0, 30)}…`
|
|
908
1005
|
: workVerbPhrase;
|
|
909
1006
|
const tail = `working ${WORK_SPINNER_FRAMES[workSpinnerFrame]} · ${shortVerb}`;
|
|
910
|
-
const pauseLabel = paused ? ' | PAUSED' : '';
|
|
911
1007
|
const elapsed = runElapsedMs ? ` | elapsed ${(runElapsedMs / 1000).toFixed(1)}s` : '';
|
|
912
|
-
|
|
913
|
-
? ` | tokens ${liveStats.inputTokens}/${liveStats.outputTokens}/${liveStats.totalTokens}`
|
|
914
|
-
: '';
|
|
915
|
-
return `model: ${activeModel} | format: ${wireFormat} | mode: ${activeMode} | provider: ${props.provider || 'auto'} | ${tail}${pauseLabel}${elapsed}${tokens}`;
|
|
1008
|
+
return `model: ${activeModel} | format: ${wireFormat} | mode: ${activeMode} | provider: ${props.provider || 'auto'} | ${tail}${elapsed}`;
|
|
916
1009
|
}, [
|
|
917
1010
|
activeModel,
|
|
918
1011
|
activeMode,
|
|
@@ -921,17 +1014,34 @@ function XibeCodeChatApp(props) {
|
|
|
921
1014
|
wireFormat,
|
|
922
1015
|
workSpinnerFrame,
|
|
923
1016
|
workVerbPhrase,
|
|
924
|
-
paused,
|
|
925
1017
|
runElapsedMs,
|
|
926
|
-
liveStats,
|
|
927
1018
|
]);
|
|
928
|
-
|
|
1019
|
+
/** Taller transcript area once there is real chat; hero stays visible for the whole session. */
|
|
1020
|
+
const hasChatContent = lines.some((l) => l.kind === 'line' &&
|
|
1021
|
+
(l.type === 'user' ||
|
|
1022
|
+
l.type === 'assistant' ||
|
|
1023
|
+
l.type === 'tool' ||
|
|
1024
|
+
l.type === 'tool_out' ||
|
|
1025
|
+
l.type === 'error'));
|
|
929
1026
|
const providerName = props.provider ? props.provider.toUpperCase() : 'AUTO';
|
|
930
1027
|
const divider = '─'.repeat(98);
|
|
931
|
-
const
|
|
932
|
-
return (_jsxs(Box, { flexDirection: "column", paddingX: 1, children: [
|
|
933
|
-
|
|
934
|
-
|
|
1028
|
+
const staticItems = useMemo(() => [{ kind: 'hero', id: 0 }, ...lines], [lines]);
|
|
1029
|
+
return (_jsxs(Box, { flexDirection: "column", paddingX: 1, children: [_jsx(Text, { dimColor: true, children: status }), _jsx(Text, { color: "subtle", children: divider }), _jsx(Static, { items: staticItems, children: (item) => {
|
|
1030
|
+
if (item.kind === 'hero') {
|
|
1031
|
+
return (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [HERO_LOGO.map((line, idx) => (_jsx(React.Fragment, { children: _jsx(Text, { bold: true, color: idx < 6 ? 'claude' : 'suggestion', children: line }) }, `logo-${idx}`))), _jsx(Text, { color: "suggestion", children: "\u2726 Any model, Every tool, Zero limits. \u2726" }), _jsxs(Box, { marginTop: 1, borderStyle: "round", borderColor: "claude", flexDirection: "column", paddingX: 1, children: [_jsxs(Text, { children: [_jsx(Text, { color: "inactive", children: "Provider " }), _jsx(Text, { color: "claude", bold: true, children: providerName })] }), _jsxs(Text, { children: [_jsx(Text, { color: "inactive", children: "Model " }), _jsx(Text, { children: activeModel })] }), _jsxs(Text, { children: [_jsx(Text, { color: "inactive", children: "Endpoint " }), _jsx(Text, { children: props.baseUrl || 'provider default' })] }), _jsxs(Text, { children: [_jsx(Text, { color: "inactive", children: "Format " }), _jsxs(Text, { children: [wireFormat, ' ', _jsxs(Text, { dimColor: true, children: ["(", isAnthropicWireFormat(wireFormat, props.provider, props.customProviderFormat)
|
|
1032
|
+
? 'Anthropic Messages'
|
|
1033
|
+
: 'OpenAI chat', ")"] })] })] })] }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { color: "suggestion", children: "\u25C8 cloud" }), _jsx(Text, { color: "inactive", children: " Ready \u2014 type " }), _jsx(Text, { color: "claude", children: "/help" }), _jsx(Text, { color: "inactive", children: " to begin" })] }), _jsxs(Text, { color: "inactive", children: ["xibecode ", _jsxs(Text, { color: "claude", children: ["v", APP_VERSION] })] }), _jsx(Text, { color: "subtle", children: '─'.repeat(98) }), _jsx(Text, { color: "inactive", children: "Agent transcript" })] }));
|
|
1034
|
+
}
|
|
1035
|
+
return item.type === 'assistant' ? (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsxs(Text, { bold: true, color: prefixColorKey('assistant'), children: [prefixForType('assistant'), ":"] }), _jsx(Box, { marginLeft: 2, flexDirection: "column", children: _jsx(AssistantMarkdown, { content: item.text }) })] })) : (_jsxs(Text, { children: [_jsxs(Text, { bold: true, color: prefixColorKey(item.type), children: [prefixForType(item.type), ":", ' '] }), _jsx(Text, { color: lineColorKey(item.type), children: item.text })] }));
|
|
1036
|
+
} }), !hasChatContent && (_jsx(Box, { marginTop: 1, flexDirection: "column", children: _jsx(Text, { color: "inactive", children: "(send a message to start)" }) })), _jsx(Text, { color: "subtle", children: divider }), isRunning && (_jsx(Box, { marginTop: 1, paddingX: 1, borderStyle: "round", borderColor: "claudeBlue_FOR_SYSTEM_SPINNER", flexDirection: "column", children: _jsxs(Text, { wrap: "wrap", children: [_jsxs(Text, { bold: true, color: "claudeBlue_FOR_SYSTEM_SPINNER", children: [WORK_SPINNER_FRAMES[workSpinnerFrame], ' '] }), _jsx(Text, { bold: true, color: "briefLabelClaude", children: workVerbPhrase })] }) })), _jsxs(Box, { marginTop: 1, borderStyle: "round", borderColor: "claude", paddingX: 1, children: [_jsx(Text, { color: "claude", children: '> ' }), _jsx(TextInput, { value: input, onChange: setInput, onSubmit: onSubmit, placeholder: isRunning ? 'Waiting for response…' : 'Message XibeCode…' })] }), isSlashMode && (_jsxs(Box, { marginTop: 1, borderStyle: "round", borderColor: "suggestion", flexDirection: "column", paddingX: 1, children: [_jsx(Text, { color: "suggestion", bold: true, children: "Commands" }), filteredCommands.length === 0 ? (_jsxs(Text, { color: "inactive", children: ["No commands match \"", input, "\""] })) : (filteredCommands.map((command, index) => (_jsx(React.Fragment, { children: _jsxs(Text, { children: [_jsx(Text, { color: index === selectedCommandIndex ? 'claude' : 'inactive', children: index === selectedCommandIndex ? '▸ ' : ' ' }), _jsx(Text, { bold: true, color: index === selectedCommandIndex ? 'claude' : 'text', children: command.name }), _jsxs(Text, { color: "inactive", children: [" \u2014 ", command.description] })] }) }, command.name)))), _jsx(Text, { color: "subtle", children: "Use \u2191/\u2193 to navigate, Tab to autocomplete." })] })), modelPickerOpen && (_jsxs(Box, { marginTop: 1, borderStyle: "round", borderColor: "claude", flexDirection: "column", paddingX: 1, children: [_jsx(Text, { bold: true, color: "claude", children: "Select model" }), isModelListLoading ? (_jsx(Text, { color: "inactive", children: "Loading models from provider..." })) : filteredModels.length === 0 ? (_jsx(Text, { color: "inactive", children: "No models matched current filter." })) : (visibleModelOptions.map((modelName, index) => {
|
|
1037
|
+
const absoluteIndex = modelPickerStart + index;
|
|
1038
|
+
const isSelected = absoluteIndex === selectedModelIndex;
|
|
1039
|
+
return (_jsx(React.Fragment, { children: _jsxs(Text, { children: [_jsx(Text, { color: isSelected ? 'claude' : 'inactive', children: isSelected ? '▸ ' : ' ' }), _jsx(Text, { color: isSelected ? 'claude' : 'text', children: modelName })] }) }, modelName));
|
|
1040
|
+
})), _jsx(Text, { color: "subtle", children: "\u2191/\u2193 navigate \u2022 Enter apply \u2022 Esc close" })] })), setupModelPickerOpen && (_jsxs(Box, { marginTop: 1, borderStyle: "round", borderColor: "claude", flexDirection: "column", paddingX: 1, children: [_jsx(Text, { bold: true, color: "claude", children: "Setup: select model" }), setupModels.length === 0 ? (_jsx(Text, { color: "inactive", children: "No models loaded." })) : (visibleSetupModelOptions.map((modelName, index) => {
|
|
1041
|
+
const absoluteIndex = setupModelPickerStart + index;
|
|
1042
|
+
const isSelected = absoluteIndex === setupSelectedModelIndex;
|
|
1043
|
+
return (_jsx(React.Fragment, { children: _jsxs(Text, { children: [_jsx(Text, { color: isSelected ? 'claude' : 'inactive', children: isSelected ? '▸ ' : ' ' }), _jsx(Text, { color: isSelected ? 'claude' : 'text', children: modelName })] }) }, modelName));
|
|
1044
|
+
})), _jsx(Text, { color: "subtle", children: "\u2191/\u2193 navigate \u2022 Enter apply \u2022 Esc cancel" })] })), configMenuOpen && (_jsxs(Box, { marginTop: 1, borderStyle: "round", borderColor: "suggestion", flexDirection: "column", paddingX: 1, children: [_jsx(Text, { bold: true, color: "suggestion", children: "Config" }), CONFIG_MENU.map((item, index) => (_jsx(React.Fragment, { children: _jsxs(Text, { children: [_jsx(Text, { color: index === configSelectedIndex ? 'claude' : 'inactive', children: index === configSelectedIndex ? '▸ ' : ' ' }), _jsx(Text, { bold: true, color: index === configSelectedIndex ? 'claude' : 'text', children: item.label }), _jsxs(Text, { color: "inactive", children: [" \u2014 ", item.description] })] }) }, item.value))), _jsx(Text, { color: "subtle", children: "\u2191/\u2193 navigate \u2022 Enter select \u2022 Esc close" })] })), configProviderPickerOpen && (_jsxs(Box, { marginTop: 1, borderStyle: "round", borderColor: "suggestion", flexDirection: "column", paddingX: 1, children: [_jsx(Text, { bold: true, color: "suggestion", children: "Provider" }), [
|
|
935
1045
|
'auto-detect',
|
|
936
1046
|
'openai',
|
|
937
1047
|
'anthropic',
|
|
@@ -1027,7 +1137,7 @@ export async function launchClaudeStyleChat(options) {
|
|
|
1027
1137
|
}
|
|
1028
1138
|
return unique;
|
|
1029
1139
|
};
|
|
1030
|
-
const runPrompt = async (prompt, onLine) => {
|
|
1140
|
+
const runPrompt = async (prompt, onLine, opts) => {
|
|
1031
1141
|
activeAgent.removeAllListeners('event');
|
|
1032
1142
|
let streamedBuffer = '';
|
|
1033
1143
|
activeAgent.on('event', (event) => {
|
|
@@ -1039,6 +1149,7 @@ export async function launchClaudeStyleChat(options) {
|
|
|
1039
1149
|
});
|
|
1040
1150
|
break;
|
|
1041
1151
|
case 'tool_call': {
|
|
1152
|
+
opts?.onVisibleOutput?.();
|
|
1042
1153
|
const name = String(event.data?.name ?? 'tool');
|
|
1043
1154
|
const input = event.data?.input;
|
|
1044
1155
|
const args = formatToolArgs(name, input);
|
|
@@ -1049,6 +1160,7 @@ export async function launchClaudeStyleChat(options) {
|
|
|
1049
1160
|
break;
|
|
1050
1161
|
}
|
|
1051
1162
|
case 'tool_result': {
|
|
1163
|
+
opts?.onVisibleOutput?.();
|
|
1052
1164
|
const name = String(event.data?.name ?? 'tool');
|
|
1053
1165
|
const result = event.data?.result;
|
|
1054
1166
|
const success = event.data?.success !== false;
|
|
@@ -1070,6 +1182,7 @@ export async function launchClaudeStyleChat(options) {
|
|
|
1070
1182
|
break;
|
|
1071
1183
|
}
|
|
1072
1184
|
case 'stream_text':
|
|
1185
|
+
opts?.onVisibleOutput?.();
|
|
1073
1186
|
streamedBuffer += event.data?.text || '';
|
|
1074
1187
|
break;
|
|
1075
1188
|
case 'stream_end':
|
|
@@ -1079,6 +1192,7 @@ export async function launchClaudeStyleChat(options) {
|
|
|
1079
1192
|
streamedBuffer = '';
|
|
1080
1193
|
break;
|
|
1081
1194
|
case 'response':
|
|
1195
|
+
opts?.onVisibleOutput?.();
|
|
1082
1196
|
onLine({ type: 'assistant', text: event.data?.text || '' });
|
|
1083
1197
|
break;
|
|
1084
1198
|
case 'error':
|
|
@@ -1093,12 +1207,14 @@ export async function launchClaudeStyleChat(options) {
|
|
|
1093
1207
|
break;
|
|
1094
1208
|
}
|
|
1095
1209
|
});
|
|
1096
|
-
await activeAgent.run(prompt, toolExecutor.getTools(), toolExecutor
|
|
1210
|
+
await activeAgent.run(prompt, toolExecutor.getTools(), toolExecutor, {
|
|
1211
|
+
images: opts?.images,
|
|
1212
|
+
signal: opts?.signal,
|
|
1213
|
+
});
|
|
1097
1214
|
activeMode = activeAgent.getMode();
|
|
1098
1215
|
toolExecutor.setMode(activeMode);
|
|
1099
1216
|
return activeAgent.getStats();
|
|
1100
1217
|
};
|
|
1101
|
-
const getLiveStats = () => activeAgent.getStats();
|
|
1102
1218
|
const listBackgroundTasks = async () => {
|
|
1103
1219
|
const result = await toolExecutor.execute('list_background_tasks', {});
|
|
1104
1220
|
if (result?.success && Array.isArray(result.tasks)) {
|
|
@@ -1129,6 +1245,6 @@ export async function launchClaudeStyleChat(options) {
|
|
|
1129
1245
|
logChain = logChain.then(() => fs.appendFile(chatLogPath, rendered, 'utf8')).catch(() => { });
|
|
1130
1246
|
};
|
|
1131
1247
|
const root = createRoot({ exitOnCtrlC: true });
|
|
1132
|
-
await renderAndRun(root, _jsx(XibeCodeChatApp, { model: model, initialMode: activeMode, provider: provider, baseUrl: baseUrl, defaultModel: model, modeOptions: modeOptions, initialRequestFormat: wireFormat, customProviderFormat: customProviderFormat, profile: options.profile, runPrompt: runPrompt,
|
|
1248
|
+
await renderAndRun(root, _jsx(XibeCodeChatApp, { model: model, initialMode: activeMode, provider: provider, baseUrl: baseUrl, defaultModel: model, modeOptions: modeOptions, initialRequestFormat: wireFormat, customProviderFormat: customProviderFormat, profile: options.profile, runPrompt: runPrompt, listBackgroundTasks: listBackgroundTasks, checkBackgroundTask: checkBackgroundTask, onUiLine: appendLogLine, loadModels: loadModels, onModelChange: onModelChange, onModeChange: onModeChange, onWireFormatChange: onWireFormatChange }));
|
|
1133
1249
|
}
|
|
1134
1250
|
//# sourceMappingURL=claude-style-chat.js.map
|