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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,8 @@ local/config.json
local/automount.sh
.env

# Track the global Claude instructions despite a global CLAUDE.md ignore
!claude/config/CLAUDE.md

# Node.js
node_modules/
1 change: 1 addition & 0 deletions Brewfile
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ cask 'battle-net'
cask 'bitwarden'
cask 'brave-browser'
cask 'chromium', args: { "no-quarantine": true }
cask 'claude-code'
cask 'cyberduck'
cask 'discord'
cask 'dungeon-crawl-stone-soup-tiles'
Expand Down
91 changes: 91 additions & 0 deletions claude/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# Claude Code

Global configuration for [Claude Code](https://claude.ai/code).

## What this topic does

- Installs Claude Code via the native installer (`install.sh`)
- Symlinks every file in `config/` → `~/.claude/` (currently `settings.json`
and `CLAUDE.md`)

The symlinks are created at provision time by `install.sh`, which `bootstrap.sh`
runs automatically. It follows the same nested-config pattern as
`postgresql`/`newsyslog`: an existing symlink is left alone, and a real file at
the target is backed up to `<name>.backup` before linking. Drop a new file in
`config/` and it gets linked on the next run — no script changes needed.

Settings reload automatically on save, except `model` and `outputStyle`, which
take effect in a new session.

## settings.json

`config/settings.json` is the source of truth. The live `~/.claude/settings.json`
is a symlink to it, so edits here apply everywhere.

Current keys:

| Key | Purpose |
| -------------------------- | ----------------------------------------------------- |
| `alwaysThinkingEnabled` | Extended thinking on by default |
| `effortLevel` | Persisted reasoning effort (`high`) |
| `skipAutoPermissionPrompt` | Don't prompt for the auto-permission opt-in |
| `permissions` | `defaultMode: auto`, plus an explicit allow/deny list |
| `attribution` | Strips the `Co-Authored-By` trailer from commits |
| `spinnerVerbs` | Custom "thinking" phrases (see below) |

## Commit attribution

```json
"attribution": {
"commit": ""
}
```

An empty `commit` string hides the standard attribution (including the
`Co-Authored-By: Claude` trailer) from git commits. Add `"pr": ""` to also drop
the attribution footer from generated pull request descriptions. (This replaces
the deprecated `includeCoAuthoredBy` boolean.)

## Custom thinking phrases (spinnerVerbs)

The words shown while Claude works are themed for fun. Schema:

```json
"spinnerVerbs": {
"mode": "replace",
"verbs": ["Seeking the Ocarina of Time", "..."]
}
```

- `mode: "replace"` — show **only** these phrases.
- `mode: "append"` — add these on top of Claude's built-in defaults.

To add or change phrases, just edit the `verbs` array in
`config/settings.json`. The current set mixes Legend of Zelda, Lord of the
Rings, and Star Wars references.

## Global instructions (CLAUDE.md)

`config/CLAUDE.md` is symlinked to `~/.claude/CLAUDE.md` and holds personal
preferences that apply to **every** project (working style, git habits, editing
defaults). Project-level `CLAUDE.md` files take precedence where they conflict.
Edit it directly — keep it cross-project; project-specific rules belong in that
project's own `CLAUDE.md`.

## Other global configs worth considering

Not set up yet, but easy to add as their own symlinked files in this topic:

- **Custom statusLine** — a `statusLine` command in `settings.json` pointed at a
script (e.g. `config/statusline.sh`) can show cwd, git branch, model, and
context usage.
- **Spinner tips** — `spinnerTipsEnabled` (bool) and `spinnerTipsOverride`
(string) control the hint line under the spinner.
- **Output style** — `outputStyle` (e.g. `"Explanatory"`) adjusts response style.
- **keybindings** — `~/.claude/keybindings.json` for custom shortcuts / vim mode
(`editorMode`).

### Do not check in

- `~/.claude.json` — OAuth tokens, MCP auth, and session state.
- `~/.claude/settings.local.json` — per-project personal overrides (gitignored).
41 changes: 41 additions & 0 deletions claude/config/CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Personal Global Instructions

These preferences apply across every project. Project-level `CLAUDE.md` files
take precedence where they conflict.

## Working style

- Lead with the answer or result, then the supporting detail. Keep prose tight.
- Verify claims before stating them. Prefer reading the code/docs over recalling
from memory, and cite `file:line` when referring to specific code.
- Match the conventions already present in a repo (naming, formatting, structure)
rather than imposing new ones.
- Ask before doing anything destructive, hard to reverse, or outward-facing
(deleting files, force-pushing, publishing). Approval in one context does not
carry to the next.

## Git

- Do not commit or push unless explicitly asked.
- Never commit secrets or local-only config (`.env`, credentials, tokens).
- Follow the repo's own commit conventions where they exist (e.g. Conventional
Commits, changelog tags, issue references). Repo rules win on conflict.
- Otherwise, follow the seven rules of a great commit message
(https://cbea.ms/git-commit/):
1. Separate subject from body with a blank line.
2. Limit the subject line to 50 characters.
3. Capitalize the subject line.
4. Do not end the subject line with a period.
5. Use the imperative mood in the subject line ("Add x", not "Added x").
6. Wrap the body at 72 characters.
7. Use the body to explain _what_ and _why_ vs. _how_.
- Always end the commit message with a final line referencing the issue:
`Issue #XXX {actual issue title here}` — replace `XXX` with the issue number
and put the issue's real title inside the braces (e.g.
`Issue #142 {Fix login redirect loop}`). Look up the actual title rather than
guessing.

