Skip to main content
← Back to list
01Issue
FeatureClosedSwamp Club
AssigneesNone

feat: implement `extension rm` command

Opened by swampadmin · 12/20/2024

Summary

Add swamp extension rm <name> (aliased as extension remove) to cleanly remove a pulled extension and all its installed files.

Why this is the right approach

The files array in upstream_extensions.json is the key. When you pull an extension, its files are scattered across multiple directories — models go to extensions/models/, workflows to extensions/workflows/, bundles to .swamp/bundles/, and additional files go to the models dir too. There's no single directory per extension. The only reliable way to know "which files belong to @keeb/ssh" is the file manifest added in PR #520. Without it, removal would require guessing based on directory names or forcing the user to specify files manually.

Warn on reverse dependencies instead of blocking. If @keeb/ssh depends on @keeb/base and you remove @keeb/base, @keeb/ssh may break. But blocking removal is too rigid — the user might be about to remove both, or might know their setup doesn't need the dependency. A warning gives them the information to make the right call. This matches how npm uninstall works.

Error on legacy entries (no files array) with a re-pull hint. Best-effort scanning would be fragile — we'd have to guess which files belong to an extension by pattern-matching directory names, and we'd almost certainly miss workflows, bundles, or additional files. The safe answer is: "we don't know what files this extension installed, re-pull with --force to get the manifest, then rm." One-time operation for anyone who pulled before PR #520.

Prune empty directories after deletion. Extensions typically install into a subdirectory like extensions/models/ssh/. After deleting all files, leaving an empty ssh/ folder is confusing — it looks like something is still installed.

Co-locate JSON mutations in extension_pull.ts. All mutations to upstream_extensions.json use the same lockfile + atomic-write pattern. Keeping removeUpstreamExtension next to updateUpstreamExtensions means one place to maintain the concurrency logic.

Proposed behavior

swamp extension rm @keeb/ssh
  1. Validate the extension name format (@namespace/name)
  2. Read upstream_extensions.json and find the entry
  3. If no files array exists (legacy entry), error with a re-pull hint
  4. Check if other installed extensions list this one as a dependency — warn if so
  5. Show files that will be deleted and prompt for confirmation (unless --force)
  6. Delete each file listed in the files array (skip gracefully if already missing)
  7. Prune empty parent directories left behind
  8. Remove the entry from upstream_extensions.json atomically
  9. Render success output

Implementation plan

Files to create

File Purpose
src/cli/commands/extension_rm.ts Command implementation (follows model_delete.ts pattern)
src/presentation/output/extension_rm_output.ts Render functions for log + JSON output
src/presentation/output/extension_rm_output_test.ts Output unit tests
src/cli/commands/extension_rm_test.ts Command unit tests
integration/extension_rm_test.ts Integration tests (pull then rm, verify cleanup)

Files to modify

File Change
src/cli/commands/extension.ts Register rm subcommand
src/cli/commands/extension_pull.ts Export new removeUpstreamExtension function

Command interface

swamp extension rm <extension>
  --repo-dir <dir>    Repository directory (default: ".")
  -f, --force         Skip confirmation prompt

Aliases: extension remove

Edge cases

  • File already deleted manually: Skip gracefully, count as filesSkipped
  • Extension not in JSON: UserError("Extension @ns/name is not installed.")
  • Legacy entry without files: Error with re-pull hint
  • Concurrent pull/rm: Handled by existing lockfile mechanism
  • Version in argument: @keeb/ssh@1.0.0 — ignore version, remove whatever is installed

Dependency warning

For each other entry in upstream_extensions.json, check if a manifest.yaml exists among its tracked files. If so, parse it and check its dependencies array. If the target extension appears, warn:

Warning: @keeb/other depends on @keeb/ssh. Removing it may break that extension.

Output

Log mode:

Removed @keeb/ssh (v2026.02.26.1) — deleted 3 files

JSON mode:

{
  "removed": {
    "name": "@keeb/ssh",
    "version": "2026.02.26.1",
    "filesDeleted": 3,
    "filesSkipped": 0,
    "dirsRemoved": 1
  }
}

Prerequisite

  • PR #520 (track extracted files in upstream_extensions.json) must be merged first
02Bog Flow
OPENTRIAGEDIN PROGRESSCLOSED

Closed

No activity in this phase yet.

03Sludge Pulse

Sign in to post a ripple.