Skip to content

feat(cli): add uninstall command, in the CLI and the TUI menu - #200

Open
sosidudku1 wants to merge 1 commit into
mainfrom
feat/uninstall-command
Open

feat(cli): add uninstall command, in the CLI and the TUI menu#200
sosidudku1 wants to merge 1 commit into
mainfrom
feat/uninstall-command

Conversation

@sosidudku1

Copy link
Copy Markdown
Collaborator

Closes #135.

There was no documented way to remove Atomic Agent. The README covered installing in one line and said nothing about the reverse, and the FAQ's answer ("delete the binary, delete the state directory") missed most of what the installers actually write.

What the installers leave behind

Three separate things, not one:

Where
Binary, atag alias, and six asset trees ~/.local/bin, or %LOCALAPPDATA%\atomic-agent on Windows
A PATH block .zshrc / .bashrc / .bash_profile / .profile / fish config, or the registry on Windows
State directory ~/.atomic-agent by default

Only the first is obvious to a user. The other two survive any manual cleanup.

The command

Scopes mirror the flag design Hermes and OpenClaw both settled on:

--app     binary, `atag` alias, and the installed asset trees
--path    the `# added by atomic-agent installer` block
--state   config, sessions, memory, secrets, downloaded models
--all     every scope
--dry-run print the plan and exit, changing nothing
--yes     skip the confirmation, for scripts

Default is --app --path. The state directory is not in it. Removing the program should not destroy the work done with it, and a reinstall then resumes where the operator left off. Erasing it takes an explicit --state / --all, and the confirmation says plainly that API keys and transcripts go with it.

Two guards worth review attention

The install directory is never deleted when other programs live there. The POSIX default is ~/.local/bin, which on a real machine also holds unrelated binaries, so the plan removes the files it installed by name and leaves the directory. A directory named after the product (the Windows default) is ours, and is removed outright. When in doubt the check returns "shared": a stray empty directory is harmless, deleting someone's ~/bin is not.

The --app scope is skipped when not running from an installed binary. Under node / tsx in a dev checkout execPath is the Node binary, so "the files beside it" would be someone's Node install. Reuses the existing canSelfUpdate() predicate.

Planning is pure and separate from execution, so --dry-run and the real run share one plan and cannot drift apart.

TUI

Same flow as /uninstall, or Setup in the Ctrl+P menu. The node carries a slash name, so it is clickable, keyboard-selectable and searchable in the palette through the existing registry projection rather than a second dispatch path.

It deliberately claims no chord: a destructive flow should not sit one keystroke behind the leader.

The confirm dialog renders the concrete plan rather than a bare "are you sure?", since the honest answer depends on which scopes are in play. s toggles the state scope (defaulting off, turning the border red when on), and the dialog locks while the removal is in flight so a repeated y cannot fire it twice. Registered in modalOwnsInput so a click cannot land on the surface painted behind it.

Testing

76 tests across the new surfaces:

  • Planner — scope selection, the shared-directory guard (asserting a foreign binary in ~/.local/bin survives every plan), PATH-block stripping and its idempotence, partial-failure reporting, binary removed last so a mid-way failure still leaves something to retry with.
  • CLI parser — defaults, --all, repeated scopes, unknown options.
  • Reducer — including that the plan cannot be changed once submitting.
  • Full-app render — real keystrokes through ink-testing-library: /uninstall opens, n cancels, s toggles with the warning, Esc closes.

Ablation-checked: breaking the shared-directory guard fails 3 tests, removing the s toggle fails 1. Full suite is green apart from two failures that also reproduce on a clean main (send-message-concurrency, fs-glob-real).

Verified by hand as well: declining at the prompt changes nothing, and a real --state removal against a sandboxed ATOMIC_AGENT_STATE_DIR removed exactly that directory.

A matching rewrite of the site's FAQ answer is staged separately in the site repo.

🤖 Generated with Claude Code

There was no documented way to remove Atomic Agent. The README covered
installing in one line and said nothing about the reverse, and the FAQ's
answer ("delete the binary, delete the state directory") missed most of
what the installers actually write.

The installers lay down three separate things: the binary plus six asset
trees beside it, a PATH block in a shell rc file, and the state
directory. Only the first is obvious to a user; the other two are left
behind by any manual cleanup.

`atomic-agent uninstall` removes them by scope, mirroring the flag design
Hermes and OpenClaw settled on:

  --app    binary, `atag` alias, and the installed asset trees
  --path   the `# added by atomic-agent installer` block
  --state  config, sessions, memory, secrets, downloaded models
  --all    every scope, plus --dry-run and --yes

The state directory is NOT in the default set. Removing the program
should not destroy the work done with it, and a reinstall then resumes
where the operator left off. Erasing it takes an explicit --state/--all,
and the confirmation says plainly that API keys and transcripts go with
it.

Two guards worth naming:

- The install directory is never deleted when other programs live there.
  The POSIX default is ~/.local/bin, which on a real machine also holds
  unrelated binaries, so the plan removes files it installed *by name*.
  A directory named after the product (the Windows default) is ours and
  is removed outright.
- The --app scope is skipped when not running from an installed binary.
  Under `node`/`tsx` in a dev checkout, execPath is the Node binary, and
  "the files beside it" would be someone's Node install.

Planning is pure and separate from execution, so --dry-run and the real
run share one plan and cannot drift apart.

In the TUI the same flow is `/uninstall`, or Setup in the Ctrl+P menu.
The node carries a slash name, so it is clickable, keyboard-selectable
and searchable in the palette through the existing registry projection
rather than a second dispatch path. It deliberately claims no chord: a
destructive flow should not be one keystroke behind the leader. The
confirm dialog renders the concrete plan instead of a bare "are you
sure?", toggles the state scope on `s`, and locks while the removal is
in flight so a repeated `y` cannot fire it twice.

Tests: the planner (scope selection, the shared-directory guard, PATH
block stripping, partial-failure reporting), the CLI parser, the reducer,
and a full-app render exercising the real keystrokes.

Closes #135

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature request: provide uninstallation setup in README

1 participant