Skip to content

Repository files navigation

dotfiles

Personal configuration references and standalone maintenance tools for macOS and Linux hosts. This repository reports drift and missing capabilities. It does not automatically overwrite $HOME or rebuild an entire machine.

Repository model

  • reference/ contains the intended examples compared with live files under $HOME.
  • mackup/ declares which application paths belong to each reference.
  • scripts/ implements typed inspection tasks and explicit maintenance tasks; every mutating task previews by default and writes only with --apply.
  • modules/bin/ contains independent user commands.
  • modules/<tool>/ contains substantial tools that own their installation and removal.
  • generated/ is optional shell runtime state. A missing generated directory is a host-health warning, not a reason to mutate the repository.
  • inventory/ contains per-host software snapshots written only by mise run inventory.

Agent-specific rules live in AGENTS.md. Files under modules/ also inherit modules/AGENTS.md.

Operating model

The human who owns the target host and decides which changes are acceptable is the Operator. A person or AI carrying out the delegated task is the Executor.

This repository has two operating modes:

  • In authoring mode, the Executor changes this repository, runs mise run verify, and presents the result to the Operator for review.
  • In operations mode, the Executor inspects or previews the target host, reports the facts, impact, options, and recommendation, obtains the Operator's decision when needed, applies only the selected operation, and reruns the relevant inspection.

A finding's suggested action is not authorization to mutate the host. Ambiguous ownership, unclear scope, destructive effects that were not already authorized, or multiple legitimate outcomes require an Operator decision before mutation.

Start

Git and mise are the only bootstrap requirements. On a machine without mise, install the latest release from the official installer only when Dotfiles will manage that host. If the operating system or another platform already owns Mise or global configuration, retain that owner, create the audit-only policy before running any Dotfiles operation, and skip mise-sync --apply.

The managed-host bootstrap is:

curl -fsSL https://mise.run | sh
export PATH="$HOME/.local/bin:$PATH"
git clone https://github.com/ipruning/dotfiles.git ~/dotfiles
cd~/dotfiles
mise trust
mise install --locked fd jq python ripgrep shellcheck uv
mise exec -- uv sync --locked
mise tasks
mise run mise-sync
mise run mise-sync -- --apply

Managed hosts default to the standalone executable at ~/.local/bin/mise. Do not install a second copy with Homebrew, apt, or another package manager: multiple owners may leave generated shell activation bound to the wrong binary. The upstream installer uses this standalone path by default, and this installation supports mise self-update.

Host-local ownership policy

A host whose operating system owns configuration or Mise can opt out of repository mutation without changing the shared reference. Create the untracked host-local file ~/.config/dotfiles/policy.toml:

mode = "audit-only"mise_path = "/usr/bin/mise"

audit-only keeps previews plus diff, check, lint, and verification available, but rejects every --apply operation before it writes files or runs updaters. Previews still show the counterfactual managed-host plan, but omit an inapplicable --apply next step. Apply-safety differences such as host-only global Mise tools remain visible facts and do not fail an audit-only preview; an incomplete inspection, unreadable configuration, or failed command probe still fails. The same mutation guard protects the underlying apply_* and execute_* Python operations. mise_path selects the host's canonical Mise owner, so a package-managed binary such as Omarchy's /usr/bin/mise is not treated as an extra installation. An absent policy preserves the managed default above. An invalid policy, including unknown fields, fails closed and is diagnosed by mise run check; it never falls back silently to the standalone owner.

Under this policy, check marks Dotfiles-owned shell integration, shared Mise capabilities, and generated runtime readiness as not applicable. Canonical Mise health, duplicate owners, Skillshare health, permissions, and other host-owned problems remain applicable and continue to gate strict inspection.

The installer intentionally has no version argument: host and Orb bootstrap take the latest mise release available from https://mise.run rather than pinning the mise binary. The hard min_version in both project and global configuration is only the compatibility floor (currently 2026.8.3), not the version bootstrap should install.

mise trust is required because this repository declares a project virtual environment. The explicit mise install --locked ... command requires every project tool to have a URL and checksum for the current platform in mise.lock. mise exec -- uv sync --locked then materializes the locked Python dependencies, including the pinned Mackup fork, before shell activation makes uv directly available. Mise auto-install is disabled by the master settings.auto_install = false gate, covering task, exec, and command-not-found paths: these two commands form the explicit bootstrap boundary, and later commands fail instead of quietly downloading a missing tool or project dependency. Do not split the master gate into narrower task or exec settings. Do not replace the explicit tool list with a bare mise install --locked: Mise also loads the global configuration and would install host-wide tools during repository bootstrap.

