programmatic subagents
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Update README.md

authored by

Laurynas Keturakis and committed by
GitHub
59cb0684 e4735791

+40 -80
+40 -80
README.md
··· 1 1 # mill 2 2 3 - A runtime for executing TypeScript programs that can spawn agents. Write plain TS with `await` and `Promise.all` — mill handles the lifecycle, persistence, and observability. 4 - 5 - ## What you can do 3 + A simple TypeScript runtime for orchestrating subagent work. The orchestrator writes a short program that spawns agents — you review it before it runs. 6 4 7 - **Orchestrate agents in plain TypeScript** — no DSL, no YAML, just sequential and parallel `await` calls with a single injected `mill.spawn()` API. 5 + ## How it works 8 6 9 - **Run async by default** — `mill run` returns a `runId` immediately and executes in a detached worker. Attach later with `watch`, `wait`, or `inspect`. 10 - 11 - **Swap agent backends** — drivers are generic adapters. Ship with pi, Claude, and Codex drivers. Write your own by implementing a codec that parses process output into structured events. 12 - 13 - **Observe everything** — structured NDJSON event log per run, real-time streaming via `mill watch`, and full session replay via `mill inspect --session`. Use the built-in `mill watch` or build a TUI around it. 7 + You talk to your main agent (in Pi, Claude Code, OpenCode etc.). When work needs to be farmed out, it writes a mill program: a TypeScript file that spawns subagents with specific instructions. You see the code before it executes. 14 8 15 9 ## Quick example 16 10 17 11 ```ts 18 - // review.ts 19 - const scan = await mill.spawn({ 20 - agent: "scout", 21 - systemPrompt: "You are a code risk analyst.", 22 - prompt: "Review src/auth and summarize top security risks.", 23 - model: "openai/gpt-5.3-codex", 12 + const analysis = await mill.spawn({ 13 + agent: "analyzer", 14 + systemPrompt: "Map key risks and unknowns.", 15 + prompt: "Analyze the auth module and summarize weak points.", 16 + model: "anthropic/claude-sonnet-4-5", 24 17 }); 25 18 26 19 const plan = await mill.spawn({ 27 20 agent: "planner", 28 - systemPrompt: "You turn findings into an execution-ready plan.", 29 - prompt: `Create remediation steps from:\n\n${scan.text}`, 30 - model: "anthropic/claude-opus-4.6", 21 + systemPrompt: "Turn findings into a concrete implementation plan.", 22 + prompt: `Use this analysis to propose fixes:\n\n${analysis.text}`, 23 + model: "anthropic/claude-opus-4-6", 31 24 }); 32 25 33 26 console.log(plan.text); ··· 55 48 56 49 All commands accept `--json` for machine-readable output on stdout (diagnostics go to stderr). 57 50 58 - ### Help & authoring guidance 59 - 60 - - `mill` or `mill --help`: prints root help with authoring guidance 61 - - `mill <command> --help`: prints command help with authoring guidance 62 - - If resolved config overrides `authoring.instructions`, help uses that text. 63 - - Otherwise help falls back to built-in static guidance (`systemPrompt` = WHO, `prompt` = WHAT). 64 - 65 - ## Configuration 66 - 67 - ```ts 68 - // mill.config.ts (local) 69 - // ~/.mill/config.ts (global) 70 - export default { 71 - authoring: { 72 - instructions: 73 - "Use systemPrompt for WHO (role/method), prompt for WHAT (explicit task + scope + validation).", 74 - }, 75 - }; 76 - ``` 51 + ## FAQ 77 52 78 - `mill init` creates `./mill.config.ts`. 79 - `mill init --global` creates `~/.mill/config.ts`. 53 + **Couldn't I just do this with bash and claude -p?** 54 + Yes — that's the point. The orchestrator can use any language to express a plan. TypeScript is optional; it's just easy to read and lets mill hook into the spawn calls to offer structured output, event logs, and session replay. 80 55 81 - Resolved in order: `./mill.config.ts` → walk up to repo root → `~/.mill/config.ts` → built-in defaults. 56 + **How is this different from Claude Code tasks?** 57 + Tasks are scoped to Claude Code. Mill programs are portable across drivers — same program can spawn Claude, Codex, or pi subagents. The program is also a readable artifact you confirm before execution, not an internal dispatch. 82 58 83 - ## Packages 59 + **Do I have to write the programs myself?** 60 + No. The orchestrator writes them. You review and confirm. 84 61 85 - | Package | Purpose | 86 - | --------------------- | -------------------------------------------------- | 87 - | `@mill/core` | Engine, run lifecycle, public API, config loader | 88 - | `@mill/cli` | CLI commands wrapping core | 89 - | `@mill/driver-pi` | Process driver for pi agent | 90 - | `@mill/driver-claude` | Driver for Claude | 91 - | `@mill/driver-codex` | Driver for Codex | 92 - | `pi-mill` | Pi extension integrating mill as execution backend | 62 + ## Configuration 93 63 94 - ## Architecture 64 + `mill.config.ts` gives the orchestrator precise instructions — model preferences per task type, driver selection, authoring conventions. The orchestrator reads the config and makes choices accordingly. 95 65 96 - ``` 97 - mill program (TS) 98 - → executor (direct | vm) 99 - → engine (lifecycle, API injection, events, persistence) 100 - → driver (generic process/http adapter + codec) 101 - → agent process 66 + ```bash 67 + mill init # creates ./mill.config.ts 68 + mill init --global # creates ~/.mill/config.ts 102 69 ``` 103 70 104 - Layers are orthogonal: executor decides _where_ the program runs, driver decides _how_ spawns invoke agents, extensions add hooks and extra API surface. 71 + Resolved in order: `./mill.config.ts` → walk up to repo root → `~/.mill/config.ts` → built-in defaults. 105 72 106 - ### Run storage 73 + ## Drivers 107 74 108 - ``` 109 - ~/.mill/runs/<runId>/ 110 - run.json metadata (status is canonical) 111 - events.ndjson append-only structured event log 112 - result.json final output 113 - program.ts copied source 114 - worker.pid detached worker pid (best effort) 115 - logs/worker.log worker lifecycle breadcrumbs 116 - logs/cancel.log cancel/kill lifecycle breadcrumbs 117 - sessions/<spawn>.jsonl per-spawn pi session transcripts (pi driver) 118 - ``` 75 + Drivers translate `mill.spawn()` into whatever protocol the agent needs. Ships with Claude, Codex, and pi drivers. Write your own by implementing a codec that parses process output into structured events. 119 76 120 - For operations/debugging conventions, see `docs/references/mill-v0-operations-and-troubleshooting.md`. 77 + | Package | Purpose | 78 + | --------------------- | ------------------------------------------ | 79 + | `@mill/core` | Engine, lifecycle, API, config | 80 + | `@mill/cli` | CLI commands | 81 + | `@mill/driver-claude` | Claude driver | 82 + | `@mill/driver-codex` | Codex driver | 83 + | `@mill/driver-pi` | Pi driver | 84 + | `pi-mill` | Pi extension for mill as execution backend | 121 85 122 - ### Internals 86 + ## Internals 123 87 124 - Built on [Effect](https://effect.website). The public API (`src/public/**/*.api.ts`) exposes Promise-based contracts. Everything else — engine, drivers, persistence — is Effect-first with Schema-validated domain types. `Runtime.runPromise` is the only bridge between the two worlds. 88 + Built on [Effect](https://effect.website). Public API is Promise-based (`src/public/**/*.api.ts`). Engine, drivers, and persistence are Effect-first with Schema-validated domain types. 89 + 90 + Run storage: `~/.mill/runs/<runId>/` — metadata, NDJSON event log, results, per-spawn session transcripts. 125 91 126 92 ## Development 127 93 128 94 ```bash 129 95 bun install 130 - bun test # run tests 131 - bun run check # full pipeline: ast-grep + lint + format + typecheck + test 132 - bun run typecheck # tsgo --noEmit 133 - bun run lint:ast-grep # structural guardrails 134 - bun run lint:boundary # public/internal boundary enforcement 135 - bun run format # oxfmt 96 + bun test 97 + bun run check # ast-grep + lint + format + typecheck + test 136 98 ``` 137 - 138 - Toolchain: ast-grep (structural rules), oxlint, oxfmt, tsgo, bun test.