pi-lens 3.3.0 → 3.6.0

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 (53) hide show
  1. package/CHANGELOG.md +91 -0
  2. package/README.md +175 -13
  3. package/clients/cache/rule-cache.js +72 -0
  4. package/clients/cache/rule-cache.ts +104 -0
  5. package/clients/dispatch/integration.js +48 -1
  6. package/clients/dispatch/integration.ts +60 -2
  7. package/clients/dispatch/plan.js +5 -2
  8. package/clients/dispatch/plan.ts +5 -2
  9. package/clients/dispatch/runners/ast-grep-napi.js +175 -56
  10. package/clients/dispatch/runners/ast-grep-napi.test.js +2 -1
  11. package/clients/dispatch/runners/ast-grep-napi.test.ts +2 -1
  12. package/clients/dispatch/runners/ast-grep-napi.ts +191 -79
  13. package/clients/dispatch/runners/similarity.js +1 -1
  14. package/clients/dispatch/runners/similarity.ts +2 -2
  15. package/clients/dispatch/runners/tree-sitter.js +137 -10
  16. package/clients/dispatch/runners/tree-sitter.ts +168 -13
  17. package/clients/dispatch/runners/ts-lsp.js +3 -2
  18. package/clients/dispatch/runners/ts-lsp.ts +3 -2
  19. package/clients/dispatch/runners/yaml-rule-parser.js +70 -2
  20. package/clients/dispatch/runners/yaml-rule-parser.ts +71 -2
  21. package/clients/dispatch/types.js +1 -1
  22. package/clients/dispatch/types.ts +1 -1
  23. package/clients/lsp/__tests__/service.test.js +3 -0
  24. package/clients/lsp/__tests__/service.test.ts +3 -0
  25. package/clients/lsp/client.js +42 -0
  26. package/clients/lsp/client.ts +79 -0
  27. package/clients/lsp/index.js +27 -0
  28. package/clients/lsp/index.ts +35 -0
  29. package/clients/lsp/launch.js +11 -6
  30. package/clients/lsp/launch.ts +11 -6
  31. package/clients/metrics-client.js +3 -160
  32. package/clients/metrics-client.tdr.test.js +78 -0
  33. package/clients/metrics-client.test.js +30 -43
  34. package/clients/metrics-client.test.ts +30 -54
  35. package/clients/metrics-client.ts +5 -219
  36. package/clients/metrics-history.js +33 -7
  37. package/clients/metrics-history.ts +47 -10
  38. package/clients/pipeline.js +272 -0
  39. package/clients/pipeline.ts +371 -0
  40. package/clients/sg-runner.js +21 -3
  41. package/clients/sg-runner.ts +22 -3
  42. package/clients/tree-sitter-client.js +23 -2
  43. package/clients/tree-sitter-client.ts +27 -2
  44. package/index.ts +604 -771
  45. package/package.json +1 -1
  46. package/rules/ast-grep-rules/rules/no-architecture-violation.yml +7 -4
  47. package/rules/ast-grep-rules/rules/no-single-char-var.yml +3 -3
  48. package/rules/ast-grep-rules/slop-patterns.yml +85 -62
  49. package/skills/ast-grep/SKILL.md +42 -1
  50. package/skills/lsp-navigation/SKILL.md +62 -0
  51. package/tsconfig.json +1 -1
  52. package/rules/ast-grep-rules/rules/no-console-log.yml +0 -10
  53. package/rules/ast-grep-rules/rules/no-default-export.yml +0 -19
