snow-ai 0.3.5 → 0.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 (101) hide show
  1. package/dist/agents/reviewAgent.d.ts +50 -0
  2. package/dist/agents/reviewAgent.js +264 -0
  3. package/dist/api/anthropic.js +104 -71
  4. package/dist/api/chat.d.ts +1 -1
  5. package/dist/api/chat.js +60 -41
  6. package/dist/api/gemini.js +97 -57
  7. package/dist/api/responses.d.ts +9 -1
  8. package/dist/api/responses.js +110 -70
  9. package/dist/api/systemPrompt.d.ts +1 -1
  10. package/dist/api/systemPrompt.js +36 -7
  11. package/dist/api/types.d.ts +8 -0
  12. package/dist/hooks/useCommandHandler.d.ts +1 -0
  13. package/dist/hooks/useCommandHandler.js +44 -1
  14. package/dist/hooks/useCommandPanel.js +13 -0
  15. package/dist/hooks/useConversation.d.ts +4 -1
  16. package/dist/hooks/useConversation.js +65 -9
  17. package/dist/hooks/useKeyboardInput.js +19 -0
  18. package/dist/hooks/useTerminalFocus.js +13 -3
  19. package/dist/mcp/aceCodeSearch.d.ts +2 -76
  20. package/dist/mcp/aceCodeSearch.js +31 -467
  21. package/dist/mcp/bash.d.ts +1 -8
  22. package/dist/mcp/bash.js +20 -40
  23. package/dist/mcp/filesystem.d.ts +3 -68
  24. package/dist/mcp/filesystem.js +32 -348
  25. package/dist/mcp/ideDiagnostics.js +2 -4
  26. package/dist/mcp/todo.d.ts +1 -17
  27. package/dist/mcp/todo.js +11 -15
  28. package/dist/mcp/types/aceCodeSearch.types.d.ts +92 -0
  29. package/dist/mcp/types/aceCodeSearch.types.js +4 -0
  30. package/dist/mcp/types/bash.types.d.ts +13 -0
  31. package/dist/mcp/types/bash.types.js +4 -0
  32. package/dist/mcp/types/filesystem.types.d.ts +44 -0
  33. package/dist/mcp/types/filesystem.types.js +4 -0
  34. package/dist/mcp/types/todo.types.d.ts +27 -0
  35. package/dist/mcp/types/todo.types.js +4 -0
  36. package/dist/mcp/types/websearch.types.d.ts +30 -0
  37. package/dist/mcp/types/websearch.types.js +4 -0
  38. package/dist/mcp/utils/aceCodeSearch/filesystem.utils.d.ts +34 -0
  39. package/dist/mcp/utils/aceCodeSearch/filesystem.utils.js +146 -0
  40. package/dist/mcp/utils/aceCodeSearch/language.utils.d.ts +14 -0
  41. package/dist/mcp/utils/aceCodeSearch/language.utils.js +99 -0
  42. package/dist/mcp/utils/aceCodeSearch/search.utils.d.ts +31 -0
  43. package/dist/mcp/utils/aceCodeSearch/search.utils.js +136 -0
  44. package/dist/mcp/utils/aceCodeSearch/symbol.utils.d.ts +20 -0
  45. package/dist/mcp/utils/aceCodeSearch/symbol.utils.js +141 -0
  46. package/dist/mcp/utils/bash/security.utils.d.ts +20 -0
  47. package/dist/mcp/utils/bash/security.utils.js +34 -0
  48. package/dist/mcp/utils/filesystem/code-analysis.utils.d.ts +18 -0
  49. package/dist/mcp/utils/filesystem/code-analysis.utils.js +165 -0
  50. package/dist/mcp/utils/filesystem/match-finder.utils.d.ts +16 -0
  51. package/dist/mcp/utils/filesystem/match-finder.utils.js +85 -0
  52. package/dist/mcp/utils/filesystem/similarity.utils.d.ts +22 -0
  53. package/dist/mcp/utils/filesystem/similarity.utils.js +75 -0
  54. package/dist/mcp/utils/todo/date.utils.d.ts +9 -0
  55. package/dist/mcp/utils/todo/date.utils.js +14 -0
  56. package/dist/mcp/utils/websearch/browser.utils.d.ts +8 -0
  57. package/dist/mcp/utils/websearch/browser.utils.js +58 -0
  58. package/dist/mcp/utils/websearch/text.utils.d.ts +16 -0
  59. package/dist/mcp/utils/websearch/text.utils.js +39 -0
  60. package/dist/mcp/websearch.d.ts +1 -31
  61. package/dist/mcp/websearch.js +21 -97
  62. package/dist/ui/components/ChatInput.d.ts +2 -1
  63. package/dist/ui/components/ChatInput.js +10 -3
  64. package/dist/ui/components/MarkdownRenderer.d.ts +1 -2
  65. package/dist/ui/components/MarkdownRenderer.js +16 -153
  66. package/dist/ui/components/MessageList.js +4 -4
  67. package/dist/ui/components/SessionListScreen.js +37 -17
  68. package/dist/ui/components/ToolResultPreview.js +27 -7
  69. package/dist/ui/components/UsagePanel.d.ts +2 -0
  70. package/dist/ui/components/UsagePanel.js +360 -0
  71. package/dist/ui/pages/ChatScreen.d.ts +4 -0
  72. package/dist/ui/pages/ChatScreen.js +70 -30
  73. package/dist/ui/pages/ConfigScreen.js +23 -19
  74. package/dist/ui/pages/HeadlessModeScreen.js +2 -4
  75. package/dist/ui/pages/SubAgentConfigScreen.js +17 -17
  76. package/dist/ui/pages/SystemPromptConfigScreen.js +7 -6
  77. package/dist/utils/commandExecutor.d.ts +3 -3
  78. package/dist/utils/commandExecutor.js +4 -4
  79. package/dist/utils/commands/home.d.ts +2 -0
  80. package/dist/utils/commands/home.js +12 -0
  81. package/dist/utils/commands/review.d.ts +2 -0
  82. package/dist/utils/commands/review.js +81 -0
  83. package/dist/utils/commands/role.d.ts +2 -0
  84. package/dist/utils/commands/role.js +37 -0
  85. package/dist/utils/commands/usage.d.ts +2 -0
  86. package/dist/utils/commands/usage.js +12 -0
  87. package/dist/utils/contextCompressor.js +99 -367
  88. package/dist/utils/fileUtils.js +3 -3
  89. package/dist/utils/mcpToolsManager.js +12 -12
  90. package/dist/utils/proxyUtils.d.ts +15 -0
  91. package/dist/utils/proxyUtils.js +50 -0
  92. package/dist/utils/retryUtils.d.ts +27 -0
  93. package/dist/utils/retryUtils.js +114 -2
  94. package/dist/utils/sessionManager.d.ts +2 -5
  95. package/dist/utils/sessionManager.js +16 -83
  96. package/dist/utils/terminal.js +4 -3
  97. package/dist/utils/usageLogger.d.ts +11 -0
  98. package/dist/utils/usageLogger.js +99 -0
  99. package/package.json +3 -7
  100. package/dist/agents/summaryAgent.d.ts +0 -31
  101. package/dist/agents/summaryAgent.js +0 -256
