Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,19 @@ jobs:
node-version: "24"
- run: make preflight

- run: |
- run: ./setup.sh --dry-run

# The real Linux path, twice: the second run must find nothing to do.
- if: runner.os == 'Linux'
run: |
export HOME="$(mktemp -d)"
./setup.sh
./setup.sh | tee second-run.log
grep -q '^skip: deploy$' second-run.log

# The macOS path needs brew and sudo; keep exercising the deploy engine alone.
- if: runner.os == 'macOS'
run: |
export HOME="$(mktemp -d)"
./deploy.sh apply
./deploy.sh audit
31 changes: 31 additions & 0 deletions .github/workflows/pages.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: pages

on:
push:
branches: [ main ]
workflow_dispatch:

permissions:
contents: read
pages: write
id-token: write

# Never cancel a deploy halfway; let the newer push queue behind it.
concurrency:
group: pages
cancel-in-progress: false

jobs:
deploy:
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- uses: actions/checkout@v7
- run: make check-site site SITE_DIR=_site
- uses: actions/upload-pages-artifact@v3
with:
path: _site
- id: deployment
uses: actions/deploy-pages@v4
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,5 @@ home/.vim/.netrwhist

home/bin.Darwin/local
home/bin.Linux/local

_site/
4 changes: 3 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@ This repo is also consumed downstream by the user's agentic alter-ego, @nonreage
## Commands

- `make test` — full suite: `test/test_deploy.sh` + `test/test_shell.sh`. Run either script directly for one suite; both sandbox a throwaway `$HOME` under mktemp and never touch the real one.
- `./setup.sh [--dry-run]` (alias `make setup ARGS=...`) — the one entry point for a new or existing machine, also the target of the README's curl line. Steps are guarded make targets in order. Design: `docs/superpowers/specs/2026-09-25-setup-script-design.md`.
- `make site` / `make check-site` — build nonration.al (`site/index.html` plus the root `setup.sh`) into `_site/`, and check the built copy of `setup.sh` matches and the page shows the curl line. `.github/workflows/pages.yml` deploys it to GitHub Pages on every push to main.
- `./deploy.sh apply|audit [--dry-run]` — manifest-driven symlink deploy/verify. `make deploy` = apply plus re-asserting the skip-worktree flag (see Gotchas).
- `./scripts/macos-defaults.sh doctor|check|audit|apply|accept [--dry-run] [domain [key]]` — the `macos-defaults` table. **`make macos-doctor` first on any new Mac**: this tooling needs Full Disk Access granted to your terminal, or Safari's and Mail's rows cannot be read and silently skip. `make macos-audit` reports drift and needs no sudo; `make macos-apply` writes; `make macos-accept` rewrites rows to match the machine. `make macos` = doctor, then apply, then the imperative remainder in `scripts/macos-bootstrap.sh`, then a restart.
- `make check-symlinks` — fail on any dangling tracked symlink; `make check-skills` — same, scoped to `home/.agents/skills` (safe in CI); `make check-copilot-instructions` — self-heals the per-file mirror of rules into `home/.copilot/instructions/` (run after renaming anything in `home/.agents/rules/`); `make check-skill-frontmatter` — needs PyYAML; `make check-editorconfig` — needs `editorconfig-checker`.
- `make preflight` — the one definition of "safe to commit": `test`, every `check-*` target, and the offline eval checks (`eval-validate`, `eval-test`; Node ≥ 24, whose npm 11 wrote the lockfile). CI runs this same target (plus a deploy apply+audit against a temp `$HOME`, kept separate since it exercises the deploy mechanism rather than checking committed content), on macOS and ubuntu — a new `check-*` target is picked up by both without a second edit to `ci.yml`.
- `make preflight` — the one definition of "safe to commit": `test`, every `check-*` target, and the offline eval checks (`eval-validate`, `eval-test`; Node ≥ 24, whose npm 11 wrote the lockfile). CI runs this same target on macOS and ubuntu, plus a check of the setup mechanism kept separate because it exercises deploy rather than committed content: on ubuntu `setup.sh` runs twice against a temp `$HOME` and the second pass must skip deploy; on macOS it keeps a deploy apply+audit — a new `check-*` target is picked up by both without a second edit to `ci.yml`.
- `make eval SKILL=<name>` — full keyed run of one skill's `evals/` suite (one Markdown case per file, `evals/README.md` for the suite; see `docs/superpowers/specs/2026-09-25-eval-cases-markdown-design.md`) through promptfoo, gated by `evals/bin/check-gate.mjs`; spends subscription usage. `make eval-compare SKILL=<name>` runs skill vs baseline side by side. Unset `ANTHROPIC_API_KEY` first, or `claude -p` bills the API instead.
- `make eval-local SKILL=<name>` — runs the same suite against a local Ollama model (default `gemma4:latest`, override `EVAL_LOCAL_MODEL`), free, about a minute per case. It puts the skill in the system prompt under `--bare`, so it checks that cases render and grade, not how the skill loads. Sonnet runs (`make eval`) stay the reference.

