codpilot-cli 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,10 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "WebFetch(domain:pypi.org)",
5
+ "Bash(pip index:*)",
6
+ "Bash(pip install:*)",
7
+ "Bash(uv pip install:*)"
8
+ ]
9
+ }
10
+ }
@@ -0,0 +1,12 @@
1
+ # Python-generated files
2
+ __pycache__/
3
+ *.py[oc]
4
+ build/
5
+ dist/
6
+ wheels/
7
+ *.egg-info
8
+
9
+ # Virtual environments
10
+ .venv
11
+ .env*.local
12
+ .DS_Store
@@ -0,0 +1 @@
1
+ 3.14
@@ -0,0 +1,88 @@
1
+ Metadata-Version: 2.4
2
+ Name: codpilot-cli
3
+ Version: 0.1.0
4
+ Summary: A multi-agent CLI tool that integrates with GitHub to review PRs, create features, and suggest changes.
5
+ License-Expression: MIT
6
+ Keywords: cli,codpilot,github
7
+ Classifier: Development Status :: 3 - Alpha
8
+ Classifier: Environment :: Console
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: Programming Language :: Python :: 3.14
11
+ Classifier: Topic :: Software Development :: Quality Assurance
12
+ Requires-Python: >=3.14
13
+ Requires-Dist: google-adk>=1.24.0
14
+ Requires-Dist: keyring>=25.7.0
15
+ Requires-Dist: litellm>=1.81.11
16
+ Requires-Dist: python-dotenv>=1.2.1
17
+ Requires-Dist: questionary>=2.1.1
18
+ Requires-Dist: rich>=14.3.2
19
+ Requires-Dist: typer>=0.23.1
20
+ Description-Content-Type: text/markdown
21
+
22
+ # CodePilot CLI
23
+
24
+ A multi agent CLI tool that integrates with GitHub that can help you to review pull requests, create new features, and suggest changes on your behalf — all from your terminal.
25
+
26
+ ## Features
27
+
28
+ **Review PR** — Analyzes the pull request and posts inline comments with suggestions.
29
+
30
+ **Create Feature** — It can implements new features, then opens a draft pull request against the target repository.
31
+
32
+ **Suggest Changes** — Participate in GitHub Issue discussions, analyze the codebase and conversation, and post technical suggestions as comments.
33
+
34
+ ## Available Commands
35
+
36
+ | Command | Description |
37
+ | ------------------------------ | ----------------------------- |
38
+ | `codepilot run` | Run the agent |
39
+ | `codepilot reset-github-token` | Reset the stored GitHub token |
40
+ | `codepilot change-llm` | Change the LLM model |
41
+ | `codepilot --version, -v` | Show the current version |
42
+
43
+ `codepilot run` has the following options:
44
+
45
+ - Review PR
46
+ - Create Feature
47
+ - Suggest Changes
48
+
49
+ ## Prerequisites
50
+
51
+ - Python 3.14+
52
+ - A GitHub personal access token
53
+ - An API key for at least one LLM provider (Gemini, OpenAI, or Anthropic)
54
+
55
+ ## Installing locally
56
+
57
+ ```bash
58
+ git clone https://github.com/<your-username>/ai-coding-agent.git
59
+ cd codepilot
60
+ pip install -e .
61
+ ```
62
+
63
+ Verify the installation:
64
+
65
+ ```bash
66
+ codepilot --version
67
+ ```
68
+
69
+ ## Usage
70
+
71
+ ```bash
72
+ codepilot run
73
+ ```
74
+
75
+ The interactive prompt will walk you through:
76
+
77
+ 1. Selecting an agent (Review PR / Create Feature / Suggest Changes)
78
+ 2. Entering the GitHub URL (PR, repository, or issue depending on the agent)
79
+ 3. Choosing an LLM provider (Gemini / OpenAI / Anthropic)
80
+ 4. Providing API credentials for github and LLM (cached in your system keychain for future runs)
81
+
82
+ ## Supported Models
83
+
84
+ | Provider | Model |
85
+ | --------- | ---------------------------- |
86
+ | Gemini | `gemini-3-flash-preview` |
87
+ | OpenAI | `gpt-5-mini` |
88
+ | Anthropic | `claude-3-7-sonnet-20250219` |
@@ -0,0 +1,67 @@
1
+ # CodePilot CLI
2
+
3
+ A multi agent CLI tool that integrates with GitHub that can help you to review pull requests, create new features, and suggest changes on your behalf — all from your terminal.
4
+
5
+ ## Features
6
+
7
+ **Review PR** — Analyzes the pull request and posts inline comments with suggestions.
8
+
9
+ **Create Feature** — It can implements new features, then opens a draft pull request against the target repository.
10
+
11
+ **Suggest Changes** — Participate in GitHub Issue discussions, analyze the codebase and conversation, and post technical suggestions as comments.
12
+
13
+ ## Available Commands
14
+
15
+ | Command | Description |
16
+ | ------------------------------ | ----------------------------- |
17
+ | `codepilot run` | Run the agent |
18
+ | `codepilot reset-github-token` | Reset the stored GitHub token |
19
+ | `codepilot change-llm` | Change the LLM model |
20
+ | `codepilot --version, -v` | Show the current version |
21
+
22
+ `codepilot run` has the following options:
23
+
24
+ - Review PR
25
+ - Create Feature
26
+ - Suggest Changes
27
+
28
+ ## Prerequisites
29
+
30
+ - Python 3.14+
31
+ - A GitHub personal access token
32
+ - An API key for at least one LLM provider (Gemini, OpenAI, or Anthropic)
33
+
34
+ ## Installing locally
35
+
36
+ ```bash
37
+ git clone https://github.com/<your-username>/ai-coding-agent.git
38
+ cd codepilot
39
+ pip install -e .
40
+ ```
41
+
42
+ Verify the installation:
43
+
44
+ ```bash
45
+ codepilot --version
46
+ ```
47
+
48
+ ## Usage
49
+
50
+ ```bash
51
+ codepilot run
52
+ ```
53
+
54
+ The interactive prompt will walk you through:
55
+
56
+ 1. Selecting an agent (Review PR / Create Feature / Suggest Changes)
57
+ 2. Entering the GitHub URL (PR, repository, or issue depending on the agent)
58
+ 3. Choosing an LLM provider (Gemini / OpenAI / Anthropic)
59
+ 4. Providing API credentials for github and LLM (cached in your system keychain for future runs)
60
+
61
+ ## Supported Models
62
+
63
+ | Provider | Model |
64
+ | --------- | ---------------------------- |
65
+ | Gemini | `gemini-3-flash-preview` |
66
+ | OpenAI | `gpt-5-mini` |
67
+ | Anthropic | `claude-3-7-sonnet-20250219` |
@@ -0,0 +1,49 @@
1
+ from google.adk.agents.llm_agent import LlmAgent
2
+ from google.adk.agents.loop_agent import LoopAgent
3
+ from google.adk.tools.tool_context import ToolContext
4
+ from app.agents.tools.github_mcp import github_mcp
5
+ from app.services.build_model_service import build_model
6
+
7
+
8
+ def finish_task(tool_context: ToolContext):
9
+ """Call this ONLY when a Pull Request URL has been successfully generated."""
10
+ tool_context.actions.escalate = True
11
+ return {"status": "complete"}
12
+
13
+
14
+ base_feature_agent = LlmAgent(
15
+ name="CodeAnalyzerAgent",
16
+ model=build_model(),
17
+ instruction="""
18
+ You are a coding agent.
19
+ 1. Analyze the repo. 2. Make changes. 3. Raise a Draft Pull Request.
20
+
21
+ IMPORTANT: If the chat history shows you already modified files but missed the PR,
22
+ skip the analysis and call the PR tool immediately.
23
+ """,
24
+ output_key="worker_output",
25
+ tools=[github_mcp()],
26
+ )
27
+
28
+ verifier_agent = LlmAgent(
29
+ name="CodeVerifierAgent",
30
+ model=build_model(),
31
+ instruction="""
32
+ Review the following output from the CodeAnalyzer:
33
+
34
+ \"\"\"
35
+ {{worker_output}}
36
+ \"\"\"
37
+
38
+ Task:
39
+ - If a GitHub Pull Request URL is visible above, call 'finish_task' immediately.
40
+ - If NO Pull Request URL is found, explicitly tell the worker: 'You failed to create a PR. Please execute the PR tool now.'
41
+ """,
42
+ tools=[finish_task],
43
+ )
44
+
45
+ feature_agent = LoopAgent(
46
+ name="PrGenerationAgent",
47
+ sub_agents=[base_feature_agent, verifier_agent],
48
+ max_iterations=3,
49
+ )
@@ -0,0 +1,70 @@
1
+ from google.adk.agents.llm_agent import LlmAgent
2
+ from app.agents.tools.github_mcp import github_mcp
3
+ from app.services.build_model_service import build_model
4
+ from google.adk.tools.tool_context import ToolContext
5
+ from google.adk.agents.loop_agent import LoopAgent
6
+
7
+
8
+ def finish_review(tool_context: ToolContext):
9
+ """Call this ONLY when the final PR summary and recommendation (Approve/Request Changes) have been posted."""
10
+ tool_context.actions.escalate = True
11
+ return {"status": "review_submitted"}
12
+
13
+
14
+ base_review_agent = LlmAgent(
15
+ name="PullRequestReviewWorker",
16
+ model=build_model(),
17
+ instruction="""
18
+ You are a senior software engineer.
19
+
20
+ TASK:
21
+ 1. Use github_mcp to get the PR diff.
22
+ 2. Review files one by one.
23
+ 3. Identify issues in:
24
+ - Correctness
25
+ - Edge cases
26
+ - Security
27
+ - Performance
28
+ - Readability & maintainability
29
+ - Architecture & consistency
30
+ - Tests (missing or insufficient)
31
+ 4. For each issue, use the 'post_inline_comment' tool.
32
+
33
+ RULES
34
+ - NO POSITIVE FEEDBACK: Never post "Good," "Consistent," or "Correct." If code is good, skip it.
35
+ - NO CHATTER: Do not explain your process.
36
+ - For code improvement comment must include a ```suggestion``` block.
37
+
38
+ IMPORTANT: Check the chat history. If you have already commented on certain files,
39
+ DO NOT repeat them. Move to the next file in the diff.
40
+
41
+ When finished with ALL files, provide a 'Final Summary' and call the tool to submit the overall review.
42
+ """,
43
+ output_key="review_output",
44
+ tools=[github_mcp()],
45
+ )
46
+
47
+ review_verifier_agent = LlmAgent(
48
+ name="ReviewVerifierAgent",
49
+ model=build_model(),
50
+ instruction="""
51
+ You are an auditor. Examine the 'review_output' provided below:
52
+
53
+ REVIEW_LOG:
54
+ {{review_output}}
55
+
56
+ CHECKLIST:
57
+ - Did the worker post a final summary? (Yes/No)
58
+ - Did the worker submit a formal recommendation (Approve/Reject)? (Yes/No)
59
+
60
+ IF BOTH ARE YES: Call 'finish_review' immediately.
61
+ IF NO: Tell the worker exactly what is missing (e.g., 'You reviewed the files but forgot to submit the final Approval').
62
+ """,
63
+ tools=[finish_review],
64
+ )
65
+
66
+ pr_review_agent = LoopAgent(
67
+ name="AutonomousPrReviewer",
68
+ sub_agents=[base_review_agent, review_verifier_agent],
69
+ max_iterations=5,
70
+ )
@@ -0,0 +1,59 @@
1
+ from google.adk.agents.llm_agent import LlmAgent
2
+ from google.adk.agents.loop_agent import LoopAgent
3
+ from google.adk.tools.tool_context import ToolContext
4
+ from app.agents.tools.github_mcp import github_mcp
5
+ from app.services.build_model_service import build_model
6
+
7
+
8
+ def finish_discussion(tool_context: ToolContext):
9
+ """Signals that the agent has successfully contributed to the discussion."""
10
+ tool_context.actions.escalate = True
11
+ return {"status": "contribution_posted"}
12
+
13
+
14
+ base_agent = LlmAgent(
15
+ name="IssueAdvisorAgent",
16
+ model=build_model(),
17
+ instruction="""
18
+ You are a Technical Advisor specializing in GitHub Issues. Your goal is to provide deep technical insights on reported problems or feature requests.
19
+
20
+ STEP 1: GATHER CONTEXT
21
+ - Fetch the Issue description and ALL existing comments.
22
+ - Search the codebase using relevant keywords from the issue to identify the specific files or functions involved.
23
+
24
+ STEP 2: ANALYZE
25
+ - Evaluate if the reported issue is a bug, a performance bottleneck, or a feature request.
26
+ - Propose a technical strategy or root cause analysis based on the codebase search.
27
+ - Acknowledge any previous comments to maintain a collaborative tone.
28
+
29
+ STEP 3: POST COMMENT
30
+ - Use the GitHub MCP tool to post your technical findings as a comment.
31
+
32
+ STRICT RULES:
33
+ - Do not look at Pull Requests. Never modify files or create branches.
34
+ - Do not offer to draft code changes.
35
+ """,
36
+ output_key="worker_output",
37
+ tools=[github_mcp()],
38
+ )
39
+
40
+ verifier_agent = LlmAgent(
41
+ name="ActionVerifier",
42
+ model=build_model(),
43
+ instruction="""
44
+ Review the 'worker_output'.
45
+
46
+ If the output mentions it is a Pull Request:
47
+ - Immediately call 'finish_discussion' to stop the loop.
48
+ If the text indicates a GitHub comment was successfully submitted via a tool call, run 'finish_discussion'.
49
+ If the text contains a technical analysis but NO evidence of a tool call (like 'comment_posted'),
50
+ explicitly command the agent: 'I see your analysis. You must now use the GitHub MCP tool to post this as a comment on the issue.'
51
+ """,
52
+ tools=[finish_discussion],
53
+ )
54
+
55
+ suggestion_agent = LoopAgent(
56
+ name="DiscussionAgent",
57
+ sub_agents=[base_agent, verifier_agent],
58
+ max_iterations=2,
59
+ )
@@ -0,0 +1,23 @@
1
+ import keyring
2
+ from google.adk.tools.mcp_tool import McpToolset
3
+ from google.adk.tools.mcp_tool.mcp_session_manager import StreamableHTTPServerParams
4
+
5
+ MCP_URL = "https://api.githubcopilot.com/mcp/"
6
+ SERVICE_NAME = "github-agent"
7
+ TOKEN_KEY = "github_token"
8
+
9
+
10
+ def github_mcp():
11
+ token = keyring.get_password(SERVICE_NAME, TOKEN_KEY)
12
+
13
+ return McpToolset(
14
+ connection_params=StreamableHTTPServerParams(
15
+ timeout=120,
16
+ url=MCP_URL,
17
+ headers={
18
+ "Authorization": f"Bearer {token}",
19
+ "X-MCP-Toolsets": "all",
20
+ "X-MCP-Readonly": "false",
21
+ },
22
+ ),
23
+ )
@@ -0,0 +1,44 @@
1
+ from app.workflows.agent_workflow import agent_workflow
2
+ import typer
3
+ import asyncio
4
+ from app.cli.inputs import (
5
+ ask_repo_url,
6
+ ask_agent_type,
7
+ ask_llm_model,
8
+ ask_github_token,
9
+ )
10
+ from app.cli.llm import ask_llm_token
11
+ from rich.console import Console
12
+ from rich.panel import Panel
13
+ from rich import box
14
+ from importlib.metadata import version
15
+ from app.cli.spinner import run_with_spinner
16
+
17
+ console = Console()
18
+
19
+
20
+ def run():
21
+ console.print(
22
+ Panel(
23
+ f"[bold cyan]CodPilot[/bold cyan] [green]v{version('codpilot')}[/green]\n"
24
+ "[dim]Autonomous coding agent for GitHub[/dim]",
25
+ box=box.DOUBLE,
26
+ border_style="cyan",
27
+ padding=(1, 4),
28
+ )
29
+ )
30
+ agent_type, description = ask_agent_type()
31
+ repo_url = ask_repo_url(agent_type)
32
+ model = ask_llm_model()
33
+ ask_llm_token(model)
34
+ ask_github_token()
35
+
36
+ asyncio.run(
37
+ run_with_spinner(
38
+ agent_workflow(
39
+ repo_url=repo_url, agent_type=agent_type, description=description
40
+ )
41
+ )
42
+ )
43
+
44
+ typer.echo("✅ Execution completed")
@@ -0,0 +1,8 @@
1
+ import typer
2
+ from importlib.metadata import version
3
+
4
+
5
+ def version_callback(value: bool):
6
+ if value:
7
+ typer.echo(f"codpilot v{version('ai-coding-agent')}")
8
+ raise typer.Exit()
@@ -0,0 +1,164 @@
1
+ import questionary
2
+ import keyring
3
+ import requests
4
+ import time
5
+ import typer
6
+
7
+ from app.cli.llm import ask_llm_token
8
+
9
+ WORKFLOW_OPTIONS = {
10
+ "review_pr": "Review PR",
11
+ "create_feature": "Create a new feature",
12
+ "suggest_changes": "Add your suggestions to the Issue",
13
+ }
14
+ LLM_MODELS = ["Gemini", "OpenAI", "Anthropic"]
15
+ SERVICE_NAME = "github-agent"
16
+ LLM_MODEL_KEY = "llm_model"
17
+ LLM_TOKEN_KEY = "llm_api_token"
18
+
19
+
20
+ def ask_repo_url(agent_type):
21
+ if agent_type == "create_feature":
22
+ msg = "Enter the GitHub repository URL:"
23
+ elif agent_type == "suggest_changes":
24
+ msg = "Enter the GitHub Issue URL:"
25
+ else:
26
+ msg = "Enter the GitHub PR URL:"
27
+
28
+ repo_url = questionary.text(
29
+ msg,
30
+ ).ask()
31
+
32
+ if not repo_url:
33
+ typer.echo("⛔️ URL is required")
34
+ raise SystemExit(1)
35
+
36
+ if not repo_url.startswith("https://github.com/"):
37
+ typer.echo("⛔️ Invalid URL")
38
+ raise SystemExit(1)
39
+
40
+ return repo_url
41
+
42
+
43
+ def ask_agent_type():
44
+ choice = questionary.select(
45
+ "Select mode:",
46
+ choices=[
47
+ questionary.Choice(title=label, value=key)
48
+ for key, label in WORKFLOW_OPTIONS.items()
49
+ ],
50
+ ).ask()
51
+
52
+ if not choice:
53
+ typer.echo("Mode is required")
54
+ raise SystemExit(1)
55
+
56
+ description = None
57
+ if choice == "create_feature":
58
+ description = questionary.text(
59
+ "Enter the details of the feature you want to create:",
60
+ ).ask()
61
+
62
+ if not description:
63
+ typer.echo("Description is required")
64
+ raise SystemExit(1)
65
+
66
+ return choice, description
67
+
68
+
69
+ def ask_llm_model():
70
+ selected_model = keyring.get_password(SERVICE_NAME, LLM_MODEL_KEY)
71
+
72
+ if selected_model:
73
+ typer.echo(f"Using LLM model {selected_model}")
74
+ return selected_model
75
+
76
+ model = questionary.select(
77
+ "Select LLM model:",
78
+ choices=LLM_MODELS,
79
+ ).ask()
80
+
81
+ if not model:
82
+ typer.echo("LLM model is required")
83
+ raise SystemExit(1)
84
+
85
+ keyring.set_password(SERVICE_NAME, LLM_MODEL_KEY, model)
86
+
87
+ return model
88
+
89
+
90
+ def ask_github_token():
91
+ token = keyring.get_password(SERVICE_NAME, "github_token")
92
+
93
+ if token:
94
+ return
95
+
96
+ token = questionary.password("Enter your GitHub API token:").ask()
97
+
98
+ if not token:
99
+ typer.echo("⛔️ GitHub token is required")
100
+ raise SystemExit(1)
101
+
102
+ if _validate_github_token(token):
103
+ keyring.set_password(SERVICE_NAME, "github_token", token)
104
+ time.sleep(1)
105
+ else:
106
+ typer.echo("⛔️ Invalid GitHub token")
107
+ raise SystemExit(1)
108
+
109
+
110
+ def reset_github_token():
111
+ existing = keyring.get_password(SERVICE_NAME, "github_token")
112
+
113
+ if not existing:
114
+ typer.echo("⚠️ No GitHub token found in keyring.")
115
+ return
116
+
117
+ confirm = questionary.confirm(
118
+ "Are you sure you want to reset the stored GitHub token?"
119
+ ).ask()
120
+
121
+ if not confirm:
122
+ typer.echo("ℹ️ Token reset cancelled.")
123
+ return
124
+
125
+ keyring.delete_password(SERVICE_NAME, "github_token")
126
+ typer.echo("🗑️ GitHub token removed.")
127
+
128
+ ask_github_token()
129
+ typer.echo("GitHub token has been reset successfully.")
130
+
131
+
132
+ def change_llm_model():
133
+ existing = keyring.get_password(SERVICE_NAME, LLM_MODEL_KEY)
134
+
135
+ if not existing:
136
+ typer.echo("⚠️ No LLM model found.")
137
+ return
138
+
139
+ confirm = questionary.confirm(
140
+ "Are you sure you want to change the LLM model?"
141
+ ).ask()
142
+
143
+ if not confirm:
144
+ typer.echo("ℹ️ Change LLM model cancelled.")
145
+ return
146
+
147
+ keyring.delete_password(SERVICE_NAME, LLM_MODEL_KEY)
148
+ model = ask_llm_model()
149
+ if keyring.get_password(SERVICE_NAME, LLM_TOKEN_KEY):
150
+ keyring.delete_password(SERVICE_NAME, LLM_TOKEN_KEY)
151
+
152
+ ask_llm_token(model)
153
+
154
+
155
+ def _validate_github_token(token: str) -> bool:
156
+ try:
157
+ resp = requests.get(
158
+ "https://api.github.com/user",
159
+ headers={"Authorization": f"Bearer {token}"},
160
+ timeout=5,
161
+ )
162
+ return resp.status_code == 200
163
+ except requests.RequestException:
164
+ return False