Add CLAUDE.md documenting repo architecture and conventions
This commit is contained in:
@@ -0,0 +1,129 @@
|
||||
# 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 <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
|
||||
same `SKILL.md` works unmodified in other Agent-Skills-compatible tools.
|
||||
- `vendor/claude-code/plugins/<name>/` holds everything Claude Code-specific:
|
||||
plugin manifest, `Bash(...)` permission whitelist, the `/setup` slash
|
||||
command, and (for git-manager) `PreToolUse` hooks. Its `skills/<name>` 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/<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.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/<name>/bin/`, never Claude Code's
|
||||
internal plugin cache path (`~/.claude/plugins/cache/.../<version>/`),
|
||||
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/<new-name>/` with `SKILL.md` (+ `scripts/`/`references/` as
|
||||
needed). Config path: `~/.agent-skills/<new-name>/config.json`.
|
||||
2. `vendor/claude-code/plugins/<new-name>/` with `.claude-plugin/plugin.json`,
|
||||
a `skills/<new-name>` 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/<name> -> ../../../../../skills/<name>`)
|
||||
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).
|
||||
Reference in New Issue
Block a user