mise tasks is the authoritative command list. The main inspection interface is:

mise run diff
mise run check
mise run lint
mise run verify

On Linux, diff and check automatically select the small linux-lite profile. macOS selects macos. Use --profile full when deliberately auditing every stored reference:

mise run diff -- --profile full
mise run check -- --profile full

Choose the operation by intent; mise tasks and each task's --help remain the authoritative command interfaces:

IntentDocumentation
Bootstrap a small Linux hostLinux Lite setup
Compare, restore, or adopt configurationConfiguration drift
Converge global mise tools across hostsGlobal mise convergence
Inspect host and repository healthHost health
Update installed toolsHost updates
Refresh generated shell stateGenerated runtime
Record installed softwareHost inventory
Operate a self-installing moduleStandalone tools
Inspect Skillshare ownershipSkillshare

Linux Lite setup

Inspection remains read-only. The explicit setup task is the only repository task that edits shell and Git bootstrap configuration in place; like every mutating task it previews by default:

mise run setup -- --profile linux-lite
mise run setup -- --profile linux-lite --apply
exec bash

It preserves the existing ~/.bashrc, adds one marked block that loads modules/bash/init.bash, and adds ~/.private.gitconfig to Git's includes. It does not create an identity, install optional tools, clone private repositories, or synchronize Skillshare extras. The Bash module adds ~/.local/bin and mise's shim directory to PATH, then activates mise and guarded Starship, Atuin, and Zoxide integrations in interactive shells. It provides .., ..., a Ctrl-W binding that deletes the same path segment as the macOS Zsh configuration, reserves Ctrl-R for Atuin when later startup files initialize FZF, and provides Zoxide's j command while leaving the ordinary up arrow to Bash. Interactive SSH logins remain ordinary shells; start Herdr explicitly when needed. It does not expose modules/bin on Linux. The managed block is placed before Ubuntu's non-interactive early return, so direct SSH commands also receive the user and mise paths without interactive shell initialization.

The Linux Lite drift profile observes Git, Mise, portable Atuin and Btop configuration, and the availability of Btop, Starship, Atuin, Zoxide, Herdr, Skillshare, Hunk, Lazygit, and Lazydocker. Optional capabilities remain warnings: inspection never installs a missing tool or restores configuration automatically. mise run check -- --strict treats those warnings as failures.

Linux Zsh contract

Linux Lite targets Bash, but the full Zsh reference is deliberately safe to restore on a Linux host as well, so mise run restore -- zsh is supported there. The contract is a split between experience and commands:

  • The Zsh experience loads on Linux. .zshrc sources modules/zsh/ (aliases, environment, completions, plugins) and the generated completion directory regardless of platform; the Homebrew block is macOS-only and the clash block is Linux-only.
  • The user bin and mise shim directories load on every platform. The repository command directory does not: .zshenv prepends modules/bin to PATH only under darwin*, so a Linux host never gains repository commands and can never shadow a system tool — most importantly iproute2 ss, whose repository namesake is skillshare-source.
  • The repository mise configuration carries no repository bins. Its committed lockfile contains URLs and checksums for both macos-arm64 and linux-x64, so bootstrap uses the same reviewed tool artifacts on both platforms.

What is not promised: an identical experience when optional tools are absent. Their integrations degrade gracefully, and mise run check reports profile-scoped missing capabilities. Starship, Atuin, and Zoxide are declared in the shared global Mise configuration for a consistent personal shell; Television remains host-managed. mise run shell reflects the same asymmetry — it syntax-checks Zsh files only where zsh is installed and marks them not-applicable elsewhere rather than failing.

Configuration drift

mise run diff compares profile-relevant files under reference/ with the corresponding paths under the current $HOME. It reports locations and file kinds, never file contents. Ordinary drift exits successfully; an incomplete inspection, such as an unreadable path or invalid Mackup response, exits non-zero.

mise run diff
mise run diff -- git
mise run diff -- --json
mise run diff -- --profile full

The command runs the immutable Mackup fork pinned as a locked git dependency in pyproject.toml and recorded in uv.lock. The bootstrap command mise exec -- uv sync --locked provisions it, so no Mackup fetch happens during inspection. It loads application definitions directly from mackup/applications/ and does not install ~/.mackup links or change either side.

