Skip to content

flora131/atomic

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1,510 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Atomic

Atomic

Ask DeepWiki TypeScript Bun License: MIT

An open-source CLI and TypeScript SDK for building harnesses around your coding agent — Claude Code, OpenCode, or GitHub Copilot CLI. Chain agent sessions into deterministic pipelines, add human-in-the-loop approval gates, dispatch 12 specialized sub-agents, and tap 57 built-in skills — then ship it as TypeScript your whole team runs.

Define how your agent works. Start for yourself, scale to your team — across GitHub, Azure DevOps (ADO), or Sapling.


Two surfaces: CLI and SDK

Atomic ships two things that share one orchestrator runtime. You can use either on its own or both together:

Atomic CLI Atomic SDK
What it is Global atomic binary @bastani/atomic/workflows TypeScript library
Install bun install -g @bastani/atomic (or install.sh / install.ps1) bun add @bastani/atomic inside your project
Entrypoint atomic <command> bun run src/<agent>-worker.ts
Code required? No — everything is pre-built Yes — you write defineWorkflow(...) + a 3-line composition root
What you get atomic chat (agent REPL), three autonomous builtins (ralph, deep-research-codebase, open-claude-design), session management, the live orchestrator panel, Atomic skills (/init, /research-codebase, /create-spec, …) defineWorkflow, createWorkflowCli, createRegistry, ctx.stage, s.save / s.transcript, headless stages, the Commander adapter (toCommand, runCli)
When to reach for You want autonomous execution of a standard pattern out of the box, or interactive chat with your agent's full toolset You want to encode your team's process — review flows, deployment gates, custom research pipelines — as TypeScript every teammate runs identically
Read next Quick Start (steps 1–3) Quick Start step 4 and Building your own atomic-powered app

Both surfaces call the same runtime underneath (tmux/psmux session graph, provider SDKs, detach/reattach) — they're two entry points, not two products. Neither depends on the other: you can bun add @bastani/atomic in a project without ever installing the global binary, and you can use atomic chat and the builtins without writing any TypeScript.


Quick Start

Install, generate context, try Ralph, then write your own workflow — four steps, a few minutes. Steps 1–3 are the CLI path (pre-built autonomous behaviour). Step 4 is the SDK path (your own workflows). Skip straight to step 4 if you only want the library.

Prerequisites

Atomic doesn't replace your coding agent or terminal — it orchestrates them. Three things have to exist on the host before a workflow can run:

  • Bun as the JavaScript runtime — Atomic and the SDK ship source that relies on Bun.spawn, native pty handling, and Bun-specific module resolution. They do not run on Node.js. The bootstrap installer below installs Bun for you; if you install @bastani/atomic manually, install Bun first.
  • A terminal multiplexer — every stage runs inside a detachable session on a dedicated atomic socket (your personal tmux is untouched). That's how workflows survive terminal disconnects, how -d/--detach puts a run in the background, and how atomic session connect reattaches later from any shell.
    • macOS / Linux: tmuxbrew install tmux or your distro's package manager
    • Windows: psmux — a PowerShell-native tmux-compatible shim, detected as psmux / pmux / tmux on PATH
  • At least one coding agent installed and logged in — Atomic spawns the agent's own CLI at each stage and talks to it via its SDK, so the CLI has to be present and authenticated:
  • Windows only: PowerShell 7+ (install guide)

The bootstrap installer below installs Bun and Atomic but does not install tmux/psmux or the coding agents. Install those separately before running any workflow — bun run src/claude-worker.ts -n <workflow-name> -a claude will fail loudly at stage spawn if either is missing. Using a devcontainer short-circuits all of this: the atomic feature bundles Bun + tmux + the agent CLI into the container image.

1. Install — CLI + SDK share the same package

@bastani/atomic ships both surfaces. A global install gives you the atomic CLI; a project-local install gives you the SDK import. Most users do both, but either stands alone.

CLI path — bootstrap script installs Bun, the atomic binary, and shell completions in one step:

# macOS / Linux
curl -fsSL https://raw.githubusercontent.com/flora131/atomic/main/install.sh | bash

# Windows (PowerShell 7+)
irm https://raw.githubusercontent.com/flora131/atomic/main/install.ps1 | iex

Upgrade later with bun update -g @bastani/atomic.

SDK-only path — if you only want to defineWorkflow(...) in your own TypeScript project and never need the atomic binary, skip the bootstrap and just add the library:

bun init -y                                      # new project
bun add @bastani/atomic                          # the SDK
bun add @anthropic-ai/claude-agent-sdk           # the provider SDK you target

Skip steps 2–3 below (those use the CLI) and jump straight to step 4. You'll still need tmux/psmux + an authenticated agent CLI at runtime — see Prerequisites.

Alternative: Already have Bun? Install the CLI directly from npm
bun install -g @bastani/atomic

This skips the Bun install step but doesn't set up shell completions — run atomic completions <shell> separately if you want them (see Commands Reference).

Prerelease builds: bun install -g @bastani/atomic@next (may contain breaking changes).

Authenticated downloads (CI / enterprise)

Set GITHUB_TOKEN to avoid GitHub API rate limits when running the bootstrap script in CI:

# macOS / Linux
GITHUB_TOKEN=ghp_... curl -fsSL https://raw.githubusercontent.com/flora131/atomic/main/install.sh | bash

# Windows PowerShell
$env:GITHUB_TOKEN='ghp_...'; irm https://raw.githubusercontent.com/flora131/atomic/main/install.ps1 | iex
Alternative: Devcontainer (recommended for autonomous workflows)

Devcontainers isolate the agent from your host, limiting the blast radius of destructive actions. This is the safest way to run workflows.

Add one feature to .devcontainer/devcontainer.json:

Feature Agent
ghcr.io/flora131/atomic/claude:1 Atomic + Claude Code
ghcr.io/flora131/atomic/opencode:1 Atomic + OpenCode
ghcr.io/flora131/atomic/copilot:1 Atomic + Copilot CLI

Full .devcontainer.json templates per agent live in .devcontainer/. Each feature installs Atomic, bun, playwright-cli, agent configs, and the agent CLI itself. First run takes ~1 minute to warm up.

Minimal example (Claude + Rust):

{
  "image": "mcr.microsoft.com/devcontainers/rust:latest",
  "features": {
    "ghcr.io/devcontainers/features/common-utils": {},
    "ghcr.io/flora131/atomic/claude:1": {},
    "ghcr.io/devcontainers/features/github-cli:1": {}
  },
  "remoteEnv": {
    "ANTHROPIC_API_KEY": "${localEnv:ANTHROPIC_API_KEY}"
  }
}

Use the Dev Containers VS Code extension or the Dev Container CLI to start the container.

Migrating from the old standalone binary?

Atomic used to ship as a standalone binary. It's now an npm package. One-time migration:

atomic uninstall
bun uninstall -g @bastani/atomic-workflows
rm -rf ~/.atomic ~/.copilot/skills ~/.opencode/skills
bun install -g @bastani/atomic

2. Generate context files — CLI

atomic chat -a <claude|opencode|copilot>

Then type /init. Atomic explores your codebase with sub-agents and writes CLAUDE.md / AGENTS.md so every future session starts with the right context.

3. Try Ralph — CLI (autonomous coding)

Ralph plans, implements, reviews, and debugs a task on its own — up to 10 iterations, exiting after 2 consecutive clean reviews.

atomic workflow -n ralph -a claude "Build a REST API for user management"

⚠️ Workflows run with agent permission checks disabled so pipelines don't block on prompts. Run them in a devcontainer or git worktree, not on your host. See Security.

4. Build your own workflow — SDK

Every team has a process — code review, CI checks, PR creation, approval, merge. Encode it as TypeScript once; everyone runs the same pipeline.

bun init && bun add @bastani/atomic

Author the workflow in src/workflows/review-to-merge/claude.ts:

import { defineWorkflow } from "@bastani/atomic/workflows";

