Releases: systeminit/swamp
swamp 20260227.211429.0-sha.5a88d8c5
What's Changed
- fix: detectConflicts uses correct nested paths for bundles (#530)
Summary
- Fixes
detectConflictsto userelative(bundlesSrc, file)instead ofbasename(file)for bundle paths, aligning it with howcopyDiractually installs bundles and how models/additional files already handle paths. - Adds a unit test verifying nested bundle conflict detection.
- Exports
detectConflictsto enable direct testing.
Context
Commit c637ddd (#509) fixed extension push to preserve nested directory structure for bundles, but detectConflicts (which runs during pull) was not updated. It still used basename(file) — the old flat-path pattern from before the fix.
User impact
Before this fix, extensions with nested bundles (like @john/k8s which installs 15 bundles under .swamp/bundles/k8s/) had broken conflict detection:
-
Missed conflicts — Re-pulling would silently overwrite files without the "files already exist" warning, because
detectConflictschecked flat paths (.swamp/bundles/netpol.js) whilecopyDirinstalled to nested paths (.swamp/bundles/k8s/netpol.js). -
False positives — Stale files from pre-fix flat installs could trigger spurious conflict warnings for paths that didn't match actual installed files.
-
Inconsistent path output —
extension rm(which uses correct nested tracking fromupstream_extensions.json) reported different paths thanextension pullconflict detection.
After this fix, detectConflicts checks the same nested paths that copyDir writes to, so conflict detection is accurate for all extensions regardless of bundle nesting depth.
Verified with
@john/k8s— 57 files including 15 nested bundles under.swamp/bundles/k8s/; all conflicts correctly detected on re-pull@stack72/system-extensions— flat bundles continue to work correctly- Full test suite (2316 tests passing)
Test plan
-
deno checkpasses -
deno lintpasses -
deno fmtpasses -
deno run test— all 2316 tests pass, including newdetectConflictstest -
deno run compile— binary compiles - Manual: init repo, pull
@john/k8s, pull again → all 57 conflicts detected at correct nested paths - Manual: init repo, pull
@stack72/system-extensions, pull again → flat bundles still detected correctly
🤖 Generated with Claude Code
Installation
macOS (Apple Silicon):
curl -L https://github.com/systeminit/swamp/releases/download/v20260227.211429.0-sha.5a88d8c5/swamp-darwin-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/macOS (Intel):
curl -L https://github.com/systeminit/swamp/releases/download/v20260227.211429.0-sha.5a88d8c5/swamp-darwin-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/Linux (x86_64):
curl -L https://github.com/systeminit/swamp/releases/download/v20260227.211429.0-sha.5a88d8c5/swamp-linux-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/Linux (aarch64):
curl -L https://github.com/systeminit/swamp/releases/download/v20260227.211429.0-sha.5a88d8c5/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/swamp 20260227.203155.0-sha.15f9ec56
What's Changed
- feat: add extension list command to show installed extensions (#526)
Fixes: #525
- Add
extension list(aliasls) command that readsupstream_extensions.json
and displays installed upstream extensions - Supports
--jsonfor structured output and--verboseto show individual files - Log mode output is column-aligned for readability, sorted alphabetically by name
Why this design
Before this change, the only way to see what extensions were installed was to
manually read upstream_extensions.json. This completes the extension lifecycle
CLI surface alongside push, pull, and rm.
The command follows the same patterns as extension rm — reusing
readUpstreamExtensions(), createContext(), requireInitializedRepo(), and
the established output rendering split. Column-aligned output keeps the list
scannable as extension names vary in length.
User impact
Users can now run swamp extension list (or swamp extension ls) to see all
installed upstream extensions at a glance, with version and pull date. The
--json flag gives machine-readable output for scripting, and --verbose lists
every file belonging to each extension.
Test plan
-
extension list --helpshows usage -
extension --helpshowslistsubcommand - Requires initialized repo
- Empty repo shows "No upstream extensions installed" with CTA
-
--jsonwith no extensions returns{ extensions: [] } - Populated repo displays entries sorted alphabetically
-
--jsonreturns structured data with name, version, pulledAt, files -
--verboseshows individual file paths -
lsalias works
Installation
macOS (Apple Silicon):
curl -L https://github.com/systeminit/swamp/releases/download/v20260227.203155.0-sha.15f9ec56/swamp-darwin-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/macOS (Intel):
curl -L https://github.com/systeminit/swamp/releases/download/v20260227.203155.0-sha.15f9ec56/swamp-darwin-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/Linux (x86_64):
curl -L https://github.com/systeminit/swamp/releases/download/v20260227.203155.0-sha.15f9ec56/swamp-linux-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/Linux (aarch64):
curl -L https://github.com/systeminit/swamp/releases/download/v20260227.203155.0-sha.15f9ec56/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/swamp 20260227.202847.0-sha.a7fb3475
What's Changed
- fix: redirect to swamp.club/cli/success after CLI auth (#527)
Summary
- Replace the localhost HTML success page with a
302 Foundredirect to{serverUrl}/cli/successafter the CLI captures the auth token - Pass
serverUrlthrough tostartCallbackServerso the redirect respectsSWAMP_CLUB_URLenv var - Remove the now-unused
SUCCESS_HTMLconstant
Fixes systeminit/swamp-club#118
Test Plan
- Updated
callback_server_test.tsto verify 302 status andLocationheader - All 2315 tests pass,
deno check,deno lint, anddeno fmtclean
🤖 Generated with Claude Code
Installation
macOS (Apple Silicon):
curl -L https://github.com/systeminit/swamp/releases/download/v20260227.202847.0-sha.a7fb3475/swamp-darwin-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/macOS (Intel):
curl -L https://github.com/systeminit/swamp/releases/download/v20260227.202847.0-sha.a7fb3475/swamp-darwin-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/Linux (x86_64):
curl -L https://github.com/systeminit/swamp/releases/download/v20260227.202847.0-sha.a7fb3475/swamp-linux-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/Linux (aarch64):
curl -L https://github.com/systeminit/swamp/releases/download/v20260227.202847.0-sha.a7fb3475/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/swamp 20260227.190729.0-sha.af5789db
What's Changed
- Design Doc: Extensions (#524)
Should have published this before I did the work - bad me!!
Installation
macOS (Apple Silicon):
curl -L https://github.com/systeminit/swamp/releases/download/v20260227.190729.0-sha.af5789db/swamp-darwin-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/macOS (Intel):
curl -L https://github.com/systeminit/swamp/releases/download/v20260227.190729.0-sha.af5789db/swamp-darwin-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/Linux (x86_64):
curl -L https://github.com/systeminit/swamp/releases/download/v20260227.190729.0-sha.af5789db/swamp-linux-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/Linux (aarch64):
curl -L https://github.com/systeminit/swamp/releases/download/v20260227.190729.0-sha.af5789db/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/swamp 20260227.184721.0-sha.c0205c99
What's Changed
- feat: add extension rm command to remove pulled extensions (#523)
Fixes: #522
- Add swamp extension rm (aliased as extension remove) to cleanly remove pulled extensions by deleting all tracked files and removing the entry from upstream_extensions.json
- Support --force to skip confirmation, --verbose for per-file deletion output, and --repo-dir for non-default repos
- Handle edge cases: already-deleted files (skipped gracefully), legacy entries without file tracking (error with re-pull hint), dependent extensions (warning before removal), empty parent directory pruning
- Fix missing-argument errors globally — swamp extension rm, swamp extension pull, swamp model delete, and all other commands with required arguments now show a clean error message instead of a raw stack trace with Cliffy internals
Why this approach
The recent addition of file tracking in upstream_extensions.json (PR #520) made clean removal possible — we know exactly which files an extension installed. The removeUpstreamExtension and readUpstreamExtensions functions are co-located with updateUpstreamExtensions in extension_pull.ts so all JSON mutations share the same lockfile and atomic-write pattern, preventing corruption from concurrent operations.
The missing-argument fix lives in error_output.ts (the global error renderer) rather than as a per-command .error() handler because this is a Cliffy framework issue that affects every command with required arguments. Fixing it at the render layer means all current and future commands benefit without needing individual error handlers.
Dependency checking scans locally-installed extensions' manifest.yaml files for references to the target extension. This is best-effort for v1 — it works without registry calls and covers the common case where dependents were pulled into the same repo.
Test Plan
- 5 unit tests for removeUpstreamExtension and readUpstreamExtensions (removes entry preserving others, handles missing extension, handles missing JSON file, reads existing entries, returns empty map when file missing)
- 5 output renderer tests (JSON output for removal success, cancellation, dependency warning, file delete, file missing)
- 1 error output test for Cliffy missing-argument rendering
- 6 integration tests (--help output, extension --help shows rm, invalid name error, non-installed extension error, requires initialized repo, end-to-end pull-then-rm verifying files deleted and JSON entry removed)
- All 2312 tests pass, deno check, deno lint, deno fmt clean
Installation
macOS (Apple Silicon):
curl -L https://github.com/systeminit/swamp/releases/download/v20260227.184721.0-sha.c0205c99/swamp-darwin-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/macOS (Intel):
curl -L https://github.com/systeminit/swamp/releases/download/v20260227.184721.0-sha.c0205c99/swamp-darwin-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/Linux (x86_64):
curl -L https://github.com/systeminit/swamp/releases/download/v20260227.184721.0-sha.c0205c99/swamp-linux-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/Linux (aarch64):
curl -L https://github.com/systeminit/swamp/releases/download/v20260227.184721.0-sha.c0205c99/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/swamp 20260227.175155.0-sha.0e6e91c3
What's Changed
- refactor: rename tags to labels in extension manifest (#521)
Summary
Renames the tags field to labels in the extension manifest, introduced
in #508. This is a follow-up rename before the field sees any adoption.
Test plan
- All existing tests updated and passing
-
deno checkpasses - Skill docs updated
🤖 Generated with Claude Code
Installation
macOS (Apple Silicon):
curl -L https://github.com/systeminit/swamp/releases/download/v20260227.175155.0-sha.0e6e91c3/swamp-darwin-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/macOS (Intel):
curl -L https://github.com/systeminit/swamp/releases/download/v20260227.175155.0-sha.0e6e91c3/swamp-darwin-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/Linux (x86_64):
curl -L https://github.com/systeminit/swamp/releases/download/v20260227.175155.0-sha.0e6e91c3/swamp-linux-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/Linux (aarch64):
curl -L https://github.com/systeminit/swamp/releases/download/v20260227.175155.0-sha.0e6e91c3/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/swamp 20260227.173856.0-sha.27f1a954
What's Changed
- feat: track extracted files in upstream_extensions.json (#520)
Summary
- Persist the list of extracted files in
upstream_extensions.jsonwhen an extension is pulled - Add optional
files: string[]field toUpstreamExtensionEntry(backward compatible — existing JSON files without it parse fine) - Add 3 unit tests and 1 integration test
Why this is needed
When an extension is pulled, the list of extracted files is computed and displayed to the user but never persisted. This means there's no way to know which files belong to which extension after the fact.
This is a prerequisite for extension rm — without knowing which files an extension installed, a remove command would have to guess or require the user to manually specify files. By recording the file list at pull time, a future extension rm can cleanly remove exactly the files that were installed.
The extractedFiles array was already computed during pull; this change simply saves it alongside the existing version and pulledAt fields.
Example output
After pulling an extension, upstream_extensions.json now looks like:
{
"@keeb/ssh": {
"version": "2026.02.26.1",
"pulledAt": "2026-02-27T17:32:41.000Z",
"files": [
"extensions/models/ssh/ssh.yaml",
"extensions/models/ssh/ssh.ts"
]
}
}Test plan
-
deno check— type checking passes -
deno lint— linting passes -
deno fmt— formatting passes -
deno run test— all 2294 tests pass - Unit tests verify files are persisted, existing entries preserved, and empty arrays handled
- Integration test pulls
@keeb/sshfrom registry and assertsfilesarray is present and non-empty
🤖 Generated with Claude Code
Installation
macOS (Apple Silicon):
curl -L https://github.com/systeminit/swamp/releases/download/v20260227.173856.0-sha.27f1a954/swamp-darwin-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/macOS (Intel):
curl -L https://github.com/systeminit/swamp/releases/download/v20260227.173856.0-sha.27f1a954/swamp-darwin-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/Linux (x86_64):
curl -L https://github.com/systeminit/swamp/releases/download/v20260227.173856.0-sha.27f1a954/swamp-linux-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/Linux (aarch64):
curl -L https://github.com/systeminit/swamp/releases/download/v20260227.173856.0-sha.27f1a954/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/swamp 20260227.173715.0-sha.547868b4
What's Changed
- fix: coerceInputTypes handles flat InputsSchema without properties wrapper (#519)
Summary
coerceInputTypes()only checkedschema.properties, skipping type coercion entirely for flat schemas (properties directly on the schema object without apropertieswrapper). This caused--input key=valuewith numeric/boolean types to fail validation since strings were never coerced.- Applied the same
schema.properties ?? schemafallback pattern already used byInputValidationServiceand otherInputsSchemaconsumers. - Added test covering flat schema coercion for number and boolean types.
Test Plan
- Added
coerceInputTypes: flat schema without properties wrappertest - All 2294 existing tests pass
deno check,deno lint,deno fmtall clean
🤖 Generated with Claude Code
Installation
macOS (Apple Silicon):
curl -L https://github.com/systeminit/swamp/releases/download/v20260227.173715.0-sha.547868b4/swamp-darwin-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/macOS (Intel):
curl -L https://github.com/systeminit/swamp/releases/download/v20260227.173715.0-sha.547868b4/swamp-darwin-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/Linux (x86_64):
curl -L https://github.com/systeminit/swamp/releases/download/v20260227.173715.0-sha.547868b4/swamp-linux-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/Linux (aarch64):
curl -L https://github.com/systeminit/swamp/releases/download/v20260227.173715.0-sha.547868b4/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/swamp 20260227.171331.0-sha.4405553a
What's Changed
- feat: add optional platforms and tags fields to extension manifest (#508)
Summary
Platforms
Extensions can contain platform-specific code that won't work across
OS/architecture boundaries:
Deno.Command()calls to OS-specific binaries (e.g.xdg-openon Linux)- FFI via
Deno.dlopen()with platform-specific library paths (e.g.
/usr/lib/libfoo.dylibvs/usr/lib/libfoo.so) - npm packages that conditionally load native bindings
The bundled output is pure JavaScript, so the system can't automatically
detect these cases — but extension authors know when their code has
platform dependencies. Today there's no way to communicate that to
consumers pulling an extension.
This adds an optional platforms string array to the manifest so authors
can declare which platforms their extension supports. The field is a
non-enforced hint — surfaced during extension push (in the resolved
summary) and during extension pull (as a warning-level message after
the archive is downloaded and the manifest is parsed).
This is the right solution because:
- The system cannot auto-detect platform-specific code inside bundled JS
- Enforcement would be wrong — we can't verify the author's claims
- A manifest hint lets authors document known limitations so consumers can
make informed decisions before adopting an extension
Tags
Adds an optional tags string array for categorization and discoverability
(e.g. aws, kubernetes, security). Displayed during push and pull.
Both fields
Both fields are fully optional — when omitted they default to [] and are
excluded entirely from the serialized archive manifest (no empty arrays
cluttering the YAML).
manifestVersion: 1
name: "@myorg/my-extension"
version: "2026.02.27.1"
platforms:
- darwin-aarch64
- linux-x86_64
tags:
- aws
- security
models:
- my_model.tsTest plan
-
parseExtensionManifestparses manifests withplatformsfield -
parseExtensionManifestparses manifests withtagsfield -
parseExtensionManifestdefaults both to[]when omitted - Push output renders platforms and tags when present
- Pull output renders platform hint when present
- Empty fields are omitted from serialized archive manifest
-
deno checkpasses -
deno lintpasses -
deno fmtpasses - All tests pass
🤖 Generated with Claude Code
Installation
macOS (Apple Silicon):
curl -L https://github.com/systeminit/swamp/releases/download/v20260227.171331.0-sha.4405553a/swamp-darwin-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/macOS (Intel):
curl -L https://github.com/systeminit/swamp/releases/download/v20260227.171331.0-sha.4405553a/swamp-darwin-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/Linux (x86_64):
curl -L https://github.com/systeminit/swamp/releases/download/v20260227.171331.0-sha.4405553a/swamp-linux-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/Linux (aarch64):
curl -L https://github.com/systeminit/swamp/releases/download/v20260227.171331.0-sha.4405553a/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/swamp 20260227.170532.0-sha.3923790a
What's Changed
- fix: skip macOS resource fork files during extension pull (#517)
- Skip macOS AppleDouble resource fork files (._*) during extension pull extraction, file listing, and file copying
- Set COPYFILE_DISABLE=1 on the tar extraction command to prevent macOS tar from creating resource fork files during extraction
- Add debug-level logging of extracted archive contents for diagnosing pull issues
Context
A user on Arch Linux reported that swamp extension pull failed with:
"/tmp/swamp_pull_.../extension/models/._zfs_health.ts":
"Hidden files are not allowed in extensions."
The extension archive itself is clean — tar -tzf confirms it only contains zfs_health.ts, not .zfs_health.ts. The . file was
being created on the user's filesystem during or after extraction, not bundled in the archive.
Why this is the right fix
PR #516 added COPYFILE_DISABLE=1 to the push side, which prevents macOS tar from including ._ files when creating archives.
That fix is correct but insufficient — the ._ files can also appear on the pull side:
- macOS tar extraction — On macOS, tar can create ._ resource fork files when extracting, even if the archive doesn't contain
them. Setting COPYFILE_DISABLE=1 on the extraction command prevents this. - Filesystem-level creation — The reporting user was on Arch Linux with ZFS. Certain filesystem configurations or tools can
create ._ AppleDouble files independently of tar. The archive was verified clean, yet the ._ file appeared in the temp
extraction directory. - Older archives — Archives pushed before PR #516 may still contain ._ files bundled inside them.
By filtering .* files in listFiles() and copyDir(), we handle all three cases regardless of origin. These files are
universally recognized as macOS metadata artifacts — they are never legitimate model files (they'd fail the existing hidden
file safety check anyway, and no model should start with .).
The safety analyzer's hidden file check remains unchanged — it still catches genuinely suspicious hidden files like .env or
.secret. The ._* files simply never reach it because they're filtered out during file enumeration.
Test plan
- Verify deno check passes
- Verify deno run test passes
- Verify deno run test integration/extension_pull_test.ts passes
- Test swamp extension pull @bixu/zfs completes without resource fork errors
- Test swamp extension pull @bixu/zfs --log-level debug shows "Archive contains:" lines
Installation
macOS (Apple Silicon):
curl -L https://github.com/systeminit/swamp/releases/download/v20260227.170532.0-sha.3923790a/swamp-darwin-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/macOS (Intel):
curl -L https://github.com/systeminit/swamp/releases/download/v20260227.170532.0-sha.3923790a/swamp-darwin-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/Linux (x86_64):
curl -L https://github.com/systeminit/swamp/releases/download/v20260227.170532.0-sha.3923790a/swamp-linux-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/Linux (aarch64):
curl -L https://github.com/systeminit/swamp/releases/download/v20260227.170532.0-sha.3923790a/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/