mirror of
https://github.com/andrepimenta/claude-code-chat.git
synced 2025-12-10 14:59:52 +00:00
Compare commits
27 Commits
feature/mc
...
d891070d9e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d891070d9e | ||
|
|
1be89d43a4 | ||
|
|
0abfab72a8 | ||
|
|
1eacc6ff74 | ||
|
|
031a2c5fc3 | ||
|
|
73c4a38da1 | ||
|
|
53acc0a79f | ||
|
|
62163dbc32 | ||
|
|
d225ff2596 | ||
|
|
ab5c393253 | ||
|
|
d6a73a1a7f | ||
|
|
5abb1fedd9 | ||
|
|
3b534cfce2 | ||
|
|
6bd906981b | ||
|
|
4f126641e4 | ||
|
|
2d63eaac58 | ||
|
|
f44dc28763 | ||
|
|
2c47349282 | ||
|
|
43c1c85efb | ||
|
|
b07857bf57 | ||
|
|
c9677b6185 | ||
|
|
2053e768a8 | ||
|
|
980d19bcb2 | ||
|
|
8a581908e3 | ||
|
|
2feaed600d | ||
|
|
826c25bdd6 | ||
|
|
3e8a9630bd |
@@ -11,3 +11,4 @@ vsc-extension-quickstart.md
|
|||||||
**/.vscode-test.*
|
**/.vscode-test.*
|
||||||
backup
|
backup
|
||||||
.claude
|
.claude
|
||||||
|
claude-code-chat-permissions-mcp/**
|
||||||
169
CHANGELOG.md
169
CHANGELOG.md
@@ -4,6 +4,175 @@ 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.
|
Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file.
|
||||||
|
|
||||||
|
## [1.0.7] - 2025-10-01
|
||||||
|
|
||||||
|
### 🚀 Features Added
|
||||||
|
- **Slash Commands Update**: Added 4 new slash commands to the commands modal
|
||||||
|
- `/add-dir` - Add additional working directories
|
||||||
|
- `/agents` - Manage custom AI subagents for specialized tasks
|
||||||
|
- `/rewind` - Rewind the conversation and/or code
|
||||||
|
- `/usage` - Show plan usage limits and rate limit status (subscription plans only)
|
||||||
|
|
||||||
|
### 📚 Documentation Updates
|
||||||
|
- Updated slash commands count from 19+ to 23+ built-in commands
|
||||||
|
- Enhanced command descriptions for better clarity:
|
||||||
|
- `/config` - Now specifies "Open the Settings interface (Config tab)"
|
||||||
|
- `/cost` - Added note about cost tracking guide for subscription-specific details
|
||||||
|
- `/status` - Expanded description to mention version, model, account, and connectivity
|
||||||
|
- `/terminal-setup` - Added clarification about iTerm2 and VSCode only support
|
||||||
|
|
||||||
|
## [1.0.6] - 2025-08-26
|
||||||
|
|
||||||
|
### 🐛 Bug Fixes
|
||||||
|
- Fixed typo in codebase
|
||||||
|
- Removed priority settings that were no longer needed
|
||||||
|
|
||||||
|
### 🔧 Technical Improvements
|
||||||
|
- Moved script to separate file for better code organization
|
||||||
|
|
||||||
|
## [1.0.5] - 2025-07-30
|
||||||
|
|
||||||
|
### 🚀 Features Added
|
||||||
|
- **MCP Integration**: Added claude-code-chat-permissions-mcp folder for enhanced permission management
|
||||||
|
- **Message Persistence**: Save message in text box for better user experience
|
||||||
|
- **UI Improvements**: Always display history and new chat options
|
||||||
|
- **Input Enhancement**: Removed maxlength limit for custom command prompt textarea
|
||||||
|
|
||||||
|
### 🐛 Bug Fixes
|
||||||
|
- Fixed new chat functionality
|
||||||
|
- Fixed request start time isProcessing issue
|
||||||
|
- Fixed close and open conversation behavior
|
||||||
|
|
||||||
|
### 🔄 Merged Pull Requests
|
||||||
|
- Merged PR #87 from horatio-sans-serif/main
|
||||||
|
|
||||||
|
## [1.0.4] - 2025-01-22
|
||||||
|
|
||||||
|
### 🐛 Bug Fixes
|
||||||
|
- Fixed input text area overflow issue by adding `box-sizing: border-box` to prevent padding from extending beyond container width
|
||||||
|
- Fixed command parameter handling for `claude-code-chat.openChat` to properly handle both ViewColumn and Uri parameters from different invocation contexts
|
||||||
|
|
||||||
|
### 🔧 Technical Improvements
|
||||||
|
- Enhanced `show()` method to accept optional ViewColumn parameter with ViewColumn.Two as default
|
||||||
|
- Added proper type checking for command parameters to handle context menu invocations
|
||||||
|
- Improved webview panel positioning with flexible column parameter support
|
||||||
|
|
||||||
|
### 🎨 UI/UX Improvements
|
||||||
|
- Resolved text input container sizing issues that caused visual overflow
|
||||||
|
- Better input field styling consistency across different VS Code themes
|
||||||
|
|
||||||
|
## [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
|
## [0.1.3] - 2025-06-24
|
||||||
|
|
||||||
### 🚀 Features Added
|
### 🚀 Features Added
|
||||||
|
|||||||
60
README.md
60
README.md
@@ -16,14 +16,15 @@ Ditch the command line and experience Claude Code like never before. This extens
|
|||||||
|
|
||||||
🖥️ **No Terminal Required** - Beautiful chat interface replaces command-line interactions
|
🖥️ **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
|
💾 **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
|
🧠 **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, paste images or screenshots and create custom commands
|
||||||
🤖 **Model Selection** - Choose between Opus, Sonnet, or Default based on your needs
|
🤖 **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 native Windows and WSL support
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -34,8 +35,9 @@ Ditch the command line and experience Claude Code like never before. This extens
|
|||||||
- No terminal required - everything through the UI
|
- No terminal required - everything through the UI
|
||||||
- Real-time streaming responses with typing indicators
|
- Real-time streaming responses with typing indicators
|
||||||
- One-click message copying with visual feedback
|
- 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
|
- Auto-resizing input that grows with your content
|
||||||
|
- Copy-to-clipboard for code blocks
|
||||||
|
|
||||||
### ⏪ **Checkpoint & Session Management**
|
### ⏪ **Checkpoint & Session Management**
|
||||||
- **Restore Checkpoints** - Instantly undo changes and restore to any previous state
|
- **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
|
- Real-time cost and token tracking
|
||||||
- Session statistics and performance metrics
|
- 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**
|
### 📁 **Smart File Integration**
|
||||||
- Type `@` to instantly search and reference workspace files
|
- 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
|
- Lightning-fast file search across your entire project
|
||||||
- Seamless context preservation for multi-file discussions
|
- 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
|
- Visual dashboard showing all available Claude Code tools
|
||||||
- Real-time tool execution with formatted results
|
- Real-time tool execution with formatted results
|
||||||
- Process control - start, stop, and monitor operations
|
- Process control - start, stop, and monitor operations
|
||||||
|
- Smart permission system for secure tool execution
|
||||||
|
|
||||||
### 🎨 **VS Code Integration**
|
### 🎨 **VS Code Integration**
|
||||||
- Native theming that matches your editor
|
- Native theming that matches your editor
|
||||||
@@ -73,7 +103,7 @@ Ditch the command line and experience Claude Code like never before. This extens
|
|||||||
|
|
||||||
### ⚡ **Slash Commands Integration**
|
### ⚡ **Slash Commands Integration**
|
||||||
- **Slash Commands Modal** - Type "/" to access all Claude Code commands instantly
|
- **Slash Commands Modal** - Type "/" to access all Claude Code commands instantly
|
||||||
- **19+ Built-in Commands** - /cost, /status, /config, /help, /memory, /review, and more
|
- **23+ Built-in Commands** - /agents, /cost, /config, /memory, /review, and more
|
||||||
- **Custom Command Support** - Execute any Claude Code command with session context
|
- **Custom Command Support** - Execute any Claude Code command with session context
|
||||||
- **Session-Aware Execution** - All commands run with current conversation context
|
- **Session-Aware Execution** - All commands run with current conversation context
|
||||||
- **Terminal Integration** - Commands open directly in VS Code terminal with WSL support
|
- **Terminal Integration** - Commands open directly in VS Code terminal with WSL support
|
||||||
@@ -214,31 +244,39 @@ Example configuration in `settings.json`:
|
|||||||
- Type `@` followed by your search term to quickly reference files
|
- Type `@` followed by your search term to quickly reference files
|
||||||
- Use `@src/` to narrow down to specific directories
|
- Use `@src/` to narrow down to specific directories
|
||||||
- Reference multiple files in one message for cross-file analysis
|
- 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**
|
### ⚡ **Productivity Boosters**
|
||||||
- **Creates checkpoints automatically** before changes for safe experimentation
|
- **Creates checkpoints automatically** before changes for safe experimentation
|
||||||
- **Restore instantly** if changes don't work out as expected
|
- **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
|
- Use the stop button to cancel long-running operations
|
||||||
- Copy message contents to reuse Claude's responses
|
- Copy message contents to reuse Claude's responses
|
||||||
- Open history panel to reference previous conversations
|
- Open history panel to reference previous conversations
|
||||||
|
- **NEW**: Sidebar integration for multi-panel workflow
|
||||||
|
|
||||||
### 🎨 **Interface Customization**
|
### 🎨 **Interface Customization**
|
||||||
- The UI automatically adapts to your VS Code theme
|
- The UI automatically adapts to your VS Code theme
|
||||||
- Messages are color-coded: Green for you, Blue for Claude
|
- Messages are color-coded: Green for you, Blue for Claude
|
||||||
- Hover over messages to reveal the copy button
|
- 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**
|
## 🔧 **Advanced Features**
|
||||||
|
|
||||||
### 🛠️ **Tool Integration**
|
### 🛠️ **Tool Integration**
|
||||||
Claude Code Chat provides full access to all Claude Code tools:
|
Claude Code Chat provides secure access to all Claude Code tools:
|
||||||
- **Bash** - Execute shell commands
|
- **Bash** - Execute shell commands with permission controls
|
||||||
- **File Operations** - Read, write, and edit files
|
- **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
|
- **Web** - Fetch and search web content
|
||||||
- **Multi-edit** - Batch file modifications
|
- **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**
|
### 📊 **Analytics & Monitoring**
|
||||||
- **Real-time cost tracking** - Monitor your API usage
|
- **Real-time cost tracking** - Monitor your API usage
|
||||||
|
|||||||
13954
claude-code-chat-permissions-mcp/mcp-permissions.js
Normal file
13954
claude-code-chat-permissions-mcp/mcp-permissions.js
Normal file
File diff suppressed because one or more lines are too long
212
claude-code-chat-permissions-mcp/mcp-permissions.ts
Normal file
212
claude-code-chat-permissions-mcp/mcp-permissions.ts
Normal file
@@ -0,0 +1,212 @@
|
|||||||
|
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);
|
||||||
|
});
|
||||||
1086
claude-code-chat-permissions-mcp/package-lock.json
generated
Normal file
1086
claude-code-chat-permissions-mcp/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
21
claude-code-chat-permissions-mcp/package.json
Normal file
21
claude-code-chat-permissions-mcp/package.json
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"name": "claude-code-chat-permissions-mcp",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"main": "dist/mcp-permissions.js",
|
||||||
|
"scripts": {
|
||||||
|
"start": "tsc && node dist/mcp-permissions.js",
|
||||||
|
"lint": "eslint . --ext .ts",
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"description": "",
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "^24.0.13",
|
||||||
|
"typescript": "^5.8.3"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@modelcontextprotocol/sdk": "^1.15.1",
|
||||||
|
"zod": "^3.25.76"
|
||||||
|
}
|
||||||
|
}
|
||||||
11
claude-code-chat-permissions-mcp/tsconfig.json
Normal file
11
claude-code-chat-permissions-mcp/tsconfig.json
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"module": "commonjs",
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"target": "es6",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"sourceMap": true,
|
||||||
|
"outDir": "dist"
|
||||||
|
},
|
||||||
|
"lib": ["es2015"]
|
||||||
|
}
|
||||||
BIN
icon-bubble.png
Normal file
BIN
icon-bubble.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 469 KiB |
BIN
icon.png
BIN
icon.png
Binary file not shown.
|
Before Width: | Height: | Size: 689 KiB After Width: | Height: | Size: 689 KiB |
10
package.json
10
package.json
@@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"name": "claude-code-chat",
|
"name": "claude-code-chat",
|
||||||
"displayName": "Claude Code Chat",
|
"displayName": "Chat for Claude Code",
|
||||||
"description": "Beautiful Claude Code Chat Interface for VS Code",
|
"description": "Beautiful Claude Code Chat Interface for VS Code",
|
||||||
"version": "1.0.0",
|
"version": "1.0.7",
|
||||||
"publisher": "AndrePimenta",
|
"publisher": "AndrePimenta",
|
||||||
"author": "Andre Pimenta",
|
"author": "Andre Pimenta",
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -56,7 +56,7 @@
|
|||||||
"command": "claude-code-chat.openChat",
|
"command": "claude-code-chat.openChat",
|
||||||
"title": "Open Claude Code Chat",
|
"title": "Open Claude Code Chat",
|
||||||
"category": "Claude Code Chat",
|
"category": "Claude Code Chat",
|
||||||
"icon": "icon.png"
|
"icon": "icon-bubble.png"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"keybindings": [
|
"keybindings": [
|
||||||
@@ -133,7 +133,7 @@
|
|||||||
"type": "webview",
|
"type": "webview",
|
||||||
"name": "Claude Code Chat",
|
"name": "Claude Code Chat",
|
||||||
"when": "true",
|
"when": "true",
|
||||||
"icon": "icon.png",
|
"icon": "icon-bubble.png",
|
||||||
"contextualTitle": "Claude Code Chat"
|
"contextualTitle": "Claude Code Chat"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -143,7 +143,7 @@
|
|||||||
{
|
{
|
||||||
"id": "claude-code-chat",
|
"id": "claude-code-chat",
|
||||||
"title": "Claude Code Chat",
|
"title": "Claude Code Chat",
|
||||||
"icon": "icon.png"
|
"icon": "icon-bubble.png"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
285
src/extension.ts
285
src/extension.ts
@@ -2,7 +2,7 @@ import * as vscode from 'vscode';
|
|||||||
import * as cp from 'child_process';
|
import * as cp from 'child_process';
|
||||||
import * as util from 'util';
|
import * as util from 'util';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import html from './ui';
|
import getHtml from './ui';
|
||||||
|
|
||||||
const exec = util.promisify(cp.exec);
|
const exec = util.promisify(cp.exec);
|
||||||
|
|
||||||
@@ -10,9 +10,9 @@ export function activate(context: vscode.ExtensionContext) {
|
|||||||
console.log('Claude Code Chat extension is being activated!');
|
console.log('Claude Code Chat extension is being activated!');
|
||||||
const provider = new ClaudeChatProvider(context.extensionUri, context);
|
const provider = new ClaudeChatProvider(context.extensionUri, context);
|
||||||
|
|
||||||
const disposable = vscode.commands.registerCommand('claude-code-chat.openChat', () => {
|
const disposable = vscode.commands.registerCommand('claude-code-chat.openChat', (column?: vscode.ViewColumn) => {
|
||||||
console.log('Claude Code Chat command executed!');
|
console.log('Claude Code Chat command executed!');
|
||||||
provider.show();
|
provider.show(column);
|
||||||
});
|
});
|
||||||
|
|
||||||
const loadConversationDisposable = vscode.commands.registerCommand('claude-code-chat.loadConversation', (filename: string) => {
|
const loadConversationDisposable = vscode.commands.registerCommand('claude-code-chat.loadConversation', (filename: string) => {
|
||||||
@@ -44,12 +44,26 @@ export function activate(context: vscode.ExtensionContext) {
|
|||||||
|
|
||||||
export function deactivate() { }
|
export function deactivate() { }
|
||||||
|
|
||||||
|
interface ConversationData {
|
||||||
|
sessionId: string;
|
||||||
|
startTime: string | undefined;
|
||||||
|
endTime: string;
|
||||||
|
messageCount: number;
|
||||||
|
totalCost: number;
|
||||||
|
totalTokens: {
|
||||||
|
input: number;
|
||||||
|
output: number;
|
||||||
|
};
|
||||||
|
messages: Array<{ timestamp: string, messageType: string, data: any }>;
|
||||||
|
filename: string;
|
||||||
|
}
|
||||||
|
|
||||||
class ClaudeChatWebviewProvider implements vscode.WebviewViewProvider {
|
class ClaudeChatWebviewProvider implements vscode.WebviewViewProvider {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _extensionUri: vscode.Uri,
|
private readonly _extensionUri: vscode.Uri,
|
||||||
private readonly _context: vscode.ExtensionContext,
|
private readonly _context: vscode.ExtensionContext,
|
||||||
private readonly _chatProvider: ClaudeChatProvider
|
private readonly _chatProvider: ClaudeChatProvider
|
||||||
) {}
|
) { }
|
||||||
|
|
||||||
public resolveWebviewView(
|
public resolveWebviewView(
|
||||||
webviewView: vscode.WebviewView,
|
webviewView: vscode.WebviewView,
|
||||||
@@ -112,6 +126,8 @@ class ClaudeChatProvider {
|
|||||||
}> = [];
|
}> = [];
|
||||||
private _currentClaudeProcess: cp.ChildProcess | undefined;
|
private _currentClaudeProcess: cp.ChildProcess | undefined;
|
||||||
private _selectedModel: string = 'default'; // Default model
|
private _selectedModel: string = 'default'; // Default model
|
||||||
|
private _isProcessing: boolean | undefined;
|
||||||
|
private _draftMessage: string = '';
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _extensionUri: vscode.Uri,
|
private readonly _extensionUri: vscode.Uri,
|
||||||
@@ -122,7 +138,6 @@ class ClaudeChatProvider {
|
|||||||
this._initializeBackupRepo();
|
this._initializeBackupRepo();
|
||||||
this._initializeConversations();
|
this._initializeConversations();
|
||||||
this._initializeMCPConfig();
|
this._initializeMCPConfig();
|
||||||
this._initializePermissions();
|
|
||||||
|
|
||||||
// Load conversation index from workspace state
|
// Load conversation index from workspace state
|
||||||
this._conversationIndex = this._context.workspaceState.get('claude.conversationIndex', []);
|
this._conversationIndex = this._context.workspaceState.get('claude.conversationIndex', []);
|
||||||
@@ -135,21 +150,22 @@ class ClaudeChatProvider {
|
|||||||
this._currentSessionId = latestConversation?.sessionId;
|
this._currentSessionId = latestConversation?.sessionId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public show() {
|
public show(column: vscode.ViewColumn | vscode.Uri = vscode.ViewColumn.Two) {
|
||||||
const column = vscode.ViewColumn.Two;
|
// Handle case where a URI is passed instead of ViewColumn
|
||||||
|
const actualColumn = column instanceof vscode.Uri ? vscode.ViewColumn.Two : column;
|
||||||
|
|
||||||
// Close sidebar if it's open
|
// Close sidebar if it's open
|
||||||
this._closeSidebar();
|
this._closeSidebar();
|
||||||
|
|
||||||
if (this._panel) {
|
if (this._panel) {
|
||||||
this._panel.reveal(column);
|
this._panel.reveal(actualColumn);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._panel = vscode.window.createWebviewPanel(
|
this._panel = vscode.window.createWebviewPanel(
|
||||||
'claudeChat',
|
'claudeChat',
|
||||||
'Claude Code Chat',
|
'Claude Code Chat',
|
||||||
column,
|
actualColumn,
|
||||||
{
|
{
|
||||||
enableScripts: true,
|
enableScripts: true,
|
||||||
retainContextWhenHidden: true,
|
retainContextWhenHidden: true,
|
||||||
@@ -158,7 +174,7 @@ class ClaudeChatProvider {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Set icon for the webview tab using URI path
|
// Set icon for the webview tab using URI path
|
||||||
const iconPath = vscode.Uri.joinPath(this._extensionUri, 'icon.png');
|
const iconPath = vscode.Uri.joinPath(this._extensionUri, 'icon-bubble.png');
|
||||||
this._panel.iconPath = iconPath;
|
this._panel.iconPath = iconPath;
|
||||||
|
|
||||||
this._panel.webview.html = this._getHtmlForWebview();
|
this._panel.webview.html = this._getHtmlForWebview();
|
||||||
@@ -166,6 +182,7 @@ class ClaudeChatProvider {
|
|||||||
this._panel.onDidDispose(() => this.dispose(), null, this._disposables);
|
this._panel.onDidDispose(() => this.dispose(), null, this._disposables);
|
||||||
|
|
||||||
this._setupWebviewMessageHandler(this._panel.webview);
|
this._setupWebviewMessageHandler(this._panel.webview);
|
||||||
|
this._initializePermissions();
|
||||||
|
|
||||||
// Resume session from latest conversation
|
// Resume session from latest conversation
|
||||||
const latestConversation = this._getLatestConversation();
|
const latestConversation = this._getLatestConversation();
|
||||||
@@ -195,18 +212,18 @@ class ClaudeChatProvider {
|
|||||||
|
|
||||||
private _sendReadyMessage() {
|
private _sendReadyMessage() {
|
||||||
// Send current session info if available
|
// Send current session info if available
|
||||||
if (this._currentSessionId) {
|
/*if (this._currentSessionId) {
|
||||||
this._postMessage({
|
this._postMessage({
|
||||||
type: 'sessionResumed',
|
type: 'sessionResumed',
|
||||||
data: {
|
data: {
|
||||||
sessionId: this._currentSessionId
|
sessionId: this._currentSessionId
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}*/
|
||||||
|
|
||||||
this._postMessage({
|
this._postMessage({
|
||||||
type: 'ready',
|
type: 'ready',
|
||||||
data: 'Ready to chat with Claude Code! Type your message below.'
|
data: this._isProcessing ? 'Claude is working...' : 'Ready to chat with Claude Code! Type your message below.'
|
||||||
});
|
});
|
||||||
|
|
||||||
// Send current model to webview
|
// Send current model to webview
|
||||||
@@ -220,6 +237,14 @@ class ClaudeChatProvider {
|
|||||||
|
|
||||||
// Send current settings to webview
|
// Send current settings to webview
|
||||||
this._sendCurrentSettings();
|
this._sendCurrentSettings();
|
||||||
|
|
||||||
|
// Send saved draft message if any
|
||||||
|
if (this._draftMessage) {
|
||||||
|
this._postMessage({
|
||||||
|
type: 'restoreInputText',
|
||||||
|
data: this._draftMessage
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleWebviewMessage(message: any) {
|
private _handleWebviewMessage(message: any) {
|
||||||
@@ -305,6 +330,12 @@ class ClaudeChatProvider {
|
|||||||
case 'deleteCustomSnippet':
|
case 'deleteCustomSnippet':
|
||||||
this._deleteCustomSnippet(message.snippetId);
|
this._deleteCustomSnippet(message.snippetId);
|
||||||
return;
|
return;
|
||||||
|
case 'enableYoloMode':
|
||||||
|
this._enableYoloMode();
|
||||||
|
return;
|
||||||
|
case 'saveInputText':
|
||||||
|
this._saveInputText(message.text);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -342,6 +373,7 @@ class ClaudeChatProvider {
|
|||||||
this._webview.html = this._getHtmlForWebview();
|
this._webview.html = this._getHtmlForWebview();
|
||||||
|
|
||||||
this._setupWebviewMessageHandler(this._webview);
|
this._setupWebviewMessageHandler(this._webview);
|
||||||
|
this._initializePermissions();
|
||||||
|
|
||||||
// Initialize the webview
|
// Initialize the webview
|
||||||
this._initializeWebview();
|
this._initializeWebview();
|
||||||
@@ -366,6 +398,7 @@ class ClaudeChatProvider {
|
|||||||
public reinitializeWebview() {
|
public reinitializeWebview() {
|
||||||
// Only reinitialize if we have a webview (sidebar)
|
// Only reinitialize if we have a webview (sidebar)
|
||||||
if (this._webview) {
|
if (this._webview) {
|
||||||
|
this._initializePermissions();
|
||||||
this._initializeWebview();
|
this._initializeWebview();
|
||||||
// Set up message handler for the webview
|
// Set up message handler for the webview
|
||||||
this._setupWebviewMessageHandler(this._webview);
|
this._setupWebviewMessageHandler(this._webview);
|
||||||
@@ -407,16 +440,21 @@ class ClaudeChatProvider {
|
|||||||
actualMessage = thinkingPrompt + thinkingMesssage + actualMessage;
|
actualMessage = thinkingPrompt + thinkingMesssage + actualMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._isProcessing = true;
|
||||||
|
|
||||||
|
// Clear draft message since we're sending it
|
||||||
|
this._draftMessage = '';
|
||||||
|
|
||||||
// Show original user input in chat and save to conversation (without mode prefixes)
|
// Show original user input in chat and save to conversation (without mode prefixes)
|
||||||
this._sendAndSaveMessage({
|
this._sendAndSaveMessage({
|
||||||
type: 'userInput',
|
type: 'userInput',
|
||||||
data: message
|
data: message
|
||||||
});
|
});
|
||||||
|
|
||||||
// Set processing state
|
// Set processing state to true
|
||||||
this._postMessage({
|
this._postMessage({
|
||||||
type: 'setProcessing',
|
type: 'setProcessing',
|
||||||
data: true
|
data: { isProcessing: true }
|
||||||
});
|
});
|
||||||
|
|
||||||
// Create backup commit before Claude makes changes
|
// Create backup commit before Claude makes changes
|
||||||
@@ -433,9 +471,6 @@ class ClaudeChatProvider {
|
|||||||
data: 'Claude is working...'
|
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
|
// Build command arguments with session management
|
||||||
const args = [
|
const args = [
|
||||||
'-p',
|
'-p',
|
||||||
@@ -462,7 +497,6 @@ class ClaudeChatProvider {
|
|||||||
// Add model selection if not using default
|
// Add model selection if not using default
|
||||||
if (this._selectedModel && this._selectedModel !== 'default') {
|
if (this._selectedModel && this._selectedModel !== 'default') {
|
||||||
args.push('--model', this._selectedModel);
|
args.push('--model', this._selectedModel);
|
||||||
console.log('Using model:', this._selectedModel);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add session resume if we have a current session
|
// Add session resume if we have a current session
|
||||||
@@ -486,8 +520,6 @@ class ClaudeChatProvider {
|
|||||||
console.log('Using WSL configuration:', { wslDistro, nodePath, claudePath });
|
console.log('Using WSL configuration:', { wslDistro, nodePath, claudePath });
|
||||||
const wslCommand = `"${nodePath}" --no-warnings --enable-source-maps "${claudePath}" ${args.join(' ')}`;
|
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], {
|
claudeProcess = cp.spawn('wsl', ['-d', wslDistro, 'bash', '-ic', wslCommand], {
|
||||||
cwd: cwd,
|
cwd: cwd,
|
||||||
stdio: ['pipe', 'pipe', 'pipe'],
|
stdio: ['pipe', 'pipe', 'pipe'],
|
||||||
@@ -501,6 +533,7 @@ class ClaudeChatProvider {
|
|||||||
// Use native claude command
|
// Use native claude command
|
||||||
console.log('Using native Claude command');
|
console.log('Using native Claude command');
|
||||||
claudeProcess = cp.spawn('claude', args, {
|
claudeProcess = cp.spawn('claude', args, {
|
||||||
|
shell: process.platform === 'win32',
|
||||||
cwd: cwd,
|
cwd: cwd,
|
||||||
stdio: ['pipe', 'pipe', 'pipe'],
|
stdio: ['pipe', 'pipe', 'pipe'],
|
||||||
env: {
|
env: {
|
||||||
@@ -554,14 +587,27 @@ class ClaudeChatProvider {
|
|||||||
console.log('Claude process closed with code:', code);
|
console.log('Claude process closed with code:', code);
|
||||||
console.log('Claude stderr output:', errorOutput);
|
console.log('Claude stderr output:', errorOutput);
|
||||||
|
|
||||||
|
if (!this._currentClaudeProcess) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Clear process reference
|
// Clear process reference
|
||||||
this._currentClaudeProcess = undefined;
|
this._currentClaudeProcess = undefined;
|
||||||
|
|
||||||
// Clear loading indicator
|
// Clear loading indicator and set processing to false
|
||||||
this._postMessage({
|
this._postMessage({
|
||||||
type: 'clearLoading'
|
type: 'clearLoading'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Reset processing state
|
||||||
|
this._isProcessing = false;
|
||||||
|
|
||||||
|
// Clear processing state
|
||||||
|
this._postMessage({
|
||||||
|
type: 'setProcessing',
|
||||||
|
data: { isProcessing: false }
|
||||||
|
});
|
||||||
|
|
||||||
if (code !== 0 && errorOutput.trim()) {
|
if (code !== 0 && errorOutput.trim()) {
|
||||||
// Error with output
|
// Error with output
|
||||||
this._sendAndSaveMessage({
|
this._sendAndSaveMessage({
|
||||||
@@ -574,6 +620,10 @@ class ClaudeChatProvider {
|
|||||||
claudeProcess.on('error', (error) => {
|
claudeProcess.on('error', (error) => {
|
||||||
console.log('Claude process error:', error.message);
|
console.log('Claude process error:', error.message);
|
||||||
|
|
||||||
|
if (!this._currentClaudeProcess) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Clear process reference
|
// Clear process reference
|
||||||
this._currentClaudeProcess = undefined;
|
this._currentClaudeProcess = undefined;
|
||||||
|
|
||||||
@@ -581,6 +631,14 @@ class ClaudeChatProvider {
|
|||||||
type: 'clearLoading'
|
type: 'clearLoading'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this._isProcessing = false;
|
||||||
|
|
||||||
|
// Clear processing state
|
||||||
|
this._postMessage({
|
||||||
|
type: 'setProcessing',
|
||||||
|
data: { isProcessing: false }
|
||||||
|
});
|
||||||
|
|
||||||
// Check if claude command is not installed
|
// Check if claude command is not installed
|
||||||
if (error.message.includes('ENOENT') || error.message.includes('command not found')) {
|
if (error.message.includes('ENOENT') || error.message.includes('command not found')) {
|
||||||
this._sendAndSaveMessage({
|
this._sendAndSaveMessage({
|
||||||
@@ -597,13 +655,23 @@ class ClaudeChatProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _processJsonStreamData(jsonData: any) {
|
private _processJsonStreamData(jsonData: any) {
|
||||||
console.log('Received JSON data:', jsonData);
|
|
||||||
|
|
||||||
switch (jsonData.type) {
|
switch (jsonData.type) {
|
||||||
case 'system':
|
case 'system':
|
||||||
if (jsonData.subtype === 'init') {
|
if (jsonData.subtype === 'init') {
|
||||||
// System initialization message - session ID will be captured from final result
|
// System initialization message - session ID will be captured from final result
|
||||||
console.log('System initialized');
|
console.log('System initialized');
|
||||||
|
this._currentSessionId = jsonData.session_id;
|
||||||
|
//this._sendAndSaveMessage({ type: 'init', data: { sessionId: jsonData.session_id; } })
|
||||||
|
|
||||||
|
// Show session info in UI
|
||||||
|
this._sendAndSaveMessage({
|
||||||
|
type: 'sessionInfo',
|
||||||
|
data: {
|
||||||
|
sessionId: jsonData.session_id,
|
||||||
|
tools: jsonData.tools || [],
|
||||||
|
mcpServers: jsonData.mcp_servers || []
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -692,7 +760,7 @@ class ClaudeChatProvider {
|
|||||||
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
|
||||||
const lastToolUse = this._currentConversation[this._currentConversation.length-1]
|
const lastToolUse = this._currentConversation[this._currentConversation.length - 1]
|
||||||
|
|
||||||
const toolName = lastToolUse?.data?.toolName;
|
const toolName = lastToolUse?.data?.toolName;
|
||||||
|
|
||||||
@@ -734,6 +802,8 @@ class ClaudeChatProvider {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._isProcessing = false;
|
||||||
|
|
||||||
// Capture session ID from final result
|
// Capture session ID from final result
|
||||||
if (jsonData.session_id) {
|
if (jsonData.session_id) {
|
||||||
const isNewSession = !this._currentSessionId;
|
const isNewSession = !this._currentSessionId;
|
||||||
@@ -762,7 +832,7 @@ class ClaudeChatProvider {
|
|||||||
// Clear processing state
|
// Clear processing state
|
||||||
this._postMessage({
|
this._postMessage({
|
||||||
type: 'setProcessing',
|
type: 'setProcessing',
|
||||||
data: false
|
data: { isProcessing: false }
|
||||||
});
|
});
|
||||||
|
|
||||||
// Update cumulative tracking
|
// Update cumulative tracking
|
||||||
@@ -797,6 +867,22 @@ class ClaudeChatProvider {
|
|||||||
|
|
||||||
|
|
||||||
private _newSession() {
|
private _newSession() {
|
||||||
|
|
||||||
|
this._isProcessing = false
|
||||||
|
|
||||||
|
// Update UI state
|
||||||
|
this._postMessage({
|
||||||
|
type: 'setProcessing',
|
||||||
|
data: { isProcessing: false }
|
||||||
|
});
|
||||||
|
|
||||||
|
// Try graceful termination first
|
||||||
|
if (this._currentClaudeProcess) {
|
||||||
|
const processToKill = this._currentClaudeProcess;
|
||||||
|
this._currentClaudeProcess = undefined;
|
||||||
|
processToKill.kill('SIGTERM');
|
||||||
|
}
|
||||||
|
|
||||||
// Clear current session
|
// Clear current session
|
||||||
this._currentSessionId = undefined;
|
this._currentSessionId = undefined;
|
||||||
|
|
||||||
@@ -838,10 +924,13 @@ class ClaudeChatProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _handleLoginRequired() {
|
private _handleLoginRequired() {
|
||||||
|
|
||||||
|
this._isProcessing = false;
|
||||||
|
|
||||||
// Clear processing state
|
// Clear processing state
|
||||||
this._postMessage({
|
this._postMessage({
|
||||||
type: 'setProcessing',
|
type: 'setProcessing',
|
||||||
data: false
|
data: { isProcessing: false }
|
||||||
});
|
});
|
||||||
|
|
||||||
// Show login required message
|
// Show login required message
|
||||||
@@ -871,7 +960,7 @@ class ClaudeChatProvider {
|
|||||||
'OK'
|
'OK'
|
||||||
);
|
);
|
||||||
|
|
||||||
// Send message to UI about terminal
|
// Send message to UI about terminal
|
||||||
this._postMessage({
|
this._postMessage({
|
||||||
type: 'terminalOpened',
|
type: 'terminalOpened',
|
||||||
data: `Please login to Claude in the terminal, then come back to this chat to continue.`,
|
data: `Please login to Claude in the terminal, then come back to this chat to continue.`,
|
||||||
@@ -881,7 +970,7 @@ class ClaudeChatProvider {
|
|||||||
private async _initializeBackupRepo(): Promise<void> {
|
private async _initializeBackupRepo(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const workspaceFolder = vscode.workspace.workspaceFolders?.[0];
|
const workspaceFolder = vscode.workspace.workspaceFolders?.[0];
|
||||||
if (!workspaceFolder) {return;}
|
if (!workspaceFolder) { return; }
|
||||||
|
|
||||||
const storagePath = this._context.storageUri?.fsPath;
|
const storagePath = this._context.storageUri?.fsPath;
|
||||||
if (!storagePath) {
|
if (!storagePath) {
|
||||||
@@ -914,7 +1003,7 @@ class ClaudeChatProvider {
|
|||||||
private async _createBackupCommit(userMessage: string): Promise<void> {
|
private async _createBackupCommit(userMessage: string): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const workspaceFolder = vscode.workspace.workspaceFolders?.[0];
|
const workspaceFolder = vscode.workspace.workspaceFolders?.[0];
|
||||||
if (!workspaceFolder || !this._backupRepoPath) {return;}
|
if (!workspaceFolder || !this._backupRepoPath) { return; }
|
||||||
|
|
||||||
const workspacePath = workspaceFolder.uri.fsPath;
|
const workspacePath = workspaceFolder.uri.fsPath;
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
@@ -1023,10 +1112,10 @@ class ClaudeChatProvider {
|
|||||||
private async _initializeConversations(): Promise<void> {
|
private async _initializeConversations(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const workspaceFolder = vscode.workspace.workspaceFolders?.[0];
|
const workspaceFolder = vscode.workspace.workspaceFolders?.[0];
|
||||||
if (!workspaceFolder) {return;}
|
if (!workspaceFolder) { return; }
|
||||||
|
|
||||||
const storagePath = this._context.storageUri?.fsPath;
|
const storagePath = this._context.storageUri?.fsPath;
|
||||||
if (!storagePath) {return;}
|
if (!storagePath) { return; }
|
||||||
|
|
||||||
this._conversationsPath = path.join(storagePath, 'conversations');
|
this._conversationsPath = path.join(storagePath, 'conversations');
|
||||||
|
|
||||||
@@ -1045,7 +1134,7 @@ class ClaudeChatProvider {
|
|||||||
private async _initializeMCPConfig(): Promise<void> {
|
private async _initializeMCPConfig(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const storagePath = this._context.storageUri?.fsPath;
|
const storagePath = this._context.storageUri?.fsPath;
|
||||||
if (!storagePath) {return;}
|
if (!storagePath) { return; }
|
||||||
|
|
||||||
// Create MCP config directory
|
// Create MCP config directory
|
||||||
const mcpConfigDir = path.join(storagePath, 'mcp');
|
const mcpConfigDir = path.join(storagePath, 'mcp');
|
||||||
@@ -1098,8 +1187,14 @@ class ClaudeChatProvider {
|
|||||||
|
|
||||||
private async _initializePermissions(): Promise<void> {
|
private async _initializePermissions(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
|
if (this._permissionWatcher) {
|
||||||
|
this._permissionWatcher.dispose();
|
||||||
|
this._permissionWatcher = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
const storagePath = this._context.storageUri?.fsPath;
|
const storagePath = this._context.storageUri?.fsPath;
|
||||||
if (!storagePath) {return;}
|
if (!storagePath) { return; }
|
||||||
|
|
||||||
// Create permission requests directory
|
// Create permission requests directory
|
||||||
this._permissionRequestsPath = path.join(path.join(storagePath, 'permission-requests'));
|
this._permissionRequestsPath = path.join(path.join(storagePath, 'permission-requests'));
|
||||||
@@ -1118,7 +1213,6 @@ class ClaudeChatProvider {
|
|||||||
);
|
);
|
||||||
|
|
||||||
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);
|
||||||
@@ -1170,7 +1264,7 @@ class ClaudeChatProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Send permission request to the UI
|
// Send permission request to the UI
|
||||||
this._postMessage({
|
this._sendAndSaveMessage({
|
||||||
type: 'permissionRequest',
|
type: 'permissionRequest',
|
||||||
data: {
|
data: {
|
||||||
id: request.id,
|
id: request.id,
|
||||||
@@ -1527,10 +1621,10 @@ class ClaudeChatProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Filter out internal servers before sending to UI
|
// Filter out internal servers before sending to UI
|
||||||
const filteredServers = Object.fromEntries(
|
const filteredServers = Object.fromEntries(
|
||||||
Object.entries(mcpConfig.mcpServers || {}).filter(([name]) => name !== 'claude-code-chat-permissions')
|
Object.entries(mcpConfig.mcpServers || {}).filter(([name]) => name !== 'claude-code-chat-permissions')
|
||||||
);
|
);
|
||||||
this._postMessage({ type: 'mcpServers', data: filteredServers });
|
this._postMessage({ type: 'mcpServers', data: filteredServers });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error loading MCP servers:', error);
|
console.error('Error loading MCP servers:', error);
|
||||||
this._postMessage({ type: 'mcpServerError', data: { error: 'Failed to load MCP servers' } });
|
this._postMessage({ type: 'mcpServerError', data: { error: 'Failed to load MCP servers' } });
|
||||||
@@ -1626,7 +1720,7 @@ class ClaudeChatProvider {
|
|||||||
|
|
||||||
private async _sendCustomSnippets(): Promise<void> {
|
private async _sendCustomSnippets(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const customSnippets = this._context.globalState.get<{[key: string]: any}>('customPromptSnippets', {});
|
const customSnippets = this._context.globalState.get<{ [key: string]: any }>('customPromptSnippets', {});
|
||||||
this._postMessage({
|
this._postMessage({
|
||||||
type: 'customSnippetsData',
|
type: 'customSnippetsData',
|
||||||
data: customSnippets
|
data: customSnippets
|
||||||
@@ -1642,7 +1736,7 @@ class ClaudeChatProvider {
|
|||||||
|
|
||||||
private async _saveCustomSnippet(snippet: any): Promise<void> {
|
private async _saveCustomSnippet(snippet: any): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const customSnippets = this._context.globalState.get<{[key: string]: any}>('customPromptSnippets', {});
|
const customSnippets = this._context.globalState.get<{ [key: string]: any }>('customPromptSnippets', {});
|
||||||
customSnippets[snippet.id] = snippet;
|
customSnippets[snippet.id] = snippet;
|
||||||
|
|
||||||
await this._context.globalState.update('customPromptSnippets', customSnippets);
|
await this._context.globalState.update('customPromptSnippets', customSnippets);
|
||||||
@@ -1664,7 +1758,7 @@ class ClaudeChatProvider {
|
|||||||
|
|
||||||
private async _deleteCustomSnippet(snippetId: string): Promise<void> {
|
private async _deleteCustomSnippet(snippetId: string): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const customSnippets = this._context.globalState.get<{[key: string]: any}>('customPromptSnippets', {});
|
const customSnippets = this._context.globalState.get<{ [key: string]: any }>('customPromptSnippets', {});
|
||||||
|
|
||||||
if (customSnippets[snippetId]) {
|
if (customSnippets[snippetId]) {
|
||||||
delete customSnippets[snippetId];
|
delete customSnippets[snippetId];
|
||||||
@@ -1705,22 +1799,19 @@ class ClaudeChatProvider {
|
|||||||
|
|
||||||
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; }
|
||||||
|
|
||||||
const configPath = path.join(storagePath, 'mcp', 'mcp-servers.json');
|
const configPath = path.join(storagePath, 'mcp', 'mcp-servers.json');
|
||||||
return path.join(configPath);
|
return path.join(configPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _sendAndSaveMessage(message: { type: string, data: any }): void {
|
private _sendAndSaveMessage(message: { type: string, data: any }): void {
|
||||||
|
|
||||||
// Initialize conversation if this is the first message
|
// Initialize conversation if this is the first message
|
||||||
if (this._currentConversation.length === 0) {
|
if (this._currentConversation.length === 0) {
|
||||||
this._conversationStartTime = new Date().toISOString();
|
this._conversationStartTime = new Date().toISOString();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (message.type === 'sessionInfo') {
|
|
||||||
message.data.sessionId;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send to UI using the helper method
|
// Send to UI using the helper method
|
||||||
this._postMessage(message);
|
this._postMessage(message);
|
||||||
|
|
||||||
@@ -1736,8 +1827,8 @@ class ClaudeChatProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async _saveCurrentConversation(): Promise<void> {
|
private async _saveCurrentConversation(): Promise<void> {
|
||||||
if (!this._conversationsPath || this._currentConversation.length === 0) {return;}
|
if (!this._conversationsPath || this._currentConversation.length === 0) { return; }
|
||||||
if(!this._currentSessionId) {return;}
|
if (!this._currentSessionId) { return; }
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Create filename from first user message and timestamp
|
// Create filename from first user message and timestamp
|
||||||
@@ -1756,7 +1847,7 @@ class ClaudeChatProvider {
|
|||||||
const datePrefix = startTime.substring(0, 16).replace('T', '_').replace(/:/g, '-');
|
const datePrefix = startTime.substring(0, 16).replace('T', '_').replace(/:/g, '-');
|
||||||
const filename = `${datePrefix}_${cleanMessage}.json`;
|
const filename = `${datePrefix}_${cleanMessage}.json`;
|
||||||
|
|
||||||
const conversationData = {
|
const conversationData: ConversationData = {
|
||||||
sessionId: sessionId,
|
sessionId: sessionId,
|
||||||
startTime: this._conversationStartTime,
|
startTime: this._conversationStartTime,
|
||||||
endTime: new Date().toISOString(),
|
endTime: new Date().toISOString(),
|
||||||
@@ -1823,8 +1914,8 @@ class ClaudeChatProvider {
|
|||||||
|
|
||||||
// Check if term matches filename or any part of the path
|
// Check if term matches filename or any part of the path
|
||||||
return fileName.includes(term) ||
|
return fileName.includes(term) ||
|
||||||
filePath.includes(term) ||
|
filePath.includes(term) ||
|
||||||
filePath.split('/').some(segment => segment.includes(term));
|
filePath.split('/').some(segment => segment.includes(term));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1877,6 +1968,14 @@ class ClaudeChatProvider {
|
|||||||
private _stopClaudeProcess(): void {
|
private _stopClaudeProcess(): void {
|
||||||
console.log('Stop request received');
|
console.log('Stop request received');
|
||||||
|
|
||||||
|
this._isProcessing = false
|
||||||
|
|
||||||
|
// Update UI state
|
||||||
|
this._postMessage({
|
||||||
|
type: 'setProcessing',
|
||||||
|
data: { isProcessing: false }
|
||||||
|
});
|
||||||
|
|
||||||
if (this._currentClaudeProcess) {
|
if (this._currentClaudeProcess) {
|
||||||
console.log('Terminating Claude process...');
|
console.log('Terminating Claude process...');
|
||||||
|
|
||||||
@@ -1894,12 +1993,6 @@ class ClaudeChatProvider {
|
|||||||
// Clear process reference
|
// Clear process reference
|
||||||
this._currentClaudeProcess = undefined;
|
this._currentClaudeProcess = undefined;
|
||||||
|
|
||||||
// Update UI state
|
|
||||||
this._postMessage({
|
|
||||||
type: 'setProcessing',
|
|
||||||
data: false
|
|
||||||
});
|
|
||||||
|
|
||||||
this._postMessage({
|
this._postMessage({
|
||||||
type: 'clearLoading'
|
type: 'clearLoading'
|
||||||
});
|
});
|
||||||
@@ -1916,7 +2009,7 @@ class ClaudeChatProvider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _updateConversationIndex(filename: string, conversationData: any): void {
|
private _updateConversationIndex(filename: string, conversationData: ConversationData): void {
|
||||||
// Extract first and last user messages
|
// Extract first and last user messages
|
||||||
const userMessages = conversationData.messages.filter((m: any) => m.messageType === 'userInput');
|
const userMessages = conversationData.messages.filter((m: any) => m.messageType === 'userInput');
|
||||||
const firstUserMessage = userMessages.length > 0 ? userMessages[0].data : 'No user message';
|
const firstUserMessage = userMessages.length > 0 ? userMessages[0].data : 'No user message';
|
||||||
@@ -1926,7 +2019,7 @@ class ClaudeChatProvider {
|
|||||||
const indexEntry = {
|
const indexEntry = {
|
||||||
filename: filename,
|
filename: filename,
|
||||||
sessionId: conversationData.sessionId,
|
sessionId: conversationData.sessionId,
|
||||||
startTime: conversationData.startTime,
|
startTime: conversationData.startTime || '',
|
||||||
endTime: conversationData.endTime,
|
endTime: conversationData.endTime,
|
||||||
messageCount: conversationData.messageCount,
|
messageCount: conversationData.messageCount,
|
||||||
totalCost: conversationData.totalCost,
|
totalCost: conversationData.totalCost,
|
||||||
@@ -1955,13 +2048,13 @@ class ClaudeChatProvider {
|
|||||||
|
|
||||||
private async _loadConversationHistory(filename: string): Promise<void> {
|
private async _loadConversationHistory(filename: string): Promise<void> {
|
||||||
console.log("_loadConversationHistory");
|
console.log("_loadConversationHistory");
|
||||||
if (!this._conversationsPath) {return;}
|
if (!this._conversationsPath) { return; }
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const filePath = path.join(this._conversationsPath, filename);
|
const filePath = path.join(this._conversationsPath, filename);
|
||||||
console.log("filePath", filePath);
|
console.log("filePath", filePath);
|
||||||
|
|
||||||
let conversationData;
|
let conversationData: ConversationData;
|
||||||
try {
|
try {
|
||||||
const fileUri = vscode.Uri.file(filePath);
|
const fileUri = vscode.Uri.file(filePath);
|
||||||
const content = await vscode.workspace.fs.readFile(fileUri);
|
const content = await vscode.workspace.fs.readFile(fileUri);
|
||||||
@@ -1970,7 +2063,6 @@ class ClaudeChatProvider {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("conversationData", conversationData);
|
|
||||||
// Load conversation into current state
|
// Load conversation into current state
|
||||||
this._currentConversation = conversationData.messages || [];
|
this._currentConversation = conversationData.messages || [];
|
||||||
this._conversationStartTime = conversationData.startTime;
|
this._conversationStartTime = conversationData.startTime;
|
||||||
@@ -1985,13 +2077,33 @@ class ClaudeChatProvider {
|
|||||||
type: 'sessionCleared'
|
type: 'sessionCleared'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let requestStartTime: number
|
||||||
|
|
||||||
// Small delay to ensure messages are cleared before loading new ones
|
// Small delay to ensure messages are cleared before loading new ones
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
for (const message of this._currentConversation) {
|
const messages = this._currentConversation;
|
||||||
|
for (let i = 0; i < messages.length; i++) {
|
||||||
|
|
||||||
|
const message = messages[i];
|
||||||
|
|
||||||
|
if(message.messageType === 'permissionRequest'){
|
||||||
|
const isLast = i === messages.length - 1;
|
||||||
|
if(!isLast){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this._postMessage({
|
this._postMessage({
|
||||||
type: message.messageType,
|
type: message.messageType,
|
||||||
data: message.data
|
data: message.data
|
||||||
});
|
});
|
||||||
|
if (message.messageType === 'userInput') {
|
||||||
|
try {
|
||||||
|
requestStartTime = new Date(message.timestamp).getTime()
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send updated totals
|
// Send updated totals
|
||||||
@@ -2005,6 +2117,13 @@ class ClaudeChatProvider {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Restore processing state if the conversation was saved while processing
|
||||||
|
if (this._isProcessing) {
|
||||||
|
this._postMessage({
|
||||||
|
type: 'setProcessing',
|
||||||
|
data: { isProcessing: this._isProcessing, requestStartTime }
|
||||||
|
});
|
||||||
|
}
|
||||||
// Send ready message after conversation is loaded
|
// Send ready message after conversation is loaded
|
||||||
this._sendReadyMessage();
|
this._sendReadyMessage();
|
||||||
}, 50);
|
}, 50);
|
||||||
@@ -2017,7 +2136,7 @@ class ClaudeChatProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _getHtmlForWebview(): string {
|
private _getHtmlForWebview(): string {
|
||||||
return html;
|
return getHtml(vscode.env?.isTelemetryEnabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _sendCurrentSettings(): void {
|
private _sendCurrentSettings(): void {
|
||||||
@@ -2037,15 +2156,43 @@ 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 _saveInputText(text: string): void {
|
||||||
|
this._draftMessage = text || '';
|
||||||
|
}
|
||||||
|
|
||||||
private async _updateSettings(settings: { [key: string]: any }): Promise<void> {
|
private async _updateSettings(settings: { [key: string]: any }): Promise<void> {
|
||||||
const config = vscode.workspace.getConfiguration('claudeCodeChat');
|
const config = vscode.workspace.getConfiguration('claudeCodeChat');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
for (const [key, value] of Object.entries(settings)) {
|
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) {
|
} catch (error) {
|
||||||
console.error('Failed to update settings:', error);
|
console.error('Failed to update settings:', error);
|
||||||
vscode.window.showErrorMessage('Failed to update settings');
|
vscode.window.showErrorMessage('Failed to update settings');
|
||||||
@@ -2193,7 +2340,7 @@ class ClaudeChatProvider {
|
|||||||
private async _createImageFile(imageData: string, imageType: string) {
|
private async _createImageFile(imageData: string, imageType: string) {
|
||||||
try {
|
try {
|
||||||
const workspaceFolder = vscode.workspace.workspaceFolders?.[0];
|
const workspaceFolder = vscode.workspace.workspaceFolders?.[0];
|
||||||
if (!workspaceFolder) {return;}
|
if (!workspaceFolder) { return; }
|
||||||
|
|
||||||
// Extract base64 data from data URL
|
// Extract base64 data from data URL
|
||||||
const base64Data = imageData.split(',')[1];
|
const base64Data = imageData.split(',')[1];
|
||||||
|
|||||||
2964
src/script.ts
Normal file
2964
src/script.ts
Normal file
File diff suppressed because it is too large
Load Diff
101
src/ui-styles.ts
101
src/ui-styles.ts
@@ -86,8 +86,8 @@ const styles = `
|
|||||||
/* Permission Request */
|
/* Permission Request */
|
||||||
.permission-request {
|
.permission-request {
|
||||||
margin: 4px 12px 20px 12px;
|
margin: 4px 12px 20px 12px;
|
||||||
background-color: var(--vscode-inputValidation-warningBackground);
|
background-color: rgba(252, 188, 0, 0.1);
|
||||||
border: 1px solid var(--vscode-inputValidation-warningBorder);
|
border: 1px solid rgba(252, 188, 0, 0.3);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||||
@@ -107,6 +107,86 @@ const styles = `
|
|||||||
font-size: 16px;
|
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 {
|
.permission-content {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
@@ -493,7 +573,7 @@ const styles = `
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 6px;
|
gap: 6px;
|
||||||
margin-top: 12px;
|
margin-top: 12px;
|
||||||
opacity: 0.8;
|
opacity: 1;
|
||||||
transition: opacity 0.2s ease;
|
transition: opacity 0.2s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -502,12 +582,12 @@ const styles = `
|
|||||||
}
|
}
|
||||||
|
|
||||||
.yolo-mode-section input[type="checkbox"] {
|
.yolo-mode-section input[type="checkbox"] {
|
||||||
transform: scale(0.8);
|
transform: scale(0.9);
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.yolo-mode-section label {
|
.yolo-mode-section label {
|
||||||
font-size: 10px;
|
font-size: 12px;
|
||||||
color: var(--vscode-descriptionForeground);
|
color: var(--vscode-descriptionForeground);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
@@ -1270,13 +1350,14 @@ const styles = `
|
|||||||
|
|
||||||
.input-field {
|
.input-field {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
color: var(--vscode-input-foreground);
|
color: var(--vscode-input-foreground);
|
||||||
border: none;
|
border: none;
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
outline: none;
|
outline: none;
|
||||||
font-family: var(--vscode-editor-font-family);
|
font-family: var(--vscode-editor-font-family);
|
||||||
min-height: 20px;
|
min-height: 68px;
|
||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
overflow-y: hidden;
|
overflow-y: hidden;
|
||||||
resize: none;
|
resize: none;
|
||||||
@@ -1447,13 +1528,13 @@ const styles = `
|
|||||||
|
|
||||||
.yolo-warning {
|
.yolo-warning {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
color: var(--vscode-inputValidation-errorForeground);
|
color: var(--vscode-foreground);
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
background-color: var(--vscode-inputValidation-errorBackground);
|
background-color: rgba(255, 99, 71, 0.08);
|
||||||
border: 1px solid var(--vscode-inputValidation-errorBorder);
|
border: 1px solid rgba(255, 99, 71, 0.2);
|
||||||
padding: 8px 12px;
|
padding: 8px 12px;
|
||||||
margin: 4px 12px;
|
margin: 4px 4px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
animation: slideDown 0.3s ease;
|
animation: slideDown 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
// "noUnusedParameters": true, /* Report errors on unused parameters. */
|
// "noUnusedParameters": true, /* Report errors on unused parameters. */
|
||||||
},
|
},
|
||||||
"exclude": [
|
"exclude": [
|
||||||
"mcp-permissions.js"
|
"mcp-permissions.js",
|
||||||
|
"claude-code-chat-permissions-mcp"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user