export default defineWorkflow({
  name: "review-to-merge",
  description: "Review → CI → PR → Notify → Approve → Merge",
}).for("claude")
  .run(async (ctx) => {
    // 1. Review
    const review = await ctx.stage({ name: "review" }, {}, {}, async (s) => {
      await s.session.query("Review uncommitted changes for correctness, security, style.");
      s.save(s.sessionId);
    });

    // 2. Run security + CI in parallel
    await Promise.all([
      ctx.stage({ name: "security-scan" }, {}, {}, async (s) => {
        await s.session.query("Run `bun audit` and scan for leaked secrets.");
        s.save(s.sessionId);
      }),
      ctx.stage({ name: "ci-checks" }, {}, {}, async (s) => {
        await s.session.query("Run `bun lint` and `bun test`. Report failures.");
        s.save(s.sessionId);
      }),
    ]);

    // 3. Open PR, then notify Slack + wait for human approval
    await ctx.stage({ name: "notify-and-merge" }, {}, {}, async (s) => {
      const t = await s.transcript(review);
      await s.session.query(`Read ${t.path}. Open a PR summarizing the changes.`);

      await fetch("https://slack.com/api/chat.postMessage", {
        method: "POST",
        headers: { Authorization: `Bearer ${process.env.SLACK_TOKEN}` },
        body: JSON.stringify({ channel: "#code-review", text: "PR ready — please approve." }),
      });

      // Human-in-the-loop: pauses until the user responds
      await s.session.query(
        "Ask the user to confirm approval, then merge with `gh pr merge --squash`.",
        { allowedTools: ["Bash", "Read", "AskUserQuestion"] },
      );
      s.save(s.sessionId);
    });
  })
  .compile();

Wire it to a CLI in src/claude-worker.ts — three lines:

import { createWorkflowCli } from "@bastani/atomic/workflows";
import workflow from "./workflows/review-to-merge/claude.ts";

await createWorkflowCli(workflow).run();

Run it:

bun run src/claude-worker.ts -n review-to-merge -a claude

That's the full shape — one workflow file, one three-line composition root. createWorkflowCli handles named dispatch (-n/--name + -a/--agent), the --<input> flags declared by your workflow, detached execution, and the interactive picker. Pass an array (createWorkflowCli([claude, copilot])) for multi-agent or multi-workflow apps; the file stays three lines. See Workflow SDK for parallel stages, input schemas, headless stages, and the full API reference.

Managing sessions

Every chat and workflow runs inside an isolated tmux session on a dedicated socket (your personal tmux is untouched). If your terminal disconnects, your session keeps running — reconnect anytime.

atomic session list              # all sessions
atomic session connect           # interactive fuzzy picker
atomic session connect <name>    # by name
atomic session kill <name>       # kill one (or all, with confirmation)

Session names follow atomic-chat-<id> or atomic-wf-<workflow>-<id>. Scope with atomic chat session … or atomic workflow session ….

Need a workflow to run in the background while you do something else? Pass -d / --detach:

atomic workflow -n ralph -a claude -d "build the auth module"   # returns immediately
atomic workflow session connect atomic-wf-claude-ralph-<id>      # attach later

Detached mode is what you want for scripted / CI automation and long-running tasks — the orchestrator keeps running on the atomic tmux socket regardless of your terminal.


Why Atomic

Better models make harnesses more important, not less. The more you trust an agent to execute complex tasks, the more value you get from defining exactly what it should execute, in what order, with what checks along the way. The harness is the durable layer — models keep improving underneath it, but your process stays the same.

  • Start for yourself. Automate the repetitive parts of your own workflow — research a codebase, add monitoring, generate specs. One TypeScript file, one afternoon.
  • Scale to your team. Encode your team's review process, deployment gates, and quality checks as TypeScript every member runs identically — versioned, testable, reproducible.
  • Work across agents. Write a harness once, run it on Claude Code, OpenCode, or Copilot CLI with a flag change.

Example use cases

These are shapes you'd author with defineWorkflow and then run from your own src/<agent>-worker.ts — see step 4 of Quick Start for the three-line entrypoint. Atomic ships three built-in workflows (ralph, deep-research-codebase, open-claude-design); everything else is yours to define.

Add production monitoring. Research observability gaps, implement missing metrics and health checks, review the changes.

bun run src/claude-worker.ts -n observability -a claude "add Prometheus metrics and health checks to all API endpoints"

Parallel UX testing with 50 personas. Spin up 50 agents, each with a distinct persona (power user, accessibility-dependent, non-technical stakeholder), each using Playwright to test your app.

bun run src/claude-worker.ts -n ux-personas -a claude --personas=50

Review-to-merge pipeline. The workflow from step 4 above — reviews code, runs CI in parallel, opens a PR, notifies Slack, waits for approval, merges.


Table of Contents


Security: Workflow Permissions Model

Caution

Atomic workflows run coding agents with all permission checks disabled. The agent can read, write, and delete files, execute arbitrary shell commands, and make network requests without prompting. This is required for unattended pipelines. Run workflows in a devcontainer, not on your host machine.

Agent How permissions are bypassed Key flags / settings
Claude Code CLI flag disables the interactive permission prompt entirely --dangerously-skip-permissions
GitHub Copilot CLI CLI flag enables auto-execution; SDK auto-approves all tool requests --yolo, COPILOT_ALLOW_ALL=true, onPermissionRequest: approveAll
OpenCode Permissions handled programmatically through the event stream Permission requests auto-replied via SSE events

Defaults live in src/services/config/definitions.ts and src/sdk/runtime/executor.ts. Override per-project via ProviderOverrides in .atomic/settings.jsonchatFlags replaces defaults entirely; envVars are merged.


Core Features

Multi-Agent Support

Atomic works across three production coding agents — switch with a flag and your workflows, skills, and sub-agents carry over.

Agent Command
Claude Code atomic chat -a claude
OpenCode atomic chat -a opencode
GitHub Copilot CLI atomic chat -a copilot

Each agent gets its own configuration directory (.claude/, .opencode/, .github/), skills, and context files — all managed by Atomic.

Workflow SDK — Build Your Own Deterministic Harness

The Workflow SDK (@bastani/atomic/workflows) lets you encode your team's process as TypeScript — spawn agent sessions dynamically with native control flow (for, if, Promise.all()), and watch them appear in a live graph as they execute.

Set up a workflow project (bun init && bun add @bastani/atomic), define your workflow with defineWorkflow, then bind it to a CLI with createWorkflowCli(definition) (single workflow) or createWorkflowCli(registry) (many workflows):

bun run src/claude-worker.ts -n <workflow-name> -a claude --prompt "describe this project"

See step 4 of Quick Start for a complete review-to-merge example. More examples and the full API reference below.

Runnable examples shipped with the repo

The examples/ directory contains small, complete user apps you can run directly. Most subdirectories ship claude/, copilot/, and opencode/ variants plus one agent-scoped worker file per agent — claude-worker.ts, copilot-worker.ts, opencode-worker.ts — each a three-line createWorkflowCli(workflow).run() entrypoint. multi-workflow/ and commander-embed/ use a single cli.ts instead, to demonstrate multi-workflow dispatch and Commander embedding respectively.

Example What it demonstrates
hello-world Minimal single-session workflow with structured inputs (greeting, style, optional notes)
sequential-describe-summarize Two stages passing data via s.save()s.transcript(handle) — the canonical handoff pattern
parallel-hello-world Promise.all() fan-out and transcript merge
headless-test Visible seed → 3 parallel headless stages → visible merge → headless verdict
hil-favorite-color Human-in-the-loop prompt mid-workflow
hil-favorite-color-headless HIL pause inside a headless stage
structured-output-demo Per-SDK structured output (JSON-schema validation, Zod)
reviewer-tool-test Custom reviewer tool wiring (Copilot — copilot-worker.ts only)
review-fix-loop Draft → loop(review → fix) with bounded iterations and early exit on a CLEAN verdict — the quintessential harness pattern, showing how a stage's return value (handle.result) drives TypeScript control flow
multi-workflow Two Claude workflows under one cli.ts-n/--name dispatch, per-workflow --<input> flag union, and the interactive picker. Shows the array form (createWorkflowCli([hello, goodbye])) and the createRegistry().register(...) variant side by side.
commander-embed Mount an atomic workflow under a parent Commander CLI with toCommand(cli, "greet"), alongside a plain Commander sibling command. runCli replaces program.parseAsync() and transparently handles detached orchestrator re-entry.

Run any of them with:

