Skip to content
Back to skills

Neru Add Config Option

ASecurity

Add or change a Neru config.toml option end to end: struct field, shared and platform defaults, validation, examples, docs, and hot-reload behavior. Use whenever a change adds, renames, or removes a key in the TOML config schema. Not for theme palette entries or CLI flags.

  • 907 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added November 7, 2026
designgobashrails

Works with

  • cli

Security analysis

A100/100

Pro scans all 2 files and shows the line behind each finding

Scanned November 7, 2026

npx -y skills add y3owk1n/neru --skill neru-add-config-option --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Neru Add Config Option?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Neru Add Config Option
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/y3owk1n-neru-add-config-option/badge)](https://www.skillsdirectory.com/skills/y3owk1n-neru-add-config-option)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: neru-add-config-option
description: "Add or change a Neru config.toml option end to end: struct field, shared and platform defaults, validation, examples, docs, and hot-reload behavior. Use whenever a change adds, renames, or removes a key in the TOML config schema. Not for theme palette entries or CLI flags."
---

# Adding a config option to Neru

Config changes fail in review when one link of the chain is missing, such as a default
that exists on macOS but not Linux, a validator that never runs, or an example
file that still shows the old key. `internal/config/AGENTS.md` states the
contract and which test fails per link. This skill gives the order to do it in.

Five steps are universal. Then check the conditional four, the ones that turn
a one-line option into a ten-file one, and that no guide used to name.

## The universal five

1. **Schema.** Add the struct field in `internal/config/config.go` with a
   `toml:"snake_case_name"` tag matching the surrounding section. Follow the
   existing nesting. Options live in their feature's sub-struct, not at root.
   Then run `just genschema`, which renders the field into
   `schemas/config.schema.json` for editors.
   `TestConfigSchema_IsGeneratedFromTheConfigStructs` fails until you do.
2. **Shared default.** Set it in `newDefaultConfig()` in
   `internal/config/config_defaults.go`. Assign the field by name even when the
   value you assign is the zero value, and say in a comment what the zero
   means, because a zero nobody wrote cannot be told from a forgotten one.
   `TestEverySchemaFieldHasAnExplicitDefault` fails on an omission.
3. **Example.** Add the line to `configs/default-config.toml`, which is the file
   a user copies. Comment it out if the option has no default, because an uncommented
   empty value asserts a default that does not exist.
   `TestConfigOptionsAppearInTheDefaultExample` fails when the line is missing.
   Update the mode-specific examples (`grid-only-config.toml`,
   `hints-only-config.toml`, `recursive-grid-only-config.toml`) only if they
   show the affected section. They are deliberately partial, and only their
   *keys* are checked, by `TestShippedExamplesWriteOnlySchemaKeys`. Those four
   are the whole shipped set. `configs/embed.go` lists them, and anything else
   in `configs/` is a working file rather than a project artifact. Only
   `default-config.toml` is embedded, and the rest are read from disk by the tests
   that check them.
4. **Docs.** Add the row to `docs/reference/configuration.md`. That file is the single
   home for config reference facts. Do not also describe the option in
   `docs/contributing/architecture.md` or README. **No test catches a missing row.** This is the one
   universal link a reviewer has to check by eye.
5. **Platform column.** Add the option's path to `PlatformSupport()` in
   `internal/config/platform_support.go`, saying which of macOS, Linux and
   Windows writing it actually does something on. Say so even when the answer
   is all three, because a column nobody wrote cannot be told from a forgotten one, and
   `TestEveryConfigOptionDeclaresItsPlatformSupport` fails on an option that is
   neither. A narrower column needs a note saying why, in the words the user
   reads, because it reaches the load-time warning, the `neru doctor`
   `platform_support` row and the published table, which you regenerate with
   `just gensupportref`. A `config.Color` is declared at the field, not at its
   `light` and `dark` leaves
   (`docs/adr/0013-parity-is-measured-in-words-not-subsystems.md`).

Steps 2 and 3 exempt three shapes, and an option with one of them passes both
tests whether or not you do the work: the `light`/`dark` leaves under a `Color`,
a collection that ships empty, and any field of a repeated table nobody ships
entries for (`app_configs`, `layers`). Do the work anyway. See
`internal/config/AGENTS.md`.

## The conditional four

Each is skippable, and each is a whole file when it is not.

- **Platform override.** If a platform needs a different default, set it in
  `applyPlatformDefaults()` in the matching `config_<os>.go`
  (`config_darwin.go`, `config_linux.go`, `config_windows.go`,
  `config_other.go`). Both layers are reached via `DefaultConfig()` in
  `config_platform.go`, so never call `newDefaultConfig()` directly from feature
  code. Nothing checks this, so pin the difference in a test.
- **Validator.** Add checks to the relevant `Validate*` method on `*Config` in
  `internal/config/validators_*.go` (hints, grid, style, hotkeys, macros,
  pointer, appconfig, …). Invalid values must fail validation with a clear
  message, not get silently clamped. New enum-ish strings get an allowlist. A
  brand-new `Validate*` method must be called from `ValidateWithWarnings` in
  `validate.go`, or `TestEveryConfigValidatorRunsInTheLadder` fails.
- **Derivation helper.** If the value the daemon runs on is not the value the
  user writes, resolve it once after the configuration is assembled rather than
  in each consumer. `internal/config/grid_labels.go` and `held_repeat.go` are
  the worked examples. The declared default stays what a user would type.
- **Cross-field rule.** If the option only makes sense alongside another, and a
  user could act on the mismatch, collect it into `Warnings` so it rides out on
  `LoadResult` and reaches `neru config validate`. `logger.Warn` reaches nobody
  (ADR 0002, `docs/adr/0002-severity-tiered-config-validation.md`).

## Hot reload

Config is hot-reloadable via `internal/config/loader` (load/watch/persist/
set-field). If the option should be settable at runtime with `neru config set`,
confirm the field path resolves through the loader's set-field lookup. If it
intentionally requires a restart, say so in the docs.

## Tests

- Table-driven validator tests next to the validator you touched
  (`validators_*_test.go`), covering the default, a valid custom value, and at
  least one rejected value.
- If the default differs per platform, pin it in `config_defaults_test.go` or
  the platform-tagged config test.

## Verify

```bash
just test-foundation    # the fast cross-platform-safe slice
```

That one recipe now runs every package this skill's guardrails live in: the
config package, its loader, and `internal/architecture`.

Then the standard pre-commit gate from AGENTS.md.

Files in this skill

  • SKILL.md6.2 KB
  • agents/openai.yaml277 B

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.