Expand Down
68 changes: 60 additions & 8 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,33 @@ MAKEFLAGS += -B
default:
@echo "Cowardly refusing to run on $(shell uname). Use platform specific targets."

BREW ?= /opt/homebrew/bin/brew
BREW_BASH ?= /opt/homebrew/bin/bash
SHELLS_FILE ?= /etc/shells

brew-install:
/bin/bash -c "$$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
@if [ -x "$(BREW)" ]; then \
echo "brew already installed at $(BREW)"; \
else \
/bin/bash -c "$$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"; \
fi

# Make the brew bash the login shell. Replaces the README's inline block;
# safe to re-run, and the only step of setup.sh that needs sudo without a
# defaults write behind it.
set-shell:
@[ -x "$(BREW_BASH)" ] || { echo "error: $(BREW_BASH) missing; run make brew-bundle first" >&2; exit 1; }
@grep -qx "$(BREW_BASH)" "$(SHELLS_FILE)" || echo "$(BREW_BASH)" | sudo tee -a "$(SHELLS_FILE)" >/dev/null
@if [ "$$(dscl . -read ~ UserShell | awk '{print $$2}')" = "$(BREW_BASH)" ]; then \
echo "login shell already $(BREW_BASH)"; \
else \
chsh -s "$(BREW_BASH)"; \
fi

# setup.sh is the one entry point for a new or existing machine; `make setup`
# is the alias, ARGS passes flags through (make setup ARGS=--dry-run).
setup:
./setup.sh $(ARGS)

brew-bundle:
/opt/homebrew/bin/brew shellenv > /tmp/brew-shell.env
Expand All @@ -29,6 +54,20 @@ macos-accept:
check-macos-defaults:
@./scripts/macos-defaults.sh check

# nonration.al is GitHub Pages built from site/ plus the root setup.sh, so the
# served script is always main's. The pages workflow and check-site share this.
SITE_DIR ?= _site
site:
@rm -rf "$(SITE_DIR)" && mkdir -p "$(SITE_DIR)"
@cp site/index.html setup.sh "$(SITE_DIR)/"

check-site:
@d="$$(mktemp -d)" && $(MAKE) -s site SITE_DIR="$$d" \
&& cmp -s "$$d/setup.sh" setup.sh \
&& grep -qF 'curl -sS https://nonration.al/setup.sh | bash' "$$d/index.html" \
|| { echo "check-site: built site is missing setup.sh or the curl line" >&2; rm -rf "$$d"; exit 1; }; \
rm -rf "$$d"

macos: macos-doctor macos-apply
./scripts/macos-bootstrap.sh
osascript -e 'tell app "loginwindow" to «event aevtrrst»'
Expand Down Expand Up @@ -88,6 +127,7 @@ test:
./test/test_kitty.sh
./test/test_fish.sh
./test/test_macos_defaults.sh
./test/test_setup.sh

deploy:
./deploy.sh apply
Expand Down Expand Up @@ -150,7 +190,7 @@ check-copilot-instructions:

# The one definition of "safe to commit" -- CI runs this same target, so a
# new check-* target is covered by both the moment it's added here.
preflight: test check-symlinks check-skills check-skill-frontmatter check-editorconfig check-copilot-instructions check-macos-defaults eval-validate eval-test
preflight: test check-symlinks check-skills check-skill-frontmatter check-editorconfig check-copilot-instructions check-macos-defaults check-site eval-validate eval-test

# Skill eval suite (evals/). eval-validate and eval-test are offline and free,
# and run in preflight. eval and eval-compare spend subscription usage: a
Expand Down Expand Up @@ -188,13 +228,24 @@ eval-compare: evals/node_modules

