diff --git a/src/cli/chat/autonomous-claude-interface.ts b/src/cli/chat/autonomous-claude-interface.ts index 74a3b9a9..3a0f5513 100644 --- a/src/cli/chat/autonomous-claude-interface.ts +++ b/src/cli/chat/autonomous-claude-interface.ts @@ -1609,12 +1609,19 @@ You are NOT a cautious assistant - you are a proactive, autonomous developer who this.keypressHandler = undefined } - // Remove all event handlers from readline + // Remove event listeners if (this.rl) { this.eventHandlers.forEach((handler, event) => { this.rl.removeListener(event, handler) }) this.eventHandlers.clear() + this.rl.removeAllListeners() + } + + // Remove global process listeners + const sigintHandler = this.eventHandlers.get('SIGINT') + if (sigintHandler) { + process.removeListener('SIGINT', sigintHandler) } // Reset raw mode with proper state restoration diff --git a/src/cli/nik-cli.ts b/src/cli/nik-cli.ts index d97b1e04..e3c26b8e 100644 --- a/src/cli/nik-cli.ts +++ b/src/cli/nik-cli.ts @@ -16936,6 +16936,11 @@ This file is automatically maintained by NikCLI to provide consistent context ac this.printShutdownLog(`Token status bar cleanup warning: ${error.message}`, 'warning') } + // Remove global process listeners to prevent memory leaks + const shutdownHandler = this.gracefulShutdown?.bind(this) || (() => {}) + process.removeListener('SIGINT', shutdownHandler) + process.removeListener('SIGTERM', shutdownHandler) + // Remove keypress listener to prevent loop if (this.keypressListener) { process.stdin.removeListener('keypress', this.keypressListener) @@ -16943,9 +16948,34 @@ This file is automatically maintained by NikCLI to provide consistent context ac } if (this.rl) { + // Remove all listeners from readline interface before closing + this.rl.removeAllListeners() this.rl.close() } + // Remove auth provider listeners + try { + authProvider.removeAllListeners('signed_in') + authProvider.removeAllListeners('signed_out') + authProvider.removeAllListeners('auto_login_success') + } catch (_e) { + // Ignore auth cleanup errors + } + + // Remove agent service listeners + try { + agentService.removeAllListeners('task_start') + agentService.removeAllListeners('task_progress') + agentService.removeAllListeners('tool_use') + agentService.removeAllListeners('task_complete') + agentService.removeAllListeners('file_read') + agentService.removeAllListeners('file_written') + agentService.removeAllListeners('file_list') + agentService.removeAllListeners('grep_results') + } catch (_e) { + // Ignore agent service cleanup errors + } + // Cleanup systems this.agentManager.cleanup() diff --git a/src/cli/unified-chat.ts b/src/cli/unified-chat.ts index 53710ba6..a6ee04fe 100644 --- a/src/cli/unified-chat.ts +++ b/src/cli/unified-chat.ts @@ -866,6 +866,13 @@ export class UnifiedChatInterface extends EventEmitter { this.rl.removeListener(event, handler) }) this.eventHandlers.clear() + this.rl.removeAllListeners() + } + + // Remove global process listeners + const sigintHandler = this.eventHandlers.get('SIGINT') + if (sigintHandler) { + process.removeListener('SIGINT', sigintHandler) } // Clear session data diff --git a/src/cli/unified-cli.ts b/src/cli/unified-cli.ts index e4f07c83..276ee6fd 100644 --- a/src/cli/unified-cli.ts +++ b/src/cli/unified-cli.ts @@ -34,6 +34,11 @@ const gracefulShutdown = (signal: string) => { } catch (error) { console.error('Error during shutdown:', error) } finally { + // Remove listeners before exit to prevent memory leaks in case of multiple starts + process.removeAllListeners('SIGINT') + process.removeAllListeners('SIGTERM') + process.removeAllListeners('uncaughtException') + process.removeAllListeners('unhandledRejection') process.exit(0) } }