xibecode 1.3.3 → 1.3.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/README.md +15 -0
- package/dist/commands/chat.d.ts.map +1 -1
- package/dist/commands/chat.js +40 -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/commands/whats-new.d.ts +2 -0
- package/dist/commands/whats-new.d.ts.map +1 -0
- package/dist/commands/whats-new.js +33 -0
- package/dist/commands/whats-new.js.map +1 -0
- package/dist/index.js +72 -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 +202 -6
- 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/npm-update-notice.d.ts +36 -0
- package/dist/utils/npm-update-notice.d.ts.map +1 -0
- package/dist/utils/npm-update-notice.js +132 -0
- package/dist/utils/npm-update-notice.js.map +1 -0
- 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';
|
|
@@ -21,6 +22,11 @@ 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) => {
|
|
@@ -727,6 +776,42 @@ function XibeCodeChatApp(props) {
|
|
|
727
776
|
props.onHooksCommand?.(subcmd || 'list', pushLine);
|
|
728
777
|
return;
|
|
729
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
|
+
}
|
|
730
815
|
if (resolvedInput === '/format' || resolvedInput.startsWith('/format ')) {
|
|
731
816
|
const arg = resolvedInput.replace(/^\/format\s*/i, '').trim().toLowerCase();
|
|
732
817
|
if (!arg) {
|
|
@@ -1400,7 +1485,7 @@ function XibeCodeChatApp(props) {
|
|
|
1400
1485
|
if (item.kind === 'hero') {
|
|
1401
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)
|
|
1402
1487
|
? 'Anthropic Messages'
|
|
1403
|
-
: '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));
|
|
1404
1489
|
}
|
|
1405
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));
|
|
1406
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 && (() => {
|
|
@@ -1470,9 +1555,38 @@ export async function launchClaudeStyleChat(options) {
|
|
|
1470
1555
|
const mcpClientManager = new MCPClientManager();
|
|
1471
1556
|
const memory = new NeuralMemory(process.cwd());
|
|
1472
1557
|
await memory.init().catch(() => { });
|
|
1473
|
-
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);
|
|
1474
1587
|
let wireFormat = config.get('requestFormat') ?? 'auto';
|
|
1475
1588
|
const customProviderFormat = config.get('customProviderFormat');
|
|
1589
|
+
const remoteWs = remoteToolWorkspaceRootForAgent(remoteExecution);
|
|
1476
1590
|
const createAgentForModel = (modelName, creds) => new EnhancedAgent({
|
|
1477
1591
|
apiKey: creds.apiKey,
|
|
1478
1592
|
baseUrl: creds.baseUrl,
|
|
@@ -1483,6 +1597,8 @@ export async function launchClaudeStyleChat(options) {
|
|
|
1483
1597
|
customProviderFormat,
|
|
1484
1598
|
requestFormat: wireFormat,
|
|
1485
1599
|
defaultSkillsPrompt,
|
|
1600
|
+
remoteToolWorkspaceRoot: remoteWs,
|
|
1601
|
+
remoteToolSandboxId: remoteToolSandboxIdForAgent(remoteExecution),
|
|
1486
1602
|
}, provider);
|
|
1487
1603
|
let activeAgent = apiKey ? createAgentForModel(model, { apiKey, baseUrl }) : null;
|
|
1488
1604
|
let activeCreds = { apiKey, baseUrl };
|
|
@@ -1952,8 +2068,88 @@ export async function launchClaudeStyleChat(options) {
|
|
|
1952
2068
|
pushLine({ type: 'info', text: 'Usage: /hooks [list]. Default: list. Use "xc hooks" CLI for full management.' });
|
|
1953
2069
|
}
|
|
1954
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
|
+
};
|
|
1955
2151
|
try {
|
|
1956
|
-
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 }));
|
|
1957
2153
|
}
|
|
1958
2154
|
finally {
|
|
1959
2155
|
process.off('unhandledRejection', onUnhandledRejection);
|