snow-ai 0.2.12 → 0.2.13

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.
@@ -18,7 +18,7 @@ import { sessionManager } from '../../utils/sessionManager.js';
18
18
  import { useSessionSave } from '../../hooks/useSessionSave.js';
19
19
  import { useToolConfirmation } from '../../hooks/useToolConfirmation.js';
20
20
  import { handleConversationWithTools } from '../../hooks/useConversation.js';
21
- import { parseAndValidateFileReferences, createMessageWithFileInstructions, getSystemInfo } from '../../utils/fileUtils.js';
21
+ import { parseAndValidateFileReferences, createMessageWithFileInstructions, getSystemInfo, } from '../../utils/fileUtils.js';
22
22
  import { compressContext } from '../../utils/contextCompressor.js';
23
23
  import { incrementalSnapshotManager } from '../../utils/incrementalSnapshot.js';
24
24
  // Import commands to register them
@@ -30,7 +30,7 @@ import '../../utils/commands/init.js';
30
30
  import '../../utils/commands/ide.js';
31
31
  import '../../utils/commands/compact.js';
32
32
  import { navigateTo } from '../../hooks/useGlobalNavigation.js';
33
- import { vscodeConnection } from '../../utils/vscodeConnection.js';
33
+ import { vscodeConnection, } from '../../utils/vscodeConnection.js';
34
34
  // Format elapsed time to human readable format