Restore is deliberately application-scoped. It defaults to the same read-only plan; --apply first moves each changed live path into ~/.local/state/dotfiles/restore-backups/, then links the live path to its reference. There is no implicit restore-all operation.

mise run restore -- git
mise run restore -- git --json
mise run restore -- git --apply

When a live configuration should become the reference, adopt is the mirror of restore: it is application-scoped, defaults to the same read-only plan, and --apply copies the live truth into reference/ (and removes reference files whose live counterpart is gone). It writes only inside the repository, never under $HOME, and refuses to run while the affected reference paths have uncommitted changes, so Git can always revert an adoption.

mise run adopt -- mise
mise run adopt -- mise --json
mise run adopt -- mise --apply

Git protects repository changes; restore backups protect the replaced live paths.

Global mise convergence

The tracked global config.toml and multi-platform mise.lock are the shared personal interactive baseline for macOS and Linux hosts. They do not own systemd services, cron jobs, workers, or project runtimes: those must use a system package or invoke the owning project's Mise config explicitly. Installed tools, download caches, shims, and generated shell functions remain local runtime state and are rebuilt from the shared declaration rather than synchronized through Git.

This convergence operation belongs to managed hosts. On an audit-only host its preview is a counterfactual comparison, not a health verdict: host-owned tools and alias differences remain in the safety report, but do not fail the preview or produce an apply handoff.

Preview the complete convergence first:

mise run mise-sync
mise run mise-sync -- --json

An apply backs up and links the live mise configuration to reference/, first installs the locked Go, Node, Ruby, and Rust runtimes, then installs the complete locked tool set against $HOME. Staging the runtimes prevents package-manager backends such as cargo: from racing an older compiler already on the host. Finally, it replaces all shims with a canonical rebuild while ~/.local/bin is forced to the front of PATH:

mise run mise-sync -- --apply

On macOS 27, the complete tool step temporarily disables Cargo release stripping. Rust stable currently produces stripped proc-macro libraries that macOS 27 refuses to load; the override can be removed after stable Rust carries LLVM 22.1.8 or newer and the locked Atuin build passes without it.

Before replacing the live declaration, mise-sync compares its [tools] entries with the tracked baseline. Any live-only tool blocks apply and is named in the report. Decide whether to add it to the shared baseline, move it to its project or service owner, or remove it explicitly; mise-sync never makes that destructive choice. An unreadable or invalid live configuration also blocks apply because ownership cannot be established safely.

This operation deliberately does not pull Git, self-update mise, update the lockfile, remove an alternate package-managed mise, or install tools during shell startup. A canonical binary older than the repository's hard minimum must still be updated directly before any repository task can launch. That minimum is a compatibility floor; bootstrap and self-update continue to select the latest official release rather than pinning mise to the floor. Backends that support lockfile URLs are artifact-locked; package-manager backends such as gem:, go:, and npm: are version-locked but retain their upstream package manager's artifact and integrity semantics.

Global tools should be interactive commands wanted on every personal host. Project tools belong in that project's Mise configuration. Long-running Linux services should use the distribution package when appropriate, or an explicit <canonical-mise> -C <project> exec -- <tool> command; they must not depend on global shims. A genuinely host-specific tool is an explicit exception, not a second global configuration truth.

Herdr, Hunk, Lazygit, and Lazydocker are part of this shared baseline on macOS and Linux. Mise owns their installs and updates; do not combine them with Homebrew, a platform package, or a direct installer. Hunk supplies Git's pager and difftool plus Lazygit's static diff renderer, while Lazygit retains responsibility for staging. Herdr's portable configuration is restored independently:

mise run restore -- herdr
mise run restore -- herdr --apply

Atuin and Zoxide follow the same Mise ownership rule. Atuin is compiled on each host from the custom fork commit pinned in the shared declaration; the lockfile records that exact revision. Only ~/.config/atuin/config.toml is portable and restored through Mackup. Atuin history, login and encryption state, and Zoxide's database stay local to each host:

mise run restore -- atuin
mise run restore -- atuin --apply

Skillshare is also part of the shared Mise baseline; mise upgrade owns its CLI updates on every host. Do not combine that install with Homebrew or skillshare upgrade --cli. GitHub CLI and Codex are host-owned instead of part of the shared baseline. The capability is shared, but its installer is not: Omarchy provisions both through its own Mise wrappers, Debian can use the official APT package, and macOS can use Homebrew. The shared Mise configuration therefore declares neither gh nor Codex, and version differences between those host owners are not Dotfiles drift.

