agentleFS
HokkenHokken/collab-skill/CLAUDE.md

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.

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
  1. Very specific:

    Runs: after DONE the reviewer waits in join --after <id>, which follows the project's newest task.

    line 74

≈ “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.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
CLAUDE.md

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: the collab CLI (~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 repo

CI (.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.renameSync races on Windows (writes go through retry()), CRLF/BOM in input (readBody), and notifications (notify() branches per platform). bin/collab.cmd is the Windows shim.
  • Exit codes are protocol: SKILL.md tells agents to branch on them (0, 1, 10–17, defined as EXIT_* constants at the top of collab.js and documented in its header comment and in docs/how-it-works.md). Changing or adding one means updating all three, plus SKILL.md.
  • Backwards-compatible state: old state.json files (including ones written by an earlier bash version without phase/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> (sets TASK_OVERRIDE), then looks up COMMANDS → cmdXxx(args) functions. Errors are signalled by throwing: die(msg) throws CollabError (exit 1 with collab: msg on stderr), exit(code) throws ExitCode. Don't call process.exit directly.
  • 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. join never uses current: without -t/$COLLAB_TASK it 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.seen heartbeat, touchReviewer/liveReviewer) unless --force. Folders are the same project when one contains the other (sameProject; used by projectTasks and the queue), so a repo root and a module inside it share tasks. A project's tasks are ordered by created_ms (strictly increasing per project; older tasks fall back to created_epoch). init allows 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 through withLock(d, () => update(d, fn)): the lock is an exclusive .lock directory with an owner file (broken only when held past COLLAB_LOCK_SECS and 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). cmdSubmit validates the turn, enforces note acknowledgements (exit 12: every note for the role since its last handoff must be named as Note #N; checked before the check runs and again under the lock; decide/escalate are exempt and don't advance acked) and --check (exit 14, via runCheck), refuses an implementer plan/ready identical to its previous one (handoffText), writes an entry to entries/NNN-<role>-<kind>.md, and appends to log.md. On the reviewer's turn, wait prints a revised plan as a line diff (printPlanDelta/lineDiff) and reviewRecap adds 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-terminal DECISION status with turn: 'human' and saves the previous status/turn in st.decision. cmdAnswer restores them. The other agent's wait returns exit 16 once (unless --self), and idle/stall checks are skipped meanwhile. Hitting the round limit makes a kind: 'limit' decision (LIMIT_OPTIONS, presented by the implementer; decisionPresenter names 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. cmdEnd sets st.ended on the last DONE task (join --after then exits 10) and collects the run's user answers and ## Decisions taken sections (collectDecisions).
  • Waiting: cmdWait/cmdJoin poll state.json every COLLAB_POLL_SECS. Idle detection (idleReason, exit 13) combines time since last handoff, collab progress pings, and (on the implementer's turn) project file mtimes via lastFileChange, which skips SKIP_DIRS.
  • Notes: user notes (NNN-human-note-<to>) and agent notes (--from, NNN-peer-note-<to>, only to the other agent). pendingNotes/markSeen track, per role, the last entry shown (notes surface in wait, progress or a refused submit); unackedNotes/acked track what each role has acknowledged. Tasks from before acknowledgements get acked frozen from seen (freezeAcked) at their first state change. agentShell() refuses a plain collab note from an agent's shell.
  • Diffs: cmdInit snapshots every git repo under the project (its own repo + nested ones up to 3 levels, even if the outer repo ignores them, findRepos) with git stash create plus the untracked-file list, without touching the working tree. cmdDiff diffs against that snapshot and flags files outside --scope globs (globRegex). It also lists new gitignored *.md files (ignoredDocs, snapshotted as untracked/<n>-ignored-md.txt). Lockfile patches (LOCKFILES) are left out unless --all, binary new files print as a size. Each build ready records st.review_trees (last two, snapshotTree: git add -A + write-tree on a temp index copy, objects in tasks/<id>/objects/<n> with the repo's as an alternate, so the repo is never written) for diff --since-review.
  • Scratch: agents keep handoff-only files in tasks/<id>/scratch/ (scratchDir, printed by init/join, returned by collab scratch), or in $COLLAB_HOME/drafts/ before a task exists. cmdClean deletes drafts older than a day.
  • Queue: $COLLAB_HOME/queue.json, edited via withQueue; items are filtered by project folder (realDir). init --from-queue pops an item and applies its options (TASK_OPTS/normTaskOpts). queue split lets the implementer split its current task: parts 2..N are queued with a part marker (index/total/parent) that init --from-queue copies into state.part. A task with a part can't be split again. When a part ends without approval, holdParts marks its later parts confirm + held, so they wait for the user.
  • Lessons: a committed .collab/lessons.md in the project (lessonsFile: nearest one up to the git top level; onDiskName keeps an existing case variant that is the same file). parseLessons reads top-level bullets outside code fences and multi-line comments (metadata in a trailing <!-- collab: … -->, consecutive public numbers, tooLong over LESSON_MAX); readLessons fails on anything but ENOENT; editLessons edits it line-wise under the home lock, keeping the rest of the file and its EOLs. cmdSubmit records a message's ## Lessons bullets (lessonBullets/addLessons) after a successful handoff and only reports lesson errors; cmdInit appends lessonsBrief; cmdEnd lists the run's lessons from every file its tasks used (runLessons). isLessonsPath/notLessons (case-insensitive when the repo's core.ignorecase is true) keep the file out of collab diff, --scope and treeMap, so recording one never voids a review.
  • Phone: task option --phone; pushPhone(st, msg) is a no-op without it, otherwise a detached node -e child POSTs to COLLAB_PHONE_URL or config.json's phone_url (ntfy). cmdInit carries phone on from a DONE, not-ended previous task and creates a topic on first use (ensurePhoneUrl).
  • Dashboard: cmdWatch is a raw-mode TTY UI; rendering is the pure function renderWatch(view, cols, rows, opts), which tests exercise through watch --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.

Download now (ZIP)

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
What did you use it for? e.g. Reviewing pull requests in a Rails app

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.