mirror of
https://github.com/andrepimenta/claude-code-chat.git
synced 2026-05-30 08:15:38 +08:00
Merge pull request #49 from andrepimenta/feature/mcp-support
Feature/mcp support
This commit is contained in:
@@ -10,3 +10,4 @@ vsc-extension-quickstart.md
|
|||||||
**/*.ts
|
**/*.ts
|
||||||
**/.vscode-test.*
|
**/.vscode-test.*
|
||||||
backup
|
backup
|
||||||
|
.claude
|
||||||
13954
mcp-permissions.js
Normal file
13954
mcp-permissions.js
Normal file
File diff suppressed because one or more lines are too long
719
package-lock.json
generated
719
package-lock.json
generated
@@ -1,15 +1,14 @@
|
|||||||
{
|
{
|
||||||
"name": "claude-code-chat",
|
"name": "claude-code-chat",
|
||||||
"version": "0.1.3",
|
"version": "1.0.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "claude-code-chat",
|
"name": "claude-code-chat",
|
||||||
"version": "0.1.3",
|
"version": "1.0.0",
|
||||||
"license": "SEE LICENSE IN LICENSE",
|
"license": "SEE LICENSE IN LICENSE",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@modelcontextprotocol/sdk": "^1.15.0",
|
|
||||||
"@types/mocha": "^10.0.10",
|
"@types/mocha": "^10.0.10",
|
||||||
"@types/node": "20.x",
|
"@types/node": "20.x",
|
||||||
"@types/vscode": "^1.94.0",
|
"@types/vscode": "^1.94.0",
|
||||||
@@ -19,8 +18,7 @@
|
|||||||
"@vscode/test-electron": "^2.5.2",
|
"@vscode/test-electron": "^2.5.2",
|
||||||
"@vscode/vsce": "^3.5.0",
|
"@vscode/vsce": "^3.5.0",
|
||||||
"eslint": "^9.25.1",
|
"eslint": "^9.25.1",
|
||||||
"typescript": "^5.8.3",
|
"typescript": "^5.8.3"
|
||||||
"zod": "^3.25.76"
|
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"vscode": "^1.94.0"
|
"vscode": "^1.94.0"
|
||||||
@@ -558,30 +556,6 @@
|
|||||||
"@jridgewell/sourcemap-codec": "^1.4.14"
|
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@modelcontextprotocol/sdk": {
|
|
||||||
"version": "1.15.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.15.0.tgz",
|
|
||||||
"integrity": "sha512-67hnl/ROKdb03Vuu0YOr+baKTvf1/5YBHBm9KnZdjdAh8hjt4FRCPD5ucwxGB237sBpzlqQsLy1PFu7z/ekZ9Q==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"ajv": "^6.12.6",
|
|
||||||
"content-type": "^1.0.5",
|
|
||||||
"cors": "^2.8.5",
|
|
||||||
"cross-spawn": "^7.0.5",
|
|
||||||
"eventsource": "^3.0.2",
|
|
||||||
"eventsource-parser": "^3.0.0",
|
|
||||||
"express": "^5.0.1",
|
|
||||||
"express-rate-limit": "^7.5.0",
|
|
||||||
"pkce-challenge": "^5.0.0",
|
|
||||||
"raw-body": "^3.0.0",
|
|
||||||
"zod": "^3.23.8",
|
|
||||||
"zod-to-json-schema": "^3.24.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@nodelib/fs.scandir": {
|
"node_modules/@nodelib/fs.scandir": {
|
||||||
"version": "2.1.5",
|
"version": "2.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||||
@@ -1539,43 +1513,6 @@
|
|||||||
"url": "https://github.com/sponsors/isaacs"
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/accepts": {
|
|
||||||
"version": "2.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz",
|
|
||||||
"integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"mime-types": "^3.0.0",
|
|
||||||
"negotiator": "^1.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/accepts/node_modules/mime-db": {
|
|
||||||
"version": "1.54.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
|
|
||||||
"integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/accepts/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==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"mime-db": "^1.54.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/acorn": {
|
"node_modules/acorn": {
|
||||||
"version": "8.14.1",
|
"version": "8.14.1",
|
||||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz",
|
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz",
|
||||||
@@ -1834,27 +1771,6 @@
|
|||||||
"node": ">= 6"
|
"node": ">= 6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/body-parser": {
|
|
||||||
"version": "2.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz",
|
|
||||||
"integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"bytes": "^3.1.2",
|
|
||||||
"content-type": "^1.0.5",
|
|
||||||
"debug": "^4.4.0",
|
|
||||||
"http-errors": "^2.0.0",
|
|
||||||
"iconv-lite": "^0.6.3",
|
|
||||||
"on-finished": "^2.4.1",
|
|
||||||
"qs": "^6.14.0",
|
|
||||||
"raw-body": "^3.0.0",
|
|
||||||
"type-is": "^2.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/boolbase": {
|
"node_modules/boolbase": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
|
||||||
@@ -1952,16 +1868,6 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/bytes": {
|
|
||||||
"version": "3.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
|
||||||
"integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/c8": {
|
"node_modules/c8": {
|
||||||
"version": "9.1.0",
|
"version": "9.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/c8/-/c8-9.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/c8/-/c8-9.1.0.tgz",
|
||||||
@@ -2317,50 +2223,6 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"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==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"safe-buffer": "5.2.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/content-disposition/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==",
|
|
||||||
"dev": true,
|
|
||||||
"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/content-type": {
|
|
||||||
"version": "1.0.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
|
|
||||||
"integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/convert-source-map": {
|
"node_modules/convert-source-map": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
|
||||||
@@ -2368,26 +2230,6 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/cookie": {
|
|
||||||
"version": "0.7.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
|
|
||||||
"integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/cookie-signature": {
|
|
||||||
"version": "1.2.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz",
|
|
||||||
"integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=6.6.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/core-util-is": {
|
"node_modules/core-util-is": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
|
||||||
@@ -2395,20 +2237,6 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/cors": {
|
|
||||||
"version": "2.8.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
|
|
||||||
"integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"object-assign": "^4",
|
|
||||||
"vary": "^1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/cross-spawn": {
|
"node_modules/cross-spawn": {
|
||||||
"version": "7.0.6",
|
"version": "7.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
||||||
@@ -2565,16 +2393,6 @@
|
|||||||
"node": ">=0.4.0"
|
"node": ">=0.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/depd": {
|
|
||||||
"version": "2.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
|
|
||||||
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/detect-libc": {
|
"node_modules/detect-libc": {
|
||||||
"version": "2.0.4",
|
"version": "2.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz",
|
||||||
@@ -2695,13 +2513,6 @@
|
|||||||
"url": "https://bevry.me/fund"
|
"url": "https://bevry.me/fund"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/ee-first": {
|
|
||||||
"version": "1.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
|
||||||
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/emoji-regex": {
|
"node_modules/emoji-regex": {
|
||||||
"version": "9.2.2",
|
"version": "9.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
|
||||||
@@ -2709,16 +2520,6 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/encodeurl": {
|
|
||||||
"version": "2.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
|
|
||||||
"integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/encoding-sniffer": {
|
"node_modules/encoding-sniffer": {
|
||||||
"version": "0.2.1",
|
"version": "0.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/encoding-sniffer/-/encoding-sniffer-0.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/encoding-sniffer/-/encoding-sniffer-0.2.1.tgz",
|
||||||
@@ -2832,13 +2633,6 @@
|
|||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/escape-html": {
|
|
||||||
"version": "1.0.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
|
|
||||||
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/escape-string-regexp": {
|
"node_modules/escape-string-regexp": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
|
||||||
@@ -3093,39 +2887,6 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/etag": {
|
|
||||||
"version": "1.8.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
|
|
||||||
"integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/eventsource": {
|
|
||||||
"version": "3.0.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz",
|
|
||||||
"integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"eventsource-parser": "^3.0.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/eventsource-parser": {
|
|
||||||
"version": "3.0.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.3.tgz",
|
|
||||||
"integrity": "sha512-nVpZkTMM9rF6AQ9gPJpFsNAMt48wIzB5TQgiTLdHiuO8XEDhUgZEhqKlZWXbIzo9VmJ/HvysHqEaVeD5v9TPvA==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=20.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/expand-template": {
|
"node_modules/expand-template": {
|
||||||
"version": "2.0.3",
|
"version": "2.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz",
|
||||||
@@ -3136,88 +2897,6 @@
|
|||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/express": {
|
|
||||||
"version": "5.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz",
|
|
||||||
"integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"accepts": "^2.0.0",
|
|
||||||
"body-parser": "^2.2.0",
|
|
||||||
"content-disposition": "^1.0.0",
|
|
||||||
"content-type": "^1.0.5",
|
|
||||||
"cookie": "^0.7.1",
|
|
||||||
"cookie-signature": "^1.2.1",
|
|
||||||
"debug": "^4.4.0",
|
|
||||||
"encodeurl": "^2.0.0",
|
|
||||||
"escape-html": "^1.0.3",
|
|
||||||
"etag": "^1.8.1",
|
|
||||||
"finalhandler": "^2.1.0",
|
|
||||||
"fresh": "^2.0.0",
|
|
||||||
"http-errors": "^2.0.0",
|
|
||||||
"merge-descriptors": "^2.0.0",
|
|
||||||
"mime-types": "^3.0.0",
|
|
||||||
"on-finished": "^2.4.1",
|
|
||||||
"once": "^1.4.0",
|
|
||||||
"parseurl": "^1.3.3",
|
|
||||||
"proxy-addr": "^2.0.7",
|
|
||||||
"qs": "^6.14.0",
|
|
||||||
"range-parser": "^1.2.1",
|
|
||||||
"router": "^2.2.0",
|
|
||||||
"send": "^1.1.0",
|
|
||||||
"serve-static": "^2.2.0",
|
|
||||||
"statuses": "^2.0.1",
|
|
||||||
"type-is": "^2.0.1",
|
|
||||||
"vary": "^1.1.2"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 18"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"type": "opencollective",
|
|
||||||
"url": "https://opencollective.com/express"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/express-rate-limit": {
|
|
||||||
"version": "7.5.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.1.tgz",
|
|
||||||
"integrity": "sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 16"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/express-rate-limit"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"express": ">= 4.11"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/express/node_modules/mime-db": {
|
|
||||||
"version": "1.54.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
|
|
||||||
"integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/express/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==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"mime-db": "^1.54.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/fast-deep-equal": {
|
"node_modules/fast-deep-equal": {
|
||||||
"version": "3.1.3",
|
"version": "3.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||||
@@ -3317,24 +2996,6 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/finalhandler": {
|
|
||||||
"version": "2.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz",
|
|
||||||
"integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"debug": "^4.4.0",
|
|
||||||
"encodeurl": "^2.0.0",
|
|
||||||
"escape-html": "^1.0.3",
|
|
||||||
"on-finished": "^2.4.1",
|
|
||||||
"parseurl": "^1.3.3",
|
|
||||||
"statuses": "^2.0.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/find-up": {
|
"node_modules/find-up": {
|
||||||
"version": "5.0.0",
|
"version": "5.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
|
||||||
@@ -3416,26 +3077,6 @@
|
|||||||
"node": ">= 6"
|
"node": ">= 6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/forwarded": {
|
|
||||||
"version": "0.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
|
|
||||||
"integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/fresh": {
|
|
||||||
"version": "2.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz",
|
|
||||||
"integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/fs-constants": {
|
"node_modules/fs-constants": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
|
||||||
@@ -3769,33 +3410,6 @@
|
|||||||
"url": "https://github.com/fb55/entities?sponsor=1"
|
"url": "https://github.com/fb55/entities?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"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==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"depd": "2.0.0",
|
|
||||||
"inherits": "2.0.4",
|
|
||||||
"setprototypeof": "1.2.0",
|
|
||||||
"statuses": "2.0.1",
|
|
||||||
"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==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/http-proxy-agent": {
|
"node_modules/http-proxy-agent": {
|
||||||
"version": "7.0.2",
|
"version": "7.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
|
||||||
@@ -3936,16 +3550,6 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
"node_modules/ipaddr.js": {
|
|
||||||
"version": "1.9.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
|
|
||||||
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/is-arrayish": {
|
"node_modules/is-arrayish": {
|
||||||
"version": "0.2.1",
|
"version": "0.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
|
||||||
@@ -4064,13 +3668,6 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/is-promise": {
|
|
||||||
"version": "4.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz",
|
|
||||||
"integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/is-unicode-supported": {
|
"node_modules/is-unicode-supported": {
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz",
|
||||||
@@ -4555,29 +4152,6 @@
|
|||||||
"integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==",
|
"integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/media-typer": {
|
|
||||||
"version": "1.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz",
|
|
||||||
"integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/merge-descriptors": {
|
|
||||||
"version": "2.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz",
|
|
||||||
"integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/sindresorhus"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/merge2": {
|
"node_modules/merge2": {
|
||||||
"version": "1.4.1",
|
"version": "1.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
|
||||||
@@ -4921,16 +4495,6 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/negotiator": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz",
|
|
||||||
"integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/node-abi": {
|
"node_modules/node-abi": {
|
||||||
"version": "3.75.0",
|
"version": "3.75.0",
|
||||||
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.75.0.tgz",
|
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.75.0.tgz",
|
||||||
@@ -5012,16 +4576,6 @@
|
|||||||
"url": "https://github.com/fb55/nth-check?sponsor=1"
|
"url": "https://github.com/fb55/nth-check?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/object-assign": {
|
|
||||||
"version": "4.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
|
||||||
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.10.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/object-inspect": {
|
"node_modules/object-inspect": {
|
||||||
"version": "1.13.4",
|
"version": "1.13.4",
|
||||||
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
|
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
|
||||||
@@ -5034,19 +4588,6 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/on-finished": {
|
|
||||||
"version": "2.4.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
|
|
||||||
"integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"ee-first": "1.1.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/once": {
|
"node_modules/once": {
|
||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||||
@@ -5386,16 +4927,6 @@
|
|||||||
"url": "https://github.com/fb55/entities?sponsor=1"
|
"url": "https://github.com/fb55/entities?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/parseurl": {
|
|
||||||
"version": "1.3.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
|
||||||
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/path-exists": {
|
"node_modules/path-exists": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
|
||||||
@@ -5443,16 +4974,6 @@
|
|||||||
"url": "https://github.com/sponsors/isaacs"
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/path-to-regexp": {
|
|
||||||
"version": "8.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz",
|
|
||||||
"integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=16"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/path-type": {
|
"node_modules/path-type": {
|
||||||
"version": "6.0.0",
|
"version": "6.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/path-type/-/path-type-6.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/path-type/-/path-type-6.0.0.tgz",
|
||||||
@@ -5490,16 +5011,6 @@
|
|||||||
"url": "https://github.com/sponsors/jonschlinkert"
|
"url": "https://github.com/sponsors/jonschlinkert"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/pkce-challenge": {
|
|
||||||
"version": "5.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.0.tgz",
|
|
||||||
"integrity": "sha512-ueGLflrrnvwB3xuo/uGob5pd5FN7l0MsLf0Z87o/UQmRtwjvfylfc9MurIxRAWywCYTgrvpXBcqjV4OfCYGCIQ==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=16.20.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/pluralize": {
|
"node_modules/pluralize": {
|
||||||
"version": "8.0.0",
|
"version": "8.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz",
|
||||||
@@ -5553,20 +5064,6 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/proxy-addr": {
|
|
||||||
"version": "2.0.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
|
||||||
"integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"forwarded": "0.2.0",
|
|
||||||
"ipaddr.js": "1.9.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/pump": {
|
"node_modules/pump": {
|
||||||
"version": "3.0.3",
|
"version": "3.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz",
|
||||||
@@ -5643,32 +5140,6 @@
|
|||||||
"safe-buffer": "^5.1.0"
|
"safe-buffer": "^5.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/range-parser": {
|
|
||||||
"version": "1.2.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
|
|
||||||
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/raw-body": {
|
|
||||||
"version": "3.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz",
|
|
||||||
"integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"bytes": "3.1.2",
|
|
||||||
"http-errors": "2.0.0",
|
|
||||||
"iconv-lite": "0.6.3",
|
|
||||||
"unpipe": "1.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/rc": {
|
"node_modules/rc": {
|
||||||
"version": "1.2.8",
|
"version": "1.2.8",
|
||||||
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
|
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
|
||||||
@@ -5823,23 +5294,6 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/router": {
|
|
||||||
"version": "2.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz",
|
|
||||||
"integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"debug": "^4.4.0",
|
|
||||||
"depd": "^2.0.0",
|
|
||||||
"is-promise": "^4.0.0",
|
|
||||||
"parseurl": "^1.3.3",
|
|
||||||
"path-to-regexp": "^8.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/run-applescript": {
|
"node_modules/run-applescript": {
|
||||||
"version": "7.0.0",
|
"version": "7.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.0.0.tgz",
|
||||||
@@ -5929,52 +5383,6 @@
|
|||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/send": {
|
|
||||||
"version": "1.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz",
|
|
||||||
"integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"debug": "^4.3.5",
|
|
||||||
"encodeurl": "^2.0.0",
|
|
||||||
"escape-html": "^1.0.3",
|
|
||||||
"etag": "^1.8.1",
|
|
||||||
"fresh": "^2.0.0",
|
|
||||||
"http-errors": "^2.0.0",
|
|
||||||
"mime-types": "^3.0.1",
|
|
||||||
"ms": "^2.1.3",
|
|
||||||
"on-finished": "^2.4.1",
|
|
||||||
"range-parser": "^1.2.1",
|
|
||||||
"statuses": "^2.0.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/send/node_modules/mime-db": {
|
|
||||||
"version": "1.54.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
|
|
||||||
"integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/send/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==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"mime-db": "^1.54.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/serialize-javascript": {
|
"node_modules/serialize-javascript": {
|
||||||
"version": "6.0.2",
|
"version": "6.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz",
|
||||||
@@ -5985,22 +5393,6 @@
|
|||||||
"randombytes": "^2.1.0"
|
"randombytes": "^2.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/serve-static": {
|
|
||||||
"version": "2.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz",
|
|
||||||
"integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"encodeurl": "^2.0.0",
|
|
||||||
"escape-html": "^1.0.3",
|
|
||||||
"parseurl": "^1.3.3",
|
|
||||||
"send": "^1.2.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/setimmediate": {
|
"node_modules/setimmediate": {
|
||||||
"version": "1.0.5",
|
"version": "1.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
|
||||||
@@ -6008,13 +5400,6 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/setprototypeof": {
|
|
||||||
"version": "1.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
|
|
||||||
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "ISC"
|
|
||||||
},
|
|
||||||
"node_modules/shebang-command": {
|
"node_modules/shebang-command": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
||||||
@@ -6237,16 +5622,6 @@
|
|||||||
"integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
|
"integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/statuses": {
|
|
||||||
"version": "2.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
|
|
||||||
"integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/stdin-discarder": {
|
"node_modules/stdin-discarder": {
|
||||||
"version": "0.2.2",
|
"version": "0.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz",
|
||||||
@@ -6688,16 +6063,6 @@
|
|||||||
"node": ">=8.0"
|
"node": ">=8.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/toidentifier": {
|
|
||||||
"version": "1.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
|
|
||||||
"integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/ts-api-utils": {
|
"node_modules/ts-api-utils": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz",
|
||||||
@@ -6764,44 +6129,6 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"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",
|
|
||||||
"integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"content-type": "^1.0.5",
|
|
||||||
"media-typer": "^1.1.0",
|
|
||||||
"mime-types": "^3.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/type-is/node_modules/mime-db": {
|
|
||||||
"version": "1.54.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
|
|
||||||
"integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/type-is/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==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"mime-db": "^1.54.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/typed-rest-client": {
|
"node_modules/typed-rest-client": {
|
||||||
"version": "1.8.11",
|
"version": "1.8.11",
|
||||||
"resolved": "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-1.8.11.tgz",
|
"resolved": "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-1.8.11.tgz",
|
||||||
@@ -6876,16 +6203,6 @@
|
|||||||
"node": ">= 10.0.0"
|
"node": ">= 10.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/unpipe": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
|
|
||||||
"integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/uri-js": {
|
"node_modules/uri-js": {
|
||||||
"version": "4.4.1",
|
"version": "4.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
|
||||||
@@ -6943,16 +6260,6 @@
|
|||||||
"spdx-expression-parse": "^3.0.0"
|
"spdx-expression-parse": "^3.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/vary": {
|
|
||||||
"version": "1.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
|
||||||
"integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/version-range": {
|
"node_modules/version-range": {
|
||||||
"version": "4.14.0",
|
"version": "4.14.0",
|
||||||
"resolved": "https://registry.npmjs.org/version-range/-/version-range-4.14.0.tgz",
|
"resolved": "https://registry.npmjs.org/version-range/-/version-range-4.14.0.tgz",
|
||||||
@@ -7280,26 +6587,6 @@
|
|||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"node_modules/zod": {
|
|
||||||
"version": "3.25.76",
|
|
||||||
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
|
|
||||||
"integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/colinhacks"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"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==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "ISC",
|
|
||||||
"peerDependencies": {
|
|
||||||
"zod": "^3.24.1"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"name": "claude-code-chat",
|
"name": "claude-code-chat",
|
||||||
"displayName": "Claude Code Chat",
|
"displayName": "Claude Code Chat",
|
||||||
"description": "Beautiful Claude Code Chat Interface for VS Code",
|
"description": "Beautiful Claude Code Chat Interface for VS Code",
|
||||||
"version": "0.1.3",
|
"version": "1.0.0",
|
||||||
"publisher": "AndrePimenta",
|
"publisher": "AndrePimenta",
|
||||||
"author": "Andre Pimenta",
|
"author": "Andre Pimenta",
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -207,8 +207,6 @@
|
|||||||
"@vscode/test-electron": "^2.5.2",
|
"@vscode/test-electron": "^2.5.2",
|
||||||
"@vscode/vsce": "^3.5.0",
|
"@vscode/vsce": "^3.5.0",
|
||||||
"eslint": "^9.25.1",
|
"eslint": "^9.25.1",
|
||||||
"typescript": "^5.8.3",
|
"typescript": "^5.8.3"
|
||||||
"@modelcontextprotocol/sdk": "^1.15.0",
|
|
||||||
"zod": "^3.25.76"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
304
src/extension.ts
304
src/extension.ts
@@ -86,6 +86,7 @@ class ClaudeChatProvider {
|
|||||||
private _webview: vscode.Webview | undefined;
|
private _webview: vscode.Webview | undefined;
|
||||||
private _webviewView: vscode.WebviewView | undefined;
|
private _webviewView: vscode.WebviewView | undefined;
|
||||||
private _disposables: vscode.Disposable[] = [];
|
private _disposables: vscode.Disposable[] = [];
|
||||||
|
private _messageHandlerDisposable: vscode.Disposable | undefined;
|
||||||
private _totalCost: number = 0;
|
private _totalCost: number = 0;
|
||||||
private _totalTokensInput: number = 0;
|
private _totalTokensInput: number = 0;
|
||||||
private _totalTokensOutput: number = 0;
|
private _totalTokensOutput: number = 0;
|
||||||
@@ -286,11 +287,35 @@ class ClaudeChatProvider {
|
|||||||
case 'addPermission':
|
case 'addPermission':
|
||||||
this._addPermission(message.toolName, message.command);
|
this._addPermission(message.toolName, message.command);
|
||||||
return;
|
return;
|
||||||
|
case 'loadMCPServers':
|
||||||
|
this._loadMCPServers();
|
||||||
|
return;
|
||||||
|
case 'saveMCPServer':
|
||||||
|
this._saveMCPServer(message.name, message.config);
|
||||||
|
return;
|
||||||
|
case 'deleteMCPServer':
|
||||||
|
this._deleteMCPServer(message.name);
|
||||||
|
return;
|
||||||
|
case 'getCustomSnippets':
|
||||||
|
this._sendCustomSnippets();
|
||||||
|
return;
|
||||||
|
case 'saveCustomSnippet':
|
||||||
|
this._saveCustomSnippet(message.snippet);
|
||||||
|
return;
|
||||||
|
case 'deleteCustomSnippet':
|
||||||
|
this._deleteCustomSnippet(message.snippetId);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _setupWebviewMessageHandler(webview: vscode.Webview) {
|
private _setupWebviewMessageHandler(webview: vscode.Webview) {
|
||||||
webview.onDidReceiveMessage(
|
// Dispose of any existing message handler
|
||||||
|
if (this._messageHandlerDisposable) {
|
||||||
|
this._messageHandlerDisposable.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up new message handler
|
||||||
|
this._messageHandlerDisposable = webview.onDidReceiveMessage(
|
||||||
message => this._handleWebviewMessage(message),
|
message => this._handleWebviewMessage(message),
|
||||||
null,
|
null,
|
||||||
this._disposables
|
this._disposables
|
||||||
@@ -342,6 +367,7 @@ class ClaudeChatProvider {
|
|||||||
// Only reinitialize if we have a webview (sidebar)
|
// Only reinitialize if we have a webview (sidebar)
|
||||||
if (this._webview) {
|
if (this._webview) {
|
||||||
this._initializeWebview();
|
this._initializeWebview();
|
||||||
|
// Set up message handler for the webview
|
||||||
this._setupWebviewMessageHandler(this._webview);
|
this._setupWebviewMessageHandler(this._webview);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -427,9 +453,9 @@ class ClaudeChatProvider {
|
|||||||
// Add MCP configuration for permissions
|
// Add MCP configuration for permissions
|
||||||
const mcpConfigPath = this.getMCPConfigPath();
|
const mcpConfigPath = this.getMCPConfigPath();
|
||||||
if (mcpConfigPath) {
|
if (mcpConfigPath) {
|
||||||
args.push('--mcp-config', mcpConfigPath);
|
args.push('--mcp-config', this.convertToWSLPath(mcpConfigPath));
|
||||||
args.push('--allowedTools', 'mcp__permissions__approval_prompt');
|
args.push('--allowedTools', 'mcp__claude-code-chat-permissions__approval_prompt');
|
||||||
args.push('--permission-prompt-tool', 'mcp__permissions__approval_prompt');
|
args.push('--permission-prompt-tool', 'mcp__claude-code-chat-permissions__approval_prompt');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -456,9 +482,13 @@ class ClaudeChatProvider {
|
|||||||
let claudeProcess: cp.ChildProcess;
|
let claudeProcess: cp.ChildProcess;
|
||||||
|
|
||||||
if (wslEnabled) {
|
if (wslEnabled) {
|
||||||
// Use WSL
|
// Use WSL with bash -ic for proper environment loading
|
||||||
console.log('Using WSL configuration:', { wslDistro, nodePath, claudePath });
|
console.log('Using WSL configuration:', { wslDistro, nodePath, claudePath });
|
||||||
claudeProcess = cp.spawn('wsl', ['-d', wslDistro, nodePath, '--no-warnings', '--enable-source-maps', claudePath, ...args], {
|
const wslCommand = `"${nodePath}" --no-warnings --enable-source-maps "${claudePath}" ${args.join(' ')}`;
|
||||||
|
|
||||||
|
console.log('wsl', ['-d', wslDistro, 'bash', '-ic', wslCommand].join(" "))
|
||||||
|
|
||||||
|
claudeProcess = cp.spawn('wsl', ['-d', wslDistro, 'bash', '-ic', wslCommand], {
|
||||||
cwd: cwd,
|
cwd: cwd,
|
||||||
stdio: ['pipe', 'pipe', 'pipe'],
|
stdio: ['pipe', 'pipe', 'pipe'],
|
||||||
env: {
|
env: {
|
||||||
@@ -653,6 +683,12 @@ class ClaudeChatProvider {
|
|||||||
for (const content of jsonData.message.content) {
|
for (const content of jsonData.message.content) {
|
||||||
if (content.type === 'tool_result') {
|
if (content.type === 'tool_result') {
|
||||||
let resultContent = content.content || 'Tool executed successfully';
|
let resultContent = content.content || 'Tool executed successfully';
|
||||||
|
|
||||||
|
// Stringify if content is an object or array
|
||||||
|
if (typeof resultContent === 'object' && resultContent !== null) {
|
||||||
|
resultContent = JSON.stringify(resultContent, null, 2);
|
||||||
|
}
|
||||||
|
|
||||||
const isError = content.is_error || false;
|
const isError = content.is_error || false;
|
||||||
|
|
||||||
// Find the last tool use to get the tool name
|
// Find the last tool use to get the tool name
|
||||||
@@ -782,6 +818,9 @@ class ClaudeChatProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public newSessionOnConfigChange() {
|
public newSessionOnConfigChange() {
|
||||||
|
// Reinitialize MCP config with new WSL paths
|
||||||
|
this._initializeMCPConfig();
|
||||||
|
|
||||||
// Start a new session due to configuration change
|
// Start a new session due to configuration change
|
||||||
this._newSession();
|
this._newSession();
|
||||||
|
|
||||||
@@ -1017,27 +1056,41 @@ class ClaudeChatProvider {
|
|||||||
console.log(`Created MCP config directory at: ${mcpConfigDir}`);
|
console.log(`Created MCP config directory at: ${mcpConfigDir}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create mcp-servers.json with correct path to compiled MCP permissions server
|
// Create or update mcp-servers.json with permissions server, preserving existing servers
|
||||||
const mcpConfigPath = path.join(mcpConfigDir, 'mcp-servers.json');
|
const mcpConfigPath = path.join(mcpConfigDir, 'mcp-servers.json');
|
||||||
const mcpPermissionsPath = path.join(this._extensionUri.fsPath, 'out', 'permissions', 'mcp-permissions.js');
|
const mcpPermissionsPath = this.convertToWSLPath(path.join(this._extensionUri.fsPath, 'mcp-permissions.js'));
|
||||||
const permissionRequestsPath = path.join(storagePath, 'permission-requests');
|
const permissionRequestsPath = this.convertToWSLPath(path.join(storagePath, 'permission-requests'));
|
||||||
|
|
||||||
const mcpConfig = {
|
// Load existing config or create new one
|
||||||
mcpServers: {
|
let mcpConfig: any = { mcpServers: {} };
|
||||||
permissions: {
|
const mcpConfigUri = vscode.Uri.file(mcpConfigPath);
|
||||||
command: 'node',
|
|
||||||
args: [mcpPermissionsPath],
|
try {
|
||||||
env: {
|
const existingContent = await vscode.workspace.fs.readFile(mcpConfigUri);
|
||||||
CLAUDE_PERMISSIONS_PATH: permissionRequestsPath
|
mcpConfig = JSON.parse(new TextDecoder().decode(existingContent));
|
||||||
}
|
console.log('Loaded existing MCP config, preserving user servers');
|
||||||
}
|
} catch {
|
||||||
|
console.log('No existing MCP config found, creating new one');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure mcpServers exists
|
||||||
|
if (!mcpConfig.mcpServers) {
|
||||||
|
mcpConfig.mcpServers = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add or update the permissions server entry
|
||||||
|
mcpConfig.mcpServers['claude-code-chat-permissions'] = {
|
||||||
|
command: 'node',
|
||||||
|
args: [mcpPermissionsPath],
|
||||||
|
env: {
|
||||||
|
CLAUDE_PERMISSIONS_PATH: permissionRequestsPath
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const configContent = new TextEncoder().encode(JSON.stringify(mcpConfig, null, 2));
|
const configContent = new TextEncoder().encode(JSON.stringify(mcpConfig, null, 2));
|
||||||
await vscode.workspace.fs.writeFile(vscode.Uri.file(mcpConfigPath), configContent);
|
await vscode.workspace.fs.writeFile(mcpConfigUri, configContent);
|
||||||
|
|
||||||
console.log(`Created MCP config at: ${mcpConfigPath}`);
|
console.log(`Updated MCP config at: ${mcpConfigPath}`);
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error('Failed to initialize MCP config:', error.message);
|
console.error('Failed to initialize MCP config:', error.message);
|
||||||
}
|
}
|
||||||
@@ -1049,7 +1102,7 @@ class ClaudeChatProvider {
|
|||||||
if (!storagePath) {return;}
|
if (!storagePath) {return;}
|
||||||
|
|
||||||
// Create permission requests directory
|
// Create permission requests directory
|
||||||
this._permissionRequestsPath = path.join(storagePath, 'permission-requests');
|
this._permissionRequestsPath = path.join(path.join(storagePath, 'permission-requests'));
|
||||||
try {
|
try {
|
||||||
await vscode.workspace.fs.stat(vscode.Uri.file(this._permissionRequestsPath));
|
await vscode.workspace.fs.stat(vscode.Uri.file(this._permissionRequestsPath));
|
||||||
} catch {
|
} catch {
|
||||||
@@ -1057,12 +1110,15 @@ class ClaudeChatProvider {
|
|||||||
console.log(`Created permission requests directory at: ${this._permissionRequestsPath}`);
|
console.log(`Created permission requests directory at: ${this._permissionRequestsPath}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log("DIRECTORY-----", this._permissionRequestsPath)
|
||||||
|
|
||||||
// Set up file watcher for *.request files
|
// Set up file watcher for *.request files
|
||||||
this._permissionWatcher = vscode.workspace.createFileSystemWatcher(
|
this._permissionWatcher = vscode.workspace.createFileSystemWatcher(
|
||||||
new vscode.RelativePattern(this._permissionRequestsPath, '*.request')
|
new vscode.RelativePattern(this._permissionRequestsPath, '*.request')
|
||||||
);
|
);
|
||||||
|
|
||||||
this._permissionWatcher.onDidCreate(async (uri) => {
|
this._permissionWatcher.onDidCreate(async (uri) => {
|
||||||
|
console.log("----file", uri)
|
||||||
// Only handle file scheme URIs, ignore vscode-userdata scheme
|
// Only handle file scheme URIs, ignore vscode-userdata scheme
|
||||||
if (uri.scheme === 'file') {
|
if (uri.scheme === 'file') {
|
||||||
await this._handlePermissionRequest(uri);
|
await this._handlePermissionRequest(uri);
|
||||||
@@ -1451,10 +1507,208 @@ class ClaudeChatProvider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async _loadMCPServers(): Promise<void> {
|
||||||
|
try {
|
||||||
|
const mcpConfigPath = this.getMCPConfigPath();
|
||||||
|
if (!mcpConfigPath) {
|
||||||
|
this._postMessage({ type: 'mcpServers', data: {} });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const mcpConfigUri = vscode.Uri.file(mcpConfigPath);
|
||||||
|
let mcpConfig: any = { mcpServers: {} };
|
||||||
|
|
||||||
|
try {
|
||||||
|
const content = await vscode.workspace.fs.readFile(mcpConfigUri);
|
||||||
|
mcpConfig = JSON.parse(new TextDecoder().decode(content));
|
||||||
|
} catch (error) {
|
||||||
|
console.log('MCP config file not found or error reading:', error);
|
||||||
|
// File doesn't exist, return empty servers
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter out internal servers before sending to UI
|
||||||
|
const filteredServers = Object.fromEntries(
|
||||||
|
Object.entries(mcpConfig.mcpServers || {}).filter(([name]) => name !== 'claude-code-chat-permissions')
|
||||||
|
);
|
||||||
|
this._postMessage({ type: 'mcpServers', data: filteredServers });
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error loading MCP servers:', error);
|
||||||
|
this._postMessage({ type: 'mcpServerError', data: { error: 'Failed to load MCP servers' } });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _saveMCPServer(name: string, config: any): Promise<void> {
|
||||||
|
try {
|
||||||
|
const mcpConfigPath = this.getMCPConfigPath();
|
||||||
|
if (!mcpConfigPath) {
|
||||||
|
this._postMessage({ type: 'mcpServerError', data: { error: 'Storage path not available' } });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const mcpConfigUri = vscode.Uri.file(mcpConfigPath);
|
||||||
|
let mcpConfig: any = { mcpServers: {} };
|
||||||
|
|
||||||
|
// Load existing config
|
||||||
|
try {
|
||||||
|
const content = await vscode.workspace.fs.readFile(mcpConfigUri);
|
||||||
|
mcpConfig = JSON.parse(new TextDecoder().decode(content));
|
||||||
|
} catch {
|
||||||
|
// File doesn't exist, use default structure
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure mcpServers exists
|
||||||
|
if (!mcpConfig.mcpServers) {
|
||||||
|
mcpConfig.mcpServers = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add/update the server
|
||||||
|
mcpConfig.mcpServers[name] = config;
|
||||||
|
|
||||||
|
// Ensure directory exists
|
||||||
|
const mcpDir = vscode.Uri.file(path.dirname(mcpConfigPath));
|
||||||
|
try {
|
||||||
|
await vscode.workspace.fs.stat(mcpDir);
|
||||||
|
} catch {
|
||||||
|
await vscode.workspace.fs.createDirectory(mcpDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save the config
|
||||||
|
const configContent = new TextEncoder().encode(JSON.stringify(mcpConfig, null, 2));
|
||||||
|
await vscode.workspace.fs.writeFile(mcpConfigUri, configContent);
|
||||||
|
|
||||||
|
this._postMessage({ type: 'mcpServerSaved', data: { name } });
|
||||||
|
console.log(`Saved MCP server: ${name}`);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error saving MCP server:', error);
|
||||||
|
this._postMessage({ type: 'mcpServerError', data: { error: 'Failed to save MCP server' } });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _deleteMCPServer(name: string): Promise<void> {
|
||||||
|
try {
|
||||||
|
const mcpConfigPath = this.getMCPConfigPath();
|
||||||
|
if (!mcpConfigPath) {
|
||||||
|
this._postMessage({ type: 'mcpServerError', data: { error: 'Storage path not available' } });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const mcpConfigUri = vscode.Uri.file(mcpConfigPath);
|
||||||
|
let mcpConfig: any = { mcpServers: {} };
|
||||||
|
|
||||||
|
// Load existing config
|
||||||
|
try {
|
||||||
|
const content = await vscode.workspace.fs.readFile(mcpConfigUri);
|
||||||
|
mcpConfig = JSON.parse(new TextDecoder().decode(content));
|
||||||
|
} catch {
|
||||||
|
// File doesn't exist, nothing to delete
|
||||||
|
this._postMessage({ type: 'mcpServerError', data: { error: 'MCP config file not found' } });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete the server
|
||||||
|
if (mcpConfig.mcpServers && mcpConfig.mcpServers[name]) {
|
||||||
|
delete mcpConfig.mcpServers[name];
|
||||||
|
|
||||||
|
// Save the updated config
|
||||||
|
const configContent = new TextEncoder().encode(JSON.stringify(mcpConfig, null, 2));
|
||||||
|
await vscode.workspace.fs.writeFile(mcpConfigUri, configContent);
|
||||||
|
|
||||||
|
this._postMessage({ type: 'mcpServerDeleted', data: { name } });
|
||||||
|
console.log(`Deleted MCP server: ${name}`);
|
||||||
|
} else {
|
||||||
|
this._postMessage({ type: 'mcpServerError', data: { error: `Server '${name}' not found` } });
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error deleting MCP server:', error);
|
||||||
|
this._postMessage({ type: 'mcpServerError', data: { error: 'Failed to delete MCP server' } });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _sendCustomSnippets(): Promise<void> {
|
||||||
|
try {
|
||||||
|
const customSnippets = this._context.globalState.get<{[key: string]: any}>('customPromptSnippets', {});
|
||||||
|
this._postMessage({
|
||||||
|
type: 'customSnippetsData',
|
||||||
|
data: customSnippets
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error loading custom snippets:', error);
|
||||||
|
this._postMessage({
|
||||||
|
type: 'customSnippetsData',
|
||||||
|
data: {}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _saveCustomSnippet(snippet: any): Promise<void> {
|
||||||
|
try {
|
||||||
|
const customSnippets = this._context.globalState.get<{[key: string]: any}>('customPromptSnippets', {});
|
||||||
|
customSnippets[snippet.id] = snippet;
|
||||||
|
|
||||||
|
await this._context.globalState.update('customPromptSnippets', customSnippets);
|
||||||
|
|
||||||
|
this._postMessage({
|
||||||
|
type: 'customSnippetSaved',
|
||||||
|
data: { snippet }
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('Saved custom snippet:', snippet.name);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error saving custom snippet:', error);
|
||||||
|
this._postMessage({
|
||||||
|
type: 'error',
|
||||||
|
data: 'Failed to save custom snippet'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _deleteCustomSnippet(snippetId: string): Promise<void> {
|
||||||
|
try {
|
||||||
|
const customSnippets = this._context.globalState.get<{[key: string]: any}>('customPromptSnippets', {});
|
||||||
|
|
||||||
|
if (customSnippets[snippetId]) {
|
||||||
|
delete customSnippets[snippetId];
|
||||||
|
await this._context.globalState.update('customPromptSnippets', customSnippets);
|
||||||
|
|
||||||
|
this._postMessage({
|
||||||
|
type: 'customSnippetDeleted',
|
||||||
|
data: { snippetId }
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('Deleted custom snippet:', snippetId);
|
||||||
|
} else {
|
||||||
|
this._postMessage({
|
||||||
|
type: 'error',
|
||||||
|
data: 'Snippet not found'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error deleting custom snippet:', error);
|
||||||
|
this._postMessage({
|
||||||
|
type: 'error',
|
||||||
|
data: 'Failed to delete custom snippet'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private convertToWSLPath(windowsPath: string): string {
|
||||||
|
const config = vscode.workspace.getConfiguration('claudeCodeChat');
|
||||||
|
const wslEnabled = config.get<boolean>('wsl.enabled', false);
|
||||||
|
|
||||||
|
if (wslEnabled && windowsPath.match(/^[a-zA-Z]:/)) {
|
||||||
|
// Convert C:\Users\... to /mnt/c/Users/...
|
||||||
|
return windowsPath.replace(/^([a-zA-Z]):/, '/mnt/$1').toLowerCase().replace(/\\/g, '/');
|
||||||
|
}
|
||||||
|
|
||||||
|
return windowsPath;
|
||||||
|
}
|
||||||
|
|
||||||
public getMCPConfigPath(): string | undefined {
|
public getMCPConfigPath(): string | undefined {
|
||||||
const storagePath = this._context.storageUri?.fsPath;
|
const storagePath = this._context.storageUri?.fsPath;
|
||||||
if (!storagePath) {return undefined;}
|
if (!storagePath) {return undefined;}
|
||||||
return path.join(storagePath, 'mcp', 'mcp-servers.json');
|
|
||||||
|
const configPath = path.join(storagePath, 'mcp', 'mcp-servers.json');
|
||||||
|
return path.join(configPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _sendAndSaveMessage(message: { type: string, data: any }): void {
|
private _sendAndSaveMessage(message: { type: string, data: any }): void {
|
||||||
@@ -1990,6 +2244,12 @@ class ClaudeChatProvider {
|
|||||||
this._panel = undefined;
|
this._panel = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Dispose message handler if it exists
|
||||||
|
if (this._messageHandlerDisposable) {
|
||||||
|
this._messageHandlerDisposable.dispose();
|
||||||
|
this._messageHandlerDisposable = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
while (this._disposables.length) {
|
while (this._disposables.length) {
|
||||||
const disposable = this._disposables.pop();
|
const disposable = this._disposables.pop();
|
||||||
if (disposable) {
|
if (disposable) {
|
||||||
|
|||||||
@@ -1,212 +0,0 @@
|
|||||||
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
||||||
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
||||||
import { z } from "zod";
|
|
||||||
import * as fs from "fs";
|
|
||||||
import * as path from "path";
|
|
||||||
|
|
||||||
const server = new McpServer({
|
|
||||||
name: "Claude Code Permissions MCP Server",
|
|
||||||
version: "0.0.1",
|
|
||||||
});
|
|
||||||
|
|
||||||
// Get permissions directory from environment
|
|
||||||
const PERMISSIONS_PATH = process.env.CLAUDE_PERMISSIONS_PATH;
|
|
||||||
if (!PERMISSIONS_PATH) {
|
|
||||||
console.error("CLAUDE_PERMISSIONS_PATH environment variable not set");
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
interface WorkspacePermissions {
|
|
||||||
alwaysAllow: {
|
|
||||||
[toolName: string]: boolean | string[]; // true for all, or array of allowed commands/patterns
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function getWorkspacePermissionsPath(): string | null {
|
|
||||||
if (!PERMISSIONS_PATH) return null;
|
|
||||||
return path.join(PERMISSIONS_PATH, 'permissions.json');
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadWorkspacePermissions(): WorkspacePermissions {
|
|
||||||
const permissionsPath = getWorkspacePermissionsPath();
|
|
||||||
if (!permissionsPath || !fs.existsSync(permissionsPath)) {
|
|
||||||
return { alwaysAllow: {} };
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const content = fs.readFileSync(permissionsPath, 'utf8');
|
|
||||||
return JSON.parse(content);
|
|
||||||
} catch (error) {
|
|
||||||
console.error(`Error loading workspace permissions: ${error}`);
|
|
||||||
return { alwaysAllow: {} };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function isAlwaysAllowed(toolName: string, input: any): boolean {
|
|
||||||
const permissions = loadWorkspacePermissions();
|
|
||||||
const toolPermission = permissions.alwaysAllow[toolName];
|
|
||||||
|
|
||||||
if (!toolPermission) return false;
|
|
||||||
|
|
||||||
// If it's true, always allow
|
|
||||||
if (toolPermission === true) return true;
|
|
||||||
|
|
||||||
// If it's an array, check for specific commands (mainly for Bash)
|
|
||||||
if (Array.isArray(toolPermission)) {
|
|
||||||
if (toolName === 'Bash' && input.command) {
|
|
||||||
const command = input.command.trim();
|
|
||||||
return toolPermission.some(allowedCmd => {
|
|
||||||
// Support exact match or pattern matching
|
|
||||||
if (allowedCmd.includes('*')) {
|
|
||||||
// Handle patterns like "npm i *" to match both "npm i" and "npm i something"
|
|
||||||
const baseCommand = allowedCmd.replace(' *', '');
|
|
||||||
if (command === baseCommand) {
|
|
||||||
return true; // Exact match for base command
|
|
||||||
}
|
|
||||||
// Pattern match for command with arguments
|
|
||||||
const pattern = allowedCmd.replace(/\*/g, '.*');
|
|
||||||
return new RegExp(`^${pattern}$`).test(command);
|
|
||||||
}
|
|
||||||
return command.startsWith(allowedCmd);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function generateRequestId(): string {
|
|
||||||
return `req_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function requestPermission(tool_name: string, input: any): Promise<{approved: boolean, reason?: string}> {
|
|
||||||
if (!PERMISSIONS_PATH) {
|
|
||||||
console.error("Permissions path not available");
|
|
||||||
return { approved: false, reason: "Permissions path not configured" };
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if this tool/command is always allowed for this workspace
|
|
||||||
if (isAlwaysAllowed(tool_name, input)) {
|
|
||||||
console.error(`Tool ${tool_name} is always allowed for this workspace`);
|
|
||||||
return { approved: true };
|
|
||||||
}
|
|
||||||
|
|
||||||
const requestId = generateRequestId();
|
|
||||||
const requestFile = path.join(PERMISSIONS_PATH, `${requestId}.request`);
|
|
||||||
const responseFile = path.join(PERMISSIONS_PATH, `${requestId}.response`);
|
|
||||||
|
|
||||||
// Write request file
|
|
||||||
const request = {
|
|
||||||
id: requestId,
|
|
||||||
tool: tool_name,
|
|
||||||
input: input,
|
|
||||||
timestamp: new Date().toISOString()
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
|
||||||
fs.writeFileSync(requestFile, JSON.stringify(request, null, 2));
|
|
||||||
|
|
||||||
// Use fs.watch to wait for response file
|
|
||||||
return new Promise<{approved: boolean, reason?: string}>((resolve) => {
|
|
||||||
const timeout = setTimeout(() => {
|
|
||||||
watcher.close();
|
|
||||||
// Clean up request file on timeout
|
|
||||||
if (fs.existsSync(requestFile)) {
|
|
||||||
fs.unlinkSync(requestFile);
|
|
||||||
}
|
|
||||||
console.error(`Permission request ${requestId} timed out`);
|
|
||||||
resolve({ approved: false, reason: "Permission request timed out" });
|
|
||||||
}, 3600000); // 1 hour timeout
|
|
||||||
|
|
||||||
const watcher = fs.watch(PERMISSIONS_PATH, (eventType, filename) => {
|
|
||||||
if (eventType === 'rename' && filename === path.basename(responseFile)) {
|
|
||||||
// Check if file exists (rename event can be for creation or deletion)
|
|
||||||
if (fs.existsSync(responseFile)) {
|
|
||||||
try {
|
|
||||||
const responseContent = fs.readFileSync(responseFile, 'utf8');
|
|
||||||
const response = JSON.parse(responseContent);
|
|
||||||
|
|
||||||
// Clean up response file
|
|
||||||
fs.unlinkSync(responseFile);
|
|
||||||
|
|
||||||
// Clear timeout and close watcher
|
|
||||||
clearTimeout(timeout);
|
|
||||||
watcher.close();
|
|
||||||
|
|
||||||
resolve({
|
|
||||||
approved: response.approved,
|
|
||||||
reason: response.approved ? undefined : "User rejected the request"
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
console.error(`Error reading response file: ${error}`);
|
|
||||||
// Continue watching in case of read error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Handle watcher errors
|
|
||||||
watcher.on('error', (error) => {
|
|
||||||
console.error(`File watcher error: ${error}`);
|
|
||||||
clearTimeout(timeout);
|
|
||||||
watcher.close();
|
|
||||||
resolve({ approved: false, reason: "File watcher error" });
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
console.error(`Error requesting permission: ${error}`);
|
|
||||||
return { approved: false, reason: `Error processing permission request: ${error}` };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
server.tool(
|
|
||||||
"approval_prompt",
|
|
||||||
'Request user permission to execute a tool via VS Code dialog',
|
|
||||||
{
|
|
||||||
tool_name: z.string().describe("The name of the tool requesting permission"),
|
|
||||||
input: z.object({}).passthrough().describe("The input for the tool"),
|
|
||||||
tool_use_id: z.string().optional().describe("The unique tool use request ID"),
|
|
||||||
},
|
|
||||||
async ({ tool_name, input }) => {
|
|
||||||
console.error(`Requesting permission for tool: ${tool_name}`);
|
|
||||||
|
|
||||||
const permissionResult = await requestPermission(tool_name, input);
|
|
||||||
|
|
||||||
const behavior = permissionResult.approved ? "allow" : "deny";
|
|
||||||
console.error(`Permission ${behavior}ed for tool: ${tool_name}`);
|
|
||||||
|
|
||||||
return {
|
|
||||||
content: [
|
|
||||||
{
|
|
||||||
type: "text",
|
|
||||||
text: behavior === "allow" ?
|
|
||||||
JSON.stringify({
|
|
||||||
behavior: behavior,
|
|
||||||
updatedInput: input,
|
|
||||||
})
|
|
||||||
:
|
|
||||||
JSON.stringify({
|
|
||||||
behavior: behavior,
|
|
||||||
message: permissionResult.reason || "Permission denied",
|
|
||||||
})
|
|
||||||
,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
async function main() {
|
|
||||||
const transport = new StdioServerTransport();
|
|
||||||
await server.connect(transport);
|
|
||||||
console.error(`Permissions MCP Server running on stdio`);
|
|
||||||
console.error(`Using permissions directory: ${PERMISSIONS_PATH}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
main().catch((error) => {
|
|
||||||
console.error("Fatal error in main():", error);
|
|
||||||
process.exit(1);
|
|
||||||
});
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
{
|
|
||||||
"mcpServers": {
|
|
||||||
"permissions": {
|
|
||||||
"command": "node",
|
|
||||||
"args": [
|
|
||||||
"./out/permissions/mcp-permissions.js"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
564
src/ui-styles.ts
564
src/ui-styles.ts
@@ -1351,6 +1351,7 @@ const styles = `
|
|||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.slash-btn,
|
||||||
.at-btn {
|
.at-btn {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
color: var(--vscode-foreground);
|
color: var(--vscode-foreground);
|
||||||
@@ -1363,6 +1364,7 @@ const styles = `
|
|||||||
transition: all 0.2s ease;
|
transition: all 0.2s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.slash-btn:hover,
|
||||||
.at-btn:hover {
|
.at-btn:hover {
|
||||||
background-color: var(--vscode-list-hoverBackground);
|
background-color: var(--vscode-list-hoverBackground);
|
||||||
}
|
}
|
||||||
@@ -1622,12 +1624,14 @@ const styles = `
|
|||||||
.tools-modal-content {
|
.tools-modal-content {
|
||||||
background-color: var(--vscode-editor-background);
|
background-color: var(--vscode-editor-background);
|
||||||
border: 1px solid var(--vscode-panel-border);
|
border: 1px solid var(--vscode-panel-border);
|
||||||
border-radius: 6px;
|
border-radius: 8px;
|
||||||
width: 500px;
|
width: 700px;
|
||||||
max-height: 600px;
|
max-width: 90vw;
|
||||||
|
max-height: 80vh;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
|
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.3);
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tools-modal-header {
|
.tools-modal-header {
|
||||||
@@ -1636,6 +1640,13 @@ const styles = `
|
|||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tools-modal-body {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tools-modal-header span {
|
.tools-modal-header span {
|
||||||
@@ -1667,6 +1678,30 @@ const styles = `
|
|||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* MCP Modal content area improvements */
|
||||||
|
#mcpModal * {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mcpModal .tools-list {
|
||||||
|
padding: 24px;
|
||||||
|
max-height: calc(80vh - 120px);
|
||||||
|
overflow-y: auto;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mcpModal .mcp-servers-list {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mcpModal .mcp-add-server {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mcpModal .mcp-add-form {
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
.tool-item {
|
.tool-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
@@ -1880,12 +1915,116 @@ const styles = `
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Slash commands modal */
|
/* Slash commands modal */
|
||||||
|
.slash-commands-search {
|
||||||
|
padding: 16px 20px;
|
||||||
|
border-bottom: 1px solid var(--vscode-panel-border);
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
background-color: var(--vscode-editor-background);
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-input-wrapper {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
border: 1px solid var(--vscode-input-border);
|
||||||
|
border-radius: 6px;
|
||||||
|
background-color: var(--vscode-input-background);
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-input-wrapper:focus-within {
|
||||||
|
border-color: var(--vscode-focusBorder);
|
||||||
|
box-shadow: 0 0 0 1px var(--vscode-focusBorder);
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-prefix {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
min-width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
background-color: var(--vscode-button-secondaryBackground);
|
||||||
|
color: var(--vscode-button-secondaryForeground);
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
border-radius: 4px 0 0 4px;
|
||||||
|
border-right: 1px solid var(--vscode-input-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.slash-commands-search input {
|
||||||
|
flex: 1;
|
||||||
|
padding: 8px 12px;
|
||||||
|
border: none !important;
|
||||||
|
background: transparent;
|
||||||
|
color: var(--vscode-input-foreground);
|
||||||
|
font-size: 13px;
|
||||||
|
outline: none !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slash-commands-search input:focus {
|
||||||
|
border: none !important;
|
||||||
|
outline: none !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slash-commands-search input::placeholder {
|
||||||
|
color: var(--vscode-input-placeholderForeground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.command-input-wrapper {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
border: 1px solid var(--vscode-input-border);
|
||||||
|
border-radius: 6px;
|
||||||
|
background-color: var(--vscode-input-background);
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
width: 100%;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.command-input-wrapper:focus-within {
|
||||||
|
border-color: var(--vscode-focusBorder);
|
||||||
|
box-shadow: 0 0 0 1px var(--vscode-focusBorder);
|
||||||
|
}
|
||||||
|
|
||||||
|
.command-prefix {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
min-width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
background-color: var(--vscode-button-secondaryBackground);
|
||||||
|
color: var(--vscode-button-secondaryForeground);
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
border-radius: 4px 0 0 4px;
|
||||||
|
border-right: 1px solid var(--vscode-input-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.slash-commands-section {
|
||||||
|
margin-bottom: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slash-commands-section:last-child {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slash-commands-section h3 {
|
||||||
|
margin: 16px 20px 12px 20px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--vscode-foreground);
|
||||||
|
}
|
||||||
|
|
||||||
.slash-commands-info {
|
.slash-commands-info {
|
||||||
padding: 12px 16px;
|
padding: 12px 20px;
|
||||||
background-color: rgba(255, 149, 0, 0.1);
|
background-color: rgba(255, 149, 0, 0.1);
|
||||||
border: 1px solid rgba(255, 149, 0, 0.2);
|
border: 1px solid rgba(255, 149, 0, 0.2);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
margin-bottom: 16px;
|
margin: 0 20px 16px 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.slash-commands-info p {
|
.slash-commands-info p {
|
||||||
@@ -1896,33 +2035,161 @@ const styles = `
|
|||||||
opacity: 0.9;
|
opacity: 0.9;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.prompt-snippet-item {
|
||||||
|
border-left: 2px solid var(--vscode-charts-blue);
|
||||||
|
background-color: rgba(0, 122, 204, 0.03);
|
||||||
|
}
|
||||||
|
|
||||||
|
.prompt-snippet-item:hover {
|
||||||
|
background-color: rgba(0, 122, 204, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-snippet-item {
|
||||||
|
border-left: 2px solid var(--vscode-charts-green);
|
||||||
|
background-color: rgba(0, 200, 83, 0.03);
|
||||||
|
border-style: dashed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-snippet-item:hover {
|
||||||
|
background-color: rgba(0, 200, 83, 0.08);
|
||||||
|
border-style: solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-snippet-form {
|
||||||
|
background-color: var(--vscode-editor-background);
|
||||||
|
border: 1px solid var(--vscode-panel-border);
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 16px;
|
||||||
|
margin: 8px 0;
|
||||||
|
animation: slideDown 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-snippet-form .form-group {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-snippet-form label {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--vscode-foreground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-snippet-form textarea {
|
||||||
|
width: 100%;
|
||||||
|
padding: 6px 8px;
|
||||||
|
border: 1px solid var(--vscode-input-border);
|
||||||
|
border-radius: 3px;
|
||||||
|
background-color: var(--vscode-input-background);
|
||||||
|
color: var(--vscode-input-foreground);
|
||||||
|
font-size: 12px;
|
||||||
|
font-family: var(--vscode-font-family);
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-snippet-form .command-input-wrapper input {
|
||||||
|
flex: 1;
|
||||||
|
padding: 6px 8px;
|
||||||
|
border: none !important;
|
||||||
|
background: transparent;
|
||||||
|
color: var(--vscode-input-foreground);
|
||||||
|
font-size: 12px;
|
||||||
|
font-family: var(--vscode-font-family);
|
||||||
|
outline: none !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-snippet-form .command-input-wrapper input:focus {
|
||||||
|
border: none !important;
|
||||||
|
outline: none !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-snippet-form textarea:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: var(--vscode-focusBorder);
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-snippet-form input::placeholder,
|
||||||
|
.add-snippet-form textarea::placeholder {
|
||||||
|
color: var(--vscode-input-placeholderForeground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-snippet-form textarea {
|
||||||
|
resize: vertical;
|
||||||
|
min-height: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-snippet-form .form-buttons {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
justify-content: flex-end;
|
||||||
|
margin-top: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-snippet-item {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.snippet-actions {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.2s ease;
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-snippet-item:hover .snippet-actions {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.snippet-delete-btn {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
color: var(--vscode-descriptionForeground);
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 4px;
|
||||||
|
border-radius: 3px;
|
||||||
|
font-size: 12px;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.snippet-delete-btn:hover {
|
||||||
|
background-color: rgba(231, 76, 60, 0.1);
|
||||||
|
color: var(--vscode-errorForeground);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
.slash-commands-list {
|
.slash-commands-list {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 8px;
|
gap: 6px;
|
||||||
max-height: 400px;
|
padding: 0 20px;
|
||||||
overflow-y: auto;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.slash-command-item {
|
.slash-command-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
padding: 12px 16px;
|
padding: 10px 14px;
|
||||||
border-radius: 6px;
|
border-radius: 4px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: all 0.2s ease;
|
transition: all 0.15s ease;
|
||||||
border: 1px solid transparent;
|
border: 1px solid transparent;
|
||||||
|
background-color: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
.slash-command-item:hover {
|
.slash-command-item:hover {
|
||||||
background-color: var(--vscode-list-hoverBackground);
|
background-color: var(--vscode-list-hoverBackground);
|
||||||
border-color: var(--vscode-focusBorder);
|
border-color: var(--vscode-list-hoverBackground);
|
||||||
}
|
}
|
||||||
|
|
||||||
.slash-command-icon {
|
.slash-command-icon {
|
||||||
font-size: 18px;
|
font-size: 16px;
|
||||||
min-width: 24px;
|
min-width: 20px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
opacity: 0.8;
|
||||||
}
|
}
|
||||||
|
|
||||||
.slash-command-content {
|
.slash-command-content {
|
||||||
@@ -1931,7 +2198,7 @@ const styles = `
|
|||||||
|
|
||||||
.slash-command-title {
|
.slash-command-title {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
font-weight: 600;
|
font-weight: 500;
|
||||||
color: var(--vscode-foreground);
|
color: var(--vscode-foreground);
|
||||||
margin-bottom: 2px;
|
margin-bottom: 2px;
|
||||||
}
|
}
|
||||||
@@ -1939,45 +2206,39 @@ const styles = `
|
|||||||
.slash-command-description {
|
.slash-command-description {
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
color: var(--vscode-descriptionForeground);
|
color: var(--vscode-descriptionForeground);
|
||||||
opacity: 0.8;
|
opacity: 0.7;
|
||||||
|
line-height: 1.3;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Custom command input */
|
/* Quick command input */
|
||||||
.custom-command-item {
|
.custom-command-item {
|
||||||
cursor: default;
|
cursor: default;
|
||||||
}
|
}
|
||||||
|
|
||||||
.custom-command-input-container {
|
.custom-command-item .command-input-wrapper {
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 2px;
|
|
||||||
margin-top: 4px;
|
margin-top: 4px;
|
||||||
|
max-width: 200px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.command-prefix {
|
.custom-command-item .command-input-wrapper input {
|
||||||
font-size: 12px;
|
flex: 1;
|
||||||
color: var(--vscode-foreground);
|
padding: 4px 6px;
|
||||||
font-weight: 500;
|
border: none !important;
|
||||||
}
|
background: transparent;
|
||||||
|
|
||||||
.custom-command-input {
|
|
||||||
background-color: var(--vscode-input-background);
|
|
||||||
border: 1px solid var(--vscode-input-border);
|
|
||||||
color: var(--vscode-input-foreground);
|
color: var(--vscode-input-foreground);
|
||||||
padding: 2px 6px;
|
|
||||||
border-radius: 3px;
|
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
outline: none;
|
|
||||||
min-width: 120px;
|
|
||||||
font-family: var(--vscode-editor-font-family);
|
font-family: var(--vscode-editor-font-family);
|
||||||
|
outline: none !important;
|
||||||
|
box-shadow: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.custom-command-input:focus {
|
.custom-command-item .command-input-wrapper input:focus {
|
||||||
border-color: var(--vscode-focusBorder);
|
border: none !important;
|
||||||
box-shadow: 0 0 0 1px var(--vscode-focusBorder);
|
outline: none !important;
|
||||||
|
box-shadow: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.custom-command-input::placeholder {
|
.custom-command-item .command-input-wrapper input::placeholder {
|
||||||
color: var(--vscode-input-placeholderForeground);
|
color: var(--vscode-input-placeholderForeground);
|
||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
}
|
}
|
||||||
@@ -2307,6 +2568,231 @@ const styles = `
|
|||||||
color: var(--vscode-foreground);
|
color: var(--vscode-foreground);
|
||||||
opacity: 0.8;
|
opacity: 0.8;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* MCP Servers styles */
|
||||||
|
.mcp-servers-list {
|
||||||
|
padding: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mcp-server-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 20px 24px;
|
||||||
|
border: 1px solid var(--vscode-panel-border);
|
||||||
|
border-radius: 8px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
background-color: var(--vscode-editor-background);
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mcp-server-item:hover {
|
||||||
|
border-color: var(--vscode-focusBorder);
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.server-info {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.server-name {
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 16px;
|
||||||
|
color: var(--vscode-foreground);
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.server-type {
|
||||||
|
display: inline-block;
|
||||||
|
background-color: var(--vscode-badge-background);
|
||||||
|
color: var(--vscode-badge-foreground);
|
||||||
|
padding: 4px 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 500;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.server-config {
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--vscode-descriptionForeground);
|
||||||
|
opacity: 0.9;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.server-delete-btn {
|
||||||
|
padding: 8px 16px;
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--vscode-errorForeground);
|
||||||
|
border-color: var(--vscode-errorForeground);
|
||||||
|
min-width: 80px;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.server-delete-btn:hover {
|
||||||
|
background-color: var(--vscode-inputValidation-errorBackground);
|
||||||
|
border-color: var(--vscode-errorForeground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.server-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
align-items: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.server-edit-btn {
|
||||||
|
padding: 8px 16px;
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--vscode-foreground);
|
||||||
|
border-color: var(--vscode-panel-border);
|
||||||
|
min-width: 80px;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.server-edit-btn:hover {
|
||||||
|
background-color: var(--vscode-list-hoverBackground);
|
||||||
|
border-color: var(--vscode-focusBorder);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mcp-add-server {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
padding: 0 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mcp-add-form {
|
||||||
|
background-color: var(--vscode-editor-background);
|
||||||
|
border: 1px solid var(--vscode-panel-border);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 24px;
|
||||||
|
margin-top: 20px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group label {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--vscode-foreground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group input,
|
||||||
|
.form-group select,
|
||||||
|
.form-group textarea {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
padding: 8px 12px;
|
||||||
|
border: 1px solid var(--vscode-input-border);
|
||||||
|
border-radius: 4px;
|
||||||
|
background-color: var(--vscode-input-background);
|
||||||
|
color: var(--vscode-input-foreground);
|
||||||
|
font-size: 13px;
|
||||||
|
font-family: var(--vscode-font-family);
|
||||||
|
box-sizing: border-box;
|
||||||
|
resize: vertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group input:focus,
|
||||||
|
.form-group select:focus,
|
||||||
|
.form-group textarea:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: var(--vscode-focusBorder);
|
||||||
|
box-shadow: 0 0 0 1px var(--vscode-focusBorder);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group textarea {
|
||||||
|
resize: vertical;
|
||||||
|
min-height: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-buttons {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
justify-content: flex-end;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-servers {
|
||||||
|
text-align: center;
|
||||||
|
color: var(--vscode-descriptionForeground);
|
||||||
|
font-style: italic;
|
||||||
|
padding: 40px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Popular MCP Servers */
|
||||||
|
.mcp-popular-servers {
|
||||||
|
margin-top: 32px;
|
||||||
|
padding-top: 24px;
|
||||||
|
border-top: 1px solid var(--vscode-panel-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mcp-popular-servers h4 {
|
||||||
|
margin: 0 0 16px 0;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--vscode-foreground);
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popular-servers-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popular-server-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
padding: 12px 16px;
|
||||||
|
background-color: var(--vscode-editor-background);
|
||||||
|
border: 1px solid var(--vscode-panel-border);
|
||||||
|
border-radius: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popular-server-item:hover {
|
||||||
|
border-color: var(--vscode-focusBorder);
|
||||||
|
background-color: var(--vscode-list-hoverBackground);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.popular-server-icon {
|
||||||
|
font-size: 24px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popular-server-info {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popular-server-name {
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--vscode-foreground);
|
||||||
|
margin-bottom: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popular-server-desc {
|
||||||
|
font-size: 11px;
|
||||||
|
color: var(--vscode-descriptionForeground);
|
||||||
|
opacity: 0.8;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
</style>`
|
</style>`
|
||||||
|
|
||||||
export default styles
|
export default styles
|
||||||
789
src/ui.ts
789
src/ui.ts
@@ -75,14 +75,15 @@ const html = `<!DOCTYPE html>
|
|||||||
<path d="M1 2.5l3 3 3-3"></path>
|
<path d="M1 2.5l3 3 3-3"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
<button class="tools-btn" onclick="showToolsModal()" title="Configure tools">
|
<button class="tools-btn" onclick="showMCPModal()" title="Configure MCP servers">
|
||||||
Tools: All
|
MCP
|
||||||
<svg width="8" height="8" viewBox="0 0 8 8" fill="currentColor">
|
<svg width="8" height="8" viewBox="0 0 8 8" fill="currentColor">
|
||||||
<path d="M1 2.5l3 3 3-3"></path>
|
<path d="M1 2.5l3 3 3-3"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="right-controls">
|
<div class="right-controls">
|
||||||
|
<button class="slash-btn" onclick="showSlashCommandsModal()" title="Slash commands">/</button>
|
||||||
<button class="at-btn" onclick="showFilePicker()" title="Reference files">@</button>
|
<button class="at-btn" onclick="showFilePicker()" title="Reference files">@</button>
|
||||||
<button class="image-btn" id="imageBtn" onclick="selectImage()" title="Attach images">
|
<button class="image-btn" id="imageBtn" onclick="selectImage()" title="Attach images">
|
||||||
<svg
|
<svg
|
||||||
@@ -148,60 +149,108 @@ const html = `<!DOCTYPE html>
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Tools modal -->
|
<!-- MCP Servers modal -->
|
||||||
<div id="toolsModal" class="tools-modal" style="display: none;">
|
<div id="mcpModal" class="tools-modal" style="display: none;">
|
||||||
<div class="tools-modal-content">
|
<div class="tools-modal-content">
|
||||||
<div class="tools-modal-header">
|
<div class="tools-modal-header">
|
||||||
<span>Claude Code Tools</span>
|
<span>MCP Servers</span>
|
||||||
<button class="tools-close-btn" onclick="hideToolsModal()">✕</button>
|
<button class="tools-close-btn" onclick="hideMCPModal()">✕</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="tools-beta-warning">
|
<div class="tools-list">
|
||||||
In Beta: All tools are enabled by default. Use at your own risk.
|
<div class="mcp-servers-list" id="mcpServersList">
|
||||||
</div>
|
<!-- MCP servers will be loaded here -->
|
||||||
<div id="toolsList" class="tools-list">
|
|
||||||
<div class="tool-item">
|
|
||||||
<input type="checkbox" id="tool-bash" checked disabled>
|
|
||||||
<label for="tool-bash">Bash - Execute shell commands</label>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="tool-item">
|
<div class="mcp-add-server">
|
||||||
<input type="checkbox" id="tool-read" checked disabled>
|
<button class="btn outlined" onclick="showAddServerForm()" id="addServerBtn">+ Add MCP Server</button>
|
||||||
<label for="tool-read">Read - Read file contents</label>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="tool-item">
|
<div class="mcp-popular-servers" id="popularServers">
|
||||||
<input type="checkbox" id="tool-edit" checked disabled>
|
<h4>Popular MCP Servers</h4>
|
||||||
<label for="tool-edit">Edit - Modify files</label>
|
<div class="popular-servers-grid">
|
||||||
|
<div class="popular-server-item" onclick="addPopularServer('context7', { type: 'http', url: 'https://context7.liam.sh/mcp' })">
|
||||||
|
<div class="popular-server-icon">📚</div>
|
||||||
|
<div class="popular-server-info">
|
||||||
|
<div class="popular-server-name">Context7</div>
|
||||||
|
<div class="popular-server-desc">Up-to-date Code Docs For Any Prompt</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="popular-server-item" onclick="addPopularServer('sequential-thinking', { type: 'stdio', command: 'npx', args: ['-y', '@modelcontextprotocol/server-sequential-thinking'] })">
|
||||||
|
<div class="popular-server-icon">🔗</div>
|
||||||
|
<div class="popular-server-info">
|
||||||
|
<div class="popular-server-name">Sequential Thinking</div>
|
||||||
|
<div class="popular-server-desc">Step-by-step reasoning capabilities</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="popular-server-item" onclick="addPopularServer('memory', { type: 'stdio', command: 'npx', args: ['-y', '@modelcontextprotocol/server-memory'] })">
|
||||||
|
<div class="popular-server-icon">🧠</div>
|
||||||
|
<div class="popular-server-info">
|
||||||
|
<div class="popular-server-name">Memory</div>
|
||||||
|
<div class="popular-server-desc">Knowledge graph storage</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="popular-server-item" onclick="addPopularServer('puppeteer', { type: 'stdio', command: 'npx', args: ['-y', '@modelcontextprotocol/server-puppeteer'] })">
|
||||||
|
<div class="popular-server-icon">🎭</div>
|
||||||
|
<div class="popular-server-info">
|
||||||
|
<div class="popular-server-name">Puppeteer</div>
|
||||||
|
<div class="popular-server-desc">Browser automation</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="popular-server-item" onclick="addPopularServer('fetch', { type: 'stdio', command: 'npx', args: ['-y', '@modelcontextprotocol/server-fetch'] })">
|
||||||
|
<div class="popular-server-icon">🌐</div>
|
||||||
|
<div class="popular-server-info">
|
||||||
|
<div class="popular-server-name">Fetch</div>
|
||||||
|
<div class="popular-server-desc">HTTP requests & web scraping</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="popular-server-item" onclick="addPopularServer('filesystem', { type: 'stdio', command: 'npx', args: ['-y', '@modelcontextprotocol/server-filesystem'] })">
|
||||||
|
<div class="popular-server-icon">📁</div>
|
||||||
|
<div class="popular-server-info">
|
||||||
|
<div class="popular-server-name">Filesystem</div>
|
||||||
|
<div class="popular-server-desc">File operations & management</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="tool-item">
|
<div class="mcp-add-form" id="addServerForm" style="display: none;">
|
||||||
<input type="checkbox" id="tool-write" checked disabled>
|
<div class="form-group">
|
||||||
<label for="tool-write">Write - Create new files</label>
|
<label for="serverName">Server Name:</label>
|
||||||
|
<input type="text" id="serverName" placeholder="my-server" required>
|
||||||
</div>
|
</div>
|
||||||
<div class="tool-item">
|
<div class="form-group">
|
||||||
<input type="checkbox" id="tool-glob" checked disabled>
|
<label for="serverType">Server Type:</label>
|
||||||
<label for="tool-glob">Glob - Find files by pattern</label>
|
<select id="serverType" onchange="updateServerForm()">
|
||||||
|
<option value="http">HTTP</option>
|
||||||
|
<option value="sse">SSE</option>
|
||||||
|
<option value="stdio">stdio</option>
|
||||||
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="tool-item">
|
<div class="form-group" id="commandGroup" style="display: none;">
|
||||||
<input type="checkbox" id="tool-grep" checked disabled>
|
<label for="serverCommand">Command:</label>
|
||||||
<label for="tool-grep">Grep - Search file contents</label>
|
<input type="text" id="serverCommand" placeholder="/path/to/server">
|
||||||
</div>
|
</div>
|
||||||
<div class="tool-item">
|
<div class="form-group" id="urlGroup">
|
||||||
<input type="checkbox" id="tool-ls" checked disabled>
|
<label for="serverUrl">URL:</label>
|
||||||
<label for="tool-ls">LS - List directory contents</label>
|
<input type="text" id="serverUrl" placeholder="https://example.com/mcp">
|
||||||
</div>
|
</div>
|
||||||
<div class="tool-item">
|
<div class="form-group" id="argsGroup" style="display: none;">
|
||||||
<input type="checkbox" id="tool-multiedit" checked disabled>
|
<label for="serverArgs">Arguments (one per line):</label>
|
||||||
<label for="tool-multiedit">MultiEdit - Edit multiple files</label>
|
<textarea id="serverArgs" placeholder="--api-key abc123" rows="3"></textarea>
|
||||||
</div>
|
</div>
|
||||||
<div class="tool-item">
|
<div class="form-group" id="envGroup" style="display: none;">
|
||||||
<input type="checkbox" id="tool-websearch" checked disabled>
|
<label for="serverEnv">Environment Variables (KEY=value, one per line):</label>
|
||||||
<label for="tool-websearch">WebSearch - Search the web</label>
|
<textarea id="serverEnv" placeholder="API_KEY=123 CACHE_DIR=/tmp" rows="3"></textarea>
|
||||||
</div>
|
</div>
|
||||||
<div class="tool-item">
|
<div class="form-group" id="headersGroup">
|
||||||
<input type="checkbox" id="tool-webfetch" checked disabled>
|
<label for="serverHeaders">Headers (KEY=value, one per line):</label>
|
||||||
<label for="tool-webfetch">WebFetch - Fetch web content</label>
|
<textarea id="serverHeaders" placeholder="Authorization=Bearer token X-API-Key=key" rows="3"></textarea>
|
||||||
|
</div>
|
||||||
|
<div class="form-buttons">
|
||||||
|
<button class="btn" onclick="saveMCPServer()">Add Server</button>
|
||||||
|
<button class="btn outlined" onclick="hideAddServerForm()">Cancel</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Settings modal -->
|
<!-- Settings modal -->
|
||||||
<div id="settingsModal" class="tools-modal" style="display: none;">
|
<div id="settingsModal" class="tools-modal" style="display: none;">
|
||||||
@@ -295,19 +344,6 @@ const html = `<!DOCTYPE html>
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h3 style="margin-top: 24px; margin-bottom: 16px; font-size: 14px; font-weight: 600;">MCP Configuration (coming soon)</h3>
|
|
||||||
<div>
|
|
||||||
<p style="font-size: 11px; color: var(--vscode-descriptionForeground); margin: 0;">
|
|
||||||
Model Context Protocol (MCP) allows Claude Code to connect to external systems and services for enhanced capabilities like databases, APIs, and tools.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div class="settings-group">
|
|
||||||
<div class="tool-item">
|
|
||||||
<input type="checkbox" id="mcp-enabled" disabled>
|
|
||||||
<label for="mcp-enabled">Enable MCP Integration <span style="font-style: italic; opacity: 0.7;">(Coming Soon)</span></label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h3 style="margin-top: 24px; margin-bottom: 16px; font-size: 14px; font-weight: 600;">Custom Slash Commands (coming soon)</h3>
|
<h3 style="margin-top: 24px; margin-bottom: 16px; font-size: 14px; font-weight: 600;">Custom Slash Commands (coming soon)</h3>
|
||||||
<div>
|
<div>
|
||||||
<p style="font-size: 11px; color: var(--vscode-descriptionForeground); margin: 0;">
|
<p style="font-size: 11px; color: var(--vscode-descriptionForeground); margin: 0;">
|
||||||
@@ -403,13 +439,121 @@ const html = `<!DOCTYPE html>
|
|||||||
<div id="slashCommandsModal" class="tools-modal" style="display: none;">
|
<div id="slashCommandsModal" class="tools-modal" style="display: none;">
|
||||||
<div class="tools-modal-content">
|
<div class="tools-modal-content">
|
||||||
<div class="tools-modal-header">
|
<div class="tools-modal-header">
|
||||||
<span>Claude Code Commands</span>
|
<span>Commands & Prompt Snippets</span>
|
||||||
<button class="tools-close-btn" onclick="hideSlashCommandsModal()">✕</button>
|
<button class="tools-close-btn" onclick="hideSlashCommandsModal()">✕</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="slash-commands-info">
|
<div class="tools-modal-body">
|
||||||
<p>These commands require the Claude CLI and will open in VS Code terminal. Return here after completion.</p>
|
|
||||||
|
<!-- Search box -->
|
||||||
|
<div class="slash-commands-search">
|
||||||
|
<div class="search-input-wrapper">
|
||||||
|
<span class="search-prefix">/</span>
|
||||||
|
<input type="text" id="slashCommandsSearch" placeholder="Search commands and snippets..." oninput="filterSlashCommands()">
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="slash-commands-list">
|
|
||||||
|
<!-- Custom Commands Section -->
|
||||||
|
<div class="slash-commands-section">
|
||||||
|
<h3>Custom Commands</h3>
|
||||||
|
<div class="slash-commands-info">
|
||||||
|
<p>Custom slash commands for quick prompt access. Click to use directly in chat.</p>
|
||||||
|
</div>
|
||||||
|
<div class="slash-commands-list" id="promptSnippetsList">
|
||||||
|
<!-- Add Custom Snippet Button -->
|
||||||
|
<div class="slash-command-item add-snippet-item" onclick="showAddSnippetForm()">
|
||||||
|
<div class="slash-command-icon">➕</div>
|
||||||
|
<div class="slash-command-content">
|
||||||
|
<div class="slash-command-title">Add Custom Command</div>
|
||||||
|
<div class="slash-command-description">Create your own slash command</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Add Custom Command Form (initially hidden) -->
|
||||||
|
<div class="add-snippet-form" id="addSnippetForm" style="display: none;">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="snippetName">Command name:</label>
|
||||||
|
<div class="command-input-wrapper">
|
||||||
|
<span class="command-prefix">/</span>
|
||||||
|
<input type="text" id="snippetName" placeholder="e.g., fix-bug" maxlength="50">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="snippetPrompt">Prompt Text:</label>
|
||||||
|
<textarea id="snippetPrompt" placeholder="e.g., Help me fix this bug in my code..." rows="3" maxlength="500"></textarea>
|
||||||
|
</div>
|
||||||
|
<div class="form-buttons">
|
||||||
|
<button class="btn" onclick="saveCustomSnippet()">Save Command</button>
|
||||||
|
<button class="btn outlined" onclick="hideAddSnippetForm()">Cancel</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Built-in Snippets -->
|
||||||
|
<div class="slash-command-item prompt-snippet-item" onclick="usePromptSnippet('performance-analysis')">
|
||||||
|
<div class="slash-command-icon">⚡</div>
|
||||||
|
<div class="slash-command-content">
|
||||||
|
<div class="slash-command-title">/performance-analysis</div>
|
||||||
|
<div class="slash-command-description">Analyze this code for performance issues and suggest optimizations</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="slash-command-item prompt-snippet-item" onclick="usePromptSnippet('security-review')">
|
||||||
|
<div class="slash-command-icon">🔒</div>
|
||||||
|
<div class="slash-command-content">
|
||||||
|
<div class="slash-command-title">/security-review</div>
|
||||||
|
<div class="slash-command-description">Review this code for security vulnerabilities</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="slash-command-item prompt-snippet-item" onclick="usePromptSnippet('implementation-review')">
|
||||||
|
<div class="slash-command-icon">🔍</div>
|
||||||
|
<div class="slash-command-content">
|
||||||
|
<div class="slash-command-title">/implementation-review</div>
|
||||||
|
<div class="slash-command-description">Review the implementation in this code</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="slash-command-item prompt-snippet-item" onclick="usePromptSnippet('code-explanation')">
|
||||||
|
<div class="slash-command-icon">📖</div>
|
||||||
|
<div class="slash-command-content">
|
||||||
|
<div class="slash-command-title">/code-explanation</div>
|
||||||
|
<div class="slash-command-description">Explain how this code works in detail</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="slash-command-item prompt-snippet-item" onclick="usePromptSnippet('bug-fix')">
|
||||||
|
<div class="slash-command-icon">🐛</div>
|
||||||
|
<div class="slash-command-content">
|
||||||
|
<div class="slash-command-title">/bug-fix</div>
|
||||||
|
<div class="slash-command-description">Help me fix this bug in my code</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="slash-command-item prompt-snippet-item" onclick="usePromptSnippet('refactor')">
|
||||||
|
<div class="slash-command-icon">🔄</div>
|
||||||
|
<div class="slash-command-content">
|
||||||
|
<div class="slash-command-title">/refactor</div>
|
||||||
|
<div class="slash-command-description">Refactor this code to improve readability and maintainability</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="slash-command-item prompt-snippet-item" onclick="usePromptSnippet('test-generation')">
|
||||||
|
<div class="slash-command-icon">🧪</div>
|
||||||
|
<div class="slash-command-content">
|
||||||
|
<div class="slash-command-title">/test-generation</div>
|
||||||
|
<div class="slash-command-description">Generate comprehensive tests for this code</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="slash-command-item prompt-snippet-item" onclick="usePromptSnippet('documentation')">
|
||||||
|
<div class="slash-command-icon">📝</div>
|
||||||
|
<div class="slash-command-content">
|
||||||
|
<div class="slash-command-title">/documentation</div>
|
||||||
|
<div class="slash-command-description">Generate documentation for this code</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Built-in Commands Section -->
|
||||||
|
<div class="slash-commands-section">
|
||||||
|
<h3>Built-in Commands</h3>
|
||||||
|
<div class="slash-commands-info">
|
||||||
|
<p>These commands require the Claude CLI and will open in VS Code terminal. Return here after completion.</p>
|
||||||
|
</div>
|
||||||
|
<div class="slash-commands-list" id="nativeCommandsList">
|
||||||
<div class="slash-command-item" onclick="executeSlashCommand('bug')">
|
<div class="slash-command-item" onclick="executeSlashCommand('bug')">
|
||||||
<div class="slash-command-icon">🐛</div>
|
<div class="slash-command-icon">🐛</div>
|
||||||
<div class="slash-command-content">
|
<div class="slash-command-content">
|
||||||
@@ -546,9 +690,9 @@ const html = `<!DOCTYPE html>
|
|||||||
<div class="slash-command-item custom-command-item">
|
<div class="slash-command-item custom-command-item">
|
||||||
<div class="slash-command-icon">⚡</div>
|
<div class="slash-command-icon">⚡</div>
|
||||||
<div class="slash-command-content">
|
<div class="slash-command-content">
|
||||||
<div class="slash-command-title">Custom Command</div>
|
<div class="slash-command-title">Quick Command</div>
|
||||||
<div class="slash-command-description">
|
<div class="slash-command-description">
|
||||||
<div class="custom-command-input-container">
|
<div class="command-input-wrapper">
|
||||||
<span class="command-prefix">/</span>
|
<span class="command-prefix">/</span>
|
||||||
<input type="text"
|
<input type="text"
|
||||||
class="custom-command-input"
|
class="custom-command-input"
|
||||||
@@ -561,6 +705,7 @@ const html = `<!DOCTYPE html>
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -923,7 +1068,7 @@ const html = `<!DOCTYPE html>
|
|||||||
} else if (valueStr.length > 100) {
|
} else if (valueStr.length > 100) {
|
||||||
const truncated = valueStr.substring(0, 97) + '...';
|
const truncated = valueStr.substring(0, 97) + '...';
|
||||||
const escapedValue = valueStr.replace(/"/g, '"').replace(/'/g, ''');
|
const escapedValue = valueStr.replace(/"/g, '"').replace(/'/g, ''');
|
||||||
result += '<strong>' + key + ':</strong> ' + truncated + ' <span class="expand-btn" data-key="' + key + '" data-value="' + escapedValue + '" onclick="toggleExpand(this)">expand</span>';
|
result += '<span class="expandable-item"><strong>' + key + ':</strong> ' + truncated + ' <span class="expand-btn" data-key="' + key + '" data-value="' + escapedValue + '" onclick="toggleExpand(this)">expand</span></span>';
|
||||||
} else {
|
} else {
|
||||||
result += '<strong>' + key + ':</strong> ' + valueStr;
|
result += '<strong>' + key + ':</strong> ' + valueStr;
|
||||||
}
|
}
|
||||||
@@ -1234,6 +1379,46 @@ const html = `<!DOCTYPE html>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function toggleExpand(button) {
|
||||||
|
const key = button.getAttribute('data-key');
|
||||||
|
const value = button.getAttribute('data-value');
|
||||||
|
|
||||||
|
// Find the container that holds just this key-value pair
|
||||||
|
let container = button.parentNode;
|
||||||
|
while (container && !container.classList.contains('expandable-item')) {
|
||||||
|
container = container.parentNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!container) {
|
||||||
|
// Fallback: create a wrapper around the current line
|
||||||
|
const parent = button.parentNode;
|
||||||
|
const wrapper = document.createElement('div');
|
||||||
|
wrapper.className = 'expandable-item';
|
||||||
|
parent.insertBefore(wrapper, button.previousSibling || button);
|
||||||
|
|
||||||
|
// Move the key, value text, and button into the wrapper
|
||||||
|
let currentNode = wrapper.nextSibling;
|
||||||
|
const nodesToMove = [];
|
||||||
|
while (currentNode && currentNode !== button.nextSibling) {
|
||||||
|
nodesToMove.push(currentNode);
|
||||||
|
currentNode = currentNode.nextSibling;
|
||||||
|
}
|
||||||
|
nodesToMove.forEach(node => wrapper.appendChild(node));
|
||||||
|
container = wrapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (button.textContent === 'expand') {
|
||||||
|
// Show full content
|
||||||
|
const decodedValue = value.replace(/"/g, '"').replace(/'/g, "'");
|
||||||
|
container.innerHTML = '<strong>' + key + ':</strong> ' + decodedValue + ' <span class="expand-btn" data-key="' + key + '" data-value="' + value + '" onclick="toggleExpand(this)">collapse</span>';
|
||||||
|
} else {
|
||||||
|
// Show truncated content
|
||||||
|
const decodedValue = value.replace(/"/g, '"').replace(/'/g, "'");
|
||||||
|
const truncated = decodedValue.substring(0, 97) + '...';
|
||||||
|
container.innerHTML = '<strong>' + key + ':</strong> ' + truncated + ' <span class="expand-btn" data-key="' + key + '" data-value="' + value + '" onclick="toggleExpand(this)">expand</span>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function sendMessage() {
|
function sendMessage() {
|
||||||
const text = messageInput.value.trim();
|
const text = messageInput.value.trim();
|
||||||
if (text) {
|
if (text) {
|
||||||
@@ -1523,8 +1708,10 @@ const html = `<!DOCTYPE html>
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Tools modal functions
|
// Tools modal functions
|
||||||
function showToolsModal() {
|
function showMCPModal() {
|
||||||
document.getElementById('toolsModal').style.display = 'flex';
|
document.getElementById('mcpModal').style.display = 'flex';
|
||||||
|
// Load existing MCP servers
|
||||||
|
loadMCPServers();
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateYoloWarning() {
|
function updateYoloWarning() {
|
||||||
@@ -1575,17 +1762,310 @@ const html = `<!DOCTYPE html>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function hideToolsModal() {
|
function hideMCPModal() {
|
||||||
document.getElementById('toolsModal').style.display = 'none';
|
document.getElementById('mcpModal').style.display = 'none';
|
||||||
|
hideAddServerForm();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close tools modal when clicking outside
|
// Close MCP modal when clicking outside
|
||||||
document.getElementById('toolsModal').addEventListener('click', (e) => {
|
document.getElementById('mcpModal').addEventListener('click', (e) => {
|
||||||
if (e.target === document.getElementById('toolsModal')) {
|
if (e.target === document.getElementById('mcpModal')) {
|
||||||
hideToolsModal();
|
hideMCPModal();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// MCP Server management functions
|
||||||
|
function loadMCPServers() {
|
||||||
|
vscode.postMessage({ type: 'loadMCPServers' });
|
||||||
|
}
|
||||||
|
|
||||||
|
function showAddServerForm() {
|
||||||
|
document.getElementById('addServerBtn').style.display = 'none';
|
||||||
|
document.getElementById('popularServers').style.display = 'none';
|
||||||
|
document.getElementById('addServerForm').style.display = 'block';
|
||||||
|
}
|
||||||
|
|
||||||
|
function hideAddServerForm() {
|
||||||
|
document.getElementById('addServerBtn').style.display = 'block';
|
||||||
|
document.getElementById('popularServers').style.display = 'block';
|
||||||
|
document.getElementById('addServerForm').style.display = 'none';
|
||||||
|
|
||||||
|
// Reset editing state
|
||||||
|
editingServerName = null;
|
||||||
|
|
||||||
|
// Reset form title and button
|
||||||
|
const formTitle = document.querySelector('#addServerForm h5');
|
||||||
|
if (formTitle) formTitle.remove();
|
||||||
|
|
||||||
|
const saveBtn = document.querySelector('#addServerForm .btn:not(.outlined)');
|
||||||
|
if (saveBtn) saveBtn.textContent = 'Add Server';
|
||||||
|
|
||||||
|
// Clear form
|
||||||
|
document.getElementById('serverName').value = '';
|
||||||
|
document.getElementById('serverName').disabled = false;
|
||||||
|
document.getElementById('serverCommand').value = '';
|
||||||
|
document.getElementById('serverUrl').value = '';
|
||||||
|
document.getElementById('serverArgs').value = '';
|
||||||
|
document.getElementById('serverEnv').value = '';
|
||||||
|
document.getElementById('serverHeaders').value = '';
|
||||||
|
document.getElementById('serverType').value = 'http';
|
||||||
|
updateServerForm();
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateServerForm() {
|
||||||
|
const serverType = document.getElementById('serverType').value;
|
||||||
|
const commandGroup = document.getElementById('commandGroup');
|
||||||
|
const urlGroup = document.getElementById('urlGroup');
|
||||||
|
const argsGroup = document.getElementById('argsGroup');
|
||||||
|
const envGroup = document.getElementById('envGroup');
|
||||||
|
const headersGroup = document.getElementById('headersGroup');
|
||||||
|
|
||||||
|
if (serverType === 'stdio') {
|
||||||
|
commandGroup.style.display = 'block';
|
||||||
|
urlGroup.style.display = 'none';
|
||||||
|
argsGroup.style.display = 'block';
|
||||||
|
envGroup.style.display = 'block';
|
||||||
|
headersGroup.style.display = 'none';
|
||||||
|
} else if (serverType === 'http' || serverType === 'sse') {
|
||||||
|
commandGroup.style.display = 'none';
|
||||||
|
urlGroup.style.display = 'block';
|
||||||
|
argsGroup.style.display = 'none';
|
||||||
|
envGroup.style.display = 'none';
|
||||||
|
headersGroup.style.display = 'block';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveMCPServer() {
|
||||||
|
const name = document.getElementById('serverName').value.trim();
|
||||||
|
const type = document.getElementById('serverType').value;
|
||||||
|
|
||||||
|
if (!name) {
|
||||||
|
// Use a simple notification instead of alert which is blocked
|
||||||
|
const notification = document.createElement('div');
|
||||||
|
notification.textContent = 'Server name is required';
|
||||||
|
notification.style.cssText = 'position: fixed; top: 20px; right: 20px; background: var(--vscode-inputValidation-errorBackground); color: var(--vscode-inputValidation-errorForeground); padding: 8px 12px; border-radius: 4px; z-index: 9999;';
|
||||||
|
document.body.appendChild(notification);
|
||||||
|
setTimeout(() => notification.remove(), 3000);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If editing, we can use the same name; if adding, check for duplicates
|
||||||
|
if (!editingServerName) {
|
||||||
|
const serversList = document.getElementById('mcpServersList');
|
||||||
|
const existingServers = serversList.querySelectorAll('.server-name');
|
||||||
|
for (let server of existingServers) {
|
||||||
|
if (server.textContent === name) {
|
||||||
|
const notification = document.createElement('div');
|
||||||
|
notification.textContent = \`Server "\${name}" already exists\`;
|
||||||
|
notification.style.cssText = 'position: fixed; top: 20px; right: 20px; background: var(--vscode-inputValidation-errorBackground); color: var(--vscode-inputValidation-errorForeground); padding: 8px 12px; border-radius: 4px; z-index: 9999;';
|
||||||
|
document.body.appendChild(notification);
|
||||||
|
setTimeout(() => notification.remove(), 3000);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const serverConfig = { type };
|
||||||
|
|
||||||
|
if (type === 'stdio') {
|
||||||
|
const command = document.getElementById('serverCommand').value.trim();
|
||||||
|
if (!command) {
|
||||||
|
const notification = document.createElement('div');
|
||||||
|
notification.textContent = 'Command is required for stdio servers';
|
||||||
|
notification.style.cssText = 'position: fixed; top: 20px; right: 20px; background: var(--vscode-inputValidation-errorBackground); color: var(--vscode-inputValidation-errorForeground); padding: 8px 12px; border-radius: 4px; z-index: 9999;';
|
||||||
|
document.body.appendChild(notification);
|
||||||
|
setTimeout(() => notification.remove(), 3000);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
serverConfig.command = command;
|
||||||
|
|
||||||
|
const argsText = document.getElementById('serverArgs').value.trim();
|
||||||
|
if (argsText) {
|
||||||
|
serverConfig.args = argsText.split('\\n').filter(line => line.trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
const envText = document.getElementById('serverEnv').value.trim();
|
||||||
|
if (envText) {
|
||||||
|
serverConfig.env = {};
|
||||||
|
envText.split('\\n').forEach(line => {
|
||||||
|
const [key, ...valueParts] = line.split('=');
|
||||||
|
if (key && valueParts.length > 0) {
|
||||||
|
serverConfig.env[key.trim()] = valueParts.join('=').trim();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else if (type === 'http' || type === 'sse') {
|
||||||
|
const url = document.getElementById('serverUrl').value.trim();
|
||||||
|
if (!url) {
|
||||||
|
const notification = document.createElement('div');
|
||||||
|
notification.textContent = 'URL is required for HTTP/SSE servers';
|
||||||
|
notification.style.cssText = 'position: fixed; top: 20px; right: 20px; background: var(--vscode-inputValidation-errorBackground); color: var(--vscode-inputValidation-errorForeground); padding: 8px 12px; border-radius: 4px; z-index: 9999;';
|
||||||
|
document.body.appendChild(notification);
|
||||||
|
setTimeout(() => notification.remove(), 3000);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
serverConfig.url = url;
|
||||||
|
|
||||||
|
const headersText = document.getElementById('serverHeaders').value.trim();
|
||||||
|
if (headersText) {
|
||||||
|
serverConfig.headers = {};
|
||||||
|
headersText.split('\\n').forEach(line => {
|
||||||
|
const [key, ...valueParts] = line.split('=');
|
||||||
|
if (key && valueParts.length > 0) {
|
||||||
|
serverConfig.headers[key.trim()] = valueParts.join('=').trim();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vscode.postMessage({
|
||||||
|
type: 'saveMCPServer',
|
||||||
|
name: name,
|
||||||
|
config: serverConfig
|
||||||
|
});
|
||||||
|
|
||||||
|
hideAddServerForm();
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteMCPServer(serverName) {
|
||||||
|
// Just delete without confirmation
|
||||||
|
vscode.postMessage({
|
||||||
|
type: 'deleteMCPServer',
|
||||||
|
name: serverName
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let editingServerName = null;
|
||||||
|
|
||||||
|
function editMCPServer(name, config) {
|
||||||
|
editingServerName = name;
|
||||||
|
|
||||||
|
// Hide add button and popular servers
|
||||||
|
document.getElementById('addServerBtn').style.display = 'none';
|
||||||
|
document.getElementById('popularServers').style.display = 'none';
|
||||||
|
|
||||||
|
// Show form
|
||||||
|
document.getElementById('addServerForm').style.display = 'block';
|
||||||
|
|
||||||
|
// Update form title and button
|
||||||
|
const formTitle = document.querySelector('#addServerForm h5') ||
|
||||||
|
document.querySelector('#addServerForm').insertAdjacentHTML('afterbegin', '<h5>Edit MCP Server</h5>') ||
|
||||||
|
document.querySelector('#addServerForm h5');
|
||||||
|
if (!document.querySelector('#addServerForm h5')) {
|
||||||
|
document.getElementById('addServerForm').insertAdjacentHTML('afterbegin', '<h5 style="margin: 0 0 20px 0; font-size: 14px; font-weight: 600;">Edit MCP Server</h5>');
|
||||||
|
} else {
|
||||||
|
document.querySelector('#addServerForm h5').textContent = 'Edit MCP Server';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update save button text
|
||||||
|
const saveBtn = document.querySelector('#addServerForm .btn:not(.outlined)');
|
||||||
|
if (saveBtn) saveBtn.textContent = 'Update Server';
|
||||||
|
|
||||||
|
// Populate form with existing values
|
||||||
|
document.getElementById('serverName').value = name;
|
||||||
|
document.getElementById('serverName').disabled = true; // Don't allow name changes when editing
|
||||||
|
|
||||||
|
document.getElementById('serverType').value = config.type || 'stdio';
|
||||||
|
|
||||||
|
if (config.command) {
|
||||||
|
document.getElementById('serverCommand').value = config.command;
|
||||||
|
}
|
||||||
|
if (config.url) {
|
||||||
|
document.getElementById('serverUrl').value = config.url;
|
||||||
|
}
|
||||||
|
if (config.args && Array.isArray(config.args)) {
|
||||||
|
document.getElementById('serverArgs').value = config.args.join('\\n');
|
||||||
|
}
|
||||||
|
if (config.env) {
|
||||||
|
const envLines = Object.entries(config.env).map(([key, value]) => \`\${key}=\${value}\`);
|
||||||
|
document.getElementById('serverEnv').value = envLines.join('\\n');
|
||||||
|
}
|
||||||
|
if (config.headers) {
|
||||||
|
const headerLines = Object.entries(config.headers).map(([key, value]) => \`\${key}=\${value}\`);
|
||||||
|
document.getElementById('serverHeaders').value = headerLines.join('\\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update form field visibility
|
||||||
|
updateServerForm();
|
||||||
|
|
||||||
|
const toolsList = document.querySelector('.tools-list');
|
||||||
|
if (toolsList) {
|
||||||
|
toolsList.scrollTop = toolsList.scrollHeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function addPopularServer(name, config) {
|
||||||
|
// Check if server already exists
|
||||||
|
const serversList = document.getElementById('mcpServersList');
|
||||||
|
const existingServers = serversList.querySelectorAll('.server-name');
|
||||||
|
for (let server of existingServers) {
|
||||||
|
if (server.textContent === name) {
|
||||||
|
const notification = document.createElement('div');
|
||||||
|
notification.textContent = \`Server "\${name}" already exists\`;
|
||||||
|
notification.style.cssText = 'position: fixed; top: 20px; right: 20px; background: var(--vscode-inputValidation-errorBackground); color: var(--vscode-inputValidation-errorForeground); padding: 8px 12px; border-radius: 4px; z-index: 9999;';
|
||||||
|
document.body.appendChild(notification);
|
||||||
|
setTimeout(() => notification.remove(), 3000);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the server
|
||||||
|
vscode.postMessage({
|
||||||
|
type: 'saveMCPServer',
|
||||||
|
name: name,
|
||||||
|
config: config
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function displayMCPServers(servers) {
|
||||||
|
const serversList = document.getElementById('mcpServersList');
|
||||||
|
serversList.innerHTML = '';
|
||||||
|
|
||||||
|
if (Object.keys(servers).length === 0) {
|
||||||
|
serversList.innerHTML = '<div class="no-servers">No MCP servers configured</div>';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [name, config] of Object.entries(servers)) {
|
||||||
|
const serverItem = document.createElement('div');
|
||||||
|
serverItem.className = 'mcp-server-item';
|
||||||
|
|
||||||
|
// Defensive check for config structure
|
||||||
|
if (!config || typeof config !== 'object') {
|
||||||
|
console.error('Invalid config for server:', name, config);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const serverType = config.type || 'stdio';
|
||||||
|
let configDisplay = '';
|
||||||
|
|
||||||
|
if (serverType === 'stdio') {
|
||||||
|
configDisplay = \`Command: \${config.command || 'Not specified'}\`;
|
||||||
|
if (config.args && Array.isArray(config.args)) {
|
||||||
|
configDisplay += \`<br>Args: \${config.args.join(' ')}\`;
|
||||||
|
}
|
||||||
|
} else if (serverType === 'http' || serverType === 'sse') {
|
||||||
|
configDisplay = \`URL: \${config.url || 'Not specified'}\`;
|
||||||
|
} else {
|
||||||
|
configDisplay = \`Type: \${serverType}\`;
|
||||||
|
}
|
||||||
|
|
||||||
|
serverItem.innerHTML = \`
|
||||||
|
<div class="server-info">
|
||||||
|
<div class="server-name">\${name}</div>
|
||||||
|
<div class="server-type">\${serverType.toUpperCase()}</div>
|
||||||
|
<div class="server-config">\${configDisplay}</div>
|
||||||
|
</div>
|
||||||
|
<div class="server-actions">
|
||||||
|
<button class="btn outlined server-edit-btn" onclick="editMCPServer('\${name}', \${JSON.stringify(config).replace(/"/g, '"')})">Edit</button>
|
||||||
|
<button class="btn outlined server-delete-btn" onclick="deleteMCPServer('\${name}')">Delete</button>
|
||||||
|
</div>
|
||||||
|
\`;
|
||||||
|
|
||||||
|
serversList.appendChild(serverItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Model selector functions
|
// Model selector functions
|
||||||
let currentModel = 'opus'; // Default model
|
let currentModel = 'opus'; // Default model
|
||||||
|
|
||||||
@@ -1605,6 +2085,10 @@ const html = `<!DOCTYPE html>
|
|||||||
// Slash commands modal functions
|
// Slash commands modal functions
|
||||||
function showSlashCommandsModal() {
|
function showSlashCommandsModal() {
|
||||||
document.getElementById('slashCommandsModal').style.display = 'flex';
|
document.getElementById('slashCommandsModal').style.display = 'flex';
|
||||||
|
// Auto-focus the search input
|
||||||
|
setTimeout(() => {
|
||||||
|
document.getElementById('slashCommandsSearch').focus();
|
||||||
|
}, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
function hideSlashCommandsModal() {
|
function hideSlashCommandsModal() {
|
||||||
@@ -1739,6 +2223,136 @@ const html = `<!DOCTYPE html>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Store custom snippets data globally
|
||||||
|
let customSnippetsData = {};
|
||||||
|
|
||||||
|
function usePromptSnippet(snippetType) {
|
||||||
|
const builtInSnippets = {
|
||||||
|
'performance-analysis': 'Analyze this code for performance issues and suggest optimizations',
|
||||||
|
'security-review': 'Review this code for security vulnerabilities',
|
||||||
|
'implementation-review': 'Review the implementation in this code',
|
||||||
|
'code-explanation': 'Explain how this code works in detail',
|
||||||
|
'bug-fix': 'Help me fix this bug in my code',
|
||||||
|
'refactor': 'Refactor this code to improve readability and maintainability',
|
||||||
|
'test-generation': 'Generate comprehensive tests for this code',
|
||||||
|
'documentation': 'Generate documentation for this code'
|
||||||
|
};
|
||||||
|
|
||||||
|
// Check built-in snippets first
|
||||||
|
let promptText = builtInSnippets[snippetType];
|
||||||
|
|
||||||
|
// If not found in built-in, check custom snippets
|
||||||
|
if (!promptText && customSnippetsData[snippetType]) {
|
||||||
|
promptText = customSnippetsData[snippetType].prompt;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (promptText) {
|
||||||
|
// Hide the modal
|
||||||
|
hideSlashCommandsModal();
|
||||||
|
|
||||||
|
// Insert the prompt into the message input
|
||||||
|
messageInput.value = promptText;
|
||||||
|
messageInput.focus();
|
||||||
|
|
||||||
|
// Auto-resize the textarea
|
||||||
|
autoResizeTextarea();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function showAddSnippetForm() {
|
||||||
|
document.getElementById('addSnippetForm').style.display = 'block';
|
||||||
|
document.getElementById('snippetName').focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
function hideAddSnippetForm() {
|
||||||
|
document.getElementById('addSnippetForm').style.display = 'none';
|
||||||
|
// Clear form fields
|
||||||
|
document.getElementById('snippetName').value = '';
|
||||||
|
document.getElementById('snippetPrompt').value = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveCustomSnippet() {
|
||||||
|
const name = document.getElementById('snippetName').value.trim();
|
||||||
|
const prompt = document.getElementById('snippetPrompt').value.trim();
|
||||||
|
|
||||||
|
if (!name || !prompt) {
|
||||||
|
alert('Please fill in both name and prompt text.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate a unique ID for the snippet
|
||||||
|
const snippetId = 'custom-' + Date.now();
|
||||||
|
|
||||||
|
// Save the snippet using VS Code global storage
|
||||||
|
const snippetData = {
|
||||||
|
name: name,
|
||||||
|
prompt: prompt,
|
||||||
|
id: snippetId
|
||||||
|
};
|
||||||
|
|
||||||
|
vscode.postMessage({
|
||||||
|
type: 'saveCustomSnippet',
|
||||||
|
snippet: snippetData
|
||||||
|
});
|
||||||
|
|
||||||
|
// Hide the form
|
||||||
|
hideAddSnippetForm();
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadCustomSnippets(snippetsData = {}) {
|
||||||
|
const snippetsList = document.getElementById('promptSnippetsList');
|
||||||
|
|
||||||
|
// Remove existing custom snippets
|
||||||
|
const existingCustom = snippetsList.querySelectorAll('.custom-snippet-item');
|
||||||
|
existingCustom.forEach(item => item.remove());
|
||||||
|
|
||||||
|
// Add custom snippets after the add button and form
|
||||||
|
const addForm = document.getElementById('addSnippetForm');
|
||||||
|
|
||||||
|
Object.values(snippetsData).forEach(snippet => {
|
||||||
|
const snippetElement = document.createElement('div');
|
||||||
|
snippetElement.className = 'slash-command-item prompt-snippet-item custom-snippet-item';
|
||||||
|
snippetElement.onclick = () => usePromptSnippet(snippet.id);
|
||||||
|
|
||||||
|
snippetElement.innerHTML = \`
|
||||||
|
<div class="slash-command-icon">📝</div>
|
||||||
|
<div class="slash-command-content">
|
||||||
|
<div class="slash-command-title">/\${snippet.name}</div>
|
||||||
|
<div class="slash-command-description">\${snippet.prompt}</div>
|
||||||
|
</div>
|
||||||
|
<div class="snippet-actions">
|
||||||
|
<button class="snippet-delete-btn" onclick="event.stopPropagation(); deleteCustomSnippet('\${snippet.id}')" title="Delete snippet">🗑️</button>
|
||||||
|
</div>
|
||||||
|
\`;
|
||||||
|
|
||||||
|
// Insert after the form
|
||||||
|
addForm.parentNode.insertBefore(snippetElement, addForm.nextSibling);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteCustomSnippet(snippetId) {
|
||||||
|
vscode.postMessage({
|
||||||
|
type: 'deleteCustomSnippet',
|
||||||
|
snippetId: snippetId
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function filterSlashCommands() {
|
||||||
|
const searchTerm = document.getElementById('slashCommandsSearch').value.toLowerCase();
|
||||||
|
const allItems = document.querySelectorAll('.slash-command-item');
|
||||||
|
|
||||||
|
allItems.forEach(item => {
|
||||||
|
const title = item.querySelector('.slash-command-title').textContent.toLowerCase();
|
||||||
|
const description = item.querySelector('.slash-command-description').textContent.toLowerCase();
|
||||||
|
|
||||||
|
if (title.includes(searchTerm) || description.includes(searchTerm)) {
|
||||||
|
item.style.display = 'flex';
|
||||||
|
} else {
|
||||||
|
item.style.display = 'none';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function openModelTerminal() {
|
function openModelTerminal() {
|
||||||
vscode.postMessage({
|
vscode.postMessage({
|
||||||
type: 'openModelTerminal'
|
type: 'openModelTerminal'
|
||||||
@@ -2141,11 +2755,28 @@ const html = `<!DOCTYPE html>
|
|||||||
case 'permissionRequest':
|
case 'permissionRequest':
|
||||||
addPermissionRequestMessage(message.data);
|
addPermissionRequestMessage(message.data);
|
||||||
break;
|
break;
|
||||||
|
case 'mcpServers':
|
||||||
|
displayMCPServers(message.data);
|
||||||
|
break;
|
||||||
|
case 'mcpServerSaved':
|
||||||
|
loadMCPServers(); // Reload the servers list
|
||||||
|
addMessage('✅ MCP server "' + message.data.name + '" saved successfully', 'system');
|
||||||
|
break;
|
||||||
|
case 'mcpServerDeleted':
|
||||||
|
loadMCPServers(); // Reload the servers list
|
||||||
|
addMessage('✅ MCP server "' + message.data.name + '" deleted successfully', 'system');
|
||||||
|
break;
|
||||||
|
case 'mcpServerError':
|
||||||
|
addMessage('❌ Error with MCP server: ' + message.data.error, 'error');
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Permission request functions
|
// Permission request functions
|
||||||
function addPermissionRequestMessage(data) {
|
function addPermissionRequestMessage(data) {
|
||||||
|
const messagesDiv = document.getElementById('messages');
|
||||||
|
const shouldScroll = shouldAutoScroll(messagesDiv);
|
||||||
|
|
||||||
const messageDiv = document.createElement('div');
|
const messageDiv = document.createElement('div');
|
||||||
messageDiv.className = 'message permission-request';
|
messageDiv.className = 'message permission-request';
|
||||||
|
|
||||||
@@ -2852,6 +3483,11 @@ const html = `<!DOCTYPE html>
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Request custom snippets from VS Code on page load
|
||||||
|
vscode.postMessage({
|
||||||
|
type: 'getCustomSnippets'
|
||||||
|
});
|
||||||
|
|
||||||
// Detect slash commands input
|
// Detect slash commands input
|
||||||
messageInput.addEventListener('input', (e) => {
|
messageInput.addEventListener('input', (e) => {
|
||||||
const value = messageInput.value;
|
const value = messageInput.value;
|
||||||
@@ -2866,7 +3502,22 @@ const html = `<!DOCTYPE html>
|
|||||||
window.addEventListener('message', event => {
|
window.addEventListener('message', event => {
|
||||||
const message = event.data;
|
const message = event.data;
|
||||||
|
|
||||||
if (message.type === 'settingsData') {
|
if (message.type === 'customSnippetsData') {
|
||||||
|
// Update global custom snippets data
|
||||||
|
customSnippetsData = message.data || {};
|
||||||
|
// Refresh the snippets display
|
||||||
|
loadCustomSnippets(customSnippetsData);
|
||||||
|
} else if (message.type === 'customSnippetSaved') {
|
||||||
|
// Refresh snippets after saving
|
||||||
|
vscode.postMessage({
|
||||||
|
type: 'getCustomSnippets'
|
||||||
|
});
|
||||||
|
} else if (message.type === 'customSnippetDeleted') {
|
||||||
|
// Refresh snippets after deletion
|
||||||
|
vscode.postMessage({
|
||||||
|
type: 'getCustomSnippets'
|
||||||
|
});
|
||||||
|
} else if (message.type === 'settingsData') {
|
||||||
// Update UI with current settings
|
// Update UI with current settings
|
||||||
const thinkingIntensity = message.data['thinking.intensity'] || 'think';
|
const thinkingIntensity = message.data['thinking.intensity'] || 'think';
|
||||||
const intensityValues = ['think', 'think-hard', 'think-harder', 'ultrathink'];
|
const intensityValues = ['think', 'think-hard', 'think-harder', 'ultrathink'];
|
||||||
|
|||||||
@@ -13,5 +13,8 @@
|
|||||||
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
|
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
|
||||||
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
|
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
|
||||||
// "noUnusedParameters": true, /* Report errors on unused parameters. */
|
// "noUnusedParameters": true, /* Report errors on unused parameters. */
|
||||||
}
|
},
|
||||||
|
"exclude": [
|
||||||
|
"mcp-permissions.js"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user