Remove live commands from CLI, delete src/live, add server-lost cleanup

1. CLI cleanup: removed live, poll, and wrap commands from bin/cli.js
   and the liveCli export from detect-antipatterns.mjs. These now live
   exclusively in the skill scripts (node scripts_path/live-server.mjs).

2. Deleted src/live/: server.mjs, poll.mjs, wrap.mjs, browser.js,
   protocol.mjs. The source of truth is now source/skills/impeccable/
   scripts/live-*.

3. Graceful server-lost handling: the browser tracks SSE reconnection
   attempts (max 5). After exhausting retries, it cleans up the UI:
   hides the bar, highlight, and cycler, shows a "Live server
   disconnected" toast, resets state to IDLE. This handles agent
   crashes, server kills, and network issues without leaving the
   browser stuck in a "Generating..." state.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Paul Bakaus
2026-04-12 19:25:29 -07:00
co-authored by Claude Opus 4.6
parent 5bad08723d
commit 7011a523e0
19 changed files with 348 additions and 2269 deletions
@@ -870,11 +870,14 @@
// ---------------------------------------------------------------------------
let evtSource = null;
let sseRetries = 0;
const SSE_MAX_RETRIES = 5;
function connectSSE() {
evtSource = new EventSource('http://localhost:' + PORT + '/events?token=' + TOKEN);
evtSource.onmessage = (e) => {
sseRetries = 0; // reset on any successful message
let msg; try { msg = JSON.parse(e.data); } catch { return; }
switch (msg.type) {
case 'connected':
@@ -918,11 +921,35 @@
};
evtSource.onerror = () => {
// EventSource auto-reconnects. Log once.
console.log('[impeccable] SSE connection lost. Reconnecting...');
sseRetries++;
if (sseRetries <= SSE_MAX_RETRIES) {
console.log('[impeccable] SSE connection lost. Retry ' + sseRetries + '/' + SSE_MAX_RETRIES + '...');
return; // EventSource auto-reconnects
}
// Server is gone. Clean up gracefully.
console.log('[impeccable] Live server unreachable. Cleaning up UI.');
evtSource.close();
evtSource = null;
handleServerLost();
};
}
/** Server died or became unreachable. Reset UI to a clean state. */
function handleServerLost() {
if (state === 'GENERATING' || state === 'CYCLING' || state === 'SAVING') {
showToast('Live server disconnected. Session ended.', 5000);
}
hideBar();
hideHighlight();
stopScrollTracking();
if (variantObserver) { variantObserver.disconnect(); variantObserver = null; }
clearSession();
selectedElement = null;
currentSessionId = null;
selectedAction = 'impeccable';
state = 'IDLE';
}
function sendEvent(msg) {
msg.token = TOKEN;
fetch('http://localhost:' + PORT + '/events', {