Skip to content

Releases: systeminit/swamp

swamp 20260303.195617.0-sha.44d28105

03 Mar 19:57
Immutable release. Only release title and notes can be modified.
44d2810

Choose a tag to compare

What's Changed

  • feat: add extension fmt command with vault support and quality gate on push (#591)

Summary

Adds swamp extension fmt — a new command that formats and lints extension TypeScript files before publishing. Push now gates on quality checks, blocking uploads when formatting or lint issues are detected. This addresses the problem described in #586 where published extensions contained formatting inconsistencies and lint errors, forcing every consumer to fix them manually before their CI would pass.

Closes #586

What changed

New files (7)

File Purpose
src/cli/commands/extension_fmt.ts New swamp extension fmt command with auto-fix and --check modes
src/cli/resolve_extension_files.ts Shared manifest file resolver (models, vaults, workflows, additional files)
src/cli/resolve_extension_files_test.ts 8 unit tests for the resolver (including 3 vault-specific tests)
src/domain/extensions/extension_quality_checker.ts Domain service: runs deno fmt --check + deno lint, returns structured results
src/domain/extensions/extension_quality_checker_test.ts Unit tests for the quality checker
src/presentation/output/extension_fmt_output.ts Output rendering for fmt (log + json modes)
integration/extension_fmt_test.ts 6 integration tests (model formatting, vault formatting, check mode, help)

Modified files (8)

File Changes
src/cli/commands/extension.ts Registers the new fmt subcommand
src/cli/commands/extension_push.ts Refactored to use shared resolveExtensionFiles; adds quality gate before bundling
src/presentation/output/extension_push_output.ts Adds renderExtensionPushQualityErrors for quality gate failures
integration/extension_push_test.ts 2 new tests (push blocked by formatting issues, push blocked by lint issues); fixes existing test model files to pass quality checks
integration/ddd_layer_rules_test.ts Bumps presentation-infra violation ratchet (35 → 36) for new output file
.claude/skills/swamp-extension-model/SKILL.md Adds fmt entries to Quick Reference table
.claude/skills/swamp-extension-model/references/publishing.md New "Extension Formatting" section with commands, process, and push relationship
.claude/skills/swamp-extension-model/references/troubleshooting.md New "Extension has formatting or lint issues" troubleshooting entry

Design decisions

Shared file resolver (resolve_extension_files.ts)

Both extension fmt and extension push need to resolve manifest files — models, vaults, workflows, additional files, and their local imports. Previously, push had ~130 lines of inline resolution logic. PR #578 added another ~25 lines for vault resolution inline in push.

Rather than duplicating all of this in fmt, we extracted a shared resolveExtensionFiles() function. Both commands now call it, eliminating duplication and ensuring vault files (added in #578) are automatically included in formatting and quality checks.

Vault-aware from day one

The resolver handles manifest.vaults entries alongside models — resolving paths relative to the vaults directory (resolveVaultsDir), auto-discovering local imports via resolveLocalImports, and including them in the file list. This means extension fmt formats vault TypeScript files just like model files, and push's quality gate checks them too.

Quality checker as a domain service

extension_quality_checker.ts is a pure function that takes a file list and deno path, runs both checks, and returns structured { passed, issues } results. This keeps the domain logic testable and reusable — fmt uses it for re-checking after auto-fix, push uses it as a gate.

--no-config for consistency

Both deno fmt and deno lint are invoked with --no-config to ensure consistent behavior regardless of the extension author's local deno configuration. Extensions should meet a universal baseline, not vary by environment.

Push gates rather than warns

Push blocks on quality check failure rather than showing a skippable warning. This matches the philosophy from #586 — enforcing quality at the push boundary ensures every published extension is clean, which is especially valuable for LLM-authored extensions that may not run local checks.

User impact

New command: swamp extension fmt

# Auto-fix formatting and lint issues in extension files
swamp extension fmt manifest.yaml

# Check-only mode (CI-friendly, exits non-zero on issues)
swamp extension fmt manifest.yaml --check

# With custom repo directory
swamp extension fmt manifest.yaml --repo-dir /path/to/repo

The command:

  1. Parses the manifest and resolves all TypeScript files (models, vaults, local imports)
  2. Runs deno fmt to fix formatting
  3. Runs deno lint --fix to auto-fix lint issues
  4. Re-checks for any remaining unfixable issues and reports them

Push now enforces quality

swamp extension push automatically runs the equivalent of --check before uploading. If issues are found:

ERR Quality checks failed (push blocked):
ERR   Formatting issues:
ERR     ...
ERR Run 'swamp extension fmt <manifest-path>' to fix these issues.

error: Extension has formatting or lint issues. Run 'swamp extension fmt <manifest-path>' to fix.

This means extensions that were previously publishable with lint errors or bad formatting will now be blocked until the author runs swamp extension fmt to fix them.

Vault extensions are covered

Extensions that include vault implementations (manifest.vaults) get the same formatting and lint treatment as model files. No extra flags needed — the command resolves vault files from the manifest automatically.

Test plan

  • 8 unit tests for resolve_extension_files (model resolution, vault resolution, missing file errors, empty arrays)
  • Unit tests for extension_quality_checker (pass/fail scenarios)
  • 6 integration tests for extension fmt (auto-fix formatting, auto-fix lint, check mode, vault formatting, vault check mode, help output)
  • 2 integration tests for push quality gate (blocked by formatting, blocked by lint)
  • All 2650 existing tests pass
  • deno check passes
  • deno lint passes
  • deno fmt --check passes
  • deno run compile produces working binary

🤖 Generated with Claude Code

Co-authored-by: Blake Irvin blakeirvin@me.com


Installation

macOS (Apple Silicon):

curl -L https://github.com/systeminit/swamp/releases/download/v20260303.195617.0-sha.44d28105/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/v20260303.195617.0-sha.44d28105/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/v20260303.195617.0-sha.44d28105/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/v20260303.195617.0-sha.44d28105/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/

swamp 20260303.194651.0-sha.57e888d5

03 Mar 19:47
Immutable release. Only release title and notes can be modified.
57e888d

Choose a tag to compare

What's Changed

  • fix: validate user-defined vault providers and YAML configs at load time (#590)

Summary

Addresses two findings from the adversarial code review on the user-defined vault implementations PR (#578):

  • Runtime VaultProvider validation: createProvider() return values are now checked for required methods (get, put, list, getName) before being registered. A broken user vault extension now fails immediately with a clear error listing the missing methods, instead of silently registering and crashing at runtime on first use.

  • YAML vault config schema validation: Vault config files loaded from .swamp/vault/ are now validated against a Zod schema (VaultConfigDataSchema) instead of being cast with as VaultConfigData. Malformed or corrupted YAML files (missing id, name, type, or createdAt) produce descriptive errors at load time. The config field defaults to {} if omitted.

User Impact

Improved error messages for vault extension authors:
Previously, a user-defined vault with a broken createProvider (e.g., returning null or an object missing required methods) would silently register and only fail when a secret operation was attempted. Now it fails at registration time with a message like:

createProvider for vault type '@myorg/broken' (vault 'my-vault') returned an invalid provider:
missing methods: put, list. A VaultProvider must implement get, put, list, and getName.

Improved resilience for corrupted vault configs:
Previously, a corrupted .swamp/vault/ YAML file (e.g., missing the id field) would cause an opaque runtime error deep in the call stack. Now it produces:

Invalid vault config in .swamp/vault/mock/bad.yaml: ...

No breaking changes — all existing vault configs and user-defined vault implementations continue to work identically. The config field now defaults to {} if omitted from YAML, which is more permissive than the previous behavior.

Changes

File Change
src/domain/vaults/vault_service.ts Added assertVaultProvider() validation after createProvider call
src/domain/vaults/vault_config.ts Added VaultConfigDataSchema Zod schema for YAML validation
src/infrastructure/persistence/yaml_vault_config_repository.ts Replaced 4 raw as VaultConfigData casts with parseVaultConfig() schema validation
src/domain/vaults/vault_service_test.ts 3 new tests: empty object, null, partial provider
src/infrastructure/persistence/yaml_vault_config_repository_test.ts 4 new tests: missing id, missing name, wrong structure, config defaulting

Test plan

  • 2630 tests pass (7 new tests added)
  • deno check passes
  • deno lint passes
  • deno fmt passes
  • deno run compile produces working binary
  • CI passes

🤖 Generated with Claude Code


Installation

macOS (Apple Silicon):

curl -L https://github.com/systeminit/swamp/releases/download/v20260303.194651.0-sha.57e888d5/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/v20260303.194651.0-sha.57e888d5/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/v20260303.194651.0-sha.57e888d5/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/v20260303.194651.0-sha.57e888d5/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/

swamp 20260303.192612.0-sha.6d952d90

03 Mar 19:27
Immutable release. Only release title and notes can be modified.
6d952d9

Choose a tag to compare

What's Changed

  • feat: add user-defined vault implementations (#578)

Summary

Enables users to create custom vault providers via TypeScript extension files, following the same pattern as extension models. Users export a vault object from .ts files in extensions/vaults/ that defines the vault type, Zod config schema, and provider factory function. These are automatically discovered, bundled, and registered at CLI startup.

Closes #101

Design Decisions

Why a VaultTypeRegistry instead of a static array

The previous VAULT_TYPES array in vault_types.ts was a static, hardcoded list. A registry pattern (VaultTypeRegistry) was chosen because:

  • Dynamic registration: User-defined vaults register at runtime during CLI startup, alongside built-in types
  • Singleton coordination: Both vault_types.ts (built-in registration) and UserVaultLoader (user vault registration) write to the same registry without coupling to each other
  • Case-insensitive lookup: Registry normalizes type identifiers to lowercase, matching existing getVaultType() behavior
  • Duplicate prevention: Registry rejects duplicate type registrations, preventing user types from shadowing built-in types

Why mirror the UserModelLoader pattern

UserVaultLoader follows the exact same architecture as UserModelLoader:

  • Bundle with cache: Uses bundleExtension() (esbuild) to compile TypeScript to JavaScript, cached in .swamp/vault-bundles/ with mtime-based invalidation. This is required because compiled swamp binaries cannot directly import TypeScript
  • File URL / data URL fallback: Import via file:// URL first, falling back to data: URL for environments where file imports are restricted
  • Reserved namespaces: @swamp/ and @si/ are blocked for user vault types to prevent namespace collisions with future built-in types
  • Graceful failure: Individual vault load failures log warnings but don't block CLI startup

This was chosen over a novel approach because the model loader pattern is battle-tested and maintains consistency across the extension system.

Why @namespace/name scoped types

User vault types use scoped identifiers (e.g., @hashicorp/vault) to:

  • Prevent collisions: Multiple extension authors can create vault types without name conflicts
  • Match npm conventions: Familiar scoping pattern for TypeScript/JavaScript developers
  • Distinguish from built-in: Built-in types use simple names (aws-sm, 1password), user types use scoped names

Why no CalVer for vault bundles

Unlike model definitions, vault providers have no persisted schema that changes over time. The VaultProvider interface (get, put, list, getName) is stable, so there's no need for versioned schemas or migration logic.

Why walk() in YamlVaultConfigRepository

The existing repository used two-level readDir scanning (type directory → YAML files), which broke for scoped types like @hashicorp/vault where the directory structure is three levels deep (.swamp/vault/@hashicorp/vault/<id>.yaml). Switched to recursive walk() from @std/fs to handle arbitrary nesting depth. The definition repository solved this same problem with manual recursion — walk() is simpler and achieves the same result.

Why --config JSON flag for user-defined types

Built-in vault types use specific CLI flags (--region, --vault-url, etc.) for configuration. User-defined types have arbitrary config schemas, so a generic --config <json> flag was added. The JSON is validated against the vault type's Zod configSchema at creation time, giving users immediate feedback on invalid configuration.

Extension push/pull integration

Vault files are treated as a parallel track alongside models and workflows:

  • Push: Vault source files go into vaults/ in the archive, bundles into vault-bundles/
  • Pull: Source extracted to the resolved vaults directory, bundles to .swamp/vault-bundles/
  • Safety analysis: Vault .ts files go through the same security analysis as model files
  • Conflict detection: Checks for existing vault files before overwriting

What Changed

New files (6)

File Purpose
src/domain/vaults/vault_type_registry.ts Map-backed singleton registry for vault types
src/domain/vaults/vault_type_registry_test.ts 7 tests: registration, lookup, case-insensitivity, duplicates
src/domain/vaults/user_vault_loader.ts Discovers, bundles, imports, validates, and registers user vault extensions
src/domain/vaults/user_vault_loader_test.ts 6 tests: loading, reserved namespaces, skip non-vault files
src/cli/resolve_vaults_dir.ts Resolves vault extension directory (env var → config → default)
src/cli/resolve_vaults_dir_test.ts 3 tests: default, marker config, env var priority

Modified files (22)

File Changes
src/domain/vaults/vault_types.ts Registers built-ins with registry; delegates getVaultTypes()/getVaultType() to registry
src/domain/vaults/vault_service.ts Checks registry for user types with createProvider; validates config against configSchema
src/cli/mod.ts Adds loadUserVaults() called after loadUserModels() in runCli()
src/cli/commands/vault_create.ts Adds --config flag; uses registry instead of getVaultType(); validates user type configs
src/domain/extensions/extension_manifest.ts Adds vaults field to manifest schema
src/cli/commands/extension_push.ts Collects, bundles, and archives vault files
src/cli/commands/extension_pull.ts Extracts vault source and bundles; safety-analyzes vault files
src/cli/commands/extension_update.ts Passes vaultsDir through install context
src/cli/commands/extension_search.ts Passes vaultsDir through pull context
src/infrastructure/persistence/paths.ts Adds vaultBundles to SWAMP_SUBDIRS
src/infrastructure/persistence/repo_marker_repository.ts Adds vaultsDir to RepoMarkerData
src/infrastructure/persistence/yaml_vault_config_repository.ts Uses recursive walk() for namespaced vault type discovery
src/domain/repo/repo_service.ts Creates vault-bundles dir; adds to .gitignore
src/presentation/output/extension_push_output.ts Adds vault files/count to push output

User Impact

Creating a custom vault provider

Users can now create vault implementations by adding a TypeScript file to extensions/vaults/:

// extensions/vaults/hashicorp_vault.ts
import { z } from "npm:zod";

class HashiCorpVaultProvider {
  // ... implements get(), put(), list(), getName()
}

export const vault = {
  type: "@hashicorp/vault",
  name: "HashiCorp Vault",
  description: "Store secrets using HashiCorp Vault KV v2",
  configSchema: z.object({
    address: z.string().url(),
    token: z.string().optional(),
    mount: z.string().default("secret"),
  }),
  createProvider: (name, config) => {
    const parsed = configSchema.parse(config);
    return new HashiCorpVaultProvider(name, parsed);
  },
};

Using custom vault types

# Custom vault types appear in type search
swamp vault type search
# → aws-sm, azure-kv, 1password, local_encryption, @hashicorp/vault

# Create vault instance with JSON config
swamp vault create @hashicorp/vault my-vault \
  --config '{"address":"https://vault.example.com:8200","token":"s.xxx"}'

# Use like any built-in vault
swamp vault put my-vault api-key "sk-12345"
swamp vault list-keys my-vault
# ${{ vault.my-vault.api-key }} in workflows

Sharing vault implementations

Vault extensions integrate with the existing extension push/pull system:

# extension.yaml
name: hashicorp-vault
version: "1.0.0"
vaults:
  - hashicorp_vault.ts
swamp extension push
swamp extension pull hashicorp-vault  # on another repo

Backward compatibility

  • All existing built-in vault types work unchanged
  • Existing vault configurations are unaffected
  • The --config flag is only required for user-defined types
  • .swamp.yaml supports optional vaultsDir override (defaults to extensions/vaults)

Test plan

  • All 2608 tests pass (4 new test files, updated assertions in 4 existing test files)
  • deno check passes
  • deno lint passes
  • deno fmt passes
  • deno run compile produces working binary
  • End-to-end: compiled binary loads user vault from extensions/vaults/, shows in vault type search, creates vault instance with --config, persists correctly, retrievable via vault get and vault search
  • CI passes

🤖 Generated with Claude Code


Installation

macOS (Apple Silicon):

curl -L https://github.com/systeminit/swamp/releases/download/v20260303.192612.0-sha.6d952d90/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/v20260303.192612.0-sha.6d952d90/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/v20260303.192612.0-sha.6d952d90/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/v20260303.192612.0-sha.6d952d90/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/

swamp 20260303.164829.0-sha.8c8e8eac

03 Mar 16:49
Immutable release. Only release title and notes can be modified.
8c8e8ea

Choose a tag to compare

What's Changed

  • feat: add adversarial code review to CI pipeline (#588)

Summary

Adds a second Claude-powered code review step to the CI pipeline that acts as a devil's advocate — systematically attempting to break the code rather than just assessing its quality.

The existing "Claude Code Review" is a constructive reviewer that checks style, DDD, tests, and general quality. The new "Adversarial Code Review" is a skeptic whose job is to find problems that the author and a standard reviewer would miss.

How it differs from the existing review

Aspect Existing Review Adversarial Review
Mindset Constructive colleague Skeptic trying to break the code
Scope Broad (style, DDD, tests, quality) Deep (logic, security, failure modes)
Style concerns Yes Explicitly excluded
DDD/architecture Yes No — focuses on correctness
Blocking bar Bugs, type errors, missing tests Only CRITICAL/HIGH severity (security, data loss, logic errors)
Pass action --approve (counts toward merge) --comment (doesn't count as approval)
Output format Free-form with categories Structured severity levels + verdict

The adversarial reviewer systematically attacks the code across 7 dimensions:

  1. Logic & Correctness — off-by-one, wrong operators, edge case inputs
  2. Error Handling & Failure Modes — swallowed errors, inconsistent state after failures
  3. Security — command injection, path traversal, data exposure, TOCTOU
  4. Concurrency & State — race conditions, async ordering, shared state corruption
  5. Data Integrity — silent truncation, unexpected mutations, cache staleness
  6. Resource Management — leaked handles, unbounded loops, memory leaks
  7. API Contract Violations — breaking changes, signature mismatches

Key design choices

  1. Runs in parallel with the existing review (both needs: [test, deps-audit]) — no added wall-clock time to the pipeline
  2. Uses --comment when passing instead of --approve — only the standard review grants approval, the adversarial reviewer never rubber-stamps
  3. Only blocks on CRITICAL/HIGH — security vulnerabilities, data corruption, logic errors in production paths. Medium/Low findings are advisory warnings
  4. Demands concrete examples — the prompt requires "name the exact input that breaks it" rather than vague warnings like "this could have edge cases"
  5. Explicit "don't invent problems" instruction — if the code is solid, the reviewer must say so rather than manufacturing findings to justify its existence
  6. Auto-merge now requires both reviews to pass before merging

Test plan

  • Open a PR with a deliberate bug (e.g., off-by-one error) and verify the adversarial review catches it
  • Open a clean PR and verify the adversarial review passes with --comment (not --approve)
  • Verify both reviews run in parallel (not sequentially)
  • Verify auto-merge waits for both reviews to complete

🤖 Generated with Claude Code


Installation

macOS (Apple Silicon):

curl -L https://github.com/systeminit/swamp/releases/download/v20260303.164829.0-sha.8c8e8eac/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/v20260303.164829.0-sha.8c8e8eac/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/v20260303.164829.0-sha.8c8e8eac/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/v20260303.164829.0-sha.8c8e8eac/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/

swamp 20260303.155423.0-sha.99728850

03 Mar 15:55
Immutable release. Only release title and notes can be modified.
9972885

Choose a tag to compare

What's Changed

  • fix: false update notification when current version is newer than cache (#587)

Summary

  • Fixed false "update available" notification that appeared on every command even when the user was already on the latest (or newer) version

Root cause

The UpdateNotificationService.getNotification() method compared the cached latest version with the current running version using !== (not-equal). This meant any difference between the two triggered the notification — including when the user's version was newer than the cached value.

Concrete scenario: The background update check cached latestVersion: "20260302.220424.0-sha.cc1fb239". The user then updated to "20260303.151256.0-sha.471cac05" (a newer build). On every subsequent command, the notification fired because "20260302..." !== "20260303...", even though the user was ahead.

Fix

Changed the comparison from !== (different) to > (lexicographically greater). Since CalVer versions (YYYYMMDD.HHMMSS.patch-sha.hash) are zero-padded and naturally sortable, the notification now only triggers when the cached version is genuinely newer than the running binary.

Added a test covering the "current version newer than cache" scenario.

Test plan

  • deno check — passes
  • deno lint — passes
  • deno fmt — passes
  • deno run test — all 79 update-related tests pass (1 new)
  • Verified: cached version 20260302... < current version 20260303... → no notification

🤖 Generated with Claude Code


Installation

macOS (Apple Silicon):

curl -L https://github.com/systeminit/swamp/releases/download/v20260303.155423.0-sha.99728850/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/v20260303.155423.0-sha.99728850/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/v20260303.155423.0-sha.99728850/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/v20260303.155423.0-sha.99728850/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/

swamp 20260303.151256.0-sha.471cac05

03 Mar 15:13
Immutable release. Only release title and notes can be modified.
471cac0

Choose a tag to compare

What's Changed

  • fix: correct Kiro hook format and add USER_PROMPT env var support (#584)

Summary

Fixes #581 — Kiro IDE does not recognize the swamp audit hook due to three issues, all addressed in this PR.

1. Hook file format

  • Wrong extension: Kiro expects .kiro.hook, not .json — renamed swamp-audit.jsonswamp-audit.kiro.hook
  • Missing toolTypes: Kiro requires toolTypes: ["*"] in the when property — added
  • No timeout: Added timeout: 5 to the then property to prevent hangs as a safety net

2. Input mechanism — USER_PROMPT env var

Kiro IDE passes postToolUse data via the USER_PROMPT environment variable, not stdin. The audit record command was blocking on readStdin() which caused timeouts. The fix:

  • For --tool kiro: check USER_PROMPT env var first, fall back to stdin (preserving kiro-cli compatibility)
  • The Kiro IDE format uses camelCase keys (toolName, toolArgs, toolResult, toolSuccess) which differs from the kiro-cli snake_case format

3. Dual-format normalizer

Updated normalizeKiro() to handle both:

  • kiro-cli (stdin, snake_case): tool_name, tool_input, tool_response
  • Kiro IDE (USER_PROMPT, camelCase): toolName, toolArgs, toolResult, toolSuccess

4. Upgrade cleanup

swamp repo upgrade --tool kiro now removes the old swamp-audit.json file so stale hooks don't linger.

Open question for @danmcclain

The USER_PROMPT env var format is based on Kiro's own description of its postToolUse data. Two things we'd appreciate verification on:

  1. Is USER_PROMPT valid JSON? We parse it with JSON.parse() — if it's a different format, the hook will silently no-op (safe but won't record).
  2. Does toolArgs contain { command: "..." } for execute_bash tools? You noted it "appears to be empty {}" in your examples — if that's always the case, we won't be able to extract the command. Could you verify with your test hook on an execute_bash invocation?

Files changed

File Change
src/domain/repo/repo_service.ts .kiro.hook extension, toolTypes, timeout, old file cleanup
src/cli/commands/audit.ts readHookInput() with USER_PROMPT env var fallback
src/domain/audit/hook_input.ts Dual-format normalizeKiro()
src/domain/audit/hook_input_test.ts 4 new tests for Kiro IDE camelCase format
src/domain/repo/repo_service_test.ts Updated hook format tests + old file cleanup test

Test plan

  • deno check passes
  • deno lint passes
  • deno fmt passes
  • deno run test — 2605 tests pass (4 new)
  • deno run compile — binary builds
  • Manual test: swamp repo init --tool kiro creates swamp-audit.kiro.hook with correct format
  • @danmcclain to verify with Kiro IDE that the hook appears and USER_PROMPT format works

🤖 Generated with Claude Code


Installation

macOS (Apple Silicon):

curl -L https://github.com/systeminit/swamp/releases/download/v20260303.151256.0-sha.471cac05/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/v20260303.151256.0-sha.471cac05/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/v20260303.151256.0-sha.471cac05/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/v20260303.151256.0-sha.471cac05/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/

swamp 20260303.145336.0-sha.fcbb0957

03 Mar 14:54
Immutable release. Only release title and notes can be modified.
fcbb095

Choose a tag to compare

What's Changed

  • fix: improve update/upgrade output and init error message (#585)

Summary

  • Enrich swamp update log output with "updated globally" and "run swamp repo upgrade" hints after a successful update
  • Enrich swamp repo upgrade log output to show what changed: skills, instructions, settings, and .gitignore status
  • Improve swamp repo init error when already initialized to show current tool and suggest swamp repo upgrade -t <tool>

Fixes #583

Output examples

swamp repo init (already initialized):

14:32:20 FTL error Error: 'Repository already initialized at /private/tmp/swamp-output-test (tool: "claude"). To switch tools, run: swamp repo upgrade -t <tool>. To reinitialize from scratch, run: swamp repo --force -t <tool>'

swamp repo upgrade -t claude (same tool, nothing changed):

14:32:21 INF repo Upgraded swamp repository: "20260206.200442.0" → "20260206.200442.0" (tool: "claude")
14:32:21 INF repo   Skills updated: swamp-data, swamp-model, swamp-repo, swamp-workflow, swamp-extension-model, swamp-vault, swamp-issue, swamp-troubleshooting
14:32:21 INF repo   Instructions: unchanged
14:32:21 INF repo   Settings: unchanged
14:32:21 INF repo   .gitignore: unchanged

swamp repo upgrade -t cursor (switching tools):

14:32:22 INF repo Upgraded swamp repository: "20260206.200442.0" → "20260206.200442.0" (tool: "cursor")
14:32:22 INF repo   Skills updated: swamp-data, swamp-model, swamp-repo, swamp-workflow, swamp-extension-model, swamp-vault, swamp-issue, swamp-troubleshooting
14:32:22 INF repo   Instructions: updated
14:32:22 INF repo   Settings: updated
14:32:22 INF repo   .gitignore: updated

swamp update (updated case — from test output):

14:33:14 INF update swamp updated successfully!
14:33:14 INF update "20260207..." → "20260208..."
14:33:14 INF update SHA-256 integrity check passed
14:33:14 INF update The swamp binary has been updated globally.
14:33:14 INF update Run `swamp repo upgrade` in your repositories to update skills and settings.

Test plan

  • deno check — type checking passes
  • deno lint — no lint errors
  • deno fmt — formatting correct
  • deno run test — all 260 tests pass
  • deno run compile — binary recompiles
  • Tested compiled binary output in /tmp directory for all three scenarios

🤖 Generated with Claude Code

Co-authored-by: Walter Heck walterheck@helixiora.com


Installation

macOS (Apple Silicon):

curl -L https://github.com/systeminit/swamp/releases/download/v20260303.145336.0-sha.fcbb0957/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/v20260303.145336.0-sha.fcbb0957/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/v20260303.145336.0-sha.fcbb0957/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/v20260303.145336.0-sha.fcbb0957/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/

swamp 20260303.045025.0-sha.aab4626d

03 Mar 04:51
Immutable release. Only release title and notes can be modified.
aab4626

Choose a tag to compare

What's Changed

  • fix: switch API token auth from Bearer to x-api-key header (#582)

Summary

  • Switch all API key authentication from Authorization: Bearer to x-api-key header, reserving Bearer exclusively for the createApiKey call during login (session token auth)
  • Reorder login flow to create the API key first, then verify identity with whoami using the new API key
  • Update ExtensionApiClient, HttpTelemetrySender, and all related tests

Fixes #577

Test Plan

  • All 2586 existing tests pass
  • swamp_club_client_test.ts: Verifies whoami sends x-api-key header
  • extension_api_client_test.ts: Verifies yank sends x-api-key header
  • http_telemetry_sender_test.ts: Verifies telemetry sends x-api-key header
  • deno check, deno lint, deno fmt --check all pass
  • deno run compile succeeds

🤖 Generated with Claude Code


Installation

macOS (Apple Silicon):

curl -L https://github.com/systeminit/swamp/releases/download/v20260303.045025.0-sha.aab4626d/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/v20260303.045025.0-sha.aab4626d/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/v20260303.045025.0-sha.aab4626d/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/v20260303.045025.0-sha.aab4626d/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/

swamp 20260303.030136.0-sha.832572e4

03 Mar 03:02
Immutable release. Only release title and notes can be modified.
832572e

Choose a tag to compare

What's Changed

  • fix: always create .gitignore on repo init (#580)

Summary

  • swamp repo init now always creates/manages a .gitignore with the swamp managed section, instead of requiring the --include-gitignore flag
  • If no .gitignore exists, one is created; if one already exists, the managed section is appended while preserving user content
  • The --include-gitignore flag is removed from the init command but remains on repo upgrade for opt-in/opt-out control
  • The gitignoreManaged: true marker is always set on init so subsequent upgrades continue managing the section

Behavior Change

Scenario Before After
swamp repo init (no flag) No .gitignore created .gitignore created with managed section
swamp repo init with existing .gitignore No changes Managed section appended to existing file
swamp repo upgrade Unchanged Unchanged (still respects marker/flag)

Without this fix, users could accidentally commit sensitive files like .swamp/secrets/keyfile.

Test Plan

  • All 64 unit tests in repo_service_test.ts pass (updated to reflect new behavior)
  • Manual testing with compiled binary across 5 scenarios:
    1. Fresh init with no existing .gitignore — file created with managed section
    2. Init with existing user .gitignore — user content preserved, managed section appended
    3. Init with non-claude tool (cursor) — tool-specific entry (.cursor/skills/) included
    4. Upgrade after init — .gitignore maintained via marker
    5. Force re-init — .gitignore stays unchanged
  • Full test suite passes (2585 tests)
  • deno check, deno lint, deno fmt all pass

🤖 Generated with Claude Code


Installation

macOS (Apple Silicon):

curl -L https://github.com/systeminit/swamp/releases/download/v20260303.030136.0-sha.832572e4/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/v20260303.030136.0-sha.832572e4/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/v20260303.030136.0-sha.832572e4/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/v20260303.030136.0-sha.832572e4/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/

swamp 20260303.025753.0-sha.26137995

03 Mar 02:58
Immutable release. Only release title and notes can be modified.
2613799

Choose a tag to compare

What's Changed

  • feat: send content metadata during extension push (#579)

Summary

  • During extension push, extract metadata from model .ts files and workflow .yaml files already in memory
  • Send the extracted contentMetadata to the registry in the confirm-push request
  • The server can now index extension contents (methods, arguments, resources, files, workflow structure) without re-parsing the uploaded archive

User impact

No user-facing behavior changes. The metadata extraction runs silently during push — results are logged at debug level only (visible with --verbose or SWAMP_LOG=debug). If extraction fails for any reason, push continues normally without metadata. The registry gains richer indexing data to power future extension search and discovery features.

Design

Why client-side extraction? The CLI already has all source files in memory at push time. Extracting metadata here avoids redundant server-side archive unpacking and parsing, reducing registry latency and complexity.

Why regex-based for models? Model .ts files follow well-defined patterns (ModelType.create(...), methods: { name: { description: ... } }, z.object({...})). Regex extraction matches the approach used by the swamp-club server (PR #145) and avoids needing to evaluate TypeScript. Balanced brace/paren matching handles nested structures like z.record(z.string(), z.string()).

Why YAML parsing for workflows? Workflow files are YAML — a proper parser (@std/yaml, already a dependency) gives exact results with no regex fragility.

Non-fatal by design. Every extraction is wrapped in try/catch at the per-file level. A single unparseable model or workflow is silently skipped, returning partial results. The top-level call is also wrapped, so even a catastrophic extractor bug cannot break extension push.

Changes

File Change
src/domain/extensions/extension_content.ts New — type definitions matching swamp-club schema
src/domain/extensions/extension_content_extractor.ts New — extraction logic (regex for models, YAML for workflows)
src/domain/extensions/extension_content_extractor_test.ts New — 15 tests
src/infrastructure/http/extension_api_client.ts Add ConfirmPushMetadata with optional contentMetadata
src/cli/commands/extension_push.ts Wire extraction after bundling, pass to confirm call

Test plan

  • deno check — type checking passes
  • deno lint — linting passes
  • deno fmt — formatting passes
  • deno run test — 2601 tests pass (15 new + 2586 existing)
  • deno run compile — binary compiles
  • Validated against real shell_model.ts — correctly extracts type, version, methods with arguments, resources, and files
  • Manual: swamp extension push --dry-run on an extension with models and workflows

🤖 Generated with Claude Code


Installation

macOS (Apple Silicon):

curl -L https://github.com/systeminit/swamp/releases/download/v20260303.025753.0-sha.26137995/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/v20260303.025753.0-sha.26137995/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/v20260303.025753.0-sha.26137995/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/v20260303.025753.0-sha.26137995/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/