shellflow 0.1.1__tar.gz → 0.2.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.
- {shellflow-0.1.1 → shellflow-0.2.0}/.gitignore +2 -0
- shellflow-0.2.0/CHANGELOG.md +8 -0
- {shellflow-0.1.1 → shellflow-0.2.0}/PKG-INFO +61 -1
- {shellflow-0.1.1 → shellflow-0.2.0}/README.md +60 -0
- {shellflow-0.1.1 → shellflow-0.2.0}/SKILL.md +130 -16
- shellflow-0.2.0/features/execution_contract.feature +29 -0
- shellflow-0.2.0/features/resilience_and_context.feature +24 -0
- shellflow-0.2.0/features/safety_controls.feature +24 -0
- shellflow-0.2.0/features/steps/shellflow_steps.py +958 -0
- shellflow-0.2.0/playbooks/hello.sh +89 -0
- {shellflow-0.1.1 → shellflow-0.2.0}/pyproject.toml +1 -1
- shellflow-0.2.0/specs/2026-03-15-01-agent-native-runner/design.md +458 -0
- shellflow-0.2.0/specs/2026-03-15-01-agent-native-runner/features/execution_contract.feature +29 -0
- shellflow-0.2.0/specs/2026-03-15-01-agent-native-runner/features/resilience_and_context.feature +24 -0
- shellflow-0.2.0/specs/2026-03-15-01-agent-native-runner/features/safety_controls.feature +24 -0
- shellflow-0.2.0/specs/2026-03-15-01-agent-native-runner/tasks.md +208 -0
- shellflow-0.2.0/src/shellflow.py +1524 -0
- {shellflow-0.1.1 → shellflow-0.2.0}/tests/test_shellflow.py +592 -0
- {shellflow-0.1.1 → shellflow-0.2.0}/uv.lock +1 -1
- shellflow-0.1.1/CHANGELOG.md +0 -12
- shellflow-0.1.1/features/steps/shellflow_steps.py +0 -237
- shellflow-0.1.1/playbooks/hello.sh +0 -8
- shellflow-0.1.1/src/shellflow.py +0 -802
- {shellflow-0.1.1 → shellflow-0.2.0}/.github/workflows/ci.yml +0 -0
- {shellflow-0.1.1 → shellflow-0.2.0}/.github/workflows/release.yml +0 -0
- {shellflow-0.1.1 → shellflow-0.2.0}/.python-version +0 -0
- {shellflow-0.1.1 → shellflow-0.2.0}/.rumdl.toml +0 -0
- {shellflow-0.1.1 → shellflow-0.2.0}/AGENTS.md +0 -0
- {shellflow-0.1.1 → shellflow-0.2.0}/CLAUDE.md +0 -0
- {shellflow-0.1.1 → shellflow-0.2.0}/Justfile +0 -0
- {shellflow-0.1.1 → shellflow-0.2.0}/assets/shellflow-run.png +0 -0
- {shellflow-0.1.1 → shellflow-0.2.0}/behave_runner.py +0 -0
- {shellflow-0.1.1 → shellflow-0.2.0}/cliff.toml +0 -0
- {shellflow-0.1.1 → shellflow-0.2.0}/context7.json +0 -0
- {shellflow-0.1.1 → shellflow-0.2.0}/features/__init__.py +0 -0
- {shellflow-0.1.1 → shellflow-0.2.0}/features/environment.py +0 -0
- {shellflow-0.1.1 → shellflow-0.2.0}/features/parser.feature +0 -0
- {shellflow-0.1.1 → shellflow-0.2.0}/features/runner.feature +0 -0
- {shellflow-0.1.1 → shellflow-0.2.0}/features/steps/__init__.py +0 -0
- {shellflow-0.1.1 → shellflow-0.2.0}/ruff.toml +0 -0
- {shellflow-0.1.1 → shellflow-0.2.0}/specs/2026-03-13-01-shellflow-runner/REFINEMENT_SUMMARY.md +0 -0
- {shellflow-0.1.1 → shellflow-0.2.0}/specs/2026-03-13-01-shellflow-runner/design.md +0 -0
- {shellflow-0.1.1 → shellflow-0.2.0}/specs/2026-03-13-01-shellflow-runner/features/config.feature +0 -0
- {shellflow-0.1.1 → shellflow-0.2.0}/specs/2026-03-13-01-shellflow-runner/features/parser.feature +0 -0
- {shellflow-0.1.1 → shellflow-0.2.0}/specs/2026-03-13-01-shellflow-runner/features/runner.feature +0 -0
- {shellflow-0.1.1 → shellflow-0.2.0}/specs/2026-03-13-01-shellflow-runner/features/steps/shellflow_steps.py +0 -0
- {shellflow-0.1.1 → shellflow-0.2.0}/specs/2026-03-13-01-shellflow-runner/tasks.md +0 -0
- {shellflow-0.1.1 → shellflow-0.2.0}/tests/__init__.py +0 -0
- {shellflow-0.1.1 → shellflow-0.2.0}/tests/fixtures/local_only.sh +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: shellflow
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: A minimal shell script orchestrator with SSH support
|
|
5
5
|
Project-URL: Homepage, https://github.com/longcipher/shellflow
|
|
6
6
|
Project-URL: Repository, https://github.com/longcipher/shellflow
|
|
@@ -22,6 +22,8 @@ Description-Content-Type: text/markdown
|
|
|
22
22
|
|
|
23
23
|
# ShellFlow
|
|
24
24
|
|
|
25
|
+
> AI agent native DevOps bash script orchestrator.
|
|
26
|
+
|
|
25
27
|
[](https://deepwiki.com/longcipher/shellflow)
|
|
26
28
|
[](https://context7.com/longcipher/shellflow)
|
|
27
29
|
[](https://www.python.org/downloads/)
|
|
@@ -40,6 +42,10 @@ ShellFlow is a minimal shell script orchestrator for mixed local and remote exec
|
|
|
40
42
|
- Run each block fail-fast, in order.
|
|
41
43
|
- Reuse the shared prelude before the first marker for every block.
|
|
42
44
|
- Pass the previous block output forward as `SHELLFLOW_LAST_OUTPUT`.
|
|
45
|
+
- Export named scalar values from a block into later block environments.
|
|
46
|
+
- Emit either a final JSON report or streaming JSON Lines events for agents.
|
|
47
|
+
- Support bounded `@TIMEOUT` and `@RETRY` directives without embedding workflow logic.
|
|
48
|
+
- Provide non-interactive, dry-run, and audit-log modes for automated execution.
|
|
43
49
|
- Resolve remote targets from `~/.ssh/config` or a custom SSH config path.
|
|
44
50
|
|
|
45
51
|
## Quick Start
|
|
@@ -102,6 +108,12 @@ Shellflow recognizes two markers:
|
|
|
102
108
|
- `# @LOCAL`
|
|
103
109
|
- `# @REMOTE <ssh-host>`
|
|
104
110
|
|
|
111
|
+
Shellflow also recognizes bounded block directives at the top of a block body:
|
|
112
|
+
|
|
113
|
+
- `# @TIMEOUT <seconds>`
|
|
114
|
+
- `# @RETRY <count>`
|
|
115
|
+
- `# @EXPORT NAME=stdout|stderr|output|exit_code`
|
|
116
|
+
|
|
105
117
|
`<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.
|
|
106
118
|
|
|
107
119
|
Example:
|
|
@@ -111,6 +123,7 @@ Example:
|
|
|
111
123
|
set -euo pipefail
|
|
112
124
|
|
|
113
125
|
# @LOCAL
|
|
126
|
+
# @EXPORT VERSION=stdout
|
|
114
127
|
echo "runs locally"
|
|
115
128
|
|
|
116
129
|
# @REMOTE sui
|
|
@@ -118,6 +131,7 @@ uname -a
|
|
|
118
131
|
|
|
119
132
|
# @LOCAL
|
|
120
133
|
echo "remote output: $SHELLFLOW_LAST_OUTPUT"
|
|
134
|
+
echo "version = $VERSION"
|
|
121
135
|
```
|
|
122
136
|
|
|
123
137
|
## SSH Configuration
|
|
@@ -163,6 +177,18 @@ echo "build-123"
|
|
|
163
177
|
echo "last output = $SHELLFLOW_LAST_OUTPUT"
|
|
164
178
|
```
|
|
165
179
|
|
|
180
|
+
Named exports are additive to `SHELLFLOW_LAST_OUTPUT`:
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
# @LOCAL
|
|
184
|
+
# @EXPORT VERSION=stdout
|
|
185
|
+
echo "2026.03.15"
|
|
186
|
+
|
|
187
|
+
# @REMOTE sui
|
|
188
|
+
echo "deploying $VERSION"
|
|
189
|
+
echo "last output = $SHELLFLOW_LAST_OUTPUT"
|
|
190
|
+
```
|
|
191
|
+
|
|
166
192
|
Lines before the first marker are treated as a shared prelude and prepended to every executable block:
|
|
167
193
|
|
|
168
194
|
```bash
|
|
@@ -176,11 +202,41 @@ echo "prelude is active"
|
|
|
176
202
|
echo "prelude is also active here"
|
|
177
203
|
```
|
|
178
204
|
|
|
205
|
+
## Agent-Native Usage
|
|
206
|
+
|
|
207
|
+
Shellflow is designed to be the execution substrate for an outer agent, not an embedded planner.
|
|
208
|
+
|
|
209
|
+
- Use `--json` when you want one final machine-readable run report.
|
|
210
|
+
- Use `--jsonl` when you want ordered event records while the script runs.
|
|
211
|
+
- Use `--no-input` for CI or agent runs where interactive prompts must fail deterministically.
|
|
212
|
+
- Use `--dry-run` to preview planned execution without running commands.
|
|
213
|
+
- Use `--audit-log <path>` to mirror the structured event stream into a redacted JSONL file.
|
|
214
|
+
|
|
215
|
+
Recommended agent flow:
|
|
216
|
+
|
|
217
|
+
1. Generate or select a plain shell script with `@LOCAL` and `@REMOTE` markers.
|
|
218
|
+
2. Add bounded directives only where needed: `@TIMEOUT`, `@RETRY`, and `@EXPORT`.
|
|
219
|
+
3. Run with `--json` or `--jsonl`.
|
|
220
|
+
4. Let the outer agent decide whether to retry, branch, or stop based on Shellflow's structured result.
|
|
221
|
+
|
|
222
|
+
Shellflow intentionally does not provide:
|
|
223
|
+
|
|
224
|
+
- Conditional directives such as `@IF stdout_contains=...`
|
|
225
|
+
- A workflow DSL or embedded ReAct loop
|
|
226
|
+
- Heuristic destructive-command detection
|
|
227
|
+
|
|
228
|
+
Those decisions belong in the outer agent or automation layer.
|
|
229
|
+
|
|
179
230
|
## CLI
|
|
180
231
|
|
|
181
232
|
```text
|
|
182
233
|
shellflow run <script>
|
|
183
234
|
shellflow run <script> --verbose
|
|
235
|
+
shellflow run <script> --json
|
|
236
|
+
shellflow run <script> --jsonl
|
|
237
|
+
shellflow run <script> --no-input
|
|
238
|
+
shellflow run <script> --dry-run
|
|
239
|
+
shellflow run <script> --audit-log ./audit.jsonl --jsonl
|
|
184
240
|
shellflow run <script> --ssh-config ./ssh_config
|
|
185
241
|
shellflow --version
|
|
186
242
|
```
|
|
@@ -190,6 +246,10 @@ Examples:
|
|
|
190
246
|
```bash
|
|
191
247
|
shellflow run playbooks/hello.sh
|
|
192
248
|
shellflow run playbooks/hello.sh -v
|
|
249
|
+
shellflow run playbooks/hello.sh --json
|
|
250
|
+
shellflow run playbooks/hello.sh --jsonl --no-input
|
|
251
|
+
shellflow run playbooks/hello.sh --dry-run --jsonl
|
|
252
|
+
shellflow run playbooks/hello.sh --audit-log ./audit.jsonl --jsonl
|
|
193
253
|
shellflow run playbooks/hello.sh --ssh-config ~/.ssh/config.work
|
|
194
254
|
```
|
|
195
255
|
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# ShellFlow
|
|
2
2
|
|
|
3
|
+
> AI agent native DevOps bash script orchestrator.
|
|
4
|
+
|
|
3
5
|
[](https://deepwiki.com/longcipher/shellflow)
|
|
4
6
|
[](https://context7.com/longcipher/shellflow)
|
|
5
7
|
[](https://www.python.org/downloads/)
|
|
@@ -18,6 +20,10 @@ ShellFlow is a minimal shell script orchestrator for mixed local and remote exec
|
|
|
18
20
|
- Run each block fail-fast, in order.
|
|
19
21
|
- Reuse the shared prelude before the first marker for every block.
|
|
20
22
|
- Pass the previous block output forward as `SHELLFLOW_LAST_OUTPUT`.
|
|
23
|
+
- Export named scalar values from a block into later block environments.
|
|
24
|
+
- Emit either a final JSON report or streaming JSON Lines events for agents.
|
|
25
|
+
- Support bounded `@TIMEOUT` and `@RETRY` directives without embedding workflow logic.
|
|
26
|
+
- Provide non-interactive, dry-run, and audit-log modes for automated execution.
|
|
21
27
|
- Resolve remote targets from `~/.ssh/config` or a custom SSH config path.
|
|
22
28
|
|
|
23
29
|
## Quick Start
|
|
@@ -80,6 +86,12 @@ Shellflow recognizes two markers:
|
|
|
80
86
|
- `# @LOCAL`
|
|
81
87
|
- `# @REMOTE <ssh-host>`
|
|
82
88
|
|
|
89
|
+
Shellflow also recognizes bounded block directives at the top of a block body:
|
|
90
|
+
|
|
91
|
+
- `# @TIMEOUT <seconds>`
|
|
92
|
+
- `# @RETRY <count>`
|
|
93
|
+
- `# @EXPORT NAME=stdout|stderr|output|exit_code`
|
|
94
|
+
|
|
83
95
|
`<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.
|
|
84
96
|
|
|
85
97
|
Example:
|
|
@@ -89,6 +101,7 @@ Example:
|
|
|
89
101
|
set -euo pipefail
|
|
90
102
|
|
|
91
103
|
# @LOCAL
|
|
104
|
+
# @EXPORT VERSION=stdout
|
|
92
105
|
echo "runs locally"
|
|
93
106
|
|
|
94
107
|
# @REMOTE sui
|
|
@@ -96,6 +109,7 @@ uname -a
|
|
|
96
109
|
|
|
97
110
|
# @LOCAL
|
|
98
111
|
echo "remote output: $SHELLFLOW_LAST_OUTPUT"
|
|
112
|
+
echo "version = $VERSION"
|
|
99
113
|
```
|
|
100
114
|
|
|
101
115
|
## SSH Configuration
|
|
@@ -141,6 +155,18 @@ echo "build-123"
|
|
|
141
155
|
echo "last output = $SHELLFLOW_LAST_OUTPUT"
|
|
142
156
|
```
|
|
143
157
|
|
|
158
|
+
Named exports are additive to `SHELLFLOW_LAST_OUTPUT`:
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
# @LOCAL
|
|
162
|
+
# @EXPORT VERSION=stdout
|
|
163
|
+
echo "2026.03.15"
|
|
164
|
+
|
|
165
|
+
# @REMOTE sui
|
|
166
|
+
echo "deploying $VERSION"
|
|
167
|
+
echo "last output = $SHELLFLOW_LAST_OUTPUT"
|
|
168
|
+
```
|
|
169
|
+
|
|
144
170
|
Lines before the first marker are treated as a shared prelude and prepended to every executable block:
|
|
145
171
|
|
|
146
172
|
```bash
|
|
@@ -154,11 +180,41 @@ echo "prelude is active"
|
|
|
154
180
|
echo "prelude is also active here"
|
|
155
181
|
```
|
|
156
182
|
|
|
183
|
+
## Agent-Native Usage
|
|
184
|
+
|
|
185
|
+
Shellflow is designed to be the execution substrate for an outer agent, not an embedded planner.
|
|
186
|
+
|
|
187
|
+
- Use `--json` when you want one final machine-readable run report.
|
|
188
|
+
- Use `--jsonl` when you want ordered event records while the script runs.
|
|
189
|
+
- Use `--no-input` for CI or agent runs where interactive prompts must fail deterministically.
|
|
190
|
+
- Use `--dry-run` to preview planned execution without running commands.
|
|
191
|
+
- Use `--audit-log <path>` to mirror the structured event stream into a redacted JSONL file.
|
|
192
|
+
|
|
193
|
+
Recommended agent flow:
|
|
194
|
+
|
|
195
|
+
1. Generate or select a plain shell script with `@LOCAL` and `@REMOTE` markers.
|
|
196
|
+
2. Add bounded directives only where needed: `@TIMEOUT`, `@RETRY`, and `@EXPORT`.
|
|
197
|
+
3. Run with `--json` or `--jsonl`.
|
|
198
|
+
4. Let the outer agent decide whether to retry, branch, or stop based on Shellflow's structured result.
|
|
199
|
+
|
|
200
|
+
Shellflow intentionally does not provide:
|
|
201
|
+
|
|
202
|
+
- Conditional directives such as `@IF stdout_contains=...`
|
|
203
|
+
- A workflow DSL or embedded ReAct loop
|
|
204
|
+
- Heuristic destructive-command detection
|
|
205
|
+
|
|
206
|
+
Those decisions belong in the outer agent or automation layer.
|
|
207
|
+
|
|
157
208
|
## CLI
|
|
158
209
|
|
|
159
210
|
```text
|
|
160
211
|
shellflow run <script>
|
|
161
212
|
shellflow run <script> --verbose
|
|
213
|
+
shellflow run <script> --json
|
|
214
|
+
shellflow run <script> --jsonl
|
|
215
|
+
shellflow run <script> --no-input
|
|
216
|
+
shellflow run <script> --dry-run
|
|
217
|
+
shellflow run <script> --audit-log ./audit.jsonl --jsonl
|
|
162
218
|
shellflow run <script> --ssh-config ./ssh_config
|
|
163
219
|
shellflow --version
|
|
164
220
|
```
|
|
@@ -168,6 +224,10 @@ Examples:
|
|
|
168
224
|
```bash
|
|
169
225
|
shellflow run playbooks/hello.sh
|
|
170
226
|
shellflow run playbooks/hello.sh -v
|
|
227
|
+
shellflow run playbooks/hello.sh --json
|
|
228
|
+
shellflow run playbooks/hello.sh --jsonl --no-input
|
|
229
|
+
shellflow run playbooks/hello.sh --dry-run --jsonl
|
|
230
|
+
shellflow run playbooks/hello.sh --audit-log ./audit.jsonl --jsonl
|
|
171
231
|
shellflow run playbooks/hello.sh --ssh-config ~/.ssh/config.work
|
|
172
232
|
```
|
|
173
233
|
|
|
@@ -114,7 +114,60 @@ pwd
|
|
|
114
114
|
|
|
115
115
|
Why it is bad: the `cd /srv/app` line becomes part of every block, including remote blocks.
|
|
116
116
|
|
|
117
|
-
## 4.
|
|
117
|
+
## 4. Use block directives for timeout, retry, and exports
|
|
118
|
+
|
|
119
|
+
Block directives must appear immediately after the `# @LOCAL` or `# @REMOTE <host>` marker, before any command lines. They configure execution behavior for that specific block.
|
|
120
|
+
|
|
121
|
+
### Timeout Directive
|
|
122
|
+
|
|
123
|
+
`# @TIMEOUT <seconds>` - Abort the block if it exceeds the specified duration.
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
# @LOCAL
|
|
127
|
+
# @TIMEOUT 30
|
|
128
|
+
sleep 60
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Retry Directive
|
|
132
|
+
|
|
133
|
+
`# @RETRY <count>` - Retry the block up to N times on failure (0 means no retry).
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
# @LOCAL
|
|
137
|
+
# @RETRY 3
|
|
138
|
+
curl -f https://api.example.com/health
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Export Directive
|
|
142
|
+
|
|
143
|
+
`# @EXPORT NAME=source` - Capture a value from the block result and pass it to subsequent blocks as an environment variable.
|
|
144
|
+
|
|
145
|
+
Valid sources:
|
|
146
|
+
|
|
147
|
+
- `stdout` - The block's standard output
|
|
148
|
+
- `stderr` - The block's standard error
|
|
149
|
+
- `output` - Combined stdout and stderr
|
|
150
|
+
- `exit_code` - The block's exit code (as string)
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
# @LOCAL
|
|
154
|
+
# @EXPORT BUILD_ID=stdout
|
|
155
|
+
echo "build-$(date +%s)"
|
|
156
|
+
|
|
157
|
+
# @LOCAL
|
|
158
|
+
echo "Building: $BUILD_ID"
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
You can use multiple exports in a single block:
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
# @LOCAL
|
|
165
|
+
# @EXPORT STATUS_CODE=exit_code
|
|
166
|
+
# @EXPORT RESPONSE=stdout
|
|
167
|
+
curl -s -w "%{http_code}" -o response.txt https://api.example.com
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## 5. Assume every block runs in a fresh shell
|
|
118
171
|
|
|
119
172
|
Each block is isolated.
|
|
120
173
|
|
|
@@ -158,7 +211,7 @@ pwd
|
|
|
158
211
|
|
|
159
212
|
Why it is bad: `artifact` and the working directory do not persist.
|
|
160
213
|
|
|
161
|
-
##
|
|
214
|
+
## 6. Use SHELLFLOW_LAST_OUTPUT for explicit handoff
|
|
162
215
|
|
|
163
216
|
Shellflow passes the previous block's combined output into the next block as `SHELLFLOW_LAST_OUTPUT`.
|
|
164
217
|
|
|
@@ -197,7 +250,7 @@ print(payload["release"])
|
|
|
197
250
|
PY
|
|
198
251
|
```
|
|
199
252
|
|
|
200
|
-
##
|
|
253
|
+
## 7. Use SSH config host aliases, not ad-hoc targets
|
|
201
254
|
|
|
202
255
|
`# @REMOTE <ssh-host>` should point to a host that resolves through SSH config.
|
|
203
256
|
|
|
@@ -210,7 +263,7 @@ Avoid assuming Shellflow accepts any arbitrary free-form destination unless it i
|
|
|
210
263
|
|
|
211
264
|
If a remote host is unknown, Shellflow fails before execution.
|
|
212
265
|
|
|
213
|
-
##
|
|
266
|
+
## 8. Keep blocks self-contained and fail-fast
|
|
214
267
|
|
|
215
268
|
Shellflow runs blocks in order and stops on the first failure.
|
|
216
269
|
|
|
@@ -243,17 +296,69 @@ docker compose up -d
|
|
|
243
296
|
|
|
244
297
|
Why: the second block cannot rely on the first block's `cd`.
|
|
245
298
|
|
|
299
|
+
## 9. CLI Options and Output Modes
|
|
300
|
+
|
|
301
|
+
Shellflow provides several CLI options for different use cases:
|
|
302
|
+
|
|
303
|
+
### Basic Options
|
|
304
|
+
|
|
305
|
+
```bash
|
|
306
|
+
shellflow run script.sh # Run a script
|
|
307
|
+
shellflow run script.sh -v # Run with verbose output
|
|
308
|
+
shellflow run script.sh --dry-run # Preview execution plan without running
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
### Structured Output
|
|
312
|
+
|
|
313
|
+
```bash
|
|
314
|
+
shellflow run script.sh --json # Single JSON report
|
|
315
|
+
shellflow run script.sh --jsonl # Streaming JSON Lines events
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
- `--json`: Outputs a single JSON object with the complete run report
|
|
319
|
+
- `--jsonl`: Outputs one JSON object per event (run_started, block_started, block_finished, run_finished)
|
|
320
|
+
|
|
321
|
+
### Execution Control
|
|
322
|
+
|
|
323
|
+
```bash
|
|
324
|
+
shellflow run script.sh --no-input # Non-interactive mode (stdin closed)
|
|
325
|
+
shellflow run script.sh --ssh-config /path/to/config # Custom SSH config
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
- `--no-input`: Closes stdin before running blocks; useful for automation
|
|
329
|
+
- `--ssh-config`: Override the default SSH config path (`~/.ssh/config`)
|
|
330
|
+
|
|
331
|
+
### Audit Logging
|
|
332
|
+
|
|
333
|
+
```bash
|
|
334
|
+
shellflow run script.sh --audit-log audit.jsonl --jsonl
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
The `--audit-log` option writes redacted JSON Lines events to a file. Secret-like exports (containing TOKEN, SECRET, or PASSWORD in the name) are automatically redacted to `[REDACTED]`.
|
|
338
|
+
|
|
339
|
+
## 10. Exit Codes
|
|
340
|
+
|
|
341
|
+
Shellflow returns distinct exit codes for different failure types:
|
|
342
|
+
|
|
343
|
+
- `0`: Success
|
|
344
|
+
- `1`: General execution failure
|
|
345
|
+
- `2`: Parse failure (invalid script syntax)
|
|
346
|
+
- `3`: SSH config failure (host not found)
|
|
347
|
+
- `4`: Timeout failure (block exceeded timeout)
|
|
348
|
+
|
|
246
349
|
## Authoring Checklist
|
|
247
350
|
|
|
248
351
|
Before returning a Shellflow playbook, verify that:
|
|
249
352
|
|
|
250
353
|
- The script is valid bash without custom DSL syntax.
|
|
251
|
-
- Only `# @LOCAL
|
|
354
|
+
- Only `# @LOCAL`, `# @REMOTE <host>`, and block directives (`# @TIMEOUT`, `# @RETRY`, `# @EXPORT`) are used.
|
|
355
|
+
- Block directives appear immediately after the block marker, before any commands.
|
|
252
356
|
- Anything before the first marker is safe to repeat for every block.
|
|
253
357
|
- Every block can run independently in a fresh shell.
|
|
254
|
-
- Cross-block data uses `SHELLFLOW_LAST_OUTPUT` explicitly.
|
|
358
|
+
- Cross-block data uses `SHELLFLOW_LAST_OUTPUT` or `@EXPORT` explicitly.
|
|
255
359
|
- Remote targets match the intended SSH host aliases.
|
|
256
360
|
- Commands that should happen once are not accidentally placed in the shared prelude.
|
|
361
|
+
- Export sources are valid (stdout, stderr, output, exit_code).
|
|
257
362
|
|
|
258
363
|
## Reference Example
|
|
259
364
|
|
|
@@ -266,20 +371,25 @@ log() {
|
|
|
266
371
|
}
|
|
267
372
|
|
|
268
373
|
# @LOCAL
|
|
374
|
+
# @EXPORT BUILD_ID=stdout
|
|
269
375
|
log "building artifact"
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
376
|
+
build_id="build-$(date +%Y%m%d%H%M%S)"
|
|
377
|
+
echo "$build_id"
|
|
378
|
+
|
|
379
|
+
# @LOCAL
|
|
380
|
+
# @TIMEOUT 60
|
|
381
|
+
# @RETRY 2
|
|
382
|
+
log "deploying to staging"
|
|
383
|
+
echo "Deploying $BUILD_ID to staging"
|
|
273
384
|
|
|
274
385
|
# @REMOTE staging
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
test -n "$artifact_path"
|
|
279
|
-
uname -a
|
|
386
|
+
# @EXPORT DEPLOYED_HOST=stdout
|
|
387
|
+
log "receiving deployment"
|
|
388
|
+
hostname
|
|
280
389
|
|
|
281
390
|
# @LOCAL
|
|
282
|
-
log "
|
|
391
|
+
log "deployed to: $DEPLOYED_HOST"
|
|
392
|
+
log "build $BUILD_ID complete"
|
|
283
393
|
```
|
|
284
394
|
|
|
285
395
|
## Common Mistakes
|
|
@@ -287,6 +397,10 @@ log "remote said: $SHELLFLOW_LAST_OUTPUT"
|
|
|
287
397
|
- Putting one-time commands before the first marker, then being surprised when they run for every block.
|
|
288
398
|
- Expecting `cd`, `export`, or local shell variables from one block to exist in the next block.
|
|
289
399
|
- Using an undefined remote host alias.
|
|
290
|
-
-
|
|
400
|
+
- Placing block directives after commands instead of immediately after the marker.
|
|
401
|
+
- Using invalid export sources (not stdout, stderr, output, or exit_code).
|
|
402
|
+
- Forgetting that `@RETRY 0` means no retry attempts.
|
|
403
|
+
- Using `@TIMEOUT` with values too small for normal operation.
|
|
404
|
+
- Printing extra debug output from a block whose output is consumed by the next block via `@EXPORT`.
|
|
291
405
|
- Forgetting to quote `"$SHELLFLOW_LAST_OUTPUT"`.
|
|
292
406
|
- Treating Shellflow as a persistent session instead of sequential isolated shells.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
Feature: Agent-facing execution contract
|
|
2
|
+
As an AI agent operating Shellflow
|
|
3
|
+
I want structured execution results and stable exit codes
|
|
4
|
+
So that I can observe outcomes and decide what to do next outside Shellflow
|
|
5
|
+
|
|
6
|
+
Scenario: JSON report mode returns machine-readable run and block results
|
|
7
|
+
Given a script file with a local block that prints a release version
|
|
8
|
+
When I run the script with JSON output enabled
|
|
9
|
+
Then the command should succeed
|
|
10
|
+
And the JSON output should contain a run id
|
|
11
|
+
And the JSON output should contain a schema version
|
|
12
|
+
And the JSON output should include the first block exit code
|
|
13
|
+
And the JSON output should include the first block stdout separately from stderr
|
|
14
|
+
|
|
15
|
+
Scenario: JSONL mode emits ordered events suitable for live observation
|
|
16
|
+
Given a script file with two local blocks that both succeed
|
|
17
|
+
When I run the script with JSON Lines output enabled
|
|
18
|
+
Then the command should succeed
|
|
19
|
+
And the output should contain a run_started event before a block_started event
|
|
20
|
+
And the output should contain a block_finished event for each block
|
|
21
|
+
And the output should end with a run_finished event
|
|
22
|
+
|
|
23
|
+
Scenario: Exit codes distinguish parse, SSH config, runtime, and timeout failures
|
|
24
|
+
Given the relevant failing scripts for parse, missing SSH host, block failure, and timeout
|
|
25
|
+
When I run each script in machine-readable mode
|
|
26
|
+
Then the parse failure should exit with code 2
|
|
27
|
+
And the missing SSH host failure should exit with code 3
|
|
28
|
+
And the block execution failure should exit with code 1
|
|
29
|
+
And the timeout failure should exit with code 4
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
Feature: Resilience and context propagation
|
|
2
|
+
As an AI agent using Shellflow as an action tool
|
|
3
|
+
I want bounded retries, timeouts, and named exports
|
|
4
|
+
So that I can recover from transient failures without turning Shellflow into a workflow engine
|
|
5
|
+
|
|
6
|
+
Scenario: Timeout stops a stuck block and reports a timeout-specific failure
|
|
7
|
+
Given a script file with a local block that exceeds its timeout directive
|
|
8
|
+
When I run the script in machine-readable mode
|
|
9
|
+
Then the command should fail with timeout exit code 4
|
|
10
|
+
And the structured output should mark the block as timed out
|
|
11
|
+
And the structured output should record the timeout duration policy
|
|
12
|
+
|
|
13
|
+
Scenario: Retry reruns a transiently failing block and reports attempts
|
|
14
|
+
Given a script file with a local block that fails once and then succeeds with a retry directive
|
|
15
|
+
When I run the script in machine-readable mode
|
|
16
|
+
Then the command should succeed
|
|
17
|
+
And the structured output should record 2 attempts for that block
|
|
18
|
+
And the structured output should include a retrying event before the successful finish event
|
|
19
|
+
|
|
20
|
+
Scenario: Named exports become environment variables for later blocks
|
|
21
|
+
Given a script file whose first block exports VERSION from stdout
|
|
22
|
+
When I run the script
|
|
23
|
+
Then the later block should receive VERSION in its environment
|
|
24
|
+
And SHELLFLOW_LAST_OUTPUT should still be available
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
Feature: Safety controls for automated runs
|
|
2
|
+
As an operator delegating execution to an AI agent
|
|
3
|
+
I want non-interactive and audit-friendly controls
|
|
4
|
+
So that automated runs are observable without relying on shell heuristics
|
|
5
|
+
|
|
6
|
+
Scenario: No-input prevents blocking on stdin
|
|
7
|
+
Given a script file with a local block that reads from standard input
|
|
8
|
+
When I run the script with no-input enabled
|
|
9
|
+
Then the command should fail deterministically instead of waiting for input
|
|
10
|
+
And the structured output should indicate that no interactive input was available
|
|
11
|
+
|
|
12
|
+
Scenario: Dry-run previews execution without running commands
|
|
13
|
+
Given a script file with local and remote blocks
|
|
14
|
+
When I run the script in dry-run mode
|
|
15
|
+
Then no block commands should be executed
|
|
16
|
+
And the output should describe the planned blocks in order
|
|
17
|
+
And the output should include structured dry-run events when machine-readable mode is enabled
|
|
18
|
+
|
|
19
|
+
Scenario: Audit-log writes structured events for later inspection
|
|
20
|
+
Given a script file with a named export that looks like a secret
|
|
21
|
+
When I run the script with an audit log path
|
|
22
|
+
Then the command should succeed
|
|
23
|
+
And the audit log file should contain JSON Lines events
|
|
24
|
+
And the audit log should redact the secret-like exported value
|