advai-cli 1.0.2__tar.gz → 1.0.4__tar.gz
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.
- advai_cli-1.0.4/PKG-INFO +52 -0
- advai_cli-1.0.4/README.md +42 -0
- advai_cli-1.0.4/advai/__init__.py +1 -0
- advai_cli-1.0.4/advai/cli.py +260 -0
- advai_cli-1.0.4/advai/cli_manager.py +253 -0
- advai_cli-1.0.4/advai_cli.egg-info/PKG-INFO +52 -0
- {advai_cli-1.0.2 → advai_cli-1.0.4}/advai_cli.egg-info/SOURCES.txt +1 -0
- {advai_cli-1.0.2 → advai_cli-1.0.4}/pyproject.toml +2 -2
- advai_cli-1.0.2/PKG-INFO +0 -64
- advai_cli-1.0.2/README.md +0 -54
- advai_cli-1.0.2/advai/__init__.py +0 -1
- advai_cli-1.0.2/advai/cli.py +0 -95
- advai_cli-1.0.2/advai_cli.egg-info/PKG-INFO +0 -64
- {advai_cli-1.0.2 → advai_cli-1.0.4}/advai/skills.py +0 -0
- {advai_cli-1.0.2 → advai_cli-1.0.4}/advai_cli.egg-info/dependency_links.txt +0 -0
- {advai_cli-1.0.2 → advai_cli-1.0.4}/advai_cli.egg-info/entry_points.txt +0 -0
- {advai_cli-1.0.2 → advai_cli-1.0.4}/advai_cli.egg-info/requires.txt +0 -0
- {advai_cli-1.0.2 → advai_cli-1.0.4}/advai_cli.egg-info/top_level.txt +0 -0
- {advai_cli-1.0.2 → advai_cli-1.0.4}/setup.cfg +0 -0
advai_cli-1.0.4/PKG-INFO
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: advai-cli
|
|
3
|
+
Version: 1.0.4
|
|
4
|
+
Summary: A cross-platform CLI tool.
|
|
5
|
+
Author: advai
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Requires-Python: >=3.8
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
Requires-Dist: click>=8.1
|
|
10
|
+
|
|
11
|
+
# advai-cli
|
|
12
|
+
|
|
13
|
+
A cross-platform CLI tool for skill management and CLI self-management.
|
|
14
|
+
|
|
15
|
+
Install:
|
|
16
|
+
```bash
|
|
17
|
+
pip install advai-cli
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Commands:
|
|
21
|
+
```bash
|
|
22
|
+
advai --help
|
|
23
|
+
advai info
|
|
24
|
+
advai update
|
|
25
|
+
advai skill list
|
|
26
|
+
advai skill install demo-skill
|
|
27
|
+
advai cli list
|
|
28
|
+
advai cli info demo-cli
|
|
29
|
+
advai cli install demo-cli --yes
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Structured command groups:
|
|
33
|
+
```bash
|
|
34
|
+
advai skill list
|
|
35
|
+
advai skill info demo-skill
|
|
36
|
+
advai skill install demo-skill
|
|
37
|
+
advai skill update demo-skill
|
|
38
|
+
advai skill uninstall demo-skill
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
advai self-management:
|
|
42
|
+
```bash
|
|
43
|
+
advai info
|
|
44
|
+
advai update
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
External CLI management:
|
|
48
|
+
```bash
|
|
49
|
+
advai cli list
|
|
50
|
+
advai cli info demo-cli
|
|
51
|
+
advai cli install demo-cli --yes
|
|
52
|
+
```
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# advai-cli
|
|
2
|
+
|
|
3
|
+
A cross-platform CLI tool for skill management and CLI self-management.
|
|
4
|
+
|
|
5
|
+
Install:
|
|
6
|
+
```bash
|
|
7
|
+
pip install advai-cli
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
Commands:
|
|
11
|
+
```bash
|
|
12
|
+
advai --help
|
|
13
|
+
advai info
|
|
14
|
+
advai update
|
|
15
|
+
advai skill list
|
|
16
|
+
advai skill install demo-skill
|
|
17
|
+
advai cli list
|
|
18
|
+
advai cli info demo-cli
|
|
19
|
+
advai cli install demo-cli --yes
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Structured command groups:
|
|
23
|
+
```bash
|
|
24
|
+
advai skill list
|
|
25
|
+
advai skill info demo-skill
|
|
26
|
+
advai skill install demo-skill
|
|
27
|
+
advai skill update demo-skill
|
|
28
|
+
advai skill uninstall demo-skill
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
advai self-management:
|
|
32
|
+
```bash
|
|
33
|
+
advai info
|
|
34
|
+
advai update
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
External CLI management:
|
|
38
|
+
```bash
|
|
39
|
+
advai cli list
|
|
40
|
+
advai cli info demo-cli
|
|
41
|
+
advai cli install demo-cli --yes
|
|
42
|
+
```
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "1.0.4"
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
import click
|
|
2
|
+
import os
|
|
3
|
+
import sys
|
|
4
|
+
|
|
5
|
+
from advai import __version__
|
|
6
|
+
from advai.cli_manager import (
|
|
7
|
+
cli_info,
|
|
8
|
+
get_available_cli_info,
|
|
9
|
+
list_external_clis,
|
|
10
|
+
get_external_cli_info,
|
|
11
|
+
cli_exists,
|
|
12
|
+
build_cli_exec_command,
|
|
13
|
+
build_external_cli_install_command,
|
|
14
|
+
opencli_available,
|
|
15
|
+
build_update_command,
|
|
16
|
+
run_manager_command,
|
|
17
|
+
run_passthrough_command,
|
|
18
|
+
)
|
|
19
|
+
from advai.skills import install_skill, uninstall_skill, list_skills, update_skill, info_skill
|
|
20
|
+
|
|
21
|
+
SKILLS_DIR = os.path.expanduser("~/.advai/skills")
|
|
22
|
+
CONFIG_DIR = os.path.expanduser("~/.advai")
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _ensure_dirs():
|
|
26
|
+
os.makedirs(SKILLS_DIR, exist_ok=True)
|
|
27
|
+
os.makedirs(CONFIG_DIR, exist_ok=True)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@click.group()
|
|
31
|
+
@click.version_option(version=__version__, prog_name="advai")
|
|
32
|
+
def cli():
|
|
33
|
+
"""advai — a cross-platform CLI tool."""
|
|
34
|
+
_ensure_dirs()
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class ExternalCLIGroup(click.Group):
|
|
38
|
+
"""Static management commands + dynamic OpenCLI proxy."""
|
|
39
|
+
|
|
40
|
+
def get_command(self, ctx, cmd_name):
|
|
41
|
+
command = super().get_command(ctx, cmd_name)
|
|
42
|
+
if command is not None:
|
|
43
|
+
return command
|
|
44
|
+
if not opencli_available():
|
|
45
|
+
return None
|
|
46
|
+
if not cli_exists(cmd_name):
|
|
47
|
+
return None
|
|
48
|
+
|
|
49
|
+
@click.command(
|
|
50
|
+
name=cmd_name,
|
|
51
|
+
context_settings={
|
|
52
|
+
"ignore_unknown_options": True,
|
|
53
|
+
"allow_extra_args": True,
|
|
54
|
+
},
|
|
55
|
+
add_help_option=False,
|
|
56
|
+
)
|
|
57
|
+
@click.argument("args", nargs=-1, type=click.UNPROCESSED)
|
|
58
|
+
def dynamic_cli_cmd(args):
|
|
59
|
+
exit_code = run_passthrough_command(build_cli_exec_command(cmd_name, list(args)))
|
|
60
|
+
raise click.exceptions.Exit(exit_code)
|
|
61
|
+
|
|
62
|
+
return dynamic_cli_cmd
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _skill_install(skill_name, force):
|
|
66
|
+
try:
|
|
67
|
+
install_skill(skill_name, force=force)
|
|
68
|
+
click.echo(f"✅ Skill '{skill_name}' installed successfully")
|
|
69
|
+
except FileExistsError:
|
|
70
|
+
click.echo(f"⚠️ Skill '{skill_name}' already exists, use --force to overwrite")
|
|
71
|
+
except Exception as e:
|
|
72
|
+
click.echo(f"❌ Installation failed: {e}", err=True)
|
|
73
|
+
sys.exit(1)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _skill_uninstall(skill_name):
|
|
77
|
+
try:
|
|
78
|
+
uninstall_skill(skill_name)
|
|
79
|
+
click.echo(f"🗑️ Skill '{skill_name}' uninstalled")
|
|
80
|
+
except FileNotFoundError:
|
|
81
|
+
click.echo(f"⚠️ Skill '{skill_name}' is not installed")
|
|
82
|
+
except Exception as e:
|
|
83
|
+
click.echo(f"❌ Uninstall failed: {e}", err=True)
|
|
84
|
+
sys.exit(1)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _skill_list():
|
|
88
|
+
skills = list_skills()
|
|
89
|
+
if not skills:
|
|
90
|
+
click.echo("(no Skills installed)")
|
|
91
|
+
return
|
|
92
|
+
click.echo("📋 Installed Skills:")
|
|
93
|
+
for s in skills:
|
|
94
|
+
click.echo(f" • {s}")
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def _skill_update(skill_name):
|
|
98
|
+
try:
|
|
99
|
+
updated = update_skill(skill_name)
|
|
100
|
+
if updated:
|
|
101
|
+
for s in updated:
|
|
102
|
+
click.echo(f"🔄 {s} updated")
|
|
103
|
+
else:
|
|
104
|
+
click.echo("(nothing to update)")
|
|
105
|
+
except Exception as e:
|
|
106
|
+
click.echo(f"❌ Update failed: {e}", err=True)
|
|
107
|
+
sys.exit(1)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def _skill_info(skill_name):
|
|
111
|
+
data = info_skill(skill_name)
|
|
112
|
+
if data is None:
|
|
113
|
+
click.echo(f"⚠️ Skill '{skill_name}' not installed or has no metadata")
|
|
114
|
+
return
|
|
115
|
+
click.echo(f"ℹ️ Skill '{skill_name}':")
|
|
116
|
+
for k, v in data.items():
|
|
117
|
+
click.echo(f" {k}: {v}")
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def _self_info():
|
|
121
|
+
data = cli_info()
|
|
122
|
+
click.echo("advai:")
|
|
123
|
+
click.echo(f" name: {data['name']}")
|
|
124
|
+
click.echo(f" version: {data['version']}")
|
|
125
|
+
click.echo(f" install_method: {data['install_method']}")
|
|
126
|
+
click.echo(f" python: {data['python']}")
|
|
127
|
+
click.echo(f" entry: {data['entry']}")
|
|
128
|
+
click.echo(f" module: {data['module']}")
|
|
129
|
+
click.echo(f" skills_dir: {data['skills_dir']}")
|
|
130
|
+
click.echo(" available_managers:")
|
|
131
|
+
for manager, available in data["available_managers"].items():
|
|
132
|
+
click.echo(f" {manager}: {'yes' if available else 'no'}")
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
@cli.command(name="info")
|
|
136
|
+
def self_info_cmd():
|
|
137
|
+
"""Show advai details."""
|
|
138
|
+
_self_info()
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
@cli.command(name="update")
|
|
142
|
+
def self_update_cmd():
|
|
143
|
+
"""Update advai itself."""
|
|
144
|
+
command = build_update_command("pip")
|
|
145
|
+
click.echo(f"Recommended update command: {' '.join(command)}")
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
@cli.group(name="skill")
|
|
149
|
+
def skill_admin():
|
|
150
|
+
"""Manage Skills with a structured command group."""
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
@skill_admin.command(name="install")
|
|
154
|
+
@click.argument("skill_name")
|
|
155
|
+
@click.option("--force", is_flag=True, help="Force reinstall (overwrite existing)")
|
|
156
|
+
def skill_install_cmd(skill_name, force):
|
|
157
|
+
"""Install a Skill."""
|
|
158
|
+
_skill_install(skill_name, force)
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
@skill_admin.command(name="uninstall")
|
|
162
|
+
@click.argument("skill_name")
|
|
163
|
+
def skill_uninstall_cmd(skill_name):
|
|
164
|
+
"""Uninstall a Skill."""
|
|
165
|
+
_skill_uninstall(skill_name)
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
@skill_admin.command(name="list")
|
|
169
|
+
def skill_list_cmd():
|
|
170
|
+
"""List locally installed Skills."""
|
|
171
|
+
_skill_list()
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
@skill_admin.command(name="update")
|
|
175
|
+
@click.argument("skill_name", required=False)
|
|
176
|
+
def skill_update_cmd(skill_name):
|
|
177
|
+
"""Update one or all Skills."""
|
|
178
|
+
_skill_update(skill_name)
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
@skill_admin.command(name="info")
|
|
182
|
+
@click.argument("skill_name")
|
|
183
|
+
def skill_info_cmd(skill_name):
|
|
184
|
+
"""Show Skill details."""
|
|
185
|
+
_skill_info(skill_name)
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
@cli.group(name="cli", cls=ExternalCLIGroup)
|
|
189
|
+
def cli_admin():
|
|
190
|
+
"""Manage or execute external CLIs."""
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
@cli_admin.command(name="info")
|
|
194
|
+
@click.argument("cli_name")
|
|
195
|
+
def cli_info_cmd(cli_name):
|
|
196
|
+
"""Show external CLI details."""
|
|
197
|
+
external = get_external_cli_info(cli_name)
|
|
198
|
+
if external is not None:
|
|
199
|
+
click.echo(f"CLI '{cli_name}':")
|
|
200
|
+
for key in ("name", "package", "binary", "installed", "description", "homepage", "tags"):
|
|
201
|
+
click.echo(f" {key}: {external.get(key)}")
|
|
202
|
+
return
|
|
203
|
+
available = get_available_cli_info(cli_name)
|
|
204
|
+
if available is not None:
|
|
205
|
+
click.echo(f"CLI '{cli_name}':")
|
|
206
|
+
click.echo(" source: opencli")
|
|
207
|
+
click.echo(f" command_count: {available['command_count']}")
|
|
208
|
+
click.echo(f" commands: {', '.join(available['commands'])}")
|
|
209
|
+
if available.get("description"):
|
|
210
|
+
click.echo(f" description: {available['description']}")
|
|
211
|
+
return
|
|
212
|
+
raise click.ClickException(f"CLI '{cli_name}' was not found")
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
@cli_admin.command(name="list")
|
|
216
|
+
@click.option("--search", default="", help="Filter CLI names")
|
|
217
|
+
def cli_list_cmd(search):
|
|
218
|
+
"""List installable external CLIs."""
|
|
219
|
+
if not opencli_available():
|
|
220
|
+
raise click.ClickException("opencli is not installed or not on PATH")
|
|
221
|
+
targets = list_external_clis(search)
|
|
222
|
+
if not targets:
|
|
223
|
+
click.echo("(no external CLIs found)")
|
|
224
|
+
return
|
|
225
|
+
click.echo("External CLIs:")
|
|
226
|
+
for item in targets:
|
|
227
|
+
installed = "installed" if item.get("installed") else "not installed"
|
|
228
|
+
click.echo(f" • {item['name']} ({installed})")
|
|
229
|
+
if item.get("description"):
|
|
230
|
+
click.echo(f" description: {item['description']}")
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def _execute_cli_command(command, action):
|
|
234
|
+
result = run_manager_command(command)
|
|
235
|
+
if result["stdout"]:
|
|
236
|
+
click.echo(result["stdout"])
|
|
237
|
+
if result["returncode"] != 0:
|
|
238
|
+
if result["stderr"]:
|
|
239
|
+
click.echo(result["stderr"], err=True)
|
|
240
|
+
raise click.ClickException(f"CLI {action} failed")
|
|
241
|
+
if result["stderr"]:
|
|
242
|
+
click.echo(result["stderr"])
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
@cli_admin.command(name="install")
|
|
246
|
+
@click.argument("cli_name")
|
|
247
|
+
@click.option("--yes", is_flag=True, help="Skip confirmation prompt")
|
|
248
|
+
def cli_install_cmd(cli_name, yes):
|
|
249
|
+
"""Install an external CLI."""
|
|
250
|
+
if not opencli_available():
|
|
251
|
+
raise click.ClickException("opencli is not installed or not on PATH")
|
|
252
|
+
command = build_external_cli_install_command(cli_name)
|
|
253
|
+
if not yes:
|
|
254
|
+
click.confirm(f"Install external CLI '{cli_name}' via: {' '.join(command)} ?", abort=True)
|
|
255
|
+
_execute_cli_command(command, "install")
|
|
256
|
+
click.echo(f"CLI '{cli_name}' install completed")
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
if __name__ == "__main__":
|
|
260
|
+
cli()
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import shutil
|
|
3
|
+
import subprocess
|
|
4
|
+
import sys
|
|
5
|
+
import json
|
|
6
|
+
from functools import lru_cache
|
|
7
|
+
|
|
8
|
+
from advai import __version__
|
|
9
|
+
|
|
10
|
+
PACKAGE_NAME = "advai-cli"
|
|
11
|
+
BREW_FORMULA = "advai-cli"
|
|
12
|
+
SUPPORTED_MANAGERS = ("pip", "npm", "brew")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _normalize_path(value: str) -> str:
|
|
16
|
+
if not value:
|
|
17
|
+
return ""
|
|
18
|
+
return os.path.realpath(os.path.expanduser(value))
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def detect_install_method() -> str:
|
|
22
|
+
argv0 = _normalize_path(sys.argv[0])
|
|
23
|
+
module_path = _normalize_path(__file__)
|
|
24
|
+
combined = f"{argv0} {module_path}".lower()
|
|
25
|
+
|
|
26
|
+
if "cellar" in combined or "homebrew" in combined:
|
|
27
|
+
return "brew"
|
|
28
|
+
if "node_modules" in combined or argv0.endswith(".js"):
|
|
29
|
+
return "npm"
|
|
30
|
+
if "site-packages" in combined or "dist-packages" in combined:
|
|
31
|
+
return "pip"
|
|
32
|
+
return "source"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def available_managers() -> dict:
|
|
36
|
+
return {
|
|
37
|
+
"pip": bool(sys.executable),
|
|
38
|
+
"npm": shutil.which("npm") is not None,
|
|
39
|
+
"brew": shutil.which("brew") is not None,
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def cli_info() -> dict:
|
|
44
|
+
return {
|
|
45
|
+
"name": PACKAGE_NAME,
|
|
46
|
+
"version": __version__,
|
|
47
|
+
"install_method": detect_install_method(),
|
|
48
|
+
"python": sys.executable,
|
|
49
|
+
"entry": _normalize_path(sys.argv[0]),
|
|
50
|
+
"module": _normalize_path(__file__),
|
|
51
|
+
"skills_dir": os.path.expanduser("~/.advai/skills"),
|
|
52
|
+
"available_managers": available_managers(),
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def list_cli_targets() -> list:
|
|
57
|
+
info = cli_info()
|
|
58
|
+
detected = info["install_method"]
|
|
59
|
+
managers = info["available_managers"]
|
|
60
|
+
items = []
|
|
61
|
+
for name, available in managers.items():
|
|
62
|
+
status = "available" if available else "unavailable"
|
|
63
|
+
if name == detected:
|
|
64
|
+
status = f"{status}, detected"
|
|
65
|
+
items.append({"name": name, "status": status})
|
|
66
|
+
if detected not in managers:
|
|
67
|
+
items.append({"name": detected, "status": "detected"})
|
|
68
|
+
return items
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def opencli_available() -> bool:
|
|
72
|
+
return shutil.which("opencli") is not None
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
@lru_cache(maxsize=1)
|
|
76
|
+
def _opencli_registry() -> list:
|
|
77
|
+
if not opencli_available():
|
|
78
|
+
return []
|
|
79
|
+
result = subprocess.run(
|
|
80
|
+
["opencli", "list", "-f", "json"],
|
|
81
|
+
capture_output=True,
|
|
82
|
+
text=True,
|
|
83
|
+
)
|
|
84
|
+
if result.returncode != 0:
|
|
85
|
+
raise RuntimeError(result.stderr.strip() or "Failed to query opencli registry")
|
|
86
|
+
try:
|
|
87
|
+
return json.loads(result.stdout)
|
|
88
|
+
except json.JSONDecodeError as exc:
|
|
89
|
+
raise RuntimeError("Failed to parse opencli registry output") from exc
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
@lru_cache(maxsize=1)
|
|
93
|
+
def _opencli_external_registry() -> list:
|
|
94
|
+
if not opencli_available():
|
|
95
|
+
return []
|
|
96
|
+
result = subprocess.run(
|
|
97
|
+
["opencli", "external", "list", "-f", "json"],
|
|
98
|
+
capture_output=True,
|
|
99
|
+
text=True,
|
|
100
|
+
)
|
|
101
|
+
if result.returncode != 0:
|
|
102
|
+
raise RuntimeError(result.stderr.strip() or "Failed to query opencli external registry")
|
|
103
|
+
try:
|
|
104
|
+
return json.loads(result.stdout)
|
|
105
|
+
except json.JSONDecodeError as exc:
|
|
106
|
+
raise RuntimeError("Failed to parse opencli external registry output") from exc
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def list_available_clis(search: str = "") -> list:
|
|
110
|
+
term = str(search or "").strip().lower()
|
|
111
|
+
grouped = {}
|
|
112
|
+
for item in _opencli_registry():
|
|
113
|
+
site = str(item.get("site") or "").strip()
|
|
114
|
+
if not site:
|
|
115
|
+
continue
|
|
116
|
+
if term and term not in site.lower():
|
|
117
|
+
continue
|
|
118
|
+
info = grouped.setdefault(
|
|
119
|
+
site,
|
|
120
|
+
{
|
|
121
|
+
"name": site,
|
|
122
|
+
"command_count": 0,
|
|
123
|
+
"commands": [],
|
|
124
|
+
"description": "",
|
|
125
|
+
},
|
|
126
|
+
)
|
|
127
|
+
info["command_count"] += 1
|
|
128
|
+
command_name = str(item.get("name") or "").strip()
|
|
129
|
+
if command_name:
|
|
130
|
+
info["commands"].append(command_name)
|
|
131
|
+
if not info["description"]:
|
|
132
|
+
info["description"] = str(item.get("description") or "").strip()
|
|
133
|
+
return [grouped[name] for name in sorted(grouped)]
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def get_available_cli_info(cli_name: str) -> dict | None:
|
|
137
|
+
target = str(cli_name or "").strip()
|
|
138
|
+
if not target:
|
|
139
|
+
return None
|
|
140
|
+
for item in list_available_clis():
|
|
141
|
+
if item["name"] == target:
|
|
142
|
+
return item
|
|
143
|
+
return None
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def list_external_clis(search: str = "") -> list:
|
|
147
|
+
term = str(search or "").strip().lower()
|
|
148
|
+
items = []
|
|
149
|
+
for item in _opencli_external_registry():
|
|
150
|
+
name = str(item.get("name") or "").strip()
|
|
151
|
+
if not name:
|
|
152
|
+
continue
|
|
153
|
+
if term and term not in name.lower():
|
|
154
|
+
continue
|
|
155
|
+
items.append(item)
|
|
156
|
+
return items
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def get_external_cli_info(cli_name: str) -> dict | None:
|
|
160
|
+
target = str(cli_name or "").strip()
|
|
161
|
+
if not target:
|
|
162
|
+
return None
|
|
163
|
+
for item in _opencli_external_registry():
|
|
164
|
+
if str(item.get("name") or "").strip() == target:
|
|
165
|
+
return item
|
|
166
|
+
return None
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def cli_exists(cli_name: str) -> bool:
|
|
170
|
+
target = str(cli_name or "").strip()
|
|
171
|
+
if not target:
|
|
172
|
+
return False
|
|
173
|
+
for item in _opencli_registry():
|
|
174
|
+
if str(item.get("site") or "").strip() == target:
|
|
175
|
+
return True
|
|
176
|
+
return False
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def build_cli_exec_command(cli_name: str, args: list | None = None) -> list:
|
|
180
|
+
command = ["opencli", cli_name]
|
|
181
|
+
if args:
|
|
182
|
+
command.extend(args)
|
|
183
|
+
return command
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def build_external_cli_install_command(cli_name: str) -> list:
|
|
187
|
+
return ["opencli", "external", "install", cli_name]
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def run_passthrough_command(command: list) -> int:
|
|
191
|
+
result = subprocess.run(command)
|
|
192
|
+
return result.returncode
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def resolve_manager(manager: str = None) -> str:
|
|
196
|
+
if manager:
|
|
197
|
+
return manager
|
|
198
|
+
detected = detect_install_method()
|
|
199
|
+
if detected in SUPPORTED_MANAGERS:
|
|
200
|
+
return detected
|
|
201
|
+
return "pip"
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def build_install_command(manager: str, reinstall: bool = False) -> list:
|
|
205
|
+
if manager == "pip":
|
|
206
|
+
cmd = [sys.executable, "-m", "pip", "install", "--upgrade"]
|
|
207
|
+
if reinstall:
|
|
208
|
+
cmd.append("--force-reinstall")
|
|
209
|
+
cmd.append(PACKAGE_NAME)
|
|
210
|
+
return cmd
|
|
211
|
+
if manager == "npm":
|
|
212
|
+
return ["npm", "install", "-g", PACKAGE_NAME]
|
|
213
|
+
if manager == "brew":
|
|
214
|
+
if reinstall:
|
|
215
|
+
return ["brew", "reinstall", BREW_FORMULA]
|
|
216
|
+
return ["brew", "install", BREW_FORMULA]
|
|
217
|
+
raise ValueError(f"Unsupported manager: {manager}")
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def build_update_command(manager: str) -> list:
|
|
221
|
+
if manager == "pip":
|
|
222
|
+
return [sys.executable, "-m", "pip", "install", "--upgrade", PACKAGE_NAME]
|
|
223
|
+
if manager == "npm":
|
|
224
|
+
return ["npm", "install", "-g", f"{PACKAGE_NAME}@latest"]
|
|
225
|
+
if manager == "brew":
|
|
226
|
+
return ["brew", "upgrade", BREW_FORMULA]
|
|
227
|
+
raise ValueError(f"Unsupported manager: {manager}")
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def build_uninstall_command(manager: str) -> list:
|
|
231
|
+
if manager == "pip":
|
|
232
|
+
return [sys.executable, "-m", "pip", "uninstall", "-y", PACKAGE_NAME]
|
|
233
|
+
if manager == "npm":
|
|
234
|
+
return ["npm", "uninstall", "-g", PACKAGE_NAME]
|
|
235
|
+
if manager == "brew":
|
|
236
|
+
return ["brew", "uninstall", BREW_FORMULA]
|
|
237
|
+
raise ValueError(f"Unsupported manager: {manager}")
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
def run_manager_command(command: list) -> dict:
|
|
241
|
+
result = subprocess.run(command, capture_output=True, text=True)
|
|
242
|
+
return {
|
|
243
|
+
"command": command,
|
|
244
|
+
"returncode": result.returncode,
|
|
245
|
+
"stdout": result.stdout.strip(),
|
|
246
|
+
"stderr": result.stderr.strip(),
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
def ensure_manager_available(manager: str) -> None:
|
|
251
|
+
available = available_managers()
|
|
252
|
+
if not available.get(manager):
|
|
253
|
+
raise RuntimeError(f"Package manager '{manager}' is not available on this system")
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: advai-cli
|
|
3
|
+
Version: 1.0.4
|
|
4
|
+
Summary: A cross-platform CLI tool.
|
|
5
|
+
Author: advai
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Requires-Python: >=3.8
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
Requires-Dist: click>=8.1
|
|
10
|
+
|
|
11
|
+
# advai-cli
|
|
12
|
+
|
|
13
|
+
A cross-platform CLI tool for skill management and CLI self-management.
|
|
14
|
+
|
|
15
|
+
Install:
|
|
16
|
+
```bash
|
|
17
|
+
pip install advai-cli
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Commands:
|
|
21
|
+
```bash
|
|
22
|
+
advai --help
|
|
23
|
+
advai info
|
|
24
|
+
advai update
|
|
25
|
+
advai skill list
|
|
26
|
+
advai skill install demo-skill
|
|
27
|
+
advai cli list
|
|
28
|
+
advai cli info demo-cli
|
|
29
|
+
advai cli install demo-cli --yes
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Structured command groups:
|
|
33
|
+
```bash
|
|
34
|
+
advai skill list
|
|
35
|
+
advai skill info demo-skill
|
|
36
|
+
advai skill install demo-skill
|
|
37
|
+
advai skill update demo-skill
|
|
38
|
+
advai skill uninstall demo-skill
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
advai self-management:
|
|
42
|
+
```bash
|
|
43
|
+
advai info
|
|
44
|
+
advai update
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
External CLI management:
|
|
48
|
+
```bash
|
|
49
|
+
advai cli list
|
|
50
|
+
advai cli info demo-cli
|
|
51
|
+
advai cli install demo-cli --yes
|
|
52
|
+
```
|
|
@@ -4,8 +4,8 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "advai-cli"
|
|
7
|
-
version = "1.0.
|
|
8
|
-
description = "
|
|
7
|
+
version = "1.0.4"
|
|
8
|
+
description = "A cross-platform CLI tool."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.8"
|
|
11
11
|
license = "MIT"
|
advai_cli-1.0.2/PKG-INFO
DELETED
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: advai-cli
|
|
3
|
-
Version: 1.0.2
|
|
4
|
-
Summary: Cross-platform AI Skill manager
|
|
5
|
-
Author: advai
|
|
6
|
-
License-Expression: MIT
|
|
7
|
-
Requires-Python: >=3.8
|
|
8
|
-
Description-Content-Type: text/markdown
|
|
9
|
-
Requires-Dist: click>=8.1
|
|
10
|
-
|
|
11
|
-
# advai-cli — Cross-platform AI Skill Manager
|
|
12
|
-
|
|
13
|
-
A Python CLI that lets you install / uninstall AI Skills.
|
|
14
|
-
|
|
15
|
-
- **Platforms**: macOS / Linux / Windows
|
|
16
|
-
- **Installation**: pip, npm, or one-line script
|
|
17
|
-
- **Commands**: install, uninstall, list, update, info
|
|
18
|
-
|
|
19
|
-
---
|
|
20
|
-
|
|
21
|
-
## Quick Start
|
|
22
|
-
|
|
23
|
-
```bash
|
|
24
|
-
# pip (recommended)
|
|
25
|
-
pip install advai-cli
|
|
26
|
-
|
|
27
|
-
# install after installation, the `advai` command is ready to use
|
|
28
|
-
|
|
29
|
-
Advai --help
|
|
30
|
-
advai --version
|
|
31
|
-
advai install <skill-name>
|
|
32
|
-
advai uninstall <skill-name>
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
---
|
|
36
|
-
|
|
37
|
-
## Project Structure
|
|
38
|
-
|
|
39
|
-
```
|
|
40
|
-
advai/ # core CLI (Python)
|
|
41
|
-
__init__.py
|
|
42
|
-
cli.py # entry point (click-based)
|
|
43
|
-
skills.py # Skill install / uninstall / metadata logic
|
|
44
|
-
Formula/advai.rb # Homebrew formula
|
|
45
|
-
bin/advai.js # npm entry point bridge
|
|
46
|
-
install.sh # one-click installer script
|
|
47
|
-
pyproject.toml # PyPI build configuration
|
|
48
|
-
package.json # npm package configuration
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
---
|
|
52
|
-
|
|
53
|
-
## Local Development
|
|
54
|
-
|
|
55
|
-
```bash
|
|
56
|
-
# run from source
|
|
57
|
-
python3 -m advai.cli --help
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
---
|
|
61
|
-
|
|
62
|
-
## License
|
|
63
|
-
|
|
64
|
-
MIT
|
advai_cli-1.0.2/README.md
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
# advai-cli — Cross-platform AI Skill Manager
|
|
2
|
-
|
|
3
|
-
A Python CLI that lets you install / uninstall AI Skills.
|
|
4
|
-
|
|
5
|
-
- **Platforms**: macOS / Linux / Windows
|
|
6
|
-
- **Installation**: pip, npm, or one-line script
|
|
7
|
-
- **Commands**: install, uninstall, list, update, info
|
|
8
|
-
|
|
9
|
-
---
|
|
10
|
-
|
|
11
|
-
## Quick Start
|
|
12
|
-
|
|
13
|
-
```bash
|
|
14
|
-
# pip (recommended)
|
|
15
|
-
pip install advai-cli
|
|
16
|
-
|
|
17
|
-
# install after installation, the `advai` command is ready to use
|
|
18
|
-
|
|
19
|
-
Advai --help
|
|
20
|
-
advai --version
|
|
21
|
-
advai install <skill-name>
|
|
22
|
-
advai uninstall <skill-name>
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
---
|
|
26
|
-
|
|
27
|
-
## Project Structure
|
|
28
|
-
|
|
29
|
-
```
|
|
30
|
-
advai/ # core CLI (Python)
|
|
31
|
-
__init__.py
|
|
32
|
-
cli.py # entry point (click-based)
|
|
33
|
-
skills.py # Skill install / uninstall / metadata logic
|
|
34
|
-
Formula/advai.rb # Homebrew formula
|
|
35
|
-
bin/advai.js # npm entry point bridge
|
|
36
|
-
install.sh # one-click installer script
|
|
37
|
-
pyproject.toml # PyPI build configuration
|
|
38
|
-
package.json # npm package configuration
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
---
|
|
42
|
-
|
|
43
|
-
## Local Development
|
|
44
|
-
|
|
45
|
-
```bash
|
|
46
|
-
# run from source
|
|
47
|
-
python3 -m advai.cli --help
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
---
|
|
51
|
-
|
|
52
|
-
## License
|
|
53
|
-
|
|
54
|
-
MIT
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "1.0.2"
|
advai_cli-1.0.2/advai/cli.py
DELETED
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
import click
|
|
2
|
-
import os
|
|
3
|
-
import sys
|
|
4
|
-
|
|
5
|
-
from advai import __version__
|
|
6
|
-
from advai.skills import install_skill, uninstall_skill, list_skills, update_skill, info_skill
|
|
7
|
-
|
|
8
|
-
SKILLS_DIR = os.path.expanduser("~/.advai/skills")
|
|
9
|
-
CONFIG_DIR = os.path.expanduser("~/.advai")
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
def _ensure_dirs():
|
|
13
|
-
os.makedirs(SKILLS_DIR, exist_ok=True)
|
|
14
|
-
os.makedirs(CONFIG_DIR, exist_ok=True)
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
@click.group()
|
|
18
|
-
@click.version_option(version=__version__, prog_name="advai")
|
|
19
|
-
def cli():
|
|
20
|
-
"""advai — cross-platform AI Skill manager"""
|
|
21
|
-
_ensure_dirs()
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
@cli.command()
|
|
25
|
-
@click.argument("skill_name")
|
|
26
|
-
@click.option("--force", is_flag=True, help="Force reinstall (overwrite existing)")
|
|
27
|
-
def install(skill_name, force):
|
|
28
|
-
"""Install a Skill: advai install <skill-name>"""
|
|
29
|
-
try:
|
|
30
|
-
install_skill(skill_name, force=force)
|
|
31
|
-
click.echo(f"✅ Skill '{skill_name}' installed successfully")
|
|
32
|
-
except FileExistsError:
|
|
33
|
-
click.echo(f"⚠️ Skill '{skill_name}' already exists, use --force to overwrite")
|
|
34
|
-
except Exception as e:
|
|
35
|
-
click.echo(f"❌ Installation failed: {e}", err=True)
|
|
36
|
-
sys.exit(1)
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
@cli.command()
|
|
40
|
-
@click.argument("skill_name")
|
|
41
|
-
def uninstall(skill_name):
|
|
42
|
-
"""Uninstall a Skill: advai uninstall <skill-name>"""
|
|
43
|
-
try:
|
|
44
|
-
uninstall_skill(skill_name)
|
|
45
|
-
click.echo(f"🗑️ Skill '{skill_name}' uninstalled")
|
|
46
|
-
except FileNotFoundError:
|
|
47
|
-
click.echo(f"⚠️ Skill '{skill_name}' is not installed")
|
|
48
|
-
except Exception as e:
|
|
49
|
-
click.echo(f"❌ Uninstall failed: {e}", err=True)
|
|
50
|
-
sys.exit(1)
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
@cli.command(name="list")
|
|
54
|
-
def list_cmd():
|
|
55
|
-
"""List locally installed Skills"""
|
|
56
|
-
skills = list_skills()
|
|
57
|
-
if not skills:
|
|
58
|
-
click.echo("(no Skills installed)")
|
|
59
|
-
return
|
|
60
|
-
click.echo("📋 Installed Skills:")
|
|
61
|
-
for s in skills:
|
|
62
|
-
click.echo(f" • {s}")
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
@cli.command()
|
|
66
|
-
@click.argument("skill_name", required=False)
|
|
67
|
-
def update(skill_name):
|
|
68
|
-
"""Update one (or all) Skills: advai update [skill-name]"""
|
|
69
|
-
try:
|
|
70
|
-
updated = update_skill(skill_name)
|
|
71
|
-
if updated:
|
|
72
|
-
for s in updated:
|
|
73
|
-
click.echo(f"🔄 {s} updated")
|
|
74
|
-
else:
|
|
75
|
-
click.echo("(nothing to update)")
|
|
76
|
-
except Exception as e:
|
|
77
|
-
click.echo(f"❌ Update failed: {e}", err=True)
|
|
78
|
-
sys.exit(1)
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
@cli.command()
|
|
82
|
-
@click.argument("skill_name")
|
|
83
|
-
def info(skill_name):
|
|
84
|
-
"""Show Skill details: advai info <skill-name>"""
|
|
85
|
-
data = info_skill(skill_name)
|
|
86
|
-
if data is None:
|
|
87
|
-
click.echo(f"⚠️ Skill '{skill_name}' not installed or has no metadata")
|
|
88
|
-
return
|
|
89
|
-
click.echo(f"ℹ️ Skill '{skill_name}':")
|
|
90
|
-
for k, v in data.items():
|
|
91
|
-
click.echo(f" {k}: {v}")
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
if __name__ == "__main__":
|
|
95
|
-
cli()
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: advai-cli
|
|
3
|
-
Version: 1.0.2
|
|
4
|
-
Summary: Cross-platform AI Skill manager
|
|
5
|
-
Author: advai
|
|
6
|
-
License-Expression: MIT
|
|
7
|
-
Requires-Python: >=3.8
|
|
8
|
-
Description-Content-Type: text/markdown
|
|
9
|
-
Requires-Dist: click>=8.1
|
|
10
|
-
|
|
11
|
-
# advai-cli — Cross-platform AI Skill Manager
|
|
12
|
-
|
|
13
|
-
A Python CLI that lets you install / uninstall AI Skills.
|
|
14
|
-
|
|
15
|
-
- **Platforms**: macOS / Linux / Windows
|
|
16
|
-
- **Installation**: pip, npm, or one-line script
|
|
17
|
-
- **Commands**: install, uninstall, list, update, info
|
|
18
|
-
|
|
19
|
-
---
|
|
20
|
-
|
|
21
|
-
## Quick Start
|
|
22
|
-
|
|
23
|
-
```bash
|
|
24
|
-
# pip (recommended)
|
|
25
|
-
pip install advai-cli
|
|
26
|
-
|
|
27
|
-
# install after installation, the `advai` command is ready to use
|
|
28
|
-
|
|
29
|
-
Advai --help
|
|
30
|
-
advai --version
|
|
31
|
-
advai install <skill-name>
|
|
32
|
-
advai uninstall <skill-name>
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
---
|
|
36
|
-
|
|
37
|
-
## Project Structure
|
|
38
|
-
|
|
39
|
-
```
|
|
40
|
-
advai/ # core CLI (Python)
|
|
41
|
-
__init__.py
|
|
42
|
-
cli.py # entry point (click-based)
|
|
43
|
-
skills.py # Skill install / uninstall / metadata logic
|
|
44
|
-
Formula/advai.rb # Homebrew formula
|
|
45
|
-
bin/advai.js # npm entry point bridge
|
|
46
|
-
install.sh # one-click installer script
|
|
47
|
-
pyproject.toml # PyPI build configuration
|
|
48
|
-
package.json # npm package configuration
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
---
|
|
52
|
-
|
|
53
|
-
## Local Development
|
|
54
|
-
|
|
55
|
-
```bash
|
|
56
|
-
# run from source
|
|
57
|
-
python3 -m advai.cli --help
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
---
|
|
61
|
-
|
|
62
|
-
## License
|
|
63
|
-
|
|
64
|
-
MIT
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|