' =================================================================== ' File: command-executor.puml ' Purpose: Single source of truth for module-level activity + per-method sequences. ' Module: command-executor.js — Execute validated repo commands via bridge API; retries, timeouts, mock mode. ' Edit rules: Follow the legend at bottom; preserve VIEW/METHOD anchors for automation. ' =================================================================== ' Neutral defaults — typography/layout only (keeps partition colors intact) skinparam Shadowing false skinparam SequenceMessageAlign center skinparam SequenceLifeLineBorderColor #666666 skinparam SequenceLifeLineBorderThickness 1 ' ==== VIEW: Branch Flow (command-executor.js) ============================== @startuml title command-executor.js — Branch Flow (full module) start :command-executor; fork ' -------- execute(command, el, label) -------- partition "execute(command, el, label)" #E7FAE3 { :execute; :log.command(action,'executing');\nautogen commit_message (for file ops); :if api.disabled -> delay + _success(mock); :else -> res=_api(command) -> _success(res); :catch -> _error(err); kill } fork again ' -------- _api(command, attempt=0) -------- partition "_api(command, attempt=0)" #FFF6D1 { :_api; :read cfg (maxRetries, timeout, bridgeKey);\nPOST JSON via GM_xmlhttpRequest; :onload 2xx -> resolve;\nelse -> reject(Error status); :onerror -> retry with backoff or fail; :ontimeout -> reject(Error timeout); kill } fork again ' -------- _getBridgeKey() -------- partition "_getBridgeKey()" #FFE1DB { :_getBridgeKey; :key = cfg.get('api.bridgeKey');\nif missing -> prompt;\noptional persist via cfg.set; :return key or throw if empty; kill } fork again ' -------- _success(response, command, el, isMock, label) -------- partition "_success(response, command, el, isMock, label)" #DCF9EE { :_success; :parse JSON responseText; _status(el, type);\nbranch to _handleGetFile/_handleListFiles; :return { success:true, data, isMock }; kill } fork again ' -------- _error(error, command, el, label) -------- partition "_error(error, command, el, label)" #FFE6F0 { :_error; :_status(el, 'ERROR', details);\nreturn { success:false, error }; kill } fork again ' -------- _status(el, type, data) -------- partition "_status(el, type, data)" #E6F3FF { :_status; :create styled div;\nleft border color via _color(type);\nappend to el; kill } fork again ' -------- _handleGetFile(data, label) -------- partition "_handleGetFile(data, label)" #F0E6FA { :_handleGetFile; :pull content from various shapes;\npush to window.AI_REPO_RESPONSES; kill } fork again ' -------- _handleListFiles(data, label) -------- partition "_handleListFiles(data, label)" #E7FAF7 { :_handleListFiles; :files[] -> text fenced listing;\npush to window.AI_REPO_RESPONSES; kill } fork again ' -------- delay(ms) -------- partition "delay(ms)" #FFF2E7 { :delay; :Promise(resolve after setTimeout(ms)); kill } end fork @enduml ' ==== METHOD: execute(command, sourceElement, label) ======================== @startuml title command-executor:execute(command, el, label): \n Build request, call API or mock, normalize and render result participant "Caller" as CL participant "execute()" as EXE participant "delay(ms)" as DLY participant "_api(command, attempt)" as API participant "_success(...)" as OK participant "_error(...)" as ERR participant "Config" as CFG participant "Logger" as LOG activate CL CL -> EXE : initial request (command, el, label) activate EXE EXE -> LOG : command(action,'executing', {repo,path,label}) LOG --> EXE : ok ' Auto-commit message for file ops EXE -> EXE : if action in {update_file, create_file} and !commit_message\n commit_message = "AI Repo Commander: ... " ' Mock path if API disabled EXE -> CFG : get('api.enabled') CFG --> EXE : true/false alt api.enabled == false EXE -> LOG : warn("API disabled, using mock") EXE -> DLY : delay(300) DLY --> EXE : done EXE -> OK : _success({status:200,responseText:'{\"success\":true,...}'}, command, el, true, label) OK --> EXE : result EXE -> LOG : command(action,'complete', {mock:true}) EXE --> CL : result else real API call EXE -> LOG : verbose("Making API request", {url,label}) EXE -> API : _api(command) API --> EXE : res {status, responseText} EXE -> LOG : verbose("API request succeeded", {status}) EXE -> OK : _success(res, command, el, false, label) OK --> EXE : result EXE -> LOG : command(action,'complete', {repo,path}) EXE --> CL : result ' error handling else error thrown EXE -> LOG : command(action,'error', {error}) EXE -> LOG : error("Command execution failed", {action,error}) EXE -> ERR : _error(err, command, el, label) ERR --> EXE : error result EXE --> CL : error result end deactivate EXE deactivate CL @enduml ' ==== METHOD: _api(command, attempt=0) ===================================== @startuml title command-executor:_api(command, attempt=0): \n POST JSON via GM_xmlhttpRequest with retries and timeout participant "Caller" as CL participant "_api(command, attempt)" as API participant "_getBridgeKey()" as KEY participant "Config" as CFG participant "Logger" as LOG participant "GM_xmlhttpRequest" as GMX participant "Timer" as TMR activate CL CL -> API : command, attempt=0 activate API API -> CFG : get('api.maxRetries') / get('api.timeout') CFG --> API : maxRetries / timeout API -> KEY : _getBridgeKey() KEY --> API : bridgeKey API -> LOG : trace("GM_xmlhttpRequest details", {method:'POST', url, timeout, hasKey, attempt}) LOG --> API : ok API -> GMX : POST { url:command.url, headers:{X-Bridge-Key,Content-Type}, data:JSON.stringify(command), timeout } alt onload 2xx GMX --> API : response {status 2xx, responseText} API --> CL : response else onload error status GMX --> API : response {status !2xx, statusText} API --> CL : throws Error(`API Error ${status}: ${statusText}`) else onerror (network) GMX --> API : error alt attempt < maxRetries API -> LOG : warn("Network error, retrying", {nextDelay}) API -> TMR : setTimeout(1000*(attempt+1)) TMR --> API : wake API -> API : return _api(command, attempt+1) API --> CL : bubbled result else max retries exceeded API -> LOG : error("Network error, max retries exceeded") API --> CL : throws Error(`Network error after ${attempt+1} attempts`) end else ontimeout GMX --> API : timeout API -> LOG : error("API request timed out", {timeout, action}) API --> CL : throws Error(`API timeout after ${timeout}ms`) end deactivate API deactivate CL @enduml ' ==== METHOD: _getBridgeKey() ============================================== @startuml title command-executor:_getBridgeKey(): \n Read bridge key from config or prompt; optionally persist participant "Caller" as CL participant "_getBridgeKey()" as KEY participant "Config" as CFG participant "Logger" as LOG participant "Prompt" as PR participant "Confirm" as CF activate CL CL -> KEY : initial request activate KEY KEY -> CFG : get('api.bridgeKey') CFG --> KEY : key | undefined alt key present KEY -> LOG : trace("Using saved bridge key from config") LOG --> KEY : ok KEY --> CL : key else key missing KEY -> LOG : warn("Bridge key not found, prompting user") LOG --> KEY : ok KEY -> PR : prompt("Enter your bridge key…") PR --> KEY : key | "" alt empty string KEY -> LOG : error("User did not provide bridge key") KEY --> CL : throws Error('Bridge key required when API is enabled') else provided KEY -> CF : confirm("Save this bridge key?") CF --> KEY : yes/no alt yes KEY -> CFG : set('api.bridgeKey', key) CFG --> KEY : ok KEY -> LOG : info("Bridge key saved to config") else no KEY -> LOG : info("Bridge key accepted for this session only") end KEY --> CL : key end end deactivate KEY deactivate CL @enduml ' ==== METHOD: _success(response, command, el, isMock, label) ================ @startuml title command-executor:_success(response, command, el, isMock, label): \n Parse body, render status, route to output handlers participant "Caller" as CL participant "_success(...)" as OK participant "JSON" as JS participant "_status(...)" as STAT participant "_handleGetFile(...)" as HGF participant "_handleListFiles(...)" as HLF activate CL CL -> OK : response, command, el, isMock, label activate OK OK -> JS : JSON.parse(response.responseText || "{}") alt parse ok JS --> OK : data else parse error JS --> OK : { message: "Operation completed" } end OK -> STAT : _status(el, isMock?'MOCK':'SUCCESS', {action, details: data.message || 'Completed successfully', label}) STAT --> OK : rendered alt command.action == "get_file" OK -> HGF : _handleGetFile(data, label) HGF --> OK : stored else command.action == "list_files" OK -> HLF : _handleListFiles(data, label) HLF --> OK : stored end OK --> CL : { success:true, data, isMock } deactivate OK deactivate CL @enduml ' ==== METHOD: _error(error, command, el, label) ============================= @startuml title command-executor:_error(error, command, el, label): \n Render error status and return failure object participant "Caller" as CL participant "_error(...)" as ERR participant "_status(...)" as STAT activate CL CL -> ERR : error, command, el, label activate ERR ERR -> STAT : _status(el, 'ERROR', {action: command.action, details: error.message, label}) STAT --> ERR : rendered ERR --> CL : { success:false, error: error.message } deactivate ERR deactivate CL @enduml ' ==== METHOD: _status(el, type, data) ====================================== @startuml title command-executor:_status(el, type, data): \n Append styled status div to message element participant "Caller" as CL participant "_status(...)" as STAT participant "_color(type)" as CLR participant "DOM" as DOM activate CL CL -> STAT : el, type, data activate STAT STAT -> CLR : _color(type) CLR --> STAT : hex color STAT -> DOM : createElement('div'); set styles (left border color) DOM --> STAT : div STAT -> DOM : set textContent `${label||action} — ${type}: details` DOM --> STAT : ready STAT -> DOM : appendChild(el, div) DOM --> STAT : appended STAT --> CL : (void) deactivate STAT deactivate CL @enduml ' ==== METHOD: _handleGetFile(data, label) =================================== @startuml title command-executor:_handleGetFile(data, label): \n Extract content and store for paste-back participant "Caller" as CL participant "_handleGetFile(...)" as HGF participant "Logger" as LOG activate CL CL -> HGF : data, label activate HGF HGF -> HGF : content = data?.content?.data || data?.content || data?.result?.content?.data || data?.result?.content alt content missing HGF -> LOG : warn("get_file response missing content field") else content present HGF -> HGF : window.AI_REPO_RESPONSES ||= []; push({label, content}) HGF -> LOG : verbose("File content stored for paste-back", {label, contentLength}) end HGF --> CL : (void) deactivate HGF deactivate CL @enduml ' ==== METHOD: _handleListFiles(data, label) ================================= @startuml title command-executor:_handleListFiles(data, label): \n Build fenced listing of files and store for paste-back participant "Caller" as CL participant "_handleListFiles(...)" as HLF participant "Logger" as LOG activate CL CL -> HLF : data, label activate HLF HLF -> HLF : files = data?.files || data?.result?.files alt files is Array HLF -> HLF : listing = "```text\\n" + map(files)->path/name + "\\n```" HLF -> HLF : window.AI_REPO_RESPONSES ||= []; push({label, content: listing}) HLF -> LOG : verbose("File listing stored", {label, fileCount: files.length}) else invalid files HLF -> LOG : warn("list_files response missing files array") end HLF --> CL : (void) deactivate HLF deactivate CL @enduml ' ==== METHOD: delay(ms) ===================================================== @startuml title command-executor:delay(ms): \n Resolve after a timeout participant "Caller" as CL participant "delay(ms)" as DLY participant "Timer" as TMR activate CL CL -> DLY : ms activate DLY DLY -> TMR : setTimeout(ms) TMR --> DLY : wake DLY --> CL : (void) deactivate DLY deactivate CL @enduml ' ==== LEGEND =============================================================== @startuml legend bottom == command-executor UML Style Guide (for future edits) == • Scope: One .puml per module. Keep two views: (1) Activity "Branch Flow" for the whole module (partitions + soft colors), (2) Per-function Sequence diagrams for each exported or significant internal method. • Sequence conventions: 1) First participant is the external caller (use "Caller" or a concrete origin). 2) Do NOT add a module lifeline; the module name appears in the title only. 3) Include every directly-called method or subsystem as a participant (e.g., "execute()", "_api()", "_getBridgeKey()", "_success()", "_error()", "_status()", "_handleGetFile()", "_handleListFiles()", "GM_xmlhttpRequest", "JSON", "Config", "Logger", "Timer", "DOM", "Prompt", "Confirm"). 4) Prefer simple messages; Use --> for returns; -> for calls. 5) Use activate/deactivate as you see fit for clarity (no strict rule). 6) Use alt blocks only when branches meaningfully change the message flow. • Activity view conventions: A) Start with module node then fork partitions for each function/method. B) One partition per function; soft background color; terminate branches with 'kill'. C) Keep wording aligned with code (e.g., exact error text patterns, parameter names). • Color palette (soft pastels) • Use --> for returns; -> for calls. • Participants use quoted method names for internals (e.g., "execute()"), and plain nouns for systems ("JSON", "localStorage", "GM_xmlhttpRequest", "Prompt", "Confirm"). • Keep this legend at the end of the file to standardize edits. endlegend @enduml