28 Commits

Author SHA1 Message Date
andrepimenta
43c1c85efb Fix permissions and add windows support 2025-07-20 00:07:02 +01:00
Andre Pimenta
b07857bf57 Update README.md 2025-07-16 00:00:06 +01:00
andrepimenta
c9677b6185 Changelog and readme 2025-07-15 22:58:59 +01:00
andrepimenta
2053e768a8 Yolo mode warning less scary 2025-07-14 23:59:22 +01:00
andrepimenta
980d19bcb2 Yolo mode a bit less subtle 2025-07-14 23:55:13 +01:00
andrepimenta
8a581908e3 Yolo mode 2025-07-14 23:46:03 +01:00
andrepimenta
2feaed600d Fix permissions on side panel 2025-07-14 23:15:40 +01:00
andrepimenta
826c25bdd6 Remove custom commands from settings 2025-07-14 22:18:29 +01:00
Andre Pimenta
3e8a9630bd Merge pull request #49 from andrepimenta/feature/mcp-support
Feature/mcp support
2025-07-14 22:12:13 +01:00
andrepimenta
00f4e272b6 Custom commands 2025-07-14 22:11:48 +01:00
andrepimenta
2aa7db86e7 custom commands init 2025-07-14 21:57:33 +01:00
andrepimenta
42a5ebf763 Remove MCP from settings 2025-07-14 21:26:55 +01:00
andrepimenta
5b671485a1 Fix panel sending multiple prompts 2025-07-14 20:47:16 +01:00
andrepimenta
6e9893e3f3 Working in windows 2025-07-12 13:24:25 +01:00
andrepimenta
5c33bc94c1 Fix expand 2025-07-12 00:10:10 +01:00
andrepimenta
1681bea37f Filter internal mcp 2025-07-12 00:02:12 +01:00
andrepimenta
b8e5c253a3 Popular MCPs 2025-07-11 23:57:55 +01:00
andrepimenta
2eceda51ed Edit MCP 2025-07-11 23:19:33 +01:00
andrepimenta
cba6138828 Add popular MCPs 2025-07-11 23:06:35 +01:00
andrepimenta
03b0eb96fe MCP Servers UI 2025-07-11 22:39:48 +01:00
andrepimenta
cf46551f00 Don't override mcp-servers.json 2025-07-09 14:48:18 +01:00
andrepimenta
857d55e8d1 change permissions mcp to claude-code-chat-permissions 2025-07-09 14:43:10 +01:00
andrepimenta
ae9ad4a794 Showing mcp servers 2025-07-09 14:39:33 +01:00
andrepimenta
83584fff60 MCP modal 2025-07-09 14:32:12 +01:00
andrepimenta
521f9a7d68 Only scroll to bottom if the user is at bottom 2025-07-09 13:56:35 +01:00
andrepimenta
4f5a4cb7ef Copy code block 2025-07-09 13:46:32 +01:00
andrepimenta
234516c1ef Parse code blocks 2025-07-09 13:36:25 +01:00
Andre Pimenta
b261f5cb8a Merge pull request #45 from andrepimenta/feature/permissions
Feature/permissions
2025-07-09 13:17:31 +01:00
12 changed files with 16034 additions and 1132 deletions

View File

@@ -9,4 +9,5 @@ vsc-extension-quickstart.md
**/*.map
**/*.ts
**/.vscode-test.*
backup
backup
.claude

View File

