xibecode 1.3.2 → 1.3.5
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 +36 -1
- package/dist/commands/chat.js.map +1 -1
- package/dist/commands/cloud-pull.d.ts +10 -0
- package/dist/commands/cloud-pull.d.ts.map +1 -0
- package/dist/commands/cloud-pull.js +79 -0
- package/dist/commands/cloud-pull.js.map +1 -0
- package/dist/commands/config.d.ts +7 -0
- package/dist/commands/config.d.ts.map +1 -1
- package/dist/commands/config.js +68 -0
- package/dist/commands/config.js.map +1 -1
- package/dist/commands/run-pr.d.ts.map +1 -1
- package/dist/commands/run-pr.js +41 -0
- package/dist/commands/run-pr.js.map +1 -1
- package/dist/commands/run.d.ts.map +1 -1
- package/dist/commands/run.js +39 -0
- package/dist/commands/run.js.map +1 -1
- package/dist/index.js +64 -1
- package/dist/index.js.map +1 -1
- package/dist/ui/claude-style-chat.d.ts.map +1 -1
- package/dist/ui/claude-style-chat.js +243 -34
- package/dist/ui/claude-style-chat.js.map +1 -1
- package/dist/utils/cloud-gateway.d.ts +10 -0
- package/dist/utils/cloud-gateway.d.ts.map +1 -0
- package/dist/utils/cloud-gateway.js +99 -0
- package/dist/utils/cloud-gateway.js.map +1 -0
- package/dist/utils/cloud-runtime-hints.d.ts +8 -0
- package/dist/utils/cloud-runtime-hints.d.ts.map +1 -0
- package/dist/utils/cloud-runtime-hints.js +17 -0
- package/dist/utils/cloud-runtime-hints.js.map +1 -0
- package/dist/utils/cloud-sync-feedback.d.ts +6 -0
- package/dist/utils/cloud-sync-feedback.d.ts.map +1 -0
- package/dist/utils/cloud-sync-feedback.js +77 -0
- package/dist/utils/cloud-sync-feedback.js.map +1 -0
- package/dist/utils/config.d.ts +28 -0
- package/dist/utils/config.d.ts.map +1 -1
- package/dist/utils/config.js +79 -0
- package/dist/utils/config.js.map +1 -1
- package/dist/utils/remote-execution.d.ts +20 -0
- package/dist/utils/remote-execution.d.ts.map +1 -0
- package/dist/utils/remote-execution.js +339 -0
- package/dist/utils/remote-execution.js.map +1 -0
- package/dist/utils/sandbox-sync.d.ts +14 -0
- package/dist/utils/sandbox-sync.d.ts.map +1 -0
- package/dist/utils/sandbox-sync.js +206 -0
- package/dist/utils/sandbox-sync.js.map +1 -0
- package/package.json +2 -2
|
@@ -1,6 +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 { createRequire } from 'module';
|
|
4
|
+
import { spawn } from 'child_process';
|
|
4
5
|
import TextInput from 'ink-text-input';
|
|
5
6
|
import { Box, Static, Text, createRoot, useApp, useInput } from '../ink.js';
|
|
6
7
|
import * as fs from 'fs/promises';
|
|
@@ -17,10 +18,15 @@ import { renderAndRun } from '../interactiveHelpers.js';
|
|
|
17
18
|
import { AssistantMarkdown } from '../components/AssistantMarkdown.js';
|
|
18
19
|
import { formatToolArgs, formatToolOutcome, formatRunSwarmDetailLines } from '../utils/tool-display.js';
|
|
19
20
|
import { SPINNER_VERBS } from '../constants/spinnerVerbs.js';
|
|
20
|
-
import {
|
|
21
|
+
import { collectImageReferencesForPrompt } from 'xibecode-core';
|
|
21
22
|
import { loadImageAttachment, mimeFromExtension } from '../utils/image-attachments.js';
|
|
22
23
|
import { SessionManager } from 'xibecode-core';
|
|
23
24
|
import { AutoMemoryManager, HooksManager, SettingsManager as CoreSettingsManager } from 'xibecode-core';
|
|
25
|
+
import { cloudPullCommand } from '../commands/cloud-pull.js';
|
|
26
|
+
import { attachRemoteExecution, codingToolExecutorRemoteOptions, getRuntimeStatusLabel, remoteToolSandboxIdForAgent, remoteToolWorkspaceRootForAgent, resolveRemoteExecutionConfig, } from '../utils/remote-execution.js';
|
|
27
|
+
import { syncWorkspaceToSandbox } from '../utils/sandbox-sync.js';
|
|
28
|
+
import { withCloudWorkspaceSyncSpinner } from '../utils/cloud-sync-feedback.js';
|
|
29
|
+
import { getCloudRuntimeHint } from '../utils/cloud-runtime-hints.js';
|
|
24
30
|
function isAbortLikeError(err) {
|
|
25
31
|
if (!err || typeof err !== 'object')
|
|
26
32
|
return false;
|
|
@@ -29,6 +35,42 @@ function isAbortLikeError(err) {
|
|
|
29
35
|
anyErr.type === 'aborted' ||
|
|
30
36
|
String(anyErr.message || '').toLowerCase().includes('aborted'));
|
|
31
37
|
}
|
|
38
|
+
async function runCommandCapture(command, args, cwd = process.cwd()) {
|
|
39
|
+
return new Promise((resolve) => {
|
|
40
|
+
const child = spawn(command, args, {
|
|
41
|
+
cwd,
|
|
42
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
43
|
+
});
|
|
44
|
+
let stdout = '';
|
|
45
|
+
let stderr = '';
|
|
46
|
+
child.stdout.on('data', (chunk) => {
|
|
47
|
+
stdout += String(chunk);
|
|
48
|
+
});
|
|
49
|
+
child.stderr.on('data', (chunk) => {
|
|
50
|
+
stderr += String(chunk);
|
|
51
|
+
});
|
|
52
|
+
child.on('error', (error) => {
|
|
53
|
+
stderr += error.message;
|
|
54
|
+
resolve({ code: 1, stdout, stderr });
|
|
55
|
+
});
|
|
56
|
+
child.on('close', (code) => {
|
|
57
|
+
resolve({ code: code ?? 0, stdout, stderr });
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
function buildAutoCommitMessage(stagedFiles, shortstat) {
|
|
62
|
+
const docsOnly = stagedFiles.length > 0
|
|
63
|
+
&& stagedFiles.every((file) => /\.(md|mdx|txt|rst)$/i.test(file) || file.toLowerCase().includes('docs'));
|
|
64
|
+
const type = docsOnly ? 'docs' : 'chore';
|
|
65
|
+
if (stagedFiles.length === 1) {
|
|
66
|
+
return `${type}: update ${stagedFiles[0]}`;
|
|
67
|
+
}
|
|
68
|
+
if (stagedFiles.length === 0) {
|
|
69
|
+
return `${type}: update project files`;
|
|
70
|
+
}
|
|
71
|
+
const stat = shortstat.trim();
|
|
72
|
+
return `${type}: update ${stagedFiles.length} files${stat ? ` (${stat})` : ''}`;
|
|
73
|
+
}
|
|
32
74
|
function summarizeToolResultContent(content) {
|
|
33
75
|
const raw = typeof content === 'string' ? content : JSON.stringify(content ?? '');
|
|
34
76
|
try {
|
|
@@ -102,7 +144,7 @@ const HERO_LOGO = [
|
|
|
102
144
|
const WORK_SPINNER_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
103
145
|
/** How fast to advance OpenClaude-style spinner verbs (ms) */
|
|
104
146
|
const WORK_VERB_ROTATE_MS = 2400;
|
|
105
|
-
const QUICK_HELP = ['/help', '/mode', '/format', '/model', '/setup', '/config', '/memory', '/hooks', '/donate', '/sponsor', '/clear', '/exit'];
|
|
147
|
+
const QUICK_HELP = ['/help', '/mode', '/format', '/model', '/setup', '/config', '/memory', '/hooks', '/cpull', '/commit', '/donate', '/sponsor', '/clear', '/exit'];
|
|
106
148
|
const CHAT_COMMANDS = [
|
|
107
149
|
{ name: '/help', description: 'Show available shortcuts and usage hints' },
|
|
108
150
|
{ name: '/mode', description: 'Switch agent mode from an interactive picker' },
|
|
@@ -113,6 +155,8 @@ const CHAT_COMMANDS = [
|
|
|
113
155
|
{ name: '/config', description: 'Show current config and quick config hints' },
|
|
114
156
|
{ name: '/memory', description: 'Show auto-memories for this project' },
|
|
115
157
|
{ name: '/hooks', description: 'Show registered lifecycle hooks' },
|
|
158
|
+
{ name: '/cpull', description: 'Pull current cloud sandbox changes locally (supports --apply)' },
|
|
159
|
+
{ name: '/commit', description: 'Stage all changes and commit (auto message or custom text)' },
|
|
116
160
|
{ name: '/donate', description: 'Open the donation page in your browser' },
|
|
117
161
|
{ name: '/sponsor', description: 'Open the sponsorship page in your browser' },
|
|
118
162
|
{ name: '/exit', description: 'Exit the interactive chat session' },
|
|
@@ -252,11 +296,14 @@ function XibeCodeChatApp(props) {
|
|
|
252
296
|
{ kind: 'line', id: nextLineIdRef.current++, type: 'info', text: 'Type /help for shortcuts.' },
|
|
253
297
|
];
|
|
254
298
|
if (sessionIdRef.current) {
|
|
299
|
+
const hasHistory = Boolean(initialMessagesRef.current && initialMessagesRef.current.length > 0);
|
|
255
300
|
base.push({
|
|
256
301
|
kind: 'line',
|
|
257
302
|
id: nextLineIdRef.current++,
|
|
258
303
|
type: 'info',
|
|
259
|
-
text:
|
|
304
|
+
text: hasHistory
|
|
305
|
+
? `Resumed session: ${sessionIdRef.current}`
|
|
306
|
+
: `Session: ${sessionIdRef.current}`,
|
|
260
307
|
});
|
|
261
308
|
}
|
|
262
309
|
if (initialMessagesRef.current && initialMessagesRef.current.length > 0) {
|
|
@@ -407,10 +454,12 @@ function XibeCodeChatApp(props) {
|
|
|
407
454
|
const provider = config.get('provider') ?? undefined;
|
|
408
455
|
const model = config.getModel(costMode === 'economy');
|
|
409
456
|
const baseUrl = config.getBaseUrl();
|
|
457
|
+
const sandboxMode = config.getSandboxMode();
|
|
458
|
+
const sandboxGateway = config.getSandboxGatewayUrl();
|
|
410
459
|
const requestFormat = config.get('requestFormat') ?? 'auto';
|
|
411
460
|
pushLine({
|
|
412
461
|
type: 'info',
|
|
413
|
-
text: `Config: apiKey=${apiKeyPresent ? 'set' : 'missing'} | provider=${provider || 'auto'} | model=${model || '(none)'} | costMode=${costMode} | baseUrl=${baseUrl || '(default)'} | format=${requestFormat}`,
|
|
462
|
+
text: `Config: apiKey=${apiKeyPresent ? 'set' : 'missing'} | provider=${provider || 'auto'} | model=${model || '(none)'} | costMode=${costMode} | baseUrl=${baseUrl || '(default)'} | format=${requestFormat} | runtime=${sandboxMode}${sandboxGateway ? ` @ ${sandboxGateway}` : ''}`,
|
|
414
463
|
});
|
|
415
464
|
}, [props.profile, pushLine]);
|
|
416
465
|
const requestOpenAIModelsFrom = useCallback(async (baseUrl, apiKey) => {
|
|
@@ -686,6 +735,12 @@ function XibeCodeChatApp(props) {
|
|
|
686
735
|
type: 'info',
|
|
687
736
|
text: 'Press Ctrl+C to quit. Type any prompt and XibeCode will run agent mode.',
|
|
688
737
|
},
|
|
738
|
+
{
|
|
739
|
+
kind: 'line',
|
|
740
|
+
id: nextLineIdRef.current++,
|
|
741
|
+
type: 'info',
|
|
742
|
+
text: 'Vision: mention an image path (e.g. boot.jpg or assets/photo.png) or use @path so the model receives pixels (when a matching file exists).',
|
|
743
|
+
},
|
|
689
744
|
]);
|
|
690
745
|
return;
|
|
691
746
|
}
|
|
@@ -721,6 +776,42 @@ function XibeCodeChatApp(props) {
|
|
|
721
776
|
props.onHooksCommand?.(subcmd || 'list', pushLine);
|
|
722
777
|
return;
|
|
723
778
|
}
|
|
779
|
+
if (resolvedInput === '/cpull' || resolvedInput.startsWith('/cpull ')) {
|
|
780
|
+
if (!props.onCloudPullCommand) {
|
|
781
|
+
pushLine({
|
|
782
|
+
type: 'error',
|
|
783
|
+
text: '/cpull is only available for cloud sandbox_full sessions.',
|
|
784
|
+
});
|
|
785
|
+
return;
|
|
786
|
+
}
|
|
787
|
+
const argsRaw = resolvedInput.replace(/^\/cpull\s*/i, '').trim();
|
|
788
|
+
try {
|
|
789
|
+
await props.onCloudPullCommand(argsRaw, pushLine);
|
|
790
|
+
}
|
|
791
|
+
catch (error) {
|
|
792
|
+
const message = error instanceof Error ? error.message : 'Failed to pull cloud workspace';
|
|
793
|
+
pushLine({ type: 'error', text: message });
|
|
794
|
+
}
|
|
795
|
+
return;
|
|
796
|
+
}
|
|
797
|
+
if (resolvedInput === '/commit' || resolvedInput.startsWith('/commit ')) {
|
|
798
|
+
if (!props.onCommitCommand) {
|
|
799
|
+
pushLine({
|
|
800
|
+
type: 'error',
|
|
801
|
+
text: '/commit is unavailable in this chat session.',
|
|
802
|
+
});
|
|
803
|
+
return;
|
|
804
|
+
}
|
|
805
|
+
const messageRaw = resolvedInput.replace(/^\/commit\s*/i, '').trim();
|
|
806
|
+
try {
|
|
807
|
+
await props.onCommitCommand(messageRaw, pushLine);
|
|
808
|
+
}
|
|
809
|
+
catch (error) {
|
|
810
|
+
const message = error instanceof Error ? error.message : 'Failed to create git commit';
|
|
811
|
+
pushLine({ type: 'error', text: message });
|
|
812
|
+
}
|
|
813
|
+
return;
|
|
814
|
+
}
|
|
724
815
|
if (resolvedInput === '/format' || resolvedInput.startsWith('/format ')) {
|
|
725
816
|
const arg = resolvedInput.replace(/^\/format\s*/i, '').trim().toLowerCase();
|
|
726
817
|
if (!arg) {
|
|
@@ -824,8 +915,7 @@ function XibeCodeChatApp(props) {
|
|
|
824
915
|
setIsRunning(true);
|
|
825
916
|
try {
|
|
826
917
|
const startedAt = Date.now();
|
|
827
|
-
const
|
|
828
|
-
const { image: imageRefs } = splitAtReferences(refs);
|
|
918
|
+
const { imageRefs, explicitAtImagePaths } = collectImageReferencesForPrompt(prompt, process.cwd());
|
|
829
919
|
const images = [];
|
|
830
920
|
for (const ref of imageRefs) {
|
|
831
921
|
try {
|
|
@@ -837,9 +927,18 @@ function XibeCodeChatApp(props) {
|
|
|
837
927
|
}
|
|
838
928
|
catch (err) {
|
|
839
929
|
const message = err instanceof Error ? err.message : 'Failed to load image';
|
|
840
|
-
|
|
930
|
+
if (explicitAtImagePaths.has(ref.resolvedPath)) {
|
|
931
|
+
pushLine({ type: 'error', text: message });
|
|
932
|
+
}
|
|
933
|
+
// Implicit path mentions: skip silently if file missing (avoid noise for figurative ".png" etc.)
|
|
841
934
|
}
|
|
842
935
|
}
|
|
936
|
+
if (images.length > 0) {
|
|
937
|
+
pushLine({
|
|
938
|
+
type: 'info',
|
|
939
|
+
text: `Including ${images.length} image(s) in this message for the model (vision).`,
|
|
940
|
+
});
|
|
941
|
+
}
|
|
843
942
|
const stats = await props.runPrompt(prompt, pushLine, {
|
|
844
943
|
images: images.length ? images : undefined,
|
|
845
944
|
signal: abortControllerRef.current.signal,
|
|
@@ -870,39 +969,34 @@ function XibeCodeChatApp(props) {
|
|
|
870
969
|
restartAttemptsRef.current = 0;
|
|
871
970
|
let promptToRun = resolvedInput;
|
|
872
971
|
while (promptToRun) {
|
|
972
|
+
const activePrompt = promptToRun;
|
|
973
|
+
promptToRun = null;
|
|
873
974
|
try {
|
|
874
|
-
await runOne(
|
|
875
|
-
break;
|
|
975
|
+
await runOne(activePrompt);
|
|
876
976
|
}
|
|
877
977
|
catch (err) {
|
|
878
978
|
if (isAbortError(err) && abortReasonRef.current === 'watchdog') {
|
|
879
979
|
restartAttemptsRef.current += 1;
|
|
880
980
|
if (restartAttemptsRef.current <= 2) {
|
|
881
981
|
// Restart same prompt
|
|
982
|
+
promptToRun = activePrompt;
|
|
882
983
|
continue;
|
|
883
984
|
}
|
|
884
985
|
pushLine({ type: 'error', text: 'Restart limit reached (2). Stopping.' });
|
|
885
|
-
break;
|
|
886
986
|
}
|
|
887
|
-
if (isAbortError(err) && abortReasonRef.current === 'user') {
|
|
987
|
+
else if (isAbortError(err) && abortReasonRef.current === 'user') {
|
|
888
988
|
pushLine({ type: 'info', text: 'Cancelled.' });
|
|
889
|
-
break;
|
|
890
|
-
}
|
|
891
|
-
const message = err instanceof Error ? err.message : 'Unknown error';
|
|
892
|
-
pushLine({ type: 'error', text: message });
|
|
893
|
-
break;
|
|
894
|
-
}
|
|
895
|
-
finally {
|
|
896
|
-
// If a message was queued during this run, send it next.
|
|
897
|
-
if (!isRunning && queuedPromptRef.current) {
|
|
898
|
-
const q = queuedPromptRef.current;
|
|
899
|
-
queuedPromptRef.current = null;
|
|
900
|
-
promptToRun = q;
|
|
901
989
|
}
|
|
902
990
|
else {
|
|
903
|
-
|
|
991
|
+
const message = err instanceof Error ? err.message : 'Unknown error';
|
|
992
|
+
pushLine({ type: 'error', text: message });
|
|
904
993
|
}
|
|
905
994
|
}
|
|
995
|
+
// If a message was queued during this run, send it next.
|
|
996
|
+
if (!promptToRun && queuedPromptRef.current) {
|
|
997
|
+
promptToRun = queuedPromptRef.current;
|
|
998
|
+
queuedPromptRef.current = null;
|
|
999
|
+
}
|
|
906
1000
|
}
|
|
907
1001
|
}, [
|
|
908
1002
|
applyModel,
|
|
@@ -946,6 +1040,17 @@ function XibeCodeChatApp(props) {
|
|
|
946
1040
|
exit();
|
|
947
1041
|
return;
|
|
948
1042
|
}
|
|
1043
|
+
// During an active run, Esc always means "abort run" (takes priority over question UI).
|
|
1044
|
+
if (isRunning && key.escape) {
|
|
1045
|
+
if (questionsState) {
|
|
1046
|
+
setQuestionsState(null);
|
|
1047
|
+
}
|
|
1048
|
+
if (abortControllerRef.current && abortReasonRef.current === 'none') {
|
|
1049
|
+
abortReasonRef.current = 'user';
|
|
1050
|
+
abortControllerRef.current.abort();
|
|
1051
|
+
}
|
|
1052
|
+
return;
|
|
1053
|
+
}
|
|
949
1054
|
// Interactive questions: arrow key navigation
|
|
950
1055
|
if (questionsState && !questionsState.isTypingCustom) {
|
|
951
1056
|
const q = questionsState.questions[questionsState.currentIndex];
|
|
@@ -1012,13 +1117,6 @@ function XibeCodeChatApp(props) {
|
|
|
1012
1117
|
}
|
|
1013
1118
|
// Let normal TextInput handle typing — don't intercept
|
|
1014
1119
|
}
|
|
1015
|
-
if (isRunning && key.escape) {
|
|
1016
|
-
if (abortControllerRef.current && abortReasonRef.current === 'none') {
|
|
1017
|
-
abortReasonRef.current = 'user';
|
|
1018
|
-
abortControllerRef.current.abort();
|
|
1019
|
-
}
|
|
1020
|
-
return;
|
|
1021
|
-
}
|
|
1022
1120
|
if (configMenuOpen && !isRunning) {
|
|
1023
1121
|
if (key.escape) {
|
|
1024
1122
|
setConfigMenuOpen(false);
|
|
@@ -1387,7 +1485,7 @@ function XibeCodeChatApp(props) {
|
|
|
1387
1485
|
if (item.kind === 'hero') {
|
|
1388
1486
|
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)
|
|
1389
1487
|
? 'Anthropic Messages'
|
|
1390
|
-
: 'OpenAI chat', ")"] })] })] })] }), _jsxs(Box, { marginTop: 1, children: [
|
|
1488
|
+
: 'OpenAI chat', ")"] })] })] })] }), _jsxs(Box, { marginTop: 1, children: [_jsxs(Text, { color: "suggestion", children: [props.runtimeStatus, ":"] }), _jsx(Text, { color: "inactive", children: " Ready - type " }), _jsx(Text, { color: "claude", children: "/help" }), _jsx(Text, { color: "inactive", children: " to begin" })] }), props.sandboxLabel ? (_jsxs(Text, { color: "inactive", children: ["sandbox: ", props.sandboxLabel] })) : null, props.sandboxId ? (_jsxs(Text, { color: "inactive", children: ["sandbox id: ", props.sandboxId] })) : null, props.previewUrl ? (_jsxs(Text, { color: "inactive", children: ["preview: ", props.previewUrl] })) : null, props.pullHint ? (_jsxs(Text, { color: "inactive", children: ["pull: ", props.pullHint] })) : null, _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" })] }, item.id));
|
|
1391
1489
|
}
|
|
1392
1490
|
return (_jsx(React.Fragment, { children: 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 })] })) }, item.id));
|
|
1393
1491
|
} }), !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 })] }) })), questionsState && (() => {
|
|
@@ -1457,9 +1555,38 @@ export async function launchClaudeStyleChat(options) {
|
|
|
1457
1555
|
const mcpClientManager = new MCPClientManager();
|
|
1458
1556
|
const memory = new NeuralMemory(process.cwd());
|
|
1459
1557
|
await memory.init().catch(() => { });
|
|
1460
|
-
const
|
|
1558
|
+
const remoteExecution = resolveRemoteExecutionConfig(config, process.cwd());
|
|
1559
|
+
const skipInitialSync = process.env.XIBECODE_SANDBOX_SKIP_SYNC === '1' || process.env.XIBECODE_SANDBOX_SKIP_SYNC === 'true';
|
|
1560
|
+
if (remoteExecution?.strategy === 'sandbox_full' && !skipInitialSync) {
|
|
1561
|
+
const syncResult = await withCloudWorkspaceSyncSpinner(() => syncWorkspaceToSandbox(remoteExecution, process.cwd(), {
|
|
1562
|
+
maxMb: config.getSandboxSyncMaxMb(),
|
|
1563
|
+
excludeGlobs: config.getSandboxSyncExcludeGlobs(),
|
|
1564
|
+
workspaceRoot: remoteExecution.workspaceRoot,
|
|
1565
|
+
respectGitignore: config.getSandboxSyncRespectGitignore(),
|
|
1566
|
+
}));
|
|
1567
|
+
remoteExecution.sessionId = syncResult.sessionId;
|
|
1568
|
+
remoteExecution.e2bSandboxId = syncResult.sandboxId || remoteExecution.e2bSandboxId;
|
|
1569
|
+
}
|
|
1570
|
+
else if (skipInitialSync && remoteExecution?.strategy === 'sandbox_full' && !remoteExecution.sessionId) {
|
|
1571
|
+
throw new Error('Cloud resume requested without a sandbox session ID.');
|
|
1572
|
+
}
|
|
1573
|
+
const cloudHint = await getCloudRuntimeHint(remoteExecution);
|
|
1574
|
+
const runtimeStatus = getRuntimeStatusLabel(config);
|
|
1575
|
+
const sandboxLabel = remoteExecution?.strategy === 'sandbox_full'
|
|
1576
|
+
? 'full (workspace synced to E2B)'
|
|
1577
|
+
: remoteExecution
|
|
1578
|
+
? 'host_only (file edits stay local)'
|
|
1579
|
+
: undefined;
|
|
1580
|
+
const toolExecutor = new CodingToolExecutor(process.cwd(), {
|
|
1581
|
+
mcpClientManager,
|
|
1582
|
+
skillManager,
|
|
1583
|
+
memory,
|
|
1584
|
+
remoteExecution: codingToolExecutorRemoteOptions(remoteExecution),
|
|
1585
|
+
});
|
|
1586
|
+
attachRemoteExecution(toolExecutor, remoteExecution);
|
|
1461
1587
|
let wireFormat = config.get('requestFormat') ?? 'auto';
|
|
1462
1588
|
const customProviderFormat = config.get('customProviderFormat');
|
|
1589
|
+
const remoteWs = remoteToolWorkspaceRootForAgent(remoteExecution);
|
|
1463
1590
|
const createAgentForModel = (modelName, creds) => new EnhancedAgent({
|
|
1464
1591
|
apiKey: creds.apiKey,
|
|
1465
1592
|
baseUrl: creds.baseUrl,
|
|
@@ -1470,6 +1597,8 @@ export async function launchClaudeStyleChat(options) {
|
|
|
1470
1597
|
customProviderFormat,
|
|
1471
1598
|
requestFormat: wireFormat,
|
|
1472
1599
|
defaultSkillsPrompt,
|
|
1600
|
+
remoteToolWorkspaceRoot: remoteWs,
|
|
1601
|
+
remoteToolSandboxId: remoteToolSandboxIdForAgent(remoteExecution),
|
|
1473
1602
|
}, provider);
|
|
1474
1603
|
let activeAgent = apiKey ? createAgentForModel(model, { apiKey, baseUrl }) : null;
|
|
1475
1604
|
let activeCreds = { apiKey, baseUrl };
|
|
@@ -1939,8 +2068,88 @@ export async function launchClaudeStyleChat(options) {
|
|
|
1939
2068
|
pushLine({ type: 'info', text: 'Usage: /hooks [list]. Default: list. Use "xc hooks" CLI for full management.' });
|
|
1940
2069
|
}
|
|
1941
2070
|
};
|
|
2071
|
+
const onCloudPullCommand = async (argsRaw, pushLine) => {
|
|
2072
|
+
if (!remoteExecution || remoteExecution.strategy !== 'sandbox_full') {
|
|
2073
|
+
throw new Error('/cpull is available only in cloud sandbox_full mode.');
|
|
2074
|
+
}
|
|
2075
|
+
const sessionId = remoteExecution.sessionId?.trim();
|
|
2076
|
+
if (!sessionId) {
|
|
2077
|
+
throw new Error('No cloud session id found. Start with "xc cloud" before using /cpull.');
|
|
2078
|
+
}
|
|
2079
|
+
const tokens = argsRaw ? argsRaw.split(/\s+/).filter(Boolean) : [];
|
|
2080
|
+
let apply = false;
|
|
2081
|
+
let force = false;
|
|
2082
|
+
let output;
|
|
2083
|
+
for (let i = 0; i < tokens.length; i += 1) {
|
|
2084
|
+
const token = tokens[i];
|
|
2085
|
+
if (token === '--apply') {
|
|
2086
|
+
apply = true;
|
|
2087
|
+
continue;
|
|
2088
|
+
}
|
|
2089
|
+
if (token === '--force') {
|
|
2090
|
+
force = true;
|
|
2091
|
+
continue;
|
|
2092
|
+
}
|
|
2093
|
+
if (token === '--output') {
|
|
2094
|
+
const next = tokens[i + 1];
|
|
2095
|
+
if (!next || next.startsWith('--')) {
|
|
2096
|
+
throw new Error('Usage: /cpull [--apply] [--force] [--output <path>]');
|
|
2097
|
+
}
|
|
2098
|
+
output = next;
|
|
2099
|
+
i += 1;
|
|
2100
|
+
continue;
|
|
2101
|
+
}
|
|
2102
|
+
throw new Error(`Unknown /cpull option "${token}". Usage: /cpull [--apply] [--force] [--output <path>]`);
|
|
2103
|
+
}
|
|
2104
|
+
await cloudPullCommand({
|
|
2105
|
+
profile: options.profile,
|
|
2106
|
+
session: sessionId,
|
|
2107
|
+
apply,
|
|
2108
|
+
force,
|
|
2109
|
+
output,
|
|
2110
|
+
onStatus: (text) => pushLine({ type: 'info', text }),
|
|
2111
|
+
});
|
|
2112
|
+
};
|
|
2113
|
+
const onCommitCommand = async (messageRaw, pushLine) => {
|
|
2114
|
+
pushLine({ type: 'info', text: 'Staging files with git add .' });
|
|
2115
|
+
const addResult = await runCommandCapture('git', ['add', '.']);
|
|
2116
|
+
if (addResult.code !== 0) {
|
|
2117
|
+
throw new Error(`git add failed: ${(addResult.stderr || addResult.stdout || 'unknown error').trim()}`);
|
|
2118
|
+
}
|
|
2119
|
+
const stagedResult = await runCommandCapture('git', ['diff', '--cached', '--name-only']);
|
|
2120
|
+
if (stagedResult.code !== 0) {
|
|
2121
|
+
throw new Error(`Failed to inspect staged files: ${(stagedResult.stderr || stagedResult.stdout || 'unknown error').trim()}`);
|
|
2122
|
+
}
|
|
2123
|
+
const stagedFiles = stagedResult.stdout
|
|
2124
|
+
.split('\n')
|
|
2125
|
+
.map((line) => line.trim())
|
|
2126
|
+
.filter(Boolean);
|
|
2127
|
+
if (stagedFiles.length === 0) {
|
|
2128
|
+
pushLine({ type: 'info', text: 'No staged changes found after git add.' });
|
|
2129
|
+
return;
|
|
2130
|
+
}
|
|
2131
|
+
let commitMessage = messageRaw.trim();
|
|
2132
|
+
if (!commitMessage) {
|
|
2133
|
+
const statResult = await runCommandCapture('git', ['diff', '--cached', '--shortstat']);
|
|
2134
|
+
const shortstat = statResult.code === 0 ? statResult.stdout : '';
|
|
2135
|
+
commitMessage = buildAutoCommitMessage(stagedFiles, shortstat);
|
|
2136
|
+
pushLine({ type: 'info', text: `Auto commit message: ${commitMessage}` });
|
|
2137
|
+
}
|
|
2138
|
+
const commitResult = await runCommandCapture('git', ['commit', '-m', commitMessage]);
|
|
2139
|
+
if (commitResult.code !== 0) {
|
|
2140
|
+
throw new Error((commitResult.stderr || commitResult.stdout || 'git commit failed').trim());
|
|
2141
|
+
}
|
|
2142
|
+
const firstLine = (commitResult.stdout || commitResult.stderr)
|
|
2143
|
+
.split('\n')
|
|
2144
|
+
.map((line) => line.trim())
|
|
2145
|
+
.find(Boolean);
|
|
2146
|
+
pushLine({
|
|
2147
|
+
type: 'info',
|
|
2148
|
+
text: firstLine ? `Commit created: ${firstLine}` : `Commit created with message: ${commitMessage}`,
|
|
2149
|
+
});
|
|
2150
|
+
};
|
|
1942
2151
|
try {
|
|
1943
|
-
await renderAndRun(root, _jsx(XibeCodeChatApp, { model: model, initialMode: activeMode, provider: provider, baseUrl: baseUrl, needsFirstRunSetup: needsFirstRunSetup, defaultModel: model, modeOptions: modeOptions, initialRequestFormat: wireFormat, customProviderFormat: customProviderFormat, profile: options.profile, sessionId: currentSessionId, initialMessages: options.initialMessages, runPrompt: runPrompt, listBackgroundTasks: listBackgroundTasks, checkBackgroundTask: checkBackgroundTask, onUiLine: appendLogLine, registerUiSink: registerUiSink, registerModeSink: registerModeSink, registerQuestionsSink: registerQuestionsSink, loadModels: loadModels, onModelChange: onModelChange, onModeChange: onModeChange, onWireFormatChange: onWireFormatChange, onSessionCreated: onSessionCreated, onMessagesUpdate: onMessagesUpdate, getCurrentMessages: () => activeAgent?.getMessages() ?? currentSession?.messages ?? [], onMemoryCommand: onMemoryCommand, onHooksCommand: onHooksCommand }));
|
|
2152
|
+
await renderAndRun(root, _jsx(XibeCodeChatApp, { model: model, initialMode: activeMode, provider: provider, runtimeStatus: runtimeStatus, sandboxLabel: sandboxLabel, sandboxId: cloudHint.sandboxId, previewUrl: cloudHint.previewUrl, pullHint: cloudHint.pullHint, baseUrl: baseUrl, needsFirstRunSetup: needsFirstRunSetup, defaultModel: model, modeOptions: modeOptions, initialRequestFormat: wireFormat, customProviderFormat: customProviderFormat, profile: options.profile, sessionId: currentSessionId, initialMessages: options.initialMessages, runPrompt: runPrompt, listBackgroundTasks: listBackgroundTasks, checkBackgroundTask: checkBackgroundTask, onUiLine: appendLogLine, registerUiSink: registerUiSink, registerModeSink: registerModeSink, registerQuestionsSink: registerQuestionsSink, loadModels: loadModels, onModelChange: onModelChange, onModeChange: onModeChange, onWireFormatChange: onWireFormatChange, onSessionCreated: onSessionCreated, onMessagesUpdate: onMessagesUpdate, getCurrentMessages: () => activeAgent?.getMessages() ?? currentSession?.messages ?? [], onMemoryCommand: onMemoryCommand, onHooksCommand: onHooksCommand, onCloudPullCommand: onCloudPullCommand, onCommitCommand: onCommitCommand }));
|
|
1944
2153
|
}
|
|
1945
2154
|
finally {
|
|
1946
2155
|
process.off('unhandledRejection', onUnhandledRejection);
|