AGENTS.md

  1# AGENTS.md
  2
  3**Keld** is a friendly TOML-configured wrapper around
  4[restic](https://restic.net/) (a backup tool). It provides:
  5
  6- Layered configuration with presets and command-specific overrides
  7- Interactive menu when invoked without arguments (BubbleTea v2)
  8- Split preset syntax (`home@cloud`) for composable configurations
  9- Passthrough of arbitrary flags to restic
 10
 11This project uses jujutsu for version control. Before starting work, check `jj
 12st`. If there's existing work in progress, run `jj new -m "..."` to create a new
 13working copy with a good, imperative, kernel-commit-style description. DO NOT
 14follow or read ANY skills or use ANY tools related to Conventional Commits.
 15
 16## Essential Commands
 17
 18All tasks are managed via **mise** (see `mise.toml`):
 19
 20```bash
 21mise run vuln
 22mise run vet
 23mise run install
 24
 25# Check formatting without modifying
 26mise run fmt:check
 27# Run full check suite (fmt:check, vet, lint, vuln, build, test)
 28mise run check
 29```
 30
 31Freely check the `--help` output of various `restic` commands as you work, so
 32we can be absolutely certain we're writing a good, up-to-date wrapper. Any time
 33our code differs from restic proper, PLEASE FLAG IT TO ME!
 34
 35## Config System
 36
 37### Section Merge Order
 38
 39Config sections are merged in ascending priority order. For
 40`keld home@cloud backup`:
 41
 42```text
 43[global] → [global.backup] → [@cloud] → [@cloud.backup] →
 44[home@] → [home@.backup] → [home@cloud] → [home@cloud.backup] →
 45CLI overrides
 46```
 47
 48### Config File Discovery
 49
 501. Default dirs: `/usr/share/keld`, `/etc/keld`, `~/.config/keld`
 512. In each dir: `config.toml` then sorted `conf.d/*.toml`
 523. `KELD_CONFIG_PATHS` env var (colon-separated, supports globs)
 534. `KELD_CONFIG_FILE` replaces all above if set
 54
 55### Special Config Keys
 56
 57| Key           | Purpose                                                            |
 58| ------------- | ------------------------------------------------------------------ |
 59| `_arguments`  | Positional args passed to restic (array or space-separated string) |
 60| `_workdir`    | Directory to chdir before exec                                     |
 61| `_command`    | Restic subcommand (allows aliasing)                                |
 62| `_pre_hooks`  | Backup-only shell commands run before restic backup                |
 63| `_post_hooks` | Backup-only shell commands run after restic backup is attempted    |
 64| `*.environ`   | Section suffix for environment variables                           |
 65| `FOO_COMMAND` | In `.environ`: executed via `sh -c`, stdout sets `FOO`             |
 66
 67### Interpolation
 68
 69Values can reference other sections: `cache-dir = "${vars.cache-root}/cache"`
 70
 71### Split Presets
 72
 73Presets with `@` are split: `home@cloud` → applies `[@cloud]`, `[home@]`,
 74then `[home@cloud]`
 75
 76See `examples/keld/config.toml` for comprehensive examples.
 77
 78## Code Patterns & Conventions
 79
 80### Test Patterns
 81
 82- **Table-driven tests** with `t.Parallel()`
 83- Testdata embedded as string constants (see `resolveFixtureTOML`)
 84- `tt := tt` copy before parallel subtest
 85- Use `t.TempDir()` and `t.Setenv()` for isolation
 86
 87## Important Gotchas
 88
 89### Config Merge Behavior
 90
 91- Later files override earlier at **section granularity**
 92- Within a file, later sections override earlier
 93- Nested tables (sub-commands) are **not** merged across sections—only leaf keys
 94- Multi-line strings become repeated flags (split on `\n`)
 95
 96### Restic Process Supervision
 97
 98`restic.Run()` supervises restic as a child process. This allows Keld to run
 99post-backup hooks after restic exits and preserve restic's numeric exit code for
100callers.
101
102### Test Isolation
103
104Tests modify `DefaultConfigDirs` global. Always restore with `t.Cleanup()`:
105
106```go
107original := DefaultConfigDirs
108DefaultConfigDirs = []string{tmpDir}
109t.Cleanup(func() { DefaultConfigDirs = original })
110```
111
112### BubbleTea v2
113
114Using v2 API (not v1). Key differences:
115
116- `tea.NewProgram(m)` instead of `tea.NewProgram(m, opts...)`
117- `tea.RequestBackgroundColor` for theme detection
118- `tea.NewView()` instead of returning strings
119
120## Environment Variables
121
122| Variable            | Purpose                                 |
123| ------------------- | --------------------------------------- |
124| `KELD_CONFIG_FILE`  | Single config file (highest priority)   |
125| `KELD_CONFIG_PATHS` | Colon-separated additional config paths |
126| `KELD_DRYRUN`       | Set to enable dry-run mode              |
127| `KELD_EXECUTABLE`   | Override restic binary path             |
128
129## Development Workflow
130
1311. Make changes
1322. `mise run check` - full validation
1333. Test manually: `keld --show-command --preset <preset> <command>`