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
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ When the user's message matches a phrase below, read and follow the correspondin
| "last screenshot", "see screenshot", "recent screenshot", "show screenshot", "last N screenshots" | `agents/skills/screenshot/SKILL.md` |
| "investigate CI failures", "find flaky tests", "why is CI failing", "diagnose test flakiness", "flaky CI" | `agents/skills/ci-investigate/SKILL.md` |
| "create skill", "new skill", "write skill", "add a slash command", "improve skill" | `agents/skills/write-skill/SKILL.md` |
| "add to PATH", "Bash tool PATH", "command not found in Claude Code", "works in terminal but not Claude Code", "CLAUDE_ENV_FILE", "env.PATH not working" | `agents/skills/bash-tool-path/SKILL.md` |
| "prioritize", "RICE score", "backlog grooming", "sprint planning", "rank items" | `agents/skills/prioritize/SKILL.md` |
| "swarm", "parallel agents", "agent team in conductor", "multi-agent", "spawn agents" | `agents/skills/swarm/SKILL.md` |
| "orchestrate", "fable workflow", "dynamic workflow", "tiered workflow", "model-tiered build", "plan on fable implement with sonnet and opus" | `agents/skills/orchestrate/SKILL.md` |
Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ dots docker stop-all # Stop all Docker containers

## Agent Skills

