apm uninstall
Remove one or more APM packages from apm.yml, the lockfile, apm_modules/, and every deployed primitive across all configured harnesses.
Synopsis
Section titled “Synopsis”apm uninstall [OPTIONS] PACKAGES...Description
Section titled “Description”apm uninstall is the inverse of apm install <package>. It removes selected
packages and unused transitive dependencies from APM-managed state,
including apm_modules/, configured targets, the manifest, and the lockfile.
The command only deletes files tracked in the lockfile’s deployed_files manifest, so hand-authored content in the same harness folders is left alone.
Arguments
Section titled “Arguments”| Argument | Description |
|---|---|
PACKAGES... |
One or more packages to remove. Accepts shorthand (owner/repo), HTTPS URL, SSH URL, FQDN, marketplace notation (name@marketplace), an exact declared local path, or the portable _local/<name> identifier printed for a direct local dependency with matching lock metadata. Required. |
Options
Section titled “Options”| Option | Description |
|---|---|
--dry-run |
Show the removal plan without writes from the uninstall engine. pre-uninstall lifecycle scripts still run and may have side effects. Registry fallback and shared-slot survivor staging are skipped. |
-v, --verbose |
Show detailed removal information. |
-g, --global |
Remove from the user scope (~/.apm/) instead of the current project. |
Examples
Section titled “Examples”Remove one package:
apm uninstall acme/my-packageRemove several at once:
apm uninstall org/pkg1 org/pkg2Preview the uninstall engine’s plan (pre-uninstall scripts still run):
apm uninstall acme/my-package --dry-runRemove from the user scope:
apm uninstall -g acme/my-packageRemove a local dependency by copying the portable key from apm deps list:
apm deps listapm uninstall "_local/my-local-package"The same key works at user scope, so scripts do not need the absolute path used when the local package was installed:
apm deps list -gapm uninstall -g "_local/my-local-package"Remove by marketplace name (resolved via lockfile, then registry):
apm uninstall my-plugin@officialResolve via URL (same identity as the shorthand):
apm uninstall https://github.com/acme/my-package.gitBehavior
Section titled “Behavior”What gets removed, in order:
- Package folders under
apm_modules/, including requested packages and transitive dependencies that no remaining package needs. A transitive dependency still required by a surviving package is preserved, including shared diamond dependencies. If a surviving package’s manifest cannot be read, APM keeps every remaining candidate rather than guessing. Run again with--verbose, fix or restore the reported manifest, then retry. Now-emptyapm_modules/parent directories are cleaned at this step. - Target-scoped files owned only by the removed packages, while lockfile ownership is still available for safe cleanup.
- Package declarations in
apm.yml. - Remaining files in the lockfile’s
deployed_filesfor the removed packages and pruned orphans, across target-owned folders such as.github/,.claude/,.grok/, and.agents/. - Hook entries inside
.claude/settings.json,.cursor/hooks.json,.gemini/settings.json, and.kiro/hooks/that the removed packages contributed. Remaining packages – including transitive dependencies still required by another package – have their hook entries rebuilt from the post-removal lockfile. - MCP and LSP servers contributed only by the removed packages. For current
lockfiles, MCP cleanup touches only the runtimes recorded as owners in
mcp_target_servers. An explicitly empty ownership map is a no-op. Older lockfiles adopt only self-defined entries that exactly match their stored configuration baseline. When another surviving package declares the same LSP server name, ownership transfers to that package instead of deleting the shared entry. Cleanup attempts every owning runtime before exiting nonzero on failure. Fix the reported configs, then runapm installto reconcile stale entries. - Lockfile entries. If no dependencies remain,
apm.lock.yamlis deleted.
Selection is atomic. If any requested identifier does not match a declaration, the command exits nonzero before lifecycle scripts or filesystem writes run. No matched package in the same invocation is removed. Fix the identifier and retry.
If deletion of a requested or orphan materialized directory fails, or APM
refuses an unsafe path that fails containment checks, uninstall exits 1 without
printing Uninstall complete. These deletions precede all target cleanup and
manifest or lockfile writes, so declarations, the on-disk lockfile, and deployed
ownership remain. Earlier deletions are not rolled back.
Fix permission or file-lock errors; for a containment refusal, correct the
unsafe path instead. Retry the same apm uninstall command, or restore declared
packages with apm install (apm install --global for user scope).
If replacing a shared local install slot fails, uninstall also retains manifest
and lockfile ownership. Resolve the filesystem problem and retry the same
command; --verbose includes the underlying error. Earlier deletions in a
multi-package request are not rolled back.
If a target-scoped file owned only by a removed package was edited or cannot be
deleted, uninstall lists the retained paths and exits before changing apm.yml
or the on-disk apm.lock.yaml. Direct and orphan directories run first and may
already be gone; declarations and deployed ownership remain. Resolve the listed
files and retry the same uninstall command.
If a managed hook changes after the initial check or is beneath a symlinked parent, uninstall preserves and lists the path. Package removal finishes, but the command exits nonzero because hook cleanup is incomplete. Inspect or repair the path before removing anything manually.
If safe LSP cleanup fails after package removal, uninstall exits nonzero,
preserves the conflicting configuration, and tells you to repair the path or
ownership conflict. Run apm install to reconcile project state, or
apm install --global after a global uninstall.
_local/<name> is resolved from the manifest and lockfile metadata that produced
the apm deps list row. APM does not reinterpret it as owner/repo or rebuild an
absolute path. If two declared local dependencies have the same portable key,
selection is ambiguous and exits nonzero without changes. Use one exact path
already declared in the relevant manifest (apm.yml or ~/.apm/apm.yml); APM
never removes both or guesses, and diagnostics do not print declared paths.
When an exact path removes one declaration from a shared local install slot, APM
validates and stages the survivor before changing the manifest, then activates
it during cleanup.
If more than one declaration would remain in that slot, uninstall fails without
APM writes. Pass enough exact paths in one command that at most one declaration
remains.
If a marketplace ref cannot be resolved (neither the lockfile nor the registry has a matching entry), APM logs an error and aborts without changes. Use owner/repo notation to uninstall directly, or run apm deps list to find the canonical name.
Supply-chain guard
Section titled “Supply-chain guard”When marketplace notation (name@marketplace) falls through to the registry (Stage 2), APM refuses any canonical the registry returns that is not already recorded in apm.lock.yaml. The refusal is reported as a warning naming the resolved canonical so you can decide whether to re-run with apm uninstall owner/repo directly. This prevents a poisoned marketplace registry from coercing APM into removing an unrelated installed package.
#ref is not meaningful for uninstall
Section titled “#ref is not meaningful for uninstall”apm install accepts an optional #ref fragment (apm install NAME@MKT#ref) to pin a specific revision. apm uninstall identifies packages by canonical name only, so any #ref fragment supplied with marketplace notation (e.g. my-plugin@official#v1.0.0) is ignored.
No-lockfile behavior
Section titled “No-lockfile behavior”If apm.lock.yaml is not present, marketplace notation has no offline anchor: Stage 1 finds nothing, and the supply-chain guard cannot cross-check the registry result. APM still attempts registry resolution and proceeds if the canonical matches an entry in apm.yml, but this path has weaker integrity guarantees. A local _local/<name> key also has no persisted mapping without the lockfile, so use its exact declared path or run apm install to regenerate the lockfile first.
--dry-run keeps the uninstall engine read-only, but pre-uninstall lifecycle scripts still run and may have their own side effects. Registry fallback and shared-slot staging are skipped, so marketplace refs not already in the lockfile cannot be previewed and a real shared-slot uninstall can still fail validation; use owner/repo notation or re-run without --dry-run.
Related
Section titled “Related”apm install– the inverse operation.apm prune– remove orphaned packages without naming them.apm deps list– list installed dependencies and copy portable local identifiers.- Lockfile spec – how
deployed_filesdrives safe cleanup.