mirror of
https://github.com/andrepimenta/claude-code-chat.git
synced 2026-06-02 02:15:45 +08:00
Fix new chat
This commit is contained in:
129
src/extension.ts
129
src/extension.ts
@@ -56,7 +56,6 @@ interface ConversationData {
|
|||||||
};
|
};
|
||||||
messages: Array<{ timestamp: string, messageType: string, data: any }>;
|
messages: Array<{ timestamp: string, messageType: string, data: any }>;
|
||||||
filename: string;
|
filename: string;
|
||||||
isProcessing?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class ClaudeChatWebviewProvider implements vscode.WebviewViewProvider {
|
class ClaudeChatWebviewProvider implements vscode.WebviewViewProvider {
|
||||||
@@ -64,7 +63,7 @@ class ClaudeChatWebviewProvider implements vscode.WebviewViewProvider {
|
|||||||
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,
|
||||||
@@ -440,7 +439,7 @@ class ClaudeChatProvider {
|
|||||||
// Set processing state to true
|
// Set processing state to true
|
||||||
this._postMessage({
|
this._postMessage({
|
||||||
type: 'setProcessing',
|
type: 'setProcessing',
|
||||||
data: {isProcessing: true}
|
data: { isProcessing: true }
|
||||||
});
|
});
|
||||||
|
|
||||||
// Create backup commit before Claude makes changes
|
// Create backup commit before Claude makes changes
|
||||||
@@ -573,6 +572,10 @@ 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;
|
||||||
|
|
||||||
@@ -584,6 +587,12 @@ class ClaudeChatProvider {
|
|||||||
// Reset processing state
|
// Reset processing state
|
||||||
this._isProcessing = false;
|
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({
|
||||||
@@ -596,6 +605,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;
|
||||||
|
|
||||||
@@ -603,6 +616,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({
|
||||||
@@ -624,6 +645,18 @@ class ClaudeChatProvider {
|
|||||||
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;
|
||||||
|
|
||||||
@@ -712,7 +745,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;
|
||||||
|
|
||||||
@@ -784,7 +817,7 @@ class ClaudeChatProvider {
|
|||||||
// Clear processing state
|
// Clear processing state
|
||||||
this._postMessage({
|
this._postMessage({
|
||||||
type: 'setProcessing',
|
type: 'setProcessing',
|
||||||
data: {isProcessing: false}
|
data: { isProcessing: false }
|
||||||
});
|
});
|
||||||
|
|
||||||
// Update cumulative tracking
|
// Update cumulative tracking
|
||||||
@@ -819,6 +852,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;
|
||||||
|
|
||||||
@@ -866,7 +915,7 @@ class ClaudeChatProvider {
|
|||||||
// Clear processing state
|
// Clear processing state
|
||||||
this._postMessage({
|
this._postMessage({
|
||||||
type: 'setProcessing',
|
type: 'setProcessing',
|
||||||
data: {isProcessing: false}
|
data: { isProcessing: false }
|
||||||
});
|
});
|
||||||
|
|
||||||
// Show login required message
|
// Show login required message
|
||||||
@@ -896,7 +945,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.`,
|
||||||
@@ -906,7 +955,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) {
|
||||||
@@ -939,7 +988,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();
|
||||||
@@ -1048,10 +1097,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');
|
||||||
|
|
||||||
@@ -1070,7 +1119,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');
|
||||||
@@ -1124,13 +1173,13 @@ class ClaudeChatProvider {
|
|||||||
private async _initializePermissions(): Promise<void> {
|
private async _initializePermissions(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
if(this._permissionWatcher){
|
if (this._permissionWatcher) {
|
||||||
this._permissionWatcher.dispose();
|
this._permissionWatcher.dispose();
|
||||||
this._permissionWatcher = undefined;
|
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'));
|
||||||
@@ -1558,10 +1607,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' } });
|
||||||
@@ -1657,7 +1706,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
|
||||||
@@ -1673,7 +1722,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);
|
||||||
@@ -1695,7 +1744,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];
|
||||||
@@ -1736,13 +1785,13 @@ 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 }, options?: { isProcessing?: boolean }): 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) {
|
||||||
@@ -1764,10 +1813,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; }
|
||||||
|
|
||||||
console.log("IS PROCESSING", this._isProcessing)
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Create filename from first user message and timestamp
|
// Create filename from first user message and timestamp
|
||||||
@@ -1786,7 +1833,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 : ConversationData = {
|
const conversationData: ConversationData = {
|
||||||
sessionId: sessionId,
|
sessionId: sessionId,
|
||||||
startTime: this._conversationStartTime,
|
startTime: this._conversationStartTime,
|
||||||
endTime: new Date().toISOString(),
|
endTime: new Date().toISOString(),
|
||||||
@@ -1800,10 +1847,6 @@ class ClaudeChatProvider {
|
|||||||
filename
|
filename
|
||||||
};
|
};
|
||||||
|
|
||||||
if (this._isProcessing !== undefined){
|
|
||||||
conversationData.isProcessing = this._isProcessing || false
|
|
||||||
}
|
|
||||||
|
|
||||||
const filePath = path.join(this._conversationsPath, filename);
|
const filePath = path.join(this._conversationsPath, filename);
|
||||||
const content = new TextEncoder().encode(JSON.stringify(conversationData, null, 2));
|
const content = new TextEncoder().encode(JSON.stringify(conversationData, null, 2));
|
||||||
await vscode.workspace.fs.writeFile(vscode.Uri.file(filePath), content);
|
await vscode.workspace.fs.writeFile(vscode.Uri.file(filePath), content);
|
||||||
@@ -1857,8 +1900,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));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1916,7 +1959,7 @@ class ClaudeChatProvider {
|
|||||||
// Update UI state
|
// Update UI state
|
||||||
this._postMessage({
|
this._postMessage({
|
||||||
type: 'setProcessing',
|
type: 'setProcessing',
|
||||||
data: {isProcessing: false}
|
data: { isProcessing: false }
|
||||||
});
|
});
|
||||||
|
|
||||||
if (this._currentClaudeProcess) {
|
if (this._currentClaudeProcess) {
|
||||||
@@ -1991,7 +2034,7 @@ 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);
|
||||||
@@ -2006,7 +2049,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;
|
||||||
@@ -2030,10 +2072,10 @@ class ClaudeChatProvider {
|
|||||||
type: message.messageType,
|
type: message.messageType,
|
||||||
data: message.data
|
data: message.data
|
||||||
});
|
});
|
||||||
if(message.messageType === 'userInput'){
|
if (message.messageType === 'userInput') {
|
||||||
try{
|
try {
|
||||||
requestStartTime = new Date(message.timestamp).getTime()
|
requestStartTime = new Date(message.timestamp).getTime()
|
||||||
}catch(e){
|
} catch (e) {
|
||||||
console.log(e)
|
console.log(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2051,11 +2093,10 @@ class ClaudeChatProvider {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Restore processing state if the conversation was saved while processing
|
// Restore processing state if the conversation was saved while processing
|
||||||
if (conversationData.isProcessing) {
|
if (this._isProcessing) {
|
||||||
this._isProcessing = conversationData.isProcessing;
|
|
||||||
this._postMessage({
|
this._postMessage({
|
||||||
type: 'setProcessing',
|
type: 'setProcessing',
|
||||||
data: {isProcessing: conversationData.isProcessing, requestStartTime}
|
data: { isProcessing: this._isProcessing, requestStartTime }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// Send ready message after conversation is loaded
|
// Send ready message after conversation is loaded
|
||||||
@@ -2270,7 +2311,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];
|
||||||
|
|||||||
Reference in New Issue
Block a user