https://github.com/blaknite/faber
TUI for managing a fleet of opencode agents in git worktrees
https://github.com/blaknite/faber
ai-agents opencode worktree-manager
Last synced: about 1 month ago
JSON representation
TUI for managing a fleet of opencode agents in git worktrees
- Host: GitHub
- URL: https://github.com/blaknite/faber
- Owner: blaknite
- License: mit
- Created: 2026-02-26T13:15:47.000Z (4 months ago)
- Default Branch: main
- Last Pushed: 2026-05-01T00:57:13.000Z (about 2 months ago)
- Last Synced: 2026-05-01T02:21:07.148Z (about 2 months ago)
- Topics: ai-agents, opencode, worktree-manager
- Language: TypeScript
- Homepage:
- Size: 915 KB
- Stars: 7
- Watchers: 0
- Forks: 1
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
- Agents: AGENTS.md
Awesome Lists containing this project
README
# faber
[](https://buildkite.com/blaknite/faber)
A terminal UI for running multiple autonomous coding agents in parallel. Give it a prompt, it spins up a git worktree and a [opencode](https://opencode.ai) agent to work on it, while you keep dispatching more tasks.
For an overview of the workflow -- how to think about using faber from idea to finished code -- see [WORKFLOW.md](WORKFLOW.md).



## How it works
Each task gets its own git worktree at `.worktrees/`. An `opencode` agent runs inside that worktree, so tasks are fully isolated from each other and from your working directory. State is persisted to `.faber/state.json`, so you can close and reopen faber without losing track of what's running.
## Prerequisites
- [Bun](https://bun.sh)
- [opencode](https://opencode.ai) on your `PATH`, connected to Claude via a Pro/Max subscription or an Anthropic API key
- A git repository to work in
## Installation
```bash
curl -fsSL https://raw.githubusercontent.com/blaknite/faber/main/install.sh | bash
```
This downloads the latest release binary for your OS and architecture, installs it to `~/.faber/bin`, and adds it to your PATH.
**Building from source**
```bash
bun install
bun run build:bin # produces a ./faber binary
```
Or run directly from source:
```bash
bun run dev
```
## Updating
```bash
faber update
```
Checks for a newer release on GitHub and replaces the running binary in-place if one is available. Supports macOS (arm64/x64) and Linux (arm64/x64).
## Getting the best results
Faber automatically injects the `working-in-faber` skill into every agent prompt. The skill tells the agent about its environment (git worktrees, branch isolation, sibling agents) and sets expectations around committing in logical units, writing meaningful commit messages, and not pushing.
For this to work, the skill needs to be available in your agent's environment.
## Usage
```bash
faber # open the TUI in the current directory
faber start # same as above, explicit subcommand
faber --dir /path/to/repo # open the TUI in a specific directory
```
### Setup
Initialise a repo for use with faber (creates `.faber/`, `.worktrees/`, and updates `.gitignore`):
```bash
faber setup
faber setup --dir /path/to/repo
```
### Headless run
Fire off a task without opening the TUI:
```bash
faber run "fix the login bug"
faber run "fix the login bug" --dir /path/to/repo
faber run "add tests for UserService" --model fast
faber run "add rate limiting" --base my-feature-branch
faber run "fix the login bug" --name fix-login-bug
```
`--base` sets which branch the new worktree is created from and what the agent diffs against. Defaults to the current branch of the main checkout. Pass `--name ` to give the task a meaningful id instead of one derived from the prompt.
### List tasks
Print a table of all tasks with their ID, status, elapsed time, and a truncated prompt:
```bash
faber list
faber list --dir /path/to/repo
faber list --status running # filter by status (running, done, failed, etc.)
```
### Read a task log
Print a task's prompt and the agent's text output, with tool calls summarised as one-liners:
```bash
faber read
faber read --full # include full tool block content
faber read --json # raw LogEntry[] as JSON
faber read --dir /path/to/repo
```
### Cross-task references
Agents can pull context from other tasks by referencing them with `@taskId` in a prompt. Selecting a task from the autocomplete inserts the task ID (e.g. `@a3f2-fix-login-bug`) into the prompt as plain text. Faber doesn't do anything special with it beyond that -- the `working-in-faber` skill, which is injected into every agent prompt, teaches the agent to recognise the `@taskId` pattern and run `faber read ` to pull the output and extract whatever context it needs.
This is useful when one task builds on the work of another -- for example, pointing a "write tests" task at a completed "refactor UserService" task so the agent can see exactly what changed before writing assertions.
### TUI keybindings
**Task list**
| Key | Action |
|-----|--------|
| `n` | New task |
| `j` / `k` or arrows | Navigate list |
| `Tab` | Toggle filter between active and all tasks |
| `Enter` / `o` | Open task |
| `c` | Continue a stopped or failed task |
| `s` | Kill running task (confirms with y/n) |
| `x` | Mark task as done |
| `d` | Delete task and remove its worktree (confirms with y/n) |
| `b` | Switch branch |
| `p` | Push branch to origin (confirms with y/n) |
| `q` / `Ctrl-C` | Quit |
**Log pane** (after pressing `Enter` on a task with no commits)
| Key | Action |
|-----|--------|
| `j` / `k` or arrows | Scroll |
| `PgUp` / `PgDn` | Scroll by page |
| `c` | Continue task |
| `s` | Kill running task |
| `x` | Mark task as done |
| `d` | Delete task and worktree |
| `f` | Open diff view |
| `,` / `.` | Cycle to next / previous active task |
| `q` / `Escape` | Back to task list |
**Diff view** (after pressing `Enter` on a task with commits, or `f` from the log)
| Key | Action |
|-----|--------|
| `j` / `k` or arrows | Scroll |
| `PgUp` / `PgDn` | Scroll by page |
| `g` / `G` | Jump to top / bottom |
| `Tab` | Toggle between side-by-side and inline layout |
| `c` | Continue task |
| `l` | Switch to log view |
| `m` | Rebase branch onto HEAD and fast-forward merge (confirms with y/n) |
| `x` | Mark task as done |
| `d` | Delete task and worktree |
| `,` / `.` | Cycle to next / previous active task |
| `q` / `Escape` | Back |
**New task / continue input**
| Key | Action |
|-----|--------|
| `Enter` | Submit |
| `Shift-Enter` / `Ctrl-Enter` / `Ctrl-J` | Insert newline |
| `Tab` | Cycle through models (or select file suggestion if autocomplete is open) |
| `Escape` | Cancel (or clear text if the field is non-empty) |
When typing `@` in a prompt, faber opens an autocomplete showing both files and tasks. Each suggestion is labelled with its type. Selecting a file inserts its path; selecting a task inserts the task ID (e.g. `@a3f2-fix-login-bug`). Use `Up` / `Down` to navigate and `Tab` or `Enter` to select. `Escape` dismisses the list.
### Models
| Label | Model |
|-------|-------|
| smart (default) | `anthropic/claude-sonnet-4-6` |
| fast | `anthropic/claude-haiku-4-5` |
| deep | `anthropic/claude-opus-4-6` |
### Custom models
To override the model for any tier, create `.faber/faber.json` in your repo (or `~/.faber/faber.json` for a global default):
```json
{
"models": {
"fast": "anthropic/claude-haiku-4-5",
"smart": "anthropic/claude-sonnet-4-6",
"deep": "anthropic/claude-opus-4-6"
}
}
```
You only need to include the tiers you want to change. Project config takes precedence over global.
## Reviewing and merging agent work
When an agent finishes with commits on its branch, the task is marked "ready to merge" and the pending count appears in the top bar. The typical flow from there:
1. Select the task and press `enter`. If the branch has commits, you go straight to the diff view. Otherwise you land on the log, where `f` takes you to the diff.
2. The diff view runs `git diff HEAD...{branch}` (three-dot syntax), so you see exactly what the agent changed relative to the point where work began, regardless of anything that landed on `HEAD` in the meantime.
3. The diff renders with character-level highlighting and two layout modes: side-by-side (default) and inline. Press `Tab` to switch between them.
4. When you're happy with the changes, press `m`. You'll get a `[y/n]` confirmation prompt, then faber rebases the task branch onto the current `HEAD` and fast-forward merges it in. The result is a linear history with no merge commits. If the rebase hits a conflict, faber automatically aborts and leaves your repo clean.
5. After merging, the task moves to "done". The worktree and branch are still there so you can review the log or diff again. When you're done with them, press `d` to delete the worktree and branch in one go.
## Working with feature branches
Faber works just as well when your repo is on a feature branch. Switch to it using `b` and agents will branch off that tip, so all their worktrees and merges stay scoped to it.
The typical flow:
1. Launch faber in your repo as usual.
2. Switch to your feature branch by pressing `b` from the task list and typing the branch name.
3. Create tasks as normal. Each agent branches off the feature branch tip, isolated from both `main` and each other.
4. Review and merge each task into the feature branch the same as any other task: open the task to reach the diff view, then press `m`.
When you're happy with the feature branch as a whole, merge it into `main` yourself outside of faber.
## Branch selection and filtering
### Switching branches
Press `b` from the task list to open the branch switcher. A modal appears with a list of your repo's branches sorted by most recently committed, task branches excluded.
Start typing to filter the list. The filter is a case-insensitive substring match, so `feat` matches `feature/auth`, `my-feat`, etc. Use `Up` / `Down` to move through the results, then `Enter` to switch.
If you type a name that doesn't match anything, the list shows "no matches -- press enter to create". Pressing `Enter` at that point creates a new branch off the current `HEAD` and switches to it.
### How tasks are scoped to a branch
Each task records its base branch -- the branch that was checked out when it was dispatched, or the value passed to `--base` when using `faber run`. The task list is always filtered to only show tasks belonging to the current branch, so switching branches gives you a clean slate for that context.
Tasks created before branch scoping was introduced show up on every branch.
The active/all toggle (Tab) applies on top of that filter, so "active" means "active tasks on this branch".
## Orchestrating tasks
Faber isn't just for one-off fixes. You can give an agent the role of orchestrator -- something with full context on a goal that breaks it down, dispatches sub-tasks in parallel, reviews the results, and keeps going until the whole thing is done.
This pattern works particularly well for implementing a product spec: the orchestrator reads the spec, identifies the independent pieces of work, dispatches them all at once, then routes each result as it finishes.
### An example
Say you have a spec for a new metrics export feature. Rather than tackling it yourself, you could dispatch a single orchestrating task:
```bash
faber run "Implement the metrics export feature described below.
Break the work into independent sub-tasks and dispatch each one using faber run with --name set to a short description of what each sub-task does. Run parallel tasks in parallel -- don't wait for one to finish before starting another. Watch each task with faber watch, review the diff, then merge, continue, or discard based on what the agent produced. Repeat until everything from the spec is merged and nothing is outstanding.
Spec:
- Users can export their usage metrics from the settings page
- Supported formats: CSV and JSON
- Export is scoped to a date range (last 7 days, last 30 days, or custom)
- Exports are generated server-side and streamed as a file download
- The endpoint requires authentication; unauthenticated requests return 401"
```
The orchestrating agent takes it from there: it decomposes the spec, runs agents for the data model, the export endpoint, the frontend UI, and the tests -- each in its own isolated worktree -- then drives each one through review and merge.
### How it works
An orchestrator is just an agent with a well-scoped prompt that instructs it to use faber's CLI to coordinate other agents. It uses the same `faber run` / `faber watch` / `faber diff` / `faber merge` commands you'd use manually, but it handles the loop automatically.
The key thing to include in an orchestrating prompt is the full goal or spec, with enough detail that sub-task agents can work independently.
Orchestrators use `--base $(git branch --show-current)` when dispatching sub-tasks, so each child worktree branches from the orchestrator's own branch. This means all sub-task diffs are relative to a common point, and the sub-tasks show up together in the TUI when you switch to the orchestrator's branch.
### Agent skills
The orchestration pattern works because of three agent skills faber injects into prompts:
- `working-in-faber` -- covers git worktree isolation, branch conventions, and commit expectations. This is what makes each sub-task agent aware of its environment.
- `orchestrating-faber-tasks` -- teaches an agent how to decompose a goal, dispatch sub-tasks in parallel, and drive the whole thing through to completion.
- `reviewing-faber-tasks` -- teaches an agent how to inspect a diff and decide whether to merge, continue, or discard. The orchestrator uses this when assessing each sub-task's output.
These skills need to be available in your agent's environment before faber can inject them. See [Getting the best results](#getting-the-best-results).
## Development
```bash
bun run dev # run from source
bun run build # compile to dist/
bun run build:bin # compile to a standalone binary
```