Skip to content

Releases: systeminit/swamp

swamp 20260302.203007.0-sha.8142ddfc

02 Mar 20:31
Immutable release. Only release title and notes can be modified.
8142ddf

Choose a tag to compare

What's Changed

  • feat: show data retrieval commands after workflow run completes (#560)

Summary

  • After a successful workflow run, the log output now shows copy-pasteable swamp data list and swamp data get commands for each unique data artifact produced
  • Skipped entirely for failed runs (no useful data) and when no data artifacts exist
  • Only affects log mode output (onWorkflowComplete in the log progress callback); JSON mode is unchanged

Fixes #559

Test plan

  • Unit test: helper commands appear for successful runs with data artifacts
  • Unit test: no helper commands for failed runs
  • Unit test: no helper commands when no data artifacts exist
  • deno check — type checking passes
  • deno lint — linting passes
  • deno fmt — formatting passes
  • deno run test — all 2452 tests pass

🤖 Generated with Claude Code


Installation

macOS (Apple Silicon):

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

swamp 20260302.191852.0-sha.061f496c

02 Mar 19:19
Immutable release. Only release title and notes can be modified.
061f496

Choose a tag to compare

What's Changed

  • fix: omit empty platforms and labels from extension search JSON output (#557)

Summary

  • Omit empty platforms and labels arrays from JSON output of extension search
  • Non-empty arrays are still included as before
  • Adds tests verifying both empty-omission and non-empty-retention behavior

Test plan

  • deno run test src/presentation/output/extension_search_output_test.tsx — all 14 tests pass
  • Manual: swamp extension search --json — verify empty platforms/labels are absent

🤖 Generated with Claude Code


Installation

macOS (Apple Silicon):

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

swamp 20260302.184040.0-sha.b0814490

02 Mar 18:41
Immutable release. Only release title and notes can be modified.
b081449

Choose a tag to compare

What's Changed

  • feat: proactive update notification after every command (#556)

Closes #493

Problem

Users currently have no way to know that a new version of swamp is available unless they manually run swamp update --check. This means users can run outdated versions for weeks or months without realizing an update exists.

Solution

Adds a zero-latency background update check that caches results and displays a one-line banner on the next invocation when an update exists. The approach is modeled after Deno's own update notification system.

How it works

  1. Command runs normally — no latency impact whatsoever
  2. After the command completes, read the cached notification and display if applicable
  3. Fire off a background HTTP HEAD check (rate-limited to once per 24h) to the stable artifact URL
  4. Cache the result in ~/.swamp/last-update-check.json for the next invocation

Two notification types

When a newer version is found in the cache:

$ swamp version
20260206.200442.0-sha.abc123

A new version of swamp is available. Run `swamp update` to upgrade.

When the installed version is >30 days old (zero-network, uses CalVer date):

$ swamp version
20260106.200442.0-sha.abc123

Your swamp version is 55 days old. Run `swamp update` to upgrade.

Guards — notification is suppressed for:

  • Dev builds — developers running from source don't need update prompts
  • SWAMP_NO_UPDATE_CHECK=1 — environment variable for CI/scripting contexts
  • --json mode — never corrupt structured output
  • update command itself — avoid redundant messaging

Architecture

Follows the existing DDD layered architecture:

Layer File Purpose
Domain version_staleness.ts Pure functions for CalVer date extraction and staleness detection
Domain update_check_cache.ts Cache data type, repository port, and isCacheStale()
Domain update_notification_service.ts Core service: getNotification() (local I/O only) + backgroundCheck() (fire-and-forget)
Infrastructure update_check_cache_file_repository.ts File-based cache at ~/.swamp/last-update-check.json with atomic writes
Presentation update_notification_output.ts Renders yellow banner to stderr
CLI mod.ts Post-command hook + isUpdateCheckDisabledByEnv()

Test plan

  • deno check — type checking passes
  • deno lint — linting passes
  • deno fmt — formatting passes
  • deno run test — all 2428 tests pass (82 new)
  • deno run compile — binary compiles
  • Manual: run any command, verify no notification on first run (no cache yet)
  • Manual: run again after cache exists with a different version — verify banner
  • Manual: SWAMP_NO_UPDATE_CHECK=1 swamp version — verify no notification
  • Manual: swamp version --json — verify no notification in JSON output

🤖 Generated with Claude Code


Installation

macOS (Apple Silicon):

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

swamp 20260302.181545.0-sha.842b020c

02 Mar 18:16
Immutable release. Only release title and notes can be modified.
842b020

Choose a tag to compare

What's Changed

  • feat: add extension search command with install from detail view (#555)

Summary

Adds a new swamp extension search subcommand and an inline install action from the search detail view.

  • New extension search command — queries the swamp extension registry with support for --namespace, --platform, --label, --sort, --page, and --per-page options. Renders an interactive fuzzy-filter UI (Ink + fzf) in log mode, or structured JSON in --json mode.
  • Install from detail view — when viewing an extension's details, pressing i pulls the extension directly into the current repo, removing the need for a separate swamp extension pull step. Pressing Enter still returns the selection without installing. Esc goes back to the list.
  • Exports pullExtension / PullContext — the core pull logic in extension_pull.ts is now exported so the search command (and future commands) can reuse it without duplicating code.

Why this approach

The extension search → install flow is the most common user journey: find an extension, then install it. Previously this required two separate commands (swamp extension search to discover, then swamp extension pull @ns/name to install). Adding i as a key command in the detail view makes this a single-step operation while keeping the existing Enter to select behavior unchanged.

The implementation follows the existing patterns in the codebase:

  • Discriminated result type (ExtensionSearchResult with action: "select" | "install") — matches the WorkflowActionSelectUI pattern already used elsewhere
  • Lazy repo resolution — the repo context is only resolved when install is actually requested, so searching works without an initialized repo
  • Reuse of pullExtension — avoids duplicating the pull logic (integrity verification, safety analysis, conflict detection, dependency resolution)

Example commands

# Search all extensions (interactive fuzzy filter)
swamp extension search

# Search with a query
swamp extension search aws

# Filter by namespace and platform
swamp extension search --namespace stack72 --platform aws

# Sort by newest, 10 per page
swamp extension search --sort new --per-page 10

# JSON output mode (for scripting)
swamp extension search aws --json

# Interactive flow:
# 1. Run: swamp extension search
# 2. Type to filter, arrow keys to navigate
# 3. Press Enter to view extension details
# 4. Press "i" to install into current repo
#    — or Enter to select, Esc to go back

Test plan

  • deno check — type checking passes
  • deno lint — no lint errors
  • deno fmt — formatting clean
  • deno run test — all 15 tests pass (12 UI + 3 command validation)
  • Manual: swamp extension search → select → press i → installs into repo
  • Manual: swamp extension search → select → press Enter → returns item (no install)
  • Manual: swamp extension search → select → press i with no repo → clear error

🤖 Generated with Claude Code


Installation

macOS (Apple Silicon):

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

swamp 20260302.135703.0-sha.7f1c73cc

02 Mar 13:58
Immutable release. Only release title and notes can be modified.
7f1c73c

Choose a tag to compare

What's Changed

  • docs: add model method describe to design/models.md (#553)

Summary

Follow-up to #552 — documents the new model method describe CLI command in the design doc.

  • Adds a ### model method describe <model_id_or_name> <method_name> section to design/models.md
  • Describes both log and JSON output modes
  • Placed before the existing model method run section

Test plan

  • Documentation-only change, no code affected

🤖 Generated with Claude Code


Installation

macOS (Apple Silicon):

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

swamp 20260302.135354.0-sha.1329659a

02 Mar 13:54
Immutable release. Only release title and notes can be modified.
1329659

Choose a tag to compare

What's Changed

  • feat: add model method describe subcommand (#552)

Summary

Closes #550

  • Adds swamp model method describe <model> <method> subcommand that surfaces method-specific documentation (description, typed arguments, data output specs) from model definitions
  • Supports both builtin and extension models, in log and JSON output modes
  • Reuses existing toMethodDescribeData() and formatSchemaAttributes() from the type_describe module — no new domain logic needed

Why this approach

Issue #550 reports that swamp model method run <model> <method> --help shows generic Cliffy help instead of method-specific argument documentation. All the metadata needed already exists in model definitions (method descriptions, Zod argument schemas with .describe()).

Rather than intercepting --help on the run command (which would mix concerns between execution and introspection), this adds a dedicated describe subcommand following the existing model type describe pattern. This is consistent with the CLI's architecture where inspection commands are separate from execution commands.

What changed

New files

File Purpose
src/cli/commands/model_method_describe.ts CLI command — resolves model, validates method, renders output
src/presentation/output/model_method_describe_output.ts Output rendering with ModelMethodDescribeData interface
src/cli/commands/model_method_describe_test.ts Command registration tests (3 tests)
src/presentation/output/model_method_describe_output_test.ts Output rendering tests for log + JSON modes (4 tests)

Modified files

File Change
src/cli/commands/model_method_run.ts Register describe subcommand on modelMethodCommand
integration/ddd_layer_rules_test.ts Bump presentation→infrastructure ratchet 31→32 (new output file follows existing writeOutput pattern)

Testing

Automated

  • 7 new unit tests (all pass)
  • Full test suite: 2383 passed, 0 failed
  • deno check / deno lint / deno fmt — clean
  • deno run compile — binary compiles

Manual (compiled binary, fresh swamp repo init)

Builtin model (command/shell) — log mode:

$ swamp model method describe my-shell-cmd execute
Model: my-shell-cmd
Type: command/shell
Version: 2026.02.09.1

Method: execute - Execute the shell command and capture stdout, stderr, and exit code

Arguments:
  run (string) *required
  workingDir (string)
  timeout (integer)
  env (object)

Data Outputs:
  result [resource] - Shell command execution result (exit code, timing, command) (infinite)
  log [file] - Shell command output (stdout and stderr) (text/plain, infinite)

Extension model (@test/echo) — JSON mode:

{
  "modelName": "my-echo",
  "modelType": "@test/echo",
  "version": "2026.03.02.1",
  "method": {
    "name": "run",
    "description": "Echo the message with a timestamp",
    "arguments": {
      "$schema": "https://json-schema.org/draft/2020-12/schema",
      "type": "object",
      "properties": {
        "prefix": {
          "description": "Optional prefix for the message",
          "type": "string"
        }
      },
      "additionalProperties": false
    },
    "dataOutputSpecs": [
      {
        "specName": "data",
        "kind": "resource",
        "description": "Echo output",
        "lifetime": "infinite",
        "garbageCollection": 10
      }
    ]
  }
}

Error cases:

$ swamp model method describe my-shell-cmd nonexistent
Error: "Unknown method 'nonexistent' for type 'command/shell'. Available methods: execute"

$ swamp model method describe nonexistent-model execute
Error: "Model not found: nonexistent-model"

Help listing:

$ swamp model method --help
Commands:
  run       <model_id_or_name> <method_name>  - Execute a method on a model
  describe  <model_id_or_name> <method_name>  - Describe a method on a model with argument details
  history                                     - Model method run history commands

Test plan

  • deno check passes
  • deno lint passes
  • deno fmt passes
  • deno run test — all tests pass
  • deno run compile — binary compiles
  • Manual: builtin model describe works (log + JSON)
  • Manual: extension model describe works (log + JSON)
  • Manual: error on invalid method name
  • Manual: error on invalid model name
  • Manual: model method --help lists describe

🤖 Generated with Claude Code


Installation

macOS (Apple Silicon):

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

swamp 20260301.183337.0-sha.a2c77652

01 Mar 18:34
Immutable release. Only release title and notes can be modified.
a2c7765

Choose a tag to compare

What's Changed

  • feat: support reading secret values from stdin in vault put (#549)

Summary

Closes #548. vault put now supports reading secret values from stdin, so secrets don't need to appear as CLI arguments (which are visible in shell history and ps output).

The problem

swamp vault put <vault> KEY=secret exposes the secret in:

  1. Shell history (~/.zsh_history, ~/.bash_history)
  2. Process listings (ps aux)
  3. Agent context — when an AI agent assists with vault setup, it may prompt the user to paste a token into conversation where it gets logged

The plan

Add stdin piping as an alternative input method using the existing readStdin() utility (already used by model edit and workflow edit), with this resolution order:

  1. If the argument contains =, parse as KEY=VALUEexisting behavior, unchanged
  2. If no =, treat argument as just KEY and read the value from stdin
  3. If no = AND no stdin data, throw a clear error explaining both formats

This preserves full backward compatibility. No new flags, no new subcommands.

The implementation

src/cli/commands/vault_put.ts

  • Extracted parseKeyValue() and new resolveKeyValue() as exported functions for testability
  • resolveKeyValue(argument, stdinContent) encapsulates the resolution order: inline KEY=VALUE first, stdin fallback second, error third
  • Stdin values have a single trailing newline stripped (.replace(/\n$/, "")) since pipes/files typically append one
  • Key detail: readStdin() is only called when parseKeyValue() returns null (no = found). This avoids consuming stdin unnecessarily, which would break promptConfirmation() for the overwrite check in the KEY=VALUE path
  • When stdin was piped and the key already exists, a clear UserError is thrown instead of attempting an interactive prompt (which can't work with a consumed stdin stream). The user must pass --force in this case

src/cli/commands/vault_put_test.ts

  • Existing parseKeyValue tests now import from source instead of duplicating the function
  • 8 new resolveKeyValue tests covering: inline precedence over stdin, stdin value used when no =, trailing newline stripping, only-one-newline stripping, no-trailing-newline preservation, error on missing value, error message format

.claude/skills/swamp-vault/SKILL.md

  • Added piped input syntax to quick reference table
  • Documented piped value examples (env var, file, 1Password CLI)
  • Added agent security note: never ask users to paste secrets into conversation

Why this is the right fix

  • Reuses existing patternsreadStdin() and the "check stdin, fallback to existing behavior" pattern are already proven in model edit and workflow edit
  • Fully backward compatibleKEY=VALUE works identically, zero breakage
  • No new flags or subcommands — just a natural extension of the existing argument
  • Covers all secure input sources — files, env vars, password managers, other CLIs
  • Addresses agent leakage — skill update prevents agents from asking for secrets in conversation
  • Handles edge cases correctly — stdin only consumed when needed, overwrite with piped stdin requires --force

End-to-end testing

Created a fresh swamp repo in /tmp with a local_encryption vault and verified all scenarios with the compiled binary:

# Test Command Result
1 Inline KEY=VALUE (backward compat) vault put test-vault API_KEY=inline-secret-123 --json Stored successfully
2 Piped from echo echo "piped-secret-456" | vault put test-vault PIPED_KEY --json Stored successfully
3 Piped from file cat secret.txt | vault put test-vault FILE_KEY --json Stored successfully
4 No = and no stdin vault put test-vault BARE_KEY --json Clear error with both usage formats
5 Inline overwrite with --force vault put test-vault API_KEY=updated -f --json Overwritten, overwritten: true
6 Piped overwrite without --force (log mode) echo "new" | vault put test-vault PIPED_KEY Clear error: "Use --force (-f) to overwrite when piping from stdin"
7 Piped overwrite with --force echo "forced" | vault put test-vault PIPED_KEY -f --json Overwritten successfully
8 Piped from env var echo "$MY_SECRET" | vault put test-vault ENV_KEY --json Stored successfully

Then created a workflow using ${{ vault.get(test-vault, KEY) }} expressions to read all 4 secrets back. All steps succeeded with values correctly resolved (redacted as *** in persisted data, confirming the secret redactor recognized them as real vault values).

Test plan

  • deno check — type checking passes
  • deno lint — linting passes
  • deno fmt — formatting passes
  • deno run test — 2376 tests pass (16 vault_put tests including 8 new)
  • deno run compile — binary compiles
  • End-to-end manual test with compiled binary in fresh repo

🤖 Generated with Claude Code


Installation

macOS (Apple Silicon):

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

swamp 20260228.015056.0-sha.cd6b983d

28 Feb 01:51
Immutable release. Only release title and notes can be modified.
cd6b983

Choose a tag to compare

What's Changed

  • feat: add allowFailure flag on workflow steps (#544)

Summary

Closes #541

  • Adds allowFailure: boolean (default false) to the step schema, allowing steps to fail without propagating failure to the job or workflow
  • When a step with allowFailure: true fails, it is recorded as failed with allowedFailure: true on the StepRun, but does not cause the job to fail
  • Trigger condition behavior is unchanged: succeeded → false, failed → true, completed → true — so downstream steps using dependsOn: completed still fire
  • Output rendering shows a icon for allowed-failure steps instead of the regular

Design

The implementation leverages the existing Promise.allSettled pattern in the execution loop. When a step with allowFailure: true catches an error, it marks the StepRun as an allowed failure and returns normally (instead of rethrowing). This means Promise.allSettled sees a fulfilled promise, and jobFailed is never set — so no changes were needed to the job execution loop itself.

Changes

  • src/domain/workflows/step.ts — Added allowFailure to schema, class, and serialization
  • src/domain/workflows/workflow_run.ts — Added allowedFailure tracking on StepRun
  • src/domain/workflows/execution_service.ts — Modified catch blocks in both executeStep() and executeExpandedStep() to handle allowFailure
  • src/domain/workflows/job.ts / workflow.ts — Added Input types for backward-compatible fromData()
  • src/presentation/output/workflow_run_output.ts — Added icon for allowed-failure steps
  • src/cli/commands/workflow_run.ts, workflow_history_get.ts, workflow_run_search.ts, workflow_history_search.ts — Thread allowedFailure through to output
  • design/workflow.md — Documented behavior
  • .claude/skills/swamp-workflow/SKILL.md — Added usage example

Test plan

  • Unit tests for StepallowFailure defaults to false, round-trips through create/fromData/toData
  • Unit tests for StepRunmarkAllowedFailure() sets flag, serializes only when true
  • Execution service tests:
    • Step with allowFailure: true fails → job still succeeds
    • Step with allowFailure: true fails → workflow still succeeds
    • Downstream step with dependsOn: succeeded skips when allowFailure step fails
    • Downstream step with dependsOn: completed runs when allowFailure step fails
    • Mix of allowFailure and regular failing steps → job fails (regular failure dominates)
  • All 2366 tests pass
  • deno check, deno lint, deno fmt pass
  • Binary compiled and manually tested end-to-end (see PR comment for example workflow)

🤖 Generated with Claude Code


Installation

macOS (Apple Silicon):

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

swamp 20260228.012758.0-sha.ab0f9681

28 Feb 01:28
Immutable release. Only release title and notes can be modified.
ab0f968

Choose a tag to compare

What's Changed

  • fix: evaluate workflow expressions at runtime and forward task.inputs in --last-evaluated (#543)

Summary

Fixes #537. Two bugs in workflow run caused ${{ inputs.* }} expressions and task.inputs to be silently dropped during execution.

Bug A — evaluated workflow not used for execution

evaluateWorkflow() returned a correctly evaluated workflow, but the result was stored in a local const evaluatedWorkflow variable that was only saved to cache. The original workflow variable — still containing raw ${{ }} expression strings — was used for the rest of execution. This meant modelIdOrName arrived at the step executor as the literal string ${{ inputs.deviceModel }} instead of the resolved model name.

Fix: Change const evaluatedWorkflow = ... to workflow = ... so the evaluated workflow is actually used for execution.

Bug B — --last-evaluated drops task.inputs

In executeModelMethod(), the task.inputs evaluation and setMethodArgument() forwarding was inside the else if (ctx.expressionContext) branch (the normal expression evaluation path). When useLastEvaluated was true, expressionContext was forced to undefined by a ternary (lastEvaluated ? undefined : expressionContext), so the entire block was skipped and model methods received undefined for all input fields.

Fix:

  • Hoist stepInputs declaration above the if/else
  • In the useLastEvaluated branch, set stepInputs = task.inputs (values are already resolved in the pre-evaluated workflow)
  • Move the setMethodArgument forwarding loop outside the if/else so it runs for both paths

Minimal expression context for --last-evaluated

To support step-output-dependent expressions (e.g. ${{ model.previous.resource.foo.bar }}) that were deferred during workflow evaluate:

  • Build a minimal expression context ({ model: {}, env: buildEnvContext() }) even when lastEvaluated is true
  • Remove the lastEvaluated ? undefined : expressionContext ternaries at both executeStep and executeExpandedStep — the expression context is now always passed through

This allows step outputs to be tracked in the context during execution, enabling deferred expressions to be evaluated at step execution time.

Test plan

Unit tests (2 new regression tests)

  • Bug A testworkflow expressions are evaluated before step execution: creates a workflow with ${{ inputs.deviceModel }} as the model name, verifies the step executor receives "my-device" (not the raw expression string)
  • Bug B testuseLastEvaluated context carries task.inputs and expressionContext: creates a workflow with task.inputs, saves it as an evaluated workflow, runs with lastEvaluated: true, verifies the expression context is provided and task.inputs are present
  • All 2348 existing tests continue to pass

End-to-end manual testing

Created a fresh swamp repo in /tmp with a command/shell model (my-echo) and two workflows:

Bug A verification — workflow with modelIdOrName: ${{ inputs.modelName }}:

swamp workflow run test-bug-a --input '{"modelName":"my-echo"}' --json

Result: succeeded${{ inputs.modelName }} resolved to my-echo, model found and executed.

Bug B verification — workflow with both expression in model name and task.inputs:

swamp workflow evaluate test-bug-b --input '{"modelName":"my-echo","message":"echo hello-from-bug-b-test"}' --json
swamp workflow run test-bug-b --last-evaluated --json

Result: succeeded — evaluated YAML shows resolved values, --last-evaluated correctly forwarded task.inputs.run as "echo hello-from-bug-b-test", stdout confirmed hello-from-bug-b-test.

Combined verification — direct workflow run with both expressions (no evaluate step needed):

swamp workflow run test-bug-b --input '{"modelName":"my-echo","message":"echo direct-run-works"}' --json

Result: succeeded — both modelIdOrName and task.inputs.run resolved correctly, stdout confirmed direct-run-works.

Verification checklist

  • deno check — type checking passes
  • deno lint — linting passes
  • deno fmt — formatting passes
  • deno run test — 2348 tests pass
  • deno run compile — binary compiles successfully

🤖 Generated with Claude Code


Installation

macOS (Apple Silicon):

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

swamp 20260228.011207.0-sha.151c2054

28 Feb 01:12
Immutable release. Only release title and notes can be modified.
151c205

Choose a tag to compare

What's Changed

  • feat: persist evaluated definitions in model evaluate (#542)

Summary

Fixes #538swamp model evaluate resolves CEL expressions correctly but never persisted evaluated definitions to .swamp/definitions-evaluated/. This was inconsistent with swamp workflow evaluate (which persists to .swamp/workflows-evaluated/), and meant --last-evaluated flags on downstream commands couldn't find the evaluated definitions.

What changed

  • src/cli/commands/model_evaluate.ts: Added evaluatedDefinitionRepo.save() calls in both the single-model and --all code paths, following the exact pattern already used by model method run (lines 240-242 of model_method_run.ts). Also populated outputPath in the output data so JSON consumers can locate the persisted file.

Secret safety

No additional redaction logic is needed. The evaluation service uses a two-phase model:

  1. Persist phase (what model evaluate does): Only CEL expressions are resolved. Vault expressions (vault.get(...)) and env expressions (env.*) are detected by containsVaultExpression() / containsEnvExpression() and left as raw ${{ ... }} strings.
  2. Runtime phase (only during method execution): resolveRuntimeExpressionsInDefinition() resolves vault/env expressions in memory, registers secrets with SecretRedactor, and never persists them.

Since model evaluate only calls the persist-phase evaluation, the persisted YAML files will never contain actual secret values.

Testing

Integration tests (integration/model_evaluate_test.ts)

Three new integration tests verify the fix:

  1. Single model persistence — Creates a model with CEL expressions (${{ 1 + 1 }}), runs model evaluate, verifies:

    • File written to .swamp/definitions-evaluated/command/shell/<id>.yaml
    • CEL expression evaluated to 2 in persisted file
    • outputPath present in JSON output pointing to definitions-evaluated/
  2. --all persistence — Creates 3 models, runs model evaluate --all, verifies:

    • All 3 files exist in .swamp/definitions-evaluated/command/shell/
    • All items in JSON output have outputPath set
  3. Vault expression preservation — Creates a model with ${{ vault.get('my-vault', 'api-key') }}, runs model evaluate, verifies:

    • Persisted file still contains vault.get as a raw string (not resolved)

Manual end-to-end verification

Created a fresh swamp repo in /tmp, added models with CEL and vault expressions, and verified:

$ swamp model evaluate my-server --json
{
  "id": "f57604ae-...",
  "name": "my-server",
  "type": "command/shell",
  "hadExpressions": true,
  "outputPath": "/tmp/swamp-evaluate-test/.swamp/definitions-evaluated/command/shell/f57604ae-....yaml",
  "globalArguments": {
    "computed_value": 2,       # was "${{ 1 + 1 }}"
    "static_value": "hello"
  }
}

Persisted evaluated definition:

globalArguments:
  computed_value: 2            # CEL evaluated
  static_value: hello
methods:
  execute:
    arguments:
      run: echo deploying version 6   # was "${{ 2 * 3 }}"

Vault expressions preserved as raw strings:

globalArguments:
  api_key: '${{ vault.get(''my-vault'', ''api-key'') }}'   # NOT resolved
  env_var: '${{ env.HOME }}'                                  # NOT resolved

CI verification

  • deno check — passed
  • deno lint — passed
  • deno fmt — passed
  • deno run test — all 2349 tests pass, 0 failures
  • deno run compile — binary compiled successfully

Test plan

  • Integration test: single model persists to definitions-evaluated/
  • Integration test: --all persists all models
  • Integration test: vault expressions preserved as raw strings
  • Manual: swamp model evaluate <name> --json produces outputPath
  • Manual: swamp model evaluate --all --json produces outputPath for all items
  • Manual: persisted files have evaluated CEL, raw vault/env expressions
  • Full test suite passes (2349 tests)

🤖 Generated with Claude Code


Installation

macOS (Apple Silicon):

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