아카이브 / 개발
codex-autodev — PRD 기반 멀티 에이전트 개발 오케스트레이터
목차 — 프로젝트 요약 · 담당 범위 · 문제와 구현 접근 · 이 작업에서 한 일 · 결과물과 원문 · 구현 상세
프로젝트 요약
대상 저장소의 PRD·인수 조건·규칙을 읽고, Codex CLI(선택적으로 Claude CLI) 에이전트로 계획·구현·검증·수리·리뷰를 반복하는 로컬 Node.js 개발 오케스트레이터.
담당 범위
설계·구현을 맡은 개인 프로젝트다. 공개 저장소에 코드와 사용 방법을 남겼다.
문제와 구현 접근
requirements.yaml의 열린 인수 조건이나 PRD 문서의 소제목에서 작업을 만들고, 개인정보·보안·데이터 무결성 항목을 먼저, 사람의 입력이 필요한 항목을 마지막에 두도록 위험도 순으로 정렬한다. 작업은 프론트엔드·백엔드·API·데이터베이스·보안 같은 도메인별 전문 에이전트 역할로 나눠 맡긴다.
작업마다 별도 git worktree에서 구현하고 설정된 검증 명령을 돌린다. 실패 로그는 정해진 횟수 안에서 수리 에이전트에 넘기고, 리뷰 에이전트가 마지막으로 규칙을 점검한다. marathon 모드는 검증을 통과한 작업만 통합 worktree에 병합하고, push는 명시적으로 켰을 때만 최종 검증을 거쳐 실행한다.
모델 호출을 코드에 넣지 않고 에이전트 CLI를 실행하는 방식으로 두었다. 실행 기록 조회 명령, 읽기 전용 대시보드(API 라우트 탐지와 OpenAPI 출력), 작업 중인 worktree를 따라 미리보기 서버를 다시 띄우는 감시 명령, macOS LaunchAgent로 설치하는 자동 운영 워치독을 함께 만들었다.
이 작업에서 한 일
아이디어를 실제로 작동하는 코드로 만들고, 다른 사람이 설치하고 사용할 수 있도록 설명서를 함께 작성했다.
결과물과 원문
관련 등록공보·논문·저장소는 아래 링크에서 볼 수 있다.
구현 상세
README공개 저장소의 구조·기능·실행 문서
codex-autodev
codex-autodev is a general-purpose autonomous development orchestrator. It reads a target repository's PRDs, specs, acceptance criteria, and repo-local rules, then coordinates multiple coding agents to plan, implement, verify, repair, and review code changes.
It is a local Node.js CLI. It does not embed model calls: every LLM-backed step shells out to an agent CLI (Codex CLI by default, Claude CLI optionally), and every task runs in an isolated Git worktree so the target repo's working tree is not modified by default.
What It Does
- Plans tasks from a user goal, from a machine-readable
docs/autodev/requirements.yaml, or from PRD headings, and orders open acceptance criteria by risk (privacy/security/data-integrity first, human-input items last). - Routes each task to engineering domains (frontend, backend, API, database, agent, DevOps, test, security, docs) and to specialist agent roles.
- Runs specialist agents in a per-task worktree, then runs deterministic verification commands, feeds failure logs to a repair agent within a bounded retry count, and finishes with a review agent.
- In
marathonmode, keeps working through the backlog and merges only verified tasks into a cumulative integration worktree. - Records events, agent outputs, command logs, and reports in a per-run blackboard under
state/. - Provides run inspection commands, a read-only local dashboard, a preview dev-server watcher, and an auto-ops watchdog that can be installed as a macOS LaunchAgent.
Requirements
- Node.js 22 or newer
- Git
- Codex CLI, installed locally by
npm install(@openai/codexis a dependency), logged in with OAuth - Optional: Claude CLI, if you configure it as an agent runtime
- macOS only for
ops install-launchagent; the other commands are plain Node.js
Core Constraint
Model access goes through Codex CLI OAuth login. This project does not call the OpenAI API directly and does not require OPENAI_API_KEY.
The runner invokes:
codex exec ...
or the local package equivalent:
node_modules/.bin/codex exec ...
Model selection and reasoning effort are inherited from Codex CLI defaults unless a project overrides them. Set them in the machine user's Codex config, for example:
model = "gpt-5.5"
model_reasoning_effort = "xhigh"
approval_policy = "never"
The runner also passes --ask-for-approval never to Codex CLI by default. It does not default to --dangerously-bypass-approvals-and-sandbox; task execution stays inside the configured sandbox unless a project explicitly sets codex.bypassApprovalsAndSandbox.
For long autonomous runs, reasoning can be adaptive per agent call instead of always using the CLI default. codex.reasoningPolicy.mode = "adaptive" makes the runner pass per-call overrides such as -c model_reasoning_effort="high":
{
"codex": {
"reasoningPolicy": {
"mode": "adaptive",
"default": "medium",
"byRole": {
"architect": "xhigh",
"frontend-engineer": "medium",
"api-engineer": "high",
"database-engineer": "high",
"security-reviewer": "xhigh",
"repair": "xhigh"
}
}
}
}
The adaptive policy keeps cheap/simple work at medium, regular API/database/test work at high, and reserves xhigh for architecture, security, agent orchestration, repairs, and critical P0 API/database/backend changes. plan --with-reasoning shows the effort that would be selected for each planned role.
Quick Start
git clone <this-repo> codex-autodev
cd codex-autodev
npm install
# Create a starter config for your target repository
node bin/codex-autodev.js init --target <path-to-target-repo> --name my-app --out configs/my-app.json
Or copy configs/example.json and edit name, targetRepo, worktreeRoot, docs, hardRules, and verification. Keep project configs one level deep inside this repo (for example in configs/): the runner takes the parent of the config's directory as the codex-autodev root and resolves stateDir from there. configs/generated.json and configs/*.local.json are git-ignored if you want to keep machine-specific paths out of commits.
Check readiness and authenticate:
node bin/codex-autodev.js doctor --config configs/my-app.json
node bin/codex-autodev.js auth login --config configs/my-app.json --device
node bin/codex-autodev.js auth status --config configs/my-app.json
node bin/codex-autodev.js auth smoke --config configs/my-app.json # tiny live model call
Plan without changing anything:
node bin/codex-autodev.js plan --config configs/my-app.json --max-tasks 3 --with-reasoning
node bin/codex-autodev.js run --config configs/my-app.json --dry-run --max-tasks 3
Run one real implementation loop:
node bin/codex-autodev.js run \
--config configs/my-app.json \
--goal "Implement the next highest-value PRD task" \
--max-tasks 1
Keep going through the PRD backlog with marathon mode:
node bin/codex-autodev.js marathon \
--config configs/my-app.json \
--include-server-checks \
--continue-on-failure
The npm run doctor, npm run dry-run, npm run dry-marathon, npm run dashboard, and npm run runs scripts run the same commands against configs/example.json.
Project Config
A project config is JSON or YAML. The schema is in schemas/project-config.schema.json; configs/example.json is a minimal starting point. The main keys:
| Key | Purpose |
|---|---|
name, targetRepo | Project name and path to the target Git repository |
workspaceMode, worktreeRoot | worktree (default) or direct, and where task worktrees are created |
stateDir | Where run state is written, relative to the codex-autodev root |
docs, hardRules | Target-repo documents given to agents, and rules every agent must follow |
verification | Commands with id, command, timeoutMs, required, and optional needsServer |
server | How to start the target's dev server for needsServer checks (command, port, healthUrl, env, ...) |
agents, agentRuntimes | Specialist mode and cap, default runtime, per-role runtime mapping |
codex, claude | CLI commands, sandbox, approval, and reasoning policy |
marathon | maxCycles, maxTasksPerCycle, stopOnFailure, and includeServerChecks (run needsServer checks without passing the flag) |
publish | Optional push of verified results (disabled unless publish.enabled is true) |
ops | Watchdog timeouts and cooldown, plus the optional capture and taskIntents coordination described under Auto-Ops Watchdog |
dashboard | Defaults for the dashboard preview link (previewUrl) and for preview-watch (previewHost, previewPort) |
Structured requirements
When you pass --goal, that goal becomes the task. Otherwise, if the target repo contains docs/autodev/requirements.yaml, the planner reads it first. Features with an open status (partial, not_implemented, stub, needs_human_input) become tasks: one per unresolved acceptance criterion, or one follow-up task for the feature when no criterion is open:
features:
- id: F4
title: Reports
phase: P0
status: partial
source_docs: [docs/PRD.md]
acceptance_criteria:
- id: F4.AC1
description: Export the monthly report as CSV
status: not_implemented
verification: vitest
Without that file, the planner falls back to ##/### headings in the PRD documents listed in docs. --ai-plan asks the agent CLI (read-only sandbox, schema-constrained JSON output) to write the plan from the same documents, and falls back to the deterministic plan if that call fails or returns invalid JSON.
Run Modes
run executes a bounded task set. By default each task gets its own worktree and does not merge back automatically. Use direct mode only when you intentionally want the agent to edit the target path in place:
node bin/codex-autodev.js run --config configs/my-app.json --direct --max-tasks 1
marathon is the long-running PRD implementation mode. It repeatedly plans unfinished tasks, creates isolated task branches from the current integration branch, verifies them, and merges successful branches into an integration worktree under:
<worktreeRoot>/<run-id>/integration
For each task, the marathon loop:
- Plans the next unfinished PRD/spec task from the target repo docs.
- Creates an isolated task worktree from the current integration branch.
- Asks specialist agents to implement only their part of the task.
- Runs the required verification gates.
- If a required gate fails, passes the logs to RepairAgent and reruns the same gates.
- Commits the task branch only after required gates pass.
- Merges the task branch into the integration worktree.
When a marathon finishes with no failed tasks and publishing is enabled, it runs final verification on the integration worktree and pushes only if that passes. Failed task branches stay isolated so later tasks continue from the last successful integration state.
Parallel independent tasks
marathon.maxTasksPerCycle (or --max-tasks-per-cycle) controls how many tasks are selected per cycle. When more than one task is selected, each gets its own worktree from the integration branch, the tasks run concurrently, and a tasks.parallel.started event is recorded. Successful results are then committed and merged into the integration worktree one by one. While tasks run in parallel, runs monitor, the dashboard, preview-watch, and the watchdog follow the first of them; the dashboard's /api/state also lists all of them in run.progress.inProgressTasks.
Resume and retry
If a marathon run stops, pass --resume <run-id> or --resume latest-marathon to continue from the saved integration worktree and completed/failed task sets. Use plain latest only when you intentionally want to resume the newest run of any mode. If tasks failed because of a runner or environment problem that has since been fixed, add --retry-failed:
node bin/codex-autodev.js marathon \
--config configs/my-app.json \
--resume latest-marathon \
--include-server-checks \
--retry-failed \
--continue-on-failure
Progress is continuously written to reports/marathon-progress.json in the run directory, which powers runs monitor and the dashboard.
Agent Loop
The runner includes these agents:
PlannerAgent: turns PRD/spec/autodev docs into executable tasks.SpecialistAgent: calls the agent CLI for domain roles:architect,frontend-engineer,backend-engineer,api-engineer,database-engineer,agent-engineer,devops-engineer,test-engineer,security-reviewer,product-reviewer.VerifierAgent: runs configured deterministic checks.RepairAgent: calls the agent CLI with failure logs and a bounded retry count.ReviewerAgent: performs a final rule/contract review.
Agents communicate through a run blackboard stored as JSON and JSONL artifacts under state/runs/<run-id>/. Agent stdout/stderr is stored under state/runs/<run-id>/commands/; JSONL events keep compact tails so long model sessions do not make the event log unusable.
agents.maxImplementationSpecialists caps the implementation roles per task (eight in configs/example.json and init output, four if unset). Specialists run sequentially by default. Projects that can tolerate same-workspace concurrent edits may opt into agents.mode = "specialist-parallel": the architect still runs first, then implementation specialists are dispatched together and a specialists.parallel.started event is recorded.
Claude CLI runtime
Codex CLI is the default runtime. A project can define additional runtimes in agentRuntimes and choose them globally (agents.defaultRuntime) or per role (agents.runtimeByRole):
{
"agentRuntimes": {
"claude": {
"provider": "claude",
"command": "claude",
"model": "<model-name>"
}
},
"agents": {
"defaultRuntime": "codex",
"runtimeByRole": { "frontend-engineer": "claude" }
}
}
The Claude runtime runs claude -p --output-format json with the prompt on stdin and passes the selected reasoning effort as --effort. Optional runtime keys (permissionMode, tools, allowedTools, disallowedTools, outputFormat, ...) are listed in the config schema. doctor checks --version and auth status for every configured Claude runtime.
The Codex sandbox setting, including the read-only sandbox used for planning and review calls, is not applied to Claude CLI. What a Claude agent may do is controlled only by the permissionMode and tool lists you set for that runtime.
Verification And Publish Gates
Before a task can be committed, it must pass the required commands in verification. A typical web project might use:
"verification": [
{ "id": "lint", "command": "npm run lint", "timeoutMs": 600000, "required": true },
{ "id": "test", "command": "npm test", "timeoutMs": 600000, "required": true },
{ "id": "build", "command": "npm run build", "timeoutMs": 900000, "required": true },
{ "id": "e2e", "command": "npx playwright test", "timeoutMs": 1200000, "required": true, "needsServer": true }
]
A needsServer check is more than a command. With --include-server-checks, the runner starts the target's dev server on a free port (by default npm run dev -- -H <host> -p <port>), waits for the health URL (default <baseUrl>/api/health), injects PORT, PLAYWRIGHT_BASE_URL, and AUTODEV_SERVER_BASE_URL, runs the check, records server stdout/stderr, and stops the server. If the server never becomes healthy, that is a verification failure and RepairAgent receives the server logs.
verify runs the configured checks on their own:
node bin/codex-autodev.js verify --config configs/my-app.json --include-server-checks
Publishing is off unless publish.enabled is true. When enabled, the publisher fetches and merges publish.targetBranch from publish.remote into the integration worktree, reruns final verification, then pushes:
- the integration branch (
autodev/<run-id>/integrationby default;publish.pushIntegrationBranch,publish.integrationBranch), - the target branch, unless
publish.pushTargetBranchisfalse, - and fast-forwards the local target checkout if it is clean, unless
publish.updateLocalTargetisfalse.
Set publish.pushTargetBranch = false to push only the verified integration branch and merge it by hand after review. Completed historical runs are not auto-published unless publish.publishCompletedRuns is enabled.
When it commits a task, the runner never stages .env or .env.* files at any depth (templates such as .env.example included) or anything under a node_modules directory. It also skips the root-level .next, coverage, playwright-report, and test-results folders and its own dev-server pid, port, and log files. Other generated files are kept out only by the target repo's .gitignore.
Inspecting Runs
node bin/codex-autodev.js runs list --config configs/my-app.json --limit 10
node bin/codex-autodev.js runs status --config configs/my-app.json --run latest
node bin/codex-autodev.js runs monitor --config configs/my-app.json --run latest
node bin/codex-autodev.js runs tail --config configs/my-app.json --run latest --limit 20
node bin/codex-autodev.js runs patch --config configs/my-app.json --run latest
node bin/codex-autodev.js runs prune --config configs/my-app.json --keep 30
runs monitor adds an attention list (failed tasks, stale in-progress tasks, runtime blockers, needs-human outcomes) and redacts token-like values from process lines and event payloads. runs patch writes a reviewable patch from the integration worktree without touching the target repo. runs prune is a dry run by default; pass --apply to delete old completed run directories. Incomplete runs are kept unless --include-incomplete is also passed.
Dashboard
node bin/codex-autodev.js dashboard --config configs/my-app.json --host 127.0.0.1 --port 8765
A read-only HTTP server built on node:http. It serves:
/: a status page for the selected run (defaultlatest-marathon) with panels for the app preview, current work, command timeline, recent events, API routes, related processes, and recent runs. An API capture panel is added whenops.capture.statusPathorops.capture.requestPathis configured./api/state: the same snapshot as JSON./openapi.jsonand/api-docs: an OpenAPI document generated from the API routes found in the active worktree, and a Swagger UI page for it (Swagger UI assets load from unpkg)./healthz
Route discovery covers Next.js pages/api/ and app/api//route.* files, Express-style app.get(...)/router.post(...) declarations, and FastAPI decorators with APIRouter(prefix=...). Each route is tagged with a heuristic security note, for example a sensitive namespace or mutating method with no auth evidence found in the file or in framework middleware.
The dashboard binds to 127.0.0.1 by default. It has no authentication, so bind it to other interfaces only on a network you trust.
Preview Watch
node bin/codex-autodev.js preview-watch --config configs/my-app.json --port 3054
Keeps one preview server pointed at the worktree the current run is working in (the in-progress task, else the integration worktree, else the target repo). It runs npm run dev -- -H <host> -p <port> (Next.js-style flags) in that workspace, so the workspace needs a dev script. Host and port default to dashboard.previewHost/dashboard.previewPort, then 127.0.0.1:3054. When the active worktree changes, it stops the old server and starts a new one. --once aligns once and exits. The dashboard shows the preview state; pass --preview-url to the dashboard (or set dashboard.previewUrl) to link it.
Before it starts a server, preview-watch stops any process that is listening on the chosen port (found with lsof) and deletes the .next folder in the workspace it is about to serve. With no run in progress, that workspace is the target repo itself. Use a port that nothing else needs.
Auto-Ops Watchdog
ops once runs one watchdog cycle; on macOS, ops install-launchagent installs a LaunchAgent that runs ops once --start-if-idle --self-repair --live-auth-check on an interval (use --no-start-if-idle, --no-self-repair, or --no-live-auth-check to drop a flag):
node bin/codex-autodev.js ops install-launchagent \
--config configs/my-app.json \
--label com.codex-autodev.autoops.my-app \
--interval 300 \
--load
The watchdog is intentionally conservative. It tracks latest-marathon, so ad-hoc verify runs do not accidentally trigger a new marathon. If a marathon or agent CLI process for this project is active, it only records status. With --start-if-idle, when the runner is idle after an incomplete or failed marathon run, it starts marathon --resume latest-marathon --retry-failed --continue-on-failure in the background (adding --include-server-checks when marathon.includeServerChecks is true, and --live-auth-check when that flag was given). It also starts one after a successful run if the deterministic planner still finds open tasks in the target docs. With --self-repair, if it sees a known runner-level failure, such as git add trying to stage ignored .env or node_modules symlinks, it first runs Codex CLI against the codex-autodev repo itself, then runs npm run lint and npm test before restarting the marathon. With self-repair on, a run that stays idle and incomplete also gets a stall diagnosis from Codex CLI in a read-only sandbox, with a cooldown between attempts. If a newly completed run has publishing enabled, the watchdog runs the final verification gates before pushing.
Manual one-shot cycle:
node bin/codex-autodev.js ops once \
--config configs/my-app.json \
--run latest-marathon \
--resume latest-marathon \
--start-if-idle \
--self-repair \
--live-auth-check
Each cycle also writes a situation summary (current task, primary mode, risks, recommendations). Optionally, ops.capture.statusPath and ops.taskIntents let the watchdog compare a separate helper process's status file with the hints declared for the current task, and write a desired_target.json request next to it when they diverge. Without that config, this step does nothing.
Auto-ops logs are written under:
state/supervisor/autoops.jsonl
state/supervisor/autoops-latest.json
state/supervisor/autoops.stdout.log
state/supervisor/autoops.stderr.log
Human Questions
The runner does not stop to ask ordinary implementation questions. Agents run non-interactively and report anything unresolved in the openQuestions field (or a needs-human status) of their JSON result, which is stored with the run. docs/HUMAN_QUESTIONS.md is a file the operator maintains by hand: keep questions that truly need a human there, together with the defaults you have decided on. No code reads or writes it. Separately, the planner classifies acceptance criteria that depend on credentials, manual approval, or evidence from external services (English or Korean wording) as needs_human_input and schedules them last.
Image Generation
The runner invokes agents through terminal/filesystem workflows, so they do not get a raster image-generation tool. For autonomous repo work, prefer code-native assets such as SVG, CSS, canvas, or existing project assets. Note tasks that need generated raster images in docs/HUMAN_QUESTIONS.md and handle them by hand.
Development
npm run lint # node --check on every source and test file
npm test # node:test suite
More design detail is in docs/PRD.md and docs/ARCHITECTURE.md.
Status And Limitations
- Personal project, built in April and May 2026 and exercised against real target repositories. Version
0.1.0; the CLI and config format may still change. - Depends on Codex CLI
^0.125.0. Agent quality, cost, and speed depend on the model and effort settings of your CLI login. - Agents run with
--ask-for-approval neverinside the configured sandbox, and marathon mode commits and merges task branches automatically. Point it at repositories where that is acceptable, and review integration branches before merging. - The planner's risk and human-input classification, and the dashboard's route security notes, are keyword heuristics, not proofs.
- The LaunchAgent installer is macOS-only. On other systems, run
ops oncefrom cron or another scheduler.