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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 24 additions & 4 deletions .chezmoi.toml.tmpl
Original file line number Diff line number Diff line change
@@ -1,9 +1,28 @@
{{- $remote := or (env "CODESPACES" | not | not) (env "SSH_CONNECTION" | not | not) (env "KUBERNETES_SERVICE_HOST" | not | not) (env "container" | not | not) (env "REMOTE_CONTAINERS" | not | not) (stat "/.dockerenv" | not | not) -}}
{{- $remote := or (env "CODESPACES" | not | not) (env "SSH_CONNECTION" | not | not) (env "KUBERNETES_SERVICE_HOST" | not | not) (env "container" | not | not) (env "REMOTE_CONTAINERS" | not | not) (env "DEVPOD" | not | not) (stat "/.dockerenv" | not | not) -}}
sourceDir = {{ .chezmoi.sourceDir | quote }}

{{ $name := promptStringOnce . "name" "Git user.name" }}
{{ $email := promptStringOnce . "email" "Git user.email" }}
{{ $gpg_key := promptStringOnce . "gpg_signing_key" "GPG key fingerprint" }}
{{- /* Collect configuration values */ -}}
{{- $name := "" -}}
{{- $email := "" -}}
{{- $gpg_key := "" -}}
{{- $ts_key := "" -}}

{{- if $remote }}
{{- /* Non-interactive: use env vars or config data */ -}}
{{- $name = env "GIT_USER_NAME" | default .name -}}
{{- $email = env "GIT_USER_EMAIL" | default .email -}}
{{- $gpg_key = env "GPG_KEY" | default .gpg_signing_key -}}
{{- $ts_key = env "TAILSCALE_AUTHKEY" | default .tailscale_authkey -}}
{{- if or (eq $name "") (eq $email "") }}
{{- fail "GIT_USER_NAME and GIT_USER_EMAIL must be set in remote/container environment" -}}
{{- end }}
{{- else }}
{{- /* Interactive: prompt for values (stored in chezmoi state) */ -}}
{{- $name = promptStringOnce . "name" "Git user.name" -}}
{{- $email = promptStringOnce . "email" "Git user.email" -}}
{{- $gpg_key = promptStringOnce . "gpg_signing_key" "GPG key fingerprint" -}}
{{- $ts_key = promptStringOnce . "tailscale_authkey" "Tailscale auth key (leave blank to skip)" -}}
{{- end }}

[data]
remote = {{ $remote }}
Expand All @@ -15,6 +34,7 @@ sourceDir = {{ .chezmoi.sourceDir | quote }}
aeos_dir = "$HOME/.aeos"
anchor_bin_dir = "$HOME/.local/bin"
aeos_ssh_user = "vagrant"
tailscale_authkey = {{ $ts_key | quote }}

{{- if not $remote }}
[git]
Expand Down
18 changes: 18 additions & 0 deletions .chezmoiscripts/run_once_after_install_tailscale.sh.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#!/bin/bash
# Installs Tailscale and authenticates once. Re-run by deleting chezmoi state entry.
set -euo pipefail

if ! command -v tailscale &>/dev/null; then
curl -fsSL https://tailscale.com/install.sh | sh
fi

sudo systemctl enable --now tailscaled

{{- if .tailscale_authkey }}
sudo tailscale up \
--authkey={{ .tailscale_authkey | quote }} \
--accept-routes \
--ssh
{{- else }}
echo "[tailscale] No auth key set. Run: tailscale up --authkey=<key> --accept-routes --ssh"
{{- end }}
85 changes: 0 additions & 85 deletions .chezmoiscripts/run_onchange_after_install_aeos.sh.tmpl

This file was deleted.

10 changes: 10 additions & 0 deletions .chezmoiscripts/run_onchange_after_install_brew.sh.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/bin/bash
# brew hash: {{ include "dot_config/brew/Brewfile" | sha256sum }}

set -euo pipefail

BREW_BIN="{{ if $b := lookPath "brew" }}{{ $b }}{{ else }}/home/linuxbrew/.linuxbrew/bin/brew{{ end }}"

BREWFILE="{{ .chezmoi.homeDir }}/.config/brew/Brewfile"

"$BREW_BIN" bundle --file "$BREWFILE"
7 changes: 7 additions & 0 deletions .claude/settings.local.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"permissions": {
"allow": [
"Bash(brew --version)"
]
}
}
162 changes: 43 additions & 119 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,150 +2,74 @@

