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, and the /pressure-test, /sidenote and /visualize commands.",
"description": "Greybeard \u2014 Karpathy-inspired coding-agent guidelines, the skillify / check-resolvable meta-skills, the verify-responsive practice skill, and the /pressure-test, /sidenote and /visualize commands.",
"owner": {
"name": "Clawnify",
"url": "https://github.com/clawnify"
},
"plugins": [
{
"name": "greybeard",
"description": "skillify + check-resolvable skills and the /pressure-test, /sidenote and /visualize commands. Pair with the npx installer for the always-on guidelines.",
"description": "skillify + check-resolvable + verify-responsive skills and the /pressure-test, /sidenote and /visualize commands. Pair with the npx installer for the always-on guidelines.",
"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), and the /pressure-test, /sidenote and /visualize commands.",
"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 and /visualize commands.",
"author": {
"name": "Clawnify",
"url": "https://github.com/clawnify"
Expand Down
10 changes: 8 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

> Built and maintained by [Clawnify](https://clawnify.com) — a managed platform that provisions AI agents with WhatsApp / Telegram / Email and browser capabilities for non-technical users.

A single `CLAUDE.md` file to improve AI coding-agent behavior, derived from [Andrej Karpathy's observations](https://x.com/karpathy/status/2015883857489522876) on LLM coding pitfalls, plus three sections we added for the AI-assisted-coding era. Ships with two runnable [meta-skills](#skills-skillify-dry--mece-resolvers), its three commands — [`/pressure-test`](#the-pressure-test-command), the decision test, [`/sidenote`](#the-sidenote-command), park-a-thought, and [`/visualize`](#the-visualize-command), draw-the-real-shape — and a [one-command installer](#install) that fans it all out to every AI coding agent you use.
A single `CLAUDE.md` file to improve AI coding-agent behavior, derived from [Andrej Karpathy's observations](https://x.com/karpathy/status/2015883857489522876) on LLM coding pitfalls, plus three sections we added for the AI-assisted-coding era. Ships with three runnable [skills](#skills-skillify-dry--mece-resolvers), its three commands — [`/pressure-test`](#the-pressure-test-command), the decision test, [`/sidenote`](#the-sidenote-command), park-a-thought, and [`/visualize`](#the-visualize-command), draw-the-real-shape — and a [one-command installer](#install) that fans it all out to every AI coding agent you use.

## The Problems

Expand Down Expand Up @@ -184,6 +184,12 @@ The principle above ships as two runnable meta-skills in [`skills/`](./skills)
| [`skillify`](./skills/skillify/SKILL.md) | Capture what you just did as a reusable, parameterized skill, then register it in the resolver. |
| [`check-resolvable`](./skills/check-resolvable/SKILL.md) | Audit the whole library so it stays DRY and MECE — no duplicates, no overlaps, no gaps. |

And the **practice skills** those two produce — procedures captured with `skillify`, kept in the same index:

| Skill | What it does |
|-------|--------------|
| [`verify-responsive`](./skills/verify-responsive/SKILL.md) | Render the real page at real viewport widths in fixed-width iframes, then drive and assert it from the parent. |

They're standard [Claude Code Agent Skills](https://code.claude.com/docs/en/skills) (portable to Cursor, Codex, OpenClaw). Install and usage: [`skills/README.md`](./skills/README.md).

## The three commands
Expand Down Expand Up @@ -240,7 +246,7 @@ Then `/visualize <what to draw>`, or a bare `/visualize` to draw whatever's curr
npx @clawnify/greybeard
```

It detects the AI coding agents you actually use and installs the right files for each: the seven guidelines into the rule file each one reads (`CLAUDE.md`, `AGENTS.md`, `GEMINI.md`, `.cursor/rules/`, `.windsurf/rules/`, `.clinerules/`, `.github/copilot-instructions.md`), plus the `skillify` / `check-resolvable` skills for Claude Code and OpenClaw, and the `/pressure-test`, `/sidenote` and `/visualize` commands for Claude Code. 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: the seven guidelines into the rule file each one reads (`CLAUDE.md`, `AGENTS.md`, `GEMINI.md`, `.cursor/rules/`, `.windsurf/rules/`, `.clinerules/`, `.github/copilot-instructions.md`), plus the `skillify` / `check-resolvable` / `verify-responsive` skills for Claude Code and OpenClaw, and the `/pressure-test`, `/sidenote` and `/visualize` commands for Claude Code. 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
Expand Down
13 changes: 6 additions & 7 deletions bin/install.js
Original file line number Diff line number Diff line change
Expand Up @@ -138,22 +138,22 @@ 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'];

// ── Per-provider install / uninstall ────────────────────────────────────────
function installClaude(dir, un) {
if (un) {
removeFenceFrom(path.join(dir, 'CLAUDE.md'));
removePath(path.join(dir, 'skills', 'skillify'));
removePath(path.join(dir, 'skills', 'check-resolvable'));
for (const s of SKILLS) removePath(path.join(dir, 'skills', s));
removePath(path.join(dir, 'commands', 'scalable.md')); // legacy name, pre-rename
removePath(path.join(dir, 'commands', 'pressure-test.md'));
removePath(path.join(dir, 'commands', 'sidenote.md'));
removePath(path.join(dir, 'commands', 'visualize.md'));
return;
}
fenceInto(path.join(dir, 'CLAUDE.md'), PRINCIPLES);
copyDir(path.join(PKG, 'skills', 'skillify'), path.join(dir, 'skills', 'skillify'));
copyDir(path.join(PKG, 'skills', 'check-resolvable'), path.join(dir, 'skills', 'check-resolvable'));
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
writeFile(path.join(dir, 'commands', 'pressure-test.md'), fs.readFileSync(path.join(PKG, 'commands', 'pressure-test.md'), 'utf8'));
writeFile(path.join(dir, 'commands', 'sidenote.md'), fs.readFileSync(path.join(PKG, 'commands', 'sidenote.md'), 'utf8'));
Expand All @@ -164,9 +164,8 @@ function applyProvider(p, un) {
if (p.kind === 'claude') return installClaude(expand('~/.claude'), un);
if (p.kind === 'openclaw') {
const base = expand('~/.openclaw/workspace/skills');
if (un) { removePath(path.join(base, 'skillify')); removePath(path.join(base, 'check-resolvable')); return; }
copyDir(path.join(PKG, 'skills', 'skillify'), path.join(base, 'skillify'));
copyDir(path.join(PKG, 'skills', 'check-resolvable'), path.join(base, 'check-resolvable'));
if (un) { for (const s of SKILLS) removePath(path.join(base, s)); return; }
for (const s of SKILLS) copyDir(path.join(PKG, 'skills', s), path.join(base, s));
return;
}
if (p.kind === 'file') {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@clawnify/greybeard",
"version": "0.1.0",
"description": "Greybeard \u2014 Karpathy-inspired guardrails that make your AI coding agent behave like a principled senior engineer. Guidelines + skillify / check-resolvable skills + the /pressure-test, /sidenote and /visualize commands, installable across every AI coding agent.",
"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 and /visualize commands, installable across every AI coding agent.",
"license": "MIT",
"homepage": "https://github.com/clawnify/greybeard",
"repository": {
Expand Down
14 changes: 11 additions & 3 deletions skills/README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
# Skills: Skillify, DRY & MECE Resolvers

Two meta-skills that make a coding agent *compound* — turning repeated work into reusable skills, and keeping the skill library clean as it grows. Derived from the YC conversation with Pete Koomen & Andrej Karpathy on how AI-native organizations build a "shared organizational brain."
Two **meta-skills** that make a coding agent *compound* — turning repeated work into reusable skills, and keeping the skill library clean as it grows — plus the **practice skills** they produce. The meta-skills are derived from the YC conversation with Pete Koomen & Andrej Karpathy on how AI-native organizations build a "shared organizational brain"; the practice skills are what came out of running `skillify` on our own work.

| Skill | What it does |
|-------|--------------|
| [`skillify`](./skillify/SKILL.md) | Capture what you just did as a reusable, parameterized skill, then register it in the resolver. |
| [`check-resolvable`](./check-resolvable/SKILL.md) | Audit the whole library so it stays **DRY** (no duplicates) and **MECE** (no overlaps, no gaps). |

## Practice skills

Procedures captured with `skillify`. Each one exists because reasoning about the answer kept being wrong and rendering it was cheap.

| Skill | What it does |
|-------|--------------|
| [`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. |

## The decision test

Behind every choice these skills make — *should I skillify this? generalize how far? merge or keep separate?* — is one question:
Expand Down Expand Up @@ -34,14 +42,14 @@ These are standard [Claude Code Agent Skills](https://code.claude.com/docs/en/sk

```bash
mkdir -p ~/.claude/skills
cp -R skillify check-resolvable ~/.claude/skills/
cp -R skillify check-resolvable verify-responsive ~/.claude/skills/
```

**One project only:**

```bash
mkdir -p .claude/skills
cp -R skillify check-resolvable .claude/skills/
cp -R skillify check-resolvable verify-responsive .claude/skills/
```

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.
74 changes: 74 additions & 0 deletions skills/verify-responsive/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
---
name: verify-responsive
description: The default way to check a responsive UI — render the real page at real viewport widths in fixed-width iframes, then drive and assert it from the parent. Use whenever you need to see or verify a layout at a given width: a mobile or tablet breakpoint, or small-screen-only behavior. Reach for it first, ahead of resizing a window.
---

# Verify Responsive

"The CSS looks right" is not "it renders right at 390px." A media query only proves itself when something evaluates it at that width. This is §4 applied to UI — *define success criteria, loop until verified* — where the criterion is a rendered page at a real width, not a stylesheet that reads correctly.

The tool is one throwaway HTML file: fixed-width `<iframe>`s pointed at your running app. An iframe is a nested browsing context, so **its viewport is the iframe box** — width and height media queries, container queries, and `vw`/`vh` all resolve to the size you set. Two frames side by side puts both breakpoints in a single screenshot.

## Why not just resize the window

Don't — go straight to the harness. Resizing buys exactly one thing, a narrower viewport, which is the same thing the iframe gives you. The iframe then gives you three more:

- **Both breakpoints in one screenshot**, side by side, rather than one width at a time.
- **A frame you can script.** The parent clicks into it, reads state back, and asserts `innerWidth` and `matchMedia(...)` *from inside* the frame. A resized window can only be photographed.
- **No dependency on the resize having worked.** Browser-automation resize calls can quietly fail to change the layout viewport the page actually sees: the screenshot comes back at the old width, the desktop branch renders, and you sign off on a mobile layout you never rendered.

The tool that genuinely beats this harness is **device emulation**, not window resizing — see [what this does not give you](#what-this-does-not-give-you).

## Inputs

- `url` — the running page to check (e.g. `http://localhost:3000/pricing`)
- `widths` — viewports to render; default `390×844` (phone) and `1280×800` (desktop)
- `static-dir` — the directory the dev server serves **at the same origin** (`public/`, `static/`, `www/`)

## Procedure

1. **Start the app** and note its origin.

2. **Put the harness in the app's own `static-dir`** — not on a second server. Same-origin is the entire point: it is what makes step 4 possible. A harness on another port is cross-origin, `iframe.contentDocument` comes back `null`, and you are reduced to screenshots.

3. **Write `_viewports.html`** into that directory:

```html
<!doctype html><meta charset="utf-8"><title>viewports</title>
<style>
body { margin:0; display:flex; gap:16px; background:#111; font:12px system-ui }
figure { margin:0 } figcaption { color:#888; padding:4px }
iframe { border:0; background:#fff }
</style>
<figure><figcaption>390 × 844</figcaption>
<iframe id="m" src="/YOUR/PATH" width="390" height="844"></iframe></figure>
<figure><figcaption>1280 × 800</figcaption>
<iframe id="d" src="/YOUR/PATH" width="1280" height="800"></iframe></figure>
```

Open `/_viewports.html` and screenshot it.

4. **Drive it and assert from the parent.** Same-origin, so the frame is fully scriptable — you can reach past what a screenshot shows:

```js
const w = m.contentWindow, doc = m.contentDocument;
w.innerWidth; // 390
w.matchMedia('(max-width: 640px)').matches; // true → the mobile arm really is live
doc.querySelector('[data-testid="menu"]').click();
doc.querySelector('.drawer').className; // read the result back
w.localStorage.getItem('seen'); // and any state it wrote
```

Assert the width from *inside* the frame before trusting a screenshot. "Looks narrow to me" is how you verify nothing.

5. **Delete the harness before committing.** Anything in the static dir ships. `rm _viewports.html`, then confirm with `git status` — don't leave it for review to catch.

## What this does not give you

The iframe narrows the **viewport**. It does not emulate a device. Measured inside a 390px frame on a laptop: `pointer: fine` and `hover: hover` are both still true, `navigator.maxTouchPoints` is `0`, and `devicePixelRatio` is the host's.

So a layout that branches on width is covered honestly. One that branches on `@media (hover: hover)`, sniffs the user agent, or swaps assets on DPR will render its **desktop** arm at 390px and mislead you with a screenshot that looks right. There is also no browser chrome, so `100dvh`-vs-`100vh` differences never show up. When the behavior under test depends on any of those, use real device emulation or a real device — this harness will lie to you.

## 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."
Loading