For AI agents: the complete documentation index is available at https://a3s-lab.github.io/Flow/en/llms.txt, the full documentation bundle is available at https://a3s-lab.github.io/Flow/en/llms-full.txt, and this page is available as Markdown at https://a3s-lab.github.io/Flow/en/reference/cli.md.

a3s-flow CLI

a3s-flow ships with @a3s-lab/flow-ui. It reads the same node manifests as the canvas cards, configuration panels, and node reference pages, so field names, defaults, ports, and runtime bindings stay aligned. The CLI works on workflow documents and standard streams. It does not connect to model providers, read credentials, or execute host tasks.

Install and verify

npm install @a3s-lab/flow-ui @a3s-lab/ui
npx a3s-flow help --pretty

The second command should return JSON with ok set to true and list nodes, new, create, read, update, delete, validate, compile, and digest. If the shell cannot find the command, confirm that the dependencies are installed in the current project and invoke the local binary through npx or a project script.

A project script can call the locally installed binary directly.

{
  "scripts": {
    "flow:check": "a3s-flow validate workflow.json --pretty"
  }
}

Every command emits JSON. Exit code 0 means the command succeeded. Exit code 1 means the workflow structure or node settings were rejected. Exit code 2 means the invocation, arguments, or file operation was invalid. Automation should check both the process status and the top-level ok value.

Command reference

CommandPurposeMain output
nodesList the 18 public manifestsEngine version, DSL version, count, and full catalog
node <type>Inspect one manifestFields, defaults, ports, runtime binding, and durable identity
new <type> --id <id>Create a node from manifest defaultsA node object ready for the graph
sampleEmit a minimal executable documentStart, task, and completion nodes with edges
create <file>Atomically create a validated sample fileFull document, plan, counts, and semantic digests
read <file>Validate and summarize an existing fileFull document, plan, counts, and semantic digests
update <file>Apply one or more graph/app edits atomicallyChanged IDs plus the new validated summary
delete <file> --forceExplicitly remove one workflow file{ deleted: true/false }
validate <file>Check the document, DAG, and node settingsCompatibility, counts, or path-specific issues
compile <file>Compile deterministic orderTop-level and container-scoped plans
digest <file>Compute semantic identityDocument and graph digests

Use --pretty for readable output and --output <file> to write JSON to a file. Pass - as the input path to read standard input. --include-internal applies to catalog inspection and exposes iteration-start and loop-start. Those internal types belong only inside matching containers and cannot be created as public nodes with new.

Inspect and create a node

Inspect the catalog, read the target manifest, and then create an instance with a stable ID.

a3s-flow nodes --pretty
a3s-flow node flow.hook --pretty
a3s-flow new flow.hook --id approval --pretty

The node result is authoritative for field names, option values, and connection handles. Editors and scripts should not maintain a separate hand-written schema. The new command fills every manifest default and preserves the structure expected by validation. Once a run exists, the node ID is durable identity. Changing it can disconnect new graph state from committed steps, hooks, progress records, or child runs.

CRUD a workflow file

Use create for a safe first file (or pass --from <file|-> to copy and validate an existing DSL). It refuses to replace an existing path unless --overwrite is supplied, creates parent directories, and writes through a same-directory temporary file before rename. read returns the validated DSL, deterministic plan, and both digests in one response.

a3s-flow create workflow.json --name invoice.approval --pretty
a3s-flow create workflow.v2.json --from workflow.json --name invoice.approval.v2 --pretty
a3s-flow read workflow.json --pretty

update accepts one focused edit per invocation, or a JSON array passed through --operations when several graph changes must be committed together. For a generated or large patch set, pass --operations - and send one JSON operation per line (NDJSON) on stdin, or pass --operations @operations.ndjson to stream an NDJSON file without loading it into the command line; Flow parses and applies each operation as it arrives, then validates and publishes one atomic snapshot. Node configuration is JSON and only manifest-owned fields are changed; unknown semantic extensions and presentation fields remain intact. Use --if-digest <sha256> to reject a stale writer whose base document has changed. The candidate is validated before the atomic replace, so a rejected or conflicting edit leaves the original file unchanged. Add --dry-run to receive the candidate summary without writing it. Both transports and direct typed callers share a 10,000-operation bound; each NDJSON record - including an ignored whitespace record - is limited to 1 MiB. Workflow files and byte/text stream chunks use fatal UTF-8 decoding, so malformed input is rejected before a candidate can be published. Operation-specific flags are rejected when they do not match the selected operation, instead of being silently ignored. Command-specific flags are checked the same way, so a typo cannot turn into a successful no-op on another command.

When a hosted adapter derives an idempotency key from an operation, use the package's canonicalizeFlowCliWorkflowUpdate helper before writing the operation to its journal. It emits compact, deterministic UTF-8 JSON and normalizes only wire-equivalent defaults; the final graph-dependent validation still belongs to the update boundary.

