agent-cli 0.68.5__py3-none-any.whl → 0.69.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.
@@ -9,7 +9,6 @@ import sys
9
9
  import time
10
10
  from typing import TYPE_CHECKING
11
11
 
12
- import pyperclip
13
12
  import typer
14
13
 
15
14
  from agent_cli import config, opts
@@ -118,6 +117,8 @@ def _display_result(
118
117
  ) -> None:
119
118
  """Handle output and clipboard copying based on desired verbosity."""
120
119
  if clipboard:
120
+ import pyperclip # noqa: PLC0415
121
+
121
122
  pyperclip.copy(corrected_text)
122
123
 
123
124
  if simple_output:
@@ -10,6 +10,7 @@ from agent_cli.core.process import set_process_title
10
10
  memory_app = typer.Typer(
11
11
  name="memory",
12
12
  help="Memory system operations (add, proxy, etc.).",
13
+ add_completion=True,
13
14
  rich_markup_mode="markdown",
14
15
  no_args_is_help=True,
15
16
  )
@@ -11,7 +11,7 @@ from rich.logging import RichHandler
11
11
  from agent_cli import constants, opts
12
12
  from agent_cli.agents.memory import memory_app
13
13
  from agent_cli.core.deps import requires_extras
14
- from agent_cli.core.utils import console, print_command_line_args, print_error_message
14
+ from agent_cli.core.utils import console, print_command_line_args
15
15
 
16
16
 
17
17
  @memory_app.command("proxy")