Btop is also platform-owned: Homebrew owns it on macOS, while each Linux distribution's package manager owns it. The repository restores only Btop's portable configuration and theme; check reports a missing binary but never installs it. Distribution package versions can therefore differ without creating configuration drift.

Host health

mise run check reports required executables and optional capabilities separately. The selected profile determines the current check set, which the plain or JSON report lists directly. On macOS, the report also consumes macos-session-health status --format json to detect an unavailable collector or notification path.

mise run check
mise run check -- --json
mise run check -- --strict

Warnings do not fail the normal command because different hosts intentionally have different capabilities. --strict treats warnings as failures.

Skillshare inspection parses the global YAML configuration and checks its source and target locations without invoking the Skillshare CLI. Installation ownership remains a separate check: Mise's JSON inventory distinguishes an inactive Mise install from the active executable and warns only when independent owners coexist. Run skillshare status --global --json or skillshare doctor explicitly when tracked-repository, synchronization, or deeper target health is needed: those commands may migrate legacy configuration or write diagnostic state and therefore do not belong in read-only check.

Missing or empty generated shell directories are reported as not ready. The report also verifies that the host's canonical Mise path is a real executable, warns when another Mise installation exists on PATH or at a common system location, verifies that Mise-owned shims target the canonical executable, and checks that generated Zsh activation names only the canonical executable.

mise run lint inspects repository paths, Mackup mappings, and dangling symlinks. Its path.* findings are host-relative: a machine-specific path referenced by tracked configuration is reported OK where it exists and as a warning on hosts where it does not, so warning counts legitimately differ between machines running the same commit. mise run verify adds Python formatting, type checking, behavior tests, the standalone module suites on macOS, and the shell gate: mise run shell runs Bash syntax checks and ShellCheck for Bash files. Zsh files receive syntax checking when zsh is available.

Host updates

Updating installed tools is an explicit mutation, separate from configuration inspection. Preview the exact commands first:

mise run update
mise run update -- --json
mise run update -- --apply

The task discovers most supported updaters on PATH, reports missing tools as skipped, applies available updates in a stable order, and continues independent steps after a failure. Mise is the exception: its tool upgrade and reshim steps always invoke the canonical Mise path explicitly. The standalone canonical Mise install also self-updates without the unrelated plugin update side effect; a host-selected Mise path is updated by its host owner instead. The preview is the authoritative list of supported updaters and the exact commands available on the current host. PLANNED means the updater command is available; the updater determines whether a newer version exists during apply. Any failed step makes the command exit non-zero. It deliberately does not run brew cleanup, brew autoremove, or mise prune; the Homebrew package step also sets HOMEBREW_NO_INSTALL_CLEANUP=1 so brew upgrade cannot trigger cleanup implicitly. Removal and pruning require a separate, explicit operation. Because Sprite's updater treats a closed upgrade prompt as a successful no-op, --apply supplies its affirmative response; preview output and JSON expose that stdin behavior. Claude receives a 30-minute outer timeout so its updater can report its own download failure. A failed Claude update is not cleaned automatically: retry claude update, then inspect its staging and versions directories before any explicit cleanup.

During a JSON apply, stdout remains one machine-readable document while stderr reports RUN, 30-second STILL RUNNING, DONE, and contextual failure records. Child output is discarded so it cannot corrupt JSON or hold the task open through inherited pipes. An agent can still distinguish active work from a stalled command.

For mise, the preview records the active installed tool versions. An apply updates a standalone Mise CLI first; a host-selected Mise binary is left to its host owner. It then passes the explicit installed-tool list to mise upgrade; a configured but missing mise tool is not installed. Other missing CLIs are skipped rather than bootstrapped. Package managers may still replace package dependencies as part of an ordinary upgrade.

When the live global mise files are linked to reference/, the mise tool upgrade may refresh the tracked lockfile. Run update --apply on a checkout where that declaration change will be reviewed and committed. Other hosts use mise-sync --apply to consume the committed lock without bumping it.

git diff -- reference/.config/mise

A hard min_version failure happens before mise can launch this repository's update task. In that bootstrap case, update the canonical binary directly with <canonical-mise> self-update when its owner supports that operation, then run the task normally. The hard minimum is only the oldest compatible release, not a mise binary pin.

