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
4 changes: 2 additions & 2 deletions .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
{
"$schema": "https://anthropic.com/claude-code/marketplace.schema.json",
"name": "greybeard",
"description": "Greybeard \u2014 Karpathy-inspired coding-agent guidelines, the skillify / check-resolvable meta-skills, the verify-responsive practice skill, and the /pressure-test, /sidenote, /visualize and /wrap commands.",
"description": "Karpathy-inspired coding-agent guidelines and seven portable skills: skillify, check-resolvable, verify-responsive, pressure-test, sidenote, visualize, and wrap.",
"owner": {
"name": "Clawnify",
"url": "https://github.com/clawnify"
},
"plugins": [
{
"name": "greybeard",
"description": "skillify + check-resolvable + verify-responsive skills and the /pressure-test, /sidenote, /visualize and /wrap commands. Pair with the npx installer for the always-on guidelines.",
"description": "Karpathy-inspired coding-agent guidelines and seven portable skills: skillify, check-resolvable, verify-responsive, pressure-test, sidenote, visualize, and wrap.",
"source": "./",
"category": "productivity"
}
Expand Down
2 changes: 1 addition & 1 deletion .claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "greybeard",
"description": "Karpathy-inspired coding-agent skills: skillify (capture repeated work), check-resolvable (keep skills DRY + MECE), verify-responsive (render a UI at real viewport widths), and the /pressure-test, /sidenote, /visualize and /wrap commands.",
"description": "Karpathy-inspired coding-agent guidelines and seven portable skills: skillify, check-resolvable, verify-responsive, pressure-test, sidenote, visualize, and wrap.",
"author": {
"name": "Clawnify",
"url": "https://github.com/clawnify"
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/sync.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ jobs:
node-version: '20'
# Fail if any harness rule copy drifted from CLAUDE.md (the single source).
- run: node scripts/build-rules.js --check
- run: npm test
# Smoke-test the installer: it must list agents and dry-run without crashing.
- run: node bin/install.js --list
- run: node bin/install.js --all --dry-run
58 changes: 36 additions & 22 deletions README.md

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions bin/install.js
Original file line number Diff line number Diff line change
Expand Up @@ -149,11 +149,11 @@ function rel(p) { return p.startsWith(HOME) ? '~' + p.slice(HOME.length) : path.
// The canonical principles payload for fenced installs = the generated AGENTS.md.
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'];
const SKILLS = ['skillify', 'check-resolvable', 'verify-responsive', '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/.
// add here for a new command — the plugin picks it up from skills/.
const LEGACY_COMMAND_COPIES = ['pressure-test', 'sidenote', 'visualize', 'wrap', 'scalable'];

// ── Per-provider install / uninstall ────────────────────────────────────────
Expand Down
4 changes: 2 additions & 2 deletions hooks/greybeard-activate.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ function alreadyLoaded(cwd) {
return false;
}

// The condensed behavioral core: the three pillars (from commands/
// pressure-test.md) plus the highest-frequency stances from CLAUDE.md
// The condensed behavioral core: the three pillars (from
// skills/pressure-test/SKILL.md) plus the highest-frequency stances from CLAUDE.md
// (opinionated tradeoffs, symptom-vs-diagnosis, stale time budgets). Keep
// wording in sync with those files — and keep this block small: it is paid
// as context in every session and subagent, so a rule earns a paragraph
Expand Down
4 changes: 2 additions & 2 deletions hooks/greybeard-opencode.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@
// Either string marks a file as already carrying the guidelines.
const MARKERS = ['<!-- karpathy-skills:start -->', '## 1. Think Before Coding'];

// The condensed behavioral core: the three pillars (from commands/
// pressure-test.md) plus the highest-frequency stances from CLAUDE.md
// The condensed behavioral core: the three pillars (from
// skills/pressure-test/SKILL.md) plus the highest-frequency stances from CLAUDE.md
// (opinionated tradeoffs, symptom-vs-diagnosis, stale time budgets). Keep
// wording in sync with hooks/greybeard-activate.js — and keep this block
// small: it is paid as context in every session, so a rule earns a paragraph
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@clawnify/greybeard",
"version": "0.4.0",
"description": "Greybeard \u2014 Karpathy-inspired guardrails that make your AI coding agent behave like a principled senior engineer. Guidelines + the skillify / check-resolvable / verify-responsive skills + the /pressure-test, /sidenote, /visualize and /wrap commands, installable across every AI coding agent.",
"description": "Greybeard — Karpathy-inspired coding-agent guidelines and seven portable skills: skillify, check-resolvable, verify-responsive, pressure-test, sidenote, visualize, and wrap.",
"license": "MIT",
"homepage": "https://github.com/clawnify/greybeard",
"repository": {
Expand All @@ -19,14 +19,14 @@
},
"scripts": {
"build": "node scripts/build-rules.js",
"test": "node scripts/check-skills.js",
"check-sync": "node scripts/build-rules.js --check"
},
"files": [
"assets/",
"bin/",
"scripts/",
"skills/",
"commands/",
"hooks/",
"CLAUDE.md",
"AGENTS.md",
Expand Down
43 changes: 43 additions & 0 deletions scripts/check-skills.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#!/usr/bin/env node
'use strict';

// Package-level regression for #43. Native client discovery is checked separately
// using the procedure in skills/README.md.
const assert = require('node:assert/strict');
const fs = require('node:fs');
const os = require('node:os');
const path = require('node:path');
const { execFileSync } = require('node:child_process');

const root = path.resolve(__dirname, '..');
const skills = ['check-resolvable', 'pressure-test', 'sidenote', 'skillify', 'verify-responsive', 'visualize', 'wrap'];
const temp = fs.mkdtempSync(path.join(os.tmpdir(), 'greybeard-package-'));

try {
const [pkg] = JSON.parse(execFileSync('npm', [
'pack', '--dry-run', '--json', '--ignore-scripts', '--cache', temp,
], { cwd: root, encoding: 'utf8' }));
const files = new Set(pkg.files.map(file => file.path));
const entries = skills.map(name => `skills/${name}/SKILL.md`);
assert.deepEqual([...files].filter(file => /^skills\/[^/]+\/SKILL\.md$/.test(file)).sort(), entries);
assert.equal([...files].some(file => file.startsWith('commands/')), false, 'Duplicate command entrypoints in package');
const commands = path.join(root, 'commands');
assert.ok(!fs.existsSync(commands) || fs.readdirSync(commands).length === 0, 'Plugin would still discover command entrypoints');

for (const [index, entry] of entries.entries()) {
const text = fs.readFileSync(path.join(root, entry), 'utf8');
assert.equal(text.match(/^name: (.+)$/m)?.[1], skills[index], `${entry}: discovery name`);
assert.match(text, /^description: .+/m, `${entry}: discovery description`);
// Ignore code examples and external/anchor links. Sibling skill references
// must survive distribution, without depending on the source checkout.
const prose = text.replace(/^```[^\n]*\n[\s\S]*?^```/gm, '');
for (const [, target] of prose.matchAll(/\[[^\]]*\]\(([^)]+)\)/g)) {
if (/^(?:[a-z]+:|#)/i.test(target)) continue;
const resolved = path.posix.normalize(path.posix.join(path.posix.dirname(entry), target.split('#')[0]));
assert.ok(files.has(resolved), `${entry}: unpackaged reference ${target}`);
}
}
console.log('Seven portable skill entrypoints; no duplicate commands; all skill references packaged.');
} finally {
fs.rmSync(temp, { recursive: true, force: true });
}
34 changes: 22 additions & 12 deletions skills/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,17 @@ Procedures captured with `skillify`. Each one exists because reasoning about the
|-------|--------------|
| [`verify-responsive`](./verify-responsive/SKILL.md) | Render the real page at real viewport widths in fixed-width iframes, then drive and assert it from the parent. |

## On-demand workflows

| Skill | Use when |
|-------|----------|
| [`pressure-test`](./pressure-test/SKILL.md) | Pressure-test a decision, or audit every approach proposed in the session when no target is supplied. |
| [`sidenote`](./sidenote/SKILL.md) | Park a thought without acting on it, or list open notes when no thought is supplied. |
| [`visualize`](./visualize/SKILL.md) | Draw the verified structure of code or a flow; no input means the current topic. |
| [`wrap`](./wrap/SKILL.md) | Decide whether a session can end and prepare a durable handoff; no input means the whole session. |

These are the canonical instruction sources. Claude Code exposes them as slash commands directly, so there are no duplicate command wrappers. Inputs come from the invoking message in any client; the substantive procedures and authorization boundaries are the same.

## The decision test

Behind every choice these skills make — *should I skillify this? generalize how far? merge or keep separate?* — is one question:
Expand All @@ -36,22 +47,21 @@ The library is only as valuable as the resolver is clean:

## Install

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).
Use the plugin manager for [Claude Code or Codex](../README.md#install). Both discover all seven skills from this directory. Claude Code also exposes `/greybeard:<name>`; Codex offers `$greybeard:<name>` through its skill selector and can match plain-language requests. Restart the client after a plugin update and verify each capability appears once in a fresh session.

The standalone installer writes guidelines for Codex, manages the Claude Code plugin, and copies all seven skills for OpenClaw. Writing `AGENTS.md` alone does not install skills. See the [installation and update instructions](../README.md#install) for the distinction.

**Claude Code** installs them as part of the greybeard plugin, through its own plugin manager — no copying, and `claude plugin update` keeps them current:
For a **manual, project-local Codex installation**, run this from the Greybeard checkout, replacing the destination with your project path:

```bash
claude plugin marketplace add clawnify/greybeard
claude plugin install greybeard@greybeard
mkdir -p /path/to/project/.agents/skills
cp -R skills/skillify skills/check-resolvable skills/verify-responsive \
skills/pressure-test skills/sidenote skills/visualize skills/wrap \
/path/to/project/.agents/skills/
```

(`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.
Copy all seven sibling folders together so relative skill links keep working after installation. Other clients need their own documented skill directory. Use either a manual copy or the plugin for a given client, not both. In clients without automatic skill indexing, add one entry per skill to the project's resolver, pointing to its installed `SKILL.md`.

**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/ # or .claude/skills/ for one project
```
## Verify discovery after changing the package

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.
Check a fresh install and an update from the previous revision using each client's plugin manager. Repeating the install must not add another entry. In Codex, query the app server's `skills/list` for a clean project and verify the seven enabled `greybeard:*` entries point into the installed plugin cache. In Claude Code, `claude plugin details greybeard@greybeard` must list seven skills once each; also check slash invocation with and without input. Inspect installed relative links, then run `check-resolvable` across the seven descriptions and procedures. Source frontmatter validation alone does not prove discovery.
7 changes: 5 additions & 2 deletions commands/pressure-test.md → skills/pressure-test/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
---
description: Pressure-test the current approach against scalability, long-term, and efficiency, then recommend the best option.
name: pressure-test
description: "Pressure-test the current approach against scalability, long-term, and efficiency, then recommend the best option. Use when the user asks to pressure-test a decision or audit the session's approaches."
argument-hint: [decision or approach to test — optional]
---

Run the decision test on **$ARGUMENTS** — or, if that's empty, on every solution this conversation has proposed.
The optional input is the text supplied with this skill invocation, through a slash command or a plain-language request. If none was supplied, follow the no-input behavior below.

Run the decision test on **the supplied input** — or, if that's empty, on every solution this conversation has proposed.

**With no argument, sweep — don't zoom.** The targeted form asks *is this the right call?* about one decision; the no-argument form asks *did we miss anything?* about all of them. Same evidence standard, same ledger — a wider surface, not a softer test.

Expand Down
9 changes: 6 additions & 3 deletions commands/sidenote.md → skills/sidenote/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
---
description: Park a passing thought as a later-task without derailing the current one — logged, not acted on.
name: sidenote
description: "Park a passing thought as a later-task without derailing the current one — logged, not acted on. Use when the user asks to park a thought, sends SIDENOTE:, or wants to list parked notes."
argument-hint: [thought to park — optional]
---

Park this thought — **$ARGUMENTS** — as a sidenote. A sidenote is a *later-task*, not a *now-task*. Your job is to record it and keep going, nothing more.
The optional input is the text supplied with this skill invocation, through a slash command or a plain-language request. If none was supplied, follow the no-input behavior below.

Park this thought — **the supplied input** — as a sidenote. A sidenote is a *later-task*, not a *now-task*. Your job is to record it and keep going, nothing more.

**The contract — hold it exactly:**

Expand All @@ -28,4 +31,4 @@ Park this thought — **$ARGUMENTS** — as a sidenote. A sidenote is a *later-t

Then: **one-line acknowledgement** (e.g. `Parked in the sidenotes file — continuing.`) and **resume exactly where you left off**, as if the note had never arrived.

**If `$ARGUMENTS` is empty** (a bare `/sidenote`), there's nothing to park — treat it as a *flush*: read the parking file (same location as above; if a legacy `.claude/sidenotes.md` also exists at the project root, read that too and say so, so its entries can be moved) and list the **open** entries back to me so I can decide what to pick up. Open means *not checked off* — every entry except the ones marked done (`- [x]`), so older bullets without a checkbox still count. Change nothing, act on nothing, then resume. (Checking an item off to `- [x]` happens when it's actually handled — not part of parking or flushing.)
**If no input was supplied** (a bare `/sidenote`), there's nothing to park — treat it as a *flush*: read the parking file (same location as above; if a legacy `.claude/sidenotes.md` also exists at the project root, read that too and say so, so its entries can be moved) and list the **open** entries back to me so I can decide what to pick up. Open means *not checked off* — every entry except the ones marked done (`- [x]`), so older bullets without a checkbox still count. Change nothing, act on nothing, then resume. (Checking an item off to `- [x]` happens when it's actually handled — not part of parking or flushing.)
2 changes: 1 addition & 1 deletion skills/verify-responsive/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,4 @@ So a layout that branches on width is covered honestly. One that branches on `@m

## Related

- [`/visualize`](../../commands/visualize.md) draws the *structure* of the code. This renders the *running UI*. Use this one when the question is "what does it actually look like at this width."
- [`/visualize`](../visualize/SKILL.md) draws the *structure* of the code. This renders the *running UI*. Use this one when the question is "what does it actually look like at this width."
7 changes: 5 additions & 2 deletions commands/visualize.md → skills/visualize/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
---
description: Draw the thing under discussion — the real shape, from the real code, anchored to file:line.
name: visualize
description: "Draw the thing under discussion — the real shape, from the real code, anchored to file:line. Use when the user asks to visualize code, architecture, or a flow; use verify-responsive to check a running UI at a viewport width."
argument-hint: [what to visualize — optional]
---

Show me **$ARGUMENTS** visually — or, if that's empty, whatever is currently on the table in this conversation. Skip the preamble, keep the prose short, and pick the *smallest* view that makes the point.
The optional input is the text supplied with this skill invocation, through a slash command or a plain-language request. If none was supplied, follow the no-input behavior below.

Show me **the supplied input** visually — or, if that's empty, whatever is currently on the table in this conversation. Skip the preamble, keep the prose short, and pick the *smallest* view that makes the point.

**Draw what's there, not what you'd expect to be there.** A diagram launders a guess better than prose ever could: boxes and arrows read as *verified* even when nobody opened the file. Prose hedges out loud ("it probably calls…"); a flowchart just asserts. That makes an ungrounded visual the most expensive thing you can hand someone — they'll build on it for weeks.

Expand Down
7 changes: 5 additions & 2 deletions commands/wrap.md → skills/wrap/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
---
description: Rule on whether this session can end here — finish the loop now, or make the work durable enough that another agent can pick it up.
name: wrap
description: "Rule on whether this session can end here — finish the loop now, or make the work durable enough that another agent can pick it up. Use when the user asks to wrap up, hand off, or decide whether this session or worktree can be closed."
argument-hint: [what to wrap — optional]
---

Answer one question about **$ARGUMENTS** — or, if that's empty, about this whole session: **can I close this and delete the worktree?**
The optional input is the text supplied with this skill invocation, through a slash command or a plain-language request. If none was supplied, follow the no-input behavior below.

Answer one question about **the supplied input** — or, if that's empty, about this whole session: **can I close this and delete the worktree?**

There are two rulings, and you owe me exactly one of them: **finish it now**, or **wrap it** — turn what's here into something durable and tell me it's safe to delete. "Mostly done, maybe keep it around" is not a ruling — it is the third outcome, the one you get by declining to choose, and it is the most expensive of the three: a worktree nobody will reopen and nobody dares delete.

Expand Down
Loading