Drop-in AI agent team framework for Claude Code projects
Project description
2real-team-framework
A drop-in AI agent team framework for Claude Code projects. Bootstrap a simulated team of specialized agents with persistent identities, trust matrices, feedback loops, and structured workflows — all driven by Claude Code's agent tooling.
What is this?
When working with Claude Code on complex software projects, a single AI agent can struggle with coordination, context management, and quality control. 2real-team-framework solves this by scaffolding a complete simulated team structure:
- Manager who decomposes work, creates GitHub Issues, and coordinates
- Architects who design systems and review for compliance
- Engineers who implement in isolated worktrees
- QA, Security, and Data specialists for quality gates
Each team member has a persistent name, personality, git identity, communication style, and trust relationships with other members. They commit under their own names, review each other's work, give feedback, and can be "fired and replaced" when performance drops.
Installation
Python (pip)
pip install 2real-team-framework
# With AI persona generation support
pip install '2real-team-framework[ai]'
Node.js (npm)
npm install -g 2real-team-framework
# Or use without installing
npx 2real-team-framework init --preset library
Quick Start
# 1. Bootstrap a new project with interactive prompts
2real-team init
# 2. Review the generated team structure
2real-team status
# 3. Validate everything is in place
2real-team validate
Or specify everything up front:
2real-team init --preset fullstack-monorepo --team-size 10 --project-name my-app
This creates the following in your project:
CLAUDE.md # Team section, written at the project root
# (an existing CLAUDE.md is preserved as CLAUDE.md.bak
# for you to reconcile)
.claude/
team/
charter.md # Team rules, org chart, workflows
trust_matrix.md # Directional trust scores between members
feedback_log.md # Feedback history
roster/
manager_*.md # One card per team member
tech_lead_*.md
...
skills/
retro.md # Wave retrospective skill
wave-start.md # Initialize a new wave
wave-end.md # Finalize a wave
review-pr.md # PR review using charter format
plan-phase.md # Phase planning skill
close-stale-issues.md
# with --with-hooks, also the 13 runtime skills
# (session-start, handoff, wave-lifecycle, wave-retro,
# phase-review, team-reset, wave-audit, ontology-*, ...
# — see the Skills section)
Commands
| Command | Description |
|---|---|
2real-team init |
Bootstrap team framework in a project |
2real-team add-member |
Add a new team member to the roster |
2real-team remove-member <name> |
Archive a team member (prefix with _departed_) |
2real-team update-member <name> |
Update role, level, or other fields |
2real-team randomize-member <name> |
Fire and replace with a new random identity |
2real-team validate |
Check charter, roster, and skills consistency |
2real-team status |
Show team composition and project status |
init options
| Option | Description |
|---|---|
--preset <name> |
Project preset: fullstack-monorepo, data-pipeline, library |
--team-size <n> |
Override the preset's default team size |
--project-name <name> |
Project name (defaults to directory name) |
--config <path> |
Path to a YAML config file — unified install config or legacy team config (see below) |
--target <dir> |
Target directory (defaults to .) |
--no-interactive |
Disable interactive prompts |
--non-interactive |
Guarantee zero prompts; the config (flags > YAML > shipped defaults) answers everything |
--git-email-prefix <prefix> |
Email prefix (e.g., myorg produces myorg+First.Last@gmail.com) |
--ai-personas |
Use Claude API to generate rich, diverse personas |
--seed <n> |
Seed for reproducible AI persona generation |
Configuration File
Instead of command-line flags, you can use a YAML config file for repeatable, automated bootstrapping:
# config.yaml
preset: library
project_name: my-library
team_size: 4
git_email_prefix: myorg
skills:
- retro
- wave-start
- wave-end
members:
- name: Alice Chen
role: Tech Lead
level: Staff
- name: Bob Garcia
role: Software Engineer
level: Senior
2real-team init --config config.yaml
Config file fields:
| Field | Type | Description |
|---|---|---|
preset |
string | Required. Preset name |
project_name |
string | Project name |
team_size |
integer | Team size override |
git_email_prefix |
string | Email prefix |
target |
string | Target directory |
skills |
list | Override preset's default skills list |
members |
list | Per-member overrides (name, role, level, personality) |
See examples/ for sample configs for each preset.
Install configuration
A single unified install.config.yaml schema covers every interactive decision point
across both installers — the stdlib-only framework/install/bootstrap.py and 2real-team init
(which auto-detects it via --config). It drives fully non-interactive installs:
# Standalone framework installer (stdlib-only, zero prompts):
python3 framework/install/bootstrap.py /path/to/repo --install-config my.install.yaml --non-interactive
# Python CLI (same file, auto-detected):
2real-team init --config my.install.yaml --non-interactive
Precedence: CLI flags > user YAML > shipped defaults
(framework/config/install.config.default.yaml — a fully commented reference copy).
The resolved config is recorded at <target>/.claude/install.config.json so downstream
tooling reads one canonical record of the install-time decisions. This is the
install-time config; the runtime config the hooks read remains
.claude/framework.config.json (overlapping keys — scm.owner, project.name,
project.model — flow into it).
Key reference
| Key | Type | Default | Effect |
|---|---|---|---|
version |
integer | 1 |
Config schema version (must be 1) |
repo.expect |
fresh | existing | any |
fresh |
Expectation about the target repo — enforced. The installer detects the actual state (git repo? commits? non-framework files?) and reports it. Non-interactive installs refuse on a mismatch (any always proceeds); interactive installs ask for explicit confirmation before touching an existing repo; idempotent re-runs skip the gate. Also available as the --expect flag |
project.name |
string | null | null |
Display name; null falls back to the target directory name |
project.model |
standalone | meta | child |
standalone |
Project shape. Maps into the runtime config (standalone → single-repo, meta → meta-and-children, child → child — first-class, so a child repo is recoverable from its runtime config) |
project.flavor |
product | infra |
product |
Child model only: this repo's flavor (see the flavor table below) |
scm.provider |
github |
github |
SCM host (only GitHub is implemented) |
scm.owner |
string | null | null |
GitHub org/user. Replaces the interactive owner prompt; flows into framework.config.json |
ci.provider |
github-actions |
github-actions |
CI system (only GitHub Actions is implemented) |
ticketing.provider |
github-issues |
github-issues |
Story/task tracker (only GitHub Issues is implemented) |
pre_push.mode |
noop | enforce | none |
noop |
Git pre-push hook mode. Validated and recorded; the hook installer is a separate tranche |
ontology.enabled |
bool | true |
Lay the two-layer ontology seed and activate the ontology hooks (--with-ontology/--no-ontology override) |
team.enabled |
bool | true |
Generate the team layer (roster, identity gate); false installs the runtime only |
team.preset |
string | null | null |
Team preset for 2real-team init (fullstack-monorepo, data-pipeline, library). Replaces the preset prompt |
team.size |
integer | null | null |
Target headcount. Replaces the team-size and roster-proceed prompts; null lets the preset/introspection decide |
parent.path |
string | null | null |
Child model only (required there): relative path from this repo to its parent meta-repo (e.g. ..). Must be relative — portable paths only. The parent must already have the framework installed |
children |
list | [] |
Meta model only: child repos to install — - path: <rel-path> with optional flavor: product|infra (default product). Non-interactive installs use this list verbatim (no detection surprises); interactive installs multi-select from detected subdir git repos |
Unknown keys are warned about and carried into the recorded snapshot (forward compatibility). Invalid values (bad enums/types) are fatal — the install refuses to proceed.
With --non-interactive there are zero prompts on any path: scm.owner left null
produces a warning (not a prompt), and the team is generated exactly as configured.
bootstrap.py stays stdlib-only — it reads the YAML with a bundled minimal parser
(framework/install/miniyaml.py); the Python CLI uses PyYAML.
Meta-repo and child installs
A meta install lays the full framework at the meta root, then a hook-less child
layout into each selected child repo. A child install lays only that child layout,
pointing at an already-bootstrapped parent. Children carry no hook code — one copy of
the hooks lives at the meta root and every child's .claude/settings.json invokes the
parent's dispatchers via portable parent-relative commands
(python3 "$CLAUDE_PROJECT_DIR/../.claude/hooks/dispatcher.py" — never machine-absolute
paths, so the whole tree clones anywhere; the traversal depth is derived from the child's
configured path).
# meta.install.yaml — run at the META root
repo: { expect: existing } # meta roots usually already exist
project: { model: meta }
children:
- path: api # full harness
- path: infra/tf # commit-safety + CI subset
flavor: infra
# child.install.yaml — run at the CHILD root (e.g. a child added later)
project: { model: child, flavor: product }
parent: { path: .. } # must already have the framework installed
Each selected child gets: a flavor-appropriate .claude/settings.json, its own
framework.config.json (project.model: child + project.parent + project.flavor,
inheriting scm/identity/policy/hooks from the parent config), a scoped
.claude/team/ roster subset (roster.json + persona cards; trust matrix and feedback
log stay at the meta), and a child CLAUDE.md (an existing one is preserved as
CLAUDE.md.bak). The meta's framework.config.json records the children in
project.repos. The commit-identity gate unions each child's roster with the meta roster
(parent ∪ child), so org leads can commit in any child but one child's engineers cannot
commit in another.
| Flavor | Wired hook events |
|---|---|
product |
The full harness: SessionStart (ontology refresh), pre/post Bash dispatchers, file-edit tracking |
infra |
Commit-safety + CI subset: pre/post Bash dispatchers only — no ontology/session extras |
User-level install (--user-space)
The simulated-team workflow depends on a few harness-global Claude settings that live in
~/.claude/settings.json, not in any repo — chiefly the agent-teams flag
(env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1), plus teammateMode: auto, the worktree
isolation base worktree.baseRef: fresh, and the framework's ~/.claude permission globs. A
fresh clone produces a correct project .claude/ but a harness that cannot spawn a team until
those are present. Opt into the consented user-level install to fill that gap:
python3 framework/install/bootstrap.py /path/to/repo --owner my-org --user-space
The step is safe by construction — check-existing first, consent-gated, merge-only:
- Consent before any write. It prints exactly which file and which keys it would add, and
writes only on an explicit interactive
y.--non-interactive(and any non-TTY / CI run) skips it entirely — it never touches user space unprompted. - Idempotent, no-clobber. A key already present and equal to the desired value is a
no-op (not even offered); if every key already matches, nothing is written. A key present
with a different value is surfaced as a conflict and left exactly as-is — your value
is never overwritten. List values (e.g.
permissions.allow) are unioned, never replaced. - Backed up + atomic. Before amending an existing
settings.jsonit is copied to a timestamped.baksibling, and the new file is written atomically (see Atomic settings writes below), so an interrupted write can never strand a half-written settings file.
Repo-level backup, archive, and restore
When an install would touch a repo that already carries its own Claude assets (a repo-root
.claude/ or CLAUDE.md), the installer offers the same consent UX for the repo's own
assets. With explicit consent you choose between two dispositions:
- Keep & amend (default, non-destructive) — leave the existing assets in place; the
bootstrapper's idempotent merge / skip-if-exists steps augment them. This is the safe default
for
--non-interactive, non-TTY, and CI. - Archive & fresh — MOVE the existing
.claude/andCLAUDE.mdout of Claude's load scope into a timestamped.claude-backups/<UTC>/directory, leaving a clean slate for a fresh install. The archive directory is deliberately named.claude-backups/(it is neither.claude/norCLAUDE.md, and is not named.claude*) so Claude does not load the archived copy — an archived asset is inert and cannot shadow the fresh install. A restore manifest is written alongside the moved files.
Restore procedure. The archive is fully restorable: every asset is moved byte-identical and
.claude-backups/<UTC>/ records what came from where. To roll back, move the archived
.claude/ and CLAUDE.md from .claude-backups/<UTC>/ back to the repo root. The
restore_assets helper in framework/install/repo_space.py does exactly this and refuses to
clobber a currently-present target unless you opt into overwrite.
Atomic settings writes
Every settings / manifest file the installer writes goes through atomic_write_text
(framework/install/atomic_io.py): the full contents are written to a uniquely-named temp file
in the same directory, flushed and fsynced, then os.replaced onto the target. Because
os.replace is an atomic rename, any concurrent reader — or a post-crash observer — sees either
the old file intact or the complete new file, never a truncated in-between. This protects
settings.json (and the repo-archive restore manifest) on every destructive install path.
AI Persona Generation
Generate culturally diverse, role-appropriate personas using Claude:
# Requires ANTHROPIC_API_KEY in environment
export ANTHROPIC_API_KEY=sk-ant-...
# Generate AI personas
2real-team init --preset library --ai-personas
# Reproducible results with a seed
2real-team init --preset library --ai-personas --seed 42
Install the optional AI dependency:
# Python
pip install '2real-team-framework[ai]'
# Node
npm install @anthropic-ai/sdk
If the API key is missing or the SDK isn't installed, the CLI falls back to the built-in name pool with a warning.
Presets
| Preset | Default Size | Required Roles | Description |
|---|---|---|---|
fullstack-monorepo |
10 | Manager, System Architect, DevOps Engineer, Tech Lead, Principal Engineer | Full-stack application with frontend, backend, and infrastructure |
data-pipeline |
12 | Manager, System Architect, DevOps Architect, DevOps Engineer, Tech Lead, Data Engineer | Data engineering with ETL, databases, and analytics |
library |
5 | Manager, Tech Lead, Principal Engineer | Lean team for open-source libraries or SDKs |
All presets include 6 skills: retro, wave-start, wave-end, review-pr, plan-phase, close-stale-issues.
Skills
Skills are Claude Code slash commands installed in .claude/skills/. The framework ships two
tiers.
Team-workflow skills
Templated per project and installed by every preset (the 6 listed in Presets):
/retro— Run a wave retrospective (collect PRs, issues, CI failures, write report)/wave-start— Initialize a new deployments branch and clean worktrees/wave-end— Review, merge, and finalize all PRs for a wave/review-pr <number>— Review a PR using the charter's structured format/plan-phase <number>— Decompose a phase into issues with acceptance criteria/close-stale-issues— Audit and close issues resolved by merged PRs
Runtime skills
Config-driven and fail-open, installed alongside the hooks/libs runtime with
2real-team init --with-hooks (13 skills). They read .claude/framework.config.json and skip
cleanly when a subsystem isn't present:
/session-start— Run first in every session: loads memory, confirms the team, reads the last handoff, and reports git/PR/CI + lifecycle + ontology status in one table/handoff— Write a session handoff note to project memory (git state, open PRs/issues, lifecycle/wave status, and the context the next session needs to resume)/wave-lifecycle— Drive one wave through its full lifecycle (allocate → start → scope → kickoff → work → wrapup → retro) on the deterministiclifecycle.pyengine/wave-retro— Full end-of-wave retrospective with mechanical, evidence-anchored trust scoring (trust_signals.py), counter drift verification, feedback log, process proposals/phase-review— Phase-level health check before scoping the next wave: phase-doc tracking state, tech-debt ratio vs the configured exit threshold, owner revision checkpoint/team-reset— Transparent agent reset: shut down unresponsive session agents and re-orient the implicit team, reporting roster changes/wave-audit— Audit open wave issues against merged PRs and close orphaned issues (with proper comments; nothing closes without user confirmation)/ontology-librarian [query]— Read-only ontology reference: checks staleness of both the hand-curated semantic overlay and the generated structural index, and retrieves context/ontology-rebuild [scope]— Reconcile the semantic overlay: scan dirty checksums, update ontology files and auto-updatable docs from code, mark resolved
The runtime tier also ships enriched versions of retro, wave-start, plan-phase, and
close-stale-issues that supersede the templated team-workflow versions when the runtime is
installed — e.g. /retro becomes a lightweight mid-wave pulse (the end-of-wave scoring moves
to /wave-retro), and /wave-start gains STOP-guards before any branch work.
Pre-push hook
The framework bootstrapper (framework/install/bootstrap.py, also run by
2real-team init --with-hooks) installs a pre-push git hook into the target repo's
effective hooks directory (it respects core.hooksPath). Three modes, driven by the unified
install config key pre_push.mode or the --pre-push {noop,enforce,none} flag (precedence:
flag > user YAML > shipped default noop):
noop(default) — an executable, clearly-commented script that always exits 0. It never blocks a push; it just makes the enforcement seam visible and documents how to turn it on.enforce— runs every command inhooks.pre_push_commandsfrom.claude/framework.config.json(in order, from the repo root) and blocks the push on the first failure. The list is read at push time, so editing the config changes the checks without reinstalling. Fail-open: a missing config or an empty list allows the push.none— installs nothing (and never deletes an existing hook).
To switch modes, re-run the bootstrapper with the new --pre-push value. An existing
pre-push hook is never clobbered: a non-framework hook is preserved as pre-push.bak
(.bak.1, .bak.2, … — never overwriting an earlier backup) with a warning. Targets without
a .git directory are skipped with a notice.
Git hooks live under .git/ and are not committed artifacts — they do not travel with a
clone, fork, or new worktree. The installed mode is recorded in the committed install
snapshot (.claude/install.config.json, key pre_push.mode); each fresh clone lays the hook
locally by re-running the installer (python3 framework/install/bootstrap.py . — or just
… . --pre-push noop to lay only the hook mode recorded for this repo).
How it works with Claude Code
- Bootstrap: Run
2real-team initin your project - Start a session: Claude Code reads
.claude/team/charter.mdand roster files - Manager spawns team: The Manager agent decomposes work and spawns specialists
- Isolated work: Each engineer works in a git worktree via
isolation: "worktree" - Coordination: Team members communicate via
SendMessagetool - Quality gates: Peer review, tech debt tracking, trust scoring
- Feedback loops: Retrospectives after each wave, firing underperformers
Dual CLI Architecture
The framework provides identical CLIs in both Python and Node.js. They share the same:
- Templates — Mustache templates in
templates/(logic-less, works in both ecosystems) - Presets — JSON preset definitions in
presets/ - Skills — Skill templates in
skills/
Neither implementation depends on the other. Use whichever fits your toolchain.
Case Study: isnad-graph
The framework was developed and battle-tested on isnad-graph, a computational hadith analysis platform:
- 8 phases of development (scaffolding through security/ops)
- 240+ pull requests across the project lifecycle
- 14-member simulated team with Manager, Architects, Engineers, QA, Security, and Data specialists
- Team members were fired and replaced based on performance feedback
- Trust matrix evolved organically based on actual delivery quality
- Wave-based deployment branches with structured peer review
The patterns, templates, and presets in this framework are directly extracted from that project's operational experience.
Contributing
See CONTRIBUTING.md for development setup, testing, and PR guidelines.
License
Apache 2.0 — see LICENSE for details.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file 2real_team_framework-0.12.0.tar.gz.
File metadata
- Download URL: 2real_team_framework-0.12.0.tar.gz
- Upload date:
- Size: 599.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5ffeae69090c1642656e666e68a148c82f3fccdc34e13cd7e41c1fefbeacf531
|
|
| MD5 |
b22505b5b607c8927819c6e845584b99
|
|
| BLAKE2b-256 |
5818a8681465282b2717ecc1faf3544350cc5eb00b0f444de3a720224bb3c9f1
|
Provenance
The following attestation bundles were made for 2real_team_framework-0.12.0.tar.gz:
Publisher:
publish-pypi.yml on parametrization/2real-team-framework
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
2real_team_framework-0.12.0.tar.gz -
Subject digest:
5ffeae69090c1642656e666e68a148c82f3fccdc34e13cd7e41c1fefbeacf531 - Sigstore transparency entry: 2130482494
- Sigstore integration time:
-
Permalink:
parametrization/2real-team-framework@3b8f2093c35ecc41f61585fb1c28ce02afdab47f -
Branch / Tag:
refs/tags/deployments-phase7-wave-1 - Owner: https://github.com/parametrization
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yml@3b8f2093c35ecc41f61585fb1c28ce02afdab47f -
Trigger Event:
release
-
Statement type:
File details
Details for the file 2real_team_framework-0.12.0-py3-none-any.whl.
File metadata
- Download URL: 2real_team_framework-0.12.0-py3-none-any.whl
- Upload date:
- Size: 739.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
319ac40c6443fda01ee9984a5fa0cb335303b106b6e568d4e26a6fbef5b0e0cf
|
|
| MD5 |
64fb67c079ce19d8238c6c25b33d333d
|
|
| BLAKE2b-256 |
9388cc069031f8460b2d92605fe623315b70b4aa946295511881cb5262376c71
|
Provenance
The following attestation bundles were made for 2real_team_framework-0.12.0-py3-none-any.whl:
Publisher:
publish-pypi.yml on parametrization/2real-team-framework
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
2real_team_framework-0.12.0-py3-none-any.whl -
Subject digest:
319ac40c6443fda01ee9984a5fa0cb335303b106b6e568d4e26a6fbef5b0e0cf - Sigstore transparency entry: 2130482603
- Sigstore integration time:
-
Permalink:
parametrization/2real-team-framework@3b8f2093c35ecc41f61585fb1c28ce02afdab47f -
Branch / Tag:
refs/tags/deployments-phase7-wave-1 - Owner: https://github.com/parametrization
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yml@3b8f2093c35ecc41f61585fb1c28ce02afdab47f -
Trigger Event:
release
-
Statement type: