diff --git a/README.md b/README.md
index b4c8e1f..b163caa 100644
--- a/README.md
+++ b/README.md
@@ -15,7 +15,7 @@
-Code Search MCP is a high-performance Model Context Protocol server that enables LLMs to intelligently search and analyze codebases across 12 programming languages. Built on universal-ctags, ripgrep, and ast-grep, it provides fast symbol search, structural AST search, text search, file search, and dependency analysis with persistent caching for 80%+ faster startup times.
+Code Search MCP is a high-performance Model Context Protocol server that enables LLMs to intelligently search and analyze codebases across 12 programming languages with comprehensive AST search support for 15 languages. Built on universal-ctags, ripgrep, and ast-grep, it provides fast symbol search, structural AST pattern matching, text search, file search, and dependency analysis with persistent caching for 80%+ faster startup times.
@@ -350,9 +350,90 @@ Search code using Abstract Syntax Tree analysis for structural pattern matching
-**Supported Languages:** JavaScript, TypeScript, TSX, HTML, CSS
+**Supported Languages (15 Total):**
-*Note: @ast-grep/napi includes these 5 languages by default. Additional language support can be added via dynamic language packages if needed.*
+
+
+
+
+
+ | Language |
+ File Extensions |
+
+
+ | Bash |
+ .sh, .bash |
+
+
+ | C |
+ .c, .h |
+
+
+ | C++ |
+ .cpp, .cc, .cxx, .hpp, .hxx |
+
+
+ | C# |
+ .cs |
+
+
+ | CSS |
+ .css |
+
+
+ | Go |
+ .go |
+
+
+ | HTML |
+ .html, .htm |
+
+
+ | Java |
+ .java |
+
+
+ | JavaScript |
+ .js, .jsx, .mjs |
+
+
+ | JSON |
+ .json |
+
+
+ | Kotlin |
+ .kt, .kts |
+
+
+ | Python |
+ .py |
+
+
+ | Rust |
+ .rs |
+
+
+ | Scala |
+ .scala |
+
+
+ | Swift |
+ .swift |
+
+
+ | TypeScript |
+ .ts, .tsx |
+
+
+ | YAML |
+ .yml, .yaml |
+
+
+
+
+
+
+*All AST language packages are bundled with the server - no additional installation required!*
diff --git a/package-lock.json b/package-lock.json
index 4f9b028..441ee4f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,6 +9,21 @@
"version": "1.0.0",
"license": "MIT",
"dependencies": {
+ "@ast-grep/lang-bash": "^0.0.4",
+ "@ast-grep/lang-c": "^0.0.3",
+ "@ast-grep/lang-cpp": "^0.0.3",
+ "@ast-grep/lang-csharp": "^0.0.3",
+ "@ast-grep/lang-go": "^0.0.3",
+ "@ast-grep/lang-java": "^0.0.4",
+ "@ast-grep/lang-json": "^0.0.4",
+ "@ast-grep/lang-kotlin": "^0.0.4",
+ "@ast-grep/lang-python": "^0.0.3",
+ "@ast-grep/lang-rust": "^0.0.4",
+ "@ast-grep/lang-scala": "^0.0.4",
+ "@ast-grep/lang-swift": "^0.0.5",
+ "@ast-grep/lang-tsx": "^0.0.4",
+ "@ast-grep/lang-typescript": "^0.0.4",
+ "@ast-grep/lang-yaml": "^0.0.3",
"@ast-grep/napi": "^0.40.0",
"@LLMTooling/code-search-mcp-universal-ctags": "^0.1.0",
"@modelcontextprotocol/sdk": "^1.0.4",
@@ -39,6 +54,276 @@
"node": ">=18.0.0"
}
},
+ "node_modules/@ast-grep/lang-bash": {
+ "version": "0.0.4",
+ "resolved": "https://registry.npmjs.org/@ast-grep/lang-bash/-/lang-bash-0.0.4.tgz",
+ "integrity": "sha512-i9Zpv+phyy94dE5hv5xZT3qqJ6YINi/YhGGhmJvagl+uWBwEkD+Ve5cDqwH+AkGad2xy8+decNE4TkllJEGPww==",
+ "hasInstallScript": true,
+ "license": "ISC",
+ "dependencies": {
+ "@ast-grep/setup-lang": "0.0.4"
+ },
+ "peerDependencies": {
+ "tree-sitter-cli": "0.25.8"
+ },
+ "peerDependenciesMeta": {
+ "tree-sitter-cli": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@ast-grep/lang-c": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/@ast-grep/lang-c/-/lang-c-0.0.3.tgz",
+ "integrity": "sha512-guoNNaQPfQsSZGD5McNVqJgD31y8egyQZxJL+vVm3uPrODbRKcTjgx9ZMFFIgI+lUHAJHZkMN8QJ2bo11FWXtQ==",
+ "hasInstallScript": true,
+ "license": "ISC",
+ "dependencies": {
+ "@ast-grep/setup-lang": "0.0.4"
+ },
+ "peerDependencies": {
+ "tree-sitter-cli": "0.25.8"
+ },
+ "peerDependenciesMeta": {
+ "tree-sitter-cli": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@ast-grep/lang-cpp": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/@ast-grep/lang-cpp/-/lang-cpp-0.0.3.tgz",
+ "integrity": "sha512-3xO6zrW/CV/U1v3YPl0y2ssaVnUWBpj4LeTnnygKJAaDQgw8RPjQeKmXdY6bOf8govOdquOGFmEmBPUkxZrsVg==",
+ "hasInstallScript": true,
+ "license": "ISC",
+ "dependencies": {
+ "@ast-grep/setup-lang": "0.0.4"
+ },
+ "peerDependencies": {
+ "tree-sitter-cli": "0.25.8"
+ },
+ "peerDependenciesMeta": {
+ "tree-sitter-cli": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@ast-grep/lang-csharp": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/@ast-grep/lang-csharp/-/lang-csharp-0.0.3.tgz",
+ "integrity": "sha512-N7FrEgoKg6yjKDkrDDVH0ckIKgLGn/IIbtENVNeAnar4zP7xXLQQiuZ9iZJl4Hu2BWOZZ+ssk2w5qN4tdoFHnA==",
+ "hasInstallScript": true,
+ "license": "ISC",
+ "dependencies": {
+ "@ast-grep/setup-lang": "0.0.4"
+ },
+ "peerDependencies": {
+ "tree-sitter-cli": "0.25.8"
+ },
+ "peerDependenciesMeta": {
+ "tree-sitter-cli": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@ast-grep/lang-go": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/@ast-grep/lang-go/-/lang-go-0.0.3.tgz",
+ "integrity": "sha512-QUqeo+X9/juW0c1wCfJHVs1LaSehMwk9X5T8uBx6jvZmom8bm7a2NMiLqnVfmtzFNS+m8SRJa+h44whFQqtClA==",
+ "hasInstallScript": true,
+ "license": "ISC",
+ "dependencies": {
+ "@ast-grep/setup-lang": "0.0.4"
+ },
+ "peerDependencies": {
+ "tree-sitter-cli": "0.25.8"
+ },
+ "peerDependenciesMeta": {
+ "tree-sitter-cli": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@ast-grep/lang-java": {
+ "version": "0.0.4",
+ "resolved": "https://registry.npmjs.org/@ast-grep/lang-java/-/lang-java-0.0.4.tgz",
+ "integrity": "sha512-x2bacyZ3ui40q0Ne8Sx5x1Ii0bBy3NmUbOY+FadQGUHEyZciR82XisHVbYNadB/NNTxLcqmXJv6szd91S1xirA==",
+ "hasInstallScript": true,
+ "license": "ISC",
+ "dependencies": {
+ "@ast-grep/setup-lang": "0.0.4"
+ },
+ "peerDependencies": {
+ "tree-sitter-cli": "0.25.8"
+ },
+ "peerDependenciesMeta": {
+ "tree-sitter-cli": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@ast-grep/lang-json": {
+ "version": "0.0.4",
+ "resolved": "https://registry.npmjs.org/@ast-grep/lang-json/-/lang-json-0.0.4.tgz",
+ "integrity": "sha512-ig+N5Ub+gV3qEBOZeKOS3e2ms/pYPHfbtC2VyEcW9EnWfRwYChVDEaMtmv8UxnWbAIRIrxHoGUnCh0L4slIbRQ==",
+ "hasInstallScript": true,
+ "license": "ISC",
+ "dependencies": {
+ "@ast-grep/setup-lang": "0.0.4"
+ },
+ "peerDependencies": {
+ "tree-sitter-cli": "0.25.8"
+ },
+ "peerDependenciesMeta": {
+ "tree-sitter-cli": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@ast-grep/lang-kotlin": {
+ "version": "0.0.4",
+ "resolved": "https://registry.npmjs.org/@ast-grep/lang-kotlin/-/lang-kotlin-0.0.4.tgz",
+ "integrity": "sha512-220biX78cRzcKYzYkJD+BxxA0PdNdJQtWQ/bPC8SUe11l45cuRDv+y3HNew9s5r4VTiUVtKRokVw7j6IwWDYRw==",
+ "hasInstallScript": true,
+ "license": "ISC",
+ "dependencies": {
+ "@ast-grep/setup-lang": "0.0.4"
+ },
+ "peerDependencies": {
+ "tree-sitter-cli": "0.25.8"
+ },
+ "peerDependenciesMeta": {
+ "tree-sitter-cli": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@ast-grep/lang-python": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/@ast-grep/lang-python/-/lang-python-0.0.3.tgz",
+ "integrity": "sha512-nbcXhzLWGNDCzEQvH5XVRz0SRJIVvW50zaN8Do2SZGDfibnTdytJKyFo1agL6fYW/KpWNRMNn+SwdS98NV6FFQ==",
+ "hasInstallScript": true,
+ "license": "ISC",
+ "dependencies": {
+ "@ast-grep/setup-lang": "0.0.4"
+ },
+ "peerDependencies": {
+ "tree-sitter-cli": "0.25.8"
+ },
+ "peerDependenciesMeta": {
+ "tree-sitter-cli": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@ast-grep/lang-rust": {
+ "version": "0.0.4",
+ "resolved": "https://registry.npmjs.org/@ast-grep/lang-rust/-/lang-rust-0.0.4.tgz",
+ "integrity": "sha512-LUYigVWdyI8hyp8s5Yrm8LWQXzCHBpPoNv2ula45EY5bBRk0EQ+LXGZgxTZ22CKuWbmsnhhiJtF6cBwHRw6D/A==",
+ "hasInstallScript": true,
+ "license": "ISC",
+ "dependencies": {
+ "@ast-grep/setup-lang": "0.0.4"
+ },
+ "peerDependencies": {
+ "tree-sitter-cli": "0.25.8"
+ },
+ "peerDependenciesMeta": {
+ "tree-sitter-cli": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@ast-grep/lang-scala": {
+ "version": "0.0.4",
+ "resolved": "https://registry.npmjs.org/@ast-grep/lang-scala/-/lang-scala-0.0.4.tgz",
+ "integrity": "sha512-NAe+5hQpkLM8Be26ocXA7vXaBX5cU/Uwo97pFlGbSNiDI+z/zxGrCeXL7Ao/Md6hgN+0fUGLhcVZBf9L8M3uFg==",
+ "hasInstallScript": true,
+ "license": "ISC",
+ "dependencies": {
+ "@ast-grep/setup-lang": "0.0.4"
+ },
+ "peerDependencies": {
+ "tree-sitter-cli": "0.25.8"
+ },
+ "peerDependenciesMeta": {
+ "tree-sitter-cli": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@ast-grep/lang-swift": {
+ "version": "0.0.5",
+ "resolved": "https://registry.npmjs.org/@ast-grep/lang-swift/-/lang-swift-0.0.5.tgz",
+ "integrity": "sha512-tuPBT05HUW7KEmPRfp/pPdwE/tl9Mra+xPPfR3OdzzfmqiNCl/NfUIsLZNTM3YgSk7LzwiFEAXKLxdqXh+WF8Q==",
+ "hasInstallScript": true,
+ "license": "ISC",
+ "dependencies": {
+ "@ast-grep/setup-lang": "0.0.4"
+ },
+ "peerDependencies": {
+ "tree-sitter-cli": "0.25.8"
+ },
+ "peerDependenciesMeta": {
+ "tree-sitter-cli": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@ast-grep/lang-tsx": {
+ "version": "0.0.4",
+ "resolved": "https://registry.npmjs.org/@ast-grep/lang-tsx/-/lang-tsx-0.0.4.tgz",
+ "integrity": "sha512-1Aduy3MTn0NGBRWdpTUbVdMHswtSQavBsl2RhrlXvVYEOoMTC0vdbdnZH0tM+3am4qEI3LFECNd3z5QBKG610g==",
+ "hasInstallScript": true,
+ "license": "ISC",
+ "dependencies": {
+ "@ast-grep/setup-lang": "0.0.4"
+ },
+ "peerDependencies": {
+ "tree-sitter-cli": "0.25.8"
+ },
+ "peerDependenciesMeta": {
+ "tree-sitter-cli": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@ast-grep/lang-typescript": {
+ "version": "0.0.4",
+ "resolved": "https://registry.npmjs.org/@ast-grep/lang-typescript/-/lang-typescript-0.0.4.tgz",
+ "integrity": "sha512-668IL2nLZKcI5dZEzAiL0r75IkpxfwyuhwR0V7LyMAnm+WKiszsFni2mR45CIhzWs9524jbCBFpvqMjx2m2rfA==",
+ "hasInstallScript": true,
+ "license": "ISC",
+ "dependencies": {
+ "@ast-grep/setup-lang": "0.0.4"
+ },
+ "peerDependencies": {
+ "tree-sitter-cli": "0.25.8"
+ },
+ "peerDependenciesMeta": {
+ "tree-sitter-cli": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@ast-grep/lang-yaml": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/@ast-grep/lang-yaml/-/lang-yaml-0.0.3.tgz",
+ "integrity": "sha512-Bhw8RDdqyqS52r+aL+EzBtzEs5oMsHGAbBBZzvUNUuEWEgF9HOBXaWwJXh/rpx+V0oPiVv+0l2+ixi+bgUatoA==",
+ "hasInstallScript": true,
+ "license": "ISC",
+ "dependencies": {
+ "@ast-grep/setup-lang": "0.0.4"
+ },
+ "peerDependencies": {
+ "tree-sitter-cli": "0.25.8"
+ },
+ "peerDependenciesMeta": {
+ "tree-sitter-cli": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@ast-grep/napi": {
"version": "0.40.0",
"resolved": "https://registry.npmjs.org/@ast-grep/napi/-/napi-0.40.0.tgz",
@@ -203,6 +488,12 @@
"node": ">= 10"
}
},
+ "node_modules/@ast-grep/setup-lang": {
+ "version": "0.0.4",
+ "resolved": "https://registry.npmjs.org/@ast-grep/setup-lang/-/setup-lang-0.0.4.tgz",
+ "integrity": "sha512-us5L9CU4pc/yLQbO82v7gkpwa66qRoQRJ3P+s36EC6ZXiYpQVbd13PLgh9bQ/LhGljEb/pcli0uclN/+S3IMSw==",
+ "license": "ISC"
+ },
"node_modules/@babel/code-frame": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
@@ -2324,9 +2615,9 @@
"license": "MIT"
},
"node_modules/baseline-browser-mapping": {
- "version": "2.8.29",
- "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.29.tgz",
- "integrity": "sha512-sXdt2elaVnhpDNRDz+1BDx1JQoJRuNk7oVlAlbGiFkLikHCAQiccexF/9e91zVi6RCgqspl04aP+6Cnl9zRLrA==",
+ "version": "2.8.30",
+ "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.30.tgz",
+ "integrity": "sha512-aTUKW4ptQhS64+v2d6IkPzymEzzhw+G0bA1g3uBRV3+ntkH+svttKseW5IOR4Ed6NUVKqnY7qT3dKvzQ7io4AA==",
"dev": true,
"license": "Apache-2.0",
"bin": {
@@ -2507,9 +2798,9 @@
}
},
"node_modules/caniuse-lite": {
- "version": "1.0.30001755",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001755.tgz",
- "integrity": "sha512-44V+Jm6ctPj7R52Na4TLi3Zri4dWUljJd+RDm+j8LtNCc/ihLCT+X1TzoOAkRETEWqjuLnh9581Tl80FvK7jVA==",
+ "version": "1.0.30001756",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001756.tgz",
+ "integrity": "sha512-4HnCNKbMLkLdhJz3TToeVWHSnfJvPaq6vu/eRP0Ahub/07n484XHhBF5AJoSGHdVrS8tKFauUQz8Bp9P7LVx7A==",
"dev": true,
"funding": [
{
@@ -2647,15 +2938,16 @@
"license": "MIT"
},
"node_modules/content-disposition": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz",
- "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==",
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz",
+ "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==",
"license": "MIT",
- "dependencies": {
- "safe-buffer": "5.2.1"
- },
"engines": {
- "node": ">= 0.6"
+ "node": ">=18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
}
},
"node_modules/content-type": {
@@ -2857,9 +3149,9 @@
"license": "MIT"
},
"node_modules/electron-to-chromium": {
- "version": "1.5.255",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.255.tgz",
- "integrity": "sha512-Z9oIp4HrFF/cZkDPMpz2XSuVpc1THDpT4dlmATFlJUIBVCy9Vap5/rIXsASP1CscBacBqhabwh8vLctqBwEerQ==",
+ "version": "1.5.259",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.259.tgz",
+ "integrity": "sha512-I+oLXgpEJzD6Cwuwt1gYjxsDmu/S/Kd41mmLA3O+/uH2pFRO/DvOjUyGozL8j3KeLV6WyZ7ssPwELMsXCcsJAQ==",
"dev": true,
"license": "ISC"
},
@@ -3813,28 +4105,23 @@
"license": "MIT"
},
"node_modules/http-errors": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
- "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
+ "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==",
"license": "MIT",
"dependencies": {
- "depd": "2.0.0",
- "inherits": "2.0.4",
- "setprototypeof": "1.2.0",
- "statuses": "2.0.1",
- "toidentifier": "1.0.1"
+ "depd": "~2.0.0",
+ "inherits": "~2.0.4",
+ "setprototypeof": "~1.2.0",
+ "statuses": "~2.0.2",
+ "toidentifier": "~1.0.1"
},
"engines": {
"node": ">= 0.8"
- }
- },
- "node_modules/http-errors/node_modules/statuses": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
- "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.8"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
}
},
"node_modules/https-proxy-agent": {
@@ -4974,15 +5261,19 @@
}
},
"node_modules/mime-types": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz",
- "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==",
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz",
+ "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==",
"license": "MIT",
"dependencies": {
"mime-db": "^1.54.0"
},
"engines": {
- "node": ">= 0.6"
+ "node": ">=18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
}
},
"node_modules/mimic-fn": {
@@ -5569,15 +5860,15 @@
}
},
"node_modules/raw-body": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.1.tgz",
- "integrity": "sha512-9G8cA+tuMS75+6G/TzW8OtLzmBDMo8p1JRxN5AZ+LAp8uxGA8V8GZm4GQ4/N5QNQEnLmg6SS7wyuSmbKepiKqA==",
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz",
+ "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==",
"license": "MIT",
"dependencies": {
- "bytes": "3.1.2",
- "http-errors": "2.0.0",
- "iconv-lite": "0.7.0",
- "unpipe": "1.0.0"
+ "bytes": "~3.1.2",
+ "http-errors": "~2.0.1",
+ "iconv-lite": "~0.7.0",
+ "unpipe": "~1.0.0"
},
"engines": {
"node": ">= 0.10"
@@ -5738,26 +6029,6 @@
"queue-microtask": "^1.2.2"
}
},
- "node_modules/safe-buffer": {
- "version": "5.2.1",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
- "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/feross"
- },
- {
- "type": "patreon",
- "url": "https://www.patreon.com/feross"
- },
- {
- "type": "consulting",
- "url": "https://feross.org/support"
- }
- ],
- "license": "MIT"
- },
"node_modules/safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
@@ -6694,12 +6965,12 @@
}
},
"node_modules/zod-to-json-schema": {
- "version": "3.24.6",
- "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.6.tgz",
- "integrity": "sha512-h/z3PKvcTcTetyjl1fkj79MHNEjm+HpD6NXheWjzOekY7kV+lwDYnHw+ivHkijnCSMz1yJaWBD9vu/Fcmk+vEg==",
+ "version": "3.25.0",
+ "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.0.tgz",
+ "integrity": "sha512-HvWtU2UG41LALjajJrML6uQejQhNJx+JBO9IflpSja4R03iNWfKXrj6W2h7ljuLyc1nKS+9yDyL/9tD1U/yBnQ==",
"license": "ISC",
"peerDependencies": {
- "zod": "^3.24.1"
+ "zod": "^3.25 || ^4"
}
}
}
diff --git a/package.json b/package.json
index 7fad47d..481b9fe 100644
--- a/package.json
+++ b/package.json
@@ -28,6 +28,21 @@
"author": "",
"license": "MIT",
"dependencies": {
+ "@ast-grep/lang-bash": "^0.0.4",
+ "@ast-grep/lang-c": "^0.0.3",
+ "@ast-grep/lang-cpp": "^0.0.3",
+ "@ast-grep/lang-csharp": "^0.0.3",
+ "@ast-grep/lang-go": "^0.0.3",
+ "@ast-grep/lang-java": "^0.0.4",
+ "@ast-grep/lang-json": "^0.0.4",
+ "@ast-grep/lang-kotlin": "^0.0.4",
+ "@ast-grep/lang-python": "^0.0.3",
+ "@ast-grep/lang-rust": "^0.0.4",
+ "@ast-grep/lang-scala": "^0.0.4",
+ "@ast-grep/lang-swift": "^0.0.5",
+ "@ast-grep/lang-tsx": "^0.0.4",
+ "@ast-grep/lang-typescript": "^0.0.4",
+ "@ast-grep/lang-yaml": "^0.0.3",
"@ast-grep/napi": "^0.40.0",
"@LLMTooling/code-search-mcp-universal-ctags": "^0.1.0",
"@modelcontextprotocol/sdk": "^1.0.4",
diff --git a/src/ast-search/ast-search-service.ts b/src/ast-search/ast-search-service.ts
index f265491..ec8c4fd 100644
--- a/src/ast-search/ast-search-service.ts
+++ b/src/ast-search/ast-search-service.ts
@@ -1,9 +1,24 @@
/**
* AST search service using ast-grep NAPI for structural code search
- * Uses bundled native binaries - no external installation required
+ * Uses bundled native binaries and language packages - no external installation required
*/
-import { parse, Lang } from '@ast-grep/napi';
+import { parse, Lang, registerDynamicLanguage } from '@ast-grep/napi';
+import langBash = require('@ast-grep/lang-bash');
+import langC = require('@ast-grep/lang-c');
+import langCpp = require('@ast-grep/lang-cpp');
+import langCsharp = require('@ast-grep/lang-csharp');
+import langGo = require('@ast-grep/lang-go');
+import langJava = require('@ast-grep/lang-java');
+import langJson = require('@ast-grep/lang-json');
+import langKotlin = require('@ast-grep/lang-kotlin');
+import langPython = require('@ast-grep/lang-python');
+import langRust = require('@ast-grep/lang-rust');
+import langScala = require('@ast-grep/lang-scala');
+import langSwift = require('@ast-grep/lang-swift');
+import langTsx = require('@ast-grep/lang-tsx');
+import langTypeScript = require('@ast-grep/lang-typescript');
+import langYaml = require('@ast-grep/lang-yaml');
import { promises as fs } from 'fs';
import path from 'path';
import fastGlob from 'fast-glob';
@@ -17,29 +32,94 @@ import type {
ASTLanguage,
} from '../types/ast-search.js';
-// Language mapping from our types to ast-grep Lang enum
-// Only includes languages built into @ast-grep/napi
-const LANGUAGE_MAP: Record = {
- javascript: Lang.JavaScript,
- typescript: Lang.TypeScript,
- tsx: Lang.Tsx,
- html: Lang.Html,
+// Type for ast-grep language (built-in or custom string)
+type NapiLang = Lang | string;
+
+// Register dynamic languages once
+let languagesRegistered = false;
+
+function ensureLanguagesRegistered() {
+ if (!languagesRegistered) {
+ registerDynamicLanguage({
+ bash: langBash as any,
+ c: langC as any,
+ cpp: langCpp as any,
+ csharp: langCsharp as any,
+ go: langGo as any,
+ java: langJava as any,
+ json: langJson as any,
+ kotlin: langKotlin as any,
+ python: langPython as any,
+ rust: langRust as any,
+ scala: langScala as any,
+ swift: langSwift as any,
+ tsx: langTsx as any,
+ typescript: langTypeScript as any,
+ yaml: langYaml as any,
+ });
+ languagesRegistered = true;
+ }
+}
+
+// Language mapping from our types to ast-grep NapiLang
+// Includes built-in languages and dynamically registered language packages
+const LANGUAGE_MAP: Record = {
+ bash: 'bash',
+ c: 'c',
+ cpp: 'cpp',
+ csharp: 'csharp',
css: Lang.Css,
+ go: 'go',
+ html: Lang.Html,
+ java: 'java',
+ javascript: Lang.JavaScript,
+ json: 'json',
+ kotlin: 'kotlin',
+ python: 'python',
+ rust: 'rust',
+ scala: 'scala',
+ swift: 'swift',
+ tsx: 'tsx',
+ typescript: 'typescript',
+ yaml: 'yaml',
};
// File extension to language mapping
const EXTENSION_MAP: Record = {
+ '.c': 'c',
+ '.h': 'c',
+ '.cpp': 'cpp',
+ '.cc': 'cpp',
+ '.cxx': 'cpp',
+ '.hpp': 'cpp',
+ '.hxx': 'cpp',
+ '.cs': 'csharp',
+ '.css': 'css',
+ '.go': 'go',
+ '.html': 'html',
+ '.htm': 'html',
+ '.java': 'java',
'.js': 'javascript',
'.mjs': 'javascript',
'.cjs': 'javascript',
'.jsx': 'javascript',
+ '.json': 'json',
+ '.kt': 'kotlin',
+ '.kts': 'kotlin',
+ '.py': 'python',
+ '.pyw': 'python',
+ '.rs': 'rust',
+ '.scala': 'scala',
+ '.sc': 'scala',
+ '.sh': 'bash',
+ '.bash': 'bash',
+ '.swift': 'swift',
'.ts': 'typescript',
'.mts': 'typescript',
'.cts': 'typescript',
'.tsx': 'tsx',
- '.html': 'html',
- '.htm': 'html',
- '.css': 'css',
+ '.yaml': 'yaml',
+ '.yml': 'yaml',
};
export class ASTSearchService {
@@ -48,16 +128,24 @@ export class ASTSearchService {
*/
async isAvailable(): Promise {
try {
- // Try to access the Lang enum to verify the module loads
- const testLang = Lang.JavaScript;
- if (testLang !== undefined) {
+ // Ensure dynamic languages are registered
+ ensureLanguagesRegistered();
+
+ // Try to access the Lang enum and language packages to verify modules load
+ const testBuiltIn = Lang.JavaScript;
+ const testPython = langPython;
+ const testGo = langGo;
+ const testJava = langJava;
+
+ if (testBuiltIn !== undefined && testPython !== undefined && testGo !== undefined && testJava !== undefined) {
+ const supportedLangs = Object.keys(LANGUAGE_MAP).sort().join(', ');
return {
available: true,
- version: '0.40.0', // @ast-grep/napi version
- path: 'bundled (native)',
+ version: '0.40.0', // @ast-grep packages version
+ path: `bundled (15 languages: ${supportedLangs})`,
};
}
- throw new Error('Failed to load ast-grep module');
+ throw new Error('Failed to load ast-grep modules');
} catch (error) {
return {
available: false,
@@ -74,6 +162,9 @@ export class ASTSearchService {
workspacePath: string,
options: ASTPatternSearchOptions
): Promise {
+ // Ensure dynamic languages are registered
+ ensureLanguagesRegistered();
+
const startTime = Date.now();
// Get files to search
@@ -155,6 +246,9 @@ export class ASTSearchService {
workspacePath: string,
options: ASTRuleSearchOptions
): Promise {
+ // Ensure dynamic languages are registered
+ ensureLanguagesRegistered();
+
const startTime = Date.now();
// Get files to search
diff --git a/src/types/ast-search.ts b/src/types/ast-search.ts
index 24f591f..a54aef7 100644
--- a/src/types/ast-search.ts
+++ b/src/types/ast-search.ts
@@ -4,14 +4,27 @@
/**
* Supported programming languages for AST search
- * These are the languages built into @ast-grep/napi
+ * Includes built-in languages and dynamically loaded language packages
*/
export type ASTLanguage =
+ | 'bash'
+ | 'c'
+ | 'cpp'
+ | 'csharp'
+ | 'css'
+ | 'go'
+ | 'html'
+ | 'java'
| 'javascript'
- | 'typescript'
+ | 'json'
+ | 'kotlin'
+ | 'python'
+ | 'rust'
+ | 'scala'
+ | 'swift'
| 'tsx'
- | 'html'
- | 'css';
+ | 'typescript'
+ | 'yaml';
/**
* Stop-by directive for relational rules
diff --git a/tests/integration/ast-mcp-integration.test.ts b/tests/integration/ast-mcp-integration.test.ts
index 54be0a7..6033a1c 100644
--- a/tests/integration/ast-mcp-integration.test.ts
+++ b/tests/integration/ast-mcp-integration.test.ts
@@ -283,7 +283,8 @@ describe('AST MCP Integration Tests', () => {
if (info.available) {
expect(info.version).toBeDefined();
- expect(info.path).toBe('bundled (native)');
+ expect(info.path).toContain('bundled');
+ expect(info.path).toContain('languages');
}
});
});
diff --git a/tests/integration/ast-search.test.ts b/tests/integration/ast-search.test.ts
index 394e0ce..f3ff5eb 100644
--- a/tests/integration/ast-search.test.ts
+++ b/tests/integration/ast-search.test.ts
@@ -215,6 +215,601 @@ describe('AST Search Integration', () => {
});
});
+ describe('Rust Support', () => {
+ it('should search Rust files with patterns', async () => {
+ if (!astGrepAvailable) {
+ return;
+ }
+
+ const result = await service.searchPattern('test-workspace', tempDir, {
+ language: 'rust',
+ pattern: 'fn $NAME($$$) { $$$ }',
+ });
+
+ expect(result.language).toBe('rust');
+ expect(result.matches.length).toBeGreaterThan(0);
+ expect(Array.isArray(result.matches)).toBe(true);
+ });
+
+ it('should find Rust struct definitions', async () => {
+ if (!astGrepAvailable) {
+ return;
+ }
+
+ const rule: ASTRule = {
+ pattern: 'struct $NAME { $$$ }',
+ };
+
+ const result = await service.searchRule('test-workspace', tempDir, {
+ language: 'rust',
+ rule,
+ });
+
+ expect(result.language).toBe('rust');
+ expect(result.matches.length).toBeGreaterThan(0);
+ });
+
+ it('should find Rust impl blocks', async () => {
+ if (!astGrepAvailable) {
+ return;
+ }
+
+ const result = await service.searchPattern('test-workspace', tempDir, {
+ language: 'rust',
+ pattern: 'impl $NAME { $$$ }',
+ });
+
+ expect(result.language).toBe('rust');
+ expect(Array.isArray(result.matches)).toBe(true);
+ });
+
+ it('should find Rust async functions', async () => {
+ if (!astGrepAvailable) {
+ return;
+ }
+
+ const rule: ASTRule = {
+ pattern: 'async fn $NAME($$$) { $$$ }',
+ };
+
+ const result = await service.searchRule('test-workspace', tempDir, {
+ language: 'rust',
+ rule,
+ });
+
+ expect(result.language).toBe('rust');
+ expect(Array.isArray(result.matches)).toBe(true);
+ });
+ });
+
+ describe('Python Support', () => {
+ it('should search Python files with patterns', async () => {
+ if (!astGrepAvailable) {
+ return;
+ }
+
+ const result = await service.searchPattern('test-workspace', tempDir, {
+ language: 'python',
+ pattern: 'def $NAME($$$): $$$',
+ });
+
+ expect(result.language).toBe('python');
+ expect(result.matches.length).toBeGreaterThan(0);
+ });
+
+ it('should find Python class definitions', async () => {
+ if (!astGrepAvailable) {
+ return;
+ }
+
+ const result = await service.searchPattern('test-workspace', tempDir, {
+ language: 'python',
+ pattern: 'class $NAME: $$$',
+ });
+
+ expect(result.language).toBe('python');
+ expect(result.matches.length).toBeGreaterThan(0);
+ });
+
+ it('should find Python async functions', async () => {
+ if (!astGrepAvailable) {
+ return;
+ }
+
+ const result = await service.searchPattern('test-workspace', tempDir, {
+ language: 'python',
+ pattern: 'async def $NAME($$$): $$$',
+ });
+
+ expect(result.language).toBe('python');
+ expect(Array.isArray(result.matches)).toBe(true);
+ });
+ });
+
+ describe('Go Support', () => {
+ it('should search Go files with patterns', async () => {
+ if (!astGrepAvailable) {
+ return;
+ }
+
+ const result = await service.searchPattern('test-workspace', tempDir, {
+ language: 'go',
+ pattern: 'func $NAME($$$) $$$',
+ });
+
+ expect(result.language).toBe('go');
+ expect(result.matches.length).toBeGreaterThan(0);
+ });
+
+ it('should find Go struct definitions', async () => {
+ if (!astGrepAvailable) {
+ return;
+ }
+
+ const result = await service.searchPattern('test-workspace', tempDir, {
+ language: 'go',
+ pattern: 'type $NAME struct { $$$ }',
+ });
+
+ expect(result.language).toBe('go');
+ expect(result.matches.length).toBeGreaterThan(0);
+ });
+
+ it('should find Go methods', async () => {
+ if (!astGrepAvailable) {
+ return;
+ }
+
+ const result = await service.searchPattern('test-workspace', tempDir, {
+ language: 'go',
+ pattern: 'func ($$$) $NAME($$$) $$$',
+ });
+
+ expect(result.language).toBe('go');
+ expect(Array.isArray(result.matches)).toBe(true);
+ });
+ });
+
+ describe('Java Support', () => {
+ it('should search Java files with patterns', async () => {
+ if (!astGrepAvailable) {
+ return;
+ }
+
+ const result = await service.searchPattern('test-workspace', tempDir, {
+ language: 'java',
+ pattern: 'public class $NAME { $$$ }',
+ });
+
+ expect(result.language).toBe('java');
+ expect(result.matches.length).toBeGreaterThan(0);
+ });
+
+ it('should find Java methods', async () => {
+ if (!astGrepAvailable) {
+ return;
+ }
+
+ const result = await service.searchPattern('test-workspace', tempDir, {
+ language: 'java',
+ pattern: 'public $TYPE $NAME($$$) { $$$ }',
+ });
+
+ expect(result.language).toBe('java');
+ expect(result.matches.length).toBeGreaterThan(0);
+ });
+
+ it('should find Java interfaces', async () => {
+ if (!astGrepAvailable) {
+ return;
+ }
+
+ const result = await service.searchPattern('test-workspace', tempDir, {
+ language: 'java',
+ pattern: 'interface $NAME { $$$ }',
+ });
+
+ expect(result.language).toBe('java');
+ expect(Array.isArray(result.matches)).toBe(true);
+ });
+ });
+
+ describe('C Support', () => {
+ it('should search C files with patterns', async () => {
+ if (!astGrepAvailable) {
+ return;
+ }
+
+ const result = await service.searchPattern('test-workspace', tempDir, {
+ language: 'c',
+ pattern: 'int $NAME($$$) { $$$ }',
+ });
+
+ expect(result.language).toBe('c');
+ expect(result.matches.length).toBeGreaterThan(0);
+ });
+
+ it('should find C struct definitions', async () => {
+ if (!astGrepAvailable) {
+ return;
+ }
+
+ const result = await service.searchPattern('test-workspace', tempDir, {
+ language: 'c',
+ pattern: 'struct $NAME',
+ });
+
+ expect(result.language).toBe('c');
+ expect(result.matches.length).toBeGreaterThan(0);
+ });
+
+ it('should find C function declarations', async () => {
+ if (!astGrepAvailable) {
+ return;
+ }
+
+ const result = await service.searchPattern('test-workspace', tempDir, {
+ language: 'c',
+ pattern: 'void $NAME($$$)',
+ });
+
+ expect(result.language).toBe('c');
+ expect(Array.isArray(result.matches)).toBe(true);
+ });
+ });
+
+ describe('C++ Support', () => {
+ it('should search C++ files with patterns', async () => {
+ if (!astGrepAvailable) {
+ return;
+ }
+
+ const result = await service.searchPattern('test-workspace', tempDir, {
+ language: 'cpp',
+ pattern: 'class $NAME',
+ });
+
+ expect(result.language).toBe('cpp');
+ expect(result.matches.length).toBeGreaterThan(0);
+ });
+
+ it('should find C++ template functions', async () => {
+ if (!astGrepAvailable) {
+ return;
+ }
+
+ const result = await service.searchPattern('test-workspace', tempDir, {
+ language: 'cpp',
+ pattern: 'template<$$$> $$$',
+ });
+
+ expect(result.language).toBe('cpp');
+ expect(result.matches.length).toBeGreaterThan(0);
+ });
+
+ it('should find C++ namespaces', async () => {
+ if (!astGrepAvailable) {
+ return;
+ }
+
+ const result = await service.searchPattern('test-workspace', tempDir, {
+ language: 'cpp',
+ pattern: 'namespace $NAME { $$$ }',
+ });
+
+ expect(result.language).toBe('cpp');
+ expect(Array.isArray(result.matches)).toBe(true);
+ });
+ });
+
+ describe('C# Support', () => {
+ it('should search C# files with patterns', async () => {
+ if (!astGrepAvailable) {
+ return;
+ }
+
+ const result = await service.searchPattern('test-workspace', tempDir, {
+ language: 'csharp',
+ pattern: 'public class $NAME { $$$ }',
+ });
+
+ expect(result.language).toBe('csharp');
+ expect(result.matches.length).toBeGreaterThan(0);
+ });
+
+ it('should find C# interfaces', async () => {
+ if (!astGrepAvailable) {
+ return;
+ }
+
+ const result = await service.searchPattern('test-workspace', tempDir, {
+ language: 'csharp',
+ pattern: 'public interface $NAME { $$$ }',
+ });
+
+ expect(result.language).toBe('csharp');
+ expect(result.matches.length).toBeGreaterThan(0);
+ });
+
+ it('should find C# static methods', async () => {
+ if (!astGrepAvailable) {
+ return;
+ }
+
+ const result = await service.searchPattern('test-workspace', tempDir, {
+ language: 'csharp',
+ pattern: 'public static $TYPE $NAME($$$)',
+ });
+
+ expect(result.language).toBe('csharp');
+ expect(Array.isArray(result.matches)).toBe(true);
+ });
+ });
+
+ describe('Kotlin Support', () => {
+ it('should search Kotlin files with patterns', async () => {
+ if (!astGrepAvailable) {
+ return;
+ }
+
+ const result = await service.searchPattern('test-workspace', tempDir, {
+ language: 'kotlin',
+ pattern: 'fun $NAME($$$): $$$',
+ });
+
+ expect(result.language).toBe('kotlin');
+ expect(result.matches.length).toBeGreaterThan(0);
+ });
+
+ it('should find Kotlin data classes', async () => {
+ if (!astGrepAvailable) {
+ return;
+ }
+
+ const result = await service.searchPattern('test-workspace', tempDir, {
+ language: 'kotlin',
+ pattern: 'data class $NAME($$$)',
+ });
+
+ expect(result.language).toBe('kotlin');
+ expect(result.matches.length).toBeGreaterThan(0);
+ });
+
+ it('should find Kotlin objects', async () => {
+ if (!astGrepAvailable) {
+ return;
+ }
+
+ const result = await service.searchPattern('test-workspace', tempDir, {
+ language: 'kotlin',
+ pattern: 'object $NAME { $$$ }',
+ });
+
+ expect(result.language).toBe('kotlin');
+ expect(Array.isArray(result.matches)).toBe(true);
+ });
+ });
+
+ describe('Scala Support', () => {
+ it('should search Scala files with patterns', async () => {
+ if (!astGrepAvailable) {
+ return;
+ }
+
+ const result = await service.searchPattern('test-workspace', tempDir, {
+ language: 'scala',
+ pattern: 'def $NAME($$$): $$$',
+ });
+
+ expect(result.language).toBe('scala');
+ expect(result.matches.length).toBeGreaterThan(0);
+ });
+
+ it('should find Scala case classes', async () => {
+ if (!astGrepAvailable) {
+ return;
+ }
+
+ const result = await service.searchPattern('test-workspace', tempDir, {
+ language: 'scala',
+ pattern: 'case class $NAME($$$)',
+ });
+
+ expect(result.language).toBe('scala');
+ expect(result.matches.length).toBeGreaterThan(0);
+ });
+
+ it('should find Scala traits', async () => {
+ if (!astGrepAvailable) {
+ return;
+ }
+
+ const result = await service.searchPattern('test-workspace', tempDir, {
+ language: 'scala',
+ pattern: 'trait $NAME { $$$ }',
+ });
+
+ expect(result.language).toBe('scala');
+ expect(Array.isArray(result.matches)).toBe(true);
+ });
+ });
+
+ describe('Swift Support', () => {
+ it('should search Swift files with patterns', async () => {
+ if (!astGrepAvailable) {
+ return;
+ }
+
+ const result = await service.searchPattern('test-workspace', tempDir, {
+ language: 'swift',
+ pattern: 'func $NAME($$$) -> $$$',
+ });
+
+ expect(result.language).toBe('swift');
+ expect(result.matches.length).toBeGreaterThan(0);
+ });
+
+ it('should find Swift structs', async () => {
+ if (!astGrepAvailable) {
+ return;
+ }
+
+ const result = await service.searchPattern('test-workspace', tempDir, {
+ language: 'swift',
+ pattern: 'struct $NAME { $$$ }',
+ });
+
+ expect(result.language).toBe('swift');
+ expect(result.matches.length).toBeGreaterThan(0);
+ });
+
+ it('should find Swift protocols', async () => {
+ if (!astGrepAvailable) {
+ return;
+ }
+
+ const result = await service.searchPattern('test-workspace', tempDir, {
+ language: 'swift',
+ pattern: 'protocol $NAME { $$$ }',
+ });
+
+ expect(result.language).toBe('swift');
+ expect(Array.isArray(result.matches)).toBe(true);
+ });
+ });
+
+ describe('Bash Support', () => {
+ it('should search Bash files with patterns', async () => {
+ if (!astGrepAvailable) {
+ return;
+ }
+
+ const result = await service.searchPattern('test-workspace', tempDir, {
+ language: 'bash',
+ pattern: 'echo $$$',
+ });
+
+ expect(result.language).toBe('bash');
+ expect(result.matches.length).toBeGreaterThan(0);
+ });
+
+ it('should find Bash conditionals', async () => {
+ if (!astGrepAvailable) {
+ return;
+ }
+
+ const result = await service.searchPattern('test-workspace', tempDir, {
+ language: 'bash',
+ pattern: 'if $$$',
+ });
+
+ expect(result.language).toBe('bash');
+ expect(result.matches.length).toBeGreaterThan(0);
+ });
+
+ it('should find Bash case statements', async () => {
+ if (!astGrepAvailable) {
+ return;
+ }
+
+ const result = await service.searchPattern('test-workspace', tempDir, {
+ language: 'bash',
+ pattern: 'case $$$',
+ });
+
+ expect(result.language).toBe('bash');
+ expect(Array.isArray(result.matches)).toBe(true);
+ });
+ });
+
+ describe('JSON Support', () => {
+ it('should search JSON files with patterns', async () => {
+ if (!astGrepAvailable) {
+ return;
+ }
+
+ const result = await service.searchPattern('test-workspace', tempDir, {
+ language: 'json',
+ pattern: '"name"',
+ });
+
+ expect(result.language).toBe('json');
+ expect(result.matches.length).toBeGreaterThan(0);
+ });
+
+ it('should find JSON object properties', async () => {
+ if (!astGrepAvailable) {
+ return;
+ }
+
+ const result = await service.searchPattern('test-workspace', tempDir, {
+ language: 'json',
+ pattern: '"dependencies"',
+ });
+
+ expect(result.language).toBe('json');
+ expect(result.matches.length).toBeGreaterThan(0);
+ });
+
+ it('should find JSON arrays', async () => {
+ if (!astGrepAvailable) {
+ return;
+ }
+
+ const result = await service.searchPattern('test-workspace', tempDir, {
+ language: 'json',
+ pattern: '"keywords": $ARRAY',
+ });
+
+ expect(result.language).toBe('json');
+ expect(Array.isArray(result.matches)).toBe(true);
+ });
+ });
+
+ describe('YAML Support', () => {
+ it('should search YAML files with patterns', async () => {
+ if (!astGrepAvailable) {
+ return;
+ }
+
+ const result = await service.searchPattern('test-workspace', tempDir, {
+ language: 'yaml',
+ pattern: 'name: $VALUE',
+ });
+
+ expect(result.language).toBe('yaml');
+ expect(result.matches.length).toBeGreaterThan(0);
+ });
+
+ it('should find YAML nested objects', async () => {
+ if (!astGrepAvailable) {
+ return;
+ }
+
+ const result = await service.searchPattern('test-workspace', tempDir, {
+ language: 'yaml',
+ pattern: 'author: $$$',
+ });
+
+ expect(result.language).toBe('yaml');
+ expect(result.matches.length).toBeGreaterThan(0);
+ });
+
+ it('should find YAML arrays', async () => {
+ if (!astGrepAvailable) {
+ return;
+ }
+
+ const result = await service.searchPattern('test-workspace', tempDir, {
+ language: 'yaml',
+ pattern: 'keywords: $$$',
+ });
+
+ expect(result.language).toBe('yaml');
+ expect(Array.isArray(result.matches)).toBe(true);
+ });
+ });
+
describe('Error Handling', () => {
it('should handle invalid patterns gracefully', async () => {
if (!astGrepAvailable) {
@@ -551,4 +1146,530 @@ const getUserStatus = (user: User): Status => {
}
}`;
await fs.writeFile(path.join(dir, 'large.ts'), largeClassContent, 'utf-8');
+
+ // Rust test file
+ const rustContent = `
+// Basic struct
+struct User {
+ name: String,
+ age: u32,
+}
+
+// Struct with lifetime
+struct Product<'a> {
+ id: u32,
+ title: &'a str,
+ price: f64,
+}
+
+// Regular function
+fn greet(name: &str) -> String {
+ format!("Hello, {}!", name)
+}
+
+// Function with multiple parameters
+fn add(a: i32, b: i32) -> i32 {
+ a + b
+}
+
+// Async function
+async fn fetch_data() -> Result> {
+ Ok("data".to_string())
+}
+
+// Implementation block
+impl User {
+ fn new(name: String, age: u32) -> Self {
+ User { name, age }
+ }
+
+ fn greet(&self) -> String {
+ format!("Hello, I'm {} and I'm {} years old", self.name, self.age)
+ }
+}
+
+// Generic function
+fn first(items: Vec) -> Option {
+ items.into_iter().next()
+}
+
+// Trait definition
+trait Printable {
+ fn print(&self);
+}
+
+// Trait implementation
+impl Printable for User {
+ fn print(&self) {
+ println!("{}: {}", self.name, self.age);
+ }
+}
+`;
+ await fs.writeFile(path.join(dir, 'test.rs'), rustContent, 'utf-8');
+
+ // Python test file
+ const pythonContent = `
+# Basic class
+class User:
+ def __init__(self, name: str, age: int):
+ self.name = name
+ self.age = age
+
+ def greet(self) -> str:
+ return f"Hello, I'm {self.name}"
+
+# Function with type hints
+def process_data(data: list[str]) -> dict[str, int]:
+ result = {}
+ for item in data:
+ result[item] = len(item)
+ return result
+
+# Async function
+async def fetch_user(user_id: int) -> User:
+ # Simulate async operation
+ await asyncio.sleep(0.1)
+ return User("Alice", 30)
+
+# Decorator
+@staticmethod
+def validate(value: str) -> bool:
+ return len(value) > 0
+`;
+ await fs.writeFile(path.join(dir, 'test.py'), pythonContent, 'utf-8');
+
+ // Go test file
+ const goContent = `
+package main
+
+import "fmt"
+
+// Basic struct
+type User struct {
+ Name string
+ Age int
+}
+
+// Method on struct
+func (u *User) Greet() string {
+ return fmt.Sprintf("Hello, I'm %s", u.Name)
+}
+
+// Function
+func processData(data []string) map[string]int {
+ result := make(map[string]int)
+ for _, item := range data {
+ result[item] = len(item)
+ }
+ return result
+}
+
+// Interface
+type Greeter interface {
+ Greet() string
+}
+`;
+ await fs.writeFile(path.join(dir, 'test.go'), goContent, 'utf-8');
+
+ // Java test file
+ const javaContent = `
+package com.example;
+
+import java.util.List;
+import java.util.Map;
+
+// Basic class
+public class User {
+ private String name;
+ private int age;
+
+ public User(String name, int age) {
+ this.name = name;
+ this.age = age;
+ }
+
+ public String greet() {
+ return String.format("Hello, I'm %s", this.name);
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+}
+
+// Interface
+interface Greeter {
+ String greet();
+}
+
+// Static method
+public class Utils {
+ public static boolean validate(String value) {
+ return value != null && !value.isEmpty();
+ }
+}
+`;
+ await fs.writeFile(path.join(dir, 'test.java'), javaContent, 'utf-8');
+
+ // C test file
+ const cContent = `
+#include
+#include
+
+// Struct definition
+struct User {
+ char name[50];
+ int age;
+};
+
+// Function declaration
+int add(int a, int b);
+
+// Function definition
+int add(int a, int b) {
+ return a + b;
+}
+
+// Function with struct
+void greet(struct User *user) {
+ printf("Hello, %s!\\n", user->name);
+}
+
+// Main function
+int main() {
+ struct User user = {"Alice", 30};
+ greet(&user);
+ return 0;
+}
+`;
+ await fs.writeFile(path.join(dir, 'test.c'), cContent, 'utf-8');
+
+ // C++ test file
+ const cppContent = `
+#include
+#include
+#include
+
+// Class definition
+class User {
+private:
+ std::string name;
+ int age;
+
+public:
+ User(std::string name, int age) : name(name), age(age) {}
+
+ std::string greet() {
+ return "Hello, I'm " + name;
+ }
+
+ std::string getName() const {
+ return name;
+ }
+};
+
+// Template function
+template
+T max(T a, T b) {
+ return (a > b) ? a : b;
+}
+
+// Namespace
+namespace utils {
+ bool validate(std::string value) {
+ return !value.empty();
+ }
+}
+
+// Main function
+int main() {
+ User user("Alice", 30);
+ std::cout << user.greet() << std::endl;
+ return 0;
+}
+`;
+ await fs.writeFile(path.join(dir, 'test.cpp'), cppContent, 'utf-8');
+
+ // C# test file
+ const csharpContent = `
+using System;
+using System.Collections.Generic;
+
+namespace Example
+{
+ // Class definition
+ public class User
+ {
+ private string name;
+ private int age;
+
+ public User(string name, int age)
+ {
+ this.name = name;
+ this.age = age;
+ }
+
+ public string Greet()
+ {
+ return $"Hello, I'm {name}";
+ }
+
+ public string Name
+ {
+ get { return name; }
+ set { name = value; }
+ }
+ }
+
+ // Interface
+ public interface IGreeter
+ {
+ string Greet();
+ }
+
+ // Static class
+ public static class Utils
+ {
+ public static bool Validate(string value)
+ {
+ return !string.IsNullOrEmpty(value);
+ }
+ }
+
+ // Program class with Main
+ class Program
+ {
+ static void Main(string[] args)
+ {
+ var user = new User("Alice", 30);
+ Console.WriteLine(user.Greet());
+ }
+ }
+}
+`;
+ await fs.writeFile(path.join(dir, 'test.cs'), csharpContent, 'utf-8');
+
+ // Kotlin test file
+ const kotlinContent = `
+package com.example
+
+// Data class
+data class User(val name: String, val age: Int) {
+ fun greet(): String {
+ return "Hello, I'm $name"
+ }
+}
+
+// Regular class
+class Product(val id: Int, val title: String) {
+ fun getDescription(): String {
+ return "$id: $title"
+ }
+}
+
+// Function
+fun processData(data: List): Map {
+ return data.associateWith { it.length }
+}
+
+// Extension function
+fun String.isValidEmail(): Boolean {
+ return this.contains("@")
+}
+
+// Interface
+interface Greeter {
+ fun greet(): String
+}
+
+// Object (singleton)
+object Utils {
+ fun validate(value: String): Boolean {
+ return value.isNotEmpty()
+ }
+}
+`;
+ await fs.writeFile(path.join(dir, 'test.kt'), kotlinContent, 'utf-8');
+
+ // Scala test file
+ const scalaContent = `
+package com.example
+
+// Case class
+case class User(name: String, age: Int) {
+ def greet(): String = s"Hello, I'm $name"
+}
+
+// Regular class
+class Product(val id: Int, val title: String) {
+ def getDescription: String = s"$id: $title"
+}
+
+// Object (singleton)
+object Utils {
+ def validate(value: String): Boolean = {
+ value.nonEmpty
+ }
+}
+
+// Trait (interface)
+trait Greeter {
+ def greet(): String
+}
+
+// Function
+def processData(data: List[String]): Map[String, Int] = {
+ data.map(s => s -> s.length).toMap
+}
+`;
+ await fs.writeFile(path.join(dir, 'test.scala'), scalaContent, 'utf-8');
+
+ // Swift test file
+ const swiftContent = `
+import Foundation
+
+// Struct
+struct User {
+ var name: String
+ var age: Int
+
+ func greet() -> String {
+ return "Hello, I'm \\(name)"
+ }
+}
+
+// Class
+class Product {
+ var id: Int
+ var title: String
+
+ init(id: Int, title: String) {
+ self.id = id
+ self.title = title
+ }
+
+ func getDescription() -> String {
+ return "\\(id): \\(title)"
+ }
+}
+
+// Protocol (interface)
+protocol Greeter {
+ func greet() -> String
+}
+
+// Extension
+extension String {
+ func isValidEmail() -> Bool {
+ return self.contains("@")
+ }
+}
+
+// Function
+func processData(_ data: [String]) -> [String: Int] {
+ return data.reduce(into: [:]) { $0[$1] = $1.count }
+}
+`;
+ await fs.writeFile(path.join(dir, 'test.swift'), swiftContent, 'utf-8');
+
+ // Bash test file
+ const bashContent = `#!/bin/bash
+
+# Function definition
+greet() {
+ local name=$1
+ echo "Hello, $name!"
+}
+
+# Function with return
+add() {
+ local a=$1
+ local b=$2
+ echo $((a + b))
+}
+
+# Conditional
+if [ "$USER" = "root" ]; then
+ echo "Running as root"
+else
+ echo "Running as regular user"
+fi
+
+# Loop
+for i in {1..5}; do
+ echo "Iteration $i"
+done
+
+# Case statement
+case "$1" in
+ start)
+ echo "Starting..."
+ ;;
+ stop)
+ echo "Stopping..."
+ ;;
+ *)
+ echo "Usage: $0 {start|stop}"
+ ;;
+esac
+
+# Call function
+greet "Alice"
+`;
+ await fs.writeFile(path.join(dir, 'test.sh'), bashContent, 'utf-8');
+
+ // JSON test file
+ const jsonContent = `{
+ "name": "test-project",
+ "version": "1.0.0",
+ "description": "A test project",
+ "author": {
+ "name": "Alice",
+ "email": "alice@example.com"
+ },
+ "dependencies": {
+ "express": "^4.18.0",
+ "lodash": "^4.17.21"
+ },
+ "scripts": {
+ "start": "node index.js",
+ "test": "jest"
+ },
+ "keywords": ["test", "example"],
+ "license": "MIT"
+}`;
+ await fs.writeFile(path.join(dir, 'test.json'), jsonContent, 'utf-8');
+
+ // YAML test file
+ const yamlContent = `name: test-project
+version: 1.0.0
+description: A test project
+
+author:
+ name: Alice
+ email: alice@example.com
+
+dependencies:
+ - express: ^4.18.0
+ - lodash: ^4.17.21
+
+scripts:
+ start: node index.js
+ test: jest
+
+keywords:
+ - test
+ - example
+
+license: MIT
+
+config:
+ port: 3000
+ debug: true
+`;
+ await fs.writeFile(path.join(dir, 'test.yaml'), yamlContent, 'utf-8');
}
\ No newline at end of file