Skip to main content

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: maintain and triage only 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)

Source distribution for addteam 1.6.0
File Size Uploaded Provenance
addteam-1.6.0.tar.gz 73.8 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for addteam 1.6.0
File Interpreter ABI Platform
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.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page