ChainWeaver CLI Reference
June 15, 2026 · View on GitHub
ChainWeaver ships a chainweaver console script built on typer.
chainweaver <command> [options]
Exit codes
All commands share the same top-level exit-code contract:
| Code | Meaning |
|---|---|
0 | Success / all flows valid |
1 | Logic error: flow not found, validation failure, execution error, or malformed input |
2 | Input file or directory not found |
Machine-readable output (--format json)
The result-producing commands — inspect, validate, check, profile,
diff, attest, and flows list — wrap their --format json payload in a
stable, versioned envelope so automation and CI can branch on status / error
codes instead of scraping human-readable text:
{
"schema_version": "1",
"status": "ok",
"data": { "...": "command-specific payload" },
"errors": [{ "code": "CW-E017", "message": "…" }]
}
schema_versionversions the envelope itself — distinct from a flow's SemVerversionand from thetrace_schema_versionof a trace. A MAJOR bump signals an incompatible envelope-shape change.statusis"ok"or"error"; on failure,errorscarries{code, message}entries using the stable error codes.- Trace-bearing commands (
profile,diff) include the sourcetrace_schema_versionindata.
run and dump-schema keep their existing (un-enveloped) JSON output.
Shell completion
The CLI ships tab-completion for bash, zsh, and fish (provided by typer). It covers every command and option. Install it once per shell:
chainweaver --install-completion # auto-detect the current shell
chainweaver --show-completion bash # print the script without installing
Restart your shell (or re-source its rc file) after installing.
Flow file format
The file-oriented commands (run, validate, check, doctor, attest,
suggest) load a flow definition from disk. Accepted extensions are
.flow.yaml, .flow.yml, and .flow.json.
Reading .flow.yaml / .flow.yml requires the YAML extra:
pip install 'chainweaver[yaml]'
.flow.json works with no extra. Without pyyaml, the YAML commands fail
with FlowSerializationError: YAML support requires 'pyyaml' to be installed.
The type: discriminator
Every flow file must declare a top-level type: key — either Flow
(linear) or DAGFlow (directed-acyclic). It tells the loader which model to
build; without it you get:
chainweaver: Missing or invalid 'type' discriminator (got None); expected 'Flow' or 'DAGFlow'
A minimal hand-authored linear flow (examples/double_add_format.flow.yaml,
shipped in the repo) looks like this:
type: Flow
name: double_add_format
version: "0.1.0"
description: Doubles a number, adds 10, and formats the result.
steps:
- tool_name: double
input_mapping:
number: number
- tool_name: add_ten
input_mapping:
value: value
- tool_name: format_result
input_mapping:
value: value
The required fields are type, name, description, and steps; version
defaults to "0.1.0" on Flow but is required on DAGFlow. See
AGENTS.md §5
for the full field table. The --tools modules supply the actual Tool
implementations referenced by tool_name.
Commands
inspect
Print the structure of a flow.
chainweaver inspect <flow_name> [--format table|json] [discovery flags]
| Flag | Default | Description |
|---|---|---|
--format / -f | table | Output format: human-readable table or machine-readable JSON |
--file | — | Load the flow directly from a .flow.yaml / .flow.json file |
--discover-dir | — | Discover flows by scanning a directory for .flow.* files |
--discover-entry-points | false | Discover flows from installed packages via the chainweaver.flows entry points |
Without a discovery flag, the flow is resolved from the registry installed via
set_default_registry (see Programmatic registration).
Discovery precedence is --file → --discover-dir → --discover-entry-points
→ the default registry; a no-match error names the source consulted and the
flows it found. Use chainweaver flows list to preview what is discoverable.
Exit codes: 0 = success, 1 = flow not found or no registry configured,
2 = a supplied file/directory does not exist.
Example:
chainweaver inspect my_etl_flow # from the default registry
chainweaver inspect my_etl_flow --discover-dir flows/ # no Python setup needed
chainweaver inspect my_etl_flow --file flows/etl.flow.yaml --format json
chainweaver flows list --discover-dir flows/ # see what is discoverable
viz
Render a flow as ASCII art, DOT (Graphviz), or Mermaid text.
chainweaver viz <flow_name> [--format ascii|dot|mermaid] [discovery flags]
chainweaver viz --result <trace.json> --format mermaid
| Flag | Default | Description |
|---|---|---|
--format / -f | ascii | Visualization format: terminal-friendly ASCII, Graphviz DOT, or Mermaid |
--result | — | Render an ExecutionResult JSON file as a Mermaid status/timing overlay (file-only; no registry or flow name needed). Requires --format mermaid |
--file | — | Load the flow directly from a .flow.yaml / .flow.json file |
--discover-dir | — | Discover flows by scanning a directory for .flow.* files |
--discover-entry-points | false | Discover flows from installed packages via the chainweaver.flows entry points |
Flow resolution works exactly like inspect. Mermaid renders
natively on GitHub and MkDocs Material, so it is the lowest-friction format for
PR descriptions and docs.
Exit codes: 0 = success, 1 = flow not found or no registry configured,
2 = usage error (no flow and no --result, or --result without
--format mermaid) or a supplied file/directory does not exist.
Example:
chainweaver viz my_etl_flow --discover-dir flows/
chainweaver viz my_etl_flow --format dot | dot -Tpng -o my_etl_flow.png
chainweaver viz my_etl_flow --discover-dir flows/ --format mermaid
chainweaver viz --result trace.json --format mermaid # overlay a real run
explain
Render a deterministic, LLM-free explanation of a flow — steps, input/output mappings, branching conditions, governance/safety attributes, and an embedded Mermaid diagram — suitable for pasting into a pull-request description or a review. Output is stable across runs (diff-friendly).
chainweaver explain <flow_name> [--format md|text] [--result trace.json] [discovery flags]
| Flag | Default | Description |
|---|---|---|
--format / -f | md | md (Markdown for PRs) or text |
--result | — | Overlay actual step outcomes from an ExecutionResult JSON file |
--file / --discover-dir / --discover-entry-points | — | Flow resolution, exactly like inspect |
Exit codes: 0 = success, 1 = flow not found or no registry configured,
2 = a supplied file/directory does not exist.
Example:
chainweaver explain my_etl_flow --discover-dir flows/ > flow-review.md
chainweaver explain my_etl_flow --file flows/etl.flow.yaml --result trace.json
init
Scaffold a runnable first flow project — tool definitions, a flow file, and a run script — in one command.
chainweaver init [directory] [--template linear|dag|mcp] [--with-tests] [--force]
| Flag | Default | Description |
|---|---|---|
--template / -t | linear | Project template: linear flow, fan-in DAG, or MCP-ready starter |
--with-tests | false | Also scaffold a passing pytest module (test_flow.py) |
--force | false | Overwrite existing files instead of aborting on a collision |
The command prints the exact next commands after generating the files.
Exit codes: 0 = success, 1 = a target file already exists and --force
was not given, 2 = directory exists but is not a directory.
Example:
chainweaver init my-first-flow --template linear --with-tests
cd my-first-flow && python run.py
validate
Validate a single flow definition file (.flow.yaml, .flow.yml, or .flow.json).
chainweaver validate <file> [--format table|json]
| Flag | Default | Description |
|---|---|---|
--format / -f | table | Output format: human-readable line or machine-readable JSON |
Exit codes: 0 = valid, 1 = validation error, 2 = file not found.
Example:
chainweaver validate flows/etl.flow.yaml
chainweaver validate flows/etl.flow.json --format json
check
Validate every flow file in a directory (recursive).
chainweaver check <directory> [--format table|json] [--quiet]
| Flag | Default | Description |
|---|---|---|
--format / -f | table | Output format |
--quiet / -q | False | Suppress per-file output; only exit code is meaningful |
Exit codes: 0 = all valid, 1 = at least one invalid file, 2 = directory not found.
Example:
chainweaver check flows/
chainweaver check flows/ --quiet # CI-friendly: exit code only
run
Load a flow definition file from disk, register tools from one or more Python modules, and execute the flow with optional JSON input.
chainweaver run <flow_file> [--tools module] [--input file] [--format table|json]
| Flag | Default | Description |
|---|---|---|
--tools / -t | (none) | Python module path to import tools from (repeatable) |
--input / -i | (none) | JSON file containing the initial input dict |
--format / -f | table | Output format |
Exit codes: 0 = success, 1 = execution error or tool not found, 2 = file not found.
Example:
# Runnable from the repository root — the flow file and tools both ship in examples/:
chainweaver run examples/double_add_format.flow.yaml --tools examples.simple_linear_flow --input '{"number": 5}'
chainweaver run flows/etl.flow.yaml --tools my_package.tools --input input.json --format json
serve
Expose a flow as MCP tools over a chosen transport. Loads a flow file and its tool modules (exactly like run), mounts the flow on a FlowServer, and serves it so MCP-aware agents call the whole compiled flow as a single deterministic tool. Requires the mcp extra (pip install 'chainweaver[mcp]').
chainweaver serve <flow_file> [--tools module] [--transport stdio|sse|streamable-http] [--name NAME] [--prefix PREFIX]
| Flag | Default | Description |
|---|---|---|
--tools / -t | (none) | Python module path to import tools from (repeatable) |
--transport | stdio | MCP transport: stdio, sse, or streamable-http |
--name | chainweaver | Server name advertised to MCP clients |
--prefix | (none) | Prefix for exposed tool names (e.g. cw → cw__my_flow) |
The startup banner is written to stderr, keeping stdout a clean MCP wire channel under stdio. The process blocks serving the transport until interrupted (Ctrl-C).
Exit codes: 0 = clean shutdown, 1 = malformed flow file or missing mcp extra, 2 = flow file not found or tools module not importable.
Example:
# Runnable from the repository root — flow file and tools both ship in examples/:
chainweaver serve examples/double_add_format.flow.yaml --tools examples.simple_linear_flow
See Use ChainWeaver as an MCP server for the full guide.
profile
Analyze one or more ExecutionResult JSON files. For a single file, surfaces per-step duration and bottlenecks. For multiple files, adds p50/p95/p99 statistics across runs. Every output (single or multi) also carries per-step and per-tool reliability aggregates — retries, skips, fallbacks, failures, and cache hits — so you can see at a glance which step or tool is responsible for instability.
chainweaver profile <traces...> [--top N] [--format table|json]
| Flag | Default | Description |
|---|---|---|
--top / -n | 5 | Number of slowest steps to surface |
--format / -f | table | Output format |
Exit codes: 0 = success, 1 = malformed trace input, 2 = file not found.
Reliability fields (issue #176, stable JSON contract):
Every entry in steps[] carries these in addition to step_index, tool_name, duration_ms, success:
| Field | Type | Meaning |
|---|---|---|
retry_count | int | Retries beyond the initial invocation. In multi-trace mode this is the sum across the N traces at the same step index. |
skipped | bool (single) / skip_count int (multi) | Step was on_error="skip"-ed. |
fallback_used | bool (single) / fallback_count int (multi) | Step's on_error="fallback:<tool_name>" policy invoked a fallback tool — set regardless of whether the fallback itself succeeded. |
cached | bool (single) / cached_count int (multi) | Outputs served from the executor's step_cache. |
error_type | str | null (single only) | Exception class name when the step failed. |
The top-level aggregates object rolls these up:
{
"aggregates": {
"retry_count": 3,
"skip_count": 0,
"fallback_count": 1,
"failure_count": 0,
"cached_count": 0,
"by_tool": {
"fetch": {
"invocation_count": 2,
"retry_count": 3,
"skip_count": 0,
"fallback_count": 0,
"failure_count": 0,
"cached_count": 0
},
"store": { "...": "same shape" }
}
}
}
The table view appends a Reliability: footer with the same data, plus a per-tool table sorted by failures → fallbacks → retries. The footer is suppressed for clean runs (every count zero) so happy-path output stays compact.
Example:
chainweaver profile trace.json
chainweaver profile trace_a.json trace_b.json trace_c.json --top 10
chainweaver profile trace.json --format json
doctor
doctor is a command group. doctor flow runs flow diagnostics against the currently registered tools; doctor vscode / doctor claude / doctor opencode inspect a workspace's coding-agent setup for the observe → suggest → compile workflow.
doctor flow
Diagnose ChainWeaver flows against the currently registered tools. With --check-drift, walks every flow file under path (single file or recursive directory), imports tools from the modules passed via --tools, and reports per-flow missing_tool / schema_mismatch issues. With --preflight, runs structural validation (tool existence and resolvable input mappings). With --profile first-run, skips flow analysis and checks environment readiness instead — Python version, optional-extra availability (with the exact install command), writable paths, and core import health — for a "is my install ready?" first check (no path required).
chainweaver doctor flow <path> --check-drift [--tools MODULE...] [--format table|json]
chainweaver doctor flow --profile first-run [--format table|json]
doctor vscode / doctor claude / doctor opencode
Read-only inspectors that report what a workspace has configured for the observe → suggest → compile workflow and what is missing. They never modify files; --fix-dry-run prints the config that would be written. Each detects the editor's MCP config and whether a ChainWeaver FlowServer is exposed, plus a trace directory (.chainweaver/traces/) and discoverable macro-flows. doctor claude additionally checks config scope (.mcp.json / .claude/settings*.json) and a PostToolUse observe hook; doctor opencode additionally checks for a ChainWeaver plugin and reminds about tool-name collisions.
chainweaver doctor vscode [--workspace DIR] [--format table|json] [--fix-dry-run]
chainweaver doctor claude [--workspace DIR] [--format table|json] [--fix-dry-run]
chainweaver doctor opencode [--workspace DIR] [--format table|json] [--fix-dry-run]
| Editor flag | Default | Description |
|---|---|---|
--workspace / -w | . | Workspace directory to inspect. |
--format / -f | table | Output format: human-readable table or structured JSON. |
--fix-dry-run | — | Also print the config that would fix detected gaps, without modifying any files. |
Editor-inspector exit codes: 0 when the workspace is inspectable (a missing config is reported as a finding, not an error), 2 when --workspace is missing or is not a directory.
| Flag | Default | Description |
|---|---|---|
--check-drift | — | Compare each step's tool reference and schema fingerprint to the current registry. |
--preflight | — | Validate flow structure: tool existence and resolvable input mappings. |
--profile | — | Named diagnostic profile. first-run checks environment readiness (no path needed). |
--tools / -t | (empty) | Python module path that exposes Tool instances at top level. Repeatable. |
--format / -f | table | Output format: human-readable table or structured JSON. |
One of --check-drift, --preflight, or --profile is required.
First-run JSON shape (when --profile first-run --format json): a top-level
ok flag plus python, writable_paths, import_health, and an extras
array where each entry carries extra, available, missing_modules, and a
copy-paste install command.
Exit codes: 0 = no drift / ready, 1 = drift detected, malformed flow file, or not ready, 2 = path or --tools module missing.
JSON output shape (when --format json):
{
"path": "flows/",
"flow_count": 2,
"drift_count": 1,
"load_errors": [],
"results": [
{
"path": "flows/ok.flow.yaml",
"flow_name": "etl",
"flow_version": "0.1.0",
"fingerprints_present": true,
"ok": true,
"missing_count": 0,
"drift_count": 0,
"issues": []
},
{
"path": "flows/legacy.flow.yaml",
"flow_name": "legacy_etl",
"flow_version": "0.1.0",
"fingerprints_present": true,
"ok": false,
"missing_count": 0,
"drift_count": 1,
"issues": [
{
"step_index": 0,
"tool_name": "fetch",
"issue_type": "schema_mismatch",
"detail": "Tool 'fetch' schema hash changed: expected 'abc123…', got 'def456…'."
}
]
}
]
}
fingerprints_present: false means the flow file was saved without a tool_schema_hashes snapshot — only missing_tool issues are detectable for those flows.
Example:
chainweaver doctor flow flows/etl.flow.yaml --check-drift --tools my_pkg.tools
chainweaver doctor flow flows/ --check-drift --tools my_pkg.tools --format json
diff
Compare two ExecutionResult JSON files step-by-step.
Aligns step records by position and checks outputs, error_type, error_message, and success for each paired step. Non-deterministic fields (trace_id, started_at, ended_at, total_duration_ms, per-step duration_ms) are ignored unless --perf-tolerance is set.
chainweaver diff <a.json> <b.json> [--perf-tolerance N] [--format table|json]
| Flag | Default | Description |
|---|---|---|
--perf-tolerance | (off) | Flag steps whose duration_ms changed by more than N %. Integer, e.g. 25 = 25%. |
--format / -f | table | Output format: human-readable table or structured JSON diff |
Exit codes:
| Code | Meaning |
|---|---|
0 | Traces are identical (modulo ignored fields) |
1 | Traces differ, or malformed trace input |
2 | File not found |
JSON output shape (when --format json):
{
"identical": false,
"flow_name": null,
"step_count": null,
"success": null,
"final_output": {
"values_changed": {
"root['key']": { "old_value": 42, "new_value": 99 }
}
},
"steps": [
{
"step_index": 1,
"tool_name": "store",
"outputs": {
"values_changed": {
"root['key']": { "old_value": 42, "new_value": 99 }
}
}
}
]
}
Fields flow_name, step_count, success, and final_output are null / {} when that dimension is identical between the two traces. The steps list contains only steps where at least one field differed.
Example:
chainweaver diff baseline.json current.json
chainweaver diff baseline.json current.json --perf-tolerance 25
chainweaver diff baseline.json current.json --format json
Use cases:
- A flow misbehaves in production — compare a known-good trace with today's failing trace to isolate the diverging step.
- A replay run (#21) finishes — was
final_outputbyte-identical to the original? - A schema drift event (#50) occurred — what was the functional impact on recorded traces?
attest
Run an observed-determinism attestation against a flow: generate --runs distinct inputs (or read them from --seed-input), run the flow --repeats times per input, and emit a JSON attestation report. When all repeats agree the attestation passes; any divergence fails it.
This produces observed-deterministic evidence, not a formal proof — re-running with the same --seed and ChainWeaver version yields a byte-identical aggregate_fingerprint.
chainweaver attest <flow_file> [--tools module] [--runs N] [--repeats N] [--seed N] [--seed-input file] [--format json|table]
| Flag | Default | Description |
|---|---|---|
--tools / -t | (none) | Python module path exposing Tool instances at top level. Repeatable. |
--runs | 100 | Number of distinct inputs to generate (ignored when --seed-input is set). |
--repeats | 3 | Executions per input. Must be ≥ 2. |
--seed | 0 | Integer seed for the input generator. Same seed → same inputs. |
--seed-input | (none) | JSON file containing a list of input objects to use directly (bypasses the generator). |
--format / -f | json | Output format: json (the attestation artifact) or table. |
Exit codes: 0 = observed-deterministic across all inputs, 1 = divergence detected / execution / argument error, 2 = flow file or tools module not found.
Example:
chainweaver attest examples/double_add_format.flow.yaml --tools examples.simple_linear_flow --runs 50 --repeats 3
suggest
Emit advisory static optimization suggestions for a flow file. A successful run always exits 0 (the suggester is advisory); machine consumers should gate on the suggestions array length in --format json.
Suggestion families (stable codes):
CW001— wasteful-passthrough (emptyinput_mapping).CW002— parallelizable-pair (adjacent steps reading disjoint context keys). Requires--tools.CW003— dead-step (step outputs are not read downstream). Requires--tools.CW004— cacheable-step (identical outputs across observed traces). Requires two or more--tracefiles.
chainweaver suggest <flow_file> [--tools module] [--trace file] [--format table|json]
| Flag | Default | Description |
|---|---|---|
--tools / -t | (none) | Python module path exposing Tool instances at top level. Required for CW003 (dead-step). Repeatable. |
--trace | (none) | Path to a recorded ExecutionResult JSON file. Two or more required for CW004 (cacheable-step). Repeatable. |
--format / -f | table | Output format: human-readable table or machine-readable JSON. |
Exit codes: 0 = ran successfully (regardless of suggestion count), 1 = malformed input, 2 = file not found.
Example:
chainweaver suggest examples/double_add_format.flow.yaml --tools examples.simple_linear_flow
chainweaver suggest flows/etl.flow.yaml --tools my_pkg.tools --trace run_a.json --trace run_b.json --format json
record
Mine candidate flows from a recorded JSONL tool trace (issue #226). Replays the trace through ChainObserver, detects repeated tool sequences offline (no LLM), and emits candidate .flow.yaml files ranked by projected LLM calls avoided (len(tools) * occurrences). Without --output-dir the command is a dry run that only reports candidates.
Each non-blank line of the trace file is a JSON object describing one tool call. Calls are grouped into traces by trace_id (file order preserved); lines without a trace_id join a single default trace:
{"trace_id": "req-1", "tool": "fetch", "inputs": {"url": "..."}, "outputs": {"body": "..."}}
tool (or its alias tool_name) is required; inputs defaults to {} and outputs to null.
chainweaver record <trace.jsonl> [--output-dir DIR] [--min-occurrences N] [--min-length N] [--max-length N] [--include-ignored] [--format table|json]
| Flag | Default | Description |
|---|---|---|
--output-dir / -o | (none) | Directory to write candidate .flow.yaml files into. Omit for a dry run. |
--min-occurrences | 3 | Minimum contiguous appearances for a pattern to be suggested. |
--min-length | 2 | Minimum pattern length (number of tools). |
--max-length | (none) | Maximum pattern length. Omit for no upper bound. |
--include-ignored | False | Report persisted ignored candidates instead of suppressing them. Ignored files are never overwritten. |
--format / -f | table | Output format: human-readable table or machine-readable JSON. |
Exit codes: 0 = ran successfully (regardless of candidate count), 1 = malformed trace or serialization error, 2 = file not found.
Example:
chainweaver record examples/agent_tool_trace.jsonl
chainweaver record examples/agent_tool_trace.jsonl --output-dir candidates/ --format json
New files are persisted with lifecycle draft; repeated runs preserve an
existing candidate's governance state. Promote a candidate deterministically:
chainweaver flows promote candidates/suggested__fetch__validate.flow.yaml --to reviewed --reviewed-by alice
chainweaver flows promote candidates/suggested__fetch__validate.flow.yaml --to active
chainweaver flows ignore candidates/suggested__noisy.flow.yaml --reason "Not useful for this workspace"
The lifecycle is observed → suggested → draft → reviewed → active.
ignored candidates are suppressed by later record runs, and archived
flows remain auditable but are not exposed by default.
traces
Import coding-agent tool-use traces and run the observe → mine → score →
draft → backtest loop for the coding-agent token-reduction use case
(issues #254, #256, #257, #266, #267). The richer JSONL format carries both
tool_call and model_call events plus token/latency/status metadata; all
analysis is offline (no LLM). See the Daily Driver guide.
chainweaver traces mine <trace.jsonl> [--min-occurrences N] [--min-length N] [--max-length N] [--limit N] [--format table|json]
chainweaver traces draft-flows <trace.jsonl> [--output-dir DIR] [--min-occurrences N] [--min-length N] [--max-length N] [--format table|json]
chainweaver traces backtest <flow.yaml> --trace <trace.jsonl> [--format table|json]
mine— mine repeated tool sequences and score each by support, success rate, schema stability, determinism, and safety; prints a ranked, human-friendly report (or JSON).draft-flows— generatedraft-lifecycle.flow.yamlfiles (with a.jsonmetadata/warnings sidecar) from the scored candidates. Without--output-dirit is a dry run.backtest— replay past traces against a draft flow (shape + sequence only, no tool execution); exits non-zero if any window fails to reproduce.
Exit codes: 0 = ran successfully (for backtest, all examples
reproduced), 1 = malformed trace / mismatches found, 2 = file not found.
Example:
chainweaver traces mine coding-agent.jsonl --limit 5
chainweaver traces draft-flows coding-agent.jsonl --output-dir flows/drafts/
chainweaver traces backtest flows/drafts/draft__fs_search__fs_read.flow.yaml --trace coding-agent.jsonl
dump-schema
Emit the JSON Schema for .flow.json / .flow.yaml files, derived from the Pydantic models in chainweaver.flow. Editors that consume JSON Schema (VS Code via redhat.vscode-yaml, JetBrains, …) get autocomplete, hover docs, and inline validation once they point yaml.schemas at the published schema.
chainweaver dump-schema [--output PATH] [--check]
| Flag | Default | Description |
|---|---|---|
--output / -o | stdout | Write the JSON Schema to this path. Recommended in-repo path: schemas/flow.schema.json. |
--check | False | Write nothing; exit 0 if the file at --output already matches the current schema, 1 if it would change. Useful as a CI guard. |
Exit codes: 0 = written / printed successfully (or --check match), 1 = --check mismatch or --output unwritable, 2 = --check used without --output.
Example:
chainweaver dump-schema --output schemas/flow.schema.json
chainweaver dump-schema --check --output schemas/flow.schema.json # CI: fails if the checked-in schema is stale
fuzz
Property-based fuzzing for a flow file (issues #220, #221, #222). Generates --runs cases — either from the flow's input_schema or by mutating a --input base — executes the flow, and checks each --property (a generic invariant over the ExecutionResult) against the result. Optionally injects malformed tool outputs (--output-fault-prob), shrinks failing inputs to a minimal reproducer (--minimize, issue #221), and saves failing traces as replayable JSON, redacted by default (--save-failures / --redact, issue #217). With --redact (the default) the failing and minimized inputs printed in the summary/table are redacted too, so secrets do not leak into CI logs; pass --no-redact for raw values. Each --property must resolve to a unique name.
A run is reproducible: re-running with the same --seed, --runs, flow, and tools yields the same cases and failures.
chainweaver fuzz <file> [--tools MODULE...] [--property NAME|module:attr ...] [--runs N] [--seed S] \
[--input JSON | --input-file PATH] [--output-fault-prob P] \
[--minimize] [--save-failures DIR] [--redact/--no-redact] [--format table|json]
| Flag | Default | Description |
|---|---|---|
--tools / -t | (empty) | Python module path that exposes Tool instances at top level. Repeatable. |
--property / -p | flow_succeeds | A built-in property name (flow_succeeds, final_output_present) or a module:attr path to a FlowProperty or a Callable[[ExecutionResult], bool]. Repeatable. |
--runs / -n | 100 | Number of cases to generate (>= 1). |
--seed | 0 | Deterministic RNG seed. |
--input / -i | (off) | JSON object used as the base input to mutate instead of generating from the schema. |
--input-file | (off) | Path to a JSON object used as the base input. |
--output-fault-prob | 0.0 | Probability in [0,1] of corrupting a tool's output per call (0 disables). |
--minimize / --no-minimize | --no-minimize | Shrink each failing input to a minimal reproducer. |
--save-failures | (off) | Directory to write failing ExecutionResult traces to (created if absent). |
--redact / --no-redact | --redact | Redact saved traces and emitted failing/minimized inputs with the default RedactionPolicy. Use --no-redact for raw values. |
--format / -f | table | Output format: human-readable table or structured JSON. |
Exit codes: 0 = no property violated, 1 = one or more violations found or a CLI-level error (bad arguments, malformed flow/input, unknown property), 2 = flow file, tools module, or property module not found / not importable.
JSON output shape (when --format json):
{
"flow": "my_flow",
"runs": 1000,
"seed": 42,
"properties": ["flow_succeeds"],
"failures": 1,
"failure_cases": [
{
"property": "flow_succeeds",
"case_index": 17,
"initial_input": {"number": 5, "junk": "x"},
"check_error": null,
"minimized_input": {"junk": "x"},
"saved": "failures/my_flow.flow_succeeds.case17.json"
}
]
}
minimized_input is present only with --minimize; saved only with --save-failures. check_error is set when a property check itself raised (treated as a violation).
Example (CI integration):
chainweaver fuzz flows/my_flow.flow.yaml \
--tools my_pkg.tools \
--property my_pkg.props:no_unauthorized_action \
--runs 1000 --seed 42 \
--minimize --save-failures failures/
# exits non-zero when a property is violated, failing the CI job;
# minimized, redacted reproducers land in failures/ as build artifacts.
service
Run one ChainWeaverService analysis pass and report the proposals it would queue (issue #101). The service ties together the static schema analyzer (--tools) and the runtime observer (--trace), surfaces candidate flows as pending proposals, and prints service metrics. Proposals are reported, never auto-registered — promotion stays a governed, in-process action.
A long-running daemon with cross-invocation approve / reject requires proposal persistence (#16) and is intentionally out of scope for the CLI; drive that loop via the ChainWeaverService Python API instead.
chainweaver service [--tools module...] [--trace trace.jsonl] [--min-occurrences N] [--min-length N] [--format table|json]
| Flag | Default | Description |
|---|---|---|
--tools / -t | (none) | Python module path exposing Tool instances at top level. Enables the static-analysis pass. Repeatable. |
--trace | (none) | JSONL tool-trace file (same format as chainweaver record) feeding the runtime-observation pass. |
--min-occurrences | 3 | Minimum runtime occurrences before an observed pattern is proposed. |
--min-length | 2 | Minimum pattern / flow length (number of tools). |
--format / -f | table | Output format: human-readable table or machine-readable JSON. |
Exit codes: 0 = ran successfully, 1 = malformed trace / input, 2 = trace file not found.
Example:
chainweaver service --tools examples.simple_linear_flow
chainweaver service --trace examples/agent_tool_trace.jsonl --min-occurrences 2 --format json
Programmatic registration (inspect, viz)
inspect and viz read from a process-scoped registry installed via cli.set_default_registry:
from chainweaver import FlowRegistry, cli
registry = FlowRegistry()
registry.register_flow(my_flow)
cli.set_default_registry(registry)
cli.main(["inspect", "my_flow"])
validate, check, run, profile, diff, attest, suggest, doctor, and fuzz read directly from disk and do not consult the default registry. dump-schema takes no flow input.