agenthub-cli 0.1.0__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.
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: agenthub-cli
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: CLI tool for AgentHub — AI Agent Skills Registry Platform
|
|
5
|
+
Requires-Python: >=3.11
|
|
6
|
+
Requires-Dist: agenthub-shared
|
|
7
|
+
Requires-Dist: httpx>=0.27
|
|
8
|
+
Requires-Dist: rich>=13.0
|
|
9
|
+
Requires-Dist: typer>=0.12
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "agenthub-cli"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "CLI tool for AgentHub — AI Agent Skills Registry Platform"
|
|
9
|
+
requires-python = ">=3.11"
|
|
10
|
+
dependencies = [
|
|
11
|
+
"typer>=0.12",
|
|
12
|
+
"httpx>=0.27",
|
|
13
|
+
"rich>=13.0",
|
|
14
|
+
"agenthub-shared",
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
[project.scripts]
|
|
18
|
+
agenthub = "skills_registry.main:app"
|
|
19
|
+
|
|
20
|
+
[tool.hatch.build.targets.wheel]
|
|
21
|
+
packages = ["skills_registry"]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Skills Registry CLI."""
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"""HTTP client for Skills Registry API."""
|
|
2
|
+
|
|
3
|
+
import httpx
|
|
4
|
+
from .config import get_registry_url, get_api_key
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class RegistryClient:
|
|
8
|
+
def __init__(self):
|
|
9
|
+
self._client = httpx.Client(
|
|
10
|
+
base_url=get_registry_url(),
|
|
11
|
+
headers={"X-API-Key": get_api_key()},
|
|
12
|
+
timeout=30.0,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
def _handle(self, resp: httpx.Response) -> dict | list | None:
|
|
16
|
+
if resp.status_code == 204:
|
|
17
|
+
return None
|
|
18
|
+
if not resp.is_success:
|
|
19
|
+
detail = resp.json().get("detail", resp.text) if resp.headers.get("content-type", "").startswith("application/json") else resp.text
|
|
20
|
+
raise RuntimeError(f"API error ({resp.status_code}): {detail}")
|
|
21
|
+
return resp.json()
|
|
22
|
+
|
|
23
|
+
# Skills
|
|
24
|
+
def search_skills(self, keyword: str | None = None, tag: str | None = None) -> dict:
|
|
25
|
+
params = {}
|
|
26
|
+
if keyword: params["keyword"] = keyword
|
|
27
|
+
if tag: params["tag"] = tag
|
|
28
|
+
return self._handle(self._client.get("/api/v1/skills", params=params))
|
|
29
|
+
|
|
30
|
+
def get_skill_install(self, skill_id: int) -> dict:
|
|
31
|
+
return self._handle(self._client.get(f"/api/v1/skills/{skill_id}/install"))
|
|
32
|
+
|
|
33
|
+
def create_skill(self, data: dict) -> dict:
|
|
34
|
+
return self._handle(self._client.post("/api/v1/skills", json=data))
|
|
35
|
+
|
|
36
|
+
def record_skill_install(self, skill_id: int, agent_type: str = "kiro"):
|
|
37
|
+
self._handle(self._client.post(f"/api/v1/skills/{skill_id}/install", params={"agent_type": agent_type}))
|
|
38
|
+
|
|
39
|
+
# MCPs
|
|
40
|
+
def search_mcps(self, keyword: str | None = None) -> dict:
|
|
41
|
+
params = {}
|
|
42
|
+
if keyword: params["keyword"] = keyword
|
|
43
|
+
return self._handle(self._client.get("/api/v1/mcps", params=params))
|
|
44
|
+
|
|
45
|
+
def get_mcp_install(self, mcp_id: int) -> dict:
|
|
46
|
+
return self._handle(self._client.get(f"/api/v1/mcps/{mcp_id}/install"))
|
|
47
|
+
|
|
48
|
+
def record_mcp_install(self, mcp_id: int, agent_type: str = "kiro"):
|
|
49
|
+
self._handle(self._client.post(f"/api/v1/mcps/{mcp_id}/install", params={"agent_type": agent_type}))
|
|
50
|
+
|
|
51
|
+
# Agents
|
|
52
|
+
def search_agents(self, keyword: str | None = None) -> dict:
|
|
53
|
+
params = {}
|
|
54
|
+
if keyword: params["keyword"] = keyword
|
|
55
|
+
return self._handle(self._client.get("/api/v1/agents", params=params))
|
|
56
|
+
|
|
57
|
+
def get_agent_install(self, agent_id: int) -> dict:
|
|
58
|
+
return self._handle(self._client.get(f"/api/v1/agents/{agent_id}/install"))
|
|
59
|
+
|
|
60
|
+
def record_agent_install(self, agent_id: int, agent_type: str = "kiro"):
|
|
61
|
+
self._handle(self._client.post(f"/api/v1/agents/{agent_id}/install", params={"agent_type": agent_type}))
|
|
62
|
+
|
|
63
|
+
# Search
|
|
64
|
+
def search_all(self, keyword: str) -> dict:
|
|
65
|
+
return self._handle(self._client.get("/api/v1/search", params={"q": keyword}))
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"""CLI configuration management — ~/.skills-registry/config.toml"""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
CONFIG_DIR = Path.home() / ".agenthub"
|
|
7
|
+
CONFIG_FILE = CONFIG_DIR / "config.toml"
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _ensure_config():
|
|
11
|
+
CONFIG_DIR.mkdir(parents=True, exist_ok=True)
|
|
12
|
+
if not CONFIG_FILE.exists():
|
|
13
|
+
CONFIG_FILE.write_text(
|
|
14
|
+
'[registry]\nurl = "http://localhost:8000"\napi_key = ""\n',
|
|
15
|
+
encoding="utf-8",
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def get_config() -> dict:
|
|
20
|
+
_ensure_config()
|
|
21
|
+
# Simple TOML parser (avoid extra dependency)
|
|
22
|
+
config = {"registry": {"url": "http://localhost:8000", "api_key": ""}}
|
|
23
|
+
for line in CONFIG_FILE.read_text(encoding="utf-8").splitlines():
|
|
24
|
+
line = line.strip()
|
|
25
|
+
if line.startswith("url"):
|
|
26
|
+
config["registry"]["url"] = line.split("=", 1)[1].strip().strip('"')
|
|
27
|
+
elif line.startswith("api_key"):
|
|
28
|
+
config["registry"]["api_key"] = line.split("=", 1)[1].strip().strip('"')
|
|
29
|
+
return config
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def set_config(key: str, value: str):
|
|
33
|
+
_ensure_config()
|
|
34
|
+
config = get_config()
|
|
35
|
+
parts = key.split(".")
|
|
36
|
+
if len(parts) == 2 and parts[0] == "registry":
|
|
37
|
+
config["registry"][parts[1]] = value
|
|
38
|
+
content = f'[registry]\nurl = "{config["registry"]["url"]}"\napi_key = "{config["registry"]["api_key"]}"\n'
|
|
39
|
+
CONFIG_FILE.write_text(content, encoding="utf-8")
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def get_registry_url() -> str:
|
|
43
|
+
return os.getenv("SKILLS_REGISTRY_URL", get_config()["registry"]["url"])
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def get_api_key() -> str:
|
|
47
|
+
return os.getenv("SKILLS_REGISTRY_API_KEY", get_config()["registry"]["api_key"])
|
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
"""Skills Registry CLI — main entry point."""
|
|
2
|
+
|
|
3
|
+
import subprocess
|
|
4
|
+
import sys
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
import httpx
|
|
8
|
+
import typer
|
|
9
|
+
from rich.console import Console
|
|
10
|
+
from rich.table import Table
|
|
11
|
+
|
|
12
|
+
from .client import RegistryClient
|
|
13
|
+
from .config import set_config, get_config, get_registry_url
|
|
14
|
+
from skills_registry_shared.parsers import parse_skill_md_file
|
|
15
|
+
from skills_registry_shared.adapters import AdapterFactory, Scope, InstallMethod
|
|
16
|
+
|
|
17
|
+
app = typer.Typer(name="agenthub", help="AgentHub CLI — AI Agent Skills Registry", pretty_exceptions_enable=False)
|
|
18
|
+
mcp_app = typer.Typer(name="mcp", help="MCP Server commands", pretty_exceptions_enable=False)
|
|
19
|
+
agent_app = typer.Typer(name="agent", help="Agent config commands", pretty_exceptions_enable=False)
|
|
20
|
+
config_app = typer.Typer(name="config", help="CLI configuration", pretty_exceptions_enable=False)
|
|
21
|
+
app.add_typer(mcp_app)
|
|
22
|
+
app.add_typer(agent_app)
|
|
23
|
+
app.add_typer(config_app)
|
|
24
|
+
|
|
25
|
+
console = Console(stderr=True)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _client() -> RegistryClient:
|
|
29
|
+
return RegistryClient()
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _handle_error(e: Exception) -> None:
|
|
33
|
+
"""Print a friendly error message and exit."""
|
|
34
|
+
if isinstance(e, httpx.ConnectError):
|
|
35
|
+
url = get_registry_url()
|
|
36
|
+
console.print(f"[red]✗ Cannot connect to registry at {url}[/red]")
|
|
37
|
+
console.print(f" Is the server running? Try: [dim]skills config show[/dim]")
|
|
38
|
+
elif isinstance(e, RuntimeError) and "API error" in str(e):
|
|
39
|
+
console.print(f"[red]✗ {e}[/red]")
|
|
40
|
+
elif isinstance(e, httpx.TimeoutException):
|
|
41
|
+
console.print(f"[red]✗ Request timed out. The server may be slow or unreachable.[/red]")
|
|
42
|
+
else:
|
|
43
|
+
console.print(f"[red]✗ {e}[/red]")
|
|
44
|
+
raise typer.Exit(1)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
# ── Config ──────────────────────────────────────────
|
|
48
|
+
|
|
49
|
+
@config_app.command("set")
|
|
50
|
+
def config_set(key: str, value: str):
|
|
51
|
+
"""Set a config value (e.g. registry.url, registry.api_key)."""
|
|
52
|
+
set_config(key, value)
|
|
53
|
+
console.print(f"✓ Set {key}")
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@config_app.command("show")
|
|
57
|
+
def config_show():
|
|
58
|
+
"""Show current configuration."""
|
|
59
|
+
cfg = get_config()
|
|
60
|
+
console.print(f"Registry URL: {cfg['registry']['url']}")
|
|
61
|
+
api_key = cfg["registry"]["api_key"]
|
|
62
|
+
masked = api_key[:6] + "..." if len(api_key) > 6 else "(not set)"
|
|
63
|
+
console.print(f"API Key: {masked}")
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
# ── Skills ──────────────────────────────────────────
|
|
67
|
+
|
|
68
|
+
@app.command()
|
|
69
|
+
def publish():
|
|
70
|
+
"""Publish a skill from the current directory."""
|
|
71
|
+
skill_md = Path("SKILL.md")
|
|
72
|
+
if not skill_md.exists():
|
|
73
|
+
console.print("[red]✗ SKILL.md not found in current directory.[/red]")
|
|
74
|
+
raise typer.Exit(1)
|
|
75
|
+
|
|
76
|
+
try:
|
|
77
|
+
meta = parse_skill_md_file(skill_md)
|
|
78
|
+
except Exception as e:
|
|
79
|
+
console.print(f"[red]✗ Failed to parse SKILL.md: {e}[/red]")
|
|
80
|
+
raise typer.Exit(1)
|
|
81
|
+
|
|
82
|
+
try:
|
|
83
|
+
git_url = subprocess.check_output(
|
|
84
|
+
["git", "remote", "get-url", "origin"], text=True
|
|
85
|
+
).strip()
|
|
86
|
+
commit_hash = subprocess.check_output(
|
|
87
|
+
["git", "rev-parse", "HEAD"], text=True
|
|
88
|
+
).strip()
|
|
89
|
+
except (subprocess.CalledProcessError, FileNotFoundError):
|
|
90
|
+
console.print("[red]✗ Not a git repository or git not installed.[/red]")
|
|
91
|
+
raise typer.Exit(1)
|
|
92
|
+
|
|
93
|
+
repo_root = Path(
|
|
94
|
+
subprocess.check_output(["git", "rev-parse", "--show-toplevel"], text=True).strip()
|
|
95
|
+
)
|
|
96
|
+
skill_path = str(skill_md.parent.resolve().relative_to(repo_root))
|
|
97
|
+
if skill_path == ".":
|
|
98
|
+
skill_path = ""
|
|
99
|
+
|
|
100
|
+
data = {
|
|
101
|
+
"name": meta.name,
|
|
102
|
+
"description": meta.description,
|
|
103
|
+
"version": meta.version,
|
|
104
|
+
"tags": meta.metadata.get("tags", []),
|
|
105
|
+
"git_url": git_url,
|
|
106
|
+
"git_ref": None,
|
|
107
|
+
"commit_hash": commit_hash,
|
|
108
|
+
"skill_path": skill_path,
|
|
109
|
+
"readme_content": skill_md.read_text(encoding="utf-8"),
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
try:
|
|
113
|
+
result = _client().create_skill(data)
|
|
114
|
+
console.print(f"✓ Published [bold]{result['name']}[/bold] (id: {result['id']})")
|
|
115
|
+
except Exception as e:
|
|
116
|
+
_handle_error(e)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
@app.command()
|
|
120
|
+
def add(name: str, agent: str = "kiro", method: str = "copy", scope: str = "workspace"):
|
|
121
|
+
"""Install a skill from the registry."""
|
|
122
|
+
try:
|
|
123
|
+
client = _client()
|
|
124
|
+
results = client.search_skills(keyword=name)
|
|
125
|
+
items = results.get("items", [])
|
|
126
|
+
if not items:
|
|
127
|
+
console.print(f"[yellow]No skill found matching '{name}'[/yellow]")
|
|
128
|
+
raise typer.Exit(1)
|
|
129
|
+
|
|
130
|
+
skill = items[0]
|
|
131
|
+
package = client.get_skill_install(skill["id"])
|
|
132
|
+
|
|
133
|
+
adapter = AdapterFactory.get_adapter(agent)
|
|
134
|
+
s = Scope(scope)
|
|
135
|
+
m = InstallMethod(method)
|
|
136
|
+
path = adapter.install_skill(package["files"], package["name"], s, m)
|
|
137
|
+
|
|
138
|
+
client.record_skill_install(skill["id"], agent_type=agent)
|
|
139
|
+
console.print(f"✓ Installed [bold]{package['name']}[/bold] → {path}")
|
|
140
|
+
console.print(adapter.get_post_install_hints())
|
|
141
|
+
except typer.Exit:
|
|
142
|
+
raise
|
|
143
|
+
except Exception as e:
|
|
144
|
+
_handle_error(e)
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
@app.command()
|
|
148
|
+
def find(keyword: str = typer.Argument("")):
|
|
149
|
+
"""Search for skills."""
|
|
150
|
+
try:
|
|
151
|
+
if keyword:
|
|
152
|
+
results = _client().search_all(keyword)
|
|
153
|
+
else:
|
|
154
|
+
results = {"skills": _client().search_skills()}
|
|
155
|
+
|
|
156
|
+
found = False
|
|
157
|
+
for asset_type, data in results.items():
|
|
158
|
+
items = data.get("items", []) if isinstance(data, dict) else []
|
|
159
|
+
if not items:
|
|
160
|
+
continue
|
|
161
|
+
found = True
|
|
162
|
+
table = Table(title=asset_type.capitalize())
|
|
163
|
+
table.add_column("Name")
|
|
164
|
+
table.add_column("Description")
|
|
165
|
+
table.add_column("Installs", justify="right")
|
|
166
|
+
for item in items[:20]:
|
|
167
|
+
table.add_row(item["name"], item["description"][:60], str(item.get("installs", 0)))
|
|
168
|
+
console.print(table)
|
|
169
|
+
|
|
170
|
+
if not found:
|
|
171
|
+
console.print(f"[yellow]No results found{' for ' + repr(keyword) if keyword else ''}.[/yellow]")
|
|
172
|
+
except Exception as e:
|
|
173
|
+
_handle_error(e)
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
@app.command("list")
|
|
177
|
+
def list_installed():
|
|
178
|
+
"""List locally installed skills."""
|
|
179
|
+
table = Table(title="Installed Skills")
|
|
180
|
+
table.add_column("Name")
|
|
181
|
+
table.add_column("Scope")
|
|
182
|
+
table.add_column("Path")
|
|
183
|
+
|
|
184
|
+
for scope_name, base in [("workspace", Path(".kiro/skills")), ("global", Path.home() / ".kiro/skills")]:
|
|
185
|
+
if base.exists():
|
|
186
|
+
for d in base.iterdir():
|
|
187
|
+
if d.is_dir() and (d / "SKILL.md").exists():
|
|
188
|
+
table.add_row(d.name, scope_name, str(d))
|
|
189
|
+
|
|
190
|
+
console.print(table)
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
@app.command()
|
|
194
|
+
def remove(name: str):
|
|
195
|
+
"""Remove an installed skill."""
|
|
196
|
+
import shutil
|
|
197
|
+
for base in [Path(".kiro/skills"), Path.home() / ".kiro/skills"]:
|
|
198
|
+
target = base / name
|
|
199
|
+
if target.exists() or target.is_symlink():
|
|
200
|
+
if target.is_symlink():
|
|
201
|
+
target.unlink()
|
|
202
|
+
else:
|
|
203
|
+
shutil.rmtree(target)
|
|
204
|
+
# Also clean cache
|
|
205
|
+
cache = Path(f".skills-registry/cache/{name}")
|
|
206
|
+
if cache.exists():
|
|
207
|
+
shutil.rmtree(cache)
|
|
208
|
+
console.print(f"✓ Removed [bold]{name}[/bold]")
|
|
209
|
+
return
|
|
210
|
+
console.print(f"[yellow]Skill '{name}' not found locally.[/yellow]")
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
# ── MCP ─────────────────────────────────────────────
|
|
214
|
+
|
|
215
|
+
@mcp_app.command("list")
|
|
216
|
+
def mcp_list():
|
|
217
|
+
"""List available MCP servers."""
|
|
218
|
+
try:
|
|
219
|
+
results = _client().search_mcps()
|
|
220
|
+
items = results.get("items", [])
|
|
221
|
+
table = Table(title="MCP Servers")
|
|
222
|
+
table.add_column("Name")
|
|
223
|
+
table.add_column("Description")
|
|
224
|
+
table.add_column("Transport")
|
|
225
|
+
for item in items:
|
|
226
|
+
table.add_row(item["name"], item["description"][:60], item.get("transport", ""))
|
|
227
|
+
console.print(table)
|
|
228
|
+
except Exception as e:
|
|
229
|
+
_handle_error(e)
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
@mcp_app.command("add")
|
|
233
|
+
def mcp_add(name: str, scope: str = "workspace"):
|
|
234
|
+
"""Install an MCP server config."""
|
|
235
|
+
try:
|
|
236
|
+
client = _client()
|
|
237
|
+
results = client.search_mcps(keyword=name)
|
|
238
|
+
items = results.get("items", [])
|
|
239
|
+
if not items:
|
|
240
|
+
console.print(f"[yellow]No MCP server found matching '{name}'[/yellow]")
|
|
241
|
+
raise typer.Exit(1)
|
|
242
|
+
|
|
243
|
+
mcp = items[0]
|
|
244
|
+
config = client.get_mcp_install(mcp["id"])
|
|
245
|
+
|
|
246
|
+
adapter = AdapterFactory.get_adapter("kiro")
|
|
247
|
+
inner = config["config"].get("mcpServers", {})
|
|
248
|
+
adapter.install_mcp(inner, Scope(scope))
|
|
249
|
+
|
|
250
|
+
client.record_mcp_install(mcp["id"])
|
|
251
|
+
console.print(f"✓ Added MCP Server [bold]{config['name']}[/bold] to mcp.json")
|
|
252
|
+
if config.get("env_vars_needed"):
|
|
253
|
+
console.print(f" Environment variables to configure: {', '.join(config['env_vars_needed'])}")
|
|
254
|
+
except typer.Exit:
|
|
255
|
+
raise
|
|
256
|
+
except Exception as e:
|
|
257
|
+
_handle_error(e)
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
# ── Agent ───────────────────────────────────────────
|
|
261
|
+
|
|
262
|
+
@agent_app.command("list")
|
|
263
|
+
def agent_list():
|
|
264
|
+
"""List available agent configs."""
|
|
265
|
+
try:
|
|
266
|
+
results = _client().search_agents()
|
|
267
|
+
items = results.get("items", [])
|
|
268
|
+
table = Table(title="Agent Configs")
|
|
269
|
+
table.add_column("Name")
|
|
270
|
+
table.add_column("Description")
|
|
271
|
+
table.add_column("Skills")
|
|
272
|
+
table.add_column("MCPs")
|
|
273
|
+
for item in items:
|
|
274
|
+
table.add_row(
|
|
275
|
+
item["name"],
|
|
276
|
+
item["description"][:60],
|
|
277
|
+
str(len(item.get("embedded_skills", []))),
|
|
278
|
+
str(len(item.get("embedded_mcps", []))),
|
|
279
|
+
)
|
|
280
|
+
console.print(table)
|
|
281
|
+
except Exception as e:
|
|
282
|
+
_handle_error(e)
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
@agent_app.command("add")
|
|
286
|
+
def agent_add(name: str, scope: str = "workspace", method: str = "copy"):
|
|
287
|
+
"""Install an agent config (full package)."""
|
|
288
|
+
try:
|
|
289
|
+
client = _client()
|
|
290
|
+
results = client.search_agents(keyword=name)
|
|
291
|
+
items = results.get("items", [])
|
|
292
|
+
if not items:
|
|
293
|
+
console.print(f"[yellow]No agent config found matching '{name}'[/yellow]")
|
|
294
|
+
raise typer.Exit(1)
|
|
295
|
+
|
|
296
|
+
agent = items[0]
|
|
297
|
+
package_data = client.get_agent_install(agent["id"])
|
|
298
|
+
|
|
299
|
+
from skills_registry_shared.schemas.agent import AgentInstallPackage
|
|
300
|
+
package = AgentInstallPackage(**package_data)
|
|
301
|
+
|
|
302
|
+
adapter = AdapterFactory.get_adapter("kiro")
|
|
303
|
+
summary = adapter.install_agent_config(package, Scope(scope), InstallMethod(method))
|
|
304
|
+
|
|
305
|
+
client.record_agent_install(agent["id"])
|
|
306
|
+
console.print(f"✓ Installed Agent: [bold]{package.name}[/bold]")
|
|
307
|
+
console.print(f" Skills: {len(summary.skills_installed)} installed")
|
|
308
|
+
console.print(f" MCP Servers: {len(summary.mcps_installed)} configured")
|
|
309
|
+
console.print(summary.hints)
|
|
310
|
+
except typer.Exit:
|
|
311
|
+
raise
|
|
312
|
+
except Exception as e:
|
|
313
|
+
_handle_error(e)
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
if __name__ == "__main__":
|
|
317
|
+
app()
|