You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
- Updated `dry_run` and `timeout_global` fields in `shellflow_models.py` for better clarity and functionality.
- Improved variable parsing in `variables.py`, including changes to error handling and regex matching.
- Adjusted test assertions in `test_annotations.py`, `test_config.py`, and `test_doctor.py` for consistency in string formatting.
- Enhanced test coverage in `test_helpers.py`, `test_hooks.py`, and `test_macros.py` with updated assertions and improved readability.
- Added comprehensive regression tests in `test_scotty_absorption_regressions.py` to cover new features and ensure stability.
- Fixed various minor issues across tests to maintain consistency and improve reliability.
@@ -17,14 +17,18 @@ ShellFlow is a minimal shell script orchestrator for mixed local and remote exec
17
17
## What It Does
18
18
19
19
- Split a shell script into `@LOCAL` and `@REMOTE` execution blocks.
20
-
- Run each block fail-fast, in order.
21
-
- Reuse the shared prelude before the first marker for every block.
20
+
- Run blocks sequentially by default, or run annotated groups with `--mode parallel`.
21
+
- Freeze uppercase prelude assignments once so values such as `BUILD_ID=$(date +%s)` stay consistent across local and remote blocks.
22
+
- Declare script parameters with `# @option` and pass them as CLI flags or agent schema values.
23
+
- Run focused parts of a playbook with `# @TASK` and `shellflow run --task`.
24
+
- Reuse command snippets with `# @MACRO` and `# @HELPER`.
25
+
- Add local lifecycle hooks for setup, cleanup, success, and failure handling.
22
26
- Pass the previous block output forward as `SHELLFLOW_LAST_OUTPUT`.
23
27
- Export named scalar values from a block into later block environments.
24
28
- Emit either a final JSON report or streaming JSON Lines events for agents.
25
29
- Support bounded `@TIMEOUT` and `@RETRY` directives without embedding workflow logic.
26
-
- Provide non-interactive, dry-run, and audit-log modes for automated execution.
27
-
- Resolve remote targets from `~/.ssh/config` or a custom SSH config path.
30
+
- Provide non-interactive, dry-run, audit-log, doctor, and agent-run modes for automated execution.
31
+
- Resolve remote targets from inline `@SERVER` definitions, `~/.ssh/config`, or a custom SSH config path.
28
32
29
33
## Quick Start
30
34
@@ -79,31 +83,41 @@ uv pip install -e .
79
83
shellflow --version
80
84
```
81
85
82
-
## Script Format
86
+
## Playbook Format
83
87
84
-
Shellflow recognizes two markers:
88
+
Shellflow playbooks are ordinary shell scripts plus comment markers. Marker names are shown uppercase here and should be written that way for readability; the parser accepts marker names case-insensitively.
89
+
90
+
### Block markers
85
91
86
92
-`# @LOCAL`
87
93
-`# @REMOTE <ssh-host>`
88
94
89
-
Shellflow also recognizes bounded block directives at the top of a block body:
95
+
`<ssh-host>` must match an inline `@SERVER` definition or a `Host` entry in your SSH config. Shellflow then connects using the configured `host`, `HostName`, `User`, `Port`, and identity key values.
96
+
97
+
### Block directives
98
+
99
+
Block directives must appear immediately after the `# @LOCAL` or `# @REMOTE <ssh-host>` marker, before the first command in that block.
90
100
91
101
-`# @TIMEOUT <seconds>`
92
102
-`# @RETRY <count>`
93
103
-`# @EXPORT NAME=stdout|stderr|output|exit_code`
94
-
-`# @SHELL <shell>` - Specify the shell to use (e.g., `zsh`, `bash`)
104
+
-`# @SHELL <shell>` - specify `bash`, `zsh`, or `sh`.
105
+
-`# @PARALLEL [group]` - mark this block for grouped parallel execution.
95
106
96
-
`<ssh-host>` must match a `Host` entry in your SSH config. Shellflow then connects using that SSH host definition, which means the actual machine can be resolved through the configured `HostName`, `User`, `Port`, and `IdentityFile` values.
107
+
`@PARALLEL` may appear immediately before a block marker or as a block directive. It applies only to that one block. Consecutive parallel blocks are grouped together when you run with `--mode parallel`.
Declare script parameters near the top of the file:
133
+
134
+
```bash
135
+
# @option staging
136
+
# @option branch=main
137
+
# @option release-name=
138
+
```
139
+
140
+
-`# @option staging` is boolean. Passing `--staging` sets `STAGING=1`.
141
+
-`# @option branch=main` has a default. Passing `--branch develop` sets `BRANCH=develop`.
142
+
-`# @option release-name=` is required. Pass `--release-name v1` or set `RELEASE_NAME` in the environment.
143
+
- Option names become uppercase environment variables with dashes converted to underscores.
144
+
145
+
Run it:
146
+
147
+
```bash
148
+
shellflow run deploy.sh --branch develop --release-name v2026.05.01 --staging
149
+
```
150
+
151
+
### Preamble freeze
152
+
153
+
Lines before the first block marker are the shared prelude. Uppercase assignments in the prelude are evaluated once locally and then exported into every block with a frozen value:
154
+
155
+
```bash
156
+
BUILD_ID=$(date +%s)
157
+
158
+
# @LOCAL
159
+
echo"$BUILD_ID"
160
+
161
+
# @REMOTE sui
162
+
echo"$BUILD_ID"
163
+
```
164
+
165
+
Both blocks receive the same `BUILD_ID`. Non-assignment prelude lines, such as `set -euo pipefail` and helper functions, are still prepended to each block.
166
+
167
+
Keep the prelude declarative. Avoid one-time side effects such as `cd`, `rm`, or deployment commands before the first marker.
168
+
169
+
### Tasks and macros
170
+
171
+
Use `# @TASK <name>` to label blocks and `--task` to run only that task:
172
+
173
+
```bash
174
+
# @TASK build
175
+
# @LOCAL
176
+
echo"build"
177
+
178
+
# @TASK deploy
179
+
# @REMOTE sui
180
+
echo"deploy"
181
+
```
182
+
183
+
```bash
184
+
shellflow run deploy.sh --task build
185
+
```
186
+
187
+
Use a single-line macro to define a task flow:
188
+
189
+
```bash
190
+
# @MACRO release build deploy smoke-test
191
+
192
+
# @TASK build
193
+
# @LOCAL
194
+
echo"build"
195
+
196
+
# @TASK deploy
197
+
# @REMOTE sui
198
+
echo"deploy"
199
+
200
+
# @TASK smoke-test
201
+
# @LOCAL
202
+
echo"smoke"
203
+
```
204
+
205
+
```bash
206
+
shellflow run deploy.sh --task release
207
+
```
208
+
209
+
Macros can also expand command snippets inside a block:
210
+
211
+
```bash
212
+
# @MACRO print_env
213
+
# env | sort
214
+
# @ENDMACRO
215
+
216
+
# @LOCAL
217
+
print_env
218
+
```
219
+
220
+
### Helpers
221
+
222
+
Helpers are reusable command snippets. They are expanded when a block contains only the helper name on a line:
223
+
224
+
```bash
225
+
# @HELPER backup_db
226
+
# pg_dump "$DATABASE_URL" > backup.sql
227
+
# @ENDHELPER
228
+
229
+
# @LOCAL
230
+
backup_db
231
+
```
232
+
233
+
### Lifecycle hooks
234
+
235
+
Hooks run locally and share Shellflow's execution context:
236
+
237
+
-`PRE` - once before all main blocks.
238
+
-`BEFORE` - before each main block.
239
+
-`AFTER` - after each main block.
240
+
-`SUCCESS` - after all main blocks succeed.
241
+
-`ERROR` - after a hook or main block fails.
242
+
-`FINISHED` - at the end, whether the run succeeds or fails.
243
+
244
+
`POST` is accepted as an alias for `AFTER`; `FINALLY` is accepted as an alias for `FINISHED`.
245
+
246
+
```bash
247
+
# @HOOK PRE
248
+
# echo "prepare"
249
+
# @ENDHOOK
250
+
251
+
# @HOOK ERROR
252
+
# echo "rollback or collect diagnostics"
253
+
# @ENDHOOK
254
+
255
+
# @HOOK FINISHED
256
+
# echo "cleanup"
257
+
# @ENDHOOK
258
+
```
259
+
260
+
### Parallel groups
261
+
262
+
Mark each block that should join the parallel group:
263
+
264
+
```bash
265
+
# @PARALLEL web
266
+
# @REMOTE web-1
267
+
systemctl restart nginx
268
+
269
+
# @PARALLEL web
270
+
# @REMOTE web-2
271
+
systemctl restart nginx
272
+
273
+
# @LOCAL
274
+
echo"runs after the parallel group"
275
+
```
276
+
277
+
Run with:
278
+
279
+
```bash
280
+
shellflow run restart.sh --mode parallel
281
+
```
282
+
283
+
Without `--mode parallel`, blocks run sequentially even if annotated.
284
+
285
+
### Remote shells and tracing
286
+
116
287
Using `@SHELL` for remote servers with non-bash default shells:
117
288
118
289
Shellflow starts remote shells in login mode. For remote `zsh` and `bash` blocks, Shellflow also bootstraps `~/.zshrc` or `~/.bashrc` quietly before running your commands so tools initialized there, such as `mise`, remain available in non-interactive automation even if the rc file exits non-zero.
@@ -131,8 +302,25 @@ compdef
131
302
ls -la
132
303
```
133
304
305
+
Remote verbose tracing uses the shell's `DEBUG` trap and executes the block as one native script. That preserves multi-line Bash and zsh constructs such as `if/else/fi`, `for` loops, and function definitions.
306
+
134
307
## SSH Configuration
135
308
309
+
You can define remote hosts inline:
310
+
311
+
```bash
312
+
# @SERVER sui
313
+
# host: 192.168.1.100
314
+
# user: deploy
315
+
# port: 22
316
+
# key: ~/.ssh/id_ed25519
317
+
318
+
# @REMOTE sui
319
+
hostname
320
+
```
321
+
322
+
Inline server definitions are useful for portable playbooks. The `host` field is required; `user`, `port`, and `key` are optional.
- Shellflow accepts configured SSH host aliases, not arbitrary comma-separated or free-form targets.
156
344
- Unknown remote targets fail early with a clear error before spawning `ssh`.
157
345
- You can override the default config path with `--ssh-config`.
158
346
@@ -199,6 +387,8 @@ echo "prelude is active"
199
387
echo"prelude is also active here"
200
388
```
201
389
390
+
Uppercase assignments in the prelude are special: Shellflow evaluates them once locally, freezes the values, and exports them into every block. That keeps release IDs, timestamps, and option-derived values stable across local and remote execution.
391
+
202
392
## Agent-Native Usage
203
393
204
394
Shellflow is designed to be the execution substrate for an outer agent, not an embedded planner.
@@ -208,47 +398,66 @@ Shellflow is designed to be the execution substrate for an outer agent, not an e
208
398
- Use `--no-input` for CI or agent runs where interactive prompts must fail deterministically.
209
399
- Use `--dry-run` to preview planned execution without running commands.
210
400
- Use `--audit-log <path>` to mirror the structured event stream into a redacted JSONL file.
401
+
- Use `agent-run --json-input` when an agent already has the script body and option values in memory.
211
402
212
403
Recommended agent flow:
213
404
214
405
1. Generate or select a plain shell script with `@LOCAL` and `@REMOTE` markers.
215
-
2. Add bounded directives only where needed: `@TIMEOUT`, `@RETRY`, and `@EXPORT`.
216
-
3. Run with `--json` or `--jsonl`.
217
-
4. Let the outer agent decide whether to retry, branch, or stop based on Shellflow's structured result.
406
+
2. Read `# @option` declarations and provide required values.
407
+
3. Add bounded directives only where needed: `@TIMEOUT`, `@RETRY`, and `@EXPORT`.
408
+
4. Run with `--json` or `--jsonl`.
409
+
5. Let the outer agent decide whether to retry, branch, or stop based on Shellflow's structured result.
- Conditional directives such as `@IF stdout_contains=...`
222
424
- A workflow DSL or embedded ReAct loop
223
425
- Heuristic destructive-command detection
426
+
- Multi-host comma expansion inside one `@REMOTE` marker
224
427
225
428
Those decisions belong in the outer agent or automation layer.
226
429
227
-
### Agent-Native Logging Optimizations
430
+
### Agent-Native Reporting
228
431
229
-
Shellflow includes several logging optimizations specifically designed for LLM agent consumption:
432
+
Shellflow's structured modes are designed for LLM agent consumption:
230
433
231
-
-**ANSI Escape Sequence Stripping**: Automatically removes color codes, cursor movements, and other terminal control sequences that would consume unnecessary tokens in LLM context windows.
434
+
-**Stable run and block identifiers**: JSON and JSONL output include `run_id`, one-based block indexes, and stable `block-N` identifiers.
232
435
233
-
-**Command-Level Granularity**: Uses `set -x` with custom `PS4` prompts to provide per-command execution traces, allowing agents to pinpoint exactly which command failed in a multi-command block.
-**Stream Debouncing**: Implements intelligent buffering for progress bars and streaming output, preventing token waste from rapid `\r` updates while ensuring important output isn't lost.
438
+
-**Command-level remote tracing**: Remote verbose execution uses shell `DEBUG` traps to report the command about to run without breaking multi-line shell syntax.
236
439
237
-
-**Semantic Pipeline**: Transforms raw terminal output into structured, agent-friendly data with clean text and preserved timing information.
440
+
-**Audit-safe exports**: Audit logs redact exported values whose names look secret-like, such as `TOKEN`, `SECRET`, or `PASSWORD`.
238
441
239
-
These optimizations ensure that when agents process Shellflow's JSONL output, they receive clean, actionable data without the visual formatting artifacts that make terminal output human-friendly but LLM-confusing.
442
+
-**Bounded verbose output**: `--output-lines` limits verbose per-command log tails while preserving full block output in structured results.
240
443
241
444
## CLI
242
445
243
446
```text
244
447
shellflow run <script>
245
448
shellflow run <script> --verbose
449
+
shellflow run <script> --output-lines 50
246
450
shellflow run <script> --json
247
451
shellflow run <script> --jsonl
248
452
shellflow run <script> --no-input
249
453
shellflow run <script> --dry-run
454
+
shellflow run <script> --mode parallel
455
+
shellflow run <script> --task <task-or-macro>
250
456
shellflow run <script> --audit-log ./audit.jsonl --jsonl
251
457
shellflow run <script> --ssh-config ./ssh_config
458
+
shellflow run <script> --release-name v1 --branch main
@@ -262,8 +471,12 @@ shellflow run playbooks/hello.sh --jsonl --no-input
262
471
shellflow run playbooks/hello.sh --dry-run --jsonl
263
472
shellflow run playbooks/hello.sh --audit-log ./audit.jsonl --jsonl
264
473
shellflow run playbooks/hello.sh --ssh-config ~/.ssh/config.work
474
+
shellflow run playbooks/deploy.sh --task release --mode parallel --json
475
+
shellflow doctor playbooks/deploy.sh --ssh-config ~/.ssh/config.work
265
476
```
266
477
478
+
Run `shellflow run --help`, `shellflow agent-run --help`, or `shellflow doctor --help` for the exact command options supported by the installed version.
479
+
267
480
## Development
268
481
269
482
Useful commands:
@@ -327,6 +540,13 @@ The release workflow then runs verification, builds distributions with `uv build
0 commit comments