link-karabiner:
# don't link entire .config directory because it may contain secrets
mkdir -p $$HOME/.config
ln -s $$PWD/karabiner $$HOME/.config/karabiner
@mkdir -p $$HOME/.config
@if [ "$$(readlink $$HOME/.config/karabiner 2>/dev/null)" = "$$PWD/karabiner" ]; then \
echo "karabiner already linked"; \
elif [ -e $$HOME/.config/karabiner ] && [ ! -L $$HOME/.config/karabiner ]; then \
echo "error: $$HOME/.config/karabiner exists and is not a symlink; move it aside" >&2; exit 1; \
else \
ln -sfn $$PWD/karabiner $$HOME/.config/karabiner; \
fi

link-sublime:
git clone https://github.com/nonrational/sublime3 $$HOME/.sublime3
rm -rf $$HOME/Library/Application\ Support/Sublime\ Text
ln -s $$HOME/.sublime3 $$HOME/Library/Application\ Support/Sublime\ Text
@[ -d $$HOME/.sublime3/.git ] || git clone https://github.com/nonrational/sublime3 $$HOME/.sublime3
@if [ "$$(readlink "$$HOME/Library/Application Support/Sublime Text" 2>/dev/null)" = "$$HOME/.sublime3" ]; then \
echo "Sublime Text already linked"; \
elif [ -e "$$HOME/Library/Application Support/Sublime Text" ] && [ ! -L "$$HOME/Library/Application Support/Sublime Text" ]; then \
echo "error: $$HOME/Library/Application Support/Sublime Text exists and is not a symlink; move it aside" >&2; exit 1; \
else \
ln -sfn $$HOME/.sublime3 "$$HOME/Library/Application Support/Sublime Text"; \
fi

# (Re)load the clipboard bridge launch agent: a socket-activated responder on
# 127.0.0.1:2224 that hands the pasteboard image to exe.dev VMs (see
Expand All @@ -219,7 +270,8 @@ restore-preferences:

macos-disable-restore-apps-on-login:
# See https://apple.stackexchange.com/a/322787
# clear the file if it isn't empty
# clear the file if it isn't empty (lifting the flag a previous run set)
find ~/Library/Preferences/ByHost/ -name 'com.apple.loginwindow*' -exec chflags nouimmutable {} \;
find ~/Library/Preferences/ByHost/ -name 'com.apple.loginwindow*' ! -size 0 -exec tee {} \; < /dev/null
# set the user immutable flag
find ~/Library/Preferences/ByHost/ -name 'com.apple.loginwindow*' -exec chflags uimmutable {} \;
Expand Down
49 changes: 8 additions & 41 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,54 +6,21 @@ _Architectural Digest_ for `$HOME`.

# Installation

## macOS
## Any machine

```shell
#!/usr/bin/env sh
xcode-select --install

git clone https://github.com/nonrational/dotfiles .dotfiles
cd .dotfiles

make brew-install
make brew-bundle

if [ -f /opt/homebrew/bin/bash ]; then
echo '/opt/homebrew/bin/bash' | sudo tee -a /etc/shells
chsh -s '/opt/homebrew/bin/bash'
else
echo 'Unable to set default shell to `/opt/homebrew/bin/bash`'
fi

# Better get a new terminal at this point.
make init-submodules
make deploy
make link-karabiner
make clipboard-bridge # launch agent that serves clipboard images to exe.dev VMs

# Authenticate with `gh` to clone private repo(s)
gh auth login
make link-sublime
make restore-preferences

# Almost there! Good idea to restart iTerm now, and take the opportunity to
# ensure it has full disk access.
make macos-reset-dock
make macos-disable-restore-apps-on-login
make macos
curl -sS https://nonration.al/setup.sh | bash
```

## GNU/Linux
That is the whole sequence, for a new Mac, a Linux host, or a machine catching up after months. `setup.sh` clones this repo into `~/.dotfiles` if it has to, then runs every step whose end state does not already hold; re-run it any time with `./setup.sh` or `make setup` (`make setup ARGS=--dry-run` shows what a run would do). On a Mac it halts at three checkpoints, each printed with a number and the instruction to re-run when done:

```shell
#!/usr/bin/env sh
1. The Command Line Tools installer, on a machine without them.
2. Full Disk Access for the terminal, which the `defaults` audit needs.
3. The reboot at the end.

git clone git@github.com:nonrational/dotfiles .dotfiles
cd .dotfiles
The Dock reset and the iTerm, Amphetamine and Moom preference restore run only when `setup.sh` had to clone, so a re-run on an existing machine never wipes either. Linux runs only the submodule init and the deploy.

make init-submodules
make deploy
```
A failed step prints `failed: <name>` and exits 2; re-run after fixing it.

# Clipboard bridge (exe.dev VMs)

Expand Down
Loading
Loading