Skip to content

Commit 4cb283f

Browse files
committed
new: implemented nerve uninstall command
1 parent db5c409 commit 4cb283f

File tree

4 files changed

+74
-3
lines changed

4 files changed

+74
-3
lines changed

nerve/cli/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from nerve.cli.namespaces import cli as namespaces_cli
88
from nerve.cli.replay import cli as replay_cli
99
from nerve.cli.run import cli as run_cli
10+
from nerve.cli.uninstall import cli as uninstall_cli
1011

1112
cli = typer.Typer(
1213
no_args_is_help=True,
@@ -20,6 +21,7 @@
2021
cli.add_typer(run_cli)
2122
cli.add_typer(replay_cli)
2223
cli.add_typer(namespaces_cli)
24+
cli.add_typer(uninstall_cli)
2325

2426

2527
@cli.command(

nerve/cli/agents.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,15 +38,27 @@ async def show_agents(path: pathlib.Path) -> None:
3838

3939
if path.exists() and path.is_dir():
4040
print()
41-
print(f"📁 {path.absolute()}")
41+
print(f"📁 agents in {path.absolute()}:")
4242

4343
for item in path.iterdir():
4444
if Workflow.is_workflow(item):
45-
print(f" {item.name} " + colored("<workflow>", "blue"))
45+
workflow = Workflow.from_path(item)
46+
print(
47+
f" {colored(item.name, 'white', attrs=['bold'])} "
48+
+ colored("<workflow>", "blue")
49+
+ f" - {workflow.description}"
50+
)
4651
anything = True
4752
elif Configuration.is_agent_config(item):
48-
print(f" {item.name} " + colored("<agent>", "green"))
53+
config = Configuration.from_path(item)
54+
print(
55+
f" {colored(item.name, 'white', attrs=['bold'])} "
56+
+ colored("<agent>", "green")
57+
+ f" - {config.description}"
58+
)
4959
anything = True
5060

61+
print()
62+
5163
if not anything:
5264
print(colored(f"No agents or workflows found in {path}", "light_grey"))

nerve/cli/uninstall.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import pathlib
2+
import shutil
3+
import typing as t
4+
5+
import typer
6+
7+
import nerve
8+
from nerve.cli.defaults import (
9+
DEFAULT_AGENTS_LOAD_PATH,
10+
)
11+
12+
cli = typer.Typer(
13+
no_args_is_help=True,
14+
pretty_exceptions_enable=False,
15+
context_settings={"help_option_names": ["-h", "--help"]},
16+
)
17+
18+
19+
@cli.command(
20+
context_settings={"help_option_names": ["-h", "--help"]},
21+
help=f"Uninstall an agent or workflow from {DEFAULT_AGENTS_LOAD_PATH}",
22+
)
23+
def uninstall(
24+
name: t.Annotated[
25+
str,
26+
typer.Argument(help="Name of the agent or workflow to uninstall."),
27+
],
28+
yes: t.Annotated[
29+
bool,
30+
typer.Option("--yes", "-y", help="Uninstall without asking for confirmation."),
31+
] = False,
32+
) -> None:
33+
print(f"🧠 nerve v{nerve.__version__}")
34+
35+
# make sure no funny business is happening
36+
if ".." in name:
37+
print(f"🚨 {name} is not a valid agent or workflow name")
38+
exit(1)
39+
40+
# get the path to the agent or workflow
41+
path = pathlib.Path(DEFAULT_AGENTS_LOAD_PATH) / name
42+
if not path.exists():
43+
print(f"❌ {name} does not exist in {DEFAULT_AGENTS_LOAD_PATH}")
44+
exit(1)
45+
46+
# ask for confirmation
47+
if not yes:
48+
typer.confirm(
49+
f"⚠️ Are you sure you want to delete {name} from {DEFAULT_AGENTS_LOAD_PATH}? This action is irreversible.",
50+
abort=True,
51+
)
52+
53+
# uninstall the agent or workflow
54+
shutil.rmtree(path)
55+
print(f"🧠 {name} uninstalled from {DEFAULT_AGENTS_LOAD_PATH}")

nerve/models.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,8 @@ class Configuration(BaseModel):
108108

109109
# optional generator
110110
generator: str | None = None
111+
# optional agent description
112+
description: str = ""
111113
# optional nerve version requirement
112114
requires: t.Annotated[str | None, AfterValidator(_check_required_version)] = None
113115
# used for versioning the agents

0 commit comments

Comments
 (0)