agent-cli 0.70.5__py3-none-any.whl → 0.72.1__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.
@@ -14,7 +14,7 @@ import typer
14
14
 
15
15
  from agent_cli.cli import app
16
16
  from agent_cli.core.deps import EXTRAS as _EXTRAS_META
17
- from agent_cli.core.utils import console, print_error_message
17
+ from agent_cli.core.utils import console, err_console, print_error_message
18
18
 
19
19
  # Extract descriptions from the centralized EXTRAS metadata
20
20
  EXTRAS: dict[str, str] = {name: desc for name, (desc, _) in _EXTRAS_META.items()}
@@ -69,7 +69,8 @@ def _install_via_uv_tool(extras: list[str], *, quiet: bool = False) -> bool:
69
69
  cmd = ["uv", "tool", "install", package_spec, "--force", "--python", python_version]
70
70
  if quiet:
71
71
  cmd.append("-q")
72
- console.print(f"Running: [cyan]{' '.join(cmd)}[/]")
72
+ # Use stderr for status messages so they don't pollute stdout (e.g., for hotkey notifications)
73
+ err_console.print(f"Running: [cyan]{' '.join(cmd)}[/]")
73
74
  result = subprocess.run(cmd, check=False)
74
75
  return result.returncode == 0
75
76
 
@@ -118,29 +119,59 @@ def install_extras_programmatic(extras: list[str], *, quiet: bool = False) -> bo
118
119
  valid = [e for e in extras if e in available]
119
120
  invalid = [e for e in extras if e not in available]
120
121
  if invalid:
121
- console.print(f"[yellow]Unknown extras (skipped): {', '.join(invalid)}[/]")
122
+ # Use stderr so warning doesn't pollute stdout (e.g., for hotkey notifications)
123
+ err_console.print(f"[yellow]Unknown extras (skipped): {', '.join(invalid)}[/]")
122
124
  return bool(valid) and _install_extras_impl(valid, quiet=quiet)
123
125
 
124
126
 
125
127
  @app.command("install-extras", rich_help_panel="Installation", no_args_is_help=True)
126
128
  def install_extras(
127
- extras: Annotated[list[str] | None, typer.Argument(help="Extras to install")] = None,
129
+ extras: Annotated[
130
+ list[str] | None,
131
+ typer.Argument(
132
+ help="Extras to install: `rag`, `memory`, `vad`, `audio`, `piper`, `kokoro`, "
133
+ "`faster-whisper`, `mlx-whisper`, `wyoming`, `server`, `speed`, `llm`",
134
+ ),
135
+ ] = None,
128
136
  list_extras: Annotated[
129
137
  bool,
130
- typer.Option("--list", "-l", help="List available extras"),
138
+ typer.Option(
139
+ "--list",
140
+ "-l",
141
+ help="Show available extras with descriptions (what each one enables)",
142
+ ),
131
143
  ] = False,
132
144
  all_extras: Annotated[
133
145
  bool,
134
- typer.Option("--all", "-a", help="Install all available extras"),
146
+ typer.Option("--all", "-a", help="Install all available extras at once"),
135
147
  ] = False,
136
148
  ) -> None:
137
- """Install optional extras (rag, memory, vad, etc.) with pinned versions.
138
-
139
- Examples:
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
149
+ """Install optional dependencies with pinned, compatible versions.
150
+
151
+ Many agent-cli features require optional dependencies. This command installs
152
+ them with version pinning to ensure compatibility. Dependencies persist
153
+ across `uv tool upgrade` when installed via `uv tool`.
154
+
155
+ **Available extras:**
156
+ - `rag` - RAG proxy server (ChromaDB, embeddings)
157
+ - `memory` - Long-term memory proxy (ChromaDB)
158
+ - `vad` - Voice Activity Detection (silero-vad)
159
+ - `audio` - Local audio recording/playback
160
+ - `piper` - Local Piper TTS engine
161
+ - `kokoro` - Kokoro neural TTS engine
162
+ - `faster-whisper` - Whisper ASR for CUDA/CPU
163
+ - `mlx-whisper` - Whisper ASR for Apple Silicon
164
+ - `wyoming` - Wyoming protocol for ASR/TTS servers
165
+ - `server` - FastAPI server components
166
+ - `speed` - Audio speed adjustment
167
+ - `llm` - LLM framework (pydantic-ai)
168
+
169
+ **Examples:**
170
+
171
+ agent-cli install-extras rag # Install RAG dependencies
172
+ agent-cli install-extras memory vad # Install multiple extras
173
+ agent-cli install-extras --list # Show available extras
174
+ agent-cli install-extras --all # Install all extras
144
175
 
