snow-ai 0.3.31 → 0.3.32

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.
@@ -0,0 +1,74 @@
1
+ import { loadCodebaseConfig } from '../utils/codebaseConfig.js';
2
+ import { addProxyToFetchOptions } from '../utils/proxyUtils.js';
3
+ /**
4
+ * Create embeddings for text array (single API call)
5
+ * @param options Embedding options
6
+ * @returns Embedding response with vectors
7
+ */
8
+ export async function createEmbeddings(options) {
9
+ const config = loadCodebaseConfig();
10
+ // Use config defaults if not provided
11
+ const model = options.model || config.embedding.modelName;
12
+ const baseUrl = options.baseUrl || config.embedding.baseUrl;
13
+ const apiKey = options.apiKey || config.embedding.apiKey;
14
+ const dimensions = options.dimensions ?? config.embedding.dimensions;
15
+ const { input, task } = options;
16
+ if (!model) {
17
+ throw new Error('Embedding model name is required');
18
+ }
19
+ if (!baseUrl) {
20
+ throw new Error('Embedding base URL is required');
21
+ }
22
+ if (!apiKey) {
23
+ throw new Error('Embedding API key is required');
24
+ }
25
+ if (!input || input.length === 0) {
26
+ throw new Error('Input texts are required');
27
+ }
28
+ // Build request body
29
+ const requestBody = {
30
+ model,
31
+ input,
32
+ };
33
+ if (task) {
34
+ requestBody.task = task;
35
+ }
36
+ if (dimensions) {
37
+ requestBody.dimensions = dimensions;
38
+ }
39
+ // Use baseUrl directly, append /embeddings if needed
40
+ const url = baseUrl.endsWith('/embeddings')
41
+ ? baseUrl
42
+ : `${baseUrl.replace(/\/$/, '')}/embeddings`;
43
+ const fetchOptions = addProxyToFetchOptions(url, {
44
+ method: 'POST',
45
+ headers: {
46
+ 'Content-Type': 'application/json',
47
+ Authorization: `Bearer ${apiKey}`,
48
+ },
49
+ body: JSON.stringify(requestBody),
50
+ });
51
+ const response = await fetch(url, fetchOptions);
52
+ if (!response.ok) {
53
+ const errorText = await response.text();
54
+ throw new Error(`Embedding API error (${response.status}): ${errorText}`);
55
+ }
56
+ const data = await response.json();
57
+ return data;
58
+ }
59
+ /**
60
+ * Create embedding for single text
61
+ * @param text Single text to embed
62
+ * @param options Optional embedding options
63
+ * @returns Embedding vector
64
+ */
65
+ export async function createEmbedding(text, options) {
66
+ const response = await createEmbeddings({
67
+ input: [text],
68
+ ...options,
69
+ });
70
+ if (response.data.length === 0) {
71
+ throw new Error('No embedding returned from API');
72
+ }
73
+ return response.data[0].embedding;
74
+ }
@@ -1,4 +1,8 @@
1
1
  /**
2
2
  * System prompt configuration for Snow AI CLI
3
3
  */
4
- export declare function getSystemPrompt(): string;
4
+ export declare function getSystemPrompt(tools?: Array<{
5
+ function: {
6
+ name: string;
7
+ };
8
+ }>): string;
@@ -90,15 +90,7 @@ const SYSTEM_PROMPT_TEMPLATE = `You are Snow AI CLI, an intelligent command-line
90
90
  - "Add validation to form" → Read form component + related validation utils → Add code → Done
91
91
  - "Refactor error handling" → Read error handler + callers → Refactor → Done
92
92
 
93
- **Your workflow:**
94
- 1. Read the primary file(s) mentioned
95
- 2. Check dependencies/imports that directly impact the change
96
- 3. Read related files ONLY if they're critical to understanding the task
97
- 4. Write/modify code with proper context
98
- 5. Verify with build
99
- 6. ❌ NO excessive exploration beyond what's needed
100
- 7. ❌ NO reading entire modules "for reference"
101
- 8. ❌ NO over-planning multi-step workflows for simple tasks
93
+ PLACEHOLDER_FOR_WORKFLOW_SECTION
102
94
 
103
95
  **Golden Rule: Read what you need to write correct code, nothing more.**
104
96
 
@@ -154,11 +146,8 @@ const SYSTEM_PROMPT_TEMPLATE = `You are Snow AI CLI, an intelligent command-line
154
146
  - \`filesystem-edit\` - Modify existing files
155
147
  - \`filesystem-create\` - Create new files
156
148
 
157
- **Code Search (ACE):**
158
- - \`ace-search-symbols\` - Find functions/classes/variables
159
- - \`ace-find-definition\` - Go to definition
160
- - \`ace-find-references\` - Find all usages
161
- - \`ace-text-search\` - Fast text/regex search
149
+ **Code Search:**
150
+ PLACEHOLDER_FOR_CODE_SEARCH_SECTION
162
151
 
163
152
  **IDE Diagnostics:**
164
153
  - \`ide-get_diagnostics\` - Get real-time diagnostics (errors, warnings, hints) from connected IDE
@@ -314,11 +303,92 @@ Guidance and recommendations:
314
303
  - This file may not exist. If you can't find it, please ignore it.
315
304
 
316
305
  Remember: **ACTION > ANALYSIS**. Write code first, investigate only when blocked.`;
