devpilot-agentic-cli 1.0.2__py3-none-any.whl → 1.0.4__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.
agent/cli.py CHANGED
@@ -12,18 +12,99 @@ import argparse
12
12
  import asyncio
13
13
  import os
14
14
  import sys
15
+ import time
15
16
  from datetime import datetime
16
17
  from pathlib import Path
17
18
 
18
- from agent.a2a_server import app as a2a_app
19
+
20
+ def _fix_windows_console() -> None:
21
+ """
22
+ Enable ANSI/VT100 support and UTF-8 encoding in Windows terminals.
23
+
24
+ Root cause of the blank black CMD screen:
25
+ - CMD.exe does NOT enable Virtual Terminal Processing by default.
26
+ - Textual sends ANSI escape sequences to probe terminal capabilities.
27
+ - Without VT support, CMD either hangs or renders nothing.
28
+ - PowerShell has VT enabled by default → works immediately.
29
+
30
+ Fixes applied:
31
+ 1. Enable ENABLE_VIRTUAL_TERMINAL_PROCESSING via Windows API (ctypes).
32
+ 2. Switch stdout/stderr to UTF-8 so → ✓ etc don't crash in cp1252.
33
+ 3. Set TERM / COLORTERM so Textual skips slow capability negotiation.
34
+ """
35
+ if sys.platform != "win32":
36
+ return
37
+
38
+ # ── 1. Enable Virtual Terminal Processing ─────────────────────────────────
39
+ try:
40
+ import ctypes
41
+ import ctypes.wintypes
42
+ kernel32 = ctypes.windll.kernel32 # type: ignore[attr-defined]
43
+
44
+ ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004
45
+ ENABLE_PROCESSED_OUTPUT = 0x0001
46
+
47
+ for handle_id in (-10, -11): # STD_INPUT=-10, STD_OUTPUT=-11
48
+ handle = kernel32.GetStdHandle(handle_id)
49
+ if handle and handle != -1:
50
+ mode = ctypes.wintypes.DWORD(0)
51
+ if kernel32.GetConsoleMode(handle, ctypes.byref(mode)):
52
+ new_mode = (mode.value
53
+ | ENABLE_VIRTUAL_TERMINAL_PROCESSING
54
+ | ENABLE_PROCESSED_OUTPUT)
55
+ kernel32.SetConsoleMode(handle, new_mode)
56
+ except Exception:
57
+ pass # non-fatal — worst case CMD is slow, not broken
58
+
59
+ # ── 2. Switch stdout/stderr to UTF-8 ──────────────────────────────────────
60
+ # Prevents UnicodeEncodeError on → ✓ ✗ characters in cp1252 CMD sessions
61
+ try:
62
+ if hasattr(sys.stdout, "reconfigure"):
63
+ sys.stdout.reconfigure(encoding="utf-8", errors="replace") # type: ignore[union-attr]
64
+ if hasattr(sys.stderr, "reconfigure"):
65
+ sys.stderr.reconfigure(encoding="utf-8", errors="replace") # type: ignore[union-attr]
66
+ # Also tell Windows console host to use UTF-8 codepage
67
+ import ctypes
68
+ ctypes.windll.kernel32.SetConsoleOutputCP(65001) # type: ignore[attr-defined]
69
+ except Exception:
70
+ pass
71
+
72
+ # ── 3. Hint terminal type so Textual doesn't hang on capability detection ─
73
+ # Textual checks these env vars first; without them it sends probe sequences
74
+ # that CMD ignores, causing a multi-second hang.
75
+ os.environ.setdefault("TERM", "xterm-256color")
76
+ os.environ.setdefault("COLORTERM", "truecolor")
77
+
78
+
79
+ # Run immediately — must happen before any rich/textual import
80
+ _fix_windows_console()
81
+
82
+ # Python version guard — must run before any other import
83
+ if sys.version_info < (3, 11):
84
+ print(
85
+ f"\n[DevPilot] Python 3.11 or newer is required.\n"
86
+ f" You are running Python {sys.version_info.major}.{sys.version_info.minor}.\n"
87
+ f" Download the latest Python from: https://python.org/downloads/\n"
88
+ )
89
+ sys.exit(1)
90
+
91
+ # Module-level imports: only lightweight config/ui — everything else is lazy inside main_async()
19
92
  from agent.config import Config, ConfigError
20
- from agent.history import HistoryManager
21
- from agent.loop import run_agent_loop
22
- from agent.mcp_client import MCPManager
23
93
  from agent.providers.factory import create_provider
24
- from agent.tools import ToolRegistry
25
94
  from agent.ui import UI
26
95
 
96
+ _T0 = time.perf_counter()
97
+
98
+ def _log_startup(msg: str) -> None:
99
+ """Print a startup progress line. Helps users see DevPilot is loading."""
100
+ elapsed = time.perf_counter() - _T0
101
+ # Use plain ASCII fallback so CMD doesn't choke on escape codes if VT failed
102
+ print(f"\r[{elapsed:.1f}s] {msg}...", end="", flush=True)
103
+
104
+ def _clear_startup() -> None:
105
+ """Clear the startup progress line once TUI takes over."""
106
+ print("\r" + " " * 60 + "\r", end="", flush=True)
107
+
27
108
 
28
109
  def parse_args() -> argparse.Namespace:
