diff --git a/CLAUDE.md b/CLAUDE.md index b603c277..316d565d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -16,7 +16,7 @@ When user says "COM": 1. Increment version in BOTH `package.json` AND `CLAUDE.md` 2. Run: `git add -A && git commit -m "chore: bump version to X.XXXX" && git push && npm run build && systemctl --user restart claudeman-web` -**Version**: 0.1421 (must match `package.json`) +**Version**: 0.1422 (must match `package.json`) ## Project Overview diff --git a/package.json b/package.json index 4f82ad40..0df593e8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "claudeman", - "version": "0.1421", + "version": "0.1422", "description": "The missing control plane for Claude Code - run 20 autonomous agents with real-time monitoring and session persistence", "type": "module", "main": "dist/index.js", diff --git a/src/web/public/app.js b/src/web/public/app.js index 70a9368e..dd5bb9e6 100644 --- a/src/web/public/app.js +++ b/src/web/public/app.js @@ -1971,6 +1971,8 @@ class ClaudemanApp { const data = JSON.parse(e.data); console.log('[Plan Started]', data); this.activePlanOrchestratorId = data.orchestratorId; + this.planGenerationStopped = false; // Reset flag for new generation + this.renderMonitorPlanAgents(); }); // Plan generation cancelled @@ -1980,6 +1982,7 @@ class ClaudemanApp { if (this.activePlanOrchestratorId === data.orchestratorId) { this.activePlanOrchestratorId = null; } + this.renderMonitorPlanAgents(); }); // Plan generation completed @@ -1989,6 +1992,7 @@ class ClaudemanApp { if (this.activePlanOrchestratorId === data.orchestratorId) { this.activePlanOrchestratorId = null; } + this.renderMonitorPlanAgents(); }); } @@ -2722,6 +2726,50 @@ class ClaudemanApp { this.wizardPosition = null; this.planAgentsMinimized = false; + // === CRITICAL: Clean up any orphaned plan generation state === + + // Cancel any in-flight plan generation + if (this.activePlanOrchestratorId) { + console.log('[Wizard Close] Cancelling active plan orchestrator:', this.activePlanOrchestratorId); + fetch('/api/cancel-plan-generation', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ orchestratorId: this.activePlanOrchestratorId }), + }).catch(err => console.error('[Wizard Close] Failed to cancel plan:', err)); + this.activePlanOrchestratorId = null; + } + + // Abort any in-flight fetch request + if (this.planGenerationAbortController) { + this.planGenerationAbortController.abort(); + this.planGenerationAbortController = null; + } + + // Clear progress handler to prevent orphaned callbacks + this._planProgressHandler = null; + + // Clear plan loading timers + if (this.planLoadingTimer) { + clearInterval(this.planLoadingTimer); + this.planLoadingTimer = null; + } + if (this.planPhaseTimer) { + clearTimeout(this.planPhaseTimer); + this.planPhaseTimer = null; + } + + // Mark generation as stopped to ignore any late SSE events + this.planGenerationStopped = true; + + // Close all plan subagent windows + this.closePlanSubagentWindows(); + + // Clear wizard minimized windows tracking + this.wizardMinimizedWindows = null; + + // Update monitor panel to reflect changes + this.renderMonitorPlanAgents(); + // Update connection lines (wizard closed, revert to normal) this.updateConnectionLines(); } @@ -4024,6 +4072,9 @@ class ClaudemanApp { if (detailEl) detailEl.textContent = detail || ''; } } + + // Update monitor panel to show plan agents + this.renderMonitorPlanAgents(); } /** @@ -4360,6 +4411,8 @@ class ClaudemanApp { this.updateWizardAgentCount(); this.removeAgentsMinimizedTab(); this.updateConnectionLines(); + // Update monitor panel (will hide section if no agents) + this.renderMonitorPlanAgents(); } // ========== Wizard Dragging ========== @@ -9837,9 +9890,35 @@ class ClaudemanApp { } this.logViewerWindows.clear(); + // Clean up plan subagent windows (wizard agents) + if (this.planSubagents) { + for (const [agentId, windowData] of this.planSubagents) { + if (windowData.dragListeners) { + document.removeEventListener('mousemove', windowData.dragListeners.move); + document.removeEventListener('mouseup', windowData.dragListeners.up); + } + if (windowData.element) { + windowData.element.remove(); + } + } + this.planSubagents.clear(); + } + + // Clear orphaned plan generation state + this.activePlanOrchestratorId = null; + this._planProgressHandler = null; + this.planGenerationStopped = true; + if (this.planGenerationAbortController) { + this.planGenerationAbortController.abort(); + this.planGenerationAbortController = null; + } + // Clear minimized agents tracking this.minimizedSubagents.clear(); + // Update monitor panel + this.renderMonitorPlanAgents(); + // Update connection lines (should be empty now) this.updateConnectionLines(); } @@ -11252,6 +11331,90 @@ class ClaudemanApp { } } + // ========== Plan Wizard Agents in Monitor ========== + + renderMonitorPlanAgents() { + const section = document.getElementById('monitorPlanAgentsSection'); + const body = document.getElementById('monitorPlanAgentsBody'); + const stats = document.getElementById('monitorPlanAgentStats'); + if (!section || !body) return; + + const planAgents = Array.from(this.planSubagents?.values() || []); + const hasActiveOrchestrator = !!this.activePlanOrchestratorId; + + // Show section only if there are plan agents or active orchestrator + if (planAgents.length === 0 && !hasActiveOrchestrator) { + section.style.display = 'none'; + return; + } + + section.style.display = ''; + + const activeCount = planAgents.filter(a => a.status === 'running').length; + const completedCount = planAgents.filter(a => a.status === 'completed' || a.status === 'failed').length; + + if (stats) { + if (hasActiveOrchestrator) { + stats.textContent = `${activeCount} running, ${completedCount} done`; + } else { + stats.textContent = `${planAgents.length} total`; + } + } + + if (planAgents.length === 0) { + body.innerHTML = `