# AGENTS.md

**Keld** is a friendly TOML-configured wrapper around
[restic](https://restic.net/) (a backup tool). It provides:

- Layered configuration with presets and command-specific overrides
- Interactive menu when invoked without arguments (BubbleTea v2)
- Split preset syntax (`home@cloud`) for composable configurations
- Passthrough of arbitrary flags to restic

This project uses jujutsu for version control. Before starting work, check `jj
st`. If there's existing work in progress, run `jj new -m "..."` to create a new
working copy with a good, imperative, kernel-commit-style description. DO NOT
follow or read ANY skills or use ANY tools related to Conventional Commits.

## Essential Commands

All tasks are managed via **mise** (see `mise.toml`):

```bash
mise run vuln
mise run vet
mise run install

# Check formatting without modifying
mise run fmt:check
# Run full check suite (fmt:check, vet, lint, vuln, build, test)
mise run check
```

Freely check the `--help` output of various `restic` commands as you work, so
we can be absolutely certain we're writing a good, up-to-date wrapper. Any time
our code differs from restic proper, PLEASE FLAG IT TO ME!

## Config System

### Section Merge Order

Config sections are merged in ascending priority order. For
`keld home@cloud backup`:

```text
[global] → [global.backup] → [@cloud] → [@cloud.backup] →
[home@] → [home@.backup] → [home@cloud] → [home@cloud.backup] →
CLI overrides
```

### Config File Discovery

1. Default dirs: `/usr/share/keld`, `/etc/keld`, `~/.config/keld`
2. In each dir: `config.toml` then sorted `conf.d/*.toml`
3. `KELD_CONFIG_PATHS` env var (colon-separated, supports globs)
4. `KELD_CONFIG_FILE` replaces all above if set

### Special Config Keys

| Key           | Purpose                                                            |
| ------------- | ------------------------------------------------------------------ |
| `_arguments`  | Positional args passed to restic (array or space-separated string) |
| `_workdir`    | Directory to chdir before exec                                     |
| `_command`    | Restic subcommand (allows aliasing)                                |
| `_pre_hooks`  | Backup-only shell commands run before restic backup                |
| `_post_hooks` | Backup-only shell commands run after restic backup is attempted    |
| `*.environ`   | Section suffix for environment variables                           |
| `FOO_COMMAND` | In `.environ`: executed via `sh -c`, stdout sets `FOO`             |

### Interpolation

Values can reference other sections: `cache-dir = "${vars.cache-root}/cache"`

### Split Presets

Presets with `@` are split: `home@cloud` → applies `[@cloud]`, `[home@]`,
then `[home@cloud]`

See `examples/keld/config.toml` for comprehensive examples.

## Code Patterns & Conventions

### Test Patterns

- **Table-driven tests** with `t.Parallel()`
- Testdata embedded as string constants (see `resolveFixtureTOML`)
- `tt := tt` copy before parallel subtest
- Use `t.TempDir()` and `t.Setenv()` for isolation

## Important Gotchas

### Config Merge Behavior

- Later files override earlier at **section granularity**
- Within a file, later sections override earlier
- Nested tables (sub-commands) are **not** merged across sections—only leaf keys
- Multi-line strings become repeated flags (split on `\n`)

### Restic Process Supervision

`restic.Run()` supervises restic as a child process. This allows Keld to run
post-backup hooks after restic exits and preserve restic's numeric exit code for
callers.

### Test Isolation

Tests modify `DefaultConfigDirs` global. Always restore with `t.Cleanup()`:

```go
original := DefaultConfigDirs
DefaultConfigDirs = []string{tmpDir}
t.Cleanup(func() { DefaultConfigDirs = original })
```

### BubbleTea v2

Using v2 API (not v1). Key differences:

- `tea.NewProgram(m)` instead of `tea.NewProgram(m, opts...)`
- `tea.RequestBackgroundColor` for theme detection
- `tea.NewView()` instead of returning strings

## Environment Variables

| Variable            | Purpose                                 |
| ------------------- | --------------------------------------- |
| `KELD_CONFIG_FILE`  | Single config file (highest priority)   |
| `KELD_CONFIG_PATHS` | Colon-separated additional config paths |
| `KELD_DRYRUN`       | Set to enable dry-run mode              |
| `KELD_EXECUTABLE`   | Override restic binary path             |

## Development Workflow

1. Make changes
2. `mise run check` - full validation
3. Test manually: `keld --show-command --preset <preset> <command>`