@@ -105,17 +105,10 @@ def proxy(
105
105
  if print_args:
106
106
  print_command_line_args(locals())
107
107
 
108
- try:
109
- import uvicorn # noqa: PLC0415
110
-
111
- from agent_cli.memory._files import ensure_store_dirs # noqa: PLC0415
112
- from agent_cli.memory.api import create_app # noqa: PLC0415
113
- except ImportError as exc:
114
- print_error_message(
115
- "Memory dependencies are not installed. Please install with "
116
- "`pip install agent-cli[memory]` or `uv sync --extra memory`.",
117
- )
118
- raise typer.Exit(1) from exc
108
+ import uvicorn # noqa: PLC0415
109
+
110
+ from agent_cli.memory._files import ensure_store_dirs # noqa: PLC0415
111
+ from agent_cli.memory.api import create_app # noqa: PLC0415
119
112
 
120
113
  logging.basicConfig(
121
114
  level=log_level.upper(),
@@ -77,16 +77,9 @@ def rag_proxy(
77
77
  logging.getLogger("chromadb").setLevel(logging.WARNING)
78
78
  logging.getLogger("uvicorn.access").setLevel(logging.WARNING)
79
79
 
80
- try:
81
- import uvicorn # noqa: PLC0415
80
+ import uvicorn # noqa: PLC0415
82
81
 
83
- from agent_cli.rag.api import create_app # noqa: PLC0415
84
- except ImportError as exc:
85
- print_error_message(
86
- "RAG dependencies are not installed. Please install with "
87
- "`pip install agent-cli[rag]` or `uv sync --extra rag`.",
88
- )
89
- raise typer.Exit(1) from exc
82
+ from agent_cli.rag.api import create_app # noqa: PLC0415
90
83
 
91
84
  docs_folder = docs_folder.resolve()
92
85
  chroma_path = chroma_path.resolve()
@@ -12,7 +12,6 @@ from datetime import UTC, datetime, timedelta
12
12
  from pathlib import Path # noqa: TC003
13
13
  from typing import Any, TypedDict
14
14
 
15
- import pyperclip
16
15
  import typer
17
16
 
18
17
  from agent_cli import config, opts
@@ -364,6 +363,8 @@ async def _async_main( # noqa: PLR0912, PLR0915, C901
364
363
  )
365
364
  clipboard_snapshot: str | None = None
366
365
  if general_cfg.clipboard:
366
+ import pyperclip # noqa: PLC0415
367
+
367
368
  clipboard_snapshot = pyperclip.paste()
368
369
  pyperclip.copy(transcript)
369
370
  LOGGER.info("Copied raw transcript to clipboard before LLM processing.")
@@ -445,6 +446,8 @@ async def _async_main( # noqa: PLR0912, PLR0915, C901
445
446
  )
446
447
 
447
448
  if general_cfg.clipboard:
449
+ import pyperclip # noqa: PLC0415
450
+
448
451
  pyperclip.copy(transcript)
449
452
  LOGGER.info("Copied transcript to clipboard.")
450
453
  else:
agent_cli/cli.py CHANGED
@@ -2,9 +2,12 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import sys
6
+ from pathlib import Path
5
7
  from typing import Annotated
6
8
 
7
9
  import typer
10
+ from rich.table import Table
8
11
 
9
12
  from . import __version__
10
13
  from .config import load_config, normalize_provider_defaults
@@ -14,15 +17,28 @@ from .core.utils import console
14
17
  app = typer.Typer(
15
18
  name="agent-cli",
16
19
  help="A suite of AI-powered command-line tools for text correction, audio transcription, and voice assistance.",
17
- add_completion=True,
18
20
  context_settings={"help_option_names": ["-h", "--help"]},
21
+ add_completion=True,
19
22
  rich_markup_mode="markdown",
23
+ no_args_is_help=True,
20
24
  )
21
25
 
22
26
 
23
27
  def _version_callback(value: bool) -> None:
24
28
  if value:
25
- console.print(f"agent-cli {__version__}")
29
+ path = Path(__file__).parent
30
+ data = [
31
+ ("agent-cli version", __version__),
32
+ ("agent-cli location", str(path)),
33
+ ("Python version", sys.version),
34
+ ("Python executable", sys.executable),
35
+ ]
36
+ table = Table(show_header=False)
37
+ table.add_column("Property", style="cyan")
38
+ table.add_column("Value", style="magenta")
39
+ for prop, val in data:
40
+ table.add_row(prop, val)
41
+ console.print(table)
26
42
  raise typer.Exit
27
43
 
28
44
 
agent_cli/config_cmd.py CHANGED
@@ -21,6 +21,7 @@ from agent_cli.core.utils import console
21
21
  config_app = typer.Typer(
22
22
  name="config",
23
23
  help="Manage agent-cli configuration files.",
24
+ add_completion=True,
24
25
  rich_markup_mode="markdown",
25
26
  no_args_is_help=True,
26
27
  )
agent_cli/core/chroma.py CHANGED
@@ -4,10 +4,6 @@ from __future__ import annotations
4
4
 
5
5
  from typing import TYPE_CHECKING, Any
6
6
 
7
- import chromadb
8
- from chromadb.config import Settings
9
- from chromadb.utils import embedding_functions
10
-
11
7
  from agent_cli.constants import DEFAULT_OPENAI_EMBEDDING_MODEL
12
8
 
13
9
  if TYPE_CHECKING:
@@ -28,6 +24,10 @@ def init_collection(
28
24
  subdir: str | None = None,
29
25
  ) -> Collection:
30
26
  """Initialize a Chroma collection with OpenAI-compatible embeddings."""
27
+ import chromadb # noqa: PLC0415
28
+ from chromadb.config import Settings # noqa: PLC0415
29
+ from chromadb.utils import embedding_functions # noqa: PLC0415
30
+
31
31
  target_path = persistence_path / subdir if subdir else persistence_path
32
32
  target_path.mkdir(parents=True, exist_ok=True)
33
33
  client = chromadb.PersistentClient(
agent_cli/core/deps.py CHANGED
@@ -142,7 +142,7 @@ def _try_auto_install(missing: list[str]) -> bool:
142
142
  console.print(
143
143
  f"[yellow]Auto-installing missing extras: {', '.join(extras_to_install)}[/]",
144
144
  )
145
- return install_extras_programmatic(extras_to_install)
145
+ return install_extras_programmatic(extras_to_install, quiet=True)
146
146
 
147
147
 
148
148
  def _check_and_install_extras(extras: tuple[str, ...]) -> list[str]:
@@ -6,13 +6,11 @@ import json
6
6
  import logging
7
7
  from typing import TYPE_CHECKING, Any, Protocol, runtime_checkable
8
8
 
9
- import httpx
10
- from fastapi import HTTPException, Request, Response
11
- from fastapi.responses import StreamingResponse
12
-
13
9
  if TYPE_CHECKING:
14
10
  from collections.abc import AsyncGenerator, Iterable
15
11
 
12
+ from fastapi import Request, Response
13
+
16
14
  LOGGER = logging.getLogger(__name__)
17
15
 
18
16
 
@@ -33,6 +31,9 @@ async def proxy_request_to_upstream(
33
31
  api_key: str | None = None,
34
32
  ) -> Response:
35
33
  """Forward a raw HTTP request to an upstream OpenAI-compatible provider."""
34
+ import httpx # noqa: PLC0415
35
+ from fastapi import Response # noqa: PLC0415
36
+
36
37
  auth_header = request.headers.get("Authorization")
37
38
  headers = {}
38
39
  if auth_header:
@@ -82,6 +83,10 @@ async def forward_chat_request(
82
83
  exclude_fields: Iterable[str] = (),
83
84
  ) -> Any:
84
85
  """Forward a chat request to a backend LLM."""
86
+ import httpx # noqa: PLC0415
87
+ from fastapi import HTTPException # noqa: PLC0415
88
+ from fastapi.responses import StreamingResponse # noqa: PLC0415
89
+
85
90
  forward_payload = request.model_dump(exclude=set(exclude_fields))
86
91
  headers = {"Authorization": f"Bearer {api_key}"} if api_key else None
87
92
 
agent_cli/core/process.py CHANGED
@@ -10,8 +10,6 @@ from contextlib import contextmanager
10
10
  from pathlib import Path
11
11
  from typing import TYPE_CHECKING
12
12
 
13
- import setproctitle
14
-
15
13
  if TYPE_CHECKING:
16
14
  from collections.abc import Generator
17
15
 
@@ -36,6 +34,8 @@ def set_process_title(process_name: str) -> None:
36
34
  process_name: The name of the process (e.g., 'transcribe', 'chat').
37
35
 
38
36
  """
37
+ import setproctitle # noqa: PLC0415
38
+
39
39
  global _original_proctitle
40
40
 
41
41
  # Capture the original command line only once, before any modification
@@ -4,15 +4,13 @@ from __future__ import annotations
4
4
 
5
5
  import logging
6
6
 
7
- from huggingface_hub import hf_hub_download
8
- from onnxruntime import InferenceSession
9
- from transformers import AutoTokenizer
10
-
11
7
  LOGGER = logging.getLogger(__name__)
12
8
 
13
9
 
14
10
  def _download_onnx_model(model_name: str, onnx_filename: str) -> str:
15
11
  """Download the ONNX model, favoring the common `onnx/` folder layout."""
12
+ from huggingface_hub import hf_hub_download # noqa: PLC0415
13
+
16
14
  if "/" in onnx_filename:
17
15
  return hf_hub_download(repo_id=model_name, filename=onnx_filename)
18
16
 
@@ -45,6 +43,9 @@ class OnnxCrossEncoder:
45
43
  onnx_filename: str = "model.onnx",
46
44
  ) -> None:
47
45
  """Initialize the ONNX CrossEncoder."""
46
+ from onnxruntime import InferenceSession # noqa: PLC0415
47
+ from transformers import AutoTokenizer # noqa: PLC0415
48
+
48
49
  self.model_name = model_name
49
50
 
50
51
  # Download model if needed
agent_cli/core/utils.py CHANGED
@@ -18,7 +18,6 @@ from contextlib import (
18
18
  )
19
19
  from typing import TYPE_CHECKING, Any
20
20
 
21
- import pyperclip
22
21
  from rich.console import Console
23
22
  from rich.live import Live
24
23
  from rich.panel import Panel
@@ -40,6 +39,7 @@ if TYPE_CHECKING:
40
39
  from pathlib import Path
41
40
 
42
41
  console = Console()
42
+ err_console = Console(stderr=True)
43
43
 
44
44
 
45
45
  def enable_json_mode() -> None:
@@ -233,6 +233,8 @@ def print_device_index(input_device_index: int | None, input_device_name: str |
233
233
 
234
234
  def get_clipboard_text(*, quiet: bool = False) -> str | None:
235
235
  """Get text from clipboard, with an optional status message."""
236
+ import pyperclip # noqa: PLC0415
237
+
236
238
  text = pyperclip.paste()
237
239
  if not text:
238
240
  if not quiet:
agent_cli/core/vad.py CHANGED
@@ -12,7 +12,6 @@ from agent_cli import constants
12
12
  try:
13
13
  import numpy as np
14
14
  import torch
15
- from silero_vad.utils_vad import OnnxWrapper
16
15
  except ImportError as e:
17
16
  msg = (
18
17
  "silero-vad is required for the transcribe-daemon command. "
@@ -57,6 +56,8 @@ class VoiceActivityDetector:
57
56
  msg = f"Sample rate must be 8000 or 16000, got {sample_rate}"
58
57
  raise ValueError(msg)
59
58
 
59
+ from silero_vad.utils_vad import OnnxWrapper # noqa: PLC0415
60
+
60
61
  self.sample_rate = sample_rate
61
62
  self.threshold = threshold
62
63
  self.silence_threshold_ms = silence_threshold_ms
agent_cli/core/watch.py CHANGED
@@ -3,13 +3,13 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import asyncio
6
- from collections.abc import Callable
7
6
  from pathlib import Path
7
+ from typing import TYPE_CHECKING
8
8
 
9
- from watchfiles import Change, awatch
9
+ if TYPE_CHECKING:
10
+ from collections.abc import Callable
10
11
 
11
- ChangeHandler = Callable[[Change, Path], None]
12
- PathFilter = Callable[[Path, Path], bool]
12
+ from watchfiles import Change
13
13
 
14
14
 
15
15
  def _default_skip_hidden(path: Path, root: Path) -> bool:
@@ -20,10 +20,10 @@ def _default_skip_hidden(path: Path, root: Path) -> bool:
20
20
 
21
21
  async def watch_directory(
22
22
  root: Path,
23
- handler: ChangeHandler,
23
+ handler: Callable[[Change, Path], None],
24
24
  *,
25
25
  skip_hidden: bool = True,
26
- ignore_filter: PathFilter | None = None,
26
+ ignore_filter: Callable[[Path, Path], bool] | None = None,
27
27
  use_executor: bool = True,
28
28
  ) -> None:
29
29
  """Watch a directory for file changes and invoke handler(change, path).
@@ -38,6 +38,8 @@ async def watch_directory(
38
38
  use_executor: If True, run handler in a thread pool executor.
39
39
 
40
40
  """
41
+ from watchfiles import awatch # noqa: PLC0415
42
+
41
43
  loop = asyncio.get_running_loop()
42
44
 
43
45
  # Determine which filter to use
agent_cli/dev/cli.py CHANGED
@@ -13,7 +13,6 @@ from pathlib import Path
13
13
  from typing import TYPE_CHECKING, Annotated, NoReturn
14
14
 
15
15
  import typer
16
- from rich.console import Console
17
16
  from rich.panel import Panel
18
17
  from rich.table import Table
19
18
 
@@ -21,6 +20,7 @@ from agent_cli.cli import app as main_app
21
20
  from agent_cli.cli import set_config_defaults
22
21
  from agent_cli.config import load_config
23
22
  from agent_cli.core.process import set_process_title
23
+ from agent_cli.core.utils import console, err_console
24
24
 
25
25
  # Word lists for generating random branch names (like Docker container names)
26
26
  _ADJECTIVES = [
@@ -112,9 +112,6 @@ if TYPE_CHECKING:
112
112
  from .coding_agents.base import CodingAgent
113
113
  from .editors.base import Editor
114
114
 
115
- console = Console()
116
- err_console = Console(stderr=True)
117
-
118
115
  app = typer.Typer(
119
116
  name="dev",
120
117
  help="Parallel development environment manager using git worktrees.",
@@ -478,7 +475,7 @@ def _launch_agent(
478
475
 
479
476
 
480
477
  @app.command("new")
481
- def new( # noqa: PLR0912, PLR0915
478
+ def new( # noqa: C901, PLR0912, PLR0915
482
479
  branch: Annotated[
483
480
  str | None,
484
481
  typer.Argument(help="Branch name (auto-generated if not provided)"),
@@ -565,6 +562,10 @@ def new( # noqa: PLR0912, PLR0915
565
562
  if prompt_file is not None:
566
563
  prompt = prompt_file.read_text().strip()
567
564
 
565
+ # If a prompt is provided, automatically enable agent mode
566
+ if prompt:
567
+ agent = True
568
+
568
569
  repo_root = _ensure_git_repo()
569
570
 
570
571
  # Generate branch name if not provided
@@ -5,6 +5,7 @@ from __future__ import annotations
5
5
  import os
6
6
  import shutil
7
7
  from abc import ABC
8
+ from pathlib import PurePath
8
9
  from typing import TYPE_CHECKING
9
10
 
10
11
  if TYPE_CHECKING:
@@ -131,8 +132,6 @@ def _get_parent_process_names() -> list[str]:
131
132
  - CLI tools that set process.title (like Claude) show their name directly
132
133
  """
133
134
  try:
134
- from pathlib import PurePath # noqa: PLC0415
135
-
136
135
  import psutil # noqa: PLC0415
137
136
 
138
137
  process = psutil.Process(os.getpid())
@@ -34,14 +34,19 @@ Do NOT spawn when:
34
34
 
35
35
  ## Core command
36
36
 
37
- For short prompts:
37
+ For new features (starts from origin/main):
38
38
  ```bash
39
- agent-cli dev new <branch-name> --agent --prompt "Fix the login bug"
39
+ agent-cli dev new <branch-name> --agent --prompt "Implement the new feature..."
40
+ ```
41
+
42
+ For work on current branch (review, test, fix) - use `--from HEAD`:
43
+ ```bash
44
+ agent-cli dev new <branch-name> --from HEAD --agent --prompt "Review/test/fix..."
40
45
  ```
41
46
 
42
47
  For longer prompts (recommended for multi-line or complex instructions):
43
48
  ```bash
44
- agent-cli dev new <branch-name> --agent --prompt-file path/to/prompt.md
49
+ agent-cli dev new <branch-name> --from HEAD --agent --prompt-file path/to/prompt.md
45
50
  ```
46
51
 
47
52
  This creates:
@@ -129,7 +134,7 @@ Each agent works independently in its own branch. Results can be reviewed and me
129
134
  | `--agent` / `-a` | Start AI coding agent after creation |
130
135
  | `--prompt` / `-p` | Initial prompt for the agent (short prompts only) |
131
136
  | `--prompt-file` / `-P` | Read prompt from file (recommended for longer prompts) |
132
- | `--from` / `-f` | Base branch (default: origin/main) |
137
+ | `--from` / `-f` | Base ref (default: origin/main). **Use `--from HEAD` when reviewing/testing current branch!** |
133
138
  | `--with-agent` | Specific agent: claude, aider, codex, gemini |
134
139
  | `--agent-args` | Extra arguments for the agent |
135
140
 
@@ -20,7 +20,68 @@ Each prompt for a spawned agent should follow this structure:
20
20
  5. **Focused scope** - Keep solutions minimal, implement only what's requested
21
21
  6. **Structured report** - Write conclusions to `.claude/REPORT.md`
22
22
 
23
- ## Scenario 1: Multi-feature implementation
23
+ ## Scenario 1: Code review of current branch
24
+
25
+ **User request**: "Review the code on this branch" or "Spawn an agent to review my changes"
26
+
27
+ **CRITICAL**: Use `--from HEAD` (or the branch name) so the review agent has access to the changes!
28
+
29
+ ```bash
30
+ # Review the current branch - MUST use --from HEAD
31
+ agent-cli dev new review-changes --from HEAD --agent --prompt "Review the code changes on this branch.
32
+
33
+ <workflow>
34
+ - Run git diff origin/main...HEAD to identify all changes
35
+ - Read changed files in parallel to understand context
36
+ - Check CLAUDE.md for project-specific guidelines
37
+ - Test changes with real services if applicable
38
+ </workflow>
39
+
40
+ <code_exploration>
41
+ - Use git diff origin/main...HEAD to see the full diff
42
+ - Read each changed file completely before judging
43
+ - Look at surrounding code to understand patterns
44
+ - Check existing tests to understand expected behavior
45
+ </code_exploration>
46
+
47
+ <context>
48
+ Code review catches issues before merge. Focus on real problems - not style nitpicks. Apply these criteria:
49
+ - Code cleanliness: Is the implementation clean and well-structured?
50
+ - DRY principle: Does it avoid duplication?
51
+ - Code reuse: Are there parts that should be reused from other places?
52
+ - Organization: Is everything in the right place?
53
+ - Consistency: Is it in the same style as other parts of the codebase?
54
+ - Simplicity: Is it over-engineered? Remember KISS and YAGNI. No dead code paths, no defensive programming.
55
+ - No pointless wrappers: Functions that just call another function should be inlined.
56
+ - User experience: Does it provide a good user experience?
57
+ - Tests: Are tests meaningful or just trivial coverage?
58
+ - Live tests: Test changes with real services if applicable.
59
+ - Rules: Does the code follow CLAUDE.md guidelines?
60
+ </context>
61
+
62
+ <scope>
63
+ Review only - identify issues but do not fix them. Write findings to report.
64
+ </scope>
65
+
66
+ <report>
67
+ Write your review to .claude/REPORT.md:
68
+
69
+ ## Summary
70
+ [Overall assessment of the changes]
71
+
72
+ ## Issues Found
73
+ | Severity | File:Line | Issue | Suggestion |
74
+ |----------|-----------|-------|------------|
75
+ | Critical/High/Medium/Low | path:123 | description | fix |
76
+
77
+ ## Positive Observations
78
+ [What's well done]
79
+ </report>"
80
+ ```
81
+
82
+ **Common mistake**: Forgetting `--from HEAD` means the agent starts from `origin/main` and won't see any of the branch changes!
83
+
84
+ ## Scenario 2: Multi-feature implementation
24
85
 
25
86
  **User request**: "Implement user auth, payment processing, and email notifications"
26
87
 
@@ -169,7 +230,7 @@ After verifying tests pass, write to .claude/REPORT.md with summary, files chang
169
230
  </report>"
170
231
  ```
171
232
 
172
- ## Scenario 2: Test-driven development
233
+ ## Scenario 3: Test-driven development
173
234
 
174
235
  **User request**: "Add a caching layer with comprehensive tests"
175
236
 
@@ -289,7 +350,7 @@ After ALL tests pass, write to .claude/REPORT.md:
289
350
  </report>"
290
351
  ```
291
352
 
292
- ## Scenario 3: Large refactoring by module
353
+ ## Scenario 4: Large refactoring by module
293
354
 
294
355
  **User request**: "Refactor the API to use consistent error handling"
295
356
 
@@ -357,7 +418,7 @@ After tests pass and linting is clean, write to .claude/REPORT.md:
357
418
  </report>"
358
419
  ```
359
420
 
360
- ## Scenario 4: Documentation and implementation in parallel
421
+ ## Scenario 5: Documentation and implementation in parallel
361
422
 
362
423
  **User request**: "Add a plugin system with documentation"
363
424
 
@@ -60,13 +60,15 @@ def _get_current_uv_tool_extras() -> list[str]:
60
60
  return []
61
61
 
62
62
 
63
- def _install_via_uv_tool(extras: list[str]) -> bool:
63
+ def _install_via_uv_tool(extras: list[str], *, quiet: bool = False) -> bool:
64
64
  """Reinstall agent-cli via uv tool with the specified extras."""
65
65
  current_version = get_version("agent-cli").split("+")[0] # Strip local version
66
66
  extras_str = ",".join(extras)
67
67
  package_spec = f"agent-cli[{extras_str}]=={current_version}"
68
68
  python_version = f"{sys.version_info.major}.{sys.version_info.minor}"
69
69
  cmd = ["uv", "tool", "install", package_spec, "--force", "--python", python_version]
70
+ if quiet:
71
+ cmd.append("-q")
70
72
  console.print(f"Running: [cyan]{' '.join(cmd)}[/]")
71
73
  result = subprocess.run(cmd, check=False)
72
74
  return result.returncode == 0
@@ -93,7 +95,7 @@ def _install_extras_impl(extras: list[str], *, quiet: bool = False) -> bool:
93
95
  if _is_uv_tool_install():
94
96
  current_extras = _get_current_uv_tool_extras()
95
97
  new_extras = sorted(set(current_extras) | set(extras))
96
- return _install_via_uv_tool(new_extras)
98
+ return _install_via_uv_tool(new_extras, quiet=quiet)
97
99
 
98
100
  cmd = _install_cmd()
99
101
  for extra in extras:
@@ -120,7 +122,7 @@ def install_extras_programmatic(extras: list[str], *, quiet: bool = False) -> bo
120
122
  return bool(valid) and _install_extras_impl(valid, quiet=quiet)
121
123
 
122
124
 
123
- @app.command("install-extras", rich_help_panel="Installation")
125
+ @app.command("install-extras", rich_help_panel="Installation", no_args_is_help=True)
124
126
  def install_extras(
125
127
  extras: Annotated[list[str] | None, typer.Argument(help="Extras to install")] = None,
126
128
  list_extras: Annotated[
@@ -135,10 +137,10 @@ def install_extras(
135
137
  """Install optional extras (rag, memory, vad, etc.) with pinned versions.
136
138
 
137
139
  Examples:
138
- agent-cli install-extras rag # Install RAG dependencies
139
- agent-cli install-extras memory vad # Install multiple extras
140
- agent-cli install-extras --list # Show available extras
141
- agent-cli install-extras --all # Install all extras
140
+ - `agent-cli install-extras rag` # Install RAG dependencies
141
+ - `agent-cli install-extras memory vad` # Install multiple extras
142
+ - `agent-cli install-extras --list` # Show available extras
143
+ - `agent-cli install-extras --all` # Install all extras
142
144
 
143
145
  """
144
146
  available = _available_extras()
@@ -9,7 +9,6 @@ from pathlib import Path
9
9
  from typing import TYPE_CHECKING
10
10
  from uuid import uuid4
11
11
 
12
- import yaml
13
12
  from pydantic import ValidationError
14
13
 
15
14
  from agent_cli.core.utils import atomic_write_text
@@ -218,6 +217,8 @@ def load_snapshot(snapshot_path: Path) -> dict[str, MemoryFileRecord]:
218
217
 
219
218
  def _render_front_matter(doc_id: str, metadata: MemoryMetadata) -> str:
220
219
  """Return YAML front matter string."""
220
+ import yaml # noqa: PLC0415
221
+
221
222
  meta_dict = metadata.model_dump(exclude_none=True)
222
223
  meta_dict = {"id": doc_id, **meta_dict}
223
224
  yaml_block = yaml.safe_dump(meta_dict, sort_keys=False)
@@ -233,6 +234,8 @@ def _split_front_matter(text: str) -> tuple[dict | None, str]:
233
234
  return None, text
234
235
  yaml_part = text[3:end]
235
236
  try:
237
+ import yaml # noqa: PLC0415
238
+
236
239
  meta = yaml.safe_load(yaml_part) or {}
237
240
  except Exception:
238
241
  return None, text
@@ -6,8 +6,6 @@ import logging
6
6
  from dataclasses import dataclass, field
7
7
  from typing import TYPE_CHECKING
8
8
 
9
- from watchfiles import Change
10
-
11
9
  from agent_cli.core.watch import watch_directory
12
10
  from agent_cli.memory._files import (
13
11
  _DELETED_DIRNAME,
@@ -24,6 +22,7 @@ if TYPE_CHECKING:
24
22
  from pathlib import Path
25
23
 
26
24
  from chromadb import Collection
25
+ from watchfiles import Change
27
26
 
28
27
  LOGGER = logging.getLogger(__name__)
29
28
 
@@ -108,6 +107,8 @@ async def watch_memory_store(collection: Collection, root: Path, *, index: Memor
108
107
 
109
108
 
110
109
  def _handle_change(change: Change, path: Path, collection: Collection, index: MemoryIndex) -> None:
110
+ from watchfiles import Change # noqa: PLC0415
111
+
111
112
  if path.suffix == ".tmp":
112
113
  return
113
114
 
@@ -9,8 +9,6 @@ from time import perf_counter
9
9
  from typing import TYPE_CHECKING
10
10
  from uuid import uuid4
11
11
 
12
- import httpx
13
-
14
12
  from agent_cli.memory._git import commit_changes
15
13
  from agent_cli.memory._persistence import delete_memory_files, persist_entries, persist_summary
16
14
  from agent_cli.memory._prompt import (
@@ -58,6 +56,7 @@ async def extract_salient_facts(
58
56
  if not user_message and not assistant_message:
59
57
  return []
60
58
 
59
+ import httpx # noqa: PLC0415
61
60
  from pydantic_ai import Agent # noqa: PLC0415
62
61
  from pydantic_ai.exceptions import AgentRunError, UnexpectedModelBehavior # noqa: PLC0415
63
62
  from pydantic_ai.models.openai import OpenAIChatModel # noqa: PLC0415
@@ -174,16 +173,18 @@ async def reconcile_facts(
174
173
  if f.strip()
175
174
  ]
176
175
  return entries, [], {}
177
- id_map: dict[int, str] = {idx: mem.id for idx, mem in enumerate(existing)}
178
- existing_json = [{"id": idx, "text": mem.content} for idx, mem in enumerate(existing)]
179
- existing_ids = set(id_map.keys())
180
176
 
177
+ import httpx # noqa: PLC0415
181
178
  from pydantic_ai import Agent, ModelRetry, PromptedOutput # noqa: PLC0415
182
179
  from pydantic_ai.exceptions import AgentRunError, UnexpectedModelBehavior # noqa: PLC0415
183
180
  from pydantic_ai.models.openai import OpenAIChatModel # noqa: PLC0415
184
181
  from pydantic_ai.providers.openai import OpenAIProvider # noqa: PLC0415
185
182
  from pydantic_ai.settings import ModelSettings # noqa: PLC0415
186
183
 
184
+ id_map: dict[int, str] = {idx: mem.id for idx, mem in enumerate(existing)}
185
+ existing_json = [{"id": idx, "text": mem.content} for idx, mem in enumerate(existing)]
186
+ existing_ids = set(id_map.keys())
187
+
187
188
  provider = OpenAIProvider(api_key=api_key or "dummy", base_url=openai_base_url)
188
189
  model_cfg = OpenAIChatModel(
189
190
  model_name=model,
@@ -4,8 +4,6 @@ from __future__ import annotations
4
4
 
5
5
  from typing import TYPE_CHECKING, Any
6
6
 
7
- import httpx
8
-
9
7
  from agent_cli.core.sse import extract_content_from_chunk, parse_chunk
10
8
 
11
9
  if TYPE_CHECKING:
@@ -20,6 +18,8 @@ async def stream_chat_sse(
20
18
  request_timeout: float = 120.0,
21
19
  ) -> AsyncGenerator[str, None]:
22
20
  """Stream Server-Sent Events from an OpenAI-compatible chat completion endpoint."""
21
+ import httpx # noqa: PLC0415
22
+
23
23
  url = f"{openai_base_url.rstrip('/')}/chat/completions"
24
24
  async with (
25
25
  httpx.AsyncClient(timeout=request_timeout) as client,
agent_cli/rag/_indexer.py CHANGED
@@ -5,8 +5,6 @@ from __future__ import annotations
5
5
  import logging
6
6
  from typing import TYPE_CHECKING
7
7
 
8
- from watchfiles import Change
9
-
10
8
  from agent_cli.core.watch import watch_directory
11
9
  from agent_cli.rag._indexing import index_file, remove_file
12
10
  from agent_cli.rag._utils import should_ignore_path
@@ -15,6 +13,7 @@ if TYPE_CHECKING:
15
13
  from pathlib import Path
16
14
 
17
15
  from chromadb import Collection
16
+ from watchfiles import Change
18
17
 
19
18
  LOGGER = logging.getLogger(__name__)
20
19
 
@@ -50,6 +49,8 @@ def _handle_change(
50
49
  file_hashes: dict[str, str],
51
50
  file_mtimes: dict[str, float],
52
51
  ) -> None:
52
+ from watchfiles import Change # noqa: PLC0415
53
+
53
54
  try:
54
55
  if change == Change.deleted:
55
56
  LOGGER.info("[deleted] Removing from index: %s", file_path.name)
agent_cli/rag/api.py CHANGED
@@ -24,6 +24,7 @@ from agent_cli.rag.models import ChatRequest # noqa: TC001
24
24
  if TYPE_CHECKING:
25
25
  from pathlib import Path
26
26
 
27
+
27
28
  LOGGER = logging.getLogger(__name__)
28
29
 
29
30
 
agent_cli/server/cli.py CHANGED
@@ -9,15 +9,13 @@ from pathlib import Path # noqa: TC003 - Typer needs this at runtime
9
9
  from typing import Annotated
10
10
 
11
11
  import typer
12
- from rich.console import Console
13
12
 
14
13
  from agent_cli.cli import app as main_app
15
14
  from agent_cli.core.deps import requires_extras
16
15
  from agent_cli.core.process import set_process_title
16
+ from agent_cli.core.utils import console, err_console
17
17
  from agent_cli.server.common import setup_rich_logging
18
18
 
19
- console = Console()
20
- err_console = Console(stderr=True)
21
19
  logger = logging.getLogger(__name__)
22
20
 
23
21
  # Check for optional dependencies at call time (not module load time)
@@ -295,7 +293,7 @@ def whisper_cmd( # noqa: PLR0912, PLR0915
295
293
 
296
294
  """
297
295
  # Setup Rich logging for consistent output
298
- setup_rich_logging(log_level, console=console)
296
+ setup_rich_logging(log_level)
299
297
 
300
298
  valid_backends = ("auto", "faster-whisper", "mlx")
301
299
  if backend not in valid_backends:
@@ -614,7 +612,7 @@ def tts_cmd( # noqa: PLR0915
614
612
 
615
613
  """
616
614
  # Setup Rich logging for consistent output
617
- setup_rich_logging(log_level, console=console)
615
+ setup_rich_logging(log_level)
618
616
 
619
617
  valid_backends = ("auto", "piper", "kokoro")
620
618
  if backend not in valid_backends:
@@ -9,10 +9,10 @@ import logging
9
9
  from contextlib import asynccontextmanager
10
10
  from typing import TYPE_CHECKING, Any, Protocol
11
11
 
12
- from rich.console import Console
13
12
  from rich.logging import RichHandler
14
13
 
15
14
  from agent_cli import constants
15
+ from agent_cli.core.utils import console
16
16
 
17
17
  if TYPE_CHECKING:
18
18
  import wave
@@ -128,7 +128,7 @@ def configure_app(app: FastAPI) -> None:
128
128
  return await log_requests_middleware(request, call_next)
129
129
 
130
130
 
131
- def setup_rich_logging(log_level: str = "info", *, console: Console | None = None) -> None:
131
+ def setup_rich_logging(log_level: str = "info") -> None:
132
132
  """Configure logging to use Rich for consistent, pretty output.
133
133
 
134
134
  This configures:
@@ -141,11 +141,10 @@ def setup_rich_logging(log_level: str = "info", *, console: Console | None = Non
141
141
 
142
142
  """
143
143
  level = getattr(logging, log_level.upper(), logging.INFO)
144
- rich_console = console or Console()
145
144
 
146
145
  # Create Rich handler with clean format
147
146
  handler = RichHandler(
148
- console=rich_console,
147
+ console=console,
149
148
  show_time=True,
150
149
  show_level=True,
151
150
  show_path=False, # Don't show file:line - too verbose
@@ -6,6 +6,7 @@ import asyncio
6
6
  import logging
7
7
  import tempfile
8
8
  from concurrent.futures import ProcessPoolExecutor
9
+ from dataclasses import dataclass
9
10
  from multiprocessing import get_context
10
11
  from pathlib import Path
11
12
  from typing import Any, Literal
@@ -19,6 +20,24 @@ from agent_cli.server.whisper.backends.base import (
19
20
  logger = logging.getLogger(__name__)
20
21
 
21
22
 
23
+ # --- Subprocess state (only used within subprocess worker) ---
24
+ # This state persists across function calls within the subprocess because:
25
+ # 1. Model loading is expensive and must be reused across transcription calls
26
+ # 2. CTranslate2 models cannot be pickled/passed through IPC queues
27
+ # 3. The subprocess is long-lived (ProcessPoolExecutor reuses workers)
28
+
29
+
30
+ @dataclass
31
+ class _SubprocessState:
32
+ """Container for subprocess-local state. Not shared with main process."""
33
+
34
+ model: Any = None
35
+ device: str | None = None
36
+
37
+
38
+ _state = _SubprocessState()
39
+
40
+
22
41
  # --- Subprocess worker functions (run in isolated process) ---
23
42
 
24
43
 
@@ -40,28 +59,22 @@ def _load_model_in_subprocess(
40
59
  cpu_threads=cpu_threads,
41
60
  download_root=download_root,
42
61
  )
43
- return str(model.model.device)
62
+
63
+ # Store in subprocess state for reuse across transcription calls
64
+ _state.model = model
65
+ _state.device = str(model.model.device)
66
+
67
+ return _state.device
44
68
 
45
69
 
46
70
  def _transcribe_in_subprocess(
47
- model_name: str,
48
- device: str,
49
- compute_type: str,
50
- cpu_threads: int,
51
- download_root: str | None,
52
71
  audio_bytes: bytes,
53
72
  kwargs: dict[str, Any],
54
73
  ) -> dict[str, Any]:
55
- """Run transcription in subprocess. Model is loaded fresh each call."""
56
- from faster_whisper import WhisperModel # noqa: PLC0415
57
-
58
- model = WhisperModel(
59
- model_name,
60
- device=device,
61
- compute_type=compute_type,
62
- cpu_threads=cpu_threads,
63
- download_root=download_root,
64
- )
74
+ """Run transcription in subprocess. Reuses model from _state."""
75
+ if _state.model is None:
76
+ msg = "Model not loaded in subprocess. Call _load_model_in_subprocess first."
77
+ raise RuntimeError(msg)
65
78
 
66
79
  # Write audio to temp file - faster-whisper needs a file path
67
80
  with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as tmp:
@@ -69,7 +82,7 @@ def _transcribe_in_subprocess(
69
82
  tmp_path = tmp.name
70
83
 
71
84
  try:
72
- segments, info = model.transcribe(tmp_path, **kwargs)
85
+ segments, info = _state.model.transcribe(tmp_path, **kwargs)
73
86
  segment_list = list(segments) # Consume lazy generator
74
87
  finally:
75
88
  Path(tmp_path).unlink(missing_ok=True)
@@ -195,16 +208,10 @@ class FasterWhisperBackend:
195
208
  "word_timestamps": word_timestamps,
196
209
  }
197
210
 
198
- download_root = str(self._config.cache_dir) if self._config.cache_dir else None
199
211
  loop = asyncio.get_running_loop()
200
212
  result = await loop.run_in_executor(
201
213
  self._executor,
202
214
  _transcribe_in_subprocess,
203
- self._config.model_name,
204
- self._config.device,
205
- self._config.compute_type,
206
- self._config.cpu_threads,
207
- download_root,
208
215
  audio,
209
216
  kwargs,
210
217
  )
agent_cli/services/llm.py CHANGED
@@ -6,7 +6,6 @@ import sys
6
6
  import time
7
7
  from typing import TYPE_CHECKING
8
8
 
9
- import pyperclip
10
9
  from rich.live import Live
11
10
 
12
11
  from agent_cli.core.utils import console, live_timer, print_error_message, print_output_panel
@@ -156,6 +155,8 @@ async def get_llm_response(
156
155
  result_text = result.output
157
156
 
158
157
  if clipboard:
158
+ import pyperclip # noqa: PLC0415
159
+
159
160
  pyperclip.copy(result_text)
160
161
  logger.info("Copied result to clipboard.")
161
162
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agent-cli
3
- Version: 0.68.5
3
+ Version: 0.69.0
4
4
  Summary: A suite of AI-powered command-line tools for text correction, audio transcription, and voice assistance.
5
5
  Project-URL: Homepage, https://github.com/basnijholt/agent-cli
6
6
  Author-email: Bas Nijholt <bas@nijho.lt>
@@ -21,9 +21,10 @@ Requires-Dist: sounddevice>=0.4.6; extra == 'audio'
21
21
  Requires-Dist: wyoming>=1.5.2; extra == 'audio'
22
22
  Provides-Extra: dev
23
23
  Requires-Dist: markdown-code-runner>=2.7.0; extra == 'dev'
24
+ Requires-Dist: markdown-gfm-admonition; extra == 'dev'
24
25
  Requires-Dist: notebook; extra == 'dev'
26
+ Requires-Dist: pre-commit-uv>=4.1.4; extra == 'dev'
25
27
  Requires-Dist: pre-commit>=3.0.0; extra == 'dev'
26
- Requires-Dist: pydantic-ai-slim[openai]; extra == 'dev'
27
28
  Requires-Dist: pylint>=3.0.0; extra == 'dev'
28
29
  Requires-Dist: pytest-asyncio>=0.20.0; extra == 'dev'
29
30
  Requires-Dist: pytest-cov>=4.0.0; extra == 'dev'
@@ -32,6 +33,7 @@ Requires-Dist: pytest-timeout; extra == 'dev'
32
33
  Requires-Dist: pytest>=7.0.0; extra == 'dev'
33
34
  Requires-Dist: ruff; extra == 'dev'
34
35
  Requires-Dist: versioningit; extra == 'dev'
36
+ Requires-Dist: zensical; extra == 'dev'
35
37
  Provides-Extra: faster-whisper
36
38
  Requires-Dist: fastapi[standard]; extra == 'faster-whisper'
37
39
  Requires-Dist: faster-whisper>=1.0.0; extra == 'faster-whisper'
@@ -71,7 +73,6 @@ Requires-Dist: fastapi[standard]; extra == 'server'
71
73
  Provides-Extra: speed
72
74
  Requires-Dist: audiostretchy>=1.3.0; extra == 'speed'
73
75
  Provides-Extra: test
74
- Requires-Dist: pydantic-ai-slim[openai]; extra == 'test'
75
76
  Requires-Dist: pytest-asyncio>=0.20.0; extra == 'test'
76
77
  Requires-Dist: pytest-cov>=4.0.0; extra == 'test'
77
78
  Requires-Dist: pytest-mock; extra == 'test'
@@ -497,9 +498,12 @@ agent-cli install-extras rag memory vad
497
498
 
498
499
  Install optional extras (rag, memory, vad, etc.) with pinned versions.
499
500
 
500
- Examples: agent-cli install-extras rag # Install RAG dependencies agent-cli
501
- install-extras memory vad # Install multiple extras agent-cli install-extras --list
502
- # Show available extras agent-cli install-extras --all # Install all extras
501
+ Examples:
502
+
503
+ agent-cli install-extras rag # Install RAG dependencies
504
+ • agent-cli install-extras memory vad # Install multiple extras
505
+ • agent-cli install-extras --list # Show available extras
506
+ • agent-cli install-extras --all # Install all extras
503
507
 
504
508
  ╭─ Arguments ────────────────────────────────────────────────────────────────────────────╮
505
509
  │ extras [EXTRAS]... Extras to install │
@@ -3,9 +3,9 @@ agent_cli/__main__.py,sha256=2wx_SxA8KRdejM-hBFLN8JTR2rIgtwnDH03MPAbJH5U,106
3
3
  agent_cli/_extras.json,sha256=PCeXMr8zX04n_fcLrticn4lRpisZTfBkT2oqdImVzEY,708
4
4
  agent_cli/_tools.py,sha256=u9Ww-k-sbwFnMTW8sreFGd71nJP6o5hKcM0Zd_D9GZk,12136
5
5
  agent_cli/api.py,sha256=FQ_HATc7DaedbEFQ275Z18wV90tkDByD_9x_K0wdSLQ,456
6
- agent_cli/cli.py,sha256=zCk7sVVZstTU3GHFXAavcXd8Kactav4x0PyVY_rX4SI,2739
6
+ agent_cli/cli.py,sha256=W1hrmWjrVeXH94pM4rRaNxGI4uJHqE5ST_q7SEeSeMg,3275
7
7
  agent_cli/config.py,sha256=dgwDV6chrQzGnVZIJ0OOg26jFKLCGIInC4Q9oXcj3rM,15413
8
- agent_cli/config_cmd.py,sha256=Fb-KBjtveft3x3_xjqlqobBwZqtI6Umrd8YzlwTAnZ4,9554
8
+ agent_cli/config_cmd.py,sha256=CiHk1WxtvT21QeMuklTTMCmAdNwjeYENO_w_Qbiys54,9579
9
9
  agent_cli/constants.py,sha256=-Q17N6qKIGqPDsu3FxpIKP33G0Cs0RUJlHwYNHxVxek,843
10
10
  agent_cli/docs_gen.py,sha256=j6mBHwoPcQzMdgIWi_bB2A6yOyhvmW_cntRfwUg_8p8,13374
11
11
  agent_cli/example-config.toml,sha256=xd9BXeOqdYx4xFJt58VBs2I49ESy6dF4-mWF_g8sM9o,7552
@@ -26,37 +26,37 @@ agent_cli/_requirements/vad.txt,sha256=HN7OB8cu5cWuVPhoKWye73I9lAWPE-ijeXeVSshCs
26
26
  agent_cli/agents/__init__.py,sha256=c1rnncDW5pBvP6BiLzFVpLWDNZzFRaUA7-a97avFVAs,321
27
27
  agent_cli/agents/_voice_agent_common.py,sha256=PUAztW84Xf9U7d0C_K5cL7I8OANIE1H6M8dFD_cRqps,4360
28
28
  agent_cli/agents/assistant.py,sha256=dXExb-UrWdMiEcI1c6bXvMhii1iGg49Zw4On6-inQUE,14046
29
- agent_cli/agents/autocorrect.py,sha256=kIkQNo6ldRre8y1KYMqBxlDLyyoc2l6Wa632CoYkZOQ,8964
29
+ agent_cli/agents/autocorrect.py,sha256=Voxv43g-IbOhCGKAyWHa4WoQ3-dJEe076XLEmEwg9eg,8990
30
30
  agent_cli/agents/chat.py,sha256=7qwDBXFQvhqtlRA4IFXJS24hZ9PN6lb0g9boxwHUS1c,17257
31
- agent_cli/agents/rag_proxy.py,sha256=GNhBoFp0C5kIGxfI6382SBlAQEgIWUahBHuZHS32DF4,4699
31
+ agent_cli/agents/rag_proxy.py,sha256=47maSXkebpRzt4acvsbIN_sPfIBOHcpBED_PHOlgiSs,4434
32
32
  agent_cli/agents/speak.py,sha256=xG2ppthpdmpzsYkNeUynwDmWe8JTpJYhdKs3L1jR6eY,7101
33
- agent_cli/agents/transcribe.py,sha256=0A1lt0kiDpzp8tfG11vxwzDzoXcDi7-nVmMbKbETYcU,24573
33
+ agent_cli/agents/transcribe.py,sha256=3N9cA9ba-3acH4-UPEw-LYIv-HQhzb4bE0efxZzSwqQ,24654
34
34
  agent_cli/agents/transcribe_daemon.py,sha256=FPqcAfGNK_PyxfgQw1b-xph4JrFeCvKy8e9b1HIhRUU,17668
35
35
  agent_cli/agents/voice_edit.py,sha256=bOZzeRici5GFq9qPUwHTQiW1TFhHcm_AcLG-EdKxwgk,11137
36
- agent_cli/agents/memory/__init__.py,sha256=gW-0NGksjxTKkSjk4PzlXZIiAoF0sS2mFrRdeGVITf8,780
36
+ agent_cli/agents/memory/__init__.py,sha256=RkJYhq0SI_62hgUHicvtkJ1k7k9JEvKLqr0302gxylw,805
37
37
  agent_cli/agents/memory/add.py,sha256=lk6q2SmuwNNFAoDnfOQxPpNHbbHjekGCyKaWUgC9x-8,6210
38
- agent_cli/agents/memory/proxy.py,sha256=BRKMn1kxNnxGcy-zQnlhmDR6hs41Qec4125NCy5a3O8,6625
38
+ agent_cli/agents/memory/proxy.py,sha256=metocNGxTFbpLQ-E4dRhjj8YMRNgPf6WYjegTMOHk_E,6326
39
39
  agent_cli/core/__init__.py,sha256=c_knH7u9QgjsfMIil9NP4bVizHawLUMYoQWU4H9vMlQ,46
40
40
  agent_cli/core/audio.py,sha256=43FpYe2Wu_BYK9xJ_55V4xHjHJeFwQ5aM-CQzlTryt8,15168
41
41
  agent_cli/core/audio_format.py,sha256=zk3qlYMAlKYPz1enrjihQQspl_C218v1Rbcm7Uktlew,8773
42
- agent_cli/core/chroma.py,sha256=Vb_ny7SzAIL9SCEGlYgYOqsdG9BgusFGMj0RUzb6W90,2728
43
- agent_cli/core/deps.py,sha256=58zyHSJdzIAkJ0gZ7-snrASy8Ro9NePZmElch13r-SQ,6429
44
- agent_cli/core/openai_proxy.py,sha256=f2kqxk6bAOeN7gOzU0JnyS-RYtXUcK5Gbsa_pBmlCv0,4470
45
- agent_cli/core/process.py,sha256=Zay6beX4JUbkBHr6xbJxwVBjVFDABmRHQCXVPQH93r8,5916
46
- agent_cli/core/reranker.py,sha256=Qv5ASGUdseLzI6eQRfNeQY-Lvv4SOgLOu56CpwmszDM,3779
42
+ agent_cli/core/chroma.py,sha256=4gC9bT2q6i8zVSrHgEvaKLlBVmmlxlKnJrpQF08fLps,2791
43
+ agent_cli/core/deps.py,sha256=pIeALIfan2Ne7fIYLMUg_zJESoZADb2ZCv2DjPmL3Dg,6441
44
+ agent_cli/core/openai_proxy.py,sha256=VOqh40vyVrOa_u3CvXgolf0Bv-rALIXbKMQfjTBtH3I,4642
45
+ agent_cli/core/process.py,sha256=Ril7HqMJc-F1E-66pHrOi27gEZD3ZR_ZYhGnN61SVSs,5937
46
+ agent_cli/core/reranker.py,sha256=_RPjLKR5ej6L0Lb05tQFoVSyXOt1cXXn3ydEkCIXj2A,3851
47
47
  agent_cli/core/sse.py,sha256=SddiWjHh7DENb1wmvf3wDvX-OhbaC61EceFwQxmDUEo,2232
48
48
  agent_cli/core/transcription_logger.py,sha256=PVVfQK0leoB9JwUu5jYAhyRDBVq9exiPC0_KNXV8ggY,2057
49
- agent_cli/core/utils.py,sha256=CrydXrpnD3nRX8zfN5i_17Qu2OhZxfor33mtptfRwho,16839
50
- agent_cli/core/vad.py,sha256=67-EBjY-pTOf61VhrjVdDXgaNIBwWFFFccYth_1rQmg,6569
51
- agent_cli/core/watch.py,sha256=MKgGxxMe0yLlu78-XXqO4ferCpu_ljNr4MAzOMDsuuo,1951
49
+ agent_cli/core/utils.py,sha256=p3OJrNcU6iwqR0C7Q5Ab3_rwJBmP0EbIYT82a9scPSI,16896
50
+ agent_cli/core/vad.py,sha256=mM8VtC4TS3OpohSrtOw38M7T8f6T625FkIKim7Q_EoM,6591
51
+ agent_cli/core/watch.py,sha256=PakxMyqJycN6bkE6fxeo0qe9zj5wjuRly3p7rN9UgjM,2000
52
52
  agent_cli/dev/__init__.py,sha256=doTYiUFEBpnOxsQA69HQP9AA4QHBN0DjuHSTGRq5Xbg,551
53
- agent_cli/dev/cli.py,sha256=SWTQzyCVWHQCfILH7fcwAziFqwsIRPa6w4wIiG6i1YQ,51121
53
+ agent_cli/dev/cli.py,sha256=jSYqZFyeVHIfS3v0ew_-MYEqoWYYfChSI5AWJyXiXaw,51192
54
54
  agent_cli/dev/project.py,sha256=wJMGKTK1rmw8letrV6l6wcLU1fkQQDjCSEixAnsvyaY,18971
55
55
  agent_cli/dev/registry.py,sha256=c6t3ClyRFPvU4GGXJT79-D-qV4FqY7W_7P-tLT7LKZs,1887
56
56
  agent_cli/dev/worktree.py,sha256=Yw8jlhkf8BeKFc6pPEazGXnUIvryvYwbUmYxTluXILs,26916
57
57
  agent_cli/dev/coding_agents/__init__.py,sha256=f2SjWB7HwbiW0IDcJmGFFYal1-oNwBNEckdcni5Rf7s,360
58
58
  agent_cli/dev/coding_agents/aider.py,sha256=7iAZj7DG1E3nTw7cJxT05r5ZSzE8EWems1YRi1VpfLg,605
59
- agent_cli/dev/coding_agents/base.py,sha256=gMpSHItqjHITomVwHHPaxbZk6TDf0VbvZfVYtt5ptEo,5822
59
+ agent_cli/dev/coding_agents/base.py,sha256=P9Xlfr5Ufm_g6R_QNgq76enUaiRcwHIZvSh-nb5mPMo,5796
60
60
  agent_cli/dev/coding_agents/claude.py,sha256=SG1Pz8yasqo0lQRH8VGEBxQLNWjiuB0FsWYPmUAjyDE,1098
61
61
  agent_cli/dev/coding_agents/codex.py,sha256=djpntxubX73EZgaNZDBqKnmG3o3yCiAOYTpyggnSLOE,566
62
62
  agent_cli/dev/coding_agents/continue_dev.py,sha256=hgXaMG_Dp-nYwS7JmjQu_3KEkBvsd802umqFUQyOHh4,398
@@ -77,8 +77,8 @@ agent_cli/dev/editors/sublime.py,sha256=owEfRSMuArSeFKqk-LE2JOXaZy5QlQfHQ-l0I4k2
77
77
  agent_cli/dev/editors/vim.py,sha256=Fo-IQMPVbIiwBdOfmkFxR37f96QW6xc5LV3Pvr3u-b0,1378
78
78
  agent_cli/dev/editors/vscode.py,sha256=GOrl4FwVdDyuSn7t4lglgnVt_T6NtpjLVh1OWBxDMwE,318
79
79
  agent_cli/dev/editors/zed.py,sha256=lRMhdN_SKmHBA1ulx8x-p7Th_0EGSIv6ppAE84xobU4,515
80
- agent_cli/dev/skill/SKILL.md,sha256=SLDavirkkPVAHxf1JmRxvJz4EUQI81Dv0fdvjDs7VJ8,4443
81
- agent_cli/dev/skill/examples.md,sha256=ZzCyfudBk4lMR-sz8ER9l5vi6hI3HTeUlvQorRFVol4,16405
80
+ agent_cli/dev/skill/SKILL.md,sha256=I1p7qd9-M95PFfDwCtOY5PUYcSv-H7mg1NtC7pKDdK4,4713
81
+ agent_cli/dev/skill/examples.md,sha256=Q42arhFXC04ptaIwH4s2j58gq95qYcUxzxb77PeKUUk,18702
82
82
  agent_cli/dev/terminals/__init__.py,sha256=yUTNtvs1Do2hvhx56XxyfI-5HA5mjiv0IbJuuaL9TeE,371
83
83
  agent_cli/dev/terminals/apple_terminal.py,sha256=s7GdxXPgbpSLKK1DUwjNpshQpjR5Nt1QbL_cKPefIRI,2595
84
84
  agent_cli/dev/terminals/base.py,sha256=dQFfCCnEQFyOg46PPXWQToHQ74kJcCAD-NPD0kTJbOU,1420
@@ -91,20 +91,20 @@ agent_cli/dev/terminals/warp.py,sha256=j-Jvz_BbWYC3QfLrvl4CbDh03c9OGRFmuCzjyB2ud
91
91
  agent_cli/dev/terminals/zellij.py,sha256=GnQnopimb9XH67CZGHjnbVWpVSWhaLCATGJizCT5TkY,2321
92
92
  agent_cli/install/__init__.py,sha256=JQPrOrtdNd1Y1NmQDkb3Nmm1qdyn3kPjhQwy9D8ryjI,124
93
93
  agent_cli/install/common.py,sha256=WvnmcjnFTW0d1HZrKVGzj5Tg3q8Txk_ZOdc4a1MBFWI,3121
94
- agent_cli/install/extras.py,sha256=VdBrb1W_P7UVpRs9lXFVuTk3hWgnKcj3Hw7KjxfJilA,5846
94
+ agent_cli/install/extras.py,sha256=bWLLHHFEm1_3jVAvvmsv7JuXgN7ZuqouQl0QB2mtHPg,5960
95
95
  agent_cli/install/hotkeys.py,sha256=bwGoPeEKK6VI-IrKU8Q0RLMW9smkDNU7CdqD3Nbsd-w,1626
96
96
  agent_cli/install/services.py,sha256=2s_7ThxaElKCuomBYTn4Z36TF_o_arNeyJ4f8Wh4jEI,2912
97
97
  agent_cli/memory/__init__.py,sha256=8XNpVzP-qjF8o49A_eXsH_Rbp_FmxTIcknnvxq7vHms,162
98
- agent_cli/memory/_files.py,sha256=Za4YeSK5f2NKVZf1zDZXo4Sf-gERaProeSAXlLt8e0c,7598
98
+ agent_cli/memory/_files.py,sha256=xA06smZNj32UfTQz9Qs-05MvAoJuNrPfKxC-FXuhxcs,7658
99
99
  agent_cli/memory/_filters.py,sha256=VzuFyWTGqWKY0JYctfxF1JZFVYfVecW7mLDAM019mzU,2044
100
100
  agent_cli/memory/_git.py,sha256=qWOJkwUrWE6TkVYdUW1vVA6VB5w00w0LKDfqHUwQrBY,4738
101
- agent_cli/memory/_indexer.py,sha256=iWIRG61g2O4_cUjJYDXe03hkpRO5kVAqOwDeCb7Efu0,4704
102
- agent_cli/memory/_ingest.py,sha256=_WeFLTxrlWKFpyB1KR0tUWLgEq-eevB8ytAgpq6BOlY,13649
101
+ agent_cli/memory/_indexer.py,sha256=H3ZKW-wJV2qd6Q7ErqNdDklb2DdJ-CE21YePawLGxg4,4759
102
+ agent_cli/memory/_ingest.py,sha256=kAFmONZkzAQcXNPIk40HTHNLBn0feJ6HMiUpgvBpYJ4,13704
103
103
  agent_cli/memory/_persistence.py,sha256=7TsphuYsJfJbp-294IvWmtW98G_Sag9GTm8AUGU9dtQ,5401
104
104
  agent_cli/memory/_prompt.py,sha256=ncvYM5ewLrYRP8KizdFHtPQl2cWYg0SW0VQxA8rz_9E,4667
105
105
  agent_cli/memory/_retrieval.py,sha256=K_2TUcgzfntBARPyf0K6VR3NIgHHJrqGFMP_53Nae_w,9778
106
106
  agent_cli/memory/_store.py,sha256=m9mD1GxjdTXpnyL-X-MIU4cj28unqxJ_azV3kwM8blM,5086
107
- agent_cli/memory/_streaming.py,sha256=-WlOPtH61ogCSJKQRpfYHp2gBgIIN6hIJqb5padjmpw,1379
107
+ agent_cli/memory/_streaming.py,sha256=P1JnkDNTJJj-lXawmXhBZnIia3ZZmKo-N6mUsLYFVgs,1400
108
108
  agent_cli/memory/_tasks.py,sha256=XgEkN_3NCVQDWafZ_rqazpAE68yQ87x-amnQKMkfPXg,1469
109
109
  agent_cli/memory/api.py,sha256=riaGTOIw7g3KuDNcPtDy9wO44-D1wDtVadRj3RTT10k,3595
110
110
  agent_cli/memory/client.py,sha256=XomHhP-hPSoosORkBKSY1dW3gjheFEVqAac0b-tAULo,9994
@@ -112,13 +112,13 @@ agent_cli/memory/engine.py,sha256=rABVC86b5wU1QxY3BM43RhvfDOxoRT7Ddm98BN_qCL4,11
112
112
  agent_cli/memory/entities.py,sha256=_8wyJz--tNa66CEtSpl2TUN_zeHQvMzm42htnDaOr6g,1219
113
113
  agent_cli/memory/models.py,sha256=KK0wToEf-tXssYVL0hYaJlcADlJ3G2lcSXwo1UmA0VU,2352
114
114
  agent_cli/rag/__init__.py,sha256=nWNh4_zFTiweNQAiUzNVt1jq7myJxaeB5kXv1063GUM,54
115
- agent_cli/rag/_indexer.py,sha256=tzce07xvCbfH2jA0F_sldPRe7VLDXVQ3PGZFRsAJN10,1998
115
+ agent_cli/rag/_indexer.py,sha256=t6age33nVfs-WaspoIrBOdh3agfgPWijcJwmQwEVSsI,2053
116
116
  agent_cli/rag/_indexing.py,sha256=z2z5BWtQVuviPOjUiu151gJ9MzJxaqRYKrSi4Jj06mA,7605
117
117
  agent_cli/rag/_prompt.py,sha256=d8_jOhZGafMmjO7BlCl4H125bj4m-dNFWDOLz5_OPrw,954
118
118
  agent_cli/rag/_retriever.py,sha256=bzMzZR43P5cROgnWwOh_BrMFsMP5tDm21ToFVZwb0gk,4505
119
119
  agent_cli/rag/_store.py,sha256=HksCLnbHp19dnY5ZWglm86azBjjuiWqvZvRPG-oJ8SY,1381
120
120
  agent_cli/rag/_utils.py,sha256=OKZvn8UFb3TsB4b0eIWU6Md5xDiaG-g659zMjVUu8oI,5923
121
- agent_cli/rag/api.py,sha256=TFsqBNDi4hJg2VbhW6MmK3eLm8Hj6bDJaQr7bSXWQI0,5563
121
+ agent_cli/rag/api.py,sha256=Q_aaRdNxh9avNGvXWEJ5qH-vMTagN7m-iJdTZSeSink,5564
122
122
  agent_cli/rag/client.py,sha256=mFiZ4yjI75Vsehie6alsV1My50uIsp-G07Qz6SaNrZw,8913
123
123
  agent_cli/rag/engine.py,sha256=XySDer0fNTsEUjbUby5yf7JqB7uCE7tw2A6tYJixHnI,9800
124
124
  agent_cli/rag/models.py,sha256=uECWoeBChlkAK7uTM-pUnPGaaMO4EYJ3pJcAf8uh1vI,1043
@@ -156,8 +156,8 @@ agent_cli/scripts/nvidia-asr-server/server.py,sha256=kPNQIVF3exblvqMtIVk38Y6sZy2
156
156
  agent_cli/scripts/nvidia-asr-server/shell.nix,sha256=IT20j5YNj_wc7MdXi7ndogGodDNSGwyq8G0bNoZEpmg,1003
157
157
  agent_cli/scripts/nvidia-asr-server/uv.lock,sha256=5WWaqWOuV_moMPC-LIZK-A-Y5oaHr1tUn_vbR-IupzY,728608
158
158
  agent_cli/server/__init__.py,sha256=NZuJHlLHck9KWrepNZHrJONptYCQI9P-uTqknSFI5Ds,71
159
- agent_cli/server/cli.py,sha256=QA7MmASRN9Z5lDIQ25IBmxnFMsmW9AzmFsVqnUuafU0,22961
160
- agent_cli/server/common.py,sha256=fD2AZdM716TXcz1T4ZDPpPaKynVOEjbVC1LDDloDmDo,6463
159
+ agent_cli/server/cli.py,sha256=nnkd_0vxpKlDlq_hHBwgXEH8XlowtVyoVqeWOWORZS8,22893
160
+ agent_cli/server/common.py,sha256=hBBp6i-2-yhDY260ffwmFBg_ndcoT5SNcfa6uFyP7Vc,6391
161
161
  agent_cli/server/model_manager.py,sha256=93l_eeZeqnPALyDIK24or61tvded9TbM8tnde0okVjY,9225
162
162
  agent_cli/server/model_registry.py,sha256=KrRV1XxbFYuXu5rJlHFh6PTl_2BKiWnWsaNrf-0c6wQ,6988
163
163
  agent_cli/server/streaming.py,sha256=nX_kMNQBxdzvPKUcFQWth7dDBYALRGy_j9mDunKXaJE,2191
@@ -180,16 +180,16 @@ agent_cli/server/whisper/model_registry.py,sha256=qoRkB0ex6aRtUlsUN5BGik-oIZlwJb
180
180
  agent_cli/server/whisper/wyoming_handler.py,sha256=HjN565YfDHeVfaGjQfoy9xjCZPx_TvYvjRYgbKn3aOI,6634
181
181
  agent_cli/server/whisper/backends/__init__.py,sha256=DfgyigwMLKiSfaRzZ1TTeV7fa9HWSmT1UJnJa-6el7k,2338
182
182
  agent_cli/server/whisper/backends/base.py,sha256=gQi5EyMCFS464mKXGIKbh1vgtBm99eNkf93SCIYRYg0,2597
183
- agent_cli/server/whisper/backends/faster_whisper.py,sha256=-BogM_-_rhlXKUZuW1qUN8zw2gD0ut1bJozPDP19knA,6717
183
+ agent_cli/server/whisper/backends/faster_whisper.py,sha256=GN51L-qBjH-YU8ASiu317NrkMKMsK_znXDOTxi90EzU,6966
184
184
  agent_cli/server/whisper/backends/mlx.py,sha256=wSkD9wL3K8PvQToJ5qkTj_HZQJD9Brs_bjNz-X0Sku8,9328
185
185
  agent_cli/services/__init__.py,sha256=8REdXC5eXfhAJJ6j85tgCinbj89PLwv8A50dysA8VUc,10004
186
186
  agent_cli/services/_wyoming_utils.py,sha256=pKPa4fOSdqcG3-kNHJOHHsMnZ1yZJZi6XohVwjAwabo,1971
187
187
  agent_cli/services/asr.py,sha256=aRaCLVCygsJ15qyQEPECOZsdSrnlLPbyY4RwAqY0qIw,17258
188
- agent_cli/services/llm.py,sha256=Kwdo6pbMYI9oykF-RBe1iaL3KsYrNWTLdRSioewmsGQ,7199
188
+ agent_cli/services/llm.py,sha256=i01utl1eYWlM13gvW2eR6ErL_ndH-g0d-BSleZra_7k,7229
189
189
  agent_cli/services/tts.py,sha256=NX5Qnq7ddLI3mwm3nzhbR3zB1Os4Ip4sSVSjDZDTBcI,14855
190
190
  agent_cli/services/wake_word.py,sha256=JFJ1SA22H4yko9DXiQ1t7fcoxeALLAe3iBrLs0z8rX4,4664
191
- agent_cli-0.68.5.dist-info/METADATA,sha256=r3jkLcsKzc9_7mzAV--UFBcRkkVoU8UJneOJL8JpIzQ,156068
192
- agent_cli-0.68.5.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
193
- agent_cli-0.68.5.dist-info/entry_points.txt,sha256=FUv-fB2atLsPUk_RT4zqnZl1coz4_XHFwRALOKOF38s,97
194
- agent_cli-0.68.5.dist-info/licenses/LICENSE,sha256=majJU6S9kC8R8bW39NVBHyv32Dq50FL6TDxECG2WVts,1068
195
- agent_cli-0.68.5.dist-info/RECORD,,
191
+ agent_cli-0.69.0.dist-info/METADATA,sha256=wrFEo6LeSLQmudIZmCuCa56WhhZlh4ldv-4BGcJOHcQ,156132
192
+ agent_cli-0.69.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
193
+ agent_cli-0.69.0.dist-info/entry_points.txt,sha256=FUv-fB2atLsPUk_RT4zqnZl1coz4_XHFwRALOKOF38s,97
194
+ agent_cli-0.69.0.dist-info/licenses/LICENSE,sha256=majJU6S9kC8R8bW39NVBHyv32Dq50FL6TDxECG2WVts,1068
195
+ agent_cli-0.69.0.dist-info/RECORD,,