collab-skill
CLAUDE.md2 starsChanged 47 days agoMITView on GitHub
Collab is an agent skill plus a CLI that lets two coding agents (e.g. Claude Code and Codex CLI) run an autonomous implementer ⇄ reviewer loop. The whole product is two files: Outside the protocol, bin/collab-up.js (collab-up) opens the Claude code / Codex / collab watch / collab note terminals in VS Code through the tiny extension in vscode/; collab-up --install packs that folder into a .vsix itself (stored zip, no deps) and runs code --install-extension. See docs/vscode.md. docs/how-it-works.md is…
In this repositoryOne other file it gives its agents.
- collabSkill
The file
We split this CLAUDE.md into the rules it gives an agent
1 rule. About 0 are common, 0 are distinctive, and 1 is very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈0
Distinctive · estimate
This author's own choices. Worth a look.
1
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 74
Runs: after DONE the reviewer waits in join --after <id>, which follows the project's newest task.
≈ “Common” is an estimate: at least 1 in 5 of the 26,980 CLAUDE.md files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
What this is
Collab is an agent skill plus a CLI that lets two coding agents (e.g. Claude Code and Codex CLI) run an autonomous implementer ⇄ reviewer loop. The whole product is two files:
bin/collab.js: thecollabCLI (~1400 lines, single file). It owns the shared state and enforces the protocol.SKILL.md: the prose instructions every agent follows, for both roles. Agents only talk to each other through the CLI.
Outside the protocol, bin/collab-up.js (collab-up) opens the Claude code / Codex / collab watch /
collab note terminals in VS Code through the tiny extension in vscode/; collab-up --install packs
that folder into a .vsix itself (stored zip, no deps) and runs code --install-extension. See docs/vscode.md.
docs/how-it-works.md is the protocol reference (loop, states, exit codes, notes, queue, on-disk layout). Read it before changing behaviour.
Commands
npm test # node --test, ~30 s, no deps to install
node --test --test-name-pattern "queue" # run matching tests only
node bin/collab.js help # run the CLI from the repoCI (.github/workflows/test.yml) runs npm test on ubuntu/macos/windows × Node 18/22.
Hard constraints
- Zero dependencies, Node >= 18 built-ins only. Keep it that way.
- Cross-platform: macOS, Linux and native Windows (PowerShell/cmd/Git Bash). Watch path handling,
fs.renameSyncraces on Windows (writes go throughretry()), CRLF/BOM in input (readBody), and notifications (notify()branches per platform).bin/collab.cmdis the Windows shim. - Exit codes are protocol:
SKILL.mdtells agents to branch on them (0, 1, 10–17, defined asEXIT_*constants at the top ofcollab.jsand documented in its header comment and indocs/how-it-works.md). Changing or adding one means updating all three, plusSKILL.md. - Backwards-compatible state: old
state.jsonfiles (including ones written by an earlier bash version withoutphase/seen/plan_round) must still load; there is a test for it.
Architecture of bin/collab.js
- Dispatch:
main()handles a leading-t <task-id>(setsTASK_OVERRIDE), then looks upCOMMANDS→cmdXxx(args)functions. Errors are signalled by throwing:die(msg)throwsCollabError(exit 1 withcollab: msgon stderr),exit(code)throwsExitCode. Don't callprocess.exitdirectly. - Task resolution:
taskDir()picks the task from-t, then$COLLAB_TASK, then the newest active (else newest) task for the current project (latestTask(cwd)), then$COLLAB_HOME/current.joinnever usescurrent: without-t/$COLLAB_TASKit takes the folder's project (joinableTask, exit 1 when a parent folder matches several active projects) and refuses a task whose reviewer claim is live (reviewer.seenheartbeat,touchReviewer/liveReviewer) unless--force. Folders are the same project when one contains the other (sameProject; used byprojectTasksand the queue), so a repo root and a module inside it share tasks. A project's tasks are ordered bycreated_ms(strictly increasing per project; older tasks fall back tocreated_epoch).initallows one active task per project. So pairs in different projects don't interfere, and agents also pin their task id with-t. - State:
$COLLAB_HOME/tasks/<slug>-<stamp>/state.json(-2,-3… when that folder already exists). All mutations go throughwithLock(d, () => update(d, fn)): the lock is an exclusive.lockdirectory with anownerfile (broken only when held pastCOLLAB_LOCK_SECSand its owner pid is gone), and writes are temp-file + rename.update(..., { touch: false })is for changes that must not count as agent activity (e.g. snooze). - Turns:
transition(st, role, kind)is the state machine (PLANNING/PLAN_REVIEW/PLAN_CHANGES → IMPLEMENTING → READY_FOR_REVIEW ⇄ CHANGES_REQUESTED → DONE, plus ESCALATED/ABORTED/STALLED terminals).cmdSubmitvalidates the turn, enforces note acknowledgements (exit 12: every note for the role since its last handoff must be named asNote #N; checked before the check runs and again under the lock;decide/escalateare exempt and don't advanceacked) and--check(exit 14, viarunCheck), refuses an implementerplan/readyidentical to its previous one (handoffText), writes an entry toentries/NNN-<role>-<kind>.md, and appends tolog.md. On the reviewer's turn,waitprints a revised plan as a line diff (printPlanDelta/lineDiff) andreviewRecapadds its notes since its last review with the implementer's acknowledgements (noteReplies). - Decisions:
submit <role> decide(numbered options,decisionOptions) parks the task in the non-terminalDECISIONstatus withturn: 'human'and saves the previous status/turn inst.decision.cmdAnswerrestores them. The other agent'swaitreturns exit 16 once (unless--self), and idle/stall checks are skipped meanwhile. Hitting the round limit makes akind: 'limit'decision (LIMIT_OPTIONS, presented by the implementer;decisionPresenternames the one agent that shows a decision) instead of escalating. - Runs: after DONE the reviewer waits in
join --after <id>, which follows the project's newest task.cmdEndsetsst.endedon the last DONE task (join --afterthen exits 10) and collects the run's user answers and## Decisions takensections (collectDecisions). - Waiting:
cmdWait/cmdJoinpollstate.jsoneveryCOLLAB_POLL_SECS. Idle detection (idleReason, exit 13) combines time since last handoff,collab progresspings, and (on the implementer's turn) project file mtimes vialastFileChange, which skipsSKIP_DIRS. - Notes: user notes (
NNN-human-note-<to>) and agent notes (--from,NNN-peer-note-<to>, only to the other agent).pendingNotes/markSeentrack, per role, the last entry shown (notes surface inwait,progressor a refusedsubmit);unackedNotes/ackedtrack what each role has acknowledged. Tasks from before acknowledgements getackedfrozen fromseen(freezeAcked) at their first state change.agentShell()refuses a plaincollab notefrom an agent's shell. - Diffs:
cmdInitsnapshots every git repo under the project (its own repo + nested ones up to 3 levels, even if the outer repo ignores them,findRepos) withgit stash createplus the untracked-file list, without touching the working tree.cmdDiffdiffs against that snapshot and flags files outside--scopeglobs (globRegex). It also lists new gitignored*.mdfiles (ignoredDocs, snapshotted asuntracked/<n>-ignored-md.txt). Lockfile patches (LOCKFILES) are left out unless--all, binary new files print as a size. Each buildreadyrecordsst.review_trees(last two,snapshotTree:git add -A+write-treeon a temp index copy, objects intasks/<id>/objects/<n>with the repo's as an alternate, so the repo is never written) fordiff --since-review. - Scratch: agents keep handoff-only files in
tasks/<id>/scratch/(scratchDir, printed byinit/join, returned bycollab scratch), or in$COLLAB_HOME/drafts/before a task exists.cmdCleandeletes drafts older than a day. - Queue:
$COLLAB_HOME/queue.json, edited viawithQueue; items are filtered by project folder (realDir).init --from-queuepops an item and applies its options (TASK_OPTS/normTaskOpts).queue splitlets the implementer split its current task: parts 2..N are queued with apartmarker (index/total/parent) thatinit --from-queuecopies intostate.part. A task with apartcan't be split again. When a part ends without approval,holdPartsmarks its later partsconfirm+held, so they wait for the user. - Lessons: a committed
.collab/lessons.mdin the project (lessonsFile: nearest one up to the git top level;onDiskNamekeeps an existing case variant that is the same file).parseLessonsreads top-level bullets outside code fences and multi-line comments (metadata in a trailing<!-- collab: … -->, consecutive public numbers,tooLongoverLESSON_MAX);readLessonsfails on anything but ENOENT;editLessonsedits it line-wise under the home lock, keeping the rest of the file and its EOLs.cmdSubmitrecords a message's## Lessonsbullets (lessonBullets/addLessons) after a successful handoff and only reports lesson errors;cmdInitappendslessonsBrief;cmdEndlists the run's lessons from every file its tasks used (runLessons).isLessonsPath/notLessons(case-insensitive when the repo'score.ignorecaseis true) keep the file out ofcollab diff,--scopeandtreeMap, so recording one never voids a review. - Phone: task option
--phone;pushPhone(st, msg)is a no-op without it, otherwise a detachednode -echild POSTs toCOLLAB_PHONE_URLorconfig.json'sphone_url(ntfy).cmdInitcarriesphoneon from a DONE, not-ended previous task and creates a topic on first use (ensurePhoneUrl). - Dashboard:
cmdWatchis a raw-mode TTY UI; rendering is the pure functionrenderWatch(view, cols, rows, opts), which tests exercise throughwatch --once.
Tests
test/collab.test.js drives the real CLI end to end via spawnSync. Use the sandbox() helper: it
creates a temp COLLAB_HOME and project dir with COLLAB_POLL_SECS=1, COLLAB_NOTIFY=0 and
COLLAB_TASK='', and returns run(args, { input, cwd, env }), state() and taskDir(). gitRepo(dir)
makes a committed repo for diff tests. Add a test for any behaviour change; keep timing-based tests
tolerant of slow CI runners.
Install / distribution
install.sh / install.ps1 only create links (symlinks on Unix, directory junctions on Windows) from
~/.agents/skills/collab and ~/.claude/skills/collab to the repo, and put collab in ~/.local/bin,
so edits in this repo are live for installed agents. They must also handle the npx skills add layout,
where the repo itself already lives in ~/.agents/skills/. package.json files controls what ships.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

