Doop
Doop is an open-source multiplayer design canvas and design MCP for Claude Code, Codex, and any AI agent. Design side by side with agents — free in beta.
colleague is a swappable coder-agent harness — one runtime, many minds. A headless task runtime that turns different model backends into repo workers behind one shared typed contract, with three rende
DesignPython · ShellGitHub @agentcultureOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
Colleague is a harness for Qwen, that is designed to run by itself - and other agents
Taken from the repository README
Loading the full README. The saved excerpt is shown below.
Colleague is a swappable coder-agent harness that turns different model
backends into repo workers behind one shared task runtime.
One runtime, many minds.Read the full README ↗Colleague is a swappable coder-agent harness that turns different model backends into repo workers behind one shared task runtime.
One runtime, many minds.
The problem it solves. AI coder backends are not interchangeable — a local vLLM model, an OpenAI-compatible endpoint, and a hosted assistant each bring their own SDK, controls, and output shape. Colleague is the harness around the model: you point it at a repo task, pick a backend, and it runs the work through that backend's bounded tool-loop and hands back the same typed result every time. Swap the backend and nothing else about your workflow changes — the caller never has to care which mind ran the task.
No model and no network required — the mock backend runs the full loop
deterministically, so you can see the shape of a task before wiring up a real
model:
uv sync
uv run pytest -n auto # full suite, offline
# Run your first task through the deterministic mock backend:
uv run colleague work "add a CONTRIBUTING.md stub" --repo . --engine mock --no-pr
# Open the interactive cockpit (the session palette) at a terminal:
uv run colleague
To drive a real model (a local vLLM server, or any OpenAI-compatible endpoint), see Running a real model (vLLM) below.
work command and the same TaskResult come back whether a local 27B or a
cloud endpoint ran it.ask-colleague skill
is built for exactly this: diversity (a second mind), not a stronger model,
is the point.Colleague is not a chat assistant and not an IDE plugin — it is a headless task runtime. For an interactive pair-programmer, use Claude Code or Codex; reach for colleague when you want a backend-agnostic worker you can script, gate, and audit.
| Command | What it does |
|---|---|
colleague work "<goal>" --repo . | Run a task autonomously through a backend. |
colleague (no args, at a terminal) | Open the interactive cockpit / session palette. |
colleague doctor --probe | Check the install and ping the model endpoint. |
colleague learn | Print a prompt that teaches an agent how to drive colleague. |
colleague learn-from claude | Absorb another agent's skills into colleague's own (writes files; add --dry-run to preview). |
learnandlearn-fromare different commands — see Two senses of "learn".
| Part | In Colleague |
|---|---|
| Mind / backend | the model/coder backend (a local vLLM model, an OpenAI-compatible endpoint, …) |
| Adapter | the code that invokes and controls one backend (colleague/engines/) |
| Task runtime | the shared task contract + lifecycle (Task → TaskResult) |
| Tool loop | the bounded agentic loop the backend works the repo through |
| Plugins | replaceable backend adapters, discovered via Python entry points |
| Run report | the JSON result artifact + step trace each run writes |
| Telemetry | opt-in OpenTelemetry traces + metrics (colleague/telemetry/) |
| Handoff | branch/commit/push + gh pr create, gated for offline/CI (colleague/handoff.py) |
| Doctor | colleague doctor — read-only configuration-readiness health check (colleague/oilcheck/) |
| Registry | colleague backends list — the backend plugins installed in this environment |
| Approval gate | colleague/policy.py — operator-declared .colleague/approvals.json that controls what the harness executes |
Colleague has graduated from v0 to v1 (see CHANGELOG.md for
the per-version history and pyproject.toml for the current
release). The runtime ships:
Task and TaskResult that every backend
consumes and produces identically.read_file, write_file,
edit_file (partial-edit; cost scales with the change, not the file), list_dir,
run_command, culture (AgentCulture CLIs), and finish,
confined to the target repo, until it finishes or hits the step budget.colleague.engines
entry-point group an out-of-tree plugin would use:
mock — deterministic and networkless; the CI workhorse.vllm-openai — drives any OpenAI-compatible /v1/chat/completions
endpoint with tool calling. The built-in default model is
unsloth/Qwen3.8-27B-NVFP4 (an NVFP4 Qwen3 checkpoint on a
vLLM server — what doctor checks for); any tool-calling model works.gh pr create, gated so
--no-pr (or no remote) stays a local commit and CI never pushes..colleague/<task-id>.json) for handoff back to
Guildmaster / Taskmaster / Steward..colleague/commands/*.md, invoked with work --command <name> [args…]
or selected in the interactive palette.task_start, pre_tool, post_tool, and finish events; a pre_tool hook
can allow, deny, or rewrite tool calls before the backend executes them. A
per-model hooks overlay (.colleague/<model>/hooks.json) layers
model-specific fixes ahead of the base hooks, giving the operator a
precision tool for recurring model biases — applied only for the targeted
model, a strict no-op for all others; no new runtime dep, socket, or daemon.colleague session is the agent-native entry
point: type a free-text goal and it routes to work or plan on colleague's
own backend automatically — no subcommand needed. Template numbers and names run
directly; opens slash commands. Override the session backend via
without touching the global .Out of scope (by design — each needs a re-spec before it lands): a
multi-backend router / routing policy, an execution
sandbox, a daemon mode, Codex/Claude/Gemini adapters, a --no-hooks escape hatch
(there is no such flag; the approval gate is the landed hook-trust increment —
a policy gate, not a sandbox), and a live MCP runtime (no mcp.json, no mcp
verb; the curated culture tool shells out to operator CLIs — no socket, no MCP
transport). The runtime package has no third-party dependencies — the vLLM
adapter speaks the OpenAI wire format over the standard library.
Each shipped feature has a focused page under docs/features/
— start at the feature index. For the per-version
list of what shipped (and when), see CHANGELOG.md.
| Feature | Doc |
|---|---|
| Work & the tool-loop | work-and-loop.md |
| Adopted harness mechanics (from Qwen Code) | adopt-from-qwen-code.md · NOTICE |
| Context budget / graceful degradation | graceful-degradation.md |
| Capacity standard / fill-line decision (v1) | spec |
| Backends & plugins | engines.md |
| Model & endpoint selection | model-selection.md |
| Git/PR handoff | handoff.md |
| Result artifact | artifact.md |
| Command templates | command-templates.md |
| Lifecycle hooks | hooks.md |
The detailed sections below remain the canonical reference; the feature pages add per-feature source pointers and cross-links.
Before this layer, colleague work accepted one raw instruction string
and ran the tool-loop with no operator gate and no saved recipes: run_command
and write_file executed unconditionally, and every task had to be typed from
scratch.
After, operators drop files into .colleague/ and gain three things that
work identically across every backend (the all-engines rule):
work --command <name> [args…] expands it into the
same Task shape a raw work "…" produces.pre_tool hooks can allow, deny (reason fed back to
the model), or rewrite tool arguments before they execute; post_tool hooks
run formatters or linters after; task_start and finish hooks bracket the
whole work item. Every firing is recorded in the result artifact.colleague session lists discovered templates,
accepts a selection (by number or name) plus optional arguments, and runs the
chosen task through the same work path, loop, hooks, and artifact — no
parallel code path.This extensibility lives in the runtime (colleague/loop.py), not in any one
backend, so it binds equally to mock, vllm-openai, and any future plugin.
To go beyond the deterministic mock backend, point colleague at a real
OpenAI-compatible model server. (Discover what's installed first with
uv run colleague backends list.)
Start an OpenAI-compatible vLLM server with tool calling enabled:
vllm serve Qwen/Qwen3-32B \
--port 8001 \
--enable-auto-tool-choice \
--tool-call-parser hermes
The right --tool-call-parser depends on the model and the vLLM build:
hermes works for many models (including Qwen/Qwen3-32B above), while other
builds need a different one — e.g. an NVFP4 Qwen3 checkpoint served via vLLM may
want qwen3_coder. The backend itself is parser-agnostic — any parser that makes
the server emit OpenAI-format tool calls works.
Tip (anecdotal). With an NVFP4 Qwen3 checkpoint,
qwen3_coderhandled tool-argument escaping more reliably thanhermesin our testing: ahermesrun over-escaped the triple-quotes in a generated docstring (writing\"\"\"instead of"""), producing aSyntaxError, whereqwen3_coderwrote the same file cleanly. This is a single observation, not a benchmark — but if a parser garbles quote-heavy edits, trying the other one is worth a shot.
Then point Colleague at it (defaults already target localhost:8001):
uv run colleague work "fix the typo in the README title" \
--repo /path/to/target/repo \
--engine vllm-openai \
--base-url http://localhost:8001/v1 \
--model Qwen/Qwen3-32B
Configuration resolves in the order: explicit flag → COLLEAGUE_* env →
OPENAI_* env → default. The built-in default --model is
unsloth/Qwen3.8-27B-NVFP4; the example above overrides it with
--model to match the server you started. Because the adapter only touches the
OpenAI surface, pointing --base-url at any compatible server (llama.cpp, an
OpenAI proxy) needs no code change.
The opt-in live end-to-end test proves this against a real server:
COLLEAGUE_VLLM_E2E=1 uv run pytest tests/test_vllm_live.py -v
Operators save reusable task recipes as Markdown files under
.colleague/commands/<name>.md (repo-level or ~/.colleague/commands/ for
user-level; repo-level shadows user-level by stem).
.colleague/commands/ is committable — it is the one part of the otherwise
gitignored .colleague/ dir that git tracks, so a team can share recipes in-repo
(run artifacts, hooks.json, and approvals.json stay local). The committed
doc-review recipe is a worked example.
A template may open with an optional --- metadata block:
---
description: Fix lint errors under a path
engine: mock
constraints: keep diffs minimal, run the formatter
arg-hint: <path>
---
Fix all lint errors under $1. Then run the formatter. $ARGUMENTS
Supported metadata keys:
| Key | Meaning |
|---|---|
description | One-line description shown in listings |
engine | Engine to use when running this command (overridden by --engine) |
constraints | Comma-separated constraints added to the Task |
arg-hint | Short argument hint shown in commands list |
If no --- block is present, the entire file content is the body.
| Placeholder | Expands to |
|---|---|
$ARGUMENTS | All arguments joined by a space |
$1, $2, … | The N-th positional argument (empty string if not supplied) |
# One-shot via work:
uv run colleague work --command fix-lint src/ --repo /path/to/repo --engine mock --no-pr
# List all discovered templates:
uv run colleague commands list --repo .
# Surface overview:
uv run colleague commands overview
The --command flag and a positional instruction are mutually exclusive; any
tokens after --command <name> are passed as template arguments ($1, $2,
$ARGUMENTS).
Hooks are operator-authored shell commands registered in
.colleague/hooks.json (repo-level or ~/.colleague/hooks.json for
user-level; repo-level wins).
{
"hooks": {
"pre_tool": [{ "matcher": "run_command", "command": "my-policy-gate.sh" }],
"post_tool": [{ "matcher": "write_file", "command": "black $file 2>/dev/null; true" }],
"task_start":[{ "command": "echo task starting" }],
"finish": [{ "command": "echo done" }]
}
}
Each entry has:
| Field | Meaning |
|---|---|
matcher | Regex (re.fullmatch) tested against the tool name. Absent or empty matches every tool. Ignored for task_start / finish events. |
command | Shell command run in the target repo directory. |
| Event | When it fires | Pre/post effect |
|---|---|---|
task_start | Before the first tool call | Observe only |
pre_tool | Before each tool call | Can allow, deny, or rewrite |
post_tool | After each tool call | Observe only (side-effects OK) |
finish | After the loop ends | Observe only |
The hook receives a JSON payload on stdin:
{
"event": "pre_tool",
"tool": "run_command",
"arguments": { "command": "pytest" },
"task_id": "<uuid>",
"repo_path": "/path/to/repo"
}
The hook signals its decision via exit code and optional structured stdout:
| Exit code | Stdout | Decision |
|---|---|---|
| non-zero | any | deny — stderr (fallback: stdout) is fed back to the model as the tool result |
| 0 | empty or non-JSON | allow — tool runs as-is |
| 0 | {"decision":"allow", ...} | allow |
| 0 | {"decision":"deny", "reason":"..."} | deny — reason fed back to model |
| 0 | {"decision":"rewrite","arguments":{...}} | rewrite — tool runs with the supplied replacement arguments |
Any response may carry an "additionalContext" string. Every firing (event,
matched command, decision, exit code) is recorded in TaskResult.hook_firings
and appears in the result artifact JSON.
post_tool, task_start, and finish hooks are observe-only: a deny from
these events is recorded but does not halt the loop.
uv run colleague hooks list --repo .
uv run colleague hooks overview
colleague session is the agent-native entry point to colleague: type a
free-text goal and it routes automatically to work or plan on colleague's
own backend — no subcommand needed.
uv run colleague session --repo /path/to/repo
# or, relying on COLLEAGUE_SESSION_ENGINE / COLLEAGUE_ENGINE:
colleague session --repo /path/to/repo
Agent-native by default (#234). Free-text input is classified by
colleague/session_intent.py (classify_intent) and routed to work (the
default) or plan without naming a subcommand. A → work: / → plan: line
confirms the dispatch. Numbers and template names run a work template directly
and are never reclassified. The session runs on colleague's own served backend:
precedence is --engine > COLLEAGUE_SESSION_ENGINE (a session-only env-var
override, new) > COLLEAGUE_ENGINE > vllm-openai. To point the conversational
session at a different backend than bare colleague work, set
COLLEAGUE_SESSION_ENGINE — without touching COLLEAGUE_ENGINE.
Legible action feed (#233). Consecutive identical feed entries collapse into
<entry> ×N instead of stacking (e.g. four back-to-back culture calls become
[culture] agtag issues ×4); the culture/devague tools now surface as
<cli> <args> rather than a bare [culture] sentinel; and step hints are capped
at 120 characters (up from 48).
AgentFront probe reflex (#235). Before the first real use of an unfamiliar
CLI in a run, the backend is instructed to check its learn/explain/--help
surface first. This is advisory and read-only — it never installs or approves
anything.
Input is line-based. Plain text runs a work item — a number (palette
entry), a template name, or a free-text instruction (intent-routed to
work or plan). A line starting with / is a slash command:
/help, /commands,
/skills, /agents, /config (the doctor readiness view), /engines,
/telemetry, /feedback./engine <name>, /model <name>, /base <branch>, /pr (toggle
push + PR) — change the session without restarting it./quit (or q / empty line) ends the session.Three render tiers of the one state, chosen automatically:
error popup when a work item step fails).--no-tui forces it on a TTY.--json — stdout carries only the work item TaskResult (one JSON object each,
preserving the machine contract); the cockpit renders to stderr as chrome.Running colleague with no arguments at a terminal opens this same cockpit
(backend resolved as above: --engine > COLLEAGUE_SESSION_ENGINE >
COLLEAGUE_ENGINE > vllm-openai, never a silent mock). By default it is a
"talk + iterate" loop — each work item commits locally but does not push or
open a PR; /pr or --pr opts in.
colleague tui exposes a headless, stdlib-only cockpit in three views of one
CockpitState: a JSON/TAUI mirror (the agent-readable, selector-addressed
source of truth — tui state), an ANSI frame (the visual render — tui render,
the default), and a Markdown view (the agent-facing readable render —
tui render --format markdown). TAUI (Textual Agentic UI) lets an agent read and
operate the UI without screen-scraping, an LLM, or any colleague import.
The cockpit is a pure reducer (event → reduce(state, event) → CockpitState) and
its state carries a popup model (skill_suggestion / confirmation / error /
progress / diff / help). tui snapshot captures a moment; tui diagnose
classifies cross-view disagreements (no LLM, no network); tui live opens the
foreground TTY cockpit.
uv run colleague tui state # the TAUI JSON mirror
uv run colleague tui render --state <file> # the ANSI frame
uv run colleague tui render --format markdown --state <file>
uv run colleague tui overview
Watching a live work item. A real work feeds the cockpit (#74):
uv run colleague work "<task>" --engine mock # auto: live cockpit on a TTY
uv run colleague work "<task>" --engine mock --no-tui # force the plain step lines
uv run colleague work "<task>" --engine mock --tui-events run.jsonl # live event stream
uv run colleague tui replay --trace .colleague/<id>.trace.jsonl # replay a finished work item
error
popup when a tool step fails). Auto-on a TTY; --tui / --no-tui force it.
Off a TTY (pipes/agents/CI) it falls back to the plain step N: <tool> [ok|err]
stderr lines, byte-for-byte unchanged.--tui-events <path> appends one WorkStep JSONL
line per step as the work item runs, so an agent can follow it turn-by-turn or
tui replay it. (A stream written into the driven repo is treated as harness
telemetry — never swept into the work branch.)tui replay --trace <id>.trace.jsonl folds a
finished work item's loop-step trace into the cockpit (live and replayed steps read
identically — one shared converter).colleague session is now cockpit-rendered with
slash commands; see Interactive cockpit (session).A mid-loop failure still writes a partial artifact (status=error) with the
steps, usage, and changed files accumulated so far.
See docs/features/tui.md for the full surface.
A work item can emit OpenTelemetry traces + metrics so it's observable against an OTLP collector — not just the per-run JSON artifact. Telemetry lives in the runtime (the loop + the shared work path), so every backend emits it identically, exactly like lifecycle hooks.
It is off by default and a strict no-op when off (no spans, no SDK import, the result artifact unchanged). The OpenTelemetry SDK is an optional extra — the base install keeps zero runtime dependencies:
pip install 'colleague[otel]' # or: uv sync --extra otel
export COLLEAGUE_OTEL_ENABLED=1
export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 # OTLP/HTTP collector
uv run colleague work "<task>" --repo . --engine mock --no-pr
# -> stderr prints "trace: <id>"; the collector receives the spans + metrics
Requested without the extra installed, colleague degrades to a no-op with a one-line stderr notice — it never fails the work item.
Signals. Spans: colleague.work (root) → colleague.tool.* (per tool
call) → colleague.handoff. Metrics: colleague.steps, colleague.tokens,
colleague.tool.latency, colleague.tool.calls, colleague.hook.denials,
colleague.work.duration.
Config (precedence: explicit > COLLEAGUE_OTEL_* > standard OTEL_* >
default): COLLEAGUE_OTEL_ENABLED, COLLEAGUE_OTEL_ENDPOINT /
OTEL_EXPORTER_OTLP_ENDPOINT, COLLEAGUE_OTEL_SERVICE_NAME /
OTEL_SERVICE_NAME. OTEL_SDK_DISABLED=true is honored as a kill-switch.
uv run colleague telemetry status # resolved config + whether the SDK is installed
uv run colleague telemetry overview # describe the surface
Together these let a caller compute the ROI of outsourcing a task: the stats say what a work item cost, a feedback record says how good it was.
Always-on stats. Every TaskResult carries a stats block
(WorkStats), written into the artifact on every work item — no flag, no opt-in.
It records the request, ISO start + wall-clock duration, model turns, step count,
per-tool counts, files changed, exact UTF-8 bytes_written, and
reasoning-vs-answer char/byte sizes. Exact token counts stay on usage, verbatim
from the model response (never estimated). Like hooks and telemetry, stats are
runtime-owned — identical for mock and vllm-openai.
Honest token limit. Colleague has no tokenizer (zero deps), and the served model reports no reasoning-token breakdown — so "thought vs written" is measured as chars/bytes, not tokens.
Feedback. A single record per work item (re-grading overwrites) lives beside the
artifact at .colleague/<task_id>.feedback.json; a per-repo last_work pointer
lets you grade the most recent work item without quoting its id. An ungraded work item
reads back as a clean "no feedback yet" state, never an error.
uv run colleague feedback record last --rating 4 --notes "correct but verbose"
uv run colleague feedback show last --repo .
uv run colleague feedback overview
The agent-facing entry is the ask-colleague feedback skill verb. See
docs/features/stats-and-feedback.md.
doctorBefore you hand colleague work, colleague doctor answers "is this install
actually ready to work?" It is colleague's read-only, diagnose-only
health check (no --fix, zero new runtime deps) that emits a
rubric-shaped {healthy, checks[]} report across five ordered check-groups:
| Group | Checks (severity) |
|---|---|
| identity | prompt_file_present / backend_consistency (error), skills_present (warning) |
| provider | resolved base_url/model with redacted api_key (info); credentials + budget advisories (warning) on a non-default provider |
| engines | backends discovered + both bundled backends present + each plugin loads (error; all-engines rule) |
| otel | telemetry enabled / SDK importable / endpoint configured (info; error only when enabled but the [otel] extra is missing) |
| environment | .colleague/ config, hooks.json validity, command-template parsing, AGENTS/skills layering, git (error) + gh (warning) on PATH, CLI integrity |
Only a failed error check flips the report unhealthy; warnings and info are
advisory. doctor exits 1 when unhealthy, else 0. The diagnostic logic lives
in the runtime-level colleague/oilcheck/ package (like telemetry); the verb
is a thin renderer. Add a check-group by appending a read-only checks() callable
to CHECK_GROUPS — see colleague explain doctor and
docs/features/doctor.md.
uv run colleague doctor # human-readable rubric; exit 1 if unhealthy
uv run colleague doctor --json # structured {healthy, checks[]}
uv run colleague doctor --probe # + a live provider ping (the one networked check)
--probe adds two opt-in checks that open a network connection — provider_reachable
(can colleague reach the endpoint?) and provider_model_available (is the
configured model actually served at that endpoint?) — gated behind the flag so the
default doctor stays network-free.
Colleague has two verbs with "learn" in the name. They do different things — this is the one place to keep them straight:
| Command | Direction | What it does | Who it's for |
|---|---|---|---|
colleague learn | colleague → reader | Prints a structured self-teaching prompt (purpose, command map, exit codes, --json, the explain pointer). Read-only — nothing is written. | An AI agent onboarding into a repo that needs to drive colleague. (Humans usually want colleague explain <path> or colleague overview instead.) |
colleague learn-from <source> | peer → colleague | Colleague absorbs another agent's skills: it reads e.g. Claude Code's .claude/skills/<name>/SKILL.md, adapts each into colleague's own .colleague/skills/<name>.md, and (optionally) has the backend rewrite Claude-isms for colleague's tool surface. Writes files. | An operator growing colleague's skill set from a peer mind. |
Mnemonic: learn teaches someone else how to use colleague; learn-from
teaches colleague by absorbing a peer's skills. See
docs/features/learn-from.md for the full
two-stage flow (a deterministic copy, then an optional LLM review-and-adapt
pass), the safety rules, and the honest "load, not execute" limit.
uv run colleague learn # self-teaching prompt (read-only)
uv run colleague learn-from claude --repo . # absorb Claude's skills (writes .colleague/skills/)
uv run colleague learn-from claude --copy-only --dry-run # preview the copy, write nothing
Colleague composes a model-specific system prompt for every work item from two layered families, resolved relative to the model currently driving. Strict per-model isolation: driving model X reads only X's overlay plus the shared base — it never even opens model Y's files (isolation is structural, built from exact paths, not filtered).
AGENTS instructions cascade from the repo root (the cross-tool standard
location — sibling agent tools read AGENTS.md there too), general → specific,
with a ~/.colleague/ user-level fallback:
AGENTS.md # shared base
AGENTS.colleague.md # colleague overlay
AGENTS.colleague.<model>.md # model overlay
Skills are markdown capability docs under .colleague/, folded into the
prompt as a compact name + one-line-summary catalog (a skill is instructional
text only — colleague loads skills as instructions, it does not execute them):
.colleague/skills/*.md # base
.colleague/<model>/skills/*.md # model overlay (shadows base by stem)
<model> is sanitized to a filename-safe token (e.g. Qwen/Qwen3-32B →
Qwen-Qwen3-32B). Inspect what resolves for a model:
uv run colleague agents list --model Qwen/Qwen3-32B --repo .
uv run colleague skills list --model Qwen/Qwen3-32B --repo .
MCP layering is not built yet. Colleague does not read
mcp.jsonor connect to any MCP server today; a live MCP client needs its own spec. There is nomcpverb — don't rely on a non-existent surface.
Mid-work, a backend may delegate scoped sub-tasks via two loop tools:
subagent (a single child) or subagents (a batch that runs concurrently). Each
child runs the same bounded tool-loop as a nested in-process call, isolated in
its own throwaway git worktree on a sub/<id> branch; its result is returned to
the parent and folded into TaskResult.sub_results (omitted when empty). A
sequential merge-subagent integrates the branches afterward, surfacing (never
force-merging) unresolvable conflicts. An optional engine/model parameter lets
a child run on a different backend or model, resolved through the existing
registry.load + EngineConfig inheritance (a config-level switch, no backend
code change).
Concurrency is opt-in: COLLEAGUE_SUBAGENT_CONCURRENCY (default 1 =
byte-identical sequential behavior); with width > 1, up to
MIN(width, MAX_SUBAGENT_FANOUT-1) children run in parallel via
concurrent.futures (threads confined to colleague/subagents.py), reserving one
slot for the merge child. Delegation is backend-judged and optional (like the
devague destination tool), never a forced gate. Termination is structural:
MAX_SUBAGENT_DEPTH=2 (checked before any child work) and MAX_SUBAGENT_FANOUT=4
(per-work-item, including the merge child). Only the top-level work item hands off —
sub-drives never branch, commit, or open a PR. This is runtime-owned (the tools
fire identically for every backend) and is explicitly not the out-of-scope
multi-backend router / routing policy: there is no automatic task→backend routing.
Honest limit: real wall-clock speedup requires the served model to handle concurrent requests; on a serializing server, gain is bounded by overlapped I/O wait, not model compute.
uv run colleague explain subagent # the loop tool's contract (not a CLI verb)
ask-colleague is colleague's one first-party Claude Code skill — the inverse
of the vendored skills. It lets another agent hand a scoped task to colleague: a
different backend/model (e.g. a local vLLM Qwen), not a stronger one — diversity
is the point. Four verbs over colleague work:
| Verb | What it does |
|---|---|
ask-colleague explore | Read-only investigation of an area (worktree-isolated). |
ask-colleague review | A diverse second opinion on the committed <base>...HEAD diff (the headline verb). |
ask-colleague write | Delegate a small change — previews by default; --apply lands a work branch, --pr opens a PR. |
ask-colleague feedback | Grade a finished work item (close the ROI loop). |
explore/review run in a throwaway git worktree (no working-tree side effects);
write previews in one too unless --apply/--pr. (Renamed from outsource;
"outsource this" still triggers it.) See
docs/features/ask-colleague.md.
The approval gate is an operator-declared allow-list that controls what the
harness executes — not just what it discovers. Approval is tamper-protection:
approve records the file's current content checksum; if the file changes after
approval, the checksum no longer matches and the approval is void.
{
"run_command": { "allow": ["git", "pytest", "uv"], "deny": [] },
"hooks": { ".colleague/lint.sh": "sha256:<hex>" },
"commands": { "fix-lint": "sha256:<hex>" }
}
Place this file at .colleague/approvals.json in the target repo (or
~/.colleague/approvals.json for user-level defaults; repo-level wins). A
per-model overlay at .colleague/<sanitized-model>/approvals.json is composed
ahead — per-model keys replace base keys for the same section; no sibling model
is ever read.
| Category | Gated by | Absent section |
|---|---|---|
run_command | Program token (shlex first token): allow/deny lists | No-op (all commands allowed) |
hooks | Content checksum of the referenced hook script file | No-op (all hooks run) |
commands | Content checksum of the template .md file (checked at expansion) | No-op (all templates expand) |
| Skills / AGENTS | Never gated — declarative, load freely | — |
A section is gated only when it is present in approvals.json. An absent
section is a strict no-op: byte-identical to behavior before the gate existed.
When a section is present, allow-list semantics apply: anything unlisted,
unapproved, or tampered is denied.
# Approve a command template by checksum (default: sha256):
uv run colleague commands approve fix-lint --repo .
# Approve a hook script by repo-relative path:
uv run colleague hooks approve .colleague/lint.sh --repo .
# Use md5 instead (drift detection; not recommended for integrity):
uv run colleague commands approve fix-lint --repo . --algo md5
# Both commands support --json for machine-readable output.
# commands list shows: approved | drifted | unapproved | ungated
uv run colleague commands list --repo .
# hooks list shows approval status per entry + the run_command policy if present
uv run colleague hooks list --repo .
# skills list always shows: accessible (never gated)
uv run colleague skills list --repo .
Status values:
| Status | Meaning |
|---|---|
approved | Entry present, checksum matches current file content |
drifted | Entry present, but file changed since approval — approval void |
unapproved | Section present but no entry for this name |
ungated | Section absent from approvals.json — gate not active |
accessible | Skills / AGENTS — never gated, always accessible |
This is a policy gate, not a sandbox.
run_command check inspects the first shell token only. It is trivially
bypassable by sh -c '...', shell pipelines, command substitution, shell
expansion, or an absolute path to a renamed binary. The gate encodes operator
intent; it does not contain a hostile process. An airtight execution sandbox
is explicitly out of scope (it would need its own re-spec).md5 detects accidental drift (file edited by mistake), not a deliberate
attacker who can recompute a hash. Use sha256 when integrity matters.version-based pinning. Approvals are
recorded and verified by content hash only. Version pinning is a documented
follow-up that is not yet built — do not rely on it.--no-hooks flag — that
remains a future follow-up.This is a code-execution risk. Read before driving an untrusted repo.
When you run colleague work (or colleague session) against a repo that
contains a .colleague/hooks.json, those hooks execute automatically with
your operating-system privileges. There is no confirmation prompt and no
sandboxing. Cloning a malicious repository and pointing Colleague at it will
run whatever shell commands that repository's hooks.json specifies.
This behavior is intentional under Colleague's trusted-operator-env model
(D2): the same design tradeoff Claude Code and Codex make for their .claude/
and .codex/ hook configs. You are expected to trust (or audit) the repos you
work in.
What is implemented: the approval gate lets you gate hook
scripts by checksum — an unapproved or tampered hook script is skipped (not a
hard deny of the tool call; it fires a skipped firing in the artifact). The
run_command allow/deny list gates which CLI programs the loop may invoke.
What is NOT yet implemented: a --no-hooks escape hatch or any other
mechanism to disable repo-shipped hooks without editing .colleague/hooks.json
yourself. The approval gate is a policy gate, not a sandbox — see its honest
limits above. A further hardening increment is tracked but has not shipped
in the current version. Do not rely on a non-existent flag.
Safe practices until the trust gate ships:
.colleague/hooks.json before running work in an unfamiliar repo.~/.colleague/hooks.json) hooks as an allow-list approach
if you want hooks without trusting any repo's config.| Verb | What it does |
|---|---|
work <goal> | Work toward a goal/instruction: work autonomously through a coder backend; write the artifact; hand off. |
work --command <name> [args…] | Expand a saved command template and run it. |
commands list | List discovered command templates for a repo (shows approval status). |
commands approve <name> | Record a checksum approval for a command template. |
commands overview | Describe the commands surface. |
hooks list | List configured hook entries for a repo (shows approval status + run_command policy). |
hooks approve <script> | Record a checksum approval for a hook script file (repo-relative path). |
hooks overview | Describe the hooks surface. |
agents list | List resolved AGENTS instruction layers for a model. |
agents overview | Describe the agents surface. |
skills list | List resolved skill docs for a model. |
skills overview | Describe the skills surface. |
telemetry status | Show the resolved telemetry / OpenTelemetry config + whether the SDK is installed. |
telemetry overview | Describe the telemetry surface. |
session | Open a foreground interactive palette. |
backends list | List discovered backend plugins (the registry; wheels is a deprecated alias). |
whoami | Report nick, version, mesh backend, and the live work engine + model (the delegate an ask-colleague would actually run). |
learn | Print a structured self-teaching prompt — teaches an agent how to drive colleague (read-only; nothing is written). |
learn-from <source> | Absorb a peer agent's skills into colleague's own .colleague/skills/ (e.g. ); writes files. Not to be confused with — see . |
Every command supports --json. Results go to stdout, errors/diagnostics to
stderr (never mixed). Exit codes: 0 success, 1 user error, 2 environment
error, 3+ reserved.
A backend is a class implementing colleague.engine.Engine (one method:
work(task, config) -> TaskResult). Advertise it under the entry-point group
and colleague backends list discovers it — no change to Colleague core:
[project.entry-points."colleague.engines"]
my-engine = "my_package.engine:MyEngine"
Most backends never re-implement the loop — they delegate to
colleague.loop.run and only supply how the model is called. Because the
loop owns hook firing, a custom backend inherits the full lifecycle extensibility
layer for free.
Apache 2.0 — see LICENSE. Some harness mechanisms are ported
(cite-don't-import: small stdlib Python, never a vendored dependency) from
Qwen Code and its Google Gemini CLI lineage — see NOTICE and the
provenance ledger at docs/adopted-from.md.
/COLLEAGUE_SESSION_ENGINECOLLEAGUE_ENGINEAGENTS.md → AGENTS.colleague.md → AGENTS.colleague.<model>.md) and
skills (.colleague/skills/*.md → .colleague/<model>/skills/*.md)
compose into a model-specific system prompt, with strict per-model isolation;
inspect them with colleague agents list / colleague skills list.[otel] extra so the base install stays dep-free.doctor — a read-only configuration-readiness health check
across identity, provider, engines, otel-readiness, and environment; emits a
rubric-shaped report and exits non-zero when unhealthy.culture.yaml nick or .colleague/identity.json) and propagates it
to subcommands via COLLEAGUE_IDENTITY. The loop exposes one curated
culture tool (allow-list: agtag, devex) that shells out to the
operator-installed CLIs with the identity injected. Operators opt into
read-only ephemeral neighbour clones via .colleague/neighbours.json
(defaults to empty; no new runtime dependency).devague loop tool to open and converge a goal-frame
before driving the repo, and declare the announcement on arrival. The
destination (frame slug + announcement) is recorded in the JSON artifact; the
curated allow-list excludes confirm/reject (user-only) and export
(operator-only), and convergence is advisory — only human-confirmed claims are
authoritative. Setting a destination is optional and backend-judged..colleague/approvals.json that
gates what the harness executes. Approval is tamper-protection, not just a
name list: approve records a file's content checksum; if the file changes
later the approval is void. Three categories are gated, each opt-in by presence
of its section: run_command CLIs by program token (allow/deny lists),
lifecycle hook scripts by checksum, and command templates by checksum. Skills
and AGENTS instructions load freely — they are never gated. See the
Approval gate section below for the full config shape and
usage.colleague work and colleague session greet an
interactive terminal with an ASCII banner. It's decorative chrome: written to
stderr, shown only on a TTY, and suppressed under --json, so it never
pollutes the stdout result stream or agent-parsed output.| Interactive palette | session.md |
| Cockpit views (tui / TAUI) | tui.md |
| Layered per-model config | layered-config.md |
| Learn skills from a peer | learn-from.md |
| Telemetry: OpenTelemetry | telemetry.md |
| Work stats & feedback (ROI) | stats-and-feedback.md |
doctor (health check) | doctor.md |
| Agent-first CLI | agent-cli.md |
| Mesh-member integration | mesh-member.md |
| Resident promote (Culture member) | resident-promote.md |
| Destination | destination.md |
| Subagents | subagents.md |
| Parallel subagents | parallel-subagents.md |
| Auto-split (too-large assignment) | auto-split.md |
| Audit fan-out | audit-fanout.md |
| Per-model configuration | per-model-configuration.md |
| Approval gate | See Approval gate section below |
| Ask colleague (a different mind) | ask-colleague.md |
| Escalation (agtag continuation) | escalation.md |
| Memory (eidetic recall/remember) | memory.md |
Background one-shot (work --background) | background.md |
| Resident appserver (mesh work requests) | resident.md |
| Deepthink (dual-model escalation) | deepthink.md |
| Mode profiles | mode-profiles.md |
| Adaptive backpressure | backpressure.md |
| Tier visibility | tier-visibility.md |
| Skill curation (budget-aware) | skill-curation.md |
| Rig budget (cross-process slots) | rig-budget.md |
| Task goals & acceptance | task-goals.md |
Session modes (shift-tab / /mode) | session-modes.md |
| Plan mode | plan-mode.md |
| Subagent roles (typed workforce) | subagent-roles.md |
| Write isolation (throwaway worktrees) | write-isolation.md |
| Lint pre-finish gate | lint-gate.md |
| Test-integrity gate | test-integrity.md |
| Affected-tests gate | affected-tests.md |
| Capacity standard / fill-line | capacity-standard.md |
| Explore never wastes a run | explore-never-wastes.md |
| Continue-working (stall recovery) | continue-working.md |
| Flight (pilot a run mid-air) | flight.md |
Cleanup / reap (clean) | cleanup-reap.md |
Config resolution (.colleague/config.json) | config-resolution.md |
| Approval gate (detail) | approval-gate.md |
| CLI on agentfront (rendered surface) | cli-on-agentfront.md |
| ask-colleague trustworthy reflex | ask-colleague-trustworthy-reflex.md |
learn-from claudelearnexplain <path> | Markdown docs for any noun/verb path. |
overview | Read-only descriptive snapshot of the agent. |
doctor | Configuration-readiness health check: identity, provider, engines, otel, environment. |
cli overview | Describe the CLI surface itself. |