addteam
Invite GitHub collaborators from a simple YAML file.
Install
pip install addteam
Or run without installing:
uvx addteam
Prerequisite: GitHub CLI must be installed and authenticated (gh auth login).
First Run
# In your repo directory, create a team.yaml
addteam --init
# Edit team.yaml with your team members, then run:
addteam
That's it! Your collaborators will be invited.
Bootstrapping From an Existing Repo
Already have collaborators on a repo and want a team.yaml that matches?
addteam --init --from-current
Groups current collaborators by permission into role buckets (admins,
maintainers, developers, triagers, readers), lists pending invitations
under the right bucket with a # pending invite comment (expired invites are
commented out — uncomment to re-invite), and sets
welcome_issue: false. Review and edit before committing — --sync run
immediately after is a no-op since the file already matches current state.
team.yaml Format
default_permission: push
admins:
- alice
developers:
- bob
- username: charlie
name: Charlie Day # optional display name
reviewers:
- eve
name: is optional anywhere a username: is given — it shows up in
audit/apply output, --json, and addresses welcome issues by name.
Role names map to permissions automatically:
| Role | Permission |
|---|---|
admins |
admin |
maintainers |
maintain |
developers, contributors, contractors |
push |
reviewers, readers |
pull |
triagers |
triage |
Unknown keys produce a warning (not silent drops), so a typo like
develoeprs: won't quietly shrink your team.
Note:
maintainandtriageonly exist on organization-owned repos. On personal repos addteam automatically degrades to push/pull with a warning (never expands access), so one config works everywhere.
Common Commands
addteam # invite from local team.yaml
addteam -n # dry-run (preview only)
addteam -a # audit (show drift)
addteam -s # sync (also removes unlisted users, asks first)
addteam --from org/config # use team.yaml from another repo
addteam accept # accept invitations sent to you (see "Accepting Invitations")
Apply mode also fixes permission drift: if a collaborator exists with the wrong permission, it is updated to match team.yaml.
Accepting Invitations
If you're on the receiving end of invitations from repos that use addteam:
addteam accept # list + accept all your pending invitations
addteam accept --from myorg # only from a specific org/owner
addteam accept -n # preview only
Expired invitations (GitHub auto-expires after 7 days) are listed but can't
be accepted — ask whoever invited you to re-run addteam.
Note: because accept is now a command, a local team.yaml literally named
accept must be passed as addteam -f accept instead of positionally.
Central Team Config
Keep one team.yaml in a shared repo, apply it anywhere:
# Anyone on your team can run this in any repo:
uvx addteam --from myorg/team-config
(addteam myorg/team-config without --from still works and means the same
thing; --from is the explicit, less ambiguous spelling. Confusing it with
-r owner/repo — which selects the target repo — is a common mistake.)
Apply only one group (e.g. just the leads) to a specific repo:
uvx addteam --from myorg/team-config --group maintainers -r myorg/the-repo
The AI summary at the end is perfect for sharing via email or Slack.
Options
| Flag | Description |
|---|---|
-n, --dry-run |
Preview without making changes |
-s, --sync |
Remove collaborators not in list |
--max-removals N |
With --sync: abort/prompt if removals exceed N or a majority (default: 3) |
--allow-mass-removal |
With --sync: allow exceeding --max-removals/majority |
-a, --audit |
Show drift without making changes |
--fail-on-drift |
With --audit: exit 1 when drift is found (CI gates) |
--sync-teams |
With --sync: also remove extra members from mapping-form teams: |
-r, --repo |
Target a specific repo |
--from |
Fetch team.yaml from another repo |
--from-current |
With --init: snapshot current collaborators (and pending invites) into team.yaml |
--group ROLE |
Only apply these role groups (repeatable; never with --sync or --sync-teams) |
--org NAME |
Report access across every repo in an org |
--repos PATH |
Report access across an explicit owner/repo list, one per line |
--include-forks |
With --org: include forked repos in the report |
--json |
Machine-readable output for audit/apply |
--json-out PATH |
Write the run payload to PATH (apply/audit/dry-run only) |
-y, --yes |
Skip confirmation prompts (sync removals) |
-q, --quiet |
Minimal output |
--welcome / --no-welcome |
Turn welcome issues on/off for this run (default: off) |
--no-ai |
Skip AI-generated summaries |
--report DIR |
Permission matrix for every repo in a directory |
--csv PATH |
With --report: also write a spreadsheet |
--format long/matrix |
With --report --csv: row-per-user or grid layout |
--no-names |
With --report: skip display-name lookups (faster) |
Exit codes: 0 success · 1 runtime error (or drift with --fail-on-drift) · 2 usage error.
Access Reports
Audit permissions across a whole directory, GitHub org, or explicit repo list:
addteam --report ~/dev # local git checkouts
addteam --org myorg # every repo in an org (forks skipped)
addteam --org myorg --include-forks
addteam --repos repos.txt # explicit owner/repo list, one per line
addteam --report ~/dev --csv access.csv --format matrix
All three sources support --csv, --format long|matrix, --no-names, and
--json. --org/--repos reports also carry visibility and fork per
row; archived repos are included and flagged. Mutually exclusive with each
other.
Scripting
addteam --audit --json | jq '.drift'
addteam --audit --fail-on-drift # CI gate: fail the job on drift
GitOps Setup
Automatically sync collaborators when team.yaml changes:
addteam --init --init-action
This creates:
team.yaml- your team config.github/workflows/sync-collaborators.yml- runs on push
Add a TEAM_SYNC_TOKEN secret (PAT with repo scope) to your repo.
Advanced Features
Expiring Access
contractors:
- username: temp-dev
permission: push
expires: 2027-06-01
Expired users are skipped on apply and removed on --sync.
GitHub itself auto-expires a pending invitation after 7 days if the invitee
never accepts. addteam detects this (separately from the expires: date
above) and automatically re-sends the invite on the next apply. --audit
shows pending (Nd) for a live invite's age, or expired once GitHub has
timed it out.
GitHub Teams (orgs)
teams:
- myorg/backend-team
- myorg/frontend-team: pull
- myorg/platform-team:
permission: push
members:
- alice
- bob
maintainers:
- carol
The scalar forms (bare string, or team: permission) only use the team's
current members to derive repo access, same as always. The mapping form
additionally manages the team's own membership on GitHub: addteam
ensures members/maintainers have the right role (--audit reports drift;
addteam -s --sync-teams removes anyone extra, subject to the same
--max-removals/--allow-mass-removal circuit breaker, computed per team).
Requires org-admin or team-maintainer rights; skipped on personal repos.
If a team lookup fails, the affected members are skipped with a warning and
--sync refuses to run — a partial config must never cause mass removals.
Sync Safety (circuit breaker)
--sync removes anyone not listed in team.yaml. A misconfigured file (or an
API blip) could otherwise strip a whole repo's access at once. By default,
addteam refuses to remove more than 3 people, or a majority (>50%) of current
collaborators (when at least 2 would go), in one run:
addteam -s # blocked if it would remove too many
addteam -s --max-removals 10 # raise the limit
addteam -s --allow-mass-removal # explicitly allow a large removal
addteam -s -n # --dry-run always reports whether it would trip
A single removal never trips the breaker — normal confirmation rules apply. Interactive runs get a loud warning and an explicit confirm instead of a hard abort.
Users removed because their authored expires: date has passed do not count
toward the breaker — they are explicit, authored intent, not unlisted drift —
and are still removed under the normal confirmation rules even when the
breaker blocks unlisted removals.
Machine-Readable Run Output
addteam --sync --yes --json-out run.json # write a report file (in addition to normal output)
The payload mirrors --json (independent of it) and includes a run metadata
block (version, UTC timestamp, actor, repo, mode) plus, for sync runs, a
circuit_breaker block with the trip decision. The --init-action /
--init-multi-repo GitHub Action scaffolds use it to upload a run artifact and
post a step summary; PRs touching team.yaml get a dry-run plan comment.
Multi-Repo Management
addteam --init-multi-repo
Creates repos.txt to sync the same team across multiple repos.
Welcome Issues
addteam can create a welcome issue for each new collaborator, with an
AI-generated summary of your repo (requires OPENAI_API_KEY,
ANTHROPIC_API_KEY, GOOGLE_API_KEY, or OPENROUTER_API_KEY).
Welcome issues are off by default. Turn them on per run with --welcome,
or per repo in config:
welcome_issue: true
--no-welcome always forces them off, even if config sets welcome_issue: true.
License
MIT
Metadata
Release files for addteam 1.6.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| addteam-1.6.0.tar.gz | 73.8 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| addteam-1.6.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 122.3 kB
Release files / addteam-1.6.0.tar.gz
| Download URL | addteam-1.6.0.tar.gz |
|---|---|
| Size | 73.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
ff1ad43438e4aa3fae4a5d81f08a31b96d234e9dd0249cc4871520bf5102408d
|
|
BLAKE2b-256 checksum How to use checksums |
28f50ddc554ec6c0aba24ceb31f8627db4a444410a7fff05aaa3c0e41ce24bb0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.14
|
Release files / addteam-1.6.0-py3-none-any.whl
| Download URL | addteam-1.6.0-py3-none-any.whl |
|---|---|
| Size | 48.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
fb65d52457ed05c16ff38475981bcd35434a6a60fc4bd51ff075a00c563266bd
|
|
BLAKE2b-256 checksum How to use checksums |
dd19bed3f94b0c1b3d3a9b51c840ebc7abd10f6e11fffbf7c00968b9a9999ce2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.14
|
Security for addteam 1.6.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.