## Editing

- Make the smallest change that fully solves the problem; avoid unrelated churn.
- Run the project's formatter/linter before considering a change done.
47 changes: 47 additions & 0 deletions claude/config/settings.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
{
"$schema": "https://claude.ai/schemas/claude-code/settings.json",
"alwaysThinkingEnabled": true,
"effortLevel": "high",
"skipAutoPermissionPrompt": true,
"permissions": {
"defaultMode": "auto",
"allow": [
"Bash(npm run test)",
"Bash(npm run lint)",
Expand All @@ -9,5 +13,48 @@
"Bash(gh pr view*)"
],
"deny": ["Bash(sed *)"]
},
"attribution": {
"commit": ""
},
"spinnerVerbs": {
"mode": "replace",
"verbs": [
"Seeking the Ocarina of Time",
"Collecting Korok seeds",
"Pulling the Master Sword",
"Solving the Water Temple",
"Charging a spin attack",
"Cooking a dubious meal",
"Bombing a suspicious wall",
"Playing Zelda's Lullaby",
"Paragliding off a cliff",
"Shattering pots for rupees",
"Climbing in the rain",
"Taming a wild horse",
"Walking into Mordor",
"Consulting the Palantír",
"Lighting the beacons of Gondor",
"Riding to Rohan",
"Second breakfasting",
"Following Gandalf's counsel",
"Whispering with the Ents",
"Sneaking past Shelob",
"Sailing to the Grey Havens",
"Holding the bridge of Khazad-dûm",
"Casting the Ring into the fire",
"Translating the Black Speech",
"Calibrating the hyperdrive",
"Consulting the Jedi Archives",
"Trusting the Force",
"Making the Kessel Run",
"Bullseyeing womp rats",
"Igniting a lightsaber",
"Negotiating with Hutts",
"Plotting a jump to lightspeed",
"Decrypting the Death Star plans",
"Rerouting power to shields",
"Listening to Master Yoda"
]
}
}
28 changes: 18 additions & 10 deletions claude/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,24 @@ if [ ! -d "$CLAUDE_CONFIG_DIR" ]; then
mkdir -p "$CLAUDE_CONFIG_DIR"
fi

# Symlink settings.json
if [ -f "$DOTFILES_CLAUDE_CONFIG/settings.json" ]; then
if [ -L "$CLAUDE_CONFIG_DIR/settings.json" ]; then
rm "$CLAUDE_CONFIG_DIR/settings.json"
elif [ -f "$CLAUDE_CONFIG_DIR/settings.json" ]; then
warn "Existing settings.json found, backing up to settings.json.backup"
mv "$CLAUDE_CONFIG_DIR/settings.json" "$CLAUDE_CONFIG_DIR/settings.json.backup"
# Symlink every file in config/ into ~/.claude/ (follows the nested-config
# pattern used by postgresql/newsyslog: leave an existing symlink alone, back
# up a real file before linking)
for src in "$DOTFILES_CLAUDE_CONFIG"/*; do
[ -f "$src" ] || continue
filename="$(basename "$src")"
target="$CLAUDE_CONFIG_DIR/$filename"

if [ -L "$target" ]; then
info "${filename} already symlinked"
else
if [ -e "$target" ]; then
warn "${filename} exists and is not a symlink, backing up"
mv "$target" "${target}.backup"
fi
ln -s "$src" "$target"
success "Symlinked ${filename}"
fi
ln -s "$DOTFILES_CLAUDE_CONFIG/settings.json" "$CLAUDE_CONFIG_DIR/settings.json"
success "Symlinked settings.json"
fi
done

success "Claude Code configuration complete"
1 change: 1 addition & 0 deletions zsh/zshrc.symlink
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,4 @@ done
# bun
export BUN_INSTALL="$HOME/.bun"
export PATH="$BUN_INSTALL/bin:$PATH"
export PATH="$HOME/.local/bin:$PATH"