# Single-workflow examples — one worker file per agent
bun run examples/<name>/<agent>-worker.ts -n <workflow-name> -a <agent> [--field=value | "<prompt>"]

# e.g.
bun run examples/hello-world/claude-worker.ts -n hello-world -a claude --greeting="Hello" --style=casual
bun run examples/sequential-describe-summarize/claude-worker.ts -n sequential-describe-summarize -a claude --topic="Bun"
bun run examples/review-fix-loop/claude-worker.ts -n review-fix-loop -a claude --topic="adopting Bun" --max_iterations=3
bun run examples/headless-test/copilot-worker.ts -n headless-test -a copilot "TypeScript"

# Multi-workflow — one cli.ts, dispatch by `-n/--name`
bun run examples/multi-workflow/cli.ts -n hello   -a claude --who=Alex
bun run examples/multi-workflow/cli.ts -n goodbye -a claude --tone=melodramatic
bun run examples/multi-workflow/cli.ts -a claude              # interactive picker (TTY)

# Commander embedding — workflow mounted as a subcommand under a parent CLI
bun run examples/commander-embed/cli.ts greet -n greet -a claude --who=Alex
bun run examples/commander-embed/cli.ts status                # sibling Commander command
bun run examples/commander-embed/cli.ts --help                # all commands

Copy an example directory into your project as a starting point — swap the workflow import in each <agent>-worker.ts (or in cli.ts for the multi-workflow / commander-embed shapes) for your own definition and you're done.

Example: Sequential workflow (describe → summarize)
import { defineWorkflow } from "@bastani/atomic/workflows";

export default defineWorkflow({
  name: "my-workflow",
  description: "Two-session pipeline: describe -> summarize",
  inputs: [{ name: "prompt", type: "text", required: true, description: "task prompt" }],
}).for("claude")
  .run(async (ctx) => {
    const prompt = ctx.inputs.prompt ?? "";

    const describe = await ctx.stage(
      { name: "describe", description: "Ask Claude to describe the project" },
      {}, {},
      async (s) => {
        await s.session.query(prompt);
        s.save(s.sessionId);
      },
    );

    await ctx.stage(
      { name: "summarize", description: "Summarize the previous session's output" },
      {}, {},
      async (s) => {
        const research = await s.transcript(describe);
        await s.session.query(`Read ${research.path} and summarize in 2-3 bullets.`);
        s.save(s.sessionId);
      },
    );
  })
  .compile();
Example: Parallel workflow (describe → [summarize-a, summarize-b] → merge)
import { defineWorkflow } from "@bastani/atomic/workflows";

export default defineWorkflow({
  name: "parallel-demo",
  description: "describe -> [summarize-a, summarize-b] -> merge",
  inputs: [{ name: "prompt", type: "text", required: true, description: "task prompt" }],
}).for("claude")
  .run(async (ctx) => {
    const prompt = ctx.inputs.prompt ?? "";

    const describe = await ctx.stage({ name: "describe" }, {}, {}, async (s) => {
      await s.session.query(prompt);
      s.save(s.sessionId);
    });

    const [summarizeA, summarizeB] = await Promise.all([
      ctx.stage({ name: "summarize-a" }, {}, {}, async (s) => {
        const research = await s.transcript(describe);
        await s.session.query(`Read ${research.path} and summarize in 2-3 bullets.`);
        s.save(s.sessionId);
      }),
      ctx.stage({ name: "summarize-b" }, {}, {}, async (s) => {
        const research = await s.transcript(describe);
        await s.session.query(`Read ${research.path} and summarize in one sentence.`);
        s.save(s.sessionId);
      }),
    ]);

    await ctx.stage({ name: "merge" }, {}, {}, async (s) => {
      const bullets = await s.transcript(summarizeA);
      const oneliner = await s.transcript(summarizeB);
      await s.session.query(
        `Combine:\n\n## Bullets\n${bullets.content}\n\n## One-liner\n${oneliner.content}`,
      );
      s.save(s.sessionId);
    });
  })
  .compile();
Example: Structured-input workflow (declared schema + CLI flag validation)

Declare inputs on defineWorkflow and the CLI materialises one --<field>=<value> flag per entry. Required fields, enum membership, and unknown-flag rejection are validated before any tmux session spawns. The interactive picker renders the same schema as a form.

import { defineWorkflow } from "@bastani/atomic/workflows";

export default defineWorkflow({
  name: "gen-spec",
  description: "Convert a research doc into an execution spec",
  inputs: [
    {
      name: "research_doc",
      type: "string",
      required: true,
      description: "path to the research doc",
      placeholder: "research/docs/2026-04-11-auth.md",
    },
    {
      name: "focus",
      type: "enum",
      required: true,
      description: "how aggressively to scope the spec",
      values: ["minimal", "standard", "exhaustive"],
      default: "standard",
    },
    {
      name: "notes",
      type: "text",
      description: "extra guidance for the spec writer (optional)",
    },
  ],
}).for("claude")
  .run(async (ctx) => {
    const { research_doc, focus } = ctx.inputs;
    const notes = ctx.inputs.notes ?? "";

    await ctx.stage({ name: "write-spec" }, {}, {}, async (s) => {
      await s.session.query(
        `Read ${research_doc} and produce a ${focus} spec.` +
          (notes ? `\n\nExtra guidance:\n${notes}` : ""),
      );
      s.save(s.sessionId);
    });
  })
  .compile();

Wire it into src/claude-worker.ts (three lines — see step 4 of Quick Start) and run it:

# Scriptable; CI-friendly
bun run src/claude-worker.ts \
  -n gen-spec \
  -a claude \
  --research_doc=research/docs/2026-04-11-auth.md \
  --focus=standard
Example: Headless (background) stages for parallel data gathering

Stages can run headlessly (headless: true) — they execute the provider SDK in-process instead of spawning a tmux window. Headless stages are invisible in the graph but tracked via a background counter in the statusline.

import { defineWorkflow, extractAssistantText } from "@bastani/atomic/workflows";

export default defineWorkflow({
  name: "headless-demo",
  description: "seed -> [3 headless background] -> merge",
  inputs: [{ name: "prompt", type: "text", required: true, description: "task prompt" }],
}).for("claude")
  .run(async (ctx) => {
    const prompt = ctx.inputs.prompt ?? "";

    const seed = await ctx.stage(
      { name: "seed", description: "Generate overview" }, {}, {},
      async (s) => {
        const result = await s.session.query(prompt);
        s.save(s.sessionId);
        return extractAssistantText(result, 0);
      },
    );

    const [pros, cons, uses] = await Promise.all([
      ctx.stage({ name: "pros", headless: true }, {}, {}, async (s) => {
        const r = await s.session.query(`List 3 pros:\n\n${seed.result}`);
        s.save(s.sessionId);
        return extractAssistantText(r, 0);
      }),
      ctx.stage({ name: "cons", headless: true }, {}, {}, async (s) => {
        const r = await s.session.query(`List 3 cons:\n\n${seed.result}`);
        s.save(s.sessionId);
        return extractAssistantText(r, 0);
      }),
      ctx.stage({ name: "uses", headless: true }, {}, {}, async (s) => {
        const r = await s.session.query(`List 3 use cases:\n\n${seed.result}`);
        s.save(s.sessionId);
        return extractAssistantText(r, 0);
      }),
    ]);

    await ctx.stage(
      { name: "merge", description: "Combine results" }, {}, {},
      async (s) => {
        await s.session.query(
          `Combine:\n\n## Pros\n${pros.result}\n\n## Cons\n${cons.result}\n\n## Uses\n${uses.result}`,
        );
        s.save(s.sessionId);
      },
    );
  })
  .compile();

The graph shows seed → merge — headless stages are transparent to the topology. The callback API (s.client, s.session, s.save(), s.transcript(), return values) is identical to interactive stages.

Key capabilities:

