From 59379336e2a20619aaa82dfa6cbc830bcbea61b8 Mon Sep 17 00:00:00 2001 From: rob Date: Tue, 7 Oct 2025 21:30:30 +0000 Subject: [PATCH] Update src/ai-repo-commander.user.js No more premature detection - Commands won't be processed until the full --- terminator appears No more history pollution - Incomplete commands won't get marked as "already executed" Better streaming resilience - Waits for the command to stabilize before processing Configurable timing - You can adjust settle times in the Tools panel --- src/ai-repo-commander.user.js | 523 +++++++++++++++++++++++----------- 1 file changed, 360 insertions(+), 163 deletions(-) diff --git a/src/ai-repo-commander.user.js b/src/ai-repo-commander.user.js index 33712a4..59bbbf4 100644 --- a/src/ai-repo-commander.user.js +++ b/src/ai-repo-commander.user.js @@ -1,8 +1,8 @@ // ==UserScript== // @name AI Repo Commander // @namespace http://tampermonkey.net/ -// @version 1.3.4 -// @description Execute ^%$bridge YAML commands from AI assistants in code blocks with persistent dedupe, robust paste, optional auto-submit, and a built-in debug console (safe manual-retry only) +// @version 1.4.0 +// @description Execute ^%$bridge YAML commands from AI assistants (safe & robust): complete-block detection, streaming-settle, persistent dedupe, paste+autosubmit, debug console with Tools/Settings, draggable/collapsible panel // @author Your Name // @match https://chat.openai.com/* // @match https://chatgpt.com/* @@ -17,40 +17,68 @@ (function () { 'use strict'; - // ---------------------- Config ---------------------- - const CONFIG = { - ENABLE_API: true, // Master kill switch (STOP API flips this to false) - DEBUG_MODE: true, // Global on/off for debug logging - DEBUG_LEVEL: 2, // 0=off, 1=errors, 2=info, 3=verbose, 4=trace - DEBUG_WATCH_MS: 120000, // Only log tight loop spam for the first 2 minutes - DEBUG_MAX_LINES: 400, // In-memory + panel lines - DEBUG_SHOW_PANEL: true, // Show floating debug console UI - DEBOUNCE_DELAY: 5000, // Bot typing protection - MAX_RETRIES: 2, // Retry attempts (=> up to MAX_RETRIES+1 total tries) - VERSION: '1.3.4', + // ---------------------- Storage keys ---------------------- + const STORAGE_KEYS = { + history: 'ai_repo_commander_executed', + cfg: 'ai_repo_commander_cfg', + panel: 'ai_repo_commander_panel_state' + }; - PROCESS_EXISTING: false, // If false, only process *new* messages (no initial rescan) - ASSISTANT_ONLY: true, // Process assistant messages by default (core use case) + // ---------------------- Config (with persistence) ---------------------- + const DEFAULT_CONFIG = { + ENABLE_API: true, + DEBUG_MODE: true, + DEBUG_LEVEL: 2, // 0=off, 1=errors, 2=info, 3=verbose, 4=trace + DEBUG_WATCH_MS: 120000, + DEBUG_MAX_LINES: 400, + DEBUG_SHOW_PANEL: true, + DEBOUNCE_DELAY: 5000, // bot-typing protection + MAX_RETRIES: 2, + VERSION: '1.4.0', + + PROCESS_EXISTING: false, + ASSISTANT_ONLY: true, // Persistent dedupe window DEDUPE_TTL_MS: 30 * 24 * 60 * 60 * 1000, // 30 days // Housekeeping - CLEANUP_AFTER_MS: 30000, // Drop COMPLETE/ERROR entries after 30s - CLEANUP_INTERVAL_MS: 60000, // Sweep cadence + CLEANUP_AFTER_MS: 30000, + CLEANUP_INTERVAL_MS: 60000, // Paste + submit behavior - APPEND_TRAILING_NEWLINE: true, // Add '\n' after pasted text - AUTO_SUBMIT: true, // Try to submit after pasting content - POST_PASTE_DELAY_MS: 250, // Delay before submit to let editors settle - SUBMIT_MODE: 'button_first', // 'button_first' | 'enter_only' | 'smart' + APPEND_TRAILING_NEWLINE: true, + AUTO_SUBMIT: true, + POST_PASTE_DELAY_MS: 250, + SUBMIT_MODE: 'button_first', // 'button_first' | 'enter_only' | 'smart' - // Runtime toggles (live-updated by the debug panel) - RUNTIME: { - PAUSED: false, // Pause scanning + execution via panel - } + // Streaming-complete hardening (DeepSeek #2) + REQUIRE_TERMINATOR: true, // require trailing '---' line + SETTLE_CHECK_MS: 1500, // time to see the block remain unchanged + SETTLE_POLL_MS: 300, // poll interval for stability + + // Runtime toggles + RUNTIME: { PAUSED: false } }; + function loadSavedConfig() { + try { + const raw = localStorage.getItem(STORAGE_KEYS.cfg); + if (!raw) return structuredClone(DEFAULT_CONFIG); + const saved = JSON.parse(raw); + // shallow merge; keep nested RUNTIME + const merged = { ...DEFAULT_CONFIG, ...saved, RUNTIME: { ...DEFAULT_CONFIG.RUNTIME, ...(saved.RUNTIME || {}) } }; + return merged; + } catch { + return structuredClone(DEFAULT_CONFIG); + } + } + function saveConfig(cfg) { + try { localStorage.setItem(STORAGE_KEYS.cfg, JSON.stringify(cfg)); } catch {} + } + + const CONFIG = loadSavedConfig(); + // ---------------------- Debug Console ---------------------- let RC_DEBUG = null; @@ -61,8 +89,12 @@ this.loopCounts = new Map(); this.startedAt = Date.now(); this.panel = null; + this.bodyLogs = null; + this.bodyTools = null; + this.collapsed = false; + this.drag = { active: false, dx: 0, dy: 0 }; + this.panelState = this._loadPanelState(); - // loop-counts periodic cleanup to avoid unbounded growth this.loopCleanupInterval = setInterval(() => { if (Date.now() - this.startedAt > this.cfg.DEBUG_WATCH_MS * 2) { this.loopCounts.clear(); @@ -74,7 +106,21 @@ if (cfg.DEBUG_SHOW_PANEL) this.mount(); this.info(`Debug console ready (level=${cfg.DEBUG_LEVEL})`); } - // Levels: 1=ERROR, 2=WARN, 3=INFO, 4=VERB, 5=TRACE + + _loadPanelState() { + try { + return JSON.parse(localStorage.getItem(STORAGE_KEYS.panel) || '{}'); + } catch { return {}; } + } + _savePanelState(partial) { + try { + const merged = { ...(this.panelState || {}), ...(partial || {}) }; + this.panelState = merged; + localStorage.setItem(STORAGE_KEYS.panel, JSON.stringify(merged)); + } catch {} + } + + // Levels error(msg, data) { this._log(1, 'ERROR', msg, data); } warn(msg, data) { this._log(2, 'WARN', msg, data); } info(msg, data) { this._log(3, 'INFO', msg, data); } @@ -89,7 +135,6 @@ nowIso() { return new Date().toISOString(); } withinWatch() { return Date.now() - this.startedAt <= this.cfg.DEBUG_WATCH_MS; } - // Loop/ticker messages → suppress after 10 repeats or after WATCH window logLoop(kind, msg) { const k = `${kind}:${msg}`; const cur = this.loopCounts.get(k) || 0; @@ -97,7 +142,6 @@ if (cur >= 10) return; this.loopCounts.set(k, cur + 1); const suffix = (cur + 1) > 1 ? ` (${cur + 1}x)` : ''; - // default to INFO (visible at level 2+) if (kind === 'ERROR') this.error(`${msg}${suffix}`); else if (kind === 'WARN') this.warn(`${msg}${suffix}`); else this.info(`${msg}${suffix}`); @@ -134,8 +178,9 @@ } setLevel(n) { - const lv = Math.max(0, Math.min(4, n)); // clamp 0..4 + const lv = Math.max(0, Math.min(4, n)); this.cfg.DEBUG_LEVEL = lv; + saveConfig(this.cfg); this.info(`Log level => ${lv}`); } @@ -155,8 +200,6 @@ _log(numericLevel, levelName, msg, data) { if (!this.cfg.DEBUG_MODE) return; - - // Threshold map: 0=off, 1=ERROR, 2=+WARN+INFO, 3=+VERB, 4=+TRACE const thresholdMap = { 0: 0, 1: 1, 2: 3, 3: 4, 4: 5 }; const threshold = thresholdMap[this.cfg.DEBUG_LEVEL] ?? 0; if (numericLevel > threshold) return; @@ -164,34 +207,34 @@ const entry = { ts: this.nowIso(), level: levelName, msg: String(msg), data: this._sanitize(data) }; this.buf.push(entry); if (this.buf.length > this.cfg.DEBUG_MAX_LINES) this.buf.splice(0, this.buf.length - this.cfg.DEBUG_MAX_LINES); - - // Keep console quiet unless verbose+ is enabled if (this.cfg.DEBUG_LEVEL >= 3) { const prefix = `[AI RC]`; if (entry.data != null) console.log(prefix, entry.level, entry.msg, entry.data); else console.log(prefix, entry.level, entry.msg); } - if (this.panel) this._renderRow(entry); } mount() { - if (!document.body) { - setTimeout(() => this.mount(), 100); - return; - } + if (!document.body) { setTimeout(() => this.mount(), 100); return; } + const root = document.createElement('div'); root.style.cssText = ` - position: fixed; right: 16px; bottom: 16px; z-index: 2147483647; - width: 420px; max-height: 45vh; display: flex; flex-direction: column; + position: fixed; ${this.panelState.left!==undefined ? `left:${this.panelState.left}px; top:${this.panelState.top}px;` : 'right:16px; bottom:16px;'} + z-index: 2147483647; + width: 460px; max-height: 55vh; display: flex; flex-direction: column; background: rgba(20,20,24,0.92); border:1px solid #3b3b46; border-radius: 8px; color:#e5e7eb; font: 12px/1.4 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; box-shadow: 0 16px 40px rgba(0,0,0,0.55); backdrop-filter: blur(4px); `; root.innerHTML = ` -
- AI Repo Commander — Debug -