agent-cli 0.67.2__py3-none-any.whl → 0.68.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/core/deps.py +58 -27
- agent_cli/install/extras.py +38 -20
- {agent_cli-0.67.2.dist-info → agent_cli-0.68.0.dist-info}/METADATA +9 -2
- {agent_cli-0.67.2.dist-info → agent_cli-0.68.0.dist-info}/RECORD +7 -7
- {agent_cli-0.67.2.dist-info → agent_cli-0.68.0.dist-info}/WHEEL +0 -0
- {agent_cli-0.67.2.dist-info → agent_cli-0.68.0.dist-info}/entry_points.txt +0 -0
- {agent_cli-0.67.2.dist-info → agent_cli-0.68.0.dist-info}/licenses/LICENSE +0 -0
agent_cli/core/deps.py
CHANGED
|
@@ -4,19 +4,29 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import functools
|
|
6
6
|
import json
|
|
7
|
+
import os
|
|
7
8
|
from importlib.util import find_spec
|
|
8
9
|
from pathlib import Path
|
|
9
10
|
from typing import TYPE_CHECKING, TypeVar
|
|
10
11
|
|
|
11
12
|
import typer
|
|
12
13
|
|
|
13
|
-
from agent_cli.
|
|
14
|
+
from agent_cli.config import load_config
|
|
15
|
+
from agent_cli.core.utils import console, print_error_message
|
|
14
16
|
|
|
15
17
|
if TYPE_CHECKING:
|
|
16
18
|
from collections.abc import Callable
|
|
17
19
|
|
|
18
20
|
F = TypeVar("F", bound="Callable[..., object]")
|
|
19
21
|
|
|
22
|
+
|
|
23
|
+
def _get_auto_install_setting() -> bool:
|
|
24
|
+
"""Check if auto-install is enabled (default: True)."""
|
|
25
|
+
if os.environ.get("AGENT_CLI_NO_AUTO_INSTALL", "").lower() in ("1", "true", "yes"):
|
|
26
|
+
return False
|
|
27
|
+
return load_config().get("settings", {}).get("auto_install_extras", True)
|
|
28
|
+
|
|
29
|
+
|
|
20
30
|
# Load extras from JSON file
|
|
21
31
|
_EXTRAS_FILE = Path(__file__).parent.parent / "_extras.json"
|
|
22
32
|
EXTRAS: dict[str, tuple[str, list[str]]] = {
|
|
@@ -44,7 +54,7 @@ def check_extra_installed(extra: str) -> bool:
|
|
|
44
54
|
return any(check_extra_installed(e) for e in extra.split("|"))
|
|
45
55
|
|
|
46
56
|
if extra not in EXTRAS:
|
|
47
|
-
return
|
|
57
|
+
return False # Unknown extra, trigger install attempt to surface error
|
|
48
58
|
_, packages = EXTRAS[extra]
|
|
49
59
|
|
|
50
60
|
# All packages must be installed
|
|
@@ -116,44 +126,65 @@ def get_combined_install_hint(extras: list[str]) -> str:
|
|
|
116
126
|
return "\n".join(lines)
|
|
117
127
|
|
|
118
128
|
|
|
119
|
-
def
|
|
120
|
-
"""
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
129
|
+
def _try_auto_install(missing: list[str]) -> bool:
|
|
130
|
+
"""Attempt to auto-install missing extras. Returns True if successful."""
|
|
131
|
+
from agent_cli.install.extras import install_extras_programmatic # noqa: PLC0415
|
|
132
|
+
|
|
133
|
+
# Flatten alternatives (e.g., "piper|kokoro" -> just pick the first one)
|
|
134
|
+
extras_to_install = []
|
|
135
|
+
for extra in missing:
|
|
136
|
+
if "|" in extra:
|
|
137
|
+
# For alternatives, install the first option
|
|
138
|
+
extras_to_install.append(extra.split("|")[0])
|
|
139
|
+
else:
|
|
140
|
+
extras_to_install.append(extra)
|
|
141
|
+
|
|
142
|
+
console.print(
|
|
143
|
+
f"[yellow]Auto-installing missing extras: {', '.join(extras_to_install)}[/]",
|
|
144
|
+
)
|
|
145
|
+
return install_extras_programmatic(extras_to_install)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def _check_and_install_extras(extras: tuple[str, ...]) -> list[str]:
|
|
149
|
+
"""Check for missing extras and attempt auto-install. Returns list of still-missing."""
|
|
150
|
+
missing = [e for e in extras if not check_extra_installed(e)]
|
|
151
|
+
if not missing:
|
|
152
|
+
return []
|
|
153
|
+
|
|
154
|
+
if not _get_auto_install_setting():
|
|
155
|
+
print_error_message(get_combined_install_hint(missing))
|
|
156
|
+
return missing
|
|
157
|
+
|
|
158
|
+
if not _try_auto_install(missing):
|
|
159
|
+
print_error_message("Auto-install failed.\n" + get_combined_install_hint(missing))
|
|
160
|
+
return missing
|
|
161
|
+
|
|
162
|
+
console.print("[green]Installation complete![/]")
|
|
163
|
+
still_missing = [e for e in extras if not check_extra_installed(e)]
|
|
164
|
+
if still_missing:
|
|
165
|
+
print_error_message(
|
|
166
|
+
"Auto-install completed but some dependencies are still missing.\n"
|
|
167
|
+
+ get_combined_install_hint(still_missing),
|
|
168
|
+
)
|
|
169
|
+
return still_missing
|
|
126
170
|
|
|
127
|
-
Process management flags (--stop, --status, --toggle) skip the dependency
|
|
128
|
-
check since they just manage running processes without using the actual
|
|
129
|
-
dependencies.
|
|
130
171
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
@requires_extras("rag")
|
|
134
|
-
def rag_proxy(...):
|
|
135
|
-
...
|
|
172
|
+
def requires_extras(*extras: str) -> Callable[[F], F]:
|
|
173
|
+
"""Decorator to declare required extras for a command.
|
|
136
174
|
|
|
175
|
+
Auto-installs missing extras by default. Disable via AGENT_CLI_NO_AUTO_INSTALL=1
|
|
176
|
+
or config [settings] auto_install_extras = false.
|
|
137
177
|
"""
|
|
138
178
|
|
|
139
179
|
def decorator(func: F) -> F:
|
|
140
|
-
# Store extras on function for test introspection
|
|
141
180
|
func._required_extras = extras # type: ignore[attr-defined]
|
|
142
181
|
|
|
143
182
|
@functools.wraps(func)
|
|
144
183
|
def wrapper(*args: object, **kwargs: object) -> object:
|
|
145
|
-
|
|
146
|
-
# These don't need the actual dependencies, just manage processes or list info
|
|
147
|
-
if any(kwargs.get(flag) for flag in ("stop", "status", "toggle", "list_devices")):
|
|
148
|
-
return func(*args, **kwargs)
|
|
149
|
-
|
|
150
|
-
missing = [e for e in extras if not check_extra_installed(e)]
|
|
151
|
-
if missing:
|
|
152
|
-
print_error_message(get_combined_install_hint(missing))
|
|
184
|
+
if _check_and_install_extras(extras):
|
|
153
185
|
raise typer.Exit(1)
|
|
154
186
|
return func(*args, **kwargs)
|
|
155
187
|
|
|
156
|
-
# Preserve the extras on wrapper too
|
|
157
188
|
wrapper._required_extras = extras # type: ignore[attr-defined]
|
|
158
189
|
return wrapper # type: ignore[return-value]
|
|
159
190
|
|
agent_cli/install/extras.py
CHANGED
|
@@ -86,6 +86,38 @@ def _install_cmd() -> list[str]:
|
|
|
86
86
|
return cmd
|
|
87
87
|
|
|
88
88
|
|
|
89
|
+
def _install_extras_impl(extras: list[str], *, quiet: bool = False) -> bool:
|
|
90
|
+
"""Install extras. Returns True on success, False on failure."""
|
|
91
|
+
if _is_uv_tool_install():
|
|
92
|
+
current_extras = _get_current_uv_tool_extras()
|
|
93
|
+
new_extras = sorted(set(current_extras) | set(extras))
|
|
94
|
+
return _install_via_uv_tool(new_extras)
|
|
95
|
+
|
|
96
|
+
cmd = _install_cmd()
|
|
97
|
+
for extra in extras:
|
|
98
|
+
req_file = _requirements_path(extra)
|
|
99
|
+
if not quiet:
|
|
100
|
+
console.print(f"Installing [cyan]{extra}[/]...")
|
|
101
|
+
result = subprocess.run(
|
|
102
|
+
[*cmd, "-r", str(req_file)],
|
|
103
|
+
check=False,
|
|
104
|
+
capture_output=quiet,
|
|
105
|
+
)
|
|
106
|
+
if result.returncode != 0:
|
|
107
|
+
return False
|
|
108
|
+
return True
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def install_extras_programmatic(extras: list[str], *, quiet: bool = False) -> bool:
|
|
112
|
+
"""Install extras programmatically (for auto-install feature)."""
|
|
113
|
+
available = _available_extras()
|
|
114
|
+
valid = [e for e in extras if e in available]
|
|
115
|
+
invalid = [e for e in extras if e not in available]
|
|
116
|
+
if invalid:
|
|
117
|
+
console.print(f"[yellow]Unknown extras (skipped): {', '.join(invalid)}[/]")
|
|
118
|
+
return bool(valid) and _install_extras_impl(valid, quiet=quiet)
|
|
119
|
+
|
|
120
|
+
|
|
89
121
|
@app.command("install-extras", rich_help_panel="Installation")
|
|
90
122
|
def install_extras(
|
|
91
123
|
extras: Annotated[list[str] | None, typer.Argument(help="Extras to install")] = None,
|
|
@@ -128,25 +160,11 @@ def install_extras(
|
|
|
128
160
|
print_error_message(f"Unknown extras: {invalid}. Use --list to see available.")
|
|
129
161
|
raise typer.Exit(1)
|
|
130
162
|
|
|
131
|
-
|
|
163
|
+
if not _install_extras_impl(extras):
|
|
164
|
+
print_error_message("Failed to install extras")
|
|
165
|
+
raise typer.Exit(1)
|
|
166
|
+
|
|
132
167
|
if _is_uv_tool_install():
|
|
133
|
-
current_extras = _get_current_uv_tool_extras()
|
|
134
|
-
new_extras = sorted(set(current_extras) | set(extras))
|
|
135
|
-
if not _install_via_uv_tool(new_extras):
|
|
136
|
-
print_error_message("Failed to reinstall via uv tool")
|
|
137
|
-
raise typer.Exit(1)
|
|
138
168
|
console.print("[green]Done! Extras will persist across uv tool upgrade.[/]")
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
# Standard pip/uv pip install for non-tool environments
|
|
142
|
-
cmd = _install_cmd()
|
|
143
|
-
|
|
144
|
-
for extra in extras:
|
|
145
|
-
req_file = _requirements_path(extra)
|
|
146
|
-
console.print(f"Installing [cyan]{extra}[/]...")
|
|
147
|
-
result = subprocess.run([*cmd, "-r", str(req_file)], check=False)
|
|
148
|
-
if result.returncode != 0:
|
|
149
|
-
print_error_message(f"Failed to install '{extra}'")
|
|
150
|
-
raise typer.Exit(1)
|
|
151
|
-
|
|
152
|
-
console.print("[green]Done![/]")
|
|
169
|
+
else:
|
|
170
|
+
console.print("[green]Done![/]")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agent-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.68.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>
|
|
@@ -459,7 +459,14 @@ All necessary scripts are bundled with the package, so you can run these command
|
|
|
459
459
|
|
|
460
460
|
#### Installing Optional Extras
|
|
461
461
|
|
|
462
|
-
Some features require additional Python dependencies.
|
|
462
|
+
Some features require additional Python dependencies. By default, **agent-cli will auto-install missing extras** when you run a command that needs them. To disable this, set `AGENT_CLI_NO_AUTO_INSTALL=1` or add to your config file:
|
|
463
|
+
|
|
464
|
+
```toml
|
|
465
|
+
[settings]
|
|
466
|
+
auto_install_extras = false
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
You can also manually install extras with `install-extras`:
|
|
463
470
|
|
|
464
471
|
```bash
|
|
465
472
|
# List available extras
|
|
@@ -40,7 +40,7 @@ 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
42
|
agent_cli/core/chroma.py,sha256=Vb_ny7SzAIL9SCEGlYgYOqsdG9BgusFGMj0RUzb6W90,2728
|
|
43
|
-
agent_cli/core/deps.py,sha256=
|
|
43
|
+
agent_cli/core/deps.py,sha256=58zyHSJdzIAkJ0gZ7-snrASy8Ro9NePZmElch13r-SQ,6429
|
|
44
44
|
agent_cli/core/openai_proxy.py,sha256=f2kqxk6bAOeN7gOzU0JnyS-RYtXUcK5Gbsa_pBmlCv0,4470
|
|
45
45
|
agent_cli/core/process.py,sha256=Zay6beX4JUbkBHr6xbJxwVBjVFDABmRHQCXVPQH93r8,5916
|
|
46
46
|
agent_cli/core/reranker.py,sha256=Qv5ASGUdseLzI6eQRfNeQY-Lvv4SOgLOu56CpwmszDM,3779
|
|
@@ -91,7 +91,7 @@ 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=
|
|
94
|
+
agent_cli/install/extras.py,sha256=BPIlMdDeUo-tsKb_9Ttx3799YsIV_yup79ESFNpoJ1o,5731
|
|
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
|
|
@@ -188,8 +188,8 @@ agent_cli/services/asr.py,sha256=aRaCLVCygsJ15qyQEPECOZsdSrnlLPbyY4RwAqY0qIw,172
|
|
|
188
188
|
agent_cli/services/llm.py,sha256=Kwdo6pbMYI9oykF-RBe1iaL3KsYrNWTLdRSioewmsGQ,7199
|
|
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.
|
|
192
|
-
agent_cli-0.
|
|
193
|
-
agent_cli-0.
|
|
194
|
-
agent_cli-0.
|
|
195
|
-
agent_cli-0.
|
|
191
|
+
agent_cli-0.68.0.dist-info/METADATA,sha256=0eWIbitl8b9hqUg_Fh8fjJNd2UJ91_qZDYdynfh60GU,156032
|
|
192
|
+
agent_cli-0.68.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
193
|
+
agent_cli-0.68.0.dist-info/entry_points.txt,sha256=FUv-fB2atLsPUk_RT4zqnZl1coz4_XHFwRALOKOF38s,97
|
|
194
|
+
agent_cli-0.68.0.dist-info/licenses/LICENSE,sha256=majJU6S9kC8R8bW39NVBHyv32Dq50FL6TDxECG2WVts,1068
|
|
195
|
+
agent_cli-0.68.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|