Observed since 20 Oct, 19:24 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
One-command collaborator management for GitHub repos with GitOps support.
Invite GitHub collaborators from a simple YAML file.
pip install addteam
Or run without installing:
uvx addteam
Prerequisite: GitHub CLI must be installed and authenticated (gh auth login).
# 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.
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.
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.
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.
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.
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.
| 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.
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.
addteam --audit --json | jq '.drift'
addteam --audit --fail-on-drift # CI gate: fail the job on drift
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 pushAdd a TEAM_SYNC_TOKEN secret (PAT with repo scope) to your repo.
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.
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 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.
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.
addteam --init-multi-repo
Creates repos.txt to sync the same team across multiple repos.
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.
MIT
Download counts and popularity will appear here as the collector receives them.
| Version | Published | Direct dependencies | Open version |
|---|---|---|---|
| 1.6.0 | 6 Sept 2026 | 7 |
This describes our collected data; it is not a security assessment.
Observed since 20 Oct, 19:24 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
package:0e6e5669003ae08768fe23f036182b54b28f11b8fd97174fc336c25e5e1c239e