29
110
  parser = argparse.ArgumentParser(
@@ -88,7 +169,8 @@ def _ensure_api_key(config: Config, args: argparse.Namespace) -> Config:
88
169
  # Force re-run wizard if --setup flag passed
89
170
  if args.setup or not config.active_api_key:
90
171
  from agent.setup_wizard import run_setup_wizard
91
- success = run_setup_wizard(env_path=Path(".env"))
172
+ _user_env = Path.home() / ".devpilot" / ".env"
173
+ success = run_setup_wizard(env_path=_user_env)
92
174
  if not success:
93
175
  # Wizard skipped (CI/no TTY) or failed — print helpful error
94
176
  UI.print_error(
@@ -115,6 +197,7 @@ async def main_async() -> None:
115
197
  _apply_cli_overrides(args)
116
198
 
117
199
  # Load config
200
+ _log_startup("Loading configuration")
118
201
  try:
119
202
  config = Config.load()
120
203
  except ConfigError as e:
@@ -123,8 +206,11 @@ async def main_async() -> None:
123
206
 
124
207
  # Handle --setup flag (force wizard even if key exists)
125
208
  if args.setup:
209
+ _clear_startup()
126
210
  from agent.setup_wizard import run_setup_wizard
127
- run_setup_wizard(env_path=Path(".env"))
211
+ _user_env = Path.home() / ".devpilot" / ".env"
212
+ run_setup_wizard(env_path=_user_env)
213
+
128
214
  try:
129
215
  config = Config.load()
130
216
  except ConfigError as e:
@@ -141,28 +227,36 @@ async def main_async() -> None:
141
227
  UI.print_error(str(e))
142
228
  sys.exit(1)
143
229
 
144
- # Wire up provider, context, registry
230
+ # Wire up provider, context, registry — lazy imports keep startup lean
231
+ _log_startup("Initializing AI provider")
145
232
  provider = create_provider(config)
146
233
 
234
+ _log_startup("Scanning project context")
147
235
  from agent.context import RepoContext
236
+ from agent.history import HistoryManager
237
+ from agent.tools import ToolRegistry
148
238
  repo_context = RepoContext(config.workdir)
149
239
  registry = ToolRegistry(config, _context=repo_context)
150
240
 
151
- # MCP
241
+ # MCP — lazy import to avoid loading the mcp library at startup
242
+ _log_startup("Connecting MCP servers")
243
+ from agent.mcp_client import MCPManager
152
244
  mcp_config_path = Path("mcp_servers.json")
153
245
  if not mcp_config_path.exists():
154
246
  mcp_config_path = Path(__file__).parent.parent / "mcp_servers.json"
155
247
  mcp_manager = MCPManager(mcp_config_path)
156
248
  await mcp_manager.connect_all(registry)
157
249
 
158
- # A2A server
159
- a2a_app.state.config = config
160
- a2a_app.state.registry = registry
161
-
250
+ # A2A server — only import fastapi/uvicorn when A2A is actually enabled
251
+ _log_startup("Starting A2A server")
162
252
  if config.a2a_enabled:
253
+ from agent.a2a_server import app as a2a_app
163
254
  import uvicorn
164
255
  import socket
165
-
256
+
257
+ a2a_app.state.config = config
258
+ a2a_app.state.registry = registry
259
+
166
260
  # Check if port is available
167
261
  port_in_use = False
168
262
  with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
@@ -170,7 +264,7 @@ async def main_async() -> None:
170
264
  s.bind(("0.0.0.0", config.a2a_port))
171
265
  except OSError:
172
266
  port_in_use = True
173
-
267
+
174
268
  if port_in_use:
175
269
  UI.print_error(f"Port {config.a2a_port} is already in use. A2A server disabled. Use --a2a-port <port> to change.")
176
270
  a2a_server = None
@@ -207,6 +301,7 @@ async def main_async() -> None:
207
301
 
208
302
  # ── CI / single-task mode ─────────────────────────────────────────────────
209
303
  if args.task:
304
+ from agent.loop import run_agent_loop
210
305
  history.append(provider.make_user_message(args.task))
211
306
  await run_agent_loop(
212
307
  provider=provider,
@@ -224,7 +319,9 @@ async def main_async() -> None:
224
319
  sys.exit(0)
225
320
 
226
321
  # ── Interactive TUI mode ──────────────────────────────────────────────────
322
+ _log_startup("Starting DevPilot TUI")
227
323
  from agent.tui.app import DevPilotApp
324
+ _clear_startup() # erase progress line before TUI takes over the screen
228
325
  app = DevPilotApp(
229
326
  provider=provider,
230
327
  registry=registry,
agent/config.py CHANGED
@@ -17,8 +17,19 @@ from typing import Literal
17
17
 
18
18
  from dotenv import load_dotenv
19
19
 
20
- # ── Load .env file if present (safe to call even if file is missing) ─────────
21
- load_dotenv(dotenv_path=Path(__file__).resolve().parent.parent / ".env", override=False)
20
+ # ── Persistent user config: ~/.devpilot/.env ─────────────────────────────────
21
+ # This is where the setup wizard saves configuration so it persists
22
+ # regardless of which directory `devpilot` is run from.
23
+ _USER_CONFIG_DIR = Path.home() / ".devpilot"
24
+ _USER_ENV_FILE = _USER_CONFIG_DIR / ".env"
25
+
26
+ # Load global user config first — this is where the setup wizard saves settings
27
+ load_dotenv(dotenv_path=_USER_ENV_FILE, override=False)
28
+
29
+ # Load project-local .env second — can ADD vars but won't override global config
30
+ # (override=False means global ~/.devpilot/.env always wins)
31
+ load_dotenv(dotenv_path=Path(".") / ".env", override=False)
32
+
22
33
 
23
34
 
24
35
  # ── Custom exception ─────────────────────────────────────────────────────────
@@ -32,8 +43,8 @@ class ConfigError(Exception):
32
43
  Provider = Literal["anthropic", "openai"]
33
44
 
34
45
  PROVIDER_DEFAULTS: dict[str, str] = {
35
- "anthropic": "claude-opus-4-5",
36
- "openai": "gpt-4o",
46
+ "anthropic": "claude-opus-4-5-20251101",
47
+ "openai": "gpt-5.4",
37
48
  }
38
49
 
39
50
  # Keys required per provider (checked lazily so we can build/test without keys)
@@ -43,11 +54,15 @@ REQUIRED_ENV_KEYS: dict[str, str] = {
43
54
  }
44
55
 
45
56
  # Models that support extended thinking (Anthropic only)
57
+ # Verified via Anthropic API — May 2026
46
58
  THINKING_CAPABLE_MODELS: set[str] = {
59
+ "claude-opus-4-7",
60
+ "claude-opus-4-6",
61
+ "claude-opus-4-5-20251101",
62
+ "claude-opus-4-1-20250805",
63
+ "claude-sonnet-4-5-20250929",
47
64
  "claude-3-7-sonnet-20250219",
48
65
  "claude-3-5-sonnet-20241022",
49
- "claude-opus-4-5",
50
- "claude-opus-4-20250514",
51
66
  }
52
67
 
53
68
 
agent/context.py CHANGED
@@ -145,33 +145,31 @@ class RepoContext:
145
145
  return sigs
146
146
 
147
147
  def _build_project_tree(self, max_entries: int = 200) -> list[str]:
148
+ """
149
+ Build a fast file-listing project tree.
150
+ AST signature extraction is intentionally skipped here to avoid
151
+ blocking startup — signatures are extracted lazily on file read.
152
+ """
148
153
  entries: list[str] = []
149
154
  ignores = {
150
- ".git", "node_modules", ".venv", "__pycache__", "dist",
155
+ ".git", "node_modules", ".venv", "__pycache__", "dist",
151
156
  "build", ".next", ".tox", "coverage_html_report", ".devpilot_sessions"
152
157
  }
153
158
 
154
159
  def _traverse(directory: Path, prefix: str = "") -> None:
155
160
  if len(entries) >= max_entries:
156
161
  return
157
-
158
162
  try:
159
163
  for item in sorted(directory.iterdir()):
160
164
  if item.name.startswith(".") and item.name not in (".env", ".gitignore", ".github"):
161
165
  continue
162
166
  if item.name in ignores or item.name.endswith(".egg-info"):
163
167
  continue
164
-
165
168
  if item.is_dir():
166
169
  entries.append(f"{prefix}📁 {item.name}/")
167
170
  _traverse(item, prefix + " ")
168
171
  else:
169
172
  entries.append(f"{prefix}📄 {item.name}")
170
- if item.suffix in (".py", ".js", ".ts"):
171
- sigs = self._extract_signatures(item)
172
- for sig in sigs:
173
- entries.append(f"{prefix}{sig}")
174
-
175
173
  if len(entries) >= max_entries:
176
174
  entries.append("… (truncated to ~200 items for context size)")
177
175
  break
agent/loop.py CHANGED
@@ -56,8 +56,10 @@ async def run_agent_loop(
56
56
  if response.thinking:
57
57
  UI.print_thinking_block(response.thinking)
58
58
 
59
- # UI: print assistant text only when NOT already streamed live.
60
- if response.text and not response.streamed_text:
59
+ # UI: always fire AssistantMessageEvent so the TUI can finalize the response.
60
+ # For streamed responses the TUI uses the already-buffered text from StreamTokenEvents
61
+ # and ignores event.text to avoid duplication. For non-streamed it uses event.text directly.
62
+ if response.text:
61
63
  UI.print_assistant_message(response.text)
62
64
 
63
65
  if not response.has_tool_uses:
agent/mcp_client.py CHANGED
@@ -8,6 +8,7 @@ and registers them into the ToolRegistry.
8
8
 
9
9
  import json
10
10
  import asyncio
11
+ import shutil
11
12
  from pathlib import Path
12
13
 
13
14
  from mcp.client.session import ClientSession
@@ -18,6 +19,43 @@ from agent.tools import ToolRegistry, ToolResult
18
19
  from agent.ui import UI
19
20
 
20
21
 
22
+ # ── Install hints for common MCP server commands ─────────────────────────────
23
+
24
+ _INSTALL_HINTS: dict[str, str] = {
25
+ "npx": "Node.js is not installed or not on PATH.\n"
26
+ " → Install it from https://nodejs.org/ (LTS version recommended)\n"
27
+ " → After installing, restart your terminal and try again.",
28
+ "node": "Node.js is not installed or not on PATH.\n"
29
+ " → Install it from https://nodejs.org/",
30
+ "uvx": "'uvx' (uv tool runner) is not installed.\n"
31
+ " → Install uv: https://docs.astral.sh/uv/getting-started/installation/",
32
+ "docker": "Docker is not installed or not running.\n"
33
+ " → Install Docker Desktop from https://www.docker.com/products/docker-desktop/",
34
+ "python": "Python is not on PATH (unexpected — DevPilot itself runs on Python).",
35
+ "python3":"Python 3 is not on PATH.",
36
+ }
37
+
38
+
39
+ def _check_command(command: str, server_name: str) -> bool:
40
+ """
41
+ Return True if `command` is available on PATH.
42
+ If not, print a clear, actionable error and return False.
43
+ """
44
+ if shutil.which(command) is not None:
45
+ return True
46
+
47
+ hint = _INSTALL_HINTS.get(command.lower(),
48
+ f"'{command}' was not found on your PATH.\n"
49
+ f" → Make sure it is installed and your terminal can see it."
50
+ )
51
+ UI.print_error(
52
+ f"MCP server '{server_name}' requires '{command}' but it was not found.\n"
53
+ f" {hint}\n"
54
+ f" This MCP server will be skipped. Disable it in mcp_servers.json to suppress this message."
55
+ )
56
+ return False
57
+
58
+
21
59
  class MCPManager:
22
60
  """Manages connections to multiple MCP servers."""
23
61
 
@@ -79,7 +117,14 @@ class MCPManager:
79
117
  ready_event.set()
80
118
 
81
119
  async def connect_all(self, registry: ToolRegistry) -> None:
82
- """Connect to all servers in mcp_servers.json and register tools."""
120
+ """Connect to all servers in mcp_servers.json and register tools.
121
+
122
+ Uses a non-blocking approach: MCP servers connect in background tasks.
123
+ Startup waits at most MCP_CONNECT_TIMEOUT seconds total so a slow
124
+ npx download never freezes DevPilot at launch.
125
+ """
126
+ MCP_CONNECT_TIMEOUT = 2.0 # seconds — enough for cached/local servers; slow npx downloads skip
127
+
83
128
  if not self.config_path.exists():
84
129
  return
85
130
 
@@ -92,7 +137,7 @@ class MCPManager:
92
137
  return
93
138
 
94
139
  if isinstance(servers, dict):
95
- server_items = servers.items()
140
+ server_items = list(servers.items())
96
141
  else:
97
142
  server_items = [(s.get("name", f"server_{i}"), s) for i, s in enumerate(servers)]
98
143
 
@@ -100,7 +145,7 @@ class MCPManager:
100
145
  for name, server_config in server_items:
101
146
  if server_config.get("enabled", True) is False:
102
147
  continue
103
-
148
+
104
149
  command = server_config.get("command")
105
150
  args = server_config.get("args", [])
106
151
 
@@ -108,6 +153,10 @@ class MCPManager:
108
153
  UI.print_error(f"MCP server '{name}' missing 'command'. Skipping.")
109
154
  continue
110
155
 
156
+ # Pre-flight: make sure the binary is actually installed
157
+ if not _check_command(command, name):
158
+ continue
159
+
111
160
  ready_event = asyncio.Event()
112
161
  events.append(ready_event)
113
162
  task = asyncio.create_task(
@@ -116,7 +165,21 @@ class MCPManager:
116
165
  self._tasks.append(task)
117
166
 
118
167
  if events:
119
- await asyncio.gather(*(e.wait() for e in events))
168
+ # Wait for all servers to be ready, but cap total wait time.
169
+ # If any server is slow (e.g. npx downloading a package), DevPilot
170
+ # still starts promptly and the server connects in the background.
171
+ try:
172
+ await asyncio.wait_for(
173
+ asyncio.gather(*(e.wait() for e in events)),
174
+ timeout=MCP_CONNECT_TIMEOUT,
175
+ )
176
+ except asyncio.TimeoutError:
177
+ pending = sum(1 for e in events if not e.is_set())
178
+ UI.print_info(
179
+ f"{pending} MCP server(s) still connecting in background "
180
+ f"(took >{MCP_CONNECT_TIMEOUT:.0f}s). DevPilot is ready now."
181
+ )
182
+
120
183
 
121
184
  async def close(self) -> None:
122
185
  """Close all connections."""
agent/setup_wizard.py CHANGED
@@ -25,24 +25,69 @@ from rich.table import Table
25
25
 
26
26
  console = Console()
27
27
 
28
- # ── Model lists ───────────────────────────────────────────────────────────────
28
+
29
+ def _prompt_secret(prompt_text: str) -> str:
30
+ """
31
+ Prompt for a secret (API key) showing '*' for each character typed.
32
+ Works on Windows (CMD / PowerShell) where rich's password=True renders
33
+ completely blank and provides no visual feedback to the user.
34
+ Falls back to rich Prompt (password=True) on non-TTY / CI environments.
35
+ """
36
+ import sys
37
+ if not sys.stdin.isatty():
38
+ return Prompt.ask(prompt_text, password=True).strip()
39
+
40
+ import platform
41
+ if platform.system() == "Windows":
42
+ import msvcrt
43
+ console.print(f"{prompt_text}: ", end="")
44
+ chars: list[str] = []
45
+ while True:
46
+ ch = msvcrt.getwch() # read one char without echo
47
+ if ch in ("\r", "\n"): # Enter key
48
+ console.print() # move to next line
49
+ break
50
+ elif ch == "\x03": # Ctrl+C
51
+ raise KeyboardInterrupt
52
+ elif ch in ("\x08", "\x7f"): # Backspace
53
+ if chars:
54
+ chars.pop()
55
+ # erase last asterisk: backspace + space + backspace
56
+ console.print("\b \b", end="")
57
+ elif ch == "\x00" or ch == "\xe0": # special key prefix — skip next byte
58
+ msvcrt.getwch()
59
+ else:
60
+ chars.append(ch)
61
+ console.print("*", end="")
62
+ return "".join(chars).strip()
63
+ else:
64
+ # Unix: getpass already hides input cleanly
65
+ import getpass
66
+ return getpass.getpass(f"{prompt_text}: ").strip()
67
+
68
+
69
+ # ── Model lists (verified against live APIs) ──────────────────────────────────
70
+ # Last verified: May 2026
29
71
 
30
72
  _ANTHROPIC_MODELS = [
31
- ("claude-opus-4-5-20251101", "Most capable best for complex tasks"),
32
- ("claude-sonnet-4-5-20251101", "Balancedfast and capable"),
33
- ("claude-haiku-4-5-20251101", "Fastestbest for simple tasks"),
34
- ("claude-3-7-sonnet-20250219", "Extended thinking support"),
73
+ # Verified via GET https://api.anthropic.com/v1/models
74
+ ("claude-opus-4-7", "Newestmost capable Claude (latest)"),
75
+ ("claude-opus-4-5-20251101", "Claude Opus 4.5 powerful & reliable"),
76
+ ("claude-sonnet-4-5-20250929", "Claude Sonnet 4.5 — balanced speed & quality"),
77
+ ("claude-haiku-4-5-20251001", "Claude Haiku 4.5 — fastest, most affordable"),
35
78
  ]
36
79
 
37
80
  _OPENAI_MODELS = [
38
- ("gpt-4o", "Latest GPT-4o best for coding"),
39
- ("gpt-4o-mini", "Fast and cheap good for simple tasks"),
40
- ("o3", "Most powerful reasoning model"),
41
- ("o4-mini", "Fast reasoninggreat for code"),
81
+ # Verified via platform.openai.com/docs/models (May 2026)
82
+ # Note: gpt-4o, o3, o4-mini are retired as of early 2026
83
+ ("gpt-5.5", "Flagship best reasoning & coding"),
84
+ ("gpt-5.4", "Primary modelcoding & professional work"),
85
+ ("gpt-5.4-mini", "Fast & cost-efficient"),
86
+ ("gpt-5.4-nano", "Fastest — high-volume low-latency tasks"),
42
87
  ]
43
88
 
44
89
  # OpenAI-compatible third-party providers
45
- # (base_url, display_name, key_env_var, key_url, models)
90
+ # (display_name, key_url, key_env_var, base_url, models)
46
91
  _COMPATIBLE_PROVIDERS = {
47
92
  "1": (
48
93
  "Groq",
@@ -50,10 +95,12 @@ _COMPATIBLE_PROVIDERS = {
50
95
  "GROQ_API_KEY",
51
96
  "https://api.groq.com/openai/v1",
52
97
  [
53
- ("llama-3.3-70b-versatile", "Best Groq model — fast & capable"),
54
- ("llama-3.1-8b-instant", "Ultra-fast, lightweight"),
55
- ("mixtral-8x7b-32768", "Large context window"),
56
- ("gemma2-9b-it", "Google Gemma 2 fast"),
98
+ # Verified via GET https://api.groq.com/openai/v1/models
99
+ ("llama-3.3-70b-versatile", "Llama 3.3 70B — best quality (131k ctx)"),
100
+ ("meta-llama/llama-4-scout-17b-16e-instruct","Llama 4 Scout 17B — latest & fast"),
101
+ ("qwen/qwen3-32b", "Qwen3 32Bstrong reasoning"),
102
+ ("llama-3.1-8b-instant", "Llama 3.1 8B — ultra-fast"),
103
+ ("groq/compound", "Groq Compound — agentic tasks"),
57
104
  ],
58
105
  ),
59
106
  "2": (
@@ -62,9 +109,12 @@ _COMPATIBLE_PROVIDERS = {
62
109
  "TOGETHER_API_KEY",
63
110
  "https://api.together.xyz/v1",
64
111
  [
65
- ("meta-llama/Llama-3-70b-chat-hf", "Llama 3 70B best quality"),
66
- ("meta-llama/Llama-3-8b-chat-hf", "Llama 3 8Bfaster"),
67
- ("mistralai/Mixtral-8x7B-v0.1", "Mixtrallarge context"),
112
+ # Verified via Together AI docs (May 2026)
113
+ ("meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8", "Llama 4 Maverickbest quality"),
114
+ ("meta-llama/Llama-4-Scout-17B-16E-Instruct-FP8", "Llama 4 Scout fast MoE"),
115
+ ("meta-llama/Llama-3.3-70B-Instruct-Turbo", "Llama 3.3 70B Turbo — reliable"),
116
+ ("meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo", "Llama 3.1 405B — most capable"),
117
+ ("meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo", "Llama 3.1 8B — very fast"),
68
118
  ],
69
119
  ),
70
120
  "3": (
@@ -73,9 +123,10 @@ _COMPATIBLE_PROVIDERS = {
73
123
  "MISTRAL_API_KEY",
74
124
  "https://api.mistral.ai/v1",
75
125
  [
76
- ("mistral-large-latest", "Most capable Mistral model"),
77
- ("mistral-small-latest", "Fast and affordable"),
78
- ("codestral-latest", "Optimised for code"),
126
+ # Verified via Mistral docs — latest aliases always point to newest stable
127
+ ("mistral-large-latest", "Most capable Mistral model"),
128
+ ("mistral-small-latest", "Fast and affordable"),
129
+ ("codestral-latest", "Optimised for code generation"),
79
130
  ],
80
131
  ),
81
132
  "4": (
@@ -84,10 +135,11 @@ _COMPATIBLE_PROVIDERS = {
84
135
  "OLLAMA_API_KEY", # Ollama doesn't need a real key
85
136
  "http://localhost:11434/v1",
86
137
  [
87
- ("qwen2.5-coder:7b", "Best local coding model (recommended)"),
88
- ("codellama:13b", "Meta CodeLlama 13B"),
89
- ("deepseek-coder:6b", "DeepSeek Coder 6B"),
90
- ("llama3.2:3b", "Llama 3.2 3B very fast"),
138
+ # Run: ollama pull <model> before using
139
+ ("qwen2.5-coder:7b", "Qwen2.5-Coder 7B — best local coding (8GB VRAM)"),
140
+ ("qwen2.5-coder:32b", "Qwen2.5-Coder 32B — benchmark king (24GB VRAM)"),
141
+ ("deepseek-coder-v2:16b","DeepSeek-Coder-V2 16Bstrong coding (16GB VRAM)"),
142
+ ("llama3.3:70b", "Llama 3.3 70B — general purpose"),
91
143
  ],
92
144
  ),
93
145
  "5": (
@@ -100,6 +152,7 @@ _COMPATIBLE_PROVIDERS = {
100
152
  }
101
153
 
102
154
 
155
+
103
156
  # ── Helpers ───────────────────────────────────────────────────────────────────
104
157
 
105
158
  def _is_interactive() -> bool:
@@ -162,12 +215,17 @@ def _pick_model(models: list[tuple[str, str]], allow_custom: bool = False) -> st
162
215
  def run_setup_wizard(env_path: Path | None = None) -> bool:
163
216
  """
164
217
  Run the interactive first-run setup wizard.
218
+ Saves configuration to ~/.devpilot/.env so settings persist
219
+ regardless of which directory `devpilot` is run from.
165
220
  Returns True if setup completed, False if skipped/failed.
166
221
  """
167
222
  if not _is_interactive():
168
223
  return False
169
224
 
170
- env_path = env_path or Path(".env")
225
+ # Always save to the persistent user config directory
226
+ user_config_dir = Path.home() / ".devpilot"
227
+ user_config_dir.mkdir(parents=True, exist_ok=True)
228
+ env_path = env_path or (user_config_dir / ".env")
171
229
 
172
230
  console.print(Panel(
173
231
  "[bold cyan]Welcome to DevPilot! 🚀[/bold cyan]\n\n"
@@ -179,12 +237,12 @@ def run_setup_wizard(env_path: Path | None = None) -> bool:
179
237
 
180
238
  # ── Step 1: Choose provider ───────────────────────────────────────────────
181
239
  console.print("\n[bold]Step 1 of 3 — Choose your AI provider[/bold]\n")
182
- console.print(" [cyan]1[/cyan] Anthropic (Claude recommended)")
183
- console.print(" [cyan]2[/cyan] OpenAI (GPT-4o, o3, o4-mini)")
184
- console.print(" [cyan]3[/cyan] Groq (Llama 3, Mixtral — very fast, free tier)")
185
- console.print(" [cyan]4[/cyan] Together AI (Llama 3, Mixtral)")
186
- console.print(" [cyan]5[/cyan] Mistral AI (Mistral, Codestral)")
187
- console.print(" [cyan]6[/cyan] Ollama (local models, no API key needed)")
240
+ console.print(" [cyan]1[/cyan] Anthropic (Claude Opus 4.7, Sonnet 4.5, Haiku 4.5)")
241
+ console.print(" [cyan]2[/cyan] OpenAI (GPT-5.5, GPT-5.4, GPT-5.4-mini)")
242
+ console.print(" [cyan]3[/cyan] Groq (Llama 4 Scout, Llama 3.3 70B — very fast, free tier)")
243
+ console.print(" [cyan]4[/cyan] Together AI (Llama 4 Maverick/Scout, Llama 3.3 70B)")
244
+ console.print(" [cyan]5[/cyan] Mistral AI (Mistral Large, Codestral)")
245
+ console.print(" [cyan]6[/cyan] Ollama (local models Qwen2.5-Coder, DeepSeek, Llama)")
188
246
  console.print(" [cyan]7[/cyan] Other (any OpenAI-compatible endpoint)")
189
247
 
190
248
  choice = Prompt.ask("\nProvider", choices=["1","2","3","4","5","6","7"], default="1")
@@ -195,7 +253,7 @@ def run_setup_wizard(env_path: Path | None = None) -> bool:
195
253
  if choice == "1":
196
254
  console.print("\n[bold]Step 2 of 3 — Enter your Anthropic API key[/bold]")
197
255
  console.print(" Get one at: [link=https://console.anthropic.com/]https://console.anthropic.com/[/link]")
198
- api_key = Prompt.ask("\nANTHROPIC_API_KEY", password=True).strip()
256
+ api_key = _prompt_secret("ANTHROPIC_API_KEY")
199
257
  if not api_key:
200
258
  console.print("[red]API key cannot be empty.[/red]")
201
259
  return False
@@ -215,7 +273,7 @@ def run_setup_wizard(env_path: Path | None = None) -> bool:
215
273
  elif choice == "2":
216
274
  console.print("\n[bold]Step 2 of 3 — Enter your OpenAI API key[/bold]")
217
275
  console.print(" Get one at: [link=https://platform.openai.com/api-keys]https://platform.openai.com/api-keys[/link]")
218
- api_key = Prompt.ask("\nOPENAI_API_KEY", password=True).strip()
276
+ api_key = _prompt_secret("OPENAI_API_KEY")
219
277
  if not api_key:
220
278
  console.print("[red]API key cannot be empty.[/red]")
221
279
  return False
@@ -244,7 +302,7 @@ def run_setup_wizard(env_path: Path | None = None) -> bool:
244
302
  api_key = "ollama"
245
303
  else:
246
304
  console.print(f" Get one at: [link={key_url}]{key_url}[/link]")
247
- api_key = Prompt.ask(f"\n{key_env}", password=True).strip()
305
+ api_key = _prompt_secret(key_env)
248
306
  if not api_key:
249
307
  console.print("[red]API key cannot be empty.[/red]")
250
308
  return False
@@ -264,7 +322,7 @@ def run_setup_wizard(env_path: Path | None = None) -> bool:
264
322
  elif choice == "7":
265
323
  console.print("\n[bold]Step 2 of 3 — Custom OpenAI-compatible endpoint[/bold]")
266
324
  base_url = Prompt.ask("Base URL (e.g. https://api.example.com/v1)").strip()
267
- api_key = Prompt.ask("API key", password=True).strip()
325
+ api_key = _prompt_secret("API key")
268
326
  model = Prompt.ask("Model name (e.g. llama-3-70b)").strip()
269
327
 
270
328
  if not base_url or not model:
agent/tools/registry.py CHANGED
@@ -15,15 +15,6 @@ from dataclasses import dataclass, field
15
15
  from typing import TYPE_CHECKING, Any, Awaitable, Callable
16
16
 
17
17
  from agent.tools.base import BaseTool, ToolResult
18
- from agent.tools.search_code import SearchCodeTool
19
-
20
- from agent.tools.shell import RunBashTool
21
- from agent.tools.a2a import A2ATool
22
- from agent.tools.web_search import WebSearchTool
23
-
24
- from agent.tools.git_ops import GitStatusTool, GitCommitTool
25
- from agent.tools.doc_gen import DocGenTool
26
- from agent.tools.diagram import DiagramTool
27
18
  from agent.ui import UI
28
19
 
29
20
  if TYPE_CHECKING:
@@ -108,8 +99,15 @@ class ToolRegistry:
108
99
 
109
100
  def _register_builtins(self) -> None:
110
101
  """Register all default native tools."""
111
- # Import here to avoid circular imports at module load time
102
+ # All heavy tool imports are lazy here keeps startup fast.
103
+ # GitPython, tavily, shell, and other deps only load when the
104
+ # registry is instantiated inside main_async(), not at module import time.
112
105
  from agent.tools.fs import ListFilesTool, ReadFileTool, WriteFileTool, EditFileTool
106
+ from agent.tools.shell import RunBashTool
107
+ from agent.tools.search_code import SearchCodeTool
108
+ from agent.tools.git_ops import GitStatusTool, GitCommitTool
109
+ from agent.tools.doc_gen import DocGenTool
110
+ from agent.tools.diagram import DiagramTool
113
111
 
114
112
  tools: list[BaseTool] = [
115
113
  ReadFileTool(self._config, self._context),
@@ -118,7 +116,6 @@ class ToolRegistry:
118
116
  ListFilesTool(self._config, self._context),
119
117
  RunBashTool(self._config),
120
118
  SearchCodeTool(self._config),
121
-
122
119
  GitStatusTool(self._config),
123
120
  GitCommitTool(self._config),
124
121
  DocGenTool(self._config),
@@ -126,10 +123,11 @@ class ToolRegistry:
126
123
  ]
127
124
 
128
125
  if self._config.web_search_enabled:
126
+ from agent.tools.web_search import WebSearchTool
129
127
  tools.append(WebSearchTool(self._config))
130
128
 
131
-
132
129
  if self._config.a2a_enabled:
130
+ from agent.tools.a2a import A2ATool
133
131
  tools.append(A2ATool(self._config))
134
132
 
135
133
  for t in tools:
agent/tui/app.py CHANGED
@@ -429,22 +429,25 @@ class DevPilotApp(App):
429
429
  """Re-enable input when the agent loop finishes."""
430
430
  if event.worker.name == "run_agent_task" and event.state.name in ("SUCCESS", "ERROR", "CANCELLED"):
431
431
  self.spinner.display = False
432
-
432
+
433
433
  try:
434
434
  inp = self.query_one("#chat-input", Input)
435
435
  inp.disabled = False
436
436
  inp.focus()
437
437
  except Exception:
438
438
  pass
439
-
440
- # If we had a buffered stream, write it out
439
+
440
+ # Safety fallback: if AssistantMessageEvent never arrived (edge case),
441
+ # finalize the stream now so the response is never lost.
441
442
  if self._active_stream:
442
443
  final_text = self._active_stream._buffer
443
444
  await self._active_stream.remove()
444
445
  self._active_stream = None
445
- await self.chat_log.mount(ChatMessage("DevPilot", final_text))
446
- self.chat_log.scroll_end(animate=False)
447
- self._last_assistant_message = final_text
446
+ if final_text:
447
+ await self.chat_log.mount(ChatMessage("DevPilot", final_text))
448
+ self.chat_log.scroll_end(animate=False)
449
+ self._last_assistant_message = final_text
450
+
448
451
  # Refresh tree to show newly read/written files
449
452
  self._refresh_project_map()
450
453
 
@@ -479,14 +482,16 @@ class DevPilotApp(App):
479
482
  @on(ThinkingEvent)
480
483
  async def handle_ui_events(self, event: UIEvent) -> None:
481
484
  if isinstance(event, AssistantMessageEvent):
482
- final_text = ""
483
485
  if self._active_stream:
486
+ # Streaming was used — use the already-buffered text.
487
+ # Ignore event.text to avoid duplication (it's the same content).
484
488
  final_text = self._active_stream._buffer
485
489
  await self._active_stream.remove()
486
490
  self._active_stream = None
487
- if event.text.strip():
488
- final_text += ("\n" + event.text.strip()) if final_text else event.text.strip()
489
-
491
+ else:
492
+ # Non-streaming path event.text is the only source.
493
+ final_text = event.text.strip()
494
+
490
495
  if final_text:
491
496
  await self.chat_log.mount(ChatMessage("DevPilot", final_text))
492
497
  self.chat_log.scroll_end(animate=False)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: devpilot-agentic-cli
3
- Version: 1.0.2
3
+ Version: 1.0.4
4
4
  Summary: Autonomous AI coding agent for your terminal — Claude, GPT-4o, Groq, Ollama, and more
5
5
  Author: Thijesh Praveen V
6
6
  License: MIT
@@ -13,6 +13,8 @@ Classifier: Environment :: Console
13
13
  Classifier: Intended Audience :: Developers
14
14
  Classifier: Programming Language :: Python :: 3.11
15
15
  Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Programming Language :: Python :: 3.14
16
18
  Classifier: License :: OSI Approved :: MIT License
17
19
  Classifier: Topic :: Software Development
18
20
  Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
@@ -46,7 +48,7 @@ Requires-Dist: pytest-asyncio>=0.23.7; extra == "dev"
46
48
  [![Python](https://img.shields.io/pypi/pyversions/devpilot-agentic-cli)](https://pypi.org/project/devpilot-agentic-cli/)
47
49
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
48
50
 
49
- **An autonomous AI coding agent for your terminal.** DevPilot gives Claude, GPT-4o, Groq, Mistral, or any local model a full suite of tools — file read/write, bash execution, code search, git, MCP, and more — orchestrated into a self-healing agentic loop inside a rich Textual TUI.
51
+ **An autonomous AI coding agent for your terminal.** DevPilot gives Claude, GPT, Groq, Mistral, or any local model a full suite of tools — file read/write, bash execution, code search, git, MCP, and more — orchestrated into a self-healing agentic loop inside a rich Textual TUI. Works in PowerShell, CMD, and any modern terminal.
50
52
 
51
53
  ```
52
54
  ┌─ DevPilot ──────────────────────────────────────────────────────┐
@@ -76,7 +78,6 @@ Requires-Dist: pytest-asyncio>=0.23.7; extra == "dev"
76
78
  | **Git** | `git_status`, `git_commit` with surgical file staging |
77
79
  | **Documentation** | `doc_gen` (markdown), `diagram` (Mermaid) |
78
80
  | **Web search** | `web_search` via Tavily (optional) |
79
-
80
81
  | **MCP** | Connect any MCP server via `mcp_servers.json` |
81
82
  | **A2A** | Agent-to-agent task delegation over HTTP |
82
83
  | **Providers** | Anthropic, OpenAI, Groq, Together AI, Mistral, Ollama, any OpenAI-compatible endpoint |
@@ -92,7 +93,7 @@ pip install devpilot-agentic-cli
92
93
  devpilot
93
94
  ```
94
95
 
95
- That's it. On first run DevPilot launches a setup wizard — pick your provider, enter your API key, choose a model. It saves everything to a `.env` file and opens the TUI. Every subsequent run starts immediately.
96
+ That's it. On first run DevPilot launches a setup wizard — pick your provider, enter your API key, choose a model. It saves everything to `~/.devpilot/.env` and opens the TUI. Every subsequent run starts immediately.
96
97
 
97
98
  ### Optional Features
98
99
 
@@ -111,19 +112,19 @@ DevPilot works with any of these out of the box — the setup wizard walks you t
111
112
 
112
113
  | Provider | Models | Get API key |
113
114
  |---|---|---|
114
- | **Anthropic** | claude-opus-4-5, claude-sonnet-4-5, claude-haiku-4-5 | [console.anthropic.com](https://console.anthropic.com/) |
115
- | **OpenAI** | gpt-4o, gpt-4o-mini, o3, o4-mini | [platform.openai.com](https://platform.openai.com/api-keys) |
116
- | **Groq** | llama-3.3-70b, mixtral-8x7b, gemma2-9b | [console.groq.com](https://console.groq.com/keys) |
117
- | **Together AI** | Llama 3, Mixtral, and more | [api.together.xyz](https://api.together.xyz/settings/api-keys) |
118
- | **Mistral AI** | mistral-large, codestral | [console.mistral.ai](https://console.mistral.ai/api-keys/) |
119
- | **Ollama** | Any local model — no API key needed | [ollama.com](https://ollama.com/library) |
115
+ | **Anthropic** | claude-opus-4-7, claude-opus-4-5-20251101, claude-sonnet-4-5-20250929, claude-haiku-4-5-20251001 | [console.anthropic.com](https://console.anthropic.com/) |
116
+ | **OpenAI** | gpt-5.5, gpt-5.4, gpt-5.4-mini, gpt-5.4-nano | [platform.openai.com](https://platform.openai.com/api-keys) |
117
+ | **Groq** | llama-3.3-70b-versatile, llama-4-scout-17b, qwen3-32b | [console.groq.com](https://console.groq.com/keys) |
118
+ | **Together AI** | Llama 4 Maverick/Scout, Llama 3.3 70B Turbo | [api.together.xyz](https://api.together.xyz/settings/api-keys) |
119
+ | **Mistral AI** | mistral-large-latest, mistral-small-latest, codestral-latest | [console.mistral.ai](https://console.mistral.ai/api-keys/) |
120
+ | **Ollama** | qwen2.5-coder:7b, deepseek-coder-v2:16b, llama3.3:70b — no API key needed | [ollama.com](https://ollama.com/library) |
120
121
  | **Other** | Any OpenAI-compatible endpoint | — |
121
122
 
122
123
  ---
123
124
 
124
125
  ## Configuration
125
126
 
126
- All settings live in a `.env` file in your project directory. The setup wizard creates this on first run. You can edit it manually anytime — or re-run the wizard with `devpilot --setup`.
127
+ All settings live in `~/.devpilot/.env` — a persistent file in your home directory that works regardless of which folder you run `devpilot` from. The setup wizard creates this on first run. You can edit it manually anytime — or re-run the wizard with `devpilot --setup`.
127
128
 
128
129
  ### Full settings reference
129
130
 
@@ -131,9 +132,10 @@ All settings live in a `.env` file in your project directory. The setup wizard c
131
132
  |---|---|---|
132
133
  | `ANTHROPIC_API_KEY` | — | API key for Anthropic provider |
133
134
  | `OPENAI_API_KEY` | — | API key for OpenAI / Groq / Together / Mistral / Ollama |
135
+ | `GROQ_API_KEY` | — | API key for Groq (optional — can reuse as OPENAI_API_KEY) |
134
136
  | `DEVPILOT_PROVIDER` | `anthropic` | `anthropic` or `openai` |
135
- | `DEVPILOT_MODEL` | `claude-opus-4-5` | Model name |
136
- | `DEVPILOT_BASE_URL` | — | Custom endpoint, e.g. `http://localhost:11434/v1` for Ollama |
137
+ | `DEVPILOT_MODEL` | `claude-opus-4-5-20251101` | Model name |
138
+ | `DEVPILOT_BASE_URL` | — | Custom endpoint, e.g. `https://api.groq.com/openai/v1` |
137
139
  | `DEVPILOT_NO_CONFIRM` | `false` | Skip confirmation prompts (useful for CI) |
138
140
  | `DEVPILOT_MAX_ITERATIONS` | `50` | Max tool-use iterations before loop aborts |
139
141
  | `DEVPILOT_WORKDIR` | `cwd` | Root directory for file operations |
@@ -144,12 +146,12 @@ All settings live in a `.env` file in your project directory. The setup wizard c
144
146
  ### Override priority
145
147
 
146
148
  ```
147
- CLI flags → env vars → .env file → defaults
149
+ CLI flags → env vars → ~/.devpilot/.env → defaults
148
150
  ```
149
151
 
150
152
  Per-run override example:
151
153
  ```bash
152
- DEVPILOT_MODEL=claude-haiku-4-5-20251101 devpilot --task "fix typos"
154
+ DEVPILOT_MODEL=llama-3.3-70b-versatile devpilot --task "fix typos"
153
155
  ```
154
156
 
155
157
  ---
@@ -172,7 +174,6 @@ Options:
172
174
  --a2a-port PORT A2A server port (default: 8000)
173
175
  --no-a2a Disable A2A server
174
176
  --no-web-search Disable Tavily web search
175
-
176
177
  --setup Re-run the setup wizard
177
178
  ```
178
179
 
@@ -232,7 +233,7 @@ agent/
232
233
  ├── setup_wizard.py First-run interactive configuration wizard
233
234
  ├── loop.py Core agentic loop (plan → act → verify → heal)
234
235
  ├── config.py Config dataclass — all settings from env vars
235
- ├── context.py RepoContext — file awareness, AST map
236
+ ├── context.py RepoContext — file awareness, AST project map
236
237
  ├── history.py Conversation history + smart context pruning
237
238
  ├── providers/
238
239
  │ ├── anthropic_provider.py
@@ -247,7 +248,6 @@ agent/
247
248
  │ ├── doc_gen.py doc_gen
248
249
  │ ├── diagram.py diagram (Mermaid)
249
250
  │ ├── web_search.py web_search (Tavily)
250
-
251
251
  │ ├── a2a.py A2A delegation
252
252
  │ └── registry.py ToolRegistry + PermissionGuard
253
253
  └── tui/
@@ -1,13 +1,13 @@
1
1
  agent/__init__.py,sha256=lfJPQT9NbRRWfa9YABD9u8apcLjs3tzDVZvrOb69GBQ,25
2
2
  agent/a2a_client.py,sha256=vY4qZahkWP-DVU2smIeTz1xLj61kkDjE9Tmt_-9PKUU,3416
3
3
  agent/a2a_server.py,sha256=3rkDy8dm49_5qCa6fKYkLdmHTnHs5ocHBuZ8j32-p48,5230
4
- agent/cli.py,sha256=YdgLLIk4vgK-gGZ-Ia-kSZ_BbgfB2WE9QX83jiGsv48,8819
5
- agent/config.py,sha256=GWS719hDNvvzgJAq9MF2FAGEVwM1AKA_B4WSDDzKEAg,10580
6
- agent/context.py,sha256=a--jOYLX5jM-8RDLV0tAvZHOx9H5M6SNxSJipzEUZe4,7323
4
+ agent/cli.py,sha256=ApAnD7pQUvHPsnHt5g8JoK0CaGAIPS78qiGQOt7B4X4,13305
5
+ agent/config.py,sha256=b53O7kkdAUYTNCm7YLxBYiySj6ZXn2qzoWqgPN1NIUw,11244
6
+ agent/context.py,sha256=atGJgIiMAmYfcNu6k9gwUzsz5cykBAx9A8PCdgK64R0,7297
7
7
  agent/history.py,sha256=IMY3IO5TN9Sh7tJPAWE9VZYC0pkAwgPj2a_F1yA4OmE,6730
8
- agent/loop.py,sha256=8lTvCSAljPdmyyJ1uf0Q9qulUFNE3d4v3ZNzRae4c1w,3514
9
- agent/mcp_client.py,sha256=lasmMq602_OEirzTNO57dNEF7mWaed_A6NaNTY3voOQ,4978
10
- agent/setup_wizard.py,sha256=jCXpp1vY9HRqJLO9eC6M9T6JlHXP7LsausmhkciB0d8,12774
8
+ agent/loop.py,sha256=3QxOb2c09ewAcIbAnd3RVCsXzKaGTJFBLZ-5OdhnOi0,3693
9
+ agent/mcp_client.py,sha256=A8WgS2DhfBbpZZGrJYwOZwMAF0gA-YG4gylBgMzkriE,7785
10
+ agent/setup_wizard.py,sha256=KNDxy-0jC6erB9IGpZoPCcrgpzU523D8p1zEx8aMbC0,15655
11
11
  agent/ui.py,sha256=LK8L2fTq_JIcYiVVjpVWaoYMFWXwqUcqYL4gspdAqvs,8283
12
12
  agent/providers/__init__.py,sha256=6xBg26vCBV2e3hybMT2JCHEZoRNpImkVOK-FytBW4xY,166
13
13
  agent/providers/anthropic_provider.py,sha256=i257T8T7fYuUEVvsy-3_eudbjSlxZqbn3zi7DFvukRE,6067
@@ -22,14 +22,14 @@ agent/tools/diagram.py,sha256=FpQRWud6GMi34ysjbEkqnvy5aaHOJwIvpmDELsyIqPE,4884
22
22
  agent/tools/doc_gen.py,sha256=TaLw5JJ1hSgfViGyfYRMfUafjRTfxa0JYtZw0ycluR8,6064
23
23
  agent/tools/fs.py,sha256=nBrGzq-yIzFcRzhPguBxJ9H23lhsUjGPzQXhwfrcQY0,16121
24
24
  agent/tools/git_ops.py,sha256=CbulyzzSuFUQdAqu-6eMkg7CpB45h7nFP_5fxTecHeE,5505
25
- agent/tools/registry.py,sha256=R6PsHIVqWyJwuFdC2EIm3GLdtTCr4j2S5BGWOTPanNA,8404
25
+ agent/tools/registry.py,sha256=0I8Pgt7obgNaVB8BTeHHRmdouTj3P3Kk7GCCra7D2fg,8620
26
26
  agent/tools/search_code.py,sha256=DwxT6D10OpWvIX7KR3-cRW1Z1MkLW2_WcJ47H_jfuHM,4490
27
27
  agent/tools/shell.py,sha256=i9nar3gyS28p6wjfAljNREUll-7RiD_LhQQ_CDkz-CM,3923
28
28
  agent/tools/web_search.py,sha256=7a533lYcRG0rwYle00xsIncfPRL1zCiS-wsL_qVxlyI,3736
29
29
  agent/tui/__init__.py,sha256=FB4u1BY9RSmYVfCM2yMeCyMmzjnb9T0bhmPp9hHH8Ys,37
30
- agent/tui/app.py,sha256=ZClXMLgXvohtLlYogKMpQISjEiFTHzNP6XPSeM6y4xU,18598
31
- devpilot_agentic_cli-1.0.2.dist-info/METADATA,sha256=HGoT4GuEaEX-l_-Fp4Cu5sewyRn9X058Bqp7HHUdXlA,11105
32
- devpilot_agentic_cli-1.0.2.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
33
- devpilot_agentic_cli-1.0.2.dist-info/entry_points.txt,sha256=Zi0zH4sqSQptehekvn5CBhU452GQ23Vyja4l8C58IuY,44
34
- devpilot_agentic_cli-1.0.2.dist-info/top_level.txt,sha256=0gvCG7PHc22NA63j3bTGi2Zc37ym9t8Pf90ZLzf1kGA,6
35
- devpilot_agentic_cli-1.0.2.dist-info/RECORD,,
30
+ agent/tui/app.py,sha256=dBa3l5J3dkS7j56b-NgTQXR1At8aRXpayORk4CbJGBo,18831
31
+ devpilot_agentic_cli-1.0.4.dist-info/METADATA,sha256=VHAXdhDQOcW-VLetIITT-AZmXmUbsdViyjK7dho1IZE,11608
32
+ devpilot_agentic_cli-1.0.4.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
33
+ devpilot_agentic_cli-1.0.4.dist-info/entry_points.txt,sha256=Zi0zH4sqSQptehekvn5CBhU452GQ23Vyja4l8C58IuY,44
34
+ devpilot_agentic_cli-1.0.4.dist-info/top_level.txt,sha256=0gvCG7PHc22NA63j3bTGi2Zc37ym9t8Pf90ZLzf1kGA,6
35
+ devpilot_agentic_cli-1.0.4.dist-info/RECORD,,