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.
Files changed (56) hide show
  1. package/README.md +15 -0
  2. package/dist/commands/chat.d.ts.map +1 -1
  3. package/dist/commands/chat.js +40 -1
  4. package/dist/commands/chat.js.map +1 -1
  5. package/dist/commands/cloud-pull.d.ts +10 -0
  6. package/dist/commands/cloud-pull.d.ts.map +1 -0
  7. package/dist/commands/cloud-pull.js +79 -0
  8. package/dist/commands/cloud-pull.js.map +1 -0
  9. package/dist/commands/config.d.ts +7 -0
  10. package/dist/commands/config.d.ts.map +1 -1
  11. package/dist/commands/config.js +68 -0
  12. package/dist/commands/config.js.map +1 -1
  13. package/dist/commands/run-pr.d.ts.map +1 -1
  14. package/dist/commands/run-pr.js +41 -0
  15. package/dist/commands/run-pr.js.map +1 -1
  16. package/dist/commands/run.d.ts.map +1 -1
  17. package/dist/commands/run.js +39 -0
  18. package/dist/commands/run.js.map +1 -1
  19. package/dist/commands/whats-new.d.ts +2 -0
  20. package/dist/commands/whats-new.d.ts.map +1 -0
  21. package/dist/commands/whats-new.js +33 -0
  22. package/dist/commands/whats-new.js.map +1 -0
  23. package/dist/index.js +72 -1
  24. package/dist/index.js.map +1 -1
  25. package/dist/ui/claude-style-chat.d.ts.map +1 -1
  26. package/dist/ui/claude-style-chat.js +202 -6
  27. package/dist/ui/claude-style-chat.js.map +1 -1
  28. package/dist/utils/cloud-gateway.d.ts +10 -0
  29. package/dist/utils/cloud-gateway.d.ts.map +1 -0
  30. package/dist/utils/cloud-gateway.js +99 -0
  31. package/dist/utils/cloud-gateway.js.map +1 -0
  32. package/dist/utils/cloud-runtime-hints.d.ts +8 -0
  33. package/dist/utils/cloud-runtime-hints.d.ts.map +1 -0
  34. package/dist/utils/cloud-runtime-hints.js +17 -0
  35. package/dist/utils/cloud-runtime-hints.js.map +1 -0
  36. package/dist/utils/cloud-sync-feedback.d.ts +6 -0
  37. package/dist/utils/cloud-sync-feedback.d.ts.map +1 -0
  38. package/dist/utils/cloud-sync-feedback.js +77 -0
  39. package/dist/utils/cloud-sync-feedback.js.map +1 -0
  40. package/dist/utils/config.d.ts +28 -0
  41. package/dist/utils/config.d.ts.map +1 -1
  42. package/dist/utils/config.js +79 -0
  43. package/dist/utils/config.js.map +1 -1
  44. package/dist/utils/npm-update-notice.d.ts +36 -0
  45. package/dist/utils/npm-update-notice.d.ts.map +1 -0
  46. package/dist/utils/npm-update-notice.js +132 -0
  47. package/dist/utils/npm-update-notice.js.map +1 -0
  48. package/dist/utils/remote-execution.d.ts +20 -0
  49. package/dist/utils/remote-execution.d.ts.map +1 -0
  50. package/dist/utils/remote-execution.js +339 -0
  51. package/dist/utils/remote-execution.js.map +1 -0
  52. package/dist/utils/sandbox-sync.d.ts +14 -0
  53. package/dist/utils/sandbox-sync.d.ts.map +1 -0
  54. package/dist/utils/sandbox-sync.js +206 -0
  55. package/dist/utils/sandbox-sync.js.map +1 -0
  56. 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: `Resumed session: ${sessionIdRef.current}`,
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: [_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" })] }, item.id));
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 toolExecutor = new CodingToolExecutor(process.cwd(), { mcpClientManager, skillManager, memory });
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);