Capability Description
Dynamic session spawning ctx.stage() spawns sessions at runtime — each gets its own tmux window and graph node
Native TypeScript control flow Use for, if/else, Promise.all(), try/catch — no framework DSL
Session return values Session callbacks can return data: const h = await ctx.stage(...); h.result
Transcript passing Access prior output via handle (s.transcript(handle)) or name (s.transcript("name"))
Declared input schemas Add an inputs: [...] array and the CLI materialises --<field>=<value> flags with built-in validation
Interactive picker atomic workflow -a <agent> is the explicit no--n discovery path; direct runs use -n <name>
Nested sub-sessions s.stage() inside a callback spawns child sessions — visible as nested graph nodes
Auto-inferred graph Topology derived from await / Promise.all patterns — no annotations
Provider-agnostic Write raw SDK code for Claude, Copilot, or OpenCode inside each callback
Live graph visualization Sessions appear in the TUI graph as they spawn — loops and conditionals visible in real time
Background (headless) stages headless: true runs in-process without a tmux window — invisible in graph, tracked by statusline counter, identical callback API

Deterministic execution guarantees:

Workflows are deterministic by design — the same definition produces the same execution order with the same data flow, anywhere.

  • Strict step ordering — Step 2 never starts until Step 1 finishes. Parallel sessions complete (or fail fast) before the next step begins.
  • Frozen definitions.compile() freezes the workflow. Once compiled, step order, session names, and the execution graph are immutable.
  • Controlled transcript access — Sessions only read transcripts from completed upstream sessions; parallel siblings can't read each other.
  • Isolated context windows — Each session runs in its own tmux pane with a fresh context. Data flows only through explicit ctx.transcript() / ctx.getMessages() calls.
  • Persisted artifacts — Every session writes messages, transcript, and metadata to disk — a complete, inspectable execution record.

Variance comes only from the LLM's responses, not from the harness.

Ask Atomic to build workflows for you: Use your workflow-creator skill to create a workflow that plans, implements, and reviews a feature.

Full Workflow SDK Reference

Builder API

Method Purpose
defineWorkflow({ name, description }) Entry point — returns a WorkflowBuilder
.run(async (ctx) => { ... }) Set the workflow's entry point — ctx is a WorkflowContext
.compile() Required — terminal method that seals the workflow definition

WorkflowContext (ctx) — top-level orchestrator

Property Type Description
ctx.inputs { [K in N]?: string } Typed inputs for this run — only declared field names are valid keys. Accessing an undeclared field is a compile-time error. Workflows that need a prompt must declare it in their inputs schema
ctx.agent AgentType Which agent is running ("claude", "copilot", "opencode")
ctx.stage(opts, clientOpts, sessionOpts, fn) Promise<SessionHandle<T>> Spawn a session — returns handle with name, id, result
ctx.transcript(ref) Promise<Transcript> Get a completed session's transcript ({ path, content })
ctx.getMessages(ref) Promise<SavedMessage[]> Get a completed session's raw native messages

SessionContext (s) — inside each session callback

Property Type Description
s.client ProviderClient<A> Pre-created SDK client (auto-managed by runtime)
s.session ProviderSession<A> Pre-created provider session (auto-managed by runtime)
s.inputs { [K in N]?: string } Same typed inputs as ctx.inputs, forwarded into every stage so callbacks can read values without closing over the outer ctx
s.agent AgentType Which agent is running
s.paneId string tmux pane ID for this session
s.sessionId string Session UUID
s.sessionDir string Path to this session's storage directory on disk
s.save(messages) SaveTranscript Save this session's output for subsequent sessions
s.transcript(ref) Promise<Transcript> Get a completed session's transcript
s.getMessages(ref) Promise<SavedMessage[]> Get a completed session's raw native messages
s.stage(opts, clientOpts, sessionOpts, fn) Promise<SessionHandle<T>> Spawn a nested sub-session (child in the graph)

Session Options (SessionRunOptions)

Property Type Description
name string Unique session name within the workflow run
description string? Human-readable description shown in the graph
headless boolean? When true, run in-process without a tmux window — invisible in graph, tracked by background counter

The runtime auto-infers parent-child edges from execution order: sequential await creates a chain, Promise.all creates parallel fan-out/fan-in — no annotations needed.

Saving Transcripts

Each provider saves transcripts differently:

Provider How to Save
Claude s.save(s.sessionId) — auto-reads via getSessionMessages()
Copilot s.save(await session.getMessages()) — pass SessionEvent[]
OpenCode s.save(result.data!) — pass the full { info, parts } response

Per-Agent Session APIs

The runtime auto-creates s.client and s.session — use them directly inside the callback:

Agent How to send a prompt
Claude await s.session.query(prompt)
Copilot await s.session.send({ prompt })
OpenCode await s.client.session.prompt({ sessionID: s.session.id, parts: [{ type: "text", text: prompt }] })

Key Rules

  1. Every workflow definition must call .run() and .compile() on the builder
  2. Session names must be unique within a workflow run
  3. transcript() / getMessages() only access completed sessions (callback returned + saves flushed)
  4. Each session runs in its own tmux window with the chosen agent
  5. Bind a workflow to a CLI with createWorkflowCli(workflow) (single workflow) or createWorkflowCli(createRegistry().register(...)) (many workflows)
  6. Set up your workflow project with bun init && bun add @bastani/atomic
  7. Background (headless) stages use the same callback API — s.client, s.session, s.save(), return values all work identically

For the authoring walkthrough ask Atomic to use the workflow-creator skill or read .agents/skills/workflow-creator/.

[!TIP] When the Workflow SDK is updated, ask the workflow-creator skill to migrate your workflows to the latest patterns: "Update this workflow to use the latest SDK patterns."

Research Codebase

The /research-codebase command dispatches specialized sub-agents in parallel to analyze your codebase — understand auth flows, trace root causes, query docs, and hit external sources via DeepWiki MCP. Get up to speed on a new project in minutes instead of hours.

Sub-Agent Model Purpose
codebase-locator Haiku Locate files, directories, and components relevant to the research topic
codebase-analyzer Sonnet Analyze implementation details, trace data flow, explain technical workings
codebase-pattern-finder Haiku Find similar implementations, usage examples, and existing patterns to model after
codebase-online-researcher Sonnet Fetch up-to-date information from the web and repository knowledge from DeepWiki
codebase-research-locator Haiku Discover relevant documents in research/ and specs/ directories
codebase-research-analyzer Sonnet Extract high-value insights, decisions, and technical details from research documents

Run parallel research sessions to compare approaches:

# Terminal 1: LangChain approach
atomic chat -a claude "/research-codebase Research GraphRAG using LangChain's graph retrieval."

# Terminal 2: Microsoft GraphRAG
atomic chat -a claude "/research-codebase Research GraphRAG using microsoft/graphrag."

# Terminal 3: LlamaIndex approach
atomic chat -a claude "/research-codebase Research GraphRAG using LlamaIndex's property graph."

Then run /create-spec on each output, spin up git worktrees, and run atomic workflow -n ralph -a <agent> in each — wake up to three complete implementations on separate branches. Research persists in research/ and specs in specs/, so every investigation compounds into future context.

Why specialized research agents instead of one general-purpose agent?

A single agent asked to "research the auth system" tries to search, read, analyze, and summarize within one context window. As that window fills with file contents, search results, and intermediate reasoning, synthesis degrades — this is a fundamental constraint of transformer attention, not a prompt-engineering problem.

Atomic dispatches purpose-built sub-agents: a codebase-locator only finds relevant files, a codebase-analyzer only reads and analyzes implementations, a codebase-online-researcher only queries external docs. Each operates in its own context with only the tools it needs; the parent receives distilled findings. The result: faster research, higher-quality findings, less hallucination.

Autonomous Execution (Ralph)

Ralph Wiggum

The Ralph Method enables multi-hour autonomous coding sessions. Approve your spec, let Ralph work in the background, focus on other things.

How Ralph works:

  1. Task Decomposition — A planner sub-agent breaks your spec into a task list with dependency tracking, stored in SQLite (WAL mode for parallel access).
  2. Orchestration — An orchestrator retrieves the task list, validates the dependency graph, and dispatches worker sub-agents for ready tasks.
  3. Review & Debug — A reviewer audits the implementation with structured JSON output; if P0–P2 findings exist, a debugger investigates root causes and feeds back to the planner on the next iteration.

Loop config: Up to 10 iterations. Exits early after 2 consecutive clean reviews (zero actionable findings). P3 (minor) findings are non-actionable.

# From a prompt
atomic workflow -n ralph -a <claude|opencode|copilot> "Build a REST API for user management"

# From a spec file
atomic workflow -n ralph -a claude "specs/YYYY-MM-DD-my-feature.md"

