DevOrchestrator (devorch) is a local-first CLI that sits above coding agents. It separates planning, execution, and review so you can use different models for different jobs while keeping the repository as the source of truth.
Plans, architecture notes, skills, and run traces live in .ai/ inside the project. Nothing about the core loop requires a hosted backend.
devorch init
devorch plan "Add Google OAuth authentication"
devorch approve PLAN-001
devorch execute PLAN-001
devorch review PLAN-001
Current version: 0.2.0. Requires Node.js 20+.
- Why it exists
- How it works
- Requirements
- Installation
- Quick start
- Command reference
- The
.ai/directory - Plans
- Execution loop
- Context engine
- Skills
- Configuration
- Environment variables
- Executors
- Providers and models
- Security
- Project detection
- Develop this repo
- Distribution and release
- Troubleshooting
- Current limitations
Coding agents are good at editing files. They are weaker at holding a durable project model, refusing scope creep, and stopping for human approval before they write code.
DevOrchestrator treats those as separate roles:
| Role | Job | Default implementation |
|---|---|---|
| Planner | Read the repo and produce a structured plan | Browser chatbot (ChatGPT / Gemini / Claude), or LLM via OpenAI / Anthropic / Google |
| Human | Approve, edit, regenerate, or reject | CLI prompts |
| Executor | Implement the approved plan | Codex CLI, Claude Code CLI, or LLM fallback |
| Validator | Run your test/lint/typecheck commands | Local shell, policy-gated |
| Reviewer | Judge the local file changes against acceptance criteria | LLM via OpenAI / Anthropic / Google |
The planner does not implement. The executor does not redefine the objective. The reviewer does not write the plan. The workspace, not chat history, is authoritative.
Request
│
▼
Context engine ─── .ai/PROJECT.md, ARCHITECTURE.md, CONVENTIONS.md
─── matched skills
─── relevant source files
─── git state, package info, existing plans
│
▼
Planner LLM ─── writes .ai/plans/PLAN-00N-title.md (awaiting_approval)
│
▼
Human approval
│
▼
Executor ─── Codex / Claude Code / LLM file writes
│
▼
Validation commands ─── test, typecheck, lint (optional, from config)
│
▼
Reviewer LLM ─── approved (and project files changed) → completed
─── no app files changed, or changes requested → executor retries (up to maxIterations)
│
▼
Trace ─── .ai/runs/RUN-00N.json
All filesystem writes go through a workspace sandbox. Validation commands go through a command policy. Secret files are kept out of planner/reviewer context.
- Node.js
>= 20 - Git in
PATH(optional; execute/review work without a repo) - A project root that contains
.gitorpackage.json(DevOrchestrator walks up from the current directory) - For planning and review: an API key for the configured provider
- For execution, one of:
codexCLI (executor.agent = "codex")claudeCLI (executor.agent = "claude-code")- an API key, which enables the LLM executor fallback
After the package is published:
npm install -g @salatech/devorch@alpha
# or
pnpm add -g @salatech/devorch@alphaThe executable is devorch (package name @salatech/devorch). Then:
devorch --version
devorch --help
devorch doctorgit clone https://github.com/salatech/DevOrchestrator.git
cd DevOrchestrator
pnpm install
pnpm build
node dist/cli.mjs --helpLink globally while developing:
pnpm build
npm link
devorch --helpThe published binary name is devorch (package name @salatech/devorch):
pnpm add -g @salatech/devorch@alpha
# or
npm install -g @salatech/devorch@alphapnpm install
pnpm dev --help
pnpm dev doctorpnpm dev is tsx src/cli.ts.
-
Export a provider key:
export OPENAI_API_KEY=sk-... # or export ANTHROPIC_API_KEY=sk-ant-...
-
Inside the target project:
devorch init devorch doctor
-
Create a plan (browser chatbot, no API key):
devorch plan --chat gemini "Add rate limiting to the public API"Or import a reply you already saved:
devorch plan --from reply.md
API planner (needs a key):
devorch plan "Add rate limiting to the public API".You will be asked to Approve, Edit, Regenerate, or Reject. After approve, you can execute immediately or later.
-
Execute an approved plan:
devorch execute PLAN-001
-
Inspect results:
devorch status devorch diff devorch show PLAN-001
One-shot variant:
devorch run "Add rate limiting to the public API"--yes / -y skips confirmation prompts.
devorch <command> --help
| Command | Purpose | Needs .ai/ |
Needs API key |
|---|---|---|---|
init |
Create .ai/ layout, config, stub docs, skills |
No | Optional (better docs if present) |
plan |
Generate or import a plan | Yes | No for --chat / --from / --link; yes for API planner |
approve |
Mark a plan approved (or reopen completed/failed) | Yes | No |
execute |
Run an approved plan (or re-run completed/failed) | Yes | Yes (or executor CLI) |
review |
Review local workspace changes against a plan | Yes | Yes |
run |
Plan + approve + execute | Yes | Yes |
plans |
List plans | Yes | No |
show |
Print one plan | Yes | No |
status |
Project, git, active plan, models | Yes | No |
context |
Preview planner context | Yes | No |
diff |
Local workspace changes (git if available) | Yes | No |
doctor |
Environment checks | No | No |
Detects the workspace root, stack, package manager, and scripts, then creates:
.ai/PROJECT.md,.ai/ARCHITECTURE.md,.ai/CONVENTIONS.md.ai/.devai.jsonwith detected validation scripts when possible.ai/plans/,.ai/tasks/,.ai/skills/,.ai/state/,.ai/runs/,.ai/inbox/- stub skills (
testing, andtypescriptwhen JS/TS is detected) .ai/.gitignoreforstate/andruns/- root
.gitignoreentries for.ai/state/,.ai/runs/, and.ai/inbox/
If OPENAI_API_KEY, ANTHROPIC_API_KEY, or GOOGLE_API_KEY is set, init asks the planner model to draft PROJECT.md and ARCHITECTURE.md. On failure it falls back to stubs.
| Flag | Description |
|---|---|
--force |
Overwrite stub documentation and .devai.json |
If .ai/ already exists, init fills in missing files unless --force is passed.
Preferred (no API key): plan in ChatGPT, Gemini, or Claude in your browser, then import the reply.
devorch plan --chat gemini "create a normal calculator app with html and css and js"That copies a local-context prompt, opens the chatbot, and waits until you save the reply to .ai/inbox/plan.md (or another file with --from). Then it becomes PLAN-00N.
Already planned in the browser? For ChatGPT and Claude, import a public share link:
devorch plan --link "https://chatgpt.com/share/xxxxxxxx"
devorch plan --link "https://claude.ai/share/xxxxxxxx"Gemini public share pages (share.gemini.google/…, gemini.google.com/share/…, g.co/gemini/share/…) load the conversation with JavaScript in the browser, so --link cannot read the replies from HTML. Copy Gemini’s reply into a file in the project, then:
devorch plan --from reply.md--from must point at a real file (relative to the project). If the file is missing, the command fails instead of importing whatever is on the clipboard.
The ChatGPT/Claude chat must be a public share link (Share → copy link). A normal private chat URL will not work.
devorch plan --chat chatgpt "…"
devorch plan --chat claude "…"
devorch plan --from .ai/inbox/plan.mdAPI planner: devorch plan "…" still calls the configured provider key (OpenAI / Anthropic / Gemini API).
After import it prompts Approve / Edit / Regenerate / Reject as before.
| Argument / flag | Description |
|---|---|
<request> |
Natural-language task (required unless --from) |
--chat chatgpt|gemini|claude |
Plan in the browser chatbot (no API key) |
--link <url> |
Import a public ChatGPT or Claude share (Gemini: use --from) |
--from <file> |
Import a saved chatbot reply (file must exist in the project) |
--include <files> |
Comma-separated extra files to force into context |
--yes, -y |
Approve immediately (does not auto-execute) |
Example:
devorch plan --chat gemini "Add Google OAuth"Loads the plan, shows it, and transitions:
draft→awaiting_approval→approvedawaiting_approval→approvedcompletedorfailed→approved(re-open so you can execute again)
Already-approved plans are left unchanged.
| Flag | Description |
|---|---|
--yes, -y |
Skip confirmation |
Requires status approved, or confirms before reopening completed / failed back to approved. Then runs the execution loop. Works in folders that are not git repositories (review uses local files, not git-only diffs).
| Flag | Description |
|---|---|
--yes, -y |
Skip the “this will modify the workspace” confirmation |
If the configured CLI agent is missing and an API key exists, DevOrchestrator warns and falls back to the LLM executor.
Reviews local workspace files (and git diff if present) against the plan’s objective and acceptance criteria. If the plan is executing or validating, it moves to reviewing; if the reviewer approves from reviewing and project files outside .ai/ changed, the plan becomes completed.
Full workflow: plan, show, approve, execute.
| Argument / flag | Description |
|---|---|
<request> |
Natural-language task (required) |
--include <files> |
Extra context files |
--yes, -y |
Skip approval and execute immediately |
Without --yes, rejecting the plan cancels it. Approving without executing leaves it approved for a later devorch execute.
Lists every plan in .ai/plans/ (table: ID, title, status, updated).
| Flag | Description |
|---|---|
--status <status> |
Filter, e.g. approved, completed, awaiting_approval |
Prints the full formatted plan (objective, files, steps, constraints, risks, acceptance criteria).
Plan files are resolved by filename (PLAN-001-*.md or PLAN-001.md) or by YAML id in frontmatter, so 000-mvp.md with id: PLAN-000 still works.
Prints:
- project name and detected stack
- workspace root
- branch and dirty-file counts
- first non-terminal / in-flight plan
- planner, executor, reviewer settings
- last run from
.ai/runs/
Assembles planner context without calling an LLM. Use this to debug file selection.
Default request is general. Example:
devorch context "oauth session jwt"Shows which .ai/ docs loaded, scored files, matched skills, git branch, and plan count.
Shows local workspace changes first (files on disk). --staged still uses git, when the folder is a git repo.
| Flag | Description |
|---|---|
--staged |
Staged diff only |
Checks:
giton PATH.ai/present- config loads
- planner and reviewer API keys
- executor CLI or LLM fallback key
Non-zero exit if any check fails. Safe to run before the first plan.
Created by devorch init:
.ai/
├── .devai.json # Project config (see Configuration)
├── .gitignore # ignores state/ and runs/
├── PROJECT.md # Purpose, stack, commands, constraints
├── ARCHITECTURE.md # System overview and components
├── CONVENTIONS.md # Style, naming, testing, architecture rules
├── plans/ # PLAN-00N-*.md (committed)
├── tasks/ # Reserved for future task breakdowns
├── skills/ # skills/<name>/SKILL.md
├── state/ # Runtime state (gitignored)
└── runs/ # RUN-00N.json execution traces (gitignored)
Commit PROJECT.md, ARCHITECTURE.md, CONVENTIONS.md, skills/, plans/, and .devai.json. Those files are how later plans stay consistent.
Do not commit .ai/state/ or .ai/runs/ (init adds gitignore rules).
Fill in the markdown stubs. The planner and reviewer receive them on every run.
Plans are Markdown with YAML frontmatter. Example filename: .ai/plans/PLAN-001-add-rate-limiting.md.
| Field | Meaning |
|---|---|
id |
PLAN-NNN (zero-padded, auto-assigned) |
title |
Short title |
status |
See lifecycle below |
created / updated |
Dates |
planner |
Model/agent that wrote the plan |
branch |
Git branch at creation |
The parser reads # headings and known ## headings:
- Objective
- Current State
- Relevant Files
- Files To Modify
- Files To Create
- Implementation Steps (
## Step N: Title) - Constraints
- Testing Strategy
- Acceptance Criteria
- Risks
- Out Of Scope
- Dependencies
A plan cannot be saved without an id matching PLAN-\d{3}, a title, an objective, at least one implementation step, and at least one acceptance criterion. The orchestrator fills safe defaults if the model omits them.
draft
→ awaiting_approval
→ approved → executing ─┬→ validating → reviewing → completed
│ │
│ └→ executing (retry)
└→ reviewing (no validation commands)
└→ failed
→ cancelled
failed → draft or approved (re-open)
completed → approved (re-run)
cancelled is terminal
| Status | Meaning |
|---|---|
draft |
Parsed but not yet offered for approval |
awaiting_approval |
Written to disk, waiting on a human |
approved |
Allowed to execute |
executing |
Coding agent is working |
validating |
Configured validation commands are running |
reviewing |
Reviewer is judging the diff |
completed |
Reviewer approved and project files changed; can be re-run |
failed |
Blocked, validation never passed, max iterations hit, or no app files changed; can be re-opened |
cancelled |
Rejected or abandoned |
Invalid transitions throw PlanningError.
devorch execute PLAN-00N (and devorch run --yes):
- Load the plan; require
approved(orreviewingto retry).completed/failedcan be reopened toapprovedfrom the execute prompt. - Transition to
executing. - Snapshot local files (fingerprints) and git state when git exists.
- Rebuild context from the plan objective.
- For
iteration = 1..limits.maxIterations:- Send plan + instructions + prior feedback to the executor.
- If the executor returns
blocked, markfailedand stop. - If
validation.commandsis non-empty, run each command under the security policy.- Failure feeds the command output back to the executor and retries.
- Reviewer sees local file changes (preferred) plus git diff if available, and the validation summary.
approvedand files outside.ai/changed →completedapprovedbut only.ai/(or nothing) changed → retry, orfailedafter max iterations (the report matches the plan status)changes_requested→ findings go back to the executor
- Snapshot again, write
.ai/runs/RUN-00N.json, print an execution report (files, line stats, validation, review, duration).
Instructions to the executor include the plan steps, files to modify/create, constraints, previous validation/review feedback, and an explicit rule: do not redefine the objective.
Before planning (and again before execute/review), DevOrchestrator gathers:
| Source | Path / method |
|---|---|
| Project brief | .ai/PROJECT.md |
| Architecture | .ai/ARCHITECTURE.md |
| Conventions | .ai/CONVENTIONS.md |
| Skills | .ai/skills/*/SKILL.md matched to the request |
| Source files | Heuristic selection, capped by limits.maxContextFiles (default 30) |
| Git | Branch, dirty files, recent commits |
| Plans | Existing plan summaries |
| Package | package.json name and dependencies |
Keywords are extracted from the request (stop-words stripped). Files score higher when:
- the path contains a keyword
- the file was recently modified
- it is a config/entry file (
package.json,tsconfig.json,index.ts, …) - it matches domain boosts (auth, routes/API, database/schema)
- it is a test file associated with an already-relevant source file
--include on plan / run always adds those paths if they exist.
Use devorch context "your request" to see the ranking before spending tokens.
These are never selected as source context:
.env,.env.*except.env.example*.pem,*.key,*.p12,*.pfxid_rsa/id_dsa/id_ecdsa/id_ed25519credentials.json,secret(s).*node_modules,.git,dist,build, coverage and cache dirs
A skill is a directory:
.ai/skills/testing/SKILL.md
Minimum shape:
# testing
## Purpose
How to write and run tests in this repository.
## When To Use
When adding features, fixing bugs, or changing behavior that needs verification.init creates testing (always) and typescript when JS/TS is detected. Add your own (database, frontend, auth, …). Discovery tokenizes the user request against skill keywords and the When To Use section; matching skills are injected into the planner prompt.
Loaded from the first file that exists, in this order:
devai.config.tsat the project root.devai.jsonat the project root.ai/.devai.json
devorch init writes .ai/.devai.json. Zod validates and merges with defaults. Environment variables override file values after parse.
{
"planner": {
"provider": "openai",
"model": "gpt-4o"
},
"executor": {
"agent": "codex",
"provider": "openai",
"model": "gpt-4o"
},
"reviewer": {
"provider": "openai",
"model": "gpt-4o"
},
"validation": {
"commands": ["pnpm test", "pnpm typecheck"]
},
"limits": {
"maxIterations": 3,
"maxContextFiles": 30
},
"security": {
"commandPolicies": {
"pnpm test": "safe",
"custom-script": "requires_approval"
}
}
}| Key | Allowed values | Default | Notes |
|---|---|---|---|
planner.provider |
openai, anthropic, google |
openai |
Google is accepted in config; runtime still needs @ai-sdk/google |
planner.model |
any string | gpt-4o |
e.g. claude-sonnet-4-5 |
executor.agent |
codex, claude-code, llm |
codex |
See Executors |
executor.provider / model |
same as planner | planner’s settings | Used by the LLM executor |
reviewer.provider / model |
same as planner | openai / gpt-4o |
Independent of planner |
validation.commands |
string[] | [] |
Empty skips the validating stage |
limits.maxIterations |
1–10 | 3 |
Executor retries after failed tests or review |
limits.maxContextFiles |
1–100 | 30 |
File cap for the planner prompt |
security.commandPolicies |
map of pattern → safe | requires_approval | blocked |
{} |
Custom rules are checked before built-in lists |
init pre-fills validation.commands from package.json scripts named test, typecheck, and/or lint.
TypeScript config example (devai.config.ts):
export default {
planner: { provider: 'anthropic', model: 'claude-sonnet-4-5' },
executor: { agent: 'llm' },
reviewer: { provider: 'anthropic', model: 'claude-sonnet-4-5' },
validation: { commands: ['pnpm test', 'pnpm typecheck'] },
limits: { maxIterations: 3, maxContextFiles: 40 },
};| Variable | Used when |
|---|---|
OPENAI_API_KEY |
provider is openai |
ANTHROPIC_API_KEY |
provider is anthropic |
GOOGLE_API_KEY or GOOGLE_GENERATIVE_AI_API_KEY |
provider is google |
These overlay the JSON/TS file:
| Variable | Sets |
|---|---|
DEVAI_PLANNER_PROVIDER |
planner.provider |
DEVAI_PLANNER_MODEL |
planner.model |
DEVAI_EXECUTOR_AGENT |
executor.agent |
DEVAI_REVIEWER_PROVIDER |
reviewer.provider |
DEVAI_REVIEWER_MODEL |
reviewer.model |
DEVAI_MAX_ITERATIONS |
limits.maxIterations |
Example:
DEVAI_PLANNER_PROVIDER=anthropic \
DEVAI_PLANNER_MODEL=claude-sonnet-4-5 \
DEVAI_EXECUTOR_AGENT=llm \
devorch plan "Describe the auth module"Spawns:
codex exec --prompt "<plan instructions>" --jsonWorking directory is the project root. Timeout: 5 minutes. After the process exits, DevOrchestrator records git diff --name-only and untracked files.
Spawns:
claude --json --prompt "<plan instructions>"Same capture rules as Codex.
Used when you set llm, or automatically when the Codex/Claude binary is missing and an API key is available.
The model returns structured file operations (write / delete). Writes go through WorkspaceManager, which refuses paths outside the project root. This path does not run an arbitrary shell; it only edits files.
- Configured CLI agent if the binary exists.
- Else LLM executor if a key exists for
executor.provider(or planner provider). - Else
ProviderErrortelling you to installcodex/claudeor setexecutor.agenttollm.
Planner, reviewer, and LLM executor go through ModelOrchestrator, a thin wrap around the Vercel AI SDK (ai, @ai-sdk/openai, @ai-sdk/anthropic). Core code does not import provider-specific types.
| Provider | Status |
|---|---|
| OpenAI | Supported |
| Anthropic | Supported |
Allowed in config / env; runtime currently errors until @ai-sdk/google is installed |
You can use different providers per role, for example Anthropic to plan and OpenAI to review.
Token usage is tracked per role during a process; estimated cost is a rough GPT-4o-style heuristic, not a bill.
DevOrchestrator is local-first. See SECURITY.md for reporting vulnerabilities.
Every relative read/write/delete is resolved against the project root. Paths that escape (../.ssh/id_rsa, /etc/passwd) throw SecurityError and stop.
Validation commands (and any future shell use through SecureCommandExecutor) are classified as:
- safe — run immediately
- requires_approval — interactive confirm in the TTY; denied if no handler
- blocked — always throws
SecurityError
Unknown commands default to requires_approval. Custom security.commandPolicies win over defaults.
Safe (subset): pnpm test, pnpm lint, pnpm typecheck, pnpm build, npm test, yarn test, tsc --noEmit, npx vitest run, git status, git diff, git log, cargo test, go test, python -m pytest.
Needs approval (subset): npm install, pnpm add, git commit, git push, git checkout, git merge, pip install, npx prisma migrate.
Blocked (subset): rm -rf /, rm -rf *, mkfs, dd if=, fork bombs, curl | sh, git push --force, DROP TABLE, DELETE FROM, TRUNCATE.
It cannot write outside the workspace. It does not get a shell. Secret files are not placed in its context by the file selector.
devorch init and devorch status inspect the workspace.
| Signal | Result |
|---|---|
package.json |
nodejs |
Cargo.toml |
rust |
pyproject.toml / requirements.txt |
python |
go.mod |
go |
pom.xml / build.gradle |
java |
Gemfile |
ruby |
*.csproj |
dotnet |
Package manager: pnpm-lock.yaml → pnpm, yarn.lock → yarn, bun.lock(b) → bun, package-lock.json → npm.
Frameworks (examples): Next.js, Nuxt, Vite/React, Vue, SvelteKit, Express, Fastify, NestJS, Django — from config files and package.json dependencies.
The walk for project root stops at the nearest directory containing .git or package.json.
pnpm install # lockfile: pnpm@10.14.0
pnpm dev --help # run CLI from TypeScript
pnpm test # Vitest unit tests
pnpm typecheck # tsc --noEmit
pnpm build # tsup → dist/cli.mjs (shebang)
pnpm format # Prettier on src/ and tests/| Path | Responsibility |
|---|---|
src/cli.ts |
Citty entry; lazy-loaded subcommands |
src/commands/ |
User-facing commands |
src/runtime.ts |
Workspace + orchestrator bootstrap |
src/core/orchestrator.ts |
Plan / execute / review loop |
src/plans/ |
Parse, serialize, CRUD, state machine |
src/context/ |
Collect and score context |
src/agents/ |
Planner, reviewer, CLI executor, LLM executor |
src/providers/ |
ModelOrchestrator + usage tracker |
src/workspace/ |
Sandboxed FS, git, stack detection |
src/security/ |
Command policy + gated exec |
src/skills/ |
Load and match SKILL.md files |
src/state/ |
Snapshots and .ai/runs traces |
src/config/ |
Zod schema, loader, defaults |
src/ui/ |
Plan/report formatting, prompts, spinners |
tests/unit/ |
Unit tests (plans, security, context, config, workspace, skills) |
CI (.github/workflows/ci.yml) on main and pull requests: typecheck, test, build on Node 22.
Release notes: DEPLOYMENT.md. Security reports: SECURITY.md. Contributing: CONTRIBUTING.md.
DevOrchestrator ships as a local npm CLI, not a cloud app.
- Version:
package.jsononly;devorch --versionmatches it - Build:
pnpm build→dist/cli.mjs - Verify tarball:
pnpm verify:pack - CI: typecheck, format, tests, build, integration, pack (Node 20 and 22)
- Tags
v*.*.*build GitHub Release artifacts; npm publish is manual
See DEPLOYMENT.md for the full pipeline. Process env templates: .env.example.
Run devorch init in the project (or a subdirectory of it).
Export OPENAI_API_KEY (or switch planner.provider / set ANTHROPIC_API_KEY). Confirm with devorch doctor.
execute accepts approved or reviewing. Completed or failed plans can be run again: devorch execute PLAN-00N asks to reopen them. Or run devorch approve PLAN-00N first.
Install Codex, or set "executor": { "agent": "llm" } and provide an API key. If a key is already present, execute should fall back automatically and print a warning.
Run the same commands yourself. Check validation.commands in .ai/.devai.json. Commands that are not on the safe list need TTY approval or a custom safe policy.
devorch context "your request"Add --include path/a.ts,path/b.ts or mention distinctive path tokens in the request. Raise limits.maxContextFiles if the repo is large and relevant files are truncated.
Only one config file is read. devai.config.ts wins over root .devai.json, which wins over .ai/.devai.json. init writes the last of those.
Set GOOGLE_API_KEY or GOOGLE_GENERATIVE_AI_API_KEY. Confirm with devorch doctor. The Google adapter (@ai-sdk/google) is included.
That is expected. Gemini share pages are a JavaScript app. Copy the assistant reply into reply.md in the project folder and run devorch plan --from reply.md.
The path is resolved from the project root (the folder with .ai/), not necessarily the directory you ran the command from if you are elsewhere. Put the file in the project and pass a path relative to that root.
MVP (0.2.0) does not include:
- MCP server or editor extension
- embeddings / semantic search (file selection is heuristic)
- GitHub PR creation
- cloud sync or team-shared context beyond git
- multi-agent parallel execution
- importing Gemini public share pages (
--link); use--frominstead - a separate
validation/package (validation runs inside the orchestrator)
The product is a local CLI. Keep secrets in the environment, not in .ai/ docs.