update does not pull this repository, synchronize Skillshare content, or converge live configuration from reference/. Its Mise step may update the tracked global declaration as described above. Inspect the resulting host state separately:

mise run runtime

After updating tools, refresh the generated runtime. Restart each affected shell to load generated integrations; for Zsh, open a new shell or run exec zsh:

mise run runtime -- --apply
exec zsh

From the refreshed shell, inspect the resulting host and configuration state:

mise run check
mise run diff

Generated runtime

Shell runtime is maintained separately from tool updates and configuration restore. Preview the owned operations, then apply them explicitly:

mise run runtime
mise run runtime -- --json
mise run runtime -- --apply

The runtime task owns generated shell functions, completions, plugins, caches, and other declared runtime artifacts. Its preview is the authoritative list of planned operations on the current host. --offline limits an apply to local generation and cache maintenance. It never runs Skillshare or writes host inventory; snapshots have their own explicit task (see Host inventory). Generated files are a cache: shell startup sources them but does not regenerate them. The Mise generator always invokes the host's canonical Mise binary by absolute path, so cached activation remains bound to the selected owner.

Host inventory

Inventory snapshots record which software a host had installed at one point in time. They are tracked under inventory/<host>/ so history stays reviewable, and they are only ever written by the explicit task:

mise run inventory
mise run inventory -- --json
mise run inventory -- --apply

The preview is the authoritative list of collectors, destinations, and skipped host capabilities. A failed collector or an invalid empty result makes the command exit non-zero and leaves the existing snapshot untouched. Collectors whose domain permits zero items instead write an empty snapshot, replacing any stale non-empty result. Nothing regenerates these files implicitly — a snapshot is as old as its last committed run, never a claim about current host truth.

Standalone tools

bag-mode, macos-session-health, macos-maxfiles, and cursoruiviewservice-watchdog are self-installing modules. Each executable owns its command interface, installation, removal, tests, and generated launchd configuration. Their local runbooks own the module-specific operating and recovery decisions:

Ordinary commands under modules/bin/ are independent of the dotfiles inspection tasks. Linux Lite deliberately does not add them to PATH. The macOS shell configuration currently expects this repository at ~/dotfiles; mise run lint reports when that assumption is false.

Skillshare

Global harness prompts and AI skills remain owned by the Skillshare source repository. This repository stores only the portable Skillshare configuration; its source and default extras directories live under ~/.config/skillshare/, while their contents remain untracked here. Adopt or restore that configuration explicitly with mise run adopt -- skillshare --apply or mise run restore -- skillshare --apply. The host check reports when the executable, configuration, source directory, targets, or known installation ownership is unhealthy. This repository does not install or synchronize Skillshare automatically; mise run update -- --apply only refreshes an already installed CLI.

Use one authoring host to run skillshare update --all, review the resulting source changes, and publish them. Consumer hosts pull that reviewed Skillshare source, rehydrate missing tracked repositories, and run skillshare sync; they do not independently update every external Skill. This single-writer boundary keeps .metadata.json and copied external Skills from diverging by host.

The default skillshare sync operation synchronizes skills. The tracked configuration also declares opt-in extras targets under ~/.codex and ~/.claude; only an explicit extras sync writes those global harness directories. Amp global guidance is configured in Amp's personal or workspace settings and is not synchronized by this repository. Skill targets use merge mode, so target-local non-symlink Skill directories are healthy and preserved. In skillshare diff --json, an action of remove with is_sync: false describes the direction of the difference; it is not a planned sync deletion.

Before any external write, preview the complete synchronization and inspect each target's local and pruned counts:

skillshare sync --all --global --dry-run --force --json

local > 0 with pruned == 0 means synchronization preserves the local entries. Only pruned > 0 means the preview plans deletion; list those entries and request explicit confirmation. A failed preview leaves inspection incomplete and must not be reported as safe. After reviewing the complete preview, an extras-only preview remains available with skillshare sync extras --dry-run --force --json.

Installing Skillshare, choosing a source repository, and synchronizing skills are separate operator decisions. Linux Lite reports the missing capability and stops there; use the Skillshare source repository's current instructions if the host should gain that capability.

License

See LICENSE.

About

Personal configuration references and tools for inspecting configuration drift across macOS and Linux hosts

Resources

Stars

13 stars

Watchers

1 watching

Forks

Used by

Contributors

Languages