Guide · Uninstall

Uninstall is manifest-aware

Every file fastpace touches gets recorded in a signed install-manifest. Uninstall reads that manifest and removes only what fastpace itself placed — your own hooks, agents, skills, and settings entries are provably untouched. This guide is the long-form version of the scope picker on the home page.

Part 1

Why the manifest exists

Claude wires hooks/agents/skills by exact filename — e.g. .claude/hooks/pre-tool-use.js, .claude/hooks/post-tool-use.js. We can't namespace fastpace's files with an fp- prefix the way we do slash commands; Claude expects specific names and we have to ship those names. So fastpace ends up writing files into .claude/ at paths a user might independently want to own.

The pre-2026-05-15 uninstall sidestepped this by wiping the whole .claude/ directory — which destroyed any non-fastpace hooks/agents/skills the user added on their own. The manifest fixes that: install records exactly what fastpace placed; uninstall removes only those entries.

The safety property

A user who put their own pre-tool-use.js in .claude/hooks/ before fastpace init ran keeps their file after both init AND uninstall. The packages/fp/test/uninstall.test.js suite asserts this end-to-end.

Part 2

The manifest itself

Two manifests, identical schema, two locations:

~/.fastpace/install-manifest.json          (user-global scope)
<repo>/fastpace/install-manifest.json      (per-repo scope)

Each is a JSON document signed by the F0.1 install identity (Ed25519). The schema:

{
  "schema_version": 1,
  "installed_by":   "fastpace v0.46.2",
  "installed_at":   "2026-05-15T08:14:00.000Z",
  "scope":          "user" | "repo",
  "synthesised":    false,
  "files": [
    {
      "path":                 ".claude/hooks/pre-tool-use.js",
      "kind":                 "hook" | "agent" | "skill" | "settings",
      "owner":                "fastpace",
      "pre_existing":         false,
      "sha256_at_install":    "ab12..."
    }
  ],
  "settings_patches": [
    {
      "path":            ".claude/settings.json",
      "added_keys":      ["hooks.PreToolUse[0]", "hooks.PreToolUse[1]"],
      "original_values": { "hooks.PreToolUse[0]": null }
    }
  ],
  "signed_by":  { "install_id": "i_xxxxxxxx", "fingerprint": "sha256:..." },
  "entry_hash": "<sha256 over canonical-JSON exclusive of entry_hash+signature>",
  "signature":  "<base64 ed25519 of entry_hash>"
}

Three load-bearing fields

Why it's signed

A tampered manifest could trick uninstall into deleting arbitrary files. Signing closes that. Uninstall verifies the signature before any destructive action; if verification fails, it refuses to act unless you explicitly pass --skip-manifest-verify (loud warning, recommended only with --dry-run for inspection).

Verification is rotation-aware. After fastpace identity rotate, the current key changes, but the archived key in ~/.fastpace/identity/archive/ still validates manifests signed before the rotation. You can rotate keys as often as compliance requires without invalidating prior installs.

Part 3

The flag matrix

Default scope is gentle: bare fastpace uninstall touches only the user-global manifest. The four optional scope flags compose, with two shorthand collapses.

CommandTouchesPrompt
fastpace uninstall user-global manifest only (gentle default) type "yes"
--repo also: current repo's .claude/ files type "yes"
--settings also: reverse-merge fastpace keys out of settings.json type "yes"
--fastpace-dir also: remove the repo's fastpace/ dir (audit log LOST) type "yes"
--user-identity also: remove ~/.fastpace/ (Ed25519 keypair + license cache) type "yes"
--all shorthand: --repo --settings --fastpace-dir type "yes"
--everything / --nuke shorthand: --all --user-identity double-confirm
--force / -y non-interactive (CI / scripts)
--dry-run print the plan, change nothing
--skip-manifest-verify proceed even if signature is invalid (loud warning)
--keep-modified default for modified files: keep
--force-delete-modified delete modified files without prompting

