@bbk1ng/agent-orch
v0.4.200
Published
Run local coding agents in a cross-audit loop on any git repo — author, cross-audit, test-gate, merge. Educational; not for production.
Downloads
1,818
Maintainers
Readme
agent-orch
[!WARNING] License Notice:
agent-orchis a Source-Available, Non-Commercial project, licensed under PolyForm Noncommercial 1.0.0 for educational, research, and other non-commercial use. Commercial use is forbidden.
🚨 Autonomous Agent Liability Disclaimer
orch operates via headless, autonomous agent loops designed to self-heal, modify codebase files, generate Pull Requests, and resolve issues dynamically. By running this software, you acknowledge that agents execute actions autonomously. The project creators accept zero liability for token expenses, infinite API loops, accidental data loss, or system vulnerabilities generated by autonomous actions. Always run inside a sandboxed environment.
Run local coding agents (Claude, Codex, Copilot, Gemini, Grok, agy) in a cross-audit loop on any git repo.
One or more agents author a small change; another (or several) audit it; on agreement + green tests it
merges into a local orch/integration branch (and opens/updates a persistent PR
to main — GitHub owns the main merge); on disagreement it revises (capped);
on stalemate it asks you. All compute is local.
Requirements
- Node ≥ 18. Runs on Linux, macOS, and Windows (CI gates every change on a
windows-latestleg alongsideubuntu-latest). - At least one agent CLI installed:
claude,codex,copilot,gemini,grok,agy, orccr(for local-llm models). PATH is preferred, but if the caller's PATH is degraded (wrappers, cron, hooks often drop~/.local/bin), preflight also probes~/.local/binand the running node's own bin dir (coversnpm i -g/ nvm) and spawns the CLI by absolute path.
Install
The CLI is exposed as orch:
npm install -g @bbk1ng/agent-orch
orch <command>Or from source:
git clone https://github.com/bbk1ng/agent-orch.git
cd agent-orch
npm install -g . # puts `orch` on your PATHPrefer not to install globally? Run the CLI in place with node bin/orch.js <command>
from the cloned checkout.
Install/update also writes bash completion to ~/.orch/completion.bash; enable it
with source ~/.orch/completion.bash, or print the script with orch completion bash.
Usage
Run from inside the git repo you want orchestrated:
orch init # scaffold .orch/orch.yml + .orch/ORCH.md, verify agent CLIs
orch init --link # also wire .orch/ORCH.md into CLAUDE.md/AGENTS.md/GEMINI.md
orch config # interactive wizard to create/edit .orch/orch.yml
orch task "fix the flaky login test" # author + cross-audit + test-gate + merge
orch task "fix x" --authors claude,codex --reviewers claude,codex
orch task --file wo.json # untrusted JSON work order (validated + fenced)
orch issue 42 # fetch issue #42 as a work order, run the cycle, Closes #42
orch review pr/claude/some-branch # audit an existing branch (no authoring)
orch pr 42 # audit a GitHub PR, post verdict as a comment
orch pr 42 --merge # ...and merge it via gh if agents approve
orch agent build mynewagent # scaffold a missing adapter via orch's own pipeline
orch continue <sid> # resume an interrupted/stalled cycle from its checkpoint
orch dashboard # live cycle status, log tail, run history, metricsAdd --dry to any task/review run to simulate a cycle without touching git,
agents, or tests. orch exits non-zero (2) when a cycle escalates for a human.
After a task run merges, orch tidies up for you: it pushes orch/integration
to GitHub and opens/updates its persistent PR to main, deletes the temporary
work branches it created, and prints a plain-English summary of what it did. Anything that
could lose work (e.g. a branch with unmerged commits) is explained and only removed
after you confirm [y/N]; with no terminal attached it is left untouched and noted.
Pass --no-tidy to skip this and leave every branch and checkout exactly as-is.
--file takes a JSON work order (untrusted intake — its free text is fenced,
never executed as instructions). title and problem are required; the arrays
may be empty:
{ "title": "fix the flaky login test",
"problem": "login test fails ~1 in 5 runs under load",
"repro_steps": ["run npm test 5x"],
"suspected_paths": ["src/auth.js"],
"acceptance_criteria": ["test passes 20x in a row"] }orch pr <n> needs the gh CLI authenticated — orch checks gh auth status
up front and fails fast with one clear error instead of letting a broken
session surface partway through a cycle. The same authenticated-gh check
also runs before orch task/orch issue whenever the repo has a remote,
since even the default no-ff path opens/updates the persistent integration
PR after a merge. It fetches the PR head, runs an
audit-only cycle (local main is never touched — GitHub owns the merge), and
posts the verdict as a PR comment. With --merge it runs gh pr merge, then
confirms the resulting merge commit is really an ancestor of origin/main
before reporting success — a gh exit code alone isn't proof the commit
landed, so a false "merged" is refused with an error instead of printed.
Agents
claude, codex, copilot, gemini, grok, agy, and three local-llm models served via llama-swap behind
claude-code-router (ccr): qwen3-coder-30b, deepseek-coder-v2-lite,
glm-4.5-air. Local models need ccr on PATH and ~/.claude-code-router/config.json
defining a local provider (see local-llm/configs/).
External testers: use the
claude/codex/copilot/gemini/grok/agyCLI agents.llama-swapis host-specific (the maintainer's LAN proxy) and not required — the CLI-agent fallback already works.
Shell completion
npm install -g installs bash completion automatically via a postinstall hook
(writes ~/.orch/completion.bash). Source it manually or generate it on demand:
orch completion install # write ~/.orch/completion.bash
source <(orch completion bash)Pick who authors and who audits explicitly in orch.yml:
author: qwen3-coder-30b # writes the change
reviewer: claude # audits itSet both or neither. Unset → the agents: list rotates author each cycle.
For parallel authors/reviewers, use plural lists:
authors: [claude, codex] # each writes a separate branch
reviewers: [claude, codex] # all audit each branch, except its authorCLI flags override orch.yml: --author/--reviewer or comma-separated
--authors/--reviewers. A role is a spec "<agent> [model] [effort]" — agent
required, model and effort optional. Valid effort values: minimal, low,
medium, high, xhigh, max (which ones a given agent CLI actually honors
depends on the agent — e.g. codex takes effort via a -c config override
rather than a flag).
Quickstart
cd your-repo
orch init
orch task "fix the flaky login test"Start a new project
repo-template is a self-bootstrapping template repository pre-wired for orch: a single curl invocation fetches the bootstrap script, clones the template, runs orch init --link, and gates on tests, giving you a working project scaffold with orch already integrated and ready to use.
Commands
orch init [--link]— scaffold.orch/orch.yml+ agent-agnostic.orch/ORCH.mdusage doc, verify agent CLIs, and print a detection summary of what's actually usable on this machine (claude/codex/copilot/gemini/grokresolvable via the same PATH + fallback-dir lookup preflight uses, local-llm models configured forccr'slocalprovider), e.g.orch: detected: claude, glm-4.5-air — not found: codex (CLI not found: PATH + fallback dirs).--linkappends an idempotent pointer to.orch/ORCH.mdin the repo'sCLAUDE.md/AGENTS.md/GEMINI.md(created for the configured agent if none exist). The@.orch/ORCH.mdline auto-loads the doc in Claude Code; other agents read the prose pointer to it.orch config— interactive wizard to create or edit.orch/orch.ymlfield by field, validating and normalizing (e.g. canonicalizing the legacymain.autoResolveConflictsalias tomain.conflictResolution) before writing.orch agent add <name>— append a registered agent to.orch/orch.yml.orch task "..."— author + cross-audit + test-gate + merge.orch issue <n>— fetch GitHub issue #n (title+body, treated as an untrusted work order), run the full cycle, and stampCloses #non the no-ff merge so it auto-closes once main reaches origin. Needs theghCLI authenticated. If the cycle escalates or falls back to a PR instead of merging, orch posts a comment on the source issue (verdict, branch, reason, round count) — headless runs have no one watching stdout, so this is the only trace besides the local.orch/reviews/<branch>/DECISION.md.orch review <branch>— audit an existing branch.orch pr <n> [--merge]— audit a GitHub PR, comment the verdict, optionally merge viagh.orch agent build <name> [--pr]— an unregistered agent name scaffoldssrc/adapters/<name>.jsthrough orch's own author → audit → test pipeline, isolated in its own worktree/branch. Default lands on that local branch only;--propens a PR instead.orch continue <sid>— resume an interrupted or stalled cycle (crash, hard kill, usage-limit abort) from its checkpoint, reattaching the same branch/author instead of re-authoring from scratch. If the saved branch is gone, stale local resume state is cleared; if it exists only asorigin/<branch>, check it out locally first.orchtells you thesidto use when a cycle dies mid-way.orch dashboard [--json] [--limit N] [--check-history] [--once|--plain] [--refresh-ms N]— read-only view of live cycle status/stage, streaming log tail, run history, and success-rate metrics. In a real interactive terminal (stdout and stdin both a TTY) with no--json/--once/--plain, it opens a live full-screen TUI that polls and redraws every--refresh-ms(default1000). Any scriptable context —--json,--once/--plain, a piped/redirected stream, or a non-TTY session — instead prints the static one-shot render, byte-identical to earlier versions, so cron/CI output stays diffable.--check-historyshows stale red history rows as resolved when their branches are gone — a view-only reconciliation that leaves the run-history file unchanged.orch completion [bash]/orch completion install— print the bash completion script or rewrite~/.orch/completion.bash. Add--reviewer nameor--reviewers a,bto override review agents fortask/issue/review/prruns. Ontask/issue/review, setting only--reviewer(s)(no--author(s)) is allowed and forces that reviewer while still rotating the author from theagents:pool — the one asymmetric case; setting only--author(s)without a reviewer is rejected the same as before.
Other flags: --cheap forces orch.yml's cheap.role (e.g. a local llm) for one
task/issue run — set cheap.paths to auto-route matching --file/orch issue
work orders without the flag; --config-file <path.yml> layers a custom YAML file
on top of orch.yml for one run; --no-banner suppresses the startup banner.
Config (.orch/orch.yml, all optional)
See orch.example.yml. Most repos need no config. A bare orch.yml at the
repo root is still read for back-compat, but .orch/orch.yml wins if both exist.
The trunk orch reads from, diffs against, and opens PRs to is baseBranch
(default main); set it to e.g. dev if main is deploy-only. Everywhere
below that says main, read "your baseBranch".
How it decides to merge
Merge happens only when every reviewer says AGREE and the repo's tests pass.
No test command detected → it refuses to auto-merge and tells you.
There's a third, independent gate the LLM reviewers can't talk their way past:
immediately before every merge attempt — the normal local-integration path and
the orch pr/PR-bridge no-merge approval path alike — orch reads the full
base→branch diff and runs it through a deterministic pattern scanner
(scanDiff in src/security-review.js) that flags added lines reading
secrets/env (process.env, .ssh/, PRIVATE KEY, ...), opening a network
connection, spawning a subprocess, or touching branch-protection/workflow
files. Any finding escalates without merging, even if every reviewer already
said AGREE and tests are green — an LLM reviewer can be prompted into
approving something it shouldn't, but this check can't be argued with. If the
diff itself can't be read, orch fails closed (escalates) rather than assuming
the unseen patch is safe.
Merge honesty and cost/catch-rate reporting
Prompted by a red-team report that found orch could
print merged for a cycle whose commit never reached origin/main:
- Verified merge claims. Before printing
merged, orch checks the merged commit is actually an ancestor oforigin/main(verifyOriginContainsinsrc/git.js). If the push didn't take,finalizereports the real, local-only outcome instead of a false success — it no longer claims a merge that didn't happen.orch pr --mergegets the same treatment on its own path: aftergh pr mergeexits 0, it re-fetchesorigin/mainand confirms the merge commitghreports is really an ancestor before loggingmerged ... verified on origin/main, since a squash/rebase merge mints a new SHA that a bare exit-code check can't vouch for. - Per-cycle cost. Every cycle's summary line includes
; cost <usageSummary>— the token/$ estimate for that cycle's author + review rounds — so cost is visible next to the verdict, not just in aggregate run stats. - Review outcome log. Every review round is appended to
.orch/review-outcomes.jsonl(src/review-log.js) withdecisionone ofAGREE,DISAGREE(editorial rejection), orERROR(reviewer crash/stall — not a code rejection). This is the raw data needed to eventually measure whether cross-audit catches real defects (reviewer catch-rate) — unmeasured today, called out as the project's central unproven assumption.
Crash recovery
A killed orch task can leave worktrees under .orch/wt. Before starting, a run
sweeps those orphans. The sweep is PID-aware: each worktree carries an ownership
marker (pid\nsid); only worktrees whose owner process is dead are removed, so a
live peer's worktree is never disturbed. The branch is deleted only when an
orch-created marker is present and it carries no commits beyond main (a
killed-before-commit throwaway) — so a same-slug retry works, while a branch with a
committed author result is kept for resume (see below) and a branch you handed to
orch review is always preserved. --dry never deletes worktrees or branches.
orch pr is still serialized by .orch/lock (one at a time per repo dir); the same
PID-liveness logic lets a crashed orch pr be reclaimed on the next run.
Resume after an abort or hard kill. When an orch task cycle dies after the
author has committed — a usage-limit abort (issue #24) or a hard process kill /
SIGKILL between the commit and review (issue #27) — the committed work survives on its
branch, and the run is recorded in .orch/resume/ keyed on the task text (with the
author). The next run with the same task reattaches that branch and continues from
the committed work (audit → gate → merge) instead of re-authoring from scratch — so
harness/orch-loop.sh resumes rather than restarts. Resume is independent of how the
run died and of author rotation: if the rotation pool has since advanced to a
different agent, the resuming run pins the surviving branch's original author rather
than authoring fresh under the next one. It restarts cleanly only if the run died
before any commit (nothing to resume), and never hijacks a live peer's branch.
Within a resumed cycle, a per-round checkpoint in .orch/checkpoints/ (keyed on the
run's sid) goes further: it records each review round's verdict, and whether the test
gate has already passed. A crash mid-review or between a green gate and merge doesn't
re-audit rounds already decided or re-run tests that already passed — the resumed
cycle picks up at the next undone step. The checkpoint is cleared once the cycle
reaches a terminal status.
orch continue <sid> also cleans up stale local resume state when the saved
branch no longer exists. If the branch still exists only on origin/<branch>,
orch refuses to guess and asks you to check it out locally before continuing.
Concurrent cycles
Multiple orch task runs can drive the same repo directory in parallel. Launch
each with explicit author and reviewer roles so they don't round-robin into each
other:
orch task "migrate auth module" --authors claude --reviewers codex &
orch task "add rate-limit header" --authors codex --reviewers claude &Concurrency cap. Set concurrency: 4 in .orch/orch.yml (default 4). An
over-cap launch exits immediately with code 2 and logs:
orch: concurrency cap 4 reached — 5 cycles live; skipping pr/claude/<slug>-<sid>Reduce the cap or wait for a live cycle to finish, then rerun.
Launching from main. main is no longer reserved by orch. You can keep
your primary checkout on main; orch's permanent worktree checks out the
dedicated orch/integration branch inside .orch/integration instead.
(main here is the default baseBranch; a repo with a dev trunk sets
baseBranch: dev and everything below tracks that branch instead.)
Uncommitted changes stay in your cwd; orch never stashes, resets, or discards
them.
Two-speed merge path. When all reviewers agree and tests pass, the cycle
merges into orch/integration through .orch/integration under a brief
merge.lock. That branch is immediately usable locally after the post-merge
test gate (and the version bump, if release.autoBump is enabled). Orch then
pushes orch/integration and opens or
updates one persistent PR from orch/integration to main; with
github.autoMergePr: true, it also enables GitHub's native auto-merge on that
PR using a merge commit so orch/integration stays in main's ancestry.
Alternatively, main.autoMerge: true triggers a direct merge of that
persistent PR once all of its checks are green — a fallback for
when native auto-merge stalls at BLOCKED because review is satisfied via a
ruleset bypass grant rather than a human approval. The merge runs as whatever
gh identity orch is authenticated as, so it only lands if that identity is
itself in the branch's ruleset bypass_actors list. This is necessary because GitHub
does not allow an actor to approve its own PR, so an orch-authored PR cannot
satisfy a required-review rule with an approval from the same bot. If you use
this path, the GitHub review is bypassed by ruleset configuration; orch's
author -> cross-audit -> test-gate remains the governing review. Without the
bypass grant, the merge call simply fails and retries next cycle.
main is a mirror of GitHub's main: orch does not merge, reset, commit, or
push it directly. After GitHub merges the PR, local main advances only by
fetching origin and fast-forwarding to origin/main.
Two guards run while the lock is held: a file-overlap pre-check (comparing your
changed paths against live in-flight peers' paths — commits already landed on
orch/integration are not pre-checked; a real conflict with them fails the
merge itself, and a semantic conflict fails the post-merge re-test) and a
post-merge re-test against the integrated tree.
PR-fallback. A cycle demotes to a PR (or local escalation) when:
overlap— your files collide with a live concurrent cycle's filesconflict— the merge itself failspost-merge-test-fail— tests fail after merge into.orch/integrationmerge-lock timeout— the lock was never acquiredmain-sync-failed— localmaincouldn't catch up toorigin/main
With a git remote and gh CLI available, the branch is pushed and a PR is
opened. Without them, .orch/reviews/<branch>/DECISION.md is written and the
branch is kept for manual review. Either way the reason is more than the
trigger name: it carries round count, the branch's base SHA (plus the
integration branch's tip once that worktree has been synced — still
"unknown" for merge-lock timeout and main-sync-failed, which fire before
that sync), the branch's changed paths, and trigger-specific detail
(overlapping paths per peer cycle, the conflicting paths, the sync failure,
or that the lock timed out) plus a one-line next action, so a human picking
up the escalation doesn't have to re-derive context orch already had.
merge: pr — per-cycle PR mode. Set merge: pr in .orch/orch.yml and an
agreed + green cycle skips the local integration branch and merge.lock; it
pushes that cycle branch and opens its own PR to main instead. This mode is
unchanged by the persistent orch/integration bridge. Needs a git remote and
the gh CLI; without them the cycle escalates locally the same way PR-fallback
does. Set github.autoMergePr: true to also enable GitHub's native auto-merge
on that PR (merged automatically once its own checks pass) — if enabling
auto-merge fails (e.g. branch protection isn't configured), the PR itself still
stands; only the auto-merge step is skipped.
Version bump on merge
agent-orch's own version, x.y.z, reads the patch field (z) as two
counters packed together: the last two digits are a merge-bump counter
(cc, 00-99, one per merge to main), and the digits above that are a
publish-bump counter — advanced only by an actual npm publish, which
also resets the merge counter to 00. "0.4.203" means: 2 publishes have happened (z=2) and 3 merges have landed since the most recent one (cc=03). It's ordinary decimal
arithmetic, not a custom encoding — a plain merge bump is just patch + 1;
...z99 + 1 naturally carries into (z+1)00. If the merge counter reaches
99 before a real publish happens, that carry happens on its own — expected,
not a bug: that many merges without a publish means a publish is overdue.
orch --version and the CLI banner display the version with a v prefix
(v0.4.203); package.json#version itself stays plain, valid semver with no
prefix, as npm requires.
With release.autoBump: true in .orch/orch.yml, every cycle that lands via
the local integration path (not merge: pr) bumps the merge counter right
after the post-merge test gate passes, mirrored into package-lock.json's
root version, and prepends a CHANGELOG.md entry. If the repo ships a
GitHub Pages site at docs/index.html, the version shown in its header (the
vX.Y.Z span) is rewritten to match — anchored to that one span so nothing
else moves. Commits as chore(release): vX.Y.Z. Best-effort: a
missing/unparsable package.json or any write/commit failure is swallowed —
it never blocks or unwinds a merge that already landed.
A separate CI workflow (.github/workflows/version-bump.yml) bumps the merge
counter for any merge to main that lands WITHOUT going through orch's
integration path — e.g. a direct-to-main PR — so every merge is traceable to
a version regardless of how it landed.
The publish counter never bumps automatically. Run node
scripts/orch-release.js by hand at actual release time — it snaps the patch
field to the next multiple of 100, commits, and tags, leaving the push and
the npm-publish.yml dispatch as separate deliberate steps.
release:
autoBump: true # off by default; orch never edits release files unless you opt inWithout the flag, a merge lands with no release-file edits at all — a generic orchestration run must not impose this repo's release policy (merge bump + CHANGELOG commit) on your repo by surprise.
Auto docs-update on merge
Opt-in per repo. With docs.autoUpdate: true in .orch/orch.yml, a successful
merge auto-spawns a detached orch task that refreshes documentation:
docs:
autoUpdate: true # off by default
prompt: "update documentation to reflect the latest merged changes"
paths: ["*.md", "docs/**", "**/*.md"] # docs-only globs = loop guardLoop guard: the trigger is skipped when the merged branch changed only docs
files (every path matches docs.paths) — so the docs-update's own docs-only
merge never re-triggers another one — and when the merge was a no-op (empty diff:
nothing to update, which would otherwise re-spawn forever). A mixed code+docs
merge triggers once.
Two surfaces, no double-fire:
- Local merges (
orch task/orch review) — handled insideorch(above). No GitHub event, so only this surface sees them. - GitHub PR merges (
orch pr --merge, GitHub UI) — handled by the.github/workflows/orch-docs.ymlAction (onpull_requestclosed+merged), which runs the same docs-update on the self-hostedorchrunner and pushes tomain. It applies the same docs-only loop guard.
Portability: the in-tool behavior ships inside orch — any standalone repo
gets it by setting docs.autoUpdate: true. The Action is a copy-paste template:
drop orch-docs.yml into another repo (e.g. printseek). It just needs orch on
the runner's PATH — the npm install -g . step assumes the repo vendors orch; a
repo that doesn't should install orch from an orch checkout instead.
License
PolyForm Noncommercial 1.0.0 — non-commercial use only; commercial use forbidden.
