agent-yaml 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. agent_yaml-0.1.0/LICENSE +21 -0
  2. agent_yaml-0.1.0/PKG-INFO +311 -0
  3. agent_yaml-0.1.0/README.md +271 -0
  4. agent_yaml-0.1.0/agent_yaml/__init__.py +8 -0
  5. agent_yaml-0.1.0/agent_yaml/adapters/__init__.py +95 -0
  6. agent_yaml-0.1.0/agent_yaml/adapters/base.py +55 -0
  7. agent_yaml-0.1.0/agent_yaml/adapters/claude.py +144 -0
  8. agent_yaml-0.1.0/agent_yaml/adapters/codex.py +112 -0
  9. agent_yaml-0.1.0/agent_yaml/adapters/gemini.py +141 -0
  10. agent_yaml-0.1.0/agent_yaml/adapters/opencode.py +147 -0
  11. agent_yaml-0.1.0/agent_yaml/adapters/qwen.py +160 -0
  12. agent_yaml-0.1.0/agent_yaml/cli.py +973 -0
  13. agent_yaml-0.1.0/agent_yaml/ensure.py +115 -0
  14. agent_yaml-0.1.0/agent_yaml/events.py +26 -0
  15. agent_yaml-0.1.0/agent_yaml/executor.py +868 -0
  16. agent_yaml-0.1.0/agent_yaml/graph.py +292 -0
  17. agent_yaml-0.1.0/agent_yaml/includes.py +222 -0
  18. agent_yaml-0.1.0/agent_yaml/loops.py +3252 -0
  19. agent_yaml-0.1.0/agent_yaml/parser.py +52 -0
  20. agent_yaml-0.1.0/agent_yaml/skip_conditions.py +287 -0
  21. agent_yaml-0.1.0/agent_yaml/task_validators.py +571 -0
  22. agent_yaml-0.1.0/agent_yaml/utils.py +338 -0
  23. agent_yaml-0.1.0/agent_yaml/validator.py +827 -0
  24. agent_yaml-0.1.0/agent_yaml/variables.py +330 -0
  25. agent_yaml-0.1.0/agent_yaml/web/__init__.py +1 -0
  26. agent_yaml-0.1.0/agent_yaml/web/app.py +281 -0
  27. agent_yaml-0.1.0/agent_yaml/web/db.py +413 -0
  28. agent_yaml-0.1.0/agent_yaml/web/runtime.py +207 -0
  29. agent_yaml-0.1.0/agent_yaml/web/scheduler.py +81 -0
  30. agent_yaml-0.1.0/agent_yaml/web/static/app.js +565 -0
  31. agent_yaml-0.1.0/agent_yaml/web/static/index.html +138 -0
  32. agent_yaml-0.1.0/agent_yaml/web/static/styles.css +814 -0
  33. agent_yaml-0.1.0/agent_yaml/web/static/webgl.js +636 -0
  34. agent_yaml-0.1.0/agent_yaml/when_conditions.py +127 -0
  35. agent_yaml-0.1.0/agent_yaml.egg-info/PKG-INFO +311 -0
  36. agent_yaml-0.1.0/agent_yaml.egg-info/SOURCES.txt +40 -0
  37. agent_yaml-0.1.0/agent_yaml.egg-info/dependency_links.txt +1 -0
  38. agent_yaml-0.1.0/agent_yaml.egg-info/entry_points.txt +2 -0
  39. agent_yaml-0.1.0/agent_yaml.egg-info/requires.txt +19 -0
  40. agent_yaml-0.1.0/agent_yaml.egg-info/top_level.txt +1 -0
  41. agent_yaml-0.1.0/pyproject.toml +146 -0
  42. agent_yaml-0.1.0/setup.cfg +4 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Joe Schlesinger
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,311 @@
1
+ Metadata-Version: 2.4
2
+ Name: agent-yaml
3
+ Version: 0.1.0
4
+ Summary: Declarative YAML workflows for AI coding-agent CLIs
5
+ Author: Joe Schlesinger
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/roboalchemist/agent-yaml
8
+ Project-URL: Repository, https://github.com/roboalchemist/agent-yaml
9
+ Project-URL: Issues, https://github.com/roboalchemist/agent-yaml/issues
10
+ Project-URL: Changelog, https://github.com/roboalchemist/agent-yaml/blob/main/CHANGELOG.md
11
+ Keywords: ai,agents,orchestration,workflow,yaml,cli,llm
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Topic :: Software Development :: Build Tools
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.8
17
+ Classifier: Programming Language :: Python :: 3.9
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Requires-Python: >=3.8
22
+ Description-Content-Type: text/markdown
23
+ License-File: LICENSE
24
+ Requires-Dist: pyyaml>=6.0
25
+ Requires-Dist: jinja2>=3.0
26
+ Requires-Dist: fastapi>=0.110
27
+ Requires-Dist: uvicorn>=0.27
28
+ Requires-Dist: pydantic>=2.0
29
+ Requires-Dist: backports.zoneinfo>=0.2.1; python_version < "3.9"
30
+ Provides-Extra: dev
31
+ Requires-Dist: pytest>=7.0; extra == "dev"
32
+ Requires-Dist: pytest-cov>=4.0; extra == "dev"
33
+ Requires-Dist: pytest-mock>=3.10; extra == "dev"
34
+ Requires-Dist: ruff>=0.6; extra == "dev"
35
+ Requires-Dist: httpx>=0.27; extra == "dev"
36
+ Provides-Extra: docs
37
+ Requires-Dist: mkdocs>=1.6; extra == "docs"
38
+ Requires-Dist: mkdocs-material<10,>=9.7; extra == "docs"
39
+ Dynamic: license-file
40
+
41
+ # agent-yaml
42
+
43
+ Write out your workflows in yaml and then run them under a harness of your choice.
44
+
45
+ ## Why it exists
46
+
47
+ Each model and framework provider is trying to provide a way to interact with their harness, without providing what is usually needed: a definable, repeatable flow-chart style workflow to accomplish some repeated task.
48
+
49
+ The yaml contains the prompts, where each loop gets its items, and the checks a step has to pass
50
+ before the next one starts. Run it again next month in the latest harness/model of the day and run the same workflow.
51
+
52
+ agent-yaml lets one define their workflows irregardless of the harness or model of the day, by breaking down what most boring workflows tend to be (or could most easily be) comprised of:
53
+ - a working directory
54
+ - prompts made with templates
55
+ - looping over something
56
+ - validating checks
57
+ - composable templating of the above
58
+
59
+ Each step is one subprocess call, with contracts between steps being files on the disk.
60
+
61
+ Model output stays opaque and is never parsed.
62
+ If you want to parse it, write it to a file and parse that.
63
+ Validators run against written files to form checkpoints.
64
+
65
+ ## Install
66
+
67
+ No PyPI release yet. Install from the repository:
68
+
69
+ ```bash
70
+ pip install "git+https://github.com/roboalchemist/agent-yaml.git"
71
+ agent-yaml --help
72
+ ```
73
+
74
+ Python 3.8+, tested on 3.9, 3.11 and 3.12, plus at least one supported agent CLI on `PATH`
75
+ (`opencode`, `claude`, `gemini`, `qwen`, or `codex`).
76
+
77
+ ## Quick start
78
+
79
+ ```yaml
80
+ # hello.yaml
81
+ directory: /tmp/hello
82
+ cli-agent: opencode
83
+ cli-agent-config:
84
+ skip-confirmations: true
85
+ model: provider/model-name
86
+
87
+ task:
88
+ - write-file:
89
+ prompt: "Create hello.txt containing exactly: hello from agent-yaml"
90
+ ```
91
+
92
+ ```bash
93
+ mkdir -p /tmp/hello
94
+ agent-yaml validate hello.yaml # parse and schema check, no agent calls
95
+ agent-yaml run hello.yaml --dry-run # print the exact command that would run
96
+ agent-yaml run hello.yaml # run it
97
+ ```
98
+
99
+ A dry run for that file prints the command it would execute, including the translated flags, so you
100
+ can spot a wrong model or a rejected option before spending tokens:
101
+
102
+ ```
103
+ [DRY RUN] Task 'write-file' would execute:
104
+ Command: opencode run --standalone --auto -- Create hello.txt containing exactly: ...
105
+ Working directory: /tmp/hello
106
+ ```
107
+
108
+ ## A real workflow
109
+
110
+ This is the shape of the workflows in `examples/` and `tasks/`: a CSV loop, a validator per item,
111
+ retries on validation failure, and a checkpoint so an interrupted run can continue.
112
+
113
+ ```yaml
114
+ directory: ~/projects/reports
115
+ cli-agent-config:
116
+ model: openai/gpt-5
117
+
118
+ task:
119
+ - summarize-rows:
120
+ loop:
121
+ type: csv
122
+ source: "{{directory}}/rows.csv"
123
+ workers: 4
124
+ retry: {max-attempts: 3, on-failure: retry}
125
+ checkpoint-file: "{{directory}}/state/summaries.json"
126
+ resume: true
127
+ prompt: |
128
+ Summarize row {{loop.index}}/{{loop.total}} ({{loop.item.name}}) and write
129
+ {{directory}}/out/{{loop.item.name}}.md
130
+ validate:
131
+ - file-not-empty: "{{directory}}/out/{{loop.item.name}}.md"
132
+ ```
133
+
134
+ Run it, then rerun the same command after an interruption. Finished items are skipped.
135
+
136
+ ## Primitives
137
+
138
+ A workflow is built from a fixed vocabulary. The forms are the same on every adapter, and only the
139
+ CLI-specific options change.
140
+
141
+ **Workflow fields**
142
+
143
+ | Field | Type | Notes |
144
+ |-------|------|-------|
145
+ | `directory` | string | Required. Working directory for every task |
146
+ | `cli-agent` | string | `opencode`, `claude`, `gemini`, `qwen`, `codex` (default `claude`) |
147
+ | `environment` | map | Env vars inherited by every task |
148
+ | `template-variables` | map | User variables, resolved in dependency order |
149
+ | `includes` | map | Reusable prompt fragments, inline or from a file |
150
+ | `ensure` | map | Pre-flight `tools` and `mcps` checks |
151
+ | `task` | list | The task tree (required) |
152
+
153
+ `cli-agent-config` holds adapter options: `model`, `timeout`, `skip-confirmations`,
154
+ `additional-dirs`, `mcp-config-file`, `raw-args`, and the per-adapter equivalents.
155
+
156
+ **Task fields**
157
+
158
+ | Field | Type | Notes |
159
+ |-------|------|-------|
160
+ | `prompt` | string | Text sent to the agent CLI |
161
+ | `task` | list or map | Nested tasks: a list runs in order, a map runs its keys concurrently |
162
+ | `include` | list | Names of top-level includes appended to the prompt |
163
+ | `loop` | map | Runs the task once per item |
164
+ | `validate` | list | Validators checked after the task exits 0 |
165
+ | `retry` | map | `max-attempts`, `on-validation-failure` (`retry`, `fail`, `skip`) |
166
+ | `skip-if` | map | `file-exists` or `file-contains`, evaluated before the task |
167
+ | `when` | string | Expression gate. An empty string skips the task |
168
+ | `env` | map | Task-level environment variables |
169
+ | `directory` | string | Task working directory, relative to the workflow `directory` |
170
+ | `cli-agent-config` | map | Per-task adapter options |
171
+ | `on-error` | string | `fail`, `continue`, `skip` |
172
+
173
+ **Loops**
174
+
175
+ | `type` | Iterates over | Required |
176
+ |--------|---------------|----------|
177
+ | `todo-list` | pending `- [ ]` items in a markdown file | `source` |
178
+ | `csv` | rows of a CSV | `source` |
179
+ | `json` | array in a JSON file, optionally nested | `source` |
180
+ | `jsonl` | one JSON object per line | `source` |
181
+ | `glob` | files matching a pattern | `pattern` |
182
+
183
+ All loops share these options:
184
+
185
+ | Option | Notes |
186
+ |--------|-------|
187
+ | `checkpoint-file` | JSON progress file, template variables expanded |
188
+ | `resume` | Continue after the last completed item |
189
+ | `retry` | `{max-attempts, on-failure}` with `skip`, `retry`, or `abort` |
190
+ | `workers` | Items run concurrently. Applies to `csv`, `json`, and `jsonl` only |
191
+ | `directory-pool` | Existing directories assigned round-robin to workers |
192
+ | `git-worktrees` | One worktree per worker: `base`, `branch-field`, `name-template`, `cleanup` |
193
+
194
+ Type-specific filters: `columns` and `filter` for `csv`, `path` and `filter` for `json`.
195
+
196
+ **Validators**
197
+
198
+ | Validator | Config | Passes when |
199
+ |-----------|--------|-------------|
200
+ | `file-exists` | string path | The path exists |
201
+ | `file-not-empty` | string path | The path exists and is not empty |
202
+ | `contains` | `{file, text}` | The file contains the text |
203
+ | `json-valid` | string path | The file parses as JSON |
204
+ | `yaml-valid` | string path | The file parses as YAML |
205
+ | `command` | `{cmd, file?, timeout?}` | The command exits 0 |
206
+
207
+ Validators run after a task exits 0. A failure follows `retry.on-validation-failure`.
208
+
209
+ **Variables**
210
+
211
+ | Variable | Value |
212
+ |----------|-------|
213
+ | `{{directory}}` | Absolute workflow directory |
214
+ | `{{gitroot}}` | Nearest ancestor with `.git/`, else `{{directory}}` |
215
+ | `{{datetime}}` | ISO-8601 timestamp, fixed for the run |
216
+ | `{{user}}` | `USER`, `USERNAME`, or `LOGNAME`, else `unknown` |
217
+ | `{{env.NAME}}` | Environment variable from the merged environment |
218
+ | `{{loop.index}}` | 0-based item index |
219
+ | `{{loop.total}}` | Number of items |
220
+ | `{{loop.remaining}}` | Items left |
221
+ | `{{loop.item}}` | Current item. Fields depend on the loop type |
222
+ | `{{loop.worker.id}}` | Worker id for parallel loops |
223
+ | `{{loop.worker.directory}}` | Worker directory with `directory-pool` or `git-worktrees` |
224
+
225
+ User variables are defined in `template-variables` and can reference each other. `--var key=value`
226
+ overrides them, and `{{env.NAME}}` reads the merged environment.
227
+
228
+ ## How a run works
229
+
230
+ 1. **Parse** (`agent_yaml/parser.py`) the YAML with `yaml.safe_load`.
231
+ 2. **Validate** (`agent_yaml/validator.py`) required fields, task-tree shape, loop, validator, skip-if,
232
+ when, and retry configuration, plus adapter option names.
233
+ 3. **Resolve includes** (`agent_yaml/includes.py`) and **variables** (`agent_yaml/variables.py`,
234
+ `agent_yaml/utils.py`). Reserved variables are injected, user variables resolve in dependency order,
235
+ and cycles are rejected.
236
+ 4. **Check prerequisites** (`agent_yaml/ensure.py`) with `shutil.which` before anything runs.
237
+ 5. **Execute** (`agent_yaml/executor.py`) the task tree. Array items run in order, mapping keys run
238
+ concurrently in a `ThreadPoolExecutor`, and each task is one `subprocess.run` with a timeout.
239
+ 6. **Validate output**, then run the loop's next item or checkpoint its progress.
240
+
241
+ ## Visual runner
242
+
243
+ `agent-yaml serve` starts a local console for browsing, running, and watching workflows:
244
+
245
+ ```bash
246
+ agent-yaml serve --host 127.0.0.1 --port 8765
247
+ ```
248
+
249
+ It discovers `*.yaml` under `examples/` and `tasks/`, draws the stage graph on a WebGL2 canvas,
250
+ streams execution events over SSE, and stores runs plus interval/cron schedules in SQLite. Details
251
+ and the HTTP API are in [docs/INTERFACE_RUNNER.md](docs/INTERFACE_RUNNER.md).
252
+
253
+ ## Pick your agent
254
+
255
+ | Adapter | Skip confirmations | Model flag |
256
+ |---------|--------------------|------------|
257
+ | `opencode` | `--auto` | `--model provider/model` |
258
+ | `claude` | `--dangerously-skip-permissions` | `--model` (aliases translated) |
259
+ | `gemini` | `--yolo` | `--model` |
260
+ | `qwen` | `--yolo` | `--model` |
261
+ | `codex` | `--full-auto` | `-m` |
262
+
263
+ Common options (`model`, `timeout`, `skip-confirmations`, `additional-dirs`, `mcp-config-file`,
264
+ `debug`, `raw-args`, `output-format`) map to each CLI. See
265
+ [docs/ADAPTER_TROUBLESHOOTING.md](docs/ADAPTER_TROUBLESHOOTING.md) for the full per-adapter table
266
+ and [spec/ADAPTER_SPEC.md](spec/ADAPTER_SPEC.md) for the contract.
267
+
268
+ ## Docs
269
+
270
+ - [docs/](docs/README.md) is the field-level reference: authoring, templating, loops, parallel
271
+ loops, validators, interface runner, adapters.
272
+ - [docs/CLI.md](docs/CLI.md) covers every flag, exit code, environment precedence, and retry
273
+ behaviour.
274
+ - [examples/](examples/README.md) has 40 runnable workflows, all dry-run by the test suite.
275
+ - [spec/](spec/) holds the authoritative schema and adapter specs.
276
+
277
+ ## Development
278
+
279
+ ```bash
280
+ pip install -e ".[dev]" # pytest, ruff, coverage
281
+ pytest -q # hermetic, no model calls
282
+ ruff check agent_yaml tests
283
+ pip install -e ".[docs]" # mkdocs
284
+ mkdocs serve # preview docs/ at http://127.0.0.1:8000
285
+ ```
286
+
287
+ The test suite is three tiers: unit (agent mocked), integration (a fake `opencode` that runs the
288
+ prompt's `bash` block), and live (`pytest --live`, a real model behind an OpenAI-compatible
289
+ gateway). See [tests/README.md](tests/README.md).
290
+
291
+ ## What it doesn't do
292
+
293
+ - It does not parse or judge model output. If a step needs to prove something, give it a validator.
294
+ - MCP detection in `ensure.mcps` is a warning-only stub.
295
+ - Nested list tasks (`task: [...]` inside a task) are rejected by the validator even though
296
+ `spec/YAML_SPEC.md` documents them. Use the mapping form.
297
+ - `agent-yaml run --resume` is a no-op. Resumption happens through each loop's `resume: true` plus
298
+ `checkpoint-file`.
299
+ - The Codex adapter emits flags that recent `codex-cli` releases reject. The Claude adapter sends
300
+ `--allowed-mcp-server-names`, which current `claude` builds reject. Both are listed with
301
+ workarounds in [docs/ADAPTER_TROUBLESHOOTING.md](docs/ADAPTER_TROUBLESHOOTING.md).
302
+ - Cancellation is best effort: in a parallel group with `on-error: fail`, tasks already scheduled
303
+ still run before the workflow aborts.
304
+
305
+ ## Contributing
306
+
307
+ See [CONTRIBUTING.md](CONTRIBUTING.md). Bug reports and pull requests are welcome.
308
+
309
+ ## License
310
+
311
+ [MIT](LICENSE) © 2026 Joe Schlesinger
@@ -0,0 +1,271 @@
1
+ # agent-yaml
2
+
3
+ Write out your workflows in yaml and then run them under a harness of your choice.
4
+
5
+ ## Why it exists
6
+
7
+ Each model and framework provider is trying to provide a way to interact with their harness, without providing what is usually needed: a definable, repeatable flow-chart style workflow to accomplish some repeated task.
8
+
9
+ The yaml contains the prompts, where each loop gets its items, and the checks a step has to pass
10
+ before the next one starts. Run it again next month in the latest harness/model of the day and run the same workflow.
11
+
12
+ agent-yaml lets one define their workflows irregardless of the harness or model of the day, by breaking down what most boring workflows tend to be (or could most easily be) comprised of:
13
+ - a working directory
14
+ - prompts made with templates
15
+ - looping over something
16
+ - validating checks
17
+ - composable templating of the above
18
+
19
+ Each step is one subprocess call, with contracts between steps being files on the disk.
20
+
21
+ Model output stays opaque and is never parsed.
22
+ If you want to parse it, write it to a file and parse that.
23
+ Validators run against written files to form checkpoints.
24
+
25
+ ## Install
26
+
27
+ No PyPI release yet. Install from the repository:
28
+
29
+ ```bash
30
+ pip install "git+https://github.com/roboalchemist/agent-yaml.git"
31
+ agent-yaml --help
32
+ ```
33
+
34
+ Python 3.8+, tested on 3.9, 3.11 and 3.12, plus at least one supported agent CLI on `PATH`
35
+ (`opencode`, `claude`, `gemini`, `qwen`, or `codex`).
36
+
37
+ ## Quick start
38
+
39
+ ```yaml
40
+ # hello.yaml
41
+ directory: /tmp/hello
42
+ cli-agent: opencode
43
+ cli-agent-config:
44
+ skip-confirmations: true
45
+ model: provider/model-name
46
+
47
+ task:
48
+ - write-file:
49
+ prompt: "Create hello.txt containing exactly: hello from agent-yaml"
50
+ ```
51
+
52
+ ```bash
53
+ mkdir -p /tmp/hello
54
+ agent-yaml validate hello.yaml # parse and schema check, no agent calls
55
+ agent-yaml run hello.yaml --dry-run # print the exact command that would run
56
+ agent-yaml run hello.yaml # run it
57
+ ```
58
+
59
+ A dry run for that file prints the command it would execute, including the translated flags, so you
60
+ can spot a wrong model or a rejected option before spending tokens:
61
+
62
+ ```
63
+ [DRY RUN] Task 'write-file' would execute:
64
+ Command: opencode run --standalone --auto -- Create hello.txt containing exactly: ...
65
+ Working directory: /tmp/hello
66
+ ```
67
+
68
+ ## A real workflow
69
+
70
+ This is the shape of the workflows in `examples/` and `tasks/`: a CSV loop, a validator per item,
71
+ retries on validation failure, and a checkpoint so an interrupted run can continue.
72
+
73
+ ```yaml
74
+ directory: ~/projects/reports
75
+ cli-agent-config:
76
+ model: openai/gpt-5
77
+
78
+ task:
79
+ - summarize-rows:
80
+ loop:
81
+ type: csv
82
+ source: "{{directory}}/rows.csv"
83
+ workers: 4
84
+ retry: {max-attempts: 3, on-failure: retry}
85
+ checkpoint-file: "{{directory}}/state/summaries.json"
86
+ resume: true
87
+ prompt: |
88
+ Summarize row {{loop.index}}/{{loop.total}} ({{loop.item.name}}) and write
89
+ {{directory}}/out/{{loop.item.name}}.md
90
+ validate:
91
+ - file-not-empty: "{{directory}}/out/{{loop.item.name}}.md"
92
+ ```
93
+
94
+ Run it, then rerun the same command after an interruption. Finished items are skipped.
95
+
96
+ ## Primitives
97
+
98
+ A workflow is built from a fixed vocabulary. The forms are the same on every adapter, and only the
99
+ CLI-specific options change.
100
+
101
+ **Workflow fields**
102
+
103
+ | Field | Type | Notes |
104
+ |-------|------|-------|
105
+ | `directory` | string | Required. Working directory for every task |
106
+ | `cli-agent` | string | `opencode`, `claude`, `gemini`, `qwen`, `codex` (default `claude`) |
107
+ | `environment` | map | Env vars inherited by every task |
108
+ | `template-variables` | map | User variables, resolved in dependency order |
109
+ | `includes` | map | Reusable prompt fragments, inline or from a file |
110
+ | `ensure` | map | Pre-flight `tools` and `mcps` checks |
111
+ | `task` | list | The task tree (required) |
112
+
113
+ `cli-agent-config` holds adapter options: `model`, `timeout`, `skip-confirmations`,
114
+ `additional-dirs`, `mcp-config-file`, `raw-args`, and the per-adapter equivalents.
115
+
116
+ **Task fields**
117
+
118
+ | Field | Type | Notes |
119
+ |-------|------|-------|
120
+ | `prompt` | string | Text sent to the agent CLI |
121
+ | `task` | list or map | Nested tasks: a list runs in order, a map runs its keys concurrently |
122
+ | `include` | list | Names of top-level includes appended to the prompt |
123
+ | `loop` | map | Runs the task once per item |
124
+ | `validate` | list | Validators checked after the task exits 0 |
125
+ | `retry` | map | `max-attempts`, `on-validation-failure` (`retry`, `fail`, `skip`) |
126
+ | `skip-if` | map | `file-exists` or `file-contains`, evaluated before the task |
127
+ | `when` | string | Expression gate. An empty string skips the task |
128
+ | `env` | map | Task-level environment variables |
129
+ | `directory` | string | Task working directory, relative to the workflow `directory` |
130
+ | `cli-agent-config` | map | Per-task adapter options |
131
+ | `on-error` | string | `fail`, `continue`, `skip` |
132
+
133
+ **Loops**
134
+
135
+ | `type` | Iterates over | Required |
136
+ |--------|---------------|----------|
137
+ | `todo-list` | pending `- [ ]` items in a markdown file | `source` |
138
+ | `csv` | rows of a CSV | `source` |
139
+ | `json` | array in a JSON file, optionally nested | `source` |
140
+ | `jsonl` | one JSON object per line | `source` |
141
+ | `glob` | files matching a pattern | `pattern` |
142
+
143
+ All loops share these options:
144
+
145
+ | Option | Notes |
146
+ |--------|-------|
147
+ | `checkpoint-file` | JSON progress file, template variables expanded |
148
+ | `resume` | Continue after the last completed item |
149
+ | `retry` | `{max-attempts, on-failure}` with `skip`, `retry`, or `abort` |
150
+ | `workers` | Items run concurrently. Applies to `csv`, `json`, and `jsonl` only |
151
+ | `directory-pool` | Existing directories assigned round-robin to workers |
152
+ | `git-worktrees` | One worktree per worker: `base`, `branch-field`, `name-template`, `cleanup` |
153
+
154
+ Type-specific filters: `columns` and `filter` for `csv`, `path` and `filter` for `json`.
155
+
156
+ **Validators**
157
+
158
+ | Validator | Config | Passes when |
159
+ |-----------|--------|-------------|
160
+ | `file-exists` | string path | The path exists |
161
+ | `file-not-empty` | string path | The path exists and is not empty |
162
+ | `contains` | `{file, text}` | The file contains the text |
163
+ | `json-valid` | string path | The file parses as JSON |
164
+ | `yaml-valid` | string path | The file parses as YAML |
165
+ | `command` | `{cmd, file?, timeout?}` | The command exits 0 |
166
+
167
+ Validators run after a task exits 0. A failure follows `retry.on-validation-failure`.
168
+
169
+ **Variables**
170
+
171
+ | Variable | Value |
172
+ |----------|-------|
173
+ | `{{directory}}` | Absolute workflow directory |
174
+ | `{{gitroot}}` | Nearest ancestor with `.git/`, else `{{directory}}` |
175
+ | `{{datetime}}` | ISO-8601 timestamp, fixed for the run |
176
+ | `{{user}}` | `USER`, `USERNAME`, or `LOGNAME`, else `unknown` |
177
+ | `{{env.NAME}}` | Environment variable from the merged environment |
178
+ | `{{loop.index}}` | 0-based item index |
179
+ | `{{loop.total}}` | Number of items |
180
+ | `{{loop.remaining}}` | Items left |
181
+ | `{{loop.item}}` | Current item. Fields depend on the loop type |
182
+ | `{{loop.worker.id}}` | Worker id for parallel loops |
183
+ | `{{loop.worker.directory}}` | Worker directory with `directory-pool` or `git-worktrees` |
184
+
185
+ User variables are defined in `template-variables` and can reference each other. `--var key=value`
186
+ overrides them, and `{{env.NAME}}` reads the merged environment.
187
+
188
+ ## How a run works
189
+
190
+ 1. **Parse** (`agent_yaml/parser.py`) the YAML with `yaml.safe_load`.
191
+ 2. **Validate** (`agent_yaml/validator.py`) required fields, task-tree shape, loop, validator, skip-if,
192
+ when, and retry configuration, plus adapter option names.
193
+ 3. **Resolve includes** (`agent_yaml/includes.py`) and **variables** (`agent_yaml/variables.py`,
194
+ `agent_yaml/utils.py`). Reserved variables are injected, user variables resolve in dependency order,
195
+ and cycles are rejected.
196
+ 4. **Check prerequisites** (`agent_yaml/ensure.py`) with `shutil.which` before anything runs.
197
+ 5. **Execute** (`agent_yaml/executor.py`) the task tree. Array items run in order, mapping keys run
198
+ concurrently in a `ThreadPoolExecutor`, and each task is one `subprocess.run` with a timeout.
199
+ 6. **Validate output**, then run the loop's next item or checkpoint its progress.
200
+
201
+ ## Visual runner
202
+
203
+ `agent-yaml serve` starts a local console for browsing, running, and watching workflows:
204
+
205
+ ```bash
206
+ agent-yaml serve --host 127.0.0.1 --port 8765
207
+ ```
208
+
209
+ It discovers `*.yaml` under `examples/` and `tasks/`, draws the stage graph on a WebGL2 canvas,
210
+ streams execution events over SSE, and stores runs plus interval/cron schedules in SQLite. Details
211
+ and the HTTP API are in [docs/INTERFACE_RUNNER.md](docs/INTERFACE_RUNNER.md).
212
+
213
+ ## Pick your agent
214
+
215
+ | Adapter | Skip confirmations | Model flag |
216
+ |---------|--------------------|------------|
217
+ | `opencode` | `--auto` | `--model provider/model` |
218
+ | `claude` | `--dangerously-skip-permissions` | `--model` (aliases translated) |
219
+ | `gemini` | `--yolo` | `--model` |
220
+ | `qwen` | `--yolo` | `--model` |
221
+ | `codex` | `--full-auto` | `-m` |
222
+
223
+ Common options (`model`, `timeout`, `skip-confirmations`, `additional-dirs`, `mcp-config-file`,
224
+ `debug`, `raw-args`, `output-format`) map to each CLI. See
225
+ [docs/ADAPTER_TROUBLESHOOTING.md](docs/ADAPTER_TROUBLESHOOTING.md) for the full per-adapter table
226
+ and [spec/ADAPTER_SPEC.md](spec/ADAPTER_SPEC.md) for the contract.
227
+
228
+ ## Docs
229
+
230
+ - [docs/](docs/README.md) is the field-level reference: authoring, templating, loops, parallel
231
+ loops, validators, interface runner, adapters.
232
+ - [docs/CLI.md](docs/CLI.md) covers every flag, exit code, environment precedence, and retry
233
+ behaviour.
234
+ - [examples/](examples/README.md) has 40 runnable workflows, all dry-run by the test suite.
235
+ - [spec/](spec/) holds the authoritative schema and adapter specs.
236
+
237
+ ## Development
238
+
239
+ ```bash
240
+ pip install -e ".[dev]" # pytest, ruff, coverage
241
+ pytest -q # hermetic, no model calls
242
+ ruff check agent_yaml tests
243
+ pip install -e ".[docs]" # mkdocs
244
+ mkdocs serve # preview docs/ at http://127.0.0.1:8000
245
+ ```
246
+
247
+ The test suite is three tiers: unit (agent mocked), integration (a fake `opencode` that runs the
248
+ prompt's `bash` block), and live (`pytest --live`, a real model behind an OpenAI-compatible
249
+ gateway). See [tests/README.md](tests/README.md).
250
+
251
+ ## What it doesn't do
252
+
253
+ - It does not parse or judge model output. If a step needs to prove something, give it a validator.
254
+ - MCP detection in `ensure.mcps` is a warning-only stub.
255
+ - Nested list tasks (`task: [...]` inside a task) are rejected by the validator even though
256
+ `spec/YAML_SPEC.md` documents them. Use the mapping form.
257
+ - `agent-yaml run --resume` is a no-op. Resumption happens through each loop's `resume: true` plus
258
+ `checkpoint-file`.
259
+ - The Codex adapter emits flags that recent `codex-cli` releases reject. The Claude adapter sends
260
+ `--allowed-mcp-server-names`, which current `claude` builds reject. Both are listed with
261
+ workarounds in [docs/ADAPTER_TROUBLESHOOTING.md](docs/ADAPTER_TROUBLESHOOTING.md).
262
+ - Cancellation is best effort: in a parallel group with `on-error: fail`, tasks already scheduled
263
+ still run before the workflow aborts.
264
+
265
+ ## Contributing
266
+
267
+ See [CONTRIBUTING.md](CONTRIBUTING.md). Bug reports and pull requests are welcome.
268
+
269
+ ## License
270
+
271
+ [MIT](LICENSE) © 2026 Joe Schlesinger
@@ -0,0 +1,8 @@
1
+ """
2
+ agent-yaml: Meta-agent orchestration using declarative YAML workflows.
3
+
4
+ This package provides a Python CLI tool that orchestrates AI CLI agents
5
+ (Claude, Gemini, Qwen, Codex) using declarative YAML specifications.
6
+ """
7
+
8
+ __version__ = "0.1.0"