flowsh-cli
flowsh-cli turns workflow YAML into executable shell scripts.
Use it when you want a small, reproducible way to encode a workflow once and rerun it locally or in CI.
Quick Start
uvx flowsh-cli path/to/workflows.yml
That reads the workflow YAML at the path you provide and writes harness scripts to the current working directory.
In this repository, the example workflow file lives at workflows.yml.
Useful flags:
uvx flowsh-cli path/to/workflows.yml --dry-run
uvx flowsh-cli path/to/workflows.yml --workflow wf_example
uvx flowsh-cli path/to/workflows.yml --force
uvx flowsh-cli path/to/workflows.yml --sidecar dagu
uvx flowsh-cli path/to/workflows.yml --workflow wf_example --output .harness/example.sh --sidecar dagu --dry-run
uvx flowsh-cli path/to/workflows.yml --schema
uvx flowsh-cli --examples
uvx flowsh-cli --example simple
You can also run it locally with uv run flowsh-cli.
Optional Dagu sidecar
--sidecar dagu writes the normal executable .sh plus a sibling .dagu.yml containing that exact generated harness. Without the option, generation is unchanged. Generation --dry-run lists both paths but writes neither. The generated Dagu workflow has no schedule and must be started manually; MADE remains responsible for schedule/enabled policy.
flowsh-cli .made/workflows.yml --workflow wf_example \
--output .harness/example.sh --sidecar dagu
# Ordinary harness arguments are a JSON string array encoded as base64.
ARGS_B64="$(python3 -c 'import base64,json; print(base64.b64encode(json.dumps(["value with spaces", "", "two\nlines"], ensure_ascii=False).encode()).decode())')"
dagu start .harness/example.dagu.yml --params "{\"project_dir\":\"$PWD\",\"harness_args_b64\":\"$ARGS_B64\"}"
The sidecar does not need the sibling .sh or an installed flowsh package at execution time. It requires Dagu 2.24.0-compatible built-in js.run and file.write actions, Bash, and the original harness's runtime tools (for example OpenCode and jq when used), plus the target project's files and referenced prompts. Dagu's environment filtering may require operator configuration (env_passthrough/dotenv) for workflow parameters and external environment dependencies; credentials are never copied into the YAML.
The harness runs as one Bash process in the selected project directory. Runtime project_dir overrides the generation-time relative default. Its runtime $0/BASH_SOURCE identifies Dagu's private materialized copy, not the original generated path. Dagu's overall start status reports orchestration success/failure and is not guaranteed to equal the harness's raw exit number. Dagu logs may contain prompts, arguments, and task output: base64 is transport, not encryption; never pass secrets in arguments or expose private workflow logs publicly. This compatibility wrapper does not translate steps, freeze external prompt files, or deploy/activate a DAG.
Use As A Library
flowsh-cli also works as a plain Python library, with no CLI dependencies (no typer, no terminal UI). This is the right choice if you want to parse or render workflows from your own Python code instead of shelling out to the CLI.
uv add flowsh-cli
from pathlib import Path
from flowsh_cli import parse_workflows, render_harness, harness_path
workflows = parse_workflows(Path("workflows.yml"))
for workflow in workflows:
harness_path(workflow).write_text(render_harness(workflow))
Public API (re-exported from flowsh_cli):
| Name | Purpose |
|---|---|
parse_workflows |
Parse and validate a workflow YAML file (given its Path) into Workflow objects. |
Workflow |
Pydantic model of a single workflow. |
WorkflowParseError |
Raised on invalid workflow YAML. |
render_harness |
Render a Workflow into a shell harness script. |
harness_path |
Compute the output path for a workflow's harness script. |
If you also want the flowsh-cli command-line tool (built on typer), install the cli extra:
uv add "flowsh-cli[cli]"
# or, for one-off use without installing:
uvx flowsh-cli path/to/workflows.yml
Workflow Shape
The input file is a single mapping with a workflows list:
workflows:
- id: wf_example
name: Example
params:
- name: ISSUE_NUMBER
description: Issue to inspect
required: true
steps:
- type: vars
values:
TODAY: date -u +%F
- type: bash
run: printf 'today=%s\n' "$TODAY"
- type: agent
agent: general
model: openai/gpt-5
command: review
expandPrompt: true
prompt: |
Review issue ${ISSUE_NUMBER} and summarize the repository state.
- type: parallel
steps:
- type: bash
run: echo "worker A"
- type: bash
run: echo "worker B"
- type: for
in: ITEMS
item: ITEM
steps:
- type: bash
run: echo "$ITEM"
- type: while
condition: '[ -n "$(ls doc/plan/steps/planned/ 2>/dev/null)" ]'
steps:
- type: bash
run: echo "keep looping until the queue is empty"
Harness paths are derived from workflow ids. wf_example becomes example.sh in the current working directory.
Step Types
| Type | Purpose | Notes |
|---|---|---|
vars |
Execute shell commands and export their stdout into shell variables | Variable names must be uppercase shell identifiers. |
bash |
Run shell commands | Runs with bash -euo pipefail. |
agent |
Call the agent runtime | Supports agent, model, command, capture, expandPrompt, and dangerouslySkipPermissions. |
for |
Iterate over newline-delimited values from a previous vars step |
Flat iteration only; nested for steps are not supported. |
while |
Re-evaluate a Bash condition before each iteration | Use for dynamic queues or other stateful loops that must keep discovering new work. |
parallel |
Run child steps concurrently | Children run as separate branches and the parent waits for all of them. capture inside a branch is not visible to sibling branches or later steps — see Agent Behavior below. |
Agent Behavior
agent prompts are literal by default. Use expandPrompt: true only when you want $VAR or ${VAR} tokens from earlier vars steps substituted at runtime.
expandPrompt: true does plain text replacement only. It does not evaluate shell expressions like $(...), backticks, or globs.
Set capture: VARIABLE_NAME on an agent step when you want the agent's final plain-text answer stored in a shell variable for later vars or bash steps. The captured value is the answer text (extracted from the underlying --format json event stream via jq), not the raw JSON stream — requires jq to be installed.
Set dangerouslySkipPermissions: true only when you want the generated harness to pass --dangerously-skip-permissions to the agent runtime. The YAML alias dangerously-skip-permissions is also accepted.
Inside a parallel step, each child branch runs in its own backgrounded subshell, so capture set in one branch is not visible to sibling branches and does not survive past the parallel block — there is no gather/join mechanism. To share results across branches (e.g. fan out N agents and merge their verdicts afterward), have each branch write its result to its own file (via a bash instruction in the agent prompt, or a bash child step), then read all the files back in a plain bash or vars step placed after the parallel block.
Validation And Safety
The parser is strict:
- workflow YAML must be UTF-8, non-empty, and under 1 MiB
- the root must be a mapping with a
workflowskey - duplicate mapping keys and YAML aliases are rejected
- workflow ids must match
wf_[A-Za-z0-9_-]+ - workflow and step names must be non-empty single-line labels
- unsafe control characters are rejected in executable fields
agentnames may contain only letters, digits,_, and-
Generated harnesses are non-interactive, owner-executable, and refuse to overwrite existing outputs unless --force is set. Sidecars are owner-readable/writable only and likewise require --force to replace.
Logs go to .flowsh/logs by default. Set FLOWSH_LOG_DIR to use another relative log directory.
Development
uv sync
make install
make qa
make clean
make qa runs linting, tests, and a local build.
Release
make bump-patch
That bumps the patch version, runs QA, and publishes the package.
Limits
This repository intentionally stays focused on the Python CLI blueprint. It does not include the old TypeScript implementation, workflow templates, DSL explorer, plugin registry, or extra workflow node types.
Metadata
Release files for flowsh-cli 0.12.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| flowsh_cli-0.12.0.tar.gz | 21.0 kB | Without provenance | Details for flowsh_cli-0.12.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| flowsh_cli-0.12.0-py3-none-any.whl | Python 3 | none | any | Details for flowsh_cli-0.12.0-py3-none-any.whl |
Total release size: 45.0 kB
Release files / flowsh_cli-0.12.0.tar.gz
| Download URL | flowsh_cli-0.12.0.tar.gz |
|---|---|
| Size | 21.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f03fa82bc11d4a757e98a6ee3f8cb1f4393b700431761598a8d8d22292830eed
|
|
BLAKE2b-256 checksum How to use checksums |
6c88b8cdd85cedbe74774b735237c7ea329d011cb7f68dbbbb1b35890271aee1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.7 {"installer":{"name":"uv","version":"0.12.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / flowsh_cli-0.12.0-py3-none-any.whl
| Download URL | flowsh_cli-0.12.0-py3-none-any.whl |
|---|---|
| Size | 24.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
1f5dbe89a9ea5fe46a0c18aca1f87e3f870b2afee14341f0452095dea929b7cd
|
|
BLAKE2b-256 checksum How to use checksums |
1e56badfa02cd3dd0448488debf1eff25a541f5c2ea89707bb2bb1fce2c42ae5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.7 {"installer":{"name":"uv","version":"0.12.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Security for flowsh-cli 0.12.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.