Best practice: run Ralph in a git worktree so autonomous changes stay isolated from your working tree:

git worktree add ../my-project-ralph feature-branch
cd ../my-project-ralph
atomic workflow -n ralph -a claude "Build the auth module"

Deep Research Codebase

Atomic ships a deep-research-codebase workflow that performs multi-agent parallel research across your codebase — a full pipeline, not a single-shot command.

  1. Scout — One agent scans the codebase structure and writes an architectural orientation.
  2. History — A parallel agent surfaces prior research from research/docs/.
  3. Explorers — Multiple parallel agents (count scaled by LOC) each investigate a partition.
  4. Aggregator — A final agent synthesizes all explorer reports + history into a dated research doc at research/docs/YYYY-MM-DD-<slug>.md.
atomic workflow -n deep-research-codebase -a claude "How does the authentication system work?"

The output is a permanent research artifact that future runs, specs, and workflows can reference.

Containerized Execution

Atomic ships as devcontainer features that bundle the CLI, agent, and all dependencies into isolated containers — the recommended way to run autonomous agents safely.

Why containerize?

  • Agents run rm, git reset --hard, and arbitrary shell commands — containers limit blast radius
  • Reproducible environments across team members and CI
  • Pre-installed dependencies: bun, playwright-cli, agent CLI, GitHub CLI
  • Features versioned in sync with Atomic releases
Feature Installs
ghcr.io/flora131/atomic/claude:1 Atomic + Claude Code
ghcr.io/flora131/atomic/opencode:1 Atomic + OpenCode
ghcr.io/flora131/atomic/copilot:1 Atomic + Copilot CLI

See Quick Start → Devcontainer for a working .devcontainer.json and the .devcontainer/ directory for per-agent templates.

Specialized Sub-Agents

Atomic dispatches purpose-built sub-agents, each with scoped context, tools, and termination conditions:

Sub-Agent Purpose
planner Decompose specs into structured task lists with dependency tracking
worker Implement single focused tasks (multiple workers run in parallel)
reviewer Audit implementations against specs and best practices
code-simplifier Simplify and refine code for clarity, consistency, maintainability
orchestrator Coordinate complex multi-step workflows
codebase-analyzer Analyze implementation details of specific components
codebase-locator Locate files, directories, and components
codebase-pattern-finder Find similar implementations and usage examples
codebase-online-researcher Research using web sources and DeepWiki
codebase-research-analyzer Deep dive on research topics
codebase-research-locator Find documents in research/ directory
debugger Debug errors, test failures, and unexpected behavior
Why specialize instead of using one general-purpose agent?

LLMs have an architectural limitation: the more context they hold, the harder it becomes to attend to the right information. A single agent juggling a spec, dozens of files, tool outputs, and its own reasoning will lose details, repeat work, or hallucinate connections. This isn't solvable via prompt engineering — it's how attention mechanisms work.

Specialized sub-agents turn the limitation into an advantage:

  • Context isolation — Fresh, minimal context scoped to one job. A codebase-locator doesn't carry file contents; a worker doesn't carry the full spec.
  • Tool scoping — Agents only see tools relevant to their role. A reviewer has read-only tools and can't edit files; a worker has edit tools but can't spawn other workers.
  • Parallel execution — Independent sub-agents run concurrently. One worker writes the migration, another writes the handler, a third generates tests — all at once.
  • Composability — Sub-agents combine into workflows or dispatch ad-hoc. The same reviewer used by Ralph is the one invoked when you ask for a code review in chat.

A specialized codebase-analyzer reading three files produces more accurate output than a generalist that has already consumed 50,000 tokens of search results and prior reasoning.

Use /agents in any chat session to see all available sub-agents.

Built-in Skills

Skills are structured capability modules that give agents best practices and reusable workflows. Atomic ships 57 skills across eight categories; each lives at .agents/skills/<name>/SKILL.md and is auto-invoked when the agent detects a relevant trigger.

Development workflows
Skill Description
init Generate CLAUDE.md and AGENTS.md by exploring the codebase
research-codebase Analyze codebase with parallel sub-agents and document findings
create-spec Create detailed execution plans from research documents
workflow-creator Create multi-agent workflows using the session-based defineWorkflow() API
explain-code Explain code functionality in detail using DeepWiki
find-skills Discover and install agent skills from the community
test-driven-development Write tests first; includes a testing anti-patterns guide
prompt-engineer Create, improve, and optimize prompts using best practices
Context engineering — working within (and around) LLM context limits
Skill Description
context-fundamentals How context windows work; attention mechanics; progressive disclosure
context-degradation Diagnose lost-in-middle, poisoning, distraction failures in long runs
context-compression Summarize transcripts at session boundaries; preserve actionable info
context-optimization KV-cache optimization, observation masking, context budgeting
filesystem-context Offload context to files; file-based agent coordination
memory-systems Cross-session knowledge retention; Mem0 / Zep / Letta comparisons
multi-agent-patterns Supervisor, swarm, handoff patterns for multi-agent systems
tool-design Design clear tool contracts; reduce agent-tool friction
hosted-agents Background agents in sandboxed VMs; warm pools; Modal sandboxes
project-development Validate task-model fit before building; cost estimation
bdi-mental-states Belief-desire-intention models for explainable agent reasoning
TypeScript & runtime
Skill Description
typescript-expert Type-level programming, perf optimization, migrations
typescript-advanced-types Generics, conditional types, mapped types, template literals
typescript-react-reviewer Expert review for TypeScript + React 19 applications
bun Build, test, deploy with Bun (runtime, package manager, bundler, tests)
opentui Build terminal UIs with OpenTUI (core, React, Solid reconcilers)
Frontend design & UI polish — used by `impeccable` and invoked individually for targeted refinement
Skill Description
impeccable Create distinctive, production-grade frontend interfaces
polish Final quality pass on alignment, spacing, consistency
critique UX evaluation with quantitative scoring and persona testing
audit Accessibility, performance, theming, responsive, anti-pattern audit
layout / typeset / colorize Layout, typography, and color refinement
adapt Responsive design: breakpoints, fluid layouts, touch targets
animate / delight Add motion, micro-interactions, and personality
clarify Improve UX copy, error messages, microcopy, labels
distill / quieter / bolder / overdrive Simplify, tone down, amplify, or push designs to their limit
harden Error handling, onboarding, empty states, i18n, overflow, edge-case resilience
optimize Diagnose and fix loading, rendering, animation, bundle-size issues
Evaluation, documents, git, meta

Evaluation:

Skill Description
evaluation Multi-dimensional evaluation, LLM-as-judge, quality gates
advanced-evaluation Pairwise comparison, position-bias mitigation, evaluation pipelines

Documents & parsing:

Skill Description
pdf Read, create, edit, split, merge, and OCR PDF files
xlsx Create, read, edit, and fix spreadsheet files (.xlsx, .csv, .tsv)
docx Create, read, edit, and manipulate Word (.docx) documents
pptx Create, read, edit, and manipulate PowerPoint (.pptx) slide decks
liteparse Parse and convert unstructured files (PDF, DOCX, PPTX, images) locally

Git / Azure DevOps / Sapling / automation:

