DOCTOR
swamp doctor runs diagnostics that verify a swamp installation and
repository's integrations are healthy. It has five subcommands:
audit— verifies the AI-tool audit integrationextensions— verifies user-defined extensions load cleanly and inspects catalog stateinstall— checks installation health (binary path, ownership, writability, autoupdate status)secrets— scans model definitions for cleartext sensitive global arguments and reports how to migrate each to a vaultworkflows— checks workflow YAML files load cleanly
Global flags
All subcommands accept these flags. Only subcommand-specific flags are listed in each section below.
| Flag | Description |
|---|---|
--json |
Output in JSON format (non-interactive). |
--log |
Force flat log output (no interactive tree). |
--log-level <level> |
Set log level (trace, debug, info, warning, error, fatal). |
-q, --quiet |
Suppress non-essential output. |
--no-telemetry |
Disable telemetry for this invocation. |
--show-properties |
Show structured properties in log output. |
--no-color |
Disable colored output. |
--repo-dir <dir> |
Repository directory (env: SWAMP_REPO_DIR). |
-h, --help |
Show help. |
doctor audit
Verifies that the AI-tool audit integration is healthy for the configured tool.
Flags
| Flag | Description |
|---|---|
--tool <tool> |
Override the tool from .swamp.yaml. Values: claude, cursor, kiro, opencode, codex, copilot, none. |
Exit codes
| Code | Condition |
|---|---|
0 |
All audit checks pass. |
1 |
One or more checks failed. |
Output
Healthy
✓ binary-on-path claude found at /Users/you/.local/bin/claude
✓ swamp-binary-on-path swamp is on PATH at /opt/homebrew/bin/swamp
✓ agent-config-loadable .claude/settings.local.json is present with both PostToolUse hooks wired
– default-agent-set does not apply to claude
✓ recording-smoke-test synthetic claude payload landed in today's audit JSONL
4 passed, 0 failed, 1 skipped — OVERALL: PASSFailed check
When a check fails, a hint suggests a remediation:
✓ binary-on-path cursor found at /Users/you/.local/bin/cursor
✓ swamp-binary-on-path swamp is on PATH at /opt/homebrew/bin/swamp
✗ agent-config-loadable .cursor/hooks.json is missing
hint: Run `swamp init --tool cursor --force` to regenerate.
– default-agent-set does not apply to cursor
✓ recording-smoke-test synthetic cursor payload landed in today's audit JSONL
3 passed, 1 failed, 1 skipped — OVERALL: FAILJSON shape
{
"tool": "claude",
"overallStatus": "pass",
"checks": [
{
"name": "binary-on-path",
"status": "pass",
"message": "claude found at /Users/you/.local/bin/claude",
"details": {
"binary": "claude",
"path": "/Users/you/.local/bin/claude"
}
},
{
"name": "agent-config-loadable",
"status": "pass",
"message": ".claude/settings.local.json is present with both PostToolUse hooks wired"
},
{
"name": "default-agent-set",
"status": "skip",
"message": "does not apply to claude"
},
{
"name": "recording-smoke-test",
"status": "pass",
"message": "synthetic claude payload landed in today's audit JSONL"
}
]
}overallStatus is "pass" when all checks pass, "fail" otherwise.
Each entry in checks includes:
name— check identifierstatus—"pass","fail", or"skip"message— human-readable resultdetails— optional object with check-specific datahint— present on failed checks when a remediation is available
Examples
swamp doctor audit # check the tool configured in .swamp.yaml
swamp doctor audit --tool kiro # check a specific tool
swamp doctor audit --json # machine-readable output for CIdoctor extensions
Verifies that user-defined extensions in a repository load cleanly and inspects the catalog's aggregate state. Checks each registry (model, vault, driver, datastore, report), summarises per-extension source health, detects orphaned catalog rows and bundle files, and optionally repairs the catalog.
Flags
| Flag | Description |
|---|---|
--verbose |
Show per-source detail for each extension. |
--repair |
Prune Tombstoned catalog rows and evict unreferenced bundle files. |
--dry-run |
Preview repair operations without executing. Use with --repair. |
-f, --force |
Skip the confirmation prompt. Use with --repair. |
Exit codes
| Code | Condition |
|---|---|
0 |
All registry checks pass. |
1 |
One or more registry checks failed. |
Output
No extensions installed
✓ model
✓ vault
✓ driver
✓ datastore
✓ report
Extension Catalog State
0 total source(s) across 0 extension(s), 0 healthy (Indexed)
5 passed, 0 failed — OVERALL: PASSHealthy catalog
✓ model
✓ vault
✓ driver
✓ datastore
✓ report
Extension Catalog State
1 total source(s) across 1 extension(s), 1 healthy (Indexed)
@swamp/1password@2026.04.22.2 [pulled] 1 source(s)
Indexed: 1
5 passed, 0 failed — OVERALL: PASSVerbose (--verbose)
Adds a Per-Source Detail section listing every source file, its state tag, fingerprint, bundle path, and registry kind.
✓ model
✓ vault
✓ driver
✓ datastore
✓ report
Extension Catalog State
1 total source(s) across 1 extension(s), 1 healthy (Indexed)
@swamp/1password@2026.04.22.2 [pulled] 1 source(s)
Indexed: 1
Per-Source Detail
vault .swamp/pulled-extensions/@swamp/1password/vaults/onepassword.ts Indexed fp:3398bcef6091 bundle:.swamp/vault-bundles/a52f5a8b/onepassword.js
5 passed, 0 failed — OVERALL: PASSRepair dry run (--repair --dry-run)
Previews what --repair would clean up without modifying the catalog.
✓ model
✓ vault
✓ driver
✓ datastore
✓ report
Extension Catalog State
1 total source(s) across 1 extension(s), 1 healthy (Indexed)
@swamp/1password@2026.04.22.2 [pulled] 1 source(s)
Indexed: 1
Repair DRY RUN
Nothing to clean up — catalog is healthy.
5 passed, 0 failed — OVERALL: PASSRepair apply (--repair or --repair --force)
Without --force, a confirmation prompt is shown before modifying the catalog.
With --force, the prompt is skipped.
RowState values
Each source file in the catalog carries a state tag. The aggregate state section groups sources by these values.
| State | Meaning |
|---|---|
Indexed |
Source is registered in the catalog and healthy. |
Bundled |
Source has a compiled bundle but is not yet indexed. |
BundleBuildFailed |
Bundle compilation failed. The source is registered but not usable. |
ValidationFailed |
Source failed validation checks. |
EntryPointUnreadable |
The source file's entry point cannot be read from disk. |
OrphanedBundleOnly |
A bundle file exists with no corresponding catalog row. |
Tombstoned |
Source has been removed but the catalog row remains. Pruned by --repair. |
JSON shape
Base (--json)
{
"overallStatus": "pass",
"registries": {
"model": { "registry": "model", "status": "pass" },
"vault": { "registry": "vault", "status": "pass" },
"driver": { "registry": "driver", "status": "pass" },
"datastore": { "registry": "datastore", "status": "pass" },
"report": { "registry": "report", "status": "pass" }
},
"orphanFiles": [],
"aggregateState": {
"aggregates": [
{
"name": "@swamp/1password",
"version": "2026.04.22.2",
"origin": "pulled",
"sourceCount": 1,
"stateDistribution": {
"Indexed": 1,
"Bundled": 0,
"BundleBuildFailed": 0,
"ValidationFailed": 0,
"EntryPointUnreadable": 0,
"OrphanedBundleOnly": 0,
"Tombstoned": 0
}
}
],
"sourceDetails": [],
"catalogOrphans": [],
"bundleOrphans": [],
"totalSources": 1,
"healthySources": 1,
"orphanRowCount": 0,
"orphanBundleFileCount": 0
},
"warnings": [
{
"sourcePath": "extensions/models/foo/bar.ts",
"category": "TypeExtractionFailed",
"message": "type field could not be extracted..."
}
]
}overallStatus is "pass" when all registries pass, "fail" otherwise.
warnings contains advisory diagnostics that do not affect overallStatus or
the exit code. Each entry includes:
sourcePath— path to the source file that triggered the warningcategory— warning type. Currently supported:TypeExtractionFailed— the extension'stypefield could not be extracted from the source file (e.g. the export is dynamically computed or uses an unsupported pattern). The extension still loads and functions normally.
message— human-readable description of the issue
The category field is extensible — future swamp versions may add new warning
types.
Each registry entry contains registry (the kind name) and status ("pass"
or "fail"). The per-registry status is derived from sourceDetails[] — a
registry is "fail" when any of its sources carry a failure-state tag
(ValidationFailed, BundleBuildFailed, EntryPointUnreadable).
sourceDetails[] is the single authoritative failure surface for extension
diagnostics.
Each entry in aggregates includes:
name— extension nameversion— installed versionorigin—"pulled"(from registry) or"local"(user-defined)sourceCount— total source files for this extensionstateDistribution— count of sources in each RowState
Verbose (--json --verbose)
sourceDetails is populated with per-source entries:
{
"sourceDetails": [
{
"sourcePath": ".swamp/pulled-extensions/@swamp/1password/vaults/onepassword.ts",
"stateTag": "Indexed",
"fingerprint": "3398bcef6091...",
"bundlePath": ".swamp/vault-bundles/a52f5a8b/onepassword.js",
"kind": "vault"
}
]
}Each source detail includes:
sourcePath— path to the source file relative to the repository rootstateTag— one of the RowState valuesfingerprint— content hash of the source filebundlePath— path to the compiled bundle (if any)kind— registry kind (model,vault,driver,datastore,report)lastError— error message explaining why the source is in a failure state. Present whenstateTagisValidationFailed,BundleBuildFailed, orEntryPointUnreadable; absent for healthy states.
Repair (--json --repair --dry-run)
Adds a repairReport object:
{
"repairReport": {
"mode": "dry-run",
"operations": [],
"prunedRowCount": 0,
"evictedFileCount": 0
}
}mode is "dry-run" or "applied". Each entry in operations describes a
pruned catalog row or evicted bundle file. prunedRowCount and
evictedFileCount are totals.
Examples
swamp doctor extensions # check this repo's extensions
swamp doctor extensions --json # machine-readable output for CI
swamp doctor extensions --verbose # show per-source detail
swamp doctor extensions --repair --dry-run # preview what repair would clean up
swamp doctor extensions --repair # apply repair (with confirmation)
swamp doctor extensions --repair --force # apply repair without promptingdoctor install
Checks swamp installation health: binary ownership, writability, and autoupdate status. Does not require a swamp repository — runs from any directory.
Exit codes
| Code | Condition |
|---|---|
0 |
Installation is healthy. |
1 |
One or more installation checks are unhealthy. |
Output
Healthy
Installation Health Check
Binary path: /Users/you/.local/bin/swamp
Version: 20260518.215711.0-sha.d4a64cc1
Binary owner: you
Writable: ✓ Binary is owned by current user
Autoupdate
Enabled: yes
Cadence: daily
Scheduler type: LaunchAgent (user)
Scheduler: installed
Last check: 2026-05-18T22:40:18.898Z (up_to_date)
HEALTHYUnhealthy
When a check fails, the status is UNHEALTHY and the failing field shows the
error:
Installation Health Check
Binary path: /usr/local/bin/swamp
Version: 20260518.215711.0-sha.d4a64cc1
Binary owner: root
Writable: ✗ Binary is owned by root, not current user
Autoupdate
Enabled: no
Last check: 2026-05-18T22:04:59.653Z (error)
Last error: Cannot update /usr/local/bin/swamp: permission denied. Re-run with: sudo swamp update
UNHEALTHYJSON shape
{
"binaryPath": "/Users/you/.local/bin/swamp",
"owner": {
"uid": 501,
"username": "you",
"isRoot": false
},
"currentVersion": "20260518.215711.0-sha.d4a64cc1",
"writable": "pass",
"writableMessage": "Binary is owned by current user",
"autoupdate": {
"enabled": true,
"cadence": "daily",
"schedulerInstalled": true,
"schedulerType": "agent",
"lastEntry": {
"timestamp": "2026-05-18T22:40:18.898Z",
"versionBefore": "20260509.235714.0-sha.7ace6b02",
"versionAfter": "20260518.215711.0-sha.d4a64cc1",
"outcome": "updated"
}
},
"overallStatus": "healthy"
}overallStatus is "healthy" when all checks pass, "unhealthy" otherwise.
writable is "pass" when the current user owns the binary, "fail"
otherwise.
autoupdate.schedulerType identifies the platform scheduler in use. Present
when a scheduler is installed. Possible values:
| Value | Display label |
|---|---|
agent |
LaunchAgent (user) |
daemon |
LaunchDaemon (system) |
systemd-user |
systemd timer (user) |
systemd-system |
systemd timer (system) |
cron-user |
cron (user) |
cron-root |
cron (root) |
autoupdate.lastEntry is present when an autoupdate has been attempted.
outcome is one of "updated", "up_to_date", or "error". When outcome is
"error", an error field contains the error message.
Examples
swamp doctor install # check installation health
swamp doctor install --json # machine-readable outputdoctor secrets
Scans model definitions for sensitive global arguments that hold a cleartext
literal value instead of a vault.get(...) expression. The scan is read-only
and never prints the secret value. It covers models/ (including
datastore-synced definitions) and .swamp/auto-definitions. It is the detection
complement to the write-time guard that rejects a literal value for a
sensitive global argument when a definition is saved.
A global argument is sensitive when the model type's globalArguments schema
marks it so. See the Model Definitions,
Vaults, and
CEL Expressions references for the schema
marking and the vault.get expression syntax.
Flags
| Flag | Description |
|---|---|
-v, --verbose |
Show detailed output. |
Exit codes
| Code | Condition |
|---|---|
0 |
No cleartext sensitive global arguments found. |
1 |
One or more cleartext sensitive global arguments found. |
Output
Clean
Scanning definitions for cleartext sensitive global arguments…
✓ No cleartext sensitive global arguments found (0 definition(s) scanned)Findings
Each finding lists the definition, the leaked argument path, and value-free remediation steps. The secret value is never shown.
Scanning definitions for cleartext sensitive global arguments…
✗ 1 definition(s) hold a cleartext sensitive global argument (1 definition(s) scanned)
leaky [@demo/secret-demo]
• apiKey
1. Store the secret: swamp vault put my-vault apiKey <value>
2. Reference it: ${{ vault.get('my-vault', 'apiKey') }}
Each secret above sits in cleartext in its definition YAML. Migrate it to a vault, then re-save the definition.Unresolved
A definition whose extension type is not installed cannot be assessed — its type
schema is unavailable, so the scan cannot tell whether any argument is
sensitive. These are reported separately as a warning and do not affect
overallStatus or the exit code.
Scanning definitions for cleartext sensitive global arguments…
✓ No cleartext sensitive global arguments found (1 definition(s) scanned)
⚠ 1 definition(s) could not be assessed (type schema unavailable — install the extension to scan them)
• mystery [@demo/uninstalled-type]JSON shape
{
"overallStatus": "fail",
"scanned": 2,
"findings": [
{
"definitionId": "48d8e9d8-aca0-4678-9c07-64b09f4bae55",
"definitionName": "leaky",
"type": "@demo/secret-demo",
"leakedPaths": ["apiKey"],
"remediations": [
{
"path": "apiKey",
"vaultName": "my-vault",
"vaultKey": "apiKey",
"expression": "${{ vault.get('my-vault', 'apiKey') }}"
}
]
}
],
"unresolved": [
{
"definitionId": "faac276d-c74b-4c54-9ca6-b79bb151166f",
"definitionName": "mystery",
"type": "@demo/uninstalled-type"
}
]
}overallStatus is "pass" when findings is empty, "fail" otherwise.
unresolved does not affect overallStatus or the exit code.
scanned is the number of definitions inspected.
Each entry in findings includes:
definitionId— the definition's iddefinitionName— the definition's nametype— the model typeleakedPaths— the global-argument paths holding a cleartext sensitive valueremediations— one entry per leaked path
Each entry in remediations includes:
path— the leaked global-argument pathvaultName— the vault to store the secret invaultKey— the key to store the secret underexpression— thevault.get(...)expression to replace the literal with
Each entry in unresolved includes:
definitionId— the definition's iddefinitionName— the definition's nametype— the model type whose schema is unavailable
Examples
swamp doctor secrets # scan this repo's definitions
swamp doctor secrets --json # machine-readable output for CI
swamp doctor secrets --verbose # show detailed outputdoctor workflows
Checks that workflow YAML files in a repository load cleanly.
Exit codes
| Code | Condition |
|---|---|
0 |
All workflows load successfully. |
1 |
One or more workflows failed. |
Output
No workflows
Checking workflows...
No workflow files found
0 passed, 0 failed — OVERALL: PASSHealthy
Checking workflows...
✓ hello
1 passed, 0 failed — OVERALL: PASSValidation failure
When a workflow has schema errors, the validation messages are shown inline:
Checking workflows...
✗ broken
→ [
{
"expected": "string",
"code": "invalid_type",
"path": [
"id"
],
"message": "Invalid input: expected string, received undefined"
},
{
"expected": "array",
"code": "invalid_type",
"path": [
"jobs"
],
"message": "Invalid input: expected array, received object"
}
]
✓ hello
1 passed, 1 failed — OVERALL: FAILJSON shape
Healthy
{
"overallStatus": "pass",
"workflows": [
{
"file": "workflows/workflow-....yaml",
"name": "hello",
"status": "pass"
}
],
"totalPassed": 1,
"totalFailed": 0
}Failed
{
"overallStatus": "fail",
"workflows": [
{
"file": "workflows/broken.yaml",
"name": "broken",
"status": "fail",
"error": "[\n {\n \"expected\": \"string\",\n ...\n }\n]"
},
{
"file": "workflows/workflow-....yaml",
"name": "hello",
"status": "pass"
}
],
"totalPassed": 1,
"totalFailed": 1
}Each entry in workflows includes:
file— path to the workflow YAML filename— workflow name from the YAMLstatus—"pass"or"fail"error— present on failed workflows, contains the validation error
Examples
swamp doctor workflows # check all workflows
swamp doctor workflows --json # machine-readable output for CI