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>`