ARCH is a multi-agent orchestration framework that automates the software development cycle
starting from a natural-language user story or task. ARCH coordinates two agents — Architect
and Worker — that drive headless instances of the Claude Code
CLI (claude -p) to plan, implement, and review work autonomously, directly on your own git
repository. A deterministic task-cycle orchestrator (worktree lifecycle, automated checks, retry
budgeting, merge safety) coordinates each task's execution around the single Worker dispatch call.
It ships with a CLI (archctl) and a terminal UI (arch-terminal) for launching and supervising runs.
Work is organized into runs. Each run moves through two phases:
-
definition— the Architect reads your prompt and the target repository, then produces a plan: a project brief (project.md) and a dependency graph (DAG) of tasks (tasks-index.yaml). You can approve the plan as-is, ask for changes (refine), or abort it. -
implementation— ARCH figures out which tasks are ready (no pending dependencies) and runs them with bounded concurrency (maxConcurrency). Each task goes through its own cycle:Worker implements the task in an isolated git worktree/branch → the orchestrator runs the task's automated checks (build, tests, lint — whatever the task defines) → Architect reviews the resulting diff → approved → merge into the base branch, commit, task done → rejected → correction feedback sent back to the Worker, up to maxRetriesIf a task exhausts its retries it is marked
failed, and every task that (transitively) depends on it is cascade-failed without ever being dispatched. A run can also be aborted at any point; in-flight agent calls are cancelled and their tasks are markedfailed.Whenever a task gives up — retries exhausted, or a crash into
failed/awaiting_human— the Architect gets one best-effort chance to turn the task brief, prior corrections, the Worker's diff, and the reason the deterministic rules gave up into a short, human-facing question with a recommended answer, surfaced as a message on its transcript. This is purely additive: it never changes the task's own outcome, and a human can still act directly viaarchctl retry-task.
All state for a run — the plan, per-agent Claude session ids (so work can be resumed), and task
metadata — is persisted under .arch/ inside the target repository, so a run can be inspected
or resumed at any time, even after the daemon restarts.
| Agent | Responsibility |
|---|---|
| Architect | Turns a prompt into a plan (definition phase) and, later, performs the semantic code review of each task's diff before it can be merged. Can also revise a plan on request (refine), and gets consulted for a human-facing question when a task gives up. |
| Worker | Implements one task inside its own git worktree, on its own branch, with a resumable Claude session so correction feedback can be applied incrementally. |
These are the only two LLM-backed roles in ARCH. Around each Worker dispatch, a deterministic
task-cycle orchestrator (packages/daemon/src/orchestrator/tl-loop.ts) — no model calls of its own
— handles worktree lifecycle, git mutex serialization, running the task's automated checks, scope
enforcement, retry budgeting, and protected-branch commit/merge safety.
Architect and Worker calls go through runClaudeHeadless (@losina/claude-runtime) — the single
integration point with the claude CLI, invoked with --print/headless flags and (when
available) --resume for the agent's existing session. Only one Architect review runs at a time
per run (protected by an in-process mutex), while multiple Workers can run concurrently, each in
its own worktree.
pending → ready → in_progress → in_review → done
↑ │
└─ needs_correction ─┘
(up to maxRetries, then → failed)
blocked → failed (cascade: any dependency of a failed task)
packages/
├── schemas/ # Shared Zod schemas: Task, RunPlan, RunMeta, CheckDefinition, RunSessions...
├── config/ # Loading/saving .agentmeshrc.json and resolving .arch/ paths
├── core/ # DAG helpers (topo-sort, ready-tasks), git (diff, worktrees), session/checkpoint state
├── agent-runtime/ # Provider-neutral agent progress normalization, shared by every *-runtime package
├── claude-runtime/ # Headless invocation of the `claude` CLI (execa) + model alias registry
├── codex-runtime/ # Headless invocation of the Codex CLI
├── opencode-runtime/ # Headless invocation of the OpenCode CLI
├── validator/ # Runs a task's automated checks and builds correction feedback from failures
├── architect/ # Architect agent: prompts, plan-project, review-task, consult-stuck-task
├── ipc/ # Message types exchanged between the daemon and its clients
├── daemon/ # Orchestrator: task-cycle (dispatch-worker, process-worker-report), definition/implementation phases, cascade-fail, mutex, persistence
├── daemon-client/ # IPC client over the socket/pipe + auto-start of the daemon (ensureDaemon)
├── cli/ # `archctl` — Commander-based CLI
├── tui/ # `arch-terminal` — Ink/React terminal UI
└── e2e/ # End-to-end tests: real daemon + real git worktrees, Claude CLI mocked
The daemon talks to the CLI and the TUI over a local IPC channel — a Unix domain socket
(.arch/daemon.sock) on macOS/Linux, a named pipe (\\.\pipe\arch-<hash>) on native Windows —
using a newline-delimited JSON protocol. Neither client starts the daemon manually —
ensureDaemon spawns it as a detached background process the first time it's needed for a given
directory, and later invocations reuse that same instance as long as the channel is alive.
- Node.js ≥ 20 (the version pinned in
.nvmrc) - pnpm 10.x (
corepack enableis enough if you use Corepack) - The Claude Code CLI installed and authenticated (
claudeavailable on yourPATH) — this is the actual engine behind every agent
There are two ways to install ARCH, depending on whether you just want to use it or you want to work on it.
npm install -g @losina/arch-cli @losina/arch-terminalThis installs the archctl and arch-terminal executables directly from the npm registry — no
cloning or building required. It only needs Node.js ≥ 20 on your PATH (see
Requirements above). Upgrade with the same command, or
npm update -g @losina/arch-cli @losina/arch-terminal.
macOS / Linux:
curl -fsSL https://raw.githubusercontent.com/Losina24/ARCH/main/scripts/install.sh | bashWindows (PowerShell):
irm https://raw.githubusercontent.com/Losina24/ARCH/main/scripts/install.ps1 | iexThis clones ARCH into a dedicated directory (~/.local/share/arch-cli, or %LOCALAPPDATA%\arch-cli
on Windows — not the directory you'll run ARCH against), builds it, and links the archctl and
arch-terminal executables onto your PATH. It requires git and Node.js ≥ 20 already on your machine
(see Requirements above); if pnpm isn't installed, the script enables it via
Corepack. Re-running the same command later pulls the latest version and rebuilds.
If linking fails with ERR_PNPM_NO_GLOBAL_BIN_DIR, that's pnpm's global bin directory being
configured for the first time — restart your shell (so the updated PATH takes effect) and re-run
the command above.
The scripts themselves live at scripts/install.sh and
scripts/install.ps1 — read them before piping them into your shell, as with
any installer.
git clone https://github.com/Losina24/ARCH.git
cd ARCH
pnpm install
pnpm buildThis compiles all 15 packages of the monorepo (via Turborepo) and produces the archctl and
arch-terminal executables. To use them outside of this repository, link them globally:
pnpm link:globalpnpm link doesn't support --filter (which forces pnpm's recursive/workspace mode), so pnpm --filter @losina/arch-cli link --global is out. pnpm --dir/-C doesn't work either — inside a pnpm
workspace it still resolves the workspace root package instead of the one at that path, so it
ends up linking the private root package (which has no bin entries) instead of
@losina/arch-cli/@losina/arch-terminal. link:global works around this by actually changing directory into each
package before linking it — see the link:global script in package.json if you
need to run the two steps separately.
If this fails with ERR_PNPM_NO_GLOBAL_BIN_DIR, pnpm's global bin directory hasn't been set up on
your machine yet. Run pnpm setup once, restart your shell (so the updated PATH takes effect),
and retry pnpm link:global.
If you already ran the old pnpm --dir packages/cli link --global form, it linked the wrong
package under the global name arch — remove it with rm ~/Library/pnpm/global/5/node_modules/arch
(path may differ by platform/pnpm version; run pnpm ls -g to locate it) before linking again.
Run either tool from inside the repository you want ARCH to work on (the target repository — this is separate from the ARCH monorepo itself).
arch-terminalOpens an interactive view of the current directory: the list of runs, a live detail view with the task DAG, and actions to approve or refine a plan without leaving the terminal.
Every command accepts --cwd <dir> (defaults to the current directory) to target a specific
repository.
# Start a new run from a prompt
archctl run "Add a DELETE /users/:id endpoint that removes the user and their active sessions"
# List runs, or inspect one in detail
archctl list
archctl show <runId>
# See the plan (project.md + tasks) produced by the Architect
archctl plan <runId>
# Ask the Architect to revise the plan before approving it
archctl refine <runId> "Split task 2 into separate authentication and cascading-deletion tasks"
# Approve the plan and start the implementation phase
archctl approve <runId>
# Abort a run in progress
archctl abort <runId>
# Check whether the daemon is up for this directory
archctl daemon-status
# Configuration (per-agent models, concurrency, retries)
archctl config get
archctl config set --architect-model claude-opus-5 \
--worker-model claude-sonnet-5 --max-concurrency 4 --max-retries 3Configuration is stored in .agentmeshrc.json at the root of the target repository. When it's
missing, these defaults apply (see .agentmeshrc.example.json):
{
"models": {
"architectModel": "claude-opus-5",
"workerModel": "claude-sonnet-5"
},
"execution": {
"maxConcurrency": 4,
"maxRetries": 3
}
}.arch/ is created inside the target repository (not the ARCH monorepo) and should never be
committed — it's already listed in .gitignore:
.arch/
├── daemon.sock # Unix socket the daemon listens on (named pipe on Windows — no on-disk file)
├── daemon.log # stdout/stderr of the detached daemon process
└── runs/<runId>/
├── meta.json # RunMeta (phase, timestamps...)
├── project.md # Architect's project brief
├── tasks-index.yaml
├── tasks/*.md
├── worktrees/ # one git worktree per in-flight task
└── sessions.json # per-agent Claude session ids, used to resume conversations
pnpm build # turbo run build across all packages
pnpm test # turbo run test (Vitest) across all packages
pnpm typecheck # turbo run typecheck
pnpm lint # biome check .
pnpm lint:fix # biome check --write .Turborepo builds each package's workspace dependencies first (build/typecheck/test all
depend on ^build), so pnpm build alone is enough to get everything compiled and ready for the
other commands.
To iterate on a single package:
pnpm --filter @losina/daemon dev # tsc --watch
pnpm --filter @losina/daemon test- Unit tests live next to the code they cover (
src/**/*.test.ts) in every package and run with Vitest. - End-to-end tests (
packages/e2e) start a real daemon in-process, talk to it over a real Unix socket, and operate on a real temporary git repository — including real worktrees, commits, and merges. The only thing they mock is the boundary with theclaudeCLI (@losina/claude-runtime), so the full orchestration logic (task graph, retries, cascade-fail, abort) is exercised for real without depending on live model calls.