Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 21 additions & 5 deletions agentdescent/integrations/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1810,16 +1810,33 @@ def _uninstall_opencode_file(path: str, e: _Eraser) -> None:
}


def _empty_uninstall_note(host: str) -> str:
"""Line printed when uninstall finds none of this package's files.

Claude Code and Codex can also be wired through the host's own
marketplace (``/plugin install``, ``codex plugin add``), which leaves
nothing here. Grok Bot, dsh and OpenCode are only installed by
``agentdescent install`` (#890).
"""
msg = f"nothing to remove: agentdescent is not installed for {host}"
if host in ("claude-code", "codex"):
msg += (" (files from the host's own marketplace are removed "
"with its plugin command)")
return msg


def uninstall(host: str, *, dry_run: bool = False, home: Optional[str] = None) -> List[str]:
"""Undo ``install(host)``. Returns the lines to print: what was removed or kept.

Removes the files ``install`` wrote and the block or entry it added to the
host's config. A config entry that is no longer exactly what ``install``
writes -- you edited the launcher, or wrote it yourself -- is kept and
reported, and a config file with comments (OpenCode's ``opencode.jsonc``) is
never rewritten. The run store under ``~/.agentdescent`` is untouched. An
install made through the host's own marketplace is removed with that host's
plugin command. ``dry_run`` reports without changing anything.
never rewritten. The run store under ``~/.agentdescent`` is untouched.
Claude Code and Codex can also be wired through the host's own marketplace,
which leaves no files here; those hosts' empty-state note points at the
host's plugin command. Grok Bot, dsh and OpenCode are only installed by
``agentdescent install``. ``dry_run`` reports without changing anything.
"""
host = canonical_host(host)
if host not in HOSTS:
Expand All @@ -1828,6 +1845,5 @@ def uninstall(host: str, *, dry_run: bool = False, home: Optional[str] = None) -
e = _Eraser(dry_run, honour_env=honour_env)
_UNINSTALL[host](home, e)
if not e.lines:
e.note(f"nothing to remove: agentdescent is not installed for {host} "
"(files from the host's own marketplace are removed with its plugin command)")
e.note(_empty_uninstall_note(host))
return e.lines
4 changes: 4 additions & 0 deletions changelog.d/890-uninstall-marketplace-hint.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Fixed
`uninstall` no longer tells grok-bot, dsh, or OpenCode users to remove files
via a host marketplace those hosts do not have. The marketplace aside stays
only for Claude Code and Codex, which can be installed that way (#890).
35 changes: 35 additions & 0 deletions tests/test_uninstall.py
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,26 @@ def test_opencode_keeps_the_other_servers_and_settings(tmp_path):
assert after["theme"] == "dark"


def test_empty_uninstall_omits_marketplace_for_file_only_hosts(tmp_path):
"""#890: grok-bot / dsh / opencode are not installed via a host marketplace."""
for host in ("grok-bot", "dsh", "opencode"):
lines = uninstall(host, home=str(tmp_path))
assert lines == [
f"nothing to remove: agentdescent is not installed for {host}"
], lines


def test_empty_uninstall_mentions_marketplace_for_claude_code_and_codex(tmp_path):
"""#890: those two hosts really can be wired through a marketplace."""
for host in ("claude-code", "codex"):
lines = uninstall(host, home=str(tmp_path))
assert len(lines) == 1, lines
assert lines[0].startswith(
f"nothing to remove: agentdescent is not installed for {host}")
assert "marketplace" in lines[0]
assert "plugin command" in lines[0]


# -- the CLI ----------------------------------------------------------------------


Expand Down Expand Up @@ -329,6 +349,21 @@ def test_cli_uninstall_rejects_an_unknown_host():
cli.main(["uninstall", "vim"])


def test_cli_empty_uninstall_grok_bot_omits_marketplace(tmp_path, capsys):
"""#890: the user-facing grok-bot empty state has no marketplace aside."""
home = str(tmp_path)
assert cli.main(["uninstall", "grok-bot", "--home", home]) == 0
out = capsys.readouterr().out
assert "nothing to remove: agentdescent is not installed for grok-bot" in out
assert "marketplace" not in out
assert cli.main(["uninstall", "grok-bot", "--home", home, "--json"]) == 0
payload = json.loads(capsys.readouterr().out)
assert payload["ok"] is True
assert payload["lines"] == [
"nothing to remove: agentdescent is not installed for grok-bot"
]


def test_opencode_uninstall_undoes_the_opencode_json_fallback(tmp_path):
"""#516: install beside a commented opencode.jsonc, then uninstall: the
commented file is byte for byte the same and opencode.json is gone again."""
Expand Down
Loading