' =================================================================== ' File: command-parser.puml ' Purpose: Single source of truth for module-level activity + per-method sequences. ' Module: command-parser.js — Extract/parse YAML-like @bridge@ blocks; defaults; validation. ' Edit rules: Follow the legend at bottom; preserve VIEW/METHOD anchors for automation. ' =================================================================== ' (Optional) 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-parser.js) ================================= @startuml title command-parser.js — Branch Flow (full module) start :command-parser; fork ' -------- REQUIRED (static map) -------- partition "REQUIRED (action → required fields[])" #E7FAE3 { :REQUIRED; :get_file:[action,repo,path]\n\n update_file:[action,repo,path,content]\n\n create_file:[action,repo,path,content]\n\n create_repo:[action,repo]\n\n create_branch:[action,repo,branch]\n\n create_pr:[action,repo,title,head,base]\n\n list_files:[action,repo,path]; kill } fork again ' -------- parse(text) -------- partition "parse(text)" #FFF6D1 { :parse; :block = extractBlock(text) or throw; :obj = parseKV(block); :applyDefaults(obj); :return obj; kill } fork again ' -------- extractBlock(text) -------- partition "extractBlock(text)" #FFE1DB { :extractBlock; :regex /^\s*@bridge@\\n([\\s\\S]*?)\\n@end@/m;\nreturn inner or null; kill } fork again ' -------- parseKV(block) -------- partition "parseKV(block)" #DCF9EE { :parseKV; :scan lines; support "key: value" and "key: |" multiline; :flush() helper to commit multiline; :return object; kill } fork again ' -------- applyDefaults(obj) -------- partition "applyDefaults(obj)" #FFE6F0 { :applyDefaults; :url default; owner default;\ncreate_branch -> source_branch default;\nrepo "owner/repo" split; :return void; kill } fork again ' -------- validate(obj) -------- partition "validate(obj)" #E6F3FF { :validate; :honor example flag -> {isValid:true, example:true}; :check action presence + known action; :ensure REQUIRED fields present; :return {isValid, errors[], example?}; kill } end fork @enduml ' ==== METHOD: parse(text) =================================================== @startuml title command-parser:parse(text): \n Extract first block, parse KV, apply defaults, return object participant "Caller" as CL participant "parse(text)" as PAR participant "extractBlock(text)" as EXT participant "parseKV(block)" as PKV participant "applyDefaults(obj)" as DEF activate CL CL -> PAR : initial request (text) activate PAR PAR -> EXT : extractBlock(text) EXT --> PAR : block | null alt block == null PAR -> PAR : throw Error("No complete @bridge@ command found (missing @end@)") PAR --> CL : (exception) ' (diagram note: thrown) else block found PAR -> PKV : parseKV(block) PKV --> PAR : obj PAR -> DEF : applyDefaults(obj) DEF --> PAR : (void) PAR --> CL : obj end deactivate PAR deactivate CL @enduml ' ==== METHOD: extractBlock(text) =========================================== @startuml title command-parser:extractBlock(text): \n Return inner text between @bridge@ and @end@ or null participant "Caller" as CL participant "extractBlock(text)" as EXT activate CL CL -> EXT : text activate EXT EXT -> EXT : m = /\\s*@bridge@\\s*\\n([\\s\\S]*?)\\n@end@/m.exec(text) EXT -> EXT : return m?.[1]?.trim() || null EXT --> CL : block | null deactivate EXT deactivate CL @enduml ' ==== METHOD: parseKV(block) =============================================== @startuml title command-parser:parseKV(block): \n Minimal YAML-like parser with multiline "|" support participant "Caller" as CL participant "parseKV(block)" as PKV participant "flush()" as FL activate CL CL -> PKV : block activate PKV PKV -> PKV : out = {}; lines = block.split('\\n')\ncurKey=null; multi=false; buf=[] PKV -> FL : define flush(): if (multi && curKey) out[curKey]=buf.join('\\n').replace(/\\s+$/,''); reset FL --> PKV : (ready) loop for each line PKV -> PKV : normalize CR; line = raw.replace(/\\r$/,'') alt multi is true alt new unindented key pattern PKV -> FL : flush() FL --> PKV : ok else still multiline PKV -> PKV : buf.push(line) ' continue end end PKV -> PKV : idx = line.indexOf(':') alt idx != -1 PKV -> PKV : key = line[0:idx].trim(); value = line[idx+1:].trim() alt value == "|" PKV -> PKV : curKey=key; multi=true; buf=[] else single-line PKV -> PKV : out[key] = value; curKey=key end else if (multi) PKV -> PKV : buf.push(line) end end PKV -> FL : flush() FL --> PKV : ok PKV --> CL : out deactivate PKV deactivate CL @enduml ' ==== METHOD: applyDefaults(obj) =========================================== @startuml title command-parser:applyDefaults(obj): \n Set sane defaults and split owner/repo when needed participant "Caller" as CL participant "applyDefaults(obj)" as DEF activate CL CL -> DEF : obj activate DEF DEF -> DEF : p.url ||= "https://n8n.brrd.tech/webhook/ai-gitea-bridge" DEF -> DEF : p.owner ||= "rob" DEF -> DEF : if action == "create_branch" && !source_branch -> "main" DEF -> DEF : if typeof repo == string && repo.includes("/")\n [owner,repo] = repo.split("/",2);\n if !p.owner then p.owner=owner;\n p.repo=repo; DEF --> CL : (void) deactivate DEF deactivate CL @enduml ' ==== METHOD: validate(obj) ================================================ @startuml title command-parser:validate(obj): \n Respect example flag, ensure action known, check required fields participant "Caller" as CL participant "validate(obj)" as VAL participant "REQUIRED" as REQ alt else example flag activate CL CL -> VAL : obj activate VAL ' example flag short-circuit VAL -> VAL : if p.example == true/"true"/"yes" -> return {isValid:true, errors:[], example:true} VAL --> CL : {isValid:true, errors:[], example:true} deactivate VAL ' (The diagram returns here in example case) else no example flag ' Normal path (no example): CL -> VAL : obj (no example) activate VAL VAL -> VAL : if !p.action -> {isValid:false, errors:["Missing required field: action"]} VAL -> REQ : req = REQUIRED[p.action] REQ --> VAL : fields[] | undefined alt else unknown action VAL --> CL : {isValid:false, errors:[`Unknown action: ${action}`]} else known action VAL -> VAL : for f in req: if missing -> errors.push(...) VAL --> CL : {isValid: errors.length==0, errors} end deactivate VAL deactivate CL end @enduml ' ==== LEGEND ================================================================ @startuml legend bottom == command-parser 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., "parse(text)", "extractBlock(text)", "parseKV(block)", "applyDefaults(obj)", "validate(obj)", "REQUIRED", "flush()"). 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., “deepClone(DEFAULT_CONFIG)”, exact error wording). • Color palette (soft pastels) • Use --> for returns; -> for calls. • Participants use quoted method names for internals (e.g., "parseKV(block)"), and plain nouns for structures ("REQUIRED"). • Keep this legend at the end of the file to standardize edits. UML_Example ------------------------------------------ title moduleName:methodName(args): \n Detailed description of what this method does participant "Caller" as CL participant "methodName()" as M ' Add collaborators as needed: ' participant "Dependency" as DEP ' participant "AnotherMethod()" as AM activate CL CL -> M : initial request (args) activate M ' -- inner flow (keep alt blocks only if they clarify) -- ' M -> DEP : call something ' DEP --> M : result ' alt branch condition ' M -> AM : call another ' AM --> M : result ' end M --> CL : return value deactivate M deactivate CL ------------------------------------------ endlegend @enduml