## What this repo is

Personal dotfiles for a zsh/bash + Neovim + mise setup. Managed by [chezmoi](https://chezmoi.io/). No build step — only configuration files and templates.
Personal dotfiles for a zsh/bash + Neovim + mise setup. Managed by [chezmoi](https://chezmoi.io/). No build step — configuration files and templates only.

## Verified commands
## Quick commands

```sh
./setup # Bootstrap: install chezmoi, apply dotfiles
chezmoi apply # Apply all dotfiles to $HOME
chezmoi update # Pull latest changes and re-apply
mise exec -- chezmoi apply # Run via mise if chezmoi not on PATH
chezmoi update # Pull latest changes and re-apply
mise exec -- chezmoi apply # Run via mise if chezmoi not on PATH
```

## Repo structure

```
dot_* → becomes $HOME/.filename (chezmoi auto-symlinks)
dot_config/ → becomes $HOME/.config/
private_dot_* → private files; never tracked in git
.chezmoiexternals/ # External resources (mise, devpod, fonts)
.chezmoiscripts/ # Hook scripts run by chezmoi
.devcontainer/ # VS Code Dev Container config
setup # Bootstrap script
```
| Prefix | Becomes | Notes |
| --------------------- | ------------------------ | ----------------------- |
| `dot_*` | `$HOME/.filename` | Chezmoi auto-symlinks |
| `dot_config/` | `$HOME/.config/` | XDG config |
| `private_dot_*` | `$HOME/.filename` | Never tracked in git |
| `.chezmoiexternals/` | External resources | Mise, devpod, fonts |
| `.chezmoiscripts/` | Hook scripts | Run by chezmoi |
| `.devcontainer/` | VS Code Dev Container | - |

## How chezmoi works here
## Templates

- `dot_*` files become `$HOME/.filename`
- `.tmpl` extension = chezmoi Go template (supports `{{ .chezmoi.os }}`, `{{ lookPath "cmd" }}`, etc.)
- Templates must not fail on missing vars — use `{{ if ... }}{{ end }}` guards
- `.chezmoiignore` excludes: `.oh-my-zsh/cache/*`, `**/*.zwc`, `setup`, `README.md`, `dot_config/zsh/local.zsh`, `dot_config/opencode/bun.lockb`, `dot_config/opencode/package-lock.json`
- `.tmpl` extension = chezmoi Go template
- Supports `{{ .chezmoi.os }}`, `{{ lookPath "cmd" }}`
- Use `{{ if ... }}{{ end }}` guards for missing vars
- Template vars defined in `.chezmoi.toml.tmpl`

## Shell script standards

- Shebang: `#!/bin/bash` or `#!/usr/bin/env bash`
- Required flags: `set -euo pipefail`
- Required: `set -euo pipefail`
- No bare `cd` — use `cd ... || exit` or pushd/popd
- Functions welcome; keep small and single-purpose

## Security boundaries
## Security

- Never commit secrets; use `private_*` prefix for any file containing credentials
- `.chezmoiignore` does NOT redact secrets — only gitignore semantics apply
- GPG config lives in `private_dot_gnupg/` (not tracked)
- If a file should never leave this machine, prefix it `private_`
- `private_*` prefix = never commit to git
- GPG config in `private_dot_gnupg/` (not tracked)
- `.chezmoiignore` uses gitignore semantics only — does NOT redact secrets
- Never hardcode secrets; use `.chezmoi.toml.tmpl` env vars or prompts

## Read these first
## Key files to read

| For this... | Read that... |
|---|---|
| chezmoi config + template variables | `.chezmoi.toml.tmpl` |
| Tool versions and tool management | `dot_config/mise/mise.toml` |
| OpenCode profiles and settings | `dot_config/opencode/ocx.jsonc` + `profiles/` |
| Purpose | File |
| --------- | ------ |
| chezmoi config + template vars | `.chezmoi.toml.tmpl` |
| Tool versions | `dot_config/mise/mise.toml` |
| OpenCode config | `.opencode/ocx.jsonc` |
| Shell config | `dot_bashrc.tmpl`, `dot_zshrc.tmpl` |
| Bootstrap logic | `setup` |
| Branch rules | `BRANCHING.md` |

## Git and branch safety rules

### Protected branches

- `main` and `master` are protected — never commit, push, merge, or rebase directly onto them

### Feature branch workflow (mandatory)

1. Before any change: `git switch -c <branch-name>` (or `git checkout -b`)
2. Work on the feature branch
3. Commit freely on the feature branch
4. Push with `git push --set-upstream origin <branch-name>` (non-force)
5. When ready: open a PR or merge locally to main/master via a merge commit (no fast-forward)

### Branch naming

- Use format: `feat/<short-description>`, `fix/<short-description>`, `chore/<short-description>`
- Examples: `feat/zsh-history`, `fix/mise-version`, `chore/update-starship`

### Pre-change verification

Before running `git checkout` or `git switch`:

1. Run `git status` — confirm no uncommitted work will be lost
2. Run `git branch` — confirm you're on the intended branch

### Force and destructive operations — ALWAYS DENIED

- `git push --force` / `git force-push` — never, under any circumstances
- `git rebase` onto a protected branch — denied
- `git reset --hard` on main/master — denied
- `git reflog expire`, `git filter-branch` — denied
- `rm -rf` with a broad path — denied
- `sudo`, `dd`, `chmod -R` — denied on system paths

### Safe operation whitelist (allowed without asking)

- `git status`, `git diff`, `git log --oneline`, `git show`, `git branch -a`
- `chezmoi apply`, `chezmoi update`, `chezmoi status`
- `mise exec`, `ls`, `cat`, `grep`, `find`, `pwd`

### Ask before doing

- `git add`, `git commit`, non-protected `git push`, `git checkout` to existing branches
- `terraform apply`, `kubectl delete`, `docker system prune`
- Editing any `.env`, `kubeconfig`, or `terraform.tfstate` file

## Safety and permission philosophy

Permission rules in `opencode.json` are intentional guardrails, not suggestions.

- `deny` means the operation is blocked — a blocked operation will not execute
- `ask` means the agent must confirm intent before proceeding
- `allow` means the operation proceeds without prompting

If a task seems to require a denied operation, stop and report back. Do not work around permission rules by splitting commands or using alternatives.

Destructive commands (`rm -rf`, `sudo`, `dd`) are always denied. When work requires clearing space or resetting state, use safe alternatives (e.g., `mv` to a trash directory) or ask.

Protected branch enforcement is implemented in `opencode.json` permission rules. Even if an agent is given raw bash access, these rules apply at the session level.

**Branch-switching safety:**
If a task requires switching branches or checking out code, always:

- Verify the current branch first
- Ensure no uncommitted changes exist or stash them
- Never switch to main/master for direct work
- Return to the feature branch after inspection

**Why these rules exist:**

- Prevent accidental commits or pushes to protected branches
- Avoid destructive operations that cannot be easily undone
- Protect secrets, state files, and production config
- Ensure all work goes through proper review workflow

## Documentation maintenance

Docs are living context — update them when your work changes behavior, commands, setup, architecture, deployment, rollback, troubleshooting, or operational constraints.
## Git safety

- Update only the docs directly affected by your change
- Never leave a doc that contradicts the actual code or commands
- When you discover a failure mode or fix, add it to `docs/troubleshooting.md`
- In your final task summary, state whether docs were updated and which ones, or explicitly say "no docs updated" with the reason
- `main`/`master` protected — no direct commits/pushes/merges/rebases
- Feature branches for all changes: `feat/`, `fix/`, `chore/`
- See `BRANCHING.md` for full rules

Do not create new docs unless genuinely useful for future agents. A stub is justified if the repo has operational complexity (infra, multi-service, deployment concerns).
## Documentation

## What not to do
- Update docs when behavior changes
- Add failure modes to `docs/troubleshooting.md`
- Don't create new docs unless genuinely useful

- Do not run `make`, `npm install`, or any build commands in this repo
- Do not modify `setup` — it must remain runnable on any vanilla Linux/macOS machine
- Do not add hardcoded paths (use `$HOME`, `$XDG_CONFIG_HOME`, or chezmoi template vars instead)
## Forbidden

- `make`, `npm install`, or any build command
- Editing `setup` (must stay runnable on any vanilla Linux/macOS)
- Hardcoded paths (use `$HOME`, `$XDG_CONFIG_HOME`, or chezmoi vars)
- Force push, rebase to protected branches, destructive git ops
Loading
Loading