145
176
  """
146
177
  available = _available_extras()
@@ -13,20 +13,31 @@ from agent_cli.install.common import execute_installation_script, get_platform_s
13
13
  def install_hotkeys() -> None:
14
14
  """Install system-wide hotkeys for agent-cli commands.
15
15
 
16
- Sets up the following hotkeys:
16
+ Sets up three global hotkeys:
17
17
 
18
- macOS:
19
- - Cmd+Shift+R: Toggle voice transcription
20
- - Cmd+Shift+A: Autocorrect clipboard text
21
- - Cmd+Shift+V: Voice edit clipboard text
18
+ | Hotkey (macOS / Linux) | Action |
19
+ |-------------------------|-------------------------------------------------|
20
+ | Cmd/Super + Shift + R | Toggle voice transcription (start/stop) |
21
+ | Cmd/Super + Shift + A | Autocorrect clipboard text (grammar/spelling) |
22
+ | Cmd/Super + Shift + V | Voice edit clipboard text (voice commands) |
22
23
 
23
- Linux:
24
- - Super+Shift+R: Toggle voice transcription
25
- - Super+Shift+A: Autocorrect clipboard text
26
- - Super+Shift+V: Voice edit clipboard text
24
+ **macOS** (fully automatic):
27
25
 
28
- Note: On macOS, you may need to grant Accessibility permissions to skhd
29
- in System Settings Privacy & Security → Accessibility.
26
+ 1. Installs `skhd` (hotkey daemon) and `terminal-notifier` via Homebrew
27
+ 2. Creates config at `~/.config/skhd/skhdrc`
28
+ 3. Starts skhd as a background service
29
+ 4. May require Accessibility permissions: System Settings → Privacy & Security → Accessibility → enable 'skhd'
30
+
31
+ **Linux** (manual DE configuration):
32
+
33
+ 1. Installs `libnotify` for notifications (if missing)
34
+ 2. Prints binding instructions for your desktop environment
35
+ 3. You manually add hotkeys pointing to the installed scripts
36
+
37
+ Supported Linux DEs: Hyprland, Sway, i3, GNOME, KDE, XFCE.
38
+
39
+ **Customizing hotkeys** (macOS): Edit `~/.config/skhd/skhdrc` and restart skhd:
40
+ `skhd --restart-service`
30
41
  """
31
42
  script_name = get_platform_script("setup-macos-hotkeys.sh", "setup-linux-hotkeys.sh")
32
43
  system = platform.system().lower()
@@ -20,13 +20,31 @@ from agent_cli.install.common import (
20
20
  def install_services() -> None:
21
21
  """Install all required services (Ollama, Whisper, Piper, OpenWakeWord).
22
22
 
23
- This command installs:
24
- - Ollama (local LLM server)
25
- - Wyoming Faster Whisper (speech-to-text)
26
- - Wyoming Piper (text-to-speech)
27
- - Wyoming OpenWakeWord (wake word detection)
23
+ This command installs the following services:
28
24
 
