From 7d61641d3190effe2536310197d4b92bd6eee4c8 Mon Sep 17 00:00:00 2001 From: Edwin Hernandez Date: Tue, 28 Apr 2026 23:42:34 -0400 Subject: [PATCH 01/23] feat: declarative apply via run_onchange_after_ scripts - brew-bundle: re-runs brew bundle when Brewfile.tmpl content changes - mise-install: re-runs mise install when mise/config.toml.tmpl changes - macos-defaults: scaffold keyed off sw_vers --buildVersion; re-runs after every macOS update so defaults write commands can be added incrementally All three short-circuit cleanly when their target tool isn't on PATH (covers the bootstrap window before mise/brew are installed). --- home/run_onchange_after_brew-bundle.sh.tmpl | 12 ++++++++++++ home/run_onchange_after_macos-defaults.sh.tmpl | 7 +++++++ home/run_onchange_after_mise-install.sh.tmpl | 12 ++++++++++++ 3 files changed, 31 insertions(+) create mode 100644 home/run_onchange_after_brew-bundle.sh.tmpl create mode 100644 home/run_onchange_after_macos-defaults.sh.tmpl create mode 100644 home/run_onchange_after_mise-install.sh.tmpl diff --git a/home/run_onchange_after_brew-bundle.sh.tmpl b/home/run_onchange_after_brew-bundle.sh.tmpl new file mode 100644 index 0000000..a0703d2 --- /dev/null +++ b/home/run_onchange_after_brew-bundle.sh.tmpl @@ -0,0 +1,12 @@ +#!/usr/bin/env bash +# Source-state hash: {{ include "dot_config/homebrew/Brewfile.tmpl" | sha256sum }} +# Re-runs whenever Brewfile.tmpl content changes. + +set -euo pipefail + +if ! command -v brew >/dev/null 2>&1; then + echo "brew not on PATH; skipping brew bundle" >&2 + exit 0 +fi + +brew bundle --file="$HOME/.config/homebrew/Brewfile" diff --git a/home/run_onchange_after_macos-defaults.sh.tmpl b/home/run_onchange_after_macos-defaults.sh.tmpl new file mode 100644 index 0000000..45a1eb6 --- /dev/null +++ b/home/run_onchange_after_macos-defaults.sh.tmpl @@ -0,0 +1,7 @@ +#!/usr/bin/env bash +# macOS build version: {{ output "sw_vers" "--buildVersion" }} +# Re-runs after every macOS update; safe to leave empty until you have defaults to write. + +set -euo pipefail + +echo "macOS build {{ output "sw_vers" "--buildVersion" }} — no defaults configured yet" diff --git a/home/run_onchange_after_mise-install.sh.tmpl b/home/run_onchange_after_mise-install.sh.tmpl new file mode 100644 index 0000000..2bd2690 --- /dev/null +++ b/home/run_onchange_after_mise-install.sh.tmpl @@ -0,0 +1,12 @@ +#!/usr/bin/env bash +# Source-state hash: {{ include "dot_config/mise/config.toml.tmpl" | sha256sum }} +# Re-runs whenever the mise config template content changes. + +set -euo pipefail + +if ! command -v mise >/dev/null 2>&1; then + echo "mise not on PATH; skipping mise install" >&2 + exit 0 +fi + +mise install From c1b97cc67ed29bb791b3f52f04a80f13846f4784 Mon Sep 17 00:00:00 2001 From: Edwin Hernandez Date: Tue, 28 Apr 2026 23:48:21 -0400 Subject: [PATCH 02/23] feat: wire age encryption framework (inactive until recipient set) Personal-only encryption pipeline; nothing is encrypted yet because the recipient public key is empty. Edwin pastes it into the .chezmoi.toml.tmpl $ageRecipient line after running keygen. - home/.chezmoi.toml.tmpl: conditional encryption block, gated on context==personal AND recipient!="" - home/.chezmoiignore.tmpl: always ignore key.txt.age (source state, not a target). On non-personal contexts, also ignore .secrets.local and the decrypt script (defense-in-depth alongside the script's own context check) - home/run_onchange_before_decrypt-private-key.sh.tmpl: on first apply, decrypts key.txt.age to ~/.config/chezmoi/key.txt with prompted passphrase; no-ops on subsequent applies and on non-personal contexts - home/dot_config/zsh/exports.zsh: source ~/.secrets.local instead of ~/.secrets (universal runtime path; personal materializes via chezmoi, work via hand) --- home/.chezmoi.toml.tmpl | 13 +++++++++++++ home/.chezmoiignore.tmpl | 12 +++++++++++- home/dot_config/zsh/exports.zsh | 4 +++- ...n_onchange_before_decrypt-private-key.sh.tmpl | 16 ++++++++++++++++ 4 files changed, 43 insertions(+), 2 deletions(-) create mode 100644 home/run_onchange_before_decrypt-private-key.sh.tmpl diff --git a/home/.chezmoi.toml.tmpl b/home/.chezmoi.toml.tmpl index 4cc2cd1..e582d99 100644 --- a/home/.chezmoi.toml.tmpl +++ b/home/.chezmoi.toml.tmpl @@ -1,5 +1,18 @@ +{{/* age recipient public key. Paste the public key here after generating + a keypair (see README "Encrypted personal secrets" section). + Empty string disables encryption (chezmoi works without it). + Safe to commit: public keys are public. */}} +{{- $ageRecipient := "" -}} {{- $context := promptChoiceOnce . "context" "Which context is this machine?" (list "personal" "work") -}} {{- $email := promptStringOnce . "email" "Git email address" -}} +{{- if and (eq $context "personal") (ne $ageRecipient "") }} +encryption = "age" + +[age] +identity = "~/.config/chezmoi/key.txt" +recipient = {{ $ageRecipient | quote }} +{{- end }} + [data] context = {{ $context | quote }} email = {{ $email | quote }} diff --git a/home/.chezmoiignore.tmpl b/home/.chezmoiignore.tmpl index f5f0225..eb490c7 100644 --- a/home/.chezmoiignore.tmpl +++ b/home/.chezmoiignore.tmpl @@ -1 +1,11 @@ -{{/* No context-specific ignores yet. Add patterns here when needed. */}} +{{/* The encrypted private key lives in source state and is read directly by the + decrypt script via .chezmoi.sourceDir. It must not be applied to ~/. */}} +key.txt.age + +{{/* The age machinery is personal-only. On a work machine, ignore both the + decrypt script and the encrypted secrets file so chezmoi behaves as if + they don't exist in source state. */}} +{{- if ne .context "personal" }} +.secrets.local +decrypt-private-key.sh +{{- end }} diff --git a/home/dot_config/zsh/exports.zsh b/home/dot_config/zsh/exports.zsh index 3d7e00a..14b4dd7 100644 --- a/home/dot_config/zsh/exports.zsh +++ b/home/dot_config/zsh/exports.zsh @@ -1,4 +1,6 @@ # zsh exports (PATH, environment variables) +# Personal: chezmoi decrypts ~/.secrets.local from age-encrypted source state. +# Work: maintain ~/.secrets.local by hand on the machine; never commit it. # shellcheck source=/dev/null -[[ -f ~/.secrets ]] && source ~/.secrets +[[ -f ~/.secrets.local ]] && source ~/.secrets.local diff --git a/home/run_onchange_before_decrypt-private-key.sh.tmpl b/home/run_onchange_before_decrypt-private-key.sh.tmpl new file mode 100644 index 0000000..300faf6 --- /dev/null +++ b/home/run_onchange_before_decrypt-private-key.sh.tmpl @@ -0,0 +1,16 @@ +#!/bin/sh +# Personal-only: decrypts the age private key on first apply. +# Defense-in-depth: .chezmoiignore.tmpl already excludes this script on +# non-personal contexts, but we also short-circuit here. +{{- if ne .context "personal" }} +exit 0 +{{- end }} + +if [ ! -f "${HOME}/.config/chezmoi/key.txt" ]; then + mkdir -p "${HOME}/.config/chezmoi" + chezmoi age decrypt \ + --output "${HOME}/.config/chezmoi/key.txt" \ + --passphrase \ + "{{ .chezmoi.sourceDir }}/key.txt.age" + chmod 600 "${HOME}/.config/chezmoi/key.txt" +fi From d136a99478fda8e0fb508cbb518c3355f34120cc Mon Sep 17 00:00:00 2001 From: Edwin Hernandez Date: Tue, 28 Apr 2026 23:53:04 -0400 Subject: [PATCH 03/23] docs: simplify README around install + commands + secrets - Drop the layering-model section (per Edwin's preference) - Tighten install to a single paragraph - Add common commands cheatsheet incl. chezmoi re-add (the bidirectional loop callout from Sayz Lim's article) - Document the personal-only age encryption flow and the work-machine manual ~/.secrets.local pattern explicitly --- README.md | 118 ++++++++++++++++++++++-------------------------------- 1 file changed, 48 insertions(+), 70 deletions(-) diff --git a/README.md b/README.md index f194f71..a8f7395 100644 --- a/README.md +++ b/README.md @@ -10,87 +10,65 @@ cd ~/Documents/github/dotfiles-public make install ``` -`make install` is idempotent. It: - -1. Installs Homebrew if missing. -2. `brew install mise` — the only dev tool brew owns directly. -3. Runs `chezmoi init --apply` (chezmoi pulled ephemerally via `mise x`). On first run, you'll be asked: **personal** or **work**. -4. `mise install` — installs the runtimes from the just-rendered `~/.config/mise/config.toml`. -5. `brew bundle` — installs the apps from the just-rendered `~/.config/homebrew/Brewfile`. - -After this, chezmoi pins `chezmoi` itself in your mise config, so `chezmoi` is always available. - -## Layout - -```txt -dotfiles-public/ -├── .chezmoiroot # points chezmoi at home/ -├── home/ # chezmoi source state -│ ├── .chezmoi.toml.tmpl # one-time prompt: context = personal | work -│ ├── .chezmoidata/defaults.toml # shared values (name, font, editor) -│ ├── .chezmoiignore.tmpl # reserved -│ ├── dot_zshenv.tmpl # exports ZDOTDIR=$HOME/.config/zsh -│ └── dot_config/ -│ ├── git/config.tmpl # [user.name] from defaults -│ ├── ghostty/config # shared -│ ├── homebrew/Brewfile.tmpl # shared + {personal|work} blocks -│ ├── mise/config.toml.tmpl # shared runtimes + tinytex/ffmpeg gated to personal -│ ├── starship.toml # shared -│ ├── tmux/tmux.conf # shared -│ ├── vscode/ # settings/keybindings/extensions -│ └── zsh/ # .zshrc, aliases, exports, plugins -├── packages/ # APM (agent package manager) packages -│ ├── business/ # not currently driven by context -│ └── development/ # personal AI agent setup -├── scripts/install.sh # bootstrap (called by `make install`) -├── mise.toml # project-level pins (apm, shellcheck, shfmt) for CI -├── apm.yml -└── makefile # fmt / lint / compile / install / apply / diff -``` +`make install` is idempotent. It installs Homebrew → `brew install mise` → uses `mise x chezmoi` ephemerally to run `chezmoi init --apply` → renders templates → `mise install` → `brew bundle`. -## Layering model +After the first apply, `chezmoi apply` is self-completing: editing `Brewfile.tmpl` triggers `brew bundle` automatically, editing `mise/config.toml.tmpl` triggers `mise install`, and a macOS update triggers any `defaults write` commands you've added (see `home/run_onchange_after_*.sh.tmpl`). -``` - .chezmoidata/defaults.toml (shared data: name, font, editor) - │ - ▼ - chezmoi init ─► prompts "context" once ─► ~/.config/chezmoi/chezmoi.toml - │ (machine-local, not committed) - ▼ - templates render with .context = "personal" | "work" - │ - ┌─────────────────┴─────────────────┐ - ▼ ▼ - Brewfile (personal) Brewfile (work) - + Discord, Aldente, + (placeholder; fill in - Synology Drive, … when seeding work box) - mise tools (personal) mise tools (work) - + tinytex, ffmpeg + (placeholder) -``` +## Common commands -Same dotfiles either way. Only the **app/tool list** diverges; everything else (zsh, ghostty, tmux, vscode, starship, git) is shared. +```sh +make apply # chezmoi apply (re-render + run any onchange scripts) +make diff # preview what apply would change +chezmoi add ~/.foo # bring an existing file under management +chezmoi re-add # pull live-edited files back into source state — use after an app rewrote its config +chezmoi edit ~/.foo # edit the source-state version of a managed file +chezmoi status # what would change vs source state +make fmt | make lint # format / check shell, md, yaml, toml +make compile # validate APM packages +``` -## Adding a new tool / app +`chezmoi re-add` is the most underrated command — it closes the loop when apps (Karabiner, VS Code, etc.) rewrite their own config files in place. -- **Tool** (mise-managed runtime): edit `home/dot_config/mise/config.toml.tmpl`. Put it in the shared `[tools]` block, or inside the `personal` / `work` conditional. Then `make apply && mise install`. -- **App** (homebrew cask or formula): edit `home/dot_config/homebrew/Brewfile.tmpl`. Same pattern. Then `make apply && brew bundle --file=~/.config/homebrew/Brewfile`. -- **Anything else** (shell config, editor settings, etc.): edit the relevant file under `home/dot_config/`. Then `make apply`. +## Encrypted personal secrets -`mise use -g ` is **not** the workflow on a managed machine — chezmoi will overwrite the file. Treat the templates as the source of truth. +**Personal machines only.** The repo is public, so secrets travel age-encrypted with a passphrase-protected private key. Work machines opt out entirely (see below). -## Commands +**One-time setup** (do this on your primary personal Mac): ```sh -make install # full bootstrap (brew + mise + chezmoi + apply + bundle) -make apply # re-run chezmoi apply against the current source -make diff # show what `chezmoi apply` would change -make fmt # format shell, Markdown, YAML -make lint # shellcheck + shfmt + prettier -make compile # validate APM packages +# 1. Generate keypair, passphrase-encrypt the private key, write to source dir. +chezmoi age-keygen | chezmoi age encrypt --passphrase \ + --output="$(chezmoi source-path)/key.txt.age" +# → prints "Public key: age1xxx..." Copy it. +# → prompts for passphrase twice. Save in 1Password (recovery on new machines). + +# 2. Open home/.chezmoi.toml.tmpl. Replace: +# {{- $ageRecipient := "" -}} +# with your printed public key, e.g.: +# {{- $ageRecipient := "age1xxx..." -}} + +# 3. Re-init chezmoi config so it picks up the new encryption block. +chezmoi init --prompt + +# 4. Create your secrets file and add it as encrypted source. +$EDITOR ~/.secrets.local # paste real exports +chmod 600 ~/.secrets.local +chezmoi add --encrypt ~/.secrets.local + +# 5. Commit and push. +git add home/key.txt.age home/encrypted_private_dot_secrets.local.age home/.chezmoi.toml.tmpl +git commit -m "feat: enable age-encrypted personal secrets" ``` +**On any other personal Mac:** clone the repo, run `make install`, type the passphrase once when prompted. `~/.secrets.local` materializes automatically, sourced by zsh. + +## Work machine secrets + +**Maintain `~/.secrets.local` by hand on the work machine.** Never commit it. The repo's encrypted personal blob is ignored on work via `home/.chezmoiignore.tmpl`, so the work machine never sees personal tokens. zsh's existing `[[ -f ~/.secrets.local ]] && source ~/.secrets.local` line picks up whatever you put there. + ## References - [chezmoi](https://www.chezmoi.io/) — dotfile manager -- [mise](https://mise.jdx.dev/) — runtime version manager (asdf-compatible) -- [ComposioHQ/awesome-claude-skills](https://github.com/ComposioHQ/awesome-claude-skills) +- [chezmoi encryption FAQ](https://www.chezmoi.io/user-guide/frequently-asked-questions/encryption/) — first-time keygen pattern reference +- [chezmoi macOS guide](https://www.chezmoi.io/user-guide/machines/macos/) — `sw_vers` + `defaults write` patterns +- [mise](https://mise.jdx.dev/) — runtime version manager From 01b48d95ae9047a32c13853e1fa79284489b42ba Mon Sep 17 00:00:00 2001 From: Edwin Hernandez Date: Wed, 29 Apr 2026 00:19:55 -0400 Subject: [PATCH 04/23] feat: enable age-encrypted personal secrets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - home/key.txt.age: passphrase-protected age private key (committed safely — requires the passphrase to decrypt) - home/encrypted_private_dot_secrets.local.age: GitHub PAT + Context7 API key, encrypted to the recipient public key in .chezmoi.toml.tmpl - home/.chezmoi.toml.tmpl: render sourceDir = repo path, plus the inline age recipient (committed safely — public keys are public). The conditional encryption block now activates because both context==personal and recipient!="" are true. - home/.chezmoidata/defaults.toml: editor.default = nano (nvim not installed) - README: encrypt-direct workflow via mktemp + chezmoi encrypt — no plaintext ever lands at ~/.secrets.local; the temp file is shredded after encrypt --- README.md | 33 ++++++++++++-------- home/.chezmoi.toml.tmpl | 4 ++- home/.chezmoidata/defaults.toml | 2 +- home/encrypted_private_dot_secrets.local.age | 7 +++++ home/key.txt.age | 10 ++++++ 5 files changed, 41 insertions(+), 15 deletions(-) create mode 100644 home/encrypted_private_dot_secrets.local.age create mode 100644 home/key.txt.age diff --git a/README.md b/README.md index a8f7395..3cff9e1 100644 --- a/README.md +++ b/README.md @@ -33,29 +33,36 @@ make compile # validate APM packages **Personal machines only.** The repo is public, so secrets travel age-encrypted with a passphrase-protected private key. Work machines opt out entirely (see below). -**One-time setup** (do this on your primary personal Mac): +**One-time setup** (do this on your primary personal Mac, from the repo root): ```sh -# 1. Generate keypair, passphrase-encrypt the private key, write to source dir. +REPO=~/Documents/github/dotfiles-public + +# 1. Generate keypair, passphrase-encrypt the private key into the repo's source. chezmoi age-keygen | chezmoi age encrypt --passphrase \ - --output="$(chezmoi source-path)/key.txt.age" + --output="$REPO/home/key.txt.age" # → prints "Public key: age1xxx..." Copy it. -# → prompts for passphrase twice. Save in 1Password (recovery on new machines). +# → prompts for passphrase twice. Save in Password manager. -# 2. Open home/.chezmoi.toml.tmpl. Replace: -# {{- $ageRecipient := "" -}} -# with your printed public key, e.g.: +# 2. Open home/.chezmoi.toml.tmpl. Replace the empty recipient with your key: # {{- $ageRecipient := "age1xxx..." -}} -# 3. Re-init chezmoi config so it picks up the new encryption block. +# 3. Re-init chezmoi config so it picks up sourceDir + encryption block. chezmoi init --prompt -# 4. Create your secrets file and add it as encrypted source. -$EDITOR ~/.secrets.local # paste real exports -chmod 600 ~/.secrets.local -chezmoi add --encrypt ~/.secrets.local +# 4. Author the encrypted secrets file directly — no plaintext on disk. +TMP=$(mktemp -t secrets-XXXXX) +trap 'rm -P "$TMP" 2>/dev/null || rm -f "$TMP"' EXIT +$EDITOR "$TMP" # paste: export GITHUB_PERSONAL_ACCESS_TOKEN="...", etc. +chezmoi encrypt --output \ + "$REPO/home/encrypted_private_dot_secrets.local.age" "$TMP" + +# 5. Apply — decrypts to ~/.secrets.local automatically. +chezmoi apply +cat ~/.secrets.local # sanity check -# 5. Commit and push. +# 6. Commit and push. +cd "$REPO" git add home/key.txt.age home/encrypted_private_dot_secrets.local.age home/.chezmoi.toml.tmpl git commit -m "feat: enable age-encrypted personal secrets" ``` diff --git a/home/.chezmoi.toml.tmpl b/home/.chezmoi.toml.tmpl index e582d99..8c6ede6 100644 --- a/home/.chezmoi.toml.tmpl +++ b/home/.chezmoi.toml.tmpl @@ -2,10 +2,12 @@ a keypair (see README "Encrypted personal secrets" section). Empty string disables encryption (chezmoi works without it). Safe to commit: public keys are public. */}} -{{- $ageRecipient := "" -}} +{{- $ageRecipient := "age1mqh9ynwrkem0kjcd5ds4wzknjhmw75dnu8cnfnxdz3hqstche97s7htkt0" -}} {{- $context := promptChoiceOnce . "context" "Which context is this machine?" (list "personal" "work") -}} {{- $email := promptStringOnce . "email" "Git email address" -}} +sourceDir = {{ joinPath .chezmoi.homeDir "Documents/github/dotfiles-public" | quote }} {{- if and (eq $context "personal") (ne $ageRecipient "") }} + encryption = "age" [age] diff --git a/home/.chezmoidata/defaults.toml b/home/.chezmoidata/defaults.toml index 7b81baa..a9a1f6a 100644 --- a/home/.chezmoidata/defaults.toml +++ b/home/.chezmoidata/defaults.toml @@ -5,4 +5,4 @@ name = "Edwin Hernandez" mono = "Monaspace Argon" [editor] -default = "nvim" +default = "nano" diff --git a/home/encrypted_private_dot_secrets.local.age b/home/encrypted_private_dot_secrets.local.age new file mode 100644 index 0000000..721ed17 --- /dev/null +++ b/home/encrypted_private_dot_secrets.local.age @@ -0,0 +1,7 @@ +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA2bDVwSm1UOWZMODM5SHhZ +UzRTd1ZhaWVQcnJNSnBvRGNWQU5LODZFMFJ3Cm85YUxFRHZKMmJYTHlabHY0dVVK +RnF0UkgrbDFWajB6RDhha2VYS3VrRlEKLS0tIFJvY1FHUEM5K0JsakdQYUozc2Jt +M2g3YU1UbVFnUmkwZ2ZBak9vL2k3bkkKtXp3t0bzwLNr2VapDplrWshcIzNNFrum +XIgWCG1UL64= +-----END AGE ENCRYPTED FILE----- diff --git a/home/key.txt.age b/home/key.txt.age new file mode 100644 index 0000000..d7c35ac --- /dev/null +++ b/home/key.txt.age @@ -0,0 +1,10 @@ +-----BEGIN AGE ENCRYPTED FILE----- +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNjcnlwdCB4SnJINUxJZ3pCcTQvVDBN +N2NaaHBBIDE4ClBLU3lJM3hSbkNIRzU5UG02THhKTUloNnVUUXNaNXZzNmE1Y0tL +M3JkU1EKLS0tIHp1bXduQ3d2RFByTGNiTGF6OEFvcnc3QlZhQldkaEt4dnNrV0pz +SGxSdzgKl+XfDB2haFzQxY0UJY7gkgTz3whIW8W4nvRHeVyNItRmEODzYWoaeAfe +d8yzbcqargl0FTF8E4X/uU8T/oTqx4j/8phewVfzzL0dDC7SqeyCqxT0W/3AY5Uq +nJLsgWFCeT8hXGsW6o86IAFH7NTITm6uiajq5dwUU46xE7IwxcMN/TH68Bz9Idwc +HaJ/PlEBrDEIl6NjnXyfn5d2m5C1Kh6xZj/p5Bbj5md3XNRzB5wxtVTkNVTupsNK +w3tvRgw6jcHSWURTMzaQ2x5eNGFXVeDjPgRDZ6KH4KGrGrI= +-----END AGE ENCRYPTED FILE----- From 91f7e0720d0d66103f2af4f14d0ecf64445f08b9 Mon Sep 17 00:00:00 2001 From: Edwin Hernandez Date: Wed, 29 Apr 2026 00:39:00 -0400 Subject: [PATCH 05/23] feat: update age recipient key and add script for decrypting private key --- home/.chezmoi.toml.tmpl | 4 ++-- home/encrypted_private_dot_secrets.local.age | 13 ++++++++----- home/key.txt.age | 16 ++++++++-------- ...pl => run_before_decrypt-private-key.sh.tmpl} | 0 4 files changed, 18 insertions(+), 15 deletions(-) rename home/{run_onchange_before_decrypt-private-key.sh.tmpl => run_before_decrypt-private-key.sh.tmpl} (100%) diff --git a/home/.chezmoi.toml.tmpl b/home/.chezmoi.toml.tmpl index 8c6ede6..4259786 100644 --- a/home/.chezmoi.toml.tmpl +++ b/home/.chezmoi.toml.tmpl @@ -2,10 +2,10 @@ a keypair (see README "Encrypted personal secrets" section). Empty string disables encryption (chezmoi works without it). Safe to commit: public keys are public. */}} -{{- $ageRecipient := "age1mqh9ynwrkem0kjcd5ds4wzknjhmw75dnu8cnfnxdz3hqstche97s7htkt0" -}} +{{- $ageRecipient := "age15lke8qvlf7zqs6xmgz2vnm7mul7ws97qy5hv8qs5uqyqj32fs45qmxjuhj" -}} {{- $context := promptChoiceOnce . "context" "Which context is this machine?" (list "personal" "work") -}} {{- $email := promptStringOnce . "email" "Git email address" -}} -sourceDir = {{ joinPath .chezmoi.homeDir "Documents/github/dotfiles-public" | quote }} +sourceDir = {{ .chezmoi.workingTree | quote }} {{- if and (eq $context "personal") (ne $ageRecipient "") }} encryption = "age" diff --git a/home/encrypted_private_dot_secrets.local.age b/home/encrypted_private_dot_secrets.local.age index 721ed17..1494170 100644 --- a/home/encrypted_private_dot_secrets.local.age +++ b/home/encrypted_private_dot_secrets.local.age @@ -1,7 +1,10 @@ -----BEGIN AGE ENCRYPTED FILE----- -YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA2bDVwSm1UOWZMODM5SHhZ -UzRTd1ZhaWVQcnJNSnBvRGNWQU5LODZFMFJ3Cm85YUxFRHZKMmJYTHlabHY0dVVK -RnF0UkgrbDFWajB6RDhha2VYS3VrRlEKLS0tIFJvY1FHUEM5K0JsakdQYUozc2Jt -M2g3YU1UbVFnUmkwZ2ZBak9vL2k3bkkKtXp3t0bzwLNr2VapDplrWshcIzNNFrum -XIgWCG1UL64= +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBaYTV5NWc5N2RwbFdJclV0 +N1JCMUhqR282YlRlbVdqQmVuWkpJV2VvL3djCnNZTXZHNGovcjNQelU4Vlk2TEN4 +SUxUNW9lMk1uUnNsQ2x4VEZ4RzBWQlUKLS0tIHhETE4yL0tYTHFIekVWQnBFN1hv +RGgrODllckNYSTEyYThjZ1hjcmNTdE0Kb3EVaZnWcU4FptGF6D7khuJFRFIF4xk0 +Skf+1k523W/jIGYcCNQKhLCLxC+t6jptCkyXXPzqxcdkLkZEOu0mR9LviCyinDSh +vRO+lNurbgU0Y7UPSokp1ljdCL39d/qdQPuPP622qqe/emXNAJrsuEVUJWloqhdH +mFx6pguN6mV1UrPfMpQskjuM6mX9Q/20S0S03zKEw+5zkEC/AA0tyVLcwY7p3j+g +c5MZDRa/lq93fTH81A== -----END AGE ENCRYPTED FILE----- diff --git a/home/key.txt.age b/home/key.txt.age index d7c35ac..6dfbd2a 100644 --- a/home/key.txt.age +++ b/home/key.txt.age @@ -1,10 +1,10 @@ -----BEGIN AGE ENCRYPTED FILE----- -YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNjcnlwdCB4SnJINUxJZ3pCcTQvVDBN -N2NaaHBBIDE4ClBLU3lJM3hSbkNIRzU5UG02THhKTUloNnVUUXNaNXZzNmE1Y0tL -M3JkU1EKLS0tIHp1bXduQ3d2RFByTGNiTGF6OEFvcnc3QlZhQldkaEt4dnNrV0pz -SGxSdzgKl+XfDB2haFzQxY0UJY7gkgTz3whIW8W4nvRHeVyNItRmEODzYWoaeAfe -d8yzbcqargl0FTF8E4X/uU8T/oTqx4j/8phewVfzzL0dDC7SqeyCqxT0W/3AY5Uq -nJLsgWFCeT8hXGsW6o86IAFH7NTITm6uiajq5dwUU46xE7IwxcMN/TH68Bz9Idwc -HaJ/PlEBrDEIl6NjnXyfn5d2m5C1Kh6xZj/p5Bbj5md3XNRzB5wxtVTkNVTupsNK -w3tvRgw6jcHSWURTMzaQ2x5eNGFXVeDjPgRDZ6KH4KGrGrI= +YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNjcnlwdCAzdlRGR2VzMmtzYW1Fajl4 +MzQzT29BIDE4Cnl1UXVvQU1KSFAzNm1sOWtuS0hrUnV4Mk5YejRoQkVPeDZKVjRh +RFg2ZE0KLS0tIDltZkNzamJUbkZrMllrQWlBTEFPMWkxaW4wUExzZEpTdmVmby84 +SkxJWWMKj8T7JuSZusJEIn460yf1V3XqsFGcmaYGinQw2jshesi56X0zbhHweLsS +F5ASkvi17TQ6rvf87yCtzlkHVuDQZe5O5iq0g/yzlxIlq1qOGHbSIFolNrsSL6hq +Oq6S7c1YDbGvapyKeS9/Ag4howRlbD2uOiikcOge2N/GV4Grgt9PU6BakyRQSBpZ +zajZ33kjYrKEUp/t0sHCayAdtGCOVw0VTB0Qihw50ZYTwUdDRZdestONpcNzNSsv +uXv585oIG84AqlcMZQg4rq6/txWxT4eK9gxxZn2UQuNFWxI= -----END AGE ENCRYPTED FILE----- diff --git a/home/run_onchange_before_decrypt-private-key.sh.tmpl b/home/run_before_decrypt-private-key.sh.tmpl similarity index 100% rename from home/run_onchange_before_decrypt-private-key.sh.tmpl rename to home/run_before_decrypt-private-key.sh.tmpl From 515b16d2ef0f0882688812396b037b3f5c260850 Mon Sep 17 00:00:00 2001 From: Edwin Hernandez Date: Thu, 30 Apr 2026 15:07:21 -0400 Subject: [PATCH 06/23] feat: add comprehensive documentation and scripts for dotfiles management --- .github/copilot-instructions.md | 70 ++++++++++++++++++ .github/dependabot.yaml | 13 ++++ AGENT.md | 29 ++++++++ CLAUDE.md | 1 + README.md | 44 ++++++----- home/.chezmoi.toml.tmpl | 12 +-- .../run_before_decrypt-private-key.sh.tmpl | 0 .../run_onchange_after_brew-bundle.sh.tmpl | 0 .../run_onchange_after_macos-defaults.sh.tmpl | 0 .../run_onchange_after_mise-install.sh.tmpl | 0 home/dot_config/git/config.tmpl | 25 ------- home/dot_config/git/ignore | 1 - .../gh/config.yml | 0 .../ghostty/config | 0 home/private_dot_config/git/config.tmpl | 50 +++++++++++++ home/private_dot_config/git/ignore | 61 +++++++++++++++ .../homebrew/Brewfile.tmpl | 0 .../mise/config.toml.tmpl | 0 .../Raycast 2026-04-26 00.55.21.rayconfig | Bin .../starship.toml | 0 .../tmux/tmux.conf | 0 .../vscode/extensions.json | 0 .../zsh/aliases.zsh | 0 .../zsh/custom/.gitkeep | 0 .../zsh/dot_zprofile.tmpl | 0 .../zsh/dot_zshrc | 0 .../zsh/exports.zsh | 0 .../zsh/plugins.zsh | 0 28 files changed, 256 insertions(+), 50 deletions(-) create mode 100644 .github/copilot-instructions.md create mode 100644 .github/dependabot.yaml create mode 100644 AGENT.md create mode 100644 CLAUDE.md rename home/{ => .chezmoiscripts}/run_before_decrypt-private-key.sh.tmpl (100%) rename home/{ => .chezmoiscripts}/run_onchange_after_brew-bundle.sh.tmpl (100%) rename home/{ => .chezmoiscripts}/run_onchange_after_macos-defaults.sh.tmpl (100%) rename home/{ => .chezmoiscripts}/run_onchange_after_mise-install.sh.tmpl (100%) delete mode 100644 home/dot_config/git/config.tmpl delete mode 100644 home/dot_config/git/ignore rename home/{dot_config => private_dot_config}/gh/config.yml (100%) rename home/{dot_config => private_dot_config}/ghostty/config (100%) create mode 100644 home/private_dot_config/git/config.tmpl create mode 100644 home/private_dot_config/git/ignore rename home/{dot_config => private_dot_config}/homebrew/Brewfile.tmpl (100%) rename home/{dot_config => private_dot_config}/mise/config.toml.tmpl (100%) rename home/{dot_config => private_dot_config}/raycast/Raycast 2026-04-26 00.55.21.rayconfig (100%) rename home/{dot_config => private_dot_config}/starship.toml (100%) rename home/{dot_config => private_dot_config}/tmux/tmux.conf (100%) rename home/{dot_config => private_dot_config}/vscode/extensions.json (100%) rename home/{dot_config => private_dot_config}/zsh/aliases.zsh (100%) rename home/{dot_config => private_dot_config}/zsh/custom/.gitkeep (100%) rename home/{dot_config => private_dot_config}/zsh/dot_zprofile.tmpl (100%) rename home/{dot_config => private_dot_config}/zsh/dot_zshrc (100%) rename home/{dot_config => private_dot_config}/zsh/exports.zsh (100%) rename home/{dot_config => private_dot_config}/zsh/plugins.zsh (100%) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..6200960 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,70 @@ +# GitHub Copilot Instructions for Dotfiles Repository + +This document provides guidelines for using GitHub Copilot effectively within this dotfiles repository. Adhering to these instructions will help maintain consistency, improve code quality, and ensure that generated suggestions align with our project's standards, especially regarding Conventional Commits. + +--- + +## 1. General Best Practices + +- **Be Specific with Prompts:** The more precise your comments and existing code, the better Copilot can understand your intent. Clearly describe what you want to achieve. +- **Review Suggestions Carefully:** Always review Copilot's suggestions before accepting them. Don't blindly accept code; ensure it's correct, efficient, and aligns with your overall goal. +- **Iterate and Refine:** If the initial suggestion isn't perfect, refine your prompt or add more context. Copilot often improves with more specific input. +- **Focus on Small, Incremental Changes:** Try to break down complex tasks into smaller, manageable chunks. This makes it easier for Copilot to provide relevant suggestions and for you to review them. + +--- + +## 2. Conventional Commits Guidelines + +We use [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) for our commit messages. This helps us maintain a clear and consistent commit history, which is crucial for changelog generation and understanding project evolution. + +When using Copilot, pay close attention to the following for commit-related suggestions: + +- **Commit Type:** Start your commit message with a **type**, followed by an optional **scope**, and a colon and space. + - **Common types for dotfiles:** + - `feat`: A new feature or configuration. + - `fix`: A bug fix or correction to an existing configuration. + - `docs`: Documentation only changes. + - `style`: Changes that do not affect the meaning of the code (white-space, formatting, missing semicolons, etc.). + - `refactor`: A code change that neither fixes a bug nor adds a feature (e.g., restructuring files, renaming variables). + - `perf`: A code change that improves performance. + - `test`: Adding missing tests or correcting existing tests. + - `build`: Changes that affect the build system or external dependencies (e.g., `deps`, `npm`). + - `ci`: Changes to our CI configuration files and scripts. + - `chore`: Other changes that don't modify src or test files (e.g., updating grunt tasks, `.gitignore`). + - **Example:** `feat: Add new Zsh aliases` + - **Example with scope:** `fix(vim): Correct keybinding for split window` + +- **Commit Subject:** Follow the type/scope with a **short, imperative, present-tense** description of the change. + - **Bad:** `added new feature` + - **Good:** `feat: Add new feature` + - **Good:** `fix: Correct typo in README` + +- **Commit Body (Optional):** If the change is complex, include a blank line after the subject and then a more detailed explanation in the commit body. + - **Wrap at 72 characters** for readability. + - **Use imperative mood:** "Add feature" not "Added feature". + +- **Breaking Changes (Optional):** For breaking changes, start a paragraph with `BREAKING CHANGE:` followed by a description of the change and justification. This should be in the footer of the commit. + +--- + +## 3. Dotfiles Specific Considerations + +- **Context is Key:** Dotfiles often rely heavily on context from your shell, editor, or other applications. Provide comments that explain the purpose of specific configurations. + ```bash + # Ensure Copilot understands this is for Zsh + # Auto-suggestion plugin configuration + zsh_autosuggestions_config() { + # ... + } + ``` +- **Idempotency:** Many dotfile configurations should be idempotent (running them multiple times has the same effect as running them once). Copilot can help suggest idempotent patterns if you provide the right context. +- **Security:** Be mindful of sensitive information. Dotfiles can sometimes contain API keys or personal data. Ensure Copilot doesn't suggest sensitive information that shouldn't be committed. +- **Cross-Platform Compatibility:** If your dotfiles are meant to be cross-platform, include comments indicating specific OS or environment dependencies. + +--- + +## 4. Troubleshooting & Tips + +- **Copilot not suggesting Conventional Commits?** Try explicitly typing the type (e.g., `feat:`) and Copilot might pick up the pattern. +- **Too many irrelevant suggestions?** Try restarting your editor or the Copilot extension. Sometimes providing more surrounding code context helps. +- **Provide examples:** If you have existing commit messages that follow Conventional Commits, Copilot will learn from them. diff --git a/.github/dependabot.yaml b/.github/dependabot.yaml new file mode 100644 index 0000000..d06edf3 --- /dev/null +++ b/.github/dependabot.yaml @@ -0,0 +1,13 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + +version: 2 +updates: + - package-ecosystem: "github-actions" # See documentation for possible values + directory: "/" # Location of package manifests + schedule: + interval: "monthly" + labels: + - "enhancement" diff --git a/AGENT.md b/AGENT.md new file mode 100644 index 0000000..452c92b --- /dev/null +++ b/AGENT.md @@ -0,0 +1,29 @@ +# AGENTS.md + +## Repository Context + +- This repository is managed with [`chezmoi`](https://www.chezmoi.io/) ([GitHub](https://github.com/twpayne/chezmoi)). +- Files under `home/` are the public source state and are applied by `chezmoi` into the user's `$HOME` directory. +- Private dotfiles are managed separately from `~/.local/share/chezmoi-private` with config at `~/.config/chezmoi-private/chezmoi.yaml`. +- Treat the public `home/` tree and the private `chezmoi` source/config as separate management domains. + +## Response Rule + +- After reading this `AGENTS.md`, say: `🤖 I read the AGENTS.md for shunk031/dotfiles.` + +## Comment Policy + +- When adding or updating comments for shell scripts or shell-based executables, always write them in English using shdoc-compatible format. + +## Git / PR Workflow + +- When you are asked to create a branch, commit, or pull request and the current worktree contains unrelated staged, unstaged, or untracked changes, prefer creating a separate `git worktree` from the default branch. +- In that separate `git worktree`, apply only the changes relevant to the current task and do not mix unrelated changes into the branch or pull request. +- Only prioritize the current branch or worktree when the user explicitly asks you to work there. +- After pushing to GitHub, always check the GitHub Actions CI results. If CI fails, investigate the failure, fix the issue, push again, and repeat until all CI checks pass. +- Always write pull request titles and descriptions in English. + +## Test Policy + +- Do not run `bats` tests locally. +- When you need to validate `bats` results, push to GitHub, let GitHub Actions CI run, and check the results there. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..43c994c --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +@AGENTS.md diff --git a/README.md b/README.md index 3cff9e1..008f397 100644 --- a/README.md +++ b/README.md @@ -33,36 +33,44 @@ make compile # validate APM packages **Personal machines only.** The repo is public, so secrets travel age-encrypted with a passphrase-protected private key. Work machines opt out entirely (see below). -**One-time setup** (do this on your primary personal Mac, from the repo root): +**One-time setup** (run from the repo root — `cd` in first): + +The piped form `chezmoi age-keygen | chezmoi age encrypt --passphrase --output=...` hides the public-key line on some chezmoi versions, leaving you with no recipient to paste. Use the two-step form below instead — it keeps the public key visible. ```sh -REPO=~/Documents/github/dotfiles-public +# 1. Generate keypair to a plaintext temp file (so the public key is visible). +TMPKEY=$(mktemp -t agekey-XXXXX) +chezmoi age-keygen --output="$TMPKEY" + +# 2. Show the public key — the line starting "# public key:". Copy it. +grep '^# public key:' "$TMPKEY" +# → e.g. # public key: age1xxxxx... + +# 3. Encrypt the keypair file with a passphrase, save into the repo. +chezmoi age encrypt --passphrase --output=home/key.txt.age "$TMPKEY" -# 1. Generate keypair, passphrase-encrypt the private key into the repo's source. -chezmoi age-keygen | chezmoi age encrypt --passphrase \ - --output="$REPO/home/key.txt.age" -# → prints "Public key: age1xxx..." Copy it. -# → prompts for passphrase twice. Save in Password manager. +# 4. Shred the plaintext intermediate. +rm -P "$TMPKEY" 2>/dev/null || rm -f "$TMPKEY" -# 2. Open home/.chezmoi.toml.tmpl. Replace the empty recipient with your key: +# 5. Open home/.chezmoi.toml.tmpl. Replace the empty recipient with your key: # {{- $ageRecipient := "age1xxx..." -}} -# 3. Re-init chezmoi config so it picks up sourceDir + encryption block. -chezmoi init --prompt +# 6. Re-init so chezmoi.toml picks up sourceDir + the encryption block. +# --source is required on this first run only; afterwards sourceDir is +# baked into ~/.config/chezmoi/chezmoi.toml. +chezmoi init --prompt --source "$(pwd)" -# 4. Author the encrypted secrets file directly — no plaintext on disk. +# 7. Author the encrypted secrets file directly — no plaintext on disk. TMP=$(mktemp -t secrets-XXXXX) -trap 'rm -P "$TMP" 2>/dev/null || rm -f "$TMP"' EXIT -$EDITOR "$TMP" # paste: export GITHUB_PERSONAL_ACCESS_TOKEN="...", etc. -chezmoi encrypt --output \ - "$REPO/home/encrypted_private_dot_secrets.local.age" "$TMP" +nano "$TMP" # paste: export GITHUB_PERSONAL_ACCESS_TOKEN="...", etc. +chezmoi encrypt --output home/encrypted_private_dot_secrets.local.age "$TMP" +rm -P "$TMP" 2>/dev/null || rm -f "$TMP" -# 5. Apply — decrypts to ~/.secrets.local automatically. +# 8. Apply — prompts for passphrase once, then materializes ~/.secrets.local. chezmoi apply cat ~/.secrets.local # sanity check -# 6. Commit and push. -cd "$REPO" +# 9. Commit and push. git add home/key.txt.age home/encrypted_private_dot_secrets.local.age home/.chezmoi.toml.tmpl git commit -m "feat: enable age-encrypted personal secrets" ``` diff --git a/home/.chezmoi.toml.tmpl b/home/.chezmoi.toml.tmpl index 4259786..babdf19 100644 --- a/home/.chezmoi.toml.tmpl +++ b/home/.chezmoi.toml.tmpl @@ -1,10 +1,6 @@ -{{/* age recipient public key. Paste the public key here after generating - a keypair (see README "Encrypted personal secrets" section). - Empty string disables encryption (chezmoi works without it). - Safe to commit: public keys are public. */}} {{- $ageRecipient := "age15lke8qvlf7zqs6xmgz2vnm7mul7ws97qy5hv8qs5uqyqj32fs45qmxjuhj" -}} + {{- $context := promptChoiceOnce . "context" "Which context is this machine?" (list "personal" "work") -}} -{{- $email := promptStringOnce . "email" "Git email address" -}} sourceDir = {{ .chezmoi.workingTree | quote }} {{- if and (eq $context "personal") (ne $ageRecipient "") }} @@ -17,4 +13,8 @@ recipient = {{ $ageRecipient | quote }} [data] context = {{ $context | quote }} -email = {{ $email | quote }} + +# git +git_username = {{ promptStringOnce "git username" | quote }} +git_email = {{ promptStringOnce "git email" | quote }} +git_signing_key = {{ (bitwardenFields "item" (bitwarden "item" "chezmoi").id).git_signing_key_id.value | quote }} diff --git a/home/run_before_decrypt-private-key.sh.tmpl b/home/.chezmoiscripts/run_before_decrypt-private-key.sh.tmpl similarity index 100% rename from home/run_before_decrypt-private-key.sh.tmpl rename to home/.chezmoiscripts/run_before_decrypt-private-key.sh.tmpl diff --git a/home/run_onchange_after_brew-bundle.sh.tmpl b/home/.chezmoiscripts/run_onchange_after_brew-bundle.sh.tmpl similarity index 100% rename from home/run_onchange_after_brew-bundle.sh.tmpl rename to home/.chezmoiscripts/run_onchange_after_brew-bundle.sh.tmpl diff --git a/home/run_onchange_after_macos-defaults.sh.tmpl b/home/.chezmoiscripts/run_onchange_after_macos-defaults.sh.tmpl similarity index 100% rename from home/run_onchange_after_macos-defaults.sh.tmpl rename to home/.chezmoiscripts/run_onchange_after_macos-defaults.sh.tmpl diff --git a/home/run_onchange_after_mise-install.sh.tmpl b/home/.chezmoiscripts/run_onchange_after_mise-install.sh.tmpl similarity index 100% rename from home/run_onchange_after_mise-install.sh.tmpl rename to home/.chezmoiscripts/run_onchange_after_mise-install.sh.tmpl diff --git a/home/dot_config/git/config.tmpl b/home/dot_config/git/config.tmpl deleted file mode 100644 index 44df038..0000000 --- a/home/dot_config/git/config.tmpl +++ /dev/null @@ -1,25 +0,0 @@ -[user] - name = {{ .git.name | quote }} - email = {{ .email | quote }} -[core] - excludesfile = ~/.gitignore - editor = {{ .editor.default }} - ignorecase = false -[push] - default = simple -[color] - status = auto - diff = auto - branch = auto - interactive = auto - grep = auto - ui = auto -[hub] - protocol = https -[github] - user = edwinhern -[ghq] - root = ~/.ghq -{{- if eq .context "work" }} -{{- /* placeholder - no work-specific git config yet */ -}} -{{- end }} diff --git a/home/dot_config/git/ignore b/home/dot_config/git/ignore deleted file mode 100644 index 66d62f8..0000000 --- a/home/dot_config/git/ignore +++ /dev/null @@ -1 +0,0 @@ -**/.claude/settings.local.json diff --git a/home/dot_config/gh/config.yml b/home/private_dot_config/gh/config.yml similarity index 100% rename from home/dot_config/gh/config.yml rename to home/private_dot_config/gh/config.yml diff --git a/home/dot_config/ghostty/config b/home/private_dot_config/ghostty/config similarity index 100% rename from home/dot_config/ghostty/config rename to home/private_dot_config/ghostty/config diff --git a/home/private_dot_config/git/config.tmpl b/home/private_dot_config/git/config.tmpl new file mode 100644 index 0000000..6fc79a4 --- /dev/null +++ b/home/private_dot_config/git/config.tmpl @@ -0,0 +1,50 @@ +[user] + name = {{ .git_username }} + email = {{ .git_email }} +[core] + editor = code --wait + pager = delta + autocrlf = input + excludesfile = ~/.config/git/ignore + ignorecase = false +[pull] + rebase = true +[push] + autoSetupRemote = true +[rebase] + autoStash = true +[interactive] + diffFilter = delta --color-only + +[diff] + colorMoved = default + +[delta] + features = unobtrusive-line-numbers decorations + whitespace-error-style = 22 reverse + plus-color = "#012800" + minus-color = "#340001" + syntax-theme = Monokai Extended + light = false + navigate = true + +[merge] + conflictstyle = diff3 + +[delta "unobtrusive-line-numbers"] + line-numbers = true + line-numbers-minus-style = "#444444" + line-numbers-zero-style = "#444444" + line-numbers-plus-style = "#444444" + line-numbers-left-format = "{nm:>4}┊" + line-numbers-right-format = "{np:>4}│" + line-numbers-left-style = blue + line-numbers-right-style = blue + +[delta "decorations"] + commit-decoration-style = bold yellow box ul + file-style = bold yellow ul + file-decoration-style = none + hunk-header-decoration-style = yellow box +[hub] + protocol = https diff --git a/home/private_dot_config/git/ignore b/home/private_dot_config/git/ignore new file mode 100644 index 0000000..5816a0e --- /dev/null +++ b/home/private_dot_config/git/ignore @@ -0,0 +1,61 @@ +# AI +**/.claude/settings.local.json + +#################### +# Mac +#################### + +# General +.DS_Store +.AppleDouble +.LSOverride +Icon[] + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdiskre + +#################### +# Mise +#################### + +# Mise configuration files +# https://mise.jdx.dev/configuration.html +# https://mise.jdx.dev/configuration/environments.html +.mise.*.local.toml +.mise.local.toml +mise.*.local.toml +mise.local.toml +.mise/*.local.toml +mise/*.local.toml + + +#################### +# VSCode +#################### + +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/*.code-snippets +!*.code-workspace + +# Built Visual Studio Code Extensions +*.vsix \ No newline at end of file diff --git a/home/dot_config/homebrew/Brewfile.tmpl b/home/private_dot_config/homebrew/Brewfile.tmpl similarity index 100% rename from home/dot_config/homebrew/Brewfile.tmpl rename to home/private_dot_config/homebrew/Brewfile.tmpl diff --git a/home/dot_config/mise/config.toml.tmpl b/home/private_dot_config/mise/config.toml.tmpl similarity index 100% rename from home/dot_config/mise/config.toml.tmpl rename to home/private_dot_config/mise/config.toml.tmpl diff --git a/home/dot_config/raycast/Raycast 2026-04-26 00.55.21.rayconfig b/home/private_dot_config/raycast/Raycast 2026-04-26 00.55.21.rayconfig similarity index 100% rename from home/dot_config/raycast/Raycast 2026-04-26 00.55.21.rayconfig rename to home/private_dot_config/raycast/Raycast 2026-04-26 00.55.21.rayconfig diff --git a/home/dot_config/starship.toml b/home/private_dot_config/starship.toml similarity index 100% rename from home/dot_config/starship.toml rename to home/private_dot_config/starship.toml diff --git a/home/dot_config/tmux/tmux.conf b/home/private_dot_config/tmux/tmux.conf similarity index 100% rename from home/dot_config/tmux/tmux.conf rename to home/private_dot_config/tmux/tmux.conf diff --git a/home/dot_config/vscode/extensions.json b/home/private_dot_config/vscode/extensions.json similarity index 100% rename from home/dot_config/vscode/extensions.json rename to home/private_dot_config/vscode/extensions.json diff --git a/home/dot_config/zsh/aliases.zsh b/home/private_dot_config/zsh/aliases.zsh similarity index 100% rename from home/dot_config/zsh/aliases.zsh rename to home/private_dot_config/zsh/aliases.zsh diff --git a/home/dot_config/zsh/custom/.gitkeep b/home/private_dot_config/zsh/custom/.gitkeep similarity index 100% rename from home/dot_config/zsh/custom/.gitkeep rename to home/private_dot_config/zsh/custom/.gitkeep diff --git a/home/dot_config/zsh/dot_zprofile.tmpl b/home/private_dot_config/zsh/dot_zprofile.tmpl similarity index 100% rename from home/dot_config/zsh/dot_zprofile.tmpl rename to home/private_dot_config/zsh/dot_zprofile.tmpl diff --git a/home/dot_config/zsh/dot_zshrc b/home/private_dot_config/zsh/dot_zshrc similarity index 100% rename from home/dot_config/zsh/dot_zshrc rename to home/private_dot_config/zsh/dot_zshrc diff --git a/home/dot_config/zsh/exports.zsh b/home/private_dot_config/zsh/exports.zsh similarity index 100% rename from home/dot_config/zsh/exports.zsh rename to home/private_dot_config/zsh/exports.zsh diff --git a/home/dot_config/zsh/plugins.zsh b/home/private_dot_config/zsh/plugins.zsh similarity index 100% rename from home/dot_config/zsh/plugins.zsh rename to home/private_dot_config/zsh/plugins.zsh From 1fd1d5709054d85a688335db00a8285fd725ea9c Mon Sep 17 00:00:00 2001 From: Edwin Hernandez Date: Fri, 1 May 2026 21:17:01 -0500 Subject: [PATCH 07/23] refactor: Restructure dotfiles management and enhance context handling - Removed GitHub Copilot instructions document to streamline repository. - Updated README to clarify hostname-aware context management for personal and work machines. - Deleted old .chezmoi.toml.tmpl and replaced it with a new .chezmoi.yaml.tmpl for improved configuration. - Consolidated package management into a single packages.yaml file for better organization. - Removed deprecated external plugin configurations and migrated to a new chezmoiexternal.yaml.tmpl. - Added new run_once and run_onchange scripts for Homebrew and mise tool installations. - Cleaned up unnecessary scripts and configurations, ensuring a more efficient setup process. - Updated Git configuration templates to align with new data structure. - Removed obsolete files and configurations to maintain repository cleanliness. --- .github/copilot-instructions.md | 70 ------------- README.md | 10 +- home/.chezmoi.toml.tmpl | 20 ---- home/.chezmoi.yaml.tmpl | 45 +++++++++ home/.chezmoidata/defaults.toml | 8 -- home/.chezmoidata/packages.yaml | 95 ++++++++++++++++++ home/.chezmoiexternal.toml | 19 ---- home/.chezmoiexternal.yaml.tmpl | 3 + home/.chezmoiignore.tmpl | 11 -- .../run_once_01_install-homebrew.sh.tmpl | 22 ++++ .../run_onchange_02_install-packages.sh.tmpl | 51 ++++++++++ ...run_onchange_03_install-mise-tools.sh.tmpl | 20 ++++ ...hange_04_install-vscode-extensions.sh.tmpl | 42 ++++++++ .../run_before_decrypt-private-key.sh.tmpl | 16 --- .../run_onchange_after_brew-bundle.sh.tmpl | 12 --- .../run_onchange_after_macos-defaults.sh.tmpl | 7 -- .../run_onchange_after_mise-install.sh.tmpl | 12 --- .../chezmoiexternal.d/darwin.yaml | 19 ++++ home/encrypted_private_dot_secrets.local.age | 10 -- home/key.txt.age | 10 -- home/private_dot_config/gh/config.yml | 27 ----- home/private_dot_config/git/config.tmpl | 4 +- .../private_dot_config/homebrew/Brewfile.tmpl | 57 ----------- home/private_dot_config/mise/config.toml.tmpl | 29 ++---- ...6 00.55.21.rayconfig => raycast.rayconfig} | Bin .../private_dot_config/vscode/extensions.json | 28 ------ home/private_dot_config/zsh/custom/.gitkeep | 0 scripts/format.sh | 4 +- scripts/install.sh | 77 ++++++-------- scripts/lint.sh | 4 +- 30 files changed, 354 insertions(+), 378 deletions(-) delete mode 100644 .github/copilot-instructions.md delete mode 100644 home/.chezmoi.toml.tmpl create mode 100644 home/.chezmoi.yaml.tmpl delete mode 100644 home/.chezmoidata/defaults.toml create mode 100644 home/.chezmoidata/packages.yaml delete mode 100644 home/.chezmoiexternal.toml create mode 100644 home/.chezmoiexternal.yaml.tmpl delete mode 100644 home/.chezmoiignore.tmpl create mode 100644 home/.chezmoiscripts/darwin/run_once_01_install-homebrew.sh.tmpl create mode 100644 home/.chezmoiscripts/darwin/run_onchange_02_install-packages.sh.tmpl create mode 100644 home/.chezmoiscripts/darwin/run_onchange_03_install-mise-tools.sh.tmpl create mode 100644 home/.chezmoiscripts/darwin/run_onchange_04_install-vscode-extensions.sh.tmpl delete mode 100644 home/.chezmoiscripts/run_before_decrypt-private-key.sh.tmpl delete mode 100644 home/.chezmoiscripts/run_onchange_after_brew-bundle.sh.tmpl delete mode 100644 home/.chezmoiscripts/run_onchange_after_macos-defaults.sh.tmpl delete mode 100644 home/.chezmoiscripts/run_onchange_after_mise-install.sh.tmpl create mode 100644 home/.chezmoitemplates/chezmoiexternal.d/darwin.yaml delete mode 100644 home/encrypted_private_dot_secrets.local.age delete mode 100644 home/key.txt.age delete mode 100644 home/private_dot_config/gh/config.yml delete mode 100644 home/private_dot_config/homebrew/Brewfile.tmpl rename home/private_dot_config/raycast/{Raycast 2026-04-26 00.55.21.rayconfig => raycast.rayconfig} (100%) delete mode 100644 home/private_dot_config/vscode/extensions.json delete mode 100644 home/private_dot_config/zsh/custom/.gitkeep diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md deleted file mode 100644 index 6200960..0000000 --- a/.github/copilot-instructions.md +++ /dev/null @@ -1,70 +0,0 @@ -# GitHub Copilot Instructions for Dotfiles Repository - -This document provides guidelines for using GitHub Copilot effectively within this dotfiles repository. Adhering to these instructions will help maintain consistency, improve code quality, and ensure that generated suggestions align with our project's standards, especially regarding Conventional Commits. - ---- - -## 1. General Best Practices - -- **Be Specific with Prompts:** The more precise your comments and existing code, the better Copilot can understand your intent. Clearly describe what you want to achieve. -- **Review Suggestions Carefully:** Always review Copilot's suggestions before accepting them. Don't blindly accept code; ensure it's correct, efficient, and aligns with your overall goal. -- **Iterate and Refine:** If the initial suggestion isn't perfect, refine your prompt or add more context. Copilot often improves with more specific input. -- **Focus on Small, Incremental Changes:** Try to break down complex tasks into smaller, manageable chunks. This makes it easier for Copilot to provide relevant suggestions and for you to review them. - ---- - -## 2. Conventional Commits Guidelines - -We use [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) for our commit messages. This helps us maintain a clear and consistent commit history, which is crucial for changelog generation and understanding project evolution. - -When using Copilot, pay close attention to the following for commit-related suggestions: - -- **Commit Type:** Start your commit message with a **type**, followed by an optional **scope**, and a colon and space. - - **Common types for dotfiles:** - - `feat`: A new feature or configuration. - - `fix`: A bug fix or correction to an existing configuration. - - `docs`: Documentation only changes. - - `style`: Changes that do not affect the meaning of the code (white-space, formatting, missing semicolons, etc.). - - `refactor`: A code change that neither fixes a bug nor adds a feature (e.g., restructuring files, renaming variables). - - `perf`: A code change that improves performance. - - `test`: Adding missing tests or correcting existing tests. - - `build`: Changes that affect the build system or external dependencies (e.g., `deps`, `npm`). - - `ci`: Changes to our CI configuration files and scripts. - - `chore`: Other changes that don't modify src or test files (e.g., updating grunt tasks, `.gitignore`). - - **Example:** `feat: Add new Zsh aliases` - - **Example with scope:** `fix(vim): Correct keybinding for split window` - -- **Commit Subject:** Follow the type/scope with a **short, imperative, present-tense** description of the change. - - **Bad:** `added new feature` - - **Good:** `feat: Add new feature` - - **Good:** `fix: Correct typo in README` - -- **Commit Body (Optional):** If the change is complex, include a blank line after the subject and then a more detailed explanation in the commit body. - - **Wrap at 72 characters** for readability. - - **Use imperative mood:** "Add feature" not "Added feature". - -- **Breaking Changes (Optional):** For breaking changes, start a paragraph with `BREAKING CHANGE:` followed by a description of the change and justification. This should be in the footer of the commit. - ---- - -## 3. Dotfiles Specific Considerations - -- **Context is Key:** Dotfiles often rely heavily on context from your shell, editor, or other applications. Provide comments that explain the purpose of specific configurations. - ```bash - # Ensure Copilot understands this is for Zsh - # Auto-suggestion plugin configuration - zsh_autosuggestions_config() { - # ... - } - ``` -- **Idempotency:** Many dotfile configurations should be idempotent (running them multiple times has the same effect as running them once). Copilot can help suggest idempotent patterns if you provide the right context. -- **Security:** Be mindful of sensitive information. Dotfiles can sometimes contain API keys or personal data. Ensure Copilot doesn't suggest sensitive information that shouldn't be committed. -- **Cross-Platform Compatibility:** If your dotfiles are meant to be cross-platform, include comments indicating specific OS or environment dependencies. - ---- - -## 4. Troubleshooting & Tips - -- **Copilot not suggesting Conventional Commits?** Try explicitly typing the type (e.g., `feat:`) and Copilot might pick up the pattern. -- **Too many irrelevant suggestions?** Try restarting your editor or the Copilot extension. Sometimes providing more surrounding code context helps. -- **Provide examples:** If you have existing commit messages that follow Conventional Commits, Copilot will learn from them. diff --git a/README.md b/README.md index 008f397..5807097 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # dotfiles-public -Personal macOS dotfiles, managed by [chezmoi](https://www.chezmoi.io/) with a single `context` switch for **work** vs **personal** machines. +Personal macOS dotfiles, managed by [chezmoi](https://www.chezmoi.io/). Hostname-aware **personal** / **work** context with a one-time prompt fallback for unknown machines. ## Install @@ -29,6 +29,14 @@ make compile # validate APM packages `chezmoi re-add` is the most underrated command — it closes the loop when apps (Karabiner, VS Code, etc.) rewrite their own config files in place. +## Machine context + +Known machines auto-classify by `LocalHostName` (`scutil --get LocalHostName` on darwin) — `Edwins-MacBook-Pro` is recognized as personal. Unknown hosts get a one-time prompt cached in `~/.config/chezmoi/chezmoi.toml`, never the repo. The prompt echoes the detected hostname, so onboarding a new machine never requires running `scutil` manually. + +- Onboard another known personal machine: add an `else if eq $hostname "..."` branch in `home/.chezmoi.toml.tmpl`. Look up the current hostname any time with `chezmoi data --format=json | jq -r .hostname`. +- Git name and email are prompted once per machine and cached locally — they never enter this public repo. +- The work hostname is intentionally not hardcoded; work machines fall through to the prompt. + ## Encrypted personal secrets **Personal machines only.** The repo is public, so secrets travel age-encrypted with a passphrase-protected private key. Work machines opt out entirely (see below). diff --git a/home/.chezmoi.toml.tmpl b/home/.chezmoi.toml.tmpl deleted file mode 100644 index babdf19..0000000 --- a/home/.chezmoi.toml.tmpl +++ /dev/null @@ -1,20 +0,0 @@ -{{- $ageRecipient := "age15lke8qvlf7zqs6xmgz2vnm7mul7ws97qy5hv8qs5uqyqj32fs45qmxjuhj" -}} - -{{- $context := promptChoiceOnce . "context" "Which context is this machine?" (list "personal" "work") -}} -sourceDir = {{ .chezmoi.workingTree | quote }} -{{- if and (eq $context "personal") (ne $ageRecipient "") }} - -encryption = "age" - -[age] -identity = "~/.config/chezmoi/key.txt" -recipient = {{ $ageRecipient | quote }} -{{- end }} - -[data] -context = {{ $context | quote }} - -# git -git_username = {{ promptStringOnce "git username" | quote }} -git_email = {{ promptStringOnce "git email" | quote }} -git_signing_key = {{ (bitwardenFields "item" (bitwarden "item" "chezmoi").id).git_signing_key_id.value | quote }} diff --git a/home/.chezmoi.yaml.tmpl b/home/.chezmoi.yaml.tmpl new file mode 100644 index 0000000..7671eaf --- /dev/null +++ b/home/.chezmoi.yaml.tmpl @@ -0,0 +1,45 @@ + +{{/* boolean feature flags */}} +{{- $personal := false -}} +{{- $work := false -}} + +{{/* osID — extended for Linux distros */}} +{{- $osID := .chezmoi.os -}} +{{- if and (eq .chezmoi.os "linux") (hasKey .chezmoi.osRelease "id") -}} +{{- $osID = printf "%s-%s" .chezmoi.os .chezmoi.osRelease.id -}} +{{- end -}} + +{{/* hostname — scutil workaround for darwin */}} +{{- $hostname := .chezmoi.hostname -}} +{{- if eq .chezmoi.os "darwin" -}} +{{- $hostname = output "scutil" "--get" "LocalHostName" | trim -}} +{{- end -}} + +{{/* machine detection */}} +{{- if or (eq $hostname "edwinhern-personal-mac") (eq $hostname "edwinhern-personal-windows") -}} +{{- $personal = true -}} +{{- else if eq $hostname "edwinhern-work-mac" -}} +{{- $work = true -}} +{{- else if stdinIsATTY -}} +{{- $ctx := promptChoiceOnce . "context" (printf "Hostname '%s' unrecognized. Which context is this machine?" $hostname) (list "personal" "work") -}} +{{- if eq $ctx "personal" -}}{{- $personal = true -}}{{- end -}} +{{- if eq $ctx "work" -}}{{- $work = true -}}{{- end -}} +{{- else -}} +{{- $personal = true -}} +{{- end -}} + +data: + hostname: {{ $hostname | quote }} + personal: {{ $personal }} + work: {{ $work }} + osid: {{ $osID | quote }} + + git: + name: {{ promptStringOnce . "git.name" "Git display name" | quote }} + email: {{ promptStringOnce . "git.email" "Git email address" | quote }} + + font: + mono: "Monaspace Argon" + + editor: + default: "nano" \ No newline at end of file diff --git a/home/.chezmoidata/defaults.toml b/home/.chezmoidata/defaults.toml deleted file mode 100644 index a9a1f6a..0000000 --- a/home/.chezmoidata/defaults.toml +++ /dev/null @@ -1,8 +0,0 @@ -[git] -name = "Edwin Hernandez" - -[font] -mono = "Monaspace Argon" - -[editor] -default = "nano" diff --git a/home/.chezmoidata/packages.yaml b/home/.chezmoidata/packages.yaml new file mode 100644 index 0000000..1ceed2f --- /dev/null +++ b/home/.chezmoidata/packages.yaml @@ -0,0 +1,95 @@ +# packages.yaml +# Single source of truth for all package manager declarations. +# Referenced by run_onchange_ scripts via Chezmoi template data (.packages.*). + +packages: + darwin: + homebrew: + shared: + formulas: + - fastfetch + - gh + - mise + - starship + - tmux + casks: + # fonts + - font-monaspace + - font-jetbrains-mono-nerd-font + # apps + - brave-browser + - ghostty + - raycast + - rectangle + - superwhisper + - visual-studio-code + + personal: + formulas: + - mas # Mac App Store CLI + casks: + - aldente + - claude-code + - cleanshot + - discord + - displaylink + - microsoft-excel + - synology-drive + + work: + formulas: [] + casks: [] + + windows: + winget: + shared: + - Brave.Brave + - Discord.Discord + - Microsoft.VisualStudioCode + - Valve.Steam + personal: [] + work: [] + + vscode: + shared: + # core + - dbaeumer.vscode-eslint + - eamodio.gitlens + - EditorConfig.EditorConfig + - esbenp.prettier-vscode + - github.copilot-chat + - usernamehw.errorlens + # ui / dx + - adpyke.codesnap + - alefragnani.project-manager + - edwinhuish.better-comments-next + - fosshaas.fontsize-shortcuts + # Themes + - miguelsolorio.symbols + - raunofreiberg.vesper + # markdown + - shd101wyy.markdown-preview-enhanced + - yzhang.markdown-all-in-one + # typescript + - yoavbls.pretty-ts-errors + + personal: + # python + - charliermarsh.ruff + - ms-python.debugpy + - ms-python.python + - ms-python.vscode-pylance + - ms-python.vscode-python-envs + # frontend + - biomejs.biome + - bradlc.vscode-tailwindcss + - csstools.postcss + # latex / pdf + - james-yu.latex-workshop + - mathematic.vscode-latex + - tomoki1207.pdf + # claude + - anthropic.claude-code + + work: + - codacy-app.codacy diff --git a/home/.chezmoiexternal.toml b/home/.chezmoiexternal.toml deleted file mode 100644 index ab618a7..0000000 --- a/home/.chezmoiexternal.toml +++ /dev/null @@ -1,19 +0,0 @@ -[".config/zsh/plugins/zsh-autosuggestions"] -type = "git-repo" -url = "https://github.com/zsh-users/zsh-autosuggestions.git" -refreshPeriod = "168h" - -[".config/zsh/plugins/zsh-history-substring-search"] -type = "git-repo" -url = "https://github.com/zsh-users/zsh-history-substring-search.git" -refreshPeriod = "168h" - -[".config/zsh/plugins/zsh-syntax-highlighting"] -type = "git-repo" -url = "https://github.com/zsh-users/zsh-syntax-highlighting.git" -refreshPeriod = "168h" - -[".config/zsh/plugins/zsh-transient-prompt"] -type = "git-repo" -url = "https://github.com/olets/zsh-transient-prompt.git" -refreshPeriod = "168h" diff --git a/home/.chezmoiexternal.yaml.tmpl b/home/.chezmoiexternal.yaml.tmpl new file mode 100644 index 0000000..64b4259 --- /dev/null +++ b/home/.chezmoiexternal.yaml.tmpl @@ -0,0 +1,3 @@ +{{ if eq .chezmoi.os "darwin" -}} +{{ template "chezmoiexternal.d/darwin.yaml.tmpl" . }} +{{- end }} \ No newline at end of file diff --git a/home/.chezmoiignore.tmpl b/home/.chezmoiignore.tmpl deleted file mode 100644 index eb490c7..0000000 --- a/home/.chezmoiignore.tmpl +++ /dev/null @@ -1,11 +0,0 @@ -{{/* The encrypted private key lives in source state and is read directly by the - decrypt script via .chezmoi.sourceDir. It must not be applied to ~/. */}} -key.txt.age - -{{/* The age machinery is personal-only. On a work machine, ignore both the - decrypt script and the encrypted secrets file so chezmoi behaves as if - they don't exist in source state. */}} -{{- if ne .context "personal" }} -.secrets.local -decrypt-private-key.sh -{{- end }} diff --git a/home/.chezmoiscripts/darwin/run_once_01_install-homebrew.sh.tmpl b/home/.chezmoiscripts/darwin/run_once_01_install-homebrew.sh.tmpl new file mode 100644 index 0000000..855add3 --- /dev/null +++ b/home/.chezmoiscripts/darwin/run_once_01_install-homebrew.sh.tmpl @@ -0,0 +1,22 @@ +{{- if eq .chezmoi.os "darwin" -}} + +#!/bin/bash +# .chezmoiscripts/darwin/run_once_01_install-homebrew.sh.tmpl +# Installs Homebrew if not present. Runs exactly once per machine. +set -euo pipefail + +if command -v brew &>/dev/null; then + echo "Homebrew already installed, skipping." + exit 0 +fi + +echo "Installing Homebrew..." +/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" + +# Activate brew in current shell (Apple Silicon path) +if [[ -f /opt/homebrew/bin/brew ]]; then + eval "$(/opt/homebrew/bin/brew shellenv)" +fi + +echo "Homebrew installed." +{{- end }} diff --git a/home/.chezmoiscripts/darwin/run_onchange_02_install-packages.sh.tmpl b/home/.chezmoiscripts/darwin/run_onchange_02_install-packages.sh.tmpl new file mode 100644 index 0000000..9307161 --- /dev/null +++ b/home/.chezmoiscripts/darwin/run_onchange_02_install-packages.sh.tmpl @@ -0,0 +1,51 @@ +{{- if eq .chezmoi.os "darwin" -}} + +#!/bin/bash +# .chezmoiscripts/darwin/run_onchange_02_install-packages.sh.tmpl +# Runs brew bundle from packages.yaml data. +# Reruns automatically whenever packages.yaml changes. +# +# packages.yaml hash: {{ include ".chezmoidata/packages.yaml" | sha256sum }} +set -euo pipefail + +if ! command -v brew &>/dev/null; then + echo "Homebrew not found — ensure run_once_01_install-homebrew ran successfully." + exit 1 +fi + +echo "Running brew bundle..." + +brew bundle --no-lock --file=/dev/stdin <<'BREWFILE' +tap "homebrew/cask-fonts" + +# shared formulas and casks +{{ range .packages.darwin.homebrew.shared.formulas -}} +brew "{{ . }}" +{{ end }} +{{ range .packages.darwin.homebrew.shared.casks -}} +cask "{{ . }}" +{{ end }} + +# personal formulas and casks +{{ if .personal -}} +{{ range .packages.darwin.homebrew.personal.formulas -}} +brew "{{ . }}" +{{ end }} +{{ range .packages.darwin.homebrew.personal.casks -}} +cask "{{ . }}" +{{ end }} +{{- end }} + +# work formulas and casks +{{ if .work -}} +{{ range .packages.darwin.homebrew.work.formulas -}} +brew "{{ . }}" +{{ end }} +{{ range .packages.darwin.homebrew.work.casks -}} +cask "{{ . }}" +{{ end }} +{{- end }} +BREWFILE + +echo "Packages installed." +{{- end }} diff --git a/home/.chezmoiscripts/darwin/run_onchange_03_install-mise-tools.sh.tmpl b/home/.chezmoiscripts/darwin/run_onchange_03_install-mise-tools.sh.tmpl new file mode 100644 index 0000000..a26bb11 --- /dev/null +++ b/home/.chezmoiscripts/darwin/run_onchange_03_install-mise-tools.sh.tmpl @@ -0,0 +1,20 @@ +{{- if eq .chezmoi.os "darwin" -}} + +#!/bin/bash +# .chezmoiscripts/darwin/run_onchange_03_install-mise-tools.sh.tmpl +# Runs `mise install` to sync tools declared in ~/.config/mise/config.toml. +# Reruns automatically whenever the mise config template changes. +# +# mise config hash: {{ include "private_dot_config/mise/config.toml.tmpl" | sha256sum }} +set -euo pipefail + +if ! command -v mise &>/dev/null; then + echo "mise not found — ensure run_onchange_02_install-packages ran successfully." + exit 1 +fi + +echo "Installing mise tools..." +mise install --yes + +echo "Mise tools installed." +{{- end }} diff --git a/home/.chezmoiscripts/darwin/run_onchange_04_install-vscode-extensions.sh.tmpl b/home/.chezmoiscripts/darwin/run_onchange_04_install-vscode-extensions.sh.tmpl new file mode 100644 index 0000000..407f03c --- /dev/null +++ b/home/.chezmoiscripts/darwin/run_onchange_04_install-vscode-extensions.sh.tmpl @@ -0,0 +1,42 @@ +{{- if eq .chezmoi.os "darwin" -}} + +#!/bin/bash +# .chezmoiscripts/darwin/run_onchange_04_install-vscode-extensions.sh.tmpl +# Installs VS Code extensions from packages.yaml vscode section. +# Reruns automatically whenever packages.yaml changes. +# +# packages.yaml hash: {{ include ".chezmoidata/packages.yaml" | sha256sum }} +set -euo pipefail + +if ! command -v code &>/dev/null; then + echo "VS Code CLI not found — open VS Code and run: Shell Command: Install 'code' command in PATH" + exit 0 +fi + +echo "Installing VS Code extensions..." + +extensions=( + # shared + {{ range .packages.vscode.shared -}} + "{{ . }}" + {{ end }} + {{ if .personal -}} + # personal + {{ range .packages.vscode.personal -}} + "{{ . }}" + {{ end -}} + {{- end }} + {{ if .work -}} + # work + {{ range .packages.vscode.work -}} + "{{ . }}" + {{ end -}} + {{- end }} +) + +for ext in "${extensions[@]}"; do + [[ -n "$ext" ]] && code --install-extension "$ext" --force +done + +echo "VS Code extensions installed." +{{- end }} diff --git a/home/.chezmoiscripts/run_before_decrypt-private-key.sh.tmpl b/home/.chezmoiscripts/run_before_decrypt-private-key.sh.tmpl deleted file mode 100644 index 300faf6..0000000 --- a/home/.chezmoiscripts/run_before_decrypt-private-key.sh.tmpl +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/sh -# Personal-only: decrypts the age private key on first apply. -# Defense-in-depth: .chezmoiignore.tmpl already excludes this script on -# non-personal contexts, but we also short-circuit here. -{{- if ne .context "personal" }} -exit 0 -{{- end }} - -if [ ! -f "${HOME}/.config/chezmoi/key.txt" ]; then - mkdir -p "${HOME}/.config/chezmoi" - chezmoi age decrypt \ - --output "${HOME}/.config/chezmoi/key.txt" \ - --passphrase \ - "{{ .chezmoi.sourceDir }}/key.txt.age" - chmod 600 "${HOME}/.config/chezmoi/key.txt" -fi diff --git a/home/.chezmoiscripts/run_onchange_after_brew-bundle.sh.tmpl b/home/.chezmoiscripts/run_onchange_after_brew-bundle.sh.tmpl deleted file mode 100644 index a0703d2..0000000 --- a/home/.chezmoiscripts/run_onchange_after_brew-bundle.sh.tmpl +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env bash -# Source-state hash: {{ include "dot_config/homebrew/Brewfile.tmpl" | sha256sum }} -# Re-runs whenever Brewfile.tmpl content changes. - -set -euo pipefail - -if ! command -v brew >/dev/null 2>&1; then - echo "brew not on PATH; skipping brew bundle" >&2 - exit 0 -fi - -brew bundle --file="$HOME/.config/homebrew/Brewfile" diff --git a/home/.chezmoiscripts/run_onchange_after_macos-defaults.sh.tmpl b/home/.chezmoiscripts/run_onchange_after_macos-defaults.sh.tmpl deleted file mode 100644 index 45a1eb6..0000000 --- a/home/.chezmoiscripts/run_onchange_after_macos-defaults.sh.tmpl +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env bash -# macOS build version: {{ output "sw_vers" "--buildVersion" }} -# Re-runs after every macOS update; safe to leave empty until you have defaults to write. - -set -euo pipefail - -echo "macOS build {{ output "sw_vers" "--buildVersion" }} — no defaults configured yet" diff --git a/home/.chezmoiscripts/run_onchange_after_mise-install.sh.tmpl b/home/.chezmoiscripts/run_onchange_after_mise-install.sh.tmpl deleted file mode 100644 index 2bd2690..0000000 --- a/home/.chezmoiscripts/run_onchange_after_mise-install.sh.tmpl +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env bash -# Source-state hash: {{ include "dot_config/mise/config.toml.tmpl" | sha256sum }} -# Re-runs whenever the mise config template content changes. - -set -euo pipefail - -if ! command -v mise >/dev/null 2>&1; then - echo "mise not on PATH; skipping mise install" >&2 - exit 0 -fi - -mise install diff --git a/home/.chezmoitemplates/chezmoiexternal.d/darwin.yaml b/home/.chezmoitemplates/chezmoiexternal.d/darwin.yaml new file mode 100644 index 0000000..954fc0e --- /dev/null +++ b/home/.chezmoitemplates/chezmoiexternal.d/darwin.yaml @@ -0,0 +1,19 @@ +".config/zsh/plugins/zsh-autosuggestions": + type: git-repo + url: https://github.com/zsh-users/zsh-autosuggestions.git + refreshPeriod: 168h + +".config/zsh/plugins/zsh-history-substring-search": + type: git-repo + url: https://github.com/zsh-users/zsh-history-substring-search.git + refreshPeriod: 168h + +".config/zsh/plugins/zsh-syntax-highlighting": + type: git-repo + url: https://github.com/zsh-users/zsh-syntax-highlighting.git + refreshPeriod: 168h + +".config/zsh/plugins/zsh-transient-prompt": + type: git-repo + url: https://github.com/olets/zsh-transient-prompt.git + refreshPeriod: 168h diff --git a/home/encrypted_private_dot_secrets.local.age b/home/encrypted_private_dot_secrets.local.age deleted file mode 100644 index 1494170..0000000 --- a/home/encrypted_private_dot_secrets.local.age +++ /dev/null @@ -1,10 +0,0 @@ ------BEGIN AGE ENCRYPTED FILE----- -YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBaYTV5NWc5N2RwbFdJclV0 -N1JCMUhqR282YlRlbVdqQmVuWkpJV2VvL3djCnNZTXZHNGovcjNQelU4Vlk2TEN4 -SUxUNW9lMk1uUnNsQ2x4VEZ4RzBWQlUKLS0tIHhETE4yL0tYTHFIekVWQnBFN1hv -RGgrODllckNYSTEyYThjZ1hjcmNTdE0Kb3EVaZnWcU4FptGF6D7khuJFRFIF4xk0 -Skf+1k523W/jIGYcCNQKhLCLxC+t6jptCkyXXPzqxcdkLkZEOu0mR9LviCyinDSh -vRO+lNurbgU0Y7UPSokp1ljdCL39d/qdQPuPP622qqe/emXNAJrsuEVUJWloqhdH -mFx6pguN6mV1UrPfMpQskjuM6mX9Q/20S0S03zKEw+5zkEC/AA0tyVLcwY7p3j+g -c5MZDRa/lq93fTH81A== ------END AGE ENCRYPTED FILE----- diff --git a/home/key.txt.age b/home/key.txt.age deleted file mode 100644 index 6dfbd2a..0000000 --- a/home/key.txt.age +++ /dev/null @@ -1,10 +0,0 @@ ------BEGIN AGE ENCRYPTED FILE----- -YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNjcnlwdCAzdlRGR2VzMmtzYW1Fajl4 -MzQzT29BIDE4Cnl1UXVvQU1KSFAzNm1sOWtuS0hrUnV4Mk5YejRoQkVPeDZKVjRh -RFg2ZE0KLS0tIDltZkNzamJUbkZrMllrQWlBTEFPMWkxaW4wUExzZEpTdmVmby84 -SkxJWWMKj8T7JuSZusJEIn460yf1V3XqsFGcmaYGinQw2jshesi56X0zbhHweLsS -F5ASkvi17TQ6rvf87yCtzlkHVuDQZe5O5iq0g/yzlxIlq1qOGHbSIFolNrsSL6hq -Oq6S7c1YDbGvapyKeS9/Ag4howRlbD2uOiikcOge2N/GV4Grgt9PU6BakyRQSBpZ -zajZ33kjYrKEUp/t0sHCayAdtGCOVw0VTB0Qihw50ZYTwUdDRZdestONpcNzNSsv -uXv585oIG84AqlcMZQg4rq6/txWxT4eK9gxxZn2UQuNFWxI= ------END AGE ENCRYPTED FILE----- diff --git a/home/private_dot_config/gh/config.yml b/home/private_dot_config/gh/config.yml deleted file mode 100644 index bb9e9e8..0000000 --- a/home/private_dot_config/gh/config.yml +++ /dev/null @@ -1,27 +0,0 @@ -# The current version of the config schema -version: 1 -# What protocol to use when performing git operations. Supported values: ssh, https -git_protocol: https -# What editor gh should run when creating issues, pull requests, etc. If blank, will refer to environment. -editor: -# When to interactively prompt. This is a global config that cannot be overridden by hostname. Supported values: enabled, disabled -prompt: enabled -# Preference for editor-based interactive prompting. This is a global config that cannot be overridden by hostname. Supported values: enabled, disabled -prefer_editor_prompt: disabled -# A pager program to send command output to, e.g. "less". If blank, will refer to environment. Set the value to "cat" to disable the pager. -pager: -# Aliases allow you to create nicknames for gh commands -aliases: - co: pr checkout -# The path to a unix socket through which to send HTTP connections. If blank, HTTP traffic will be handled by net/http.DefaultTransport. -http_unix_socket: -# What web browser gh should use when opening URLs. If blank, will refer to environment. -browser: -# Whether to display labels using their RGB hex color codes in terminals that support truecolor. Supported values: enabled, disabled -color_labels: disabled -# Whether customizable, 4-bit accessible colors should be used. Supported values: enabled, disabled -accessible_colors: disabled -# Whether an accessible prompter should be used. Supported values: enabled, disabled -accessible_prompter: disabled -# Whether to use a animated spinner as a progress indicator. If disabled, a textual progress indicator is used instead. Supported values: enabled, disabled -spinner: enabled diff --git a/home/private_dot_config/git/config.tmpl b/home/private_dot_config/git/config.tmpl index 6fc79a4..4967678 100644 --- a/home/private_dot_config/git/config.tmpl +++ b/home/private_dot_config/git/config.tmpl @@ -1,6 +1,6 @@ [user] - name = {{ .git_username }} - email = {{ .git_email }} + name = {{ .git.name }} + email = {{ .git.email }} [core] editor = code --wait pager = delta diff --git a/home/private_dot_config/homebrew/Brewfile.tmpl b/home/private_dot_config/homebrew/Brewfile.tmpl deleted file mode 100644 index a45da8f..0000000 --- a/home/private_dot_config/homebrew/Brewfile.tmpl +++ /dev/null @@ -1,57 +0,0 @@ -# vi: ft=ruby -# Shared (all contexts) ---------------------------------------------------- - -# Fonts -cask "font-monaspace" -cask "font-jetbrains-mono-nerd-font" - -# Apps -cask "brave-browser" -cask "ghostty" -cask "raycast" -cask "rectangle" -cask "superwhisper" -cask "visual-studio-code" - -# VS Code extensions -vscode "adpyke.codesnap" -vscode "alefragnani.project-manager" -vscode "edwinhuish.better-comments-next" -vscode "esbenp.prettier-vscode" -vscode "fosshaas.fontsize-shortcuts" -vscode "github.copilot-chat" -vscode "miguelsolorio.symbols" -vscode "raunofreiberg.vesper" -vscode "shd101wyy.markdown-preview-enhanced" -vscode "usernamehw.errorlens" -vscode "yoavbls.pretty-ts-errors" -vscode "yzhang.markdown-all-in-one" - -{{ if eq .context "personal" -}} -# Personal-only ----------------------------------------------------------- -brew "mas" -cask "aldente" -cask "claude-code" -cask "cleanshot" -cask "discord" -cask "displaylink" -cask "microsoft-excel" -cask "synology-drive" - -vscode "anthropic.claude-code" -vscode "biomejs.biome" -vscode "bradlc.vscode-tailwindcss" -vscode "charliermarsh.ruff" -vscode "csstools.postcss" -vscode "ms-python.debugpy" -vscode "ms-python.python" -vscode "ms-python.vscode-pylance" -vscode "ms-python.vscode-python-envs" -vscode "james-yu.latex-workshop" -vscode "mathematic.vscode-latex" -vscode "tomoki1207.pdf" -{{- end }} -{{ if eq .context "work" -}} -# Work-only --------------------------------------------------------------- -# placeholder - add work-specific casks/brews here -{{- end }} diff --git a/home/private_dot_config/mise/config.toml.tmpl b/home/private_dot_config/mise/config.toml.tmpl index 83336ac..a5dec59 100644 --- a/home/private_dot_config/mise/config.toml.tmpl +++ b/home/private_dot_config/mise/config.toml.tmpl @@ -1,23 +1,16 @@ [tools] -chezmoi = 'latest' -node = 'lts' -python = 'latest' -github-cli = 'latest' -tmux = 'latest' -starship = 'latest' -fastfetch = 'latest' -{{ if eq .context "personal" -}} -pnpm = 'latest' -uv = 'latest' -biome = 'latest' +node = "lts" +pnpm = "latest" +python = "latest" + +{{ if .personal -}} +biome = "latest" +uv = "latest" {{- end }} -{{- if eq .context "work" }} -# placeholder - add work-specific tools here + +{{ if .work -}} +redis = "latest" {{- end }} [settings] -# tools can read the versions files used by other version managers -idiomatic_version_file_enable_tools = ['node', 'python', 'pnpm'] - -# config files with these prefixes will be trusted by default -trusted_config_paths = ['~/Documents/github'] +idiomatic_version_file_enable_tools = ["node", "python", "pnpm"] \ No newline at end of file diff --git a/home/private_dot_config/raycast/Raycast 2026-04-26 00.55.21.rayconfig b/home/private_dot_config/raycast/raycast.rayconfig similarity index 100% rename from home/private_dot_config/raycast/Raycast 2026-04-26 00.55.21.rayconfig rename to home/private_dot_config/raycast/raycast.rayconfig diff --git a/home/private_dot_config/vscode/extensions.json b/home/private_dot_config/vscode/extensions.json deleted file mode 100644 index d17bf93..0000000 --- a/home/private_dot_config/vscode/extensions.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "recommendations": [ - "adpyke.codesnap", - "alefragnani.project-manager", - "anthropic.claude-code", - "biomejs.biome", - "bradlc.vscode-tailwindcss", - "charliermarsh.ruff", - "csstools.postcss", - "edwinhuish.better-comments-next", - "esbenp.prettier-vscode", - "fosshaas.fontsize-shortcuts", - "github.copilot-chat", - "james-yu.latex-workshop", - "mathematic.vscode-latex", - "miguelsolorio.symbols", - "ms-python.debugpy", - "ms-python.python", - "ms-python.vscode-pylance", - "ms-python.vscode-python-envs", - "raunofreiberg.vesper", - "shd101wyy.markdown-preview-enhanced", - "tomoki1207.pdf", - "usernamehw.errorlens", - "yoavbls.pretty-ts-errors", - "yzhang.markdown-all-in-one" - ] -} diff --git a/home/private_dot_config/zsh/custom/.gitkeep b/home/private_dot_config/zsh/custom/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/scripts/format.sh b/scripts/format.sh index 350a09a..913117b 100755 --- a/scripts/format.sh +++ b/scripts/format.sh @@ -4,8 +4,8 @@ set -eu printf "* %s\n\n" "Formatting shell scripts..." -# Collect shell targets from scripts/ and home/dot_config/zsh/ -SH_TARGETS=$(find scripts home/dot_config/zsh -type f \( -name '*.sh' -o -name '*.zsh' -o -name '.zshrc' \)) +# Collect shell targets from scripts/ and home/private_dot_config/zsh/ +SH_TARGETS=$(find scripts home/private_dot_config/zsh -type f \( -name '*.sh' -o -name '*.zsh' -o -name '.zshrc' \)) # shellcheck disable=SC2086 mise exec -- shfmt --language-dialect bash --find ${SH_TARGETS} diff --git a/scripts/install.sh b/scripts/install.sh index c015d4d..ac084ed 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -1,47 +1,32 @@ -#!/usr/bin/env bash -# Bootstrap a fresh macOS machine from this dotfiles repo. -# Order: brew -> mise -> chezmoi (via mise) -> chezmoi apply -> mise install -> brew bundle. -# Idempotent: safe to re-run. - -set -euo pipefail - -REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)" - -step() { - printf "\n==> %s\n" "$*" -} - -# 1. Homebrew -------------------------------------------------------------- -if ! command -v brew >/dev/null 2>&1; then - step "Installing Homebrew" - /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" +#!/bin/sh +# install.sh +# Bootstrap script for a fresh macOS machine. +# Installs Chezmoi if absent, then hands off to chezmoi init --apply. +# Chezmoi run_once_ and run_onchange_ scripts handle everything from there. +# +# Usage: +# curl -fsSL https://raw.githubusercontent.com/edwinhern/dotfiles/main/scripts/install.sh | sh +# — or — +# ./scripts/install.sh (from a local clone) + +set -e + +if [ ! "$(command -v chezmoi)" ]; then + bin_dir="$HOME/.local/bin" + chezmoi="$bin_dir/chezmoi" + + if [ "$(command -v curl)" ]; then + sh -c "$(curl -fsSL https://get.chezmoi.io)" -- -b "$bin_dir" + elif [ "$(command -v wget)" ]; then + sh -c "$(wget -qO- https://get.chezmoi.io)" -- -b "$bin_dir" + else + echo "To install chezmoi, you must have curl or wget installed." >&2 + exit 1 + fi +else + chezmoi=chezmoi fi - -# Ensure brew is on PATH for this shell (Apple Silicon path). -if [ -x /opt/homebrew/bin/brew ]; then - eval "$(/opt/homebrew/bin/brew shellenv)" -elif [ -x /usr/local/bin/brew ]; then - eval "$(/usr/local/bin/brew shellenv)" -fi - -# 2. mise ------------------------------------------------------------------ -if ! command -v mise >/dev/null 2>&1; then - step "Installing mise" - brew install mise -fi - -eval "$(mise activate bash --shims)" - -# 3. chezmoi (ephemeral via mise; the managed config will pin it after apply) -step "Running chezmoi init --apply (will prompt for context on first run)" -mise x chezmoi@latest -- chezmoi init --apply --source "${REPO_ROOT}" - -# 4. mise install ---------------------------------------------------------- -step "Installing mise-managed runtimes" -mise install - -# 5. brew bundle ----------------------------------------------------------- -step "Installing apps via brew bundle" -brew bundle --file="${HOME}/.config/homebrew/Brewfile" - -step "Done. Open a new shell to pick up ZDOTDIR/PATH changes." + +# init + apply in one shot. +# Chezmoi will prompt for git.name, git.email, and context if hostname is unrecognized. +exec "$chezmoi" init --apply "gh:edwinhern/dotfiles" \ No newline at end of file diff --git a/scripts/lint.sh b/scripts/lint.sh index e804ce8..82be83e 100755 --- a/scripts/lint.sh +++ b/scripts/lint.sh @@ -9,8 +9,8 @@ mise exec -- prettier --check --ignore-unknown \ printf "* %s\n" "Linting shell scripts..." -# Collect shell targets from scripts/ and home/dot_config/zsh/ -SH_TARGETS=$(find scripts home/dot_config/zsh -type f \( -name '*.sh' -o -name '*.zsh' -o -name '.zshrc' \)) +# Collect shell targets from scripts/ and home/private_dot_config/zsh/ +SH_TARGETS=$(find scripts home/private_dot_config/zsh -type f \( -name '*.sh' -o -name '*.zsh' -o -name '.zshrc' \)) # check format Shell scripts (bash dialect covers posix + zsh constructs). # shellcheck disable=SC2086 From e2a050d2f806384b4a1af893c792b01a8442219c Mon Sep 17 00:00:00 2001 From: Edwin Hernandez Date: Fri, 1 May 2026 21:41:26 -0500 Subject: [PATCH 08/23] feat: add comprehensive dotfiles configuration for various tools and environments --- .../ghostty/config | 0 .../git/config.tmpl | 0 home/{private_dot_config => dot_config}/git/ignore | 0 .../mise/config.toml.tmpl | 0 .../raycast/raycast.rayconfig | Bin .../starship.toml | 0 .../tmux/tmux.conf | 0 .../zsh/aliases.zsh | 0 .../zsh/dot_zprofile.tmpl | 0 .../zsh/dot_zshrc | 0 .../zsh/exports.zsh | 0 .../zsh/plugins.zsh | 0 scripts/install.sh | 10 +++++----- 13 files changed, 5 insertions(+), 5 deletions(-) rename home/{private_dot_config => dot_config}/ghostty/config (100%) rename home/{private_dot_config => dot_config}/git/config.tmpl (100%) rename home/{private_dot_config => dot_config}/git/ignore (100%) rename home/{private_dot_config => dot_config}/mise/config.toml.tmpl (100%) rename home/{private_dot_config => dot_config}/raycast/raycast.rayconfig (100%) rename home/{private_dot_config => dot_config}/starship.toml (100%) rename home/{private_dot_config => dot_config}/tmux/tmux.conf (100%) rename home/{private_dot_config => dot_config}/zsh/aliases.zsh (100%) rename home/{private_dot_config => dot_config}/zsh/dot_zprofile.tmpl (100%) rename home/{private_dot_config => dot_config}/zsh/dot_zshrc (100%) rename home/{private_dot_config => dot_config}/zsh/exports.zsh (100%) rename home/{private_dot_config => dot_config}/zsh/plugins.zsh (100%) diff --git a/home/private_dot_config/ghostty/config b/home/dot_config/ghostty/config similarity index 100% rename from home/private_dot_config/ghostty/config rename to home/dot_config/ghostty/config diff --git a/home/private_dot_config/git/config.tmpl b/home/dot_config/git/config.tmpl similarity index 100% rename from home/private_dot_config/git/config.tmpl rename to home/dot_config/git/config.tmpl diff --git a/home/private_dot_config/git/ignore b/home/dot_config/git/ignore similarity index 100% rename from home/private_dot_config/git/ignore rename to home/dot_config/git/ignore diff --git a/home/private_dot_config/mise/config.toml.tmpl b/home/dot_config/mise/config.toml.tmpl similarity index 100% rename from home/private_dot_config/mise/config.toml.tmpl rename to home/dot_config/mise/config.toml.tmpl diff --git a/home/private_dot_config/raycast/raycast.rayconfig b/home/dot_config/raycast/raycast.rayconfig similarity index 100% rename from home/private_dot_config/raycast/raycast.rayconfig rename to home/dot_config/raycast/raycast.rayconfig diff --git a/home/private_dot_config/starship.toml b/home/dot_config/starship.toml similarity index 100% rename from home/private_dot_config/starship.toml rename to home/dot_config/starship.toml diff --git a/home/private_dot_config/tmux/tmux.conf b/home/dot_config/tmux/tmux.conf similarity index 100% rename from home/private_dot_config/tmux/tmux.conf rename to home/dot_config/tmux/tmux.conf diff --git a/home/private_dot_config/zsh/aliases.zsh b/home/dot_config/zsh/aliases.zsh similarity index 100% rename from home/private_dot_config/zsh/aliases.zsh rename to home/dot_config/zsh/aliases.zsh diff --git a/home/private_dot_config/zsh/dot_zprofile.tmpl b/home/dot_config/zsh/dot_zprofile.tmpl similarity index 100% rename from home/private_dot_config/zsh/dot_zprofile.tmpl rename to home/dot_config/zsh/dot_zprofile.tmpl diff --git a/home/private_dot_config/zsh/dot_zshrc b/home/dot_config/zsh/dot_zshrc similarity index 100% rename from home/private_dot_config/zsh/dot_zshrc rename to home/dot_config/zsh/dot_zshrc diff --git a/home/private_dot_config/zsh/exports.zsh b/home/dot_config/zsh/exports.zsh similarity index 100% rename from home/private_dot_config/zsh/exports.zsh rename to home/dot_config/zsh/exports.zsh diff --git a/home/private_dot_config/zsh/plugins.zsh b/home/dot_config/zsh/plugins.zsh similarity index 100% rename from home/private_dot_config/zsh/plugins.zsh rename to home/dot_config/zsh/plugins.zsh diff --git a/scripts/install.sh b/scripts/install.sh index ac084ed..d4ea73d 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -8,13 +8,13 @@ # curl -fsSL https://raw.githubusercontent.com/edwinhern/dotfiles/main/scripts/install.sh | sh # — or — # ./scripts/install.sh (from a local clone) - + set -e - + if [ ! "$(command -v chezmoi)" ]; then bin_dir="$HOME/.local/bin" chezmoi="$bin_dir/chezmoi" - + if [ "$(command -v curl)" ]; then sh -c "$(curl -fsSL https://get.chezmoi.io)" -- -b "$bin_dir" elif [ "$(command -v wget)" ]; then @@ -26,7 +26,7 @@ if [ ! "$(command -v chezmoi)" ]; then else chezmoi=chezmoi fi - + # init + apply in one shot. # Chezmoi will prompt for git.name, git.email, and context if hostname is unrecognized. -exec "$chezmoi" init --apply "gh:edwinhern/dotfiles" \ No newline at end of file +exec "$chezmoi" init --apply "gh:edwinhern/dotfiles" From 48838e066083025d6f7ddc51c7796b9b4cfb130e Mon Sep 17 00:00:00 2001 From: Edwin Hernandez Date: Fri, 1 May 2026 22:01:55 -0500 Subject: [PATCH 09/23] feat: migrate AGENTS.md content to CLAUDE.md and update mise.toml for chezmoi version Co-authored-by: Copilot --- AGENT.md | 29 ----------------------------- CLAUDE.md | 30 +++++++++++++++++++++++++++++- mise.toml | 1 + 3 files changed, 30 insertions(+), 30 deletions(-) delete mode 100644 AGENT.md diff --git a/AGENT.md b/AGENT.md deleted file mode 100644 index 452c92b..0000000 --- a/AGENT.md +++ /dev/null @@ -1,29 +0,0 @@ -# AGENTS.md - -## Repository Context - -- This repository is managed with [`chezmoi`](https://www.chezmoi.io/) ([GitHub](https://github.com/twpayne/chezmoi)). -- Files under `home/` are the public source state and are applied by `chezmoi` into the user's `$HOME` directory. -- Private dotfiles are managed separately from `~/.local/share/chezmoi-private` with config at `~/.config/chezmoi-private/chezmoi.yaml`. -- Treat the public `home/` tree and the private `chezmoi` source/config as separate management domains. - -## Response Rule - -- After reading this `AGENTS.md`, say: `🤖 I read the AGENTS.md for shunk031/dotfiles.` - -## Comment Policy - -- When adding or updating comments for shell scripts or shell-based executables, always write them in English using shdoc-compatible format. - -## Git / PR Workflow - -- When you are asked to create a branch, commit, or pull request and the current worktree contains unrelated staged, unstaged, or untracked changes, prefer creating a separate `git worktree` from the default branch. -- In that separate `git worktree`, apply only the changes relevant to the current task and do not mix unrelated changes into the branch or pull request. -- Only prioritize the current branch or worktree when the user explicitly asks you to work there. -- After pushing to GitHub, always check the GitHub Actions CI results. If CI fails, investigate the failure, fix the issue, push again, and repeat until all CI checks pass. -- Always write pull request titles and descriptions in English. - -## Test Policy - -- Do not run `bats` tests locally. -- When you need to validate `bats` results, push to GitHub, let GitHub Actions CI run, and check the results there. diff --git a/CLAUDE.md b/CLAUDE.md index 43c994c..452c92b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1 +1,29 @@ -@AGENTS.md +# AGENTS.md + +## Repository Context + +- This repository is managed with [`chezmoi`](https://www.chezmoi.io/) ([GitHub](https://github.com/twpayne/chezmoi)). +- Files under `home/` are the public source state and are applied by `chezmoi` into the user's `$HOME` directory. +- Private dotfiles are managed separately from `~/.local/share/chezmoi-private` with config at `~/.config/chezmoi-private/chezmoi.yaml`. +- Treat the public `home/` tree and the private `chezmoi` source/config as separate management domains. + +## Response Rule + +- After reading this `AGENTS.md`, say: `🤖 I read the AGENTS.md for shunk031/dotfiles.` + +## Comment Policy + +- When adding or updating comments for shell scripts or shell-based executables, always write them in English using shdoc-compatible format. + +## Git / PR Workflow + +- When you are asked to create a branch, commit, or pull request and the current worktree contains unrelated staged, unstaged, or untracked changes, prefer creating a separate `git worktree` from the default branch. +- In that separate `git worktree`, apply only the changes relevant to the current task and do not mix unrelated changes into the branch or pull request. +- Only prioritize the current branch or worktree when the user explicitly asks you to work there. +- After pushing to GitHub, always check the GitHub Actions CI results. If CI fails, investigate the failure, fix the issue, push again, and repeat until all CI checks pass. +- Always write pull request titles and descriptions in English. + +## Test Policy + +- Do not run `bats` tests locally. +- When you need to validate `bats` results, push to GitHub, let GitHub Actions CI run, and check the results there. diff --git a/mise.toml b/mise.toml index afe3c02..36f5495 100644 --- a/mise.toml +++ b/mise.toml @@ -1,4 +1,5 @@ [tools] +chezmoi = "2.0.0" apm = "0.8.11" prettier = "3.8.3" shellcheck = "0.11.0" From 88b4a9e1b6129a58b8d51a6c1b029e57ffe30fa9 Mon Sep 17 00:00:00 2001 From: Edwin Hernandez Date: Fri, 1 May 2026 22:17:24 -0500 Subject: [PATCH 10/23] fix: update path for shell targets in format and lint scripts --- scripts/format.sh | 4 ++-- scripts/lint.sh | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/format.sh b/scripts/format.sh index 913117b..350a09a 100755 --- a/scripts/format.sh +++ b/scripts/format.sh @@ -4,8 +4,8 @@ set -eu printf "* %s\n\n" "Formatting shell scripts..." -# Collect shell targets from scripts/ and home/private_dot_config/zsh/ -SH_TARGETS=$(find scripts home/private_dot_config/zsh -type f \( -name '*.sh' -o -name '*.zsh' -o -name '.zshrc' \)) +# Collect shell targets from scripts/ and home/dot_config/zsh/ +SH_TARGETS=$(find scripts home/dot_config/zsh -type f \( -name '*.sh' -o -name '*.zsh' -o -name '.zshrc' \)) # shellcheck disable=SC2086 mise exec -- shfmt --language-dialect bash --find ${SH_TARGETS} diff --git a/scripts/lint.sh b/scripts/lint.sh index 82be83e..e804ce8 100755 --- a/scripts/lint.sh +++ b/scripts/lint.sh @@ -9,8 +9,8 @@ mise exec -- prettier --check --ignore-unknown \ printf "* %s\n" "Linting shell scripts..." -# Collect shell targets from scripts/ and home/private_dot_config/zsh/ -SH_TARGETS=$(find scripts home/private_dot_config/zsh -type f \( -name '*.sh' -o -name '*.zsh' -o -name '.zshrc' \)) +# Collect shell targets from scripts/ and home/dot_config/zsh/ +SH_TARGETS=$(find scripts home/dot_config/zsh -type f \( -name '*.sh' -o -name '*.zsh' -o -name '.zshrc' \)) # check format Shell scripts (bash dialect covers posix + zsh constructs). # shellcheck disable=SC2086 From 0d6957a97d1d76a051452b09280e533ac59967e3 Mon Sep 17 00:00:00 2001 From: Edwin Hernandez Date: Fri, 1 May 2026 22:40:34 -0500 Subject: [PATCH 11/23] fix: clean up stale paths, docs, and script consistency (#3) - run_onchange_03_install-mise-tools.sh.tmpl: fix include path (private_dot_config -> dot_config) so the hash trigger actually re-runs when the mise config changes. - run_onchange_02_install-packages.sh.tmpl: drop the deprecated homebrew/cask-fonts tap (fonts moved into homebrew/cask in 2024). - CLAUDE.md: update the title and response rule to reference edwinhern/dotfiles-public; remove the bats Test Policy section (no bats files exist in the repo). - README.md: rewrite the install + apply paragraphs to match what scripts/install.sh and the run_onchange_* scripts actually do (no Brewfile.tmpl, no mise x chezmoi); fix the work-secrets paragraph to describe the actual mechanism (no .chezmoiignore.tmpl exists). - makefile: invoke scripts directly instead of via sh/bash so each script's shebang is respected and the targets are consistent. - scripts/install.sh: standardize shebang on /usr/bin/env sh to match the other scripts. --- CLAUDE.md | 9 ++------- README.md | 6 +++--- .../darwin/run_onchange_02_install-packages.sh.tmpl | 2 -- .../darwin/run_onchange_03_install-mise-tools.sh.tmpl | 2 +- makefile | 8 ++++---- scripts/install.sh | 2 +- 6 files changed, 11 insertions(+), 18 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 452c92b..3dd2d92 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,4 +1,4 @@ -# AGENTS.md +# CLAUDE.md ## Repository Context @@ -9,7 +9,7 @@ ## Response Rule -- After reading this `AGENTS.md`, say: `🤖 I read the AGENTS.md for shunk031/dotfiles.` +- After reading this `CLAUDE.md`, say: `🤖 I read the CLAUDE.md for edwinhern/dotfiles-public.` ## Comment Policy @@ -22,8 +22,3 @@ - Only prioritize the current branch or worktree when the user explicitly asks you to work there. - After pushing to GitHub, always check the GitHub Actions CI results. If CI fails, investigate the failure, fix the issue, push again, and repeat until all CI checks pass. - Always write pull request titles and descriptions in English. - -## Test Policy - -- Do not run `bats` tests locally. -- When you need to validate `bats` results, push to GitHub, let GitHub Actions CI run, and check the results there. diff --git a/README.md b/README.md index 5807097..990ebb7 100644 --- a/README.md +++ b/README.md @@ -10,9 +10,9 @@ cd ~/Documents/github/dotfiles-public make install ``` -`make install` is idempotent. It installs Homebrew → `brew install mise` → uses `mise x chezmoi` ephemerally to run `chezmoi init --apply` → renders templates → `mise install` → `brew bundle`. +`make install` is idempotent. It runs `scripts/install.sh`, which installs `chezmoi` via `https://get.chezmoi.io` if absent, then hands off to `chezmoi init --apply gh:edwinhern/dotfiles`. From there, `home/.chezmoiscripts/darwin/run_once_*` and `run_onchange_*` scripts install Homebrew, run `brew bundle`, run `mise install`, and install VS Code extensions. -After the first apply, `chezmoi apply` is self-completing: editing `Brewfile.tmpl` triggers `brew bundle` automatically, editing `mise/config.toml.tmpl` triggers `mise install`, and a macOS update triggers any `defaults write` commands you've added (see `home/run_onchange_after_*.sh.tmpl`). +After the first apply, `chezmoi apply` is self-completing: editing `home/.chezmoidata/packages.yaml` triggers `brew bundle` and the VS Code extension sync, and editing `home/dot_config/mise/config.toml.tmpl` triggers `mise install`. Each `run_onchange_*` script embeds a `sha256sum` of the file it watches, so chezmoi reruns it whenever that hash changes. ## Common commands @@ -87,7 +87,7 @@ git commit -m "feat: enable age-encrypted personal secrets" ## Work machine secrets -**Maintain `~/.secrets.local` by hand on the work machine.** Never commit it. The repo's encrypted personal blob is ignored on work via `home/.chezmoiignore.tmpl`, so the work machine never sees personal tokens. zsh's existing `[[ -f ~/.secrets.local ]] && source ~/.secrets.local` line picks up whatever you put there. +**Maintain `~/.secrets.local` by hand on the work machine.** Never commit it. The work context never decrypts the repo's encrypted personal blob (no private key is materialized), so the work machine never sees personal tokens. zsh's existing `[[ -f ~/.secrets.local ]] && source ~/.secrets.local` line picks up whatever you put there. ## References diff --git a/home/.chezmoiscripts/darwin/run_onchange_02_install-packages.sh.tmpl b/home/.chezmoiscripts/darwin/run_onchange_02_install-packages.sh.tmpl index 9307161..1c56367 100644 --- a/home/.chezmoiscripts/darwin/run_onchange_02_install-packages.sh.tmpl +++ b/home/.chezmoiscripts/darwin/run_onchange_02_install-packages.sh.tmpl @@ -16,8 +16,6 @@ fi echo "Running brew bundle..." brew bundle --no-lock --file=/dev/stdin <<'BREWFILE' -tap "homebrew/cask-fonts" - # shared formulas and casks {{ range .packages.darwin.homebrew.shared.formulas -}} brew "{{ . }}" diff --git a/home/.chezmoiscripts/darwin/run_onchange_03_install-mise-tools.sh.tmpl b/home/.chezmoiscripts/darwin/run_onchange_03_install-mise-tools.sh.tmpl index a26bb11..2c0ec6a 100644 --- a/home/.chezmoiscripts/darwin/run_onchange_03_install-mise-tools.sh.tmpl +++ b/home/.chezmoiscripts/darwin/run_onchange_03_install-mise-tools.sh.tmpl @@ -5,7 +5,7 @@ # Runs `mise install` to sync tools declared in ~/.config/mise/config.toml. # Reruns automatically whenever the mise config template changes. # -# mise config hash: {{ include "private_dot_config/mise/config.toml.tmpl" | sha256sum }} +# mise config hash: {{ include "dot_config/mise/config.toml.tmpl" | sha256sum }} set -euo pipefail if ! command -v mise &>/dev/null; then diff --git a/makefile b/makefile index e5cd49d..04fbfcb 100644 --- a/makefile +++ b/makefile @@ -1,17 +1,17 @@ fmt: - sh scripts/format.sh + ./scripts/format.sh .PHONY: fmt lint: - sh scripts/lint.sh + ./scripts/lint.sh .PHONY: lint compile: - sh scripts/compile.sh + ./scripts/compile.sh .PHONY: compile install: - bash scripts/install.sh + ./scripts/install.sh .PHONY: install apply: diff --git a/scripts/install.sh b/scripts/install.sh index d4ea73d..fdd129b 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env sh # install.sh # Bootstrap script for a fresh macOS machine. # Installs Chezmoi if absent, then hands off to chezmoi init --apply. From dec151de30af2295a63f4124dce3a3796c890180 Mon Sep 17 00:00:00 2001 From: Edwin Hernandez Date: Fri, 1 May 2026 22:49:41 -0500 Subject: [PATCH 12/23] docs: drop age-encryption sections from README (#4) The age-encryption framework was removed in 1fd1d57 (refactor: Restructure dotfiles management). The README still described the old setup in detail, including a "Work machine secrets" section that referenced a non-existent home/.chezmoiignore.tmpl. Replace both sections with a short "Local secrets" note that matches what the repo actually does: each machine maintains its own ~/.secrets.local by hand and zsh sources it from home/dot_config/zsh/exports.zsh. Drop the chezmoi encryption FAQ link from References since it's no longer relevant. --- README.md | 53 ++--------------------------------------------------- 1 file changed, 2 insertions(+), 51 deletions(-) diff --git a/README.md b/README.md index 990ebb7..8204dda 100644 --- a/README.md +++ b/README.md @@ -37,61 +37,12 @@ Known machines auto-classify by `LocalHostName` (`scutil --get LocalHostName` on - Git name and email are prompted once per machine and cached locally — they never enter this public repo. - The work hostname is intentionally not hardcoded; work machines fall through to the prompt. -## Encrypted personal secrets +## Local secrets -**Personal machines only.** The repo is public, so secrets travel age-encrypted with a passphrase-protected private key. Work machines opt out entirely (see below). - -**One-time setup** (run from the repo root — `cd` in first): - -The piped form `chezmoi age-keygen | chezmoi age encrypt --passphrase --output=...` hides the public-key line on some chezmoi versions, leaving you with no recipient to paste. Use the two-step form below instead — it keeps the public key visible. - -```sh -# 1. Generate keypair to a plaintext temp file (so the public key is visible). -TMPKEY=$(mktemp -t agekey-XXXXX) -chezmoi age-keygen --output="$TMPKEY" - -# 2. Show the public key — the line starting "# public key:". Copy it. -grep '^# public key:' "$TMPKEY" -# → e.g. # public key: age1xxxxx... - -# 3. Encrypt the keypair file with a passphrase, save into the repo. -chezmoi age encrypt --passphrase --output=home/key.txt.age "$TMPKEY" - -# 4. Shred the plaintext intermediate. -rm -P "$TMPKEY" 2>/dev/null || rm -f "$TMPKEY" - -# 5. Open home/.chezmoi.toml.tmpl. Replace the empty recipient with your key: -# {{- $ageRecipient := "age1xxx..." -}} - -# 6. Re-init so chezmoi.toml picks up sourceDir + the encryption block. -# --source is required on this first run only; afterwards sourceDir is -# baked into ~/.config/chezmoi/chezmoi.toml. -chezmoi init --prompt --source "$(pwd)" - -# 7. Author the encrypted secrets file directly — no plaintext on disk. -TMP=$(mktemp -t secrets-XXXXX) -nano "$TMP" # paste: export GITHUB_PERSONAL_ACCESS_TOKEN="...", etc. -chezmoi encrypt --output home/encrypted_private_dot_secrets.local.age "$TMP" -rm -P "$TMP" 2>/dev/null || rm -f "$TMP" - -# 8. Apply — prompts for passphrase once, then materializes ~/.secrets.local. -chezmoi apply -cat ~/.secrets.local # sanity check - -# 9. Commit and push. -git add home/key.txt.age home/encrypted_private_dot_secrets.local.age home/.chezmoi.toml.tmpl -git commit -m "feat: enable age-encrypted personal secrets" -``` - -**On any other personal Mac:** clone the repo, run `make install`, type the passphrase once when prompted. `~/.secrets.local` materializes automatically, sourced by zsh. - -## Work machine secrets - -**Maintain `~/.secrets.local` by hand on the work machine.** Never commit it. The work context never decrypts the repo's encrypted personal blob (no private key is materialized), so the work machine never sees personal tokens. zsh's existing `[[ -f ~/.secrets.local ]] && source ~/.secrets.local` line picks up whatever you put there. +Secrets stay out of the repo. Each machine maintains its own `~/.secrets.local` (e.g. `export GITHUB_PERSONAL_ACCESS_TOKEN="…"`), and zsh sources it on shell start via the `[[ -f ~/.secrets.local ]] && source ~/.secrets.local` line in `home/dot_config/zsh/exports.zsh`. Never commit this file. ## References - [chezmoi](https://www.chezmoi.io/) — dotfile manager -- [chezmoi encryption FAQ](https://www.chezmoi.io/user-guide/frequently-asked-questions/encryption/) — first-time keygen pattern reference - [chezmoi macOS guide](https://www.chezmoi.io/user-guide/machines/macos/) — `sw_vers` + `defaults write` patterns - [mise](https://mise.jdx.dev/) — runtime version manager From b8908a2562c494b44bb59a1562cd0051baf74850 Mon Sep 17 00:00:00 2001 From: Edwin Hernandez Date: Fri, 1 May 2026 23:29:42 -0500 Subject: [PATCH 13/23] =?UTF-8?q?chore:=20tighten=20config=20=E2=80=94=20d?= =?UTF-8?q?rop=20dead=20code,=20fix=20doc=20drift,=20widen=20lint=20covera?= =?UTF-8?q?ge=20(#5)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Applies a few principle-driven cleanups: YAGNI — repo is macOS-only: - home/.chezmoi.yaml.tmpl: drop the `edwinhern-personal-windows` branch from machine detection. There are no Windows scripts under home/.chezmoiscripts/ to act on a Windows context. - home/.chezmoidata/packages.yaml: drop the `windows.winget` block for the same reason — no script ever reads it. Dead template data: - home/.chezmoi.yaml.tmpl: drop `font.mono` and `editor.default` — no template references either field. Pointless template: - Rename home/dot_zshenv.tmpl -> home/dot_zshenv. The file contains only `export ZDOTDIR="$HOME/.config/zsh"` with zero template syntax; the .tmpl suffix forces a needless render pass. Deprecated flag: - home/.chezmoiscripts/darwin/run_onchange_02_install-packages.sh.tmpl: drop `--no-lock` from `brew bundle`. Homebrew Bundle 4 removed lockfiles entirely; the flag is a no-op. Documentation drift: - README.md: `Edwins-MacBook-Pro` -> `edwinhern-personal-mac` to match the actual hostname check, and `chezmoi.toml.tmpl` -> `chezmoi.yaml.tmpl` to match the file that actually exists. Coverage gap: - scripts/lint.sh, scripts/format.sh: prettier glob extended to `**/*.yaml`. The repo has several .yaml files (packages.yaml, dependabot.yaml, the chezmoiexternal partial) that the .yml-only glob silently skipped. --- README.md | 4 ++-- home/.chezmoi.yaml.tmpl | 10 ++-------- home/.chezmoidata/packages.yaml | 10 ---------- .../darwin/run_onchange_02_install-packages.sh.tmpl | 2 +- home/{dot_zshenv.tmpl => dot_zshenv} | 0 scripts/format.sh | 3 ++- scripts/lint.sh | 3 ++- 7 files changed, 9 insertions(+), 23 deletions(-) rename home/{dot_zshenv.tmpl => dot_zshenv} (100%) diff --git a/README.md b/README.md index 8204dda..f7f8551 100644 --- a/README.md +++ b/README.md @@ -31,9 +31,9 @@ make compile # validate APM packages ## Machine context -Known machines auto-classify by `LocalHostName` (`scutil --get LocalHostName` on darwin) — `Edwins-MacBook-Pro` is recognized as personal. Unknown hosts get a one-time prompt cached in `~/.config/chezmoi/chezmoi.toml`, never the repo. The prompt echoes the detected hostname, so onboarding a new machine never requires running `scutil` manually. +Known machines auto-classify by `LocalHostName` (`scutil --get LocalHostName` on darwin) — `edwinhern-personal-mac` is recognized as personal. Unknown hosts get a one-time prompt cached in `~/.config/chezmoi/chezmoi.toml`, never the repo. The prompt echoes the detected hostname, so onboarding a new machine never requires running `scutil` manually. -- Onboard another known personal machine: add an `else if eq $hostname "..."` branch in `home/.chezmoi.toml.tmpl`. Look up the current hostname any time with `chezmoi data --format=json | jq -r .hostname`. +- Onboard another known personal machine: add an `else if eq $hostname "..."` branch in `home/.chezmoi.yaml.tmpl`. Look up the current hostname any time with `chezmoi data --format=json | jq -r .hostname`. - Git name and email are prompted once per machine and cached locally — they never enter this public repo. - The work hostname is intentionally not hardcoded; work machines fall through to the prompt. diff --git a/home/.chezmoi.yaml.tmpl b/home/.chezmoi.yaml.tmpl index 7671eaf..cf99260 100644 --- a/home/.chezmoi.yaml.tmpl +++ b/home/.chezmoi.yaml.tmpl @@ -16,7 +16,7 @@ {{- end -}} {{/* machine detection */}} -{{- if or (eq $hostname "edwinhern-personal-mac") (eq $hostname "edwinhern-personal-windows") -}} +{{- if eq $hostname "edwinhern-personal-mac" -}} {{- $personal = true -}} {{- else if eq $hostname "edwinhern-work-mac" -}} {{- $work = true -}} @@ -36,10 +36,4 @@ data: git: name: {{ promptStringOnce . "git.name" "Git display name" | quote }} - email: {{ promptStringOnce . "git.email" "Git email address" | quote }} - - font: - mono: "Monaspace Argon" - - editor: - default: "nano" \ No newline at end of file + email: {{ promptStringOnce . "git.email" "Git email address" | quote }} \ No newline at end of file diff --git a/home/.chezmoidata/packages.yaml b/home/.chezmoidata/packages.yaml index 1ceed2f..ef010c9 100644 --- a/home/.chezmoidata/packages.yaml +++ b/home/.chezmoidata/packages.yaml @@ -40,16 +40,6 @@ packages: formulas: [] casks: [] - windows: - winget: - shared: - - Brave.Brave - - Discord.Discord - - Microsoft.VisualStudioCode - - Valve.Steam - personal: [] - work: [] - vscode: shared: # core diff --git a/home/.chezmoiscripts/darwin/run_onchange_02_install-packages.sh.tmpl b/home/.chezmoiscripts/darwin/run_onchange_02_install-packages.sh.tmpl index 1c56367..7f894a7 100644 --- a/home/.chezmoiscripts/darwin/run_onchange_02_install-packages.sh.tmpl +++ b/home/.chezmoiscripts/darwin/run_onchange_02_install-packages.sh.tmpl @@ -15,7 +15,7 @@ fi echo "Running brew bundle..." -brew bundle --no-lock --file=/dev/stdin <<'BREWFILE' +brew bundle --file=/dev/stdin <<'BREWFILE' # shared formulas and casks {{ range .packages.darwin.homebrew.shared.formulas -}} brew "{{ . }}" diff --git a/home/dot_zshenv.tmpl b/home/dot_zshenv similarity index 100% rename from home/dot_zshenv.tmpl rename to home/dot_zshenv diff --git a/scripts/format.sh b/scripts/format.sh index 350a09a..caf56bb 100755 --- a/scripts/format.sh +++ b/scripts/format.sh @@ -17,7 +17,8 @@ printf "\n* %s\n\n" "Formatting markdown and YAML..." # format Markdown and YAML files. mise exec -- prettier --write --ignore-unknown \ "**/*.md" \ - "**/*.yml" + "**/*.yml" \ + "**/*.yaml" printf "\n* %s\n\n" "Formatting TOML..." diff --git a/scripts/lint.sh b/scripts/lint.sh index e804ce8..a7bfe37 100755 --- a/scripts/lint.sh +++ b/scripts/lint.sh @@ -5,7 +5,8 @@ set -eu # check format Markdown and YAML files. mise exec -- prettier --check --ignore-unknown \ "**/*.md" \ - "**/*.yml" + "**/*.yml" \ + "**/*.yaml" printf "* %s\n" "Linting shell scripts..." From 89bda4c6ef4be77d54cee10b96e1cbf98ef3ce16 Mon Sep 17 00:00:00 2001 From: Edwin Hernandez Date: Fri, 1 May 2026 23:32:23 -0500 Subject: [PATCH 14/23] feat: add CI workflow and APM configuration files for personal, business, and development packages Co-authored-by: Copilot --- .github/workflows/{ci.yml => ci.yaml} | 0 apm.yml => apm.yaml | 0 packages/business/{apm.yml => apm.yaml} | 0 packages/development/{apm.yml => apm.yaml} | 0 scripts/compile.sh | 4 ++-- scripts/format.sh | 1 - scripts/lint.sh | 1 - 7 files changed, 2 insertions(+), 4 deletions(-) rename .github/workflows/{ci.yml => ci.yaml} (100%) rename apm.yml => apm.yaml (100%) rename packages/business/{apm.yml => apm.yaml} (100%) rename packages/development/{apm.yml => apm.yaml} (100%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yaml similarity index 100% rename from .github/workflows/ci.yml rename to .github/workflows/ci.yaml diff --git a/apm.yml b/apm.yaml similarity index 100% rename from apm.yml rename to apm.yaml diff --git a/packages/business/apm.yml b/packages/business/apm.yaml similarity index 100% rename from packages/business/apm.yml rename to packages/business/apm.yaml diff --git a/packages/development/apm.yml b/packages/development/apm.yaml similarity index 100% rename from packages/development/apm.yml rename to packages/development/apm.yaml diff --git a/scripts/compile.sh b/scripts/compile.sh index c2fa90b..e68f29f 100755 --- a/scripts/compile.sh +++ b/scripts/compile.sh @@ -4,9 +4,9 @@ set -eu printf "* %s\n\n" "Compiling APM packages..." -# Find all directories in packages/ that contain apm.yml +# Find all directories in packages/ that contain apm.yaml for package_dir in packages/*/; do - if [ -f "${package_dir}apm.yml" ]; then + if [ -f "${package_dir}apm.yaml" ]; then package_name=$(basename "$package_dir") printf "Compiling package: %s\n" "$package_name" diff --git a/scripts/format.sh b/scripts/format.sh index caf56bb..f04d808 100755 --- a/scripts/format.sh +++ b/scripts/format.sh @@ -17,7 +17,6 @@ printf "\n* %s\n\n" "Formatting markdown and YAML..." # format Markdown and YAML files. mise exec -- prettier --write --ignore-unknown \ "**/*.md" \ - "**/*.yml" \ "**/*.yaml" printf "\n* %s\n\n" "Formatting TOML..." diff --git a/scripts/lint.sh b/scripts/lint.sh index a7bfe37..8961ab7 100755 --- a/scripts/lint.sh +++ b/scripts/lint.sh @@ -5,7 +5,6 @@ set -eu # check format Markdown and YAML files. mise exec -- prettier --check --ignore-unknown \ "**/*.md" \ - "**/*.yml" \ "**/*.yaml" printf "* %s\n" "Linting shell scripts..." From c1500ae279dc13a91af4bdf487c8192affb7c60a Mon Sep 17 00:00:00 2001 From: Edwin Hernandez Date: Sat, 2 May 2026 00:13:34 -0500 Subject: [PATCH 15/23] feat: add vscode extensions configuration and macOS defaults script Co-authored-by: Copilot --- home/.chezmoidata/extensions.yaml | 43 ++++++++++ home/.chezmoidata/packages.yaml | 44 ----------- ...hange_04_install-vscode-extensions.sh.tmpl | 8 +- .../run_onchange_after_10-defaults.sh.tmpl | 78 +++++++++++++++++++ 4 files changed, 125 insertions(+), 48 deletions(-) create mode 100644 home/.chezmoidata/extensions.yaml create mode 100644 home/.chezmoiscripts/darwin/run_onchange_after_10-defaults.sh.tmpl diff --git a/home/.chezmoidata/extensions.yaml b/home/.chezmoidata/extensions.yaml new file mode 100644 index 0000000..8db28e4 --- /dev/null +++ b/home/.chezmoidata/extensions.yaml @@ -0,0 +1,43 @@ +vscode: + shared: + # core + - dbaeumer.vscode-eslint + - eamodio.gitlens + - EditorConfig.EditorConfig + - esbenp.prettier-vscode + - github.copilot-chat + - usernamehw.errorlens + # ui / dx + - adpyke.codesnap + - alefragnani.project-manager + - edwinhuish.better-comments-next + - fosshaas.fontsize-shortcuts + # Themes + - miguelsolorio.symbols + - raunofreiberg.vesper + # markdown + - shd101wyy.markdown-preview-enhanced + - yzhang.markdown-all-in-one + # typescript + - yoavbls.pretty-ts-errors + + personal: + # python + - charliermarsh.ruff + - ms-python.debugpy + - ms-python.python + - ms-python.vscode-pylance + - ms-python.vscode-python-envs + # frontend + - biomejs.biome + - bradlc.vscode-tailwindcss + - csstools.postcss + # latex / pdf + - james-yu.latex-workshop + - mathematic.vscode-latex + - tomoki1207.pdf + # claude + - anthropic.claude-code + + work: + - codacy-app.codacy diff --git a/home/.chezmoidata/packages.yaml b/home/.chezmoidata/packages.yaml index ef010c9..618d904 100644 --- a/home/.chezmoidata/packages.yaml +++ b/home/.chezmoidata/packages.yaml @@ -39,47 +39,3 @@ packages: work: formulas: [] casks: [] - - vscode: - shared: - # core - - dbaeumer.vscode-eslint - - eamodio.gitlens - - EditorConfig.EditorConfig - - esbenp.prettier-vscode - - github.copilot-chat - - usernamehw.errorlens - # ui / dx - - adpyke.codesnap - - alefragnani.project-manager - - edwinhuish.better-comments-next - - fosshaas.fontsize-shortcuts - # Themes - - miguelsolorio.symbols - - raunofreiberg.vesper - # markdown - - shd101wyy.markdown-preview-enhanced - - yzhang.markdown-all-in-one - # typescript - - yoavbls.pretty-ts-errors - - personal: - # python - - charliermarsh.ruff - - ms-python.debugpy - - ms-python.python - - ms-python.vscode-pylance - - ms-python.vscode-python-envs - # frontend - - biomejs.biome - - bradlc.vscode-tailwindcss - - csstools.postcss - # latex / pdf - - james-yu.latex-workshop - - mathematic.vscode-latex - - tomoki1207.pdf - # claude - - anthropic.claude-code - - work: - - codacy-app.codacy diff --git a/home/.chezmoiscripts/darwin/run_onchange_04_install-vscode-extensions.sh.tmpl b/home/.chezmoiscripts/darwin/run_onchange_04_install-vscode-extensions.sh.tmpl index 407f03c..465061f 100644 --- a/home/.chezmoiscripts/darwin/run_onchange_04_install-vscode-extensions.sh.tmpl +++ b/home/.chezmoiscripts/darwin/run_onchange_04_install-vscode-extensions.sh.tmpl @@ -5,7 +5,7 @@ # Installs VS Code extensions from packages.yaml vscode section. # Reruns automatically whenever packages.yaml changes. # -# packages.yaml hash: {{ include ".chezmoidata/packages.yaml" | sha256sum }} +# packages.yaml hash: {{ include ".chezmoidata/extensions.yaml" | sha256sum }} set -euo pipefail if ! command -v code &>/dev/null; then @@ -17,18 +17,18 @@ echo "Installing VS Code extensions..." extensions=( # shared - {{ range .packages.vscode.shared -}} + {{ range .vscode.shared -}} "{{ . }}" {{ end }} {{ if .personal -}} # personal - {{ range .packages.vscode.personal -}} + {{ range .vscode.personal -}} "{{ . }}" {{ end -}} {{- end }} {{ if .work -}} # work - {{ range .packages.vscode.work -}} + {{ range .vscode.work -}} "{{ . }}" {{ end -}} {{- end }} diff --git a/home/.chezmoiscripts/darwin/run_onchange_after_10-defaults.sh.tmpl b/home/.chezmoiscripts/darwin/run_onchange_after_10-defaults.sh.tmpl new file mode 100644 index 0000000..19342b2 --- /dev/null +++ b/home/.chezmoiscripts/darwin/run_onchange_after_10-defaults.sh.tmpl @@ -0,0 +1,78 @@ +{{- if eq .chezmoi.os "darwin" -}} + +#!/bin/bash +# .chezmoiscripts/darwin/run_onchange_after_10-defaults.sh.tmpl +# Sets default preferences for macOS. +# Reruns automatically whenever defaults.yaml changes. +set -euo pipefail + +osascript -e 'tell application "System Preferences" to quit' + +# Finder +## Show all filename extensions +defaults write NSGlobalDomain AppleShowAllExtensions -bool true +## Dont save to iCloud by default +defaults write NSGlobalDomain NSDocumentSaveNewDocumentsToCloud -bool false +## Show all files +defaults write com.apple.finder AppleShowAllFiles -bool true +## Show path bar +defaults write com.apple.finder ShowPathbar -bool true +## Show tab bar +defaults write com.apple.finder ShowTabView -bool true +## Show status bar +defaults write com.apple.finder ShowStatusBar -bool true +## Set default view to list +defaults write com.apple.finder FXPreferredViewStyle -string "Nlsv" +## Dont display warning when changing a file extension +defaults write com.apple.finder FXEnableExtensionChangeWarning -bool false +## Show absolute path in title bar +defaults write com.apple.finder _FXShowPosixPathInTitle -bool true +## Show folder on top +defaults write com.apple.finder _FXSortFoldersFirst -bool true +## Dont show desktop icons +defaults write com.apple.finder CreateDesktop -bool false +killall Finder + + +# Dock +## +defaults write com.apple.dock orientation -string bottom +## +defaults write com.apple.dock launchanim -bool false +## +defaults write com.apple.dock tilesize -int 36 +## +defaults write com.apple.dock autohide -bool true +## +defaults write com.apple.dock autohide-time-modifier -float 0 +## +defaults write com.apple.dock autohide-delay -float 0 +## +defaults write com.apple.dock show-recents -bool false +## remove all icons +defaults write com.apple.dock persistent-apps '()' +killall Dock + +# Screenshots +## +defaults write com.apple.screencapture "disable-shadow" -bool "true" +## +mkdir -p ~/Pictures/Screenshots && defaults write com.apple.screencapture "location" -string "~/Pictures/Screenshots" +killall SystemUIServer +## +defaults write com.apple.screencapture "show-thumbnail" -bool "false" + +# Disable chime on boot +sudo nvram StartupMute=%01 + +# Disable the crash reporter +defaults write com.apple.CrashReporter DialogType none +# +defaults write com.apple.LaunchServices LSQuarantine -bool false +# +defaults write com.apple.LaunchServices LSQuarantine -bool false +# +defaults write -g AppleShowScrollBars -string Always + +open -a "Brave Browser" --args --make-default-browser +{{- end }} \ No newline at end of file From 1935e679d9ba329328bb0ea74bee08ba5547cb72 Mon Sep 17 00:00:00 2001 From: Edwin Hernandez Date: Sat, 2 May 2026 18:45:42 -0500 Subject: [PATCH 16/23] docs: update README for clarity and remove obsolete sections --- README.md | 30 +++++------------------------- 1 file changed, 5 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index f7f8551..aeed052 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,11 @@ -# dotfiles-public +# github.com/edwinhern/dotfiles -Personal macOS dotfiles, managed by [chezmoi](https://www.chezmoi.io/). Hostname-aware **personal** / **work** context with a one-time prompt fallback for unknown machines. +Edwin's dotfiles, managed with [`chezmoi`](https://github.com/twpayne/chezmoi). -## Install +Install them with: -```sh -git clone https://github.com/edwinhern/dotfiles-public.git ~/Documents/github/dotfiles-public -cd ~/Documents/github/dotfiles-public -make install +```console +$ chezmoi init edwinhern ``` `make install` is idempotent. It runs `scripts/install.sh`, which installs `chezmoi` via `https://get.chezmoi.io` if absent, then hands off to `chezmoi init --apply gh:edwinhern/dotfiles`. From there, `home/.chezmoiscripts/darwin/run_once_*` and `run_onchange_*` scripts install Homebrew, run `brew bundle`, run `mise install`, and install VS Code extensions. @@ -28,21 +26,3 @@ make compile # validate APM packages ``` `chezmoi re-add` is the most underrated command — it closes the loop when apps (Karabiner, VS Code, etc.) rewrite their own config files in place. - -## Machine context - -Known machines auto-classify by `LocalHostName` (`scutil --get LocalHostName` on darwin) — `edwinhern-personal-mac` is recognized as personal. Unknown hosts get a one-time prompt cached in `~/.config/chezmoi/chezmoi.toml`, never the repo. The prompt echoes the detected hostname, so onboarding a new machine never requires running `scutil` manually. - -- Onboard another known personal machine: add an `else if eq $hostname "..."` branch in `home/.chezmoi.yaml.tmpl`. Look up the current hostname any time with `chezmoi data --format=json | jq -r .hostname`. -- Git name and email are prompted once per machine and cached locally — they never enter this public repo. -- The work hostname is intentionally not hardcoded; work machines fall through to the prompt. - -## Local secrets - -Secrets stay out of the repo. Each machine maintains its own `~/.secrets.local` (e.g. `export GITHUB_PERSONAL_ACCESS_TOKEN="…"`), and zsh sources it on shell start via the `[[ -f ~/.secrets.local ]] && source ~/.secrets.local` line in `home/dot_config/zsh/exports.zsh`. Never commit this file. - -## References - -- [chezmoi](https://www.chezmoi.io/) — dotfile manager -- [chezmoi macOS guide](https://www.chezmoi.io/user-guide/machines/macos/) — `sw_vers` + `defaults write` patterns -- [mise](https://mise.jdx.dev/) — runtime version manager From 87ea73cb612356fc5cf375f7a9a8c77142329d98 Mon Sep 17 00:00:00 2001 From: Edwin Hernandez Date: Wed, 6 May 2026 20:43:20 -0500 Subject: [PATCH 17/23] feat: add macOS defaults + tmux mouse/vi-keys; fix silent template bug MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit run_onchange_05_defaults.sh.tmpl (renamed from run_onchange_after_10-defaults.sh.tmpl to slot into the existing 02/03/04 sequence): - Sets Finder, Dock, Screenshots, and misc system defaults. - Dock pinned apps now declarative via dockutil — added dockutil to packages.yaml shared formulas. Each app entry is guarded by `[ -d "$app" ]` so a missing app silently skips instead of crashing the apply. - Removed `sudo nvram StartupMute=%01` (would block unattended apply on machines without TouchID-for-sudo). - Brave default-browser check now actually works — the previous draft captured awk's empty stdout into a variable and ran it as a command, so the guard was always a no-op. Pipeline goes directly into `if !`. - Screenshot location uses "$HOME/..." instead of literal "~/..." (defaults doesn't expand tilde inside quotes). - Deduped a double LSQuarantine write. - Dropped empty `## ` placeholder comments per CLAUDE.md's shdoc-compatible-English-comments rule. - Header comment fixed to reference the new filename. home/.chezmoiexternal.yaml.tmpl: - {{ template "chezmoiexternal.d/darwin.yaml.tmpl" . }} → {{ template "chezmoiexternal.d/darwin.yaml" . }}. The partial in .chezmoitemplates/ is registered by its on-disk name (darwin.yaml), not its target name. The .tmpl-suffixed invocation has been silently failing — chezmoi reported `template "chezmoiexternal.d/darwin.yaml.tmpl" not defined` on every apply, which means the zsh plugin externals (autosuggestions, syntax-highlighting, etc.) haven't been refreshed by chezmoi for an unknown stretch. home/dot_config/tmux/tmux.conf: - Enable mouse mode and vi-style copy-mode keybindings. --- home/.chezmoidata/packages.yaml | 1 + home/.chezmoiexternal.yaml.tmpl | 2 +- .../darwin/run_onchange_05_defaults.sh.tmpl | 74 ++++++++++++++++++ .../run_onchange_after_10-defaults.sh.tmpl | 78 ------------------- home/dot_config/tmux/tmux.conf | 4 + 5 files changed, 80 insertions(+), 79 deletions(-) create mode 100644 home/.chezmoiscripts/darwin/run_onchange_05_defaults.sh.tmpl delete mode 100644 home/.chezmoiscripts/darwin/run_onchange_after_10-defaults.sh.tmpl diff --git a/home/.chezmoidata/packages.yaml b/home/.chezmoidata/packages.yaml index 618d904..718e480 100644 --- a/home/.chezmoidata/packages.yaml +++ b/home/.chezmoidata/packages.yaml @@ -7,6 +7,7 @@ packages: homebrew: shared: formulas: + - dockutil - fastfetch - gh - mise diff --git a/home/.chezmoiexternal.yaml.tmpl b/home/.chezmoiexternal.yaml.tmpl index 64b4259..b9526f2 100644 --- a/home/.chezmoiexternal.yaml.tmpl +++ b/home/.chezmoiexternal.yaml.tmpl @@ -1,3 +1,3 @@ {{ if eq .chezmoi.os "darwin" -}} -{{ template "chezmoiexternal.d/darwin.yaml.tmpl" . }} +{{ template "chezmoiexternal.d/darwin.yaml" . }} {{- end }} \ No newline at end of file diff --git a/home/.chezmoiscripts/darwin/run_onchange_05_defaults.sh.tmpl b/home/.chezmoiscripts/darwin/run_onchange_05_defaults.sh.tmpl new file mode 100644 index 0000000..de58dc4 --- /dev/null +++ b/home/.chezmoiscripts/darwin/run_onchange_05_defaults.sh.tmpl @@ -0,0 +1,74 @@ +{{- if eq .chezmoi.os "darwin" -}} + +#!/bin/bash +# .chezmoiscripts/darwin/run_onchange_05_defaults.sh.tmpl +# Sets default preferences for macOS. +# Reruns automatically whenever this script's content changes. +set -euo pipefail + +osascript -e 'tell application "System Settings" to quit' + +# Finder +defaults write NSGlobalDomain AppleShowAllExtensions -bool true +defaults write NSGlobalDomain NSDocumentSaveNewDocumentsToCloud -bool false +defaults write com.apple.finder AppleShowAllFiles -bool true +defaults write com.apple.finder ShowPathbar -bool true +defaults write com.apple.finder ShowTabView -bool true +defaults write com.apple.finder ShowStatusBar -bool true +defaults write com.apple.finder FXPreferredViewStyle -string "Nlsv" +defaults write com.apple.finder FXEnableExtensionChangeWarning -bool false +defaults write com.apple.finder _FXShowPosixPathInTitle -bool true +defaults write com.apple.finder _FXSortFoldersFirst -bool true +defaults write com.apple.finder CreateDesktop -bool false +killall Finder + +# Dock — behavior +defaults write com.apple.dock orientation -string bottom +defaults write com.apple.dock launchanim -bool false +defaults write com.apple.dock tilesize -int 36 +defaults write com.apple.dock autohide -bool true +defaults write com.apple.dock autohide-time-modifier -float 0 +defaults write com.apple.dock autohide-delay -float 0 +defaults write com.apple.dock show-recents -bool false + +# Dock — pinned apps (declarative via dockutil; missing apps are skipped) +if command -v dockutil >/dev/null 2>&1; then + dockutil --no-restart --remove all >/dev/null 2>&1 || true + for app in \ + "/System/Applications/System Settings.app" \ + "/System/Applications/Utilities/Activity Monitor.app" \ + "/Applications/Brave Browser.app" \ + "/Applications/Visual Studio Code.app" \ + "/Applications/Ghostty.app" \ + "/Applications/Discord.app" \ + "/System/Applications/Music.app" \ + "/System/Applications/Mail.app" \ + "/System/Applications/Notes.app" \ + "/System/Applications/Reminders.app" \ + "/Applications/CleanShot X.app" \ + "/Applications/superwhisper.app"; do + [ -d "$app" ] && dockutil --no-restart --add "$app" >/dev/null + done +else + echo "dockutil not found — skipping dock pinning. Run 'brew install dockutil' or rerun 'chezmoi apply'." >&2 +fi +killall Dock + +# Screenshots +defaults write com.apple.screencapture disable-shadow -bool true +mkdir -p "$HOME/Pictures/Screenshots" +defaults write com.apple.screencapture location -string "$HOME/Pictures/Screenshots" +defaults write com.apple.screencapture show-thumbnail -bool false +killall SystemUIServer + +# Misc +defaults write com.apple.CrashReporter DialogType none +defaults write com.apple.LaunchServices LSQuarantine -bool false +defaults write -g AppleShowScrollBars -string Always + +# Default browser — Brave (only set if not already, avoids macOS confirmation popup) +if ! defaults read com.apple.LaunchServices/com.apple.launchservices.secure 2>/dev/null \ + | grep -q '"LSHandlerRoleAll" = "com\.brave\.browser"'; then + open -a "Brave Browser" --args --make-default-browser +fi +{{- end }} diff --git a/home/.chezmoiscripts/darwin/run_onchange_after_10-defaults.sh.tmpl b/home/.chezmoiscripts/darwin/run_onchange_after_10-defaults.sh.tmpl deleted file mode 100644 index 19342b2..0000000 --- a/home/.chezmoiscripts/darwin/run_onchange_after_10-defaults.sh.tmpl +++ /dev/null @@ -1,78 +0,0 @@ -{{- if eq .chezmoi.os "darwin" -}} - -#!/bin/bash -# .chezmoiscripts/darwin/run_onchange_after_10-defaults.sh.tmpl -# Sets default preferences for macOS. -# Reruns automatically whenever defaults.yaml changes. -set -euo pipefail - -osascript -e 'tell application "System Preferences" to quit' - -# Finder -## Show all filename extensions -defaults write NSGlobalDomain AppleShowAllExtensions -bool true -## Dont save to iCloud by default -defaults write NSGlobalDomain NSDocumentSaveNewDocumentsToCloud -bool false -## Show all files -defaults write com.apple.finder AppleShowAllFiles -bool true -## Show path bar -defaults write com.apple.finder ShowPathbar -bool true -## Show tab bar -defaults write com.apple.finder ShowTabView -bool true -## Show status bar -defaults write com.apple.finder ShowStatusBar -bool true -## Set default view to list -defaults write com.apple.finder FXPreferredViewStyle -string "Nlsv" -## Dont display warning when changing a file extension -defaults write com.apple.finder FXEnableExtensionChangeWarning -bool false -## Show absolute path in title bar -defaults write com.apple.finder _FXShowPosixPathInTitle -bool true -## Show folder on top -defaults write com.apple.finder _FXSortFoldersFirst -bool true -## Dont show desktop icons -defaults write com.apple.finder CreateDesktop -bool false -killall Finder - - -# Dock -## -defaults write com.apple.dock orientation -string bottom -## -defaults write com.apple.dock launchanim -bool false -## -defaults write com.apple.dock tilesize -int 36 -## -defaults write com.apple.dock autohide -bool true -## -defaults write com.apple.dock autohide-time-modifier -float 0 -## -defaults write com.apple.dock autohide-delay -float 0 -## -defaults write com.apple.dock show-recents -bool false -## remove all icons -defaults write com.apple.dock persistent-apps '()' -killall Dock - -# Screenshots -## -defaults write com.apple.screencapture "disable-shadow" -bool "true" -## -mkdir -p ~/Pictures/Screenshots && defaults write com.apple.screencapture "location" -string "~/Pictures/Screenshots" -killall SystemUIServer -## -defaults write com.apple.screencapture "show-thumbnail" -bool "false" - -# Disable chime on boot -sudo nvram StartupMute=%01 - -# Disable the crash reporter -defaults write com.apple.CrashReporter DialogType none -# -defaults write com.apple.LaunchServices LSQuarantine -bool false -# -defaults write com.apple.LaunchServices LSQuarantine -bool false -# -defaults write -g AppleShowScrollBars -string Always - -open -a "Brave Browser" --args --make-default-browser -{{- end }} \ No newline at end of file diff --git a/home/dot_config/tmux/tmux.conf b/home/dot_config/tmux/tmux.conf index db406c6..eb9a3df 100644 --- a/home/dot_config/tmux/tmux.conf +++ b/home/dot_config/tmux/tmux.conf @@ -1 +1,5 @@ # tmux configuration + +set -g mouse on + +setw -g mode-keys vi \ No newline at end of file From d163ac1025ee35b1fe36eee9cf148cdfa3b75d1a Mon Sep 17 00:00:00 2001 From: Edwin Hernandez Date: Wed, 6 May 2026 20:51:13 -0500 Subject: [PATCH 18/23] feat: add voiceink to the list of applications in packages.yaml --- home/.chezmoidata/packages.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/home/.chezmoidata/packages.yaml b/home/.chezmoidata/packages.yaml index 718e480..6c2fb8f 100644 --- a/home/.chezmoidata/packages.yaml +++ b/home/.chezmoidata/packages.yaml @@ -24,6 +24,7 @@ packages: - rectangle - superwhisper - visual-studio-code + - voiceink personal: formulas: From 9b215e8ddd65ddbcbc5e95c0d6b919eac634d88c Mon Sep 17 00:00:00 2001 From: Edwin Hernandez Date: Wed, 6 May 2026 21:03:33 -0500 Subject: [PATCH 19/23] fix(mise): bump chezmoi pin from v2.0.0 to v2.70.2 The "2.0.0" pin was added thinking it was a stable major version, but chezmoi v2.0.0 is the very first 2.x release from March 2021. It predates `stdinIsATTY` (added in v2.16.0, July 2022), which home/.chezmoi.yaml.tmpl depends on for non-interactive fallback. When mise activates this project's tool versions, `chezmoi init` runs the pinned v2.0.0 binary and fails with: template: chezmoi.yaml:23: function "stdinIsATTY" not defined Pin to v2.70.2 to match what the system already has installed and what we've validated against in this branch. Also incorporate the staged doc-comment fix in run_onchange_04_install-vscode-extensions.sh.tmpl (header now references extensions.yaml, matching the include path). --- .../run_onchange_04_install-vscode-extensions.sh.tmpl | 6 +++--- mise.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/home/.chezmoiscripts/darwin/run_onchange_04_install-vscode-extensions.sh.tmpl b/home/.chezmoiscripts/darwin/run_onchange_04_install-vscode-extensions.sh.tmpl index 465061f..6bedf73 100644 --- a/home/.chezmoiscripts/darwin/run_onchange_04_install-vscode-extensions.sh.tmpl +++ b/home/.chezmoiscripts/darwin/run_onchange_04_install-vscode-extensions.sh.tmpl @@ -2,10 +2,10 @@ #!/bin/bash # .chezmoiscripts/darwin/run_onchange_04_install-vscode-extensions.sh.tmpl -# Installs VS Code extensions from packages.yaml vscode section. -# Reruns automatically whenever packages.yaml changes. +# Installs VS Code extensions from extensions.yaml vscode section. +# Reruns automatically whenever extensions.yaml changes. # -# packages.yaml hash: {{ include ".chezmoidata/extensions.yaml" | sha256sum }} +# extensions.yaml hash: {{ include ".chezmoidata/extensions.yaml" | sha256sum }} set -euo pipefail if ! command -v code &>/dev/null; then diff --git a/mise.toml b/mise.toml index 36f5495..54c9ce8 100644 --- a/mise.toml +++ b/mise.toml @@ -1,5 +1,5 @@ [tools] -chezmoi = "2.0.0" +chezmoi = "2.70.2" apm = "0.8.11" prettier = "3.8.3" shellcheck = "0.11.0" From 1231966ab50e238d818dbcd878bac80a461ed01e Mon Sep 17 00:00:00 2001 From: Edwin Hernandez Date: Wed, 6 May 2026 21:13:13 -0500 Subject: [PATCH 20/23] fix(vscode): drop built-in copilot-chat; tolerate per-extension failures VS Code 1.95+ ships GitHub Copilot Chat as a built-in extension (currently v0.46.2 on this machine). The marketplace version is older (v0.45.1), so `code --install-extension github.copilot-chat` errors with "cannot be downgraded" and aborts the whole apply under `set -euo pipefail`, preventing run_onchange_05_defaults from running. Two changes: - extensions.yaml: drop github.copilot-chat from shared extensions. It's installed automatically with VS Code now and managed via VS Code's update channel, not the marketplace install path. - run_onchange_04_install-vscode-extensions.sh.tmpl: catch per-extension failures into a `failed` array and log them at the end instead of letting one bad extension halt apply. Future cases (a marketplace listing yanked, a corporate policy block, etc.) will warn but not break the apply chain. --- home/.chezmoidata/extensions.yaml | 1 - ...n_onchange_04_install-vscode-extensions.sh.tmpl | 14 +++++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/home/.chezmoidata/extensions.yaml b/home/.chezmoidata/extensions.yaml index 8db28e4..7f0cb73 100644 --- a/home/.chezmoidata/extensions.yaml +++ b/home/.chezmoidata/extensions.yaml @@ -5,7 +5,6 @@ vscode: - eamodio.gitlens - EditorConfig.EditorConfig - esbenp.prettier-vscode - - github.copilot-chat - usernamehw.errorlens # ui / dx - adpyke.codesnap diff --git a/home/.chezmoiscripts/darwin/run_onchange_04_install-vscode-extensions.sh.tmpl b/home/.chezmoiscripts/darwin/run_onchange_04_install-vscode-extensions.sh.tmpl index 6bedf73..0b8ca87 100644 --- a/home/.chezmoiscripts/darwin/run_onchange_04_install-vscode-extensions.sh.tmpl +++ b/home/.chezmoiscripts/darwin/run_onchange_04_install-vscode-extensions.sh.tmpl @@ -34,9 +34,21 @@ extensions=( {{- end }} ) +# Install best-effort. A built-in / pre-release extension may refuse to be +# replaced by an older marketplace version — we log and continue rather than +# aborting the whole apply. +failed=() for ext in "${extensions[@]}"; do - [[ -n "$ext" ]] && code --install-extension "$ext" --force + [[ -z "$ext" ]] && continue + if ! code --install-extension "$ext" --force; then + failed+=("$ext") + fi done +if [[ ${#failed[@]} -gt 0 ]]; then + echo "WARNING: ${#failed[@]} extension(s) failed to install:" >&2 + printf ' - %s\n' "${failed[@]}" >&2 +fi + echo "VS Code extensions installed." {{- end }} From 4a29dd1ff4fabbd0e12de80cff0c862541202f0e Mon Sep 17 00:00:00 2001 From: Edwin Hernandez Date: Wed, 6 May 2026 21:23:11 -0500 Subject: [PATCH 21/23] fix(dock): update dock tile size and autohide timing settings --- home/.chezmoiscripts/darwin/run_onchange_05_defaults.sh.tmpl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/home/.chezmoiscripts/darwin/run_onchange_05_defaults.sh.tmpl b/home/.chezmoiscripts/darwin/run_onchange_05_defaults.sh.tmpl index de58dc4..3578a0b 100644 --- a/home/.chezmoiscripts/darwin/run_onchange_05_defaults.sh.tmpl +++ b/home/.chezmoiscripts/darwin/run_onchange_05_defaults.sh.tmpl @@ -25,9 +25,9 @@ killall Finder # Dock — behavior defaults write com.apple.dock orientation -string bottom defaults write com.apple.dock launchanim -bool false -defaults write com.apple.dock tilesize -int 36 +defaults write com.apple.dock tilesize -int 42 defaults write com.apple.dock autohide -bool true -defaults write com.apple.dock autohide-time-modifier -float 0 +defaults write com.apple.dock autohide-time-modifier -float 0.5 defaults write com.apple.dock autohide-delay -float 0 defaults write com.apple.dock show-recents -bool false From 59d07dc6281475ee523c51a6b002dd72a1e8ccde Mon Sep 17 00:00:00 2001 From: Edwin Hernandez Date: Thu, 7 May 2026 21:40:16 -0500 Subject: [PATCH 22/23] fix(mise): update apm version from 0.8.11 to 0.12.2 --- mise.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mise.toml b/mise.toml index 54c9ce8..17d5b00 100644 --- a/mise.toml +++ b/mise.toml @@ -1,6 +1,6 @@ [tools] chezmoi = "2.70.2" -apm = "0.8.11" +apm = "0.12.2" prettier = "3.8.3" shellcheck = "0.11.0" shfmt = "3.12.0" From 1bedbd6edaefdc0f84e87f050514cab880a2d9ab Mon Sep 17 00:00:00 2001 From: Edwin Hernandez Date: Thu, 7 May 2026 21:45:40 -0500 Subject: [PATCH 23/23] fix(apm): update apm version to 0.12.4 and add business and development configurations --- mise.toml | 2 +- packages/business/{apm.yaml => apm.yml} | 0 packages/development/{apm.yaml => apm.yml} | 0 scripts/compile.sh | 4 ++-- 4 files changed, 3 insertions(+), 3 deletions(-) rename packages/business/{apm.yaml => apm.yml} (100%) rename packages/development/{apm.yaml => apm.yml} (100%) diff --git a/mise.toml b/mise.toml index 17d5b00..a9f3500 100644 --- a/mise.toml +++ b/mise.toml @@ -1,6 +1,6 @@ [tools] chezmoi = "2.70.2" -apm = "0.12.2" +apm = "0.12.4" prettier = "3.8.3" shellcheck = "0.11.0" shfmt = "3.12.0" diff --git a/packages/business/apm.yaml b/packages/business/apm.yml similarity index 100% rename from packages/business/apm.yaml rename to packages/business/apm.yml diff --git a/packages/development/apm.yaml b/packages/development/apm.yml similarity index 100% rename from packages/development/apm.yaml rename to packages/development/apm.yml diff --git a/scripts/compile.sh b/scripts/compile.sh index e68f29f..c2fa90b 100755 --- a/scripts/compile.sh +++ b/scripts/compile.sh @@ -4,9 +4,9 @@ set -eu printf "* %s\n\n" "Compiling APM packages..." -# Find all directories in packages/ that contain apm.yaml +# Find all directories in packages/ that contain apm.yml for package_dir in packages/*/; do - if [ -f "${package_dir}apm.yaml" ]; then + if [ -f "${package_dir}apm.yml" ]; then package_name=$(basename "$package_dir") printf "Compiling package: %s\n" "$package_name"