Customization

Configuration Files

Swival supports persistent settings through TOML config files at two levels: a user-global file and a project-local file. Both are optional.

The global config lives at ~/.config/swival/config.toml (or $XDG_CONFIG_HOME/swival/config.toml if you set that variable). The project config is swival.toml in the base directory.

When both files exist, project settings override global settings. CLI flags override everything. The full precedence order is:

CLI flags > project config > global config > hardcoded defaults

To generate a starter config with all settings commented out, run one of:

swival --init-config            # global config
swival --init-config --project  # project config in current directory

Settings use the same names as CLI flags. Lists use TOML arrays instead of comma-separated strings.

provider = "openrouter"
model = "qwen/qwen3-coder-next"
max_turns = 250
allowed_commands = ["ls", "git", "python3"]
allowed_dirs = ["/tmp"]
allowed_dirs_ro = ["/opt/zig/lib/std"]
proactive_summaries = true
# no_continue = false
quiet = false
extra_body = { chat_template_kwargs = { enable_thinking = false } }
reasoning_effort = "high"

# Reviewer settings
reviewer = "swival --reviewer-mode"
review_prompt = "Focus on correctness and test coverage"
max_review_rounds = 10
objective = "objective.md"
verify = "verification/working.md"

For the chatgpt provider (ChatGPT Plus/Pro subscriptions), no API key is needed since authentication is handled through OAuth:

provider = "chatgpt"
model = "gpt-5.4"

Relative paths in allowed_dirs, allowed_dirs_ro, skills_dir, objective, and verify resolve against the config file's parent directory, not the working directory. Tilde paths like ~/projects expand to the home directory.

The reviewer value is shell-split; only path-like first tokens (./, ../, ~) are resolved against the config directory, while bare command names like swival are left for PATH lookup at runtime.

If a project config contains api_key inside a git repository, Swival prints a warning because the key could be committed accidentally. Prefer environment variables for credentials.

The --system-prompt and no_system_prompt settings are mutually exclusive in config files, just as they are on the command line.

The library API (Session class) does not auto-load config files. If you want config file support in library code, call load_config() and config_to_session_kwargs() explicitly.

Instruction Files

Swival loads instruction files during startup and appends them to the system prompt. CLAUDE.md provides project rules (<project-instructions>), while AGENTS.md provides agent conventions (<agent-instructions>).

CLAUDE.md

Loaded from the project base directory only. Capped at 10,000 characters.

AGENTS.md

Loaded from two locations, in this order:

  1. User-level: ~/.config/swival/AGENTS.md (or $XDG_CONFIG_HOME/swival/AGENTS.md)
  2. Project-level: <base-dir>/AGENTS.md

Both are optional. When both exist, user-level content is prepended to project-level content inside a single <agent-instructions> block. The two levels share a combined 10,000 character budget.

User-level content is read first and gets priority — a large user-level file will truncate or entirely displace the project-level file. Size your user-level file accordingly if you rely on project-level instructions.

The user-level file is a good place for personal conventions that apply across all projects (preferred language style, test habits, tool preferences). The project-level file is for project-specific rules.

This is a Go project using Chi for routing. Tests use testify.
Always run `go test ./...` after making changes.
Don't add dependencies without asking.

Disabling instructions

Use --no-instructions to skip all instruction files (both CLAUDE.md and AGENTS.md at all levels).

swival --no-instructions "task"

Use --no-memory to skip loading auto-memory from .swival/memory/.

swival --no-memory "task"

If you set --system-prompt, instruction files are also skipped because you are providing the full prompt text directly.

System Prompt Control

The built-in prompt is stored in swival/system_prompt.txt and defines default behavior, tool policy, and coding expectations.

You can replace it completely with --system-prompt.

swival --system-prompt "You are a security auditor. Only report vulnerabilities." "Audit src/"

You can also remove the system message entirely with --no-system-prompt.

swival --no-system-prompt "Just answer: what is 2+2?"

--system-prompt and --no-system-prompt are mutually exclusive.

When a system message is present, Swival appends the current local date and time to that system content.

Sampling And Reproducibility

--temperature and --top-p control response sampling.

swival --temperature 0.3 --top-p 0.9 "task"

If you do not set --temperature, provider defaults apply. --top-p defaults to 1.0.

--seed passes a deterministic seed when the provider supports it.

swival --seed 42 "task"

Seeded runs are usually more stable, but identical output is still not guaranteed across all providers, model versions, and hardware environments.

Extra LLM Parameters

Some models accept parameters beyond the standard OpenAI chat completions API. The extra_body setting passes an arbitrary dictionary through to the underlying API call, so you can use provider-specific or model-specific options without Swival needing dedicated flags for each one.

On the command line, pass a JSON object:

swival --extra-body '{"chat_template_kwargs": {"enable_thinking": false}}' "task"

In a config file, use TOML inline table syntax:

extra_body = { chat_template_kwargs = { enable_thinking = false } }

In the library API:

session = Session(extra_body={"chat_template_kwargs": {"enable_thinking": False}})

The dictionary is forwarded as extra_body to the LiteLLM completion call, which passes it through to the provider's API. Refer to your model's documentation for supported parameters.

Reasoning Effort

Some models support a tunable reasoning level that controls how much effort the model puts into thinking before responding. This is a first-class parameter in Swival, separate from extra_body.

On the command line:

swival --provider chatgpt --model gpt-5.4 --reasoning-effort high "task"

In a config file:

reasoning_effort = "high"

In the library API:

session = Session(reasoning_effort="high")

Valid levels are none, minimal, low, medium, high, xhigh, and default. Not all models support this parameter — when used with a model that doesn't support it, the behavior depends on the provider (it may be ignored or cause an error). Only set it when you know your model supports it.

Turn And Token Limits

--max-turns limits how many agent-loop iterations are allowed.

swival --max-turns 10 "quick task"

The default turn limit is 100. If the loop reaches this limit without a final answer, Swival exits with code 2.

--max-output-tokens limits tokens generated per model call.

swival --max-output-tokens 16384 "task"

The default is 32768. If prompt size and context constraints require it, Swival clamps output budget downward automatically.

--max-context-tokens sets requested context length.

swival --max-context-tokens 65536 "task"

For LM Studio, this can trigger a model reload. When both --max-context-tokens and --max-output-tokens are set, --max-output-tokens must be less than or equal to context length.