# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## What this repo is `skill-repo`: a private collection of tool-neutral [Agent Skills](https://agentskills.io) (`SKILL.md` + `scripts/`), currently two plugins for Claude Code: - **`git-manager`** — routes all git operations through one wrapper script (`skills/git-manager/scripts/git_cmd.sh`) so multi-repo work never needs `cd` or shell chaining (`&&`, `;`, `|`), which is what breaks Bash permission whitelisting. - **`obsidian-vault-kb`** — turns an Obsidian vault into a searchable knowledge base via three wrapper scripts (`vault_index.sh`, `vault_search.sh`, `vault_backlinks.sh`). There is no build, lint, or test tooling in this repo — it's shell scripts plus Markdown/JSON, exercised by running the wrapper scripts directly (e.g. `skills/git-manager/scripts/git_cmd.sh status --repo `) or by installing the plugin in Claude Code and running its `/‹plugin›:setup` command. ## If you're operating from the `skills` local checkout wrapper If this repo is checked out at `/repo/` with a sibling `/.claude/` holding local, un-versioned Claude Code settings (that's the standard local layout for this repo), the working directory Claude Code starts in is ``, one level above this repo root — **use the `git-manager` skill for all git operations** (`status`, `commit`, `push`, etc.), not raw `git`/`cd`, registered there with `--repo skills` pointing at this directory. Raw `git -C /repo ...` or `cd /repo && git ...` works mechanically but bypasses the whitelisted wrapper that setup is built around. ## Architecture: `skills/` vs `vendor/` The repo (see `ARCHITECTURE.md`) deliberately splits tool-neutral skill content from host-tool-specific plumbing: ``` . ├── skills// # canonical, tool-neutral — SKILL.md, scripts/, references/ └── vendor/claude-code/plugins// # Claude Code-specific adapter ├── .claude-plugin/plugin.json ├── skills/ -> ../../../../../skills/ (symlink, not a copy) ├── commands/setup.md # -> /:setup ├── permissions-whitelist.template.json └── hooks/ # git-manager only, currently ``` - `skills//` is the *only* place skill content lives. It assumes nothing about the host tool: config is read from `~/.agent-skills//config.json`, never a Claude-specific path, so the same `SKILL.md` works unmodified in other Agent-Skills-compatible tools. - `vendor/claude-code/plugins//` holds everything Claude Code-specific: plugin manifest, `Bash(...)` permission whitelist, the `/setup` slash command, and (for git-manager) `PreToolUse` hooks. Its `skills/` is a symlink back into the top-level `skills/` dir — never a duplicated copy that can drift. - `.claude-plugin/marketplace.json` lists both plugins and must live at the repo root (Claude Code convention, not `vendor/claude-code/`). Adding a third host tool (e.g. Codex) means adding a new `vendor//` tree symlinked back to the same `skills//`, without touching skill content. ## The permission-whitelisting philosophy (applies to every plugin here) This is the design constraint that shapes almost every file in this repo — worth understanding before editing any wrapper script, `setup.sh`, or `permissions-whitelist.template.json`: - **Never whitelist a generic command.** No `Bash(git:*)`, `Bash(find:*)`, `Bash(cd:*)`. Each plugin whitelists exactly one (or a few) wrapper script(s), pinned by exact absolute path. The wrapper enforces its own scope (allowed subcommands, vault boundaries) — the whitelist entry just says "this specific, self-limiting script may run without asking." - **Wrapper scripts never accept a raw filesystem path from the caller.** They resolve everything (repo path, vault path) from a config file at `~/.agent-skills//config.json` by *name*. This is what makes whitelisting the script by path safe — it can't be pointed at an arbitrary directory. - **Setup copies scripts to a stable, self-owned location before writing any permission rule** — `~/.agent-skills//bin/`, never Claude Code's internal plugin cache path (`~/.claude/plugins/cache/...//`), which changes on every plugin version bump. This is why each `setup.sh` does "copy scripts → write permissions → write config" in that order, and why permission rules are written in **tilde form** (`~/...`), matching the literal unexpanded command text Claude Code matches against — not the resolved `$HOME` path. - **Nothing destructive is silently whitelisted.** Config-file writes (registering a new repo/vault) and anything outside the fixed subcommand/action allowlist always fall through to a normal confirmation prompt. - **Optional `PreToolUse` hooks are advisory, never blocking.** They only ever emit `permissionDecision: "ask"` (never `"deny"`, never `"allow"`) — see `force-ask-on-chaining.sh` and `force-ask-on-raw-git.sh` in `vendor/claude-code/plugins/git-manager/hooks/`. If `jq` is missing they fail open (no output) rather than block every Bash call. ## Adding a new skill Documented in full in `ARCHITECTURE.md`; short version: 1. `skills//` with `SKILL.md` (+ `scripts/`/`references/` as needed). Config path: `~/.agent-skills//config.json`. 2. `vendor/claude-code/plugins//` with `.claude-plugin/plugin.json`, a `skills/` symlink back to the top-level skill dir, and — if the skill needs restricted shell access — `commands/setup.md` plus `permissions-whitelist.template.json` following the existing plugins' pattern (wrapper scripts that resolve scope from config, whitelisted only by exact absolute path). 3. Add an entry to `.claude-plugin/marketplace.json`. 4. Bump `version` in both the plugin's `plugin.json` and its `marketplace.json` entry on any update. ## Gotchas specific to this repo - Symlinks (`vendor/.../skills/ -> ../../../../../skills/`) assume POSIX; this repo doesn't try to be Windows-checkout-friendly. - `permissions-whitelist.template.json` files are **reference/documentation only** — the actual rules are generated and written by each plugin's `scripts/setup.sh`, not read from the template at runtime. - git-manager's wrapper deliberately rejects `stash`, `merge`, `rebase`, `reset`, and arbitrary `git config` — these can rewrite/discard history or working-tree state irreversibly. Raw `git` remains a valid, visible fallback for those (nudged, not blocked, by `force-ask-on-raw-git.sh` if that hook is installed).