Skill Description
gh-commit Conventional-commit Git commits
gh-create-pr Commit unstaged changes, push, and submit a GitHub PR
ado-commit Conventional-commit Git commits for Azure DevOps (adds AB#<id> trailers)
ado-create-pr Commit, push, and open an Azure DevOps PR via the azure-devops MCP server
sl-commit Conventional-commit Sapling commits
sl-submit-diff Submit Sapling commits as Phabricator diffs
playwright-cli Automate browser interactions, tests, screenshots

Note on source control providers: the GitHub and Azure DevOps MCP servers are disabled by default to avoid consuming tokens on projects that don't need them. Set scm in .atomic/settings.json (or run atomic config set scm <provider>) to github, azure-devops, or sapling — on every atomic chat / atomic workflow startup Atomic reconciles .claude/settings.json (disabledMcpjsonServers), .opencode/opencode.json (mcp.<server>.enabled), and appends --disable-mcp-server <name> to the Copilot CLI invocation (Copilot has no on-disk MCP toggle). sapling disables both servers everywhere.

Meta:

Skill Description
skill-creator Create, modify, evaluate, and benchmark your own skills

Skills are auto-invoked when relevant. Run ls .agents/skills/ for the complete, current list on disk.

Workflow Orchestrator Panel

During atomic workflow execution, Atomic renders a live orchestrator panel built on OpenTUI over the workflow's tmux session graph. It shows:

  • Session graph — Nodes per .stage() with status (pending / running / completed / failed) and edges for sequential / parallel dependencies
  • Task list tracking — Ralph's decomposed task list with dependency arrows, updated in real time
  • Pane previews — Thumbnail of each tmux pane so you can see every agent without context-switching
  • Transcript passing visibility — Highlights s.save() / s.transcript() handoffs as they happen

During atomic chat, there is no Atomic-owned TUI — atomic chat -a <agent> spawns the native agent CLI inside a tmux session, so chat features (streaming, @ mentions, /slash-commands, model selection, theme, keyboard shortcuts) come from the agent CLI itself. Atomic handles config sync, tmux session management, and argument passthrough.

Context UI provider
atomic workflow -n <name> -a <agent> Atomic (orchestrator panel + tmux session graph)
atomic chat -a <agent> The native agent CLI (Claude Code / OpenCode / Copilot CLI)

Commands Reference

CLI Commands

Command Description
atomic chat Spawn the native agent CLI inside a tmux session
atomic workflow Run a named multi-session workflow with the Atomic orchestrator panel
atomic workflow list List available workflows, grouped by source
atomic session list List all running sessions on the atomic tmux socket
atomic session connect [name] Attach to a session (interactive picker when no name given)
atomic session kill [name] Kill a session by name, or all sessions when no name is given
atomic completions <shell> Output shell completion script (bash, zsh, fish, powershell)
atomic config set <k> <v> Set configuration values (supports telemetry and scm)

Global Flags

Flag Description
-y, --yes Auto-confirm all prompts (non-interactive)
--no-banner Skip ASCII banner display
-v, --version Show version number

atomic session Subcommands

Available at three levels — scoped or global:

Command Description
atomic session list List all running sessions
atomic session connect [name] Attach to a session (interactive picker when no name)
atomic session kill [name] Kill a session, or all sessions when no name is given
atomic chat session list List running chat sessions only
atomic chat session connect [name] Attach to a chat session
atomic chat session kill [name] Kill a chat session, or all chat sessions
atomic workflow session list List running workflow sessions only
atomic workflow session connect [name] Attach to a workflow session
atomic workflow session kill [name] Kill a workflow session, or all workflow sessions

list, connect, and kill accept -a <agent> (repeatable) to filter by agent. kill prompts for confirmation.

atomic session list                      # all sessions
atomic session list -a claude            # only Claude sessions
atomic session connect my-session        # attach by name
atomic session connect                   # interactive picker
atomic chat session list -a copilot      # chat sessions for Copilot only
atomic session kill my-session           # kill one session by name
atomic session kill                      # kill all sessions (with confirmation)
atomic workflow session kill -a claude   # kill all Claude workflow sessions

atomic chat Flags

Flag Description
-a, --agent <name> Agent: claude, opencode, copilot

All other arguments are forwarded directly to the native agent CLI:

atomic chat -a claude "fix the bug"          # initial prompt
atomic chat -a copilot --model gpt-5.4       # custom model
atomic chat -a claude --verbose              # forward --verbose to claude

atomic workflow Flags

Flag Description
-n, --name <name> Workflow name (required for direct runs; omit only for the interactive picker)
-a, --agent <name> Agent: claude, opencode, copilot
-d, --detach Start the workflow in the background without attaching — ideal for scripted / CI runs; attach later with atomic workflow session connect <name>
--<field>=<value> Structured input for workflows that declare an inputs schema (also accepts --<field> <value>)
[prompt...] Positional prompt — requires the workflow to declare a prompt input

Five invocation shapes:

# 1. List every workflow available, grouped by source
atomic workflow list
atomic workflow list -a claude       # filter by agent

# 2. Launch the interactive picker (no -n) — fuzzy-search, fill the form, confirm with y/n
atomic workflow -a claude

# 3. Run with a positional prompt (workflow must declare a "prompt" input)
atomic workflow -n ralph -a claude "build a REST API for user management"

# 4. Run a structured-input workflow with one --<field> flag per declared input
atomic workflow -n open-claude-design -a claude \
  --prompt="a dashboard for monitoring API latency" \
  --output-type=prototype

# 5. Run detached — orchestrator runs in the background; prints the session name
#    and returns immediately. Attach any time with `atomic workflow session connect`.
atomic workflow -n ralph -a claude -d "build a REST API for user management"

Workflows that declare inputs: WorkflowInput[] get CLI flag validation for free. Builtin workflows (e.g. ralph) are reserved — a local/global workflow with the same name will not shadow a builtin.

atomic completions — Shell Completions

Atomic ships tab-completion for bash, zsh, fish, and PowerShell. Cache the script once so new shells don't re-spawn the atomic binary on startup.

Bash / Zsh / Fish / PowerShell setup

Bash

mkdir -p ~/.atomic/completions
atomic completions bash > ~/.atomic/completions/atomic.bash
echo '[ -f "$HOME/.atomic/completions/atomic.bash" ] && source "$HOME/.atomic/completions/atomic.bash"' >> ~/.bashrc

Zsh

mkdir -p ~/.atomic/completions
atomic completions zsh > ~/.atomic/completions/atomic.zsh
echo '[ -f "$HOME/.atomic/completions/atomic.zsh" ] && source "$HOME/.atomic/completions/atomic.zsh"' >> ~/.zshrc

Fish

atomic completions fish > ~/.config/fish/completions/atomic.fish

PowerShell

$cache = Join-Path $HOME '.atomic\completions\atomic.ps1'
New-Item -ItemType Directory -Force -Path (Split-Path $cache) | Out-Null
atomic completions powershell | Out-File -FilePath $cache -Encoding utf8
Add-Content $PROFILE "`nif (Test-Path `"$cache`") { . `"$cache`" }"

The bootstrap installer (install.sh / install.ps1) sets this up automatically and migrates older eval "$(atomic completions …)" snippets to the cached form.

Atomic-Provided Skills (invokable from any agent chat)

Atomic ships skills — not slash commands. Skills are auto-discovered by Claude Code, OpenCode, and Copilot CLI, invoked by typing /<skill-name> (Claude Code) or by natural-language reference (OpenCode / Copilot CLI).

Skill Typical invocation Purpose
init /init Generate CLAUDE.md and AGENTS.md by exploring the codebase
research-codebase /research-codebase "<question>" Dispatch parallel sub-agents to analyze the codebase and write a research doc
create-spec /create-spec "<research-path>" Produce a technical spec grounded in a research document
explain-code /explain-code "<path>" Deep-dive explanation of specific code using DeepWiki
gh-commit /gh-commit Create a conventional-commit Git commit
gh-create-pr /gh-create-pr Commit, push, and open a GitHub pull request
ado-commit /ado-commit Create a conventional-commit Git commit on an Azure DevOps-hosted repo
ado-create-pr /ado-create-pr Commit, push, and open an Azure DevOps PR through the azure-devops MCP server
sl-commit /sl-commit Create a Sapling commit
sl-submit-diff /sl-submit-diff Submit a Sapling commit as a Phabricator diff
workflow-creator natural language Generate a multi-agent workflow definition using defineWorkflow + registry

Native slash commands (/help, /clear, /compact, /model, /theme, /agents, /mcp, /exit) come from the underlying agent CLI, not Atomic.


Building your own atomic-powered app

@bastani/atomic/workflows is a library, not just a CLI. Use it directly to build your own TypeScript app that runs your team's workflows.

SDK-only users: you don't need the global atomic binary, but you still need the runtime prerequisites — Bun (the SDK does not run on Node.js), a terminal multiplexer (tmux on macOS/Linux, psmux on Windows), and at least one authenticated coding agent CLI (claude, opencode, or copilot). See Prerequisites for the "why" and install commands. The SDK spawns the agent CLI at each stage and wraps it in a detachable multiplexer session — those are orchestration primitives the SDK doesn't embed.

