Drop markdown files in a folder, number them, and run. Each file is one task sent to an AI agent. When you need loops, retries, or conditional logic — add an .ash script. Start simple. Grow as needed.
AI agents handle ambiguity — understanding intent, making judgments. They're bad at sequencing, branching, and retrying. Ash handles the structure. Agents handle the content.
Tasks run in numbered order every time. No "what should I do next?" — the script decides. The agent executes.
The agent decides how to do its step. Ash decides whether the step happens, how many times, and what comes next.
A weekly report, a recurring audit, a batch job — same process every time, even when the content of each step varies.
Pre-determined control flow runs at CPU speed. AI time is spent only where it adds value — on the content of each step.
Review content, audit compliance, publish updates — all live in files you commit, version, share, and re-run. Not buried in chat threads.
OpenCode, Claude Code, Aider, or a custom tool. Define in a config file. Swap providers without touching your workflows.
Drop markdown files in a folder, number them, and run. Each file is a task sent to an AI agent. Output flows between steps.
tasks/
├── 01-research.md
├── 02-implement.md
├── 03-review.ash
└── 04-deploy.md
Built-in support: opencode, claude-code, aider, codex, gemini-cli, kimi, and more. See supported agents for the full list and default configs.
Use --agent to specify which agent to run with:
ash --agent opencode ./tasks
The tasks/ folder contains numbered .md files — each file is one task sent to the agent, executed in sorted order. Drop in an .ash file when you need loops, retries, or conditionals. See How Ash Works for the expected folder layout.
If you add a new agent after ash was already installed, run:
ash discover
Any CLI-based agent can be configured manually via ash.yml:
agents:
my-tool:
type: local-cli
cmd: my-tool
message_flag: "--prompt"
yes_flag: "--yes"
Markdown tasks handle simple agent calls. Drop in an .ash file when a single prompt isn't enough — variables, conditionals, shell commands within one step. Standalone scripts give you the full language: loops, retry, parallelism, functions. Same runtime, same state passing. Just add power as you need it.
MSG = "hello"
ITEMS = ["a", "b", "c"]
FIRST = ITEMS[0]
print "count: ${len(ITEMS)}"
working_dir = $(pwd)
do "Review this code" with opencode
try {
do "Fix the bug" with fixer
} fail {
do "Retry: ${stderr}"
} upto 3
for FILE in FILES {
do "Review ${FILE}"
if $? != 0 { exit 1 }
}
exec npm test
if $? == 0 {
print "all good"
}
fn review(path) {
do "Review ${path}" with opencode
}
include "helpers.ash"
review("src/main.rs")
For .ash scripts, declare the agent with a shebang:
#!opencode:1.0
do "Review src/" with opencode
For .md tasks, optionally set the agent in YAML frontmatter:
---
agent: opencode
model: sonnet
---
# Task Title
The prompt content goes here...
Write and run Ash scripts in your browser, or use the REPL for interactive experimentation. The built-in js-echo agent echoes your prompt back.