feat(claude): improve ai workflow

This commit is contained in:
Barrett Ruth 2026-03-04 14:33:06 -05:00
parent e84a22dbcb
commit 902f0f3ad6
Signed by: barrett
GPG key ID: A6C96C9349D2FC81
11 changed files with 196 additions and 33 deletions

View file

@ -14,6 +14,10 @@ If given express permission to use git, NEVER push to a main/master branch.
If given express permission to use git, NEVER commit ai-related files (e.g. CLAUDE.md).
If given express permission to use git, ALWAYS work on a feature branch. If on
`main` or `master`, create and switch to a branch before making changes. Branch
naming: `type/short-description` (e.g. `fix/diagnostic-range`).
If given express permission to use git, ALWAYS use this commit message format:
type(scope): imperative summary
@ -21,7 +25,10 @@ If given express permission to use git, ALWAYS use this commit message format:
- Valid types: `feat` `fix` `docs` `refactor` `perf` `test` `ci` `build` `revert`
- Scope is optional, lowercase. Subject: lowercase after colon, no trailing period, max 72 chars.
- Body required for non-trivial commits, using `Problem:` / `Solution:` format.
Keep each section to 2-3 sentences.
- One logical change per commit. Refactors, formatting, and features must be separate commits.
- Use backticks around code identifiers, function names, and file paths in
commit messages and PR descriptions (e.g. `setup()`, `lua/oil/view.lua`).
If given express permission to use git, ALWAYS check for a PR template at
`.github/pull_request_template.md` and follow it. If none exists, use

25
config/claude/hooks/guard.sh Executable file
View file

@ -0,0 +1,25 @@
#!/usr/bin/env bash
set -euo pipefail
INPUT=$(cat)
CMD=$(printf '%s' "$INPUT" | jq -r '.tool_input.command // empty')
if printf '%s' "$CMD" | grep -qE '\bgh\b.*\s(-R|--repo)\b'; then
echo "Blocked: do not target other repos with -R/--repo. Run gh commands against the current repo only." >&2
exit 2
fi
if printf '%s' "$CMD" | grep -qE '\bgh\s+issue\s+create\b'; then
echo "Blocked: gh issue create must be run manually or explicitly approved." >&2
exit 2
fi
if printf '%s' "$CMD" | grep -qE '\bgit\s+push\b'; then
BRANCH=$(git branch --show-current 2>/dev/null || true)
if [ "$BRANCH" = "main" ] || [ "$BRANCH" = "master" ]; then
echo "Blocked: never push directly to $BRANCH. Use a feature branch." >&2
exit 2
fi
fi
exit 0

View file

