Ash is a scripting language where the control flow is rigid and the steps are intelligent. Sequencing,
branching, retry limits — defined in code. Each step executes autonomously by an AI agent.
Designed to be readable enough that non-programmers can automate their daily routines.
AI agents are powerful, but letting them drive the entire workflow — deciding what to do next, how many times to retry, when to stop — burns tokens on decisions that should be deterministic.
Any multi-step process — review content, generate a report, audit compliance, publish an update — should live in a file you commit, version, share, and rerun. Not buried in a chat thread.
"Research, then draft, then review, publish only if quality passes" — this is control flow, not intelligence. The agent executes the step, not the sequence.
The agent decides how to do its step. The script decides whether the step happens, how many times, and what comes next.
Every agent decision — even "what next?" — burns tokens and adds latency. Pre-determined control flow runs at CPU speed. AI time is spent only where it adds value.
A weekly report, a recurring audit, a batch processing job — these need to run the same way every time, even if the content of each step varies.
Scripts invoke agents. Agents invoke scripts. The boundary dissolves — deterministic and intelligent execution woven into a single runtime, not stacked in layers.
Bash and Python have no concept of an "agent" — no built-in retry, no evaluation, no stdout/stderr per step. You end up hand-rolling infrastructure that has nothing to do with your workflow.
Same task — loop over items, process each with an agent, retry on failure:
#!/bin/bash
# monitor support inbox — manual retry per ticket
TICKETS=("password reset request" "refund inquiry" "account locked")
for TICKET in "${TICKETS[@]}"; do
for TRY in 1 2; do
RESULT=$(opencode -p "Classify and reply to: $TICKET" 2>/dev/null)
if [ -n "$RESULT" ]; then
echo "handled: $TICKET"
break
fi
sleep 2
done
done
# check PRs — another nearly identical block
for TRY in 1 2; do
opencode -p "Summarize open PRs" 2>/dev/null && break
sleep 2
done
# verify deployment — branching on result
for TRY in 1 2; do
RESULT=$(opencode -p "Verify deployment health" 2>/dev/null)
if echo "$RESULT" | grep -qi "healthy"; then
echo "all good"
else
opencode -p "Investigate and alert" 2>/dev/null
fi
break
done
#!opencode:0.1.0
# monitor support inbox — each ticket processed with a prompt file
TICKETS = ["password reset request", "refund inquiry", "account locked"]
for TICKET in TICKETS {
do @classify-and-reply.md
print "handled: ${TICKET}"
}
# one-shot review using a spec file
do @review-prs.md
# verify deployment — branch on result
do @verify-deploy.md
if $? != 0 {
do @investigate-and-alert.md
}
Bash makes you build agent infrastructure. Ash gives you agents as a first-class language feature —
so you write the workflow, not the plumbing.
Plain enough that non-technical team members can read (and write) their daily routines.
MSG = "hello"
COUNT = 3
ITEMS = ["a", "b", "c"]
FIRST = ITEMS[0]
print "count: ${len(ITEMS)}"
exec RUSTFLAGS="-D warnings" cargo build 2>&1
working_dir = $(pwd)
for ITEM in ITEMS {
do "Process ${ITEM}" with js-echo
if $? != 0 {
print "failed on ${ITEM}"
exit 1
} else if len(stdout) < 5 {
print "too short, retrying"
continue
}
print "${ITEM} done"
}
within "/project" {
try {
do @refactor_auth.md with subagent fixer
} evaluate with {
exec python complexity_analysis.py
} accept {
print "approved"
} partial {
do "Revise: ${report}"
} fail {
print "rejected"
} upto 3
}
try {
do "Refactor" with subagent refactor
exec RUSTFLAGS="-D warnings" cargo build 2>&1
} fail {
print "attempt failed: ${stderr}"
do "Retry with context: ${stderr}"
} upto 3
Write and run Ash scripts in your browser. The built-in js-echo agent echoes your prompt back — useful for testing control flow and syntax. Use real agent engines with the Ash CLI locally.