diff --git a/README.md b/README.md
index 14a1377..1ee7c2f 100644
--- a/README.md
+++ b/README.md
@@ -196,17 +196,21 @@ They're standard [Claude Code Agent Skills](https://code.claude.com/docs/en/skil
Greybeard's slash commands are **`/pressure-test`**, **`/sidenote`**, **`/visualize`** and **`/wrap`** — one guards the *quality* of a decision, one guards your *focus* while you make it, one makes sure what you're shown is the code that's actually there, and one decides whether the session can end. The skills and always-on guidelines back them up, but these are the four you'll reach for by hand.
-## The `/pressure-test` command
-
-[`commands/pressure-test.md`](./commands/pressure-test.md) is a Claude Code slash command that runs the §5 decision test on demand: pressure-test the approach on the table against **scalable / long-term / efficient** — *whatever is scalable, long term, and cannot be done in a more efficient way* — and get one decisive recommendation, with stale-time-budget shortcuts called out. The main context runs the test and owns the ruling; subagents are scoped instruments, spawned only where a check benefits from being outside the session — challenging a belief the conversation already holds (a fresh context can't inherit its anchoring), or online research and docs verification that parallelize while the main agent keeps reasoning. Never the test wholesale: a main agent that only orchestrates stops being the main brain. Every ruling ships with a claims ledger: each load-bearing claim — one per plan item at minimum, every size-word counts — cited to the `file:line` actually opened, or tagged **ASSUMED**; no estimate may attach to an assumed claim.
+**Getting them.** The commands ship inside the greybeard plugin, so Claude Code installs and updates them through its own plugin manager — `npx @clawnify/greybeard` does it for you, or do it by hand:
```bash
-mkdir -p ~/.claude/commands
-cp commands/pressure-test.md ~/.claude/commands/ # personal, all projects
-# or: .claude/commands/ for one project
+claude plugin marketplace add clawnify/greybeard
+claude plugin install greybeard@greybeard
+claude plugin update greybeard@greybeard # later, to pull new commands
```
-Then `/pressure-test ` to test one call in depth, or bare `/pressure-test` to sweep every solution the session proposed — an inventory with a verdict on each (the holds stated as plainly as the fails), plus the section only the sweep produces: the decisions that were never examined at all.
+Don't also copy the files into `~/.claude/commands/`: you'd have two copies of every command in the resolver, drifting apart the moment one side updates. Plugin commands are namespaced — `/greybeard:pressure-test`, `/greybeard:wrap` — so the bare names used below are how they read in a harness where you drop the file in yourself.
+
+## The `/pressure-test` command
+
+[`commands/pressure-test.md`](./commands/pressure-test.md) is a Claude Code slash command that runs the §5 decision test on demand: pressure-test the approach on the table against **scalable / long-term / efficient** — *whatever is scalable, long term, and cannot be done in a more efficient way* — and get one decisive recommendation, with stale-time-budget shortcuts called out. The main context runs the test and owns the ruling; subagents are scoped instruments, spawned only where a check benefits from being outside the session — challenging a belief the conversation already holds (a fresh context can't inherit its anchoring), or online research and docs verification that parallelize while the main agent keeps reasoning. Never the test wholesale: a main agent that only orchestrates stops being the main brain. Every ruling ships with a claims ledger: each load-bearing claim — one per plan item at minimum, every size-word counts — cited to the `file:line` actually opened, or tagged **ASSUMED**; no estimate may attach to an assumed claim.
+
+Use `/pressure-test ` to test one call in depth, or bare `/pressure-test` to sweep every solution the session proposed — an inventory with a verdict on each (the holds stated as plainly as the fails), plus the section only the sweep produces: the decisions that were never examined at all.
## The `/sidenote` command
@@ -214,13 +218,7 @@ You're mid-way through a big task and a passing thought hits — *"the retry log
[`commands/sidenote.md`](./commands/sidenote.md) reframes that thought as a **parked later-task, not a now-task**. The agent logs it verbatim to a parking file (durable across compaction, session end, and worktree removal) — with a one-line anchor (task in progress, the file it points at, branch) so it still makes sense when another session or agent reads it cold — gives a one-line ack, and resumes *exactly* where it was — same scope, same pace, no cutting corners to reach the note. A bare `/sidenote` flushes the open list back to you.
-```bash
-mkdir -p ~/.claude/commands
-cp commands/sidenote.md ~/.claude/commands/ # personal, all projects
-# or: .claude/commands/ for one project
-```
-
-Then `/sidenote ` to park one, or `/sidenote` to see what's parked. The notes land in `$(git rev-parse --path-format=absolute --git-common-dir)/info/sidenotes.md` — inside `.git/`, so they are personal per-repo scratch that no ignore rule has to protect and no `git add -A` can sweep into a PR, shared across all worktrees of the repo. Outside a git repo the fallback is `.claude/sidenotes.md` at the project root.
+Use `/sidenote ` to park one, or `/sidenote` to see what's parked. The notes land in `$(git rev-parse --path-format=absolute --git-common-dir)/info/sidenotes.md` — inside `.git/`, so they are personal per-repo scratch that no ignore rule has to protect and no `git add -A` can sweep into a PR, shared across all worktrees of the repo. Outside a git repo the fallback is `.claude/sidenotes.md` at the project root.
> **No slash commands?** For agents that read a rule file but have no `/` commands (Cursor, Codex, Copilot…), the same contract works as a plain-text convention: prefix the message with `SIDENOTE:` and the agent parks it instead of acting. Add one line to your rule file so it's honored reliably — see [`commands/sidenote.md`](./commands/sidenote.md) for the exact contract.
@@ -230,13 +228,7 @@ Ask an agent to explain how something works and you get paragraphs. Ask it to *d
[`commands/visualize.md`](./commands/visualize.md) is the §7 rule applied to pictures. It gives the agent a menu of visual forms — pseudocode, call tree, component tree, file tree, text sequence diagrams, shaped `diff`s, or one focused HTML file — and tells it to pick the *smallest* one that makes the point, **matched to the surface it lands on**: a terminal has no diagram renderer, so a `mermaid` fence there just prints its own source; the text forms are the picture already. Mermaid is reserved for surfaces that actually render it. Then the Greybeard part: **read the code before drawing it** (the actual files, this session — if the diagram has five nodes, you opened five things), **anchor every node** to a real `file:line`, **trace the current shape with the real input** (an anchor proves the node exists, not that the arrow fires — if the concrete key dead-ends in the registry, the dead end is the picture), and **tag anything unread as ASSUMED** with the check that would settle it. A diagram that's 90% verified and 10% quietly invented is 100% untrustworthy.
-```bash
-mkdir -p ~/.claude/commands
-cp commands/visualize.md ~/.claude/commands/ # personal, all projects
-# or: .claude/commands/ for one project
-```
-
-Then `/visualize `, or a bare `/visualize` to draw whatever's currently on the table.
+Use `/visualize `, or a bare `/visualize` to draw whatever's currently on the table.
## The `/wrap` command
@@ -244,13 +236,7 @@ The question at the end of every session: *can I close this and delete the workt
[`commands/wrap.md`](./commands/wrap.md) makes the agent rule on it, and there are only two rulings: **finish it now**, or **wrap it** — turn what's here into something durable and say it's safe to delete. The test isn't *is this important*, it's **what dies when the worktree dies**: uncommitted edits, untracked files, commits that never left the machine, and the part no diff carries — what you tried, what you rejected, and why. Four hard stops force *finish now* (the world outside the repo is mid-change; the trunk is worse than you found it; what's left is minutes of work — §5's pre-AI estimate again; or you can't write the brief, which means you don't understand the state well enough to hand it off). Otherwise it picks the cheapest durable form that actually carries the work — a `/sidenote` entry, a committed doc, a GitHub issue, or the branch pushed with a draft PR — writes the five-line brief (goal / done / left / **rejected** / verify, anchored to `file:line`), shows you the artifact before creating it, and confirms it landed *outside* the worktree before answering. An unpushed commit is not a handoff.
-```bash
-mkdir -p ~/.claude/commands
-cp commands/wrap.md ~/.claude/commands/ # personal, all projects
-# or: .claude/commands/ for one project
-```
-
-Then a bare `/wrap` at the point you'd otherwise close the tab. It ends on the verdict line — `Safe to close — carries it.` or `Not yet — has to close here first.` — and hands you the `git worktree remove` command rather than running it, since it's standing in the worktree it would delete.
+Reach for a bare `/wrap` at the point you'd otherwise close the tab. It ends on the verdict line — `Safe to close — carries it.` or `Not yet — has to close here first.` — and hands you the `git worktree remove` command rather than running it, since it's standing in the worktree it would delete.
## Install
@@ -260,7 +246,7 @@ Then a bare `/wrap` at the point you'd otherwise close the tab. It ends on the v
npx @clawnify/greybeard
```
-It detects the AI coding agents you actually use and installs the right files for each, at two tiers: what's **installed on your system** (Claude Code, OpenCode, OpenClaw) gets its global files once — the guidelines into `~/.claude/CLAUDE.md`, the `skillify` / `check-resolvable` / `verify-responsive` skills, the `/pressure-test`, `/sidenote`, `/visualize` and `/wrap` commands, and the OpenCode guidelines plugin; what's **used in this repo** (its rule file or directory exists here — `AGENTS.md`, `GEMINI.md`, `copilot-instructions.md`, `.cursor/`, `.windsurf/`, `.clinerules/`) gets the seven guidelines in the format it reads. Having an agent installed on your machine never sprinkles rule files into repos that don't use it — adopt one there with `--all` or `--only `. Shared files are edited between markers, so re-running is a safe no-op and your own content is preserved.
+It detects the AI coding agents you actually use and installs the right files for each, at two tiers: what's **installed on your system** (Claude Code, OpenCode, OpenClaw) gets its global files once — the guidelines into `~/.claude/CLAUDE.md`, the greybeard plugin installed (or updated) through Claude Code's own `claude plugin` CLI, which is where its skills and commands live, the `skillify` / `check-resolvable` / `verify-responsive` skills for OpenClaw, and the OpenCode guidelines plugin; what's **used in this repo** (its rule file or directory exists here — `AGENTS.md`, `GEMINI.md`, `copilot-instructions.md`, `.cursor/`, `.windsurf/`, `.clinerules/`) gets the seven guidelines in the format it reads. Having an agent installed on your machine never sprinkles rule files into repos that don't use it — adopt one there with `--all` or `--only `. Shared files are edited between markers, so re-running is a safe no-op and your own content is preserved.
```bash
npx @clawnify/greybeard --list # show detected agents
diff --git a/bin/install.js b/bin/install.js
index 2dc1d20..c359ccf 100644
--- a/bin/install.js
+++ b/bin/install.js
@@ -4,9 +4,10 @@
// greybeard — cross-platform installer.
//
// Detects the AI coding agents on your machine / in this project and installs
-// the Karpathy-inspired guidelines (+ Claude Code skills and the /pressure-test,
-// /sidenote, /visualize and /wrap commands) into each one's rule location. Pure Node
-// stdlib, zero runtime deps.
+// the Karpathy-inspired guidelines into each one's rule location. Claude Code's
+// skills and commands come from the greybeard plugin, installed and kept current
+// through Claude Code's own `claude plugin` CLI. Pure Node stdlib, zero runtime
+// deps.
//
// npx @clawnify/greybeard # install for every detected agent
// npx @clawnify/greybeard --all # install for all agents, detected or not
@@ -17,6 +18,7 @@
const fs = require('fs');
const os = require('os');
const path = require('path');
+const { spawnSync } = require('child_process');
const PKG = path.resolve(__dirname, '..');
const HOME = os.homedir();
@@ -39,7 +41,7 @@ const HANDMERGE_MARK = '## 1. Think Before Coding';
// installed on the machine must not sprinkle rule files into every repo.
// Use --all or --only to adopt an agent in a repo it doesn't use yet.
// kind: how to install —
-// claude global ~/.claude: principles block + skills/ + commands/
+// claude global ~/.claude: principles block + the greybeard plugin
// openclaw global ~/.openclaw/workspace/skills: skills only
// opencode global ~/.config/opencode/plugins: guidelines plugin
// file project: write the dedicated rule file verbatim (overwrite)
@@ -148,8 +150,11 @@ function rel(p) { return p.startsWith(HOME) ? '~' + p.slice(HOME.length) : path.
const PRINCIPLES = fs.readFileSync(path.join(PKG, 'AGENTS.md'), 'utf8');
// Skill directories shipped to every provider that reads skills. Add a new one here only.
const SKILLS = ['skillify', 'check-resolvable', 'verify-responsive'];
-// Slash commands shipped to every provider that reads commands. Add a new one here only.
-const COMMANDS = ['pressure-test', 'sidenote', 'visualize', 'wrap'];
+// Command files earlier versions copied into ~/.claude/commands (plus 'scalable',
+// the pre-rename name of pressure-test). The plugin ships these now; this list
+// exists only to clear the copies off machines that still carry them. Nothing to
+// add here for a new command — the plugin picks it up from commands/.
+const LEGACY_COMMAND_COPIES = ['pressure-test', 'sidenote', 'visualize', 'wrap', 'scalable'];
// ── Per-provider install / uninstall ────────────────────────────────────────
// The OpenCode plugin serves both `opencode` (V1) and `opencode2` (V2 beta):
@@ -161,20 +166,82 @@ function installOpencode(un) {
return writeFile(dest, fs.readFileSync(path.join(PKG, 'hooks', 'greybeard-opencode.js'), 'utf8'));
}
+// Claude Code gets the guidelines as a fenced block in ~/.claude/CLAUDE.md, and
+// everything else — skills and commands — from the greybeard plugin, through
+// Claude Code's own plugin manager. Earlier versions copied those files into
+// ~/.claude/skills and ~/.claude/commands as well; anyone who also had the
+// plugin ended up with two copies of every command in the resolver, which drift
+// the moment one channel updates. The copies are removed on every run, install
+// or uninstall, so an existing machine de-duplicates itself.
function installClaude(dir, un) {
+ for (const s of SKILLS) removePath(path.join(dir, 'skills', s));
+ for (const c of LEGACY_COMMAND_COPIES) removePath(path.join(dir, 'commands', `${c}.md`));
if (un) {
removeFenceFrom(path.join(dir, 'CLAUDE.md'));
- for (const s of SKILLS) removePath(path.join(dir, 'skills', s));
- removePath(path.join(dir, 'commands', 'scalable.md')); // legacy name, pre-rename
- for (const c of COMMANDS) removePath(path.join(dir, 'commands', `${c}.md`));
- return;
+ return ensurePlugin(true);
}
fenceInto(path.join(dir, 'CLAUDE.md'), PRINCIPLES);
- for (const s of SKILLS) copyDir(path.join(PKG, 'skills', s), path.join(dir, 'skills', s));
- removePath(path.join(dir, 'commands', 'scalable.md')); // clean up the pre-rename command from prior installs
- for (const c of COMMANDS) writeFile(path.join(dir, 'commands', `${c}.md`), fs.readFileSync(path.join(PKG, 'commands', `${c}.md`), 'utf8'));
+ return ensurePlugin(false);
+}
+
+// ── The Claude Code plugin channel ──────────────────────────────────────────
+const PLUGIN_ID = 'greybeard@greybeard';
+const MARKETPLACE_SRC = 'clawnify/greybeard'; // what `marketplace add` takes
+const MARKETPLACE_NAME = 'greybeard'; // what it's called once added
+
+const lastLine = (s) => (s || '').split('\n').filter(Boolean).pop() || 'no output';
+
+function claudeCli(args) {
+ const r = spawnSync('claude', args, { encoding: 'utf8', shell: process.platform === 'win32' });
+ if (r.error) return { missing: r.error.code === 'ENOENT' };
+ return { ok: r.status === 0, out: `${r.stdout || ''}${r.stderr || ''}`.trim() };
+}
+
+// Installed version at user scope, or null. Read from --json rather than matched
+// out of human output, so a wording change upstream can't silently break this.
+function installedVersion(probe) {
+ if (!probe.ok) return null;
+ try {
+ const hit = JSON.parse(probe.out).find((p) => p.id === PLUGIN_ID && p.scope === 'user');
+ return hit ? hit.version : null;
+ } catch (e) {
+ return null;
+ }
+}
+
+function ensurePlugin(un) {
+ const probe = claudeCli(['plugin', 'list', '--json']);
+ if (probe.missing) {
+ warnings.push(un
+ ? `claude CLI not on PATH — run \`claude plugin uninstall ${PLUGIN_ID}\` to remove the skills and commands.`
+ : `claude CLI not on PATH — run \`claude plugin marketplace add ${MARKETPLACE_SRC}\` then \`claude plugin install ${PLUGIN_ID}\` to get the skills and commands.`);
+ return;
+ }
+ const before = installedVersion(probe);
+ if (un) {
+ if (!before) return;
+ if (DRY) { actions.push('would uninstall the greybeard plugin'); return; }
+ const r = claudeCli(['plugin', 'uninstall', PLUGIN_ID, '-y', '--scope', 'user']);
+ if (!r.ok) return warnings.push(`claude plugin uninstall ${PLUGIN_ID} failed: ${lastLine(r.out)}`);
+ actions.push('uninstalled the greybeard plugin');
+ return;
+ }
+ if (DRY) { actions.push(`would ${before ? 'update' : 'install'} the greybeard plugin`); return; }
+ const steps = before
+ ? [['plugin', 'marketplace', 'update', MARKETPLACE_NAME], ['plugin', 'update', PLUGIN_ID, '-y', '--scope', 'user']]
+ : [['plugin', 'marketplace', 'add', MARKETPLACE_SRC], ['plugin', 'install', PLUGIN_ID, '-y', '--scope', 'user']];
+ for (const args of steps) {
+ const r = claudeCli(args);
+ if (!r.ok) return warnings.push(`claude ${args.join(' ')} failed: ${lastLine(r.out)}`);
+ }
+ // Report only a real change, so a re-run stays a true no-op in the summary.
+ const after = installedVersion(claudeCli(['plugin', 'list', '--json']));
+ if (after && after !== before) {
+ actions.push(`${before ? 'updated' : 'installed'} the greybeard plugin \u2192 ${after} (restart Claude Code to apply)`);
+ }
}
+
function applyProvider(p, un) {
if (p.kind === 'claude') return installClaude(expand('~/.claude'), un);
if (p.kind === 'opencode') return installOpencode(un);
diff --git a/skills/README.md b/skills/README.md
index e478c39..e4030a1 100644
--- a/skills/README.md
+++ b/skills/README.md
@@ -38,18 +38,20 @@ The library is only as valuable as the resolver is clean:
These are standard [Claude Code Agent Skills](https://code.claude.com/docs/en/skills) — a directory with a `SKILL.md`. The `name` + `description` frontmatter is portable to any harness that reads skills (Cursor, Codex, OpenClaw).
-**Personal (all your projects):**
+**Claude Code** installs them as part of the greybeard plugin, through its own plugin manager — no copying, and `claude plugin update` keeps them current:
```bash
-mkdir -p ~/.claude/skills
-cp -R skillify check-resolvable verify-responsive ~/.claude/skills/
+claude plugin marketplace add clawnify/greybeard
+claude plugin install greybeard@greybeard
```
-**One project only:**
+(`npx @clawnify/greybeard` runs those two for you.) Don't also copy the directories into `~/.claude/skills/` — that puts two copies of every skill in the resolver, which is the exact thing `check-resolvable` exists to catch.
+
+**Any other harness** reads skills from a directory, so copy them in:
```bash
-mkdir -p .claude/skills
-cp -R skillify check-resolvable verify-responsive .claude/skills/
+mkdir -p ~/.claude/skills
+cp -R skillify check-resolvable verify-responsive ~/.claude/skills/ # or .claude/skills/ for one project
```
Then add a one-line entry for each to your project's resolver (`AGENTS.md` / `CLAUDE.md`) so the agent — and your teammates — can find them. After that, just say *"skillify this"* when you finish something worth keeping.