29
- The appropriate installation method is used based on your operating system.
25
+ - **Ollama** - Local LLM server for text processing
26
+ - **Wyoming Faster Whisper** - Speech-to-text transcription
27
+ - **Wyoming Piper** - Text-to-speech synthesis
28
+ - **Wyoming OpenWakeWord** - Wake word detection ("ok nabu", etc.)
29
+
30
+ The appropriate installation method is used based on your operating system
31
+ (Homebrew on macOS, apt/pip on Linux).
32
+
33
+ **Requirements:**
34
+
35
+ - macOS: Homebrew must be installed
36
+ - Linux: Requires sudo access for system packages
37
+
38
+ **Examples:**
39
+
40
+ Install all services:
41
+ `agent-cli install-services`
42
+
43
+ **After installation:**
44
+
45
+ 1. Start the services: `agent-cli start-services`
46
+ 2. Test transcription: `agent-cli transcribe --list-devices`
47
+ 3. Set up hotkeys (optional): `agent-cli install-hotkeys`
30
48
  """
31
49
  script_name = get_platform_script("setup-macos.sh", "setup-linux.sh")
32
50
 
@@ -46,19 +64,41 @@ def start_services(
46
64
  attach: bool = typer.Option(
47
65
  True, # noqa: FBT003
48
66
  "--attach/--no-attach",
49
- help="Attach to Zellij session after starting",
67
+ help=(
68
+ "Attach to the Zellij session after starting. "
69
+ "With `--no-attach`, services start in background and you can "
70
+ "reattach later with `zellij attach agent-cli`"
71
+ ),
50
72
  ),
51
73
  ) -> None:
52
74
  """Start all agent-cli services in a Zellij session.
53
75
 
54
- This starts:
55
- - Ollama (LLM server)
56
- - Wyoming Faster Whisper (speech-to-text)
57
- - Wyoming Piper (text-to-speech)
58
- - Wyoming OpenWakeWord (wake word detection)
76
+ Starts these services, each in its own Zellij pane:
77
+
78
+ - **Ollama** - LLM server (port 11434)
79
+ - **Wyoming Whisper** - Speech-to-text (port 10300)
80
+ - **Wyoming Piper** - Text-to-speech (port 10200)
81
+ - **Wyoming OpenWakeWord** - Wake word detection (port 10400)
82
+
83
+ Services run in a Zellij terminal multiplexer session named `agent-cli`.
84
+ If a session already exists, the command attaches to it instead of
85
+ starting new services.
86
+
87
+ **Keyboard shortcuts:**
88
+ - `Ctrl-O d` - Detach (keeps services running in background)
89
+ - `Ctrl-Q` - Quit (stops all services)
90
+ - `Alt + arrows` - Navigate between panes
91
+
92
+ **Examples:**
93
+
94
+ Start services and attach:
95
+ `agent-cli start-services`
96
+
97
+ Start in background (for scripts or automation):
98
+ `agent-cli start-services --no-attach`
59
99
 