Management commands ship natively. createWorkflowCli auto-registers session and status subcommands on every worker CLI by default, so bun run src/claude-worker.ts session list, … status <id>, … session connect <id>, and … session kill <id> -y all work with zero extra code. Sessions live on the shared atomic tmux socket, so the worker CLI, the global atomic binary, and bunx atomic (for SDK-only installs) all see the same runtime state. Opt out with createWorkflowCli(workflow, { includeManagementCommands: false }) when you want a minimal CLI or are embedding under a parent Commander program that owns session management. The names session and status are reserved — workflow inputs with those names throw at defineWorkflow time to prevent flag collisions.

One factory, three input shapes

createWorkflowCli is the single factory. Pick whichever input shape matches how you organize your workflows:

Input When to use
createWorkflowCli(workflow) One workflow. Direct runs still use -n/--name + -a/--agent; the CLI exposes only that workflow's declared --<input> flags.
createWorkflowCli([wf1, wf2]) Multiple workflows inline. Uses the same -n/--name + -a/--agent dispatch and the interactive picker.
createWorkflowCli(registry) Dynamic composition (loop-register, conditional registration). Same runtime shape as the array form.

Single workflow (most common) — one file, three lines:

// src/claude-worker.ts
import { createWorkflowCli } from "@bastani/atomic/workflows";
import workflow from "./workflows/review-to-merge/claude.ts";

await createWorkflowCli(workflow).run({ inputs: { target_branch: "main" } });
// defaults above; CLI flags override.

Run it:

bun run src/claude-worker.ts -n review-to-merge -a claude --target_branch=release/v2

Multiple workflows — inline array:

// src/cli.ts
import { createWorkflowCli } from "@bastani/atomic/workflows";
import reviewToMerge from "./workflows/review-to-merge/claude.ts";
import genSpec from "./workflows/gen-spec/claude.ts";

await createWorkflowCli([reviewToMerge, genSpec]).run();

Run it:

bun run src/cli.ts -n review-to-merge -a claude
bun run src/cli.ts -a claude                    # interactive picker (TTY)

See examples/multi-workflow/ for a complete runnable version — two Claude workflows (hello, goodbye) registered under one cli.ts, with the createRegistry() variant shown side by side in a comment.

Dynamic composition — createRegistry:

import { createWorkflowCli, createRegistry } from "@bastani/atomic/workflows";

const registry = workflowFiles.reduce((r, wf) => r.register(wf), createRegistry());
await createWorkflowCli(registry).run();

Need a listing subcommand? Use toCommand(cli) from @bastani/atomic/workflows/commander and attach your own list subcommand — the same way atomic workflow list is wired up in src/cli.ts.

One method: run()

WorkflowCli exposes one method — run(options?). Default parses process.argv; pass argv: [...] to parse an explicit list, or argv: false to skip parsing entirely. inputs merge as defaults under CLI flags; argv: false makes them final. run() also accepts name / agent, which layer the same way.

The WorkflowCli type is framework-agnostic — no Commander imports in sight. If you want one, reach for the adapter below.

Example — programmatic invocation without argv:

// Single workflow: name + agent are still required when argv parsing is skipped.
await cli.run({
  argv: false,
  name: "review-to-merge",
  agent: "claude",
  inputs: { target_branch: "main" },
});

// Multi-workflow cli: name + agent required under argv: false.
await cli.run({
  argv: false,
  name: "review-to-merge",
  agent: "claude",
  inputs: { target_branch: "main" },
});

Embedding under a parent CLI — toCommand + runCli

For integration with a bigger Commander program, import the adapter from the dedicated subpath:

import { createWorkflowCli } from "@bastani/atomic/workflows";
import { toCommand, runCli } from "@bastani/atomic/workflows/commander";
import { Command } from "@commander-js/extra-typings";
import workflow from "./workflows/deploy/claude.ts";

const cli = createWorkflowCli(workflow);

const program = new Command("my-app");
program.addCommand(toCommand(cli, "deploy"));
program.command("hello").action(() => console.log("hi"));

// Replaces program.parseAsync(). runCli transparently handles detached
// re-entry — when the process is a tmux-spawned orchestrator, it drives
// runOrchestrator; otherwise it invokes your callback (argv parse + any
// bootstrap you want). PyTorch's init_process_group for rank-zero
// dispatch — no guards, no env-var checks in user code.
await runCli(cli, () => program.parseAsync());

toCommand(cli, "workflow") is exactly how the internal atomic workflow command is wired (src/commands/cli/workflow.ts). Because the Commander dependency lives only on the subpath, a future @bastani/atomic/workflows/yargs adapter can ship alongside without touching the core SDK.

entry — for bundled apps and test harnesses

createWorkflowCli accepts { entry?: string }, defaulting to process.argv[1]. That's the file the runtime re-executes on --detach to resume the orchestrator, so it has to be the composition root. Override it when you bundle the app (entry should point at the bundle), when the composition root isn't argv[1] (tests, embedded CLIs), or with import.meta.url for ESM-native correctness.

const cli = createWorkflowCli(workflow, { entry: import.meta.url });

Registry rules

  • createRegistry() returns an immutable registry. Each .register(wf) call returns a new registry — the original is unchanged. Chain calls to accumulate workflows.
  • Each workflow is keyed by ${agent}/${name} — the (agent, name) pair must be unique. Registering a duplicate throws immediately.
  • createWorkflowCli(registry) inspects every registered workflow and builds a union of their declared inputs. Same-name / same-type flags are shared; same-name / different-type conflicts throw at construction time so ambiguity never reaches runtime.
  • Builtin workflows (ralph, deep-research-codebase, open-claude-design) are managed by atomic's internal createBuiltinRegistry(). They are reserved — user-registered workflows with the same name will not shadow builtins when running the atomic CLI.

Input precedence

CLI flags always win when parsing is active. Under them, the order is:

  1. defineWorkflow default values (on each WorkflowInput)
  2. Layer supplied at construction or invocation:
    • cli.run({ inputs }) for the single-workflow shape
    • createWorkflowCli(registry, { inputs }) / cli.run({ inputs }) for the multi-workflow shape
  3. CLI flags — --<field>=<value> passed at runtime

With argv: false, the CLI-flag layer is skipped — your programmatic inputs become top-of-chain.

Builtin workflows via the atomic CLI

The atomic workflow command still works for the three built-in workflows — internally it's toCommand(createWorkflowCli(createBuiltinRegistry()), "workflow"):

atomic workflow -n ralph -a claude "Build the auth module"
atomic workflow -n deep-research-codebase -a claude "How does auth work?"
atomic workflow -n open-claude-design -a claude

These are not affected by your own createRegistry() — they are separate.

Migration from 0.x (directory-scanning) to current

This is a breaking change. The SDK no longer scans .atomic/workflows/ directories.

  1. Delete .atomic/workflows/ from your repo.
  2. Create one entrypoint file per agent, e.g. src/claude-worker.ts:
    import { createWorkflowCli } from "@bastani/atomic/workflows";
    import workflow from "./workflows/my-workflow/claude.ts";
    
    await createWorkflowCli(workflow).run();
  3. Update invocations: replace atomic workflow -n foo -a claude with bun run src/claude-worker.ts -n foo -a claude for your custom workflows. For the Atomic builtin set (ralph, deep-research-codebase, open-claude-design) keep using atomic workflow -n <name> -a <agent>.

Configuration

.atomic/settings.json

Resolution order:

  1. Local: .atomic/settings.json
  2. Global: ~/.atomic/settings.json
{
  "$schema": "https://raw.githubusercontent.com/flora131/atomic/main/assets/settings.schema.json",
  "version": 1,
  "scm": "github",
  "providers": {
    "claude": {
      "chatFlags": ["--model", "claude-sonnet-4-6"],
      "envVars": { "CLAUDE_CODE_MAX_OUTPUT_TOKENS": "16384" }
    }
  }
}
Field Type Description
$schema string JSON Schema URL for editor autocomplete
version number Config schema version (currently 1)
scm string Source control provider — github, azure-devops, or sapling. Reconciles the GitHub / Azure DevOps MCP servers in agent configs on startup.
providers object Per-provider overrides for claude, opencode, copilot. chatFlags replaces built-in defaults entirely; envVars are merged