@ -19,36 +19,43 @@ Solution: describe what this commit does
### Body
Required for any non-trivial change. Use `Problem:` / `Solution:` sections.
Wrap at 72 characters. Separate from header with a blank line.
2-3 sentences per section, max. Wrap at 72 characters. Separate from header
with a blank line.
Use backticks around code identifiers, function names, and file paths
(e.g. `setup()`, `lua/oil/view.lua`, `FIELD_NAME`).
### Examples
Good:
```
fix(lsp): correct off-by-one in diagnostic range
fix(lsp): correct off-by-one in `diagnostic_range`
Problem: diagnostics highlighted one character past the actual error,
causing confusion when multiple diagnostics appeared on adjacent tokens.
causing confusion on adjacent tokens.
Solution: subtract 1 from the end column returned by the language server
before converting to 0-indexed nvim columns.
Solution: subtract 1 from the end column returned by the language
server before converting to 0-indexed nvim columns.
```
```
refactor: extract repeated buffer lookup into helper
refactor: extract repeated buffer lookup into `get_buf_entry`
```
Bad:
```
Fixed stuff # not imperative, vague
feat: Add Feature. # uppercase after colon, trailing period
Fixed stuff
feat: Add Feature.
fix(lsp): correct off-by-one in diagnostic range and also refactor the
entire highlight module and add new tests # multiple concerns
entire highlight module and add new tests
```
## Branch Naming
## Branch Rules
Always work on a feature branch. Never commit or push directly to `main` or
`master`. If on the default branch, create and switch to a topic branch first.
```
type/short-description
@ -65,15 +72,15 @@ If no template exists, fall back to:
```
## Problem
<why this change is needed>
<1-2 sentences>
## Solution
<what the change does>
<1-2 sentences>
```
Either way, write in plain prose. No bullet-point walls, no AI-style markdown
headings beyond what the template calls for. Keep it concise and human.
Write concise prose. No bullet-point walls, no verbose AI-style markdown.
Use backticks for code references.
## Decomposition Rules

View file

@ -1,4 +1,4 @@
# /commit
# /gc
Create a conventional commit from staged or unstaged changes.
@ -26,7 +26,10 @@ Create a conventional commit from staged or unstaged changes.
- Scope is optional, lowercase.
- Non-trivial changes require a body with `Problem:` / `Solution:` sections,
wrapped at 72 chars, separated from header by a blank line.
- Keep the body tight: 2-3 sentences per section, max.
- Trivial one-liners: header alone is fine.
- Use backticks around code identifiers, function names, and file paths
(e.g. `setup()`, `lua/pending/store.lua`).
- Match the style of the recent commits from step 1.
4. Present the full message and ask for approval.

View file

@ -0,0 +1,51 @@
# /guide
Interactive step-by-step guide for nvim plugin development tasks.
## Instructions
1. Ask the user what they want to accomplish. Gather enough context to build a
step-by-step plan (the plugin name, the goal, any constraints).
2. Determine the plugin name from the current repo (basename of the working
directory) and today's date. Run exactly one Bash command:
```
basename "$(git rev-parse --show-toplevel)" && date +%Y-%m-%d
```
3. Build a numbered step-by-step plan as a markdown file. Run exactly one Bash
command:
```
mkdir -p /tmp/<plugin>-<date> && cat > /tmp/<plugin>-<date>/guide.md <<'EOF'
# <Goal>
Plugin: <plugin>
Date: <date>
Branch: <branch name to create>
## Steps
1. <step>
2. <step>
...
## Current
Step 1: <description>
EOF
```
4. Present step 1 to the user. Execute it. Show the result. Ask for
confirmation before moving to the next step.
5. After the user confirms, update the `## Current` section in the guide to
reflect the next step, then execute it. Repeat until all steps are done.
6. When all steps are complete, update the guide with a `## Done` section and
print the path to the guide file.
Keep each step small and self-contained. Prefer one logical change per step.
If a step reveals unexpected complexity, break it into sub-steps and update
the guide before proceeding.

View file

@ -21,28 +21,35 @@ Create a pull request from the current branch.
```
## Problem
<why this change is needed>
<1-2 sentences>
## Solution
<what the change does>
<1-2 sentences>
```
- Write in plain prose. No bullet walls, no AI markdown soup.
- Write concise prose. No bullet walls, no verbose explanations.
- Use backticks around code identifiers, function names, and file paths.
3. Present the title and body. Ask for approval.
4. After approval, run exactly one Bash command (push + create chained):
4. After approval, if `scripts/ci.sh` exists, run it:
```
git push -u origin <branch> && gh pr create --title "<title>" --body "$(cat <<'EOF'
bash scripts/ci.sh
```
If it fails, show the output and stop. Do NOT create the PR.
5. Run exactly one Bash command:
```
gh pr create --title "<title>" --body "$(cat <<'EOF'
<body here>
EOF
)"
```
Print the PR URL from the output.
Total: 2 Bash calls (gather + push/create). Do not run any other commands. Do
not read files, explore code, or run additional git commands beyond what is
listed above.
Total: 2 Bash calls (gather + create), or 3 if CI ran. Do not run any other
commands. Do not read files, explore code, or run additional git commands beyond
what is listed above.
Never force-push, even with lease. Never target main/master as the head branch.

View file