@@ -168,11 +168,30 @@ export class SgRunner {
168
168
 
169
169
  proc.on("close", (code: number | null) => {
170
170
  if (code !== 0 && !stdout.trim()) {
171
+ // Enhanced error messages for common pattern issues
172
+ let errorMsg = stderr.trim() || `Exit code ${code}`;
173
+
174
+ if (stderr.includes("Multiple AST nodes are detected")) {
175
+ errorMsg =
176
+ `Invalid AST pattern: The pattern appears to contain multiple AST nodes or is malformed.\n` +
177
+ `Common causes:\n` +
178
+ ` 1. Missing parentheses: use it($TEST) not it"test"\n` +
179
+ ` 2. Raw text without structure: use console.log($MSG) not just "console.log"\n` +
180
+ ` 3. Unclosed quotes or brackets\n\n` +
181
+ `Original error: ${errorMsg}`;
182
+ } else if (stderr.includes("Cannot parse query")) {
183
+ errorMsg =
184
+ `Pattern syntax error: The pattern could not be parsed as valid code.\n` +
185
+ `Tips:\n` +
186
+ ` - Patterns must be valid ${args.includes("--lang") ? args[args.indexOf("--lang") + 1] : "language"} syntax\n` +
187
+ ` - Use metavariables like $NAME, $ARGS for variable parts\n` +
188
+ ` - Example: 'function $NAME($$$PARAMS) { $$$BODY }'\n\n` +
189
+ `Original error: ${errorMsg}`;
190
+ }
191
+
171
192
  resolve({
172
193
  matches: [],
173
- error: stderr.includes("No files found")
174
- ? undefined
175
- : stderr.trim() || `Exit code ${code}`,
194
+ error: stderr.includes("No files found") ? undefined : errorMsg,
176
195
  });
177
196
  return;
178
197
  }
@@ -43,6 +43,8 @@ export class TreeSitterClient {
43
43
  this.ParserClass = null;
44
44
  // biome-ignore lint/suspicious/noExplicitAny: Language loader from module
45
45
  this.LanguageLoader = null;
46
+ // biome-ignore lint/suspicious/noExplicitAny: Compiled query cache by language+pattern hash
47
+ this.queryCache = new Map();
46
48
  this.queryLoader = new TreeSitterQueryLoader();
47
49
  this.queriesLoaded = false;
48
50
  this.grammarsDir = this.findGrammarsDir();
@@ -347,8 +349,24 @@ export class TreeSitterClient {
347
349
  // If we can't convert, return empty to trigger fallback
348
350
  return { query: "", metavars: [] };
349
351
  }
350
- /** Compile a pattern into a tree-sitter Query */
352
+ /** Generate cache key for compiled query */
353
+ getQueryCacheKey(pattern, languageId) {
354
+ // Simple hash for the query string
355
+ let hash = 0;
356
+ for (let i = 0; i < pattern.length; i++) {
357
+ const char = pattern.charCodeAt(i);
358
+ hash = ((hash << 5) - hash + char) | 0;
359
+ }
360
+ return `${languageId}:${hash.toString(36)}`;
361
+ }
362
+ /** Compile a pattern into a tree-sitter Query with caching */
351
363
  async compileQuery(pattern, languageId) {
364
+ const cacheKey = this.getQueryCacheKey(pattern, languageId);
365
+ // Check cache first
366
+ if (this.queryCache.has(cacheKey)) {
367
+ this.dbg(`Query cache hit: ${cacheKey}`);
368
+ return this.queryCache.get(cacheKey);
369
+ }
352
370
  const language = await this.loadLanguage(languageId);
353
371
  if (!language) {
354
372
  this.dbg(`Could not load language ${languageId}`);
@@ -362,7 +380,10 @@ export class TreeSitterClient {
362
380
  // biome-ignore lint/suspicious/noExplicitAny: Language type compatibility
363
381
  const query = new Query(language, queryStr);
364
382
  this.dbg(`Query compiled with ${query.patternCount} patterns`);
365
- return { query, metavars, postFilter, postFilterParams };
383
+ const result = { query, metavars, postFilter, postFilterParams };
384
+ // Cache the compiled query
385
+ this.queryCache.set(cacheKey, result);
386
+ return result;
366
387
  }
367
388
  catch (err) {
368
389
  this.dbg(`Query compilation failed: ${err}`);
@@ -93,6 +93,8 @@ export class TreeSitterClient {
93
93
  private ParserClass: any = null;
94
94
  // biome-ignore lint/suspicious/noExplicitAny: Language loader from module
95
95
  private LanguageLoader: any = null;
96
+ // biome-ignore lint/suspicious/noExplicitAny: Compiled query cache by language+pattern hash
97
+ private queryCache = new Map<string, any>();
96
98
  private queryLoader = new TreeSitterQueryLoader();
97
99
  private queriesLoaded = false;
98
100
  private verbose: boolean;
@@ -518,7 +520,18 @@ export class TreeSitterClient {
518
520
  return { query: "", metavars: [] };
519
521
  }
520
522
 
521
- /** Compile a pattern into a tree-sitter Query */
523
+ /** Generate cache key for compiled query */
524
+ private getQueryCacheKey(pattern: string, languageId: string): string {
525
+ // Simple hash for the query string
526
+ let hash = 0;
527
+ for (let i = 0; i < pattern.length; i++) {
528
+ const char = pattern.charCodeAt(i);
529
+ hash = ((hash << 5) - hash + char) | 0;
530
+ }
531
+ return `${languageId}:${hash.toString(36)}`;
532
+ }
533
+
534
+ /** Compile a pattern into a tree-sitter Query with caching */
522
535
  private async compileQuery(
523
536
  pattern: string,
524
537
  languageId: string,
@@ -528,6 +541,14 @@ export class TreeSitterClient {
528
541
  postFilter?: string;
529
542
  postFilterParams?: unknown;
530
543
  } | null> {
544
+ const cacheKey = this.getQueryCacheKey(pattern, languageId);
545
+
546
+ // Check cache first
547
+ if (this.queryCache.has(cacheKey)) {
548
+ this.dbg(`Query cache hit: ${cacheKey}`);
549
+ return this.queryCache.get(cacheKey);
550
+ }
551
+
531
552
  const language = await this.loadLanguage(languageId);
532
553
  if (!language) {
533
554
  this.dbg(`Could not load language ${languageId}`);
@@ -548,7 +569,11 @@ export class TreeSitterClient {
548
569
  // biome-ignore lint/suspicious/noExplicitAny: Language type compatibility
549
570
  const query = new Query(language as any, queryStr);
550
571
  this.dbg(`Query compiled with ${query.patternCount} patterns`);
551
- return { query, metavars, postFilter, postFilterParams };
572
+
573
+ const result = { query, metavars, postFilter, postFilterParams };
574
+ // Cache the compiled query
575
+ this.queryCache.set(cacheKey, result);
576
+ return result;
552
577
  } catch (err) {
553
578
  this.dbg(`Query compilation failed: ${err}`);
554
579
  return null;