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.* +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
LanguageFile 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