60
- Services run in a Zellij terminal multiplexer session named 'agent-cli'.
61
- Use Ctrl-Q to quit or Ctrl-O d to detach from the session.
100
+ Reattach to running services:
101
+ `zellij attach agent-cli`
62
102
  """
63
103
  try:
64
104
  script_path = get_script_path("start-all-services.sh")
agent_cli/opts.py CHANGED
@@ -47,7 +47,8 @@ TTS_PROVIDER: str = typer.Option(
47
47
  LLM: bool = typer.Option(
48
48
  False, # noqa: FBT003
49
49
  "--llm/--no-llm",
50
- help="Use an LLM to process the transcript.",
50
+ help="Clean up transcript with LLM: fix errors, add punctuation, remove filler words. "
51
+ "Uses `--extra-instructions` if set (via CLI or config file).",
51
52
  rich_help_panel="LLM Configuration",
52
53
  )
53
54
  # Ollama (local service)
@@ -114,19 +115,19 @@ EMBEDDING_MODEL: str = typer.Option(
114
115
  INPUT_DEVICE_INDEX: int | None = typer.Option(
115
116
  None,
116
117
  "--input-device-index",
117
- help="Index of the audio input device to use.",
118
+ help="Audio input device index (see `--list-devices`). Uses system default if omitted.",
118
119
  rich_help_panel="Audio Input",
119
120
  )
120
121
  INPUT_DEVICE_NAME: str | None = typer.Option(
121
122
  None,
122
123
  "--input-device-name",
123
- help="Device name keywords for partial matching.",
124
+ help="Select input device by name substring (e.g., `MacBook` or `USB`).",
124
125
  rich_help_panel="Audio Input",
125
126
  )
126
127
  LIST_DEVICES: bool = typer.Option(
127
128
  False, # noqa: FBT003
128
129
  "--list-devices",
129
- help="List available audio input and output devices and exit.",
130
+ help="List available audio devices with their indices and exit.",
130
131
  is_eager=True,
131
132
  rich_help_panel="Audio Input",
132
133
  )
@@ -181,7 +182,7 @@ ASR_GEMINI_MODEL: str = typer.Option(
181
182
  WAKE_SERVER_IP: str = typer.Option(
182
183
  "localhost",
183
184
  "--wake-server-ip",
184
- help="Wyoming wake word server IP address.",
185
+ help="Wyoming wake word server IP (requires wyoming-openwakeword or similar).",
185
186
  rich_help_panel="Wake Word",
186
187
  )
187
188
  WAKE_SERVER_PORT: int = typer.Option(
@@ -193,7 +194,7 @@ WAKE_SERVER_PORT: int = typer.Option(
193
194
  WAKE_WORD: str = typer.Option(
194
195
  "ok_nabu",
195
196
  "--wake-word",
196
- help="Name of wake word to detect (e.g., 'ok_nabu', 'hey_jarvis').",
197
+ help="Wake word to detect. Common options: `ok_nabu`, `hey_jarvis`, `alexa`. Must match a model loaded in your wake word server.",
197
198
  rich_help_panel="Wake Word",
198
199
  )
199
200
 
@@ -215,13 +216,13 @@ TTS_SPEED: float = typer.Option(
215
216
  OUTPUT_DEVICE_INDEX: int | None = typer.Option(
216
217
  None,
217
218
  "--output-device-index",
218
- help="Index of the audio output device to use for TTS.",
219
+ help="Audio output device index (see `--list-devices` for available devices).",
219
220
  rich_help_panel="Audio Output",
220
221
  )
221
222
  OUTPUT_DEVICE_NAME: str | None = typer.Option(
222
223
  None,
223
224
  "--output-device-name",
224
- help="Output device name keywords for partial matching.",
225
+ help="Partial match on device name (e.g., 'speakers', 'headphones').",
225
226
  rich_help_panel="Audio Output",
226
227
  )
227
228
  # Wyoming (local service)
@@ -265,7 +266,7 @@ TTS_OPENAI_MODEL: str = typer.Option(
265
266
  TTS_OPENAI_VOICE: str = typer.Option(
266
267
  "alloy",
267
268
  "--tts-openai-voice",
268
- help="The voice to use for OpenAI-compatible TTS.",
269
+ help="Voice for OpenAI TTS (alloy, echo, fable, onyx, nova, shimmer).",
269
270
  rich_help_panel="Audio Output: OpenAI-compatible",
270
271
  )
271
272
  TTS_OPENAI_BASE_URL: str | None = typer.Option(
@@ -315,21 +316,19 @@ TTS_GEMINI_VOICE: str = typer.Option(
315
316
  STOP: bool = typer.Option(
316
317
  False, # noqa: FBT003
317
318
  "--stop",
318
- help="Stop any running background process.",
319
+ help="Stop any running instance of this command.",
319
320
  rich_help_panel="Process Management",
320
321
  )
321
322
  STATUS: bool = typer.Option(
322
323
  False, # noqa: FBT003
323
324
  "--status",
324
- help="Check if a background process is running.",
325
+ help="Check if an instance is currently running.",
325
326
  rich_help_panel="Process Management",
326
327
  )
327
328
  TOGGLE: bool = typer.Option(
328
329
  False, # noqa: FBT003
329
330
  "--toggle",
330
- help="Toggle the background process on/off. "
331
- "If the process is running, it will be stopped. "
332
- "If the process is not running, it will be started.",
331
+ help="Start if not running, stop if running. Ideal for hotkey binding.",
333
332
  rich_help_panel="Process Management",
334
333
  )
335
334
 
@@ -365,13 +364,14 @@ CLIPBOARD: bool = typer.Option(
365
364
  rich_help_panel="General Options",
366
365
  )
367
366
  LOG_LEVEL: LogLevel = typer.Option(
368
- "info",
367
+ "warning",
369
368
  "--log-level",
370
369
  envvar="LOG_LEVEL",
371
370
  help="Set logging level.",
372
371
  case_sensitive=False,
373
372
  rich_help_panel="General Options",
374
373
  )
374
+ SERVER_LOG_LEVEL: LogLevel = with_default(LOG_LEVEL, "info")
375
375
  LOG_FILE: str | None = typer.Option(
376
376
  None,
377
377
  "--log-file",
@@ -388,19 +388,20 @@ QUIET: bool = typer.Option(
388
388
  JSON_OUTPUT: bool = typer.Option(
389
389
  False, # noqa: FBT003
390
390
  "--json",
391
- help="Output result as JSON for automation. Implies --quiet and --no-clipboard.",
391
+ help="Output result as JSON (implies `--quiet` and `--no-clipboard`).",
392
392
  rich_help_panel="General Options",
393
393
  )
394
394
  SAVE_FILE: Path | None = typer.Option(
395
395
  None,
396
396
  "--save-file",
397
- help="Save TTS response audio to WAV file.",
397
+ help="Save audio to WAV file instead of playing through speakers.",
398
398
  rich_help_panel="General Options",
399
399
  )
400
400
  TRANSCRIPTION_LOG: Path | None = typer.Option(
401
401
  None,
402
402
  "--transcription-log",
403
- help="Path to log transcription results with timestamps, hostname, model, and raw output.",
403
+ help="Append transcripts to JSONL file (timestamp, hostname, model, raw/processed text). "
404
+ "Recent entries provide context for LLM cleanup.",
404
405
  rich_help_panel="General Options",
405
406
  )
406
407
 
@@ -416,18 +417,21 @@ SERVER_HOST: str = typer.Option(
416
417
  FROM_FILE: Path | None = typer.Option(
417
418
  None,
418
419
  "--from-file",
419
- help="Transcribe audio from a file (supports wav, mp3, m4a, ogg, flac, aac, webm). Requires ffmpeg for non-WAV formats with Wyoming provider.",
420
+ help="Transcribe from audio file instead of microphone. "
421
+ "Supports wav, mp3, m4a, ogg, flac, aac, webm. "
422
+ "Requires `ffmpeg` for non-WAV formats with Wyoming.",
420
423
  rich_help_panel="Audio Recovery",
421
424
  )
422
425
  LAST_RECORDING: int = typer.Option(
423
426
  0,
424
427
  "--last-recording",
425
- help="Transcribe a saved recording. Use 1 for most recent, 2 for second-to-last, etc. Use 0 to disable (default).",
428
+ help="Re-transcribe a saved recording (1=most recent, 2=second-to-last, etc). "
429
+ "Useful after connection failures or to retry with different options.",
426
430
  rich_help_panel="Audio Recovery",
427
431
  )
428
432
  SAVE_RECORDING: bool = typer.Option(
429
433
  True, # noqa: FBT003
430
434
  "--save-recording/--no-save-recording",
431
- help="Save the audio recording to disk for recovery.",
435
+ help="Save recordings to ~/.cache/agent-cli/ for `--last-recording` recovery.",
432
436
  rich_help_panel="Audio Recovery",
433
437
  )