306
+ /**
307
+ * Check if codebase-search tool is available
308
+ */
309
+ function hasCodebaseSearchTool(tools) {
310
+ if (!tools)
311
+ return false;
312
+ return tools.some(tool => tool.function.name === 'codebase-search');
313
+ }
314
+ /**
315
+ * Generate workflow section based on available tools
316
+ */
317
+ function getWorkflowSection(hasCodebase) {
318
+ if (hasCodebase) {
319
+ return `**Your workflow:**
320
+ 1. **Understand the task** - For conceptual questions, try \\\`codebase-search\\\` FIRST (semantic search)
321
+ 2. Read the primary file(s) mentioned (or files found by codebase search)
322
+ 3. Check dependencies/imports that directly impact the change
323
+ 4. For precise symbol lookup, use \\\`ace-search-symbols\\\`, \\\`ace-find-definition\\\`, or \\\`ace-find-references\\\`
324
+ 5. Read related files ONLY if they're critical to understanding the task
325
+ 6. Write/modify code with proper context
326
+ 7. Verify with build
327
+ 8. ❌ NO excessive exploration beyond what's needed
328
+ 9. ❌ NO reading entire modules "for reference"
329
+ 10. ❌ NO over-planning multi-step workflows for simple tasks`;
330
+ }
331
+ else {
332
+ return `**Your workflow:**
333
+ 1. Read the primary file(s) mentioned
334
+ 2. Use \\\`ace-search-symbols\\\`, \\\`ace-find-definition\\\`, or \\\`ace-find-references\\\` to find related code
335
+ 3. Check dependencies/imports that directly impact the change
336
+ 4. Read related files ONLY if they're critical to understanding the task
337
+ 5. Write/modify code with proper context
338
+ 6. Verify with build
339
+ 7. ❌ NO excessive exploration beyond what's needed
340
+ 8. ❌ NO reading entire modules "for reference"
341
+ 9. ❌ NO over-planning multi-step workflows for simple tasks`;
342
+ }
343
+ }
344
+ /**
345
+ * Generate code search section based on available tools
346
+ */
347
+ function getCodeSearchSection(hasCodebase) {
348
+ if (hasCodebase) {
349
+ // When codebase tool is available, prioritize it
350
+ return `**Code Search:**
351
+
352
+ 🎯 **Priority Order (use in this sequence):**
353
+
354
+ 1. **Codebase Semantic Search** (⚡ HIGHEST PRIORITY):
355
+ - \\\`codebase-search\\\` - Semantic search using embeddings
356
+ - 🔍 Find code by MEANING, not just keywords
357
+ - 🎯 Best for: "how is authentication handled", "error handling patterns"
358
+ - 📊 Returns: Full code content + similarity scores + file locations
359
+ - 💡 **IMPORTANT**: Always try this FIRST for conceptual queries!
360
+ - 🚀 **When to use**: Understanding concepts, finding similar code, pattern discovery
361
+ - ❌ **When to skip**: Exact symbol names, file-specific searches (use ACE instead)
362
+
363
+ 2. **ACE Code Search** (Fallback for precise lookups):
364
+ - \\\`ace-search-symbols\\\` - Find functions/classes/variables by exact name
365
+ - \\\`ace-find-definition\\\` - Go to definition of a symbol
366
+ - \\\`ace-find-references\\\` - Find all usages of a symbol
367
+ - \\\`ace-text-search\\\` - Fast text/regex search across files
368
+ - 💡 **When to use**: Exact symbol lookup, reference finding, regex patterns`;
369
+ }
370
+ else {
371
+ // When codebase tool is NOT available, only show ACE
372
+ return `**Code Search (ACE):**
373
+ - \\\`ace-search-symbols\\\` - Find functions/classes/variables by exact name
374
+ - \\\`ace-find-definition\\\` - Go to definition of a symbol
375
+ - \\\`ace-find-references\\\` - Find all usages of a symbol
376
+ - \\\`ace-text-search\\\` - Fast text/regex search across files`;
377
+ }
378
+ }
317
379
  // Export SYSTEM_PROMPT as a getter function for real-time ROLE.md updates
