AI-Repo-Commander/Docs/diagrams/command-parser.puml

285 lines
8.8 KiB
Plaintext

' ===================================================================
' 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