Skip to content

Commit 371c69d

Browse files
committed
feat: Introduce agent-native execution contract and resilience features
- Added structured output for machine-readable execution results in JSON and JSONL formats. - Implemented stable exit codes for various failure scenarios including parse, SSH config, runtime, and timeout failures. - Introduced resilience controls with timeout and retry directives for block execution. - Enabled named exports to propagate environment variables between blocks. - Added safety controls including no-input mode and dry-run functionality. - Created comprehensive BDD scenarios to validate new features and ensure backward compatibility. - Developed detailed design documentation outlining the architecture and implementation plan.
1 parent 7efe874 commit 371c69d

13 files changed

Lines changed: 2970 additions & 53 deletions

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,5 @@ Thumbs.db
4141
.env
4242
.env.local
4343
.rumdl_cache/
44+
.benchmarks/
45+
.hypothesis/

README.md

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# ShellFlow
22

3+
> AI agent native DevOps bash script orchestrator.
4+
35
[![DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/longcipher/shellflow)
46
[![Context7](https://img.shields.io/badge/Website-context7.com-blue)](https://context7.com/longcipher/shellflow)
57
[![Python 3.12+](https://img.shields.io/badge/python-3.12%2B-blue.svg)](https://www.python.org/downloads/)
@@ -18,6 +20,10 @@ ShellFlow is a minimal shell script orchestrator for mixed local and remote exec
1820
- Run each block fail-fast, in order.
1921
- Reuse the shared prelude before the first marker for every block.
2022
- 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.
2127
- Resolve remote targets from `~/.ssh/config` or a custom SSH config path.
2228

2329
## Quick Start
@@ -80,6 +86,12 @@ Shellflow recognizes two markers:
8086
- `# @LOCAL`
8187
- `# @REMOTE <ssh-host>`
8288

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+
8395
`<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.
8496

8597
Example:
@@ -89,13 +101,15 @@ Example:
89101
set -euo pipefail
90102

91103
# @LOCAL
104+
# @EXPORT VERSION=stdout
92105
echo "runs locally"
93106

94107
# @REMOTE sui
95108
uname -a
96109

97110
# @LOCAL
98111
echo "remote output: $SHELLFLOW_LAST_OUTPUT"
112+
echo "version = $VERSION"
99113
```
100114

101115
## SSH Configuration
@@ -141,6 +155,18 @@ echo "build-123"
141155
echo "last output = $SHELLFLOW_LAST_OUTPUT"
142156
```
143157

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+
144170
Lines before the first marker are treated as a shared prelude and prepended to every executable block:
145171

146172
```bash
@@ -154,11 +180,41 @@ echo "prelude is active"
154180
echo "prelude is also active here"
155181
```
156182

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+
157208
## CLI
158209

159210
```text
160211
shellflow run <script>
161212
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
162218
shellflow run <script> --ssh-config ./ssh_config
163219
shellflow --version
164220
```
@@ -168,6 +224,10 @@ Examples:
168224
```bash
169225
shellflow run playbooks/hello.sh
170226
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
171231
shellflow run playbooks/hello.sh --ssh-config ~/.ssh/config.work
172232
```
173233

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

features/safety_controls.feature

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

0 commit comments

Comments
 (0)