| title | status | date | parents | tags | input | output | position | ||
|---|---|---|---|---|---|---|---|---|---|
dagain |
active |
2026-02-04 |
|
GitHub/npm readers and CLI users |
User-facing docs for `dagain` |
Repo/package README and primary usage reference |
DAG-based orchestration for coding agents (Codex, Claude Code, Gemini).
Dagain runs a work graph (nodes + deps) stored in SQLite, and executes each node with a configured runner. It’s built to keep agents “fresh”: context is loaded from the graph/DB when needed, not carried indefinitely in prompts.
Once published to npm:
npx dagain --helpOr install globally:
npm i -g dagain
dagain --help# 1) init state + config (creates .dagain/ and a new session)
dagain init --goal "Add a CLI flag --foo and tests" --no-refine
# 2) run the supervisor (defaults to 3 workers; drops into chat on completion)
dagain run --live
# disable post-run chat:
dagain run --no-post-chat
# 3) in another terminal: check status / interact
dagain status
dagain chat # TUI by default on a real terminal
dagain chat --plain # force the plain readline REPL (useful for piping / non-TTY)
dagain control resume # enqueue resume (auto-starts supervisor if stopped; add --no-start to enqueue only)
# 4) optional: live dashboards
dagain tui # terminal dashboard + chat (shows a GUI URL)
dagain ui # web dashboard (chat + DAG + node logs, sessions drawer, pan/zoom+fit, controls)Tip: in a fresh repo you can also just run dagain (no args) or dagain ui — both will auto-initialize a session with a placeholder goal if no state exists yet.
Note: if you run dagain as root (e.g. via sudo) inside a repo, it will prefer executing runners as the repo owner to avoid root-owned outputs.
dagainstores state per session under.dagain/sessions/<sessionId>/.dagain init --goal "..."creates/updates the current session goal (.dagain/GOAL.md). If the current session already has state and is “inactive” (all nodesdone), it auto-creates a new session unless you pass--reuse.- Use
--new-sessionto force a new session even if the current one still has unfinished work.
Inside dagain chat (both TUI and --plain):
/status— print graph status/run— start supervisor/pause//resume— stop/resume launching new nodes (in-flight nodes finish)/workers <n>— set concurrency (default: 3)/replan— force plan node (plan-000) to reopen and block launches until it completes/cancel <nodeId>— cancel a running node (best-effort)/answer [nodeId] <answer...>— answer a checkpoint and reopen aneeds_humannode/artifacts [nodeId]— show run artifact paths (and last stdout/result for a node)/memory//forget— inspect/reset chat memory stored in SQLite KV
Dagain is a DAG of nodes:
plannodes decompose goals into taskstasknodes do work (code, analysis, etc)verifynodes check task outputsintegratenodes merge/roll up resultsfinal_verifynodes do final checks
Dependencies live in the deps table. A dep can require:
done(default): upstream must bedoneterminal: upstream must be terminal (doneorfailed) — useful for “investigate failure” / escalation flows
All durable state is session-scoped under .dagain/sessions/<sessionId>/state.sqlite.
For backwards-compat and convenience, .dagain/state.sqlite is a symlink to the current session DB:
nodes/deps— the DAG and statuseskv_latest/kv_history— durable “memory” and artifactsmailbox— supervisor control queue (pause/resume/workers/replan/cancel)
Chat memory is stored in KV under node_id="__run__":
chat.rollup— rolling summary (router-maintained)chat.turns— last ~10 turnschat.last_ops— last emitted ops JSONchat.summary— last assistant reply
Dagain keeps the model from directly mutating state by having it emit ops. The host applies them safely.
In dagain chat, the router can emit:
control.*ops (pause/resume/workers/replan/cancel)ctx.*ops (read-only context requests likectx.readFile,ctx.rg,ctx.gitStatus) — Dagain executes these and re-invokes the router with resultsnode.add,node.update,node.setStatusdep.add,dep.removerun.start,run.stop,status
Runners are just shell commands that receive a {packet} filepath and should print:
<result>{...json...}</result>
Configure them in .dagain/config.json:
{
"version": 1,
"runners": {
"codex": { "cmd": "codex exec --yolo --skip-git-repo-check -" },
"claude": { "cmd": "claude --dangerously-skip-permissions -p \"$(cat {packet})\"" },
"gemini": { "cmd": "gemini -y -p \"$(cat {packet})\"" }
},
"roles": {
"planner": "codex",
"executor": "codex",
"verifier": "codex",
"integrator": "codex",
"finalVerifier": "codex"
}
}Notes:
- Dagain strips Claude’s
--dangerously-skip-permissionswhen running as root. - For speed, you can set
defaults.verifyRunnertoshellVerifyso verification doesn’t use an LLM.
dagain run --workers Nruns up toNnodes concurrently (subject to ownership locks). If you omit--workers, Dagain defaults to at least 3 workers.- For conflict-prone code edits, set
supervisor.worktrees.mode="always"to run executors in worktrees and merge serially. - If a node reaches
needs_humanin a non-interactive context, the supervisor waits up tosupervisor.needsHumanTimeoutMs(default: 30 minutes), then auto-answers with “decide safest default” and reopens the node so planning can continue. - To unblock
needs_humanimmediately, usedagain answer --node <id> --answer "..."or (in the web UI chat) send/answer [nodeId] <answer...>.
Dagain stores state in:
.dagain/config.json— runner + role configuration (shared across sessions).dagain/current-session.json— pointer to the current session id.dagain/sessions/<sessionId>/— session storage (goal + db + runs + logs + artifacts).dagain/state.sqlite/.dagain/workgraph.json/.dagain/runs//.dagain/artifacts//.dagain/checkpoints//.dagain/memory/— current-session “view” (symlinks).dagain/GOAL.md— current session goal (symlink)
The UI “Log” panel shows human-readable result output (status/summary, checkpoint question) derived from result.json by default; raw stdout is still available in .dagain/runs/*/stdout.log.
gh repo create knot0-com/dagain --public --source=. --remote=origin --pushIf you don’t have permission to create repos in knot0-com, create a staging repo under your user and transfer it:
gh repo create <you>/dagain --public --source=. --remote=origin --pushThen transfer via GitHub UI: Settings → General → Transfer ownership.
Publishing is automated via GitHub Actions on version tags (vX.Y.Z). The tag must match package.json.version.
-
Configure npm Trusted Publishing (OIDC) for
knot0-com/dagainand workflow filenamenpm-publish.yml(npmjs.com → package Settings → Trusted Publisher). -
Cut a release:
npm version patch
git push --follow-tags- Verify:
npx dagain --help- Ensure you’re logged in:
npm whoami- Publish:
npm publish --access publicThen:
npx dagain --help