agent-cli 0.66.2__py3-none-any.whl → 0.67.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.
- agent_cli/_extras.json +13 -0
- agent_cli/_requirements/audio.txt +60 -0
- agent_cli/_requirements/{whisper.txt → faster-whisper.txt} +4 -112
- agent_cli/_requirements/{tts-kokoro.txt → kokoro.txt} +4 -107
- agent_cli/_requirements/llm.txt +177 -0
- agent_cli/_requirements/memory.txt +6 -97
- agent_cli/_requirements/{whisper-mlx.txt → mlx-whisper.txt} +61 -124
- agent_cli/_requirements/{tts.txt → piper.txt} +4 -121
- agent_cli/_requirements/rag.txt +9 -96
- agent_cli/_requirements/server.txt +3 -120
- agent_cli/_requirements/speed.txt +5 -138
- agent_cli/_requirements/vad.txt +4 -137
- agent_cli/agents/assistant.py +2 -0
- agent_cli/agents/autocorrect.py +2 -0
- agent_cli/agents/chat.py +2 -0
- agent_cli/agents/memory/add.py +2 -0
- agent_cli/agents/memory/proxy.py +2 -0
- agent_cli/agents/rag_proxy.py +2 -0
- agent_cli/agents/speak.py +2 -0
- agent_cli/agents/transcribe.py +2 -0
- agent_cli/agents/transcribe_daemon.py +2 -0
- agent_cli/agents/voice_edit.py +2 -0
- agent_cli/core/deps.py +130 -14
- agent_cli/dev/skill/SKILL.md +2 -2
- agent_cli/docs_gen.py +0 -42
- agent_cli/install/extras.py +6 -14
- agent_cli/memory/__init__.py +1 -18
- agent_cli/rag/__init__.py +0 -19
- agent_cli/scripts/sync_extras.py +138 -0
- agent_cli/server/cli.py +4 -0
- agent_cli/services/_wyoming_utils.py +4 -2
- agent_cli/services/asr.py +13 -3
- agent_cli/services/tts.py +5 -2
- agent_cli/services/wake_word.py +6 -3
- {agent_cli-0.66.2.dist-info → agent_cli-0.67.0.dist-info}/METADATA +33 -29
- {agent_cli-0.66.2.dist-info → agent_cli-0.67.0.dist-info}/RECORD +39 -35
- {agent_cli-0.66.2.dist-info → agent_cli-0.67.0.dist-info}/WHEEL +0 -0
- {agent_cli-0.66.2.dist-info → agent_cli-0.67.0.dist-info}/entry_points.txt +0 -0
- {agent_cli-0.66.2.dist-info → agent_cli-0.67.0.dist-info}/licenses/LICENSE +0 -0
agent_cli/_requirements/vad.txt
CHANGED
|
@@ -1,100 +1,39 @@
|
|
|
1
1
|
# This file was autogenerated by uv via the following command:
|
|
2
2
|
# uv export --extra vad --no-dev --no-emit-project --no-hashes
|
|
3
|
-
annotated-types==0.7.0
|
|
4
|
-
# via pydantic
|
|
5
3
|
anyio==4.12.1
|
|
6
|
-
# via
|
|
7
|
-
# google-genai
|
|
8
|
-
# httpx
|
|
9
|
-
# openai
|
|
10
|
-
brotli==1.2.0 ; platform_python_implementation == 'CPython'
|
|
11
|
-
# via httpx
|
|
12
|
-
brotlicffi==1.2.0.0 ; platform_python_implementation != 'CPython'
|
|
13
4
|
# via httpx
|
|
14
5
|
certifi==2026.1.4
|
|
15
6
|
# via
|
|
16
7
|
# httpcore
|
|
17
8
|
# httpx
|
|
18
|
-
# requests
|
|
19
|
-
cffi==2.0.0
|
|
20
|
-
# via
|
|
21
|
-
# brotlicffi
|
|
22
|
-
# sounddevice
|
|
23
|
-
charset-normalizer==3.4.4
|
|
24
|
-
# via requests
|
|
25
9
|
click==8.3.1
|
|
26
|
-
# via
|
|
27
|
-
|
|
28
|
-
#
|
|
29
|
-
colorama==0.4.6
|
|
30
|
-
# via
|
|
31
|
-
# click
|
|
32
|
-
# griffe
|
|
33
|
-
# tqdm
|
|
10
|
+
# via typer
|
|
11
|
+
colorama==0.4.6 ; sys_platform == 'win32'
|
|
12
|
+
# via click
|
|
34
13
|
coloredlogs==15.0.1
|
|
35
14
|
# via onnxruntime
|
|
36
|
-
ddgs==9.10.0
|
|
37
|
-
# via pydantic-ai-slim
|
|
38
|
-
distro==1.9.0
|
|
39
|
-
# via
|
|
40
|
-
# google-genai
|
|
41
|
-
# openai
|
|
42
15
|
dotenv==0.9.9
|
|
43
16
|
# via agent-cli
|
|
44
|
-
fake-useragent==2.2.0
|
|
45
|
-
# via ddgs
|
|
46
17
|
filelock==3.20.3
|
|
47
18
|
# via torch
|
|
48
19
|
flatbuffers==25.12.19
|
|
49
20
|
# via onnxruntime
|
|
50
21
|
fsspec==2026.1.0
|
|
51
22
|
# via torch
|
|
52
|
-
genai-prices==0.0.51
|
|
53
|
-
# via pydantic-ai-slim
|
|
54
|
-
google-auth==2.47.0
|
|
55
|
-
# via
|
|
56
|
-
# google-genai
|
|
57
|
-
# pydantic-ai-slim
|
|
58
|
-
google-genai==1.58.0
|
|
59
|
-
# via agent-cli
|
|
60
|
-
griffe==1.15.0
|
|
61
|
-
# via pydantic-ai-slim
|
|
62
23
|
h11==0.16.0
|
|
63
24
|
# via httpcore
|
|
64
|
-
h2==4.3.0
|
|
65
|
-
# via httpx
|
|
66
|
-
hpack==4.1.0
|
|
67
|
-
# via h2
|
|
68
25
|
httpcore==1.0.9
|
|
69
26
|
# via httpx
|
|
70
27
|
httpx==0.28.1
|
|
71
|
-
# via
|
|
72
|
-
# agent-cli
|
|
73
|
-
# ddgs
|
|
74
|
-
# genai-prices
|
|
75
|
-
# google-genai
|
|
76
|
-
# openai
|
|
77
|
-
# pydantic-ai-slim
|
|
78
|
-
# pydantic-graph
|
|
28
|
+
# via agent-cli
|
|
79
29
|
humanfriendly==10.0
|
|
80
30
|
# via coloredlogs
|
|
81
|
-
hyperframe==6.1.0
|
|
82
|
-
# via h2
|
|
83
31
|
idna==3.11
|
|
84
32
|
# via
|
|
85
33
|
# anyio
|
|
86
34
|
# httpx
|
|
87
|
-
# requests
|
|
88
|
-
importlib-metadata==8.7.1
|
|
89
|
-
# via opentelemetry-api
|
|
90
35
|
jinja2==3.1.6
|
|
91
36
|
# via torch
|
|
92
|
-
jiter==0.12.0
|
|
93
|
-
# via openai
|
|
94
|
-
logfire-api==4.18.0
|
|
95
|
-
# via pydantic-graph
|
|
96
|
-
lxml==6.0.2
|
|
97
|
-
# via ddgs
|
|
98
37
|
markdown-it-py==4.0.0
|
|
99
38
|
# via rich
|
|
100
39
|
markupsafe==3.0.3
|
|
@@ -150,43 +89,14 @@ nvidia-nvtx-cu12==12.8.90 ; platform_machine == 'x86_64' and sys_platform == 'li
|
|
|
150
89
|
# via torch
|
|
151
90
|
onnxruntime==1.20.1
|
|
152
91
|
# via silero-vad
|
|
153
|
-
openai==2.15.0
|
|
154
|
-
# via
|
|
155
|
-
# agent-cli
|
|
156
|
-
# pydantic-ai-slim
|
|
157
|
-
opentelemetry-api==1.39.1
|
|
158
|
-
# via pydantic-ai-slim
|
|
159
92
|
packaging==25.0
|
|
160
93
|
# via
|
|
161
94
|
# onnxruntime
|
|
162
95
|
# silero-vad
|
|
163
|
-
primp==0.15.0
|
|
164
|
-
# via ddgs
|
|
165
96
|
protobuf==6.33.4
|
|
166
97
|
# via onnxruntime
|
|
167
98
|
psutil==7.2.1 ; sys_platform == 'win32'
|
|
168
99
|
# via agent-cli
|
|
169
|
-
pyasn1==0.6.1
|
|
170
|
-
# via
|
|
171
|
-
# pyasn1-modules
|
|
172
|
-
# rsa
|
|
173
|
-
pyasn1-modules==0.4.2
|
|
174
|
-
# via google-auth
|
|
175
|
-
pycparser==2.23 ; implementation_name != 'PyPy'
|
|
176
|
-
# via cffi
|
|
177
|
-
pydantic==2.12.5
|
|
178
|
-
# via
|
|
179
|
-
# genai-prices
|
|
180
|
-
# google-genai
|
|
181
|
-
# openai
|
|
182
|
-
# pydantic-ai-slim
|
|
183
|
-
# pydantic-graph
|
|
184
|
-
pydantic-ai-slim==1.42.0
|
|
185
|
-
# via agent-cli
|
|
186
|
-
pydantic-core==2.41.5
|
|
187
|
-
# via pydantic
|
|
188
|
-
pydantic-graph==1.42.0
|
|
189
|
-
# via pydantic-ai-slim
|
|
190
100
|
pygments==2.19.2
|
|
191
101
|
# via rich
|
|
192
102
|
pyperclip==1.11.0
|
|
@@ -195,20 +105,10 @@ pyreadline3==3.5.4 ; sys_platform == 'win32'
|
|
|
195
105
|
# via humanfriendly
|
|
196
106
|
python-dotenv==1.2.1
|
|
197
107
|
# via dotenv
|
|
198
|
-
regex==2026.1.15
|
|
199
|
-
# via tiktoken
|
|
200
|
-
requests==2.32.5
|
|
201
|
-
# via
|
|
202
|
-
# google-auth
|
|
203
|
-
# google-genai
|
|
204
|
-
# pydantic-ai-slim
|
|
205
|
-
# tiktoken
|
|
206
108
|
rich==14.2.0
|
|
207
109
|
# via
|
|
208
110
|
# agent-cli
|
|
209
111
|
# typer
|
|
210
|
-
rsa==4.9.1
|
|
211
|
-
# via google-auth
|
|
212
112
|
setproctitle==1.3.7
|
|
213
113
|
# via agent-cli
|
|
214
114
|
setuptools==80.9.0 ; python_full_version >= '3.12'
|
|
@@ -217,30 +117,16 @@ shellingham==1.5.4
|
|
|
217
117
|
# via typer
|
|
218
118
|
silero-vad==6.2.0
|
|
219
119
|
# via agent-cli
|
|
220
|
-
sniffio==1.3.1
|
|
221
|
-
# via
|
|
222
|
-
# google-genai
|
|
223
|
-
# openai
|
|
224
|
-
socksio==1.0.0
|
|
225
|
-
# via httpx
|
|
226
|
-
sounddevice==0.5.3
|
|
227
|
-
# via agent-cli
|
|
228
120
|
sympy==1.14.0
|
|
229
121
|
# via
|
|
230
122
|
# onnxruntime
|
|
231
123
|
# torch
|
|
232
|
-
tenacity==9.1.2
|
|
233
|
-
# via google-genai
|
|
234
|
-
tiktoken==0.12.0
|
|
235
|
-
# via pydantic-ai-slim
|
|
236
124
|
torch==2.9.1
|
|
237
125
|
# via
|
|
238
126
|
# silero-vad
|
|
239
127
|
# torchaudio
|
|
240
128
|
torchaudio==2.9.1
|
|
241
129
|
# via silero-vad
|
|
242
|
-
tqdm==4.67.1
|
|
243
|
-
# via openai
|
|
244
130
|
triton==3.5.1 ; platform_machine == 'x86_64' and sys_platform == 'linux'
|
|
245
131
|
# via torch
|
|
246
132
|
typer==0.21.1
|
|
@@ -248,24 +134,5 @@ typer==0.21.1
|
|
|
248
134
|
typing-extensions==4.15.0
|
|
249
135
|
# via
|
|
250
136
|
# anyio
|
|
251
|
-
# google-genai
|
|
252
|
-
# openai
|
|
253
|
-
# opentelemetry-api
|
|
254
|
-
# pydantic
|
|
255
|
-
# pydantic-core
|
|
256
137
|
# torch
|
|
257
138
|
# typer
|
|
258
|
-
# typing-inspection
|
|
259
|
-
typing-inspection==0.4.2
|
|
260
|
-
# via
|
|
261
|
-
# pydantic
|
|
262
|
-
# pydantic-ai-slim
|
|
263
|
-
# pydantic-graph
|
|
264
|
-
urllib3==2.3.0
|
|
265
|
-
# via requests
|
|
266
|
-
websockets==15.0.1
|
|
267
|
-
# via google-genai
|
|
268
|
-
wyoming==1.8.0
|
|
269
|
-
# via agent-cli
|
|
270
|
-
zipp==3.23.0
|
|
271
|
-
# via importlib-metadata
|
agent_cli/agents/assistant.py
CHANGED
|
@@ -41,6 +41,7 @@ from agent_cli.agents._voice_agent_common import (
|
|
|
41
41
|
from agent_cli.cli import app
|
|
42
42
|
from agent_cli.core import audio, process
|
|
43
43
|
from agent_cli.core.audio import setup_devices
|
|
44
|
+
from agent_cli.core.deps import requires_extras
|
|
44
45
|
from agent_cli.core.utils import (
|
|
45
46
|
InteractiveStopEvent,
|
|
46
47
|
maybe_live,
|
|
@@ -254,6 +255,7 @@ async def _async_main(
|
|
|
254
255
|
|
|
255
256
|
|
|
256
257
|
@app.command("assistant", rich_help_panel="Voice Commands")
|
|
258
|
+
@requires_extras("audio", "llm")
|
|
257
259
|
def assistant(
|
|
258
260
|
*,
|
|
259
261
|
# --- Provider Selection ---
|
agent_cli/agents/autocorrect.py
CHANGED
|
@@ -14,6 +14,7 @@ import typer
|
|
|
14
14
|
|
|
15
15
|
from agent_cli import config, opts
|
|
16
16
|
from agent_cli.cli import app
|
|
17
|
+
from agent_cli.core.deps import requires_extras
|
|
17
18
|
from agent_cli.core.utils import (
|
|
18
19
|
console,
|
|
19
20
|
create_status,
|
|
@@ -209,6 +210,7 @@ async def _async_autocorrect(
|
|
|
209
210
|
|
|
210
211
|
|
|
211
212
|
@app.command("autocorrect", rich_help_panel="Text Commands")
|
|
213
|
+
@requires_extras("llm")
|
|
212
214
|
def autocorrect(
|
|
213
215
|
*,
|
|
214
216
|
text: str | None = typer.Argument(
|
agent_cli/agents/chat.py
CHANGED
|
@@ -29,6 +29,7 @@ from agent_cli._tools import tools
|
|
|
29
29
|
from agent_cli.cli import app
|
|
30
30
|
from agent_cli.core import process
|
|
31
31
|
from agent_cli.core.audio import setup_devices
|
|
32
|
+
from agent_cli.core.deps import requires_extras
|
|
32
33
|
from agent_cli.core.utils import (
|
|
33
34
|
InteractiveStopEvent,
|
|
34
35
|
console,
|
|
@@ -374,6 +375,7 @@ async def _async_main(
|
|
|
374
375
|
|
|
375
376
|
|
|
376
377
|
@app.command("chat", rich_help_panel="Voice Commands")
|
|
378
|
+
@requires_extras("audio", "llm")
|
|
377
379
|
def chat(
|
|
378
380
|
*,
|
|
379
381
|
# --- Provider Selection ---
|
agent_cli/agents/memory/add.py
CHANGED
|
@@ -13,6 +13,7 @@ import typer
|
|
|
13
13
|
|
|
14
14
|
from agent_cli import opts
|
|
15
15
|
from agent_cli.agents.memory import memory_app
|
|
16
|
+
from agent_cli.core.deps import requires_extras
|
|
16
17
|
from agent_cli.core.utils import console, print_command_line_args
|
|
17
18
|
|
|
18
19
|
if TYPE_CHECKING:
|
|
@@ -110,6 +111,7 @@ def _write_memories(
|
|
|
110
111
|
|
|
111
112
|
|
|
112
113
|
@memory_app.command("add")
|
|
114
|
+
@requires_extras("memory")
|
|
113
115
|
def add(
|
|
114
116
|
memories: list[str] = typer.Argument( # noqa: B008
|
|
115
117
|
None,
|
agent_cli/agents/memory/proxy.py
CHANGED
|
@@ -10,10 +10,12 @@ from rich.logging import RichHandler
|
|
|
10
10
|
|
|
11
11
|
from agent_cli import constants, opts
|
|
12
12
|
from agent_cli.agents.memory import memory_app
|
|
13
|
+
from agent_cli.core.deps import requires_extras
|
|
13
14
|
from agent_cli.core.utils import console, print_command_line_args, print_error_message
|
|
14
15
|
|
|
15
16
|
|
|
16
17
|
@memory_app.command("proxy")
|
|
18
|
+
@requires_extras("memory")
|
|
17
19
|
def proxy(
|
|
18
20
|
memory_path: Path = typer.Option( # noqa: B008
|
|
19
21
|
"./memory_db",
|
agent_cli/agents/rag_proxy.py
CHANGED
|
@@ -10,6 +10,7 @@ from rich.logging import RichHandler
|
|
|
10
10
|
|
|
11
11
|
from agent_cli import constants, opts
|
|
12
12
|
from agent_cli.cli import app
|
|
13
|
+
from agent_cli.core.deps import requires_extras
|
|
13
14
|
from agent_cli.core.utils import (
|
|
14
15
|
console,
|
|
15
16
|
print_command_line_args,
|
|
@@ -18,6 +19,7 @@ from agent_cli.core.utils import (
|
|
|
18
19
|
|
|
19
20
|
|
|
20
21
|
@app.command("rag-proxy", rich_help_panel="Servers")
|
|
22
|
+
@requires_extras("rag")
|
|
21
23
|
def rag_proxy(
|
|
22
24
|
docs_folder: Path = typer.Option( # noqa: B008
|
|
23
25
|
"./rag_docs",
|
agent_cli/agents/speak.py
CHANGED
|
@@ -14,6 +14,7 @@ from agent_cli import config, opts
|
|
|
14
14
|
from agent_cli.cli import app
|
|
15
15
|
from agent_cli.core import process
|
|
16
16
|
from agent_cli.core.audio import setup_devices
|
|
17
|
+
from agent_cli.core.deps import requires_extras
|
|
17
18
|
from agent_cli.core.utils import (
|
|
18
19
|
enable_json_mode,
|
|
19
20
|
get_clipboard_text,
|
|
@@ -80,6 +81,7 @@ async def _async_main(
|
|
|
80
81
|
|
|
81
82
|
|
|
82
83
|
@app.command("speak", rich_help_panel="Text Commands")
|
|
84
|
+
@requires_extras("audio")
|
|
83
85
|
def speak(
|
|
84
86
|
*,
|
|
85
87
|
text: str | None = typer.Argument(
|
agent_cli/agents/transcribe.py
CHANGED
|
@@ -19,6 +19,7 @@ from agent_cli import config, opts
|
|
|
19
19
|
from agent_cli.cli import app
|
|
20
20
|
from agent_cli.core import process
|
|
21
21
|
from agent_cli.core.audio import setup_devices
|
|
22
|
+
from agent_cli.core.deps import requires_extras
|
|
22
23
|
from agent_cli.core.utils import (
|
|
23
24
|
enable_json_mode,
|
|
24
25
|
format_short_timedelta,
|
|
@@ -461,6 +462,7 @@ async def _async_main( # noqa: PLR0912, PLR0915, C901
|
|
|
461
462
|
|
|
462
463
|
|
|
463
464
|
@app.command("transcribe", rich_help_panel="Voice Commands")
|
|
465
|
+
@requires_extras("audio", "llm")
|
|
464
466
|
def transcribe( # noqa: PLR0912
|
|
465
467
|
*,
|
|
466
468
|
extra_instructions: str | None = typer.Option(
|
|
@@ -25,6 +25,7 @@ from agent_cli.cli import app
|
|
|
25
25
|
from agent_cli.core import process
|
|
26
26
|
from agent_cli.core.audio import open_audio_stream, setup_devices, setup_input_stream
|
|
27
27
|
from agent_cli.core.audio_format import check_ffmpeg_available, save_audio_as_mp3
|
|
28
|
+
from agent_cli.core.deps import requires_extras
|
|
28
29
|
from agent_cli.core.utils import (
|
|
29
30
|
console,
|
|
30
31
|
print_command_line_args,
|
|
@@ -287,6 +288,7 @@ async def _daemon_loop(cfg: DaemonConfig) -> None: # noqa: PLR0912, PLR0915
|
|
|
287
288
|
|
|
288
289
|
|
|
289
290
|
@app.command("transcribe-daemon", rich_help_panel="Voice Commands")
|
|
291
|
+
@requires_extras("audio", "vad", "llm")
|
|
290
292
|
def transcribe_daemon( # noqa: PLR0912
|
|
291
293
|
*,
|
|
292
294
|
# Daemon-specific options
|
agent_cli/agents/voice_edit.py
CHANGED
|
@@ -47,6 +47,7 @@ from agent_cli.agents._voice_agent_common import (
|
|
|
47
47
|
from agent_cli.cli import app
|
|
48
48
|
from agent_cli.core import process
|
|
49
49
|
from agent_cli.core.audio import setup_devices
|
|
50
|
+
from agent_cli.core.deps import requires_extras
|
|
50
51
|
from agent_cli.core.utils import (
|
|
51
52
|
enable_json_mode,
|
|
52
53
|
get_clipboard_text,
|
|
@@ -173,6 +174,7 @@ async def _async_main(
|
|
|
173
174
|
|
|
174
175
|
|
|
175
176
|
@app.command("voice-edit", rich_help_panel="Voice Commands")
|
|
177
|
+
@requires_extras("audio", "llm")
|
|
176
178
|
def voice_edit(
|
|
177
179
|
*,
|
|
178
180
|
# --- Provider Selection ---
|
agent_cli/core/deps.py
CHANGED
|
@@ -2,22 +2,138 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import functools
|
|
6
|
+
import json
|
|
5
7
|
from importlib.util import find_spec
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import TYPE_CHECKING, TypeVar
|
|
6
10
|
|
|
11
|
+
import typer
|
|
7
12
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
from agent_cli.core.utils import print_error_message
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from collections.abc import Callable
|
|
17
|
+
|
|
18
|
+
F = TypeVar("F", bound="Callable[..., object]")
|
|
19
|
+
|
|
20
|
+
# Load extras from JSON file
|
|
21
|
+
_EXTRAS_FILE = Path(__file__).parent.parent / "_extras.json"
|
|
22
|
+
EXTRAS: dict[str, tuple[str, list[str]]] = {
|
|
23
|
+
k: (v[0], v[1]) for k, v in json.loads(_EXTRAS_FILE.read_text()).items()
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _check_package_installed(pkg: str) -> bool:
|
|
28
|
+
"""Check if a single package is installed."""
|
|
29
|
+
top_module = pkg.split(".")[0]
|
|
30
|
+
try:
|
|
31
|
+
return find_spec(top_module) is not None
|
|
32
|
+
except (ValueError, ModuleNotFoundError):
|
|
33
|
+
return False
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def check_extra_installed(extra: str) -> bool:
|
|
37
|
+
"""Check if packages for an extra are installed using find_spec (no actual import).
|
|
38
|
+
|
|
39
|
+
Supports `|` syntax for alternatives: "piper|kokoro" means ANY of these extras.
|
|
40
|
+
For regular extras, ALL packages must be installed.
|
|
41
|
+
"""
|
|
42
|
+
# Handle "extra1|extra2" syntax - any of these extras is sufficient
|
|
43
|
+
if "|" in extra:
|
|
44
|
+
return any(check_extra_installed(e) for e in extra.split("|"))
|
|
45
|
+
|
|
46
|
+
if extra not in EXTRAS:
|
|
47
|
+
return True # Unknown extra, assume OK
|
|
48
|
+
_, packages = EXTRAS[extra]
|
|
49
|
+
|
|
50
|
+
# All packages must be installed
|
|
51
|
+
return all(_check_package_installed(pkg) for pkg in packages)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def get_install_hint(extra: str) -> str:
|
|
55
|
+
"""Get install command hint for an extra.
|
|
56
|
+
|
|
57
|
+
Supports `|` syntax for alternatives: "piper|kokoro" shows both options.
|
|
58
|
+
"""
|
|
59
|
+
# Handle "extra1|extra2" syntax - show all options
|
|
60
|
+
if "|" in extra:
|
|
61
|
+
alternatives = extra.split("|")
|
|
62
|
+
options = []
|
|
63
|
+
for alt in alternatives:
|
|
64
|
+
desc, _ = EXTRAS.get(alt, ("", []))
|
|
65
|
+
options.append((alt, desc))
|
|
66
|
+
|
|
67
|
+
lines = ["This command requires one of:"]
|
|
68
|
+
for alt, desc in options:
|
|
69
|
+
if desc:
|
|
70
|
+
lines.append(f" - '{alt}' ({desc})")
|
|
71
|
+
else:
|
|
72
|
+
lines.append(f" - '{alt}'")
|
|
73
|
+
lines.append("")
|
|
74
|
+
lines.append("Install one with:")
|
|
75
|
+
for alt, _ in options:
|
|
76
|
+
lines.append(f' uv tool install "agent-cli[{alt}]" -p 3.13')
|
|
77
|
+
lines.append(" # or")
|
|
78
|
+
for alt, _ in options:
|
|
79
|
+
lines.append(f" agent-cli install-extras {alt}")
|
|
80
|
+
return "\n".join(lines)
|
|
81
|
+
|
|
82
|
+
desc, _ = EXTRAS.get(extra, ("", []))
|
|
83
|
+
lines = [
|
|
84
|
+
f"This command requires the '{extra}' extra",
|
|
17
85
|
]
|
|
18
|
-
if
|
|
19
|
-
|
|
86
|
+
if desc:
|
|
87
|
+
lines[0] += f" ({desc})"
|
|
88
|
+
lines[0] += "."
|
|
89
|
+
lines.append("")
|
|
90
|
+
lines.append("Install with:")
|
|
91
|
+
lines.append(f' uv tool install "agent-cli[{extra}]" -p 3.13')
|
|
92
|
+
lines.append(" # or")
|
|
93
|
+
lines.append(f" agent-cli install-extras {extra}")
|
|
94
|
+
return "\n".join(lines)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def requires_extras(*extras: str) -> Callable[[F], F]:
|
|
98
|
+
"""Decorator to declare required extras for a command.
|
|
99
|
+
|
|
100
|
+
When a required extra is missing, the decorator prints a helpful error
|
|
101
|
+
message and exits with code 1.
|
|
102
|
+
|
|
103
|
+
The decorator stores the required extras on the function for test validation.
|
|
104
|
+
|
|
105
|
+
Process management flags (--stop, --status, --toggle) skip the dependency
|
|
106
|
+
check since they just manage running processes without using the actual
|
|
107
|
+
dependencies.
|
|
108
|
+
|
|
109
|
+
Example:
|
|
110
|
+
@app.command("rag-proxy")
|
|
111
|
+
@requires_extras("rag")
|
|
112
|
+
def rag_proxy(...):
|
|
113
|
+
...
|
|
114
|
+
|
|
115
|
+
"""
|
|
116
|
+
|
|
117
|
+
def decorator(func: F) -> F:
|
|
118
|
+
# Store extras on function for test introspection
|
|
119
|
+
func._required_extras = extras # type: ignore[attr-defined]
|
|
120
|
+
|
|
121
|
+
@functools.wraps(func)
|
|
122
|
+
def wrapper(*args: object, **kwargs: object) -> object:
|
|
123
|
+
# Skip dependency check for process management and info operations
|
|
124
|
+
# These don't need the actual dependencies, just manage processes or list info
|
|
125
|
+
if any(kwargs.get(flag) for flag in ("stop", "status", "toggle", "list_devices")):
|
|
126
|
+
return func(*args, **kwargs)
|
|
127
|
+
|
|
128
|
+
missing = [e for e in extras if not check_extra_installed(e)]
|
|
129
|
+
if missing:
|
|
130
|
+
for extra in missing:
|
|
131
|
+
print_error_message(get_install_hint(extra))
|
|
132
|
+
raise typer.Exit(1)
|
|
133
|
+
return func(*args, **kwargs)
|
|
134
|
+
|
|
135
|
+
# Preserve the extras on wrapper too
|
|
136
|
+
wrapper._required_extras = extras # type: ignore[attr-defined]
|
|
137
|
+
return wrapper # type: ignore[return-value]
|
|
20
138
|
|
|
21
|
-
|
|
22
|
-
msg = f"Missing required dependencies for {extra_name}: {', '.join(missing)}. Please install with {hint}."
|
|
23
|
-
raise ImportError(msg)
|
|
139
|
+
return decorator
|
agent_cli/dev/skill/SKILL.md
CHANGED
|
@@ -13,10 +13,10 @@ If `agent-cli` is not available, install it first:
|
|
|
13
13
|
|
|
14
14
|
```bash
|
|
15
15
|
# Install globally
|
|
16
|
-
uv tool install agent-cli
|
|
16
|
+
uv tool install agent-cli -p 3.13
|
|
17
17
|
|
|
18
18
|
# Or run directly without installing
|
|
19
|
-
uvx agent-cli dev new <branch-name> --agent --prompt "..."
|
|
19
|
+
uvx --python 3.13 agent-cli dev new <branch-name> --agent --prompt "..."
|
|
20
20
|
```
|
|
21
21
|
|
|
22
22
|
## When to spawn parallel agents
|
agent_cli/docs_gen.py
CHANGED
|
@@ -16,7 +16,6 @@ Example usage in Markdown files:
|
|
|
16
16
|
|
|
17
17
|
from __future__ import annotations
|
|
18
18
|
|
|
19
|
-
from pathlib import Path
|
|
20
19
|
from typing import Any, get_origin
|
|
21
20
|
|
|
22
21
|
import click
|
|
@@ -369,47 +368,6 @@ def config_example(command_path: str | None = None) -> str:
|
|
|
369
368
|
return "\n".join(lines)
|
|
370
369
|
|
|
371
370
|
|
|
372
|
-
def readme_section(section_name: str) -> str:
|
|
373
|
-
"""Extract a section from README.md for reuse in other docs.
|
|
374
|
-
|
|
375
|
-
Sections are marked with HTML comments like:
|
|
376
|
-
<!-- SECTION:section_name:START -->
|
|
377
|
-
Content here...
|
|
378
|
-
<!-- SECTION:section_name:END -->
|
|
379
|
-
|
|
380
|
-
Args:
|
|
381
|
-
section_name: The name of the section to extract (e.g., "why-i-built-this")
|
|
382
|
-
|
|
383
|
-
Returns:
|
|
384
|
-
The content between the section markers (without the markers themselves)
|
|
385
|
-
|
|
386
|
-
"""
|
|
387
|
-
# Find the README.md relative to this module
|
|
388
|
-
readme_path = Path(__file__).parent.parent / "README.md"
|
|
389
|
-
if not readme_path.exists():
|
|
390
|
-
return f"*Could not find README.md at {readme_path}*"
|
|
391
|
-
|
|
392
|
-
content = readme_path.read_text()
|
|
393
|
-
|
|
394
|
-
# Look for section markers
|
|
395
|
-
start_marker = f"<!-- SECTION:{section_name}:START -->"
|
|
396
|
-
end_marker = f"<!-- SECTION:{section_name}:END -->"
|
|
397
|
-
|
|
398
|
-
start_idx = content.find(start_marker)
|
|
399
|
-
if start_idx == -1:
|
|
400
|
-
return f"*Section '{section_name}' not found in README.md*"
|
|
401
|
-
|
|
402
|
-
end_idx = content.find(end_marker, start_idx)
|
|
403
|
-
if end_idx == -1:
|
|
404
|
-
return f"*End marker for section '{section_name}' not found in README.md*"
|
|
405
|
-
|
|
406
|
-
# Extract content between markers (excluding the markers themselves)
|
|
407
|
-
section_content = content[start_idx + len(start_marker) : end_idx]
|
|
408
|
-
|
|
409
|
-
# Strip leading/trailing whitespace but preserve internal formatting
|
|
410
|
-
return section_content.strip()
|
|
411
|
-
|
|
412
|
-
|
|
413
371
|
def all_options_for_docs(command_path: str) -> str:
|
|
414
372
|
"""Generate complete options documentation for a command page.
|
|
415
373
|
|
agent_cli/install/extras.py
CHANGED
|
@@ -12,20 +12,11 @@ from typing import Annotated
|
|
|
12
12
|
import typer
|
|
13
13
|
|
|
14
14
|
from agent_cli.cli import app
|
|
15
|
+
from agent_cli.core.deps import EXTRAS as _EXTRAS_META
|
|
15
16
|
from agent_cli.core.utils import console, print_error_message
|
|
16
17
|
|
|
17
|
-
#
|
|
18
|
-
EXTRAS: dict[str, str] = {
|
|
19
|
-
"rag": "RAG proxy (ChromaDB, embeddings)",
|
|
20
|
-
"memory": "Long-term memory proxy",
|
|
21
|
-
"vad": "Voice Activity Detection (silero-vad)",
|
|
22
|
-
"whisper": "Local Whisper ASR (faster-whisper)",
|
|
23
|
-
"whisper-mlx": "MLX Whisper for Apple Silicon",
|
|
24
|
-
"tts": "Local Piper TTS",
|
|
25
|
-
"tts-kokoro": "Kokoro neural TTS",
|
|
26
|
-
"server": "FastAPI server components",
|
|
27
|
-
"speed": "Audio speed adjustment (audiostretchy)",
|
|
28
|
-
}
|
|
18
|
+
# Extract descriptions from the centralized EXTRAS metadata
|
|
19
|
+
EXTRAS: dict[str, str] = {name: desc for name, (desc, _) in _EXTRAS_META.items()}
|
|
29
20
|
|
|
30
21
|
|
|
31
22
|
def _requirements_dir() -> Path:
|
|
@@ -72,8 +63,9 @@ def _install_via_uv_tool(extras: list[str]) -> bool:
|
|
|
72
63
|
"""Reinstall agent-cli via uv tool with the specified extras."""
|
|
73
64
|
extras_str = ",".join(extras)
|
|
74
65
|
package_spec = f"agent-cli[{extras_str}]"
|
|
75
|
-
|
|
76
|
-
|
|
66
|
+
python_version = f"{sys.version_info.major}.{sys.version_info.minor}"
|
|
67
|
+
console.print(f"Reinstalling via uv tool: [cyan]{package_spec}[/] (Python {python_version})")
|
|
68
|
+
cmd = ["uv", "tool", "install", package_spec, "--force", "--python", python_version]
|
|
77
69
|
result = subprocess.run(cmd, check=False)
|
|
78
70
|
return result.returncode == 0
|
|
79
71
|
|
agent_cli/memory/__init__.py
CHANGED
|
@@ -2,23 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
from agent_cli.
|
|
6
|
-
|
|
7
|
-
_REQUIRED_DEPS = {
|
|
8
|
-
"chromadb": "chromadb",
|
|
9
|
-
"fastapi": "fastapi",
|
|
10
|
-
"uvicorn": "uvicorn",
|
|
11
|
-
"onnxruntime": "onnxruntime",
|
|
12
|
-
"huggingface_hub": "huggingface-hub",
|
|
13
|
-
"transformers": "transformers",
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
ensure_optional_dependencies(
|
|
17
|
-
_REQUIRED_DEPS,
|
|
18
|
-
extra_name="memory",
|
|
19
|
-
install_hint="`pip install agent-cli[memory]` or `uv sync --extra memory`",
|
|
20
|
-
)
|
|
21
|
-
|
|
22
|
-
from agent_cli.memory.client import MemoryClient # noqa: E402
|
|
5
|
+
from agent_cli.memory.client import MemoryClient
|
|
23
6
|
|
|
24
7
|
__all__ = ["MemoryClient"]
|
agent_cli/rag/__init__.py
CHANGED
|
@@ -1,22 +1,3 @@
|
|
|
1
1
|
"""RAG module."""
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
from agent_cli.core.deps import ensure_optional_dependencies
|
|
6
|
-
|
|
7
|
-
_REQUIRED_DEPS = {
|
|
8
|
-
"chromadb": "chromadb",
|
|
9
|
-
"watchfiles": "watchfiles",
|
|
10
|
-
"markitdown": "markitdown",
|
|
11
|
-
"fastapi": "fastapi",
|
|
12
|
-
"uvicorn": "uvicorn",
|
|
13
|
-
"onnxruntime": "onnxruntime",
|
|
14
|
-
"huggingface_hub": "huggingface-hub",
|
|
15
|
-
"transformers": "transformers",
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
ensure_optional_dependencies(
|
|
19
|
-
_REQUIRED_DEPS,
|
|
20
|
-
extra_name="rag",
|
|
21
|
-
install_hint="`pip install agent-cli[rag]` or `uv sync --extra rag`",
|
|
22
|
-
)
|