# Configuration

Shuck can be configured through a `shuck.toml` or `.shuck.toml` file.

This page covers Shuck's native configuration for `shuck check`, `shuck format`, `shuck run`, and related native commands. ShellCheck compatibility mode uses `.shellcheckrc` instead; see the [ShellCheck compatibility guide](/docs/shellcheck-compat).

## Default behavior

If you do not add a config file, Shuck enables all implemented non-style rules by default.

That baseline includes:

- Correctness rules (`C`)
- Performance rules (`P`)
- Portability rules (`X`)
- Security rules (`K`)

All style rules are disabled by default, including `S074`. Shuck also reserves a small internal compatibility exclude list for ShellCheck-style opt-in checks when they map to standalone non-style rules.

You only need a config file when you want to change that baseline.

For example, the following configuration keeps the defaults, adds all style rules, and ignores one specific rule:

```toml
[lint]
extend-select = ["S"]
ignore = ["S074"]
fixable = ["ALL"]
unfixable = []
```

## Config file discovery

When Shuck needs configuration for a file or input path, it searches upward from that path for either:

- `.shuck.toml`
- `shuck.toml`

If both files exist in the same directory, `.shuck.toml` wins.

Shuck uses the nearest matching config root for each analyzed path. A single invocation can therefore pick up different config files when you lint inputs from different project roots.

Discovered config files do not cascade or merge. Shuck uses the closest config file it finds and ignores parent config files.

## Precedence

Configuration is resolved in this order:

1. `--isolated` disables discovered config files.
2. `--config path/to/file.toml` uses one explicit config file for the entire run.
3. Otherwise, Shuck discovers the nearest `.shuck.toml` or `shuck.toml` for each analyzed path.
4. Inline overrides passed as `--config "<key> = <value>"` are applied last.
5. If you pass the same inline setting more than once, the last one wins.

`--isolated` cannot be combined with `--config path/to/file.toml`, but it can still be used with inline overrides.

```bash
# Use the nearest discovered config
shuck check .

# Use one explicit config file for the whole run
shuck --config ci/shuck.toml check .

# Ignore discovered config files and rely only on inline overrides
shuck --isolated --config "lint.select = ['C001']" check .
```

## Check settings

The `[check]` section controls file-level analysis behavior.

### `embedded`

Enables linting for supported embedded shell scripts in non-shell files such as GitHub Actions workflows and composite actions.

This setting defaults to `true`.

```toml
[check]
embedded = true
```

Set it to `false` if you want `shuck check` to ignore embedded `run:` blocks and only lint standalone shell files.

See the [Embedded Scripts guide](/docs/embedded-scripts) for the supported file types and how diagnostics are remapped back to workflow YAML.

## Lint settings

Shuck's stable rule-selection config surface lives under `[lint]`.

### `select`

Replaces the default rule set with the selectors you provide.

Selectors can be:

- A named selector like `google`
- A full category prefix like `C` or `K`
- A narrower prefix like `C12`
- An exact rule code like `S074`
- `ALL`

```toml
[lint]
select = ["google"]
```

### `ignore`

Removes rules from the currently selected set.

```toml
[lint]
select = ["ALL"]
ignore = ["S", "C011"]
```

### `extend-select`

Adds more rules on top of the current selection.

```toml
[lint]
extend-select = ["google"]
```

### `per-file-ignores`

Defines rule ignores for files matching a glob pattern.

```toml
[lint]
extend-select = ["S"]

[lint.per-file-ignores]
"scripts/*.sh" = ["S074"]
"testdata/**" = ["ALL"]
```

### `extend-per-file-ignores`

Adds more per-file ignores on top of any existing `per-file-ignores` entries.

```toml
[lint]
extend-select = ["S"]
extend-per-file-ignores = { "vendor/**" = ["ALL"], "scripts/*.sh" = ["S074"] }
```

### `fixable`

Restricts which rules are eligible when you run `shuck check --fix`.

By default, all rules are considered fixable candidates when fixes exist.

```toml
[lint]
extend-select = ["S"]
fixable = ["C", "S074"]
```

### `unfixable`

Marks selected rules as ineligible for automatic fixes, even when `--fix` is enabled.

```toml
[lint]
select = ["ALL"]
unfixable = ["C001"]
```

### `extend-fixable`

Adds more fixable rules on top of an existing `fixable` selection.

```toml
[lint]
extend-select = ["S"]
fixable = ["C"]
extend-fixable = ["S074"]
```

## Zsh plugin resolution

Shuck's zsh-specific plugin resolution settings live under `[lint.zsh.plugins]`.

Use that section when you want Shuck to resolve real zsh plugin entrypoints for startup files such as `.zshrc`, especially for `oh-my-zsh` roots, dynamic plugin lists, and custom plugin entrypoints.

See the dedicated [Zsh Support guide](/docs/zsh-support) for the recommended setup patterns and the [Settings Reference](/docs/settings) for the generated key-by-key reference.

## Contracts

Shuck prefers to discover behavior from real sourced files and resolved plugin
entrypoints first. Use `[lint.contracts]` when important runtime or framework
behavior cannot be recovered from source alone.

Typical examples include:

- runtime-provided names that a script reads;
- plugin settings assigned in `.zshrc` and consumed later by framework code;
- metadata assigned in one file and consumed elsewhere by a dispatcher.

See the dedicated [Contracts guide](/docs/contracts) for the discovery boundary,
examples, and when to prefer Contracts over more source resolution. The
[Settings Reference](/docs/settings) includes the exact `[lint.contracts]`
syntax.

## Format settings

The formatter reads style options from `[format]`.

```toml
[format]
indent-style = "space"
indent-width = 2
binary-next-line = true
switch-case-indent = true
space-redirects = true
keep-padding = true
function-next-line = false
never-split = false
```

Formatter config follows the same discovery and override rules as the rest of Shuck config. CLI flags passed to `shuck format` override `[format]` for that invocation.

Dialect is the exception: `[format].dialect` is rejected so projects do not accidentally force one parser mode for every file. Use file names, shebangs, or `shuck format --dialect ...` for a one-off override.

See the dedicated [Formatting guide](/docs/formatting) for command examples, stdin behavior, `--check`, `--diff`, `--simplify`, and `--minify`.

## Runtime settings

The `[run]` section controls managed interpreter defaults for `shuck run`, `shuck install`, and `shuck shell`.

```toml
[run]
shell = "bash"
shell-version = "5.2"

[run.shells]
bash = "5.2"
zsh = "5.9"
```

Use `[run].shell` for a project-wide default interpreter, `[run].shell-version` for a generic version constraint, and `[run.shells]` for per-shell pins that apply after Shuck has resolved the script's shell.

See the dedicated [Managed Runtime guide](/docs/run) for resolution order, inline script metadata, and `--system` behavior. The generated [Settings Reference](/docs/settings) also includes every supported `[run]` key.

## CLI-only file selection

Some commonly expected settings are command-line flags today, not `shuck.toml` keys. In particular:

- `--exclude`
- `--extend-exclude`
- `--respect-gitignore` and `--no-respect-gitignore`
- `--force-exclude` and `--no-force-exclude`

Use those flags directly on `shuck check`:

```bash
shuck check --exclude vendor --extend-exclude fixtures --force-exclude .
```

Shuck does not currently support configuring those file-selection options in `shuck.toml`.