35
35
  function formatElapsedTime(seconds) {
36
36
  if (seconds < 60) {
@@ -61,7 +61,16 @@ export default function ChatScreen({}) {
61
61
  const [showMcpInfo, setShowMcpInfo] = useState(false);
62
62
  const [mcpPanelKey, setMcpPanelKey] = useState(0);
63
63
  const [streamTokenCount, setStreamTokenCount] = useState(0);
64
- const [yoloMode, setYoloMode] = useState(false);
64
+ const [yoloMode, setYoloMode] = useState(() => {
65
+ // Load yolo mode from localStorage on initialization
66
+ try {
67
+ const saved = localStorage.getItem('snow-yolo-mode');
68
+ return saved === 'true';
69
+ }
70
+ catch {
71
+ return false;
72
+ }
73
+ });
65
74
  const [contextUsage, setContextUsage] = useState(null);
66
75
  const [elapsedSeconds, setElapsedSeconds] = useState(0);
67
76
  const [timerStartTime, setTimerStartTime] = useState(null);
@@ -85,8 +94,17 @@ export default function ChatScreen({}) {
85
94
  useEffect(() => {
86
95
  pendingMessagesRef.current = pendingMessages;
87
96
  }, [pendingMessages]);
97
+ // Persist yolo mode to localStorage
98
+ useEffect(() => {
99
+ try {
100
+ localStorage.setItem('snow-yolo-mode', String(yoloMode));
101
+ }
102
+ catch {
103
+ // Ignore localStorage errors
104
+ }
105
+ }, [yoloMode]);
88
106
  // Use tool confirmation hook
89
- const { pendingToolConfirmation, requestToolConfirmation, isToolAutoApproved, addMultipleToAlwaysApproved } = useToolConfirmation();
107
+ const { pendingToolConfirmation, requestToolConfirmation, isToolAutoApproved, addMultipleToAlwaysApproved, } = useToolConfirmation();
90
108
  // Animation for streaming/saving indicator
91
109
  useEffect(() => {
92
110
  if (!isStreaming && !isSaving)
@@ -123,58 +141,54 @@ export default function ChatScreen({}) {
123
141
  }, [timerStartTime]);
124
142
  // Monitor VSCode connection status and editor context
125
143
  useEffect(() => {
126
- let connectingTimeout = null;
127
- const checkConnection = setInterval(() => {
144
+ const checkConnectionInterval = setInterval(() => {
128
145
  const isConnected = vscodeConnection.isConnected();
129
- const isServerRunning = vscodeConnection.isServerRunning();
130
146
  setVscodeConnected(isConnected);
131
147
  // Update connection status based on actual connection state
132
148
  if (isConnected && vscodeConnectionStatus !== 'connected') {
133
149
  setVscodeConnectionStatus('connected');
134
- if (connectingTimeout) {
135
- clearTimeout(connectingTimeout);
136
- connectingTimeout = null;
137
- }
138
150
  }
139
151
  else if (!isConnected && vscodeConnectionStatus === 'connected') {
140
152
  setVscodeConnectionStatus('disconnected');
141
153
  }
142
- else if (vscodeConnectionStatus === 'connecting' && !isServerRunning) {
143
- // Server failed to start
144
- setVscodeConnectionStatus('error');
145
- if (connectingTimeout) {
146
- clearTimeout(connectingTimeout);
147
- connectingTimeout = null;
148
- }
149
- }
150
154
  }, 1000);
151
- // Set timeout for connecting state (15 seconds)
152
- if (vscodeConnectionStatus === 'connecting') {
153
- connectingTimeout = setTimeout(() => {
154
- if (vscodeConnectionStatus === 'connecting') {
155
- setVscodeConnectionStatus('error');
156
- }
157
- }, 15000);
158
- }
159
- const unsubscribe = vscodeConnection.onContextUpdate((context) => {
155
+ const unsubscribe = vscodeConnection.onContextUpdate(context => {
160
156
  setEditorContext(context);
161
157
  // When we receive context, it means connection is successful
162
158
  if (vscodeConnectionStatus !== 'connected') {
163
159
  setVscodeConnectionStatus('connected');
164
- if (connectingTimeout) {
165
- clearTimeout(connectingTimeout);
166
- connectingTimeout = null;
167
- }
168
160
  }
169
161
  });
170
162
  return () => {
171
- clearInterval(checkConnection);
172
- if (connectingTimeout) {
173
- clearTimeout(connectingTimeout);
174
- }
163
+ clearInterval(checkConnectionInterval);
175
164
  unsubscribe();
176
165
  };
177
166
  }, [vscodeConnectionStatus]);
167
+ // Separate effect for handling connecting timeout
168
+ useEffect(() => {
169
+ if (vscodeConnectionStatus !== 'connecting') {
170
+ return;
171
+ }
172
+ // Set timeout for connecting state (30 seconds to allow for VSCode extension reconnection)
173
+ const connectingTimeout = setTimeout(() => {
174
+ const isConnected = vscodeConnection.isConnected();
175
+ const isServerRunning = vscodeConnection.isServerRunning();
176
+ // Only set error if still not connected after timeout
177
+ if (!isConnected) {
178
+ if (isServerRunning) {
179
+ // Server is running but no connection - show error with helpful message
180
+ setVscodeConnectionStatus('error');
181
+ }
182
+ else {
183
+ // Server not running - go back to disconnected
184
+ setVscodeConnectionStatus('disconnected');
185
+ }
186
+ }
187
+ }, 30000); // Increased to 30 seconds
188
+ return () => {
189
+ clearTimeout(connectingTimeout);
190
+ };
191
+ }, [vscodeConnectionStatus]);
178
192
  // Load snapshot file counts when session changes
179
193
  useEffect(() => {
180
194
  const loadSnapshotFileCounts = async () => {
@@ -231,12 +245,15 @@ export default function ChatScreen({}) {
231
245
  // Abort the controller
232
246
  abortController.abort();
233
247
  // Add discontinued message
234
- setMessages(prev => [...prev, {
248
+ setMessages(prev => [
249
+ ...prev,
250
+ {
235
251
  role: 'assistant',
236
252
  content: '',
237
253
  streaming: false,
238
- discontinued: true
239
- }]);
254
+ discontinued: true,
255
+ },
256
+ ]);
240
257
  // Stop streaming state
241
258
  setIsStreaming(false);
242
259
  setAbortController(null);
@@ -245,7 +262,9 @@ export default function ChatScreen({}) {
245
262
  });
246
263
  const handleCommandExecution = async (commandName, result) => {
247
264
  // Handle /compact command
248
- if (commandName === 'compact' && result.success && result.action === 'compact') {
265
+ if (commandName === 'compact' &&
266
+ result.success &&
267
+ result.action === 'compact') {
249
268
  // Set compressing state (不添加命令面板消息)
250
269
  setIsCompressing(true);
251
270
  setCompressionError(null);
@@ -256,7 +275,7 @@ export default function ChatScreen({}) {
256
275
  .map(msg => ({
257
276
  role: msg.role,
258
277
  content: msg.content,
259
- tool_call_id: msg.toolCallId
278
+ tool_call_id: msg.toolCallId,
260
279
  }));
261
280
  // Compress the context
262
281
  const result = await compressContext(chatMessages);
@@ -264,7 +283,7 @@ export default function ChatScreen({}) {
264
283
  const summaryMessage = {
265
284
  role: 'assistant',
266
285
  content: result.summary,
267
- streaming: false
286
+ streaming: false,
268
287
  };
269
288
  // Clear session and set new compressed state
270
289
  sessionManager.clearCurrentSession();
@@ -275,7 +294,7 @@ export default function ChatScreen({}) {
275
294
  setContextUsage({
276
295
  prompt_tokens: result.usage.prompt_tokens,
277
296
  completion_tokens: result.usage.completion_tokens,
278
- total_tokens: result.usage.total_tokens
297
+ total_tokens: result.usage.total_tokens,
279
298
  });
280
299
  }
281
300
  catch (error) {
@@ -285,7 +304,7 @@ export default function ChatScreen({}) {
285
304
  const errorMessage = {
286
305
  role: 'assistant',
287
306
  content: `**Compression Failed**\n\n${errorMsg}`,
288
- streaming: false
307
+ streaming: false,
289
308
  };
290
309
  setMessages(prev => [...prev, errorMessage]);
291
310
  }
@@ -302,7 +321,7 @@ export default function ChatScreen({}) {
302
321
  const commandMessage = {
303
322
  role: 'command',
304
323
  content: '',
305
- commandName: commandName
324
+ commandName: commandName,
306
325
  };
307
326
  setMessages(prev => [...prev, commandMessage]);
308
327
  }
@@ -322,11 +341,13 @@ export default function ChatScreen({}) {
322
341
  setRemountKey(prev => prev + 1);
323
342
  // Reset context usage (token statistics)
324
343
  setContextUsage(null);
344
+ // Note: yoloMode is preserved via localStorage (lines 68-76, 104-111)
345
+ // Note: VSCode connection is preserved and managed by vscodeConnection utility
325
346
  // Add command execution feedback
326
347
  const commandMessage = {
327
348
  role: 'command',
328
349
  content: '',
329
- commandName: commandName
350
+ commandName: commandName,
330
351
  };
331
352
  setMessages([commandMessage]);
332
353
  }
@@ -335,7 +356,7 @@ export default function ChatScreen({}) {
335
356
  const commandMessage = {
336
357
  role: 'command',
337
358
  content: '',
338
- commandName: commandName
359
+ commandName: commandName,
339
360
  };
340
361
  setMessages(prev => [...prev, commandMessage]);
341
362
  }
@@ -345,7 +366,7 @@ export default function ChatScreen({}) {
345
366
  const commandMessage = {
346
367
  role: 'command',
347
368
  content: '',
348
- commandName: commandName
369
+ commandName: commandName,
349
370
  };
350
371
  setMessages(prev => [...prev, commandMessage]);
351
372
  }
@@ -354,7 +375,7 @@ export default function ChatScreen({}) {
354
375
  const commandMessage = {
355
376
  role: 'command',
356
377
  content: '',
357
- commandName: commandName
378
+ commandName: commandName,
358
379
  };
359
380
  setMessages(prev => [...prev, commandMessage]);
360
381
  }
@@ -366,16 +387,18 @@ export default function ChatScreen({}) {
366
387
  const commandMessage = {
367
388
  role: 'command',
368
389
  content: '',
369
- commandName: commandName
390
+ commandName: commandName,
370
391
  };
371
392
  setMessages(prev => [...prev, commandMessage]);
372
393
  }
373
- else if (result.success && result.action === 'initProject' && result.prompt) {
394
+ else if (result.success &&
395
+ result.action === 'initProject' &&
396
+ result.prompt) {
374
397
  // Add command execution feedback
375
398
  const commandMessage = {
376
399
  role: 'command',
377
400
  content: '',
378
- commandName: commandName
401
+ commandName: commandName,
379
402
  };
380
403
  setMessages(prev => [...prev, commandMessage]);
381
404
  // Auto-send the prompt using basicModel, hide the prompt from UI
@@ -456,8 +479,16 @@ export default function ChatScreen({}) {
456
479
  const regularFiles = validFiles.filter(f => !f.isImage);
457
480
  // Convert image files to image content format
458
481
  const imageContents = [
459
- ...(images || []).map(img => ({ type: 'image', data: img.data, mimeType: img.mimeType })),
460
- ...imageFiles.map(f => ({ type: 'image', data: f.imageData, mimeType: f.mimeType }))
482
+ ...(images || []).map(img => ({
483
+ type: 'image',
484
+ data: img.data,
485
+ mimeType: img.mimeType,
486
+ })),
487
+ ...imageFiles.map(f => ({
488
+ type: 'image',
489
+ data: f.imageData,
490
+ mimeType: f.mimeType,
491
+ })),
461
492
  ];
462
493
  // Get system information
463
494
  const systemInfo = getSystemInfo();
@@ -468,7 +499,7 @@ export default function ChatScreen({}) {
468
499
  content: cleanContent,
469
500
  files: validFiles.length > 0 ? validFiles : undefined,
470
501
  images: imageContents.length > 0 ? imageContents : undefined,
471
- systemInfo
502
+ systemInfo,
472
503
  };
473
504
  setMessages(prev => [...prev, userMessage]);
474
505
  }
@@ -497,7 +528,7 @@ export default function ChatScreen({}) {
497
528
  useBasicModel,
498
529
  getPendingMessages: () => pendingMessagesRef.current,
499
530
  clearPendingMessages: () => setPendingMessages([]),
500
- setIsStreaming
531
+ setIsStreaming,
501
532
  });
502
533
  }
503
534
  catch (error) {
@@ -508,7 +539,7 @@ export default function ChatScreen({}) {
508
539
  const finalMessage = {
509
540
  role: 'assistant',
510
541
  content: `Error: ${errorMessage}`,
511
- streaming: false
542
+ streaming: false,
512
543
  };
513
544
  setMessages(prev => [...prev, finalMessage]);
514
545
  }
@@ -538,7 +569,7 @@ export default function ChatScreen({}) {
538
569
  // Save user message
539
570
  saveMessage({
540
571
  role: 'user',
541
- content: combinedMessage
572
+ content: combinedMessage,
542
573
  }).catch(error => {
543
574
  console.error('Failed to save user message:', error);
544
575
  });
@@ -560,7 +591,7 @@ export default function ChatScreen({}) {
560
591
  setContextUsage,
561
592
  getPendingMessages: () => pendingMessagesRef.current,
562
593
  clearPendingMessages: () => setPendingMessages([]),
563
- setIsStreaming
594
+ setIsStreaming,
564
595
  });
565
596
  }
566
597
  catch (error) {
@@ -571,7 +602,7 @@ export default function ChatScreen({}) {
571
602
  const finalMessage = {
572
603
  role: 'assistant',
573
604
  content: `Error: ${errorMessage}`,
574
- streaming: false
605
+ streaming: false,
575
606
  };
576
607
  setMessages(prev => [...prev, finalMessage]);
577
608
  }
@@ -589,12 +620,13 @@ export default function ChatScreen({}) {
589
620
  if (terminalHeight < MIN_TERMINAL_HEIGHT) {
590
621
  return (React.createElement(Box, { flexDirection: "column", padding: 2 },
591
622
  React.createElement(Box, { borderStyle: "round", borderColor: "red", padding: 1 },
592
- React.createElement(Text, { color: "red", bold: true }, "\u26A0 Terminal Too Small")),
623
+ React.createElement(Text, { color: "red", bold: true }, "\u26A0 Terminal Too Small")),
593
624
  React.createElement(Box, { marginTop: 1 },
594
625
  React.createElement(Text, { color: "yellow" },
595
626
  "Your terminal height is ",
596
627
  terminalHeight,
597
- " lines, but at least ",
628
+ " lines, but at least",
629
+ ' ',
598
630
  MIN_TERMINAL_HEIGHT,
599
631
  " lines are required.")),
600
632
  React.createElement(Box, { marginTop: 1 },
@@ -613,7 +645,9 @@ export default function ChatScreen({}) {
613
645
  React.createElement(Text, { color: "gray", dimColor: true },
614
646
  "\u2022 Working directory: ",
615
647
  workingDirectory))),
616
- ...messages.filter(m => !m.streaming && !m.toolPending).map((message, index) => {
648
+ ...messages
649
+ .filter(m => !m.streaming && !m.toolPending)
650
+ .map((message, index) => {
617
651
  // Determine tool message type and color
618
652
  let toolStatusColor = 'cyan';
619
653
  let isToolMessage = false;
@@ -636,42 +670,89 @@ export default function ChatScreen({}) {
636
670
  }
637
671
  return (React.createElement(Box, { key: `msg-${index}`, marginBottom: isToolMessage ? 0 : 1, marginX: 1, flexDirection: "column" },
638
672
  React.createElement(Box, null,
639
- React.createElement(Text, { color: message.role === 'user' ? 'green' :
640
- message.role === 'command' ? 'gray' : toolStatusColor, bold: true }, message.role === 'user' ? '⛇' : message.role === 'command' ? '⌘' : '❆'),
673
+ React.createElement(Text, { color: message.role === 'user'
674
+ ? 'green'
675
+ : message.role === 'command'
676
+ ? 'gray'
677
+ : toolStatusColor, bold: true }, message.role === 'user'
678
+ ? '⛇'
679
+ : message.role === 'command'
680
+ ? '⌘'
681
+ : '❆'),
641
682
  React.createElement(Box, { marginLeft: 1, marginBottom: 1, flexDirection: "column" }, message.role === 'command' ? (React.createElement(Text, { color: "gray", dimColor: true },
642
683
  "\u2514\u2500 ",
643
684
  message.commandName)) : message.showTodoTree ? (React.createElement(TodoTree, { todos: currentTodos })) : (React.createElement(React.Fragment, null,
644
- React.createElement(MarkdownRenderer, { content: message.content || ' ', color: message.role === 'user' ? 'gray' :
645
- isToolMessage ? (message.content.startsWith('') ? 'yellow' :
646
- message.content.startsWith('✓') ? 'green' : 'red') : undefined }),
647
- message.toolDisplay && message.toolDisplay.args.length > 0 && (React.createElement(Box, { flexDirection: "column" }, message.toolDisplay.args.map((arg, argIndex) => (React.createElement(Text, { key: argIndex, color: "gray", dimColor: true },
685
+ React.createElement(MarkdownRenderer, { content: message.content || ' ', color: message.role === 'user'
686
+ ? 'gray'
687
+ : isToolMessage
688
+ ? message.content.startsWith('⚡')
689
+ ? 'yellow'
690
+ : message.content.startsWith('✓')
691
+ ? 'green'
692
+ : 'red'
693
+ : undefined }),
694
+ message.toolDisplay &&
695
+ message.toolDisplay.args.length > 0 && (React.createElement(Box, { flexDirection: "column" }, message.toolDisplay.args.map((arg, argIndex) => (React.createElement(Text, { key: argIndex, color: "gray", dimColor: true },
648
696
  arg.isLast ? '└─' : '├─',
649
697
  " ",
650
698
  arg.key,
651
- ": ",
699
+ ":",
700
+ ' ',
652
701
  arg.value))))),
653
- message.toolCall && (message.toolCall.name === 'filesystem-create' || message.toolCall.name === 'filesystem-write') && message.toolCall.arguments.content && (React.createElement(Box, { marginTop: 1 },
654
- React.createElement(DiffViewer, { newContent: message.toolCall.arguments.content, filename: message.toolCall.arguments.path, maxLines: 50 }))),
655
- message.toolCall && message.toolCall.name === 'filesystem-edit' && message.toolCall.arguments.oldContent && message.toolCall.arguments.newContent && (React.createElement(Box, { marginTop: 1 },
656
- React.createElement(DiffViewer, { oldContent: message.toolCall.arguments.oldContent, newContent: message.toolCall.arguments.newContent, filename: message.toolCall.arguments.filename, maxLines: 50 }))),
657
- message.toolCall && message.toolCall.name === 'terminal-execute' && message.toolCall.arguments.command && (React.createElement(Box, { marginTop: 1, flexDirection: "column" },
702
+ message.toolCall &&
703
+ (message.toolCall.name === 'filesystem-create' ||
704
+ message.toolCall.name === 'filesystem-write') &&
705
+ message.toolCall.arguments.content && (React.createElement(Box, { marginTop: 1 },
706
+ React.createElement(DiffViewer, { newContent: message.toolCall.arguments.content, filename: message.toolCall.arguments.path }))),
707
+ message.toolCall &&
708
+ message.toolCall.name === 'filesystem-edit' &&
709
+ message.toolCall.arguments.oldContent &&
710
+ message.toolCall.arguments.newContent && (React.createElement(Box, { marginTop: 1 },
711
+ React.createElement(DiffViewer, { oldContent: message.toolCall.arguments.oldContent, newContent: message.toolCall.arguments.newContent, filename: message.toolCall.arguments.filename }))),
712
+ message.toolCall &&
713
+ message.toolCall.name === 'terminal-execute' &&
714
+ message.toolCall.arguments.command && (React.createElement(Box, { marginTop: 1, flexDirection: "column" },
658
715
  React.createElement(Text, { color: "gray", dimColor: true },
659
- "\u2514\u2500 Command: ",
716
+ "\u2514\u2500 Command:",
717
+ ' ',
660
718
  React.createElement(Text, { color: "white" }, message.toolCall.arguments.command)),
661
719
  React.createElement(Text, { color: "gray", dimColor: true },
662
- "\u2514\u2500 Exit Code: ",
663
- React.createElement(Text, { color: message.toolCall.arguments.exitCode === 0 ? 'green' : 'red' }, message.toolCall.arguments.exitCode)),
664
- message.toolCall.arguments.stdout && message.toolCall.arguments.stdout.trim().length > 0 && (React.createElement(Box, { flexDirection: "column", marginTop: 1 },
720
+ "\u2514\u2500 Exit Code:",
721
+ ' ',
722
+ React.createElement(Text, { color: message.toolCall.arguments.exitCode === 0
723
+ ? 'green'
724
+ : 'red' }, message.toolCall.arguments.exitCode)),
725
+ message.toolCall.arguments.stdout &&
726
+ message.toolCall.arguments.stdout.trim()
727
+ .length > 0 && (React.createElement(Box, { flexDirection: "column", marginTop: 1 },
665
728
  React.createElement(Text, { color: "green", dimColor: true }, "\u2514\u2500 stdout:"),
666
729
  React.createElement(Box, { paddingLeft: 2 },
667
- React.createElement(Text, { color: "white" }, message.toolCall.arguments.stdout.trim().split('\n').slice(0, 20).join('\n')),
668
- message.toolCall.arguments.stdout.trim().split('\n').length > 20 && (React.createElement(Text, { color: "gray", dimColor: true }, "... (output truncated)"))))),
669
- message.toolCall.arguments.stderr && message.toolCall.arguments.stderr.trim().length > 0 && (React.createElement(Box, { flexDirection: "column", marginTop: 1 },
730
+ React.createElement(Text, { color: "white" }, message.toolCall.arguments.stdout
731
+ .trim()
732
+ .split('\n')
733
+ .slice(0, 20)
734
+ .join('\n')),
735
+ message.toolCall.arguments.stdout
736
+ .trim()
737
+ .split('\n').length > 20 && (React.createElement(Text, { color: "gray", dimColor: true }, "... (output truncated)"))))),
738
+ message.toolCall.arguments.stderr &&
739
+ message.toolCall.arguments.stderr.trim()
740
+ .length > 0 && (React.createElement(Box, { flexDirection: "column", marginTop: 1 },
670
741
  React.createElement(Text, { color: "red", dimColor: true }, "\u2514\u2500 stderr:"),
671
742
  React.createElement(Box, { paddingLeft: 2 },
672
- React.createElement(Text, { color: "red" }, message.toolCall.arguments.stderr.trim().split('\n').slice(0, 10).join('\n')),
673
- message.toolCall.arguments.stderr.trim().split('\n').length > 10 && (React.createElement(Text, { color: "gray", dimColor: true }, "... (output truncated)"))))))),
674
- message.content.startsWith('✓') && message.toolResult && !message.toolCall && (React.createElement(ToolResultPreview, { toolName: message.content.replace('✓ ', '').split('\n')[0] || '', result: message.toolResult, maxLines: 5 })),
743
+ React.createElement(Text, { color: "red" }, message.toolCall.arguments.stderr
744
+ .trim()
745
+ .split('\n')
746
+ .slice(0, 10)
747
+ .join('\n')),
748
+ message.toolCall.arguments.stderr
749
+ .trim()
750
+ .split('\n').length > 10 && (React.createElement(Text, { color: "gray", dimColor: true }, "... (output truncated)"))))))),
751
+ message.content.startsWith('✓') &&
752
+ message.toolResult &&
753
+ !message.toolCall && (React.createElement(ToolResultPreview, { toolName: message.content
754
+ .replace('✓ ', '')
755
+ .split('\n')[0] || '', result: message.toolResult, maxLines: 5 })),
675
756
  message.role === 'user' && message.systemInfo && (React.createElement(Box, { marginTop: 1, flexDirection: "column" },
676
757
  React.createElement(Text, { color: "gray", dimColor: true },
677
758
  "\u2514\u2500 Platform: ",
@@ -680,20 +761,27 @@ export default function ChatScreen({}) {
680
761
  "\u2514\u2500 Shell: ",
681
762
  message.systemInfo.shell),
682
763
  React.createElement(Text, { color: "gray", dimColor: true },
683
- "\u2514\u2500 Working Directory: ",
764
+ "\u2514\u2500 Working Directory:",
765
+ ' ',
684
766
  message.systemInfo.workingDirectory))),
685
767
  message.files && message.files.length > 0 && (React.createElement(Box, { flexDirection: "column" }, message.files.map((file, fileIndex) => (React.createElement(Text, { key: fileIndex, color: "gray", dimColor: true },
686
768
  "\u2514\u2500 ",
687
769
  file.path,
688
- file.exists ? ` (total line ${file.lineCount})` : ' (file not found)'))))),
689
- message.role === 'user' && message.images && message.images.length > 0 && (React.createElement(Box, { marginTop: 1, flexDirection: "column" }, message.images.map((_image, imageIndex) => (React.createElement(Text, { key: imageIndex, color: "gray", dimColor: true },
770
+ file.exists
771
+ ? ` (total line ${file.lineCount})`
772
+ : ' (file not found)'))))),
773
+ message.role === 'user' &&
774
+ message.images &&
775
+ message.images.length > 0 && (React.createElement(Box, { marginTop: 1, flexDirection: "column" }, message.images.map((_image, imageIndex) => (React.createElement(Text, { key: imageIndex, color: "gray", dimColor: true },
690
776
  "\u2514\u2500 [image #",
691
777
  imageIndex + 1,
692
778
  "]"))))),
693
779
  message.discontinued && (React.createElement(Text, { color: "red", bold: true }, "\u2514\u2500 user discontinue"))))))));
694
- })
695
- ] }, (item) => item),
696
- messages.filter(m => m.toolPending).map((message, index) => (React.createElement(Box, { key: `pending-tool-${index}`, marginBottom: 1, marginX: 1 },
780
+ }),
781
+ ] }, item => item),
782
+ messages
783
+ .filter(m => m.toolPending)
784
+ .map((message, index) => (React.createElement(Box, { key: `pending-tool-${index}`, marginBottom: 1, marginX: 1 },
697
785
  React.createElement(Text, { color: "yellowBright", bold: true }, "\u2746"),
698
786
  React.createElement(Box, { marginLeft: 1, marginBottom: 1, flexDirection: "row" },
699
787
  React.createElement(MarkdownRenderer, { content: message.content || ' ', color: "yellow" }),
@@ -709,15 +797,20 @@ export default function ChatScreen({}) {
709
797
  streamTokenCount > 0 && (React.createElement(React.Fragment, null,
710
798
  ' · ',
711
799
  React.createElement(Text, { color: "cyan" },
712
- "\u2193 ",
800
+ "\u2193",
801
+ ' ',
713
802
  streamTokenCount >= 1000
714
803
  ? `${(streamTokenCount / 1000).toFixed(1)}k`
715
804
  : streamTokenCount,
716
- " tokens"))),
805
+ ' ',
806
+ "tokens"))),
717
807
  ")")) : ('Create the first dialogue record file...'))))),
718
808
  React.createElement(Box, { marginX: 1 },
719
809
  React.createElement(PendingMessages, { pendingMessages: pendingMessages })),
720
- pendingToolConfirmation && (React.createElement(ToolConfirmation, { toolName: pendingToolConfirmation.batchToolNames || pendingToolConfirmation.tool.function.name, onConfirm: pendingToolConfirmation.resolve })),
810
+ pendingToolConfirmation && (React.createElement(ToolConfirmation, { toolName: pendingToolConfirmation.batchToolNames ||
811
+ pendingToolConfirmation.tool.function.name, toolArguments: !pendingToolConfirmation.allTools
812
+ ? pendingToolConfirmation.tool.function.arguments
813
+ : undefined, allTools: pendingToolConfirmation.allTools, onConfirm: pendingToolConfirmation.resolve })),
721
814
  showSessionPanel && (React.createElement(Box, { marginX: 1 },
722
815
  React.createElement(SessionListPanel, { onSelectSession: handleSessionPanelSelect, onClose: () => setShowSessionPanel(false) }))),
723
816
  showMcpPanel && (React.createElement(Box, { marginX: 1, flexDirection: "column" },
@@ -725,24 +818,43 @@ export default function ChatScreen({}) {
725
818
  React.createElement(Box, { marginTop: 1 },
726
819
  React.createElement(Text, { color: "gray", dimColor: true }, "Press ESC to close")))),
727
820
  pendingRollback && (React.createElement(FileRollbackConfirmation, { fileCount: pendingRollback.fileCount, onConfirm: handleRollbackConfirm })),
728
- !pendingToolConfirmation && !isCompressing && !showSessionPanel && !showMcpPanel && !pendingRollback && (React.createElement(React.Fragment, null,
729
- React.createElement(ChatInput, { onSubmit: handleMessageSubmit, onCommand: handleCommandExecution, placeholder: "Ask me anything about coding...", disabled: !!pendingToolConfirmation, chatHistory: messages, onHistorySelect: handleHistorySelect, yoloMode: yoloMode, contextUsage: contextUsage ? {
730
- inputTokens: contextUsage.prompt_tokens,
731
- maxContextTokens: getOpenAiConfig().maxContextTokens || 4000,
732
- cacheCreationTokens: contextUsage.cache_creation_input_tokens,
733
- cacheReadTokens: contextUsage.cache_read_input_tokens,
734
- cachedTokens: contextUsage.cached_tokens
735
- } : undefined, snapshotFileCount: snapshotFileCount }),
821
+ !pendingToolConfirmation &&
822
+ !isCompressing &&
823
+ !showSessionPanel &&
824
+ !showMcpPanel &&
825
+ !pendingRollback && (React.createElement(React.Fragment, null,
826
+ React.createElement(ChatInput, { onSubmit: handleMessageSubmit, onCommand: handleCommandExecution, placeholder: "Ask me anything about coding...", disabled: !!pendingToolConfirmation, chatHistory: messages, onHistorySelect: handleHistorySelect, yoloMode: yoloMode, contextUsage: contextUsage
827
+ ? {
828
+ inputTokens: contextUsage.prompt_tokens,
829
+ maxContextTokens: getOpenAiConfig().maxContextTokens || 4000,
830
+ cacheCreationTokens: contextUsage.cache_creation_input_tokens,
831
+ cacheReadTokens: contextUsage.cache_read_input_tokens,
832
+ cachedTokens: contextUsage.cached_tokens,
833
+ }
834
+ : undefined, snapshotFileCount: snapshotFileCount }),
736
835
  vscodeConnectionStatus !== 'disconnected' && (React.createElement(Box, { marginTop: 1 },
737
- React.createElement(Text, { color: vscodeConnectionStatus === 'connecting' ? 'yellow' :
738
- vscodeConnectionStatus === 'connected' ? 'green' :
739
- vscodeConnectionStatus === 'error' ? 'red' : 'gray', dimColor: vscodeConnectionStatus !== 'error' },
740
- "\u25CF ",
741
- vscodeConnectionStatus === 'connecting' ? 'Connecting to VSCode...' :
742
- vscodeConnectionStatus === 'connected' ? 'VSCode Connected' :
743
- vscodeConnectionStatus === 'error' ? 'Connection Failed' : 'VSCode',
744
- vscodeConnectionStatus === 'connected' && editorContext.activeFile && ` | ${editorContext.activeFile}`,
745
- vscodeConnectionStatus === 'connected' && editorContext.selectedText && ` | ${editorContext.selectedText.length} chars selected`))))),
836
+ React.createElement(Text, { color: vscodeConnectionStatus === 'connecting'
837
+ ? 'yellow'
838
+ : vscodeConnectionStatus === 'connected'
839
+ ? 'green'
840
+ : vscodeConnectionStatus === 'error'
841
+ ? 'red'
842
+ : 'gray', dimColor: vscodeConnectionStatus !== 'error' },
843
+ "\u25CF",
844
+ ' ',
845
+ vscodeConnectionStatus === 'connecting'
846
+ ? 'Waiting for VSCode extension to connect...'
847
+ : vscodeConnectionStatus === 'connected'
848
+ ? 'VSCode Connected'
849
+ : vscodeConnectionStatus === 'error'
850
+ ? 'Connection Failed - Make sure Snow CLI extension is installed and active in VSCode'
851
+ : 'VSCode',
852
+ vscodeConnectionStatus === 'connected' &&
853
+ editorContext.activeFile &&
854
+ ` | ${editorContext.activeFile}`,
855
+ vscodeConnectionStatus === 'connected' &&
856
+ editorContext.selectedText &&
857
+ ` | ${editorContext.selectedText.length} chars selected`))))),
746
858
  isCompressing && (React.createElement(Box, { marginTop: 1 },
747
859
  React.createElement(Text, { color: "cyan" },
748
860
  React.createElement(Spinner, { type: "dots" }),