aceit
v0.27.4
Published
Terminal workflow orchestrator for opencode: resumable prompt pipelines with evaluator-driven loops.
Readme
aceit
Resumable, evaluator-driven workflow loops for opencode, in your terminal.
aceit runs chains of prompts (markdown files or inline text) as opencode sessions. An evaluator step judges the result; if it fails, its feedback is injected back into the target step and the work is redone — until the evaluator passes or a hard iteration limit is hit. Every step's state is persisted after each step, so crashes, Ctrl-C and failures resume where they left off instead of starting over.
The three execution modes
1 · Straight pipeline — no eval step. Steps run once, in order:
+----------+ +----------+ +---------+
| step 1 | --> | step 2 | --> | done |
+----------+ +----------+ +---------+2 · Evaluator loop — default. Fail loops back with feedback injected; a clean pass finishes the run:
+------------+ +------+ +---------+
| implement | --> | ship | --> | eval | -- pass --> [done]
+-----^------+ +------+ +----|----+
| |
+------------ fail <-----------+On fail, aceit increments the iteration counter, resets every step between
the loop-back target and the evaluator (their earlier outputs are now stale),
and injects a Previous attempt rejected block containing the evaluator's
feedback into the target step's next attempt. When max_iterations fails are
reached, the run stops with status failed — never loops forever on its own.
3 · Forever mode — never stops, even on pass.
on_pass: loop or --forever: a clean pass also loops back, starting a fresh
cycle with brand-new sessions. Only Ctrl-C ends it:
+------------+ +------+ +---------+
| implement | --> | ship | --> | eval |
+-----^------+ +--^---+ +----|----+
| | |
| +-- pass: fresh cycle (new sessions)
| |
+-- fail: nudge <--------------+ repeats until Ctrl-Copencode versions (v1 / v2)
aceit currently targets opencode v1. Version handling is built in so adding v2 later won't break anything:
- Every command resolves the target major via:
--oc-versionflag → repo config (.aceit/config.json→"opencodeVersion": 1|2) → default v1. aceit runprints which one it's using (opencode server: … (v1, source: repo …)).- Unsupported majors fail fast with a clear message instead of misbehaving.
aceit setupreports the detected major of your installed CLI.
Per-repo pinning means one project can stay on v1 while another moves to v2 (once supported).
Requirements
- Node.js >= 20
Install
npm i -g aceit
aceit setup # installs the opencode CLI if it's missing (asks first)aceit up also offers the install automatically when opencode isn't found.
Quickstart
Go into your project folder and run the wizard. That's it:
cd my-project
aceit newThe wizard guides you through everything and always tells you your exact next
steps. In an empty folder it creates scaffolded prompt files for you (with
guidance inside); in a folder that already has *.md prompts, it picks them up.
The short version of what it will tell you:
aceit run --dry-run # 1. preview the plan (optional)
aceit run # 2. start the loop — Ctrl-C pauses, rerun resumesNon-interactive (agents, scripts): every wizard answer is also a flag —
no TTY needed. With any flag present, aceit new skips the wizard entirely:
aceit new -n my-loop -g "Ship feature X" \
-s implement \
-e eval --on-fail implement -m 5 \
--model anthropic/claude-sonnet-4The same capability is exposed to coding agents via the MCP tool
workflow_create (aceit mcp).
Typical output:
Run 20260823-141502-k3f1 — workflow 'my-project-workflow' (2 steps)
▶ [1/2] implement …
✓ passed (134s)
▶ [2/2] eval …
eval verdict: fail (4/10)
✗ FAIL → looping back to 'implement' (iteration 1/5)
▶ [1/2] implement (attempt 2) …
✓ passed (98s)
▶ [2/2] eval …
eval verdict: pass (9/10)
✔ Workflow completedThe workflow file
Each workflow is a self-contained bundle under .aceit/<workflow-name>/ —
aceit new generates it; you can also write it by hand:
my-project/
└── .aceit/
└── fix-flaky-test/ # one folder per workflow
├── workflow.yaml
└── prompts/
├── implement.md
└── eval.mdname: fix-flaky-test
goal: Make the test suite pass reliably. # optional context prepended to every step
vars: # optional, usable as {{vars.name}}
area: checkout
defaults:
model: anthropic/claude-sonnet-4 # fallback model
permissions: auto # auto | ask | deny
timeout: 1800 # per-step seconds
max_retries: 2 # transient-error retries
max_iterations: 5 # eval-loop safety stop
steps:
- name: implement
prompt_file: prompts/implement.md # relative to this workflow's bundle folder (.aceit/<name>/)
# agent: orchestrator # run as an opencode agent (.opencode/agents/*.md)
# model: openai/gpt-5.1 # hard override (wins over agent's own model)
# inherit_context: false # opt out of receiving previous step output
timeout: 1200
max_retries: 2
- name: eval
type: eval
prompt_file: prompts/eval.md
on_fail: implement # which step to loop back to
on_pass: loop # loop | stop (default) — keep cycling even when eval passes
max_iterations: 5Cross-step context: context_from
Any step can pull the latest output of other steps into its prompt — including from the previous cycle (e.g. an implement step that wants the evaluator's last verdict):
steps:
- name: implement
prompt_file: prompts/implement.md
context_from: [eval] # latest eval output, even from last cycle
- name: review
prompt_file: prompts/review.md
context_from: [implement, ship] # multiple sources, deduplicatedEach source becomes a truncated ## Output of <step> block (capped, max 6).
aceit stores only a pointer per step in state.json (latestOutputs) and
reuses the log files it already writes — no duplicated disk usage. Cycle
resets deliberately keep these pointers; a brand-new cycle starts fresh
sessions but can still read what happened before.
Never stop, even on pass
Two ways to run in "endless polish" mode — every pass starts a fresh cycle:```yaml
per-workflow: the eval step never terminates the run
steps:
- name: eval type: eval on_fail: implement on_pass: loop
```bash
# or per-invocation: forces loop-on-pass for every eval in the workflow
aceit run --foreverIn both modes only Ctrl-C ends the run (status interrupted, fully resumable).
aceit status shows which cycle you're in (cycles: N).
Model precedence
| Priority | Source |
|---|---|
| 1 | model: on the step |
| 2 | the step's agent md frontmatter (model: in .opencode/agents/*.md) — only when no step model: is set |
| 3 | defaults.model — only applied when the step has no agent |
| 4 | opencode global default |
Rule of thumb: if you assign an agent, let the agent own its model.
How evaluation works
Eval steps get a rubric appended to their prompt requiring a fenced JSON verdict:
{ "verdict": "pass", "score": 9, "feedback": "…", "blocking_issues": [] }Permissions (unattended runs vs human-in-the-loop)
| mode | behavior |
|---|---|
| auto (default) | permission requests are approved automatically — fully hands-off |
| ask | aceit pauses and shows the request in your terminal: [a] allow [n] deny, with a timeout that denies and leaves the run resumable |
| deny | requests are rejected immediately; good for read-only steps like evaluators |
Fine-grained rules (e.g. allow git diff, ask for git push) belong in your
agent md files / opencode.json — aceit only picks the coarse mode and
forwards opencode's decisions.
Multi-agent workflows
Steps can run as any primary agent defined in .opencode/agents/*.md
via the agent: key. Inside such a step, opencode's native task tool can
delegate to subagents — including parallel workers on cheaper models.
A common pattern is a first "setup" step whose prompt writes the worker agent
files themselves; subsequent fresh sessions pick them up automatically.
See examples/02-orchestrator-agents.
Agent names are validated whenever you create or edit a step through aceit
(CLI steps/new, MCP tools, GUI): the name must exist as a markdown agent
in .opencode/agents/ (project) or ~/.config/opencode/agents/ (global), in
the "agent" map of opencode.json, or be an opencode built-in (build,
plan, general, ...). Hand-edited yaml files skip this check — opencode
itself will complain at runtime if the agent doesn't exist.
Parallelism guidance: parallel workers inside one step editing the same files will conflict. Parallelize research/review-style delegation; keep file-editing scopes disjoint or sequential. (DAG-level parallel branches are planned for v2.)
Resume & exit codes
State lives in .aceit/runs/<run-id>/state.json, written atomically after
every step. Full step outputs land in .aceit/runs/<run-id>/logs/.
- Re-running
aceit rundetects unfinished runs and offers resume (passed steps are skipped; failed/interrupted/pending ones re-run).--freshforces a new run,--resumeskips the question. - True retry via snapshots (
git initrequired): every step attempt pins the exact working tree as an invisible git object.aceit retrylists those points (any step, any cycle), restores the chosen world + run state, and execution resumes from there — judging what existed then, not now. Refuses on tree drift without--force; never touches your branch or HEAD. Ctrl-Cmarks the current stepinterrupted, aborts its session, saves state, exit code130.- Exit codes:
0completed ·1failed ·130interrupted-resumable ·2usage/validation error.
Telegram notifications
Get pinged on your phone when runs fail, stall, or finish — configured per repo, scoped per workflow:
aceit notify setup # wizard: pick a channel, pair it, choose events
aceit notify status # what's on/off, per workflow
aceit notify test # one sample message
aceit notify off # interactive: whole repo or picked workflows
aceit notify on # same, to re-enableNon-interactive (agents, scripts): setup takes flags instead of the wizard:
# relay: first call prints a pairing link; rerun with --relay-key after pairing
aceit notify setup --mode relay
aceit notify setup --mode relay --relay-key <key>
# direct: token from @BotFather; --wait-seconds captures your chat id
# automatically once you press START on the bot and send any message
aceit notify setup --mode direct --bot-token <token> --chat-id <id>
aceit notify setup --mode direct --bot-token <token> --wait-seconds 90
# event toggles work in both worlds
aceit notify setup --mode relay ... --event step_passed=off --event run_started=offAgents get the same powers over MCP via notify_status, notify_configure,
notify_toggle and notify_test — so a coding agent can walk you through the
choice (direct vs relay), hand you the pairing link, and wire everything up.
Credentials never appear in tool output, and telegram.json is auto-gitignored
on every save.
Two ways to connect:
- direct — your own bot from
@BotFather; the token stays in.aceit/telegram.jsonon your machine (auto-added to.gitignore, written with 0600 permissions). Optional HTTP proxy support — setup validates it with a real request and tells you exactly why a bad proxy failed. - relay — one shared bot (
@AceitRelayBot) behind a Cloudflare Worker (deployed already athttps://relay.aceit-68f.workers.dev— the default). Machines hold only a random relay key; the bot token never leaves Cloudflare. Pairing is one tap:https://t.me/<bot>?start=<KEY>or typing/pair <KEY>. Moved servers later?aceit notify url <new>re-points any repo and verifies with a test message.
Events are an interactive checklist; defaults stay quiet on success-noise and
loud on failures (step_failed, eval_fail, run_failed, interruptions,
watchdog/provider errors). Messages are HTML-formatted with severity emojis
and always carry the repo name, workflow name, run id, cycle and durations.
Evaluator feedback excerpts can be included in eval-fail pings (toggle during
setup).
Interruption coverage includes SIGHUP (tmux kill / SSH disconnect) with a
bounded delivery window before exit, and hard deaths (SIGKILL, power loss) are
detected retroactively on the next resume (run found dead).
Watching your own sessions
The easy way: aceit up
One command replaces bare opencode and gives you the TUI plus a built-in
watchdog that auto-nudges any session stopping mid-work:
aceit up # starts server + your TUI + watchdog, cleans up on exit
aceit up -c # same, but continue the last conversation
aceit up -s <session-id> # open a specific sessionExtra args after up are passed as-is to the opencode TUI (--continue,
-s <id>, --fork, …). Timestamped watchdog output interleaves with your
normal workflow — nudges, errors, done detection (--done-text optional).
Nudges carry a clean continuation prompt; diagnostic reasons are printed in
the console only. No second terminal needed.
Manual setup: aceit watch
aceit watch must talk to the server that owns the session (two servers on
one session would conflict), so it requires --attach. Without it you get the
full step-by-step recipe printed instead of a confusing failure:
opencode serve --port 4096 # terminal 1: host
opencode attach http://localhost:4096 # terminal 2: your TUI
aceit watch <sessionId> --attach http://localhost:4096 # terminal 3: watchdogWatchdog output:
[14:03:22] watching ses_abc123 (status: busy)
[14:05:10] idle detected — last turn completed cleanly · error: ApiError
[14:05:11] >> nudging (1): "You stopped before finishing. Continue working towa…"
[14:11:48] DONE — session reports finished (matched "TASK COMPLETE")Optional: --done-text "TASK COMPLETE" exits the watcher when your agent's
message contains the phrase; --max N; -p "custom continuation text".
Why no auto-discovery? A plain
opencodeTUI picks a random port and doesn't expose an HTTP server by default, and nothing is written to disk to find it later (#8948 proposes a registry).aceit upsidesteps the whole problem by owning the server.
Sessions, cycles and context
Mental model: one conversation per step per cycle.
cycle N: implement ──► ship ──► eval ── PASS ──► new cycle
(transient retries stay in the SAME session;
eval-FAIL loop-backs and cycle boundaries start FRESH sessions)- Session reuse happens in exactly two cases: transient connection errors (the turn keeps running server-side — we watch, then harvest or nudge), and resumes after Ctrl-C / kills (the interrupted step continues its session).
- Fresh sessions on: moving to the next step, eval-fail loop-backs (the evaluator's feedback travels inside the new prompt text), and cycle boundaries.
inherit_context: true(default): prepends the immediately preceding step's output from the current cycle as one truncated block.context_from: [a, b]: opt-in bridge across cycles - pulls the latest output of any named step(s), even from the previous cycle, into the prompt.
Managing steps interactively
aceit steps # interactive manager for the (only) workflow
aceit steps add polish # scaffold + insert (defaults, prompts created)
aceit steps edit ship --set model=openai/gpt-x timeout=90 context_from=implement
aceit steps move eval --to after:ship
aceit steps rename-less # 'edit <step> --rename <new>' rewrites all references
aceit steps delete lintEvery mutation is validated through aceit's own workflow loader and rolled back on any error — you can never save a broken yaml. All actions work as plain flags too, so agents without a TTY get full parity.
Mission-control GUI
aceit gui # http://localhost:4603 (this machine)
aceit gui --open # same, opens your browser
aceit gui --host 0.0.0.0 --auth user:pass # remote hosting (auth REQUIRED)A space-themed live dashboard: drag-and-drop step graph (boxes + animated
flow edges), click a node to edit its settings, edit prompt.md files inline,
watch the cycle counter and per-step statuses update live, tail step logs,
inspect the session tree (main + delegated subagents) and SAY to any of
them mid-run, browse snapshots and time-travel with one click, manage
Telegram notification toggles, run safe cleanup previews — plus create new
workflows from scratch.
Live updates arrive over SSE every second; MCP-driven changes show up in the GUI automatically. Remote hosting enforces HTTP basic auth on all routes (including the event stream).
MCP — let your agent drive aceit
Opt-in Model Context Protocol server so coding agents can do everything this README describes, themselves:
aceit mcp --print-config # paste the fragment into opencode.json (or add manually):
# { "mcp": { "aceit": { "type": "local", "command": ["aceit", "mcp"], "enabled": true } } }Once registered the agent gets tools for: listing/reading workflows, editing
steps (add/edit/move/link-context/delete — validated with rollback),
starting/stopping runs, listing runs, listing snapshots and true time-travel
retry (with all live-run collision and drift guards), plus an aceit_help
guide covering concepts and safety rules. The server speaks stdio; stdout is
reserved for protocol traffic.
Cleaning up
aceit cleanup # removes runs/, registries — keeps workflows & config
aceit cleanup --all -y # also removes your workflow bundles + notify config
aceit cleanup --dry-run # exact preview firstScoped strictly to .aceit/ — code, commits and changes are never touched.
Refuses while a runner is alive. To remove the CLI itself: npm -g rm aceit.
Note: retry snapshots live inside .aceit/runs/<run>/snaps/, so cleanup
removes time-travel history as well.
FAQ
Q: aceit new scaffolded prompt files — now what?
Open each file it listed and replace the guidance with your real instructions. aceit refuses to start (prompt file ... is EMPTY) until they contain something, so you can never accidentally loop on an empty prompt.
Q: How does aceit know a turn actually finished (and succeeded)?
Two mechanisms, matching opencode #3075: prompts aceit sends are synchronous — when session.prompt returns, the turn is done, and its success is read from the response's error field. Sessions we only watch use the dedicated session.idle event plus a debounce on raw status-idle (which can blip between tool calls), then confirm via the last assistant message's time.completed timestamp and error field.
Q: Can aceit babysit a session I'm running manually?
Yes — that's aceit watch <sessionId>. See Watching your own sessions.
Q: Does the loop stop when the evaluator says pass?
By default yes — a pass completes the run (exit 0). Add on_pass: loop to the eval step or run with --forever to keep starting fresh cycles; only Ctrl-C stops it.
Q: The loop is still running — can I check its progress?
Yes. State is written to disk atomically after every step, so aceit status in another terminal always shows live progress: which step is running, attempts, iteration/cycle counts. aceit runs lists all runs. For raw output, aceit logs -f tails the active step's transcript and auto-switches as steps change.
Q: Can I watch what the agent is doing right now?
Yes — and it's zero-config. Every aceit run spawns its own opencode server,
prints its URL, registers it in .aceit/, and cleans it up on exit/interrupt.
All other commands auto-discover it:
aceit run # prints: "opencode server: http://127.0.0.1:PORT …"
aceit logs -f # live transcript tail (terminal 2)
aceit open # LIVE TUI mirror of the active step
aceit status # progress tablePrefer a server you manage yourself? aceit run --attach http://localhost:4096
after opencode serve --port 4096 works exactly the same.
Q: What happens if I run aceit run while a workflow is already running?
aceit never double-starts. You'll get:
Workflow 'game-polish-loop' is already running (20260824-011135-suiy, pid 12345).
aceit open # watch it live & prompt into it
aceit logs -f # tail its transcript
aceit kill # stop it (resume later)With multiple workflows, aceit run shows an interactive picker marked with
● running / ○ idle — selecting a running one shows the same friendly info
instead of starting a duplicate.
Q: Multiple runs at the same time?
Each run gets its own isolated server + state folder. aceit open <runId>,
aceit say --run-id <runId>, and aceit watch route to the right server
automatically; bare aceit open/say/watch target the most recent one.
Q: Can I inject a prompt into a running step?
aceit say "Also add unit tests for the save button."Auto-routes to the owning run's server (or pass --attach/--run-id).
Avoid typing into an aceit open snapshot window started without a live
server behind it — that writes through the wrong process.
Safety notes:
- Pressing Ctrl-C inside an attached TUI only closes your view — the loop (owned by its server) keeps running.
- Every spawned server is killed and unregistered when its run exits, is interrupted, or crashes — no orphan processes.
- Injection rules of thumb: touching a live run → just run the command from the same repo (auto-discovery). Targeting a remote/foreign server → pass
--attach. Old/finished sessions → standalone works.
Q: Can I have multiple runs in the same repo?
Yes — every run gets its own isolated folder under .aceit/runs/<run-id>/, and history is kept until you aceit clean. You can also keep several different workflows side by side (aceit run -w other.yaml). One caveat: launching two simultaneous aceit run processes for the same workflow isn't supported yet — both would try to resume the same state file. Run them sequentially or use separate workflows.
Q: What happens if I Ctrl-C mid-run?
The current step is marked interrupted, its session aborted, state saved; exit code 130. Re-running aceit run offers resume — passed steps are skipped, work continues from where you stopped.
Q: How do I start completely over?
aceit run --fresh. Previous run folders remain on disk until aceit clean.
Q: What do the exit codes mean?
0 completed · 1 failed (eval exhausted max_iterations or a step hard-failed) · 130 interrupted-resumable · 2 usage/validation error.
Q: Where do step outputs and logs go?
Full output of every attempt: .aceit/runs/<run-id>/logs/<step>.attempt-<n>.md. Machine-readable progress: .aceit/runs/<run-id>/state.json.
Q: Can implement and eval use different models or agents?
Yes — per-step model: overrides everything, otherwise an assigned agent: uses its own md config; defaults.model applies only to agent-less steps. See Model precedence.
Q: Can steps delegate to opencode subagents / workers?
Yes — that's native opencode behavior inside any step. Assign a primary agent via agent: and have it delegate via the task tool; see examples/02-orchestrator-agents.
Q: What if the evaluator replies garbage instead of the verdict JSON? It's retried as a hard failure of that eval execution — the run fails fast rather than looping on noise. Fix by making your eval prompt demand the fenced JSON block (the rubric aceit appends already does).
Q: Does it work headless (CI, no human at the keyboard)?
Yes — permissions: auto never asks. Avoid permissions: ask in CI; unanswered requests auto-deny after the timeout and leave the run resumable.
Q: When a step retries, does it continue the same conversation? Transient connection errors: yes — the turn keeps running server-side, so we watch it and harvest/nudge in the same session. Eval-fail loop-backs: no — the target step opens a fresh session, and the evaluator's feedback is embedded directly in its new prompt (nothing important lives only in old conversation history). Interrupted runs also resume their existing step sessions.
CLI reference
aceit new [options] Interactive wizard → .aceit/<workflow-name>/ bundle
-n, --name <name> Workflow name
-g, --goal <text> One-line goal prepended to every step
-s, --step <spec> Action step: 'name' or 'name:prompts/file.md' (repeatable)
-e, --eval [spec] Evaluator step ('name' or 'name:prompts/eval.md')
--on-fail <step> Eval loop-back target (default: last action step)
-m, --max-iterations <n> Max eval iterations before giving up
-p, --permissions <mode> auto | ask | deny
--model <provider/model> Default model for agent-less steps
--agent <name> Primary agent for the first step
aceit run [options]
-w, --workflow <path> Workflow file (default: the only one in .aceit/, else prompted)
--resume Resume latest unfinished run
--fresh Ignore unfinished runs, start new
--forever Never stop on eval pass; fresh cycle each pass (Ctrl-C to pause)
--dry-run Validate + print plan, run nothing
--json Machine-readable summary at the end
--attach <url> Use a running server (opencode serve) instead of spawning one
aceit status [runId] Step-by-step status of latest/given run
aceit open [runId] Attach an opencode TUI to a step's session
-s, --step <name> Open a specific step's session (default: active step)
-l, --list List steps + full session ids, then exit
--attach <url> Live mirror via shared server (pair with aceit run --attach)
aceit logs [runId] Print the active/latest step's output
-s, --step <name> Show a specific step's log
-f, --follow Tail live; auto-switches to the next step
aceit say [words...] Inject a prompt into the active/latest session
ses_xxx <words...> Optionally target a specific session id
--run-id <id> / -s <step> Target a specific run or step's session
aceit setup [-y] Install the opencode CLI if missing
aceit up Server + your TUI + auto-nudge watchdog, one command
aceit watch <sessionId> Nudge a session via --attach (refuses without it)
-p, --prompt <text> Nudge text sent each time the session stops
--done-text <phrase> Exit happily once a message contains this phrase
--max <n> Stop after N nudges (0 = unlimited, default)
-i, --interval <sec> Minimum seconds between nudges (default 5)
--attach <url> Attach to an already-running opencode server
aceit retry [at] Time-travel: restore a step-start snapshot, resume from there
-w, --workflow <name> Workflow whose run to retry
--force Restore even if the tree drifted since the newest snapshot
aceit ps Session tree of the active step: main + delegated subagents (live status)
-w, --workflow <name> Inspect a specific workflow
-s, --step <name> Show a specific step's tree
--session <id> --attach <url> Inspect ANY opencode session - even from a plain TUI, no aceit needed
--json Machine-readable output
aceit mcp Opt-in MCP server (stdio) for agent-driven aceit
aceit gui Live mission-control GUI (graph editor, logs, snapshots, sessions)
-p, --port <n> Port (default 4603)
--host <h> Bind host (remote requires --auth)
--auth user:pass HTTP basic auth for remote hosting
-o, --open Open the browser automatically
aceit runs List all runs
aceit notify [action] Telegram notifications: setup | status | test | on | off | url
-w, --workflow <name> Scope on/off to a single workflow
-a, --all Scope on/off to the entire repo
--json Machine-readable status output
setup flags (non-TTY): --mode direct|relay --bot-token --chat-id --wait-seconds
--relay-url --relay-key --relay-chat-id --event name=on|off
--include-eval-excerpt --no-verify
aceit kill [runId] Stop a live loop — interactive picker when several are running (● markers)
-a, --all Kill every live run
-y, --yes Skip confirmation
aceit clean [--all] [-y] Delete run stateExamples
| Example | Shows |
|---|---|
| 01-simple-eval-loop | fix-a-bug loop with evaluator feedback injection |
| 02-orchestrator-agents | orchestrator + parallel worker agents, self-created agent files, mixed models |
| 03-docs-pipeline | plain multi-step pipeline, read-only permissions, no evaluator |
Development
npm install
npm test # build + unit tests (57 tests: template, config, state, evaluator, executor)
npm run dev -- run --dry-runThe opencode client interface is abstracted, so the whole engine is tested against a fake client without spawning opencode.
Roadmap (v2)
- DAG-level parallel branches with fan-in/fan-out and multiple evaluators
- Optional git snapshot per iteration (revert bad attempts)
- Global cost/token ceilings
- Shared-session mode for context continuity across steps
Contributing from source
git clone https://github.com/SC0d3r/aceit && cd aceit
npm install
npm run build
npm link # local dev version of the CLI
npm test # unit tests (no opencode needed — engine runs against a fake client)PRs welcome. Keep the engine's opencode transport behind the thin
src/opencode.ts wrapper so tests stay hermetic.
License
MIT — see LICENSE.