Model selection and reasoning effort are managed by each underlying agent CLI (e.g. Claude Code's /model), not Atomic. Atomic's chat command spawns the agent's native TUI — use the agent's own controls.

Agent-Specific Files

Agent Folder Skills Context File
Claude Code .claude/ .claude/skills/ (symlink → .agents/skills/) CLAUDE.md
OpenCode .opencode/ .agents/skills/ AGENTS.md
GitHub Copilot .github/ .agents/skills/ AGENTS.md

All three agents share the same skill set via .agents/skills/. Claude Code accesses them through a .claude/skills/ symlink.


Updating & Uninstalling

Update

bun update -g @bastani/atomic      # latest stable
bun install -g @bastani/atomic@next # prerelease

The first atomic run after upgrading auto-syncs tooling deps and global skills — no separate command needed.

Uninstall

bun remove -g @bastani/atomic
Also remove global config and cached agent configs
# macOS / Linux
rm -rf ~/.atomic/

# Windows PowerShell
Remove-Item -Path "$env:USERPROFILE\.atomic" -Recurse -Force

Troubleshooting

Git identity error
git config --global user.name "Your Name"
git config --global user.email "you@example.com"
Windows: agents fail to spawn

Ensure the agent CLI is in your PATH. Atomic uses Bun.which(), which handles .cmd, .exe, and .bat extensions automatically.


FAQ

How does Atomic differ from Spec-Kit?

Spec Kit is GitHub's toolkit for "Spec-Driven Development." Both improve AI-assisted development, but solve different problems:

In short: Spec-Kit works well for greenfield projects where you start from a spec and use a single Copilot session to generate code. Atomic is built for the harder case — large existing codebases where you need to research what's already there before changing anything. Atomic gives you multi-session pipelines with isolated context windows, deterministic execution, and support for Claude Code, OpenCode, and Copilot CLI instead of just one agent.

Aspect Spec-Kit Atomic
Focus Greenfield projects with spec-first workflow Large existing codebases + greenfield — research-first or spec-first
First Step Define project principles and specs Analyze existing architecture with parallel research sub-agents
Workflow Definition Shell scripts and markdown templates TypeScript Workflow SDK (defineWorkflow().run().compile()) with deterministic execution
Session Management Single agent session Multi-session pipelines — sequential and parallel — each in isolated context windows
Data Flow Manual — copy output between steps Controlled transcript passing via ctx.transcript() and ctx.getMessages()
Agent Support GitHub Copilot CLI Claude Code + OpenCode + Copilot CLI — switch with a flag
Sub-Agents Single general-purpose agent 12 specialized sub-agents with scoped tools and isolated contexts
Skills Not available 57 built-in skills (development, design, docs, agent architecture)
Autonomous Execution Not available Ralph — multi-hour autonomous sessions with plan/implement/review/debug loop
Execution Guarantees Non-deterministic Deterministic — strict step ordering, frozen definitions, controlled transcript access
Isolation Not addressed Devcontainer features for containerized execution
How does Atomic differ from DeerFlow?

DeerFlow is ByteDance's agent harness built on LangGraph/LangChain. Both are multi-agent orchestrators, but take different approaches:

In short: DeerFlow is a general-purpose agent orchestrator with a web UI. Atomic is narrowly focused on coding workflows. The key difference is that Atomic runs on top of production coding agents (Claude Code, OpenCode, Copilot CLI) rather than reimplementing coding tools through a generic API — you get each agent's native file editing, permissions, MCP integrations, and hooks out of the box. Atomic also gives you deterministic execution, which matters when encoding a team's dev process.

Aspect DeerFlow Atomic
Runtime Python (LangGraph) TypeScript (Bun)
Agent SDKs OpenAI-compatible API Claude Code + OpenCode + Copilot CLI native SDKs — write raw SDK code in each session
Focus General-purpose agent tasks (research, reports) Coding-specific: research, spec, implement, review, debug
Workflow Definition LangGraph state machines with graph nodes TypeScript Workflow SDK — defineWorkflow().run().compile()
Execution Model DAG-based with conditional edges Deterministic — strict step ordering, frozen definitions, controlled transcript passing
Parallelism Via LangGraph branch nodes Native parallel sessions via Promise.all() with ctx.session() in isolated context windows
Sub-Agents Researcher, coder, reporter nodes 12 specialized sub-agents with scoped tools (planner, worker, reviewer, debugger, etc.)
Skills Not available 57 built-in skills auto-invoked by context
Isolation Sandbox containers Devcontainer features + git worktrees
Interface Web UI (Streamlit) Terminal chat with tmux-based session management
Autonomous Not available Ralph — bounded iteration with plan/implement/review/debug loop
Distribution pip install + local server bun install -g or devcontainer features
How does Atomic differ from Hermes Agent?

Hermes Agent is Nous Research's general-purpose AI agent with a self-improving learning loop. Both are open-source agent frameworks, but serve different use cases:

In short: Hermes is a broad AI assistant that learns across sessions and connects to messaging platforms. Atomic is a coding-specific harness for engineering teams. It lets you encode your development process as deterministic TypeScript workflows that run identically across team members, machines, and CI. Atomic inherits production-hardened tools from Claude Code, OpenCode, and Copilot CLI — including their permission systems, MCP integrations, and hooks — giving you two independent security boundaries (devcontainer isolation + agent permissions). Fresh context per session keeps output sharp over multi-hour tasks. Developer-authored skills don't drift the way auto-generated ones can.

Aspect Hermes Agent Atomic
Focus General-purpose AI assistant (coding, messaging, smart home, research) Coding-specific: multi-session workflows on coding agents
Runtime Python 3.11+ (uv) TypeScript (Bun)
Agent SDKs OpenAI-compatible API as universal adapter (200+ models via OpenRouter) Claude Code + OpenCode + Copilot CLI native SDKs — write raw SDK code in each session
Workflow Definition Cron scheduler + subagent delegation TypeScript Workflow SDK — defineWorkflow().run().compile()
Session Management Single conversation loop with context compression Multi-session pipelines — sequential and parallel — each in isolated context windows
Data Flow In-context within a single conversation Controlled transcript passing via ctx.transcript() and ctx.getMessages()
Self-Improvement Closed learning loop — auto-creates skills from experience, persistent user model via Honcho Skills authored by developers; memory via CLAUDE.md / AGENTS.md context files
Sub-Agents delegate_task spawns isolated subagents 12 specialized sub-agents with scoped tools and model tiers (Opus, Sonnet, Haiku)
Skills 40+ tools + community Skills Hub (agentskills.io) 57 built-in skills (development, design, docs, agent architecture)
Interface Terminal TUI + multi-platform messaging gateway (Telegram, Discord, Slack, WhatsApp, etc.) Terminal chat with tmux-based session management
Isolation Six terminal backends (local, Docker, SSH, Daytona, Singularity, Modal) Devcontainer features + git worktrees
Autonomous Execution Cron scheduler with inactivity-based timeouts Ralph — bounded iteration with plan/implement/review/debug loop
Execution Guarantees Non-deterministic conversation loop Deterministic — strict step ordering, frozen definitions, controlled transcript access
Team Process Encoding Personal assistant — no concept of team-shared workflows Encode your team's dev process as TypeScript — repeatable across members, projects, and CI
Coding Agent Tooling Reimplements file/terminal tools from scratch via model_tools.py Inherits production-hardened tool ecosystems from Claude Code, OpenCode, and Copilot CLI (file editing, permissions, MCP, hooks)
Reproducibility Conversation loop produces different execution paths each run Frozen workflow definitions run identically across machines, team members, and CI pipelines
Context Quality Lossy compression within a single conversation — degrades on long coding tasks Fresh context window per session with only distilled transcripts passed forward — stays sharp over multi-hour tasks
Skill Authoring Auto-created skills may drift, accumulate errors, or encode bad patterns over time Developer-authored, version-controlled skills — intentional and auditable
Security Model Command approval + container backends (single boundary) Devcontainer isolation + coding agent permission systems (Claude Code permissions, Copilot safeguards) — two independent security boundaries
Distribution uv / pip bun install -g or devcontainer features

Contributing

See DEV_SETUP.md for development setup, testing guidelines, and contribution workflow.


License

MIT License — see LICENSE for details.

Credits

About

Build your own harness. Understand and research any codebase. Plan complex features. Ship them autonomously.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors