agent-cli 0.70.5__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.
Files changed (196) hide show
  1. agent_cli/__init__.py +5 -0
  2. agent_cli/__main__.py +6 -0
  3. agent_cli/_extras.json +14 -0
  4. agent_cli/_requirements/.gitkeep +0 -0
  5. agent_cli/_requirements/audio.txt +79 -0
  6. agent_cli/_requirements/faster-whisper.txt +215 -0
  7. agent_cli/_requirements/kokoro.txt +425 -0
  8. agent_cli/_requirements/llm.txt +183 -0
  9. agent_cli/_requirements/memory.txt +355 -0
  10. agent_cli/_requirements/mlx-whisper.txt +222 -0
  11. agent_cli/_requirements/piper.txt +176 -0
  12. agent_cli/_requirements/rag.txt +402 -0
  13. agent_cli/_requirements/server.txt +154 -0
  14. agent_cli/_requirements/speed.txt +77 -0
  15. agent_cli/_requirements/vad.txt +155 -0
  16. agent_cli/_requirements/wyoming.txt +71 -0
  17. agent_cli/_tools.py +368 -0
  18. agent_cli/agents/__init__.py +23 -0
  19. agent_cli/agents/_voice_agent_common.py +136 -0
  20. agent_cli/agents/assistant.py +383 -0
  21. agent_cli/agents/autocorrect.py +284 -0
  22. agent_cli/agents/chat.py +496 -0
  23. agent_cli/agents/memory/__init__.py +31 -0
  24. agent_cli/agents/memory/add.py +190 -0
  25. agent_cli/agents/memory/proxy.py +160 -0
  26. agent_cli/agents/rag_proxy.py +128 -0
  27. agent_cli/agents/speak.py +209 -0
  28. agent_cli/agents/transcribe.py +671 -0
  29. agent_cli/agents/transcribe_daemon.py +499 -0
  30. agent_cli/agents/voice_edit.py +291 -0
  31. agent_cli/api.py +22 -0
  32. agent_cli/cli.py +106 -0
  33. agent_cli/config.py +503 -0
  34. agent_cli/config_cmd.py +307 -0
  35. agent_cli/constants.py +27 -0
  36. agent_cli/core/__init__.py +1 -0
  37. agent_cli/core/audio.py +461 -0
  38. agent_cli/core/audio_format.py +299 -0
  39. agent_cli/core/chroma.py +88 -0
  40. agent_cli/core/deps.py +191 -0
  41. agent_cli/core/openai_proxy.py +139 -0
  42. agent_cli/core/process.py +195 -0
  43. agent_cli/core/reranker.py +120 -0
  44. agent_cli/core/sse.py +87 -0
  45. agent_cli/core/transcription_logger.py +70 -0
  46. agent_cli/core/utils.py +526 -0
  47. agent_cli/core/vad.py +175 -0
  48. agent_cli/core/watch.py +65 -0
  49. agent_cli/dev/__init__.py +14 -0
  50. agent_cli/dev/cli.py +1588 -0
  51. agent_cli/dev/coding_agents/__init__.py +19 -0
  52. agent_cli/dev/coding_agents/aider.py +24 -0
  53. agent_cli/dev/coding_agents/base.py +167 -0
  54. agent_cli/dev/coding_agents/claude.py +39 -0
  55. agent_cli/dev/coding_agents/codex.py +24 -0
  56. agent_cli/dev/coding_agents/continue_dev.py +15 -0
  57. agent_cli/dev/coding_agents/copilot.py +24 -0
  58. agent_cli/dev/coding_agents/cursor_agent.py +48 -0
  59. agent_cli/dev/coding_agents/gemini.py +28 -0
  60. agent_cli/dev/coding_agents/opencode.py +15 -0
  61. agent_cli/dev/coding_agents/registry.py +49 -0
  62. agent_cli/dev/editors/__init__.py +19 -0
  63. agent_cli/dev/editors/base.py +89 -0
  64. agent_cli/dev/editors/cursor.py +15 -0
  65. agent_cli/dev/editors/emacs.py +46 -0
  66. agent_cli/dev/editors/jetbrains.py +56 -0
  67. agent_cli/dev/editors/nano.py +31 -0
  68. agent_cli/dev/editors/neovim.py +33 -0
  69. agent_cli/dev/editors/registry.py +59 -0
  70. agent_cli/dev/editors/sublime.py +20 -0
  71. agent_cli/dev/editors/vim.py +42 -0
  72. agent_cli/dev/editors/vscode.py +15 -0
  73. agent_cli/dev/editors/zed.py +20 -0
  74. agent_cli/dev/project.py +568 -0
  75. agent_cli/dev/registry.py +52 -0
  76. agent_cli/dev/skill/SKILL.md +141 -0
  77. agent_cli/dev/skill/examples.md +571 -0
  78. agent_cli/dev/terminals/__init__.py +19 -0
  79. agent_cli/dev/terminals/apple_terminal.py +82 -0
  80. agent_cli/dev/terminals/base.py +56 -0
  81. agent_cli/dev/terminals/gnome.py +51 -0
  82. agent_cli/dev/terminals/iterm2.py +84 -0
  83. agent_cli/dev/terminals/kitty.py +77 -0
  84. agent_cli/dev/terminals/registry.py +48 -0
  85. agent_cli/dev/terminals/tmux.py +58 -0
  86. agent_cli/dev/terminals/warp.py +132 -0
  87. agent_cli/dev/terminals/zellij.py +78 -0
  88. agent_cli/dev/worktree.py +856 -0
  89. agent_cli/docs_gen.py +417 -0
  90. agent_cli/example-config.toml +185 -0
  91. agent_cli/install/__init__.py +5 -0
  92. agent_cli/install/common.py +89 -0
  93. agent_cli/install/extras.py +174 -0
  94. agent_cli/install/hotkeys.py +48 -0
  95. agent_cli/install/services.py +87 -0
  96. agent_cli/memory/__init__.py +7 -0
  97. agent_cli/memory/_files.py +250 -0
  98. agent_cli/memory/_filters.py +63 -0
  99. agent_cli/memory/_git.py +157 -0
  100. agent_cli/memory/_indexer.py +142 -0
  101. agent_cli/memory/_ingest.py +408 -0
  102. agent_cli/memory/_persistence.py +182 -0
  103. agent_cli/memory/_prompt.py +91 -0
  104. agent_cli/memory/_retrieval.py +294 -0
  105. agent_cli/memory/_store.py +169 -0
  106. agent_cli/memory/_streaming.py +44 -0
  107. agent_cli/memory/_tasks.py +48 -0
  108. agent_cli/memory/api.py +113 -0
  109. agent_cli/memory/client.py +272 -0
  110. agent_cli/memory/engine.py +361 -0
  111. agent_cli/memory/entities.py +43 -0
  112. agent_cli/memory/models.py +112 -0
  113. agent_cli/opts.py +433 -0
  114. agent_cli/py.typed +0 -0
  115. agent_cli/rag/__init__.py +3 -0
  116. agent_cli/rag/_indexer.py +67 -0
  117. agent_cli/rag/_indexing.py +226 -0
  118. agent_cli/rag/_prompt.py +30 -0
  119. agent_cli/rag/_retriever.py +156 -0
  120. agent_cli/rag/_store.py +48 -0
  121. agent_cli/rag/_utils.py +218 -0
  122. agent_cli/rag/api.py +175 -0
  123. agent_cli/rag/client.py +299 -0
  124. agent_cli/rag/engine.py +302 -0
  125. agent_cli/rag/models.py +55 -0
  126. agent_cli/scripts/.runtime/.gitkeep +0 -0
  127. agent_cli/scripts/__init__.py +1 -0
  128. agent_cli/scripts/check_plugin_skill_sync.py +50 -0
  129. agent_cli/scripts/linux-hotkeys/README.md +63 -0
  130. agent_cli/scripts/linux-hotkeys/toggle-autocorrect.sh +45 -0
  131. agent_cli/scripts/linux-hotkeys/toggle-transcription.sh +58 -0
  132. agent_cli/scripts/linux-hotkeys/toggle-voice-edit.sh +58 -0
  133. agent_cli/scripts/macos-hotkeys/README.md +45 -0
  134. agent_cli/scripts/macos-hotkeys/skhd-config-example +5 -0
  135. agent_cli/scripts/macos-hotkeys/toggle-autocorrect.sh +12 -0
  136. agent_cli/scripts/macos-hotkeys/toggle-transcription.sh +37 -0
  137. agent_cli/scripts/macos-hotkeys/toggle-voice-edit.sh +37 -0
  138. agent_cli/scripts/nvidia-asr-server/README.md +99 -0
  139. agent_cli/scripts/nvidia-asr-server/pyproject.toml +27 -0
  140. agent_cli/scripts/nvidia-asr-server/server.py +255 -0
  141. agent_cli/scripts/nvidia-asr-server/shell.nix +32 -0
  142. agent_cli/scripts/nvidia-asr-server/uv.lock +4654 -0
  143. agent_cli/scripts/run-openwakeword.sh +11 -0
  144. agent_cli/scripts/run-piper-windows.ps1 +30 -0
  145. agent_cli/scripts/run-piper.sh +24 -0
  146. agent_cli/scripts/run-whisper-linux.sh +40 -0
  147. agent_cli/scripts/run-whisper-macos.sh +6 -0
  148. agent_cli/scripts/run-whisper-windows.ps1 +51 -0
  149. agent_cli/scripts/run-whisper.sh +9 -0
  150. agent_cli/scripts/run_faster_whisper_server.py +136 -0
  151. agent_cli/scripts/setup-linux-hotkeys.sh +72 -0
  152. agent_cli/scripts/setup-linux.sh +108 -0
  153. agent_cli/scripts/setup-macos-hotkeys.sh +61 -0
  154. agent_cli/scripts/setup-macos.sh +76 -0
  155. agent_cli/scripts/setup-windows.ps1 +63 -0
  156. agent_cli/scripts/start-all-services-windows.ps1 +53 -0
  157. agent_cli/scripts/start-all-services.sh +178 -0
  158. agent_cli/scripts/sync_extras.py +138 -0
  159. agent_cli/server/__init__.py +3 -0
  160. agent_cli/server/cli.py +721 -0
  161. agent_cli/server/common.py +222 -0
  162. agent_cli/server/model_manager.py +288 -0
  163. agent_cli/server/model_registry.py +225 -0
  164. agent_cli/server/proxy/__init__.py +3 -0
  165. agent_cli/server/proxy/api.py +444 -0
  166. agent_cli/server/streaming.py +67 -0
  167. agent_cli/server/tts/__init__.py +3 -0
  168. agent_cli/server/tts/api.py +335 -0
  169. agent_cli/server/tts/backends/__init__.py +82 -0
  170. agent_cli/server/tts/backends/base.py +139 -0
  171. agent_cli/server/tts/backends/kokoro.py +403 -0
  172. agent_cli/server/tts/backends/piper.py +253 -0
  173. agent_cli/server/tts/model_manager.py +201 -0
  174. agent_cli/server/tts/model_registry.py +28 -0
  175. agent_cli/server/tts/wyoming_handler.py +249 -0
  176. agent_cli/server/whisper/__init__.py +3 -0
  177. agent_cli/server/whisper/api.py +413 -0
  178. agent_cli/server/whisper/backends/__init__.py +89 -0
  179. agent_cli/server/whisper/backends/base.py +97 -0
  180. agent_cli/server/whisper/backends/faster_whisper.py +225 -0
  181. agent_cli/server/whisper/backends/mlx.py +270 -0
  182. agent_cli/server/whisper/languages.py +116 -0
  183. agent_cli/server/whisper/model_manager.py +157 -0
  184. agent_cli/server/whisper/model_registry.py +28 -0
  185. agent_cli/server/whisper/wyoming_handler.py +203 -0
  186. agent_cli/services/__init__.py +343 -0
  187. agent_cli/services/_wyoming_utils.py +64 -0
  188. agent_cli/services/asr.py +506 -0
  189. agent_cli/services/llm.py +228 -0
  190. agent_cli/services/tts.py +450 -0
  191. agent_cli/services/wake_word.py +142 -0
  192. agent_cli-0.70.5.dist-info/METADATA +2118 -0
  193. agent_cli-0.70.5.dist-info/RECORD +196 -0
  194. agent_cli-0.70.5.dist-info/WHEEL +4 -0
  195. agent_cli-0.70.5.dist-info/entry_points.txt +4 -0
  196. agent_cli-0.70.5.dist-info/licenses/LICENSE +21 -0
agent_cli/opts.py ADDED
@@ -0,0 +1,433 @@
1
+ """Shared Typer options for the Agent CLI agents."""
2
+
3
+ import copy
4
+ from pathlib import Path
5
+ from typing import Literal
6
+
7
+ import typer
8
+ from typer.models import OptionInfo
9
+
10
+ from agent_cli.constants import DEFAULT_OPENAI_EMBEDDING_MODEL, DEFAULT_OPENAI_MODEL
11
+
12
+ LogLevel = Literal["debug", "info", "warning", "error"]
13
+
14
+
15
+ def with_default(option: OptionInfo, default: str) -> OptionInfo:
16
+ """Create a copy of a typer Option with a different default value."""
17
+ opt = copy.copy(option)
18
+ opt.default = default
19
+ return opt
20
+
21
+
22
+ # --- Provider Selection ---
23
+ LLM_PROVIDER: str = typer.Option(
24
+ "ollama",
25
+ "--llm-provider",
26
+ envvar="LLM_PROVIDER",
27
+ help="The LLM provider to use ('ollama', 'openai', 'gemini').",
28
+ rich_help_panel="Provider Selection",
29
+ )
30
+ ASR_PROVIDER: str = typer.Option(
31
+ "wyoming",
32
+ "--asr-provider",
33
+ envvar="ASR_PROVIDER",
34
+ help="The ASR provider to use ('wyoming', 'openai', 'gemini').",
35
+ rich_help_panel="Provider Selection",
36
+ )
37
+ TTS_PROVIDER: str = typer.Option(
38
+ "wyoming",
39
+ "--tts-provider",
40
+ envvar="TTS_PROVIDER",
41
+ help="The TTS provider to use ('wyoming', 'openai', 'kokoro', 'gemini').",
42
+ rich_help_panel="Provider Selection",
43
+ )
44
+
45
+
46
+ # --- LLM Configuration ---
47
+ LLM: bool = typer.Option(
48
+ False, # noqa: FBT003
49
+ "--llm/--no-llm",
50
+ help="Use an LLM to process the transcript.",
51
+ rich_help_panel="LLM Configuration",
52
+ )
53
+ # Ollama (local service)
54
+ LLM_OLLAMA_MODEL: str = typer.Option(
55
+ "gemma3:4b",
56
+ "--llm-ollama-model",
57
+ envvar="LLM_OLLAMA_MODEL",
58
+ help="The Ollama model to use. Default is gemma3:4b.",
59
+ rich_help_panel="LLM: Ollama",
60
+ )
61
+ LLM_OLLAMA_HOST: str = typer.Option(
62
+ "http://localhost:11434",
63
+ "--llm-ollama-host",
64
+ envvar="LLM_OLLAMA_HOST",
65
+ help="The Ollama server host. Default is http://localhost:11434.",
66
+ rich_help_panel="LLM: Ollama",
67
+ )
68
+ # OpenAI
69
+ LLM_OPENAI_MODEL: str = typer.Option(
70
+ DEFAULT_OPENAI_MODEL,
71
+ "--llm-openai-model",
72
+ envvar="LLM_OPENAI_MODEL",
73
+ help="The OpenAI model to use for LLM tasks.",
74
+ rich_help_panel="LLM: OpenAI-compatible",
75
+ )
76
+ OPENAI_API_KEY: str | None = typer.Option(
77
+ None,
78
+ "--openai-api-key",
79
+ help="Your OpenAI API key. Can also be set with the OPENAI_API_KEY environment variable.",
80
+ envvar="OPENAI_API_KEY",
81
+ rich_help_panel="LLM: OpenAI-compatible",
82
+ )
83
+ OPENAI_BASE_URL: str | None = typer.Option(
84
+ None,
85
+ "--openai-base-url",
86
+ help="Custom base URL for OpenAI-compatible API (e.g., for llama-server: http://localhost:8080/v1).",
87
+ envvar="OPENAI_BASE_URL",
88
+ rich_help_panel="LLM: OpenAI-compatible",
89
+ )
90
+ # Gemini
91
+ LLM_GEMINI_MODEL: str = typer.Option(
92
+ "gemini-3-flash-preview",
93
+ "--llm-gemini-model",
94
+ envvar="LLM_GEMINI_MODEL",
95
+ help="The Gemini model to use for LLM tasks.",
96
+ rich_help_panel="LLM: Gemini",
97
+ )
98
+ GEMINI_API_KEY: str | None = typer.Option(
99
+ None,
100
+ "--gemini-api-key",
101
+ help="Your Gemini API key. Can also be set with the GEMINI_API_KEY environment variable.",
102
+ envvar="GEMINI_API_KEY",
103
+ rich_help_panel="LLM: Gemini",
104
+ )
105
+ EMBEDDING_MODEL: str = typer.Option(
106
+ DEFAULT_OPENAI_EMBEDDING_MODEL,
107
+ "--embedding-model",
108
+ help="Embedding model to use for vectorization.",
109
+ rich_help_panel="LLM Configuration",
110
+ )
111
+
112
+ # --- ASR (Audio) Configuration ---
113
+ # General ASR
114
+ INPUT_DEVICE_INDEX: int | None = typer.Option(
115
+ None,
116
+ "--input-device-index",
117
+ help="Index of the audio input device to use.",
118
+ rich_help_panel="Audio Input",
119
+ )
120
+ INPUT_DEVICE_NAME: str | None = typer.Option(
121
+ None,
122
+ "--input-device-name",
123
+ help="Device name keywords for partial matching.",
124
+ rich_help_panel="Audio Input",
125
+ )
126
+ LIST_DEVICES: bool = typer.Option(
127
+ False, # noqa: FBT003
128
+ "--list-devices",
129
+ help="List available audio input and output devices and exit.",
130
+ is_eager=True,
131
+ rich_help_panel="Audio Input",
132
+ )
133
+ # Wyoming (local service)
134
+ ASR_WYOMING_IP: str = typer.Option(
135
+ "localhost",
136
+ "--asr-wyoming-ip",
137
+ envvar="ASR_WYOMING_IP",
138
+ help="Wyoming ASR server IP address.",
139
+ rich_help_panel="Audio Input: Wyoming",
140
+ )
141
+ ASR_WYOMING_PORT: int = typer.Option(
142
+ 10300,
143
+ "--asr-wyoming-port",
144
+ envvar="ASR_WYOMING_PORT",
145
+ help="Wyoming ASR server port.",
146
+ rich_help_panel="Audio Input: Wyoming",
147
+ )
148
+ # OpenAI
149
+ ASR_OPENAI_MODEL: str = typer.Option(
150
+ "whisper-1",
151
+ "--asr-openai-model",
152
+ envvar="ASR_OPENAI_MODEL",
153
+ help="The OpenAI model to use for ASR (transcription).",
154
+ rich_help_panel="Audio Input: OpenAI-compatible",
155
+ )
156
+ ASR_OPENAI_BASE_URL: str | None = typer.Option(
157
+ None,
158
+ "--asr-openai-base-url",
159
+ envvar="ASR_OPENAI_BASE_URL",
160
+ help="Custom base URL for OpenAI-compatible ASR API (e.g., for custom Whisper server: http://localhost:9898).",
161
+ rich_help_panel="Audio Input: OpenAI-compatible",
162
+ )
163
+ ASR_OPENAI_PROMPT: str | None = typer.Option(
164
+ None,
165
+ "--asr-openai-prompt",
166
+ envvar="ASR_OPENAI_PROMPT",
167
+ help="Custom prompt to guide transcription (optional).",
168
+ rich_help_panel="Audio Input: OpenAI-compatible",
169
+ )
170
+ # Gemini ASR
171
+ ASR_GEMINI_MODEL: str = typer.Option(
172
+ "gemini-3-flash-preview",
173
+ "--asr-gemini-model",
174
+ envvar="ASR_GEMINI_MODEL",
175
+ help="The Gemini model to use for ASR (transcription).",
176
+ rich_help_panel="Audio Input: Gemini",
177
+ )
178
+
179
+
180
+ # --- Wake Word Options ---
181
+ WAKE_SERVER_IP: str = typer.Option(
182
+ "localhost",
183
+ "--wake-server-ip",
184
+ help="Wyoming wake word server IP address.",
185
+ rich_help_panel="Wake Word",
186
+ )
187
+ WAKE_SERVER_PORT: int = typer.Option(
188
+ 10400,
189
+ "--wake-server-port",
190
+ help="Wyoming wake word server port.",
191
+ rich_help_panel="Wake Word",
192
+ )
193
+ WAKE_WORD: str = typer.Option(
194
+ "ok_nabu",
195
+ "--wake-word",
196
+ help="Name of wake word to detect (e.g., 'ok_nabu', 'hey_jarvis').",
197
+ rich_help_panel="Wake Word",
198
+ )
199
+
200
+
201
+ # --- TTS (Text-to-Speech) Configuration ---
202
+ # General TTS
203
+ ENABLE_TTS: bool = typer.Option(
204
+ False, # noqa: FBT003
205
+ "--tts/--no-tts",
206
+ help="Enable text-to-speech for responses.",
207
+ rich_help_panel="Audio Output",
208
+ )
209
+ TTS_SPEED: float = typer.Option(
210
+ 1.0,
211
+ "--tts-speed",
212
+ help="Speech speed multiplier (1.0 = normal, 2.0 = twice as fast, 0.5 = half speed).",
213
+ rich_help_panel="Audio Output",
214
+ )
215
+ OUTPUT_DEVICE_INDEX: int | None = typer.Option(
216
+ None,
217
+ "--output-device-index",
218
+ help="Index of the audio output device to use for TTS.",
219
+ rich_help_panel="Audio Output",
220
+ )
221
+ OUTPUT_DEVICE_NAME: str | None = typer.Option(
222
+ None,
223
+ "--output-device-name",
224
+ help="Output device name keywords for partial matching.",
225
+ rich_help_panel="Audio Output",
226
+ )
227
+ # Wyoming (local service)
228
+ TTS_WYOMING_IP: str = typer.Option(
229
+ "localhost",
230
+ "--tts-wyoming-ip",
231
+ help="Wyoming TTS server IP address.",
232
+ rich_help_panel="Audio Output: Wyoming",
233
+ )
234
+ TTS_WYOMING_PORT: int = typer.Option(
235
+ 10200,
236
+ "--tts-wyoming-port",
237
+ help="Wyoming TTS server port.",
238
+ rich_help_panel="Audio Output: Wyoming",
239
+ )
240
+ TTS_WYOMING_VOICE: str | None = typer.Option(
241
+ None,
242
+ "--tts-wyoming-voice",
243
+ help="Voice name to use for Wyoming TTS (e.g., 'en_US-lessac-medium').",
244
+ rich_help_panel="Audio Output: Wyoming",
245
+ )
246
+ TTS_WYOMING_LANGUAGE: str | None = typer.Option(
247
+ None,
248
+ "--tts-wyoming-language",
249
+ help="Language for Wyoming TTS (e.g., 'en_US').",
250
+ rich_help_panel="Audio Output: Wyoming",
251
+ )
252
+ TTS_WYOMING_SPEAKER: str | None = typer.Option(
253
+ None,
254
+ "--tts-wyoming-speaker",
255
+ help="Speaker name for Wyoming TTS voice.",
256
+ rich_help_panel="Audio Output: Wyoming",
257
+ )
258
+ # OpenAI
259
+ TTS_OPENAI_MODEL: str = typer.Option(
260
+ "tts-1",
261
+ "--tts-openai-model",
262
+ help="The OpenAI model to use for TTS.",
263
+ rich_help_panel="Audio Output: OpenAI-compatible",
264
+ )
265
+ TTS_OPENAI_VOICE: str = typer.Option(
266
+ "alloy",
267
+ "--tts-openai-voice",
268
+ help="The voice to use for OpenAI-compatible TTS.",
269
+ rich_help_panel="Audio Output: OpenAI-compatible",
270
+ )
271
+ TTS_OPENAI_BASE_URL: str | None = typer.Option(
272
+ None,
273
+ "--tts-openai-base-url",
274
+ help="Custom base URL for OpenAI-compatible TTS API (e.g., http://localhost:8000/v1 for a proxy).",
275
+ rich_help_panel="Audio Output: OpenAI-compatible",
276
+ )
277
+
278
+
279
+ # Kokoro
280
+ TTS_KOKORO_MODEL: str = typer.Option(
281
+ "kokoro",
282
+ "--tts-kokoro-model",
283
+ help="The Kokoro model to use for TTS.",
284
+ rich_help_panel="Audio Output: Kokoro",
285
+ )
286
+ TTS_KOKORO_VOICE: str = typer.Option(
287
+ "af_sky",
288
+ "--tts-kokoro-voice",
289
+ help="The voice to use for Kokoro TTS.",
290
+ rich_help_panel="Audio Output: Kokoro",
291
+ )
292
+ TTS_KOKORO_HOST: str = typer.Option(
293
+ "http://localhost:8880/v1",
294
+ "--tts-kokoro-host",
295
+ help="The base URL for the Kokoro API.",
296
+ rich_help_panel="Audio Output: Kokoro",
297
+ )
298
+
299
+ # Gemini TTS
300
+ TTS_GEMINI_MODEL: str = typer.Option(
301
+ "gemini-2.5-flash-preview-tts",
302
+ "--tts-gemini-model",
303
+ help="The Gemini model to use for TTS.",
304
+ rich_help_panel="Audio Output: Gemini",
305
+ )
306
+ TTS_GEMINI_VOICE: str = typer.Option(
307
+ "Kore",
308
+ "--tts-gemini-voice",
309
+ help="The voice to use for Gemini TTS (e.g., 'Kore', 'Puck', 'Charon', 'Fenrir').",
310
+ rich_help_panel="Audio Output: Gemini",
311
+ )
312
+
313
+
314
+ # --- Process Management Options ---
315
+ STOP: bool = typer.Option(
316
+ False, # noqa: FBT003
317
+ "--stop",
318
+ help="Stop any running background process.",
319
+ rich_help_panel="Process Management",
320
+ )
321
+ STATUS: bool = typer.Option(
322
+ False, # noqa: FBT003
323
+ "--status",
324
+ help="Check if a background process is running.",
325
+ rich_help_panel="Process Management",
326
+ )
327
+ TOGGLE: bool = typer.Option(
328
+ False, # noqa: FBT003
329
+ "--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.",
333
+ rich_help_panel="Process Management",
334
+ )
335
+
336
+ # --- General Options ---
337
+
338
+
339
+ def _conf_callback(ctx: typer.Context, param: typer.CallbackParam, value: str) -> str: # noqa: ARG001
340
+ from agent_cli.cli import set_config_defaults # noqa: PLC0415
341
+
342
+ set_config_defaults(ctx, value)
343
+ return value
344
+
345
+
346
+ CONFIG_FILE: str | None = typer.Option(
347
+ None,
348
+ "--config",
349
+ help="Path to a TOML configuration file.",
350
+ is_eager=True,
351
+ callback=_conf_callback,
352
+ rich_help_panel="General Options",
353
+ )
354
+ PRINT_ARGS: bool = typer.Option(
355
+ False, # noqa: FBT003
356
+ "--print-args",
357
+ help="Print the command line arguments, including variables taken from the configuration file.",
358
+ is_eager=True,
359
+ rich_help_panel="General Options",
360
+ )
361
+ CLIPBOARD: bool = typer.Option(
362
+ True, # noqa: FBT003
363
+ "--clipboard/--no-clipboard",
364
+ help="Copy result to clipboard.",
365
+ rich_help_panel="General Options",
366
+ )
367
+ LOG_LEVEL: LogLevel = typer.Option(
368
+ "info",
369
+ "--log-level",
370
+ envvar="LOG_LEVEL",
371
+ help="Set logging level.",
372
+ case_sensitive=False,
373
+ rich_help_panel="General Options",
374
+ )
375
+ LOG_FILE: str | None = typer.Option(
376
+ None,
377
+ "--log-file",
378
+ help="Path to a file to write logs to.",
379
+ rich_help_panel="General Options",
380
+ )
381
+ QUIET: bool = typer.Option(
382
+ False, # noqa: FBT003
383
+ "-q",
384
+ "--quiet",
385
+ help="Suppress console output from rich.",
386
+ rich_help_panel="General Options",
387
+ )
388
+ JSON_OUTPUT: bool = typer.Option(
389
+ False, # noqa: FBT003
390
+ "--json",
391
+ help="Output result as JSON for automation. Implies --quiet and --no-clipboard.",
392
+ rich_help_panel="General Options",
393
+ )
394
+ SAVE_FILE: Path | None = typer.Option(
395
+ None,
396
+ "--save-file",
397
+ help="Save TTS response audio to WAV file.",
398
+ rich_help_panel="General Options",
399
+ )
400
+ TRANSCRIPTION_LOG: Path | None = typer.Option(
401
+ None,
402
+ "--transcription-log",
403
+ help="Path to log transcription results with timestamps, hostname, model, and raw output.",
404
+ rich_help_panel="General Options",
405
+ )
406
+
407
+ # --- Server Options ---
408
+ SERVER_HOST: str = typer.Option(
409
+ "0.0.0.0", # noqa: S104
410
+ "--host",
411
+ help="Host/IP to bind API servers to.",
412
+ rich_help_panel="Server Configuration",
413
+ )
414
+
415
+ # --- Transcribe Specific Options ---
416
+ FROM_FILE: Path | None = typer.Option(
417
+ None,
418
+ "--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
+ rich_help_panel="Audio Recovery",
421
+ )
422
+ LAST_RECORDING: int = typer.Option(
423
+ 0,
424
+ "--last-recording",
425
+ help="Transcribe a saved recording. Use 1 for most recent, 2 for second-to-last, etc. Use 0 to disable (default).",
426
+ rich_help_panel="Audio Recovery",
427
+ )
428
+ SAVE_RECORDING: bool = typer.Option(
429
+ True, # noqa: FBT003
430
+ "--save-recording/--no-save-recording",
431
+ help="Save the audio recording to disk for recovery.",
432
+ rich_help_panel="Audio Recovery",
433
+ )
agent_cli/py.typed ADDED
File without changes
@@ -0,0 +1,3 @@
1
+ """RAG module."""
2
+
3
+ from __future__ import annotations
@@ -0,0 +1,67 @@
1
+ """File watcher and indexing logic using watchfiles."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import logging
6
+ from typing import TYPE_CHECKING
7
+
8
+ from agent_cli.core.watch import watch_directory
9
+ from agent_cli.rag._indexing import index_file, remove_file
10
+ from agent_cli.rag._utils import should_ignore_path
11
+
12
+ if TYPE_CHECKING:
13
+ from pathlib import Path
14
+
15
+ from chromadb import Collection
16
+ from watchfiles import Change
17
+
18
+ LOGGER = logging.getLogger(__name__)
19
+
20
+
21
+ async def watch_docs(
22
+ collection: Collection,
23
+ docs_folder: Path,
24
+ file_hashes: dict[str, str],
25
+ file_mtimes: dict[str, float],
26
+ ) -> None:
27
+ """Watch docs folder for changes and update index asynchronously."""
28
+ LOGGER.info("📁 Watching folder: %s", docs_folder)
29
+
30
+ await watch_directory(
31
+ docs_folder,
32
+ lambda change, path: _handle_change(
33
+ change,
34
+ path,
35
+ collection,
36
+ docs_folder,
37
+ file_hashes,
38
+ file_mtimes,
39
+ ),
40
+ ignore_filter=should_ignore_path,
41
+ )
42
+
43
+
44
+ def _handle_change(
45
+ change: Change,
46
+ file_path: Path,
47
+ collection: Collection,
48
+ docs_folder: Path,
49
+ file_hashes: dict[str, str],
50
+ file_mtimes: dict[str, float],
51
+ ) -> None:
52
+ from watchfiles import Change # noqa: PLC0415
53
+
54
+ try:
55
+ if change == Change.deleted:
56
+ LOGGER.info("[deleted] Removing from index: %s", file_path.name)
57
+ remove_file(collection, docs_folder, file_path, file_hashes, file_mtimes)
58
+ return
59
+ if change in {Change.added, Change.modified} and file_path.is_file():
60
+ action = "created" if change == Change.added else "modified"
61
+ LOGGER.info("[%s] Indexing: %s", action, file_path.name)
62
+ index_file(collection, docs_folder, file_path, file_hashes, file_mtimes)
63
+ except (OSError, UnicodeDecodeError):
64
+ LOGGER.warning("Watcher handler transient IO error for %s", file_path, exc_info=True)
65
+ except Exception:
66
+ LOGGER.exception("Watcher handler failed for %s", file_path)
67
+ raise