Skip to main content

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 integration
  • extensions — verifies user-defined extensions load cleanly and inspects catalog state
  • install — 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 vault
  • workflows — 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: PASS

Failed 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: FAIL

JSON 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 identifier
  • status"pass", "fail", or "skip"
  • message — human-readable result
  • details — optional object with check-specific data
  • hint — 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 CI

doctor 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: PASS

Healthy 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: PASS

Verbose (--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: PASS

Repair 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: PASS

Repair 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 warning
  • category — warning type. Currently supported:
    • TypeExtractionFailed — the extension's type field 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 name
  • version — installed version
  • origin"pulled" (from registry) or "local" (user-defined)
  • sourceCount — total source files for this extension
  • stateDistribution — 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 root
  • stateTag — one of the RowState values
  • fingerprint — content hash of the source file
  • bundlePath — 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 when stateTag is ValidationFailed, BundleBuildFailed, or EntryPointUnreadable; 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 prompting

doctor 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)


HEALTHY

Unhealthy

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


UNHEALTHY

JSON 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 output

doctor 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 id
  • definitionName — the definition's name
  • type — the model type
  • leakedPaths — the global-argument paths holding a cleartext sensitive value
  • remediations — one entry per leaked path

Each entry in remediations includes:

  • path — the leaked global-argument path
  • vaultName — the vault to store the secret in
  • vaultKey — the key to store the secret under
  • expression — the vault.get(...) expression to replace the literal with

Each entry in unresolved includes:

  • definitionId — the definition's id
  • definitionName — the definition's name
  • type — 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 output

doctor 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: PASS

Healthy

Checking workflows...
  ✓ hello

1 passed, 0 failed — OVERALL: PASS

Validation 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: FAIL

JSON 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 file
  • name — workflow name from the YAML
  • status"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