Dots includes 68 reusable slash-command skills for AI coding agents, following the [Agent Skills](https://agentskills.io) open standard. Each skill lives in `agents/skills/<name>/SKILL.md` and is available as `/<name>` in Claude Code after running `dots install agents`.
Dots includes 71 reusable slash-command skills for AI coding agents, following the [Agent Skills](https://agentskills.io) open standard. Each skill lives in `agents/skills/<name>/SKILL.md` and is available as `/<name>` in Claude Code after running `dots install agents`.

| Skill | Description |
|-------|-------------|
Expand Down Expand Up @@ -97,6 +97,7 @@ Dots includes 68 reusable slash-command skills for AI coding agents, following t
| `/plan` | Generate an implementation plan from a PRD, spike, or spec |
| `/contest` | Competing implementations with judge evaluation |
| `/write-skill` | Create or improve a skill with best practices |
| `/bash-tool-path` | Safely add a directory to Claude Code's Bash tool PATH via a SessionStart hook and `CLAUDE_ENV_FILE` |
| `/screenshot` | View recent screenshots from `~/Downloads` |
| `/handoff` | Generate handoff prompt for another agent thread |
| `/standup` | Daily standup summary from git activity |
Expand Down Expand Up @@ -192,7 +193,7 @@ Run `dots install agents` to symlink them to `~/.claude/agents/`.
├── pi/ # pi.dev coding agent config (models.json only)
├── agents/ # Agent configuration
│ ├── skills/ # 68 reusable skills (SKILL.md per skill)
│ ├── skills/ # 71 reusable skills (SKILL.md per skill)
│ └── custom/ # 3 custom agent types (.md per agent)
│ └── tests/ # Skill test suite
Expand Down
92 changes: 92 additions & 0 deletions agents/skills/bash-tool-path/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
---
name: bash-tool-path
description: Safely add a directory to Claude Code's Bash tool PATH. Use when a binary runs in your terminal but is "command not found" inside Claude Code's Bash tool, when adding a directory to PATH for Claude Code, when configuring CLAUDE_ENV_FILE, or when settings.json env.PATH did not work. Explains why env.PATH replaces rather than appends PATH and is not variable-expanded, why ~/.zshrc PATH exports are dropped, and the supported SessionStart hook fix.
---

## Context

- Worked example hook: ~/.dots/agents/hooks/session-start-path.sh
- Existing SessionStart hooks (user): !`grep -n SessionStart ~/.claude/settings.json 2>/dev/null | head -10`
- env.PATH in user settings (a footgun if present): !`grep -n '"PATH"' ~/.claude/settings.json 2>/dev/null | head -5`

## When to use this skill

Use this when a command that works in an interactive terminal fails with
"command not found" inside Claude Code's Bash tool, or when the user wants to
add a directory (a go/bin, cargo, asdf shim, or custom tool dir) to the PATH
that Claude Code's Bash tool sees.

## Why the obvious fixes do not work

Three footguns make this harder than it looks. State them plainly so nobody
re-discovers them by hand.

1. settings.json `env` values are set **literally**. Claude Code does not
expand `$VAR` or `${VAR}` in them. So `"PATH": "${PATH}:/x/bin"` sets PATH
to that literal, broken string, not to the inherited PATH plus a directory.

2. `env.PATH` **replaces** the inherited PATH wholesale. It cannot append,
and replacing it drops the directories Claude Code already needs (for
example plugin bin dirs). So `env.PATH` is the wrong tool for adding a dir.

3. The Bash tool sources `~/.zshrc` (or `~/.bashrc`/`~/.profile`) at session
start **only to capture aliases, functions, and shell options**. PATH
`export` lines in `~/.zshrc` or `~/.zshenv` are not captured. Claude Code
builds the Bash tool PATH from the process that launched it (often a daemon),
not from your interactive shell, so daemon-launched sessions can lack
go/bin and similar dirs even though they resolve fine in your terminal.

## The supported fix: a SessionStart hook that writes to CLAUDE_ENV_FILE

Claude Code sources the file named by `$CLAUDE_ENV_FILE` before every Bash
command. Because the file is sourced, `$PATH` expands at source time, so you
can prepend a directory and preserve the inherited PATH. Populate that file
from a `SessionStart` hook. `$CLAUDE_ENV_FILE` is available to the
SessionStart, Setup, CwdChanged, and FileChanged hooks.

A static alternative exists — point `CLAUDE_ENV_FILE` at a fixed shell script
before launching Claude Code — but the SessionStart hook is the portable,
dynamic choice and is what this repo ships.

### Steps

1. Write (or extend) a SessionStart hook script that appends an `export` line
per directory to `$CLAUDE_ENV_FILE`. Read the worked, tested reference
first: `~/.dots/agents/hooks/session-start-path.sh`. It is idempotent,
guards that each directory exists, and prepends rather than clobbers. Mirror
its shape rather than writing a new pattern from scratch. The core line is:

```bash
export PATH="/your/dir:$PATH"
```

Keep `$PATH` literal (escaped or single-quoted in the heredoc/printf) so it
expands when the env file is sourced, not when the hook runs.

2. Make the hook a no-op when `$CLAUDE_ENV_FILE` is unset, and append each
export only if the file does not already contain it (the file persists
across repeated SessionStart fires within a session).

3. Register the hook in settings.json under `hooks.SessionStart`, invoked via
`bash`. In this repo, registration is handled by `dots install agents`; for
a standalone setup, add the SessionStart entry to `~/.claude/settings.json`.
Use the `/update-config` skill for the settings.json edit mechanics.

4. Verify in a fresh session: run the bare command name in the Bash tool and
confirm it resolves.

### What NOT to do

- Do not set `env.PATH` in settings.json to add a directory (it replaces, and
is not variable-expanded — see footguns 1 and 2).
- Do not add the PATH export only to `~/.zshrc`/`~/.zshenv` and expect the
Bash tool to pick it up (footgun 3).

## References

- Bash tool behavior (what gets sourced at session start):
https://code.claude.com/docs/en/tools
- Persisting environment variables across Bash commands via CLAUDE_ENV_FILE
and SessionStart hooks: https://code.claude.com/docs/en/hooks
- Worked reference implementation in this repo:
~/.dots/agents/hooks/session-start-path.sh
1 change: 1 addition & 0 deletions agents/skills/write-skill/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ After writing, run two validation passes. Fix issues silently — only flag if t

#### Syntax and Safety

- [ ] Dynamic context uses the LIVE exclamation-mark-plus-backtick syntax, NOT the `!{...}` curly-brace form. The curly form is a documentation-only placeholder used in this file's examples to prevent self-execution (see the note under Dynamic Context) — a real skill that uses it renders the line as inert text instead of running the command.
- [ ] No `$()` in any dynamic context line
- [ ] No `||` or `&&` operators in any dynamic context line
- [ ] Error-prone commands use `2>/dev/null | head -N` (suppresses stderr AND neutralizes exit code)
Expand Down
Loading