One YAML role spec → provider-native agent config, at spawn time.
agent-profile is a spawn-time role-profile resolver: a free Rust CLI that renders one declarative role profile (model, tools allowlist, permission mode, MCP servers, skills, instructions) into Claude Code subagent/teammate config or Codex agent TOML at the moment your orchestrator spawns a worker — and reverses it cleanly when the worker exits.
It is a leaf utility your orchestrator calls. It is deliberately not a control plane, registry, scenario switcher, or business (see Non-goals).
Free & open source · zero network calls · no accounts, no telemetry · dry-run by default.
Orchestrators (CAO, crew-code, agent-loop, your shell script) got good at spawning workers. Nothing got good at provisioning them:
- Claude Code teammates don't honor the config you give them. Teammate
frontmatter ignores
skillsandmcpServers, and there is no per-teammatepermissionModeat spawn (claude-code issues #23669, #24505). Your reviewer teammate gets the same blast radius as your implementer. - Every provider speaks a different permission grammar.
Bash(git diff:*)in Claude,prefix_rule(...)in Codex. Run a mixed fleet and you maintain the same role twice, by hand, and they drift. - Config files are shared mutable state.
.mcp.json,settings.local.json,~/.codex/config.toml— files the CLIs themselves rewrite, that secrets leak into. Editing them per-spawn means surgical merges and guaranteed reversal, or it means breakage. - Nothing validates a profile against the CLI you actually have installed. Vendors ship weekly; frontmatter fields appear and vanish. You find out a field stopped being honored when a worker silently runs with the wrong permissions.
agent-profile fixes per-worker capability provisioning at spawn time: one YAML role spec → rendered, diffable, doctor-validated, reversibly applied per-worker config, for two runtimes, at the one moment orchestrators need it. If Anthropic closes the Agent Teams gap natively, the Claude half of the pitch shrinks to doctor + mixed-fleet consistency — we say so plainly.
Until the v0.1.0 release lands on Homebrew and crates.io (sprint S6), build from source:
git clone https://github.com/xiaoleiy/agent-profile
cd agent-profile
cargo install --path . # or: cargo build --release
agent-profile --version # agent-profile 0.1.0 (<git sha>)Planned distribution (S6): brew install xiaoleiy/tap/agent-profile and
cargo install agent-profile.
Shell completions:
agent-profile completions zsh > ~/.zfunc/_agent-profile # bash/zsh/fishIn any git repo (or try it on this repo's examples/ first — see below):
mkdir -p .agent-profile/profiles
$EDITOR .agent-profile/profiles/reviewer.yaml # see the schema below
agent-profile validate --role reviewer
# ✓ reviewer: schema OK, 2 capability blocks resolved, no secret literals
agent-profile render --role reviewer --target claude-subagent # dry-run, always
agent-profile doctor --role reviewer --target claude-teammate # preflight vs the INSTALLED CLI
agent-profile apply --role reviewer --target claude-teammate --session-id run1-reviewer
# … spawn your worker …
agent-profile teardown --session-id run1-reviewerA role profile is plain YAML — no new skills format (SKILL.md is the
standard), no new MCP bundle format (server definitions keep their existing
shape), secrets only by ${env:…} reference (literals fail validate):
# .agent-profile/profiles/reviewer.yaml
apiVersion: agent-profile/v1
name: reviewer
description: Read-only code reviewer for implement-review loops.
role: reviewer
targets: [claude-subagent, claude-teammate, codex-agent]
include: [mcp-github-readonly, perms-readonly] # reusable capability blocks
model:
claude: claude-fable-5
codex: gpt-5.3-codex
effort: high
permissionMode: readonly
tools:
allow: [Read, Grep, "Bash(git diff:*)", "Bash(git log:*)"]
deny: [Write, Edit]
mcpServers:
github-readonly:
command: github-mcp
args: ["--readonly"]
env: { GITHUB_TOKEN: "${env:GITHUB_PAT_RO}" }
skills: [code-review] # resolves ~/.agents/skills/<name>/SKILL.md
context: [fragments/reviewer-instructions.md]Try everything against the checked-in examples without writing a profile:
cargo run -- validate --dir examples/.agent-profile
cargo run -- show --role reviewer --resolved --dir examples/.agent-profile
cargo run -- render --role implementer --target codex-agent --dir examples/.agent-profile
scripts/smoke.sh # the whole cycle, all targets, sandboxed HOMESee examples/README.md for the full walkthrough.
renderis dry-run by default — there is no--dry-runflag to forget. The only ways to touch disk are--out <sandbox-dir>andapply.applyis spawn-scoped by construction:--session-idis mandatory, every mutation is atomic (write-then-rename), backed up, and recorded with sha256 hashes in.agent-profile/state.json.teardownreplays the session in reverse — key-level, so foreign edits made while the session was active survive. apply→teardown round-trips the repo byte-identically.- Drift refuses, never clobbers (exit 3): an owned file without our
provenance header, a merge-key collision, or a post-apply edit stops the
command;
--forceis the human-decided override and still backs up first. - A hardcoded never-touch denylist (
~/.claude.json,auth.json, plugin registries, caches/history) sits below the adapters; no flag bypasses it, and the executor re-checks per action right before mutating. - Anything a target cannot express is reported as
SKIPPEDwith the nearest workaround named. Nothing is silently dropped. - Two runtimes, three targets, deliberately (grounded in a
field survey of what each CLI
reads, rewrites, and ignores):
claude-subagent— a single generated.claude/agents/<role>.md.claude-teammate— routes each profile section to the nearest surface Claude Code does honor: agent .md (owned) +.mcp.jsonmerge (only keys we add) +settings.local.jsonmerge (allow/deny/defaultMode) + skill symlinks into~/.agents/skills/+ a marked@includeblock inCLAUDE.md. Honest limitation, printed in render output: settings.local.json is project-wide, not per-teammate — give each worker its own worktree (orchestrators already do) and project scope ≈ worker scope.codex-agent— owned.codex/agents/<role>.toml+ surgical[mcp_servers.*]merge intoconfig.tomlvia toml_edit (Codex rewrites that file constantly; we never own it) + marked block inAGENTS.md;Bash(...)rules translated toprefix_rule(...).
Two shell calls around the worker lifecycle — concrete recipes for CAO,
crew-code, and agent-loop (trap-based teardown, worktree pattern, jq
parsing) in docs/integrations.md:
agent-profile apply --role "$ROLE" --target claude-teammate \
--session-id "$LOOP_ID-$ROLE" --json || exit $?
# … spawn/run the worker …
agent-profile teardown --session-id "$LOOP_ID-$ROLE" --json # in a trapExit codes are uniform and scriptable: 0 success · 1 internal error ·
2 validation failure · 3 drift · 4 doctor errors · 5 session/state error.
--json envelopes are frozen at schemaVersion: 1 and golden-tested —
contract + real captured examples in docs/json-api.md.
doctor --role <r> --target <t> validates a profile against the installed
CLI version's actually-supported fields — not against documentation. It
probes claude --version / codex --version (or trusts
--assume-version), then checks a versioned field-support matrix, MCP
command/url reachability, ${env:VAR} resolvability, skill resolution,
Codex grammar translatability, and stale sessions. Errors → exit 4;
warnings alone → exit 0, so orchestrators can gate on it.
Stable finding codes (doctor --json exposes them as findings[].code;
codes are never reused):
| Code | Level | Meaning |
|---|---|---|
| F001 | error | CLI binary (claude/codex) not found on PATH |
| F002 | warn | CLI version could not be determined; version-gated checks skipped |
| F012 | error | Profile field not honored by the installed CLI version (workaround named) |
| F031 | warn | stdio MCP server command not found on PATH |
| F032 | error | http MCP server url not well-formed |
| F044 | warn | ${env:VAR} reference unset in the current environment |
| F051 | error | Referenced skill resolves nowhere (store + repo fallback checked) |
| F061 | error | Tool rule untranslatable to a Codex prefix_rule(...) |
| F071 | info | state.json session older than 24h — consider teardown |
Load-bearing — these define the tool as much as the features do (design §6, analysis):
- Not a registry. No
publish, nopull, no marketplace, no sharing hub. No demand evidence for one, so it doesn't exist. - Not a scenario switcher. Home/office/client machine switching is
cc-switch's job, and cc-switch is free and good at it. There is no
persistent
usemode — apply is spawn-scoped by construction. - Not a platform. No server, no accounts, no telemetry, no per-seat anything. The binary makes zero network calls. No policy engine or audit trail beyond the local state ledger.
- Not an orchestrator. It never spawns, schedules, supervises, or monitors agents. CAO, crew-code, agent-loop, your shell script — they spawn; agent-profile provisions.
- Not a new format factory. SKILL.md is the skills standard; existing MCP server definitions are the MCP standard. agent-profile references both and invents neither. No Cursor adapter either — Cursor exposes no per-role spawn primitive to target.
- Not a business. A prior market validation said: don't build a company here. This is the sanctioned narrow OSS probe that came out of it, with falsifiable kill criteria.
This project succeeds or folds on 90-day falsifiable metrics, measured from the v0.1.0 release announcement:
- Demand: ≥50 GitHub stars and ≥5 non-author issues/PRs describing the spawn-time use case in their own words — or one real orchestrator integration calling agent-profile at spawn.
- Window stays open: Anthropic has not natively closed the Agent Teams gap (#23669 / #24505) first. Measured vendor close cadence is ~7 weeks, so this is a live coin-flip, monitored weekly.
- Codex pull: ≥3 independent users request or use the Codex target.
Failure on these terms → stop, fold the code into an orchestrator as an internal module (the single-crate, library-first architecture is built for exactly that), write up what was learned. No pivot, no zombie maintenance. Either outcome is fine. We'd rather know.
Sprints 1–5 of the MVP are implemented (plan: docs/product/sprints.md;
build contract: docs/product/design.md). Working today, end-to-end:
validate/list/show [--resolved]— schema v1, capability-block includes with per-key provenance, secret-literal scanning.render/difffor all three targets, dry-run by default;diffexits 3 on differences (git diff --exit-codesemantics).doctorwith the versioned field-support matrix and the finding codes above.apply/teardown/current— the reversible spawn-time cycle: atomic mutations, per-session backups, sha256-verified state ledger, drift refusal, key-level reversal that preserves foreign edits, secret-hygiene gate (git check-ignore) for materialized${env:VAR}.completions <shell>(bash/zsh/fish and friends) and--versionwith git-sha build metadata.--jsoneverywhere, frozen atschemaVersion: 1, golden-tested againstdocs/json-api.mdso the documented examples cannot rot.- Full-cycle smoke test (
scripts/smoke.sh) in a sandboxed HOME; a real captured render/apply/teardown walkthrough indocs/dogfood.md.
CI is live (.github/workflows/ci.yml): fmt, clippy -D warnings, the full
test suite, the sandboxed smoke cycle, and a zsh-completions load check, on
Ubuntu and macOS. Tag-triggered release builds
(.github/workflows/release.yml: macOS arm64/x86_64, Linux x86_64/arm64
tarballs + sha256 checksums) and GitHub Pages deploys of site/
(.github/workflows/pages.yml) are wired but no release tag has been cut yet.
Not yet done (sprint S6): first tagged release, Homebrew/crates.io
distribution, probe scorecard (docs/probe.md).
| Doc | What's in it |
|---|---|
docs/json-api.md |
Frozen --json contracts (schemaVersion 1) with real captured envelopes |
docs/integrations.md |
Orchestrator hook recipes: CAO, crew-code, agent-loop, trap teardown, worktrees, jq |
docs/dogfood.md |
A real render→apply→teardown cycle, captured verbatim from a sandbox |
examples/README.md |
Walkthrough of the example roles (reviewer/implementer/qa) |
docs/product/design.md |
The build contract: schema, CLI surface, adapters, safety model |
docs/product/analysis.md |
Market validation: target users, competitive landscape, risks, metrics |
docs/product/sprints.md |
Sprint plan + recorded deviations |
docs/research/provider-config-surfaces.md |
Field survey grounding every adapter file-ownership decision |
docs/roadmap.md |
Long-term direction (conditional on the probe metrics) |
MIT.