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.
To learn more about protecting yourself from malicious actors, visit our package security documentation.