@ -0,0 +1,48 @@
# /qpr
Create a pull request immediately, no approval step.
## Instructions
1. Run exactly this one Bash command:
```
echo "---BRANCH---" && git branch --show-current && echo "---LOG---" && git log --oneline main..HEAD && echo "---STAT---" && git diff main...HEAD --stat && echo "---TEMPLATE---" && cat .github/pull_request_template.md 2>/dev/null || true
```
If the branch is `main` or `master`, tell the user and stop.
2. If `scripts/ci.sh` exists, run it:
```
bash scripts/ci.sh
```
If it fails, show the output and stop.
3. Draft the PR (do NOT present for approval — create it immediately):
- **Title**: `type(scope): imperative summary`, max 72 chars.
- **Body**: if a PR template was found, fill it in. Otherwise:
```
## Problem
<1-2 sentences>
## Solution
<1-2 sentences>
```
- Use backticks around code identifiers, function names, and file paths.
Run exactly one Bash command:
```
gh pr create --title "<title>" --body "$(cat <<'EOF'
<body here>
EOF
)"
```
Print the PR URL from the output.
Total: 2-3 Bash calls. Do not run any other commands.
Never force-push. Never target main/master as the head branch.

View file

@ -61,7 +61,7 @@ local git_status = new_git_status()
return {
{
'barrettruth/midnight.nvim',
enabled = false,
enabled = true,
after = function()
vim.cmd.colorscheme('midnight')
end,
@ -300,6 +300,6 @@ return {
latex = { open = { 'sioyek', '--new-instance' } },
})
end,
keys = { { '<leader>p', '<cmd>Preview toggle<cr>' } },
keys = { { '<leader>p', '<cmd>Preview watch<cr>' } },
},
}

View file

@ -225,12 +225,14 @@ end)
return {
{
'barrettruth/diffs.nvim',
enabled = true,
before = function()
vim.g.diffs = {
debug = '/tmp/diffs.log',
fugitive = true,
neogit = false,
extra_filetypes = { 'diff' },
hide_prefix = true,
hide_prefix = false,
highlights = {
vim = {
enabled = true,

View file

@ -354,6 +354,19 @@ in
"api.github.com"
];
tools.web_fetch = true;
hooks = {
PreToolUse = [
{
matcher = "Bash";
hooks = [
{
type = "command";
command = "${config.xdg.configHome}/claude/hooks/guard.sh";
}
];
}
];
};
};
};
@ -369,6 +382,10 @@ in
source = config.lib.file.mkOutOfStoreSymlink "${repoDir}/config/claude/skills";
};
xdg.configFile."claude/hooks" = lib.mkIf claude {
source = config.lib.file.mkOutOfStoreSymlink "${repoDir}/config/claude/hooks";
};
xdg.configFile."tmux/themes/midnight.conf".source =
config.lib.file.mkOutOfStoreSymlink "${config.home.homeDirectory}/.config/nix/config/tmux/themes/midnight.conf";
xdg.configFile."tmux/themes/daylight.conf".source =

View file

@ -145,7 +145,7 @@ ai)
;;
code)
require nvim
spawn_or_focus code 'nvim -c "lua require([[config.tmux]]).run([[nvim]])"'
spawn_or_focus code 'nvim .'
;;
git)
require nvim git
@ -153,13 +153,9 @@ git)
if ! git -C "$pane_path" rev-parse --is-inside-work-tree >/dev/null 2>&1; then
tmux display-message "Not a git repository"
else
spawn_or_focus git 'nvim -c "lua require([[config.tmux]]).run([[git]])"'
spawn_or_focus git 'nvim -c "Git|only"'
fi
;;
run)
require nvim
spawn_or_focus run 'nvim -c "lua require([[config.tmux]]).run([[run]])"'
;;
shell)
spawn_or_focus shell
;;
@ -178,7 +174,7 @@ cmd)
action=$(printf '%s' "$result" | sed -n '2p')
[ $rc -eq 130 ] && exit
if [ -n "$query" ] && [ "$query" != "$action" ]; then
tmux $query
tmux "$query"
elif [ -n "$action" ]; then
case "$action" in
switch-client) pick_session ;;