Per-file behaviour

Order of operations

Most-recoverable first, identity last:

  1. settings.json reverse-merge
  2. user-scope file deletes
  3. repo-scope file deletes
  4. repo fastpace/ directory removal
  5. user identity directory removal — last, because it destroys the signing key and would invalidate manifest verification on any subsequent step
Part 4

Install-side: refuse to overwrite

The manifest model only works if install never silently replaces user-owned files. fastpace init now refuses to overwrite. The behaviour at install time, per file:

The collision summary at the end of fastpace init looks like:

fastpace init

✗ skipped 2 files that already existed (your versions kept,
  fastpace will not touch them):
    • .claude/hooks/pre-tool-use.js
    • .claude/agents/reviewer.md

  To adopt fastpace's versions instead: back yours up, delete
  the file, then run fastpace init --reinstall.

Skipped files don't enter the manifest, so uninstall correctly never touches them — that's the safety guarantee end-to-end.

Part 5

`fastpace update` orphan handling

When a hook was in yesterday's bundled set but isn't today's, fastpace update:

Customisations are preserved; truly orphan unmodified files go away without prompting. From the customisation moment forward, fastpace doesn't manage that file, and uninstall doesn't touch it either.

Upgrading from a pre-manifest version

Users on a pre-manifest build who run fastpace update get a synthesised manifest: update walks today's bundled file set against on-disk content and claims anything that exists with its current SHA-256. Imperfect — a user-customised file that happens to live at a fastpace-bundled path will be claimed — but the modification check in uninstall protects them: the next uninstall sees the SHA-256 has drifted from what's bundled today, classifies it as modified, and prompts before doing anything.

Part 6

The full back-out story

Removing fastpace from a machine in one sitting:

# 1. Clean the repo + reverse-merge settings.json + delete fastpace/
fastpace uninstall --all

# 2. Remove the per-user identity + license cache (optional but
#    completes the "no traces left" goal)
fastpace uninstall --user-identity

# 3. Remove the CLI itself
npm uninstall -g @fastpace-ai/fp

Or in one command (the --nuke shorthand):

fastpace uninstall --nuke
npm uninstall -g @fastpace-ai/fp

Nothing about fastpace phones home. Removing the CLI removes the binary; no server-side state to clean up. The licensing service auto-prunes inactive install_ids after 90 days as housekeeping — no action required from you.

Part 7

Recovery + edge cases

Manifest verification failed

If the manifest has been tampered with (or corrupted), uninstall refuses to act and prints:

✗ repo-scope install-manifest failed signature verification:
    entry_hash does not match recomputation (manifest tampered with)

  Recovery: fastpace uninstall --dry-run --skip-manifest-verify
  to inspect without touching anything; then pass --skip-manifest-verify
  to force.

Always start with --dry-run — it prints the plan without changing anything, so you can see what would happen before committing.

Forensic verification after --user-identity

Deleting ~/.fastpace/ destroys the Ed25519 keypair that signs the audit chain. Past audit logs and manifests remain verifiable only against the archived public key, which also lives in ~/.fastpace/identity/archive/ — so deleting ~/.fastpace takes the archive with it.

If you might need forensic verification of past audit logs later (e.g. for an audit two years out), export the identity first:

fastpace identity show --json > identity.json
# Stash identity.json somewhere durable (S3, password manager, etc.).
fastpace uninstall --user-identity

The one-time post-upgrade notice

Users on the prior (wipe-the-repo) fastpace uninstall will see a one-time notice the first time they run uninstall after upgrading:

NOTE: in this version 'fastpace uninstall' no longer touches the current
repo by default. Pass --repo to also remove .claude/ files in this
directory. Pass --all for repo + settings + fastpace/ removal
(the old default behaviour).

The notice is tracked in ~/.fastpace/notices-seen.json so it fires once and then shuts up.

Related

See also