Chris Collins

Project

Chief

A multi-project Claude Code orchestrator for macOS: it tracks the backlog of every project I work in and hands the next task to the right Claude session.

Started 15 September 2026 · updated 12 October 2026 · repository

Applied AIDeveloper tools

I usually have several projects open at once, each with its own Claude Code session. Chief knows about all of them: it tracks their backlogs and completions in one place and can hand the next task to the Claude session running in the right project.

It’s for any engineer trying to manage a lot of concurrent work.

Why I built it

I run several coding loops at once in cmux, and I was spending my time jumping between Claude sessions to type in the prompt for the next feature or task. I’d also seen a lot of people’s work on giving agentic coding a backlog of items to work through, and I thought marrying that with cmux would work well for me.

It also fits the way I think: I often have a bunch of ideas that I can’t act on straight away. With Chief I can drop them into a project’s backlog, or ask Claude to add backlog items to Chief for a particular project, then sort them, pick which ones to run, group them together and send them off to the Claude sessions in cmux. It has become a very efficient way to manage task lists across multiple agents.

What it does

  • One inbox across projects. Each repository keeps a human-written, git-versioned backlog.md; Chief mirrors them into SQLite so I can query and act across every project.

  • Completion history. When a task is ticked off, Chief moves it to completedlog.md with the full details: the original ask, when it was added, started and finished, and how long it took. The working backlog stays short.

  • Send to Claude. Clicking a task sends its prompt to the Claude session running in that project’s directory inside cmux; the choice of session is remembered per project.

  • Analyzer deep-link. If claude-session-analyzer is installed, Chief opens it straight to the focused project — and can install it in one click.

What else it does

  • Batch send: several tasks, across projects, in one go.

  • A unified Next Up queue across every project.

  • Task dependencies (blocks / blocked-by).

  • Notifications through ntfy, Pushover, Telegram or Slack, with Telegram commands coming back in.

  • Token and dollar cost tracking from Claude’s session logs.

  • Git-based sync of Chief’s files between machines.

  • Auto-branch and PR gating for finished work.

  • A constitution linter for each project’s rules.

  • Launchers for Little Log Peep (log search), Code Graph Search and History Viewer.

How it works

The backlog is just markdown in the project’s repository. A task is a checkbox line with a short ID that Chief mints, a category taken from the heading it sits under, and an optional body. When a task is done it lands in completedlog.md like this (from Chief’s own log):

- [x] {id:34e5} when adding to completed include full details [category:Backlog] (done 2026-09-16 14:35:04)
  - added: 2026-09-16 14:28:07 · started: 2026-09-16 14:28:44 · duration: 6m
  When you add to the completed list you only keep the title.  Keep the full details both title and body text. ...

Because the files are plain text, anyone can edit them — me in an editor, Claude in a session, or the chief CLI (chief task new "<title>"). Chief watches every registered project with fsnotify, debounces the events, and re-parses the file into SQLite. The catch is that Chief writes these files too (minting IDs, sweeping completions), and those writes would come straight back through the watcher. So before writing, Chief records the hash of the bytes it is about to write, and when the event settles it drops it if the content matches:

func (w *Watcher) onSettled(projectID, path string) {
	// Echo-suppression check: if we recorded an expected hash and the file's
	// current content matches, this event was our own write — swallow it.
	b, err := os.ReadFile(path)
	if err == nil {
		got := HashBytes(b)
		w.mu.Lock()
		want, hasWant := w.echoHashes[path]
		if hasWant && want == got {
			delete(w.echoHashes, path)
			delete(w.timers, path)
			w.mu.Unlock()
			return
		}
		w.mu.Unlock()
	}
	...
	w.handler(projectID, path)
}

Sending a task to Claude means finding the cmux surface running Claude in the project’s directory (across all cmux workspaces) and typing a prompt into it. The prompt also tells Claude how to close the loop — by ticking the box in backlog.md, which the watcher then picks up and sweeps into the completed log:

fmt.Fprintf(&b, "[chief task %s] %s\n", t.ID, t.Title)
...
b.WriteString("\nPlease work on this backlog item. When it's done, mark it complete in backlog.md by changing `- [ ]` to `- [x]` on the line beginning with `{id:")
b.WriteString(t.ID)
b.WriteString("}`.\n")

How it is built

Go throughout: a launchd-supervised daemon (chiefd) with SQLite, fsnotify-based backlog watching (with the hash-based echo suppression above), and a JSON-RPC API on a unix socket; a Cobra CLI; a menu-bar helper; and a Wails main window. Every front end is a thin client of the daemon.

Status

Chief went from first commit to its current shape in about nine days (15–24 September 2026), and most of that was built through Chief itself: Chief’s own completedlog.md holds about 70 finished tasks. The daemon, CLI, backlog mirror, menu bar, main window, Send to Claude, completion sweep, analyzer integration and notifications are done. Still on the roadmap: an MCP server so Claude can pull tasks, automatic wake-up on backlog changes, and shared resource leases.

What’s next

Building it, I realised the next step is handing work off outside my laptop. That needs isolated environments, which is where my cloud stack comes in: I’m working on getting Chief to manage backlog items and farm them out to remote containers. That work is coming, but it’s private for now.

Try it

git clone https://github.com/geekychris/chief.git && cd chief
make install install-menu install-ui

Chief drives cmux through its socket CLI, which by default only accepts calls from processes started inside cmux. Because chiefd runs under launchd, you need to switch cmux to password mode once: set automation.socketControlMode to password with a socketPassword in ~/.config/cmux/cmux.json, put the same token under cmux.socket_password in ~/Library/Application Support/Chief/config.yaml, then reload cmux and restart chiefd. The README has the exact steps.

Updates

No updates yet.