airbyte_ops_mcp.cli.roster

CLI commands for internal team roster operations.

Commands:

airbyte-ops roster generate - Generate roster artifact from Slack and GitHub APIs airbyte-ops roster summary - Print a matching summary from a roster JSON artifact

CLI reference

The commands below are regenerated by poe docs-generate via cyclopts's programmatic docs API; see docs/generate_cli.py.

airbyte-ops roster COMMAND

Internal team roster operations.

Commands:

  • generate: Generate the internal team roster from Slack and GitHub APIs.
  • summary: Print a matching summary from a roster JSON artifact.

airbyte-ops roster generate

airbyte-ops roster generate OUTPUT

Generate the internal team roster from Slack and GitHub APIs.

Requires SLACK_HYDRA_BOT_TOKEN (or SLACK_AIRBYTE_TEAM_READ_USERS) and GITHUB_TOKEN environment variables.

Parameters:

  • OUTPUT, --output: Output file path for the roster JSON artifact. [required]

airbyte-ops roster summary

airbyte-ops roster summary INPUT

Print a matching summary from a roster JSON artifact.

Categorises roster members into matched, Slack-only, and GitHub-only groups and prints unmatched members for review.

Parameters:

  • INPUT, --input: Path to a roster JSON file (produced by the generate command). [required]
 1# Copyright (c) 2025 Airbyte, Inc., all rights reserved.
 2"""CLI commands for internal team roster operations.
 3
 4Commands:
 5    airbyte-ops roster generate - Generate roster artifact from Slack and GitHub APIs
 6    airbyte-ops roster summary  - Print a matching summary from a roster JSON artifact
 7
 8## CLI reference
 9
10The commands below are regenerated by `poe docs-generate` via cyclopts's
11programmatic docs API; see `docs/generate_cli.py`.
12
13.. include:: ../../../docs/generated/cli/roster.md
14   :start-line: 2
15"""
16
17from __future__ import annotations
18
19# Hide Python-level members from the pdoc page for this module; the rendered
20# docs for this CLI group come entirely from the grafted `.. include::` in
21# the module docstring above.
22__all__: list[str] = []
23
24import os
25from pathlib import Path
26from typing import Annotated
27
28from cyclopts import Parameter
29from fastmcp_extensions.cli import exit_with_error, print_success
30from rich.console import Console
31
32from airbyte_ops_mcp.cli._base import App, app
33from airbyte_ops_mcp.github_api import resolve_default_github_token
34from airbyte_ops_mcp.internal_team_roster import (
35    generate_roster_to_file,
36    summarize_roster,
37)
38
39console = Console()
40
41# Create the roster sub-app
42roster_app = App(name="roster", help="Internal team roster operations.")
43app.command(roster_app)
44
45
46@roster_app.command(name="generate")
47def roster_generate(
48    output: Annotated[
49        str,
50        Parameter(help="Output file path for the roster JSON artifact."),
51    ],
52) -> None:
53    """Generate the internal team roster from Slack and GitHub APIs.
54
55    Requires SLACK_HYDRA_BOT_TOKEN (or SLACK_AIRBYTE_TEAM_READ_USERS) and
56    GITHUB_TOKEN environment variables.
57    """
58    slack_token = os.environ.get("SLACK_HYDRA_BOT_TOKEN") or os.environ.get(
59        "SLACK_AIRBYTE_TEAM_READ_USERS",
60    )
61    if not slack_token:
62        exit_with_error(
63            "SLACK_HYDRA_BOT_TOKEN or SLACK_AIRBYTE_TEAM_READ_USERS "
64            "environment variable is required."
65        )
66
67    github_token = os.environ.get("GITHUB_TOKEN")
68    if not github_token:
69        github_token = resolve_default_github_token()
70
71    count = generate_roster_to_file(
72        output_path=Path(output),
73        slack_token=slack_token,
74        github_token=github_token,
75    )
76    print_success(f"Roster generated: {count} members written to {output}")
77
78
79@roster_app.command(name="summary")
80def roster_summary(
81    input: Annotated[
82        str,
83        Parameter(
84            help="Path to a roster JSON file (produced by the generate command)."
85        ),
86    ],
87) -> None:
88    """Print a matching summary from a roster JSON artifact.
89
90    Categorises roster members into matched, Slack-only, and GitHub-only
91    groups and prints unmatched members for review.
92    """
93    path = Path(input)
94    if not path.exists():
95        exit_with_error(f"Roster file not found: {input}")
96
97    text = summarize_roster(path)
98    console.print(text)