snow-ai 0.2.13 → 0.2.15

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.
@@ -5,9 +5,6 @@ interface SearchMatch {
5
5
  lineContent: string;
6
6
  column: number;
7
7
  matchedText?: string;
8
- nodeType?: string;
9
- nodeName?: string;
10
- language?: string;
11
8
  }
12
9
  interface SearchResult {
13
10
  query: string;
@@ -151,7 +148,7 @@ export declare class FilesystemMCPService {
151
148
  * @param maxResults - Maximum number of results to return (default: 100)
152
149
  * @returns Search results with file paths, line numbers, and matched content
153
150
  */
154
- searchCode(query: string, dirPath?: string, fileExtensions?: string[], caseSensitive?: boolean, maxResults?: number, searchMode?: 'text' | 'regex' | 'ast'): Promise<SearchResult>;
151
+ searchCode(query: string, dirPath?: string, fileExtensions?: string[], caseSensitive?: boolean, maxResults?: number, searchMode?: 'text' | 'regex'): Promise<SearchResult>;
155
152
  /**
156
153
  * Resolve path relative to base path and normalize it
157
154
  * @private
@@ -3,7 +3,6 @@ import * as path from 'path';
3
3
  import { execSync } from 'child_process';
4
4
  import { vscodeConnection } from '../utils/vscodeConnection.js';
5
5
  import { incrementalSnapshotManager } from '../utils/incrementalSnapshot.js';
6
- import { multiLanguageASTParser } from './multiLanguageASTParser.js';
7
6
  const { resolve, dirname, isAbsolute } = path;
8
7
  /**
9
8
  * Filesystem MCP Service
@@ -471,31 +470,39 @@ export class FilesystemMCPService {
471
470
  let finalTotalLines = newTotalLines;
472
471
  let finalContextEnd = newContextEnd;
473
472
  let finalContextContent = newContextContent;
474
- try {
475
- execSync(`npx prettier --write "${fullPath}"`, {
476
- stdio: 'pipe',
477
- encoding: 'utf-8',
478
- });
479
- // Re-read the file after formatting to get the formatted content
480
- const formattedContent = await fs.readFile(fullPath, 'utf-8');
481
- finalLines = formattedContent.split('\n');
482
- finalTotalLines = finalLines.length;
483
- // Recalculate the context end line based on formatted content
484
- finalContextEnd = Math.min(finalTotalLines, contextStart + (newContextEnd - contextStart));
485
- // Extract formatted content for context with line numbers
486
- const formattedContextLines = finalLines.slice(contextStart - 1, finalContextEnd);
487
- finalContextContent = formattedContextLines
488
- .map((line, idx) => {
489
- const lineNum = contextStart + idx;
490
- const paddedNum = String(lineNum).padStart(String(finalContextEnd).length, ' ');
491
- return `${paddedNum}→${line}`;
492
- })
493
- .join('\n');
494
- }
495
- catch (formatError) {
496
- // If formatting fails, continue with the original content
497
- // This ensures editing is not blocked by formatting issues
498
- console.warn(`⚠️ Prettier formatting failed for ${filePath}, returning unformatted content:`, formatError instanceof Error ? formatError.message : 'Unknown error');
473
+ // Check if Prettier supports this file type
474
+ const prettierSupportedExtensions = [
475
+ '.js', '.jsx', '.ts', '.tsx', '.json', '.css', '.scss', '.less',
476
+ '.html', '.vue', '.yaml', '.yml', '.md', '.graphql', '.gql'
477
+ ];
478
+ const fileExtension = path.extname(fullPath).toLowerCase();
479
+ const shouldFormat = prettierSupportedExtensions.includes(fileExtension);
480
+ if (shouldFormat) {
481
+ try {
482
+ execSync(`npx prettier --write "${fullPath}"`, {
483
+ stdio: 'pipe',
484
+ encoding: 'utf-8',
485
+ });
486
+ // Re-read the file after formatting to get the formatted content
487
+ const formattedContent = await fs.readFile(fullPath, 'utf-8');
488
+ finalLines = formattedContent.split('\n');
489
+ finalTotalLines = finalLines.length;
490
+ // Recalculate the context end line based on formatted content
491
+ finalContextEnd = Math.min(finalTotalLines, contextStart + (newContextEnd - contextStart));
492
+ // Extract formatted content for context with line numbers
493
+ const formattedContextLines = finalLines.slice(contextStart - 1, finalContextEnd);
494
+ finalContextContent = formattedContextLines
495
+ .map((line, idx) => {
496
+ const lineNum = contextStart + idx;
497
+ const paddedNum = String(lineNum).padStart(String(finalContextEnd).length, ' ');
498
+ return `${paddedNum}→${line}`;
499
+ })
500
+ .join('\n');
501
+ }
502
+ catch (formatError) {
503
+ // If formatting fails, continue with the original content
504
+ // This ensures editing is not blocked by formatting issues
505
+ }
499
506
  }
500
507
  // Analyze code structure of the edited content (using formatted content if available)
501
508
  const editedContentLines = finalLines.slice(startLine - 1, startLine - 1 + newContentLines.length);
@@ -652,37 +659,8 @@ export class FilesystemMCPService {
652
659
  searchedFiles++;
653
660
  try {
654
661
  const content = await fs.readFile(fullPath, 'utf-8');
655
- // AST search mode - supports multiple languages via tree-sitter
656
- if (searchMode === 'ast') {
657
- // Check if file is supported for AST parsing
658
- if (multiLanguageASTParser.isSupported(fullPath)) {
659
- try {
660
- const astResults = multiLanguageASTParser.searchAST(content, fullPath, query, caseSensitive);
661
- for (const result of astResults) {
662
- if (matches.length >= maxResults) {
663
- break;
664
- }
665
- const lineContent = content.split('\n')[result.startPosition.line - 1] ||
666
- '';
667
- matches.push({
668
- filePath: path.relative(this.basePath, fullPath),
669
- lineNumber: result.startPosition.line,
670
- lineContent: lineContent.trim(),
671
- column: result.startPosition.column,
672
- matchedText: result.name,
673
- nodeType: result.type,
674
- nodeName: result.name,
675
- language: result.language,
676
- });
677
- }
678
- }
679
- catch (error) {
680
- // Skip files with AST parsing errors
681
- }
682
- }
683
- }
684
- else if (searchRegex) {
685
- // Text or Regex search mode
662
+ // Text or Regex search mode
663
+ if (searchRegex) {
686
664
  const lines = content.split('\n');
687
665
  lines.forEach((line, index) => {
688
666
  if (matches.length >= maxResults) {
@@ -892,8 +870,8 @@ export const mcpTools = [
892
870
  },
893
871
  searchMode: {
894
872
  type: 'string',
895
- enum: ['text', 'regex', 'ast'],
896
- description: 'Search mode: "text" for literal text search (default), "regex" for regular expression search, "ast" for AST-based semantic search (supports function/class/variable names)',
873
+ enum: ['text', 'regex'],
874
+ description: 'Search mode: "text" for literal text search (default), "regex" for regular expression search',
897
875
  default: 'text',
898
876
  },
899
877
  },
@@ -392,8 +392,8 @@ export async function executeMCPTool(toolName, args) {
392
392
  case 'edit':
393
393
  return await filesystemService.editFile(args.filePath, args.startLine, args.endLine, args.newContent, args.contextLines);
394
394
  case 'search': {
395
- // 兼容性处理:如果 searchMode 不存在或无效,默认使用 'text'
396
- const validSearchModes = ['text', 'regex', 'ast'];
395
+ // 兼容性处理:如果 searchMode 不存在或无效,默认使用 'text'
396
+ const validSearchModes = ['text', 'regex'];
397
397
  let searchMode = 'text';
398
398
  if (args.searchMode && validSearchModes.includes(args.searchMode)) {
399
399
  searchMode = args.searchMode;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "snow-ai",
3
- "version": "0.2.13",
3
+ "version": "0.2.15",
4
4
  "description": "Intelligent Command Line Assistant powered by AI",
5
5
  "license": "MIT",
6
6
  "bin": {
@@ -59,18 +59,6 @@
59
59
  "react": "^18.2.0",
60
60
  "string-width": "^7.2.0",
61
61
  "tiktoken": "^1.0.22",
62
- "tree-sitter": "^0.25.0",
63
- "tree-sitter-c": "^0.24.1",
64
- "tree-sitter-c-sharp": "^0.23.1",
65
- "tree-sitter-cpp": "^0.23.4",
66
- "tree-sitter-go": "^0.25.0",
67
- "tree-sitter-java": "^0.23.5",
68
- "tree-sitter-javascript": "^0.25.0",
69
- "tree-sitter-php": "^0.24.2",
70
- "tree-sitter-python": "^0.25.0",
71
- "tree-sitter-ruby": "^0.23.1",
72
- "tree-sitter-rust": "^0.24.0",
73
- "tree-sitter-typescript": "^0.23.2",
74
62
  "ws": "^8.14.2"
75
63
  },
76
64
  "devDependencies": {
package/readme.md CHANGED
@@ -57,9 +57,9 @@ $ npm uninstall --global snow-ai
57
57
 
58
58
  ## Install VSCode Extension
59
59
 
60
- * download [VSIX/snow-cli-0.2.5.vsix](https://github.com/MayDay-wpf/snow-cli/blob/main/VSIX/snow-cli-0.2.5.vsix)
60
+ * download [VSIX/snow-cli-0.2.6.vsix](https://github.com/MayDay-wpf/snow-cli/blob/main/VSIX/snow-cli-0.2.6.vsix)
61
61
 
62
- * open VSCode, click `Extensions` -> `Install from VSIX...` -> select `snow-cli-0.2.5.vsix`
62
+ * open VSCode, click `Extensions` -> `Install from VSIX...` -> select `snow-cli-0.2.6.vsix`
63
63
 
64
64
  ## Live View
65
65
  * **Welcome & Settings**
@@ -73,6 +73,8 @@ $ npm uninstall --global snow-ai
73
73
 
74
74
  * When mounting: double-click ESC, view the dialogue recorder, select rollback, including file checkpoints
75
75
 
76
+ * Windows & MacOS:`ctrl + v` Paste image
77
+
76
78
  * **Commands**
77
79
 
78
80
  ![alt text](image-2.png)
@@ -1,67 +0,0 @@
1
- /**
2
- * Supported programming languages for AST parsing
3
- */
4
- export type SupportedLanguage = 'javascript' | 'typescript' | 'tsx' | 'python' | 'java' | 'go' | 'cpp' | 'c' | 'csharp' | 'rust' | 'ruby' | 'php';
5
- /**
6
- * AST node result with metadata
7
- */
8
- export interface ASTNodeResult {
9
- name: string;
10
- type: string;
11
- startPosition: {
12
- line: number;
13
- column: number;
14
- };
15
- endPosition: {
16
- line: number;
17
- column: number;
18
- };
19
- text: string;
20
- language: SupportedLanguage;
21
- }
22
- /**
23
- * Multi-language AST parser using tree-sitter
24
- * Supports JavaScript, TypeScript, Python, Java, Go, C/C++, C#, Rust, Ruby, PHP
25
- */
26
- export declare class MultiLanguageASTParser {
27
- private parsers;
28
- private languageConfigs;
29
- constructor();
30
- /**
31
- * Initialize language configurations
32
- */
33
- private initializeLanguageConfigs;
34
- /**
35
- * Initialize parsers for all supported languages
36
- */
37
- private initializeParsers;
38
- /**
39
- * Detect language from file extension
40
- */
41
- detectLanguage(filePath: string): SupportedLanguage | null;
42
- /**
43
- * Parse source code and search for nodes matching the query
44
- */
45
- searchAST(sourceCode: string, filePath: string, query: string, caseSensitive?: boolean): ASTNodeResult[];
46
- /**
47
- * Extract the name/identifier from an AST node based on language
48
- */
49
- private extractNodeName;
50
- /**
51
- * Recursively find the first identifier node
52
- */
53
- private findIdentifier;
54
- /**
55
- * Get supported file extensions
56
- */
57
- getSupportedExtensions(): string[];
58
- /**
59
- * Check if a file is supported for AST parsing
60
- */
61
- isSupported(filePath: string): boolean;
62
- /**
63
- * Get node types for a specific language
64
- */
65
- getNodeTypes(language: SupportedLanguage): string[];
66
- }
67
- export declare const multiLanguageASTParser: MultiLanguageASTParser;
@@ -1,360 +0,0 @@
1
- import Parser from 'tree-sitter';
2
- import JavaScript from 'tree-sitter-javascript';
3
- import TypeScript from 'tree-sitter-typescript';
4
- import Python from 'tree-sitter-python';
5
- import Java from 'tree-sitter-java';
6
- import Go from 'tree-sitter-go';
7
- import Cpp from 'tree-sitter-cpp';
8
- import C from 'tree-sitter-c';
9
- import CSharp from 'tree-sitter-c-sharp';
10
- import Rust from 'tree-sitter-rust';
11
- import Ruby from 'tree-sitter-ruby';
12
- import PHP from 'tree-sitter-php';
13
- /**
14
- * Multi-language AST parser using tree-sitter
15
- * Supports JavaScript, TypeScript, Python, Java, Go, C/C++, C#, Rust, Ruby, PHP
16
- */
17
- export class MultiLanguageASTParser {
18
- constructor() {
19
- Object.defineProperty(this, "parsers", {
20
- enumerable: true,
21
- configurable: true,
22
- writable: true,
23
- value: new Map()
24
- });
25
- Object.defineProperty(this, "languageConfigs", {
26
- enumerable: true,
27
- configurable: true,
28
- writable: true,
29
- value: new Map()
30
- });
31
- this.initializeLanguageConfigs();
32
- this.initializeParsers();
33
- }
34
- /**
35
- * Initialize language configurations
36
- */
37
- initializeLanguageConfigs() {
38
- // JavaScript
39
- this.languageConfigs.set('javascript', {
40
- extensions: ['.js', '.jsx', '.mjs', '.cjs'],
41
- parser: JavaScript,
42
- nodeTypes: [
43
- 'function_declaration',
44
- 'arrow_function',
45
- 'function_expression',
46
- 'class_declaration',
47
- 'method_definition',
48
- 'variable_declarator',
49
- 'lexical_declaration',
50
- ],
51
- });
52
- // TypeScript
53
- this.languageConfigs.set('typescript', {
54
- extensions: ['.ts'],
55
- parser: TypeScript.typescript,
56
- nodeTypes: [
57
- 'function_declaration',
58
- 'arrow_function',
59
- 'function_expression',
60
- 'class_declaration',
61
- 'method_definition',
62
- 'interface_declaration',
63
- 'type_alias_declaration',
64
- 'enum_declaration',
65
- 'variable_declarator',
66
- 'lexical_declaration',
67
- ],
68
- });
69
- // TSX
70
- this.languageConfigs.set('tsx', {
71
- extensions: ['.tsx'],
72
- parser: TypeScript.tsx,
73
- nodeTypes: [
74
- 'function_declaration',
75
- 'arrow_function',
76
- 'function_expression',
77
- 'class_declaration',
78
- 'method_definition',
79
- 'interface_declaration',
80
- 'type_alias_declaration',
81
- 'enum_declaration',
82
- 'variable_declarator',
83
- 'lexical_declaration',
84
- ],
85
- });
86
- // Python
87
- this.languageConfigs.set('python', {
88
- extensions: ['.py', '.pyw'],
89
- parser: Python,
90
- nodeTypes: [
91
- 'function_definition',
92
- 'class_definition',
93
- 'decorated_definition',
94
- 'assignment',
95
- ],
96
- });
97
- // Java
98
- this.languageConfigs.set('java', {
99
- extensions: ['.java'],
100
- parser: Java,
101
- nodeTypes: [
102
- 'method_declaration',
103
- 'class_declaration',
104
- 'interface_declaration',
105
- 'enum_declaration',
106
- 'constructor_declaration',
107
- 'field_declaration',
108
- ],
109
- });
110
- // Go
111
- this.languageConfigs.set('go', {
112
- extensions: ['.go'],
113
- parser: Go,
114
- nodeTypes: [
115
- 'function_declaration',
116
- 'method_declaration',
117
- 'type_declaration',
118
- 'type_spec',
119
- 'var_declaration',
120
- 'const_declaration',
121
- ],
122
- });
123
- // C++
124
- this.languageConfigs.set('cpp', {
125
- extensions: ['.cpp', '.cc', '.cxx', '.hpp', '.hxx', '.h'],
126
- parser: Cpp,
127
- nodeTypes: [
128
- 'function_definition',
129
- 'function_declarator',
130
- 'class_specifier',
131
- 'struct_specifier',
132
- 'enum_specifier',
133
- 'namespace_definition',
134
- 'declaration',
135
- ],
136
- });
137
- // C
138
- this.languageConfigs.set('c', {
139
- extensions: ['.c', '.h'],
140
- parser: C,
141
- nodeTypes: [
142
- 'function_definition',
143
- 'function_declarator',
144
- 'struct_specifier',
145
- 'enum_specifier',
146
- 'declaration',
147
- ],
148
- });
149
- // C#
150
- this.languageConfigs.set('csharp', {
151
- extensions: ['.cs'],
152
- parser: CSharp,
153
- nodeTypes: [
154
- 'method_declaration',
155
- 'class_declaration',
156
- 'interface_declaration',
157
- 'struct_declaration',
158
- 'enum_declaration',
159
- 'property_declaration',
160
- 'field_declaration',
161
- 'constructor_declaration',
162
- ],
163
- });
164
- // Rust
165
- this.languageConfigs.set('rust', {
166
- extensions: ['.rs'],
167
- parser: Rust,
168
- nodeTypes: [
169
- 'function_item',
170
- 'struct_item',
171
- 'enum_item',
172
- 'trait_item',
173
- 'impl_item',
174
- 'mod_item',
175
- 'const_item',
176
- 'static_item',
177
- ],
178
- });
179
- // Ruby
180
- this.languageConfigs.set('ruby', {
181
- extensions: ['.rb'],
182
- parser: Ruby,
183
- nodeTypes: [
184
- 'method',
185
- 'singleton_method',
186
- 'class',
187
- 'module',
188
- 'assignment',
189
- ],
190
- });
191
- // PHP
192
- this.languageConfigs.set('php', {
193
- extensions: ['.php'],
194
- parser: PHP.php,
195
- nodeTypes: [
196
- 'function_definition',
197
- 'method_declaration',
198
- 'class_declaration',
199
- 'interface_declaration',
200
- 'trait_declaration',
201
- 'property_declaration',
202
- ],
203
- });
204
- }
205
- /**
206
- * Initialize parsers for all supported languages
207
- */
208
- initializeParsers() {
209
- for (const [language, config] of this.languageConfigs.entries()) {
210
- try {
211
- const parser = new Parser();
212
- parser.setLanguage(config.parser);
213
- this.parsers.set(language, parser);
214
- }
215
- catch (error) {
216
- console.error(`Failed to initialize parser for ${language}:`, error instanceof Error ? error.message : 'Unknown error');
217
- }
218
- }
219
- }
220
- /**
221
- * Detect language from file extension
222
- */
223
- detectLanguage(filePath) {
224
- const ext = filePath.substring(filePath.lastIndexOf('.')).toLowerCase();
225
- for (const [language, config] of this.languageConfigs.entries()) {
226
- if (config.extensions.includes(ext)) {
227
- return language;
228
- }
229
- }
230
- return null;
231
- }
232
- /**
233
- * Parse source code and search for nodes matching the query
234
- */
235
- searchAST(sourceCode, filePath, query, caseSensitive = false) {
236
- const language = this.detectLanguage(filePath);
237
- if (!language) {
238
- return [];
239
- }
240
- const parser = this.parsers.get(language);
241
- if (!parser) {
242
- return [];
243
- }
244
- try {
245
- const tree = parser.parse(sourceCode);
246
- const results = [];
247
- const searchQuery = caseSensitive ? query : query.toLowerCase();
248
- // Traverse the AST
249
- const traverse = (node) => {
250
- const nodeType = node.type;
251
- const config = this.languageConfigs.get(language);
252
- // Check if this is a node type we're interested in
253
- if (config && config.nodeTypes.includes(nodeType)) {
254
- const nodeName = this.extractNodeName(node, language);
255
- if (nodeName) {
256
- const nameToCheck = caseSensitive
257
- ? nodeName
258
- : nodeName.toLowerCase();
259
- if (nameToCheck.includes(searchQuery)) {
260
- results.push({
261
- name: nodeName,
262
- type: nodeType,
263
- startPosition: {
264
- line: node.startPosition.row + 1,
265
- column: node.startPosition.column + 1,
266
- },
267
- endPosition: {
268
- line: node.endPosition.row + 1,
269
- column: node.endPosition.column + 1,
270
- },
271
- text: node.text,
272
- language,
273
- });
274
- }
275
- }
276
- }
277
- // Recursively traverse children
278
- for (let i = 0; i < node.childCount; i++) {
279
- const child = node.child(i);
280
- if (child) {
281
- traverse(child);
282
- }
283
- }
284
- };
285
- traverse(tree.rootNode);
286
- return results;
287
- }
288
- catch (error) {
289
- console.error(`AST parsing error for ${filePath}:`, error instanceof Error ? error.message : 'Unknown error');
290
- return [];
291
- }
292
- }
293
- /**
294
- * Extract the name/identifier from an AST node based on language
295
- */
296
- extractNodeName(node, _language) {
297
- // Common patterns for extracting names from different node types
298
- const nameFields = ['name', 'identifier', 'declarator', 'property'];
299
- for (const field of nameFields) {
300
- const nameNode = node.childForFieldName(field);
301
- if (nameNode) {
302
- // For some languages, we need to go deeper
303
- if (nameNode.type === 'identifier') {
304
- return nameNode.text;
305
- }
306
- // Try to find identifier in children
307
- const identifierChild = this.findIdentifier(nameNode);
308
- if (identifierChild) {
309
- return identifierChild.text;
310
- }
311
- }
312
- }
313
- // Fallback: try to find any identifier child
314
- const identifier = this.findIdentifier(node);
315
- return identifier ? identifier.text : null;
316
- }
317
- /**
318
- * Recursively find the first identifier node
319
- */
320
- findIdentifier(node) {
321
- if (node.type === 'identifier') {
322
- return node;
323
- }
324
- for (let i = 0; i < node.childCount; i++) {
325
- const child = node.child(i);
326
- if (child) {
327
- const identifier = this.findIdentifier(child);
328
- if (identifier) {
329
- return identifier;
330
- }
331
- }
332
- }
333
- return null;
334
- }
335
- /**
336
- * Get supported file extensions
337
- */
338
- getSupportedExtensions() {
339
- const extensions = [];
340
- for (const config of this.languageConfigs.values()) {
341
- extensions.push(...config.extensions);
342
- }
343
- return [...new Set(extensions)];
344
- }
345
- /**
346
- * Check if a file is supported for AST parsing
347
- */
348
- isSupported(filePath) {
349
- return this.detectLanguage(filePath) !== null;
350
- }
351
- /**
352
- * Get node types for a specific language
353
- */
354
- getNodeTypes(language) {
355
- const config = this.languageConfigs.get(language);
356
- return config ? config.nodeTypes : [];
357
- }
358
- }
359
- // Export singleton instance
360
- export const multiLanguageASTParser = new MultiLanguageASTParser();