- TypeScript 99.7%
- Python 0.1%
- Shell 0.1%
## Summary - `swamp serve` hot-reload (SIGHUP) reported success but kept executing stale model code — Deno's `import()` caches modules by URL within the process, so re-importing the same file URL returned the old module - Added a reload generation counter that appends `&gen=N` to import URLs, forcing `import()` to treat each reload as a new module - `reloadPulledExtensions` now re-bundles only changed source files (fingerprint comparison) before re-importing — unchanged files skip the expensive `deno bundle` subprocess entirely - Failed re-bundles log a warning and keep the old bundle (graceful degradation) ### What this fixes The hot-reload path: source updated → SIGHUP → serve now executes the new code. Verified with model, vault, datastore, and report extension kinds. ### What this does NOT fix (related issues) - Stale code surviving full process restart — could not reproduce; fresh processes pick up new code correctly - Lockfile path mismatch with `extensionsDir` — tracked in swamp-club#1133 - `findByExtension` returning 0 rows for large pulled extensions (e.g. `@swamp/aws/ec2`) — filed as swamp-club#1149 ### Performance Tested with `@swamp/aws/ec2` (108 model types, 107 bundles): - Serve startup: no regression - Hot-reload with 0 changed files: instant (fingerprint match → skip) - Hot-reload with changed files: only changed files re-bundle (~200ms each) ## Test plan - [x] Reproduced bug: serve V1 → update source → SIGHUP → still V1 (before fix) - [x] Verified fix: serve V1 → update source → SIGHUP → V2 executes (after fix) - [x] Tested all 4 extension kinds (model, vault, datastore, report) — all reload V1→V2 - [x] `@swamp/aws/ec2` (108 types): no startup regression, no unnecessary re-bundling on reload - [x] Confirmed Deno `import()` busts cache with different query params - [x] Failed re-bundle keeps old bundle (graceful degradation) - [x] Full process restart works correctly (was not broken) - [x] 8907 unit/integration tests pass - [x] `integration/bundle_cache_freshness_test.ts` (8 tests) pass — CLI path unaffected Closes swamp-club#1140 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Blake Irvin <bixu@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|---|---|---|
| .agents/skills | ||
| .claude | ||
| .github | ||
| agent-constraints | ||
| design | ||
| evals/promptfoo | ||
| extensions/models | ||
| integration | ||
| packages | ||
| scripts | ||
| src | ||
| .gitattributes | ||
| .gitignore | ||
| banner.png | ||
| CLAUDE.md | ||
| CODE_OF_CONDUCT.md | ||
| CONTRIBUTING.md | ||
| COPYING | ||
| COPYING-EXCEPTION | ||
| COPYRIGHT | ||
| deno.json | ||
| deno.lock | ||
| Dockerfile | ||
| FILE-LICENSE-TEMPLATE.md | ||
| LICENSE | ||
| logo.png | ||
| main.ts | ||
| main_test.ts | ||
| OSS-FAQ.md | ||
| README.md | ||
| sc-mark.png | ||
| SECURITY.md | ||
| swamp-club-wordmark.png | ||
| TRADEMARKS.md | ||
Swamp
Deterministic Automation for AI Agents.
Swamp is a CLI that supercharges AI agents to create operational workflows that
are reviewable, shareable, and accurate. Built for agents, there to empower
humans. All the data lives in the .swamp/ (the swamp).
Come join the swamp party on discord.
Getting Started
curl -fsSL https://swamp-club.com/install.sh | sh
Quick Start
swamp repo init # Claude Code (default)
swamp repo init --tool cursor # Cursor
swamp repo init --tool opencode # OpenCode
swamp repo init --tool codex # Codex
Start your AI agent in the repo and tell it what you want to do. Just ask:
- "Manage my EC2 fleet — inventory every instance across all regions and flag anything without a cost-center tag"
- "Set up a workflow to check my bare metal Minecraft servers are online and under 80% memory"
- "Audit our DNS records and compare them against what's actually running"
- "Build a workflow that rotates database credentials and stores them in the vault"
The agent will create models, wire up workflows, and run them — all reviewable
in .swamp/ before anything touches production.
Learn More
You can learn more about swamp by reading the manual. How Swamp Works is the best place to start - it provides an overview of the entire system.
Core Concepts
- Models — Typed representations of external systems (cloud resources, CLI tools, APIs). Each model type defines metadata, arguments, methods, and inputs.
- Definitions — YAML files that instantiate a model type with specific configuration. Support CEL expressions for dynamic values and cross-model references.
- Workflows — Orchestrate model method executions across parallel jobs and steps, with dependency ordering and trigger conditions.
- Data — Versioned, immutable artifacts (resources, logs, files) produced by method runs. Searchable by tags.
- Vaults — Secure storage for secrets and credentials, referenced in definitions via CEL expressions.
- Tags — Key-value labels on definitions, workflows, and data. Flow from
definitions to produced data, overridable at runtime with
--tag.
Everything lives in a .swamp/ directory inside a Git repository, with
human-friendly symlink views under /models/ and /workflows/.
Local Execution
Swamp runs entirely on your machine. It picks up the environment variables from the shell you run it in — your AWS credentials, SSH keys, kubeconfig, whatever the task needs. No credentials leave your laptop unless a model explicitly calls an external API.
Update
Swamp can update itself in place:
swamp update
Shell Completions
Tab-completion for commands, model names, and workflow names:
# Bash — add to ~/.bashrc
eval "$(swamp completions bash)"
# Zsh with oh-my-zsh
mkdir -p ~/.oh-my-zsh/completions
swamp completions zsh > ~/.oh-my-zsh/completions/_swamp
rm -f ~/.zcompdump* && exec zsh
# Zsh without oh-my-zsh — add to ~/.zshrc
eval "$(swamp completions zsh)"
# Fish
swamp completions fish > ~/.config/fish/completions/swamp.fish
Completions are directory-dependent — they return names from the current directory's swamp repository.
Using Swamp with AI Agents
Swamp ships first-class skills for four AI coding tools:
| Tool | Init flag | Skills dir | Instructions file |
|---|---|---|---|
| Claude Code | (default) | .claude/skills/ |
CLAUDE.md |
| Cursor | --tool cursor |
.cursor/skills/ |
.cursor/rules/swamp.mdc |
| OpenCode | --tool opencode |
.agents/skills/ |
AGENTS.md |
| Codex | --tool codex |
.agents/skills/ |
AGENTS.md |
The skills are bundled into the swamp binary and written into the appropriate directory so your agent discovers them automatically. Each skill teaches the agent how to work with swamp — search for models, create definitions, run workflows, manage vaults, and more.
You can switch tools later or run multiple tools side-by-side — each tool's skills directory is independent and gitignored:
swamp repo upgrade --tool cursor
User-Defined Models
Extend Swamp with custom TypeScript models. Place them in extensions/models/
(or configure via SWAMP_MODELS_DIR or .swamp.yaml).
See the swamp skill in your skills directory (e.g.
.claude/skills/swamp/SKILL.md for Claude Code) for details.
Developer Guide
Prerequisites
- Deno (latest)
Commands
deno run dev # Run the CLI from source
deno run test # Run the test suite
deno check # Type-check
deno lint # Lint
deno fmt # Format
deno run compile # Compile the binary
License Compliance
deno run sbom generates a CycloneDX SBOM of every
npm and JSR dependency for license-compliance scanning. See
design/license-compliance.md for details on the
generator, license resolution, and scanning with FOSSA.
Contributing
Swamp uses an issue-driven contribution model. We don't accept pull requests from external contributors — fork PRs are automatically closed. This isn't about gatekeeping; it's about supply chain security in the age of AI-generated code. When AI agents can produce large, plausible-looking changes, the only way to maintain quality and security is to tightly control the inputs to the development process.
Here's how it works:
- You file an issue — bug reports and feature requests are very welcome. Be as detailed as you like.
- We triage it — A maintainer triages the issue locally using Claude, confirms bugs by tracing through the codebase, and generates a detailed implementation plan. Plans are revised interactively until the approach is solid.
- We build it — Elder Swamp Club engineers (with AI agents under our direct control) implement the plan, with full test coverage and code review.
- You get credit — We're happy to include you as a co-author on any PR generated from your request.
This means you get the feature you asked for, maintained over time, without having to worry about keeping a fork in sync. See CONTRIBUTING.md for the full details.
Datastores
By default, swamp stores all runtime data (model data, workflow runs, outputs,
audit logs, etc.) in the local .swamp/ directory. You can configure a
different datastore backend to share state across machines or centralise data.
Default (local filesystem)
When you run swamp repo init, the datastore is .swamp/ inside the repo. No
extra configuration needed.
Setting up an external filesystem datastore
Move runtime data to a directory outside the repo (e.g. a shared NFS mount):
swamp datastore setup filesystem --path /mnt/shared/swamp-data
This migrates existing .swamp/ runtime data to the new path and updates
.swamp.yaml. A file-based lock prevents concurrent access from multiple
processes.
Setting up an S3 datastore
Store runtime data in S3 for team collaboration using the @swamp/s3-datastore
extension:
swamp datastore setup extension @swamp/s3-datastore \
--config '{"bucket":"my-swamp-bucket","prefix":"my-project","region":"us-east-1"}'
This pushes existing local data to S3 and updates .swamp.yaml. Subsequent
commands automatically pull changes before execution and push changes after. A
distributed lock (S3 conditional writes) prevents concurrent access.
Use --skip-migration on either setup command to skip the initial data
migration.
Migrating between datastores
Run swamp datastore setup again with the new backend. For example, to move
from a filesystem datastore to S3:
swamp datastore setup extension @swamp/s3-datastore \
--config '{"bucket":"my-bucket","region":"us-east-1"}'
Or from S3 back to local filesystem:
swamp datastore setup filesystem --path /path/to/data
Each setup command migrates existing data to the new backend.
Checking datastore status
swamp datastore status # Shows type, health, and config
swamp datastore sync # Manual bidirectional sync (S3 only)
swamp datastore sync --pull # Pull-only from S3
swamp datastore sync --push # Push-only to S3
Stuck locks
If a process crashes without releasing the datastore lock, subsequent commands will wait up to 60 seconds before timing out (locks auto-expire after 30 seconds). To inspect or force-release a stuck lock:
swamp datastore lock status # Show who holds the lock
swamp datastore lock release --force # Force-release the lock
Environment variable override
For CI/CD, override the datastore without modifying .swamp.yaml:
export SWAMP_DATASTORE=s3:my-bucket/my-prefix
export SWAMP_DATASTORE=filesystem:/tmp/swamp-data
Repository Directory
Every command runs against a swamp repository. By default this is the current
working directory. Pass --repo-dir to point at a different repo per
invocation, or set SWAMP_REPO_DIR to persist the override across a shell
session or CI job:
swamp model search --repo-dir /path/to/repo
export SWAMP_REPO_DIR=/path/to/repo
swamp model search
Priority order (highest to lowest): --repo-dir flag → SWAMP_REPO_DIR env var
→ current working directory.
Log Level
By default, swamp outputs at the info level. You can change this once rather
than repeating a flag on every command.
Per-invocation flags (highest priority):
swamp -q workflow run my-workflow # error level only
swamp --log-level debug workflow run my-workflow
Via environment variable (useful for CI/CD):
export SWAMP_LOG_LEVEL=warning
swamp workflow run my-workflow
Permanently for a repository — add to .swamp.yaml:
logLevel: error
Valid levels: trace, debug, info, warning, error, fatal.
Priority order (highest to lowest): -q / --log-level flag →
SWAMP_LOG_LEVEL env var → .swamp.yaml logLevel → default (info).
Tracing
Swamp has native OpenTelemetry tracing for diagnosing slow or failing operations. Tracing is opt-in and has zero overhead when disabled.
Set OTEL_EXPORTER_OTLP_ENDPOINT to enable:
# Send traces to a local Jaeger instance
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 swamp workflow run my-workflow
# Quick debug: print spans to stderr (no collector needed)
OTEL_TRACES_EXPORTER=console swamp workflow run my-workflow
Traces capture the full execution hierarchy — CLI command, workflow, job, step,
model method, and driver execution — with automatic context propagation to
in-process extensions and Docker containers via TRACEPARENT.
Local development
Run Jaeger for a local trace UI:
docker run -d --name jaeger -p 16686:16686 -p 4318:4318 jaegertracing/all-in-one:latest
Then open http://localhost:16686 and search for the swamp service.
Configuration
| Variable | Purpose | Default |
|---|---|---|
OTEL_EXPORTER_OTLP_ENDPOINT |
Collector URL (tracing off when unset) | (unset = off) |
OTEL_TRACES_EXPORTER |
otlp, console, or none |
otlp |
OTEL_SERVICE_NAME |
Service name in traces | swamp |
OTEL_EXPORTER_OTLP_HEADERS |
Auth headers (key=val,key=val) |
(none) |
Telemetry
Swamp collects anonymous usage telemetry to help us understand which commands are used, how long they take, and what errors occur. All user-identifiable values are redacted before transmission — nothing sensitive is ever sent.
Here is a complete example of a telemetry event:
{
"event": "cli_invocation",
"distinct_id": "a3f1b2c4-5678-9abc-def0-1234567890ab",
"properties": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"invocation": {
"command": "model",
"subcommand": "create",
"args": ["prompt", "<REDACTED>"],
"optionKeys": ["--force"],
"globalOptions": ["--json"]
},
"result": {
"status": "success",
"exitCode": 0
},
"startedAt": "2026-02-16T10:00:00.000Z",
"completedAt": "2026-02-16T10:00:01.234Z",
"durationMs": 1234,
"swampVersion": "0.13.0",
"denoVersion": "2.1.0",
"platform": "linux"
}
}
Note that positional arguments containing user data (model names, file paths,
queries) are replaced with <REDACTED>. Only categorical values defined by
swamp itself (like model types) are recorded. Option values are never recorded —
only the option keys.
Disabling Telemetry
Per-invocation:
swamp --no-telemetry workflow run my-workflow
Via environment variable (useful for CI/UAT environments):
export SWAMP_NO_TELEMETRY=1
swamp workflow run my-workflow
Permanently for a repository — add to .swamp.yaml:
telemetryDisabled: true
Priority order (highest to lowest): --no-telemetry flag → SWAMP_NO_TELEMETRY
env var → .swamp.yaml telemetryDisabled: true.
License
Swamp is licensed under the GNU Affero General Public License v3.0 with the Swamp Extension and Definition Exception. No rights are granted for use of our trademarks outside our explicit policy See COPYRIGHT, CONTRIBUTING.md and Open Source FAQ for details.