318
- export function getSystemPrompt() {
380
+ export function getSystemPrompt(tools) {
319
381
  const basePrompt = getSystemPromptWithRole();
320
382
  const systemEnv = getSystemEnvironmentInfo();
321
- return `${basePrompt}
383
+ const hasCodebase = hasCodebaseSearchTool(tools);
384
+ // Generate dynamic sections
385
+ const workflowSection = getWorkflowSection(hasCodebase);
386
+ const codeSearchSection = getCodeSearchSection(hasCodebase);
387
+ // Replace placeholders with actual content
388
+ let finalPrompt = basePrompt
389
+ .replace('PLACEHOLDER_FOR_WORKFLOW_SECTION', workflowSection)
390
+ .replace('PLACEHOLDER_FOR_CODE_SEARCH_SECTION', codeSearchSection);
391
+ return `${finalPrompt}
322
392
 
323
393
  ## 💻 System Environment
324
394
 
@@ -37,7 +37,7 @@ export async function handleConversationWithTools(options) {
37
37
  const mcpTools = await collectAllMCPTools();
38
38
  // Build conversation history with TODO context as pinned user message
39
39
  let conversationMessages = [
40
- { role: 'system', content: getSystemPrompt() },
40
+ { role: 'system', content: getSystemPrompt(mcpTools) },
41
41
  ];
42
42
  // If there are TODOs, add pinned context message at the front
43
43
  if (existingTodoList && existingTodoList.todos.length > 0) {
@@ -86,19 +86,6 @@ export declare class ACECodeSearchService {
86
86
  * Search with language-specific context (cross-reference search)
87
87
  */
88
88
  semanticSearch(query: string, searchType?: 'definition' | 'usage' | 'implementation' | 'all', language?: string, maxResults?: number): Promise<SemanticSearchResult>;
89
- /**
90
- * Clear the symbol index cache and force full re-index on next search
91
- */
92
- clearCache(): void;
93
- /**
94
- * Get index statistics
95
- */
96
- getIndexStats(): {
97
- totalFiles: number;
98
- totalSymbols: number;
99
- languageBreakdown: Record<string, number>;
100
- cacheAge: number;
101
- };
102
89
  }
103
90
  export declare const aceCodeSearchService: ACECodeSearchService;
104
91
  export declare const mcpTools: ({
@@ -282,24 +269,4 @@ export declare const mcpTools: ({
282
269
  };
283
270
  required: string[];
284
271
  };
285
- } | {
286
- name: string;
287
- description: string;
288
- inputSchema: {
289
- type: string;
290
- properties: {
291
- query?: undefined;
292
- symbolType?: undefined;
293
- language?: undefined;
294
- maxResults?: undefined;
295
- symbolName?: undefined;
296
- contextFile?: undefined;
297
- searchType?: undefined;
298
- filePath?: undefined;
299
- pattern?: undefined;
300
- fileGlob?: undefined;
301
- isRegex?: undefined;
302
- };
303
- required?: undefined;
304
- };
305
272
  })[];
@@ -875,36 +875,6 @@ export class ACECodeSearchService {
875
875
  searchTime,
876
876
  };
877
877
  }
878
- /**
879
- * Clear the symbol index cache and force full re-index on next search
880
- */
881
- clearCache() {
882
- this.indexCache.clear();
883
- this.fileModTimes.clear();
884
- this.allIndexedFiles.clear();
885
- this.fileContentCache.clear();
886
- this.lastIndexTime = 0;
887
- }
888
- /**
889
- * Get index statistics
890
- */
891
- getIndexStats() {
892
- let totalSymbols = 0;
893
- const languageBreakdown = {};
894
- for (const symbols of this.indexCache.values()) {
895
- totalSymbols += symbols.length;
896
- for (const symbol of symbols) {
897
- languageBreakdown[symbol.language] =
898
- (languageBreakdown[symbol.language] || 0) + 1;
899
- }
900
- }
901
- return {
902
- totalFiles: this.indexCache.size,
903
- totalSymbols,
904
- languageBreakdown,
905
- cacheAge: Date.now() - this.lastIndexTime,
906
- };
907
- }
908
878
  }
909
879
  // Export a default instance
910
880
  export const aceCodeSearchService = new ACECodeSearchService();
@@ -1075,20 +1045,4 @@ export const mcpTools = [
1075
1045
  required: ['pattern'],
1076
1046
  },
1077
1047
  },
1078
- {
1079
- name: 'ace-index_stats',
1080
- description: 'ACE Code Search: Get statistics about the code index. Shows number of indexed files, symbols, language breakdown, and cache status. Useful for understanding search coverage.',
1081
- inputSchema: {
1082
- type: 'object',
1083
- properties: {},
1084
- },
1085
- },
1086
- {
1087
- name: 'ace-clear_cache',
1088
- description: 'ACE Code Search: Clear the symbol index cache and force a full re-index on next search. Use when codebase has changed significantly or search results seem stale.',
1089
- inputSchema: {
1090
- type: 'object',
1091
- properties: {},
1092
- },
1093
- },
1094
1048
  ];
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Codebase Search Service
3
+ * Provides semantic search capabilities for the codebase using embeddings
4
+ */
5
+ declare class CodebaseSearchService {
6
+ /**
7
+ * Check if codebase index is available and has data
8
+ */
9
+ private isCodebaseIndexAvailable;
10
+ /**
11
+ * Calculate cosine similarity between two vectors
12
+ */
13
+ private cosineSimilarity;
14
+ /**
15
+ * Search codebase using semantic similarity
16
+ */
17
+ search(query: string, topN?: number): Promise<any>;
18
+ }
19
+ export declare const codebaseSearchService: CodebaseSearchService;
20
+ /**
21
+ * MCP Tools Definition
22
+ */
23
+ export declare const mcpTools: {
24
+ name: string;
25
+ description: string;
26
+ inputSchema: {
27
+ type: string;
28
+ properties: {
29
+ query: {
30
+ type: string;
31
+ description: string;
32
+ };
33
+ topN: {
34
+ type: string;
35
+ description: string;
36
+ default: number;
37
+ minimum: number;
38
+ maximum: number;
39
+ };
40
+ };
41
+ required: string[];
42
+ };
43
+ }[];
44
+ export {};
@@ -0,0 +1,146 @@
1
+ import { CodebaseDatabase } from '../utils/codebaseDatabase.js';
2
+ import { createEmbedding } from '../api/embedding.js';
3
+ import { logger } from '../utils/logger.js';
4
+ import path from 'node:path';
5
+ import fs from 'node:fs';
6
+ /**
7
+ * Codebase Search Service
8
+ * Provides semantic search capabilities for the codebase using embeddings
9
+ */
10
+ class CodebaseSearchService {
11
+ /**
12
+ * Check if codebase index is available and has data
13
+ */
14
+ isCodebaseIndexAvailable() {
15
+ try {
16
+ const projectRoot = process.cwd();
17
+ const dbPath = path.join(projectRoot, '.snow', 'codebase', 'embeddings.db');
18
+ // Check if database file exists
19
+ if (!fs.existsSync(dbPath)) {
20
+ return {
21
+ available: false,
22
+ reason: 'Codebase index not found. Please run codebase indexing first.',
23
+ };
24
+ }
25
+ // Initialize database and check for data
26
+ const db = new CodebaseDatabase(projectRoot);
27
+ db.initialize();
28
+ const totalChunks = db.getTotalChunks();
29
+ db.close();
30
+ if (totalChunks === 0) {
31
+ return {
32
+ available: false,
33
+ reason: 'Codebase index is empty. Please run indexing to build the index.',
34
+ };
35
+ }
36
+ return { available: true };
37
+ }
38
+ catch (error) {
39
+ logger.error('Error checking codebase index availability:', error);
40
+ return {
41
+ available: false,
42
+ reason: `Error checking codebase index: ${error instanceof Error ? error.message : 'Unknown error'}`,
43
+ };
44
+ }
45
+ }
46
+ /**
47
+ * Calculate cosine similarity between two vectors
48
+ */
49
+ cosineSimilarity(a, b) {
50
+ if (a.length !== b.length) {
51
+ throw new Error('Vectors must have same length');
52
+ }
53
+ let dotProduct = 0;
54
+ let normA = 0;
55
+ let normB = 0;
56
+ for (let i = 0; i < a.length; i++) {
57
+ dotProduct += a[i] * b[i];
58
+ normA += a[i] * a[i];
59
+ normB += b[i] * b[i];
60
+ }
61
+ return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
62
+ }
63
+ /**
64
+ * Search codebase using semantic similarity
65
+ */
66
+ async search(query, topN = 10) {
67
+ // Check if codebase index is available
68
+ const { available, reason } = this.isCodebaseIndexAvailable();
69
+ if (!available) {
70
+ return {
71
+ error: reason,
72
+ results: [],
73
+ totalResults: 0,
74
+ };
75
+ }
76
+ try {
77
+ const projectRoot = process.cwd();
78
+ const db = new CodebaseDatabase(projectRoot);
79
+ db.initialize();
80
+ const totalChunks = db.getTotalChunks();
81
+ // Generate embedding for query
82
+ logger.info(`Generating embedding for query: "${query}"`);
83
+ const queryEmbedding = await createEmbedding(query);
84
+ // Search similar chunks
85
+ logger.info(`Searching top ${topN} similar chunks from ${totalChunks} total chunks`);
86
+ const results = db.searchSimilar(queryEmbedding, topN);
87
+ // Format results with similarity scores and full content (no truncation)
88
+ const formattedResults = results.map((chunk, index) => {
89
+ const score = this.cosineSimilarity(queryEmbedding, chunk.embedding);
90
+ const scorePercent = (score * 100).toFixed(2);
91
+ return {
92
+ rank: index + 1,
93
+ filePath: chunk.filePath,
94
+ startLine: chunk.startLine,
95
+ endLine: chunk.endLine,
96
+ content: chunk.content, // Full content, no truncation
97
+ similarityScore: scorePercent,
98
+ location: `${chunk.filePath}:${chunk.startLine}-${chunk.endLine}`,
99
+ };
100
+ });
101
+ db.close();
102
+ return {
103
+ query,
104
+ totalChunks,
105
+ resultsCount: formattedResults.length,
106
+ results: formattedResults,
107
+ };
108
+ }
109
+ catch (error) {
110
+ logger.error('Codebase search failed:', error);
111
+ throw error;
112
+ }
113
+ }
114
+ }
115
+ // Export singleton instance
116
+ export const codebaseSearchService = new CodebaseSearchService();
117
+ /**
118
+ * MCP Tools Definition
119
+ */
120
+ export const mcpTools = [
121
+ {
122
+ name: 'codebase-search',
123
+ description: '🔍 Semantic search across the codebase using embeddings. ' +
124
+ 'Finds code snippets similar to your query based on meaning, not just keywords. ' +
125
+ 'Returns full code content with similarity scores and file locations. ' +
126
+ 'NOTE: Only available when codebase indexing is enabled and the index has been built. ' +
127
+ 'If the index is not available, the tool will return an error message with instructions.',
128
+ inputSchema: {
129
+ type: 'object',
130
+ properties: {
131
+ query: {
132
+ type: 'string',
133
+ description: 'Search query describing the code you want to find (e.g., "database query", "error handling", "authentication logic")',
134
+ },
135
+ topN: {
136
+ type: 'number',
137
+ description: 'Maximum number of results to return (default: 10, max: 50)',
138
+ default: 10,
139
+ minimum: 1,
140
+ maximum: 50,
141
+ },
142
+ },
143
+ required: ['query'],
144
+ },
145
+ },
146
+ ];