@@ -4,6 +4,118 @@ All notable changes to the "claude-code-chat" extension will be documented in th
Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file.
## [1.0.0] - 2025-01-15
### 🚀 Major Features Added
#### **Advanced Permissions Management System**
- Complete permissions framework with MCP integration for secure tool execution
- Interactive permission dialogs with detailed tool information and command previews
- "Always Allow" functionality with smart command pattern matching for common tools (npm, git, docker, etc.)
- YOLO mode for power users to skip all permission checks
- Comprehensive permissions settings UI with ability to add/remove specific permissions
- File system watcher for real-time permission request handling
- Workspace-specific permission storage and management
#### **MCP (Model Context Protocol) Server Management**
- Complete MCP server configuration interface
- Popular MCP servers gallery with one-click installation
- Custom MCP server creation with validation
- Server management (edit, delete, enable/disable)
- Automatic permissions server integration
- WSL path conversion for cross-platform compatibility
#### **Sidebar Integration & Multi-Panel Support**
- Native VS Code sidebar view with full chat functionality
- Smart panel management (auto-close main panel when sidebar opens)
- Persistent session state across panel switches
- Proper webview lifecycle management
- Activity bar integration with custom icon
#### **Image & Clipboard Enhancements**
- Drag-and-drop image support directly into chat
- Clipboard image paste functionality (Ctrl+V for screenshots)
- Multiple image selection with VS Code's native file picker
- Automatic image organization in `.claude/claude-code-chat-images/` folder
- Automatic `.gitignore` creation for image folders
- Support for PNG, JPG, JPEG, GIF, SVG, WebP, BMP formats
#### **Code Block & Syntax Improvements**
- Enhanced markdown parsing with proper code block detection
- Syntax highlighting for code blocks with language detection
- Copy-to-clipboard functionality for code blocks
- Improved inline code rendering
- Better handling of technical identifiers and underscores
### 🎨 UI/UX Improvements
#### **Settings & Configuration**
- Comprehensive settings modal with organized sections
- YOLO mode toggle with visual warnings and explanations
- Real-time settings synchronization between UI and VS Code config
- Better visual hierarchy and professional styling
- Smart configuration validation and error handling
#### **Message & Chat Interface**
- Improved message spacing and visual consistency
- Enhanced tool result display with better formatting
- Smart scrolling behavior (only auto-scroll if user is at bottom)
- Loading indicators and processing states
- Better error handling and user feedback
#### **YOLO Mode Visual Design**
- Less subtle YOLO mode section (increased opacity and size)
- Changed warning icon from ⚠️ to 🚀 for less intimidating appearance
- Soft tomato red styling that's noticeable but not scary
- Clear explanation of YOLO mode functionality
### 🔧 Technical Enhancements
#### **Session & State Management**
- Persistent session state across VS Code restarts
- Proper cleanup of resources and event listeners
- Better error handling for failed operations
- Improved memory management for large conversations
#### **Cross-Platform Compatibility**
- Enhanced WSL support with proper path conversion
- Windows-specific improvements and fixes
- Better handling of different operating system environments
- Improved subprocess management and cleanup
#### **Performance Optimizations**
- Reduced context usage with more efficient tool operations
- Better file search and workspace integration
- Optimized message handling and UI updates
- Improved extension activation and initialization times
#### **Developer Experience**
- Better error messages and debugging information
- Improved extension logging and troubleshooting
- Enhanced development workflow support
- Better TypeScript integration and type safety
### 🐛 Bug Fixes
- Fixed multiple permission prompts being sent simultaneously
- Resolved panel management issues with multiple webviews
- Fixed expand/collapse functionality for long content
- Corrected Unix timestamp parsing for usage limit messages
- Fixed WSL integration on Windows systems
- Resolved markdown parsing issues with underscores in code
- Fixed copy-paste functionality for images and code blocks
- Corrected file path handling across different platforms
### 🔄 Breaking Changes
- Permission system now requires explicit approval for tool execution (unless YOLO mode is enabled)
- Image files are now stored in `.claude/claude-code-chat-images/` instead of root directory
- MCP configuration moved to extension storage instead of global config
### 📚 Documentation & Community
- Updated README with comprehensive feature documentation
- Fixed GitHub issues link in repository
- Enhanced examples and usage guides
- Better onboarding experience for new users
## [0.1.3] - 2025-06-24
### 🚀 Features Added

View File

