Fix new chat

This commit is contained in:
andrepimenta
2025-07-28 23:33:17 +01:00
parent 4f126641e4
commit 6bd906981b

View File

@@ -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];