agentcode-cli 1.0.0__py3-none-any.whl → 1.2.0__py3-none-any.whl

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,362 @@
1
+ Metadata-Version: 2.4
2
+ Name: agentcode-cli
3
+ Version: 1.2.0
4
+ Summary: An open, multi-model agentic coding CLI — inspired by Claude Code
5
+ Author: Vignesh Pai
6
+ License: MIT License
7
+
8
+ Copyright (c) 2026 Vignesh Pai
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+
28
+ Project-URL: Homepage, https://github.com/vigp17/AgentCode
29
+ Project-URL: Repository, https://github.com/vigp17/AgentCode
30
+ Project-URL: Bug Tracker, https://github.com/vigp17/AgentCode/issues
31
+ Classifier: Programming Language :: Python :: 3
32
+ Classifier: Programming Language :: Python :: 3.11
33
+ Classifier: License :: OSI Approved :: MIT License
34
+ Classifier: Operating System :: OS Independent
35
+ Classifier: Environment :: Console
36
+ Classifier: Topic :: Software Development
37
+ Classifier: Topic :: Utilities
38
+ Requires-Python: >=3.11
39
+ Description-Content-Type: text/markdown
40
+ License-File: LICENSE
41
+ Requires-Dist: litellm>=1.40.0
42
+ Requires-Dist: rich>=13.0.0
43
+ Requires-Dist: python-dotenv>=1.0.0
44
+ Requires-Dist: mcp>=1.0.0
45
+ Provides-Extra: dev
46
+ Requires-Dist: build; extra == "dev"
47
+ Requires-Dist: twine; extra == "dev"
48
+ Dynamic: license-file
49
+
50
+ # AgentCode
51
+
52
+ An open, multi-model agentic coding assistant — available as a CLI and VS Code extension. Inspired by Claude Code.
53
+
54
+ Works with Claude, GPT, Gemini, and any model supported by [LiteLLM](https://docs.litellm.ai/docs/providers).
55
+
56
+ ---
57
+
58
+ ## Choose your setup
59
+
60
+ ### Option A: CLI only
61
+
62
+ ```bash
63
+ pip install agentcode-cli
64
+ ```
65
+
66
+ Add your API key to a `.env` file in your project:
67
+
68
+ ```
69
+ ANTHROPIC_API_KEY=sk-ant-...
70
+ OPENAI_API_KEY=sk-... # optional
71
+ GEMINI_API_KEY=... # optional
72
+ ```
73
+
74
+ Run:
75
+
76
+ ```bash
77
+ agentcode # interactive REPL
78
+ agentcode "fix the failing tests" # one-shot mode
79
+ agentcode --model gpt-4o # use a specific model
80
+ ```
81
+
82
+ ---
83
+
84
+ ### Option B: VS Code Extension
85
+
86
+ > The extension requires the CLI installed as a backend. Install it first.
87
+
88
+ **Step 1 — Install the CLI:**
89
+ ```bash
90
+ pip install agentcode-cli
91
+ ```
92
+
93
+ **Step 2 — Install the extension:**
94
+ - Search **AgentCode** in the VS Code Marketplace and install, or
95
+ - Download `agentcode-1.1.1.vsix` from the [GitHub releases page](https://github.com/vigp17/AgentCode/releases) and install via `Cmd+Shift+X` → `...` → **Install from VSIX...**
96
+
97
+ **Step 3 — Add your API key:**
98
+ `Cmd+,` → search **AgentCode** → paste your key into `agentcode.anthropicApiKey`
99
+
100
+ **Step 4 — Open the chat panel:**
101
+ Press `Cmd+Shift+A`
102
+
103
+ #### Extension settings
104
+
105
+ | Setting | Description |
106
+ |---------|-------------|
107
+ | `agentcode.anthropicApiKey` | Anthropic API key |
108
+ | `agentcode.openaiApiKey` | OpenAI API key |
109
+ | `agentcode.geminiApiKey` | Google Gemini API key |
110
+ | `agentcode.model` | Default model (e.g. `claude-sonnet-4-6`) |
111
+ | `agentcode.executablePath` | Path to `agentcode` if not on PATH |
112
+ | `agentcode.inlineCompletions.enabled` | Enable/disable inline completions (default: `true`) |
113
+
114
+ #### Extension commands
115
+
116
+ | Command | Shortcut | Description |
117
+ |---------|----------|-------------|
118
+ | `AgentCode: Open` | `Cmd+Shift+A` | Open the chat panel |
119
+ | `AgentCode: Ask about selection` | Right-click | Ask about highlighted code |
120
+ | `AgentCode: Explain this file` | Right-click | Explain the current file |
121
+ | `AgentCode: Toggle inline completions` | Command Palette | Enable or disable inline completions |
122
+
123
+ #### Extension features
124
+
125
+ - **Inline completions** — AI-powered ghost-text suggestions as you type, powered by Claude Haiku. Press `Tab` to accept
126
+ - **Live streaming** — responses stream in real time
127
+ - **Model picker** — switch between Claude, GPT, and Gemini models from the dropdown in the header
128
+ - **Active file context** — your current file is sent automatically, no copy-pasting needed
129
+ - **Diff viewer** — edit tool calls open VS Code's native diff viewer so you can review changes before applying
130
+ - **Right-click actions** — ask about selected code or explain a file directly from the editor
131
+
132
+ ---
133
+
134
+ ## Supported Models
135
+
136
+ | Provider | Model | API Key |
137
+ |----------|-------|---------|
138
+ | Anthropic | `claude-sonnet-4-6` (default) | `ANTHROPIC_API_KEY` |
139
+ | Anthropic | `claude-opus-4-7` | `ANTHROPIC_API_KEY` |
140
+ | Anthropic | `claude-haiku-4-5-20251001` | `ANTHROPIC_API_KEY` |
141
+ | OpenAI | `gpt-4o`, `gpt-4o-mini`, `gpt-5.5` | `OPENAI_API_KEY` |
142
+ | Google | `gemini/gemini-2.5-pro`, `gemini/gemini-2.5-flash` | `GEMINI_API_KEY` |
143
+
144
+ ---
145
+
146
+ ## Cost-Aware Routing
147
+
148
+ AgentCode automatically picks the cheapest model that can handle the task:
149
+
150
+ | Tier | Anthropic | OpenAI | Gemini |
151
+ |------|-----------|--------|--------|
152
+ | Light | Haiku 4.5 | GPT-4o Mini | Gemini 2.0 Flash |
153
+ | Medium | Sonnet 4.6 | GPT-4o | Gemini 2.5 Flash |
154
+ | Heavy | Opus 4.7 | GPT-5.5 | Gemini 2.5 Pro |
155
+
156
+ Simple questions go to cheap/fast models. Complex multi-file tasks go to powerful ones. Use `--no-route` to always use the specified model.
157
+
158
+ ---
159
+
160
+ ## Tools
161
+
162
+ ### File & Shell
163
+
164
+ | Tool | Description | Permission |
165
+ |------|-------------|------------|
166
+ | `read_file` | Read file contents with line numbers | Auto |
167
+ | `write_file` | Create or overwrite a file | Ask |
168
+ | `edit_file` | Surgical find-and-replace edit | Ask |
169
+ | `run_command` | Execute a bash command | Ask |
170
+ | `list_directory` | Tree view of directory structure | Auto |
171
+ | `search_files` | Find files by glob pattern | Auto |
172
+ | `search_text` | Grep for text across files | Auto |
173
+
174
+ ### Git
175
+
176
+ | Tool | Description | Permission |
177
+ |------|-------------|------------|
178
+ | `git_status` | Show working tree status | Auto |
179
+ | `git_diff` | Show staged or unstaged changes | Auto |
180
+ | `git_log` | Show recent commit history | Auto |
181
+ | `git_commit` | Stage files and create a commit | Ask |
182
+ | `git_branch` | List, create, or switch branches | Ask |
183
+ | `git_push` | Push commits to a remote | Ask |
184
+
185
+ ### Subagents
186
+
187
+ | Tool | Description | Permission |
188
+ |------|-------------|------------|
189
+ | `spawn_subagents` | Run multiple agents in parallel on subtasks | Auto |
190
+
191
+ **Permission model:** Read-only tools auto-approve. Write/execute tools ask before running (unless `--auto-approve` / `-y` is set).
192
+
193
+ ---
194
+
195
+ ## Slash Commands (CLI only)
196
+
197
+ | Command | Description |
198
+ |---------|-------------|
199
+ | `/model <name>` | Switch model on the fly |
200
+ | `/route` | Show or toggle cost-aware routing |
201
+ | `/cost` | Show session cost breakdown |
202
+ | `/mcp` | Manage MCP server connections |
203
+ | `/mcp list` | Show connected servers |
204
+ | `/mcp add <server>` | Connect a server |
205
+ | `/mcp remove <server>` | Disconnect a server |
206
+ | `/clear` | Reset conversation and delete saved session |
207
+ | `/compact` | Force LLM-powered context compaction |
208
+ | `/tokens` | Show estimated token usage |
209
+ | `/init` | Create an AGENTCODE.md template |
210
+ | `/settings` | Show resolved settings |
211
+ | `/help` | Show help |
212
+ | `/exit` | Quit |
213
+
214
+ ---
215
+
216
+ ## Session Persistence
217
+
218
+ Conversations are automatically saved to `.agentcode_session.json` in your project directory and resumed on next launch. Use `/clear` to start fresh.
219
+
220
+ ---
221
+
222
+ ## MCP Support (CLI only)
223
+
224
+ Connect to any [MCP server](https://modelcontextprotocol.io) using `/mcp add`:
225
+
226
+ ```
227
+ /mcp add github # prompts for GitHub token
228
+ /mcp add filesystem # no credentials needed
229
+ /mcp add postgres # prompts for connection string
230
+ /mcp add sqlite # prompts for database path
231
+ ```
232
+
233
+ Config is saved to `.agentcode/mcp.json` and reloaded on next launch.
234
+
235
+ **Advanced** — edit `.agentcode/mcp.json` directly for custom servers:
236
+
237
+ ```json
238
+ {
239
+ "mcpServers": {
240
+ "my-server": {
241
+ "command": "npx",
242
+ "args": ["-y", "@myorg/mcp-server"],
243
+ "env": {"API_KEY": "..."}
244
+ }
245
+ }
246
+ }
247
+ ```
248
+
249
+ Global config goes in `~/.agentcode/mcp.json`.
250
+
251
+ ---
252
+
253
+ ## Hooks
254
+
255
+ Run shell commands before or after any tool call. Create `.agentcode/hooks.json`:
256
+
257
+ ```json
258
+ {
259
+ "post_edit_file": "prettier --write \"$AGENTCODE_PATH\"",
260
+ "post_write_file": "prettier --write \"$AGENTCODE_PATH\"",
261
+ "pre_run_command": "echo \"Running: $AGENTCODE_COMMAND\""
262
+ }
263
+ ```
264
+
265
+ Supported keys: `pre_<toolname>`, `post_<toolname>`, `pre_tool` / `post_tool` (wildcard). Global hooks go in `~/.agentcode/hooks.json`.
266
+
267
+ ---
268
+
269
+ ## AGENTCODE.md
270
+
271
+ AgentCode loads project instructions from `AGENTCODE.md` in your project directory and injects them into the system prompt automatically. Run `/init` to generate a starter template.
272
+
273
+ ---
274
+
275
+ ## Subagents
276
+
277
+ AgentCode can spawn parallel agents for independent subtasks:
278
+
279
+ > "Analyze agent.py, router.py, and tools.py in parallel and summarize each one"
280
+
281
+ The agent calls `spawn_subagents` internally, runs up to 5 agents in parallel, and returns combined results.
282
+
283
+ ---
284
+
285
+ ## Architecture
286
+
287
+ ```
288
+ ┌─────────────────────────────────────────────────┐
289
+ │ cli.py (UI) │
290
+ │ REPL loop · slash commands · Rich terminal UI │
291
+ │ AGENTCODE.md · session persistence │
292
+ └──────────────────────┬──────────────────────────┘
293
+
294
+ ┌──────────────────────▼──────────────────────────┐
295
+ │ agent.py (Brain) │
296
+ │ Agentic loop · context management · permissions│
297
+ │ Hooks · subagents · LLM-powered compaction │
298
+ │ │
299
+ │ while needs_follow_up: │
300
+ │ 1. Send messages + tools → LLM │
301
+ │ 2. If tool_calls → execute, append, loop │
302
+ │ 3. If text only → done │
303
+ │ │
304
+ │ LiteLLM ──→ Claude / GPT / Gemini │
305
+ └──────────────────────┬──────────────────────────┘
306
+
307
+ ┌────────────┴────────────┐
308
+ │ │
309
+ ┌─────────▼───────────┐ ┌─────────▼───────────┐
310
+ │ tools.py (Hands) │ │ mcp_client.py │
311
+ │ read_file │ │ Connect to MCP │
312
+ │ write_file │ │ servers and expose │
313
+ │ edit_file │ │ their tools to the │
314
+ │ run_command │ │ agent loop. │
315
+ │ list_directory │ └─────────────────────┘
316
+ │ search_files │
317
+ │ search_text │
318
+ │ git_status/diff │
319
+ │ git_log/commit │
320
+ │ git_branch/push │
321
+ │ spawn_subagents │
322
+ └─────────────────────┘
323
+ ```
324
+
325
+ ---
326
+
327
+ ## Environment Variables
328
+
329
+ | Variable | Description | Default |
330
+ |----------|-------------|---------|
331
+ | `AGENTCODE_MODEL` | Default model | `claude-sonnet-4-6` |
332
+ | `AGENTCODE_MAX_ITERATIONS` | Max tool-call iterations per turn | `25` |
333
+ | `ANTHROPIC_API_KEY` | Anthropic API key | — |
334
+ | `OPENAI_API_KEY` | OpenAI API key | — |
335
+ | `GEMINI_API_KEY` | Google Gemini API key | — |
336
+
337
+ ---
338
+
339
+ ## How to Extend
340
+
341
+ ### Add a new tool
342
+
343
+ 1. Add the function schema to `TOOL_DEFINITIONS` in `tools.py`
344
+ 2. Implement the function
345
+ 3. Register it in `TOOL_MAP`
346
+ 4. If it requires approval, omit it from `permissions.auto_approve` in `.agentcode/settings.json`
347
+
348
+ ---
349
+
350
+ ## Publishing to PyPI
351
+
352
+ ```bash
353
+ pip install build twine
354
+ python -m build
355
+ twine upload dist/*
356
+ ```
357
+
358
+ ---
359
+
360
+ ## License
361
+
362
+ MIT
@@ -0,0 +1,13 @@
1
+ agent.py,sha256=zcYfTAarYhj1hdLc9DH5sWs6Qqqp1VpmrHGJfHSJZIQ,17118
2
+ cli.py,sha256=lzZzJkfAJ7nzmTZqIkRnVfJg1SsfuhMwge9J7iX9xM4,24948
3
+ mcp_client.py,sha256=2PviTqJtXM4UC_fsYLbAOAfWJvayWy7Q8VOQIIsDiqQ,6710
4
+ router.py,sha256=IjomIOaLVmGHQG9rNgc3xNpGhr82rCnn5h8X-QvZhRU,11573
5
+ server.py,sha256=w7EXPCsXHiSqfIXep3VIcS66t_ZY02ulF2Yn6e7N5TY,9654
6
+ settings.py,sha256=Qjc3tiVbT1cqIrnQW6m2UG8Xsvqsxl9qXPMTueqwn50,6903
7
+ tools.py,sha256=MBYy0OeSIZjqyGOezaMvZ6AMW3WRDESU-2u48sifd0Q,24741
8
+ agentcode_cli-1.2.0.dist-info/licenses/LICENSE,sha256=BqTzyKKaSaVQoumXzhYCj1UgOSPCgvn-sxV6BIuT558,1068
9
+ agentcode_cli-1.2.0.dist-info/METADATA,sha256=DQDncSy8pCDk9jy-Ac6b2HbVV9skzbSe5gfQvPjmOBA,12709
10
+ agentcode_cli-1.2.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
11
+ agentcode_cli-1.2.0.dist-info/entry_points.txt,sha256=xP_zeySufuVhL5v10_EqCooKRSxGYB8QuRVNUj4_m1E,39
12
+ agentcode_cli-1.2.0.dist-info/top_level.txt,sha256=PQseaNK25xxImV2WLHRHRIkipStqhLdVWYmrccM5ln0,50
13
+ agentcode_cli-1.2.0.dist-info/RECORD,,
@@ -2,5 +2,6 @@ agent
2
2
  cli
3
3
  mcp_client
4
4
  router
5
+ server
5
6
  settings
6
7
  tools
cli.py CHANGED
@@ -18,6 +18,7 @@ Supported models (via LiteLLM):
18
18
  import os
19
19
  import sys
20
20
  import argparse
21
+ from pathlib import Path
21
22
 
22
23
  from dotenv import load_dotenv
23
24
  load_dotenv()
@@ -551,6 +552,11 @@ def main():
551
552
  action="store_true",
552
553
  help="Create a starter .agentcode/settings.json and exit",
553
554
  )
555
+ parser.add_argument(
556
+ "--server",
557
+ action="store_true",
558
+ help="Run in server mode (JSON stdio protocol for VS Code extension)",
559
+ )
554
560
 
555
561
  args = parser.parse_args()
556
562
 
@@ -617,7 +623,10 @@ def main():
617
623
  settings=settings,
618
624
  )
619
625
 
620
- if args.prompt:
626
+ if args.server:
627
+ from server import run_server
628
+ run_server(config)
629
+ elif args.prompt:
621
630
  one_shot(args.prompt, config)
622
631
  else:
623
632
  repl(config)
router.py CHANGED
@@ -47,11 +47,20 @@ GEMINI_TIERS = [
47
47
  ModelTier("gemini/gemini-2.5-pro", "heavy", "Gemini 2.5 Pro", 1.25, 10.00),
48
48
  ]
49
49
 
50
+ # Azure deployment names are user-defined, so tiers use common defaults.
51
+ # Users can override via settings.json model.light/medium/heavy.
52
+ AZURE_TIERS = [
53
+ ModelTier("azure/gpt-4o-mini", "light", "Azure GPT-4o Mini", 0.15, 0.60),
54
+ ModelTier("azure/gpt-4o", "medium", "Azure GPT-4o", 2.50, 10.00),
55
+ ModelTier("azure/gpt-4o", "heavy", "Azure GPT-4o", 2.50, 10.00),
56
+ ]
57
+
50
58
  # Provider configs keyed by prefix
51
59
  PROVIDER_TIERS = {
52
60
  "anthropic": ANTHROPIC_TIERS,
53
61
  "openai": OPENAI_TIERS,
54
62
  "gemini": GEMINI_TIERS,
63
+ "azure": AZURE_TIERS,
55
64
  }
56
65
 
57
66
 
@@ -203,7 +212,9 @@ class ModelRouter:
203
212
  def detect_provider(self, model_string: str) -> str:
204
213
  """Detect provider from a model string."""
205
214
  m = model_string.lower()
206
- if "claude" in m or "anthropic" in m:
215
+ if m.startswith("azure/"):
216
+ return "azure"
217
+ elif "claude" in m or "anthropic" in m:
207
218
  return "anthropic"
208
219
  elif "gpt" in m or "openai" in m or "o1" in m or "o3" in m:
209
220
  return "openai"
server.py ADDED
@@ -0,0 +1,260 @@
1
+ """AgentCode server mode — JSON stdio protocol for the VS Code extension."""
2
+
3
+ import sys
4
+ import json
5
+ from pathlib import Path
6
+
7
+ import litellm
8
+
9
+ from tools import TOOL_DEFINITIONS, execute_tool
10
+ from agent import (
11
+ AgentConfig, Conversation,
12
+ build_system_prompt, load_project_config, load_hooks,
13
+ _run_subagents, _run_hook, _is_denied, _get_permission,
14
+ )
15
+
16
+
17
+ # ── Wire protocol ─────────────────────────────────────────────────────────────
18
+
19
+ def _write(msg: dict) -> None:
20
+ print(json.dumps(msg), flush=True)
21
+
22
+
23
+ def _read() -> dict | None:
24
+ line = sys.stdin.readline()
25
+ if not line:
26
+ return None
27
+ try:
28
+ return json.loads(line.strip())
29
+ except (json.JSONDecodeError, ValueError):
30
+ return None
31
+
32
+
33
+ # ── Permission over stdio ─────────────────────────────────────────────────────
34
+
35
+ def _ask_permission_server(tool_name: str, args: dict) -> bool:
36
+ """Send a permission request and block until the extension responds."""
37
+ _write({"type": "permission_request", "tool": tool_name, "args": args})
38
+ while True:
39
+ msg = _read()
40
+ if msg is None:
41
+ return False
42
+ if msg.get("type") == "permission_response":
43
+ return bool(msg.get("approved", False))
44
+
45
+
46
+ # ── Agentic loop (server edition) ─────────────────────────────────────────────
47
+
48
+ def _server_turn(
49
+ user_input: str,
50
+ conversation: Conversation,
51
+ config: AgentConfig,
52
+ file_context: str | None,
53
+ ) -> None:
54
+ """Run one agentic turn, streaming all output as JSON lines."""
55
+ content = f"{file_context}\n\n{user_input}" if file_context else user_input
56
+ conversation.messages.append({"role": "user", "content": content})
57
+
58
+ router = config.router
59
+ if router and router.enabled:
60
+ router.cost_tracker.begin_turn()
61
+ model, _tier, _reason = router.route(user_input)
62
+ else:
63
+ model = config.model
64
+
65
+ conversation.compact(max_tokens=80_000, model=model)
66
+ hooks = load_hooks(config.project_dir, config.settings)
67
+
68
+ mcp = config.mcp_manager
69
+ all_tools = TOOL_DEFINITIONS + (mcp.get_tool_definitions() if mcp else [])
70
+
71
+ for _ in range(config.max_iterations):
72
+ stream = litellm.completion(
73
+ model=model,
74
+ messages=[{"role": "system", "content": conversation.system}, *conversation.messages],
75
+ tools=all_tools,
76
+ tool_choice="auto",
77
+ stream=True,
78
+ stream_options={"include_usage": True},
79
+ )
80
+
81
+ full_text = ""
82
+ tool_calls_accum: dict[int, dict] = {}
83
+ usage = None
84
+
85
+ for chunk in stream:
86
+ if hasattr(chunk, "usage") and chunk.usage:
87
+ usage = chunk.usage
88
+ if not chunk.choices:
89
+ continue
90
+ delta = chunk.choices[0].delta
91
+
92
+ if delta.content:
93
+ full_text += delta.content
94
+ _write({"type": "text", "delta": delta.content})
95
+
96
+ if delta.tool_calls:
97
+ for tc in delta.tool_calls:
98
+ idx = tc.index
99
+ if idx not in tool_calls_accum:
100
+ tool_calls_accum[idx] = {"id": "", "name": "", "arguments": ""}
101
+ if tc.id:
102
+ tool_calls_accum[idx]["id"] = tc.id
103
+ if tc.function:
104
+ if tc.function.name:
105
+ tool_calls_accum[idx]["name"] += tc.function.name
106
+ if tc.function.arguments:
107
+ tool_calls_accum[idx]["arguments"] += tc.function.arguments
108
+
109
+ if router and usage:
110
+ try:
111
+ cost = litellm.completion_cost(
112
+ model=model,
113
+ prompt_tokens=usage.prompt_tokens or 0,
114
+ completion_tokens=usage.completion_tokens or 0,
115
+ )
116
+ except Exception:
117
+ cost = 0.0
118
+ router.cost_tracker.record(
119
+ model,
120
+ usage.prompt_tokens or 0,
121
+ usage.completion_tokens or 0,
122
+ cost,
123
+ )
124
+
125
+ if not tool_calls_accum:
126
+ conversation.messages.append({"role": "assistant", "content": full_text})
127
+ turn_cost = router.cost_tracker.last_turn_cost if router else 0.0
128
+ _write({"type": "done", "cost": turn_cost})
129
+ return
130
+
131
+ conversation.messages.append({
132
+ "role": "assistant",
133
+ "content": full_text or "",
134
+ "tool_calls": [
135
+ {
136
+ "id": tc["id"],
137
+ "type": "function",
138
+ "function": {"name": tc["name"], "arguments": tc["arguments"]},
139
+ }
140
+ for tc in tool_calls_accum.values()
141
+ ],
142
+ })
143
+
144
+ for tc in tool_calls_accum.values():
145
+ tool_name = tc["name"]
146
+ try:
147
+ args = json.loads(tc["arguments"])
148
+ except json.JSONDecodeError:
149
+ args = {}
150
+
151
+ _write({"type": "tool_call", "name": tool_name, "args": args})
152
+
153
+ pre_hook = hooks.get(f"pre_{tool_name}") or hooks.get("pre_tool")
154
+ if pre_hook:
155
+ _run_hook(pre_hook, tool_name, args)
156
+
157
+ if tool_name == "spawn_subagents":
158
+ if _is_denied(tool_name, config):
159
+ result = f"Tool '{tool_name}' is blocked by settings."
160
+ else:
161
+ result = _run_subagents(args.get("tasks", []), config, True)
162
+ elif mcp and mcp.is_mcp_tool(tool_name):
163
+ if _is_denied(tool_name, config):
164
+ result = f"Tool '{tool_name}' is blocked by settings."
165
+ else:
166
+ result = mcp.call_tool(tool_name, args)
167
+ else:
168
+ perm = _get_permission(tool_name, config)
169
+ if perm == "deny":
170
+ result = f"Tool '{tool_name}' is blocked by settings."
171
+ elif perm == "ask":
172
+ approved = _ask_permission_server(tool_name, args)
173
+ result = execute_tool(tool_name, args) if approved else f"Tool '{tool_name}' denied."
174
+ else:
175
+ result = execute_tool(tool_name, args)
176
+
177
+ post_hook = hooks.get(f"post_{tool_name}") or hooks.get("post_tool")
178
+ if post_hook:
179
+ _run_hook(post_hook, tool_name, args)
180
+
181
+ _write({"type": "tool_result", "name": tool_name, "result": result[:2000]})
182
+ conversation.messages.append({
183
+ "role": "tool",
184
+ "tool_call_id": tc["id"],
185
+ "content": result,
186
+ })
187
+
188
+ _write({"type": "done", "cost": 0.0})
189
+
190
+
191
+ # ── Main server loop ──────────────────────────────────────────────────────────
192
+
193
+ def run_server(config: AgentConfig) -> None:
194
+ """
195
+ Read JSON messages from stdin, write JSON messages to stdout.
196
+
197
+ Client → Server messages:
198
+ {"type": "message", "content": "..."}
199
+ {"type": "context", "file_path": "...", "content": "..."}
200
+ {"type": "permission_response", "approved": true}
201
+ {"type": "clear"}
202
+ {"type": "ping"}
203
+
204
+ Server → Client messages:
205
+ {"type": "ready", "model": "...", "project_dir": "..."}
206
+ {"type": "text", "delta": "..."}
207
+ {"type": "tool_call", "name": "...", "args": {...}}
208
+ {"type": "tool_result", "name": "...", "result": "..."}
209
+ {"type": "permission_request", "tool": "...", "args": {...}}
210
+ {"type": "done", "cost": 0.0}
211
+ {"type": "error", "message": "..."}
212
+ {"type": "cleared"}
213
+ {"type": "pong"}
214
+ """
215
+ project_config = load_project_config(config.project_dir)
216
+ system_prompt = build_system_prompt(config.project_dir, project_config["combined"])
217
+ conversation = Conversation(system=system_prompt)
218
+
219
+ sess = Path(config.project_dir) / ".agentcode_session.json"
220
+ conversation.load(sess)
221
+
222
+ _write({"type": "ready", "model": config.model, "project_dir": config.project_dir})
223
+
224
+ file_context: str | None = None
225
+
226
+ while True:
227
+ msg = _read()
228
+ if msg is None:
229
+ break
230
+
231
+ msg_type = msg.get("type")
232
+
233
+ if msg_type == "message":
234
+ content = msg.get("content", "").strip()
235
+ if not content:
236
+ continue
237
+ try:
238
+ _server_turn(content, conversation, config, file_context)
239
+ conversation.save(sess)
240
+ except Exception as e:
241
+ _write({"type": "error", "message": str(e)})
242
+
243
+ elif msg_type == "context":
244
+ file_path = msg.get("file_path", "")
245
+ file_content = msg.get("content", "")
246
+ if file_path and file_content:
247
+ file_context = (
248
+ f"[Active file: {file_path}]\n```\n{file_content[:3000]}\n```"
249
+ )
250
+ else:
251
+ file_context = None
252
+
253
+ elif msg_type == "clear":
254
+ conversation.messages.clear()
255
+ sess.unlink(missing_ok=True)
256
+ file_context = None
257
+ _write({"type": "cleared"})
258
+
259
+ elif msg_type == "ping":
260
+ _write({"type": "pong"})
@@ -1,303 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: agentcode-cli
3
- Version: 1.0.0
4
- Summary: An open, multi-model agentic coding CLI — inspired by Claude Code
5
- Author: Vignesh Pai
6
- License: MIT License
7
-
8
- Copyright (c) 2026 Vignesh Pai
9
-
10
- Permission is hereby granted, free of charge, to any person obtaining a copy
11
- of this software and associated documentation files (the "Software"), to deal
12
- in the Software without restriction, including without limitation the rights
13
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
- copies of the Software, and to permit persons to whom the Software is
15
- furnished to do so, subject to the following conditions:
16
-
17
- The above copyright notice and this permission notice shall be included in all
18
- copies or substantial portions of the Software.
19
-
20
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
- SOFTWARE.
27
-
28
- Project-URL: Homepage, https://github.com/vigp17/AgentCode
29
- Project-URL: Repository, https://github.com/vigp17/AgentCode
30
- Project-URL: Bug Tracker, https://github.com/vigp17/AgentCode/issues
31
- Classifier: Programming Language :: Python :: 3
32
- Classifier: Programming Language :: Python :: 3.11
33
- Classifier: License :: OSI Approved :: MIT License
34
- Classifier: Operating System :: OS Independent
35
- Classifier: Environment :: Console
36
- Classifier: Topic :: Software Development
37
- Classifier: Topic :: Utilities
38
- Requires-Python: >=3.11
39
- Description-Content-Type: text/markdown
40
- License-File: LICENSE
41
- Requires-Dist: litellm>=1.40.0
42
- Requires-Dist: rich>=13.0.0
43
- Requires-Dist: python-dotenv>=1.0.0
44
- Requires-Dist: mcp>=1.0.0
45
- Provides-Extra: dev
46
- Requires-Dist: build; extra == "dev"
47
- Requires-Dist: twine; extra == "dev"
48
- Dynamic: license-file
49
-
50
- # AgentCode
51
-
52
- An open, multi-model agentic coding CLI — inspired by Claude Code.
53
-
54
- ## Architecture
55
-
56
- ```
57
- ┌─────────────────────────────────────────────────┐
58
- │ cli.py (UI) │
59
- │ REPL loop · slash commands · Rich terminal UI │
60
- │ AGENTCODE.md · session persistence │
61
- └──────────────────────┬──────────────────────────┘
62
-
63
- ┌──────────────────────▼──────────────────────────┐
64
- │ agent.py (Brain) │
65
- │ Agentic loop · context management · permissions│
66
- │ Hooks · subagents · LLM-powered compaction │
67
- │ │
68
- │ while needs_follow_up: │
69
- │ 1. Send messages + tools → LLM │
70
- │ 2. If tool_calls → execute, append, loop │
71
- │ 3. If text only → done │
72
- │ │
73
- │ LiteLLM ──→ Claude / GPT / Gemini │
74
- └──────────────────────┬──────────────────────────┘
75
-
76
- ┌────────────┴────────────┐
77
- │ │
78
- ┌─────────▼───────────┐ ┌─────────▼───────────┐
79
- │ tools.py (Hands) │ │ mcp_client.py │
80
- │ read_file │ │ Connect to MCP │
81
- │ write_file │ │ servers and expose │
82
- │ edit_file │ │ their tools to the │
83
- │ run_command │ │ agent loop. │
84
- │ list_directory │ └─────────────────────┘
85
- │ search_files │
86
- │ search_text │
87
- │ git_status/diff │
88
- │ git_log/commit │
89
- │ git_branch/push │
90
- │ spawn_subagents │
91
- └─────────────────────┘
92
- ```
93
-
94
- ## Quick Start
95
-
96
- ```bash
97
- # Option A: Install from PyPI
98
- pip install agentcode-cli
99
-
100
- # Option B: Install from source
101
- git clone https://github.com/vigp17/AgentCode.git
102
- cd AgentCode
103
- pip install -r requirements.txt
104
- ```
105
-
106
- ```bash
107
- # Add your API keys to a .env file in your project directory
108
- ANTHROPIC_API_KEY=sk-ant-...
109
- OPENAI_API_KEY=sk-...
110
- GEMINI_API_KEY=...
111
- ```
112
-
113
- ```bash
114
- # Run
115
- agentcode # Interactive REPL
116
- agentcode "fix the failing tests" # One-shot mode
117
- agentcode --model gpt-4o # Use a different model
118
- agentcode --init-settings # Create a settings file
119
- ```
120
-
121
- ## Supported Models
122
-
123
- Via [LiteLLM](https://docs.litellm.ai/docs/providers), AgentCode works with any model that supports function calling — including Claude, GPT, Gemini, and select open-source models via Ollama.
124
-
125
- | Provider | Model String | API Key Env Var |
126
- |------------|---------------------------------|-------------------------|
127
- | Anthropic | `claude-sonnet-4-6` (default) | `ANTHROPIC_API_KEY` |
128
- | OpenAI | `gpt-4o` | `OPENAI_API_KEY` |
129
- | Google | `gemini/gemini-2.5-pro` | `GEMINI_API_KEY` |
130
-
131
- ## Cost-Aware Routing
132
-
133
- AgentCode automatically picks the cheapest model that can handle the task:
134
-
135
- | Tier | Anthropic | OpenAI | Gemini |
136
- |--------|--------------------|--------------|-------------------------|
137
- | Light | Haiku 4.5 | GPT-4o Mini | Gemini 2.0 Flash |
138
- | Medium | Sonnet 4.6 | GPT-4o | Gemini 2.5 Flash |
139
- | Heavy | Opus 4.6 | GPT-5.5 | Gemini 2.5 Pro |
140
-
141
- Simple questions go to cheap/fast models. Complex multi-file tasks go to powerful ones. Use `--no-route` to always use the specified model.
142
-
143
- ## Tools
144
-
145
- ### File & Shell
146
-
147
- | Tool | Description | Permission |
148
- |------------------|--------------------------------------|------------|
149
- | `read_file` | Read file contents with line numbers | Auto |
150
- | `write_file` | Create or overwrite a file | Ask |
151
- | `edit_file` | Surgical find-and-replace edit | Ask |
152
- | `run_command` | Execute a bash command | Ask |
153
- | `list_directory` | Tree view of directory structure | Auto |
154
- | `search_files` | Find files by glob pattern | Auto |
155
- | `search_text` | Grep for text across files | Auto |
156
-
157
- ### Git
158
-
159
- | Tool | Description | Permission |
160
- |---------------|------------------------------------------|------------|
161
- | `git_status` | Show working tree status | Auto |
162
- | `git_diff` | Show staged or unstaged changes | Auto |
163
- | `git_log` | Show recent commit history | Auto |
164
- | `git_commit` | Stage files and create a commit | Ask |
165
- | `git_branch` | List, create, or switch branches | Ask |
166
- | `git_push` | Push commits to a remote | Ask |
167
-
168
- ### Subagents
169
-
170
- | Tool | Description | Permission |
171
- |-------------------|--------------------------------------------------|------------|
172
- | `spawn_subagents` | Run multiple agents in parallel on subtasks | Auto |
173
-
174
- **Permission model:** Read-only tools auto-approve. Write/execute tools ask before running (unless `--auto-approve` / `-y` flag is set).
175
-
176
- ## Slash Commands
177
-
178
- | Command | Description |
179
- |--------------------------|--------------------------------------------------|
180
- | `/model <name>` | Switch LLM model on the fly (disables routing) |
181
- | `/route` | Show or toggle cost-aware routing |
182
- | `/cost` | Show session cost breakdown |
183
- | `/mcp` | Manage MCP server connections |
184
- | `/mcp list` | Show connected servers and tool counts |
185
- | `/mcp add <server>` | Connect a server (prompts for credentials) |
186
- | `/mcp remove <server>` | Disconnect a server |
187
- | `/clear` | Reset conversation and delete saved session |
188
- | `/compact` | Force LLM-powered context compaction |
189
- | `/tokens` | Show estimated token usage |
190
- | `/init` | Create an AGENTCODE.md template |
191
- | `/settings` | Show resolved settings and active config files |
192
- | `/help` | Show help |
193
- | `/exit` | Quit |
194
-
195
- ## Session Persistence
196
-
197
- Conversations are automatically saved to `.agentcode_session.json` in your project directory and resumed on next launch. Use `/clear` to start fresh.
198
-
199
- ## Hooks
200
-
201
- Run shell commands before or after any tool call. Create `.agentcode/hooks.json`:
202
-
203
- ```json
204
- {
205
- "post_edit_file": "prettier --write \"$AGENTCODE_PATH\"",
206
- "post_write_file": "prettier --write \"$AGENTCODE_PATH\"",
207
- "pre_run_command": "echo \"Running: $AGENTCODE_COMMAND\""
208
- }
209
- ```
210
-
211
- Supported keys: `pre_<toolname>`, `post_<toolname>`, `pre_tool` / `post_tool` (wildcard). Tool args are passed as `AGENTCODE_<ARG>=value` env vars. Global hooks go in `~/.agentcode/hooks.json`.
212
-
213
- ## MCP Support
214
-
215
- Connect to any [MCP server](https://modelcontextprotocol.io) using the `/mcp add` command — no manual config editing needed:
216
-
217
- ```
218
- /mcp add github # prompts for GitHub token, connects immediately
219
- /mcp add filesystem # connects to current directory, no credentials needed
220
- /mcp add postgres # prompts for connection string
221
- /mcp add sqlite # prompts for database file path
222
- /mcp list # show connected servers and tool counts
223
- /mcp remove github # disconnect a server
224
- ```
225
-
226
- Servers connect live without restarting. Config is saved to `.agentcode/mcp.json` and reloaded automatically on next launch.
227
-
228
- MCP tools are exposed as `mcp__<server>__<toolname>` (e.g. `mcp__github__create_issue`) and available to the LLM alongside built-in tools. Connected servers are shown in the banner on startup.
229
-
230
- **Advanced:** You can also edit `.agentcode/mcp.json` directly for custom servers:
231
-
232
- ```json
233
- {
234
- "mcpServers": {
235
- "my-server": {
236
- "command": "npx",
237
- "args": ["-y", "@myorg/mcp-server"],
238
- "env": {"API_KEY": "..."}
239
- }
240
- }
241
- }
242
- ```
243
-
244
- Global config goes in `~/.agentcode/mcp.json`.
245
-
246
- ## Subagents
247
-
248
- AgentCode can spawn parallel agents to work on independent subtasks simultaneously. Just ask naturally:
249
-
250
- > "Analyze agent.py, router.py, and tools.py in parallel and summarize each one"
251
-
252
- The agent calls `spawn_subagents` internally, runs up to 5 agents in parallel, and returns combined results.
253
-
254
- ## AGENTCODE.md
255
-
256
- AgentCode loads project-level instructions from `AGENTCODE.md` in your project directory (and global config from `~/.agentcode/AGENTCODE.md`), injecting them into the system prompt automatically.
257
-
258
- Run `/init` to generate a starter template, then edit it to define your project's coding standards, preferences, and constraints.
259
-
260
- ## How to Extend
261
-
262
- ### Add a new tool
263
-
264
- 1. Add the function schema to `TOOL_DEFINITIONS` in `tools.py`
265
- 2. Implement the function (e.g., `_my_tool(...)`)
266
- 3. Register it in `TOOL_MAP`
267
- 4. If it should require user approval, omit it from `permissions.auto_approve` in `.agentcode/settings.json`
268
-
269
- ## Environment Variables
270
-
271
- | Variable | Description | Default |
272
- |----------------------------|-----------------------------------|---------------------|
273
- | `AGENTCODE_MODEL` | Default model | `claude-sonnet-4-6` |
274
- | `AGENTCODE_MAX_ITERATIONS` | Max tool-call iterations per turn | `25` |
275
- | `ANTHROPIC_API_KEY` | Anthropic API key | — |
276
- | `OPENAI_API_KEY` | OpenAI API key | — |
277
- | `GEMINI_API_KEY` | Google Gemini API key | — |
278
-
279
- ## Publishing to PyPI
280
-
281
- ```bash
282
- # 1. Install build tools
283
- pip install build twine
284
-
285
- # 2. Bump the version in pyproject.toml, then build
286
- python -m build
287
-
288
- # 3. Upload to PyPI
289
- twine upload dist/*
290
- ```
291
-
292
- You'll be prompted for your PyPI credentials on first upload. Use an API token from [pypi.org/manage/account](https://pypi.org/manage/account) for security.
293
-
294
- For test uploads before going live:
295
-
296
- ```bash
297
- twine upload --repository testpypi dist/*
298
- pip install --index-url https://test.pypi.org/simple/ agentcode
299
- ```
300
-
301
- ## License
302
-
303
- MIT
@@ -1,12 +0,0 @@
1
- agent.py,sha256=zcYfTAarYhj1hdLc9DH5sWs6Qqqp1VpmrHGJfHSJZIQ,17118
2
- cli.py,sha256=naUTUUgvL2DJRTTZzxQCOi6bjZ7rmtmpwlqaGDQ4eQk,24677
3
- mcp_client.py,sha256=2PviTqJtXM4UC_fsYLbAOAfWJvayWy7Q8VOQIIsDiqQ,6710
4
- router.py,sha256=kjPpF1js6mA7FN2x_NleyN-BLeVzRAy-Q0wbhjelQzM,11109
5
- settings.py,sha256=Qjc3tiVbT1cqIrnQW6m2UG8Xsvqsxl9qXPMTueqwn50,6903
6
- tools.py,sha256=MBYy0OeSIZjqyGOezaMvZ6AMW3WRDESU-2u48sifd0Q,24741
7
- agentcode_cli-1.0.0.dist-info/licenses/LICENSE,sha256=BqTzyKKaSaVQoumXzhYCj1UgOSPCgvn-sxV6BIuT558,1068
8
- agentcode_cli-1.0.0.dist-info/METADATA,sha256=gJjlU0B67K1iMGnSgYVk901xeCuaHc12RSXL5Cs6WM0,13389
9
- agentcode_cli-1.0.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
10
- agentcode_cli-1.0.0.dist-info/entry_points.txt,sha256=xP_zeySufuVhL5v10_EqCooKRSxGYB8QuRVNUj4_m1E,39
11
- agentcode_cli-1.0.0.dist-info/top_level.txt,sha256=5jYoRO_eF5cWLQAvnel4HxTjCgsG0iBHpOkcTn3Gqv8,43
12
- agentcode_cli-1.0.0.dist-info/RECORD,,