@@ -3,7 +3,7 @@ import { createStreamingChatCompletion } from '../api/chat.js';
3
3
  import { createStreamingResponse } from '../api/responses.js';
4
4
  import { createStreamingGeminiCompletion } from '../api/gemini.js';
5
5
  import { createStreamingAnthropicCompletion } from '../api/anthropic.js';
6
- import { SYSTEM_PROMPT } from '../api/systemPrompt.js';
6
+ import { getSystemPrompt } from '../api/systemPrompt.js';
7
7
  import { collectAllMCPTools, getTodoService } from '../utils/mcpToolsManager.js';
8
8
  import { executeToolCalls } from '../utils/toolExecutor.js';
9
9
  import { getOpenAiConfig } from '../utils/apiConfig.js';
@@ -13,6 +13,7 @@ import { formatToolCallMessage } from '../utils/messageFormatter.js';
13
13
  import { resourceMonitor } from '../utils/resourceMonitor.js';
14
14
  /**
15
15
  * Handle conversation with streaming and tool calls
16
+ * Returns the usage data collected during the conversation
16
17
  */
17
18
  export async function handleConversationWithTools(options) {
18
19
  const { userContent, imageContents, controller,
@@ -34,7 +35,7 @@ export async function handleConversationWithTools(options) {
34
35
  const mcpTools = await collectAllMCPTools();
35
36
  // Build conversation history with TODO context as pinned user message
36
37
  let conversationMessages = [
37
- { role: 'system', content: SYSTEM_PROMPT },
38
+ { role: 'system', content: getSystemPrompt() },
38
39
  ];
39
40
  // If there are TODOs, add pinned context message at the front
40
41
  if (existingTodoList && existingTodoList.todos.length > 0) {
@@ -98,6 +99,8 @@ export async function handleConversationWithTools(options) {
98
99
  : config.advancedModel || 'gpt-4.1';
99
100
  // Tool calling loop (no limit on rounds)
100
101
  let finalAssistantMessage = null;
102
+ // Accumulate usage data across all rounds
103
+ let accumulatedUsage = null;
101
104
  // Local set to track approved tools in this conversation (solves async setState issue)
102
105
  const sessionApprovedTools = new Set();
103
106
  try {
@@ -108,6 +111,7 @@ export async function handleConversationWithTools(options) {
108
111
  }
109
112
  let streamedContent = '';
110
113
  let receivedToolCalls;
114
+ let receivedReasoning;
111
115
  // Stream AI response - choose API based on config
112
116
  let toolCallAccumulator = ''; // Accumulate tool call deltas for token counting
113
117
  let reasoningAccumulator = ''; // Accumulate reasoning summary deltas for token counting (Responses API only)
@@ -149,6 +153,7 @@ export async function handleConversationWithTools(options) {
149
153
  messages: conversationMessages,
150
154
  temperature: 0,
151
155
  tools: mcpTools.length > 0 ? mcpTools : undefined,
156
+ tool_choice: 'auto',
152
157
  prompt_cache_key: cacheKey, // Use session ID as cache key
153
158
  }, controller.signal, onRetry)
154
159
  : createStreamingChatCompletion({
@@ -211,9 +216,41 @@ export async function handleConversationWithTools(options) {
211
216
  else if (chunk.type === 'tool_calls' && chunk.tool_calls) {
212
217
  receivedToolCalls = chunk.tool_calls;
213
218
  }
219
+ else if (chunk.type === 'reasoning_data' && chunk.reasoning) {
220
+ // Capture reasoning data from Responses API
221
+ receivedReasoning = chunk.reasoning;
222
+ }
214
223
  else if (chunk.type === 'usage' && chunk.usage) {
215
- // Capture usage information
224
+ // Capture usage information both in state and locally
216
225
  setContextUsage(chunk.usage);
226
+ // Note: Usage is now saved at API layer (chat.ts, anthropic.ts, etc.)
227
+ // No need to call onUsageUpdate here to avoid duplicate saves
228
+ // Accumulate for final return (UI display purposes)
229
+ if (!accumulatedUsage) {
230
+ accumulatedUsage = {
231
+ prompt_tokens: chunk.usage.prompt_tokens || 0,
232
+ completion_tokens: chunk.usage.completion_tokens || 0,
233
+ total_tokens: chunk.usage.total_tokens || 0,
234
+ cache_creation_input_tokens: chunk.usage.cache_creation_input_tokens,
235
+ cache_read_input_tokens: chunk.usage.cache_read_input_tokens,
236
+ };
237
+ }
238
+ else {
239
+ // Add to existing usage for UI display
240
+ accumulatedUsage.prompt_tokens += chunk.usage.prompt_tokens || 0;
241
+ accumulatedUsage.completion_tokens += chunk.usage.completion_tokens || 0;
242
+ accumulatedUsage.total_tokens += chunk.usage.total_tokens || 0;
243
+ if (chunk.usage.cache_creation_input_tokens !== undefined) {
244
+ accumulatedUsage.cache_creation_input_tokens =
245
+ (accumulatedUsage.cache_creation_input_tokens || 0) +
246
+ chunk.usage.cache_creation_input_tokens;
247
+ }
248
+ if (chunk.usage.cache_read_input_tokens !== undefined) {
249
+ accumulatedUsage.cache_read_input_tokens =
250
+ (accumulatedUsage.cache_read_input_tokens || 0) +
251
+ chunk.usage.cache_read_input_tokens;
252
+ }
253
+ }
217
254
  }
218
255
  }
219
256
  // Reset token count after stream ends
@@ -238,6 +275,7 @@ export async function handleConversationWithTools(options) {
238
275
  arguments: tc.function.arguments,
239
276
  },
240
277
  })),
278
+ reasoning: receivedReasoning, // Include reasoning data for caching
241
279
  };
242
280
  conversationMessages.push(assistantMessage);
243
281
  // Save assistant message with tool calls
@@ -326,7 +364,7 @@ export async function handleConversationWithTools(options) {
326
364
  options.setIsStreaming(false);
327
365
  }
328
366
  freeEncoder();
329
- return; // Exit the conversation loop
367
+ return { usage: accumulatedUsage }; // Exit the conversation loop
330
368
  }
331
369
  // If approved_always, add ALL these tools to both global and session-approved sets
332
370
  if (confirmation === 'approve_always') {
@@ -531,10 +569,24 @@ export async function handleConversationWithTools(options) {
531
569
  for (const result of toolResults) {
532
570
  const toolCall = receivedToolCalls.find(tc => tc.id === result.tool_call_id);
533
571
  if (toolCall) {
534
- // Skip displaying result for sub-agent tools here
535
- // Sub-agent results will be added by the callback after internal messages
572
+ // Special handling for sub-agent tools - show completion message
573
+ // Pass the full JSON result to ToolResultPreview for proper parsing
536
574
  if (toolCall.function.name.startsWith('subagent-')) {
537
- // Still save the tool result to conversation history
575
+ const isError = result.content.startsWith('Error:');
576
+ const statusIcon = isError ? '✗' : '✓';
577
+ const statusText = isError ? `\n └─ ${result.content}` : '';
578
+ // Display subagent completion message in main flow
579
+ setMessages(prev => [
580
+ ...prev,
581
+ {
582
+ role: 'assistant',
583
+ content: `${statusIcon} ${toolCall.function.name}${statusText}`,
584
+ streaming: false,
585
+ // Pass the full result.content for ToolResultPreview to parse
586
+ toolResult: !isError ? result.content : undefined,
587
+ },
588
+ ]);
589
+ // Save the tool result to conversation history
538
590
  conversationMessages.push(result);
539
591
  saveMessage(result).catch(error => {
540
592
  console.error('Failed to save tool result:', error);
@@ -638,7 +690,8 @@ export async function handleConversationWithTools(options) {
638
690
  // Continue loop to get next response
639
691
  continue;
640
692
  }
641
- // No tool calls - display text content if any
693
+ // No tool calls - conversation is complete
694
+ // Display text content if any
642
695
  if (streamedContent.trim()) {
643
696
  finalAssistantMessage = {
644
697
  role: 'assistant',
@@ -651,13 +704,14 @@ export async function handleConversationWithTools(options) {
651
704
  const assistantMessage = {
652
705
  role: 'assistant',
653
706
  content: streamedContent.trim(),
707
+ reasoning: receivedReasoning, // Include reasoning data for caching
654
708
  };
655
709
  conversationMessages.push(assistantMessage);
656
710
  saveMessage(assistantMessage).catch(error => {
657
711
  console.error('Failed to save assistant message:', error);
658
712
  });
659
713
  }
660
- // Conversation complete
714
+ // Conversation complete - exit the loop
661
715
  break;
662
716
  }
663
717
  // Free encoder
@@ -667,4 +721,6 @@ export async function handleConversationWithTools(options) {
667
721
  freeEncoder();
668
722
  throw error;
669
723
  }
724
+ // Return the accumulated usage data
725
+ return { usage: accumulatedUsage };
670
726
  }
@@ -223,6 +223,25 @@ export function useKeyboardInput(options) {
223
223
  if (key.return) {
224
224
  const message = buffer.getFullText().trim();
225
225
  if (message) {
226
+ // Check if message is a command with arguments (e.g., /review [note])
227
+ if (message.startsWith('/')) {
228
+ const commandMatch = message.match(/^\/(\w+)(?:\s+(.+))?$/);
229
+ if (commandMatch && commandMatch[1]) {
230
+ const commandName = commandMatch[1];
231
+ const commandArgs = commandMatch[2];
232
+ // Execute command with arguments
233
+ executeCommand(commandName, commandArgs).then(result => {
234
+ if (onCommand) {
235
+ onCommand(commandName, result);
236
+ }
237
+ });
238
+ buffer.setText('');
239
+ setShowCommands(false);
240
+ setCommandSelectedIndex(0);
241
+ triggerUpdate();
242
+ return;
243
+ }
244
+ }
226
245
  // Get images data, but only include images whose placeholders still exist
227
246
  const currentText = buffer.text; // Use internal text (includes placeholders)
228
247
  const allImages = buffer.getImages();
@@ -25,6 +25,7 @@ import { useState, useEffect } from 'react';
25
25
  export function useTerminalFocus() {
26
26
  const [hasFocus, setHasFocus] = useState(true); // Default to focused
27
27
  useEffect(() => {
28
+ let syncTimer = null;
28
29
  // Set up listener first
29
30
  const handleData = (data) => {
30
31
  const str = data.toString();
@@ -53,18 +54,27 @@ export function useTerminalFocus() {
53
54
  process.stdin.on('data', handleData);
54
55
  // Enable focus reporting AFTER listener is set up
55
56
  // Add a small delay to ensure listener is fully registered
56
- const timer = setTimeout(() => {
57
+ const enableTimer = setTimeout(() => {
57
58
  // ESC[?1004h - Enable focus events
58
59
  process.stdout.write('\x1b[?1004h');
60
+ // After enabling focus reporting, assume terminal has focus
61
+ // This ensures cursor is visible after component remount (e.g., after /clear)
62
+ // The terminal will send ESC[O if it doesn't have focus
63
+ syncTimer = setTimeout(() => {
64
+ setHasFocus(true);
65
+ }, 100);
59
66
  }, 50);
60
67
  return () => {
61
- clearTimeout(timer);
68
+ clearTimeout(enableTimer);
69
+ if (syncTimer) {
70
+ clearTimeout(syncTimer);
71
+ }
62
72
  // Disable focus reporting on cleanup
63
73
  // ESC[?1004l - Disable focus events
64
74
  process.stdout.write('\x1b[?1004l');
65
75
  process.stdin.off('data', handleData);
66
76
  };
67
- }, [hasFocus]); // Add hasFocus to dependencies to access current state
77
+ }, []); // Remove hasFocus from dependencies to avoid re-running effect
68
78
  // Helper function to check if input is a focus event
69
79
  const isFocusEvent = (input) => {
70
80
  return input === '\x1b[I' || input === '\x1b[O';
@@ -1,43 +1,4 @@
1
- /**
2
- * ACE Code Search Types
3
- */
4
- export interface CodeSymbol {
5
- name: string;
6
- type: 'function' | 'class' | 'method' | 'variable' | 'constant' | 'interface' | 'type' | 'enum' | 'import' | 'export';
7
- filePath: string;
8
- line: number;
9
- column: number;
10
- endLine?: number;
11
- endColumn?: number;
12
- signature?: string;
13
- scope?: string;
14
- language: string;
15
- context?: string;
16
- }
17
- export interface CodeReference {
18
- symbol: string;
19
- filePath: string;
20
- line: number;
21
- column: number;
22
- context: string;
23
- referenceType: 'definition' | 'usage' | 'import' | 'type';
24
- }
25
- export interface SemanticSearchResult {
26
- query: string;
27
- symbols: CodeSymbol[];
28
- references: CodeReference[];
29
- totalResults: number;
30
- searchTime: number;
31
- }
32
- export interface ASTNode {
33
- type: string;
34
- name?: string;
35
- line: number;
36
- column: number;
37
- endLine?: number;
38
- endColumn?: number;
39
- children?: ASTNode[];
40
- }
1
+ import type { CodeSymbol, CodeReference, SemanticSearchResult } from './types/aceCodeSearch.types.js';
41
2
  export declare class ACECodeSearchService {
42
3
  private basePath;
43
4
  private indexCache;
@@ -48,47 +9,17 @@ export declare class ACECodeSearchService {
48
9
  private fileModTimes;
49
10
  private customExcludes;
50
11
  private excludesLoaded;
51
- private regexCache;
52
12
  private fileContentCache;
53
- private readonly FILE_CONTENT_CACHE_SIZE;
54
- private readonly DEFAULT_EXCLUDES;
13
+ private regexCache;
55
14
  constructor(basePath?: string);
56
15
  /**
57
16
  * Load custom exclusion patterns from .gitignore and .snowignore
58
17
  */
59
18
  private loadExclusionPatterns;
60
- /**
61
- * Check if a directory should be excluded based on exclusion patterns
62
- */
63
- private shouldExcludeDirectory;
64
- /**
65
- * Detect programming language from file extension
66
- */
67
- private detectLanguage;
68
- /**
69
- * Read file with LRU cache to reduce repeated file system access
70
- */
71
- private readFileWithCache;
72
- /**
73
- * Parse file content to extract code symbols using regex patterns
74
- */
75
- private parseFileSymbols;
76
- /**
77
- * Get context lines around a specific line
78
- */
79
- private getContext;
80
19
  /**
81
20
  * Check if a directory is a Git repository
82
21
  */
83
22
  private isGitRepository;
84
- /**
85
- * Check if a command is available in the system PATH
86
- */
87
- private isCommandAvailable;
88
- /**
89
- * Parse grep output (format: filePath:lineNumber:lineContent)
90
- */
91
- private parseGrepOutput;
92
23
  /**
93
24
  * Build or refresh the code symbol index with incremental updates
94
25
  */
@@ -143,11 +74,6 @@ export declare class ACECodeSearchService {
143
74
  * Files modified within last 24 hours are prioritized
144
75
  */
145
76
  private sortResultsByRecency;
146
- /**
147
- * Convert glob pattern to RegExp
148
- * Supports: *, **, ?, [abc], {js,ts}
149
- */
150
- private globToRegex;
151
77
  /**
152
78
  * Get code outline for a file (all symbols in the file)
153
79
  */