github-mcp-agent 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,5 @@
1
+ {
2
+ "permissions": {
3
+ "deny": ["Read(.env)", "Read(.env.*)"]
4
+ }
5
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Skill(update-config)",
5
+ "Skill(update-config:*)",
6
+ "WebFetch(domain:github.com)",
7
+ "WebFetch(domain:raw.githubusercontent.com)",
8
+ "WebFetch(domain:docs.github.com)",
9
+ "WebFetch(domain:api.github.com)",
10
+ "WebSearch"
11
+ ]
12
+ }
13
+ }
@@ -0,0 +1,3 @@
1
+ GITHUB_TOKEN=your_github_token_here
2
+ AWS_REGION=eu-west-3
3
+ MODEL_ID=anthropic.claude-3-5-sonnet-20240620-v1:0
@@ -0,0 +1,4 @@
1
+ .env
2
+ .venv/
3
+ __pycache__/
4
+ *.pyc
@@ -0,0 +1,14 @@
1
+ Metadata-Version: 2.4
2
+ Name: github-mcp-agent
3
+ Version: 0.1.0
4
+ Summary: A GitHub AI agent powered by AWS Bedrock and MCP
5
+ Requires-Python: >=3.10
6
+ Requires-Dist: anthropic
7
+ Requires-Dist: boto3
8
+ Requires-Dist: click
9
+ Requires-Dist: litellm
10
+ Requires-Dist: mcp
11
+ Requires-Dist: python-dotenv
12
+ Requires-Dist: questionary
13
+ Requires-Dist: rich
14
+ Requires-Dist: strands-agents
@@ -0,0 +1,108 @@
1
+ # GitHub MCP Agent
2
+
3
+ Talk to your GitHub repos, issues, and project boards in plain English from your terminal.
4
+
5
+ ```
6
+ You > list open issues in my raytracer repo
7
+ You > set priority of issue #42 to urgent
8
+ You > what's in progress on the project board?
9
+ You > which issues are assigned to maryam?
10
+ ```
11
+
12
+ ---
13
+
14
+ ## Install
15
+
16
+ ```bash
17
+ git clone https://github.com/OmarCodes022/GitHub-MCP-Agent
18
+ cd GitHub-MCP-Agent
19
+ uv tool install . # or: pip install .
20
+ ```
21
+
22
+ ---
23
+
24
+ ## Setup
25
+
26
+ ```bash
27
+ github-agent setup
28
+ ```
29
+
30
+ Interactive wizard:
31
+
32
+ 1. GitHub token (validated immediately)
33
+ 2. AI provider — choose one:
34
+ - **AWS Bedrock** — existing profile, access keys, or SSO
35
+ - **Anthropic API** — API key from console.anthropic.com
36
+ - **OpenAI** — API key from platform.openai.com
37
+ - **Google Gemini** — API key from aistudio.google.com
38
+ - **GitHub Copilot** — uses your GitHub token, requires a Copilot subscription
39
+ - **Local (Ollama)** — picks from your installed models, no API key needed
40
+ 3. Model selection (scrollable menu)
41
+ 4. Pulls the GitHub MCP Docker image
42
+
43
+ Config saved to `~/.config/github-mcp-agent/.env`.
44
+
45
+ ---
46
+
47
+ ## Usage
48
+
49
+ ```bash
50
+ github-agent # start the agent
51
+ github-agent setup # full setup wizard
52
+ github-agent provider # switch AI provider and model
53
+ github-agent model # switch model within current provider
54
+ github-agent token # update GitHub token
55
+ github-agent config # edit config file in $EDITOR
56
+ github-agent prompt # customize the system prompt in $EDITOR
57
+ github-agent -v # start with verbose tool output
58
+ ```
59
+
60
+ ---
61
+
62
+ ## Requirements
63
+
64
+ | Requirement | Notes |
65
+ |---|---|
66
+ | Python 3.10+ | |
67
+ | Docker | Must be running — used for the GitHub MCP server |
68
+ | GitHub token | Scopes: `repo`, `read:org`, `project` — [github.com/settings/tokens](https://github.com/settings/tokens) |
69
+ | Provider credentials | See setup wizard |
70
+
71
+ **For AWS Bedrock:** enable Claude model access in the [Bedrock console](https://console.aws.amazon.com/bedrock) under Model access.
72
+
73
+ **For Ollama:** install from [ollama.com](https://ollama.com), run `ollama serve`. Models with good tool-calling support: `qwen2.5`, `llama3.1`, `mistral`.
74
+
75
+ **For GitHub Copilot:** requires an active Copilot subscription (student pack, individual, or business).
76
+
77
+ ---
78
+
79
+ ## How it works
80
+
81
+ ```
82
+ You (terminal)
83
+
84
+
85
+ github-agent (CLI)
86
+
87
+ ├── AI model (Bedrock / Anthropic / OpenAI / Gemini / Copilot / Ollama)
88
+
89
+ └── GitHub MCP Server (Docker)
90
+
91
+ └── GitHub API
92
+ ```
93
+
94
+ Uses the [Strands Agents SDK](https://github.com/strands-agents/sdk-python) with the [GitHub MCP server](https://github.com/github/github-mcp-server).
95
+
96
+ ---
97
+
98
+ ## Troubleshooting
99
+
100
+ **`GITHUB_TOKEN` not set** — run `github-agent setup`
101
+
102
+ **Docker not running** — start Docker Desktop or `sudo systemctl start docker`
103
+
104
+ **Bedrock access denied** — check IAM permissions and model access in your region
105
+
106
+ **Ollama model not found** — run `ollama list` to confirm the model name, re-run `github-agent model`
107
+
108
+ **`No module named github_mcp_agent`** — reinstall with `uv tool install .` from the project directory
File without changes
@@ -0,0 +1,103 @@
1
+ import os
2
+ from contextlib import contextmanager
3
+ from pathlib import Path
4
+
5
+ from dotenv import load_dotenv
6
+ from strands import Agent
7
+ from strands.tools.mcp.mcp_client import MCPClient
8
+ from mcp import StdioServerParameters
9
+ from mcp.client.stdio import stdio_client
10
+
11
+ from github_mcp_agent.tools import detect_current_repo, local_tools
12
+ from github_mcp_agent import providers
13
+
14
+
15
+ _config_dir = Path.home() / ".config" / "github-mcp-agent"
16
+ load_dotenv(_config_dir / ".env")
17
+ load_dotenv()
18
+
19
+ MODEL_ID = os.getenv("MODEL_ID", "us.anthropic.claude-haiku-4-5-20251001-v1:0")
20
+ PROVIDER = os.getenv("PROVIDER", "bedrock")
21
+ VERBOSE = os.getenv("VERBOSE", "").lower() in ("1", "true", "yes")
22
+
23
+
24
+ def _load_system_prompt() -> tuple[str, str | None]:
25
+ custom = _config_dir / "system_prompt.txt"
26
+ if custom.exists():
27
+ prompt = custom.read_text()
28
+ else:
29
+ from importlib.resources import files
30
+ prompt = files("github_mcp_agent").joinpath("system_prompt.txt").read_text()
31
+
32
+ repo = detect_current_repo()
33
+ if repo != "No GitHub remote detected":
34
+ prompt += f"\n\nThe user is currently working in the GitHub repository: {repo}. Default to this repository for all actions unless the user explicitly specifies another."
35
+ return prompt, repo
36
+ return prompt, None
37
+
38
+
39
+ def _make_verbose_callback(provider: str):
40
+ import json as _json
41
+ is_ollama = provider == "ollama"
42
+
43
+ def callback(**kwargs):
44
+ if "current_tool_use" in kwargs:
45
+ tool = kwargs["current_tool_use"]
46
+ if tool.get("name"):
47
+ print(f"\n \033[2;36m> {tool['name']}\033[0m", flush=True)
48
+ if "data" in kwargs:
49
+ text = kwargs["data"]
50
+ if is_ollama:
51
+ try:
52
+ parsed = _json.loads(text)
53
+ if isinstance(parsed, dict) and "text" in parsed:
54
+ text = parsed["text"]
55
+ except Exception:
56
+ pass
57
+ print(text, end="", flush=True)
58
+
59
+ return callback
60
+
61
+
62
+ @contextmanager
63
+ def create_agent(provider=None, model_id=None, verbose=False):
64
+ _provider = provider or PROVIDER
65
+ _model_id = model_id or MODEL_ID
66
+
67
+ token = os.environ.get("GITHUB_TOKEN")
68
+ if not token:
69
+ raise RuntimeError(
70
+ "GITHUB_TOKEN is not set. Run 'github-agent setup' to configure."
71
+ )
72
+
73
+ mcp_client = MCPClient(
74
+ lambda: stdio_client(
75
+ StdioServerParameters(
76
+ command="docker",
77
+ args=[
78
+ "run", "-i", "--rm",
79
+ "-e", "GITHUB_PERSONAL_ACCESS_TOKEN",
80
+ "ghcr.io/github/github-mcp-server",
81
+ "stdio",
82
+ "--toolsets", "all",
83
+ "--log-file", "/dev/null",
84
+ ],
85
+ env={"GITHUB_PERSONAL_ACCESS_TOKEN": token},
86
+ )
87
+ )
88
+ )
89
+
90
+ model = providers.build_model(_provider, _model_id)
91
+ system_prompt, current_repo = _load_system_prompt()
92
+
93
+ with mcp_client:
94
+ mcp_tools = mcp_client.list_tools_sync()
95
+ agent_kwargs = dict(model=model, tools=mcp_tools + local_tools, system_prompt=system_prompt)
96
+ if verbose or VERBOSE:
97
+ agent_kwargs["callback_handler"] = _make_verbose_callback(_provider)
98
+ else:
99
+ cb = providers.make_callback(_provider)
100
+ if cb:
101
+ agent_kwargs["callback_handler"] = cb
102
+ agent = Agent(**agent_kwargs)
103
+ yield agent, current_repo, len(mcp_tools) + len(local_tools)
@@ -0,0 +1,184 @@
1
+ import os
2
+ import readline
3
+ import select
4
+ import subprocess
5
+ import sys
6
+ from pathlib import Path
7
+
8
+ import click
9
+ import questionary
10
+ from rich.console import Console
11
+ from rich.rule import Rule
12
+ from rich.text import Text
13
+
14
+ console = Console()
15
+
16
+ CONFIG_DIR = Path.home() / ".config" / "github-mcp-agent"
17
+
18
+ _PROVIDER_CHOICES = [
19
+ "AWS Bedrock", "Anthropic API", "OpenAI", "Google Gemini", "GitHub Copilot", "Local (Ollama)"
20
+ ]
21
+ _PROVIDER_KEY = {
22
+ "AWS Bedrock": "bedrock",
23
+ "Anthropic API": "anthropic",
24
+ "OpenAI": "openai",
25
+ "Google Gemini": "gemini",
26
+ "GitHub Copilot": "copilot",
27
+ "Local (Ollama)": "ollama",
28
+ }
29
+
30
+
31
+ def _pick_model_for_provider(provider: str, _ask) -> tuple[str, str]:
32
+ import github_mcp_agent.providers as pkg
33
+ mod = getattr(pkg, provider)
34
+ console.print(f" [dim]Provider: {provider} (to switch, run: github-agent provider)[/dim]")
35
+ if provider == "ollama":
36
+ model_id = mod.pick_model(_ask)
37
+ else:
38
+ model_choices = [f"{name} ({desc})" for _, name, desc in mod.MODELS]
39
+ model_display = _ask(questionary.select, "Model:", choices=model_choices)
40
+ model_id = mod.MODELS[model_choices.index(model_display)][0]
41
+ return provider, model_id
42
+
43
+
44
+ def _run_agent(verbose: bool = False):
45
+ from github_mcp_agent.agent import MODEL_ID, PROVIDER, create_agent
46
+ try:
47
+ with create_agent(verbose=verbose) as (agent, current_repo, total_tools):
48
+ console.print()
49
+ console.print(Rule("[bold green]GitHub MCP Agent[/bold green]"))
50
+ repo_label = f"[bold]{current_repo}[/bold]" if current_repo else "[dim]none detected[/dim]"
51
+ console.print(f" [dim]Loaded [bold]{total_tools}[/bold] tools | Repo: {repo_label} | Provider: [bold]{PROVIDER}[/bold] | Model: [bold]{MODEL_ID}[/bold] | Type 'exit' to quit[/dim]")
52
+ console.print(Rule())
53
+ console.print()
54
+
55
+ while True:
56
+ try:
57
+ user_input = input("\033[1;36m You > \033[0m")
58
+ while select.select([sys.stdin], [], [], 0.05)[0]:
59
+ user_input += "\n" + sys.stdin.readline().rstrip("\n")
60
+ except (KeyboardInterrupt, EOFError):
61
+ break
62
+
63
+ if not user_input.strip():
64
+ continue
65
+ if user_input.strip().lower() in ["exit", "quit"]:
66
+ break
67
+
68
+ console.print()
69
+ console.print(Text(" Agent", style="bold magenta"))
70
+ console.print()
71
+
72
+ try:
73
+ agent(user_input)
74
+ except Exception as e:
75
+ console.print(f"[bold red]Error:[/bold red] {e}")
76
+
77
+ console.print()
78
+
79
+ console.print()
80
+ console.print(Rule("[dim]Session ended[/dim]"))
81
+ console.print()
82
+
83
+ except RuntimeError as e:
84
+ console.print(f"\n[bold red]{e}[/bold red]\n")
85
+ sys.exit(1)
86
+ except Exception as e:
87
+ console.print(f"\n[bold red]Failed to start:[/bold red] {e}\n")
88
+ sys.exit(1)
89
+
90
+
91
+ def _open_config():
92
+ CONFIG_DIR.mkdir(parents=True, exist_ok=True)
93
+ config_file = CONFIG_DIR / ".env"
94
+ if not config_file.exists():
95
+ config_file.write_text(
96
+ "GITHUB_TOKEN=\nAWS_PROFILE=default\nAWS_REGION=us-east-1\nMODEL_ID=us.anthropic.claude-haiku-4-5-20251001-v1:0\n"
97
+ )
98
+ editor = os.environ.get("EDITOR", "nano")
99
+ subprocess.run([editor, str(config_file)])
100
+
101
+
102
+ def _open_prompt():
103
+ CONFIG_DIR.mkdir(parents=True, exist_ok=True)
104
+ prompt_file = CONFIG_DIR / "system_prompt.txt"
105
+ if not prompt_file.exists():
106
+ from importlib.resources import files
107
+ default = files("github_mcp_agent").joinpath("system_prompt.txt").read_text()
108
+ prompt_file.write_text(default)
109
+ console.print(f" [dim]Created {prompt_file} with default prompt - edit to customize.[/dim]\n")
110
+ editor = os.environ.get("EDITOR", "nano")
111
+ subprocess.run([editor, str(prompt_file)])
112
+
113
+
114
+ @click.group(invoke_without_command=True, context_settings={"help_option_names": ["-h", "--help"]})
115
+ @click.option("--verbose", "-v", is_flag=True, default=False, help="Print tool calls as they happen")
116
+ @click.pass_context
117
+ def cli(ctx, verbose):
118
+ """GitHub MCP Agent - talk to your repos in plain English."""
119
+ if ctx.invoked_subcommand is None:
120
+ _run_agent(verbose=verbose)
121
+
122
+
123
+ @cli.command()
124
+ def setup():
125
+ """Run the interactive setup wizard."""
126
+ from github_mcp_agent.setup_wizard import run
127
+ run()
128
+
129
+
130
+ @cli.command(name="provider")
131
+ def switch_provider():
132
+ """Switch AI provider - pick credentials + model and save to config."""
133
+ from github_mcp_agent.setup_wizard import _ask, _write_config
134
+ from github_mcp_agent import providers as _providers
135
+ provider_display = _ask(questionary.select, "Provider:", choices=_PROVIDER_CHOICES)
136
+ provider_key = _PROVIDER_KEY[provider_display]
137
+ provider_values = _providers.setup(provider_key, _ask)
138
+ _write_config({"PROVIDER": provider_key, **provider_values})
139
+ console.print(" [green]Saved.[/green]")
140
+
141
+
142
+ @cli.command(name="model")
143
+ def switch_model():
144
+ """Pick a model for the current provider and save to config."""
145
+ from github_mcp_agent.agent import PROVIDER
146
+ from github_mcp_agent.setup_wizard import _ask, _write_config
147
+ _, effective_model = _pick_model_for_provider(PROVIDER, _ask)
148
+ _write_config({"MODEL_ID": effective_model})
149
+ console.print(f" [green]Saved:[/green] model={effective_model}")
150
+
151
+
152
+ @cli.command()
153
+ def token():
154
+ """Update your GitHub Personal Access Token."""
155
+ from github_mcp_agent.setup_wizard import _ask, _validate_github_token, _write_config
156
+ new_token = _ask(questionary.password, "GitHub Personal Access Token:")
157
+ console.print(" Validating...", end=" ")
158
+ if _validate_github_token(new_token):
159
+ console.print("[green]valid[/green]")
160
+ _write_config({"GITHUB_TOKEN": new_token})
161
+ console.print(" [green]Saved.[/green]")
162
+ else:
163
+ console.print("[red]invalid - check the token and scopes[/red]")
164
+ sys.exit(1)
165
+
166
+
167
+ @cli.command()
168
+ def config():
169
+ """Open the config file in $EDITOR."""
170
+ _open_config()
171
+
172
+
173
+ @cli.command()
174
+ def prompt():
175
+ """Open the system prompt file in $EDITOR."""
176
+ _open_prompt()
177
+
178
+
179
+ def main():
180
+ cli()
181
+
182
+
183
+ if __name__ == "__main__":
184
+ main()
@@ -0,0 +1,32 @@
1
+ from . import anthropic, bedrock, copilot, gemini, ollama, openai
2
+
3
+
4
+ _REGISTRY = {
5
+ "bedrock": bedrock,
6
+ "anthropic": anthropic,
7
+ "openai": openai,
8
+ "gemini": gemini,
9
+ "copilot": copilot,
10
+ "ollama": ollama,
11
+ }
12
+
13
+
14
+ def build_model(provider: str, model_id: str):
15
+ mod = _REGISTRY.get(provider)
16
+ if mod is None:
17
+ raise RuntimeError(f"Unknown provider: {provider}")
18
+ return mod.build_model(model_id)
19
+
20
+
21
+ def make_callback(provider: str):
22
+ if provider == "ollama":
23
+ return ollama.make_callback()
24
+ return None
25
+
26
+
27
+ def setup(provider: str, _ask) -> dict:
28
+ mod = _REGISTRY.get(provider)
29
+ if mod is None:
30
+ raise RuntimeError(f"Unknown provider: {provider}")
31
+ return mod.setup(_ask)
32
+ pypi-AgEIcHlwaS5vcmcCJGU0ZDI1YWQ5LTEyOGMtNDUyZi1iNmZkLTEzOWU0MmRkNDIzOAACKlszLCJjZTAzNzUyYy00ZjNhLTQ3MTYtYjM2ZS02YmY0ZGNhZjkyMDciXQAABiD04xa6bIcHRFlnmMHzKPunpg81Z6OhNO_P102kWxtY9g
@@ -0,0 +1,28 @@
1
+ import os
2
+
3
+ import questionary
4
+
5
+ MODELS = [
6
+ ("claude-haiku-4-5-20251001", "claude-haiku-4-5", "fastest"),
7
+ ("claude-sonnet-4-6", "claude-sonnet-4-6", "balanced"),
8
+ ("claude-opus-4-7", "claude-opus-4-7", "most capable"),
9
+ ]
10
+
11
+
12
+ def build_model(model_id: str):
13
+ from strands.models.anthropic import AnthropicModel
14
+ if not os.environ.get("ANTHROPIC_API_KEY"):
15
+ raise RuntimeError("ANTHROPIC_API_KEY is not set. Run 'github-agent setup' to configure.")
16
+ return AnthropicModel(model_id=model_id, max_tokens=8096)
17
+
18
+
19
+ def setup(_ask) -> dict:
20
+ from rich.console import Console
21
+ Console().print(" [dim]Get your key at: console.anthropic.com/settings/keys[/dim]")
22
+ key = _ask(questionary.password, "Anthropic API key (sk-ant-...):")
23
+ model_choices = [f"{name} ({desc})" for _, name, desc in MODELS]
24
+ model_display = _ask(questionary.select, "Model:", choices=model_choices)
25
+ return {
26
+ "ANTHROPIC_API_KEY": key,
27
+ "MODEL_ID": MODELS[model_choices.index(model_display)][0],
28
+ }
@@ -0,0 +1,112 @@
1
+ import os
2
+ import subprocess
3
+ from pathlib import Path
4
+
5
+ import questionary
6
+
7
+ REGIONS = [
8
+ ("us-east-1", "N. Virginia - recommended"),
9
+ ("us-west-2", "Oregon"),
10
+ ("eu-west-1", "Ireland"),
11
+ ("eu-central-1", "Frankfurt"),
12
+ ("eu-west-3", "Paris"),
13
+ ("ap-northeast-1", "Tokyo"),
14
+ ("ap-southeast-1", "Singapore"),
15
+ ("ap-southeast-2", "Sydney"),
16
+ ("ca-central-1", "Canada"),
17
+ ("sa-east-1", "Sao Paulo"),
18
+ ]
19
+
20
+ MODELS = [
21
+ ("us.anthropic.claude-haiku-4-5-20251001-v1:0", "claude-haiku-4-5", "fastest, cheapest"),
22
+ ("us.anthropic.claude-sonnet-4-6", "claude-sonnet-4-6", "balanced"),
23
+ ("us.anthropic.claude-opus-4-7", "claude-opus-4-7", "most capable"),
24
+ ]
25
+
26
+
27
+ def build_model(model_id: str):
28
+ from strands.models import BedrockModel
29
+ return BedrockModel(model_id=model_id, region_name=os.getenv("AWS_REGION", "us-east-1"))
30
+
31
+
32
+ def _list_aws_profiles() -> list[str]:
33
+ profiles = []
34
+ for f in [Path.home() / ".aws" / "credentials", Path.home() / ".aws" / "config"]:
35
+ if f.exists():
36
+ for line in f.read_text().splitlines():
37
+ if line.startswith("[") and line.endswith("]"):
38
+ name = line[1:-1].replace("profile ", "")
39
+ if name not in profiles:
40
+ profiles.append(name)
41
+ return profiles
42
+
43
+
44
+ def _validate_aws_profile(profile: str) -> bool:
45
+ env = os.environ.copy()
46
+ env["AWS_PROFILE"] = profile
47
+ return subprocess.run(["aws", "sts", "get-caller-identity"], capture_output=True, env=env).returncode == 0
48
+
49
+
50
+ def setup(_ask) -> dict:
51
+ values = {}
52
+
53
+ while True:
54
+ cred_method = _ask(
55
+ questionary.select,
56
+ "AWS credentials:",
57
+ choices=["Use existing profile", "Enter access keys directly", "AWS SSO / browser login"],
58
+ )
59
+
60
+ if cred_method == "Use existing profile":
61
+ profiles = _list_aws_profiles()
62
+ choices = profiles + ["other (type manually)"] if profiles else ["other (type manually)"]
63
+ choice = _ask(questionary.select, "AWS profile:", choices=choices)
64
+ if choice == "other (type manually)":
65
+ profile = _ask(questionary.text, "Profile name:", default="default")
66
+ else:
67
+ profile = choice
68
+ from rich.console import Console
69
+ Console().print(" Validating...", end=" ")
70
+ if _validate_aws_profile(profile):
71
+ Console().print("[green]valid[/green]")
72
+ values["AWS_PROFILE"] = profile
73
+ break
74
+ else:
75
+ Console().print("[red]invalid - check your AWS credentials[/red]")
76
+
77
+ elif cred_method == "Enter access keys directly":
78
+ values["AWS_ACCESS_KEY_ID"] = _ask(questionary.text, "AWS Access Key ID:")
79
+ values["AWS_SECRET_ACCESS_KEY"] = _ask(questionary.password, "AWS Secret Access Key:")
80
+ session_token = _ask(questionary.text, "AWS Session Token (leave blank if none):")
81
+ if session_token:
82
+ values["AWS_SESSION_TOKEN"] = session_token
83
+ break
84
+
85
+ elif cred_method == "AWS SSO / browser login":
86
+ profiles = _list_aws_profiles()
87
+ choices = profiles + ["other (type manually)"] if profiles else ["other (type manually)"]
88
+ choice = _ask(questionary.select, "SSO profile to use:", choices=choices)
89
+ if choice == "other (type manually)":
90
+ profile = _ask(questionary.text, "Profile name:")
91
+ else:
92
+ profile = choice
93
+ from rich.console import Console
94
+ console = Console()
95
+ console.print(f"\n Running [bold]aws sso login --profile {profile}[/bold]")
96
+ result = subprocess.run(["aws", "sso", "login", "--profile", profile])
97
+ if result.returncode == 0:
98
+ values["AWS_PROFILE"] = profile
99
+ break
100
+ console.print("[red]SSO login failed - profile may not be configured for SSO.[/red]")
101
+ console.print("[dim] Run: aws configure sso --profile <name>[/dim]")
102
+ console.print("[dim] Or pick a different credential method below.[/dim]\n")
103
+
104
+ region_choices = [f"{r} ({label})" for r, label in REGIONS]
105
+ region_display = _ask(questionary.select, "AWS Region:", choices=region_choices)
106
+ values["AWS_REGION"] = region_display.split()[0]
107
+
108
+ model_choices = [f"{name} ({desc}) -> {mid}" for mid, name, desc in MODELS]
109
+ model_display = _ask(questionary.select, "Model:", choices=model_choices)
110
+ values["MODEL_ID"] = MODELS[model_choices.index(model_display)][0]
111
+
112
+ return values
@@ -0,0 +1,32 @@
1
+ import os
2
+
3
+ import questionary
4
+
5
+ MODELS = [
6
+ ("gpt-4o", "gpt-4o", "flagship"),
7
+ ("gpt-4o-mini", "gpt-4o-mini", "fast, cheap"),
8
+ ("claude-sonnet-4-5", "claude-sonnet-4-5", "Anthropic via Copilot"),
9
+ ("o3-mini", "o3-mini", "reasoning"),
10
+ ("gemini-1.5-pro", "gemini-1.5-pro", "Google via Copilot"),
11
+ ]
12
+
13
+
14
+ def build_model(model_id: str):
15
+ from strands.models.litellm import LiteLLMModel
16
+ token = os.environ.get("GITHUB_TOKEN")
17
+ if not token:
18
+ raise RuntimeError("GITHUB_TOKEN is not set. Run 'github-agent setup' to configure.")
19
+ return LiteLLMModel(
20
+ model_id=f"openai/{model_id}",
21
+ params={"api_base": "https://api.githubcopilot.com", "api_key": token},
22
+ )
23
+
24
+
25
+ def setup(_ask) -> dict:
26
+ from rich.console import Console
27
+ console = Console()
28
+ console.print(" [dim]Uses your GitHub token - no extra API key needed.[/dim]")
29
+ console.print(" [dim]Requires an active Copilot subscription (student pack, individual, or business).[/dim]")
30
+ model_choices = [f"{name} ({desc})" for _, name, desc in MODELS]
31
+ model_display = _ask(questionary.select, "Model:", choices=model_choices)
32
+ return {"MODEL_ID": MODELS[model_choices.index(model_display)][0]}
@@ -0,0 +1,28 @@
1
+ import os
2
+
3
+ import questionary
4
+
5
+ MODELS = [
6
+ ("gemini-2.0-flash", "gemini-2.0-flash", "fast, recommended"),
7
+ ("gemini-1.5-pro", "gemini-1.5-pro", "most capable"),
8
+ ("gemini-1.5-flash", "gemini-1.5-flash", "fast"),
9
+ ]
10
+
11
+
12
+ def build_model(model_id: str):
13
+ from strands.models.litellm import LiteLLMModel
14
+ if not os.environ.get("GEMINI_API_KEY"):
15
+ raise RuntimeError("GEMINI_API_KEY is not set. Run 'github-agent setup' to configure.")
16
+ return LiteLLMModel(model_id=f"gemini/{model_id}")
17
+
18
+
19
+ def setup(_ask) -> dict:
20
+ from rich.console import Console
21
+ Console().print(" [dim]Get your key at: aistudio.google.com/apikey[/dim]")
22
+ key = _ask(questionary.password, "Google AI Studio API key:")
23
+ model_choices = [f"{name} ({desc})" for _, name, desc in MODELS]
24
+ model_display = _ask(questionary.select, "Model:", choices=model_choices)
25
+ return {
26
+ "GEMINI_API_KEY": key,
27
+ "MODEL_ID": MODELS[model_choices.index(model_display)][0],
28
+ }