@@ -15,15 +15,16 @@ Ditch the command line and experience Claude Code like never before. This extens
## ✨ **Why Choose Claude Code Chat?**
🖥️ **No Terminal Required** - Beautiful chat interface replaces command-line interactions
**Restore Checkpoints** - Undo changes and restore code to any previous state
**Restore Checkpoints** - Undo changes and restore code to any previous state
🔌 **MCP Server Support** - Complete Model Context Protocol server management
💾 **Conversation History** - Automatic conversation history and session management
🎨 **VS Code Native** - Claude Code integrated directly into VS Code with native theming
🎨 **VS Code Native** - Claude Code integrated directly into VS Code with native theming and sidebar support
🧠 **Plan and Thinking modes** - Plan First and configurable Thinking modes for better results
**Smart File Context and Commands** - Reference any file with simple @ mentions and / for commands
**Smart File/Image Context and Custom Commands** - Reference any file, copy images or screenshots, and create custom commands
🤖 **Model Selection** - Choose between Opus, Sonnet, or Default based on your needs
🐧 **WSL Support** - Full Windows Subsystem for Linux integration and compatibility
🐧 **Windows/WSL Support** - Full Windows support and Windows Subsystem for Linux integration and compatibility
![Claude Code Chat Cut](https://github.com/user-attachments/assets/d4ded28f-a4ed-4862-9766-c1ff89947775)
![Claude Code Chat 1 0 0](https://github.com/user-attachments/assets/5954a74c-eff7-4205-8482-6a1c9de6e102)
---
@@ -34,8 +35,9 @@ Ditch the command line and experience Claude Code like never before. This extens
- No terminal required - everything through the UI
- Real-time streaming responses with typing indicators
- One-click message copying with visual feedback
- Rich markdown support for code blocks and formatting
- Enhanced markdown support with syntax highlighting
- Auto-resizing input that grows with your content
- Copy-to-clipboard for code blocks
### ⏪ **Checkpoint & Session Management**
- **Restore Checkpoints** - Instantly undo changes and restore to any previous state
@@ -45,9 +47,36 @@ Ditch the command line and experience Claude Code like never before. This extens
- Real-time cost and token tracking
- Session statistics and performance metrics
### 🔌 **MCP Server Management** ⭐ **NEW IN V1.0**
- **Popular Servers Gallery** - One-click installation of common MCP servers
- **Custom Server Creation** - Build and configure your own MCP servers
- **Server Management** - Edit, delete, enable/disable servers through UI
- **Automatic Integration** - Seamless permissions and tool integration
- **Cross-platform Support** - Full WSL compatibility with path conversion
### 🔒 **Advanced Permissions System** ⭐ **NEW IN V1.0**
- **Interactive Permission Dialogs** - Detailed tool information with command previews
- **Always Allow Functionality** - Smart command pattern matching for common tools (npm, git, docker)
- **YOLO Mode** - Skip all permission checks for power users
- **Workspace Permissions** - Granular control over what tools can execute
- **Real-time Permission Management** - Add/remove permissions through intuitive UI
### 🖼️ **Image & Clipboard Support** ⭐ **NEW IN V1.0**
- **Drag & Drop Images** - Simply drag images directly into the chat
- **Clipboard Paste** - Press Ctrl+V to paste screenshots and copied images
- **Multiple Image Selection** - Choose multiple images through VS Code's file picker
- **Organized Storage** - Automatic organization in `.claude/claude-code-chat-images/`
- **Format Support** - PNG, JPG, JPEG, GIF, SVG, WebP, BMP formats
### 📱 **Sidebar Integration** ⭐ **NEW IN V1.0**
- **Native VS Code Sidebar** - Full chat functionality in the sidebar panel
- **Smart Panel Management** - Automatic switching between main and sidebar views
- **Persistent Sessions** - State maintained across panel switches
- **Activity Bar Integration** - Quick access from VS Code's activity bar
### 📁 **Smart File Integration**
- Type `@` to instantly search and reference workspace files
- Image attachments via file browser
- Image attachments via file browser and copy-paste screeshots
- Lightning-fast file search across your entire project
- Seamless context preservation for multi-file discussions
@@ -55,6 +84,7 @@ Ditch the command line and experience Claude Code like never before. This extens
- Visual dashboard showing all available Claude Code tools
- Real-time tool execution with formatted results
- Process control - start, stop, and monitor operations
- Smart permission system for secure tool execution
### 🎨 **VS Code Integration**
- Native theming that matches your editor
@@ -214,31 +244,39 @@ Example configuration in `settings.json`:
- Type `@` followed by your search term to quickly reference files
- Use `@src/` to narrow down to specific directories
- Reference multiple files in one message for cross-file analysis
- **NEW**: Copy-paste images directly into chat for visual context
- **NEW**: Paste screenshots with Ctrl+V for instant visual communication
### ⚡ **Productivity Boosters**
- **Creates checkpoints automatically** before changes for safe experimentation
- **Restore instantly** if changes don't work out as expected
- **NEW**: Permission system prevents accidental tool execution
- **NEW**: YOLO mode for power users who want speed over safety
- Use the stop button to cancel long-running operations
- Copy message contents to reuse Claude's responses
- Open history panel to reference previous conversations
- **NEW**: Sidebar integration for multi-panel workflow
### 🎨 **Interface Customization**
- The UI automatically adapts to your VS Code theme
- Messages are color-coded: Green for you, Blue for Claude
- Hover over messages to reveal the copy button
- **NEW**: Enhanced code block rendering with syntax highlighting
- **NEW**: Copy-to-clipboard functionality for code blocks
---
## 🔧 **Advanced Features**
### 🛠️ **Tool Integration**
Claude Code Chat provides full access to all Claude Code tools:
- **Bash** - Execute shell commands
Claude Code Chat provides secure access to all Claude Code tools:
- **Bash** - Execute shell commands with permission controls
- **File Operations** - Read, write, and edit files
- **Search** - Grep and glob pattern matching
- **Search** - Grep and glob pattern matching across workspace
- **Web** - Fetch and search web content
- **Multi-edit** - Batch file modifications
- **While in Beta, all tools are enabled by default, use at your own risk!**
- **MCP Servers** - Extend functionality with Model Context Protocol servers
- **Permissions System** - Granular control over tool execution for security
### 📊 **Analytics & Monitoring**
- **Real-time cost tracking** - Monitor your API usage

13954
mcp-permissions.js Normal file

File diff suppressed because one or more lines are too long

719
package-lock.json generated
View File

@@ -1,15 +1,14 @@
{
"name": "claude-code-chat",
"version": "0.1.3",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "claude-code-chat",
"version": "0.1.3",
"version": "1.0.0",
"license": "SEE LICENSE IN LICENSE",
"devDependencies": {
"@modelcontextprotocol/sdk": "^1.15.0",
"@types/mocha": "^10.0.10",
"@types/node": "20.x",
"@types/vscode": "^1.94.0",
@@ -19,8 +18,7 @@
"@vscode/test-electron": "^2.5.2",
"@vscode/vsce": "^3.5.0",
"eslint": "^9.25.1",
"typescript": "^5.8.3",
"zod": "^3.25.76"
"typescript": "^5.8.3"
},
"engines": {
"vscode": "^1.94.0"
@@ -558,30 +556,6 @@
"@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": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@@ -1539,43 +1513,6 @@
"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": {
"version": "8.14.1",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz",
@@ -1834,27 +1771,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": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
@@ -1952,16 +1868,6 @@
"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": {
"version": "9.1.0",
"resolved": "https://registry.npmjs.org/c8/-/c8-9.1.0.tgz",
@@ -2317,50 +2223,6 @@
"dev": true,
"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": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
@@ -2368,26 +2230,6 @@
"dev": true,
"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": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
@@ -2395,20 +2237,6 @@
"dev": true,
"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": {
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
@@ -2565,16 +2393,6 @@
"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": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz",
@@ -2695,13 +2513,6 @@
"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": {
"version": "9.2.2",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
@@ -2709,16 +2520,6 @@
"dev": true,
"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": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/encoding-sniffer/-/encoding-sniffer-0.2.1.tgz",
@@ -2832,13 +2633,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": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
@@ -3093,39 +2887,6 @@
"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": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz",
@@ -3136,88 +2897,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": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
@@ -3317,24 +2996,6 @@
"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": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
@@ -3416,26 +3077,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": {
"version": "1.0.0",
"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"
}
},
"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": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
@@ -3936,16 +3550,6 @@
"dev": 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": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
@@ -4064,13 +3668,6 @@
"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": {
"version": "0.1.0",
"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==",
"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": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
@@ -4921,16 +4495,6 @@
"dev": true,
"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": {
"version": "3.75.0",
"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"
}
},
"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": {
"version": "1.13.4",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
@@ -5034,19 +4588,6 @@
"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": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@@ -5386,16 +4927,6 @@
"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": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
@@ -5443,16 +4974,6 @@
"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": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/path-type/-/path-type-6.0.0.tgz",
@@ -5490,16 +5011,6 @@
"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": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz",
@@ -5553,20 +5064,6 @@
"dev": true,
"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": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz",
@@ -5643,32 +5140,6 @@
"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": {
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
@@ -5823,23 +5294,6 @@
"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": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.0.0.tgz",
@@ -5929,52 +5383,6 @@
"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": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz",
@@ -5985,22 +5393,6 @@
"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": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
@@ -6008,13 +5400,6 @@
"dev": true,
"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": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
@@ -6237,16 +5622,6 @@
"integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
"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": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz",
@@ -6688,16 +6063,6 @@
"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": {
"version": "2.1.0",
"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"
}
},
"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": {
"version": "1.8.11",
"resolved": "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-1.8.11.tgz",
@@ -6876,16 +6203,6 @@
"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": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
@@ -6943,16 +6260,6 @@
"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": {
"version": "4.14.0",
"resolved": "https://registry.npmjs.org/version-range/-/version-range-4.14.0.tgz",
@@ -7280,26 +6587,6 @@
"funding": {
"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"
}
}
}
}

View File

@@ -2,7 +2,7 @@
"name": "claude-code-chat",
"displayName": "Claude Code Chat",
"description": "Beautiful Claude Code Chat Interface for VS Code",
"version": "0.1.3",
"version": "1.0.3",
"publisher": "AndrePimenta",
"author": "Andre Pimenta",
"repository": {
@@ -207,8 +207,6 @@
"@vscode/test-electron": "^2.5.2",
"@vscode/vsce": "^3.5.0",
"eslint": "^9.25.1",
"typescript": "^5.8.3",
"@modelcontextprotocol/sdk": "^1.15.0",
"zod": "^3.25.76"
"typescript": "^5.8.3"
}
}

View File

@@ -86,6 +86,7 @@ class ClaudeChatProvider {
private _webview: vscode.Webview | undefined;
private _webviewView: vscode.WebviewView | undefined;
private _disposables: vscode.Disposable[] = [];
private _messageHandlerDisposable: vscode.Disposable | undefined;
private _totalCost: number = 0;
private _totalTokensInput: number = 0;
private _totalTokensOutput: number = 0;
@@ -121,7 +122,6 @@ class ClaudeChatProvider {
this._initializeBackupRepo();
this._initializeConversations();
this._initializeMCPConfig();
this._initializePermissions();
// Load conversation index from workspace state
this._conversationIndex = this._context.workspaceState.get('claude.conversationIndex', []);
@@ -165,6 +165,7 @@ class ClaudeChatProvider {
this._panel.onDidDispose(() => this.dispose(), null, this._disposables);
this._setupWebviewMessageHandler(this._panel.webview);
this._initializePermissions();
// Resume session from latest conversation
const latestConversation = this._getLatestConversation();
@@ -286,11 +287,38 @@ class ClaudeChatProvider {
case 'addPermission':
this._addPermission(message.toolName, message.command);
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;
case 'enableYoloMode':
this._enableYoloMode();
return;
}
}
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),
null,
this._disposables
@@ -317,6 +345,7 @@ class ClaudeChatProvider {
this._webview.html = this._getHtmlForWebview();
this._setupWebviewMessageHandler(this._webview);
this._initializePermissions();
// Initialize the webview
this._initializeWebview();
@@ -341,7 +370,9 @@ class ClaudeChatProvider {
public reinitializeWebview() {
// Only reinitialize if we have a webview (sidebar)
if (this._webview) {
this._initializePermissions();
this._initializeWebview();
// Set up message handler for the webview
this._setupWebviewMessageHandler(this._webview);
}
}
@@ -407,9 +438,6 @@ class ClaudeChatProvider {
data: 'Claude is working...'
});
// Call claude with the message via stdin using stream-json format
console.log('Calling Claude with message via stdin:', message);
// Build command arguments with session management
const args = [
'-p',
@@ -427,16 +455,15 @@ class ClaudeChatProvider {
// Add MCP configuration for permissions
const mcpConfigPath = this.getMCPConfigPath();
if (mcpConfigPath) {
args.push('--mcp-config', mcpConfigPath);
args.push('--allowedTools', 'mcp__permissions__approval_prompt');
args.push('--permission-prompt-tool', 'mcp__permissions__approval_prompt');
args.push('--mcp-config', this.convertToWSLPath(mcpConfigPath));
args.push('--allowedTools', 'mcp__claude-code-chat-permissions__approval_prompt');
args.push('--permission-prompt-tool', 'mcp__claude-code-chat-permissions__approval_prompt');
}
}
// Add model selection if not using default
if (this._selectedModel && this._selectedModel !== 'default') {
args.push('--model', this._selectedModel);
console.log('Using model:', this._selectedModel);
}
// Add session resume if we have a current session
@@ -456,9 +483,11 @@ class ClaudeChatProvider {
let claudeProcess: cp.ChildProcess;
if (wslEnabled) {
// Use WSL
// Use WSL with bash -ic for proper environment loading
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(' ')}`;
claudeProcess = cp.spawn('wsl', ['-d', wslDistro, 'bash', '-ic', wslCommand], {
cwd: cwd,
stdio: ['pipe', 'pipe', 'pipe'],
env: {
@@ -471,6 +500,7 @@ class ClaudeChatProvider {
// Use native claude command
console.log('Using native Claude command');
claudeProcess = cp.spawn('claude', args, {
shell: process.platform === 'win32',
cwd: cwd,
stdio: ['pipe', 'pipe', 'pipe'],
env: {
@@ -567,8 +597,6 @@ class ClaudeChatProvider {
}
private _processJsonStreamData(jsonData: any) {
console.log('Received JSON data:', jsonData);
switch (jsonData.type) {
case 'system':
if (jsonData.subtype === 'init') {
@@ -653,6 +681,12 @@ class ClaudeChatProvider {
for (const content of jsonData.message.content) {
if (content.type === 'tool_result') {
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;
// Find the last tool use to get the tool name
@@ -782,6 +816,9 @@ class ClaudeChatProvider {
}
public newSessionOnConfigChange() {
// Reinitialize MCP config with new WSL paths
this._initializeMCPConfig();
// Start a new session due to configuration change
this._newSession();
@@ -1017,27 +1054,41 @@ class ClaudeChatProvider {
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 mcpPermissionsPath = path.join(this._extensionUri.fsPath, 'out', 'permissions', 'mcp-permissions.js');
const permissionRequestsPath = path.join(storagePath, 'permission-requests');
const mcpPermissionsPath = this.convertToWSLPath(path.join(this._extensionUri.fsPath, 'mcp-permissions.js'));
const permissionRequestsPath = this.convertToWSLPath(path.join(storagePath, 'permission-requests'));
const mcpConfig = {
mcpServers: {
permissions: {
command: 'node',
args: [mcpPermissionsPath],
env: {
CLAUDE_PERMISSIONS_PATH: permissionRequestsPath
}
}
// Load existing config or create new one
let mcpConfig: any = { mcpServers: {} };
const mcpConfigUri = vscode.Uri.file(mcpConfigPath);
try {
const existingContent = await vscode.workspace.fs.readFile(mcpConfigUri);
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));
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) {
console.error('Failed to initialize MCP config:', error.message);
}
@@ -1045,11 +1096,17 @@ class ClaudeChatProvider {
private async _initializePermissions(): Promise<void> {
try {
if(this._permissionWatcher){
this._permissionWatcher.dispose();
this._permissionWatcher = undefined;
}
const storagePath = this._context.storageUri?.fsPath;
if (!storagePath) {return;}
// Create permission requests directory
this._permissionRequestsPath = path.join(storagePath, 'permission-requests');
this._permissionRequestsPath = path.join(path.join(storagePath, 'permission-requests'));
try {
await vscode.workspace.fs.stat(vscode.Uri.file(this._permissionRequestsPath));
} catch {
@@ -1057,12 +1114,15 @@ class ClaudeChatProvider {
console.log(`Created permission requests directory at: ${this._permissionRequestsPath}`);
}
console.log("DIRECTORY-----", this._permissionRequestsPath)
// Set up file watcher for *.request files
this._permissionWatcher = vscode.workspace.createFileSystemWatcher(
new vscode.RelativePattern(this._permissionRequestsPath, '*.request')
);
this._permissionWatcher.onDidCreate(async (uri) => {
console.log("----file", uri)
// Only handle file scheme URIs, ignore vscode-userdata scheme
if (uri.scheme === 'file') {
await this._handlePermissionRequest(uri);
@@ -1451,10 +1511,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 {
const storagePath = this._context.storageUri?.fsPath;
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 {
@@ -1783,15 +2041,39 @@ class ClaudeChatProvider {
});
}
private async _enableYoloMode(): Promise<void> {
try {
// Update VS Code configuration to enable YOLO mode
const config = vscode.workspace.getConfiguration('claudeCodeChat');
// Clear any global setting and set workspace setting
await config.update('permissions.yoloMode', true, vscode.ConfigurationTarget.Workspace);
console.log('YOLO Mode enabled - all future permissions will be skipped');
// Send updated settings to UI
this._sendCurrentSettings();
} catch (error) {
console.error('Error enabling YOLO mode:', error);
}
}
private async _updateSettings(settings: { [key: string]: any }): Promise<void> {
const config = vscode.workspace.getConfiguration('claudeCodeChat');
try {
for (const [key, value] of Object.entries(settings)) {
await config.update(key, value, vscode.ConfigurationTarget.Global);
if (key === 'permissions.yoloMode') {
// YOLO mode is workspace-specific
await config.update(key, value, vscode.ConfigurationTarget.Workspace);
} else {
// Other settings are global (user-wide)
await config.update(key, value, vscode.ConfigurationTarget.Global);
}
}
vscode.window.showInformationMessage('Settings updated successfully');
console.log('Settings updated:', settings);
} catch (error) {
console.error('Failed to update settings:', error);
vscode.window.showErrorMessage('Failed to update settings');
@@ -1990,6 +2272,12 @@ class ClaudeChatProvider {
this._panel = undefined;
}
// Dispose message handler if it exists
if (this._messageHandlerDisposable) {
this._messageHandlerDisposable.dispose();
this._messageHandlerDisposable = undefined;
}
while (this._disposables.length) {
const disposable = this._disposables.pop();
if (disposable) {

View File

@@ -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);
});

View File

@@ -1,10 +0,0 @@
{
"mcpServers": {
"permissions": {
"command": "node",
"args": [
"./out/permissions/mcp-permissions.js"
]
}
}
}

View File

@@ -86,8 +86,8 @@ const styles = `
/* Permission Request */
.permission-request {
margin: 4px 12px 20px 12px;
background-color: var(--vscode-inputValidation-warningBackground);
border: 1px solid var(--vscode-inputValidation-warningBorder);
background-color: rgba(252, 188, 0, 0.1);
border: 1px solid rgba(252, 188, 0, 0.3);
border-radius: 8px;
padding: 16px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
@@ -107,6 +107,86 @@ const styles = `
font-size: 16px;
}
.permission-menu {
position: relative;
margin-left: auto;
}
.permission-menu-btn {
background: none;
border: none;
color: var(--vscode-descriptionForeground);
cursor: pointer;
padding: 4px 8px;
border-radius: 4px;
font-size: 16px;
font-weight: bold;
transition: all 0.2s ease;
line-height: 1;
}
.permission-menu-btn:hover {
background-color: var(--vscode-list-hoverBackground);
color: var(--vscode-foreground);
}
.permission-menu-dropdown {
position: absolute;
top: 100%;
right: 0;
background-color: var(--vscode-menu-background);
border: 1px solid var(--vscode-menu-border);
border-radius: 6px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
z-index: 1000;
min-width: 220px;
padding: 4px 0;
margin-top: 4px;
}
.permission-menu-item {
display: flex;
align-items: flex-start;
gap: 10px;
padding: 12px 16px;
background: none;
border: none;
width: 100%;
text-align: left;
cursor: pointer;
color: var(--vscode-foreground);
transition: background-color 0.2s ease;
}
.permission-menu-item:hover {
background-color: var(--vscode-list-hoverBackground);
}
.permission-menu-item .menu-icon {
font-size: 16px;
margin-top: 1px;
flex-shrink: 0;
}
.permission-menu-item .menu-content {
display: flex;
flex-direction: column;
gap: 2px;
}
.permission-menu-item .menu-title {
font-weight: 500;
font-size: 13px;
line-height: 1.2;
}
.permission-menu-item .menu-subtitle {
font-size: 11px;
color: var(--vscode-descriptionForeground);
opacity: 0.8;
line-height: 1.2;
}
.permission-content {
font-size: 13px;
line-height: 1.4;
@@ -493,7 +573,7 @@ const styles = `
align-items: center;
gap: 6px;
margin-top: 12px;
opacity: 0.8;
opacity: 1;
transition: opacity 0.2s ease;
}
@@ -502,12 +582,12 @@ const styles = `
}
.yolo-mode-section input[type="checkbox"] {
transform: scale(0.8);
transform: scale(0.9);
margin: 0;
}
.yolo-mode-section label {
font-size: 10px;
font-size: 12px;
color: var(--vscode-descriptionForeground);
cursor: pointer;
font-weight: 400;
@@ -830,6 +910,96 @@ const styles = `
padding-left: 6px;
}
/* Code blocks generated by markdown parser only */
.message-content pre.code-block {
background-color: var(--vscode-textCodeBlock-background);
border: 1px solid var(--vscode-panel-border);
border-radius: 4px;
padding: 12px;
margin: 8px 0;
overflow-x: auto;
font-family: var(--vscode-editor-font-family);
font-size: 13px;
line-height: 1.5;
white-space: pre;
}
.message-content pre.code-block code {
background: none;
border: none;
padding: 0;
color: var(--vscode-editor-foreground);
}
.code-line {
white-space: pre-wrap;
word-break: break-word;
}
/* Code block container and header */
.code-block-container {
margin: 8px 0;
border: 1px solid var(--vscode-panel-border);
border-radius: 4px;
background-color: var(--vscode-textCodeBlock-background);
overflow: hidden;
}
.code-block-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 4px 6px;
background-color: var(--vscode-editor-background);
border-bottom: 1px solid var(--vscode-panel-border);
font-size: 10px;
}
.code-block-language {
color: var(--vscode-descriptionForeground);
font-family: var(--vscode-editor-font-family);
font-weight: 500;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.code-copy-btn {
background: none;
border: none;
color: var(--vscode-descriptionForeground);
cursor: pointer;
padding: 4px;
border-radius: 3px;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s ease;
opacity: 0.7;
}
.code-copy-btn:hover {
background-color: var(--vscode-list-hoverBackground);
opacity: 1;
}
.code-block-container .code-block {
margin: 0;
border: none;
border-radius: 0;
background: none;
}
/* Inline code */
.message-content code {
background-color: var(--vscode-textCodeBlock-background);
border: 1px solid var(--vscode-panel-border);
border-radius: 3px;
padding: 2px 4px;
font-family: var(--vscode-editor-font-family);
font-size: 0.9em;
color: var(--vscode-editor-foreground);
}
.priority-badge {
display: inline-block;
padding: 2px 6px;
@@ -1261,6 +1431,7 @@ const styles = `
opacity: 1;
}
.slash-btn,
.at-btn {
background-color: transparent;
color: var(--vscode-foreground);
@@ -1273,6 +1444,7 @@ const styles = `
transition: all 0.2s ease;
}
.slash-btn:hover,
.at-btn:hover {
background-color: var(--vscode-list-hoverBackground);
}
@@ -1355,13 +1527,13 @@ const styles = `
.yolo-warning {
font-size: 12px;
color: var(--vscode-inputValidation-errorForeground);
color: var(--vscode-foreground);
text-align: center;
font-weight: 500;
background-color: var(--vscode-inputValidation-errorBackground);
border: 1px solid var(--vscode-inputValidation-errorBorder);
background-color: rgba(255, 99, 71, 0.08);
border: 1px solid rgba(255, 99, 71, 0.2);
padding: 8px 12px;
margin: 4px 12px;
margin: 4px 4px;
border-radius: 4px;
animation: slideDown 0.3s ease;
}
@@ -1532,12 +1704,14 @@ const styles = `
.tools-modal-content {
background-color: var(--vscode-editor-background);
border: 1px solid var(--vscode-panel-border);
border-radius: 6px;
width: 500px;
max-height: 600px;
border-radius: 8px;
width: 700px;
max-width: 90vw;
max-height: 80vh;
display: flex;
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 {
@@ -1546,6 +1720,13 @@ const styles = `
display: flex;
justify-content: space-between;
align-items: center;
flex-shrink: 0;
}
.tools-modal-body {
flex: 1;
overflow-y: auto;
overflow-x: hidden;
}
.tools-modal-header span {
@@ -1577,6 +1758,30 @@ const styles = `
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 {
display: flex;
align-items: flex-start;
@@ -1790,12 +1995,116 @@ const styles = `
}
/* 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 {
padding: 12px 16px;
padding: 12px 20px;
background-color: rgba(255, 149, 0, 0.1);
border: 1px solid rgba(255, 149, 0, 0.2);
border-radius: 4px;
margin-bottom: 16px;
margin: 0 20px 16px 20px;
}
.slash-commands-info p {
@@ -1806,33 +2115,161 @@ const styles = `
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 {
display: grid;
gap: 8px;
max-height: 400px;
overflow-y: auto;
gap: 6px;
padding: 0 20px;
}
.slash-command-item {
display: flex;
align-items: center;
gap: 12px;
padding: 12px 16px;
border-radius: 6px;
padding: 10px 14px;
border-radius: 4px;
cursor: pointer;
transition: all 0.2s ease;
transition: all 0.15s ease;
border: 1px solid transparent;
background-color: transparent;
}
.slash-command-item:hover {
background-color: var(--vscode-list-hoverBackground);
border-color: var(--vscode-focusBorder);
border-color: var(--vscode-list-hoverBackground);
}
.slash-command-icon {
font-size: 18px;
min-width: 24px;
font-size: 16px;
min-width: 20px;
text-align: center;
opacity: 0.8;
}
.slash-command-content {
@@ -1841,7 +2278,7 @@ const styles = `
.slash-command-title {
font-size: 13px;
font-weight: 600;
font-weight: 500;
color: var(--vscode-foreground);
margin-bottom: 2px;
}
@@ -1849,45 +2286,39 @@ const styles = `
.slash-command-description {
font-size: 11px;
color: var(--vscode-descriptionForeground);
opacity: 0.8;
opacity: 0.7;
line-height: 1.3;
}
/* Custom command input */
/* Quick command input */
.custom-command-item {
cursor: default;
}
.custom-command-input-container {
display: flex;
align-items: center;
gap: 2px;
.custom-command-item .command-input-wrapper {
margin-top: 4px;
max-width: 200px;
}
.command-prefix {
font-size: 12px;
color: var(--vscode-foreground);
font-weight: 500;
}
.custom-command-input {
background-color: var(--vscode-input-background);
border: 1px solid var(--vscode-input-border);
.custom-command-item .command-input-wrapper input {
flex: 1;
padding: 4px 6px;
border: none !important;
background: transparent;
color: var(--vscode-input-foreground);
padding: 2px 6px;
border-radius: 3px;
font-size: 11px;
outline: none;
min-width: 120px;
font-family: var(--vscode-editor-font-family);
outline: none !important;
box-shadow: none !important;
}
.custom-command-input:focus {
border-color: var(--vscode-focusBorder);
box-shadow: 0 0 0 1px var(--vscode-focusBorder);
.custom-command-item .command-input-wrapper input:focus {
border: none !important;
outline: none !important;
box-shadow: none !important;
}
.custom-command-input::placeholder {
.custom-command-item .command-input-wrapper input::placeholder {
color: var(--vscode-input-placeholderForeground);
opacity: 0.7;
}
@@ -2217,6 +2648,231 @@ const styles = `
color: var(--vscode-foreground);
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>`
export default styles

983
src/ui.ts

File diff suppressed because it is too large Load Diff

View File

@@ -13,5 +13,8 @@
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
// "noUnusedParameters": true, /* Report errors on unused parameters. */
}
},
"exclude": [
"mcp-permissions.js"
]
}