This is the full developer documentation for Dotfiles
# Dotfiles
> An agent-driven Omarchy setup for development, desktop, and automation.
These are my public [Omarchy](https://omarchy.org) dotfiles (Omarchy is an Arch Linux based distro), managed with [GNU Stow](https://www.gnu.org/software/stow/) and a single compiled `dot` binary. They cover shell, editor, and desktop config, plus the tooling that keeps multiple machines in sync.
These are my personal dotfiles
They are tightly tuned for my [Omarchy](https://omarchy.org) setup and my specific machines, and are **not** meant to be installed as-is by anyone else. They are unlikely to work on plain Arch (or any other distro) without replicating my setup, and they lean on a deeply integrated private overlay (`dotfiles-private`) that is not public, so the public repo on its own is an incomplete picture. Use them as a reference to borrow from, or pull individual pieces into your own dotfiles, rather than as a turnkey setup.
The shared [OpenCode](https://opencode.ai) config (agents, commands, skills, and plugins) is the exception: it is published separately as [`opencode-config`](https://github.com/timmo001/opencode-config) and deliberately written to be portable and reusable on its own.
AI-generated
Large parts of this project - including this documentation and much of the codebase - are generated or heavily assisted by LLMs and coding agents. Expect the rough edges that come with that, and verify anything before relying on it.
## At a glance
[Section titled “At a glance”](#at-a-glance)
Stow-based dotfiles
Public config for shell, editor, and tooling, rooted at `~/.config/dotfiles` and laid down with GNU Stow.
The dot binary
A single compiled binary ([Bun](https://bun.sh) + [Effect](https://effect.website) v4 + [OpenTUI](https://github.com/anomalyco/opentui)) with a TUI dashboard and a full CLI for setup, updates, and git tooling.
Git & GitHub tooling
Diff, log, status, and a notification inbox across every managed repo, with status-bar modules.
Omarchy & Hyprland
Managed Omarchy repos and per-host Hyprland overrides, with a desktop controls menu.
OpenCode & agents
Shared agents, commands, skills, and plugins, published to [opencode-config](https://github.com/timmo001/opencode-config).
Private overlay
A deeply integrated private overlay (`~/.config/dotfiles-private`) layers machine-specific config on top of the public repo.
## Get started
[Section titled “Get started”](#get-started)
[Install](/getting-started/install/)Prerequisites, bootstrap build, and the ongoing workflow.
[New Machine Checklist](/getting-started/new-machine/)Step-by-step setup for a new Omarchy machine.
## Explore
[Section titled “Explore”](#explore)
[The dot Command](/dot/)The TUI dashboard and CLI, with the stow, notes, and utilities workflows.
[Configuration](/configuration/)Environment options, private git config, and private packages.
[Git & GitHub](/git/)Diff, log, status, and notifications.
[Omarchy & Hyprland](/omarchy/)Managed Omarchy repos and per-host Hyprland overrides.
[Knowledge Base](/knowledge-base/)Practical runbooks for system customisations and one-off fixes.
[OpenCode & Agents](/opencode/)Agents, commands, skills, and plugins, with the generated reference.
## Reference
[Section titled “Reference”](#reference)
[Bar Integrations](/bar-integrations/)The shared --bar-json status-bar contract.
[Cleanup](/cleanup/)Remove stowed links and reverse setup side effects.
[Agents / LLMs](/agents-llms/)llms.txt bundles for loading the docs into AI-assisted sessions.
# Agents / LLMs
> Context bundles for working with this documentation in AI-assisted sessions.
This site publishes [llms.txt](https://llmstxt.org) bundles so the docs can be loaded as plain text during AI-assisted work. The files are generated at build time from the same source as the site, which keeps them aligned with the published pages without maintaining a second copy.
Use them when you want the dotfiles docs available outside the browser: setup steps, command behaviour, configuration notes, and the generated reference pages can all be pulled from the same published source.
## Pick A File
[Section titled “Pick A File”](#pick-a-file)
| File | Contents |
| ---------------------------------- | ---------------------------------------------------------------------------- |
| [/llms.txt](/llms.txt) | Start here when you want an index of the available bundles. |
| [/llms-full.txt](/llms-full.txt) | Use this when the task needs the whole docs site in one file. |
| [/llms-small.txt](/llms-small.txt) | Use this for quick lookups when the full site is more context than you need. |
The full bundle is the safest default for broad questions because it includes the generated command and OpenCode reference pages alongside the hand-written docs. The compact bundle is better for focused lookups where a smaller prompt matters more than complete coverage.
## Copy-Ready URLs
[Section titled “Copy-Ready URLs”](#copy-ready-urls)
Complete docs bundle:
```text
https://dotfiles.timmo.dev/llms-full.txt
```
Compact docs bundle:
```text
https://dotfiles.timmo.dev/llms-small.txt
```
Bundle index:
```text
https://dotfiles.timmo.dev/llms.txt
```
## Notes
[Section titled “Notes”](#notes)
* The bundles are generated from the docs source during the site build. If a page is stale, fix the source page rather than editing the generated text output.
* The URLs are stable, but the contents change whenever the docs are rebuilt from new commits.
* For repository-specific work, pair these docs with current branch context from [`context git`](https://context.timmo.dev/context/git/) and tech-stack context from [`context stack`](https://context.timmo.dev/context/stack/). The docs explain the workflow; the commands explain the live checkout.
# Bar Integrations
> The --bar-json status-bar contract shared by dot commands and external tools.
`--bar-json` is a shared status-bar contract rather than a single command. Tools emit one-line output with `text`, `tooltip`, and `class` fields. Git commands also include safe repository or notification rows for the native shell panel; generic status bars can ignore those extra fields and continue polling through their short-lived caches.
`dot git-diff` status scans disable Git’s optional index locks. Background bar and TUI polling can read working-tree state during a rebase, merge, or another index-writing operation without refreshing the index or competing for `.git/index.lock`.
The JSON format is bar-agnostic: it works with [Quickshell](https://quickshell.outfoxxed.me), [Waybar](https://github.com/Alexays/Waybar), or any bar that can run a command and parse JSON. My setup uses one `timmo.git` Quickshell service to poll repository and notification output directly, plus one `timmo.home-assistant` service for Home Assistant rows. The commands themselves do not depend on either plugin. See [Shell (Quickshell)](/omarchy/shell/) for the bar configuration.
## dot commands with bar output
[Section titled “dot commands with bar output”](#dot-commands-with-bar-output)
[Diff & Repo Watcher](/git/diff/)dot git-diff --bar-json flags managed repos that have uncommitted or unpushed changes.
[Notifications](/git/notifications/)dot git-notifications --bar-json shows the GitHub notification inbox; left click opens the filtered TUI.
The [command reference](/dot/commands/) lists the full `--bar-json` flag set on each command.
## External applications
[Section titled “External applications”](#external-applications)
`--bar-json` is not limited to `dot`. Other tools in this setup emit the same bar-friendly JSON, so one bar can poll them the same way. [go-automate](https://go-automate.timmo.dev) is one example: its [`--bar-json` reference](https://go-automate.timmo.dev/reference/bar-json/) documents the Home Assistant watcher output it exposes to a status bar.
go-automate’s entity watcher is a long-running stream, but a polled bar module or a dashboard source card wants one line per refresh. The bundled `ha-entity-bar-json-once` script bridges the two: it runs `go-automate ha bridge watch entity --bar-json`, reads the first line, stops the watcher, and prints that single line (or a hidden placeholder if nothing arrives). It takes the same flags as the watcher, for example:
```bash
ha-entity-bar-json-once --icon '' input_text.current_next_event_in_an_hour
```
### `ha-module-bar`
[Section titled “ha-module-bar”](#ha-module-bar)
`ha-module-bar` wraps go-automate with opinionated status-bar modules for common Home Assistant entities. Numeric readings keep the state text supplied by Home Assistant without display-side rounding. Each mode ships default entity IDs and display rules; override them with `--entity`, `--name`, `--icon`, and mode-specific flags when your setup differs. Run `ha-module-bar --help` for the full flag list.
Numeric shell items omit units to keep the bar compact. Their tooltips retain the unit of measurement.
| Mode | Behaviour |
| -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `temperature` | Show a signed numeric temperature reading with its Celsius unit. Add `--show-above ` to hide it unless the reading is strictly above that threshold. A matching `--gate-entity` with `--gate-state` or `--gate-below` also shows it. Use `--icon-only` to keep the reading in the tooltip. |
| `co2-alert` | Show a warning or critical CO₂ reading above 1400 ppm and 2000 ppm respectively; hide otherwise. |
| `voc-alert` | Show a warning or critical VOC reading when the quality entity is `very abnormal` or `extremely abnormal`. Uses `--quality-entity` and `--value-entity`. |
| `nas-activity` | Show NAS activity while the gate switch is on; highlight when the inactivity script is armed. Uses `--switch-entity` and `--inactive-script-entity`. |
| `dining-temperature` | Show dining-room temperature only while the air-conditioner target is below `--gate-below` (default `25`). Uses `--gate-entity` for the AC target. |
| `current-next-event` | Pass through the entity’s bar JSON when the text is non-empty. |
| `doorbell` | Stream doorbell state through `singleton-stream` so the bar can keep one long-lived watcher per module instance. |
Most modes poll once per refresh. The unified Home Assistant shell service owns those pollers alongside continuous `ha-watch-singleton` status streams. `doorbell` is the exception: it keeps a singleton stream alive for the shell ancestor process and emits JSON only when the output changes. Override the stream identity with `--stream-key` when multiple doorbell modules share an entity.
```bash
ha-module-bar temperature
ha-module-bar temperature --entity sensor.outdoor_temperature --show-above 25
ha-module-bar temperature --entity sensor.outdoor_temperature --show-above 25 --gate-entity input_boolean.air_conditioner --gate-state on
ha-module-bar co2-alert --entity sensor.office_co2
ha-module-bar dining-temperature --gate-below 24
ha-module-bar doorbell --entity input_boolean.doorbell
```
CO₂ and VOC modes accept `--fake-state warning` or `--fake-state critical` for layout testing (`WAYBAR_FAKE_CO2_ALERT` and `WAYBAR_FAKE_VOC_ALERT` set the same values per mode). `doorbell` accepts `--simulate on|off` or `--force-true` for the same purpose.
All modes except `doorbell` support optional trigger hooks that run a shell command when entity state changes:
```bash
ha-module-bar co2-alert \
--trigger-state critical \
--trigger-command 'notify-send "CO2 critical"' \
--trigger-on transition \
--trigger-cooldown 300
```
`--trigger-on` accepts `transition` (default; fire when state enters `--trigger-state`) or `match` (fire while state equals `--trigger-state`). `--trigger-initial true` allows the first observed state to fire; the default skips the initial read so restarts do not replay alerts. `--trigger-cooldown` enforces a minimum interval between fires. Trigger state persists under `$XDG_RUNTIME_DIR` (override the key with `--trigger-key` when multiple modules share a mode).
`dot dashboard` rejects commands that embed `singleton-stream` or `doorbell`, so use `ha-entity-bar-json-once` for dashboard cards and reserve `ha-module-bar doorbell` for the streaming Home Assistant shell service. See [Private Dashboard Config](/configuration/private-dashboard/).
### `package-updates-bar`
[Section titled “package-updates-bar”](#package-updates-bar)
`package-updates-bar` reports updates for packages listed in `.dot-public-packages`. It caches status-bar JSON under `$XDG_CACHE_HOME/status-bar`, refreshes stale data in the background, and keeps the previous AUR backoff behaviour. Run `package-updates-bar refresh` for an immediate retry.
## Configuration
[Section titled “Configuration”](#configuration)
Which repos and which activity reach the bar is controlled by the private `dot-git.yml` config; the `notifications.bar.ignore_bot_activity` key tames bot noise. See [Private Git Config](/configuration/private-git/).
## Health checks
[Section titled “Health checks”](#health-checks)
`dot doctor` verifies `dot-git.yml` and GitHub notification API access.
# Cleanup
> Remove stowed links and manually reverse first-use setup changes.
Use this page when you want to remove the changes `dot` made to a machine. The safe first step is always `dot clean`; everything after that is optional and more destructive.
Caution
Review each command before running it. Some steps remove system config, package repositories, cloned repos, or user services. Do not run the whole page as a script.
## Remove stowed links
[Section titled “Remove stowed links”](#remove-stowed-links)
`dot clean` only removes symlinks managed by GNU Stow. It unstows the private overlay first when it is available, then the public packages.
```bash
dot clean
```
This does not remove packages, cloned repositories, pacman config, firewall rules, systemd user units, shell changes, generated agent instruction copies, logs, or backup files.
## Restore backed-up files
[Section titled “Restore backed-up files”](#restore-backed-up-files)
`dot install`, `dot init`, and `dot stow` move conflicting live files into backup paths instead of deleting them. Check the public/private repo backup directories and any Omarchy init backups before removing the repos.
```bash
ls ~/.config/dotfiles/backup
ls ~/.config/dotfiles-private/backup
ls ~/.config/*.dot-init-backup-*
```
Move back only the files you still want to keep.
## Disable user services
[Section titled “Disable user services”](#disable-user-services)
Disable user timers/services that were enabled from these dotfiles.
```bash
systemctl --user disable --now dot-doctor-startup.timer
systemctl --user disable --now daily-volume-zero.timer
systemctl --user daemon-reload
```
`daily-volume-zero.timer` only exists on laptop stow packages, so that command may report that the unit is missing.
## Remove synced agent instruction copies
[Section titled “Remove synced agent instruction copies”](#remove-synced-agent-instruction-copies)
`dot init` runs `dot agents-sync`, which mirrors the global OpenCode agent instructions into other harnesses. Remove these only if you want those harnesses unmanaged too. Check for the `dot agents-sync` header before deleting anything you may have edited by hand.
```bash
grep -H "dot agents-sync" ~/.cursor/rules/global-agents.mdc ~/.claude/CLAUDE.md
rm -f ~/.cursor/rules/global-agents.mdc ~/.claude/CLAUDE.md
```
## Remove managed firewall rules
[Section titled “Remove managed firewall rules”](#remove-managed-firewall-rules)
`dot init` adds managed `ufw` allow rules when `ufw` is installed. Delete only the rules you no longer want.
```bash
sudo ufw status numbered
sudo ufw delete
sudo ufw reload
```
The managed rules are labelled in `ufw status` with their purpose, for example KDE Connect, Home Assistant, OpenCode server, LocalSend, and libvirt.
## Remove public pacman repo config
[Section titled “Remove public pacman repo config”](#remove-public-pacman-repo-config)
Remove the managed include and snippet, then refresh package databases. Do not replace the signed configuration with `TrustAll` or another relaxed signature policy.
```bash
sudoedit /etc/pacman.conf
sudo rm -f /etc/pacman.d/timmo.conf
sudo pacman-key --delete F94469C08E3B717014E2815FA026A3671E9151DA
sudo pacman -Syu
```
Remove this line from `/etc/pacman.conf` if present:
```ini
Include = /etc/pacman.d/timmo.conf
```
Existing installed packages remain installed. AUR helpers remain available and can build subsequent versions from AUR. To recover a damaged or removed setup, run `dot setup-public-repo`; it downloads the key again and refuses to trust it unless the full fingerprint matches.
## Remove private pacman repo config
[Section titled “Remove private pacman repo config”](#remove-private-pacman-repo-config)
If `dot setup-private-repo` or `dot init` registered the private package repo, remove the include line and snippet manually.
```bash
sudoedit /etc/pacman.conf
sudo rm -f /etc/pacman.d/timmo-private.conf
sudo pacman -Sy
```
Remove this line from `/etc/pacman.conf` if present:
```ini
Include = /etc/pacman.d/timmo-private.conf
```
If your private config overrides `DOT_PRIVATE_PACMAN_REPO_CONFIG` or `DOT_PRIVATE_PACMAN_MAIN_CONFIG`, use those paths instead.
`dot setup-private-repo` also syncs a local `file://` mirror from the private package repo config. If you want to remove that mirror and source clone too, check `path=` and `mirror_path=` in `~/.config/dotfiles-private/.dot-private-package-repo` and remove only those directories.
## Remove pacman hooks
[Section titled “Remove pacman hooks”](#remove-pacman-hooks)
Public/private pacman hooks are installed into `/etc/pacman.d/hooks` from stowed hook sources. Remove only hooks that came from these dotfiles.
```bash
ls ~/.config/pacman-hooks
ls /etc/pacman.d/hooks
sudo rm /etc/pacman.d/hooks/.hook
```
## Restore the login shell
[Section titled “Restore the login shell”](#restore-the-login-shell)
`dot init` sets the login shell to zsh when needed. Change it back if you no longer want zsh as the login shell.
```bash
chsh -s /bin/bash "$USER"
```
If init added `/usr/bin/zsh` to `/etc/shells`, leave it unless you know nothing else on the machine needs it.
## Remove Git config include
[Section titled “Remove Git config include”](#remove-git-config-include)
`dot init` adds the managed Git include to your global Git config. Remove it if you no longer want Git to load the stowed dotfiles settings.
```bash
git config --global --fixed-value --unset-all include.path "~/.config/git/config.dotfiles"
```
## Revert generated locales
[Section titled “Revert generated locales”](#revert-generated-locales)
`dot init` ensures `en_GB.UTF-8` is enabled in `/etc/locale.gen` and runs `locale-gen`. Leave the locale in place unless you specifically want to remove it.
```bash
sudoedit /etc/locale.gen
sudo locale-gen
```
## Remove cloned repos and state
[Section titled “Remove cloned repos and state”](#remove-cloned-repos-and-state)
After the stowed links and system config are removed, delete cloned repos and generated state only if you no longer need them.
```bash
rm -rf ~/.config/dotfiles-private
rm -rf ~/.local/state/dot ~/.cache/dot
```
Private package repos and other private Git clones are configured by the private overlay. Review `~/.config/dotfiles-private/.dot-private-package-repo` and `~/.config/dotfiles-private/dot-git.yml`, then remove only clones and mirrors you no longer need.
If you removed stowed config directories that Omarchy should own again, refresh the stock Omarchy defaults afterwards. Quattro’s UWSM defaults are package-owned under `/usr/share`, so there is no `uwsm/env` user config to refresh.
```bash
omarchy refresh shell
omarchy refresh hyprland
omarchy refresh config ghostty/config
```
Run `omarchy refresh --help` on the target machine for the exact refresh commands supported by that Omarchy version.
Do not remove `~/.config/dotfiles` until you no longer need the `dot` binary, docs, or backup directory.
## Remove installed packages
[Section titled “Remove installed packages”](#remove-installed-packages)
`dot init` installs the public/private package lists but does not track ownership of packages afterwards. Removing packages is intentionally manual. This command only covers the default public package list; if you override `DOT_PUBLIC_PACKAGES_FILE`, use that file instead.
```bash
comm -12 <(sort ~/.config/dotfiles/.dot-public-packages) <(pacman -Qq | sort)
```
Review that output, then remove only packages you no longer use.
```bash
sudo pacman -Rns
```
For private packages, review the private package list before removing anything.
`dot install` and `dot init` may also install setup prerequisites such as `stow`, `gum`, or `mise` when they are missing. Remove those manually only if nothing else uses them.
## Remove mise-managed tools
[Section titled “Remove mise-managed tools”](#remove-mise-managed-tools)
`dot init` runs `mise install`, which can install language and CLI tool versions from the stowed mise config. Remove only versions you no longer use.
```bash
mise ls --installed
mise uninstall
```
## Remove GitHub CLI extensions
[Section titled “Remove GitHub CLI extensions”](#remove-github-cli-extensions)
`dot init` installs the GitHub CLI extensions listed in `.dot-gh-extensions` when `gh` is available. Remove only extensions you no longer want.
```bash
gh extension list
gh extension remove
```
## Final check
[Section titled “Final check”](#final-check)
Run doctor after cleanup to see what managed state remains. It will report missing dotfiles pieces if the repo is partly removed, which is expected during a full uninstall.
```bash
dot doctor
```
# Configuration
> Environment options and private git configuration.
Most behaviour is controlled by explicit CLI flags. Runtime configuration uses environment variables for process context, tracked application config files, and private YAML files for git/GitHub repo tracking and optional dashboard source cards.
[Environment Variables](/configuration/environment/)All DOT\_\*, DOTFILES\_\*, and OMARCHY\_\* options.
[Private Git Config](/configuration/private-git/)The dot-git.yml schema for repo activity and notifications.
[Private Dashboard Config](/configuration/private-dashboard/)The dot-dashboard.yml schema for optional dashboard source cards.
[Yazi](/configuration/yazi/)Restore the shared Yazi plugin set across devices.
[Plannotator](/configuration/plannotator/)Restore shared Plannotator review settings across devices.
[Lazygit](/configuration/lazygit/)Run shared repository maintenance commands from Lazygit.
[Cursor](/configuration/cursor/)Restore the last window type or choose Editor and Agents explicitly.
[Packages](/configuration/packages/)Use the signed public repository, AUR fallback, and private packages.
Configuration policy
For dotfiles and system scripts, prefer explicit CLI flags over environment-variable toggles for runtime behaviour. Environment variables are used for standard process context (`HOME`, `PATH`, `XDG_*`), secrets, paths, and compatibility shims.
# Cursor
> Cursor launchers and shell command defaults.
Cursor’s system desktop entry and the `cursor` shell function use plain launch behaviour, which routes to the last active window type.
Two additional application entries provide explicit choices in the desktop launcher:
* **Cursor Editor** opens the Editor window with `--classic`.
* **Cursor Agents** uses Cursor’s `cursor://anysphere.cursor-deeplink/glass` route to open or focus the Agents window. Cursor calls this interface Glass internally; there is no `--modern` flag.
# Environment Variables
> All DOT_*, DOTFILES_*, and OMARCHY_* options that influence dot.
These variables tune paths and behaviour for `dot`. Most have sensible defaults; set them only when you need to override the default.
## Managed toolchain
[Section titled “Managed toolchain”](#managed-toolchain)
The global mise configuration (`mise/.config/mise/config.toml`) pins the shared developer toolchain, including the Skills CLI used by `dot skill-updates` and [memex](https://github.com/nicosuave/memex) for browsing and searching local coding-agent history. The matching Herdr plugin provides session palettes, browsing, resume, indexing, and web UI actions. Node.js and Bun versions are also declared in the repository root `mise.toml` for `dot` and `docs` tasks.
Python 3.14 and [uv](https://docs.astral.sh/uv/) are installed through mise for Python package and virtual-environment management. Use `uv` directly for project work; `topgrade` leaves mise pins unchanged unless you run `mise install` or `dot update`.
The stowed `mise` wrapper keeps the global config under dotfiles control. Commands such as Omarchy’s `mise use -g ` are redirected to `~/.local/state/mise/omarchy-config.toml`, while reads, installs, upgrades, project-local writes, and mise-generated tool shims continue normally. To intentionally change the stowed global config, opt in explicitly:
```bash
mise --write-global-config use -g --pin node@26
```
The configuration also provides the Android SDK command-line tools and sets the SDK environment for Gradle and `sdkmanager`. Individual Android repositories still declare their required platform and build-tools versions; install those SDK packages with `sdkmanager` when a checkout requires them.
## Paths and overlay
[Section titled “Paths and overlay”](#paths-and-overlay)
| Variable | Description |
| ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| `DOTFILES_PUBLIC_DIR` | Public dotfiles path (default `~/.config/dotfiles`). |
| `DOTFILES_PRIVATE_DIR` | Private dotfiles path (default `~/.config/dotfiles-private`). |
| `DOT_ALLOW_PRIVATE` | Private overlay policy: optional `auto`, required `always`, or skipped `never` (default `auto`). A failed attempted clone is fatal in `auto` mode. |
## Git and GitHub
[Section titled “Git and GitHub”](#git-and-github)
| Variable | Description |
| ---------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `DOT_GIT_CONFIG_FILE` | Private git repo config for `git-diff`, `git-notifications --bar-json`, `update`, and `doctor` (default `$DOTFILES_PRIVATE_DIR/dot-git.yml`). |
| `DOT_GITHUB_RETRIES` | Extra `gh` retry attempts after the first try (default `2`). |
| `DOT_GITHUB_RATE_LIMIT_TTL_SECONDS` | Seconds to cache `gh api rate_limit` results (default `60`). |
| `DOT_GITHUB_RATE_LIMIT_MIN_REMAINING` | Minimum REST quota remaining before `gh` calls wait (default `0`). |
| `DOT_GITHUB_RATE_LIMIT_MAX_WAIT_SECONDS` | Upper bound on rate-limit backoff waits (default `60`). |
| `DOT_FETCH_TTL_SECONDS` | Seconds to reuse the last upstream fetch (default `300`). |
| `DOT_GH_EXTENSIONS_FILE` | Public `gh` extension list installed by `dot init` (default `$DOTFILES_PUBLIC_DIR/.dot-gh-extensions`). |
| `DOT_GH_MCP_BEARER` | Bearer token for the read-only GitHub MCP server. The shell wrappers and `opencode-server` set it only for agent harness processes; it is not exported globally. |
## Packages
[Section titled “Packages”](#packages)
| Variable | Description |
| --------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `DOT_PUBLIC_PACKAGES_FILE` | Public Arch/AUR package list for `dot init` and package checks (default `$DOTFILES_PUBLIC_DIR/.dot-public-packages`). |
| `DOT_PUBLIC_PACMAN_REPO_CONFIG` | Signed public pacman repo snippet written by `dot` (default `/etc/pacman.d/timmo.conf`). |
| `DOT_PUBLIC_PACMAN_MAIN_CONFIG` | Main pacman config scanned for the public repo include (default `/etc/pacman.conf`). |
| `DOT_PRIVATE_PACKAGE_REPO_FILE` | Private pacman repo config (default `$DOTFILES_PRIVATE_DIR/.dot-private-package-repo`). |
| `DOT_PRIVATE_PACKAGES_FILE` | Private package list override (default base list plus `.dot-private-packages--`). |
| `DOT_PRIVATE_PACKAGE_MAP_FILE` | Private package name-to-source map for `dot private-pkg-publish` (default `$DOTFILES_PRIVATE_DIR/.dot-private-package-map`). |
| `DOT_PRIVATE_PACMAN_REPO_CONFIG` | Pacman repo snippet path written by `dot` (default `/etc/pacman.d/timmo-private.conf`). |
| `DOT_PRIVATE_PACMAN_MAIN_CONFIG` | Main pacman config file scanned for the private repo `Include` (default `/etc/pacman.conf`). |
| `DOT_PRIVATE_BROWSER_CHECKS_FILE` | Private browser-extension doctor checks (default `$DOTFILES_PRIVATE_DIR/.dot-browser-checks`). |
## Omarchy and Hyprland
[Section titled “Omarchy and Hyprland”](#omarchy-and-hyprland)
| Variable | Description |
| ----------------------- | ------------------------------------------------------------------------------------------------------ |
| `OMARCHY_REPO_BASE_DIR` | Omarchy repo base path (default `~/.config`). |
| `OMARCHY_HOST` | Hypr host override name; `dot init` defaults to `desktop` when unset unless `--host ` is passed. |
## Init and logging
[Section titled “Init and logging”](#init-and-logging)
| Variable | Description |
| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| `DOT_INIT_NONINTERACTIVE` | Force non-interactive init mode (`1\|0`, default `0`). |
| `DOT_INIT_LOG_FILE` | Default `dot init` log path when `--log` is not passed (default `~/.local/state/dot/init.log`). |
| `DOT_LOG_FILE` | Active log file for the current `dot` run. Set automatically during `dot init`; child commands inherit it when `DOT_TEE_INHERIT_LOG=1`. |
| `DOT_LOG_MIRROR_FILE` | Secondary log destination mirrored after each write. Set automatically when `dot init --log` targets a GVFS path that cannot be written directly. |
| `DOT_TEE_INHERIT_LOG` | When `1`, child processes launched by `dot` append stdout/stderr to `DOT_LOG_FILE` (set automatically during `dot init`). |
| `DOT_UFW_RULES_FILE` | ufw rules file scanned by the firewall setup and doctor check (default `/etc/ufw/user.rules`). |
## Agents sync
[Section titled “Agents sync”](#agents-sync)
`dot agents-sync` mirrors `~/.config/opencode/AGENTS.md` to agent harness instruction files. It runs automatically at the end of full `dot update` and `dot init`; phase-scoped updates skip it. There is no environment toggle to disable it.
Each target receives a transformed copy with harness-appropriate formatting where needed. All targets include a `dot agents-sync` HTML comment with source path and timestamp. OpenCode remains the single source of truth; other agent harnesses get plain file copies, not symlinks.
| Variable | Description |
| --------------------------- | --------------------------------------------------------------------------------------------- |
| `DOT_AGENTS_SYNC_SOURCE` | AGENTS file to mirror (default `~/.config/opencode/AGENTS.md`). |
| `DOT_AGENTS_SYNC_RULE_FILE` | Override path for a primary agents-sync target (default `~/.cursor/rules/global-agents.mdc`). |
## MCP sync
[Section titled “MCP sync”](#mcp-sync)
| Variable | Description |
| --------------------- | --------------------------------------------------------------------------------------- |
| `DOT_MCP_CONFIG_FILE` | Private MCP sync spec read by `dot mcp-sync` (default `$DOTFILES_PRIVATE_DIR/mcp.yml`). |
## Debugging and output
[Section titled “Debugging and output”](#debugging-and-output)
| Variable | Description |
| ------------------------- | --------------------------------------------------------------------------------------------- |
| `DOT_DEBUG` | Enable stderr debug logging from `dot` subsystems (`1` or any non-empty value). |
| `DOT_USAGE_DIR` | Usage event root (default `$XDG_STATE_HOME/tool-usage`). |
| `DOT_USAGE_DISABLE` | Disable automatic live `dot` usage recording when set to `1`; explicit backfill still writes. |
| `DOT_CONTEXT_CAPTURE` | Capture assembled OpenCode starter context when set to `1`. |
| `DOT_CONTEXT_CAPTURE_DIR` | Parent for private `context-baseline-*` capture directories (default `/tmp/opencode`). |
| `NO_COLOR` | Disable ANSI colour in TTY-aware output such as `context git` (any non-empty value). |
Context capture writes numbered system-prompt segments, `system-index.json`, per-tool schemas, and `tools.jsonl` into a unique private child directory. It measures assembled context without changing prompts or tool definitions.
## Agent detection
[Section titled “Agent detection”](#agent-detection)
| Variable | Description |
| ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| `DOT_AGENT` | Override AI agent detection used by `dot is-agent` and the interactive-TUI guard: `1` forces agent mode, `0` forces it off, unset auto-detects. |
## Notes
[Section titled “Notes”](#notes)
| Variable | Description |
| --------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| `NOTES` | Notes vault git repo used by the standalone `notes` CLI/MCP server and OpenCode note commands (preferred; default `~/Documents/notes`). |
| `DOT_NOTES_DIR` | Compatibility notes vault override used when `NOTES` is unset. |
# Lazygit
> Run shared repository maintenance commands from Lazygit.
The Lazygit configuration is stowed from `lazygit/.config/lazygit/config.yml`.
Press `Ctrl+B` in the Local Branches panel to run `gh poi`, which cleans up local branches whose pull requests have been merged. Lazygit suspends its interface while the command runs in the terminal, then restores it when the command exits.
Press `Ctrl+F` in the Local Branches panel to run `git-rebase-default`. The command resolves and fetches the default branch from `upstream`, falling back to `origin`, then rebases the checked-out branch onto it with `--autostash`. If local `/HEAD` is missing or disagrees with the advertised default, the shared resolver asks before repairing it and fails without prompting under an agent. Run the same command from a shell with the `grd` alias.
# Packages
> Install packages from the signed public repository, AUR, and private repository.
`dot` configures the signed public `timmo` repository, keeps AUR available as a source-build fallback, and can build and publish mapped private packages into a separate local repository.
## Public repository
[Section titled “Public repository”](#public-repository)
`dot init` runs `dot setup-public-repo` before resolving public packages. The setup downloads the public key from `packages.timmo.dev`, requires the pinned full fingerprint `F94469C08E3B717014E2815FA026A3671E9151DA`, locally signs it in pacman’s keyring, and registers this snippet before the other package repositories:
```ini
[timmo]
SigLevel = PackageRequired DatabaseOptional TrustedOnly
Server = https://packages.timmo.dev/$arch
```
The repository overlays maintained package names. `omarchy-pkg-aur-add` uses the configured binary repository when a matching package is available and retains AUR resolution for packages or source-build variants not published there. A missing repository database or fingerprint mismatch stops setup before trust or pacman configuration is changed.
## Public packages
[Section titled “Public packages”](#public-packages)
`dot init` installs the Arch and AUR packages listed in `.dot-public-packages` at the repo root. The file is one package name per line; blank lines and `#` comments are ignored. Ongoing package health is reported by `dot doctor`; `dot update` does not check or install packages. Its pull phase does resync the configured private repository mirror and refresh pacman metadata when that repository is updated.
The list covers shared tooling rather than desktop apps you might install separately, including build helpers, diagnostics, shell and terminal tools, and desktop integrations. Override the path with `DOT_PUBLIC_PACKAGES_FILE`.
Missing public packages are installed with `omarchy-pkg-aur-add`; already-installed packages are left in place. When the package list requests the official `mise` package, init removes the conflicting AUR `mise-bin` package before installation. `dot doctor` checks the public repository trust and configuration plus the public and private package lists after setup.
Private packages from `.dot-private-packages` in the private overlay are installed after the public list during init when the overlay is available. A host-specific `.dot-private-packages--` list is additive, so hardware-specific packages can be limited to hosts such as `desktop`.
## Register the private repo
[Section titled “Register the private repo”](#register-the-private-repo)
```bash
dot setup-private-repo
```
Syncs the private Arch package repo mirror, writes the private pacman repo snippet, adds the `Include` line to `/etc/pacman.conf` when it is missing, and refreshes pacman metadata. If the local source clone is missing, setup skips cloning only when the configured mirror already contains `.db`, `.db.tar.gz`, or `.db.tar.zst` and pacman registration is current. This supports an already usable local mirror, but publishing still requires the configured source clone to exist. The command repairs Omarchy `pacman.conf` refreshes that remove local repository includes. Privileged writes prefer `pkexec` and fall back to `sudo`.
## Publish a package
[Section titled “Publish a package”](#publish-a-package)
```bash
dot private-pkg-publish
dot private-pkg-publish --install
dot private-pkg-publish --skip-build --no-git
```
Builds and publishes a mapped private package into the private pacman repo, syncs the mirror, refreshes pacman metadata, optionally installs it, and commits/pushes by default.
| Flag | Effect |
| -------------- | ------------------------------------------------------- |
| `--no-git` | Skip the package repo commit and push. |
| `--skip-build` | Publish an existing dist package artifact. |
| `--install` | Install the published package after syncing the mirror. |
## Configuration
[Section titled “Configuration”](#configuration)
Package lists, the repo map, and pacman paths are overridable with environment variables:
* `DOT_PUBLIC_PACKAGES_FILE` — public Arch/AUR package list (default `$DOTFILES_PUBLIC_DIR/.dot-public-packages`).
* `DOT_PUBLIC_PACMAN_REPO_CONFIG` — public pacman repo snippet path written by `dot` (default `/etc/pacman.d/timmo.conf`).
* `DOT_PUBLIC_PACMAN_MAIN_CONFIG` — main pacman config scanned for the public repo include (default `/etc/pacman.conf`).
* `DOT_PRIVATE_PACKAGE_REPO_FILE` — private pacman repo config (default `$DOTFILES_PRIVATE_DIR/.dot-private-package-repo`).
* `DOT_PRIVATE_PACKAGES_FILE` — private package list override (default `$DOTFILES_PRIVATE_DIR/.dot-private-packages` plus `.dot-private-packages--` when present).
* `DOT_PRIVATE_PACKAGE_MAP_FILE` — package name-to-source map for `dot private-pkg-publish` (default `$DOTFILES_PRIVATE_DIR/.dot-private-package-map`).
* `DOT_PRIVATE_PACMAN_REPO_CONFIG` — pacman repo snippet path written by `dot` (default `/etc/pacman.d/timmo-private.conf`).
* `DOT_PRIVATE_PACMAN_MAIN_CONFIG` — main pacman config scanned for the private repo `Include` (default `/etc/pacman.conf`).
See [Environment Variables](/configuration/environment/) for the full list.
# Plannotator
> Restore shared Plannotator review settings across devices.
Plannotator provides a local browser interface for reviewing plans, code changes, files, folders, and web pages. The CLI is installed from the pinned `backnotprop/plannotator` GitHub release through mise because it is not available from Arch, AUR, or the Aqua registry.
See the [Plannotator documentation](https://plannotator.ai/docs/) for installation, commands, OpenCode integration, configuration, remote use, and sharing.
## Dotfiles integration
[Section titled “Dotfiles integration”](#dotfiles-integration)
This setup adds:
* The `plannotator` CLI through mise’s GitHub backend.
* The `@plannotator/opencode` plugin in the private OpenCode configuration.
* `/plannotator-review`, `/plannotator-annotate`, and `/plannotator-last` command registrations.
* A public stow package for portable settings.
* A dedicated Chromium window for every browser review surface.
The plugin uses the `plan-agent` workflow and explicitly targets OpenCode’s built-in `plan` agent. The `/plan` command starts that agent directly, while execution agents such as `build-ask` and `refactorer` can hand broad work to it through `plan_enter`. They do not need to be listed as Plannotator planning agents because `submit_plan` runs after the handoff, inside the built-in `plan` agent.
This keeps `submit_plan` out of build and specialist agents. The plugin’s `planningAgents` option only needs another name if a separate custom planning agent is added later.
The command files contain frontmatter only because the plugin handles their names through OpenCode’s command hook. Restart OpenCode after changing its plugin configuration.
For usage, see the upstream [OpenCode integration guide](https://plannotator.ai/docs/guides/opencode/) and its linked command guides.
Code review feedback authorises the agent to apply the requested corrections immediately. The correction run stays within the human feedback, performs relevant validation, and asks before proceeding when a request is unclear, conflicts with the codebase, or cannot be applied safely.
## Browser workspace
[Section titled “Browser workspace”](#browser-workspace)
Plans, reviews, annotations, archives, and reopened sessions open in a dedicated Chromium window on Hyprland workspace `2`. The desktop session supplies Plannotator with:
```text
PLANNOTATOR_BROWSER=plannotator-browser
```
The launcher gives Chromium the `plannotator` class and an isolated data directory under `${XDG_DATA_HOME:-~/.local/share}/plannotator-browser`. The separate browser state prevents Chromium from handing the URL to a normal running browser process and discarding the dedicated class. A shared Hyprland rule moves that class to workspace `2` without switching the active workspace. Later Plannotator surfaces reuse that browser process and open in new tabs.
After changing `PLANNOTATOR_BROWSER`, relaunch Hyprland so newly started agents inherit it. A config reload is sufficient for changes to the matching window rule itself.
## Local data
[Section titled “Local data”](#local-data)
Only `config.json` belongs in dotfiles. The rest of `~/.plannotator/` contains runtime or machine-local data and is deliberately not tracked:
This includes drafts, sessions, semantic-diff data, history, and saved plans. Keeping these directories local avoids publishing reviewed project content, transient state, or machine-specific session details.
# Private Dashboard Config
> The dot-dashboard.yml schema for optional dashboard source cards.
`dot dashboard` reads an optional private YAML file that wires bounded external commands into dashboard cards. It lives in the private overlay at `$DOTFILES_PRIVATE_DIR/dot-dashboard.yml` by default.
Without this file, the dashboard still shows git diff, notifications, and update cards from in-process services. The optional `sources` block adds Twitch, environment, calendar, and todo cards when commands are configured.
## Schema
[Section titled “Schema”](#schema)
The file has a top-level `sources` map. Each key is a source ID; each value is a bounded shell command plus optional display metadata:
| Key | Type | Required | Purpose |
| -------------- | ------ | -------- | ----------------------------------------------------------------------------------------------------- |
| `command` | string | yes | One-shot shell command run on each refresh. Must exit quickly and print a single JSON line on stdout. |
| `unit` | string | no | Unit suffix appended to numeric readings (for example `°C` or `ppm`). |
| `open_command` | string | no | Shell command run when Enter opens the card (for example a Home Assistant more-info launcher). |
Supported source IDs:
| Source ID | Dashboard card |
| --------------- | ----------------------------------------------------------- |
| `twitch` | Live Channels |
| `calendar` | Events in the next hour (hidden when the source is missing) |
| `todo_my_tasks` | My Tasks |
| `todo_work` | Work Tasks |
| `temperature` | Temperature |
| `co2` | CO2 |
| `voc` | VOC |
Example:
```yaml
sources:
twitch:
command: twitch-notifications --status-bar-json --max-chars 60
calendar:
command: ha-entity-bar-json-once input_text.current_next_event_in_an_hour
temperature:
command: ha-entity-bar-json-once sensor.living_room_temperature
unit: "°C"
open_command: home-assistant-tui more-info sensor.living_room_temperature
todo_my_tasks:
command: home-assistant-tui todo todo.my_tasks --bar-json
open_command: home-assistant-tui todo todo.my_tasks
```
## Command contract
[Section titled “Command contract”](#command-contract)
Each `command` must behave like a status-bar poll, not a long-running watcher:
* Print one JSON object on the first line of stdout.
* Use the shared `--bar-json` status-bar shape: `text`, `tooltip`, and `class` fields. See [Bar Integrations](/bar-integrations/).
* Finish within eight seconds. `dot dashboard` kills overdue commands with `SIGTERM`.
* Avoid unbounded stream helpers. Commands containing `ha-watch-singleton`, `singleton-stream`, or `doorbell` are rejected as unsafe.
On refresh failure the card shows a diagnostic message; a `{"error":"..."}` JSON line is treated as an error state. An empty first line hides the card.
## Refresh behaviour
[Section titled “Refresh behaviour”](#refresh-behaviour)
After the initial load, dashboard sources refresh automatically every 60 seconds and on manual refresh (`r` in the TUI). Git and notification cards use the same live services as their dedicated TUI views.
Private by default
`dot-dashboard.yml` lives in the private dotfiles overlay because it contains machine-specific commands and entity names. The public dotfiles only contain the logic that reads it.
# Private Git Config
> The dot-git.yml schema for repository shortcuts, updates, activity, and notifications.
The git and GitHub tooling reads a private YAML config that lists the repositories to track and how. It lives in the private overlay at `$DOTFILES_PRIVATE_DIR/dot-git.yml` by default (override with `DOT_GIT_CONFIG_FILE`).
It is consumed by `dot git-diff`, `dot git-notifications --bar-json`, `dot notes-capture-sync`, `dot stow`, `dot update`, and `dot doctor`.
## Per-repo keys
[Section titled “Per-repo keys”](#per-repo-keys)
Each repo entry has two required sections, each with an explicit `enabled` flag and a 5-field cron `schedule`:
The optional `aliases` list generates Zsh repository shortcuts during `dot stow` and `dot update`. The same entries feed Herdr’s repository picker alongside `~/` and `~/repos`. Inside Herdr, a shortcut changes directory in the current tab when it already belongs to the repository workspace; otherwise it creates or focuses that workspace using the repository’s `name`. Outside Herdr, it changes directory normally:
```yaml
- name: "[HA] Frontend"
path: ~/repos/home-assistant/frontend
aliases: [haf]
```
Top-level `shortcuts` use the same `name`, `path`, and `aliases` keys for directories that are not managed repositories, such as the private dotfiles checkout or a repository group directory.
Open the Herdr picker with `prefix+s`. Its cache is regenerated from validated `dot-git.yml` data by `dot stow` and `dot update`. The Home and Repos entries leave the workspace title unset.
An optional `post_update` command runs from the repository root when `dot update` successfully pulls that repo to a new HEAD. Use it for local generated artefacts that must match the checkout, such as rebuilding an unpacked browser extension:
```yaml
post_update: mise run build
```
The command does not run when the repo is already current or its pull is skipped. A failure or five-minute timeout stops the update so stale build output is not silently retained.
* `activity` — include the repo in `dot git-diff` and pull it during `dot update`.
* `notifications` — include the repo in GitHub notification checks and in the notes capture repository picker generated by `dot notes-capture-sync`.
`dot notes-capture-sync` finds the managed `timmo001/notes` checkout, mirrors the active Worker’s non-secret variables and KV bindings into its ignored `capture/wrangler.local.jsonc`, and updates `vars.CAPTURE_REPOSITORIES`. Secret values are never written locally. When the live picker differs, the command deploys the capture Worker; `keep_vars` preserves dashboard-managed runtime variables and secrets on later Git-connected builds. Run it explicitly or from the Dot TUI when watched repositories change.
* `notifications` — include the repo in the notification inbox surfaces.
The `notifications.bar.ignore_bot_activity` key controls status-bar bot noise. A repo’s `remote.upstream.url` lets upstream notifications match a managed fork.
Private by default
`dot-git.yml` lives in the private dotfiles overlay because it contains a machine- and user-specific repository list. The public dotfiles only contain the logic that reads it.
## Requirements
[Section titled “Requirements”](#requirements)
* `dot git-notifications` requires `gh` authenticated with a classic token carrying the `notifications` or `repo` scope.
* `dot doctor` verifies `dot-git.yml` and GitHub notification access.
# Yazi
> Restore the shared Yazi plugin set across devices.
Yazi is installed from the public package list. Its configuration and `package.toml` lockfile are stowed from `yazi/.config/yazi/`, while downloaded plugin code remains local to each device.
In the file manager, `Enter` only enters directories. Press `o` to explicitly open the selected file, or `O` to choose how to open it. Both `q` and the Vim-style `:q` followed by `Enter` quit Yazi.
After `dot init` or `dot update` has stowed the configuration, restore the locked plugins:
```bash
ya pkg install
```
Add, remove, or upgrade plugins with `ya pkg`. These commands update the tracked lockfile through its stowed path:
```bash
ya pkg add yazi-rs/plugins:git
ya pkg delete yazi-rs/plugins:git
ya pkg upgrade
```
# The dot Command
> The TUI dashboard and CLI that drives the dotfiles system.
`dot` is the single binary that manages these dotfiles. It is compiled from `dot/` in the repo ([Bun](https://bun.sh) + [Effect](https://effect.website) v4 + [OpenTUI](https://github.com/anomalyco/opentui)) to a single executable, stowed to `~/.local/bin/dot`.
It has two faces:
* **A TUI dashboard**: run `dot` with no arguments to open the interactive menu, with git diff/log views, the notification inbox, and Omarchy menus.
* **A native CLI**: core maintenance actions are also subcommands (`dot update`, `dot git-diff --raw`, `dot doctor`, …), suitable for scripts and status bars.
[Command Reference](/dot/commands/)Every command, alias, flag, and example, generated from the CLI registry.
[Stow Workflow](/dot/stow/)How packages are stowed into your home directory.
[Shell Setup](/dot/shell/)Zsh completions, editing keys, aliases, and small shell helpers.
[System Utilities](/dot/utilities/)Health checks, benchmarks, and optional timers.
[Notes & Handoffs](/dot/notes/)Standalone notes CLI, MCP server, and handoff workflow.
[Cleanup](/cleanup/)Remove stowed links and reverse setup side effects.
## Common commands
[Section titled “Common commands”](#common-commands)
| Command | What it does |
| ---------------- | -------------------------------------------------------------------------------------------------------- |
| `dot dashboard` | Open the dot dashboard. |
| `dot init` | One-time first-use setup for a fresh machine. |
| `dot update` | Self-update and relaunch, pull, refresh config, stow, and rebuild. |
| `dot stow` | Stow refresh only (no git pull). |
| `dot doctor` | Run dotfiles system health checks. |
| `dot firewall` | Reconcile managed ufw firewall rules. |
| `dot git-diff` | Diff / repo watcher view across managed repos. |
| `context git` | Branch context for the current repo (agent context), provided by [`context`](https://context.timmo.dev). |
| `context stack` | Tech-stack context for a directory (agent context), provided by [`context`](https://context.timmo.dev). |
| `dot git-commit` | Guarded commit gateway for scoped, styled commits. |
| `dot omarchy` | Open the Omarchy desktop controls menu. |
See the full [Command Reference](/dot/commands/) for every subcommand and flag.
## Progress and timeouts
[Section titled “Progress and timeouts”](#progress-and-timeouts)
Long-running Effect-managed CLI workflows use the same progress wrapper: an interactive spinner, elapsed duration logging, and a timeout that interrupts the running child process group. This covers the main first-use setup phases (`dot init`), updates, doctor checks, skill update checks/applies/diffs, stow/install/clean, private package publishing, and other bounded maintenance commands.
`dot update` keeps each phase bounded so a slow remote, stuck hook, or helper process cannot leave the update sitting silently. Individual repository pulls are retried once and skipped after repeated failure or timeout. When a managed repository’s pull moves HEAD, any configured `post_update` command in [Private Git Config](/configuration/private-git/) runs from that repository root before the update continues; a failure or five-minute timeout stops the update. Full updates install Herdr Lazy when it is missing, then run `herdr-lazy restore` after stow and rebuild so installed Herdr plugins match the tracked lockfile. A repository scan, pull phase, stow phase, rebuild, or configured Herdr plugin restore timeout stops the update with failure instead of continuing with a partial result.
`dot init` uses the spinner and timeout wrapper for each required setup phase. Its private-overlay pull or clone is a synchronous preflight before that bounded workflow and before the already-initialised check. If a required init phase times out, init stops instead of writing the complete marker. `dot doctor` keeps its live “running checks” spinner and also times out individual checks, reporting timed-out checks as warnings.
Post-hooks and the final resume refresh are bounded but non-fatal. The refresh restarts status-bar services without auto-opening Twitch streams. If either helper does not return, the step is stopped and the completed update is not turned into a failure.
## Dashboard
[Section titled “Dashboard”](#dashboard)
`dot dashboard` opens a full-screen dashboard. It combines tracked repo state, GitHub notifications, and optional bounded source commands for Twitch, environment, todo, and calendar cards. Cards are intentionally readable and always present; Enter opens an existing deeper TUI view, or suspends `dot` while another TUI runs, when a card has one.
### Layout
[Section titled “Layout”](#layout)
Cards are grouped into four sections:
| Section | Cards | Source |
| ----------- | ----------------------------------------------------------------- | --------------------------------------------------------------- |
| Overview | Events in the next hour (when configured), Updates, Live Channels | Private `calendar`/`twitch` sources; in-process repo diff state |
| Git | Git Diff, Git Notifications | In-process `DotDiff` and notification inbox services |
| Todos | My Tasks, Work Tasks | Private `todo_*` sources |
| Environment | Temperature, CO2, VOC | Private `temperature`/`co2`/`voc` sources |
The summary header at the top highlights attention signals (dirty worktrees, behind core repos, important notifications, environment warnings).
### Keyboard
[Section titled “Keyboard”](#keyboard)
| Key | Action |
| ---------------- | ------------------------------------------- |
| Arrow keys / Tab | Move between cards |
| Enter | Open the linked view or run the card action |
| `r` | Refresh all dashboard sources |
| Esc / Backspace | Return to the main menu |
### Enter actions
[Section titled “Enter actions”](#enter-actions)
| Card | Enter behaviour |
| ----------------- | ------------------------------------------------------------------ |
| Updates | Runs `dot update` when core repos are behind (exits the dashboard) |
| Git Diff | Opens the `dot git-diff` TUI |
| Git Notifications | Opens the `dot git-notifications` TUI |
| Live Channels | Exits the dashboard and opens the `timmo.twitch` shell panel |
| Todo cards | Runs the configured Home Assistant todo TUI (suspends) |
| Environment cards | Runs `open_command` from `dot-dashboard.yml` when set (silent) |
Optional source cards read from `dot-dashboard.yml` in the private overlay. Each source runs a one-shot `--bar-json` command on refresh (eight-second timeout, 60-second auto-refresh). Without that file, git and update cards still work from in-process services. See [Private Dashboard Config](/configuration/private-dashboard/) for the schema and supported source IDs.
## Source of truth
[Section titled “Source of truth”](#source-of-truth)
Command and flag metadata lives in `dot/src/cli/spec.ts`. Help rendering, shell completions, and the generated [Command Reference](/dot/commands/) all read from that one registry. When a command changes, regenerate the reference page and shell completions from `spec.ts`.
# Command Reference
> Every dot command, alias, flag and example, generated from the CLI registry.
This page lists every `dot` command, generated from the same registry that powers `dot help` and shell completions. Run any command with `--help` to see the same details at the terminal.
## `dot dashboard`
[Section titled “dot dashboard”](#dot-dashboard)
Open the dot dashboard
```text
dot dashboard [options]
```
Open the full-screen dot dashboard. It combines tracked repo state, GitHub notifications, and optional bounded source commands for Twitch, environment, and calendar cards.
**Modes**
```text
(default) Interactive dashboard
```
**Examples**
```bash
dot dashboard
```
## `dot init`
[Section titled “dot init”](#dot-init)
Run one-time first-use machine setup
```text
dot init [options]
```
Run the one-time first-use setup workflow for a fresh machine. Init prepares repos, stow links, mise tools, packages, and machine hooks. After init completes, reboot so the Omarchy session picks up host env, then run dot doctor. Before the bounded workflow starts, init updates or clones the optional private overlay according to DOT\_ALLOW\_PRIVATE. Use dot update for ongoing maintenance.
**Options**
| Option | Description |
| ------------------ | ----------------------------------------------------------------- |
| `--confirm` | Compatibility flag; accepted but does not suppress prompts |
| `--noninteractive` | Skip the Hypr host questionnaire for this run |
| `--interactive` | Enable the Hypr host questionnaire when no host is selected |
| `--force` | Re-run init even if the machine looks initialised |
| `--host` `` | Hypr host to link before stow (default: OMARCHY\_HOST or desktop) |
| `--log` `` | Init log path (default: \~/.local/state/dot/init.log) |
**Examples**
```bash
dot init --noninteractive
dot init --host laptop --noninteractive
dot init --force --noninteractive
```
## `dot install`
[Section titled “dot install”](#dot-install)
Ensure prerequisites, then backup/adopt dotfiles
```text
dot install
```
## `dot update`
[Section titled “dot update”](#dot-update)
Aliases: `dot up`
Self-update, pull repos, stow dotfiles, rebuild
```text
dot update
```
A full update pulls the public dotfiles, installs Bun dependencies, rebuilds and relaunches dot, then scans and pulls tracked repositories. It trusts tracked mise configs, regenerates completions, installs missing public Arch/AUR packages, runs the required MCP sync, stows, rebuilds again, runs agents sync, backfills the init marker, and starts the resume refresh.
Phase flags are inclusive: passing any of —pull, —stow, or —app runs only the selected phases. Scoped runs skip full-update package reconciliation, agents sync, and init-marker backfill. Every mode that reaches the end starts the bounded resume refresh.
**Options**
| Option | Description |
| ------------- | -------------------------------------------------------------------- |
| `--pull` | Run the repository pull phase only |
| `--stow` | Generate completions, sync MCP configs, and stow only |
| `--app` | Install Bun dependencies and rebuild the dot binary only |
| `--check` | Report core/system repos behind upstream (no update); exit 10 if any |
| `--check-all` | Report all tracked repos behind upstream (no update); exit 10 if any |
**Exit codes**
```text
0 Update completed, or an update check found nothing behind
1 Fatal workflow failure
2 Update check could not scan repositories
10 Update check found repositories behind upstream
11 Legacy Hypr migration is required before update can continue
```
## `dot stow`
[Section titled “dot stow”](#dot-stow)
Re-stow public/private dotfiles
```text
dot stow
```
**Options**
| Option | Description |
| ----------- | -------------------------- |
| `--public` | Stow public dotfiles only |
| `--private` | Stow private dotfiles only |
## `dot omarchy-shell-config`
[Section titled “dot omarchy-shell-config”](#dot-omarchy-shell-config)
Regenerate the Omarchy shell layout
```text
dot omarchy-shell-config
```
Regenerate \~/.config/omarchy/shell.json from Omarchy’s shipped default and the host-specific dotfiles layout without running the full stow flow.
**Examples**
```bash
dot omarchy-shell-config
```
## `dot firewall`
[Section titled “dot firewall”](#dot-firewall)
Reconcile managed ufw firewall rules
```text
dot firewall
```
Ensure the managed ufw allow rules are present with their exact source, destination, interface/direction, and purpose comment. Missing rules are added, stale-comment rules are deleted and re-added, then ufw is reloaded once. A source-restricted rule does not satisfy a managed any-source rule.
**Examples**
```bash
dot firewall
```
## `dot doctor`
[Section titled “dot doctor”](#dot-doctor)
Run dotfiles system health checks
```text
dot doctor [options]
```
Run health checks on the dotfiles system. Verifies dependencies, repos, stow integrity, systemd timers, packages, browser config, and more.
All checks run in parallel and each section streams to the terminal as it finishes, so sections appear in completion order. A grouped summary of any errors and warnings, ordered by section, follows at the end. A log file is always written to \~/.local/state/dot/logs/.
**Options**
| Option | Description |
| ----------------- | -------------------------------------------------- |
| `--open-opencode` | Save the report and attempt to open it in OpenCode |
**Checks performed**
```text
Dependencies Required/optional CLI tools (git, stow, gh, gum, ...)
gh extensions Configured gh CLI extensions are installed
Locale Required locales from shell config are generated
Zsh key bindings Delete/forward-delete bindings and other expected defaults
Repositories Public/private dotfiles + private git repos exist and have upstreams
Origin HEAD Local origin/HEAD tracks the remote default branch (not stale)
Stow integrity Dry-run restow to detect drift
OpenCode location Canonical paths, legacy remnants
OpenCode server Shared Hypr autostart and ~/.config/opencode/.env password
Herdr integration Herdr binary and OpenCode integration installed
GitHub MCP auth gh token available for DOT_GH_MCP_BEARER
Git config Managed include is active
Git notifications API scope and notification access
Doctor startup Startup notification timer
uwsm session PATH ~/.local/bin on the uwsm/systemd user-environment PATH
Daily volume reset Laptop-only optional timer
Omarchy config Managed repos and Hypr host-link correctness
Legacy Hypr repo Flags a retired omarchy-hypr clone at ~/.config/hypr
Neovim theme link Repairs a mislocated omarchy-nvim theme.lua symlink
Private access Private dotfiles overlay enabled or explains why it is disabled
Browser flags Symlinks from private stow package
Hardware video VAAPI render nodes, drivers, packages
Browser extensions Private extension check list
Public packages AUR packages installed + version check
Private package repo Private pacman repo registered
Private packages Private repo + packages installed
Pacman hooks Hook files installed and up to date
Firewall rules Managed ufw rules (KDE Connect, Home Assistant, OpenCode, LocalSend, libvirt); repair with dot firewall
```
**Exit codes**
```text
0 No critical errors (warnings may still be present)
1 One or more critical errors found
```
**Examples**
```bash
dot doctor
dot doctor --open-opencode
```
## `dot clean`
[Section titled “dot clean”](#dot-clean)
Unstow managed dotfiles
```text
dot clean
```
## `dot git-diff`
[Section titled “dot git-diff”](#dot-git-diff)
Aliases: `dot diff`
Open the git diff/repo watcher view
```text
dot git-diff [options]
```
Open the diff/repo watcher view. Without flags, opens the interactive TUI.
**Modes**
```text
(default) Interactive TUI diff view
--raw Text summary of repos with changes
--bar-json JSON output for status bars and shell modules
--list-changed Changed repos as name|path rows
--list-all All tracked repos as name|path rows
```
**Options**
| Option | Description |
| ----------------- | ----------------------------------------------------------------------------------------- |
| `--no-fetch` | Skip fetching from remotes (use local refs only) |
| `--tab` `` | Initial pane to focus in TUI (default: changed) (one of: `changed`, `other`, `unchanged`) |
| `--repo` `` | Open a changed repository directly in lazygit |
| `--raw` | Text summary output |
| `--bar-json` | JSON output for status bars and shell modules |
| `--list-changed` | Changed repos as rows |
| `--list-all` | All tracked repos as rows |
**Examples**
```bash
dot git-diff
dot git-diff --raw
dot git-diff --bar-json
dot git-diff --tab other
dot git-diff --repo dotfiles
```
## `dot git-commit`
[Section titled “dot git-commit”](#dot-git-commit)
Commit staged changes through the guarded gateway
```text
dot git-commit --message [options] | --amend [options]
```
Create a commit through dot’s guarded gateway instead of raw git commit. The subject is validated as a single line with no trailing full stop and a length limit, then the staged set (or an explicit —path scope) is committed. It never runs git add -A.
Pass —amend to rewrite the previous commit instead of creating a new one; it keeps the existing message unless you pass —message. With —push, an amend force-pushes with —force-with-lease (never a plain force).
Agents are routed here by the git-commit skill and blocked from raw git commit in the OpenCode permission config, so commits stay in the maintainer’s concise one-line style.
**Modes**
```text
(default) Commit the staged set
--path Commit only the named files
--amend Rewrite the previous commit
--dry-run Preview the plan, change nothing
```
**Options**
| Option | Description |
| ---------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| `--message` `-m` `` | Single-line commit subject (required unless —amend) |
| `--path` `` | Commit only this file; repeatable |
| `--amend` | Amend the previous commit; keeps its message unless —message is given |
| `--push` | Push the current branch after committing (pulls —rebase first, or force-with-lease when amending, never a plain force) |
| `--dry-run` | Preview the commit and push plan without changing anything |
**Message guards**
```text
Single line Rejects multi-line messages
No em/en-dash Rejects '—' and '–'; use a hyphen
No full stop Rejects a trailing '.'
Warn over 60 Warns on stderr, still commits
Reject over 120 Fails; shorten the subject
```
**Base branch guard**
```text
Refuses commits to the base branch of a repo you do not own,
including a fork kept for upstream PRs. Owners you control are
listed in `git config dot.owner`. Work on a feature branch.
```
**Examples**
```bash
dot git-commit -m "Add commit gateway"
dot git-commit -m "Scope to one file" --path src/git/commands/Status.ts
dot git-commit -m "Commit and push" --push
dot git-commit --amend
dot git-commit --amend -m "Reword the previous commit"
dot git-commit -m "Preview only" --dry-run
```
## `dot git-notifications`
[Section titled “dot git-notifications”](#dot-git-notifications)
Open GitHub notification inbox
```text
dot git-notifications [options]
```
Open the authenticated user’s GitHub notification inbox. Without machine or action flags, opens the interactive TUI.
**Modes**
```text
(default) Interactive notifications TUI
--raw Text summary of notification threads
--bar-json JSON output for status bars and shell modules
--list-threads Notification threads as rows
--bar-filter Apply watched-repo filtering in raw/list output
```
**Options**
| Option | Description |
| -------------------- | --------------------------------------------------------- |
| `--raw` | Text summary of notification threads |
| `--bar-json` | JSON output for status bars and shell modules |
| `--list-threads` | Notification threads as rows |
| `--bar-filter` | Apply watched-repo filtering in raw/list output |
| `--all` | Include read notifications |
| `--participating` | Only include participating or mentioned threads |
| `--since` `` | Only include notifications updated after this date |
| `--mark-read` `` | Mark a notification thread as read |
| `--mark-bot-read` | Mark unread Renovate/Dependabot/bot notifications as read |
| `--dry-run` | Preview —mark-bot-read without mutating GitHub state |
| `--mark-done` `` | Mark a notification thread as done |
| `--ignore` `` | Ignore new notifications for a thread |
| `--unignore` `` | Stop ignoring notifications for a thread |
**Examples**
```bash
dot git-notifications
dot git-notifications --bar-json
dot git-notifications --participating
dot git-notifications --mark-bot-read --dry-run
dot git-notifications --mark-read 12345
```
## `dot agents-sync`
[Section titled “dot agents-sync”](#dot-agents-sync)
Mirror AGENTS.md to agent harness instruction files
```text
dot agents-sync
```
## `dot mcp-sync`
[Section titled “dot mcp-sync”](#dot-mcp-sync)
Regenerate MCP configs for all harnesses from the spec
```text
dot mcp-sync
```
Regenerate each active harness’s native MCP config from the single private spec (mcp.yml), keeping agent harness MCP configs aligned. Writes into the stowed private source tree; run dot stow after.
Some agent harnesses are documented stubs and are not written. OpenCode gated servers also receive a default-off tools gate so their tool schemas stay out of the baseline context until an agent re-enables them.
**Examples**
```bash
dot mcp-sync
```
## `dot notes-capture-sync`
[Section titled “dot notes-capture-sync”](#dot-notes-capture-sync)
Sync watched repositories to the notes capture picker
```text
dot notes-capture-sync
```
Regenerate the notes capture repository picker from repositories with GitHub notifications enabled in the private dot-git.yml configuration. Updates only CAPTURE\_REPOSITORIES in the ignored capture/wrangler.local.jsonc file, creating it from the deploy template when needed. Mirrors non-secret settings from the active Worker, then deploys when the live picker differs.
**Examples**
```bash
dot notes-capture-sync
```
## `dot is-agent`
[Section titled “dot is-agent”](#dot-is-agent)
Detect whether an AI coding agent is running dot
```text
dot is-agent [options]
```
Detect whether dot is running under an agent harness from agent environment variables, falling back to a Linux /proc process-ancestry check. Exits 0 when an agent is detected and 1 otherwise, so scripts can branch with `if dot is-agent`.
Set DOT\_AGENT=1 to force detection on or DOT\_AGENT=0 to force it off.
**Modes**
```text
(default) Print the detected agent, or a no-agent message
--quiet Print only the provider id (nothing when no agent)
--json Print the detection result as JSON
```
**Options**
| Option | Description |
| -------------- | ---------------------------------- |
| `--quiet` `-q` | Print only the provider id |
| `--json` | Print the detection result as JSON |
**Examples**
```bash
dot is-agent
dot is-agent --quiet
dot is-agent --json
dot is-agent && echo running under an agent
```
## `dot setup-public-repo`
[Section titled “dot setup-public-repo”](#dot-setup-public-repo)
Trust and register the public timmo pacman repository
```text
dot setup-public-repo
```
Download the public signing key, require its pinned full fingerprint, locally sign it in pacman’s keyring, and register the signed \[timmo] repository before the other package repositories.
The command fails before changing trust or pacman configuration when the repository is unavailable or the downloaded fingerprint does not match.
**Examples**
```bash
dot setup-public-repo
```
## `dot setup-private-repo`
[Section titled “dot setup-private-repo”](#dot-setup-private-repo)
Sync and register the private pacman repository
```text
dot setup-private-repo
```
Sync the private Arch package repo mirror, write the private pacman repo snippet, and add the Include line to /etc/pacman.conf when it is missing.
This repairs Omarchy pacman.conf refreshes that remove local repository includes. Privileged writes prefer pkexec and fall back to sudo.
**Examples**
```bash
dot setup-private-repo
```
## `dot private-pkg-publish`
[Section titled “dot private-pkg-publish”](#dot-private-pkg-publish)
Build and publish a private package
```text
dot private-pkg-publish [options]
```
Build and publish a mapped private package into the private pacman repo.
**Options**
| Option | Description |
| -------------- | ------------------------------------------------------ |
| `--no-git` | Skip package repo commit and push |
| `--skip-build` | Publish an existing dist package artifact |
| `--install` | Install the published package after syncing the mirror |
**Arguments**
| Argument | Description |
| ---------------- | ----------- |
| `` | |
**Examples**
```bash
dot private-pkg-publish twitch-notifications --install
dot private-pkg-publish --skip-build --no-git twitch-notifications
```
## `dot skill-updates`
[Section titled “dot skill-updates”](#dot-skill-updates)
Check/apply imported skill updates
```text
dot skill-updates
```
**Options**
| Option | Description |
| ------------------ | ------------------------------------------------ |
| `--check` | Check only without applying |
| `--update` | Auto-apply clean updates |
| `--json` | Report update states as JSON without applying |
| `--skill` `` | Limit checking or updating to one imported skill |
| `--no-commit` | Apply updates without creating a commit |
| `--skip-review` | Skip local-edit review |
**Examples**
```bash
dot skill-updates --json
dot skill-updates --update --skill browser-control --no-commit
```
## `dot skill-check`
[Section titled “dot skill-check”](#dot-skill-check)
Validate skill maintenance wiring
```text
dot skill-check
```
**Options**
| Option | Description |
| ----------------- | -------------------------------------------------------------------------------------------------------- |
| `--open-opencode` | Run checks and attempt OpenCode analysis |
| `--diff-origin` | Diff imported skills against their upstream origins; with —open-opencode, include the diff in the prompt |
## `dot completions`
[Section titled “dot completions”](#dot-completions)
Generate shell completions
```text
dot completions [bash|fish|zsh] [--stdout]
```
Generate shell completions for dot.
By default this writes the managed completion file for the selected shell in the public dotfiles repo so the next dot stow installs it.
**Options**
| Option | Description |
| ---------- | ------------------------------------------------- |
| `--stdout` | Print the completion script instead of writing it |
**Arguments**
| Argument | Description |
| --------- | ------------------------------ |
| `` | One of: `bash`, `fish`, `zsh`. |
**Examples**
```bash
dot completions zsh
dot completions bash --stdout
dot completions fish --stdout
```
## `dot omarchy`
[Section titled “dot omarchy”](#dot-omarchy)
Open an Omarchy submenu by path
```text
dot omarchy [submenu...]
```
Open the Omarchy desktop controls menu. Pass a submenu path to jump straight to it:
dot omarchy theme Theme submenu dot omarchy theme set Execute theme set directly
**Arguments**
| Argument | Description |
| ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `` | Repeatable. One of: `theme`, `font`, `toggle`, `capture`, `system`, `launch`, `refresh`, `restart`, `install`, `remove`, `packages`, `share`, `reminder`, `setup`, `snapshot`, `brightness`, `power`. |
**Available submenus**
```text
theme Theme management
font Font management
toggle Toggle system features
capture Screenshots and recordings
system Lock, logout, reboot, shutdown
launch Launch applications
refresh Refresh system components
restart Restart system services
install Install software and tools
remove Remove software and features
packages Package management
share Share clipboard, files, folders
reminder Reminders
setup DNS, security setup
snapshot System snapshots
brightness Display and keyboard brightness
power Power profiles
```
## `dot usage`
[Section titled “dot usage”](#dot-usage)
Local-first analytics for dot usage
```text
dot usage [summary|stale|path|backfill] [options]
```
Report local-first usage analytics for dot. Dispatched dot commands append NDJSON events under $XDG\_STATE\_HOME/tool-usage with timestamps, machine, canonical command, recognised flag names, exit status, duration, source, and invoker. Live dot events never store positional values.
Optional shell-history backfill observes selected standalone tools without requiring integration. It uses whitespace tokenisation, so review the source history before applying when arguments may contain sensitive text.
Set DOT\_USAGE\_DISABLE=1 to stop automatic live recording, or DOT\_USAGE\_DIR to relocate the event root. Explicit backfill —apply still writes events.
**Modes**
```text
summary Per-feature usage table (default)
stale Features not used within the window
path Print the event storage root
backfill Import whitelisted invocations from shell history
```
**Options**
| Option | Description |
| ------------------ | -------------------------------------------------------- |
| `--days` `` | Window for summary/stale (default: 90) |
| `--format` `` | summary format (one of: `text`, `json`, `agent-context`) |
| `--root` `` | Extra event root to combine (repeatable) |
| `--history` | Backfill from shell history (accepted for clarity) |
| `--apply` | Write events during backfill (default: dry run) |
**Examples**
```bash
dot usage summary --days 30
dot usage summary --format agent-context
dot usage stale --days 90
dot usage backfill --history
dot usage backfill --history --apply
```
## `dot help`
[Section titled “dot help”](#dot-help)
Show this help menu
```text
dot help
```
# Notes & Handoffs
> Repository notes and the handoff workflow.
Repository notes live in the standalone [`notes`](https://notes.timmo.dev) CLI and MCP server. Dotfiles keeps the OpenCode plugins, slash commands, and handoff skill that consume it.
Notes live in a notes vault git repo (`~/Documents/notes` by default, overridable with `NOTES`). Inside the vault, files are scoped per repository under `repo-notes/{owner}/{repo}/`, keyed off the current git remote.
## Browse notes
[Section titled “Browse notes”](#browse-notes)
```bash
notes list
notes list --all
notes list --format json
notes root --repo-notes
```
## Handoffs
[Section titled “Handoffs”](#handoffs)
Handoffs are notes tagged `handoff`, used to pass context between agents or sessions.
```bash
notes handoffs
notes handoffs --all
notes handoffs --format json
```
`notes handoff` is an alias for `notes handoffs`.
Handoffs carry a `priority` of `low`, `medium`, `high`, or `critical`. Any handoff without an explicit `priority` is treated as `medium`.
## Process the issue queue
[Section titled “Process the issue queue”](#process-the-issue-queue)
Run `notes-process` to start the isolated OpenCode service if needed, wait for it to become ready, process the current Notes issue queue once using `~/.config/notes/daemon.yml`, then exit. The command reads `OPENCODE_SERVER_PASSWORD` from `~/.config/opencode/.env` and exposes it only to the daemon process.
The supervised capture daemon stays available during transient OpenCode service failures and restarts with the isolated server when systemd recovers it. Explicitly restarting the OpenCode service also restarts the daemon with it.
## Read / write note files
[Section titled “Read / write note files”](#read--write-note-files)
```bash
notes read --path
notes write --path --stdin
notes delete --path
```
Writes and deletes are committed to the notes vault and pushed when it has a remote. The push is best-effort: a failed or skipped push never fails the note operation.
## OpenCode integration
[Section titled “OpenCode integration”](#opencode-integration)
Agents do not touch the vault with built-in file tools. The same files are created and loaded inside an OpenCode session through slash commands backed by two plugins.
### Slash commands
[Section titled “Slash commands”](#slash-commands)
| Command | What it does |
| ----------------- | ----------------------------------------------------------------------------------------- |
| `/note-create` | Summarise the current conversation into a new note for this repo. |
| `/note-append` | Add new content to an existing note (pick from a ranked list). |
| `/note-reference` | Load one or more notes, any skills they reference, and suggested next steps into context. |
| `/notes-list` | List this repo’s notes, optionally filtered by tag. |
| `/notes-search` | Rank this repo’s notes against a topic, keyword, or tag. |
| `/handoff` | Write a handoff document for the next agent session. |
| `/handoffs-list` | List handoff notes for this repo (equivalent to `/notes-list handoff`). |
See the [commands reference](/reference/commands/) for the full list.
### How it works
[Section titled “How it works”](#how-it-works)
Two OpenCode [plugins](/reference/plugins/) wire the commands to the vault:
* **`repo-notes`** injects a `` block at the top of each note command. It runs `notes context --command --json`, which resolves the owner and repo from git and reports the target notes path. For listing and search commands it includes existing note metadata; `/note-reference` reads only the selected note bodies through `notes_note_read`.
* **`notes-guard`** blocks built-in file and shell tools from touching the vault, so the note MCP tools are the only way in.
Agent harnesses prefix MCP server names onto tool calls, so note commands and plugins refer to `notes_note_read`, `notes_note_write`, `notes_note_delete`, and `notes_note_list`. The underlying standalone MCP server registers them as `note_read`, `note_write`, `note_delete`, and `note_list`; see the [Notes MCP docs](https://notes.timmo.dev/mcp/).
So a typical create flow is: run `/note-create` -> `repo-notes` injects the repo context -> the command summarises the conversation and calls the pre-approved `notes_note_write` tool -> `notes mcp` writes the file, commits it, best-effort pushes the vault, then emits a desktop notification with the push result. Read-only agents still deny note writes.
### Handoffs
[Section titled “Handoffs”](#handoffs-1)
`/handoff` defers to the [`handoff` skill](/reference/skills/), which compacts the conversation into a `handoff-{slug}.md` note tagged `handoff`. For work spanning multiple phases, branches, or PRs, the skill offers to split the handoff rather than writing one combined note, using a shared `handoff-{feature}-{phase}` naming convention so related handoffs group together under `notes handoffs`.
## Configuration
[Section titled “Configuration”](#configuration)
* `NOTES` - notes vault git repo (preferred; default `~/Documents/notes`).
* `DOT_NOTES_DIR` - compatibility override used when `NOTES` is unset.
# Shell Setup
> Shell completions, editing keys, aliases, and small shell helpers.
## Shell completions
[Section titled “Shell completions”](#shell-completions)
The `zsh/` package adds `${XDG_DATA_HOME:-~/.local/share}/zsh/site-functions` to `fpath` before `zsh-autocomplete` runs `compinit`.
Generated completions are stowed for the two local CLIs:
* `dot completions bash|fish|zsh` writes the stowed `dot` completions in this repo.
* `context completions bash|fish|zsh` writes the stowed `context` completions in this repo.
* `_omarchy` is a stowed zsh completion wrapper for dynamic Omarchy subcommands.
Installed package completions are also wired into common aliases where needed. `handoffs` completes through `_notes` and runs `notes handoffs`.
Zsh also has a live generated completion:
* `mise completion zsh` is generated at shell startup into the live, non-stowed `_mise` path and refreshed when the `mise` binary is newer than the cached completion.
The live `_mise` file is intentionally not version-controlled; it tracks the installed `mise` binary on each machine.
`watchexec` is managed through mise, so `mise watch ` can rerun short feedback tasks such as tests and type checks when files change. Keep persistent development servers under the project’s documented server workflow or Pitchfork instead.
## Editing keys
[Section titled “Editing keys”](#editing-keys)
fzf’s native zsh integration provides fuzzy history search with `Ctrl+R`, file and directory insertion with `Ctrl+T`, directory changes with `Alt+C`, and fuzzy tab completion.
The zsh config restores the standard editing bindings used by this setup after plugins load, including Delete as forward-delete. `dot doctor` checks the Delete binding and reports drift when a plugin or local override changes it.
## Small helpers
[Section titled “Small helpers”](#small-helpers)
The shell initialises zoxide as `zd`, which also backs Omarchy’s `cd` alias. Directory changes teach zoxide which paths are used most often, while `zd ` jumps directly to a ranked match.
Omarchy’s `ff` alias opens an fzf file picker with previews. `fast` runs Fastfetch without replacing that picker. `cat` and `b` use bat’s normal highlighted output, while `bp` uses plain output.
The shell keeps a few typo and navigation helpers close to the aliases. For example, `cwd` prints a short reminder that the real command is `pwd`, then runs `pwd` so the mistake still returns useful output.
Run `update` to select maintenance steps with Gum. Dotfiles and Omarchy remain separate choices, while each enabled Topgrade step can be selected independently. Selected work runs in the displayed order: Dotfiles, Omarchy, then Topgrade. Mise, GitHub CLI extensions, and Yazi appear first within Topgrade and start selected; the remaining steps are opt-in. Selected Topgrade steps run together through `topgrade --only`; selecting every Topgrade step uses the normal full `topgrade` command instead.
The Omarchy step redirects Quattro’s global mise writes to `~/.local/state/mise/omarchy-config.toml`, then re-stows the public dotfiles after Omarchy regenerates its command wrappers. This keeps `mise/.config/mise/config.toml` under dotfiles control during the complete update, not just normal shell commands.
Use `update -y` or `update --yes`, or run it in a non-interactive shell, to select every step without prompting. Agent-driven runs without a controlling terminal route internal `sudo` calls through a temporary `pkexec` helper so authentication can use the desktop PolicyKit prompt. Interactive terminals keep normal `sudo` credential caching. The sequence stops if a selected step fails.
# Stow Workflow
> How dotfiles packages are applied into your home directory with GNU Stow.
The repository is a [GNU Stow](https://www.gnu.org/software/stow/) package root targeting `~/`. Each top-level directory (`zsh`, `neovim`, `starship`, `uwsm`, `hypr`, `ghostty`, `scripts`, …) is a stow package whose contents are symlinked into your home directory.
* **dotfiles/** public repo root
* **zsh/** stow package
* .zshrc
* **neovim/** stow package
* .config
* nvim
* init.lua
* **hypr/** stow package
* .config
* hypr
* …
* **uwsm/** stow package
* .config
* uwsm
* env.d
* 90-dotfiles
* **ghostty/** stow package
* .config
* ghostty
* config
* **scripts/** stow package
* .local
* bin
* dot
* docs/ ignored by `.stowrc`
* …
* dot/ ignored by `.stowrc`
* …
## Always use `dot stow`
[Section titled “Always use dot stow”](#always-use-dot-stow)
Apply packages with `dot stow` (or `dot update`, which refreshes stow). Do **not** run GNU `stow` directly from the repo root: `dot` applies the correct backup, no-folding, and public-then-private flow. The first-use `dot install` and `dot init` commands add the adopt step; `dot stow` is the steady-state relink.
Apply stow packages
```bash
dot stow # stow public + private
dot stow --public # public only
dot stow --private # private only
```
## What `dot stow` does
[Section titled “What dot stow does”](#what-dot-stow-does)
* Lays down public packages first, then the private overlay from `~/.config/dotfiles-private`.
* Stows any package that targets runtime-owned directories with `--no-folding`, including `uwsm/`, `hypr/`, `ghostty/`, `herdr/`, shell completions, `.local/bin`, and systemd user units. This keeps generated UWSM migration files and Herdr runtime state out of the source directories. For Hypr it also creates/repairs the `~/.config/hypr/host` symlink for the active host.
* Before stow, moves unmanaged real files or directories that block active package targets into the repo’s `backup/` directory and logs each source-to-backup path. This includes host-specific packages such as `chromium--laptop`. It does not follow an unmanaged parent symlink into an external tree; those conflicts are left for manual resolution.
* Before public stow, backs up the retired `timmo001/omarchy-ghostty` clone at `~/.config/ghostty` when present, so the `ghostty/` package can own that path.
* Before public stow, removes the retired `timmo001/omarchy-uwsm` checkout, including Quattro’s generated migration files, then links only the intentional `90-dotfiles` override.
* During `dot install` and `dot init`, public packages use `--adopt`, but a committed-wins pre-pass first backs up differing live files so stock config cannot silently overwrite committed public files. Any remaining adopted changes are reported for review. Private packages use normal stow without `--adopt`.
## Hypr package handling
[Section titled “Hypr package handling”](#hypr-package-handling)
The Hypr package is treated differently from every other stow package.
Hyprland enables config autoreload by default. If `~/.config/hypr/hyprland.lua` goes missing even briefly, Hyprland can write a default stub as a real file. That stub then blocks the next stow because stow cannot replace a regular file with a symlink.
To avoid that gap, the steady-state `dot stow` flow and the install flow used by `dot install` and `dot init` never unstow the `hypr` package. Before stowing Hypr, they atomically repair `~/.config/hypr/hyprland.lua` when the link is missing or points at the wrong target: the link is created through a temporary path and renamed into place, so Hyprland never sees a missing file. The idempotent stow step then fills in any other missing Hypr files without an unstow/restow cycle.
After the Hypr package is laid down, `dot stow` also creates or repairs the `~/.config/hypr/host` symlink for the active `OMARCHY_HOST`. See [Host Overrides](/omarchy/host-overrides/) for how host overrides are selected.
## Unstowing packages
[Section titled “Unstowing packages”](#unstowing-packages)
Use `dot clean` when you need to remove the symlinks that `dot stow` manages, for example before inspecting a conflict or proving whether a file is coming from the dotfiles repo.
Temporarily remove managed links
```bash
dot clean # unstow private packages first, then public packages
dot stow # reapply the managed symlinks afterwards
```
`dot clean` discovers stow packages the same way `dot stow` does: top-level package directories only, excluding repo internals such as `dot/`, `docs/`, and `backup/`. Host-specific packages (`name--host`) are included only when their suffix matches the resolved Omarchy host, from `OMARCHY_HOST` or the current `~/.config/hypr/host` link. If the private overlay is available, it is unstowed before the public repo so overlay links are removed before the base packages.
The command removes managed Stow links; it is not a general home-directory cleanup tool. Re-run `dot stow` or `dot update` after a clean to restore the expected links and host repairs.
## Ignore rules
[Section titled “Ignore rules”](#ignore-rules)
`.stowrc` sets the stow target and ignore rules. Files that should never be symlinked into `~/` are ignored there, including top-level docs, the `dot/` source, the `docs/` site, and repo metadata. Keep `.stowrc` ignore rules in sync when adding root-only files.
Caution
The repo root stows to `~/`. Anything not ignored in `.stowrc` becomes a symlink under your home directory. When adding new root-level files or directories that should not be stowed (such as `docs/`), add a matching `--ignore` rule.
## Health check
[Section titled “Health check”](#health-check)
`dot doctor` runs a dry-run restow to detect drift, alongside its other checks. Run it after changing stow packages to confirm nothing is broken.
# Usage Analytics
> Local-first usage tracking for dot with optional shell-history observations.
`dot usage` records local-first analytics for the `dot` CLI so you can see which features are actually used, split human from agent from automated calls, and decide whether a feature earns its keep. Its optional history backfill can also observe selected standalone CLI invocations without requiring those projects to integrate with dotfiles.
Recording stays local: each dispatched `dot` command appends one NDJSON event under `$XDG_STATE_HOME/tool-usage`. Live events store the timestamp, machine, canonical subcommand path, recognised flag names, exit status, duration, source, and invoker. They do not store positional values such as paths, ids, or note text. Help and unresolved invocations exit before the recording hook.
## How it works
[Section titled “How it works”](#how-it-works)
`dot` installs a best-effort exit hook that appends one event when the process exits, capturing the real exit code and duration:
```json
{"ts":"2026-07-08T20:45:30Z","machine":"desktop","tool":"dot","invokedAs":"dot","command":["git-diff"],"flags":["--raw"],"exitCode":0,"durationMs":63,"source":"live","invoker":"human"}
```
Events are written per machine and per day:
```text
$XDG_STATE_HOME/tool-usage/events//YYYY-MM-DD.ndjson
```
`invoker` is one of `human`, `agent` (an AI coding agent was detected), or `automation` (a status-bar poll, detected from `--bar-json`), so shell polling does not drown out genuine feature usage.
## Reporting
[Section titled “Reporting”](#reporting)
```bash
dot usage summary # per-feature table (last 90 days)
dot usage summary --days 30 # narrow the window
dot usage summary --format json # machine-readable
dot usage summary --format agent-context # compact, for feeding an agent
dot usage stale --days 90 # features not used recently + dot commands never seen
dot usage path # print the event root
```
The summary groups by tool and command, showing total, human, agent, automation, failure count, median duration, and last-seen date.
## Backfill from shell history
[Section titled “Backfill from shell history”](#backfill-from-shell-history)
Prefill from existing shell history. Only whitelisted binaries (`dot`, `context`, `notes`, `note`, `handoff`, `handoffs`) are observed, and only from shells that timestamp history (fish and zsh extended history); bash is skipped because its history is undated. This is a dot-owned observation layer: the standalone projects remain independent and do not write dot usage events.
The backfill parser splits history on whitespace and does not interpret shell quoting. `dot` history entries keep only flags declared by the matched command, but standalone tools have no command-specific flag allowlist. Review the dry run and source history before applying if standalone arguments may include sensitive text beginning with `-`.
```bash
dot usage backfill --history # dry run: counts by tool, writes nothing
dot usage backfill --history --apply # write the imported events
```
Backfilled events are attributed to `human`, marked `source: "history"`, and have a null exit code and duration. Re-running is safe: exact history duplicates are removed, and matching history events at or after the first live event for the same machine, tool, command, and flags are suppressed.
## Two machines
[Section titled “Two machines”](#two-machines)
Each machine writes its own per-machine files, so nothing conflicts when the directory is synced (Syncthing, a shared folder, or a private repo). Combine roots at read time for `summary` and `stale`:
```bash
dot usage summary --root ~/synced/other-machine/tool-usage
```
`--root` does not change where `backfill --apply` writes; imports always use the local `DOT_USAGE_DIR` root.
## Configuration
[Section titled “Configuration”](#configuration)
* `DOT_USAGE_DIR` - relocate the event root (default `$XDG_STATE_HOME/tool-usage`).
* `DOT_USAGE_DISABLE=1` - stop automatic live dot recording. Explicit `backfill --apply` still writes.
* `OMARCHY_HOST` - overrides the machine identifier used to partition event files (falls back to the system hostname).
# System Utilities
> Health checks, system updates, benchmarks, and optional timers.
## System health check
[Section titled “System health check”](#system-health-check)
`system-health-check` is a friendly multi-snapshot system health report covering CPU, memory, network, pressure, and known logs.
```bash
system-health-check # write a health report
system-health-check --open-opencode # run opencode run against the report, then open an interactive session
```
Add `--open-opencode` to run `opencode run` against the saved report and then open a full interactive OpenCode session with `opencode --continue`.
## Times
[Section titled “Times”](#times)
`times` prints current local, Pacific, Mountain, Central, and Eastern times. `SUPER+CTRL+ALT+T` shows an aligned compact version in a desktop notification.
```bash
times
```
## Floating webapps
[Section titled “Floating webapps”](#floating-webapps)
`launch-floating-webapp ` opens one webapp as a `380x500` floating window in the focused monitor’s bottom-right corner without changing normal launches of the same site. Use `--monitor`, `--workspace`, `--width`, `--height`, `--right-margin`, or `--bottom-margin` to override the geometry. `--address` applies the same placement to an existing window.
## Git branch sync
[Section titled “Git branch sync”](#git-branch-sync)
`git-default-ref` is the shared guarded resolver for default-branch operations. It prefers `upstream`, falls back to `origin`, compares the remote’s advertised default branch with local `/HEAD`, and fetches the branch. A missing or mismatched local ref requires confirmation before repair. Under `dot is-agent`, or without an interactive terminal, it fails instead of prompting.
The wrappers print section headings and the resolved remote, branch, comparison range, or operation before running Git. Operational detail goes to stderr for `git-diff-default` and `git-log-default`, leaving their Git output clean for piping.
* `git-switch-default` (`gsd`) switches to the default branch and fast-forwards it to the fetched remote branch. `gsdp` then runs `gh poi` under a separate cleanup section.
* `git-rebase-default` (`grd`) rebases the checked-out branch onto the default branch with `--autostash`. Press `Ctrl+F` in Lazygit’s Local Branches panel to run it. Use `gra` to abort a rebase in progress.
* `git-diff-default` (`gdd`) shows the current branch diff from its merge base with the default branch.
* `git-log-default` (`gld`) shows commits on the current branch since it diverged from the default branch.
## System updates
[Section titled “System updates”](#system-updates)
[topgrade](https://github.com/topgrade-rs/topgrade) runs the machine’s update steps (AUR via `yay`, Flatpak, firmware checks, `mise` tools, `rustup`, `cargo`, and more) in one pass. The repo stows a tuned config to `~/.config/topgrade.toml` and a logging wrapper at `scripts/.local/bin/topgrade` that shadows the system binary via `~/.local/bin` on `PATH`.
```bash
topgrade # full run (all enabled steps)
topgrade mise cargo # run only named steps
```
* The wrapper logs the full session to `$XDG_STATE_HOME/topgrade.log` (default `~/.local/state/topgrade.log`) with `script`, mirroring the `omarchy-update` pattern, so you can review a run afterwards.
* It adds `--sudoloop` automatically when a run includes steps that need root (a full run, or the `system`, `firmware`, or `containers` steps) so credentials stay cached during long runs, and skips it for user-only steps.
* Firmware is check-only. The `mise` step still runs but does not auto-bump toolchain versions (`[mise] bump = false` in the stowed config); use `mise install` or `dot update` to move pins forward. `yay` runs with `--noconfirm --cleanafter`.
* Steps managed elsewhere or unused are disabled (for example `bun`, `deno`, `go`, and `pnpm` come from `mise`; `hyprpm` is skipped because it drops the shared sudo credential). A desktop notification fires only on failure.
## Benchmarks and diagnostics
[Section titled “Benchmarks and diagnostics”](#benchmarks-and-diagnostics)
The stowed utility scripts include a quick system benchmark and an ambient resource-growth diagnostic:
* `system-quick-benchmark` - short CPU/memory/network benchmark snapshot.
* `system-resource-leak-check` - samples whole-system memory, swap, pressure, and sockets over time. It is a diagnostic, not a deterministic test.
```bash
# Quick benchmark (minimal defaults)
system-quick-benchmark
# Include LAN throughput (requires an iperf3 target)
system-quick-benchmark --iperf-host 192.168.1.50
# Resource growth diagnostic
system-resource-leak-check
```
* Reports are written beneath `$XDG_STATE_HOME` (default `~/.local/state`) in directories matching each command name. Use `--output` to select another path.
* LAN network throughput is opt-in and requires `--iperf-host`.
* Scripts use ANSI colour output by default; set `NO_COLOR=1` to disable.
* All scripts include an uptime/load snapshot near the top of output.
`dot doctor` also checks the system VA-API drivers and Chromium flag files. Chromium enables its Linux GL decode and zero-copy capability checks by default, so the doctor accepts flag files without explicit acceleration features. It warns about obsolete VA-API feature names and explicit acceleration disablement.
Driver capability does not prove smooth playback. For a live stream, use `chrome://media-internals` to confirm `kVideoDecoderName` is `VaapiVideoDecoder` and `kIsPlatformVideoDecoder` is `true`, then inspect dropped frames and buffering separately.
Repository regression tests live under `tests/`, use temporary directories, and run through mise tasks and the `lint.yml` workflow:
* `tests/github/opencode-publish.test.sh` checks publication of shared `lib/` modules and rejects missing relative plugin imports before cleaning the publish checkout.
* `tests/scripts/workspace-restore.test.sh` checks that captured browser URLs remain one shell argument and cannot execute command substitutions during restore.
* `tests/dot/cli-smoke.test.sh` builds `dot` and checks side-effect-free CLI entry points.
The `lint.yml` `validate-skills` job uses the shared `lint-agent-skills` workflow to validate public `SKILL.md` files with [`skills-ref`](https://github.com/agentskills/agentskills/tree/main/skills-ref).
The `mise-toolchain.yml` workflow runs when the stowed global mise config changes. It force-builds Terminal Control with the pinned Rust and Zig toolchain, catching incompatible automated version updates before they merge.
Run `mise run tests:integration` for deterministic repository tests and `mise run tests:smoke` for the build plus CLI smoke checks. TypeScript unit tests mirror `dot/src/` under `dot/tests/` and run through `mise run dot:test`.
## Firewall rules
[Section titled “Firewall rules”](#firewall-rules)
`dot firewall` configures a managed set of [ufw](https://wiki.archlinux.org/title/Uncomplicated_Firewall) rules, `dot init` runs it during first-use setup, and `dot doctor` verifies the rules are still present. The setup reads the world-readable ufw rules file first, so a fully configured machine adds nothing and never prompts for a password. When changes are needed, missing or stale-comment rules are applied in one elevated batch, followed by a single `ufw reload`, so the firewall step should only ask for authentication once. Each rule is tagged with its purpose as a ufw comment, so it appears in `ufw status`.
Most rules are inbound port allows on any interface. The libvirt rules are scoped to the `virbr0` bridge: two inbound allows for guest DHCP and DNS, plus a forwarding (route) allow so the default NAT network can route guest traffic off the bridge. Without them, ufw’s default `deny (incoming)` and `deny (routed)` policy leaves guests without an address or internet access.
Rule identity includes the complete ufw tuple: source, destination, protocol/port, and interface/direction. A source-restricted existing rule does not satisfy a managed any-source rule, so `dot firewall` adds the broader managed rule rather than treating the restricted rule as equivalent.
| Port(s) | Protocol | Scope | Purpose |
| ----------- | --------- | -------- | -------------------------------------------------- |
| `1714:1764` | UDP + TCP | any | KDE Connect device discovery and transfer. |
| `8123` | TCP | any | Home Assistant frontend. |
| `8124` | TCP | any | Home Assistant companion port. |
| `4096` | TCP | any | OpenCode local server. |
| `53317` | UDP + TCP | any | LocalSend device discovery and transfer. |
| `67` | UDP | `virbr0` | libvirt guest DHCP. |
| `53` | TCP + UDP | `virbr0` | libvirt guest DNS. |
| forward | any | `virbr0` | libvirt NAT: forward guest traffic off the bridge. |
If `ufw` is not installed, firewall setup and doctor skip the firewall step with a warning. The doctor check reports any missing rule with the `sudo ufw allow ...` command to add it, or a rule present without its managed comment, and you can run `dot firewall` to reconcile. Override the scanned rules file with `DOT_UFW_RULES_FILE`.
After elevation, `dot firewall` re-reads the rules file and fails when any managed rule is still missing or carries a stale comment. That catches silent ufw write failures instead of leaving init marked complete with half-applied rules.
## Daily volume reset (laptop only)
[Section titled “Daily volume reset (laptop only)”](#daily-volume-reset-laptop-only)
Public dotfiles provide `daily-volume-zero.timer` in laptop-only stow packages (`scripts--laptop` and `systemd--laptop`), a user systemd timer that runs at 5am local time.
* The timer runs `daily-volume-zero`, which sends a 10-second desktop notification, clears default sink mute, then sets the default PipeWire/WirePlumber sink volume to `0%`.
* It is optional and not enabled by `dot`. Enable it on machines that should use it:
```bash
systemctl --user enable --now daily-volume-zero.timer
```
# Getting Started
> Bootstrap a fresh Omarchy machine and run the ongoing dotfiles workflow.
The dotfiles are designed to bootstrap a fresh [Omarchy](https://omarchy.org) machine and then keep it in sync with a single command. Start with the install guide, then use the checklist for a clean walkthrough.
Personal dotfiles
These are my own dotfiles, tuned for [Omarchy](https://omarchy.org) and my machines, and they lean on a deeply integrated private overlay that is not public. Don’t install them wholesale on a machine you care about, and don’t expect them to work on plain Arch without replicating my setup. Read them, take the bits you want, and fold them into your own dotfiles instead. The steps below are how *I* set up a new machine, documented for reference.
[Install](/getting-started/install/)Prerequisites, bootstrap build, and the ongoing workflow.
[New Machine Checklist](/getting-started/new-machine/)A numbered, end-to-end setup for a new machine.
## How it fits together
[Section titled “How it fits together”](#how-it-fits-together)
* Public dotfiles live at `~/.config/dotfiles` and are applied with GNU Stow.
* Optional private dotfiles at `~/.config/dotfiles-private` overlay machine-specific config.
* The [`dot`](/dot/) binary owns setup (`dot init`), ongoing sync (`dot update`), and day-to-day tooling.
Note
The `dot` binary is compiled from `dot/` in the repo. On a fresh machine you build it once with system `mise` before it is on your `PATH`. See [Install](/getting-started/install/).
# Install
> Prerequisites, bootstrap build, and the ongoing dotfiles workflow.
## Prerequisites
[Section titled “Prerequisites”](#prerequisites)
On a fresh [Omarchy](https://omarchy.org) machine, install the bootstrap build prerequisites ([git](https://git-scm.com) and [mise](https://mise.jdx.dev)):
```bash
yay -S --needed git mise
```
If you want the private dotfiles overlay, authenticate [`gh`](https://cli.github.com) (the GitHub CLI) before `dot init` so it can clone `dotfiles-private` automatically:
```bash
gh auth status || gh auth login
```
## Clone and build
[Section titled “Clone and build”](#clone-and-build)
Clone the public dotfiles first, then build the `dot` binary before it is on your `PATH`. Trust and install the pinned toolchain with mise, then run the build task (it installs dependencies first, and `mise run` provides Bun without needing shell activation yet):
```bash
git clone --recurse-submodules git@github.com:timmo001/dotfiles.git ~/.config/dotfiles
cd ~/.config/dotfiles
mise trust
mise install
mise run dot:build
```
## First-use setup
[Section titled “First-use setup”](#first-use-setup)
`dot init` runs the one-time first-use setup: it bootstraps private dotfiles when allowed, selects the Hypr host, installs and adopts config, installs stowed mise tools, verifies and registers the signed `timmo` package repository, sets up packages and machine hooks, and syncs agents. It logs to `~/.local/state/dot/init.log` by default. The private-overlay pull or clone runs first as an unbounded preflight; the setup phases that follow use the same spinner and timeout handling as `dot update`.
```bash
~/.config/dotfiles/scripts/.local/bin/dot init --noninteractive
```
For a laptop, select the laptop host:
```bash
dot init --host laptop --noninteractive
```
Or run `dot init` in an interactive shell to be prompted.
`--noninteractive` skips only the Hypr host questionnaire; elevation and package tools may still prompt. `--confirm` remains accepted for compatibility but does not suppress prompts. Private overlay preflight is controlled by `DOT_ALLOW_PRIVATE`: `auto` skips without GitHub authentication and tolerates an existing-overlay pull failure, but a failed attempted clone is fatal; `always` requires the overlay to update or clone successfully; `never` skips it.
Note
UWSM, Hyprland, and Ghostty customisations are stowed from the `uwsm/`, `hypr/`, and `ghostty/` packages. If the retired `timmo001/omarchy-uwsm` checkout is present, init removes it before linking the Quattro-compatible environment override.
## Ongoing workflow
[Section titled “Ongoing workflow”](#ongoing-workflow)
After init completes, restart your shell so `dot` is on `PATH`, then reboot so the Omarchy session picks up `OMARCHY_HOST`. Run doctor after the reboot to verify the setup, and use update for ongoing maintenance:
```bash
dot doctor # health checks
dot update # self-update and relaunch, then pull, stow, and rebuild
dot git-diff # review changes across managed repos
context git # branch context for the current repo (from the context-git package)
```
`context git` and `context stack` come from the `context-git` AUR package installed during init. They are used by OpenCode plugins and agent harnesses for repository context; see [Context Integration](/git/context/).
`dot update` is the everyday command. It pulls the public dotfiles, installs Bun dependencies, rebuilds and relaunches on the new binary, then scans and pulls tracked repositories. The remaining full-update phases trust mise configs, regenerate completions, sync MCP configs, stow the committed skills snapshot, rebuild, install Herdr Lazy when needed, restore the declared Herdr plugins from its lockfile, sync agents, backfill the init marker, and refresh resume-managed services. It never fetches mutable upstream skill content. Package setup stays in `dot init`, while `dot doctor` reports package health. See the [Command Reference](/dot/commands/) for scoped phase flags and exit codes.
# New Machine Checklist
> Step-by-step setup for a new Omarchy machine.
A clean, end-to-end walkthrough for setting up a new machine.
1. Clone `dotfiles` to `~/.config/dotfiles`.
2. If you want private dotfiles, confirm `gh auth status` works before `dot init`; init clones `dotfiles-private` to `~/.config/dotfiles-private` automatically when auth is available.
3. Install bootstrap build prerequisites:
Install bootstrap prerequisites
```bash
yay -S --needed git mise
```
4. Build the `dot` binary (mise trusts and installs the pinned toolchain, then the build task installs dependencies and compiles it):
Build dot
```bash
cd ~/.config/dotfiles
mise trust
mise install
mise run dot:build
```
5. Run first-use setup using the mode that matches this machine.
6. UWSM, Hyprland, and Ghostty customisations are stowed from their public packages. Init removes only the retired `timmo001/omarchy-uwsm` checkout before linking the Quattro-compatible override.
7. `dot init` verifies the pinned public package signing fingerprint, registers the signed `timmo` repository before the standard repositories, and retains AUR as the fallback for packages not published there.
8. `dot init` opens the managed [firewall rules](/dot/utilities/#firewall-rules) (KDE Connect, Home Assistant, the OpenCode server, LocalSend, and the libvirt NAT network) when `ufw` is installed.
9. Restart your shell and confirm `dot help` is on `PATH`.
10. Run `dot git-diff` and verify the expected repo state.
11. Reboot so the Omarchy session picks up `OMARCHY_HOST`, then run `dot doctor` to verify the setup. Use `dot update` for ongoing sync, stow refreshes, and rebuilds.
* Desktop / VM
First-use setup
```bash
~/.config/dotfiles/scripts/.local/bin/dot init --noninteractive
```
* Laptop
First-use setup
```bash
dot init --host laptop --noninteractive
```
* Interactive
First-use setup
```bash
dot init
```
`--noninteractive` skips the Hypr host questionnaire; elevation and package tools may still prompt. The accepted `--confirm` compatibility flag does not suppress those prompts.
Hypr host and mise tools
`dot init` selects the Hypr host early and creates `~/.config/hypr/host`. Reboot after init so Hyprland, the Omarchy shell, launchers, services, and new shells inherit `OMARCHY_HOST` from the stowed host config. Init runs `mise install` immediately after stowing dotfiles and before installing managed Arch/AUR package lists, so Bun, Node, pnpm, and similar tools come from the stowed mise config rather than global pacman packages. Later full `dot update` runs repo pulls, rebuilds, mise trust refreshes, MCP sync, and stow without checking or installing packages.
GNOME Boxes shared folders
Arch provides `spice-webdavd` in the `phodav` package. Share a host folder from Boxes and pass `--log /dot-init.log` when you want init output written somewhere visible from the host.
# Git & GitHub
> Diff, log, status, and a notification inbox across managed repos.
`dot` includes git and GitHub tools that work across every managed repo (public and private dotfiles, configured private git repos, and Omarchy repos), and integrates the standalone `context` CLI for branch and stack context. Most watcher commands have an interactive TUI plus `--raw` and `--bar-json` output for scripts and status bars. Local commit automation goes through the guarded `dot git-commit` gateway.
[Diff & Repo Watcher](/git/diff/)The git-diff TUI and CLI for dirty worktrees and ahead/behind state.
[Context Integration](/git/context/)How OpenCode plugins consume the standalone context CLI.
[Commit Gateway](/git/commit/)Guarded commits with scoped paths, message checks, and safe push mode.
[Notifications](/git/notifications/)The GitHub notification inbox with read/done/ignore actions.
## Status bar integration
[Section titled “Status bar integration”](#status-bar-integration)
Several of these commands emit `--bar-json` output for a status bar module. See [Bar Integrations](/bar-integrations/) for the JSON format, click actions, and which commands support it.
## Private repo configuration
[Section titled “Private repo configuration”](#private-repo-configuration)
Repo activity and notification settings live in the private `dot-git.yml` config. See [Private Git Config](/configuration/private-git/) for the schema.
# Commit Gateway
> Guarded commits through dot git-commit instead of raw git commit.
## `dot git-commit`
[Section titled “dot git-commit”](#dot-git-commit)
`dot git-commit` is the guarded commit path for agents and local workflow commands. It creates normal git commits, but wraps the dangerous parts so commits stay scoped, styled, and harder to run on the wrong branch.
```bash
dot git-commit -m "Add commit gateway" # commit the staged set
dot git-commit -m "Scope to one file" --path src/git/Status.ts # commit only named files
dot git-commit -m "Commit and push" --push # commit, rebase-pull, then push
dot git-commit --amend # fold staged changes into HEAD, keep its message
dot git-commit --amend -m "Reword last commit" # rewrite HEAD's subject
dot git-commit -m "Preview only" --dry-run # show the plan, change nothing
```
Agents use this command through the `git-commit` skill. Raw `git commit` is blocked in the OpenCode permission config, so `/commit`, `/commit-push`, and `/commit-push-watch` all route through the gateway. A commit or push request authorises only that specific action; a later change still needs a fresh explicit request. `/commit-push` omits post-push background workflow watchers, while `/commit-push-watch` opts into them. Direct use of the skill continues to watch after a successful push by default.
## Agent workflow
[Section titled “Agent workflow”](#agent-workflow)
The `commit-context` plugin injects a `` block before `/commit`, `/commit-push`, and `/commit-push-watch` run. The commands stay in the parent session, so they retain the reviewed conversation without injecting full patches.
When files are already staged, that staged set supplies the attribution candidates. Otherwise, candidates are current dirty paths that OpenCode recorded as touched by the session tree. Candidate paths are a discovery superset, not authorisation to commit them all. The agent filters every path against the active user request and excludes or clarifies unrelated work, even when it is staged or belongs to a separate coherent change. Session attribution is path-level, not hunk-level: a file can still contain pre-existing or concurrent edits that require a question.
The agent refreshes through the Context MCP server’s `git_context` tool or stops when attribution is incomplete. It never broadens the scope to every dirty file. See [Context Integration](/git/context/#commit-context-commit-context) for the evidence sources, multi-repository scopes, and fallback rules.
Staging and commit still go through `dot git-commit` only after the user explicitly requests a commit or push.
## Scope
[Section titled “Scope”](#scope)
Without `--path`, the command commits the current staged set. It never runs `git add -A`.
Use repeated `--path ` flags when one working tree contains several unrelated changes. That commits only those files and leaves other staged or unstaged files alone.
`--dry-run` validates the message, resolves the target branch, prints the commit or push plan, and exits before staging, committing, or pushing.
## Amend Mode
[Section titled “Amend Mode”](#amend-mode)
`--amend` rewrites the previous commit instead of creating a new one. It folds the staged set (or a `--path` scope) into HEAD and, without `--message`, keeps HEAD’s existing message. Pass `--message` to reword the subject; it runs through the same message guards. An amend with nothing staged is allowed, so `--amend -m "..."` is the way to reword the last commit.
## Message Guards
[Section titled “Message Guards”](#message-guards)
The gateway validates the subject before committing:
| Guard | Behaviour |
| --------------------- | --------------------------------------------------------------------------------------------------- |
| Single line | Rejects multi-line messages and bodies. |
| Non-empty | Rejects blank subjects after trimming. |
| No em/en-dash | Rejects typographic dash punctuation; use a hyphen. |
| No trailing full stop | Rejects subjects ending in `.`. |
| Length limits | Warns over 60 characters, rejects over 120. |
| Plain text | Rejects tabs and control characters; warns on curly quotes, non-breaking spaces, and double spaces. |
The preferred style is a concise, imperative, single-line subject, for example `Add git commit gateway`.
## Branch Guard
[Section titled “Branch Guard”](#branch-guard)
The command refuses commits to the base branch of a repository you do not own. Ownership is configured with one or more `git config dot.owner ` values.
The guard catches both a direct clone of someone else’s repository and a fork with a foreign `upstream` remote. It resolves the base branch from the repository’s default branch metadata rather than assuming `main` or `master`.
Work on a feature branch for upstream PRs. Personal repos, takeover forks with no foreign remote, and non-base branches are allowed.
## Push Mode
[Section titled “Push Mode”](#push-mode)
`--push` commits first, then pulls with `--rebase` before pushing. It sets an upstream for the current branch when one is missing and never runs a plain force-push. Agents must only use it for the specific commit/push request the user just made.
When combined with `--amend`, the push instead uses `--force-with-lease`: the rewritten commit only overwrites the remote branch when it still matches the ref we last saw, so a teammate’s or bot’s newer commit blocks the push rather than being clobbered. The pull-rebase step is skipped in this case.
If the rebase conflicts, the command aborts the push path and leaves the commit in place for manual integration.
# Context Integration
> How dotfiles OpenCode plugins consume the standalone context tool.
Generic branch and stack context lives in the standalone [`context`](https://context.timmo.dev) CLI and MCP server. The dotfiles repo no longer embeds that logic in `dot`; it installs the tool, stows shell completions, and keeps the OpenCode plugins that turn the JSON payloads into prompt XML.
## Install
[Section titled “Install”](#install)
`dot init` installs the `context-git` package from [`.dot-public-packages`](/configuration/packages/#public-packages). That package provides the `context` binary on `PATH`. After init, verify in any git checkout:
```bash
context git --json | head
context stack --json | head
```
Shell completions for `context` are stowed from this repo. Regenerate them after upgrading the binary with `context completions bash|fish|zsh`, then `dot stow`. See [Shell Setup](/dot/shell/#shell-completions).
## CLI usage
[Section titled “CLI usage”](#cli-usage)
Use the commands directly when scripting or debugging agent context:
```bash
context git # human-readable branch context
context git --json # structured payload for tools and plugins
context stack # human-readable tech-stack summary
context stack --json # structured stack payload
context help git # command help (also exposed as an MCP resource)
```
`NO_COLOR` disables ANSI colour in TTY-aware output. See [Environment Variables](/configuration/environment/#debugging-and-output).
## MCP server
[Section titled “MCP server”](#mcp-server)
Repository context tools are provided by the standalone `context` MCP server:
```bash
context mcp
```
Typical tools (names from `tools/list`):
| Tool | Purpose |
| --------------- | ----------------------------------------- |
| `git_context` | Branch context for the current repository |
| `stack_context` | Tech-stack summary for a directory |
| `command_help` | Help text for a `context` subcommand |
Read-only resources include `context://git`, `context://stack`, and `context://command/{name}`. Wire the server in an agent harness with `["context", "mcp"]` as the command. Full schemas and examples are on .
Use [`notes mcp`](https://notes.timmo.dev/mcp/) for the notes vault. Agent harnesses that need repository context and notes load both standalone stdio servers side by side.
## OpenCode plugins in dotfiles
[Section titled “OpenCode plugins in dotfiles”](#opencode-plugins-in-dotfiles)
The dotfiles repo keeps the OpenCode plugins that consume `context` JSON and render prompt blocks:
* `branch-context` runs `context git --json` and injects `` XML for scoped slash commands (PR-inclusive branch context, or work-scope-only variants without the pull request). PR descriptions are capped at 4,000 characters; up to 20 comments and 20 reviews are included, with each body capped at 2,000 characters.
* `stack-context` runs `context stack --json` and injects `` XML automatically on the first message of a session inside a git repository, and on demand for `/inject-stack` or `/inject-context`.
Plugin-specific command allowlists and injection rules stay in `agents/.config/opencode/plugins/`. For CLI flags, JSON payload fields, and MCP tool parameters, see .
## Injected XML blocks
[Section titled “Injected XML blocks”](#injected-xml-blocks)
The plugins render the structured JSON into tagged XML sections. Treat injected blocks as the primary source for branch and stack analysis; avoid re-running `git`/`gh` or re-scanning the tree unless the user asks for a fresh snapshot or the block reports collection failure.
### Branch context (``)
[Section titled “Branch context (\)”](#branch-context-branch-context)
| Section | Purpose |
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `context-metadata` | How the snapshot was produced (`context git --json`) and when it was generated. |
| `branch-metadata` | Repository identity, current branch, HEAD, default remote/branch, base ref, upstream, ahead/behind, and whether HEAD is on the default branch. Unresolved default-branch metadata is labelled `(unresolved)` rather than guessed. |
| `status` | Compact `git status -sb` summary. |
| `work-scope` | Current work scope in priority order: unstaged, staged, then branch-only changes. On the default branch, branch scope is skipped and recent commits are shown instead. |
| `pull-request` | PR metadata, description, comments, reviews, and checks (branch-focused commands only). |
| `truncations` | Applied output limits. Treat affected sections as partial. |
| `warnings` | Non-fatal collection issues, fallbacks, or missing data. |
`work-scope` reflects the producer’s `workScope.state`:
| State | Meaning |
| ---------------- | ------------------------------------------------------------------------------------ |
| `collected` | Branch-only commits, files, and diff stat are present. |
| `not-applicable` | HEAD is on the default branch; branch scope is skipped and recent commits are shown. |
| `unresolved` | Default branch could not be resolved; recent commits are shown with a reason. |
Work-scope-only commands (for example scoped refactor helpers) call `context git --json --no-pr` and omit the `pull-request` section.
### Commit context (``)
[Section titled “Commit context (\)”](#commit-context-commit-context)
`/commit`, `/commit-push`, and `/commit-push-watch` receive a narrower block from the `commit-context` plugin. It combines the current Context CLI snapshot with successful `edit`, `write`, and `apply_patch` tool inputs plus OpenCode’s persisted session patch parts. Failed mutation calls are ignored. The block lists candidate and excluded paths separately, includes a compact diff stat plus five recent subjects, and omits full patches and repeated path or worktree lists.
An existing staged set takes precedence for attribution. Without one, only current dirty paths touched by the active session tree become candidates. Candidates are a discovery superset, not an authorised commit scope: the active user request determines which paths belong, and unrelated or ambiguous paths must be excluded or clarified. This is path-level evidence rather than hunk ownership, so mixed files still require clarification. A complete block means only that attribution collection succeeded. Missing, stale, malformed, truncated, or unattributed evidence marks the block partial and requires a Context MCP refresh or a stop.
When successful mutation tool calls identify files under several Git roots, the outer block contains one independently collected `` per repository. Each scope names the root where its gateway commands must run. Unresolved roots and legacy patch evidence outside the current repository remain partial and require a refresh or stop.
### Stack context (``)
[Section titled “Stack context (\)”](#stack-context-stack-context)
| Section | Purpose |
| ------------------ | -------------------------------------------------------------------------------------------------------------- |
| `context-metadata` | How the snapshot was produced (`context stack --json`), repository root, and files scanned. |
| `languages` | Detected languages with file counts and general locations. |
| `ecosystems` | Package ecosystems from manifest files. |
| `tooling` | Package managers, linters, formatters, task runners, build tools, and test runners from lockfiles and configs. |
| `frameworks` | Frameworks and libraries from declared dependencies. |
| `truncations` | Scan or output limits that make a section partial. |
| `warnings` | Non-fatal collection issues. |
Automatic session injection runs at most once per session and only inside a git worktree with a non-empty detection result.
## MCP refresh outside plugin-backed commands
[Section titled “MCP refresh outside plugin-backed commands”](#mcp-refresh-outside-plugin-backed-commands)
Plugin injection covers registered slash commands. For ad-hoc work or a missing, stale, or partial injected block, use the Context MCP server’s `git_context` tool with only the fields the task needs (`diff`, `branchDiff`, PR details, and so on). See [Commit Gateway](/git/commit/#agent-workflow) for the commit path.
# Diff & Repo Watcher
> The dot git-diff TUI and CLI for tracking dirty worktrees and ahead/behind state across managed repos.
## `dot git-diff`
[Section titled “dot git-diff”](#dot-git-diff)
A two-pane repo watcher across every managed repository: public and private dotfiles, the notes vault, Omarchy repos (when enabled), and schedule-gated activity repos from `dot-git.yml`. The **Changed** pane lists repos with uncommitted changes or non-zero ahead/behind counts against their upstream; **Other** lists the rest.
```bash
dot git-diff # interactive TUI (alias: dot diff)
dot git-diff --raw # text summary of repos with changes
dot git-diff --bar-json # JSON for status bars and shell modules
dot git-diff --list-changed # changed repos as name|path rows
dot git-diff --list-all # all tracked repos as name|path rows
dot git-diff --no-fetch # skip upstream fetches; use local refs only
dot git-diff --tab other # open with the Other pane focused
dot git-diff --repo dotfiles # open a changed repo directly in lazygit
```
The TUI polls every ten seconds and performs an initial poll for a fast first paint. `--repo ` selects a changed repository and immediately suspends the TUI into lazygit. Quitting lazygit resumes the diff TUI with that repository still selected.
## TUI layout
[Section titled “TUI layout”](#tui-layout)
| Pane | Contents |
| ------- | ------------------------------------------------------------- |
| Changed | Repos with dirty worktrees or ahead/behind upstream |
| Other | Tracked repos with a clean worktree and no ahead/behind drift |
The status bar shows when the last poll ran, how many repos changed, and how many have a stale `.git/index.lock` file. Repos with a lock file show a lock icon in the list.
## Keyboard
[Section titled “Keyboard”](#keyboard)
| Key | Action |
| --------------- | ------------------------------------------------------- |
| ↑ / ↓ | Navigate the active pane |
| Tab | Switch between Changed and Other |
| Enter | Open lazygit in the selected repo (suspends the TUI) |
| `e` | Open the repo in your default editor |
| `E` | Open the repo in your visual editor |
| `o` | Open an interactive OpenCode session |
| `O` | Open an OpenCode plan session |
| `t` | Open a terminal in the repo directory |
| `w` | Open the repo on GitHub in the browser |
| `x` | Remove a stale `.git/index.lock` from the selected repo |
| `r` | Refresh immediately |
| Esc / Backspace | Return to the main menu |
Repo pulls and fetches are not available from this view; use `dot update` or work inside the repo directly.
## Index locks
[Section titled “Index locks”](#index-locks)
Status scans run `git --no-optional-locks status` so background polling does not refresh the index or compete with an in-flight rebase, merge, or other index-writing operation. That keeps bar modules and the TUI from creating or waiting on `.git/index.lock` during normal reads.
When a crashed git process leaves a stale lock behind, the repo appears with a lock indicator. Press `x` on the selected repo to remove the lock file and trigger a refresh. Only remove a lock when no git command is actively running in that repository.
## Upstream fetches
[Section titled “Upstream fetches”](#upstream-fetches)
When a repo has an upstream configured, `dot git-diff` fetches the tracking branch before computing ahead/behind counts. Fetches are TTL-cached (default five minutes, controlled by `DOT_FETCH_TTL_SECONDS`). Pass `--no-fetch` to skip network fetches and rely on local tracking refs.
## Status bar module
[Section titled “Status bar module”](#status-bar-module)
The `timmo.git` Quickshell plugin polls `dot git-diff --bar-json` and combines its repository rows with GitHub notifications in one bar widget and native panel. The panel opens the Changed or Other TUI separately and refreshes both sources together. The stowed `git-diff-bar` cache command remains available to generic status bars. See [Bar Integrations](/bar-integrations/) for the shared JSON contract.
## Configuration
[Section titled “Configuration”](#configuration)
Which repositories appear is controlled by the private `dot-git.yml` config. See [Private Git Config](/configuration/private-git/).
# Notifications
> The GitHub notification inbox with read, done, ignore, and bot-read actions.
## `dot git-notifications`
[Section titled “dot git-notifications”](#dot-git-notifications)
The authenticated user’s GitHub notification inbox. Without machine or action flags it opens the interactive TUI with open, mark-read, done, ignore, unignore, and bot-read actions.
```bash
dot git-notifications # interactive TUI
dot git-notifications --raw # text summary of notification threads
dot git-notifications --bar-json # JSON for status bars and shell modules
dot git-notifications --list-threads # notification threads as rows
dot git-notifications --participating # only participating or mentioned threads
dot git-notifications --all # include read notifications
```
### Actions
[Section titled “Actions”](#actions)
```bash
dot git-notifications --mark-read # mark a thread read
dot git-notifications --mark-done # mark a thread done
dot git-notifications --ignore # ignore new notifications for a thread
dot git-notifications --unignore # stop ignoring a thread
```
### Bot notifications
[Section titled “Bot notifications”](#bot-notifications)
```bash
dot git-notifications --mark-bot-read --dry-run # preview matched bot threads
dot git-notifications --mark-bot-read # mark Renovate/Dependabot/bot threads read
```
Use `--dry-run` first to preview the threads that would be marked.
## Requirements
[Section titled “Requirements”](#requirements)
The notification API requires `gh` authenticated with a classic token carrying the `notifications` or `repo` scope.
## Status bar module
[Section titled “Status bar module”](#status-bar-module)
The `timmo.git` Quickshell plugin polls `dot git-notifications --bar-json` and combines its filtered threads with repository state in one bar widget and native panel. Notification surfaces hide repos that are not enabled in `dot-git.yml`, while upstream notifications can match a managed fork’s `remote.upstream.url`. The panel opens thread URLs or the filtered notifications TUI and refreshes both Git sources together. The stowed `git-notifications-bar` cache command remains available to generic status bars. `dot doctor` verifies GitHub notification API access.
# Knowledge Base
> Practical setup notes for system customisations and one-off fixes.
This section collects practical runbooks that sit below the main dotfiles reference. They are for system state that is useful to repeat, but too specific for the core command docs.
[FIDO2 Security Key Auth](/knowledge-base/fido2-security-key-auth/)Set up Omarchy FIDO2 auth for sudo, polkit, and Hyprlock with touch-first daily prompts.
[Workspace Setup](/knowledge-base/workspace-setup/)Rebuild the desktop host's work and non-work workspace layout.
[Workspace Relayout](/knowledge-base/workspace-relayout/)Apply and capture ratio-based Hyprland layouts for the active workspace.
[Workspace Session Recovery](/knowledge-base/workspace-session-recovery/)Capture and restore Hyprland window sessions after restarts, crashes, or layout experiments.
[Work Browser Launchers](/knowledge-base/work-browser-launchers/)Launch work Chrome, Slack, and Discord into predictable Hyprland workspaces.
[URL Routing and Webapps](/knowledge-base/url-routing-webapps/)Route matching URLs to Omarchy webapps while keeping the normal browser fallback.
[Doorbell Popup](/knowledge-base/doorbell-popup/)Open a Home Assistant camera popup from a doorbell or motion entity.
[Twitch Desktop Tools](/knowledge-base/twitch-desktop-tools/)Use the Twitch panel and notification recovery scripts.
[Resume Recovery](/knowledge-base/resume-recovery/)Refresh the Omarchy shell, Git panel, and Twitch notifications after suspend.
[Git Shell Panel](/knowledge-base/git-shell-panel/)Use and recover the unified repository and GitHub notification panel.
[System Health Triage](/knowledge-base/system-health-triage/)Collect a short health report for freezes, slowdowns, thermal issues, and noisy logs.
[Desktop Command Wrappers](/knowledge-base/desktop-command-wrappers/)Small helper scripts for terminal launches, notifications, loading overlays, and workspace placement.
[GitHub Dotenv Secrets](/knowledge-base/github-dotenv-secrets/)Import simple .env keys into GitHub Actions repository secrets with gh.
# Desktop Command Wrappers
> Small helper scripts for terminal launches, notifications, loading overlays, and workspace placement.
These wrappers make other scripts and Hypr bindings friendlier on the desktop. They are intentionally small, but they are useful when composing menu actions or one-shot helpers.
## Run a command in a terminal
[Section titled “Run a command in a terminal”](#run-a-command-in-a-terminal)
`run-in-terminal` opens a command in a new terminal window:
```bash
run-in-terminal 'dot doctor'
```
Modes:
| Mode | Behaviour |
| --------- | ------------------------------------------------------------------------------------ |
| `--shell` | Default. Run the command, then stay in an interactive shell. |
| `--wait` | Run the command, then print a prompt saying Ctrl-D closes or the shell can continue. |
| `--exit` | Run the command and close the terminal when it exits. |
Examples:
```bash
run-in-terminal --wait 'workspace-restore --dry-run'
run-in-terminal --exit 'dot doctor'
```
For `--shell` and `--wait`, the wrapper creates a temporary `ZDOTDIR` so the command runs after shell initialisation, then removes that temp directory from the generated shell startup file.
## Notify around a command
[Section titled “Notify around a command”](#notify-around-a-command)
`run-with-notify` runs a command and sends a desktop notification with the result:
```bash
run-with-notify 'Capture' workspace-capture
run-with-notify -a 'Workspace' 'Restore' workspace-restore
```
On success, the notification body is command output or `Done`. On failure, it sends a critical notification with the captured stderr/stdout or `Unknown error`. Both use Omarchy’s glyph and spacing format.
## Loading overlay
[Section titled “Loading overlay”](#loading-overlay)
`popup-loading` shows a singleton GTK layer-shell loading overlay on the focused Hyprland monitor:
```bash
popup-loading show 'Updating...'
popup-loading hide
```
Calling it with only a message also shows the overlay:
```bash
popup-loading 'Loading dashboard...'
```
The overlay keeps a runtime socket and lock under `${XDG_RUNTIME_DIR:-/tmp}`:
```text
popup-loading.sock
popup-loading.lock
```
It reads theme colours from:
```text
~/.config/omarchy/current/theme/colors.toml
```
If theme colours are unavailable, it falls back to a dark default.
## Wrap a command with the loading overlay
[Section titled “Wrap a command with the loading overlay”](#wrap-a-command-with-the-loading-overlay)
`with-popup-loading` shows the overlay, runs the command, then hides the overlay on exit:
```bash
with-popup-loading 'Updating dotfiles...' dot update
```
If no command is passed, it only shows the overlay and lets the shell trap hide it when the wrapper exits.
## Launch on a workspace
[Section titled “Launch on a workspace”](#launch-on-a-workspace)
`launch-on-workspace` starts a command, waits for a new Hyprland client matching a class regex, then moves that new window to the target workspace:
```bash
launch-on-workspace 3 '^(work-browser|google-chrome)$' 'uwsm app -- google-chrome-stable --new-window'
```
It compares matching client addresses before and after launch. Only a newly detected address is moved.
This helper is used by the work browser launchers documented in [Work Browser Launchers](/knowledge-base/work-browser-launchers/).
## Toggle HDR on the primary monitor
[Section titled “Toggle HDR on the primary monitor”](#toggle-hdr-on-the-primary-monitor)
`toggle-primary-monitor-hdr` temporarily enables 10-bit HDR with the full BT.2020 colour gamut and raises SDR content brightness through Hyprland’s `sdrbrightness` setting (currently `2.0`):
```bash
toggle-primary-monitor-hdr
```
On the `desktop` host it targets the main `HDMI-A-2` display, preserving the original right-monitor behaviour. On other hosts it targets the focused display, using its live resolution, refresh rate, position, scale, and transform.
Run it again while HDR is active to restore 8-bit SDR colour and normal SDR brightness. Both rules are applied at runtime and are not written to `monitors.conf`.
## Troubleshooting
[Section titled “Troubleshooting”](#troubleshooting)
If `run-in-terminal --exit` closes too quickly, use `--wait` while debugging.
If `popup-loading` fails to show, check that the session supports GTK4 layer shell and that `hyprctl -j monitors` works.
If `launch-on-workspace` launches the app but does not move it, the class regex probably does not match the final Hyprland class. Inspect clients with:
```bash
hyprctl -j clients
```
# Doorbell Popup
> Open a Home Assistant camera popup from a doorbell or motion entity.
`doorbell-popup` opens a Home Assistant more-info camera popup as an Omarchy webapp and positions it as a floating Hyprland window. It can run once, or watch a Home Assistant entity and open the popup when that entity turns on.
## Host bindings
[Section titled “Host bindings”](#host-bindings)
Both public host overrides expose a permanent open-only popup on:
```text
SUPER+ALT+C
```
Both hosts use the same mobile-sized bottom-right geometry on the active workspace’s monitor. Nothing is forced to a host-specific monitor or workspace.
## Defaults
[Section titled “Defaults”](#defaults)
The script defaults to:
| Setting | Default |
| ------------------ | --------------------------------- |
| Motion entity | `input_boolean.doorbell` |
| Camera entity | `camera.front_door_snapshot` |
| Home Assistant URL | `http://homeassistant.local:8123` |
| Target workspace | Active workspace |
| Size | `380x500` |
| Duration | `20` seconds |
The popup URL is built as:
```text
/lovelace/home?more-info-entity-id=
```
## Open once
[Section titled “Open once”](#open-once)
Open the popup and exit:
```bash
doorbell-popup --open-only
```
Keep it open instead of scheduling the auto-close timer:
```bash
doorbell-popup --open-only --no-auto-close
```
Focus the popup after opening or repositioning:
```bash
doorbell-popup --open-only --focus-popup
```
## Watch mode
[Section titled “Watch mode”](#watch-mode)
Run without `--open-only` to watch the motion entity:
```bash
doorbell-popup
```
Watch mode uses:
```bash
go-automate ha bridge watch entity --bar-json --icon '' input_boolean.doorbell
```
When the emitted JSON has class `on`, the popup opens or repositions. The Omarchy Shell Home Assistant service keeps this watcher loaded in the background through `ha-module-bar doorbell`; it does not add a visible dashboard row.
## Positioning
[Section titled “Positioning”](#positioning)
`doorbell-popup` uses the focused monitor by default, keeping the popup on the active workspace. Explicit flags override that:
* `--monitor ` targets that monitor.
* `--workspace ` moves the popup to that workspace and uses its monitor.
`doorbell-popup` delegates geometry to `launch-floating-webapp`, the same global command used by shell webapp clicks. It places the exact launched window in the target monitor’s bottom-right corner and accounts for output scale and reserved areas.
## Useful overrides
[Section titled “Useful overrides”](#useful-overrides)
```bash
doorbell-popup --open-only --monitor DP-1 --width 380 --height 500
doorbell-popup --entity input_boolean.doorbell --camera-entity camera.front_door_snapshot
doorbell-popup --base-url http://homeassistant.local:8123 --workspace 1
doorbell-popup --duration 30 --margin 20 --bottom-margin 12
```
Numeric values must be positive integers.
## Test without a real event
[Section titled “Test without a real event”](#test-without-a-real-event)
Simulate one on event:
```bash
doorbell-popup --simulate-event on --simulate-exit
```
Force the startup simulation path:
```bash
doorbell-popup --force-motion-true --simulate-exit
```
## State
[Section titled “State”](#state)
The script stores the last popup address at:
```text
${XDG_RUNTIME_DIR:-/tmp}/doorbell-popup.address
```
If that address still exists, the script repositions the existing popup instead of opening a duplicate. If the address is stale, it removes the file.
## Recovery flow
[Section titled “Recovery flow”](#recovery-flow)
1. Test with `doorbell-popup --open-only` first.
2. Add `--monitor`, `--width`, and `--height` until placement is right.
3. Test the event path with `--simulate-event on --simulate-exit`.
4. Run watch mode only after the open-once path works.
## Troubleshooting
[Section titled “Troubleshooting”](#troubleshooting)
If the script exits immediately, check required commands:
```bash
command -v go-automate hyprctl jq launch-floating-webapp
```
If placement is on the wrong monitor, pass `--monitor ` and verify monitor names with:
```bash
hyprctl -j monitors
```
If no event opens the popup, check the entity stream directly:
```bash
go-automate ha bridge watch entity --bar-json --icon '' input_boolean.doorbell
```
If the popup keeps reusing a dead address, remove the runtime file:
```bash
rm -f "${XDG_RUNTIME_DIR:-/tmp}/doorbell-popup.address"
```
# FIDO2 Security Key Auth
> Set up Omarchy FIDO2 authentication for sudo, polkit, and Hyprlock with touch-first daily prompts.
This setup uses a FIDO2 security key through `pam-u2f`. The key becomes the preferred local authentication path for `sudo`, polkit prompts, and Hyprlock, while the account password stays available as fallback.
The daily path is touch-only. Do not add `pinverification=1` unless you want the security-key PIN on every `sudo`, `pkexec`, and Hyprlock authentication.
It does not change LUKS disk decryption. Boot still uses the LUKS passphrase unless a separate LUKS token setup is configured.
Keep a fallback
Keep an unlocked terminal open while editing PAM files. Do not remove the password fallback unless you have a separate recovery path.
## Start with the Omarchy menu
[Section titled “Start with the Omarchy menu”](#start-with-the-omarchy-menu)
Use Omarchy’s built-in FIDO2 setup first:
1. Open the Omarchy menu.
2. Go to **Setup**.
3. Go to **Security**.
4. Choose **Fido2**.
5. Touch the security key when prompted.
6. Let the script test `sudo` before closing the terminal.
The setup installs `libfido2` and `pam-u2f`, registers the key with `pamu2fcfg`, stores the mapping at `/etc/fido2/fido2`, and adds `pam_u2f.so` to `sudo` and `polkit-1`.
The default Omarchy setup is touch-based. Keep that behaviour for daily use, then add Hyprlock manually.
## Confirm sudo and polkit
[Section titled “Confirm sudo and polkit”](#confirm-sudo-and-polkit)
Use this touch-only line in `/etc/pam.d/sudo` and `/etc/pam.d/polkit-1`:
```text
auth sufficient pam_u2f.so cue [cue_prompt=Touch your security key] authfile=/etc/fido2/fido2
```
Keep the existing password lines below it. With `sufficient`, a successful security-key auth skips the password prompt; failure falls through to the password fallback.
## Add Hyprlock
[Section titled “Add Hyprlock”](#add-hyprlock)
The Omarchy shell lock screen uses its own PAM service. Omarchy’s FIDO2 setup does not add this line automatically, so prepend it to `/etc/pam.d/omarchy-lock-password`:
```text
auth sufficient pam_u2f.so cue [cue_prompt=Touch your security key] authfile=/etc/fido2/fido2
```
Leave the default password stack below it so normal password unlock still works. The packaged lock-screen prompt is owned by Omarchy 4 and is not configured through a stowed `hyprlock.conf`.
## Remove fingerprint leftovers
[Section titled “Remove fingerprint leftovers”](#remove-fingerprint-leftovers)
If Omarchy’s fingerprint setup was run by mistake, remove its PAM lines and packages:
```bash
pkexec sed -i '/pam_fprintd\.so/d' /etc/pam.d/sudo /etc/pam.d/polkit-1
pkexec pacman -Rns fprintd libfprint-git
```
The Omarchy shell detects fingerprint support through its separate `omarchy-lock-fingerprint` PAM service.
## PIN mode
[Section titled “PIN mode”](#pin-mode)
`pinverification=1` is stricter, but `pam-u2f` prompts for the security-key PIN on every PAM authentication. It does not cache the PIN for the whole login session.
Use PIN mode only if that tradeoff is intentional:
```text
auth sufficient pam_u2f.so cue [cue_prompt=Touch your security key] pinverification=1 authfile=/etc/fido2/fido2
```
## Test order
[Section titled “Test order”](#test-order)
Test each layer before relying on it:
1. Run `sudo -k`, then `sudo true`.
2. Touch the security key when prompted.
3. Trigger a polkit prompt if convenient.
4. Lock the screen with the Omarchy shell while another session or terminal remains available.
5. Test password fallback by trying without the security key.
## Rollback
[Section titled “Rollback”](#rollback)
Restore the backed-up PAM files, or remove only the added `pam_u2f.so` line from `/etc/pam.d/omarchy-lock-password` if the lock screen is the only broken part.
Do not delete `/etc/fido2/fido2` unless you are fully removing FIDO2 auth.
# Git Shell Panel
> Use and recover the unified repository and GitHub notification panel.
The `timmo.git` Omarchy Shell plugin combines managed repository state and GitHub notifications in one primary-output bar widget and native panel.
## Open and refresh
[Section titled “Open and refresh”](#open-and-refresh)
| Control | Action |
| -------------- | ----------------------------------------- |
| `CTRL+ALT+G` | Toggle the panel |
| Left click | Toggle the panel |
| Right click | Refresh repository and notification state |
| `r` while open | Refresh repository and notification state |
Open or refresh it from a terminal with:
```bash
omarchy-shell timmo.git toggle
omarchy-shell timmo.git refresh
```
The widget stays hidden when tracked repositories are clean and the GitHub inbox has no unread threads. Hovering the bar reveals its clean-state icons. A question mark indicates that one of the data sources could not be refreshed.
## Panel actions
[Section titled “Panel actions”](#panel-actions)
The panel lists changed repositories followed by filtered GitHub notification threads. Its actions open the Changed or Other repository view, open the filtered notifications TUI, or refresh both sources. Selecting a repository opens it in the diff TUI; selecting a notification opens its GitHub URL.
Use Up/Down or `j`/`k` to move, Enter to activate, Escape to close, and Tab to move to the next bar panel.
## Data sources
[Section titled “Data sources”](#data-sources)
The service polls these structured commands every minute:
```bash
dot git-diff --bar-json
dot git-notifications --bar-json
```
Repository and notification visibility follows the private `dot-git.yml` configuration. GitHub notifications require an authenticated `gh` token with the `notifications` or `repo` scope.
## Troubleshooting
[Section titled “Troubleshooting”](#troubleshooting)
Run each source directly when the panel shows an unavailable state:
```bash
dot git-diff --bar-json
dot git-notifications --bar-json
```
Then request a fresh panel poll:
```bash
omarchy-shell timmo.git refresh
```
If the panel does not open, confirm that the plugin is discovered and summon it directly:
```bash
omarchy-shell shell listPlugins
omarchy-shell timmo.git open
```
# GitHub Dotenv Secrets
> Import simple .env keys into GitHub Actions repository secrets with gh.
`gh-import-dotenv-to-secrets` is a small helper for copying simple `.env` entries into GitHub Actions repository secrets.
Use it only in the repository that should receive the secrets. It has no dry-run mode and `gh secret set` overwrites secrets with matching names.
Read the file first
Inspect `.env` before running this helper. Do not use it on untrusted files or in the wrong repository.
## Input format
[Section titled “Input format”](#input-format)
The script reads `.env` from the current directory:
```text
KEY=value
QUOTED="value"
SINGLE_QUOTED='value'
```
It skips empty keys and lines whose key starts with `#`.
For each remaining line, it strips one leading and trailing single or double quote from the value, then runs:
```bash
gh secret set "$key" -b"$value_no_quotes"
```
## Run it
[Section titled “Run it”](#run-it)
From the target GitHub repository:
```bash
gh auth status
gh repo view --json nameWithOwner
gh-import-dotenv-to-secrets
```
Verify the resulting secret names:
```bash
gh secret list
```
## What it is not
[Section titled “What it is not”](#what-it-is-not)
This helper is not a full dotenv parser. Avoid using it for:
* Multiline values.
* `export KEY=value` syntax.
* Values containing unescaped `=` that need exact dotenv parsing semantics.
* Environment or organisation secrets.
* Selective import.
For those cases, use `gh secret set` directly.
## Safer manual alternative
[Section titled “Safer manual alternative”](#safer-manual-alternative)
For one or two secrets, prefer setting them explicitly:
```bash
gh secret set MY_SECRET -b"$MY_SECRET"
```
That keeps the target secret name and value source obvious.
## Troubleshooting
[Section titled “Troubleshooting”](#troubleshooting)
If the command writes to the wrong repository, stop and rotate or replace the affected secrets in GitHub. Then rerun from the correct repository.
If a value imports with quotes still attached, set that secret manually with `gh secret set`. This wrapper only strips a simple matching quote at the beginning and end.
If `gh secret set` fails, check GitHub authentication and repository permissions:
```bash
gh auth status
gh repo view --json nameWithOwner
```
# Resume Recovery
> Refresh the Omarchy shell, Git panel, and Twitch notifications after suspend.
`reload-ui-monitor` watches logind for resume events and runs `reload-ui`, which refreshes desktop services and shell widgets that can go stale after sleep.
## Where it runs
[Section titled “Where it runs”](#where-it-runs)
The `dot-reload-ui-monitor.service` user unit runs `reload-ui-monitor` throughout the session. It calls `reload-ui` after logind reports that sleep has ended.
```text
systemctl --user status dot-reload-ui-monitor.service
```
On the laptop, `reload-ui` also re-arms keyboard backlight handling.
You can run the recovery manually without auto-opening Twitch streams with the shared binding:
```text
SUPER+SHIFT+R
```
Or from a terminal:
```bash
reload-ui
```
## What it refreshes
[Section titled “What it refreshes”](#what-it-refreshes)
`reload-ui` detaches itself, writes a fresh log, then:
* Re-arms keyboard backlight handling when available.
* Gracefully restarts `twitch-notifications`, auto-opening configured live channels after an automatic resume.
* Regenerates the host-specific Omarchy shell configuration.
* Restarts the Omarchy shell, reloads its plugins, and refreshes its indicators and default clock.
* Refreshes the unified Git panel’s repository and notification state.
The `SUPER+SHIFT+R` binding passes `--no-auto-open`, so manual recovery restarts Twitch without opening live channels.
The log is written to:
```text
${XDG_STATE_HOME:-~/.local/state}/reload-ui.log
```
## Manual recovery flow
[Section titled “Manual recovery flow”](#manual-recovery-flow)
Use this when the machine wakes but the shell or desktop helpers look stale:
1. Press `SUPER+SHIFT+R`, or run `reload-ui`.
2. Wait for the Omarchy shell to restart and its indicators to refresh.
3. If something still looks wrong, read `~/.local/state/reload-ui.log`.
## Troubleshooting
[Section titled “Troubleshooting”](#troubleshooting)
If shell widgets stay stale, restart the Omarchy shell:
```bash
omarchy restart shell
```
If Git status in the bar stays stale after recovery, refresh the unified shell service directly:
```bash
omarchy-shell timmo.git refresh
```
# System Health Triage
> Collect a short health report for freezes, slowdowns, thermal issues, and noisy logs.
Use `system-health-check` when the machine feels slow, hot, network-stuck, or unstable and you want one report to inspect before guessing.
The command samples system signals, scans recent logs for known failure words, prints a friendly verdict, and saves the full output to a timestamped report.
For the reference summary of utility commands, see [System Utilities](/dot/utilities/).
## Quick run
[Section titled “Quick run”](#quick-run)
```bash
system-health-check
```
Reports are saved under:
```text
${XDG_STATE_HOME:-~/.local/state}/system-health-check/
```
The default run collects five snapshots, fifteen seconds apart, and scans the last fifteen minutes of user and kernel journal logs.
## Faster focused checks
[Section titled “Faster focused checks”](#faster-focused-checks)
Run only the sections you need:
```bash
system-health-check --only cpu,memory,psi --samples 3 --interval 5
system-health-check --only thermal --samples 4 --interval 10
system-health-check --only logs --known-logs-minutes 60
```
Valid sections are:
| Section | What it checks |
| --------- | --------------------------------------------------------------------- |
| `cpu` | Load average and CPU busy percentage. |
| `memory` | Available memory and swap growth. |
| `psi` | Linux pressure stall information for CPU, memory, and IO. |
| `network` | Primary interface traffic and TCP socket growth. |
| `disk` | Root filesystem usage. |
| `thermal` | Thermal zone readings and temperature rise. |
| `logs` | Recent user and kernel journal lines matching known failure patterns. |
## OpenCode handoff
[Section titled “OpenCode handoff”](#opencode-handoff)
When you want immediate AI follow-up, run:
```bash
system-health-check --open-opencode
```
After saving the report, the script opens an interactive OpenCode session with a prompt that points to the report path.
Use this when you want diagnosis and next steps in the same flow. Without an interactive TTY, the script prints a warning instead of trying to open OpenCode.
## Triage flow
[Section titled “Triage flow”](#triage-flow)
1. Run `system-health-check` while the problem is happening or soon after it happens.
2. Read the `Friendly Summary` section first.
3. Treat `Watch` as a signal to monitor and `Stressed` as a signal to investigate immediately.
4. Use the report path from the final line for follow-up analysis.
5. If the problem is narrow, rerun with `--only` for that subsystem.
## Interpreting the verdict
[Section titled “Interpreting the verdict”](#interpreting-the-verdict)
`Healthy` means the sampled window looked stable.
`Watch` means one or more signals crossed a moderate threshold, such as elevated CPU pressure, noticeable memory drop, high disk usage, thermal rise, or noisy logs.
`Stressed` means at least one stronger threshold was crossed, such as very high CPU busy, sharp swap growth, high PSI, critical disk usage, high thermal readings, or many known log matches.
These are heuristics, not proof of root cause. Use them to decide where to inspect next.
## Login doctor notification
[Section titled “Login doctor notification”](#login-doctor-notification)
The repo also ships a user timer for a delayed `dot doctor` check at login:
```text
~/.config/systemd/user/dot-doctor-startup.timer
~/.config/systemd/user/dot-doctor-startup.service
```
The timer runs `dot-doctor-notify` sixty seconds after startup. Use it as a passive startup check; run `dot doctor` directly when you need an immediate health result.
Enable it if you want a startup warning for broken dotfiles state:
```bash
systemctl --user enable --now dot-doctor-startup.timer
```
## Related update checks
[Section titled “Related update checks”](#related-update-checks)
For broader machine updates, use the `topgrade` wrapper documented in [System Utilities](/dot/utilities/). It logs the full session to:
```text
${XDG_STATE_HOME:-~/.local/state}/topgrade.log
```
Keep health triage and update runs separate: collect a health report first when debugging instability, then update once you know whether the machine is already under stress.
# Twitch Desktop Tools
> Use the Omarchy Twitch panel and notification recovery commands.
The `timmo.twitch` Omarchy Shell plugin shows notification state in the bar and opens an attached channel panel. `twitch-notifications` remains the state owner and notification daemon.
## Autostart
[Section titled “Autostart”](#autostart)
The shared Hyprland autostart starts Twitch notifications for both desktop and laptop in UWSM’s background graphical slice:
```bash
uwsm-app -s b -- twitch-notifications
```
The shared resume hook also restarts Twitch notifications after suspend. See [Resume Recovery](/knowledge-base/resume-recovery/).
## Bindings
[Section titled “Bindings”](#bindings)
| Binding | Action |
| ------------- | ------------------------------------------------ |
| `CTRL+ALT+T` | Toggle the Twitch panel. |
| `SUPER+ALT+T` | Open the Twitch following live page as a webapp. |
## Twitch panel
[Section titled “Twitch panel”](#twitch-panel)
Open the panel from a terminal:
```bash
omarchy-shell shell summon timmo.twitch '{}'
```
The panel shows live channels first, followed by configured offline channels. Select a live channel to open its stream or an offline channel to open its recent broadcasts. Opening a Twitch link closes the panel.
Panel actions include:
* Recheck notifications.
* Open all live autolaunch channels.
* Open following.
* Open following live.
* Restart notifications.
Mouse controls on the bar item:
* Left click opens or closes the panel.
* Middle click rechecks notifications.
* Right click restarts the daemon.
Use Up/Down or `j`/`k` to move, Enter to activate, `r` to recheck, Escape to close, and Tab to move to the next bar panel.
On the `desktop` host, channel links use `xdg-open`. Other hosts use `omarchy-launch-webapp`.
## Channel state
[Section titled “Channel state”](#channel-state)
The panel reads structured status from:
```bash
twitch-notifications --status-json
```
Configured channels still come from:
```text
${XDG_CONFIG_HOME:-~/.config}/twitch-notifications/channels.yml
```
The existing status-bar format remains available for the dot dashboard source:
```bash
twitch-notifications --status-bar-json --max-chars 60
```
## Recheck notifications
[Section titled “Recheck notifications”](#recheck-notifications)
```bash
twitch-notifications-recheck
```
The helper detaches from the caller. If `twitch-notifications --status` reports `active`, it runs `twitch-notifications --recheck`. Otherwise it falls back to the restart helper.
## Restart notifications
[Section titled “Restart notifications”](#restart-notifications)
```bash
twitch-notifications-restart
```
The helper detaches, stops matching `twitch-notifications` processes, waits one second, then relaunches through:
```bash
uwsm-app -s b -- twitch-notifications
```
To gracefully restart the running daemon and open configured live channels during its initial check, run:
```bash
twitch-notifications --restart --open
```
## Troubleshooting
[Section titled “Troubleshooting”](#troubleshooting)
If the panel has no channels, check that the channels file exists and contains `- name: ...` entries:
```bash
ls ~/.config/twitch-notifications/channels.yml
```
If live state looks stale, run:
```bash
twitch-notifications-recheck
```
If the process is wedged, run:
```bash
twitch-notifications-restart
```
If the panel does not open, confirm that the plugin is discovered and summon it directly:
```bash
omarchy-shell shell listPlugins
omarchy-shell shell summon timmo.twitch '{}'
```
# URL Routing and Webapps
> Route matching URLs to Omarchy webapps while keeping the normal browser fallback.
The custom URL handler makes `http` and `https` links choose between two launch paths:
* Matching URLs open as standalone Omarchy webapps.
* Everything else opens in Chromium as a normal browser tab or window.
This keeps common app-like sites in dedicated windows without making every link a webapp.
## Files
[Section titled “Files”](#files)
The handler is part of the stowed Hypr package:
| Path | Purpose |
| -------------------------------------------------------- | ---------------------------------------- |
| `~/.config/hypr/bin/timmo-url-handler` | Runtime URL router. |
| `~/.config/hypr/bin/timmo-setup-url-handler` | Idempotent install/remove helper. |
| `~/.config/hypr/url-handler/patterns.conf` | Routing rules. |
| `~/.config/hypr/url-handler/omarchy-url-handler.desktop` | Desktop entry used by xdg mime handling. |
Edit the source files in the dotfiles repo, then run `dot stow`:
```text
~/.config/dotfiles/hypr/.config/hypr/
```
## Install the handler
[Section titled “Install the handler”](#install-the-handler)
Run the setup helper after the Hypr package is stowed and `~/.config/hypr/bin` is on `PATH`:
```bash
timmo-setup-url-handler
```
The setup helper:
* Creates or updates the desktop file.
* Symlinks it into `~/.local/share/applications/`.
* Sets it as the default handler for `http`, `https`, `about`, and `unknown` schemes in `~/.config/mimeapps.list`.
* Backs up `mimeapps.list` before changing it.
* Runs `update-desktop-database` when that command is available.
## Pattern rules
[Section titled “Pattern rules”](#pattern-rules)
Rules live in:
```text
~/.config/hypr/url-handler/patterns.conf
```
Each non-comment line starts with an action and a glob pattern:
```text
webapp:discord.com
webapp:*.youtube.com
browser:*.gitlab.com
```
Patterns are checked against both the full URL and the extracted domain. The first matching rule wins.
If nothing matches, the default action is `browser`.
## Add a webapp route
[Section titled “Add a webapp route”](#add-a-webapp-route)
1. Edit `~/.config/dotfiles/hypr/.config/hypr/url-handler/patterns.conf`.
2. Add a `webapp:` line above any broader browser rule.
3. Run `dot stow`.
4. Test with `timmo-url-handler `.
Example:
```text
webapp:calendar.google.com
webapp:*.calendar.google.com
```
## Force a normal browser route
[Section titled “Force a normal browser route”](#force-a-normal-browser-route)
Use `browser:` when a site should not become a webapp:
```text
browser:gitlab.com
browser:*.gitlab.com
```
Browser routes launch Chromium directly through `uwsm-app` so the default handler does not recurse back into itself.
## Test routing
[Section titled “Test routing”](#test-routing)
Run the handler directly:
```bash
timmo-url-handler https://github.com
timmo-url-handler https://gitlab.com
```
To check the desktop default:
```bash
xdg-mime query default x-scheme-handler/https
```
Expected output:
```text
omarchy-url-handler.desktop
```
## Remove the handler
[Section titled “Remove the handler”](#remove-the-handler)
```bash
timmo-setup-url-handler remove
```
The remove path deletes the applications symlink and desktop file. If the setup backup exists, it restores `~/.config/mimeapps.list` from that backup. If no backup exists, clean up `mimeapps.list` manually.
## Troubleshooting
[Section titled “Troubleshooting”](#troubleshooting)
If the setup script cannot find `timmo-url-handler`, check that the Hypr bin directory is on `PATH` and run `dot stow` again.
If a URL opens in the browser when it should be a webapp, put the more specific `webapp:` rule before broader `browser:` rules and test the exact URL with `timmo-url-handler`.
If every browser launch loops, check that the `browser` branch still launches `/usr/bin/chromium` directly rather than calling `xdg-open`.
# Work Browser Launchers
> Launch work Chrome, Slack, and Discord into predictable Hyprland workspaces.
The work launchers isolate a Google Chrome work profile and place work apps on known workspaces. They are used directly by Hypr bindings and indirectly by workspace setup/restore flows.
## Bindings
[Section titled “Bindings”](#bindings)
| Binding | Action |
| -------------------- | -------------------- |
| `SUPER+ALT+B` | Launch work browser. |
| `SUPER+CTRL+SHIFT+S` | Launch Slack. |
| `SUPER+D` | Launch Discord. |
## Work Chrome profile
[Section titled “Work Chrome profile”](#work-chrome-profile)
All three launchers use `google-chrome-stable` with the work profile data directory:
```text
$HOME/.config/google-chrome-work
```
The shared Chrome flags include:
```text
--ozone-platform=wayland
--force-device-scale-factor=0.8
--profile-directory=Profile 1
```
The work browser also sets:
```text
--class=work-browser
```
## Work browser
[Section titled “Work browser”](#work-browser)
Launch a new work browser window on workspace `3`:
```bash
launch-work-browser
```
Open a URL in the work browser:
```bash
launch-work-browser https://example.com
```
Open as a tab instead of using the workspace-placement helper:
```bash
launch-work-browser --tab https://example.com
```
Window mode uses:
```bash
launch-on-workspace 3 '^(work-browser|google-chrome)$' ''
```
## Slack and Discord
[Section titled “Slack and Discord”](#slack-and-discord)
Slack launches as a Chrome app window on workspace `1`:
```bash
launch-work-slack
```
Discord launches as a Chrome app window on workspace `1`:
```bash
launch-work-discord
```
They match these Hyprland classes after launch:
| App | Class regex |
| ------- | --------------------------------- |
| Slack | `^chrome-app\.slack\.com__client` |
| Discord | `^chrome-discord\.com__app` |
## Relationship to workspace recovery
[Section titled “Relationship to workspace recovery”](#relationship-to-workspace-recovery)
`workspace-setup` uses equivalent commands for the work slots. `workspace-restore` also knows how to relaunch Slack, Discord, and the work browser from captured client classes.
If a work app does not restore correctly, test the launcher directly before debugging restore logic.
## Troubleshooting
[Section titled “Troubleshooting”](#troubleshooting)
If a window launches but stays on the current workspace, inspect its class:
```bash
hyprctl -j clients
```
If Chrome opens the wrong profile, check that no external launcher is overriding `--user-data-dir` and that the work profile directory exists:
```bash
ls ~/.config/google-chrome-work
```
If `--tab` opens in an unexpected existing Chrome instance, use the default window mode instead. The `--tab` path does not use `launch-on-workspace`; it asks Hyprland to execute the Chrome command directly.
# Workspace Relayout
> Apply and capture ratio-based Hyprland layouts for the active workspace.
`workspace-relayout` applies saved layout presets to the active Hyprland workspace. It works on tiled windows only, using ratio-based split trees instead of fixed pixels, so the same preset can fit different monitors, scales, gaps, and bars.
Use it when a workspace has the right windows but the split tree is wrong.
## Key bindings
[Section titled “Key bindings”](#key-bindings)
The shared Hypr bindings are:
| Binding | Action |
| --------------- | ------------------------------------------------------------------ |
| `SUPER+TAB` | Apply a saved layout preset for the active workspace window count. |
| `SUPER+ALT+TAB` | Edit presets by capturing the current layout. |
| `SUPER+ALT+W` | Open `workspace-menu`, which includes the relayout action. |
## Presets
[Section titled “Presets”](#presets)
Presets are stowed from the dotfiles repo to:
```text
~/.local/share/workspace-relayout/presets.json
```
They are grouped by tiled window count under `layouts.`. Preset version 3 gives each layout a family, a concise scale label, and a generic split tree:
```json
{
"group": "Top / bottom",
"name": "75% top",
"tree": {
"dir": "tb",
"ratio": 75.4,
"a": "w",
"b": {
"dir": "lr",
"ratio": 40.67,
"a": "w",
"b": "w"
}
}
}
```
The checked-in presets cover workspaces with two to five tiled windows. Their JSON order controls both the family menu and the presets within each family. Common top/bottom layouts come first, ordered from the largest top region down. Other families distinguish columns, left or right stacks, and purpose-specific arrangements.
Preset names do not need to be unique. The menu appends a rounded ratio signature, such as `75% top [75/25, 16/16/16/52]`, to distinguish variants.
The script writes back through the stowed symlink when you edit presets, keeping the symlink in place.
## Apply a layout
[Section titled “Apply a layout”](#apply-a-layout)
1. Focus the workspace to relayout.
2. Keep at least two tiled, mapped, non-hidden windows on that workspace.
3. Press `SUPER+TAB`.
4. Choose a layout family from the Omarchy menu, such as **Top / bottom** or **Left / right**.
5. Choose an ordered scale preset from that family.
6. Let the script move windows through the temporary workspace and rebuild the split tree.
The script restores focus to the previously active window when it can.
## Capture a new preset
[Section titled “Capture a new preset”](#capture-a-new-preset)
Use edit mode when the current workspace already has the layout you want to save:
```bash
workspace-relayout --edit
```
Or press:
```text
SUPER+ALT+TAB
```
Edit mode reconstructs the current layout into a split tree. Choose a family first, then overwrite one of its presets or add a new layout to it using the generated description or a custom name. Choose **Add new family** to name a new family and save the current layout as its first preset.
## Temporary workspace
[Section titled “Temporary workspace”](#temporary-workspace)
Relayout rebuilds the split tree by moving windows through a temporary workspace. The default temporary workspace is `99`.
Override it only when workspace `99` is already used for something important:
```bash
WORKSPACE_RELAYOUT_TEMP_WS=98 workspace-relayout
```
The temp workspace must be a positive numeric workspace id and must not match the active workspace.
## Requirements
[Section titled “Requirements”](#requirements)
`workspace-relayout` requires:
* `hyprctl`
* `jq`
* `awk`
* `omarchy-menu-select`
* `omarchy-menu-input`
* The presets file at `~/.local/share/workspace-relayout/presets.json`
If a requirement is missing, the script sends a desktop notification and exits.
## Troubleshooting
[Section titled “Troubleshooting”](#troubleshooting)
If the menu says there are no presets, the active workspace has a window count that is not present in `presets.json`. Use edit mode to save the current arrangement for that count.
If the script says it needs at least two windows, make sure the windows are tiled. Floating windows are ignored.
If a layout applies but the order feels wrong, rearrange the windows manually and save a preset in edit mode. The assignment logic keeps windows near their current target positions, but windows are still treated as interchangeable leaves.
## Reset
[Section titled “Reset”](#reset)
There is no persistent Hyprland state to reset. To recover from a bad relayout, use Hyprland’s normal move/split controls, apply another preset, or restore a saved workspace session from [Workspace Session Recovery](/knowledge-base/workspace-session-recovery/).
# Workspace Session Recovery
> Capture and restore Hyprland window sessions after restarts, crashes, or layout experiments.
The workspace session scripts save the current Hyprland clients to JSON, then restore the latest capture by moving existing windows back to their saved workspaces and relaunching the supported missing apps.
Use this before risky layout work, before restarting Hyprland, or when you want a quick recovery point for open workspaces.
Note
This is best-effort recovery. Hyprland can move and resize windows, but some application state still belongs to the application itself.
## Capture a session
[Section titled “Capture a session”](#capture-a-session)
Capture every visible workspace:
```bash
workspace-capture
```
Capture only the active workspace:
```bash
workspace-capture --current-workspace
```
By default captures are written to:
```text
~/.local/state/workspace-sessions/workspace-.json
```
The script also writes a capture log to:
```text
~/.local/state/workspace-sessions/capture.log
```
To write a named capture:
```bash
workspace-capture --output=~/workspace-before-restart.json
```
## Restore safely
[Section titled “Restore safely”](#restore-safely)
Start with a dry run. It prints the restore plan without launching or moving windows:
```bash
workspace-restore --dry-run
```
Restore the newest capture from the state directory:
```bash
workspace-restore
```
Restore a specific file:
```bash
workspace-restore --file=~/workspace-before-restart.json
```
Search a different state directory for the newest capture and write `restore.log` there:
```bash
workspace-restore --state-dir=~/somewhere
```
The restore log is written to:
```text
~/.local/state/workspace-sessions/restore.log
```
## What gets restored
[Section titled “What gets restored”](#what-gets-restored)
The restore script reads `hyprctl -j clients` and tries these paths in order:
* Reuse a still-running window by saved address.
* Match an existing window by class and workspace.
* Launch a supported missing app, then move it to the saved workspace.
* Restore floating geometry or tiled size when the saved client has enough geometry data.
* Switch back to the saved active workspace at the end.
Supported relaunch paths are encoded in `workspace-restore` and cover Chromium, work Chrome app windows, Slack, Discord, Twitch webapps, generic Chrome webapps with captured URLs, and Ghostty terminals. Ghostty launches through `ghostty-host-config` to keep the active `config.$OMARCHY_HOST` override, and passes `--working-directory` when the saved directory still exists.
Captured browser URLs are passed to the Hyprland launcher as one shell-quoted argument, so query strings and shell metacharacters are preserved as URL data rather than interpreted as commands.
Unsupported classes are skipped with a reason in the restore output.
## Browser URLs
[Section titled “Browser URLs”](#browser-urls)
`workspace-capture` can attach a real browser URL when the browser URL state file exists:
```text
${XDG_STATE_HOME:-~/.local/state}/browser-urls.json
```
When that file is missing, Chromium windows can still be matched or relaunched by class, but generic webapp URL recovery is limited.
## Workspace menu
[Section titled “Workspace menu”](#workspace-menu)
The Omarchy menu wraps the common actions:
```bash
workspace-menu
```
The shared Hypr binding is:
```text
SUPER+ALT+W
```
It offers capture, dry-run restore, restore, and relayout actions. On the `desktop` host it also exposes the host-specific workspace setup action.
## Recovery flow
[Section titled “Recovery flow”](#recovery-flow)
1. Run `workspace-capture` before the risky change.
2. Make the change, restart Hyprland, or recover from the crash.
3. Run `workspace-restore --dry-run` and read the plan.
4. Run `workspace-restore` when the plan looks safe.
5. Check `restore.log` for skipped windows.
## Troubleshooting
[Section titled “Troubleshooting”](#troubleshooting)
If nothing restores, check that a capture exists:
```bash
ls ~/.local/state/workspace-sessions
```
If restore launches too much, try moving only existing windows:
```bash
workspace-restore --no-launch
```
If restore launches apps but leaves current placement alone:
```bash
workspace-restore --no-move
```
If you want to inspect one capture directly:
```bash
jq '.clients[] | {workspace: .workspace.id, class, title, browser_url}' ~/.local/state/workspace-sessions/workspace-*.json
```
## Rollback
[Section titled “Rollback”](#rollback)
The restore script only moves, resizes, and launches windows. There is no persistent layout state to undo. Move windows manually or rerun a better capture file with `--file=...`.
# Workspace Setup
> Rebuild the desktop host's work and non-work workspace layout.
`workspace-setup` is the desktop host’s opinionated workspace arranger. It launches or reuses the expected apps, tags them by slot, moves them to fixed workspaces, and rebuilds the main workspace split tree.
Use it when the desktop workspace set has drifted, after login, or after a restart where capture/restore is not the right tool.
Note
This is more specific than [Workspace Relayout](/knowledge-base/workspace-relayout/). Relayout reshapes whatever tiled windows are already on the active workspace. Workspace setup chooses specific apps and workspaces.
## Entry points
[Section titled “Entry points”](#entry-points)
The desktop host autostarts setup after Hyprland starts:
```text
exec-once = workspace-setup --sleep=5
```
On the `desktop` host, `workspace-menu` also exposes a **Setup workspace** action. The shared menu binding is:
```text
SUPER+ALT+W
```
You can also run it directly:
```bash
workspace-setup
```
## Work-time mode
[Section titled “Work-time mode”](#work-time-mode)
The script branches on `is-work-time`, which returns success Monday to Friday from `07:00` through `16:59` local time.
During work time, setup prepares:
| Workspace | Slots |
| --------- | ----------------------------------------------------------------- |
| `1` | Slack, personal Chromium, Ghostty in the home directory, Discord. |
| `2` | Standard Herdr. |
| `3` | Work browser. |
Outside work time, setup prepares:
| Workspace | Slots |
| --------- | ------------------------------------------------------------------------ |
| `1` | Personal Chromium and two blank Ghostty terminals in the home directory. |
| `2` | Standard Herdr. |
After setup, it switches to workspace `2`.
Terminal slots launch through `ghostty-host-config`, so they keep the active `config.$OMARCHY_HOST` override. The left monitor’s terminal always starts in the home directory. The central monitor attaches directly to the shared default Herdr session. Repository shortcuts only create or focus named workspaces when they are run inside Herdr; in a normal shell they keep their usual `cd` behaviour. Herdr’s `prefix+s` picker exposes the same configured repositories plus the home and `~/repos` directories.
Hyprland starts the shared Herdr server once in UWSM’s background graphical slice. Every terminal is only a client of that server.
## How slots are matched
[Section titled “How slots are matched”](#how-slots-are-matched)
Each slot has a Hyprland tag such as `wssetup-ws1-slack` or `wssetup-ws1-term`. Setup prefers an existing tagged window, then an existing matching candidate, then launches the command for that slot.
Before tagging a window, it removes other `wssetup-*` tags from that window and clears the target tag from any older window. This keeps each slot bound to one current address.
## Debug a setup run
[Section titled “Debug a setup run”](#debug-a-setup-run)
Use step-through mode when you need to see where the arrangement goes wrong:
```bash
workspace-setup --step-through
```
Run faster when you are iterating and Hyprland is behaving reliably:
```bash
workspace-setup --fast
```
Slow it down when windows are not ready before the script tries to place them:
```bash
workspace-setup --speed-multiplier=2.5
```
Delay startup logic after login:
```bash
workspace-setup --sleep=10
```
## Logs
[Section titled “Logs”](#logs)
By default logs are written under:
```text
~/.local/state/workspace-setup/runs/
```
Override the log path for one run:
```bash
WORKSPACE_SETUP_LOG_FILE=/tmp/workspace-setup.log workspace-setup --step-through
```
The script removes `workspace-setup-*.log` files older than seven days from the default log directory.
## Temporary workspace
[Section titled “Temporary workspace”](#temporary-workspace)
Setup moves windows through a numeric temporary workspace while rebuilding split trees. The default is `99`.
Override it if workspace `99` is in use:
```bash
WORKSPACE_SETUP_TEMP_WS=98 workspace-setup
```
The temp workspace must be a positive integer.
## Move dispatcher
[Section titled “Move dispatcher”](#move-dispatcher)
The default move dispatcher is `movetoworkspace`, because `movetoworkspacesilent` has caused Hyprland crashes in this flow.
Use the silent dispatcher only when intentionally testing it:
```bash
WORKSPACE_SETUP_MOVE_DISPATCHER=movetoworkspacesilent workspace-setup
```
Valid values are `movetoworkspace` and `movetoworkspacesilent`.
## Recovery flow
[Section titled “Recovery flow”](#recovery-flow)
1. Run `workspace-setup --step-through`.
2. Watch the log output for the slot that fails to resolve.
3. Check whether the app window class matches the slot regex.
4. If timing is the issue, rerun with a higher `--speed-multiplier` or `--sleep`.
5. If placement goes wrong, move windows manually or restore a capture from [Workspace Session Recovery](/knowledge-base/workspace-session-recovery/).
## Troubleshooting
[Section titled “Troubleshooting”](#troubleshooting)
If a terminal slot reuses an OpenCode terminal, the setup script filters titles starting with `OC |` for terminal candidates that should not be reused.
If the startup browser should become the main browser slot, focus a Chromium window before running setup. The script prefers the focused startup Chromium address for that slot.
If the non-work layout puts the blank tab in the wrong slot, setup tries to detect blank Chromium tabs and swap the top/main browser tags.
# Omarchy & Hyprland
> Stowed Omarchy Quattro customisations and host overrides.
These dotfiles run on [Omarchy](https://omarchy.org). UWSM, [Hyprland](https://hyprland.org), and Ghostty customisations are stowed dotfiles packages, with per-host overrides where needed.
[Host Overrides](/omarchy/host-overrides/)Managed Omarchy repos and stowed host config.
[Shell (Quickshell)](/omarchy/shell/)The Omarchy 4 Quickshell bar, generated shell.json, and custom plugins.
[Controls](/omarchy/controls/)The dot omarchy desktop controls menu.
## UWSM
[Section titled “UWSM”](#uwsm)
Omarchy Quattro owns its UWSM bootstrap and session defaults under `/usr/share`. The `uwsm/.config/uwsm/env.d/90-dotfiles` package adds only the user environment values needed by these dotfiles. `dot stow` removes the retired `timmo001/omarchy-uwsm` checkout before linking that override; upgrade-generated preservation files and backups are not imported.
## Hyprland
[Section titled “Hyprland”](#hyprland)
Hyprland config is **not** a tracked Omarchy repo. It is a stowed dotfiles package (`hypr/.config/hypr/`) laid down with `--no-folding`, with host-specific Lua overrides selected by a runtime symlink. See [Host Overrides](/omarchy/host-overrides/).
Shared application floating rules generated by [`float-app`](https://github.com/timmo001/float-app) live in the stowed `~/.config/hypr/float-app.lua`. `looknfeel.lua` requires that module, so new rules remain part of the Hypr package rather than becoming unmanaged live config.
`SUPER+W` uses Hyprland’s normal close-window action.
## Ghostty
[Section titled “Ghostty”](#ghostty)
Ghostty config is also stowed from `ghostty/.config/ghostty/`. The stowed launcher loads `config.$OMARCHY_HOST` when a host override exists.
Terminal bells mark the Ghostty tab title without requesting focus, so notifications do not switch Hyprland workspaces.
## Shell
[Section titled “Shell”](#shell)
Omarchy 4 runs a single [Quickshell](https://quickshell.outfoxxed.me) process (`omarchy-shell`) for the bar, notifications, OSD, launcher, and settings. These dotfiles extend it through a generated `shell.json` and a few custom bar plugins rather than forking it. See [Shell (Quickshell)](/omarchy/shell/).
## Herdr
[Section titled “Herdr”](#herdr)
`dot update` restores the Herdr Lazy lockfile, links the stowed local Herdr plugins, and starts the title watcher.
Agent Usage and GitHub PR status are tracked through the same Herdr Lazy list and lockfile. The Agent sidebar shows the workspace-aware title and current branch PR status, billing provider and shortest limit window, then context usage. `ALT+X`, then `U` opens the full limits pane; `ALT+X`, then `SHIFT+U` refreshes the meters. Browser-cookie import is disabled in the managed Herdr server environment, so OpenCode Go uses local estimates unless an explicit cookie is supplied outside this public repository. Agent Usage notifications are disabled by default and no credentials are stored in dotfiles.
The local Mise Task Runner opens with `ALT+X`, then `SHIFT+M`. It lists local tasks from the focused pane’s directory, previews the selected task, and runs it in a focused new tab so its output remains available. The memex session palette opens with `ALT+X`, then `F`.
Ghostty’s normal tab shortcuts are redirected to Herdr while its client window is active: `CTRL+SHIFT+T` creates a tab in the current workspace, `CTRL+SHIFT+W` closes the current tab, and `CTRL+TAB` / `CTRL+SHIFT+TAB` select the next or previous tab. Other applications receive the original key event unchanged. New tabs skip the naming prompt; the local Terminal Title plugin mirrors each pane’s existing Zsh or application title into its Herdr tab label. `SUPER+Q` and `SUPER+SHIFT+Q` attach another client to the shared default session. `ALT+X`, then `R` opens the repository picker generated from private `dot-git.yml` repositories and shortcuts. For direct shell access, `SUPER+RETURN` opens Ghostty and `SUPER+SHIFT+T` opens a new Ghostty tab.
`dot update` applies the maintained Omarchy crash-dispatch patch, and the top-level `update` command reapplies it after `omarchy update`. Both paths authenticate through PolicyKit with `pkexec`. The patch adds only an optional `~/.config/omarchy/hooks/agent-crash` handoff: a successful hook owns the diagnosis launch, while a missing or failed hook keeps Omarchy’s stock agent window. The public hook reuses a Herdr workspace rooted at `~/`, or creates one when absent, then opens a focused crash tab with the default agent and confirms that the agent starts processing the prompt. Patch application stops when upstream context changes instead of forcing a stale patch.
# Controls
> The dot omarchy desktop controls menu and local keybindings.
## `dot omarchy`
[Section titled “dot omarchy”](#dot-omarchy)
Opens the Omarchy desktop controls menu. Pass a submenu path to jump straight to it, or chain a path to execute an action directly.
```bash
dot omarchy # open the controls menu (TUI)
dot omarchy theme # jump to the Theme submenu
dot omarchy theme set # execute theme set directly
```
## Available submenus
[Section titled “Available submenus”](#available-submenus)
| Submenu | Controls |
| ------------ | ------------------------------- |
| `theme` | Theme management |
| `font` | Font management |
| `toggle` | Toggle system features |
| `capture` | Screenshots and recordings |
| `system` | Lock, logout, reboot, shutdown |
| `launch` | Launch applications |
| `refresh` | Refresh system components |
| `restart` | Restart system services |
| `install` | Install software and tools |
| `remove` | Remove software and features |
| `packages` | Package management |
| `share` | Share clipboard, files, folders |
| `reminder` | Reminders |
| `setup` | DNS, security setup |
| `snapshot` | System snapshots |
| `brightness` | Display and keyboard brightness |
| `power` | Power profiles |
Tip
When restarting Omarchy-managed apps, prefer `omarchy restart ` (via the `restart` submenu) over manual process kills.
## Keybinding overrides
[Section titled “Keybinding overrides”](#keybinding-overrides)
The shared Hyprland config loads after Omarchy’s defaults. It replaces these default bindings:
| Binding | Omarchy default | Local action |
| ----------------------------------------- | ------------------------- | --------------------------------------------------------------------- |
| `SUPER+TAB` | Next workspace | Apply a saved [workspace layout](/knowledge-base/workspace-relayout/) |
| `SUPER+ALT+TAB` | Next grouped window | Edit workspace layout presets |
| `CTRL+ALT+TAB` | Focus next monitor | Unbound in Hyprland; available to applications such as Ghostty |
| `CTRL+ALT+SHIFT+TAB` | Focus previous monitor | Unbound in Hyprland; available to applications such as Ghostty |
| `SUPER+SHIFT+F` | File manager | Add the active application to the floating rules |
| `SUPER+RETURN` | Omarchy terminal launcher | Open host-configured Ghostty in the active terminal directory |
| `SUPER+SHIFT+B` | Browser | Open a private personal Chromium window |
| `SUPER+CTRL+ALT+T` | Local time notification | Show local and US time zones |
| `SUPER+ALT+-` / `SUPER+ALT+=` | Resize width by 25 | Resize width by 2 |
| `SUPER+ALT+SHIFT+-` / `SUPER+ALT+SHIFT+=` | Resize height by 25 | Resize height by 2 |
The config also redirects `CTRL+SHIFT+T`, `CTRL+SHIFT+W`, `CTRL+TAB`, and `CTRL+SHIFT+TAB` to Herdr tab actions while Ghostty is active. Other applications receive the original key event.
Custom bindings that conflicted with Quattro defaults use these chords instead:
| Binding | Local action | Avoided default |
| -------------------- | ------------------------------------- | --------------------------------------------------- |
| `SUPER+CTRL+ALT+P` | Power profile menu | Power panel on `SUPER+CTRL+P` |
| `SUPER+CTRL+SHIFT+S` | Slack | Google Maps on `SUPER+SHIFT+S` |
| `SUPER+ALT+X` | X notifications | Universal cut on `SUPER+X` and X on `SUPER+SHIFT+X` |
| `SUPER+ALT+G` | GitHub notifications | Move window out of group on `SUPER+CTRL+G` |
| `SUPER+CTRL+SHIFT+C` | Toggle in-call automation | Calendar on `SUPER+SHIFT+C` |
| `SUPER+CTRL+SHIFT+M` | Toggle microphone mute | Music on `SUPER+SHIFT+M` |
| `SUPER+CTRL+SHIFT+B` | Reconnect laptop Bluetooth headphones | Battery status on `SUPER+CTRL+ALT+B` |
`CTRL+ALT+H` toggles the Home Assistant dashboard panel from the Omarchy bar. `CTRL+ALT+G` toggles the unified Git panel from the Omarchy bar.
The config leaves Quattro’s native `SUPER+W` close-window, `SUPER+SHIFT+RETURN` browser, `SUPER+ALT+RETURN` Tmux, and `SUPER+SHIFT+/` 1Password bindings in place. Application defaults such as Tmux and 1Password require Omarchy’s preinstalled bindings to be enabled.
## Power profiles
[Section titled “Power profiles”](#power-profiles)
Power-profile control has two entrypoints:
* `dot omarchy power` opens the Omarchy power-profile submenu from the `dot` TUI.
* `SUPER+CTRL+ALT+P` runs `power-profile-menu`, an Omarchy menu that shows the current profile and selects one of the profiles reported by `omarchy-powerprofiles-list`.
The menu writes the selected profile with `powerprofilesctl set`, so the available choices and final state come from `power-profiles-daemon` rather than a dot-specific state file.
### Laptop automation
[Section titled “Laptop automation”](#laptop-automation)
The `laptop` Hypr host starts `power-profile-daemon` from `~/.config/hypr/host/autostart.lua`. It replaces Omarchy’s AC udev auto-switching, which otherwise forces `performance` on AC.
On laptops, the daemon keeps the profile conservative by default:
* Startup sets `balanced`, unless the machine is on battery during the 01:00-07:00 local night window or below 20% battery.
* Unplugging from AC drops `performance` to `balanced`; it leaves existing `balanced` and `power-saver` choices alone.
* Plugging into AC relaxes `power-saver` back to `balanced`; it leaves `balanced` and `performance` alone.
* While on battery, the daemon switches to `power-saver` once when the night window starts or when battery drops below 20%.
* Manual changes after an automatic night switch are respected. At the end of the night window, the daemon only returns to `balanced` if it is still on the auto-applied `power-saver` profile and the battery is not below the low threshold.
The daemon keeps a single-instance pidfile under `${XDG_RUNTIME_DIR:-/tmp}/power-profile-daemon.pid` and polls AC/time/battery state every two seconds. It sends best-effort desktop notifications through `omarchy notification send`.
### Troubleshooting
[Section titled “Troubleshooting”](#troubleshooting)
Use the underlying tools to verify state before changing the dotfiles:
```bash
powerprofilesctl get
omarchy-powerprofiles-list
pgrep -af power-profile-daemon
```
If `SUPER+CTRL+ALT+P` opens no choices, check that `omarchy-powerprofiles-list` prints profile IDs. If the laptop policy is not running, confirm the active host symlink points at the laptop overrides and that Hyprland loaded `~/.config/hypr/host/autostart.lua`; see [Host Overrides](/omarchy/host-overrides/).
# Host Overrides
> Stowed Omarchy configuration and host overrides.
## UWSM environment
[Section titled “UWSM environment”](#uwsm-environment)
Quattro provides `/usr/share/uwsm/env.d/10-omarchy` and `/usr/share/omarchy/default/uwsm/default`, including the user-local binary path and mise activation. The stowed `~/.config/uwsm/env.d/90-dotfiles` adds only the custom Hypr helper path and OpenCode feature flags, without copying the generated `99-omarchy-upgrade-env` or timestamped upgrade backups. Omarchy keeps `BROWSER` shell-scoped so browser default selection continues to work.
## Ghostty host overrides
[Section titled “Ghostty host overrides”](#ghostty-host-overrides)
Ghostty config is a stowed dotfiles package (`ghostty/.config/ghostty/`), not a tracked Omarchy repo. The stowed `ghostty-host-config` launcher checks `OMARCHY_HOST` and loads `~/.config/ghostty/config.$OMARCHY_HOST` when present, falling back to the default `~/.config/ghostty/config` otherwise.
The shared config uses 12px window padding. The laptop override reduces the padding to 8px and the font size from 10 to 9.
| Binding | Action |
| -------------------- | ------------------------------------------ |
| `CTRL+ALT+G` | Toggle the unified Omarchy Shell Git panel |
| `CTRL+ALT+SHIFT+G` | Type and run `lazygit` in Ghostty |
| `CTRL+ALT+TAB` | Send tmux’s next-window sequence |
| `CTRL+ALT+SHIFT+TAB` | Send tmux’s previous-window sequence |
`dot install`, `dot init`, and `dot stow` back up the retired `timmo001/omarchy-ghostty` clone at `~/.config/ghostty` before linking the stowed config.
## Hyprland host overrides
[Section titled “Hyprland host overrides”](#hyprland-host-overrides)
Hyprland config is a stowed dotfiles package (`hypr/.config/hypr/`), not a tracked repo. Host-specific Lua overrides live under `~/.config/hypr/hosts/$OMARCHY_HOST`, selected by the runtime `~/.config/hypr/host` symlink.
* `dot stow` lays down the Hypr package with `--no-folding` and creates/repairs `~/.config/hypr/host` to point at the active host.
* `dot init` selects the Hypr host early (via `--host `, defaulting to `OMARCHY_HOST` or `desktop`), and the stow phase creates the `host` symlink.
* `dot doctor` checks the host link and flags any leftover legacy `omarchy-hypr` clone at `~/.config/hypr`.
* Both host monitor overrides detect common virtual machines through DMI data and use unscaled toolkit and fallback monitor settings; bare-metal hosts keep their normal HiDPI scale.
Shared Hypr autostart lives in `~/.config/hypr/autostart.lua` and runs on every host before the selected host override is loaded. Host-only services stay in `~/.config/hypr/host/autostart.lua`. KDE Connect starts on both hosts, while the OpenCode server starts only on the desktop.
Retired omarchy-hypr clone
A machine with the retired `~/.config/hypr` `omarchy-hypr` clone halts `dot update` until the clone is backed up and re-stowed. Hyprland config is a stowed package, not a cloned repo.
## Environment
[Section titled “Environment”](#environment)
* `OMARCHY_HOST` — the Hypr host override name (e.g. `desktop`, `laptop`).
* `OMARCHY_REPO_BASE_DIR` — Omarchy repo base path (default `~/.config`).
See [Environment Variables](/configuration/environment/) for the full list.
# Shell (Quickshell)
> The Omarchy 4 Quickshell shell, its generated shell.json, and the custom bar plugins.
Omarchy 4 replaces Waybar with a single long-running [Quickshell](https://quickshell.outfoxxed.me) process, `omarchy-shell`. That one process hosts the bar, the notification daemon, the on-screen display, the launcher, and the settings panel. Restarting “the shell” restarts all of them together.
These dotfiles do not fork the shell. They extend it through supported user configuration: a generated `shell.json` that lays out the bar, a stowed `shell.toml` style override, and a small set of user plugins that the bar loads as extra widgets.
## Source of truth
[Section titled “Source of truth”](#source-of-truth)
Four files drive the shell customisation, and none is hand-edited live:
* **`~/.config/omarchy/shell.json`** is generated, not stowed. `dot` renders it from Omarchy’s shipped default and inserts the personal modules. The generator is `dot/src/lib/omarchyShellConfig.ts` (`mergeOmarchyShellConfig`). The live file is mode `0600` and tracked by neither dotfiles repo.
* **`~/.config/omarchy/shell.toml`** is stowed from `omarchy/.config/omarchy/shell.toml`. It keeps the shell-wide 12px type scale while setting the compact bar surface to 12px.
* **`~/.config/omarchy/extensions/omarchy-menu.jsonc`** is stowed from `omarchy/.config/omarchy/extensions/omarchy-menu.jsonc`. It adds **Dotfiles** (`dot update`) and **Topgrade** (`topgrade`) to the stock **Update** submenu without replacing the upstream menu.
* **Bar plugins** live under `omarchy/.config/omarchy/plugins//` in this repo and stow to `~/.config/omarchy/plugins//`. Each plugin is a `manifest.json` plus an entry-point QML file.
To change the bar, edit the generator (then rebuild `dot`) or edit a plugin’s QML, never the live `shell.json`.
Omarchy’s shell source is read-only
The shell itself lives in `~/.local/share/omarchy/shell/`. Reading it is useful (the `BarWidget` / `WidgetButton` base classes live there), but edits are lost on `omarchy update`. Customisation belongs in plugins and the generated config.
## Generated `shell.json`
[Section titled “Generated shell.json”](#generated-shelljson)
`dot stow` regenerates `shell.json` for the active [host](/omarchy/host-overrides/), starting from Omarchy’s default and adding personal modules around the stock ones (“add, not remove”). The merge is idempotent: it only rewrites the file when the rendered content changes.
Per-host differences:
* **Clock format**: compact `HH:mm d MMM`, based on the final pre-Quattro Waybar clock without its weekday or ordinal day suffix. The `timmo.clock` clone reduces the stock clock’s 8.75px cell padding to 6px. Left-click opens the calendar, middle-click opens timezone settings, and right-click has no action.
* **Idle timers**: screensaver at 2.5 minutes and lock at 5 minutes on `laptop`; screensaver at 30 minutes and lock at 60 minutes on every other host.
* **Home Assistant dashboard**: desktop uses the office temperature and CO₂ sensors and adds office curtain controls; laptop uses the living-room sensors and adds VOC and dining-room temperature rows. Each host puts its climate status and Low/High fan controls at the top of the Air conditioner subsection.
Secondary outputs keep the core menu, `timmo.workspaces`, `timmo.clock`, and built-in system widgets; Twitch, Git, command cells, and the Home Assistant widget collapse without starting per-output pollers or loading their panels.
The personal status widgets read from bar-agnostic scripts, `dot` JSON output, and Home Assistant. Command cells and the Home Assistant aggregate render at 10px; stock-sized custom icons, the clock, and workspaces render at 11px. See [Bar Integrations](/bar-integrations/) for the JSON commands behind the bar and dashboard.
## Stock Quattro comparison
[Section titled “Stock Quattro comparison”](#stock-quattro-comparison)
The generated config starts from Omarchy Quattro’s shipped `shell.json` and modifies that layout rather than replacing it wholesale.
`timmo.workspaces` replaces the stock workspace widget, shows only workspaces that currently exist, displays the focused workspace number at full opacity, and dims the others. The Home Assistant dashboard replaces the stock weather widget; its outdoor row opens the same Met Office weather entity and hourly forecast. The stock `omarchy.agents` widget is intentionally removed without replacement.
The Home Assistant plugin uses one bar widget whose width follows its visible content. It shows each currently visible row’s original compact icon and value with its configured colour, or the Home Assistant icon when every row is quiet. In a Call is icon-only while active. Conditional rows appear only while active, warning, or critical; regular readings remain visible whenever their source output is available, except outdoor temperature, which appears only above 25 °C. The desktop air-conditioner target appears while its enabled input is on; the laptop target appears below 26 °C. It is blue while the matching climate entity is cooling, faded blue otherwise, and hidden while that climate entity is unavailable. Hovering does not reveal extra states. Clicking the widget opens the complete dashboard panel, whose heading includes the Home Assistant logo and whose rows include quiet and unavailable states. Activating a row that opens a link closes the panel first.
The stock alternate clock format, opaque bar, config version, and plugin list are also preserved. The normal clock format uses the compact pre-Quattro layout.
### Host overrides
[Section titled “Host overrides”](#host-overrides)
| Setting | Stock Quattro | Desktop | Laptop |
| ----------- | ------------- | ---------- | ----------- |
| Screensaver | 2.5 minutes | 30 minutes | 2.5 minutes |
| Lock | 5 minutes | 60 minutes | 5 minutes |
Home Assistant entity IDs vary by host. The laptop adds the VOC and dining-room temperature rows; the desktop omits them. The background doorbell watcher uses the active workspace on both hosts.
The session keeps its normal Qt scale for applications, while the `~/.config/hypr/bin/quickshell` wrapper resets `QT_SCALE_FACTOR` to `1` only for Omarchy shell launches. Wayland output scaling still handles the shell’s HiDPI rendering, avoiding an additional Qt multiplier across the bar, notifications, and popup plugins.
## Custom plugins
[Section titled “Custom plugins”](#custom-plugins)
A plugin is a folder with `manifest.json` (schema version 1, an `id` like `timmo.`, its `kinds`, and entry-point QML) plus the QML itself. A bar widget extends `BarWidget`, reads per-instance settings from `shell.json` via `setting(name, fallback)`, and uses `WidgetButton` for clickable cells.
| Plugin | Kind | What it does |
| ---------------------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `timmo.clock` | bar-widget | Keeps the stock clock and calendar behaviour with compact 6px cell padding. |
| `timmo.command` | bar-widget | Runs a shell command on an interval and renders its status-bar JSON (`text` / `tooltip` / `class`) with compact 6px horizontal cell margins. The Waybar `custom/*` equivalent. |
| `timmo.home-assistant` | service, bar-widget | Summarises active HA schedule, status, NAS, and environment rows in one widget and adds a native dashboard panel while keeping the doorbell watcher alive in the background. |
| `timmo.git` | service, bar-widget | Combines repository state and filtered GitHub notifications in one widget and native panel. |
| `timmo.stream-command` | bar-widget | Runs a long-running command that streams status-bar JSON lines and renders the latest line with compact 6px horizontal cell margins (for watchers like `ha-watch-singleton`). |
| `timmo.twitch` | service, bar-widget | Shows live Twitch state and opens an attached panel for channels and notification controls. |
| `timmo.workspaces` | bar-widget | Workspace numbers without persistent workspaces: only existing workspaces show, the focused one at full opacity and the rest dimmed. |
`timmo.command` and `timmo.stream-command` both support `classColors` (class-name to colour), `hideClasses`, `hiddenText`, `onClick` / `onClickRight`, and `revealOnHover`, so the generator can style and wire every cell without bespoke QML per module. Shell-launched web apps run through the reusable `launch-floating-webapp` command, which places only the new window at mobile size in the monitor’s bottom-right corner. Normal launches of the same sites remain tiled. TUI click targets use the existing `TUI.float` app id.
`timmo.twitch` keeps one polling service for the whole shell and shares it across bar instances. Left click opens its channel panel, middle click rechecks notifications, and right click restarts the notifier. The active state stays hidden until the bar is hovered; live and unavailable states remain visible.
`timmo.git` polls `dot git-diff --bar-json` and `dot git-notifications --bar-json` once per minute through one shell service. Each source appears only while its count is above zero, so a clean source contributes neither an icon nor a count. Important notifications are red, ordinary changes or unread notifications amber, pull-only repositories green, private-only dirt blue, and unavailable state grey. When both sources are clear, the widget collapses and reveals both bare icons dimmed while hovering the bar or while its panel is open. Left click opens the panel with actions, changed repository rows, and notification rows; right click refreshes both sources. Activating a changed repository opens `dot git-diff` directly in lazygit for that repository; quitting lazygit resumes the selected diff TUI. Other actions open the full Changed TUI, Other TUI, filtered notifications TUI, or a notification URL.
`timmo.home-assistant` keeps the HA pollers and singleton streams in one shell service. `Config.qml` owns the desktop and laptop entity mappings, commands, actions, aggregate show conditions, labels, icons, colours, and panel sizing; the other QML files only run and render that configuration. The host-specific target-temperature reading keeps its desktop/laptop visibility gates and cooling colour in the bar, but is omitted from the Environment section because the Controls section already provides its editor. The Controls section separately groups climate status, fan mode, target editing, and enabled controls under one Air conditioner subheading without repeating that name on each row. The climate status row shows the HVAC state and labelled fan mode separated by a bullet, opens Home Assistant more-info, and puts Low (`1`) and High (`2`) fan actions at the top of the subsection; the row and actions are omitted while the host’s climate entity is unavailable. The target editor has decrement and increment buttons using the helper’s configured step. Laptop also has 23.8 °C and Off (36 °C) presets; desktop instead has an enabled toggle immediately below the target. Desktop also starts Controls with an Office curtains status row and a compact left-to-right grid for the Close, 10%, 20%, 30%, and 60% presets on `cover.curtain`; these panel-only actions do not affect the bar and are omitted on laptop. Clicking Calendar or a sensor opens its existing floating full view; Time check and In a call retain their direct toggle actions. The doorbell stream stays loaded without a visible row and continues opening the camera popup on an active transition.
Twitch, Git, and Home Assistant share the same quick-filter controls. Typing filters their visible actions and rows. Up and Down move through matches, Enter activates one, Backspace edits the filter, Ctrl+Backspace removes a word, Ctrl+U clears it, Ctrl+R refreshes, and Escape clears an active filter before closing the panel.
New plugins need a stow
`~/.config/omarchy/plugins/` is a real directory with per-plugin symlinks. A brand-new plugin needs `dot stow` to create its symlink before the shell sees it; editing an existing plugin’s files is already live.
## Reloading the shell
[Section titled “Reloading the shell”](#reloading-the-shell)
| Change | Action |
| ----------------------------------------------------------- | -------------------------------------------------------------------- |
| `shell.json` layout or settings, existing modules only | Hot-reloads on save, nothing to run |
| New plugin added | `omarchy-shell shell rescanPlugins`, then the hot-reload picks it up |
| User plugin QML edited | Hot-reloads on save, nothing to run |
| Omarchy’s first-party shell QML edited, or hot-reload fails | `omarchy restart shell` (full restart) |
`dot update` bakes this in: it regenerates `shell.json` and reloads the running shell **only when the rendered config changed**. A standalone `dot stow` regenerates the file but does not reload.
Force Wayland on restart
`omarchy restart shell` inherits the caller’s environment. If `QT_QPA_PLATFORM=xcb`, Quickshell starts under XWayland, the layer-shell surface cannot attach, and the bar renders as a floating window with no error. Interactive shells here set `QT_QPA_PLATFORM="wayland;xcb"`, and `dot update` forces `QT_QPA_PLATFORM=wayland` on its reload. From any non-interactive context (SSH, systemd, an agent shell), force Wayland explicitly:
```bash
QT_QPA_PLATFORM=wayland omarchy restart shell
```
# OpenCode & Agents
> Shared OpenCode agents, commands, skills, and plugins.
The repo ships a shared [OpenCode](https://opencode.ai) configuration: agents, slash commands, skills, and lifecycle plugins. Agents, commands, plugins, and shared `lib/` modules live in `agents/.config/opencode/`. Portable skills and reviewed third-party snapshots are maintained independently in [`timmo001/skills`](https://github.com/timmo001/skills), pinned at `agents/.agents/skills/`, and exposed through the shared `~/.agents/skills/` path.
The repo-local `opencode.json` allows agents to inspect the live directories managed by stow and list their parent config directories. Direct edits to unmanaged files in those directories are denied; change the matching source under `~/.config/dotfiles` and run `dot stow` instead. Existing stowed symlinks resolve to their repository source, so edits still land in the tracked package. Personal repository references are supplied by the private machine overlay as writable local paths rather than repository-backed cache clones. References declared inside `docs/` resolve back to this checkout, including the `opencode-config` alias because that repository is a generated mirror of this source.
The notes capture daemon is intentionally separate from this shared interactive configuration. Dotfiles only installs its systemd units and private launch wrappers; the dedicated fail-closed agent and OpenCode config are owned by the `notes` repository.
The OpenCode-specific assets are generated from this dotfiles repo and published automatically to [`timmo001/opencode-config`](https://github.com/timmo001/opencode-config). Its `skills/` directory pins [`timmo001/skills`](https://github.com/timmo001/skills) rather than duplicating those files.
The portable basis
Unlike the rest of these (personal) dotfiles, the OpenCode config is built to be reused. `opencode-config` is a standalone, portable export with its own install instructions, so you can adopt individual agents, commands, skills, or plugins without taking on anything else here. Plugins that share support modules are published with the `lib/` directory they import and should be copied with that directory.
[Agents](/reference/agents/)Agent catalogue with links to each execution posture.
[Commands](/reference/commands/)Slash commands that route to agents and skills.
[Skills](/reference/skills/)Reusable workflows and behavioural contracts.
[Plugins](/reference/plugins/)Context, evidence, and enforcement hooks.
[MCP Servers](/opencode/mcp/)Model Context Protocol servers wired into the config.
[Agents / LLMs](/agents-llms/)llms.txt bundles for loading these docs into AI-assisted sessions.
## How it fits together
[Section titled “How it fits together”](#how-it-fits-together)
* **Graduated agent permissions** - agents range from workspace-read-only (`reviewer`) through ask-gated (`build-ask`) to edit-capable (`refactorer`). Read-only primary agents use native task allowlists, while `general-readonly` and `researcher-readonly` are terminal subagents with task delegation denied. The interactive `researcher` can fan out once without allowing recursive research chains.
* **Comparative primary-source research** - `/research` answers direct facts from the source that owns them. When judgement is involved, it compares relevant project policy, maintainer and contributor practice, independent evidence, and credible disagreement claim by claim rather than ranking people by reputation. Clear findings can hand straight to the build agent, while unresolved work routes to a targeted question, `/plan`, or `/grill` according to its size. Research agents can inspect indexed local agent history through the read-only `ctx` status, source, search, show, locate, docs, and SQL commands; shell output flags remain blocked across these read-only agents.
* **Plain-language restatement** - `/bro` re-pitches the previous response with enough context to recover the thread, using short, direct, unambiguous language while preserving concrete facts, decisions, paths, commands, citations, and next actions.
* **Session references** - `/session-reference ` resolves another OpenCode session by its listed title and loads the complete indexed `ctx` transcript into the current conversation. It adds context only and does not continue that session’s work unless the request says to.
* **Workflow failure diagnosis** - `/fix-workflows` inspects recent GitHub Actions failures for the current branch, or a named workflow or run, reproduces the failing path locally where possible, applies the smallest fix, and verifies it without committing, pushing, or rerunning workflows automatically.
* **Skill-based routing** - commands are thin wrappers that name an agent, declare required skills, and state whether branch context is needed. The workflow logic lives in skills and plugins.
* **Coding-practices routing** - code work loads `changeset-scope` first when bounded, then `effect` for Effect code or `effect-principles` for non-Effect code. Independently applicable repository, language, framework, cleanup, domain, and design skills activate through their own descriptions.
* **Question-led stress-testing** - `/grill` asks every currently unblocked material decision in one numbered round. Light runs one highest-leverage round; Full recomputes the decision frontier until no material branch remains. Phrases such as “lightly grill me” select Light directly, and unclear intensity is asked once.
* **Questionnaire drafts** - `to-questionnaire` turns knowledge held by another person into a channel-aware draft for a GitHub comment, Slack or Discord conversation, or a full Markdown document. It returns text in chat by default and never posts a comment, sends a message, or writes a file without an explicit request for that exact action. Slack and Discord remain draft-only.
* **Human-only steps** - `human-step-guide` activates only when available tools cannot cross a human boundary such as an approval, physical action, credential entry, or dashboard step. It gives the location, exact action, expected result, information to return, and follow-up verification without changing secrets, environment files, external settings, comments, or messages.
* **Staged implementation** - `/plan` identifies one active stage, records separate reviewable stages as deferred, and makes no edits. It recommends one numbered handoff per deferred reviewable phase. In a new repository it tries handoffs first, then proposes one temporary, all-in-one repository-local working Markdown plan when repository notes are not available yet; that document tracks numbered phases and updates their status at every checkpoint. During execution, the `staged-implementation` skill defines shared contracts before delegation, uses targeted checks while iterating, and pauses at validated checkpoints between separate review units unless the user requested one combined delivery. Broad, risky stages backed by an approved plan or handoff may use one implementation worker and one fresh read-only review. Bounded local review fixes may resume that worker once; broader remediation starts a fresh worker from a compact handoff. Ordinary stages stay with the host, and deterministic validation rather than agent agreement remains the completion gate.
* **Visual plan and code review** - Plannotator opens plan review from OpenCode’s `plan` agent and provides `/plannotator-review`, `/plannotator-annotate`, and `/plannotator-last` for manual review. Its CLI is pinned through mise’s GitHub backend because it is not available from Arch, AUR, or the Aqua registry.
* **Visible browser control** - The Browser Control skill drives the mise-managed `browser-control` CLI against attached Chromium tabs. The stowed `browser-control-extension-sync` helper copies the bundled extension to `~/.local/share/browser-control/extension` and links the installed package back to that stable path, so Chromium keeps the same unpacked-extension identity across upgrades. Mise’s global `postinstall` hook runs that helper after `npm:@opencode-ai/browser-control` installs or upgrades. Load the stable directory once in Chromium, then restart Chromium or reload the extension after upgrades. Private doctor checks can require the extension in Chromium and reject copies in other browser profiles because one relay supports only one extension instance. Its MCP server is optional and is not configured by default.
* **Branch context injection** - commands registered with the `branch-context` plugin receive pre-computed git and PR state as structured XML instead of running their own `git` or `gh` calls. The plugin consumes the standalone [`context`](https://context.timmo.dev) CLI (`context git --json`), preserves explicit unresolved default-branch state, and keeps the plugin-specific injection rules here. `/update-docs` queries the Context MCP server’s `git_context` tool directly when it needs recent-change state.
* **Commit context injection** - `/commit`, `/commit-push`, and `/commit-push-watch` stay in the active parent session and receive current Context CLI state plus path-level ownership evidence from persisted session patches and successful mutation tools. Existing staged files take precedence, unrelated dirty paths remain excluded, and multi-repository sessions receive one independently collected scope per Git root. Incomplete evidence falls back to an explicit Context MCP refresh rather than widening commit scope.
* **Codebase stack injection** - the `stack-context` plugin injects a deterministic tech-stack summary (languages, ecosystems, and frameworks with their general locations) computed by [`context stack --json`](https://context.timmo.dev/context/stack/), including structured notices when safety limits make a section partial. It injects automatically on the first message of a session inside a git repository, and on demand via `/inject-stack` or (alongside branch context) `/inject-context`.
* **Repository notes**: the `repo-notes` plugin injects the current repository’s notes path, the `notes` MCP server owns vault reads and writes, and `notes-guard` blocks direct file or shell access to the notes vault. See [Notes & Handoffs](/dot/notes/) and [MCP Servers](/opencode/mcp/).
* **Secret protection** - the `env-protection` plugin blocks reads of `.env` files (except `.env.example`).
* **Generated artefact protection** - the `generated-artifact-guard` plugin blocks direct mutation of generated dotfiles artefacts and names the canonical regeneration command. Read-only inspection (`cat`, `git diff`, `rg`, and similar) is allowed. The guard activates only inside this dotfiles repository layout.
| Generated artefact | Regenerate with |
| --------------------------------------------------------------------- | ---------------------------- |
| `docs/src/content/docs/dot/commands.md` | `mise run docs:gen:cli` |
| `docs/src/content/docs/reference/{agents,commands,skills,plugins}.md` | `mise run docs:gen:opencode` |
| `bash/.local/share/bash-completion/completions/dot` | `dot completions bash` |
| `fish/.config/fish/completions/dot.fish` | `dot completions fish` |
| `zsh/.local/share/zsh/site-functions/_dot` | `dot completions zsh` |
| `scripts/.local/bin/dot` | `mise run dot:build` |
| `docs/public/og.png` | `mise run docs:og` |
* **Background dev servers** - starting a long-running dev server follows a precedence: the project’s own `AGENTS.md` workflow first, then framework-native background mode (Astro 7+ self-detaches `astro dev` under an agent), then pitchfork as the fallback. The `pitchfork-dev-server-guard` plugin covers only the pitchfork tier, redirecting foreground dev commands to `serve:*` or `pitchfork start` when a project declares `pitchfork.toml`, and it leaves `astro dev` to Astro’s native mode.
* **Herdr agent launches** - agent-created Herdr layouts stay provisional until OpenCode starts in the pane returned by Herdr, receives its instructions through `agent prompt`, and reports a pane-bound lifecycle state through `agent get`. A new workspace or tab already supplies its root pane. Failed starts or deliveries remove only the topology created by that attempt, while transcript or artefact evidence remains required before claiming the requested work completed.
* **Contextual notifications** - main-session idle completions and all permission prompts send an Omarchy-formatted desktop notification when `omarchy` is available. Outside Herdr they also ring BEL and play the freedesktop message sound when `paplay` is available; Herdr sessions leave those sounds to Herdr. When the plugin starts under Hyprland, it captures the active window address so clicking a notification can return focus to that window. In Herdr it also focuses the originating workspace and tab. Background completion events stay silent.
### Context reliability warnings
[Section titled “Context reliability warnings”](#context-reliability-warnings)
| Model | Warning | Critical |
| --------------- | ------: | -------: |
| GPT-5.6 Sol | 256,000 | 512,000 |
| Claude Opus 4.8 | 100,000 | 150,000 |
| Claude Opus 5 | 100,000 | 150,000 |
| Other models | 64,000 | 128,000 |
These are context-health policies, not hard model limits. GPT-5.6 Sol’s bands follow OpenAI’s published [256K-512K and 512K-1M long-context results](https://openai.com/index/gpt-5-6/#long-context), where MRCR falls from 91.5% to 73.8%. The Opus critical band follows Anthropic’s documented [150,000-token default compaction trigger](https://docs.anthropic.com/en/docs/build-with-claude/compaction#parameters), with 50,000 tokens of warning headroom. The conservative fallback reflects cross-model reports that advertised context and effective context differ, including [RULER](https://arxiv.org/abs/2404.06654), [NoLiMa](https://arxiv.org/abs/2502.05167), and [Context Rot](https://research.trychroma.com/context-rot).
Each band appears once per session and resets after successful compaction. The count uses prompt occupancy (`input + cache read`), matching the context supplied to the model without counting generated output, reasoning, or cache writes. The thresholds remain context-health policies rather than authoritative next-request limits. Alerts appear as both built-in TUI toasts and clickable system notifications, so background sessions remain visible; headless runs silently skip unavailable notification paths.
## Tooling
[Section titled “Tooling”](#tooling)
* `opencode` is installed through mise’s Aqua backend. The explicit `c` alias resolves to the same binary and keeps the GitHub MCP bearer scoped to the launched harness process.
* `dot agents-sync` — mirror `~/.config/opencode/AGENTS.md` into agent harness instruction files. Runs automatically at the end of full `dot update` and `dot init`.
* `opencode debug ` — inspect resolved OpenCode config, paths, skills, and agents directly.
* `DOT_CONTEXT_CAPTURE=1` - capture assembled system-prompt segments and tool definitions for starter-context profiling. See [Environment Variables](/configuration/environment/#debugging-and-output).
* `/agent-benchmark [benchmark flags]` - launch this repository’s OpenCode benchmark as the managed Pitchfork daemon `agent-benchmark` through the repo-local skill. The command returns after startup; use `mise run benchmarks:opencode:status`, `mise run benchmarks:opencode:logs`, and `mise run benchmarks:opencode:stop` to manage it. `mise run benchmarks:opencode:logs:follow` provides an interactive live stream (`mise run benchmarks:opencode:logs -- --follow` is equivalent), but it keeps running after the benchmark completes and must be cancelled manually. Use status and ordinary logs rather than follow mode to check completion. Completed logs contain the full benchmark invocation, deterministic pass count, artifact path, and host report path. The process invokes `mise run benchmarks:opencode`; pass `--model ` to override the current session model. Valid ids come from `opencode models`, limited to providers OpenCode authenticates natively: each run loads the context-capture plugin alone, so plugin-backed providers such as `cursor/*` are unavailable inside the benchmark and fail every scenario. Each scenario receives a fresh repository with one synthetic baseline commit and no upstream history, plus the current worktree’s shipped `refactorer` or `reviewer` agent and required skills. Implementation scenarios expose one scenario-owned verification command; the harness first proves each verifier rejects the baseline and accepts a maintained reference implementation, then requires both the agent invocation and the host rerun to pass. Deterministic checks also audit agent and skill source hashes, skill load order, changed paths, implementation content, review findings and Standards/Spec sections, recorded tool calls, external access, answer lookup, and successful starter-context capture. Aggregate output reports per-scenario all-run consistency plus tool-call, repeated-call, post-mutation investigation, elapsed-time, context-size, and loaded-skill measurements without imposing arbitrary efficiency thresholds. A separate read-only model report assesses that evidence and flags unnecessary skill loads. The stable `host-report.json` entry point combines behavioural and context results, and checksummed, write-protected evidence remains under the ignored `.benchmarks/output/opencode/` path. The benchmark uses provider credentials and is intentionally not part of CI.
* `dot skill-updates` checks the writable `~/repos/skills` checkout. Clean updates are materialised through the Vercel Skills CLI and repository-owned `imports.json` overlay; adapted changes open an interactive review without replacing the committed snapshot. The scheduled skills-repo workflow uses the same importer to open clean update PRs and keeps adapted changes on a dashboard issue.
* `dot skill-check` validates branch-context command wiring.
* `dot is-agent` — detect whether dot is running under an AI agent, from agent environment variables with a Linux `/proc` ancestry fallback (exit `0`/`1`, so scripts can branch with `if dot is-agent`). `DOT_AGENT=1`/`0` overrides detection. When an agent (or a non-interactive stdout) invokes a `dot` command that would open the TUI, dot refuses and points at the machine-readable equivalent instead.
## Adding Skills
[Section titled “Adding Skills”](#adding-skills)
For a new repository-owned skill, add `/SKILL.md` in `timmo001/skills`, classify it in `skills.sh.json` and `PORTABILITY.md`, then run the repository validator and installer discovery check.
For a third-party skill, add its origin, reviewed path SHA, licence, and local-edit declarations to `imports.json`. Run `python scripts/import_skill.py ` to review the complete CLI-materialised snapshot. Use `--apply` only for a clean import; adapted skills are edited after review, then their accepted SHA is recorded in `imports.json` and materialised with `--metadata-only`. Commit and review the snapshot before updating the dotfiles submodule pin. Normal `dot update` only pulls and stows that committed revision.
Dotfiles-specific global skills belong in `dotfiles-skills/.agents/skills//` and are applied with `dot stow`; they are not published through the shared skills repository.
Publishing to `opencode-config` happens automatically via GitHub Actions on push. The Agents, Commands, Skills, and Plugins pages above are generated by `mise run docs:gen:opencode` from the live assets and pinned skills checkout, so they always match what ships. Edit OpenCode assets here and skills in `timmo001/skills`, not the generated pages. Renovate manages the skills submodule revision after reviewed snapshots land upstream. See also the generated [`dot` Command Reference](/dot/commands/).
# MCP Servers
> The Model Context Protocol servers wired into the OpenCode config, and a sanitised opencode.json example.
The OpenCode setup wires in a set of [Model Context Protocol](https://modelcontextprotocol.io) servers. They give agents documentation lookups, code search, browser control, system data, and dev-server management without shelling out for everything.
Where the real config lives
Unlike the agents, commands, skills, and plugins, the stowed harness `opencode.json` that declares these servers is **not** published. It lives only in the private overlay (`~/.config/dotfiles-private/agents/.config/opencode/opencode.json`), because the same file carries machine-specific repository paths in its permission allow-lists. The repo root `opencode.json` is a public, repo-local session config and may declare project-specific servers, but it is not the generated harness config described here. The servers themselves hold no secrets: the two that need credentials read them from the environment. This page documents the servers and a sanitised config so the setup can be reproduced.
## Servers in use
[Section titled “Servers in use”](#servers-in-use)
| Server | Transport | Purpose |
| ----------------- | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `answer-overflow` | remote | Searchable archive of programming Discord help channels. Community troubleshooting context. |
| `astro-docs` | remote | Search the official Astro framework docs. Loaded only in Astro projects (an `astro.config.*` marker, see below). |
| `chrome-devtools` | local | Drive Chrome DevTools for frontend debugging: page snapshots, console, network, Lighthouse, performance traces. Loaded only in frontend projects (an `index.html` or web-framework/bundler config marker, see below). |
| `context` | local | Branch and tech-stack context for the current repository (`git_context`, `stack_context`, `command_help`). Runs `context mcp` from the `context-git` package installed by `dot init`. |
| `context7` | remote | Up-to-date library and framework documentation with code examples. Needs an API key. |
| `convex` | local | Convex backend tooling: query data, run functions, inspect deployments. Loaded only in Convex projects (a `convex.json` or `convex/` marker, see below). |
| `github` | remote | Read-only GitHub access: issues, PRs, Actions, code scanning, Dependabot, discussions, projects. Needs a bearer token. |
| `grep` | remote | Search real-world code across public GitHub repos via [grep.app](https://grep.app). |
| `notes` | local | Repository notes vault (`note_read`, `note_list`, `note_write`, `note_delete`) and notes context. Runs `notes mcp` from the `repo-notes-git` package installed by `dot init`. |
| `pitchfork` | local | Manage long-running dev-server daemons: start, stop, restart, status, logs. Loaded only in repos with a `pitchfork.toml` (see below). |
| `system-bridge` | local | System Bridge: system info modules (cpu, memory, disks, media) and media control. |
Remote servers connect over HTTP to a hosted endpoint. Local servers are launched on demand over stdio (`opencode x`, `npx`, or an installed binary).
Chrome DevTools tools can execute browser-context JavaScript. The `subagent-chrome-devtools-guard` plugin keeps them available only in top-level sessions, so delegated subagents cannot use DevTools as a research path around web, GitHub, or repository-reading tools.
The `system-bridge` server runs the `system-bridge-mcp` binary from the `system-bridge-git` AUR package. That package is listed in the public `.dot-public-packages` manifest and installed by `dot init`, so the MCP command is available on a fully set-up machine without a separate install step.
The `context` server runs `context mcp` from the `context-git` AUR package (also in `.dot-public-packages`). Repository context tools and schemas live entirely in the standalone [`context`](https://context.timmo.dev) project. See [Context Integration](/git/context/) for how OpenCode plugins consume its JSON output.
The `notes` server runs `notes mcp` from the `repo-notes-git` package (also in `.dot-public-packages`). Repository note commands use it for vault reads, writes, and deletes; the `repo-notes` OpenCode plugin supplies the current repository’s note context. New note content omits `date`, which `note_write` inserts or refreshes. Updates read the note first and pass its SHA-256 revision as `expectedHash`; a mismatch means the note changed concurrently and must be reread before reconciling the update. Deletion requires explicit confirmation and remains permission-gated.
Credentials
`context7` and `github` need credentials, supplied through the environment and referenced from `opencode.json` with `{env:...}` so no secret ever lands in the config file. The GitHub server reads `DOT_GH_MCP_BEARER`; the `opencode`/`cursor` shell wrappers and `opencode-server` obtain it from `gh auth token` only for the launched harness process, rather than exporting it to every shell child. `dot doctor` warns when `gh` is missing or has no token.
## Generated from one spec
[Section titled “Generated from one spec”](#generated-from-one-spec)
Every harness’s MCP config is generated from a single private spec, `~/.config/dotfiles-private/mcp.yml`, by `dot mcp-sync`. One edit to the spec regenerates each harness’s native config in the stowed source tree, so the harnesses stay aligned instead of drifting apart by hand.
Active harnesses (generated):
* **OpenCode** (`agents/.config/opencode/opencode.json`, `mcp` block)
* **Cursor** (`agents/.cursor/mcp.json`, `mcpServers`)
* **VS Code** (`agents/.config/Code/User/mcp.json`, `servers`)
* **Copilot CLI** (`agents/.copilot/mcp-config.json`, `mcpServers`)
The spec defines each server once and enables it for every active harness, so all four expose the same set. Enablement is written out as an explicit per-harness matrix, so any single cell can be flipped later. A canonical `{env:VAR}` reference is rewritten to each harness’s native syntax (`{env:VAR}`, `${env:VAR}`, or `${VAR}`), and commands that differ by harness (such as the OpenCode `opencode x` runner for `chrome-devtools`) are carried as per-harness overrides.
The OpenCode config is written as a full catalogue: every server appears with an explicit `enabled: true` or `enabled: false`, so a server can be turned off in place while staying documented. The other harnesses list only their enabled servers.
Two more harnesses are kept as documented stubs, not generated:
* **Gemini CLI** would need an `mcpServers` block in `~/.gemini/settings.json` using `$VAR` env syntax.
* **Claude Code** is special: `~/.claude.json` is a live runtime state file Claude rewrites, so it is wired with `claude mcp add`, not file generation.
Run `dot mcp-sync` after editing the spec, then `dot stow`. `dot update` runs it automatically before re-stowing.
## Context gating
[Section titled “Context gating”](#context-gating)
Some servers are only useful in repos with a matching marker. The `mcp-repo-gate` plugin prunes them from the merged config at startup when none of their markers are present, so their tools do not load where they are irrelevant. Each server maps to one or more markers (files or directories); any one, found in the project directory, an ancestor, or a nearby descendant (up to two levels down, so a monorepo whose project sits in a subdirectory still counts), keeps the server. For example, `pitchfork` is dropped in any repo without a `pitchfork.toml`, `convex` outside Convex projects, `astro-docs` outside Astro projects, and `chrome-devtools` (\~27 tools, \~4.3k tokens of schema) outside frontend projects:
```ts
const REPO_REQUIRED_MARKERS: Record = {
pitchfork: ["pitchfork.toml"],
convex: ["convex.json", "convex"],
"astro-docs": [
"astro.config.mjs",
"astro.config.ts",
"astro.config.mts",
"astro.config.js",
"astro.config.cjs",
],
// Any one frontend signal keeps chrome-devtools loaded (static entry point,
// web-framework config, bundler config, task runner, or Storybook setup).
// Attach it from a non-frontend directory by restarting OpenCode there.
"chrome-devtools": [
"index.html",
"astro.config.mjs",
"vite.config.ts",
"next.config.js",
"nuxt.config.ts",
"svelte.config.js",
"vue.config.js",
"angular.json",
"webpack.config.js",
"rspack.config.ts",
"rollup.config.mjs",
"gulpfile.js",
".storybook",
// ...plus the remaining astro/vite/next/bundler config extensions
],
};
```
The plugin is deliberately conservative: only servers listed in that map are ever removed, and only when none of their markers are found upward to the filesystem root or in a short downward scan (which skips hidden and build/vendor directories). Every other server stays on everywhere, so no tool is hidden where it might be wanted.
## Example config
[Section titled “Example config”](#example-config)
A trimmed, sanitised `opencode.json`. Machine-specific repository paths are removed from the permission allow-lists, and secrets are `{env:...}` references with generic `FOO`/`BAR` placeholders standing in for real variable names. Adjust the `permission` allow-lists to your own trusted paths. In the real generated file every server also carries an explicit `"enabled": true` (or `false`), which is omitted here for brevity.
```json
{
"$schema": "https://opencode.ai/config.json",
"autoupdate": false,
"shell": "/bin/zsh -l",
"permission": {
"external_directory": {
"~/.config/dotfiles/**": "allow",
"/tmp/**": "allow",
"**/.ssh/**": "deny",
"**/.aws/**": "deny",
"**/.gnupg/**": "deny",
"**/.kube/**": "deny",
"~/.docker/config.json": "deny",
"~/.config/gh/hosts.yml": "deny",
"~/.local/share/opencode/mcp-auth.json": "deny"
},
"read": {
"/tmp/**": "allow",
"**/.env.example": "allow",
"**/.env": "deny",
"**/.env.*": "deny",
"*.pem": "deny",
"*.key": "deny",
"**/.ssh/**": "deny",
"**/.gnupg/**": "deny"
},
"bash": {
"git commit": "deny",
"git commit *": "deny",
"git commit --amend": "ask",
"dot": "allow",
"dot *": "allow"
},
"lsp": "allow",
"webfetch": "allow",
"websearch": "allow"
},
"lsp": true,
"mcp": {
"answer-overflow": {
"type": "remote",
"url": "https://www.answeroverflow.com/mcp"
},
"astro-docs": {
"type": "remote",
"url": "https://mcp.docs.astro.build/mcp"
},
"chrome-devtools": {
"type": "local",
"command": ["opencode", "x", "chrome-devtools-mcp@latest"]
},
"context": {
"type": "local",
"command": ["context", "mcp"]
},
"context7": {
"type": "remote",
"url": "https://mcp.context7.com/mcp",
"headers": {
"CONTEXT7_API_KEY": "{env:FOO}"
}
},
"convex": {
"type": "local",
"command": ["npx", "-y", "convex@latest", "mcp", "start"]
},
"notes": {
"type": "local",
"command": ["notes", "mcp"]
},
"github": {
"type": "remote",
"url": "https://api.githubcopilot.com/mcp/x/all/readonly",
"oauth": false,
"headers": {
"Authorization": "Bearer {env:BAR}"
}
},
"grep": {
"type": "remote",
"url": "https://mcp.grep.app"
},
"pitchfork": {
"type": "local",
"command": ["pitchfork", "mcp"]
},
"system-bridge": {
"type": "local",
"command": ["system-bridge-mcp"]
}
}
}
```
# Agents
> OpenCode agents defined in this repo.
Agents set the execution posture (permissions, tool access, delegation) for OpenCode sessions. They are published to [`opencode-config`](https://github.com/timmo001/opencode-config) and loaded from `~/.config/opencode/agents/`.
| Agent | Description |
| -------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [`build-ask`](https://github.com/timmo001/dotfiles/blob/distro/arch-omarchy/agents/.config/opencode/agents/build-ask.md) | Build agent that executes clear tasks and relies on permissions for write actions |
| [`general-readonly`](https://github.com/timmo001/dotfiles/blob/distro/arch-omarchy/agents/.config/opencode/agents/general-readonly.md) | General-style parallel subagent that researches with read-only tools and a narrow shell inspection allowlist (for delegation from read-only primaries). |
| [`grill`](https://github.com/timmo001/dotfiles/blob/distro/arch-omarchy/agents/.config/opencode/agents/grill.md) | Read-only planning stress-test agent for light or full round-based grilling |
| [`refactorer`](https://github.com/timmo001/dotfiles/blob/distro/arch-omarchy/agents/.config/opencode/agents/refactorer.md) | Refactor code while preserving behavior and following local command and skill workflows |
| [`researcher-readonly`](https://github.com/timmo001/dotfiles/blob/distro/arch-omarchy/agents/.config/opencode/agents/researcher-readonly.md) | Primary-source research subagent that compares claim-specific evidence and cannot delegate further |
| [`researcher`](https://github.com/timmo001/dotfiles/blob/distro/arch-omarchy/agents/.config/opencode/agents/researcher.md) | Interactive primary-source research agent that compares claim-specific evidence and may delegate one layer of read-only legwork |
| [`reviewer`](https://github.com/timmo001/dotfiles/blob/distro/arch-omarchy/agents/.config/opencode/agents/reviewer.md) | Reviews code for quality, bugs, security, and best practices |
| [`workflow-watcher`](https://github.com/timmo001/dotfiles/blob/distro/arch-omarchy/agents/.config/opencode/agents/workflow-watcher.md) | Watches host-resolved GitHub Actions targets and optionally fixes an explicitly scoped quick-check failure without rediscovering workflows |
# Commands
> OpenCode slash commands defined in this repo.
Commands are thin routing prompts: they name an agent, declare required skills or injected context, and define the output shape. They are loaded from `~/.config/opencode/commands/`.
| Command | Description | Agent |
| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- | ------------ |
| [`/all-lit-skills`](https://github.com/timmo001/dotfiles/blob/distro/arch-omarchy/agents/.config/opencode/commands/all-lit-skills.md) | Apply all Lit rendering skills in current git scope | `default` |
| [`/all-ts-skills`](https://github.com/timmo001/dotfiles/blob/distro/arch-omarchy/agents/.config/opencode/commands/all-ts-skills.md) | Apply all TypeScript-specific skills in current git scope | `default` |
| [`/bro`](https://github.com/timmo001/dotfiles/blob/distro/arch-omarchy/agents/.config/opencode/commands/bro.md) | Re-pitch the previous response plainly, concisely, and with enough context | `default` |
| [`/check-skill-updates`](https://github.com/timmo001/dotfiles/blob/distro/arch-omarchy/agents/.config/opencode/commands/check-skill-updates.md) | Check imported skills for upstream updates | `default` |
| [`/code-review`](https://github.com/timmo001/dotfiles/blob/distro/arch-omarchy/agents/.config/opencode/commands/code-review.md) | Review current branch work with the code-review skill and BranchContextPlugin context | `reviewer` |
| [`/commit-push-watch`](https://github.com/timmo001/dotfiles/blob/distro/arch-omarchy/agents/.config/opencode/commands/commit-push-watch.md) | Split current changes into coherent commits, push, then watch workflows | `default` |
| [`/commit-push`](https://github.com/timmo001/dotfiles/blob/distro/arch-omarchy/agents/.config/opencode/commands/commit-push.md) | Split current changes into coherent commits and push without workflow watchers | `default` |
| [`/commit`](https://github.com/timmo001/dotfiles/blob/distro/arch-omarchy/agents/.config/opencode/commands/commit.md) | Split current changes into coherent commits via the dot git-commit gateway | `default` |
| [`/debug-frontend`](https://github.com/timmo001/dotfiles/blob/distro/arch-omarchy/agents/.config/opencode/commands/debug-frontend.md) | Debug browser-specific UI issues with Chrome DevTools tools first | `default` |
| [`/explore-codebase`](https://github.com/timmo001/dotfiles/blob/distro/arch-omarchy/agents/.config/opencode/commands/explore-codebase.md) | Explore a codebase topic with the task explore subagent | `default` |
| [`/fix-workflows`](https://github.com/timmo001/dotfiles/blob/distro/arch-omarchy/agents/.config/opencode/commands/fix-workflows.md) | Diagnose and fix recent GitHub Actions failures, optionally scoped to a workflow or run | `default` |
| [`/grill`](https://github.com/timmo001/dotfiles/blob/distro/arch-omarchy/agents/.config/opencode/commands/grill.md) | Stress-test a plan, decision, or idea with light or full question rounds | `grill` |
| [`/handoff`](https://github.com/timmo001/dotfiles/blob/distro/arch-omarchy/agents/.config/opencode/commands/handoff.md) | Write a handoff document for the next agent session | `default` |
| [`/handoffs-list`](https://github.com/timmo001/dotfiles/blob/distro/arch-omarchy/agents/.config/opencode/commands/handoffs-list.md) | List handoff notes for the current repository | `default` |
| [`/home-assistant/all-frontend-skills`](https://github.com/timmo001/dotfiles/blob/distro/arch-omarchy/agents/.config/opencode/commands/home-assistant/all-frontend-skills.md) | Apply all Home Assistant frontend skills in current git scope | `default` |
| [`/home-assistant/lazy-context`](https://github.com/timmo001/dotfiles/blob/distro/arch-omarchy/agents/.config/opencode/commands/home-assistant/lazy-context.md) | Review and fix Home Assistant frontend lazy-context and memoization usage in current git scope | `default` |
| [`/home-assistant/list-components`](https://github.com/timmo001/dotfiles/blob/distro/arch-omarchy/agents/.config/opencode/commands/home-assistant/list-components.md) | Migrate Home Assistant list components from MWC to new primitives in current git scope | `default` |
| [`/home-assistant/lit-rendering`](https://github.com/timmo001/dotfiles/blob/distro/arch-omarchy/agents/.config/opencode/commands/home-assistant/lit-rendering.md) | Review and fix Home Assistant Lit rendering and picker callback-shape patterns in current git scope | `default` |
| [`/home-assistant/replace-spacing`](https://github.com/timmo001/dotfiles/blob/distro/arch-omarchy/agents/.config/opencode/commands/home-assistant/replace-spacing.md) | Replace hardcoded spacing values with ha-space tokens from core.globals.ts | `default` |
| [`/import-external-skill`](https://github.com/timmo001/dotfiles/blob/distro/arch-omarchy/agents/.config/opencode/commands/import-external-skill.md) | Import or review external skills for the local skill library | `default` |
| [`/improve-codebase-architecture`](https://github.com/timmo001/dotfiles/blob/distro/arch-omarchy/agents/.config/opencode/commands/improve-codebase-architecture.md) | Review a codebase area for architectural friction and focused structural improvements | `plan` |
| [`/inject-context`](https://github.com/timmo001/dotfiles/blob/distro/arch-omarchy/agents/.config/opencode/commands/inject-context.md) | Inject branch and codebase stack context and optionally execute an instruction | `default` |
| [`/inject-stack`](https://github.com/timmo001/dotfiles/blob/distro/arch-omarchy/agents/.config/opencode/commands/inject-stack.md) | Inject codebase stack context and optionally execute an instruction | `default` |
| [`/investigate`](https://github.com/timmo001/dotfiles/blob/distro/arch-omarchy/agents/.config/opencode/commands/investigate.md) | Investigate a topic, issue, or area without editing by default | `default` |
| [`/note-append`](https://github.com/timmo001/dotfiles/blob/distro/arch-omarchy/agents/.config/opencode/commands/note-append.md) | Append new notes to an existing note file for the current repository | `default` |
| [`/note-create`](https://github.com/timmo001/dotfiles/blob/distro/arch-omarchy/agents/.config/opencode/commands/note-create.md) | Create a new note for the current repository in your Obsidian notes vault | `default` |
| [`/note-reference`](https://github.com/timmo001/dotfiles/blob/distro/arch-omarchy/agents/.config/opencode/commands/note-reference.md) | Load one or more notes, relevant skills, and next steps for the current repository | `default` |
| [`/notes-list`](https://github.com/timmo001/dotfiles/blob/distro/arch-omarchy/agents/.config/opencode/commands/notes-list.md) | List notes for the current repository, optionally filtered by tag | `default` |
| [`/notes-search`](https://github.com/timmo001/dotfiles/blob/distro/arch-omarchy/agents/.config/opencode/commands/notes-search.md) | Search notes for the current repository by topic, keyword, or tag | `default` |
| [`/plan`](https://github.com/timmo001/dotfiles/blob/distro/arch-omarchy/agents/.config/opencode/commands/plan.md) | Manual entrypoint to native plan mode from the current conversation context | `plan` |
| [`/plannotator-annotate`](https://github.com/timmo001/dotfiles/blob/distro/arch-omarchy/agents/.config/opencode/commands/plannotator-annotate.md) | Open interactive annotation UI for a file, folder, or URL | `default` |
| [`/plannotator-last`](https://github.com/timmo001/dotfiles/blob/distro/arch-omarchy/agents/.config/opencode/commands/plannotator-last.md) | Annotate the last assistant message | `default` |
| [`/plannotator-review`](https://github.com/timmo001/dotfiles/blob/distro/arch-omarchy/agents/.config/opencode/commands/plannotator-review.md) | Open interactive code review for current changes or a PR URL; pass —git to force Git in JJ workspaces | `default` |
| [`/refactor-cleanup-variables`](https://github.com/timmo001/dotfiles/blob/distro/arch-omarchy/agents/.config/opencode/commands/refactor-cleanup-variables.md) | Refactor - inline and remove unnecessary variables from current git scope | `refactorer` |
| [`/refactor-current-work`](https://github.com/timmo001/dotfiles/blob/distro/arch-omarchy/agents/.config/opencode/commands/refactor-current-work.md) | Refactor current branch work while preserving behaviour | `refactorer` |
| [`/refactor-enforce-types`](https://github.com/timmo001/dotfiles/blob/distro/arch-omarchy/agents/.config/opencode/commands/refactor-enforce-types.md) | Refactor - enforce TypeScript type safety in current git scope | `refactorer` |
| [`/refactor-remove-single-use`](https://github.com/timmo001/dotfiles/blob/distro/arch-omarchy/agents/.config/opencode/commands/refactor-remove-single-use.md) | Refactor - inline and remove safe single-use functions from current git scope | `refactorer` |
| [`/research`](https://github.com/timmo001/dotfiles/blob/distro/arch-omarchy/agents/.config/opencode/commands/research.md) | Research a topic from primary sources and compare evidence where judgement is involved | `researcher` |
| [`/reset-branch-reapply`](https://github.com/timmo001/dotfiles/blob/distro/arch-omarchy/agents/.config/opencode/commands/reset-branch-reapply.md) | Reset branch to default and reapply current diff staged | `build` |
| [`/session-reference`](https://github.com/timmo001/dotfiles/blob/distro/arch-omarchy/agents/.config/opencode/commands/session-reference.md) | Load another OpenCode session into this conversation by its sidebar title | `default` |
| [`/update-docs`](https://github.com/timmo001/dotfiles/blob/distro/arch-omarchy/agents/.config/opencode/commands/update-docs.md) | Keep documentation current with recent code changes, via Context MCP and delegated investigation | `default` |
# Plugins
> OpenCode lifecycle plugins defined in this repo.
Plugins provide context, evidence, or enforcement hooks for OpenCode. They are loaded from `~/.config/opencode/plugins/`.
| Plugin | Description |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ |
| [`branch-context`](https://github.com/timmo001/dotfiles/blob/distro/arch-omarchy/agents/.config/opencode/plugins/branch-context.ts) | Injects branch-context blocks into command prompts before execution |
| [`commit-context`](https://github.com/timmo001/dotfiles/blob/distro/arch-omarchy/agents/.config/opencode/plugins/commit-context.ts) | Injects session-attributed commit scope into commit command prompts |
| [`context-capture`](https://github.com/timmo001/dotfiles/blob/distro/arch-omarchy/agents/.config/opencode/plugins/context-capture.ts) | Opt-in capture of the assembled starter context for token profiling |
| [`context-zone-warning`](https://github.com/timmo001/dotfiles/blob/distro/arch-omarchy/agents/.config/opencode/plugins/context-zone-warning.ts) | Warns when long-context models enter less reliable context ranges |
| [`env-protection`](https://github.com/timmo001/dotfiles/blob/distro/arch-omarchy/agents/.config/opencode/plugins/env-protection.ts) | Blocks direct access to .env files to prevent leaking secrets |
| [`generated-artifact-guard`](https://github.com/timmo001/dotfiles/blob/distro/arch-omarchy/agents/.config/opencode/plugins/generated-artifact-guard.ts) | Blocks direct mutation of generated dotfiles artefacts |
| [`mcp-repo-gate`](https://github.com/timmo001/dotfiles/blob/distro/arch-omarchy/agents/.config/opencode/plugins/mcp-repo-gate.ts) | Per-repo MCP server gating for OpenCode |
| [`notes-guard`](https://github.com/timmo001/dotfiles/blob/distro/arch-omarchy/agents/.config/opencode/plugins/notes-guard.ts) | Blocks direct file and shell access to the repository notes vault |
| [`notification`](https://github.com/timmo001/dotfiles/blob/distro/arch-omarchy/agents/.config/opencode/plugins/notification.ts) | Sends contextual desktop notifications and terminal attention for agent events |
| [`pitchfork-dev-server-guard`](https://github.com/timmo001/dotfiles/blob/distro/arch-omarchy/agents/.config/opencode/plugins/pitchfork-dev-server-guard.ts) | Enforces a project’s declared pitchfork dev-server workflow for agents |
| [`readonly-subagent-shell-guard`](https://github.com/timmo001/dotfiles/blob/distro/arch-omarchy/agents/.config/opencode/plugins/readonly-subagent-shell-guard.ts) | Rejects shell syntax that can turn read-only subagent commands into writes |
| [`repo-notes`](https://github.com/timmo001/dotfiles/blob/distro/arch-omarchy/agents/.config/opencode/plugins/repo-notes.ts) | Injects repo-note context into OpenCode note commands |
| [`stack-context`](https://github.com/timmo001/dotfiles/blob/distro/arch-omarchy/agents/.config/opencode/plugins/stack-context.ts) | Injects codebase stack-context blocks into prompts |
| [`subagent-chrome-devtools-guard`](https://github.com/timmo001/dotfiles/blob/distro/arch-omarchy/agents/.config/opencode/plugins/subagent-chrome-devtools-guard.ts) | Blocks Chrome DevTools tools from delegated subagent sessions |
| [`tui-dot-git-diff`](https://github.com/timmo001/dotfiles/blob/distro/arch-omarchy/agents/.config/opencode/plugins/tui-dot-git-diff.ts) | Registers a TUI keybinding (ctrl+shift+g) to open `dot git-diff` |
| [`tui-lazygit`](https://github.com/timmo001/dotfiles/blob/distro/arch-omarchy/agents/.config/opencode/plugins/tui-lazygit.ts) | Registers a TUI keybinding to open lazygit |
| [`workflow-manifest`](https://github.com/timmo001/dotfiles/blob/distro/arch-omarchy/agents/.config/opencode/plugins/workflow-manifest.ts) | Resolves pushed GitHub Actions runs into a compact watcher manifest |
# Skills
> OpenCode skills defined in this repo, including imported ones.
Skills own reusable workflows and behavioural contracts. Shared skills are exposed via `~/.agents/skills/` and published to [`opencode-config`](https://github.com/timmo001/opencode-config). Dotfiles-owned skills are stowed directly from this repository.
## Skills
[Section titled “Skills”](#skills)
| Skill | Description |
| ------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| [`branch-context-consumer`](https://github.com/timmo001/skills/blob/main/branch-context-consumer/SKILL.md) | Consume BranchContextPlugin injections in commands. Use when a command depends on an injected block for its scope. |
| [`changeset-scope`](https://github.com/timmo001/skills/blob/main/changeset-scope/SKILL.md) | Keep all scoped code work contained to the user-defined changeset. Use for implementation, fixes, diagnosis, refactoring, cleanup, and review when explicit instructions, named files, diffs, branches, pull requests, or injected work scopes define the boundary. |
| [`check-skill-updates`](https://github.com/timmo001/skills/blob/main/check-skill-updates/SKILL.md) | Check imported skills for upstream changes and review safe updates. Use when a tracked `# origin:` may have changed or when refreshing installed skills from their source repositories. |
| [`chill`](https://github.com/timmo001/skills/blob/main/chill/SKILL.md) | Stop overengineering and reinventing the wheel. Use ONLY when the user explicitly invokes /chill or asks to simplify an approach that has become unnecessarily complex. |
| [`cleanup-unnecessary-variables`](https://github.com/timmo001/skills/blob/main/cleanup-unnecessary-variables/SKILL.md) | Safe removal of unnecessary variables during code review and refactoring. Use when simplifying code, inlining temporary or single-use variables, or removing redundant aliases, while preserving runtime behaviour, evaluation order, and variables kept for readability or debugging. |
| [`code-review`](https://github.com/timmo001/skills/blob/main/code-review/SKILL.md) | Review code changes along two axes - Standards (does it follow the repo’s conventions, plus a Fowler code-smell baseline?) and Spec (does it implement what the originating issue or spec asked for?). Use when reviewing a pull request, a branch, work-in-progress changes, or a diff. |
| [`effect-principles`](https://github.com/timmo001/skills/blob/main/effect-principles/SKILL.md) | Apply the Effect way of reasoning in codebases that do not use Effect, in any programming language. Use when editing or reviewing non-Effect code so dependencies, failures, state, boundaries, resources, time, and workflows stay explicit without adding Effect-shaped architecture or broader scope. |
| [`evidence-first`](https://github.com/timmo001/skills/blob/main/evidence-first/SKILL.md) | Check questions and uncertain statements before answering, while following clear user choices and limits. Use in any agent mode when the user asks why or how something works, says things like I think, I remember, or I don’t think, asks whether something is correct, requests advice, or gives a firm preference such as I don’t want this, reduce the scope, or this is going too far. |
| [`git-commit`](https://github.com/timmo001/skills/blob/main/git-commit/SKILL.md) | Commit workflow using the dot git-commit gateway, splitting a reviewed changeset into coherent commits by default. Use only after the user explicitly requests a commit or push, including /commit, /commit-push, or /commit-push-watch. Never infer authorisation for later changes; never run raw git commit. |
| [`git-context`](https://github.com/timmo001/skills/blob/main/git-context/SKILL.md) | Patterns for working with git branches, remotes, diffs against the default branch, and rebases. Use when resolving rebase conflicts, continuing interactive rebases, amending commits, or any git operation that would open an interactive editor. |
| [`handoff`](https://github.com/timmo001/skills/blob/main/handoff/SKILL.md) | Compact the current conversation into a handoff document for another agent to pick up. |
| [`herdr-workflows`](https://github.com/timmo001/skills/blob/main/herdr-workflows/SKILL.md) | Apply local safeguards for Herdr session recovery and transferring linked-worktree changes back to a host checkout. Use alongside the herdr skill when diagnosing Herdr socket routing, recovering the default session, or moving, consolidating, or continuing Herdr worktree changes from the main or host checkout. The herdr skill remains authoritative for all Herdr CLI, topology, targeting, lifecycle, and safety behaviour. |
| [`home-assistant-frontend`](https://github.com/timmo001/skills/blob/main/home-assistant-frontend/SKILL.md) | Home Assistant frontend skill routing and personal engineering overlays. Use when editing or reviewing the Home Assistant frontend so repository-local `ha-frontend-*` skills stay authoritative and applicable Lit, TypeScript, cleanup, and HA companion skills are also loaded. |
| [`home-assistant-lazy-context`](https://github.com/timmo001/skills/blob/main/home-assistant-lazy-context/SKILL.md) | Home Assistant frontend lazy-context, memoization, and `hass` removal guidance. Use when migrating Lit components from `hass!: HomeAssistant`, `.hass=${...}`, or broad `hass` access to context slices. |
| [`home-assistant-list-components`](https://github.com/timmo001/skills/blob/main/home-assistant-list-components/SKILL.md) | Home Assistant list component migration and usage guidance. Use when editing ha-list, ha-list-item, ha-md-list, or migrating to ha-list-nav, ha-list-selectable, ha-list-item-button, ha-list-item-option, or ha-list-item-base. |
| [`home-assistant-lit-rendering`](https://github.com/timmo001/skills/blob/main/home-assistant-lit-rendering/SKILL.md) | Home Assistant Lit rendering extensions for HA components and context-aware picker callback shape. |
| [`human-step-guide`](https://github.com/timmo001/skills/blob/main/human-step-guide/SKILL.md) | Prepare a concise guide when progress is blocked by a genuinely human-only action. Use for approvals, physical actions, credential entry, or dashboard steps the agent cannot perform; do not use for work available tools can complete. |
| [`import-external-skill`](https://github.com/timmo001/skills/blob/main/import-external-skill/SKILL.md) | Import skills from external repositories into this Agent Skills repository. Use when pulling in a public skill, reviewing an external skill set, or adapting upstream content into an existing skill. |
| [`install-tool`](https://github.com/timmo001/skills/blob/main/install-tool/SKILL.md) | Install tools, applications, CLIs, runtimes, and packages. Use when an installation request should prefer mise for development tools, then fall back to pacman or yay for system-integrated software. |
| [`lit-rendering`](https://github.com/timmo001/skills/blob/main/lit-rendering/SKILL.md) | Lit rendering and picker callback-shape guidance for editing and reviewing Lit components. |
| [`maintain-docs`](https://github.com/timmo001/skills/blob/main/maintain-docs/SKILL.md) | Keep documentation current and accurate with recent code changes, across in-code docs (docstrings, annotations, comments), in-repo docs sites, and external docs repositories. Use when asked to update docs, check docs accuracy, keep documentation current, document recent changes, refresh docstrings or annotations, or catch documentation up with the codebase. Matches the codebase’s existing documentation density and stops before commit. |
| [`pitchfork-dev-servers`](https://github.com/timmo001/skills/blob/main/pitchfork-dev-servers/SKILL.md) | Manage long-running local dev servers by precedence - the project’s own AGENTS.md workflow first, framework-native background mode next, then pitchfork as the fallback. Use when starting, stopping, restarting, checking, or tailing development servers, background servers, `pitchfork.toml`, pitchfork MCP tools, or local AGENTS/mise tasks that mention pitchfork. |
| [`pkexec-root`](https://github.com/timmo001/skills/blob/main/pkexec-root/SKILL.md) | Use pkexec first for commands that need root directly or indirectly. |
| [`plan`](https://github.com/timmo001/skills/blob/main/plan/SKILL.md) | Produce implementation-ready plans from the current conversation and repository context. Use when entering native plan mode, invoking /plan, or when a task needs concrete implementation sequencing before edits begin; do not use for round-based grilling. |
| [`remove-single-use-functions`](https://github.com/timmo001/skills/blob/main/remove-single-use-functions/SKILL.md) | Safe inlining and removal of single-use functions during code review and refactoring. Use when a local, non-exported helper has exactly one real call site and inlining preserves behaviour and readability. |
| [`research`](https://github.com/timmo001/skills/blob/main/research/SKILL.md) | Investigate a topic against primary sources and return cited findings, comparing credible maintainer and contributor perspectives when judgement is involved. Use when the user asks why, says show evidence, validate this, or use trusted sources; wants research, docs, API, or spec facts; needs external library or GitHub behaviour verified; compares competing views; or delegates reading legwork to a background agent. |
| [`safe-process-signals`](https://github.com/timmo001/skills/blob/main/safe-process-signals/SKILL.md) | Safe process killing and signal handling for agent/subprocess contexts. Use when running pkill, killall, kill, or any process termination command from a shell subprocess, automated script, or coding agent. |
| [`shared-workflows`](https://github.com/timmo001/skills/blob/main/shared-workflows/SKILL.md) | Use, configure, maintain, or create reusable GitHub Actions workflows for personal and organisation repositories. Use when a task mentions shared workflows, reusable workflows, `workflow_call`, cross-repository workflow `uses:`, or the personal workflows repository; do not use for repository-specific or proof-of-concept CI unless evaluating whether it should be shared. |
| [`staged-implementation`](https://github.com/timmo001/skills/blob/main/staged-implementation/SKILL.md) | Execute broad changes one coherent, independently verifiable stage at a time. Use when work spans multiple independently reviewable changes, or when contracts, producer-consumer migrations, generated artefacts, or release packaging create an ordered multi-stage rollout; skip small single-purpose changes. |
| [`types-enforce-ts`](https://github.com/timmo001/skills/blob/main/types-enforce-ts/SKILL.md) | TypeScript type-safety guidance for editing and reviewing `.ts`, `.tsx`, `.mts`, and `.cts` files. |
| [`workflows-watch`](https://github.com/timmo001/skills/blob/main/workflows-watch/SKILL.md) | Watch GitHub Actions workflows in an experimental background task and return the result. Use when asked to watch checks, wait for workflows, or follow workflow runs without blocking the main agent; diagnose and fix only when the caller explicitly requests fix mode. |
| [`writing-dot-skills`](https://github.com/timmo001/skills/blob/main/writing-dot-skills/SKILL.md) | Craft for authoring Agent Skills that select reliably and stay lean. Use when creating or revising a skill’s description, workflow, references, scripts, or structure. |
| [`writing-style`](https://github.com/timmo001/skills/blob/main/writing-style/SKILL.md) | Write commit messages, PR and issue text, docs (README), code comments, and user-facing strings (notifications, UI labels, toasts, error messages) in the project owner’s voice: concise, human, UK English, no em-dashes, no robotic or marketing tone. Use when authoring or editing any of these. Defer to a repo’s established house style when it has one; otherwise this sets the default voice. |
## Dotfiles-Owned Skills
[Section titled “Dotfiles-Owned Skills”](#dotfiles-owned-skills)
These skills are maintained and stowed by the dotfiles repository rather than published through `opencode-config`.
| Skill | Description |
| -------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| [`dotfiles-stow`](https://github.com/timmo001/dotfiles/blob/distro/arch-omarchy/dotfiles-skills/.agents/skills/dotfiles-stow/SKILL.md) | REQUIRED when changing configs managed by \~/.config/dotfiles or \~/.config/dotfiles-private. Enforces editing stow source paths (not ad-hoc live paths) and using the dot command for stow/update/validation workflows. |
## Imported Skills
[Section titled “Imported Skills”](#imported-skills)
These skills were imported from other repos and tracked with an `# origin:` marker. Some are used as-is; others are adapted for local workflows.
| Skill | Description | Origin |
| -------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| [`agentic-workflows`](https://github.com/timmo001/skills/blob/main/agentic-workflows/SKILL.md) | Design, create, update, debug, audit, or upgrade GitHub Agentic Workflows with the `gh aw` extension. Use when work involves workflow Markdown, compiled `.lock.yml` files, agent engines, MCP tools, safe outputs, or `gh aw` commands. | [github/gh-aw](https://github.com/github/gh-aw/tree/main/.github/skills/agentic-workflows) |
| [`ask-questions-if-underspecified`](https://github.com/timmo001/skills/blob/main/ask-questions-if-underspecified/SKILL.md) | Ask minimal clarifying questions only when ambiguity materially changes implementation. Use for routine underspecification; do not use for user-requested light or full grilling, plan stress-testing, or broad design interviews. | [trailofbits/skills](https://github.com/trailofbits/skills/tree/main/plugins/ask-questions-if-underspecified/skills/ask-questions-if-underspecified) |
| [`bro`](https://github.com/timmo001/skills/blob/main/bro/SKILL.md) | Re-pitch the immediately preceding response with enough context to follow, using plain, concise, unambiguous language. Use ONLY when the user explicitly invokes /bro or says the previous response did not land. | [dmmulroy/skills](https://github.com/dmmulroy/skills/tree/main/bro) |
| [`browser-control`](https://github.com/timmo001/skills/blob/main/browser-control/SKILL.md) | Control the user’s existing Chromium-family browser through the Browser Control extension and local relay. Use when asked to automate, test, inspect, or drive the visible browser with Browser Control, especially in this repo or once the extension is installed. | [anomalyco/browser-control](https://github.com/anomalyco/browser-control/tree/main/skills/browser-control) |
| [`codebase-design`](https://github.com/timmo001/skills/blob/main/codebase-design/SKILL.md) | Shared vocabulary for designing deep modules. Use when the user wants to design or improve a module’s interface, find deepening opportunities, decide where a seam goes, make code more testable or AI-navigable, or when another skill needs the deep-module vocabulary. | [mattpocock/skills](https://github.com/mattpocock/skills/tree/main/skills/engineering/codebase-design) |
| [`css-motion-systems`](https://github.com/timmo001/skills/blob/main/css-motion-systems/SKILL.md) | CSS motion design and implementation for web interfaces. Use when designing or building transitions, animations, `linear()` easing, transform strategy, View Transitions API patterns, motion tokens, or reviewing motion quality and accessibility. | [stolinski/s-stack](https://github.com/stolinski/s-stack/tree/main/skills/css-motion-systems) |
| [`diagnose`](https://github.com/timmo001/skills/blob/main/diagnose/SKILL.md) | Disciplined workflow for diagnosing bugs - hard bugs, regressions, flaky behavior, and performance issues. Use when behavior is broken, failing, intermittent, or slower than expected and the agent needs a reproducible feedback loop before fixing. | [mattpocock/skills](https://github.com/mattpocock/skills/tree/main/skills/engineering/diagnosing-bugs) |
| [`domain-modeling`](https://github.com/timmo001/skills/blob/main/domain-modeling/SKILL.md) | Build and sharpen a project’s domain model. Use when the user wants to pin down domain terminology or a ubiquitous language, challenge or record a design decision, or when another skill needs to maintain the domain model. | [mattpocock/skills](https://github.com/mattpocock/skills/tree/main/skills/engineering/domain-modeling) |
| [`gh-stack`](https://github.com/timmo001/skills/blob/main/gh-stack/SKILL.md) | Manage stacked branches and pull requests with GitHub’s `gh stack` extension. Use when work involves stacked PRs, dependent branches, stack creation, navigation, submission, synchronisation, rebasing, restructuring, linking, or merging. | [github/gh-stack](https://github.com/github/gh-stack/tree/main/skills/gh-stack) |
| [`grilling`](https://github.com/timmo001/skills/blob/main/grilling/SKILL.md) | Grill the user about a plan, decision, or idea in dependency-ready rounds. Use when the user wants to stress-test their thinking or uses a grill trigger phrase. | [mattpocock/skills](https://github.com/mattpocock/skills/tree/main/skills/productivity/grilling) |
| [`html`](https://github.com/timmo001/skills/blob/main/html/SKILL.md) | Writes and reviews semantic, accessible HTML and template markup that stays readable and low-noise. Use when creating or refactoring HTML or Svelte templates, cleaning up div soup, choosing better elements, improving form markup, fixing heading or landmark structure, or replacing custom controls with native HTML. | [stolinski/s-stack](https://github.com/stolinski/s-stack/tree/main/skills/html) |
| [`improve-codebase-architecture`](https://github.com/timmo001/skills/blob/main/improve-codebase-architecture/SKILL.md) | Scan a codebase for deepening opportunities, present them as a visual HTML report, then grill through whichever one you pick. | [mattpocock/skills](https://github.com/mattpocock/skills/tree/main/skills/engineering/improve-codebase-architecture) |
| [`to-questionnaire`](https://github.com/timmo001/skills/blob/main/to-questionnaire/SKILL.md) | Turn a decision the user cannot answer alone into a channel-aware questionnaire draft for one other person. Use when missing knowledge belongs to a colleague, maintainer, or domain expert and the user needs questions for GitHub, Slack, Discord, or a document. | [mattpocock/skills](https://github.com/mattpocock/skills/tree/main/skills/productivity/to-questionnaire) |