6.7 KiB
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
(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 needscdor 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 <name>) 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 <workspace>/repo/ with a sibling
<workspace>/.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 <workspace>, 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 <workspace>/repo ... or
cd <workspace>/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/<name>/ # canonical, tool-neutral — SKILL.md, scripts/, references/
└── vendor/claude-code/plugins/<name>/ # Claude Code-specific adapter
├── .claude-plugin/plugin.json
├── skills/<name> -> ../../../../../skills/<name> (symlink, not a copy)
├── commands/setup.md # -> /<name>:setup
├── permissions-whitelist.template.json
└── hooks/ # git-manager only, currently
skills/<name>/is the only place skill content lives. It assumes nothing about the host tool: config is read from~/.agent-skills/<name>/config.json, never a Claude-specific path, so the sameSKILL.mdworks unmodified in other Agent-Skills-compatible tools.vendor/claude-code/plugins/<name>/holds everything Claude Code-specific: plugin manifest,Bash(...)permission whitelist, the/setupslash command, and (for git-manager)PreToolUsehooks. Itsskills/<name>is a symlink back into the top-levelskills/dir — never a duplicated copy that can drift..claude-plugin/marketplace.jsonlists both plugins and must live at the repo root (Claude Code convention, notvendor/claude-code/).
Adding a third host tool (e.g. Codex) means adding a new vendor/<tool>/
tree symlinked back to the same skills/<name>/, 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/<name>/config.jsonby 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/<name>/bin/, never Claude Code's internal plugin cache path (~/.claude/plugins/cache/.../<version>/), which changes on every plugin version bump. This is why eachsetup.shdoes "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$HOMEpath. - 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
PreToolUsehooks are advisory, never blocking. They only ever emitpermissionDecision: "ask"(never"deny", never"allow") — seeforce-ask-on-chaining.shandforce-ask-on-raw-git.shinvendor/claude-code/plugins/git-manager/hooks/. Ifjqis missing they fail open (no output) rather than block every Bash call.
Adding a new skill
Documented in full in ARCHITECTURE.md; short version:
skills/<new-name>/withSKILL.md(+scripts//references/as needed). Config path:~/.agent-skills/<new-name>/config.json.vendor/claude-code/plugins/<new-name>/with.claude-plugin/plugin.json, askills/<new-name>symlink back to the top-level skill dir, and — if the skill needs restricted shell access —commands/setup.mdpluspermissions-whitelist.template.jsonfollowing the existing plugins' pattern (wrapper scripts that resolve scope from config, whitelisted only by exact absolute path).- Add an entry to
.claude-plugin/marketplace.json. - Bump
versionin both the plugin'splugin.jsonand itsmarketplace.jsonentry on any update.
Gotchas specific to this repo
- Symlinks (
vendor/.../skills/<name> -> ../../../../../skills/<name>) assume POSIX; this repo doesn't try to be Windows-checkout-friendly. permissions-whitelist.template.jsonfiles are reference/documentation only — the actual rules are generated and written by each plugin'sscripts/setup.sh, not read from the template at runtime.- git-manager's wrapper deliberately rejects
stash,merge,rebase,reset, and arbitrarygit config— these can rewrite/discard history or working-tree state irreversibly. Rawgitremains a valid, visible fallback for those (nudged, not blocked, byforce-ask-on-raw-git.shif that hook is installed).