# Swarmie Docs > Swarmie documentation site ## Troubleshooting This page is grounded in: * `crates/tui/src/app/update/pickers.rs` * `crates/core/src/config/resolve.rs` * `crates/cli/src/cmd/oneshot.rs` * `crates/provider/src/models/registry.rs` * `crates/tui/src/app/update/core_events.rs` * `crates/tui/src/app/update/commands.rs` * `crates/tui/src/app/update/keys.rs` ### 1) Auth/provider setup issues Symptom: * Model picker shows: `No connected providers. Run /connect to configure a provider.` Fix: 1. Run `/connect` in TUI. 2. Or configure `[providers.]` and `[defaults]` in `~/.swarmie/config.toml`. 3. For API-key providers, ensure the configured `api_key_env` variable is set. ### 2) OAuth provider returns unauthorized Symptom: * Requests fail with unauthorized/401 behavior after OAuth setup. Fix: 1. Re-run `/connect` for that provider. 2. Ensure provider config uses the intended `authentication` mode. 3. Confirm stored OAuth credentials exist (runtime resolves OAuth credentials from credential store in `resolve_runtime_provider`). ### 3) Tools are denied in headless mode Symptom: * `swarmie prompt` prints warnings like `permission denied for tool ...`. Fix: 1. Expected default: non-interactive runs auto-deny permission requests. 2. For trusted automation only, use `--dangerously-bypass-approvals`. ### 4) Model name not behaving as expected Symptom: * You pass a model name, but behavior/protocol selection is not what you intended. Fix: 1. Use `/models` in TUI to pick from known registry entries. 2. Prefer canonical IDs over ad-hoc names. 3. Remember: unknown names pass through `resolve_model_id` unchanged. ### 5) MCP commands show no servers Symptom: * Footer hint: `No MCP servers configured`. Fix: 1. Add `[mcp.servers.]` entries in config. 2. Re-open `/mcps` after config changes. 3. Use `/status` to verify server states once configured. ### 6) Session resume doesn’t restore expected history Symptom: * Resume reports empty/fresh behavior. Fix: 1. Use `/resume` picker or `swarmie resume --last`. 2. Confirm the selected session actually has stored messages. 3. If no messages are stored, Swarmie intentionally starts fresh. ### 7) Context or cost grows too high Symptom: * Context usage keeps climbing, or session cost is higher than expected. Fix: 1. Check `/status` and footer context/cost indicators. 2. Run `/compact` to summarize and reduce prompt load. 3. Lower thinking level with `Alt+T` when a reasoning-capable model is active. ## CLI Reference This page is grounded in `crates/cli/src/cli.rs`. ### Binary ```bash swarmie [COMMAND] ``` If no command is provided, Swarmie starts the interactive REPL. ### Commands ### `prompt` (alias: `e`) Run Swarmie non-interactively. ```bash swarmie prompt [PROMPT] ``` `PROMPT` behavior: * If omitted, reads from stdin. * If set to `-`, reads from stdin. Options: | Flag | Type | Description | | ---------------------------------- | ----------------- | ---------------------------------------------------------------------------------- | | `-c`, `--config ` | repeatable string | Override config value (dotted path supported, value parsed as TOML when possible). | | `-m`, `--model ` | string | Model to use. | | `--provider ` | string | Provider to use. | | `-p`, `--profile ` | string | Config profile from `config.toml`. | | `--dangerously-bypass-approvals` | bool | Bypass approval prompts in non-interactive runs. | | `-C`, `--cd ` | path | Working directory root for the agent. | | `--json` | bool | Print events to stdout as JSONL. | Examples: ```bash swarmie prompt "Summarize this repo" swarmie prompt -m sonnet -p work cat prompt.txt | swarmie prompt - swarmie prompt "Explain src" --json ``` ### `resume` Resume an interactive session. ```bash swarmie resume [--last] ``` Options: | Flag | Type | Description | | -------- | ---- | --------------------------------------------------- | | `--last` | bool | Continue most recent session for current workspace. | ### `web` Start HTTP server and open web interface. ```bash swarmie web [OPTIONS] ``` Options: | Flag | Type | Default | Description | | ----------------------- | ----------------- | ----------- | --------------------------------------------------- | | `--port ` | `u16` | `4200` | Port to listen on. | | `--hostname ` | string | `127.0.0.1` | Hostname/interface to bind. | | `--password ` | string | none | Server password (prefer `SWARMIE_SERVER_PASSWORD`). | | `--cors ` | repeatable string | none | Additional CORS origins. | | `--no-open` | bool | false | Do not open browser automatically. | ### Notes * `swarmie web` resolves password from `--password` first, then `SWARMIE_SERVER_PASSWORD`. * `swarmie prompt` exits non-zero on startup/runtime failures and auto-denies permissions unless bypass is set. ## Environment Variables This page is grounded in: * `crates/core/src/config/loader.rs` * `crates/cli/src/cmd/server.rs` * `crates/cli/src/cmd/mod.rs` * `crates/core/src/agent/runner.rs` ### User-set `SWARMIE_*` variables These variables are read directly by Swarmie runtime code. | Variable | Used by | Config mapping / effect | | ------------------------- | ----------------------------- | ----------------------------------------------------------------------------------------- | | `SWARMIE_MODEL` | Config loader | Maps to `defaults.model` | | `SWARMIE_PROVIDER` | Config loader | Maps to `defaults.provider` | | `SWARMIE_TRUST` | Config loader | Maps to `permissions.trust_level` | | `SWARMIE_SERVER_PORT` | Config loader | Maps to `server.port` | | `SWARMIE_SERVER_PASSWORD` | Config loader + `swarmie web` | Maps to `server.password`; also used as fallback when `swarmie web --password` is not set | | `SWARMIE_LOG_LEVEL` | CLI logging init | Controls log level written under `~/.swarmie/logs/` | ### Internal agent-session variables These are injected by the runtime for spawned agents/tools. They are not normally set manually. | Variable | Producer | Purpose | | -------------------- | ------------ | --------------------------------------------------- | | `SWARMIE_AGENT_ID` | Agent runner | Current agent name | | `SWARMIE_SESSION_ID` | Agent runner | Current session ID | | `SWARMIE_DB_PATH` | Agent runner | Mailbox/session DB path for orchestration tools | | `SWARMIE_AGENTS` | Agent runner | Comma-separated agent names for orchestration tools | ### Example ```bash export SWARMIE_MODEL=sonnet export SWARMIE_PROVIDER=default export SWARMIE_LOG_LEVEL=info export SWARMIE_SERVER_PASSWORD='change-me' swarmie # or swarmie web --port 4200 ``` ### Notes * Env overrides are string-based and applied during config layer resolution. * Unknown `SWARMIE_*` names are ignored by the config loader. ## Keyboard Shortcuts Keyboard handling source: `crates/tui/src/app/update/keys.rs`. ### Global / Input Shortcuts | Shortcut | Behavior | | ----------------------- | -------------------------------------------------------------------------------------------- | | `Ctrl+C` | Interrupt streaming; or clear input; or press twice on empty input to exit. | | `Ctrl+D` | Immediate shutdown. | | `Ctrl+L` | Clear output buffer. | | `Ctrl+V` | Paste from clipboard. | | `Ctrl+Y` | Copy last assistant response to clipboard. | | `Ctrl+-` / `Ctrl+_` | Send undo operation. | | `Ctrl+O` | Toggle collapse for latest tool block. | | `Ctrl+N` | Focus next agent (multi-agent view). | | `Ctrl+P` | Focus previous agent (multi-agent view). | | `Alt+P` / `Alt+Shift+P` | Open model picker. | | `Alt+T` / `Alt+Shift+T` | Cycle thinking level. | | `Esc` | Interrupt stream, dismiss completer, open backtrack flow, or clear input depending on state. | | `Tab` | Cycle completion candidates; trigger completion; or cycle agent focus if input is empty. | | `Shift+Tab` | Cycle mode. | | `Enter` | Apply completion and submit input. | | `Shift+Enter` | Insert newline. | | `Up` / `Down` | History navigation or completer navigation. | | `Right` / `Left` | Navigate file completion directory context when file completer is active. | | `?` (empty input) | Open help overlay. | ### Permission Prompt Shortcuts When a permission request is active: | Key | Decision | | -------- | ---------------------- | | `y` | Allow once | | `n` | Deny | | `a` | Always allow (session) | | `!` | Bypass all approvals | | `Ctrl+C` | Deny | ### Behavior Notes * Modals intercept keys before normal input routing. * Permission prompts intercept keys before normal routing. * Any non-`Ctrl+C` key resets pending "press Ctrl+C again to exit" state. * Any non-`Esc` key clears pending "Esc again to clear input" state. ## Permission Responses This page is grounded in: * `crates/tui/src/app/update/keys.rs` * `crates/protocol/src/protocol.rs` * `crates/tui/src/render/overlays.rs` * `crates/cli/src/cmd/oneshot.rs` ### Interactive responses (TUI) When a permission prompt is active, these keys map to `PermissionReplyDecision`: | Key | Decision enum | Behavior | | -------- | ------------- | -------------------------------------------------------- | | `y` | `Allow` | Allow this specific tool call | | `n` | `Deny` | Deny this specific tool call | | `a` | `AlwaysAllow` | Always allow matching tool calls for the session | | `!` | `BypassAll` | Bypass all permission checks for the rest of the session | | `Ctrl+C` | `Deny` | Fast deny shortcut | The inline overlay shows `y / n / a / !` options and queued-count context. ### Queue behavior After a response: 1. Current pending permission is cleared. 2. Next queued permission becomes active (if any). 3. `Op::PermissionReply { request_id, decision }` is sent to core. Unknown keys are ignored while a permission prompt is open. ### Non-interactive (`swarmie prompt`) In oneshot/headless mode: * default: permission requests are auto-denied, with a warning to stderr * with `--dangerously-bypass-approvals`: requests are answered as `BypassAll` This behavior is implemented in `crates/cli/src/cmd/oneshot.rs`. ## Slash Commands Swarmie parses slash commands in `crates/tui/src/input/slash.rs`. ### Command List | Command | Aliases | Arguments | Effect | | ---------- | -------- | --------------- | ------------------------------------------ | | `/agent` | none | `[description]` | Spawn/configure agent from free text args. | | `/status` | none | none | Open status modal. | | `/mcps` | `/mcp` | none | Open MCP server picker/list flow. | | `/connect` | none | none | Start provider auth/connect flow. | | `/models` | `/model` | `[name]` | Open model picker or switch model. | | `/compact` | none | none | Trigger compaction flow. | | `/resume` | none | none | Open resume picker. | | `/undo` | none | none | Open transcript picker in undo mode. | | `/fork` | none | none | Open transcript picker in fork mode. | | `/new` | none | none | Start a new session. | | `/file` | none | none | Start inline file completion. | | `/skills` | none | none | Open skills picker modal. | | `/hooks` | none | none | Open hooks picker modal. | | `/export` | none | `[--json]` | Export session transcript. | | `/rename` | none | none | Open rename input modal. | | `/close` | none | none | Close currently focused sub-agent. | | `/config` | none | none | Open unified config picker. | | `/pager` | none | none | Open pager modal (conversation view). | | `/diff` | none | none | Open pager modal (git diff view). | | `/init` | none | none | Generate `AGENTS.md` instructions. | | `/exit` | `/quit` | none | Exit the TUI. | ### Validation Rules * Command names are case-insensitive. * `/export` only accepts optional `--json`. * `/rename` and `/close` reject extra arguments. * Unknown slash commands return an error. ### Related Input Forms `resolve_input()` also supports: * `!`: run shell command input path (bash action) * `/ [args]`: invoke user-available skills when command name matches a known skill * bare `exit` or `quit` (without `/`): mapped to exit command ### Examples ```text /models /models gpt-5 /export --json /agent reviewer focused on test coverage /file /init ``` ## Thinking Levels This page is grounded in: * `crates/protocol/src/lib.rs` * `crates/tui/src/app/update/keys.rs` * `crates/tui/src/app/update/core_events.rs` * `crates/provider/src/llm_api/types.rs` * `crates/provider/src/providers/openai_responses.rs` * `crates/provider/src/providers/anthropic.rs` ### Levels Protocol-level thinking levels are: * `Minimal` * `Low` * `Medium` * `High` * `Max` ### TUI cycling (`Alt+T`) `Alt+T` (and `Alt+Shift+T`) cycles this sequence: * `Off` (`None`) -> `Medium` -> `High` -> `Max` -> `Off` `Minimal` and `Low` are valid protocol values but are not part of the current keyboard cycle. If the active model does not support reasoning, TUI keeps the value unchanged and shows: * `Current model doesn't support thinking` Support is resolved from `ModelRegistry` via provider+model metadata. ### Provider mapping Swarmie maps protocol levels to provider-internal levels: * `Max` -> internal `Xhigh` * other levels map 1:1 (`Minimal/Low/Medium/High`) OpenAI Responses mapping uses effort strings: * `minimal`, `low`, `medium`, `high`, `xhigh` Anthropic mapping uses either: * adaptive mode for models that support xhigh * budget-token mode (`Low/Medium/High` budgets) * `Minimal` disables thinking for Anthropic payloads ### UI visibility * Footer shows `Thinking: ` when set. * Streaming reasoning text appears as `[thinking] ...` in output rendering. ## Common Workflows ### Explore a codebase quickly 1. Start Swarmie in project root: ```bash swarmie ``` 2. Ask for a map of modules and responsibilities. 3. Use `@` file completion to target files precisely. Example prompt: ```text Map this repo: crates, runtime loop, and where tool permissions are enforced. ``` ### Inspect and modify code 1. Ask for a concrete implementation plan. 2. Let the agent read files and propose minimal edits. 3. Review generated diffs with `/diff` and transcript with `/pager`. Useful commands: ```text /status /diff /pager ``` ### Run shell steps inline Use `!` input for shell commands from the TUI input line. ```text !rg "SwarmConfig" crates/core/src -n !ls -la crates/tui/src ``` Shell execution still goes through permission rules. ### Work with file completion (`@path`) Type `@` to start inline workspace-aware file completion. Examples: ```text open @crates/core/src/turn.rs review @docs/docs/pages/reference/cli.mdx ``` Tips: * `Tab` cycles completion candidates. * Arrow keys and `Left`/`Right` navigate directory suggestions. * `/file` starts file completion explicitly. ### Fix bugs and add tests Recommended loop: 1. Reproduce and describe the bug in one sentence. 2. Ask agent to locate root cause and affected files. 3. Request targeted fix with rationale. 4. Request/author tests near the changed behavior. 5. Re-run verification commands and inspect output. Example prompt: ```text Find why /connect OAuth providers return 401 and patch the provider resolution path with tests. ``` ### Refactor safely 1. Ask for invariants that must remain true. 2. Request small, staged edits instead of one large rewrite. 3. Use `/compact` when context gets too large. 4. Use `/undo` or `/fork` from transcript checkpoints if a direction regresses. ### Multi-agent execution 1. Spawn specialized agents with `/agent`. 2. Switch focus with `Ctrl+N` / `Ctrl+P`. 3. Close inactive agents with `/close`. Pattern that works well: * Agent A: implementation * Agent B: tests and edge cases * Agent C: docs and UX polish ### Export and resume work * `/export` writes transcript output. * `/resume` opens session picker. * `swarmie resume --last` continues the most recent workspace session. ## Custom Modes Custom modes extend built-in `default` and `plan` behavior with your own instructions and tool policies. Primary sources: * Mode registry: `crates/core/src/config/modes/registry.rs` * Mode discovery + inheritance: `crates/core/src/config/modes/resolver.rs` * Tool filtering at runtime: `crates/core/src/tools_config.rs` ### File locations and precedence For mode name ``, Swarmie resolves in this order: 1. `{cwd}/.swarmie/modes/.toml` 2. `{cwd}/.swarmie/modes//MODE.md` 3. `~/.swarmie/modes/.toml` 4. `~/.swarmie/modes//MODE.md` Custom modes cannot override built-in names (`default`, `plan`). ### TOML mode format ```toml name = "review" description = "Read-heavy review mode" extends = "plan" read_only = true max_turns = 30 allowed_tools = ["read", "grep", "glob"] developer_instructions = "Prioritize correctness and risk analysis." ``` Supported fields map to `ModeDefinition`: * `description`, `developer_instructions`, `extends` * `read_only`, `max_turns` * `excluded_tools`, `allowed_tools` ### MODE.md format `MODE.md` supports optional TOML frontmatter delimited by `+++`. Markdown body is appended to `developer_instructions`. ### Inheritance and tool filtering rules Inheritance merge behavior: * `developer_instructions`: parent then child (concatenated) * `max_turns`, description, tool lists: child overrides when set * `read_only`: inherited if either parent or child enables it Tool visibility precedence in `build_tools_config()`: 1. `excluded_tools` denylist (highest) 2. `allowed_tools` allowlist (only listed tools visible) 3. Read-only default exclusions (`bash`, `write`, `edit`, `patch`, `notebook_edit`, `undo`) 4. Agent-level allow/deny lists then apply on top ## Headless Mode Use `swarmie prompt` for non-interactive runs in scripts, CI, and automation. Primary sources: * CLI args: `crates/cli/src/cli.rs` * Runtime behavior: `crates/cli/src/cmd/oneshot.rs` ### Basic usage ```bash swarmie prompt "Summarize this repository" ``` Prompt input rules: * `swarmie prompt "..."`: uses argument text * `swarmie prompt -`: reads stdin * `swarmie prompt` with no arg: reads stdin ### Key flags ```bash swarmie prompt "Check TODOs" --model sonnet --provider openai swarmie prompt - --json < prompt.txt swarmie prompt "Apply patch" --dangerously-bypass-approvals swarmie prompt "Analyze" -C /path/to/workspace ``` Behavior details: * `--json` emits JSONL with event debug payloads (`{"event":"..."}`) * Without `--json`, assistant text is streamed as plain output * In non-interactive mode, permission requests are denied unless `--dangerously-bypass-approvals` is set ### CI/CD example ```yaml name: swarmie-headless on: [workflow_dispatch] jobs: run-agent: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Run Swarmie prompt run: | cat <<'PROMPT' | swarmie prompt - --json > swarmie-events.jsonl Review crates/core/src for error handling gaps and propose fixes. PROMPT - name: Upload events uses: actions/upload-artifact@v4 with: name: swarmie-events path: swarmie-events.jsonl ``` Only use `--dangerously-bypass-approvals` in trusted, controlled environments. ## MCP Servers Swarmie supports MCP transports over `stdio`, `http`, and `sse`. Primary sources: * MCP config/transport types: `crates/mcp/src/config.rs` * MCP lifecycle manager: `crates/mcp/src/manager.rs` * Config resolution/validation: `crates/core/src/config/resolve.rs` * TUI `/mcps` flow: `crates/tui/src/input/slash.rs`, `crates/tui/src/app/update/commands.rs`, `crates/tui/src/app/update/modals.rs` * Runtime toggle persistence: `crates/core/src/submission/op_handlers.rs` ### Configure servers ```toml [mcp.servers.github] enabled = true type = "http" url = "https://api.githubcopilot.com/mcp/" headers = { Authorization = "Bearer ${GITHUB_TOKEN}" } [mcp.servers.local_db] type = "stdio" command = "npx" args = ["-y", "@bytebase/dbhub"] env = { DB_URL = "sqlite:///tmp/app.db" } [mcp.servers.events] type = "sse" url = "https://example.com/events" ``` Validation rules: * `stdio` requires `command` * `http` requires `url` * `sse` requires `url` ### Manage MCP servers in TUI 1. Run `/mcps` (or `/mcp`) to open the picker. 2. Swarmie dispatches `Op::McpList` and renders grouped Running/Stopped entries. 3. Press `Enter` to toggle selected server (`Op::McpToggle`). 4. Core starts/stops the server and emits `Event::McpServerToggled`. Enabled state is persisted for future sessions via config update when possible. ### Troubleshooting checklist * `cannot toggle MCP server ... MCP is not configured`: no `[mcp.servers.*]` resolved for this session. * `failed to start/stop MCP server ...`: transport init/shutdown failure; verify command/url/headers. * Toggle works but restart loses state: config persist warning (`mcp.config_persist_failed`) indicates a write failure. * Tool list empty for a running server: MCP handshake succeeded but `list_tools` failed or returned no tools. ## Multi-Agent Swarmie multi-agent orchestration is implemented in core tooling and surfaced in the TUI. Primary sources: * Agent tools and lifecycle: `crates/core/src/orchestration_tools/spawn_worker.rs` * Tool registration: `crates/core/src/orchestration_tools/mod.rs` * Agent focus + keybindings: `crates/tui/src/app/update/keys.rs` * `/agent` slash behavior: `crates/tui/src/input/slash.rs`, `crates/tui/src/app/update/commands.rs` * Session orchestration loop: `crates/core/src/orchestrator.rs` ### Orchestration workflow A typical workflow: 1. Ask the main agent to delegate work with orchestration tools (`spawn_agent`, `send_input`, `wait`, `close_agent`, `resume_agent`, `merge_agents`). 2. Let sub-agents run in parallel on scoped tasks. 3. Check status in TUI and switch focus between agents. 4. Wait for completion and merge/close when done. `/agent` in the TUI is for listing or switching focused agent context. It does not itself spawn workers. ### Switching and focusing agents Use keyboard focus controls while in multi-agent state: * `Ctrl+N`: focus next agent * `Ctrl+P`: focus previous agent * `Tab` with empty input: cycle agent focus When focus changes, the output pane is rebuilt from that agent's buffered events and the footer agent label updates. ### Mailbox and inter-agent messaging Swarmie includes a `mailbox` tool (`crates/core/src/orchestration_tools/mailbox.rs`) with actions: * `send`, `reply`, `broadcast` * `inbox`, `outbox`, `thread` Use this for structured coordination between specialist agents instead of mixing all updates into one stream. ### Worktree isolation When running in a git repo, sub-agents can get isolated worktrees (`crates/core/src/worktree/`). Worktree creation is gated in `should_create_worktree()` and currently requires: * repository root available * more than one active agent * write-capable tool set (`write`/`edit`/`patch`, or empty allowlist) On close, Swarmie attempts auto-commit + worktree cleanup. ## Project Instructions Swarmie supports repository-scoped instructions through `AGENTS.md`. Primary sources: * AGENTS.md load/injection: `crates/core/src/submission/component_init.rs` * Instruction formatting utilities: `crates/core/src/prompt/messaging.rs` * `/init` slash command: `crates/tui/src/input/slash.rs`, `crates/tui/src/app/update/commands.rs` * `/init` prompt template: `crates/tui/prompt_for_init_command.md` ### How AGENTS.md is applied At session startup, core checks `{cwd}/AGENTS.md`. If present and non-empty, Swarmie injects its contents as a developer message early in session state initialization. This makes repository instructions part of the model context from turn one. ### Creating AGENTS.md with `/init` Run `/init` in the TUI to generate a repository-specific instruction document. Behavior: 1. If `AGENTS.md` already exists in current directory, Swarmie shows an informational message and does nothing. 2. If missing, Swarmie sends an embedded generation prompt as a user message. 3. The generation prompt is not echoed as raw slash-command output; the UI displays `Generating AGENTS.md...`. ### Authoring guidelines Keep `AGENTS.md` concrete and enforceable: * Describe workspace structure (`crates/*`, `specs/`, docs paths) * List mandatory verification commands * Define code-style and testing expectations * Declare constraints (no backward-compat shims, required specs, review checklist) Prefer short, high-signal rules over long narrative text. ## Web Server Use `swarmie web` to run the HTTP frontend (REST + SSE). Primary sources: * CLI web command: `crates/cli/src/cmd/server.rs` * Server config and middleware: `crates/server/src/lib.rs` * Routes: `crates/server/src/routes/mod.rs` * Basic auth middleware: `crates/server/src/auth.rs` ### Start server ```bash swarmie web swarmie web --port 8080 --hostname 0.0.0.0 swarmie web --password "secret" --cors https://app.example.com --no-open ``` Password resolution: 1. `--password` 2. `SWARMIE_SERVER_PASSWORD` If password is set, HTTP Basic auth is enabled with username `swarmie`. ### API surface Core endpoints: * Health: `GET /health` * Sessions: `GET/POST /sessions`, `GET/DELETE /sessions/{id}` * Messaging: `POST /sessions/{id}/messages`, `POST /sessions/{id}/abort` * Session controls: mode/model/agent/objective/plan/undo/compact endpoints * Events: `GET /events` and `GET /sessions/{id}/events` (SSE) ### CORS behavior Default allowed origins include local dev hosts: * `http://localhost:*` * `http://127.0.0.1:*` Use `--cors ` repeatedly for additional origins. ### Server config notes (`[server]`) TOML schema for `[server]` exists in `crates/core/src/config/toml_types.rs` (`port`, `hostname`, `password`, `cors`). Current `swarmie web` runtime uses CLI/env wiring from `crates/cli/src/cmd/server.rs`; it does not currently read `[server]` values from resolved runtime config. ## Authentication Swarmie supports OAuth and API-key authentication. The recommended starting flow is `/connect` inside the TUI. ### OAuth via `/connect` Run: ```text /connect ``` OAuth-capable providers in the built-in wizard include: * `openai-codex` * `anthropic` (subscription flow) * `github-copilot` * `google-gemini-cli` * `google-antigravity` After successful OAuth, Swarmie persists credentials and updates default provider/model selections. ### API key setup You can configure providers in `~/.swarmie/config.toml` (or project `.swarmie/config.toml`). Example: ```toml [defaults] provider = "anthropic" model = "sonnet" [providers.anthropic] type = "anthropic" authentication = "api_key" api_key_env = "ANTHROPIC_API_KEY" ``` OpenAI-compatible example: ```toml [defaults] provider = "openai" model = "gpt-4o" [providers.openai] type = "openai-compatible" authentication = "api_key" api_key_env = "OPENAI_API_KEY" base_url = "https://api.openai.com/v1" ``` Local Ollama example: ```toml [defaults] provider = "ollama" model = "llama3.1" [providers.ollama] type = "openai-compatible" authentication = "api_key" base_url = "http://localhost:11434/v1" ``` ### Credential storage Credential storage mode is configurable via: ```toml [auth] store = "auto" # auto | keyring | file | ephemeral ``` * `auto` tries OS keyring first and falls back to file storage. * File mode uses `~/.swarmie/auth.json`. ### Resolution order At runtime, API keys/tokens are resolved in this precedence: 1. Explicit override 2. Stored OAuth/Bearer credentials 3. Provider environment variables 4. Stored API key credentials 5. External CLI credential detection (Claude/Codex/Gemini/Kimi) 6. Canonical provider-alias fallback ### Switching providers/models during a session * `Alt+P` opens the model picker * `/models` opens model picker (or `/models ` to set directly) Use `/status` to verify the currently active model/provider. ## Installation Swarmie ships as the `swarmie` binary from the `crates/cli` crate. ### Requirements * Rust toolchain with Cargo (stable recommended) * A terminal environment (TUI runs in terminal) * Network access for remote model providers ### Install from source (this repository) ```bash git clone https://github.com/j0nl1/swarmie.git cd swarmie cargo install --path crates/cli ``` ### Install from git directly ```bash cargo install --git https://github.com/j0nl1/swarmie swarmie ``` ### Verify installation ```bash swarmie --help ``` You should see top-level commands: * `prompt` (alias `e`) * `resume` * `web` If you run `swarmie` with no subcommand, it starts the interactive TUI REPL. ### First launch behavior On first run in a project without `.swarmie/`, Swarmie asks whether you trust the workspace. If accepted, it creates: * `.swarmie/config.toml` * `.swarmie/.gitignore` * `.swarmie/modes/` The generated `config.toml` is minimal and expects provider setup via `/connect`. ### Config file locations Swarmie merges config layers in this order (lowest to highest precedence): 1. `/etc/swarmie/config.toml` 2. `~/.swarmie/config.toml` 3. `/.swarmie/config.toml` (only for trusted workspaces) 4. CLI `--config key=value` overrides 5. `SWARMIE_*` environment overrides If no provider/model can be resolved at startup, use `/connect` in the TUI to set one up. ## Quickstart This guide gets you from first launch to your first completed turn. ### 1. Launch the TUI ```bash swarmie ``` If this is a new workspace, accept trust to initialize `.swarmie/`. ### 2. Connect a provider In the prompt, run: ```text /connect ``` Choose an OAuth provider (for example `openai-codex`, `anthropic`, `github-copilot`) or API-key setup (`anthropic`, `openai`, `ollama`). ### 3. Send your first message Type normal text and press `Enter`: ```text Explain the architecture of this repository. ``` Swarmie streams assistant output as it arrives. ### 4. Respond to permission prompts When a tool action needs approval, use: * `y` allow once * `n` deny * `a` always allow this session rule * `!` bypass all approvals ### 5. Useful slash commands * `/status` open runtime status * `/models` open model picker * `/config` open configuration modal * `/skills` open skills picker * `/hooks` open hooks picker * `/resume` open session picker * `/new` start a fresh session * `/exit` or `/quit` exit Type `/` to open command completion. ### Keyboard quick reference * `Shift+Enter` newline * `Ctrl+C` cancel/clear/exit flow * `Ctrl+D` exit * `Ctrl+L` clear output * `Ctrl+O` collapse/expand tool blocks * `Ctrl+N` / `Ctrl+P` switch active agent * `Ctrl+Y` copy last assistant response * `Alt+P` model picker * `Alt+T` cycle thinking level ### One-shot mode (non-interactive) ```bash swarmie prompt "Summarize the project in 5 bullets" ``` Read prompt from stdin: ```bash cat request.txt | swarmie prompt - ``` Emit JSONL-ish event lines: ```bash swarmie prompt "Describe current dir" --json ``` ### Resume previous sessions ```bash swarmie resume swarmie resume --last ``` ## Context Window Context usage is tracked by `ContextWindowTracker` (`crates/core/src/context_window.rs`) and emitted to frontends via `Event::ContextUpdate` (`crates/protocol/src/protocol.rs`). ### Token Accounting After each completed turn, core updates cumulative token usage (`crates/core/src/submission/turn_lifecycle.rs`): * `input_tokens` * `output_tokens` The tracker computes: * `used_tokens` (`input + output`) * `max_tokens` (model context limit) * `usage_ratio` (`used / max`, clamped) ### Model Limits Limits are resolved from provider model metadata (`swarmie_provider::models::get_model_info`): * context window fallback: `200_000` * max output tokens fallback: `16_384` ### Auto-Compaction Before a new user turn, core checks `needs_auto_compact()` (`crates/core/src/submission/op_handlers.rs`). Auto-compaction threshold is: ```text threshold = context_window - max_output_tokens - safety_margin safety_margin = max(context_window / 10, 4096) ``` If usage crosses threshold, core summarizes older history and emits `Event::ContextCompacted` (`crates/core/src/handlers/compaction.rs`). ### Manual Compaction Protocol supports `Op::Compact`, handled by `run_manual_compaction()` in `crates/core/src/handlers/compaction.rs`. In current TUI slash handling (`crates/tui/src/app/update/commands.rs`), `/compact` reports current context usage text; it does not directly dispatch `Op::Compact`. ### Related Budgeting Context window pressure (tokens) and session budget (USD) are separate systems: * context controls prompt/token capacity * budget controls turn admission by cost (`hard_cap`, `soft_cap`, warnings) ## How Swarmie Works Swarmie is event-driven: frontends send `Op` messages to core, and core emits `Event` messages back. ### Core model * Frontend -> core: `Op` (for example `UserMessage`, `SwitchModel`, `Shutdown`) * Core -> frontend: `Event` (for example `TextDelta`, `ToolCallStart`, `TurnComplete`, `Idle`) `SwarmCore` starts a background session task and relays events to subscribers (TUI, server, or headless harnesses). ### Session task states The session loop has two modes: * `Idle`: waiting for operations * `Active turn`: running an agentic turn and multiplexing turn output + incoming ops While a turn is active, some ops execute immediately (`Interrupt`, `PermissionReply`, `Shutdown`), while others are deferred until idle. ### Turn loop lifecycle For each user message: 1. Build a `TurnContext` snapshot (messages, tools, mode, model/provider, limits). 2. Call provider streaming API. 3. Emit deltas (`TextDelta`, `ThinkingDelta`, tool-call start/input events). 4. Append assistant response to local message history. 5. If stop reason is tool-use, execute tools through registry, run permission checks/hooks, append tool results, and continue. 6. If end-turn condition is reached, emit `TurnComplete`, update context usage, and emit `Idle`. ### Tools and permissions Tool calls are executed through the shared tool registry. Permission-gated calls emit `PermissionRequest`; user responses (`y/n/a/!`) map to `PermissionReplyDecision`. ### Context and compaction Core tracks token usage and emits `ContextUpdate` during and after turns. Compaction can run with a separate compact model when configured. ### Persistence and resume Core records rollout/state metadata as turns complete. Sessions can be resumed from persisted records via `swarmie resume` or server APIs. ### Modes, thinking, and hooks * Mode is included in prompt assembly and can be switched at runtime. * Thinking level is part of request shaping. * Hooks can fire across lifecycle events (for example prompt submit, tool phases, config changes). ## Modes Modes are behavioral presets that inject developer instructions and can adjust permissions/tool visibility. Source modules: * model and validation: `crates/core/src/config/modes/mod.rs` * registry and built-ins: `crates/core/src/config/modes/registry.rs` * custom mode file loading: `crates/core/src/config/modes/resolver.rs` * runtime switch handler: `crates/core/src/handlers/mode.rs` ### Built-in Modes `ModeRegistry::new()` includes two built-ins: * `default`: full access, autonomous execution (`read_only = false`) * `plan`: read-only exploration/planning (`read_only = true`) ### Runtime Switching Mode switch flow (`Op::ModeSwitch`): 1. Core resolves mode from registry. 2. Core builds a developer message using: * `{mode}` * mode `developer_instructions` 3. Core appends that developer message to conversation history. 4. Core emits `Event::ModeSwitched`. In the TUI, `Shift+Tab` cycles visible modes (`crates/tui/src/app/update/keys.rs`). ### Custom Modes Custom modes are loaded from `.swarmie/modes` (project + home), first-match precedence: 1. `{cwd}/.swarmie/modes/.toml` 2. `{cwd}/.swarmie/modes//MODE.md` 3. `~/.swarmie/modes/.toml` 4. `~/.swarmie/modes//MODE.md` #### File formats * TOML file: `.toml` * Markdown mode file: `MODE.md` with TOML frontmatter delimited by `+++` `MODE.md` body is appended to `developer_instructions`. #### Fields `ModeDefinition` supports: * `name` * `description` * `developer_instructions` * `read_only` * `extends` * `max_turns` * `excluded_tools` * `allowed_tools` `extends` supports inheritance; child mode overrides parent fields, and developer instructions are concatenated. ### Name Rules Mode names must match: `[a-z][a-z0-9_-]*`. ## Permissions Swarmie uses rule-based permission evaluation with trust presets. Source of truth: * Presets: `crates/permissions/src/preset.rs` * Rule syntax: `crates/permissions/src/rule.rs` * Evaluation precedence: `crates/permissions/src/evaluator.rs` ### Trust Presets #### `safe` Read-only workflow with sandboxed web tools. * Allow: `Read`, `Glob`, `Grep`, `FileSearch` * Sandbox: `WebFetch`, `WebSearch` * Deny: `Bash`, `Write`, `Edit`, `NotebookEdit`, `Patch` #### `standard` (default) Recommended default for day-to-day coding. * Allow: file tools + `Undo` + safe bash patterns * `Bash(cargo *)` * `Bash(npm run *)` * `Bash(git status)` * `Bash(git diff *)` * `Bash(git log *)` * `Bash(git branch *)` * Sandbox: `WebFetch`, `WebSearch` * Ask: risky git operations (`git push`, `git commit`, `git checkout`, `git rebase`, `git merge`, `git reset`) * Deny: `Bash(rm -rf *)` #### `full` All core tools allowed natively, no sandbox/ask/deny rules. ### Rule Syntax Rules are strings in one of these forms: * `ToolName` * `ToolName(specifier)` Examples: * `Bash(npm run *)` * `Read(./src/**)` * `WebFetch(domain:*.example.com)` Matching behavior: * Tool name is case-insensitive. * No specifier means "all calls to this tool". * Specifiers use glob/prefix matching. * `domain:` specifiers match URL hostnames for `WebFetch`. ### Evaluation Precedence `PermissionEvaluator::evaluate()` applies rules in this order: 1. Bypass mode (`bypass=true`) -> allow. 2. Read-only mode + mutating tool -> reject. 3. Deny rules (agent overrides, then global) -> reject. 4. Session allows -> allow. 5. Agent non-deny rules (`ask` > `sandbox` > `allow`). 6. Global non-deny rules (`ask` > `sandbox` > `allow`). 7. Default -> escalate (ask user). ### Interactive Permission Responses When a permission prompt appears in TUI (`pending_permission`): * `y` -> allow once * `n` -> deny * `a` -> always allow this session rule * `!` -> bypass all checks for the session * `Ctrl+C` -> deny These are handled in `crates/tui/src/app/update/keys.rs`. ## Sessions Session lifecycle operations are protocol-level `Op` variants (`crates/protocol/src/protocol.rs`) and are handled in core session handlers (`crates/core/src/handlers/session.rs`, `crates/core/src/handlers/mode.rs`). ### Session Operations | Operation | Op | Core behavior | Primary event | | --------------------------- | ------------------- | ----------------------------------------------------------------------------------------------- | ----------------------- | | Resume | `Op::ResumeSession` | Loads messages from persisted rollout and restores session source metadata. | `Event::SessionResumed` | | Rewind (conversation only) | `Op::RewindTo` | Truncates messages to selected user turn; does not restore files. | `Event::RewindComplete` | | Undo (conversation + files) | `Op::UndoTo` | Attempts persistence backtrack with file restore; falls back to message-only rewind on failure. | `Event::UndoToComplete` | | Fork | `Op::ForkSession` | Truncates to selected turn, creates a new `SessionId`, and continues in new session lineage. | `Event::SessionForked` | | New session | `Op::NewSession` | Resets in-memory state and emits fresh `SessionReady` + `AvailableSkills`. | `Event::SessionReady` | | Rename | `Op::RenameSession` | Updates active session title. | `Event::SessionRenamed` | ### TUI Entry Points From slash commands (`crates/tui/src/app/update/commands.rs` and `crates/tui/src/app/update/modals.rs`): * `/resume` opens resume picker and eventually sends `Op::ResumeSession`. * Double-`Esc` opens transcript picker in backtrack mode and sends `Op::RewindTo`. * `/undo` opens transcript picker in undo mode and sends `Op::UndoTo`. * `/fork` opens transcript picker in fork mode and sends `Op::ForkSession`. * `/new` resets UI state and sends `Op::NewSession`. * `/rename` opens rename modal and sends `Op::RenameSession`. ### Export `/export` and `/export --json` are TUI-side transcript exports (`crates/tui/src/app/update/commands.rs`): * reads rollout items * extracts message history * writes `session-.md` or `session-.json` in current working directory ## Built-in Tools Swarmie registers its built-in tools in `crates/tools/src/registry.rs` via `core_registry()`. The current core registry includes **13** tools: | Tool | Purpose | Key Parameters | | --------------- | ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------ | | `bash` | Run shell commands in the workspace. | `command` (required), `timeout`, `description` | | `read` | Read file contents (text or image). | `file_path` (required), `offset`, `limit` | | `glob` | Find files by glob pattern. | `pattern` (required), `path` | | `grep` | Search file content by pattern. | `pattern` (required), `path`, `output_mode`, `glob`, `type`, `-i`, `-A/-B/-C`, `-n`, `multiline`, `head_limit`, `offset` | | `write` | Write/replace file contents. | `file_path` (required), `content` (required) | | `edit` | Replace a string in a file. | `file_path`, `old_string`, `new_string` (required), `replace_all` | | `web_fetch` | Fetch a URL and return content. | `url` (required), `prompt` | | `web_search` | Search the web. | `query` (required), `allowed_domains`, `blocked_domains` | | `notebook_edit` | Modify Jupyter notebook cells. | `notebook_path`, `new_source` (required), `cell_number`, `cell_type`, `edit_mode` | | `patch` | Apply unified diff patches. | `patch` (required), `file_path` | | `file_search` | Fuzzy filename search in workspace. | `query` (required), `limit` | | `memory_write` | Append project memory notes. | `content` (required), `file` | | `undo` | Revert the latest ghost snapshot. | `rollout_path` (required) | ### Examples #### Read a file segment ```json {"file_path":"/abs/path/src/main.rs","offset":120,"limit":80} ``` #### Search text with context lines ```json {"pattern":"SwarmConfig","path":".","output_mode":"content","-C":2} ``` #### Apply an edit ```json {"file_path":"/abs/path/src/lib.rs","old_string":"foo","new_string":"bar","replace_all":false} ``` #### Fetch from the web ```json {"url":"https://example.com/docs","prompt":"Extract the API authentication section"} ``` ### Notes * Input validation happens per tool implementation in `crates/tools/src/*.rs`. * Most filesystem tools require paths to stay within the current workspace root. * Tool usage still goes through Swarmie's permission evaluator before execution. ## Agents Per-agent profiles are configured under `[agents.]` in `config.toml`. ### Agent Profile Fields Defined in `crates/core/src/config/toml_types.rs` and resolved in `crates/core/src/config/resolve.rs`. | Field | Type | Description | | -------------- | ------------- | ----------------------------------------------------------------- | | `model` | string | Agent model override. Falls back to `[defaults].model`. | | `provider` | string | Agent provider override. Falls back to `[defaults].provider`. | | `instructions` | string | Extra instructions for the agent. | | `mode` | string | Agent mode (`default` if not set). | | `tools_allow` | array(string) | Allowlist rules for tools. | | `tools_deny` | array(string) | Denylist rules for tools. | | `mcp_servers` | array(string) | MCP servers available to this agent. | | `max_turns` | integer | Turn cap for this agent. Falls back to session default max turns. | | `cwd` | path | Agent working directory override. | | `worktree` | bool | Agent requests worktree isolation in profile config. | ### Example ```toml [agents.reviewer] model = "claude-sonnet-4-5-20250929" provider = "anthropic" instructions = "Review code for correctness, risk, and tests" mode = "default" tools_allow = ["Read", "Glob", "Grep"] tools_deny = ["Bash(rm -rf *)"] mcp_servers = ["github", "docs"] max_turns = 40 cwd = "/opt/agentbox/projects/swarm" worktree = true ``` ### How Defaults Are Applied `resolve_toml_agents()` applies defaults when fields are omitted: * `model` and `provider` inherit from `[defaults]`. * `mode` inherits from `[defaults].mode` then falls back to `default`. * `max_turns` inherits from resolved session default max turns. * `tools_allow`/`tools_deny` default to empty lists. * `worktree` defaults to `false`. ### Worktree Isolation Behavior Runtime worktree creation for spawned agents is controlled by orchestration code in `crates/core/src/orchestration_tools/spawn_worker.rs` plus git helpers in `crates/core/src/worktree/`. `should_create_worktree()` requires: * Repository root detected. * More than one active agent. * Agent has write capability (empty allowlist or includes write/edit/patch). When enabled, Swarmie creates and locks a git worktree for the agent, and cleanup on close includes optional auto-commit and worktree prune. ### Agent Selection in Runtime Agent profile data becomes `AgentProfile` (`crates/core/src/config/types.rs`) and is consumed by orchestration/session runtime for per-agent behavior. ## Budget Swarmie budget settings come from `[budget]` in `config.toml` (`crates/core/src/config/toml_types.rs`) and are enforced per session by `BudgetEvaluator` (`crates/core/src/budget/evaluator.rs`). ### Config Shape ```toml [budget] hard_cap = 25.0 soft_cap = 20.0 warning_threshold_pct = 75 ``` | Field | Type | Meaning | | ----------------------- | --------------- | ------------------------------------------------------------------------------------------- | | `hard_cap` | float (USD) | Hard stop threshold. New turns are blocked once projected/recorded cost reaches this value. | | `soft_cap` | float (USD) | Warning-only threshold. Turns continue but emit warning state once crossed. | | `warning_threshold_pct` | integer (0-100) | Percent warning threshold, evaluated against `hard_cap`. | ### Runtime Behavior Budget decisions are evaluated before each user turn (`crates/core/src/submission/op_handlers.rs`): * `Allow`: turn proceeds. * `AllowWithWarning`: turn proceeds and emits a warning message. * `Block`: turn is rejected with `budget.session_hard_cap_exceeded`. After each completed turn, cumulative session cost is recorded from `Event::TurnComplete.cost` (`crates/core/src/submission/turn_lifecycle.rs`). ### Important Details * Soft-cap and warning thresholds do not block turns. * Hard cap is terminal for the session budget state unless explicitly overridden in code. * If only `soft_cap` is set (no `hard_cap`), soft-cap warnings still work. * If no `[budget]` section is set, budget enforcement is disabled. ### Example Workflow 1. Session starts at `$0.00`. 2. Cost crosses `warning_threshold_pct` or `soft_cap` and Swarmie emits a warning. 3. Session cost reaches `hard_cap` and subsequent turns are blocked. ## Config Reference This reference maps directly to `crates/core/src/config/toml_types.rs`. ### Top-Level Structure ```toml [defaults] [memory] [budget] [plugins] [permissions] [sandbox] [hooks] [session] [tui] [tui.colors] [mcp] [server] [providers.] [agents.] [wasm_tools.] [mcp.servers.] ``` ### `[defaults]` | Field | Type | Description | | ---------- | ------ | ---------------------- | | `model` | string | Default model id. | | `provider` | string | Default provider name. | | `mode` | string | Default runtime mode. | ### `[memory]` | Field | Type | Description | | ------------------ | ------- | -------------------------------------- | | `enabled` | bool | Enable project memory injection. | | `max_prompt_lines` | integer | Max memory lines injected into prompt. | ### `[budget]` | Field | Type | Description | | ----------------------- | ------- | ----------------------------- | | `hard_cap` | float | Hard budget cap. | | `soft_cap` | float | Soft warning cap. | | `warning_threshold_pct` | integer | Warning threshold percentage. | ### `[plugins]` | Field | Type | Description | | ------ | ----------- | ----------------------------- | | `dirs` | array(path) | Plugin discovery directories. | ### `[providers.]` | Field | Type | Description | | ---------------- | ------ | --------------------------------------------------- | | `type` | string | Provider type identifier. | | `authentication` | string | Authentication mode (for example API key or OAuth). | | `api_key_env` | string | Env var name for API key lookup. | | `base_url` | string | Provider API base URL. | ### `[agents.]` | Field | Type | Description | | -------------- | ------------- | ---------------------------------------- | | `model` | string | Agent-specific model override. | | `provider` | string | Agent-specific provider override. | | `instructions` | string | Agent-specific instruction override. | | `mode` | string | Agent runtime mode. | | `tools_allow` | array(string) | Tool allowlist rules. | | `tools_deny` | array(string) | Tool denylist rules. | | `mcp_servers` | array(string) | MCP server names enabled for this agent. | | `max_turns` | integer | Per-agent max turns. | | `cwd` | path | Agent working directory override. | | `worktree` | bool | Enable isolated worktree for this agent. | ### `[permissions]` | Field | Type | Description | | -------------- | ------------- | ----------------------------------------------- | | `trust_level` | string | Trust level selector. | | `preset` | string | Permission preset (`safe`, `standard`, `full`). | | `allow` | array(string) | Explicit allow rules. | | `sandbox` | array(string) | Sandbox rules. | | `ask` | array(string) | Escalation rules. | | `deny` | array(string) | Deny rules. | | `default_mode` | string | Default policy mode for unresolved calls. | ### `[sandbox]` | Field | Type | Description | | ----------------- | ------------- | ------------------------- | | `allowed_paths` | array(string) | Allowed filesystem roots. | | `denied_paths` | array(string) | Denied filesystem roots. | | `allowed_domains` | array(string) | Allowed network domains. | ### `[hooks]` Each event key takes `[{ matcher = "...", hooks = [ ... ] }]`. Supported event keys: * `session_start` * `user_prompt_submit` * `permission_request` * `pre_tool_use` * `post_tool_use` * `post_tool_use_failure` * `stop` * `pre_compact` * `config_change` * `session_end` * `subagent_start` * `subagent_stop` * `teammate_idle` * `teammate_idle_warning` * `task_completed` * `notification` * `stall_detected` Hook handler variants: * `type = "command"`: `command`, `timeout`, `async`, `status_message` * `type = "prompt"`: `prompt`, `model`, `provider`, `timeout` * `type = "agent"`: `prompt`, `model`, `provider`, `tools`, `max_turns`, `timeout` ### `[session]` | Field | Type | Description | | ------------------------ | ------- | -------------------------------- | | `max_consecutive_errors` | integer | Stop after N consecutive errors. | | `max_total_errors` | integer | Stop after N total errors. | | `max_turns` | integer | Session turn cap. | | `liveness` | table | Optional liveness controls. | #### `[session.liveness]` | Field | Type | Description | | -------------------------- | ------- | ---------------------------------- | | `enabled` | bool | Enable liveness monitoring. | | `idle_nudge_after_secs` | integer | Seconds before first idle nudge. | | `idle_nudge_interval_secs` | integer | Interval between nudges. | | `max_nudges` | integer | Max nudges before escalation. | | `idle_warn_after_secs` | integer | Seconds before idle warning event. | | `stall_timeout_secs` | integer | Stall timeout threshold. | | `auto_interrupt_stalled` | bool | Auto-interrupt stalled runs. | ### `[tui]` | Field | Type | Description | | ---------------- | ------- | ---------------------- | | `theme` | string | Theme name. | | `frame_rate_cap` | integer | Max frame rate cap. | | `colors` | table | Theme color overrides. | #### `[tui.colors]` Color keys: `primary`, `success`, `error`, `warning`, `muted`, `accent`, `text`, `bg_subtle`, `border`, `spinner`, `text_weak`, `text_strong`, `border_focus`, `info`, `background`. ### `[wasm_tools.]` | Field | Type | Description | | -------------------- | --------------------- | --------------------------------- | | `path` | string | WASM artifact path. | | `capabilities` | array(string) | Capability list. | | `limits` | table | Resource limits. | | `secrets` | array(string) | Secret names injected at runtime. | | `workspace_prefixes` | array(string) | Allowed workspace prefixes. | | `endpoint_allowlist` | array(string) | Allowed outbound endpoints. | | `tool_aliases` | table(string->string) | Alias names for this tool. | #### `[wasm_tools..limits]` | Field | | ------------------------ | | `max_memory_bytes` | | `fuel_limit` | | `execution_timeout_secs` | | `max_log_entries` | | `max_http_requests` | | `max_tool_invocations` | | `max_file_read_bytes` | ### `[mcp]` and `[mcp.servers.]` `[mcp]` contains the `servers` map. Server fields: | Field | Type | Description | | --------- | --------------------- | ---------------------------- | | `enabled` | bool | Enable server. | | `type` | string | Transport type. | | `command` | string | Command for stdio transport. | | `args` | array(string) | Command arguments. | | `url` | string | URL for HTTP transport. | | `headers` | table(string->string) | HTTP headers. | | `env` | table(string->string) | Environment variables. | ### `[server]` | Field | Type | Description | | ------------------------- | ------------- | ---------------------------------------- | | `port` | integer | Web server port. | | `hostname` | string | Bind address/hostname. | | `password` | string | Server password. | | `cors` | array(string) | Additional CORS origins. | | `permission_timeout_secs` | integer | Permission response timeout in web mode. | ## Hooks Swarmie hooks attach custom handlers to lifecycle events. Primary source: `crates/core/src/hooks/config.rs`. ### Event Types `HookEventType` currently defines 17 event kinds: * `SessionStart` * `UserPromptSubmit` * `PermissionRequest` * `PreToolUse` * `PostToolUse` * `PostToolUseFailure` * `Stop` * `PreCompact` * `ConfigChange` * `SessionEnd` * `SubagentStart` * `SubagentStop` * `TeammateIdle` * `TeammateIdleWarning` * `TaskCompleted` * `Notification` * `StallDetected` In TOML these map to snake\_case keys under `[hooks]` via `crates/core/src/config/toml_types.rs`. ### Matcher Groups Each event can contain matcher groups: ```toml [[hooks.pre_tool_use]] matcher = "^(bash|write)$" hooks = [ { type = "command", command = "echo pre-tool", timeout = 10 } ] ``` `MatcherGroup` fields: | Field | Type | Notes | | --------- | ------ | ----------------------------------------------------------- | | `matcher` | string | Regex-like matcher; omitted means match all for that event. | | `hooks` | array | Handler list executed for matched events. | ### Handler Types `HookHandler` supports three variants: #### `type = "command"` | Field | Type | Required | | ---------------- | ------- | -------- | | `command` | string | yes | | `timeout` | integer | no | | `async` | bool | no | | `status_message` | string | no | #### `type = "prompt"` | Field | Type | Required | | ---------- | ------- | -------- | | `prompt` | string | yes | | `model` | string | no | | `provider` | string | no | | `timeout` | integer | no | #### `type = "agent"` | Field | Type | Required | | ----------- | ------------- | -------- | | `prompt` | string | yes | | `model` | string | no | | `provider` | string | no | | `tools` | array(string) | no | | `max_turns` | integer | no | | `timeout` | integer | no | Default timeouts from `HookHandler`: * command handlers: 30 seconds * prompt/agent handlers: 60 seconds ### Example ```toml [[hooks.session_start]] hooks = [ { type = "command", command = "echo session started", status_message = "Running startup hook" } ] [[hooks.permission_request]] matcher = "^Bash" hooks = [ { type = "prompt", prompt = "Summarize risk of this request", timeout = 20 } ] [[hooks.stall_detected]] hooks = [ { type = "agent", prompt = "Diagnose stall and propose next action", max_turns = 2 } ] ``` ## Config Overview Swarmie loads layered TOML configuration and merges it into one runtime config. ### Config File Locations Layer order (lowest to highest precedence): 1. System: `/etc/swarmie/config.toml` 2. User: `~/.swarmie/config.toml` 3. Project: `/.swarmie/config.toml` (only if workspace is trusted) 4. CLI `--config key=value` overrides 5. `SWARMIE_*` environment overrides This order is implemented by `ConfigLayerStack::load()` in `crates/core/src/config/loader.rs`. ### Merge Behavior When layers merge: * Tables merge recursively. * Arrays replace whole arrays. * Scalars replace previous values. ### CLI Overrides Any command that supports `--config key=value` can override nested config with dotted keys. Examples: ```bash swarmie prompt "hello" --config defaults.model=sonnet swarmie prompt "hello" --config permissions.default_mode=ask ``` Override values are parsed as TOML scalars where possible (`true`, numbers, etc.), otherwise used as strings. ### Profiles The CLI schema includes `--profile ` on `swarmie prompt`. Current source notes: * The flag exists in `crates/cli/src/cli.rs`. * Profile-specific resolution is not wired in `crates/cli/src/config_builder.rs` yet. Use explicit `--config` overrides for deterministic behavior until profile resolution is integrated. ### Main Sections Top-level TOML sections supported by `ConfigToml` (`crates/core/src/config/toml_types.rs`): * `[defaults]` * `[memory]` * `[budget]` * `[plugins]` * `[providers.]` * `[agents.]` * `[permissions]` * `[sandbox]` * `[hooks]` * `[session]` * `[tui]` and `[tui.colors]` * `[wasm_tools.]` * `[mcp]` and `[mcp.servers.]` * `[server]` See [Config Reference](/configuration/config-reference) for all fields. ## MCP Swarmie supports Model Context Protocol servers with `stdio`, `http`, and `sse` transports. Schema sources: * TOML config: `crates/core/src/config/toml_types.rs` * Runtime transport types: `crates/mcp/src/config.rs` * Resolution/validation: `crates/core/src/config/resolve.rs` ### Config Format ```toml [mcp.servers.github] enabled = true type = "http" url = "https://api.githubcopilot.com/mcp/" headers = { Authorization = "Bearer ${GITHUB_TOKEN}" } [mcp.servers.local_db] type = "stdio" command = "npx" args = ["-y", "@bytebase/dbhub"] env = { DB_URL = "sqlite:///tmp/app.db" } [mcp.servers.stream] type = "sse" url = "https://example.com/events" ``` Fields for `[mcp.servers.]`: | Field | Type | Required | Notes | | --------- | ------------- | -------- | ------------------------------------------------- | | `enabled` | bool | no | Defaults to `true`. | | `type` | string | no | `stdio` default; allowed: `stdio`, `http`, `sse`. | | `command` | string | stdio | Required for stdio transport. | | `args` | array(string) | no | Optional stdio args. | | `url` | string | http/sse | Required for http/sse. | | `headers` | table | no | Optional HTTP/SSE headers. | | `env` | table | no | Optional environment vars for stdio processes. | ### Transport Semantics `McpTransport` enum (`crates/mcp/src/config.rs`): * `Stdio { command, args }`: spawn child process and speak MCP over stdio. * `Http { url, headers }`: connect over streamable HTTP. * `Sse { url, headers }`: connect over server-sent events. ### Validation Rules `resolve_toml_mcp_servers()` enforces: * `stdio` requires `command`. * `http` requires `url`. * `sse` requires `url`. * Unknown `type` is rejected with validation error. ### `/mcps` Management in TUI `/mcps` (or `/mcp`) opens the MCP picker modal (`crates/tui/src/input/slash.rs`, `crates/tui/src/app/update/pickers.rs`). Behavior: * Requests server list via `Op::McpList`. * Shows grouped server states (`Running` / `Stopped`) with transport labels. * Enter toggles selected server via `Op::McpToggle`. * Core emits `McpServers` and `McpServerToggled` updates (`crates/core/src/submission/op_handlers.rs`). * Enabled state is persisted via config update (`set_mcp_server_enabled`). ## Providers Swarmie provider configuration is defined in `crates/core/src/config/toml_types.rs` and resolved at runtime in `crates/core/src/config/resolve.rs`. ### Provider Config Shape Use `[providers.]` entries and point `[defaults].provider` at one of those names. ```toml [defaults] provider = "anthropic" model = "sonnet" [providers.anthropic] type = "anthropic" authentication = "api_key" api_key_env = "ANTHROPIC_API_KEY" [providers.openai] type = "openai-compatible" api_key_env = "OPENAI_API_KEY" base_url = "https://api.openai.com/v1" ``` Supported fields (`crates/core/src/config/toml_types.rs`): | Field | Type | Notes | | ---------------- | ------ | --------------------------------------------------- | | `type` | string | Provider runtime type. | | `authentication` | string | `api_key` or `oauth`. | | `api_key_env` | string | Environment variable name for API key lookup. | | `base_url` | string | Optional endpoint override for compatible backends. | ### Runtime Resolution `resolve_runtime_provider()` in `crates/core/src/config/resolve.rs` resolves providers in this order: 1. Load `[defaults].provider` and corresponding `[providers.]` entry. 2. Resolve credentials by `authentication`: * `oauth`: load token from `~/.swarmie` credential store. * otherwise: read the configured `api_key_env`. 3. Resolve model via `swarmie_provider::resolve_model_id()` (short names like `sonnet` map to full model IDs). 4. Build API client from provider `type`: * `anthropic` uses `SwarmProviderLlmApi`. * `openai-compatible` uses OpenAI-compatible runtime API selection. If stage 1 fails, `try_resolve_runtime_provider()` falls back to OAuth built-ins from provider metadata. ### Supported Provider IDs Canonical provider IDs come from `crates/provider/src/metadata.rs` (`PROVIDER_METADATA`): * `amazon-bedrock` * `anthropic` * `google` * `google-gemini-cli` * `google-antigravity` * `google-vertex` * `openai` * `openai-codex` * `azure-openai-responses` * `github-copilot` * `xai` * `groq` * `cerebras` * `openrouter` * `vercel-ai-gateway` * `zai` * `mistral` * `minimax` * `minimax-cn` * `huggingface` * `opencode` * `kimi-coding` * `gitlab` ### Provider Types vs Provider IDs `[providers.].type` is a runtime transport category, while provider IDs are specific integrations. * `type = "anthropic"` for Anthropic-native API flow. * `type = "openai-compatible"` for OpenAI-compatible style providers and endpoints. Most providers in metadata use `openai-compatible`; Anthropic uses `anthropic`. ### Custom Endpoints Set `base_url` in `[providers.]` to route to custom or self-hosted endpoints: ```toml [providers.internal-gateway] type = "openai-compatible" api_key_env = "INTERNAL_LLM_KEY" base_url = "https://llm-gateway.example.com/v1" ``` ### OAuth Example ```toml [providers.openai-codex] type = "openai-compatible" authentication = "oauth" api_key_env = "OPENAI_API_KEY" # retained for schema consistency ``` With `authentication = "oauth"`, Swarmie reads credentials from the credential store instead of env. ## Sandbox Swarmie sandbox config lives under `[sandbox]` in `config.toml` (`crates/core/src/config/toml_types.rs`) and is resolved into `SandboxConfig` (`crates/core/src/config/types.rs`). ### Config Shape ```toml [sandbox] allowed_paths = ["src/**", "docs/**"] denied_paths = ["src/secrets/**", ".git/**"] allowed_domains = ["api.example.com", "*.internal.local"] ``` | Field | Type | Description | | ----------------- | ------------- | ------------------------------------------------------ | | `allowed_paths` | array(string) | Glob-like path patterns allowed for filesystem access. | | `denied_paths` | array(string) | Explicit deny patterns applied on top of allow rules. | | `allowed_domains` | array(string) | Domain allowlist value carried in config. | ### What It Enforces Today Filesystem sandbox checks are applied in tool execution (`crates/core/src/turn/tool_execution.rs`) and file mention resolution (`crates/core/src/mention.rs`): * Read/write targets from tools like `Read`, `Write`, `Edit`, `Patch`, `Glob`, and `Grep` are validated. * `@file` mention expansion is validated before file content is injected into prompt context. Denied access returns user-visible errors such as `Filesystem sandbox denied ...`. ### Rule Semantics * If `[sandbox]` is absent: policy defaults to working-directory scope. * If `[sandbox]` exists but `allowed_paths` is empty: policy still falls back to working-directory scope. * Invalid path patterns are ignored with warnings. ### Notes on `allowed_domains` `allowed_domains` is part of resolved config shape, but filesystem sandbox enforcement in `turn/tool_execution.rs` and `mention.rs` is path-focused. Domain enforcement is tool/runtime-specific. ## Skills Skills are Markdown files with optional YAML frontmatter parsed by `crates/core/src/skills/frontmatter.rs`. ### Skill Locations Discovery order (first match wins by name): 1. Project directory-style: `.swarmie/skills//SKILL.md` 2. Project flat legacy: `.skills/.md` 3. User directory-style: `~/.swarmie/skills//SKILL.md` 4. System skills: `$SWARMIE_HOME/skills/.system//SKILL.md` 5. Admin skills: `/etc/swarmie/skills//SKILL.md` Implemented in `SkillsManager` (`crates/core/src/skills/manager.rs`). ### Frontmatter Schema Supported frontmatter keys (`SkillFrontmatter`): | Key | Type | Purpose | | -------------------------- | ------------- | ------------------------------------------------------ | | `name` | string | Display/command name override. | | `description` | string | Skill description. | | `user-invocable` | bool | Enables slash invocation as `/skill-name ...`. | | `disable-model-invocation` | bool | Hide from model-driven skill execution. | | `allowed-tools` | array(string) | Skill tool allowlist hint. | | `model` | string | Skill-specific model preference. | | `context` | string | `fork` for subagent execution context. | | `agent` | string | Subagent type when using fork context. | | `argument-hint` | string | UI hint shown in pickers. | | `hooks` | table | Optional hooks config on skill. | | `policy` | table | Includes `allow-implicit-invocation`. | | `interface` | table | `display-name`, `short-description`, `default-prompt`. | | `dependencies` | table | Tool dependency metadata. | | `permissions` | table | Skill network/file-system permissions metadata. | | `unsafe` | bool | Reserved/no-op sandbox marker. | ### Example Skill ```md --- name: review description: Review patch for risks user-invocable: true argument-hint: "" interface: short-description: Focused code review dependencies: tools: - type: mcp value: github permissions: network: true file-system: read: ["src", "docs"] write: [] --- Review the diff in $ARGUMENTS and summarize risks. ``` ### Arguments `substitute_arguments()` supports: * `$ARGUMENTS` (full argument string) * `$ARGUMENTS[0]..[9]` * `$0..$9` shorthand Defined in `crates/core/src/skills/loader.rs`. ### `/skills` Picker TUI integration: * `/skills` opens the `Manage Skills` dialog. * Core sends `Op::SkillsList` and returns `Event::AvailableSkills`. * Space toggles enabled state in the picker. * Enter sends `Op::SkillVisibility { name, enabled }` diffs for changed skills. * Disabled skill paths are persisted in `~/.swarmie/config.toml` under `[skills].disabled`. Relevant files: * `crates/tui/src/input/slash.rs` * `crates/tui/src/app/update/commands.rs` * `crates/tui/src/app/update/pickers.rs` * `crates/tui/src/app/update/modals.rs` * `crates/core/src/submission/op_handlers.rs` * `crates/core/src/skills/manager.rs` ### Built-in System Skills Swarmie embeds and installs system skills: * `skill-creator` * `skill-installer` Installed under `$SWARMIE_HOME/skills/.system` by `crates/core/src/skills/system.rs`. ## Themes TUI theming is implemented in `crates/tui/src/theme/theme.rs` with terminal palette support in `crates/tui/src/theme/terminal.rs`. ### Built-in Themes Recognized names: * `auto` (alias of terminal-adaptive mode) * `terminal` * `default` (alias of terminal-adaptive mode) * `dark` * `light` * `dracula` * `nord` * `gruvbox` * `gruvbox-dark` (alias of `gruvbox`) `ThemeChoice::names()` surfaces: `auto`, `dark`, `light`, `dracula`, `nord`, `gruvbox`. ### Configure Theme ```toml [tui] theme = "nord" frame_rate_cap = 60 ``` ### Semantic Color Keys Swarmie exposes 15 semantic color keys (`Theme` and `[tui.colors]`): * `primary` * `success` * `error` * `warning` * `muted` * `accent` * `text` * `bg_subtle` * `border` * `spinner` * `text_weak` * `text_strong` * `border_focus` * `info` * `background` ### Color Overrides Use `[tui.colors]` to override any subset of semantic colors: ```toml [tui] theme = "terminal" [tui.colors] primary = "#f97316" accent = "#60a5fa" text = "#e5e7eb" bg_subtle = "#1f2937" border_focus = "#f97316" ``` ### Terminal-Adaptive Mode `terminal`/`auto` mode queries terminal foreground/background via OSC color detection and derives readable colors at runtime. Capabilities from `crates/tui/src/theme/terminal.rs`: * Detect terminal light/dark tendency. * Refresh palette on re-query. * Best-effort downgrade for truecolor/256/16-color terminals. * Blend and perceptual color distance helpers used for adaptive selection. If terminal palette detection fails, Swarmie falls back to safe defaults. ## WASM Tools Swarmie defines WASM tool configuration under `[wasm_tools.]` in TOML (`crates/core/src/config/toml_types.rs`). Runtime structures are in `crates/core/src/config/types.rs` and execution limits are in `crates/wasm/src/limits.rs`. ### Config Shape ```toml [wasm_tools.echo] path = "tools/echo.wasm" capabilities = ["Logging", "HttpRequest"] secrets = ["API_TOKEN"] workspace_prefixes = ["src/", "tests/"] endpoint_allowlist = ["https://api.example.com/*"] [wasm_tools.echo.tool_aliases] old_name = "new_name" [wasm_tools.echo.limits] max_memory_bytes = 67108864 fuel_limit = 1000000000 execution_timeout_secs = 30 max_log_entries = 1000 max_http_requests = 50 max_tool_invocations = 20 max_file_read_bytes = 10485760 ``` ### Fields | Field | Type | Description | | -------------------- | ------------- | ---------------------------------------------------------- | | `path` | string | Path to `.wasm` component file. | | `capabilities` | array(string) | Capability grants exposed to host bindings. | | `secrets` | array(string) | Secret names accessible via WASM secret APIs. | | `workspace_prefixes` | array(string) | Workspace directory prefixes permitted for file access. | | `endpoint_allowlist` | array(string) | Allowed endpoint patterns for outbound HTTP. | | `tool_aliases` | table | Alias map (`alias -> real tool name`) for tool invocation. | | `limits.*` | table | Resource limit overrides for this tool. | ### Resource Limits Default `ResourceLimits` (`crates/wasm/src/limits.rs`): * `max_memory_bytes`: `64 MiB` * `fuel_limit`: `1_000_000_000` * `execution_timeout_secs`: `30` * `max_log_entries`: `1000` * `max_http_requests`: `50` * `max_tool_invocations`: `20` * `max_file_read_bytes`: `10 MiB` Hard validation includes: * memory must be `> 0` and `<= 512 MiB` * timeout must be `> 0` and `<= 300s` * all counters must be `> 0` ### Runtime Note `[wasm_tools]` is defined in TOML schema and runtime types; the current resolved runtime config path (`resolve_from_toml`) does not yet project `wasm_tools` into `ResolvedConfig` (`crates/core/src/config/resolve.rs`).