{"id":"a36632bc-206f-4a2f-9211-9ec1955632f6","slug":"battlestation","kind":"mcp","name":"battlestation","summary":"Capture macOS system preferences to an auditable TOML file and restore them on a new machine","owner":"stevekinney","repoUrl":"https://github.com/stevekinney/battlestation","license":"MIT","licenseTier":"permissive","trust":"community","stars":1,"match":null,"fewStars":true,"format":"mcp","createdAt":"2026-08-04T21:01:09.000Z","addedAt":"2026-10-21T04:54:58.888Z","logoUrl":"https://avatars.githubusercontent.com/u/251000?u=744cf65c3cf52e36c61a990fcc8ca1e9db625a80&v=4","linkOnly":false,"collection":null,"skillPath":null,"categories":["files-documents","cloud-infra"],"tags":["bun","cli","dotfiles","macos","macos-defaults","toml","typescript"],"runtime":"node","local":true,"cloudCapable":false,"cloudCapability":"unknown","cloudReadyOnPlatform":false,"needsApiKey":false,"reviewed":false,"syncedAt":"2026-10-21T04:55:21.031Z","sample":false,"url":"https://conduits.dev/tools/battlestation","language":"TypeScript","transports":["stdio"],"install":{"npm":"battlestation"},"requirements":{"env":[],"upstreamMayCharge":false},"tools":null,"readme":"# battlestation\n\nCapture your macOS system preferences to a readable, auditable TOML file—and restore them on a new Mac.\n\n`battlestation` knows about ~150 curated settings across keyboard, text substitution, trackpad, mouse, Dock, Mission Control, hot corners, Finder, screenshots, appearance, region and language formats, menu bar, Control Center, window management, Dock pinned apps, default app handlers, and system behavior—including hidden preferences that System Settings doesn't expose (Dock auto-hide delay and animation speed, dimming hidden apps, POSIX paths in Finder titles, `.DS_Store` hygiene on network and USB volumes, and more). Structured settings—system keyboard shortcuts, text replacements, input sources—are captured as native TOML (arrays with inline tables, dictionaries as sub-tables), and per-host settings (Control Center modules, screen saver) are read and written with `defaults -currentHost`.\n\n## What it deliberately does not capture\n\nSettings that live outside `defaults` need different mechanisms and are out of scope today: power management (`pmset`), Software Update automation and Time Machine (system-level, `sudo`), timezone (`systemsetup`), Night Shift, per-app notification preferences, login items, network and Bluetooth configuration, and Finder sidebar contents. Third-party apps' own preferences (Alfred, Karabiner, and so on) are their own domains—sync those with the apps' own export tooling or dotfiles. Some captured values come with caveats noted in the file itself: text replacements also sync via iCloud, and Control Center placement codes are opaque values managed by macOS.\n\n## Usage\n\n```bash\n# Snapshot the current machine's settings to battlestation.toml\nbattlestation capture\n\n# See how the live system differs from the file\nbattlestation diff\n\n# Make the system match the file (restarts Dock/Finder/SystemUIServer as needed)\nbattlestation apply\n\n# Preview without changing anything, or skip the confirmation prompt\nbattlestation apply --dry-run\nbattlestation apply --yes\n\n# Check the file for problems; --fix removes unknown entries and rewrites canonically\nbattlestation doctor\nbattlestation doctor --fix\n\n# Inspect and edit the file without opening it (great for scripts and agents)\nbattlestation list --json\nbattlestation get dock.icon-size\nbattlestation set dock.icon-size 48\nbattlestation unset dock.icon-size\n\n# Machine-readable diff/apply for scripts and UIs\nbattlestation diff --json\nbattlestation apply --json --yes\n\n# Exit 1 when the system has drifted, so a script or CI job can act on it\nbattlestation diff --exit-code\n\n# Detailed help for any command\nbattlestation apply --help\n\n# All commands accept an explicit path\nbattlestation capture --file ~/settings/macbook.toml\n```\n\n`apply` is declarative: settings present in the TOML are written, and registry settings absent from the TOML (commented out) that are set on the system are deleted, restoring the macOS default. It shows the full change list and asks for confirmation before touching anything—`--yes` skips the prompt for scripted runs.\n\nBefore writing anything, `apply` saves a full snapshot of the pre-apply state next to your file (`battlestation.undo.toml`, gitignored)—revert any apply with `battlestation apply --file battlestation.undo.toml`. `diff` and `apply` also take `--json` for machine consumption (`apply --json` requires `--yes`), so scripts, agents, and a future UI all speak the same protocol.\n\nDuring development, run the same commands with `bun run src/index.ts <command>`, or use the repo shortcuts `bun run capture` and `bun run apply`, which operate on the gitignored `tmp/battlestation.toml` scratch file.\n\nThe captured TOML is fully annotated—every setting carries a comment explaining what it does and what its values mean, and settings that aren't set on the machine appear as commented-out keys so the file documents everything it can manage:\n\n```toml\n[keyboard]\n\n# How fast a held key repeats. Lower is faster; 1 is faster than System Settings allows (its fastest is 2).\nkey-repeat-rate = 1\n\n[dock]\n\n# Seconds the pointer must rest at the screen edge before the hidden Dock appears. 0 shows it instantly.\nauto-hide-delay = 0.0\n```\n\nEdit the file by hand, keep it in version control—**a private repo**: the capture includes personal details like your text replacements, installed apps, and default app choices—and `apply` it on a fresh machine. `apply` only touches settings that actually differ, restarts the affected processes once, and tells you when a change needs a re-login to take full effect.\n\n## Configuration\n\nThe manifest lives at `~/.battlestation.toml` by default, so you can run `battlestation diff` from anywhere without remembering a path. Configuration resolves through [`@lostgradient/environmentalist`](https://github.com/stevekinney/environmentalist), most-explicit-wins:\n\n| Priority | Source                                                       |\n| -------- | ------------------------------------------------------------ |\n| 1        | `--file` / `--interval` flags                                |\n| 2        | `BATTLESTATION_CONFIGURATION` / `BATTLESTATION_INTERVAL`     |\n| 3        | `.env` files                                                 |\n| 4        | `battlestation.config.{ts,js,json,toml,yaml}` in the project |\n| 5        | `~/.battlestation`, `~/.config/battlestation/config.*`       |\n| 6        | Defaults: `~/.battlestation.toml`, `weekly`                  |\n\n```bash\nexport BATTLESTATION_CONFIGURATION=~/dotfiles/mac.toml\nbattlestation diff                     # uses the exported path\nbattlestation diff --file ./other.toml # a flag still wins\n```\n\nPaths beginning with `~` are expanded, and relative paths resolve against the working directory. Only settings that describe _how battlestation runs_ are configurable this way — per-invocation switches like `--json`, `--yes`, and `--dry-run` are deliberately not, since a persisted `yes = true` would be a footgun rather than a convenience.\n\n## Catching drift\n\nmacOS updates and apps quietly reset preferences. `battlestation diff --exit-code` exits 1 when the live system no longer matches your file, the same way `git diff --exit-code` does, which makes drift something a script can act on:\n\n```bash\nbattlestation diff --exit-code || echo \"settings drifted\"\n```\n\n`battlestation schedule` wires that into a launchd agent that checks periodically and posts a macOS notification when something has moved:\n\n```bash\nbattlestation schedule --interval weekly --file ~/dotfiles/battlestation.toml\nbattlestation schedule --uninstall\n```\n\nThe agent only ever notifies — it never applies anything, because a background job that silently changed system settings would be a trap rather than a feature. It writes stderr to `~/Library/Logs/battlestation-drift-check.log`, so a broken agent is diagnosable instead of silent. Pass an absolute `--file` path: the agent runs outside your shell, with a different working directory.\n\n## MCP server\n\n`battlestation mcp` runs the tool as a STDIO [Model Context Protocol](https://modelcontextprotocol.io) server, exposing every capability as a tool—`capture`, `diff`, `apply`, `doctor`, `list_settings`, `get_setting`, `set_setting`, and `unset_setting`—backed by the exact same command implementations as the CLI. Apply runs without an interactive prompt; the MCP client's tool-approval flow is the confirmation, and the undo snapshot is still written first. Register it with Claude Code:\n\n```bash\nclaude mcp add battlestation -- npx battlestation mcp\n```\n\n## Value domains and validation\n\nEvery enumerated or bounded setting carries its legal values as data—`choices` with human labels, numeric `range`s with units—and the TOML legend comments are generated from that data, so they can never drift from what the tool validates. `doctor` checks values against these domains and reports out-of-domain values as advisory `[warning]`s (exit 0) rather than blocking errors: macOS often accepts values beyond what System Settings offers, and the file records what your system actually stores. Settings whose misuse can bite (keyboard shortcuts, input sources, default app handlers) are flagged `risk: caution` in the registry and surfaced in `--json` output.\n\n## Using it as a library\n\nThe package exports the full engine alongside the CLI: `registry` (with labels, descriptions, choices, ranges, and risk metadata per setting), `captureToml`, `diffSettings`, `applyChanges` (with a per-change progress callback), `readSetting`/`writeSetting`, and the TOML analyze/render functions. The CLI is a thin shell over these—anything it does, a menu-bar app or script can do in-process.\n\n## How it works\n\nEverything is driven by a declarative registry in `src/settings/`—each entry maps a friendly TOML address (like `dock.auto-hide-delay`) to a `defaults` domain and key, a value type, a human description, and the process that must restart for the change to stick. `capture` reads each key with `defaults read`, `apply` writes with `defaults write` (mirroring domains like the Bluetooth trackpad where macOS keeps duplicates), and the TOML is emitted by hand so every key keeps its documentation.\n\n## Releases\n\nPublishing a GitHub Release triggers `.github/workflows/executables.yaml`, which compiles standalone macOS executables (`bun build --compile`, Apple Silicon and Intel) and attaches them to the release as `battlestation-darwin-{arm64,x64}.tar.gz`. No Bun or Node required on the target machine.\n\n## Development\n\n```bash\nbun install\nbun test              # tests (100% coverage enforced)\nbun run check         # format check + lint + typecheck\nbun run validate      # the full gate, including build and package checks\nbun run build         # dual Node/Bun bundles in dist/\n```\n\n## License\n\nMIT\n"}