Imported from sergueik/springboot_study (
basic-uipath-skills/.github/skills/uipath-maestro-flow/SKILL.md). Install upstream withnpx skills add sergueik/springboot_study --skill uipath-maestro-flow. Copyright stays with the author.
UiPath Flow Skill
Comprehensive guide for creating, editing, validating, debugging, publishing, diagnosing, and evaluating UiPath Flow projects using the uip CLI and .flow file format. The skill is organized into four capabilities — Author, Operate, Diagnose, Evaluate — each with its own index doc.
When to use this skill
Author — building or editing a .flow file. Read references/author/CAPABILITY.md.
- Create a new Flow project (
uip maestro flow init) - Edit a
.flowfile — add nodes, edges, variables, subflows, transforms, triggers - Explore available node types via the registry
- Validate or format a Flow file locally
- Apply Node ownership — Edit/Write for user-owned nodes, CLI for CLI-owned nodes (see Author capability)
- Configure connector, connector-trigger, or managed HTTP nodes; scaffold inline-agent projects
- Add IxP / document-extraction nodes to a flow, or list IxP models available to Maestro Flow through the flow registry
- Plan a complex flow before building
Operate — publishing, running, or managing a deployed flow. Read references/operate/CAPABILITY.md.
- Push a flow to Studio Web (
uip solution upload) - Deploy a flow to Orchestrator (
uip maestro flow pack+uip solution publish) - Debug a flow end-to-end against real systems
- Trigger a deployed process
- Check job status or stream traces
- Pause, resume, cancel, or retry a running instance
Diagnose — investigating a failed or misbehaving run. Read references/diagnose/CAPABILITY.md.
- Triage a failed
flow debugor deployed process run - Read incidents, runtime variables, deployed BPMN
- Recognize known failure modes (MST-9107, MST-9061, HITL-stuck, reused reference IDs, single-nested layout)
Evaluate — designing and running evaluations against a deployed flow. Read references/evaluate/CAPABILITY.md.
- Create evaluators (
exact-match,json-similarity,contains,llm-judge-*) for a Flow project - Create eval sets, add data points (test cases), pin entry points
- Start Studio Web eval runs, poll status, fetch results, compare runs
- Decide whether to call
uip solution upload(almost always: don't auto-run; ask first)
Capability router
| I want to... | Read |
|---|---|
| Create a new flow or edit an existing one | references/author/CAPABILITY.md |
| Publish, deploy, debug, or manage a flow's lifecycle | references/operate/CAPABILITY.md |
| Diagnose a failed or misbehaving flow run | references/diagnose/CAPABILITY.md |
Design and run evaluations (uip maestro flow eval) |
references/evaluate/CAPABILITY.md |
| Look up CLI command syntax | references/shared/cli-commands.md |
Look up CLI conventions (--output json, --output-filter, login, FOLDER_KEY) |
references/shared/cli-conventions.md |
Understand the .flow JSON format |
references/shared/file-format.md |
Understand variables and =js: expressions |
references/shared/variables-and-expressions.md |
| Wire one node's output into another node's input | references/shared/node-output-wiring.md |
Look up action-node boilerplate shared across plugin impl.mds |
references/shared/action-nodes.md |
| Narrate progress + maintain todos (opt-in — when user wants verbosity) | references/shared/ux-narration-and-todos.md |
Critical rules (universal)
These rules apply across all three capabilities. Each capability index adds capability-scoped rules on top.
Tool vocabulary. Tool names in this skill use Claude Code conventions:
Edit= in-place string replacement,Write= full-file write,Read/Glob/Grep= file read/search,Bash= shell, progress list = the harness's task list. On another harness, map each to its equivalent — the semantics (reviewable in-place diff vs wholesale rewrite, shell as last resort for file edits) carry unchanged.
-
ALWAYS use
--output jsonand prefer--output-filterfor extraction on alluipcommands when parsing output programmatically.--output-filter <jmespath>is a global CLI flag applied to theDataenvelope before printing — write expressions starting atData(noData.prefix). Canonical recipe:uip maestro flow registry search <keyword> --output json --output-filter "[*].{NodeType:NodeType,DisplayName:DisplayName,Description:Description,AvailableOnTenant:AvailableOnTenant}".registry searchreturnsDataas a flat array of PascalCase objects (NodeType,DisplayName,Description,AvailableOnTenant) — NOTData.Nodes, lowercasetype, or lowercasecategory. With--local,AvailableOnTenantis omitted (no tenant lookup) — drop it from the projection. External parsers (python3 -c,jq) remain valid for transforms JMESPath cannot express; reach for them only after the shape is verified. Full mechanics, fall-back guidance, and shape-inspection probes: cli-conventions.md §3. -
Do NOT run
flow debugwithout explicit user consent — debug executes the flow for real (sends emails, posts messages, calls APIs). -
Resource discovery order — search before creating. When the prompt references an existing resource by name ("use the X agent", "call the Y API workflow", "invoke the Z RPA process"), follow this order strictly before deciding the resource doesn't exist:
- Pull, then search the tenant registry —
uip maestro flow registry pull --force && uip maestro flow registry search "<name>" --output json. Always pull first:searchreads the local cache (populated bypull, expires after 30 min), so a search before a fresh pull can return empty for a resource that exists. Requiresuip login; returns published resources. - In-solution local discovery —
uip maestro flow registry list --local --output json, oruip maestro flow registry search "<name>" --local --output jsonfor keyword match. No login required; returns sibling projects in the same.uipxsolution. An emptysearch --localis not proof of absence (the keyword may not match the project's naming) — confirm withlist --localbefore concluding the resource is not in the solution. - Only then create/scaffold — scaffold an inline agent, mock, or create-new-resource only when both searches return no match AND either the user explicitly asks to embed/inline/create, or no published resource can satisfy the requirement.
The words "coded" and "low-code" describe the implementation style of a published agent — they are NOT synonyms for "inline".
uipath.agent.autonomous(inline) is only correct when the user explicitly asks to embed/inline/create a new agent inside this flow. Only usecore.logic.mockwhen the resource is not in the same solution and not yet published. See the relevant resource plugin'simpl.md(e.g., rpa, agent).The same "search before deciding it doesn't exist" discipline applies to connector connections. The connector key MUST come from a
registry searchnode type (uipath.connector.<connector-key>.<activity>) — never inferred from the service's brand name. The registry key is frequently prefixed or qualified differently than the product name, so a guessed key silently misses the real connector. Anyuip is connections listused for discovery MUST pass--all-folders. An empty result from an unverified key or without--all-foldersis a false negative, never authoritative — do not conclude "no connection exists" or ask the user to create one until both hold.It also applies to ANY external service named in the prompt — not just in-tenant resources. Before picking a node type for a named service (Slack, open-meteo, Stripe, any REST API), run
uip maestro flow registry search "<service>" --output jsonand follow the Selecting External Service Nodes ladder (connector → managed HTTP → RPA). Manualcore.action.http.v2is the bottom of that ladder, chosen only after a search finds no connector — never a first guess from the brand name. This holds even when full planning is skipped (see greenfield.md — Select the node type for each external service).Two tells that you skipped the search and took the brand-name shortcut — both are build defects, not valid manual-mode HTTP: (a) you authored a manual-mode
core.action.http.v2node whoseurltargets a well-known SaaS API domain that has a connector (slack.com/api/*,api.github.com,*.salesforce.com,graph.microsoft.com, …); (b) you declared aninvariable to hold that service's API token or secret (e.g. aslackTokenholding anxoxb-…bot token, anapiKey, a bearer token). A connector-backed flow never carries the raw credential — the IS connection does. If you find yourself writing either, stop: runuip maestro flow registry search "<service>"anduip is connections list "<connector-key>" --all-folders, then use the connector activity (or connector-mode HTTP:authentication:"connector"+targetConnector+ a boundconnectionId/folderKey). Manual mode is legitimate only for a service the search proves has no connector. - Pull, then search the tenant registry —
-
Never invoke other skills automatically — when a flow needs an RPA process, agent, or app, identify the gap and provide handoff instructions. Let the user decide when to switch skills.
-
Always present user questions as a dropdown with a "Something else" escape hatch — Whenever this skill needs a decision from the user (which solution to use, publish vs debug vs deploy, which connector to pick, which trigger type, which resource to bind, etc.), ask the user a question with the enumerated choices as options AND include "Something else" as the last option so the user can supply free-form string input. Never ask open-ended questions in chat when a finite set of sensible defaults exists. If the user picks "Something else", parse their string answer and continue. No structured-question facility on the harness → ask in chat as a numbered list with "Something else" last. Running non-interactively (CI/headless — no user available to answer) → take the pre-selected/recommended option, proceed, and record the decision prominently in the final report; if no option is marked recommended, stop and report the open decision instead of guessing. Exception: consent gates (
flow debug, destructive operations) are never auto-answered — in non-interactive mode, stop and report the blocked step instead. These fallbacks define "ask the user" / "confirm with the user" wherever this skill's references require it. -
Discover the target solution before you scaffold — a Flow project MUST live inside a solution (layout is always double-nested:
<Solution>/<Project>/<Project>.flow). Before anyuip solution initoruip maestro flow initfor a NEW Flow, runfind . -maxdepth 2 -type f -name '*.uipx' -print. If it lists one or more solutions, STOP — do not scaffold, initialize, delete, or repair anything. Ask which solution to use (dropdown per rule #5): one option per discovered solution, "Create a new solution", then "Something else" last; continue only after the answer. This holds even when the user says they want a new solution or supplies only a Flow-project name — never silently adopt an existing solution, and when they pick "Create a new solution", ask for its name rather than defaulting to the Flow-project name. If none are found, create one automatically (default its name to the Flow name unless the user specifies otherwise). Prefer solution-first — it works on every CLI version and lets you set the two names independently:uip solution init "<SolutionName>" --output json && cd "<SolutionName>" && uip maestro flow init "<FlowName>" --output json→<SolutionName>/<FlowName>/<FlowName>.flow, with the project auto-registered in the parent.uipx(Data.SolutionRegistration.Status: "Registered");<SolutionName>and<FlowName>are independent and need not match. On a current CLI, runninguip maestro flow init "<FlowName>"outside any solution instead auto-scaffolds<FlowName>Solution/<FlowName>Solution.uipxwith the project at<FlowName>Solution/<FlowName>/(response carriesData.AutoCreatedSolution) — convenient, but it forces the<FlowName>Solutionname, so use it only when the solution name doesn't matter.--skip-solution-registrationopts out of both auto-scaffold and registration, leaving a bare single-nested<Flow>/<Flow>.flowthat fails Studio Web upload and packaging. If a non-empty directory already exists at the path you typed, init warns and leaves it untouched. Never drop thecdbetweensolution initandflow init—flow initin the old directory auto-scaffolds a duplicate<FlowName>Solution/. Oneproject.uiprojat finish; delete strays. See author/greenfield.md Step 2. -
Narrate progress in plain English only when the user has opted into verbosity — silent by default. Engage when the user asks for narration / progress ("walk me through it", "show your steps", "verbose", "be detailed") or signals a verbosity preference; otherwise work quietly and surface only decisions, failures, consent gates, and the final result. When engaged: one short line per logical step, in user terms ("checking your tenant login", "adding the Slack node and wiring its inputs", "running validate") — no flag-level or JSON-structure-level detail, applied uniformly across
uipCLI calls, shell builtins, file edits, and bulk searches. See shared/ux-narration-and-todos.md §When to engage. -
Maintain a user-facing progress list only when the user has opted into progress tracking / verbosity. In silent mode there is no user-facing todo list (the agent MAY track privately). When engaged: any journey above the trivial threshold gets a granular list — one logical step ≈ one todo, granularity per-step not per-phase. The count emerges from the journey's actual steps; do not target a number. Bash plumbing inside a step (registry lookups, JSON parsing, intermediate file reads) is invisible — do not surface as todos. See shared/ux-narration-and-todos.md for the engage triggers, granularity rules, threshold table, and pivot rules.
-
Every node has exactly one author — Edit/Write or CLI, never both. Connector activities (
uipath.connector.<key>.<op>), connector triggers (uipath.connector.trigger.<key>.<trigger>), wait for events (uipath.connector.event.<key>.<event>— a mid-flow event wait, configured exactly like a trigger), and managed HTTP (core.action.http.v2) are CLI-owned — useuip maestro flow node add+uip maestro flow node configure. Every other node type — triggers, control flow, logic, HITL, patterns, agents, resource nodes, queue — is user-owned: author the.flowJSON directly withEdit— orWrite, but never a full-fileWriteon a flow that also contains CLI-owned nodes (it clobbers their CLI-setbindings[]/inputs.detail;Editin place, or re-runnode configureas the last step).inputs.detailon CLI-owned nodes is a=jsonString:essentialConfigurationenvelope that the validator rejects when hand-authored. Inline-agent CLI is limited to agent project lifecycle (uip agent init / refresh / validate --inline-in-flow); theuipath.agent.autonomousflow node itself is user-owned. Scripting languages (python,node,jq,sed,awk, inline shell heredocs) are a last resort for user-owned edits and require explicit user approval after the trade-offs (state bypass, opaque diff, no interruption point) are surfaced. Canonical source of truth: author/CAPABILITY.md — Node ownership (full table); author/editing-operations.md — Tool Selection Ladder (per-operation ladder). -
Batch tool calls into one assistant turn whenever data dependencies allow — minimize wall-clock round-trips. A typical greenfield build is 3 turns, not 10+: (T1) one chained
Bashfor scaffold + registry pull + CLI-ownednode add, in parallel withregistry getandReadcalls for any extra discovery; (T2) oneReadof the scaffolded.flowin parallel with theEdit/Writecalls that add the End node and wire edges; (T3) one chainedBashfornode configure && validate && format. Within an assistant message: chain sequentialuipcalls with&&in a singleBash, and emit independentBash/Read/Editcalls as parallel tool uses. Only split turns where a later call truly depends on an earlier call's stdout or on a file mutation. See author/references/greenfield.md — Three-turn execution map for the canonical pattern. -
Cross-node bindings inside
=js:need the$vars.prefix — a bare node reference resolves toundefinedat runtime."recordId": "=js:$vars.createEntityRecord1.output.Id"is correct;"recordId": "=js:createEntityRecord1.output.Id"(missing$vars.) silently resolves toundefined. Pattern:=js:$vars.<nodeId>.output..., never=js:<nodeId>.output.... See variables-and-expressions.md — IS Activity Inputs Require=js:. -
Node and edge
ids MUST start with a letter — never a bare UUID. Edges:edge_<sourceNodeId>_<sourcePort>_<targetNodeId>_<targetPort>. Nodes: descriptive camelCase. UUIDs only for the top-level flowid/entryPointId.
Anti-patterns (universal)
- Never use
--format jsonon anyuipcommand — the flag is--output json(rule #1).--formatproduceserror: unknown option '--format'and exit code 3 on everyuipsubcommand, not a helpful message pointing you at--output. - Avoid piping
uip ... --output jsontopython3 -c/jqfor simple field extraction — use--output-filterinstead (rule #1). The common failure mode: the agent assumesData.Nodes(or another nested shape) and the external parser silently returns[]againstData's actual flat array, prompting a second and third retry with different access paths.--output-filterdoes not rescue a wrong-shape guess by itself — a valid expression that doesn't match (e.g."Nodes"against the flat array) still returnsData: []with exit 0; only an invalid expression (bad syntax, or a type error likekeys(@)on an array) exits non-zero. The fix is the pinned shape in rule #1, so you write the right expression on call #1. Reserve external parsers for transforms JMESPath cannot express (multi-step joins, format conversion); when in doubt, verify the shape first — see cli-conventions.md §3 for thetype(@)first-probe sequence (note:keys(@)throws on arrays, so usetype(@)first). - Never run
flow debugas a validation step — debug executes the flow with real side effects (rule #2). Useflow validatefor checking correctness. - Never silently pick the first match from
uip maestro flow registry search. When a search returns multiple connectors for the same intent, apply the canonical Connector Disambiguation ladder via connector/planning.md — Disambiguation, which defers to the Integration Service rules. - Never conclude "no connection exists" from a bare
uip is connections list— discovery requires--all-foldersand a connector key derived fromregistry search, not a brand name (rule #3). Without both, "No connections found" is a false negative; do not surface it as an Open Question or ask the user to create one. - Never write
customFieldsRequestDetails.parameterValuesas a JSON object map — Studio Web's TS port emitsMap<string,string|null>viaArray.from(entries()), so the on-wire shape is[[key, value], ...]tuples. Object-form{key: value}is rejected by the CLI at validate time. Inner keys are camelCase (objectActionName,parameterValues), not PascalCase. See connector/impl.md Step 6c. - Never treat a
flow validateexit code of 0 as "done" when it printed warnings. Warnings are build defects the validator chose not to hard-fail — read every one and resolve it before declaring the flow complete. The connector-keyword warning (node "…" mentions the "<connector>" connector keyword but uses the generic Managed HTTP type core.action.http.v2 with no connection binding) means you took the brand-name shortcut (rule #3) — fix it by binding the connector, do not ship past it. A green exit with an unresolved connector warning still fails downstream connector checks and runs against an undefined endpoint at debug time. - Never issue setup or finalization CLI calls one-per-turn. Each separate Bash call adds a model round-trip — the dominant wall-clock cost. Chain them per rule #10 and the Three-turn execution map.
Trouble? If something didn't work as expected, use
/uipath-feedbackto send a report.