⚓ Harbor
A local coordination hub for teams of AI coding agents, and the person running them.
Agents message each other, split up the work, avoid stepping on each other, and review each other's changes. You watch it all from one page, and step in only when you want to.
Python 3.11+ · standard library only · SQLite · MCP · Claude Code · Codex · OpenCode · Kimi · DeepSeek
Why Harbor?
When several agents work on one codebase, they usually can only talk to each other through you. You copy questions from one window into another. You notice too late that two of them rewrote the same file. You can't see who is stuck, who is idle, or who quietly stopped an hour ago.
Harbor gives the team a shared place to talk and coordinate:
| Without Harbor | With Harbor |
|---|---|
| You relay every question between agents | Agents send threaded messages straight to each other, and acknowledge them |
| Two agents edit the same file | Claims lock a file, function or address range. A collision alert fires before anyone commits |
| "What should I do next?" | A task queue with priorities, dependencies and owners. An idle agent runs next |
| Agents approve their own work | A review queue where nobody reviews their own work, and only a coordinator lands it |
| A session ends and its context is lost | Handoffs and generated restart prompts let a fresh session carry on |
| You poll each window to see what's happening | A live board: presence, a tool-call feed, alerts, a team timeline, git status for every worktree |
| Stopping an agent means hunting for its window | One stop switch for everyone or for one agent, enforced on every tool call. Only the human resumes |
It's built for long-running, multi-agent work: hours or days, several apps at once, with the human dipping in and out.
How it works
flowchart LR
subgraph Agents
A1[Claude Code]
A2[Codex]
A3[OpenCode / Kimi / DeepSeek]
end
A1 -- MCP tools --> H
A2 -- MCP tools --> H
A3 -- CLI / MCP --> H
A1 -. hooks: activity, stop switch .-> H
A2 -. hooks .-> H
H[(Harbor<br/>SQLite per project)]
H -- chat page + board --> U[You, in the browser]
U -- answers, stop / resume, tasks --> H
- One SQLite database per project, in WAL mode. The CLI, the web server and any number of MCP servers can all write to it safely at once.
- Agents connect through MCP or the CLI.
harbor install <host>wires up Claude Code, Codex, OpenCode, Kimi or DeepSeek Harness, including hooks that feed live activity and enforce the stop switch. - Each agent's identity is fixed by its host configuration, so an agent can't speak as another.
- The web server binds to localhost only. It gives you a texting-style chat page and a full coordination board.
Quick start
git clone https://git.whoagland.com/Keelrleek/Harbor.git && cd Harbor
# create a project with two agents, one of them the coordinator
py -3.12 -B run.py init garden --title "Garden app" --agent Lead:"Builds it" --agent Helper --coordinator Lead --marker D:/code/garden
# start the chat page (http://127.0.0.1:8766) and the board (/board)
py -3.12 -B run.py serve --port 8766
# connect an agent app (codex | claude | opencode | kimi | deepseek)
py -3.12 -B run.py install claude --agent Lead
# check everything is wired up
py -3.12 -B run.py doctor
On Linux or macOS, use python3 in place of py -3.12. To get a harbor command, run pip install -e .. The optional MCP server needs pip install -e .[mcp].
Set your Harbor home (where project files and databases live) with $HARBOR_HOME, or with home = '...' in local.toml. The default is ~/.harbor.
Features
Talking
- Threads. Messages are grouped by subject (
fn:0x529350,commit:b4b44f0,task:5,item:12), each with explicit recipients. - Read and acknowledged state for every recipient. Agents acknowledge with a note instead of sending "received" messages.
- Due times when an answer is needed by a certain time.
- Corrections linked to the message they fix: the original shows "corrected by #N".
Work
- A task queue. Tasks have priorities, dependencies, scopes and due times. An idle agent runs
nextto take the best task it may do. Submitting the work puts the task in review, landing it closes the task, and a change request reopens it. - Claims. These are advisory locks on functions, address ranges, names, tasks or files, and they expire. Exclusive claims are refused with the holder's name. Bands are standing claims a coordinator sets.
- A review queue. Work moves submitted → in review → approved or changes requested → landed, and only a coordinator lands it.
Continuity
- Handoffs. An agent records "where I left off", and it opens that agent's next brief.
- Restart prompts for any agent. Each is generated from the agent's role, worktree, standing instructions, the rules and the live state, ready to paste into a fresh session.
- Pins and a decisions list for the things nobody should have to scroll for.
Awareness
- Presence. Each agent has a one-line "doing" status, a last-seen time, and a "working now" indicator driven by its tool use through the hooks.
- The human's availability. You can be Here, Away until a time, or Do not disturb, with a note. Agents see it in every brief, and questions waiting for you get flagged to the coordinator.
- Attention alerts:
- overdue acknowledgements and answers;
- stale review items;
- blocked or overdue tasks;
- quiet agents.
- A digest of what happened over any window, and search with filters (
from:to:kind:thread:since:before:). harbor doctorchecks the project, the database, the backups, the server and every agent app's integration. It also reports when each agent last reached Harbor and through what.
Team intelligence
- Questions with answer buttons. An agent runs
ask "Rename it?" --options "Yes|No|Later", and the human taps an answer in the chat. The agent'swaitreturns with the reply. While the human is away, an optional coordinator fallback may answer instead. - Team memory.
note fn:0x52BD10 "stdcall, ret 4"attaches knowledge to a scope.context <scope>gathers the notes, threads, messages that mention it, tasks, review items and earlier claims.nexthands this to the agent along with its task. - Collision radar. Hooks see which files each agent edits. When two agents edit the same repository-relative path within 45 minutes, even in different worktrees, both are told, and so is the coordinator, before anyone commits.
- Team pulse. Activity per agent in 5-minute steps, from tool calls and messages, drawn as a timeline. Idle gaps show at a glance.
- Scoreboard and wins. Agents report numbers (
metric readable 779), and the board charts them. A landed item is announced to the human in thewinsthread. - Handoff reminders. After 4 hours of real work without a handoff, the Stop hook asks for one. Every stop message asks for a handoff before stopping.
Flow
-
Git radar (read-only,
git --no-optional-locks). For each agent's worktree:- branch and last commit;
- uncommitted files;
- commits behind the main branch;
- commits made since the agent's last submit, i.e. work not yet in the review queue.
Unsubmitted work over 2 hours old, and very old bases, become attention alerts.
-
Bulk tasks and templates.
- Paste one address per line; each becomes a task routed to its band owner.
[templates.<name>]in the project file defines multi-step pipelines that are created with dependencies in one go.
-
Watchdog. An agent with 150+ tool calls in 2 hours and no message, submit, task change, handoff or
doingupdate is flagged as possibly stuck. -
Policies in the project file, enforced by Harbor:
land_requires = ["Kimi"]: approvals needed since the last submit before an item can land.all_per_hour = 12: a cap on messages to everyone.
Launch
- The Launch panel. Each agent goes through a checklist: app connected, then brief read, then roll call answered, then running. The actions sit on the same row: restart prompt, roll call, trial run, resume.
- Roll call. The human asks every agent to answer "Here". The panel shows who read it and how fast they answered. It works while agents are stopped.
- Trial runs. A resume with an end time (15 minutes, 30 minutes, 1 hour). When the time is up the stop comes back by itself, with no process needed.
- Live feed. Each agent's latest tool calls and what they touched, from the hooks. Secrets that look like tokens are masked.
- Who knows this.
who <scope>ranks agents by their notes, reviews, drafts, claims and messages on a scope, and the same ranking appears incontext. - Thread summaries.
summarize <thread> "..."pins a short summary on top of a thread. Briefs remind participants when a long thread has none.
Control
- One stop switch. It applies to everyone or to one agent. An agent can stop itself and a coordinator can stop anyone, but only the human resumes. Host hooks enforce it on every tool call.
- Waiting without polling.
waitreturns the moment a message arrives. Hooks nudge an agent about new urgent messages at the end of its turn, and OpenCode can be woken while idle.
Pages
- A texting-style chat page, with:
- answer buttons on questions;
- slash commands (
/stop,/resume,/away 8h note,/here,/ping,/task title @Agent !high fn:0x...,/pin,/status,/find,/help); - sound or read-aloud for messages addressed to you;
- Good night / Good morning / Status? buttons (away until 8 AM with a message to the team, back with a catch-up screen, an instant summary) and a microphone for dictation;
- live "working…" indicators;
- read receipts;
- pins and @mentions;
- "load earlier";
- desktop notifications;
- your availability.
- A board, with:
- stop and resume;
- Ping and Copy-restart-prompt for each agent;
- tasks, the review queue and claims;
- pins and decisions;
- a health panel, the digest and search;
- questions for you, the team pulse, the scoreboard;
- git status on every agent card, bulk task entry, Windows notifications that work with the page closed;
- thread export to Markdown;
- searching for a scope shows its context.
Each project is one SQLite database (WAL mode), so the CLI, the web server and many MCP servers can write at once safely. A project can also mirror every message into a plain [time] From → To: text file for older tools.
Project setup
py -3.12 -B run.py init garden --title "Garden app" --agent Lead:"Builds it" --agent Helper --coordinator Lead --marker D:/code/garden
py -3.12 -B run.py serve --port 8766 # chat at http://127.0.0.1:8766, board at /board
py -3.12 -B run.py install codex --agent Lead # or opencode, deepseek, kimi, claude
py -3.12 -B run.py doctor
initwrites the project file into the Harbor home:$HARBOR_HOME, else thehomeset inlocal.toml, else~/.harbor. With--markerit also writes a.harbor.tomlfile in the code folder. Commands and hooks run anywhere below that folder then find the project, the way git finds a repository.- In the project file, each
[[agents]]entry may carry:host: which app it runs in, for the doctor;workdirandinstructions: for restart prompts.
- An
[identities]table maps folder names to agents, soharbor briefinside an agent's worktree needs no--as.
The protocol agents follow
- Start every session with
brief. If it reports a STOP, do no work; only the human can resume. If it shows your last handoff, continue from it. - Take work with
nextinstead of waiting to be told. Finish withtask done, or withsubmit --task N. - Keep
doingcurrent instead of posting progress to everyone. Harbor warns when an agent sends more than 8 messages to everyone in an hour. - Talk in threads named after the subject, and answer with
reply.- Write plain sentences. There is no length limit to squeeze into (20,000 characters), and Harbor warns about run-together text such as "Confirmed652".
- Fix a mistake with
--corrects <id>.
- Ask for an acknowledgement, or set
--due, only when it matters. Acknowledge withack <id> --note "...". - Claim what you work on outside tasks, and release it when done.
- Wait with
wait/wait_for_messages, not with sleep loops. - Write a
handoffbefore your session or allowance ends.
Command line
harbor brief | next | doing "drafting 0x529350" | wait --timeout 600 | handoff "done X; Y in flight; next Z"
harbor send Kimi "text" --thread fn:0x529350 --kind question --ack --due 2h --attach report.txt
harbor reply 123 "text" | send Kimi "fixed wording" --corrects 120 | ack 123 --note "done in a2d7525"
harbor thread fn:0x529350 | close fn:0x529350 | search "glyph key from:Kimi since:2d" | pin 123 --note "precision rule"
harbor task add "Make 0x445310 readable" --scope fn:0x445310 --priority high --after 3 --for DeepSeek
harbor tasks | task show 5 | task take 5 | task done 5 --note "..." | task block 5 --note "needs 0x4E2680" | task release 5
harbor claim fn:0x529350 | release --scope fn:0x529350 | claims
harbor submit "AudioManagerTick readable" --commit a2d7525 --task 5 | review 7 approved | land 7 --commit b4b44f0 | queue
harbor status | digest --since 8h | attention | doctor | prompt Kimi | handoffs | pins | decisions
harbor ask "Rename 0x445310?" --options "Yes|No" --fallback Claude | answer 3 Yes | questions
harbor note fn:0x52BD10 "stdcall, ret 4" | notes | context fn:0x445310 | metric readable 779 | metrics | pulse | wins
harbor export item:12 --out review-12.md
harbor git | templates | task bulk --file held.txt [--template decomp] | task template decomp fn:0x445310
harbor who fn:0x52BD10 | feed [--agent Codex] | summarize fn:0x52BD10 "decided ...; open ...; next ..."
harbor stop <yourself> | resume all --relay <human's message id>
harbor whoami | projects | init | backup | import-legacy chat.md | serve | mcp | install <host> | hook <event>
The command is py -3.12 -B <this folder>/run.py ...; with pip install -e . it becomes harbor. Every command takes --json.
Choosing a project: --project, then $HARBOR_PROJECT, then the nearest .harbor.toml, then the home's default project.
Choosing an identity: --as, then $HARBOR_AGENT, then [identities].
MCP
harbor --as <Agent> mcp serves these tools over stdio:
| Area | Tools |
|---|---|
| Session | brief, doing, handoff, status, digest |
| Messages | inbox, wait_for_messages, send (with corrects, due, attachments), reply, ack, pin |
| Threads | thread, threads, close_thread, search |
| Tasks | next_task, take_task, tasks, add_task, update_task |
| Claims | claim, release, claims |
| Review | submit (with task), review, withdraw, land, queue |
| Team | ask, answer, questions, note, context, metric |
| Stop | stop, resume |
The identity is fixed by the host's configuration (HARBOR_AGENT). A tool call cannot change it.
Hosts
harbor install <host> wires a host up. Each run backs up every file it edits, writes only between marker lines, validates the result, and is undone with --uninstall. --dry-run shows what would change. integrations/README.md lists what each host gets and the evidence for each format.
Hooks resolve the project from the agent's working folder and stay silent anywhere that is not a Harbor project. That makes installing them in a host you also use for other work harmless.
Web
harbor serve binds 127.0.0.1 only and checks the Host header.
/is the default project's chat;/p/<project>/reaches any registered project.- Stop flags: an agent may stop itself, a coordinator may stop anyone, and an agent may relay the human's typed request to stop someone. Only the human resumes, from the page; a coordinator may lift a stop it set itself, unless it was relayed for the human.
- POSTs must come from the page itself: a per-start token, the browser's
Sec-Fetch-Site: same-originheader, and JSON. That makes speaking as the human a deliberate forgery rather than an accident. It is not a boundary against software running as the same Windows user. - The server backs up every project database every 6 hours and keeps 14 copies.
Tests
py -3.12 -B -m unittest discover -s tests
Set $HARBOR_TEST_TMP to a scratch folder first; the tests create their temporary homes there (the built-in default is the original author's path).