Configuration
Upgrading from 1.x? 2.0 is a breaking config release. See Migrating to 2.0 for the HARD vs SOFT change list and before/after snippets, then run
gth config validateto check your migrated config.
Gaunt Sloth runs from a directory tree that contains a config file. The fastest way to create one is the interactive walkthrough:
gth initIt detects which providers are already usable (an API key set, or a local Ollama running) and lists
those first, asks you to pick a provider and then a model from that provider’s live catalog
(preferred models are starred), and asks whether to store the config for this project
(.gsloth/.gsloth-settings/) or globally for all projects (~/.gsloth/). If a config already
exists at the chosen location, it asks before overwriting.
Pass -g/--global to skip that question and write straight to the global config, and
-i, --identity-profile <name>/--profile <name> to create a named profile instead of the
unscoped config — the folder labels in the dialog change to spell out the profile subdirectory
(.gsloth/.gsloth-settings/<name> / ~/.gsloth/.gsloth-settings/<name>). See
Creating a profile for the profile walkthrough.
Already know the provider? Pass it directly and skip the dialog:
gth init anthropicEither way you get a .gsloth.config.json — the project-scoped one lands in
.gsloth/.gsloth-settings/ (the .gsloth directory is created if needed) — that you can commit
and tune. From there, follow the page for whatever you want to set up:
| Page | What it covers |
|---|---|
| Providers | Per-provider setup (Anthropic, Vertex AI, OpenAI, Groq, Ollama, …) and the model-identity prompt. |
| Tools | Built-in tools, the shell tool, content search, custom tools, middleware, and the allow-list. |
| MCP servers | Connecting MCP servers, including remote OAuth and TLS trust. |
| Content sources | Pulling review requirements from GitHub issues or Jira, and change-requirements discovery. |
| Prompts | The prompts object — guidelines, review, system and the other prompt segments, and scoping them to paths. |
| Output & files | Where and whether gth writes output, run headers, logging, colour, and redaction. |
| Profiles & runtime | Named identity profiles, subagents, the AG-UI server, and the agent backend. |
| Interactive sessions | The autocompact key — the conversation size at which a session folds its own history. |
Config file names and discovery
Section titled “Config file names and discovery”A config file is one of these, in the project root or under .gsloth/.gsloth-settings/:
.gsloth.config.json(JSON).gsloth.config.jsonc(JSON with comments).gsloth.config.js(JavaScript module).gsloth.config.mjs(JavaScript module, explicit extension)
When more than one exists in the same location, the first match wins in the order
.json → .jsonc → .js → .mjs. The same order applies to the global ~/.gsloth/ config.
Gaunt Sloth walks up the directory tree to find the nearest config, so it works from a subdirectory
of a monorepo — see Work in a monorepo.
Both JSON names get lenient JSONC parsing — comments and trailing commas work in either. Use the
.jsonc name when you want comments without editors flagging them as invalid JSON. You can also
point at a config directly with the -c/--config flag:
gth -c /path/to/config.json ask "who are you?"Use a JavaScript config (.gsloth.config.js/.mjs) when you need custom middleware or tools that
JSON can’t express — see Providers → JavaScript configuration.
The global config and your project config
Section titled “The global config and your project config”Your project config wins, but it does not replace the global one — it overrides only the keys it
sets, and the global’s other keys stand. The global ~/.gsloth/.gsloth.config.* loads first and
your project config merges on top of it, on every run. So settings you want everywhere (your
provider and model, tui, writeOutputToFile) belong in the global config, and a project config
only has to state what it changes. With no project config anywhere up-tree, the global config is
used on its own.
Arrays are the exception to the merge: most replace across layers instead of combining, and a few accumulate — see Array merge policy across config layers.
The global layer cannot be turned off: there is no flag, environment variable or config key for it,
and neither -c/--config nor -i/--identity-profile (--profile) bypasses it — each chooses the
project-layer config that merges over the global one. See
Identity profiles for where a profile sits in the full precedence
chain.
Run under the global config only
Section titled “Run under the global config only”-g/--global drops the project layer instead: config discovery does not walk up from the working
directory, and configuration resolves from ~/.gsloth/ alone. Use it when you want a run to use
your own configuration — provider, model, tools, approvals — rather than whatever the repository
you are standing in configures:
gth -g reviewWith -i/--profile, the named profile’s config is resolved globally too, from
~/.gsloth/.gsloth-settings/<name>/ — so this reads your own devops config and never the
project’s, and fails if you have no global devops profile:
gth -g -i devops pr 42-g scopes configuration only; it is not a boundary around the project directory. Prompt files
(guidelines, review checklist and the rest) still resolve from the working directory
as usual, falling back to the built-in defaults — so a project’s own guidelines or review prompt are
read into the prompt under -g just as they are without it.
-g and -c cannot be combined: both choose where configuration comes from, so passing the pair
is rejected rather than silently honouring one of them.
To see what a run actually resolves to, print the effective merged config; to find which file to fix when a key is wrong, validate each layer on its own:
gth config printgth config validateUsing the .gsloth directory
Section titled “Using the .gsloth directory”Create a .gsloth directory in your project root for a tidier layout. When it exists, Gaunt Sloth:
- writes output files (command responses) into
.gsloth/instead of the project root, and - looks for config in
.gsloth/.gsloth-settings/.
.gsloth/.gsloth-settings/.gsloth.config.json.gsloth/.gsloth-settings/.gsloth.guidelines.md.gsloth/.gsloth-settings/.gsloth.review.md.gsloth/gth_2025-05-18_09-34-38_ASK.mdWithout a .gsloth directory, everything stays in the project root. gth init creates the
directory and writes config into .gsloth/.gsloth-settings/ by default; there is no automatic
migration, so if you add a .gsloth directory after initializing, move your existing config files
into .gsloth/.gsloth-settings/ by hand.
AI ignore (.aiignore)
Section titled “AI ignore (.aiignore)”Put files and directories out of the built-in file tools’ reach with a .aiignore file in the
project root. Lines starting with # are comments.
node_modules/dist/*.logControl it in config with aiignore.enabled (boolean, default true) and aiignore.patterns (an
array supplied directly instead of reading .aiignore):
{ "aiignore": { "enabled": true, "patterns": ["node_modules/", "dist/", "*.log"] }}When .aiignore is missing, Gaunt Sloth logs that at debug level only.
Pattern rules
Section titled “Pattern rules”Patterns follow .gitignore rules, with the exceptions noted below. Paths are matched relative to
the working directory.
| Pattern | Hides |
|---|---|
*.log |
every .log file at any depth — app.log and sub/app.log alike |
secrets.txt |
any file or directory of that name, at any depth |
dist/ |
the dist directory and everything inside it |
build/out |
only build/out and its contents — not src/build/out |
/dist |
only a dist at the project root, not one nested deeper |
Two rules carry most of the weight:
- A pattern without a
/applies at every depth.*.logreaches into subdirectories; you do not need to write**/*.log. - A pattern that names a directory hides its name and its whole subtree. One line is enough:
secretdirremoves the directory from every listing and withholds every file beneath it. You do not need a secondsecretdir/**line.
A pattern containing a / is anchored to the working directory instead of applying at every depth,
which is what makes build/out above miss src/build/out. A leading / anchors an otherwise-bare
pattern the same way.
Two deliberate differences from .gitignore:
- A trailing
/does not restrict the match to directories.dist/anddistbehave identically, so a file nameddistis hidden too. Matching directories only would require knowing each entry’s type, and.aiignoreerrs toward hiding: a file wrongly hidden is visible to you and easy to rename around, whereas the opposite mistake silently exposes something you asked to be hidden. - Re-inclusion (
!pattern) is not supported. A leading!is matched literally rather than un-hiding anything, so you cannot carve an exception out of a broader pattern. Narrow the pattern instead.
What .aiignore covers
Section titled “What .aiignore covers”.aiignore keeps matching files out of what the built-in filesystem and search tools disclose —
directory listings, file searches, and gth_grep results and the file contents behind them. It is a
privacy boundary rather than a tidiness setting, which is why the rules above resolve every
ambiguity by hiding more rather than less.
It is a may not touch boundary rather than a may not read one, so it runs in both directions.
The built-in write tools — write_file, create_directory and move_file — refuse a path inside
an ignored directory just as the read tools refuse to disclose one, so a directory you hid to keep
the agent’s eyes off it is also one you cannot ask the agent to write into. Move a path out of
.aiignore if you want the agent working in it. The refusal deliberately names no path: resolving
one into the message would disclose where a symlink points.
What .aiignore is worth in each mode
Section titled “What .aiignore is worth in each mode”.aiignore binds the built-in file tools. It does not bind the shell, and a shell command is
arbitrary code running as you — so the same file has two routes:
read_file secrets/keys.txt → refusedcat secrets/keys.txt → readWhich of those the agent can take on its own is decided by the approvals mode:
- At Manual and Write, every shell command comes to you before it runs, so
.aiignoreholds against anything the agent does unattended — until you tell it to always allow a command, which is a standing yes for that command from then on. Answering always allow at the prompt does this as surely as listing the command inapprovals.allowdoes. - At Assisted, Auto and Bypass, a shell command can run without your seeing it. A
command that happens to read an ignored file is judged like any other command, on its text, and
nothing in it is checked against
.aiignore.
So .aiignore is worth most where you are reading each command anyway, and much less in the modes
you would leave running. It keeps a secret out of the agent’s ordinary reach; it does not keep one
out of the run. Anything that must not be read at all belongs outside the working folder, or outside
the machine the agent runs on —
what approvals protect you from is the fuller
account of where that line falls and what does hold.
The full config object
Section titled “The full config object”The pages above cover each area in depth. For the exhaustive, type-checked surface — every key and its default — see the generated reference:
GthConfiginterfaceDEFAULT_CONFIGvalues- Source of truth:
packages/core/src/config/schema.ts