Use --set-edge <id> --source <id> --target <id> to redirect an existing edge in place. Optional --source-handle and --target-handle replace only the handles that are supplied; omitted handles, the edge ID, and unknown semantic or presentation fields are preserved. Use --clear-source-handle or --clear-target-handle to remove a handle explicitly (or null in a JSON operation). This avoids a remove/add cycle that could break references to the edge.

Use --parent <container-id> with --add-node to place a public node inside an existing iteration or loop scope. A new container is publishable only after its matching internal start node and at least one executable child have been added, so create those dependent nodes in one JSON operation array or NDJSON stream. JSON operations use parentId; iteration accepts only an iteration-start child and loop accepts only a loop-start child. The intermediate incomplete container is kept in the candidate clone and is never written to disk.

Use --move-node <id> --parent <container-id> to relocate an existing public node while preserving its stable ID, configuration, presentation fields, and unknown extensions. Omit --parent to move it back to the top level. JSON and NDJSON use {"kind":"move-node","id":"node-id","parentId":"container-id"}; use parentId: null for the top level. Internal start nodes must stay under their matching container, and parent cycles are rejected before publication.

a3s-flow update workflow.json --set-app-name invoice.approval.v2 --pretty
a3s-flow update workflow.json --set-node run-step \
  --config '{"step_name":"payments.charge"}' --pretty
a3s-flow update workflow.json --add-node flow.progress \
  --id report-progress --pretty
a3s-flow update workflow.json --set-edge run-step-complete \
  --source run-step --target approval \
  --source-handle success --target-handle in --pretty
a3s-flow update workflow.json --operations \
  '[{"kind":"add-node","id":"each","type":"iteration","configuration":{"start_node_id":"each-start"}},{"kind":"add-node","id":"each-start","type":"iteration-start","parentId":"each"},{"kind":"add-node","id":"process","type":"flow.step","parentId":"each"}]' --pretty
a3s-flow update workflow.json --operations \
  '[{"kind":"set-app-name","name":"invoice.approval.v2"},{"kind":"set-node","id":"run-step","configuration":{"step_name":"payments.charge"}}]' \
  --dry-run --pretty
printf '%s\n' \
  '{"kind":"set-app-name","name":"invoice.approval.v2"}' \
  '{"kind":"set-node","id":"run-step","configuration":{"step_name":"payments.charge"}}' \
  | a3s-flow update workflow.json --operations - --if-digest <sha256> --pretty

Remove files only with explicit confirmation. Repeating the command is safe and reports deleted: false when the file is already absent.

a3s-flow delete workflow.json --force --pretty

Create the sample document

a3s-flow sample --output workflow.json --pretty
a3s-flow validate workflow.json --pretty

The sample registers graph structure only. It does not execute task.run. A host must provide that task handler and choose an event store, scheduler, and worker topology. For a real project, change the workflow name and node IDs before creating the first persistent run.

Validate, compile, and digest

Run the three commands in this order after every semantic field or edge change.

a3s-flow validate workflow.json --pretty
a3s-flow compile workflow.json --output workflow.plan.json --pretty
a3s-flow digest workflow.json --output workflow.digest.json --pretty

Validation checks the document envelope, public node types, field constraints, port connections, duplicate IDs, dangling edges, self edges, ordinary cycles, and container scope. Successful compilation proves that the top-level graph and each iteration or loop child canvas have deterministic order. Digests ignore position, title, description, dimensions, and selection state while binding executable node settings and edges. If a host needs an integrity pin, store the digest in release metadata instead of writing it back into the workflow document.

Standard input and CI

git show HEAD:workflows/order-review.json |
  a3s-flow validate - --pretty

In CI, validate first and retain the compiled plan and digest as build artifacts when review requires them. Rejected output contains code, path, and message for every issue. Repair the field or edge named by the path instead of deleting unknown extension fields. For imported workflows, separately review task names, runtime entry points, public callbacks, and host bindings because the CLI does not make authorization or credential decisions.

Custom node boundary

The CLI ships with the official built-in registry and does not load host application modules. A workflow containing a custom type will fail CLI validation with flow.node.unknown_type. Keep that rejection in place. Projects with custom nodes should provide a typed release command that imports the project catalog and calls compileA3SFlowWorkflowDagForPublication. See Custom nodes for the registration and publication contract.

Diagnosing failures

unknown_node means the installed package does not expose the requested public type, so inspect nodes before changing the document. flow.node.internal_scope_required means an internal start marker has escaped its matching container. Graph errors usually point to an incorrect handle, a cross-scope edge, or a back edge. Field errors identify the exact node data path under workflow.graph.nodes.

For a file-read failure, check the working directory and supplied path first. When standard input never completes, the upstream command has probably kept its output open; write the document to a temporary file and validate it separately. After repair, rerun validation, compilation, and digesting so the final plan and release document come from the same check.