Skip to content

feat: add chainweaver run CLI subcommand to execute flows from disk (#129)#158

Open
dgenio wants to merge 1 commit into
refactor/cli-extract-shared-helpersfrom
feat/129-cli-run-subcommand
Open

feat: add chainweaver run CLI subcommand to execute flows from disk (#129)#158
dgenio wants to merge 1 commit into
refactor/cli-extract-shared-helpersfrom
feat/129-cli-run-subcommand

Conversation

@dgenio
Copy link
Copy Markdown
Owner

@dgenio dgenio commented May 16, 2026

Summary

Adds the chainweaver run verb so users can execute a flow without writing Python — the most-requested DX gap per the issue body. Stacked on top of #157 (scaffolding refactor); the base auto-retargets to main once #157 merges.

chainweaver run flows/etl.flow.yaml \
    --tools my_pkg.tools \
    --input '{"date": "2026-05-15"}'

Closes #129.

Changes

  • chainweaver/cli.py — new run_command, two private helpers (_import_tools_from, _parse_initial_input), _run_result_to_table renderer, module-docstring update listing the verb.
  • tests/test_cli.pyTestRunCommand with 12 cases (happy path table + JSON, --input-file, --quiet, missing flow file (exit 2), unimportable tools module (exit 2), missing tool (exit 1), malformed JSON input, non-object input, missing --input/--input-file, mutual exclusion, invalid flow file).
  • README.md — new "Command-line interface" section listing all five subcommands with run first.
  • docs/v1-release-criteria.md §6 — adds chainweaver run to the v1 CLI checklist.

Behavior

Flag Meaning
--tools <module> (repeatable, -t) Python import path; module's top-level Tool instances are registered, de-duplicated by Tool.name.
--input <json-string> (-i) JSON object passed as initial_input. Mutually exclusive with --input-file.
--input-file <path> Path to a JSON file holding the initial input object.
--format table|json (-f) Default table shows step rows + final_output; json emits ExecutionResult.model_dump_json().
--quiet (-q) Suppress stdout/stderr; result via exit code only.

Exit codes match the existing contract:

  • 0 — flow succeeded.
  • 1 — execution failed (result.success == False), missing tool, malformed input JSON, or CLI-level error.
  • 2 — flow file or tools module not found / not importable.

On failure, the first failing StepRecord is also surfaced to stderr as chainweaver: step N (tool '<name>') failed: <error_message> so CI / scripts can grep without parsing table output.

Deferred per the issue body

Per the issue's "once that lands" framing:

No half-wired stubs were added now.

Testing

  • Linting passes (ruff check chainweaver/ tests/ examples/)
  • Formatting check passes (ruff format --check chainweaver/ tests/ examples/)
  • Type checking passes (python -m mypy chainweaver/ tests/)
  • All existing tests pass (python -m pytest tests/ -v --no-cov) — 479/479 passed in 1.77s (467 pre-existing + 12 new)
  • New tests added for new functionality
$ ruff check chainweaver/ tests/ examples/
All checks passed!
$ ruff format --check chainweaver/ tests/ examples/
49 files already formatted
$ python -m mypy chainweaver/ tests/
Success: no issues found in 42 source files
$ python -m pytest tests/ -q --no-cov
479 passed in 1.77s

Diff stat: 4 files changed, 625 insertions(+), 1 deletion(-) (240 LoC of CLI code + 353 LoC of tests + 31 LoC README + 2 LoC v1 criteria).

Related Issues

Closes #129.

Checklist

  • Code follows project conventions (see AGENTS.md and docs/agent-context/)
  • Public API changes are documented — run_command is reachable via the chainweaver console script; CLI docstring + README + v1-release-criteria all updated
  • No secrets or credentials included

Tradeoffs / risks

Scope notes

Closes #129 only. Adjacent items (cookbook recipe in #146, GH Action wrapper in #149) are intentionally deferred to their own issues.

https://claude.ai/code/session_01QcSJ3NWhe5B4k1EP25Hx3n


Generated by Claude Code

Closes #129.

Adds the `chainweaver run` verb so users can execute a flow without
writing Python — the most-requested DX gap per the issue body.

Usage:

    chainweaver run flows/etl.flow.yaml --tools my_pkg.tools \
        --input '{"date": "2026-05-15"}'

Flags:
- --tools <module> (repeatable) — Python import path that exposes
  Tool instances at top level. The CLI imports the module via
  importlib.import_module and registers every isinstance(obj, Tool)
  found in vars(module).values(), de-duplicating by Tool.name.
- --input <json-string> / --input-file <path> — mutually exclusive;
  exactly one is required. Initial input must be a JSON object.
- --format table|json — table (default, human-readable) shows one
  row per executed step plus the final_output; json emits the full
  ExecutionResult.model_dump_json() for machine consumption.
- --quiet — suppresses all stdout/stderr; result communicated via
  exit code only.

Exit codes match the existing CLI contract:
- 0 — flow succeeded.
- 1 — flow execution failed (result.success == False), missing tool,
  malformed input JSON, or CLI-level error.
- 2 — flow file or tools module not found / not importable.

On failure, the first failing StepRecord is also surfaced to stderr
as 'chainweaver: step N (tool '<name>') failed: <error_message>' so
CI/scripts can grep without parsing the table output.

Deferred per the issue body ("once that lands"):
- --checkpoint <path> — depends on the Checkpointer landing (PR #136).
- --cache <path> — depends on StepCache landing (PR #136).
- Plugin entry-point fallback — depends on #130.

Each of those reads cleanly as a small follow-up once its dependency
merges; no half-wired stubs are added now.

Tests: 12 new cases in tests/test_cli.py covering happy path (table
+ JSON), input-file flag, quiet mode, missing flow file (exit 2),
unimportable tools module (exit 2), missing tool (exit 1), malformed
JSON input, non-object input, missing input flag, --input/--input-file
mutual exclusion, and invalid flow file.

Verification:
  $ ruff check chainweaver/ tests/ examples/      # All checks passed
  $ ruff format --check chainweaver/ tests/ ...   # 49 files already formatted
  $ python -m mypy chainweaver/ tests/            # Success: no issues
  $ python -m pytest tests/ -q --no-cov           # 479 passed in 1.77s

Docs:
- README gains a 'Command-line interface' section listing all five
  subcommands and placing `run` first.
- docs/v1-release-criteria.md §6 adds `chainweaver run` to the
  v1 CLI coverage checklist.
- chainweaver/cli.py module docstring lists `run` alongside the
  other verbs.

Stacked on top of #157 (CLI scaffolding refactor); base will
auto-retarget to main once #157 merges.

https://claude.ai/code/session_01QcSJ3NWhe5B4k1EP25Hx3n
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants