diff --git a/gitnexus-mcp/package-lock.json b/gitnexus-mcp/package-lock.json index 07821db63..5b2350221 100644 --- a/gitnexus-mcp/package-lock.json +++ b/gitnexus-mcp/package-lock.json @@ -9,6 +9,7 @@ "version": "0.2.0", "license": "MIT", "dependencies": { + "@huggingface/transformers": "^3.5.1", "@modelcontextprotocol/sdk": "^1.0.0", "kuzu": "^0.11.0", "minisearch": "^7.1.0", @@ -29,6 +30,16 @@ "node": ">=18.0.0" } }, + "node_modules/@emnapi/runtime": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.8.1.tgz", + "integrity": "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.27.2", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.2.tgz", @@ -483,6 +494,513 @@ "hono": "^4" } }, + "node_modules/@huggingface/jinja": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/@huggingface/jinja/-/jinja-0.5.5.tgz", + "integrity": "sha512-xRlzazC+QZwr6z4ixEqYHo9fgwhTZ3xNSdljlKfUFGZSdlvt166DljRELFUfFytlYOYvo3vTisA/AFOuOAzFQQ==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@huggingface/transformers": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@huggingface/transformers/-/transformers-3.8.1.tgz", + "integrity": "sha512-tsTk4zVjImqdqjS8/AOZg2yNLd1z9S5v+7oUPpXaasDRwEDhB+xnglK1k5cad26lL5/ZIaeREgWWy0bs9y9pPA==", + "license": "Apache-2.0", + "dependencies": { + "@huggingface/jinja": "^0.5.3", + "onnxruntime-node": "1.21.0", + "onnxruntime-web": "1.22.0-dev.20250409-89f8206ba4", + "sharp": "^0.34.1" + } + }, + "node_modules/@img/colour": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz", + "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", + "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", + "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", + "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", + "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", + "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", + "cpu": [ + "arm" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", + "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", + "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", + "cpu": [ + "ppc64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", + "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", + "cpu": [ + "riscv64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", + "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", + "cpu": [ + "s390x" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", + "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", + "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", + "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", + "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", + "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", + "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", + "cpu": [ + "ppc64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-riscv64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", + "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", + "cpu": [ + "riscv64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", + "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", + "cpu": [ + "s390x" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", + "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", + "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", + "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", + "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.7.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", + "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", + "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", + "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@isaacs/fs-minipass/node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/@modelcontextprotocol/sdk": { "version": "1.25.2", "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.25.2.tgz", @@ -522,11 +1040,74 @@ } } }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", + "license": "BSD-3-Clause" + }, "node_modules/@types/node": { "version": "20.19.30", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.30.tgz", "integrity": "sha512-WJtwWJu7UdlvzEAUm484QNg5eAoq5QR08KDNx7g45Usrs2NtOPiX8ugDqmKdXkyL03rBqU5dYNYVQetEpBHq2g==", - "dev": true, "license": "MIT", "dependencies": { "undici-types": "~6.21.0" @@ -680,6 +1261,13 @@ "url": "https://opencollective.com/express" } }, + "node_modules/boolean": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz", + "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "license": "MIT" + }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -905,6 +1493,40 @@ "node": ">=4.0.0" } }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -929,6 +1551,21 @@ "node": ">= 0.8" } }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "license": "MIT" + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -1009,6 +1646,12 @@ "node": ">= 0.4" } }, + "node_modules/es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "license": "MIT" + }, "node_modules/esbuild": { "version": "0.27.2", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.2.tgz", @@ -1066,6 +1709,18 @@ "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", "license": "MIT" }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", @@ -1197,6 +1852,12 @@ "url": "https://opencollective.com/express" } }, + "node_modules/flatbuffers": { + "version": "25.9.23", + "resolved": "https://registry.npmjs.org/flatbuffers/-/flatbuffers-25.9.23.tgz", + "integrity": "sha512-MI1qs7Lo4Syw0EOzUl0xjs2lsoeqFku44KpngfIduHBYvzm8h2+7K8YMQh1JtVVVrUvhLpNwqVi4DERegUJhPQ==", + "license": "Apache-2.0" + }, "node_modules/follow-redirects": { "version": "1.15.11", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", @@ -1413,6 +2074,39 @@ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" } }, + "node_modules/global-agent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz", + "integrity": "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==", + "license": "BSD-3-Clause", + "dependencies": { + "boolean": "^3.0.1", + "es6-error": "^4.1.1", + "matcher": "^3.0.0", + "roarr": "^2.15.3", + "semver": "^7.3.2", + "serialize-error": "^7.0.1" + }, + "engines": { + "node": ">=10.0" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "license": "MIT", + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/gopd": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", @@ -1431,6 +2125,24 @@ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "license": "ISC" }, + "node_modules/guid-typescript": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/guid-typescript/-/guid-typescript-1.0.9.tgz", + "integrity": "sha512-Y8T4vYhEfwJOTbouREvG+3XDsjr8E3kIr7uf+JZ0BYloFsttiHU0WfvANVsR7TxNUJa/WpCnw/Ino/p+DeBhBQ==", + "license": "ISC" + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-symbols": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", @@ -1585,6 +2297,12 @@ "integrity": "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==", "license": "BSD-2-Clause" }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "license": "ISC" + }, "node_modules/jsonfile": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", @@ -1609,6 +2327,24 @@ "node-addon-api": "^6.0.0" } }, + "node_modules/long": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", + "license": "Apache-2.0" + }, + "node_modules/matcher": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz", + "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==", + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -1798,6 +2534,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/on-finished": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", @@ -1819,6 +2564,104 @@ "wrappy": "1" } }, + "node_modules/onnxruntime-common": { + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/onnxruntime-common/-/onnxruntime-common-1.21.0.tgz", + "integrity": "sha512-Q632iLLrtCAVOTO65dh2+mNbQir/QNTVBG3h/QdZBpns7mZ0RYbLRBgGABPbpU9351AgYy7SJf1WaeVwMrBFPQ==", + "license": "MIT" + }, + "node_modules/onnxruntime-node": { + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/onnxruntime-node/-/onnxruntime-node-1.21.0.tgz", + "integrity": "sha512-NeaCX6WW2L8cRCSqy3bInlo5ojjQqu2fD3D+9W5qb5irwxhEyWKXeH2vZ8W9r6VxaMPUan+4/7NDwZMtouZxEw==", + "hasInstallScript": true, + "license": "MIT", + "os": [ + "win32", + "darwin", + "linux" + ], + "dependencies": { + "global-agent": "^3.0.0", + "onnxruntime-common": "1.21.0", + "tar": "^7.0.1" + } + }, + "node_modules/onnxruntime-node/node_modules/chownr": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/onnxruntime-node/node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/onnxruntime-node/node_modules/minizlib": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", + "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", + "license": "MIT", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/onnxruntime-node/node_modules/tar": { + "version": "7.5.7", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.7.tgz", + "integrity": "sha512-fov56fJiRuThVFXD6o6/Q354S7pnWMJIVlDBYijsTNx6jKSE4pvrDTs6lUnmGvNyfJwFQQwWy3owKz1ucIhveQ==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.1.0", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/onnxruntime-node/node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/onnxruntime-web": { + "version": "1.22.0-dev.20250409-89f8206ba4", + "resolved": "https://registry.npmjs.org/onnxruntime-web/-/onnxruntime-web-1.22.0-dev.20250409-89f8206ba4.tgz", + "integrity": "sha512-0uS76OPgH0hWCPrFKlL8kYVV7ckM7t/36HfbgoFw6Nd0CZVVbQC4PkrR8mBX8LtNUFZO25IQBqV2Hx2ho3FlbQ==", + "license": "MIT", + "dependencies": { + "flatbuffers": "^25.1.24", + "guid-typescript": "^1.0.9", + "long": "^5.2.3", + "onnxruntime-common": "1.22.0-dev.20250409-89f8206ba4", + "platform": "^1.3.6", + "protobufjs": "^7.2.4" + } + }, + "node_modules/onnxruntime-web/node_modules/onnxruntime-common": { + "version": "1.22.0-dev.20250409-89f8206ba4", + "resolved": "https://registry.npmjs.org/onnxruntime-common/-/onnxruntime-common-1.22.0-dev.20250409-89f8206ba4.tgz", + "integrity": "sha512-vDJMkfCfb0b1A836rgHj+ORuZf4B4+cc2bASQtpeoJLueuFc5DuYwjIZUBrSvx/fO5IrLjLz+oTrB3pcGlhovQ==", + "license": "MIT" + }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -1856,6 +2699,36 @@ "node": ">=16.20.0" } }, + "node_modules/platform": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/platform/-/platform-1.3.6.tgz", + "integrity": "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==", + "license": "MIT" + }, + "node_modules/protobufjs": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.4.tgz", + "integrity": "sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==", + "hasInstallScript": true, + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -1971,6 +2844,23 @@ "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } }, + "node_modules/roarr": { + "version": "2.15.4", + "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz", + "integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==", + "license": "BSD-3-Clause", + "dependencies": { + "boolean": "^3.0.1", + "detect-node": "^2.0.4", + "globalthis": "^1.0.1", + "json-stringify-safe": "^5.0.1", + "semver-compare": "^1.0.0", + "sprintf-js": "^1.1.2" + }, + "engines": { + "node": ">=8.0" + } + }, "node_modules/router": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", @@ -2025,6 +2915,12 @@ "node": ">=10" } }, + "node_modules/semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==", + "license": "MIT" + }, "node_modules/send": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", @@ -2051,6 +2947,21 @@ "url": "https://opencollective.com/express" } }, + "node_modules/serialize-error": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", + "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", + "license": "MIT", + "dependencies": { + "type-fest": "^0.13.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/serve-static": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", @@ -2082,6 +2993,50 @@ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", "license": "ISC" }, + "node_modules/sharp": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", + "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@img/colour": "^1.0.0", + "detect-libc": "^2.1.2", + "semver": "^7.7.3" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.34.5", + "@img/sharp-darwin-x64": "0.34.5", + "@img/sharp-libvips-darwin-arm64": "1.2.4", + "@img/sharp-libvips-darwin-x64": "1.2.4", + "@img/sharp-libvips-linux-arm": "1.2.4", + "@img/sharp-libvips-linux-arm64": "1.2.4", + "@img/sharp-libvips-linux-ppc64": "1.2.4", + "@img/sharp-libvips-linux-riscv64": "1.2.4", + "@img/sharp-libvips-linux-s390x": "1.2.4", + "@img/sharp-libvips-linux-x64": "1.2.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", + "@img/sharp-libvips-linuxmusl-x64": "1.2.4", + "@img/sharp-linux-arm": "0.34.5", + "@img/sharp-linux-arm64": "0.34.5", + "@img/sharp-linux-ppc64": "0.34.5", + "@img/sharp-linux-riscv64": "0.34.5", + "@img/sharp-linux-s390x": "0.34.5", + "@img/sharp-linux-x64": "0.34.5", + "@img/sharp-linuxmusl-arm64": "0.34.5", + "@img/sharp-linuxmusl-x64": "0.34.5", + "@img/sharp-wasm32": "0.34.5", + "@img/sharp-win32-arm64": "0.34.5", + "@img/sharp-win32-ia32": "0.34.5", + "@img/sharp-win32-x64": "0.34.5" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -2181,6 +3136,12 @@ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "license": "ISC" }, + "node_modules/sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "license": "BSD-3-Clause" + }, "node_modules/statuses": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", @@ -2261,6 +3222,13 @@ "node": ">=0.6" } }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD", + "optional": true + }, "node_modules/tsx": { "version": "4.21.0", "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz", @@ -2281,6 +3249,18 @@ "fsevents": "~2.3.3" } }, + "node_modules/type-fest": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", + "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/type-is": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", @@ -2313,7 +3293,6 @@ "version": "6.21.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "dev": true, "license": "MIT" }, "node_modules/universalify": { diff --git a/gitnexus-mcp/package.json b/gitnexus-mcp/package.json index 782cfc19e..4bf1eb250 100644 --- a/gitnexus-mcp/package.json +++ b/gitnexus-mcp/package.json @@ -30,6 +30,7 @@ "prepublishOnly": "npm run build" }, "dependencies": { + "@huggingface/transformers": "^3.5.1", "@modelcontextprotocol/sdk": "^1.0.0", "kuzu": "^0.11.0", "minisearch": "^7.1.0", diff --git a/gitnexus-mcp/src/bridge/protocol.ts b/gitnexus-mcp/src/bridge/protocol.ts deleted file mode 100644 index 1d0d63fe4..000000000 --- a/gitnexus-mcp/src/bridge/protocol.ts +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Bridge Protocol Types - * - * JSON-RPC-like protocol for communication between bridge and browser. - */ - -export interface BridgeMessage { - id: string; - type?: 'register_peer' | 'tool_call' | 'tool_result' | 'agent_info' | 'handshake' | 'handshake_ack' | 'context'; - method?: string; - params?: any; - result?: any; - error?: { - code?: number; - message: string; - }; - agentName?: string; - peerId?: string; -} - -export type ToolCallRequest = BridgeMessage & { method: string }; -export type ToolCallResponse = BridgeMessage & ({ result: any } | { error: any }); - -/** - * Check if message is a request (has method) - */ -export function isRequest(msg: BridgeMessage): msg is ToolCallRequest { - return typeof msg.method === 'string'; -} - -/** - * Check if message is a response (has result or error) - */ -export function isResponse(msg: BridgeMessage): msg is ToolCallResponse { - return 'result' in msg || 'error' in msg; -} diff --git a/gitnexus-mcp/src/bridge/websocket-server.ts b/gitnexus-mcp/src/bridge/websocket-server.ts deleted file mode 100644 index 450414941..000000000 --- a/gitnexus-mcp/src/bridge/websocket-server.ts +++ /dev/null @@ -1,397 +0,0 @@ -import { WebSocketServer, WebSocket } from 'ws'; -import { createServer as createNetServer } from 'net'; -import { BridgeMessage, isRequest, isResponse } from './protocol.js'; -import { v4 as uuidv4 } from 'uuid'; - -/** - * Codebase context sent from the GitNexus browser app - */ -export interface CodebaseContext { - projectName: string; - stats: { - fileCount: number; - functionCount: number; - classCount: number; - interfaceCount: number; - methodCount: number; - }; - hotspots: Array<{ - name: string; - type: string; - filePath: string; - connections: number; - }>; - folderTree: string; -} - -/** - * Check if a Port is available - */ -async function isPortAvailable(port: number): Promise { - return new Promise((resolve) => { - const server = createNetServer(); - server.once('error', () => resolve(false)); - server.once('listening', () => { - server.close(); - resolve(true); - }); - server.listen(port); - }); -} - -export class WebSocketBridge { - private wss: WebSocketServer | null = null; // Used if we are the Hub - private client: WebSocket | null = null; // Used if we are a Peer (connecting to Hub), OR if we are Hub (clients connecting to us) - - // Hub State - private browserClient: WebSocket | null = null; - private peerClients: Map = new Map(); - - // Common State - private pendingRequests: Map void, reject: (err: any) => void }> = new Map(); - private requestId = 0; - private started = false; - private _context: any | null = null; // CodebaseContext - private contextListeners: Set<(context: any | null) => void> = new Set(); - private agentName: string; - private isHub = false; - private port = 54319; - - constructor(port: number = 54319, agentName?: string) { - this.port = port; - this.agentName = agentName || process.env.GITNEXUS_AGENT || this.detectAgent(); - } - - private detectAgent(): string { - if (process.env.CURSOR_SESSION_ID) return 'Cursor'; - if (process.env.CLAUDE_CODE) return 'Claude Code'; - if (process.env.WINDSURF_SESSION) return 'Windsurf'; - return 'Unknown Agent'; - } - - async start(): Promise { - const available = await isPortAvailable(this.port); - - if (available) { - return this.startAsHub(); - } else { - return this.startAsPeer(); - } - } - - // ------------------------------------------------------------------------- - // Hub Implementation (Master) - // ------------------------------------------------------------------------- - - private async startAsHub(): Promise { - console.error(`Starting as MCP Hub on port ${this.port}`); - this.isHub = true; - - return new Promise((resolve) => { - this.wss = new WebSocketServer({ port: this.port }); - - this.wss.on('connection', (ws, req) => { - // Security: Origin check could go here if req.headers.origin available - - ws.on('message', (data) => this.handleHubMessage(ws, data)); - ws.on('close', () => this.handleHubDisconnect(ws)); - ws.on('error', (err) => console.error('Hub client error:', err)); - }); - - this.wss.on('listening', () => { - this.started = true; - resolve(true); - }); - - this.wss.on('error', (err) => { - console.error('Hub server error:', err); - resolve(false); - }); - }); - } - - private handleHubMessage(ws: WebSocket, data: any) { - try { - const msg: BridgeMessage = JSON.parse(data.toString()); - - if (msg.type === 'handshake') { - // Peer verifying we are GitNexus - ws.send(JSON.stringify({ type: 'handshake_ack', id: msg.id })); - return; - } - - if (msg.type === 'register_peer') { - // Peer registering itself - const peerId = uuidv4(); - this.peerClients.set(peerId, ws); - (ws as any).peerId = peerId; - (ws as any).agentName = msg.agentName; - console.error(`Peer connected: ${msg.agentName} (${peerId})`); - - // Forward current context to new peer if available - if (this._context) { - ws.send(JSON.stringify({ type: 'context', params: this._context })); - } - return; - } - - // Handle Context updates (from Browser) - if (msg.type === 'context') { - // Browser identified itself (implicitly) - if (this.browserClient !== ws) { - if (this.browserClient) this.browserClient.close(); - this.browserClient = ws; - console.error('Browser connected to Hub'); - } - - this._context = msg.params; - this.notifyContextListeners(); - - // Broadcast context to all peers - this.broadcastToPeers(msg); - return; - } - - // Handle Tool Calls (Peer/Hub -> Browser) - if (isRequest(msg)) { - // If it came from a ws client (Peer), validation needed? - // We assume it's destined for the Browser - if (this.browserClient && this.browserClient.readyState === WebSocket.OPEN) { - // Attach agent info if missing (for UI) - if (!msg.agentName && (ws as any).agentName) { - msg.agentName = (ws as any).agentName; - } - // Attach peerId so we can route response back - if (!msg.peerId && (ws as any).peerId) { - msg.peerId = (ws as any).peerId; - } - - this.browserClient.send(JSON.stringify(msg)); - } else { - // Browser not connected, fail - if (msg.id) { - ws.send(JSON.stringify({ - id: msg.id, - error: { message: "Browser not connected. Open GitNexus." } - })); - } - } - return; - } - - // Handle Tool Results (Browser -> Peer/Hub) - if (isResponse(msg)) { - // Route to the correct peer - if (msg.peerId && this.peerClients.has(msg.peerId)) { - const peer = this.peerClients.get(msg.peerId); - if (peer?.readyState === WebSocket.OPEN) { - peer.send(JSON.stringify(msg)); - } - } else { - // It might be for Us (the Hub) - this.handleResponseLocal(msg); - } - return; - } - - } catch (e) { - console.error('Hub: Failed to parse message', e); - } - } - - private handleHubDisconnect(ws: WebSocket) { - if (ws === this.browserClient) { - console.error('Browser disconnected from Hub'); - this.browserClient = null; - this._context = null; - this.notifyContextListeners(); - } else { - const peerId = (ws as any).peerId; - if (peerId) { - this.peerClients.delete(peerId); - console.error(`Peer disconnected: ${peerId}`); - } - } - } - - private broadcastToPeers(msg: any) { - for (const client of this.peerClients.values()) { - if (client.readyState === WebSocket.OPEN) { - client.send(JSON.stringify(msg)); - } - } - } - - // ------------------------------------------------------------------------- - // Peer Implementation (Spoke) - // ------------------------------------------------------------------------- - - private async startAsPeer(): Promise { - console.error(`Port ${this.port} busy. Attempting to connect as Peer...`); - - return new Promise((resolve) => { - const ws = new WebSocket(`ws://localhost:${this.port}`); - - const timeout = setTimeout(() => { - console.error('Handshake timeout. Port is busy by unknown app.'); - ws.close(); - resolve(false); - }, 1000); - - ws.on('open', () => { - // Send Handshake - ws.send(JSON.stringify({ type: 'handshake', id: 'init' })); - }); - - ws.on('message', (data) => { - try { - const msg = JSON.parse(data.toString()); - - // Handshake success? - if (msg.type === 'handshake_ack') { - clearTimeout(timeout); - console.error('Handshake successful. Joining as Peer.'); - - // Register ourselves - ws.send(JSON.stringify({ - type: 'register_peer', - agentName: this.agentName - })); - - this.client = ws; - this.started = true; - resolve(true); - return; - } - - // Normal messages from Hub - this.handlePeerMessage(msg); - - } catch (e) { - // ignore garbage - } - }); - - ws.on('error', (err) => { - console.error('Peer connection error:', err); - resolve(false); - }); - - // If connection fails immediately - ws.on('close', () => { - if (!this.started) resolve(false); - else { - this.client = null; - this._context = null; - this.notifyContextListeners(); - } - }); - }); - } - - private handlePeerMessage(msg: BridgeMessage) { - if (msg.type === 'context') { - this._context = msg.params; - this.notifyContextListeners(); - return; - } - - if (isResponse(msg)) { - this.handleResponseLocal(msg); - } - } - - // ------------------------------------------------------------------------- - // Shared / Public API - // ------------------------------------------------------------------------- - - private handleResponseLocal(msg: any) { - if (msg.id && this.pendingRequests.has(msg.id)) { - const { resolve, reject } = this.pendingRequests.get(msg.id)!; - this.pendingRequests.delete(msg.id); - - if (msg.error) { - // We'll reject the promise so caller knows - reject(new Error(msg.error.message)); - } else { - resolve(msg.result); - } - } - } - - get isConnected(): boolean { - if (this.isHub) { - return this.browserClient !== null && this.browserClient.readyState === WebSocket.OPEN; - } else { - return this.client !== null && this.client.readyState === WebSocket.OPEN; - } - } - - get context(): any { - return this._context; - } - - onContextChange(listener: (context: any) => void) { - this.contextListeners.add(listener); - return () => this.contextListeners.delete(listener); - } - - private notifyContextListeners() { - this.contextListeners.forEach((listener) => listener(this._context)); - } - - async callTool(method: string, params: any): Promise { - if (!this.isConnected) { - if (this.isHub) throw new Error('GitNexus Browser not connected.'); - else throw new Error('GitNexus Hub disonnected.'); - } - - const id = `req_${++this.requestId}`; - - return new Promise((resolve, reject) => { - this.pendingRequests.set(id, { resolve, reject }); - - const msg: BridgeMessage = { - id, - method, - params, - agentName: this.agentName, - // type is implicitly request because of method - }; - - if (this.isHub) { - // Send directly to browser - if (this.browserClient && this.browserClient.readyState === WebSocket.OPEN) { - this.browserClient.send(JSON.stringify(msg)); - } else { - this.pendingRequests.delete(id); - reject(new Error('Browser not connected')); - } - } else { - // Send to Hub (who forwards to browser) - if (this.client && this.client.readyState === WebSocket.OPEN) { - this.client.send(JSON.stringify(msg)); - } else { - this.pendingRequests.delete(id); - reject(new Error('Hub disconnected')); - } - } - - setTimeout(() => { - if (this.pendingRequests.has(id)) { - this.pendingRequests.delete(id); - reject(new Error('Request timeout')); - } - }, 30000); - }); - } - - close() { - this.wss?.close(); - this.client?.close(); - } - - disconnect() { - this.close(); - } -} diff --git a/gitnexus-mcp/src/commands/serve.ts b/gitnexus-mcp/src/commands/serve.ts index 97e13fc78..bfde486e5 100644 --- a/gitnexus-mcp/src/commands/serve.ts +++ b/gitnexus-mcp/src/commands/serve.ts @@ -1,45 +1,102 @@ /** * Serve Command * - * Starts the MCP server with hybrid mode: - * 1. First tries local .gitnexus/ index (standalone mode) - * 2. Falls back to WebSocket bridge if browser is running + * Starts the MCP server in standalone mode using local .gitnexus/ index. + * + * Auto-detects repository by trying (in order): + * 1. GITNEXUS_CWD env var (explicit override) + * 2. process.cwd() (IDE working directory) + * 3. VSCODE_WORKSPACE_FOLDER env var */ import { startMCPServer } from '../mcp/server.js'; -import { WebSocketBridge } from '../bridge/websocket-server.js'; -import { LocalBackend } from '../local/local-backend.js'; +import { LocalBackend, findRepo } from '../local/local-backend.js'; +import path from 'path'; +import fs from 'fs/promises'; interface ServeOptions { port: string; } -export async function serveCommand(options: ServeOptions) { - const port = parseInt(options.port, 10); - // Use GITNEXUS_CWD env var if set, otherwise use process.cwd() - const cwd = process.env.GITNEXUS_CWD || process.cwd(); +/** + * Get candidate paths to search for .gitnexus/ folder + */ +function getCandidatePaths(): string[] { + const candidates: string[] = []; - // Try local backend first (standalone mode) - const local = new LocalBackend(); - const hasLocalIndex = await local.init(cwd); - - if (hasLocalIndex) { - console.error(`GitNexus: Using local index at ${local.storagePath}`); - await startMCPServer(local); - return; + // 1. Explicit override (highest priority) + if (process.env.GITNEXUS_CWD) { + candidates.push(process.env.GITNEXUS_CWD); } - // No local index - fall back to browser bridge - console.error('GitNexus: No local .gitnexus/ found, starting browser bridge...'); + // 2. Current working directory + candidates.push(process.cwd()); - const bridge = new WebSocketBridge(port); - const started = await bridge.start(); - - if (!started) { - console.error(`Failed to start GitNexus browser bridge on port ${port}.`); - console.error('Run "gitnexus analyze" to index this repository for standalone mode.'); - process.exit(1); + // 3. VS Code workspace folders (if available via env) + if (process.env.VSCODE_WORKSPACE_FOLDER) { + candidates.push(process.env.VSCODE_WORKSPACE_FOLDER); } - await startMCPServer(bridge); + // Deduplicate while preserving order + return [...new Set(candidates.map(p => path.resolve(p)))]; +} + +/** + * Find a git repository root by walking up the directory tree + */ +async function findGitRoot(startPath: string): Promise { + let current = path.resolve(startPath); + const root = path.parse(current).root; + + while (current !== root) { + try { + const gitPath = path.join(current, '.git'); + const stat = await fs.stat(gitPath); + if (stat.isDirectory()) return current; + } catch {} + current = path.dirname(current); + } + return null; +} + +export async function serveCommand(_options: ServeOptions) { + // Try multiple candidate paths to find .gitnexus/ + const candidates = getCandidatePaths(); + + for (const candidate of candidates) { + const repo = await findRepo(candidate); + if (repo) { + const local = new LocalBackend(); + await local.init(candidate); + console.error(`GitNexus: Found index at ${repo.storagePath}`); + await startMCPServer(local); + return; + } + } + + // No index found - give helpful error message + for (const candidate of candidates) { + const gitRoot = await findGitRoot(candidate); + if (gitRoot) { + console.error(''); + console.error('╔════════════════════════════════════════════════════╗'); + console.error('║ GitNexus: Repository Not Indexed ║'); + console.error('╠════════════════════════════════════════════════════╣'); + console.error(`║ Found git repo: ${gitRoot.slice(0, 35).padEnd(35)} ║`); + console.error('║ ║'); + console.error('║ To enable AI code understanding, run: ║'); + console.error('║ ║'); + console.error('║ npx gitnexus-cli analyze ║'); + console.error('║ ║'); + console.error('║ Then restart your IDE. ║'); + console.error('╚════════════════════════════════════════════════════╝'); + console.error(''); + process.exit(1); + } + } + + // No git repo found + console.error('GitNexus: No git repository found.'); + console.error(`Searched: ${candidates.join(', ')}`); + process.exit(1); } diff --git a/gitnexus-mcp/src/core/embedder.ts b/gitnexus-mcp/src/core/embedder.ts new file mode 100644 index 000000000..2979ddc53 --- /dev/null +++ b/gitnexus-mcp/src/core/embedder.ts @@ -0,0 +1,110 @@ +/** + * Embedder Module (Read-Only) + * + * Singleton factory for transformers.js embedding pipeline. + * For MCP, we only need to compute query embeddings, not batch embed. + */ + +import { pipeline, env, type FeatureExtractionPipeline } from '@huggingface/transformers'; + +// Model config +const MODEL_ID = 'Snowflake/snowflake-arctic-embed-xs'; +const EMBEDDING_DIMS = 384; + +// Module-level state for singleton pattern +let embedderInstance: FeatureExtractionPipeline | null = null; +let isInitializing = false; +let initPromise: Promise | null = null; + +/** + * Initialize the embedding model (lazy, on first search) + */ +export const initEmbedder = async (): Promise => { + if (embedderInstance) { + return embedderInstance; + } + + if (isInitializing && initPromise) { + return initPromise; + } + + isInitializing = true; + + initPromise = (async () => { + try { + env.allowLocalModels = false; + + console.error('GitNexus: Loading embedding model (first search may take a moment)...'); + + // Try WebGPU first (Windows DirectX12), fall back to CPU + const devicesToTry: Array<'webgpu' | 'cpu'> = ['webgpu', 'cpu']; + + for (const device of devicesToTry) { + try { + embedderInstance = await (pipeline as any)( + 'feature-extraction', + MODEL_ID, + { + device: device, + dtype: 'fp32', + } + ); + console.error(`GitNexus: Embedding model loaded (${device})`); + return embedderInstance!; + } catch { + if (device === 'cpu') throw new Error('Failed to load embedding model'); + } + } + + throw new Error('No suitable device found'); + } catch (error) { + isInitializing = false; + initPromise = null; + embedderInstance = null; + throw error; + } finally { + isInitializing = false; + } + })(); + + return initPromise; +}; + +/** + * Check if embedder is ready + */ +export const isEmbedderReady = (): boolean => embedderInstance !== null; + +/** + * Embed a query text for semantic search + */ +export const embedQuery = async (query: string): Promise => { + const embedder = await initEmbedder(); + + const result = await embedder(query, { + pooling: 'mean', + normalize: true, + }); + + return Array.from(result.data as ArrayLike); +}; + +/** + * Get embedding dimensions + */ +export const getEmbeddingDims = (): number => EMBEDDING_DIMS; + +/** + * Cleanup embedder + */ +export const disposeEmbedder = async (): Promise => { + if (embedderInstance) { + try { + if ('dispose' in embedderInstance && typeof embedderInstance.dispose === 'function') { + await embedderInstance.dispose(); + } + } catch {} + embedderInstance = null; + initPromise = null; + } +}; diff --git a/gitnexus-mcp/src/local/local-backend.ts b/gitnexus-mcp/src/local/local-backend.ts index 0aa191dac..4bcd13bcf 100644 --- a/gitnexus-mcp/src/local/local-backend.ts +++ b/gitnexus-mcp/src/local/local-backend.ts @@ -9,6 +9,7 @@ import fs from 'fs/promises'; import path from 'path'; import { initKuzu, executeQuery, closeKuzu, isKuzuReady } from '../core/kuzu-adapter.js'; import { loadBM25Index, searchBM25, isBM25Ready } from '../core/bm25-index.js'; +import { embedQuery, getEmbeddingDims, disposeEmbedder } from '../core/embedder.js'; export interface RepoMeta { repoPath: string; @@ -46,7 +47,18 @@ function getStoragePaths(repoPath: string) { async function loadMeta(storagePath: string): Promise { try { + // Verify both meta.json and kuzu exist for a valid index const metaPath = path.join(storagePath, 'meta.json'); + const kuzuPath = path.join(storagePath, 'kuzu'); + + // Check kuzu exists (can be file or directory depending on how it was saved) + try { + await fs.stat(kuzuPath); + } catch { + return null; // kuzu doesn't exist + } + + // Load and parse meta.json const raw = await fs.readFile(metaPath, 'utf-8'); return JSON.parse(raw) as RepoMeta; } catch { @@ -205,88 +217,204 @@ export class LocalBackend { ].join('\n'); } - private async search(params: { query: string; limit?: number; depth?: string }): Promise { + private async search(params: { query: string; limit?: number; depth?: string; groupByProcess?: boolean }): Promise { await this.ensureInitialized(); const limit = params.limit || 10; const query = params.query; const depth = params.depth || 'definitions'; - // BM25 keyword search - const bm25Results = isBM25Ready() ? searchBM25(query, limit * 2) : []; + // Run BM25 and semantic search in parallel + const [bm25Results, semanticResults] = await Promise.all([ + this.bm25Search(query, limit * 2), + this.semanticSearch(query, limit * 2), + ]); - if (bm25Results.length === 0) { - return { message: 'No results found', query, bm25Ready: isBM25Ready() }; + // Merge and deduplicate results using reciprocal rank fusion + const scoreMap = new Map(); + + // BM25 results + for (let i = 0; i < bm25Results.length; i++) { + const result = bm25Results[i]; + const key = result.filePath; + const rrfScore = 1 / (60 + i); // RRF formula with k=60 + const existing = scoreMap.get(key); + if (existing) { + existing.score += rrfScore; + existing.source = 'hybrid'; + } else { + scoreMap.set(key, { score: rrfScore, source: 'bm25', data: result }); + } } - // Get node details from kuzu for top results + // Semantic results + for (let i = 0; i < semanticResults.length; i++) { + const result = semanticResults[i]; + const key = result.filePath; + const rrfScore = 1 / (60 + i); + const existing = scoreMap.get(key); + if (existing) { + existing.score += rrfScore; + existing.source = 'hybrid'; + } else { + scoreMap.set(key, { score: rrfScore, source: 'semantic', data: result }); + } + } + + // Sort by fused score and take top results + const merged = Array.from(scoreMap.entries()) + .sort((a, b) => b[1].score - a[1].score) + .slice(0, limit); + + // Enrich with graph data const results: any[] = []; - for (const bm25Result of bm25Results.slice(0, limit)) { + for (const [_, item] of merged) { + const result = item.data; + result.searchSource = item.source; + result.fusedScore = item.score; + + // Add relationships if depth is 'full' and we have a node ID + if (depth === 'full' && result.nodeId) { + try { + const relQuery = ` + MATCH (n {id: '${result.nodeId.replace(/'/g, "''")}'})-[r:CodeRelation]->(m) + RETURN r.type AS type, m.name AS targetName, m.filePath AS targetPath + LIMIT 5 + `; + const rels = await executeQuery(relQuery); + result.connections = rels.map((rel: any) => ({ + type: rel.type || rel[0], + name: rel.targetName || rel[1], + path: rel.targetPath || rel[2], + })); + } catch { + result.connections = []; + } + } + + results.push(result); + } + + return results; + } + + /** + * BM25 keyword search helper + */ + private async bm25Search(query: string, limit: number): Promise { + if (!isBM25Ready()) return []; + + const bm25Results = searchBM25(query, limit); + const results: any[] = []; + + for (const bm25Result of bm25Results) { + const fileName = bm25Result.filePath.split('/').pop() || bm25Result.filePath; try { - // Use CONTAINS to match file paths (handles relative vs full paths) - const fileName = bm25Result.filePath.split('/').pop() || bm25Result.filePath; const symbolQuery = ` MATCH (n) WHERE n.filePath CONTAINS '${fileName.replace(/'/g, "''")}' RETURN n.id AS id, n.name AS name, labels(n)[0] AS type, n.filePath AS filePath, n.startLine AS startLine, n.endLine AS endLine - LIMIT 5 + LIMIT 3 `; const symbols = await executeQuery(symbolQuery); if (symbols.length > 0) { for (const sym of symbols) { - const result: any = { + results.push({ + nodeId: sym.id || sym[0], name: sym.name || sym[1], type: sym.type || sym[2], filePath: sym.filePath || sym[3], startLine: sym.startLine || sym[4], endLine: sym.endLine || sym[5], - score: bm25Result.score, - }; - - // Add relationships if depth is 'full' - if (depth === 'full') { - const relQuery = ` - MATCH (n {id: '${(sym.id || sym[0]).replace(/'/g, "''")}' })-[r:CodeRelation]->(m) - RETURN r.type AS type, m.name AS targetName, m.filePath AS targetPath - LIMIT 5 - `; - try { - const rels = await executeQuery(relQuery); - result.connections = rels.map((rel: any) => ({ - type: rel.type || rel[0], - name: rel.targetName || rel[1], - path: rel.targetPath || rel[2], - })); - } catch { - result.connections = []; - } - } - - results.push(result); + bm25Score: bm25Result.score, + }); } } else { - // No symbols found in kuzu, return file info from BM25 results.push({ name: fileName, type: 'File', filePath: bm25Result.filePath, - score: bm25Result.score, + bm25Score: bm25Result.score, }); } } catch { - // On kuzu error, still return BM25 result results.push({ - name: bm25Result.filePath.split('/').pop(), + name: fileName, type: 'File', filePath: bm25Result.filePath, - score: bm25Result.score, + bm25Score: bm25Result.score, }); } } - return results.slice(0, limit); + return results; + } + + /** + * Semantic vector search helper + */ + private async semanticSearch(query: string, limit: number): Promise { + try { + // Embed the query + const queryVec = await embedQuery(query); + const dims = getEmbeddingDims(); + const queryVecStr = `[${queryVec.join(',')}]`; + + // Query vector index + const vectorQuery = ` + CALL QUERY_VECTOR_INDEX('CodeEmbedding', 'code_embedding_idx', + CAST(${queryVecStr} AS FLOAT[${dims}]), ${limit}) + YIELD node AS emb, distance + WITH emb, distance + WHERE distance < 0.6 + RETURN emb.nodeId AS nodeId, distance + ORDER BY distance + `; + + const embResults = await executeQuery(vectorQuery); + + if (embResults.length === 0) return []; + + // Get metadata for each result + const results: any[] = []; + + for (const embRow of embResults) { + const nodeId = embRow.nodeId ?? embRow[0]; + const distance = embRow.distance ?? embRow[1]; + + // Extract label from node ID + const labelEndIdx = nodeId.indexOf(':'); + const label = labelEndIdx > 0 ? nodeId.substring(0, labelEndIdx) : 'Unknown'; + + try { + const nodeQuery = label === 'File' + ? `MATCH (n:File {id: '${nodeId.replace(/'/g, "''")}'}) RETURN n.name AS name, n.filePath AS filePath` + : `MATCH (n:${label} {id: '${nodeId.replace(/'/g, "''")}'}) RETURN n.name AS name, n.filePath AS filePath, n.startLine AS startLine, n.endLine AS endLine`; + + const nodeRows = await executeQuery(nodeQuery); + if (nodeRows.length > 0) { + const nodeRow = nodeRows[0]; + results.push({ + nodeId, + name: nodeRow.name ?? nodeRow[0] ?? '', + type: label, + filePath: nodeRow.filePath ?? nodeRow[1] ?? '', + distance, + startLine: label !== 'File' ? (nodeRow.startLine ?? nodeRow[2]) : undefined, + endLine: label !== 'File' ? (nodeRow.endLine ?? nodeRow[3]) : undefined, + }); + } + } catch {} + } + + return results; + } catch (err: any) { + // Semantic search unavailable (no embeddings or model not loaded) + console.error('GitNexus: Semantic search unavailable -', err.message); + return []; + } } private async cypher(params: { query: string }): Promise { @@ -580,8 +708,9 @@ export class LocalBackend { }; } - disconnect(): void { + async disconnect(): Promise { closeKuzu(); + await disposeEmbedder(); this.repo = null; this._context = null; this.initialized = false; diff --git a/gitnexus-mcp/src/mcp/server.ts b/gitnexus-mcp/src/mcp/server.ts index a5c4ef7e7..74f4b6100 100644 --- a/gitnexus-mcp/src/mcp/server.ts +++ b/gitnexus-mcp/src/mcp/server.ts @@ -2,12 +2,10 @@ * MCP Server * * Model Context Protocol server that runs on stdio. - * External AI tools (Cursor, Claude Code) spawn this process and + * External AI tools (Cursor, Claude) spawn this process and * communicate via stdin/stdout using the MCP protocol. * - * Exposes: - * - Tools: search, cypher, blastRadius, highlight - * - Resources: codebase context (stats, hotspots, folder tree) + * Tools: context, search, cypher, overview, explore, impact, analyze */ import { Server } from '@modelcontextprotocol/sdk/server/index.js'; @@ -19,93 +17,50 @@ import { ReadResourceRequestSchema, } from '@modelcontextprotocol/sdk/types.js'; import { GITNEXUS_TOOLS } from './tools.js'; -import type { CodebaseContext } from '../bridge/websocket-server.js'; - -// Interface for anything that can call tools (DaemonClient or WebSocketBridge) -interface ToolCaller { - callTool(method: string, params: any): Promise; - disconnect?(): void; - context?: CodebaseContext | null; - onContextChange?: (listener: (context: CodebaseContext | null) => void) => () => void; -} +import type { LocalBackend, CodebaseContext } from '../local/local-backend.js'; /** * Format context as markdown for the resource */ function formatContextAsMarkdown(context: CodebaseContext): string { - const { projectName, stats, hotspots, folderTree } = context; + const { projectName, stats } = context; const lines: string[] = []; lines.push(`# GitNexus: ${projectName}`); lines.push(''); - lines.push('This codebase is currently loaded in GitNexus. Use the tools below to explore it.'); + lines.push('## Stats'); + lines.push(`- Files: ${stats.fileCount}`); + lines.push(`- Functions: ${stats.functionCount}`); + if (stats.communityCount > 0) lines.push(`- Communities: ${stats.communityCount}`); + if (stats.processCount > 0) lines.push(`- Processes: ${stats.processCount}`); lines.push(''); - // Stats - lines.push('## 📊 Statistics'); - lines.push(`- **Files**: ${stats.fileCount}`); - lines.push(`- **Functions**: ${stats.functionCount}`); - if (stats.classCount > 0) lines.push(`- **Classes**: ${stats.classCount}`); - if (stats.interfaceCount > 0) lines.push(`- **Interfaces**: ${stats.interfaceCount}`); - if (stats.methodCount > 0) lines.push(`- **Methods**: ${stats.methodCount}`); + lines.push('## Available Tools'); + lines.push(''); + lines.push('- **context**: Codebase overview and stats'); + lines.push('- **search**: Hybrid semantic + keyword search'); + lines.push('- **cypher**: Execute Cypher queries on graph'); + lines.push('- **overview**: List communities and processes'); + lines.push('- **explore**: Deep dive on symbol/cluster/process'); + lines.push('- **impact**: Change impact analysis'); + lines.push('- **analyze**: Index/re-index repository'); lines.push(''); - // Hotspots - if (hotspots.length > 0) { - lines.push('## 🔥 Hotspots (Most Connected Nodes)'); - lines.push(''); - hotspots.forEach(h => { - lines.push(`- \`${h.name}\` (${h.type}) — ${h.connections} connections — ${h.filePath}`); - }); - lines.push(''); - } - - // Folder tree - if (folderTree) { - lines.push('## 📁 Project Structure'); - lines.push('```'); - lines.push(projectName + '/'); - lines.push(folderTree); - lines.push('```'); - lines.push(''); - } - - // Usage hints - lines.push('## 🛠️ Available Tools'); + lines.push('## Graph Schema'); lines.push(''); - lines.push('- **search**: Semantic + keyword search across codebase'); - lines.push('- **cypher**: Execute Cypher queries on knowledge graph'); - lines.push('- **grep**: Regex pattern search in files'); - lines.push('- **read**: Read file contents'); - lines.push('- **explore**: Deep dive on symbol, cluster, or process'); - lines.push('- **overview**: Codebase map (all clusters + processes)'); - lines.push('- **impact**: Analyze change impact (upstream/downstream)'); - lines.push('- **highlight**: Visualize nodes in graph'); + lines.push('**Nodes**: File, Function, Class, Interface, Method, Community, Process'); lines.push(''); - lines.push('## 📝 Graph Schema'); - lines.push(''); - lines.push('**Node Types**: File, Folder, Function, Class, Interface, Method, Community, Process'); - lines.push(''); - lines.push('**Relation**: `CodeRelation` with `type` property:'); - lines.push('- CALLS, IMPORTS, EXTENDS, IMPLEMENTS, CONTAINS, DEFINES'); - lines.push('- MEMBER_OF (symbol → community), STEP_IN_PROCESS (symbol → process)'); - lines.push(''); - lines.push('**Example Cypher Queries**:'); - lines.push('```cypher'); - lines.push('MATCH (f:Function) RETURN f.name LIMIT 10'); - lines.push("MATCH (f:File)-[:CodeRelation {type: 'IMPORTS'}]->(g:File) RETURN f.name, g.name"); - lines.push("MATCH (s)-[:CodeRelation {type: 'MEMBER_OF'}]->(c:Community) RETURN c.label, count(s)"); - lines.push('```'); + lines.push('**Relations**: CALLS, IMPORTS, EXTENDS, IMPLEMENTS, DEFINES, MEMBER_OF, STEP_IN_PROCESS'); return lines.join('\n'); } -export async function startMCPServer(client: ToolCaller): Promise { +export async function startMCPServer(backend: LocalBackend): Promise { const server = new Server( { name: 'gitnexus', - version: '0.1.0', + version: '0.2.0', }, { capabilities: { @@ -117,7 +72,7 @@ export async function startMCPServer(client: ToolCaller): Promise { // Handle list resources request server.setRequestHandler(ListResourcesRequestSchema, async () => { - const context = client.context; + const context = backend.context; if (!context) { return { resources: [] }; @@ -128,7 +83,7 @@ export async function startMCPServer(client: ToolCaller): Promise { { uri: 'gitnexus://codebase/context', name: `GitNexus: ${context.projectName}`, - description: `Codebase context for ${context.projectName} (${context.stats.fileCount} files, ${context.stats.functionCount} functions)`, + description: `Codebase context for ${context.projectName} (${context.stats.fileCount} files)`, mimeType: 'text/markdown', }, ], @@ -140,7 +95,7 @@ export async function startMCPServer(client: ToolCaller): Promise { const { uri } = request.params; if (uri === 'gitnexus://codebase/context') { - const context = client.context; + const context = backend.context; if (!context) { return { @@ -148,7 +103,7 @@ export async function startMCPServer(client: ToolCaller): Promise { { uri, mimeType: 'text/plain', - text: 'No codebase loaded. Open GitNexus in your browser and load a repository.', + text: 'No codebase loaded.', }, ], }; @@ -182,8 +137,7 @@ export async function startMCPServer(client: ToolCaller): Promise { const { name, arguments: args } = request.params; try { - // Forward the tool call to the browser via daemon - const result = await client.callTool(name, args); + const result = await backend.callTool(name, args); return { content: [ @@ -213,13 +167,13 @@ export async function startMCPServer(client: ToolCaller): Promise { // Handle graceful shutdown process.on('SIGINT', async () => { - client.disconnect?.(); + await backend.disconnect(); await server.close(); process.exit(0); }); process.on('SIGTERM', async () => { - client.disconnect?.(); + await backend.disconnect(); await server.close(); process.exit(0); });