Skip to main content

fs-skill-authoring

End-to-end workflow for adding a new skill/plugin to founder-skills: scripts/add-skill.sh anatomy step-by-step, its verified hazards (manifest-schema-only validation, the rm -rf symlink swap of ~/.claude/skills/<name>, the maybe-absent ~/claude-code-plugins cross-list, the folded-description misparse), the conformance pass against the house style, and the sanitize-and-graduate gate for publishing a private skill. Use when a maintainer asks "how do I add a new skill", "scaffold a plugin", "run add-skill.sh", "publish this private skill", "graduate a skill from claude-skills", "is this safe to make public", or "why did add-skill.sh eat my skill directory".

الانتقال إلى التثبيت

معلومات المصدر

المستودع
craigm26/founder-skills
آخر نشاط في المصدر
٢ يوليو ٢٠٢٦ في ٢٢:٥٠
لغة SKILL.md المكتشفة
الإنجليزية
النجوم
٠
التفرعات
٠

خيارات التثبيت

يُحدَّد Prompt الذي يراجع المصدر أولًا بشكل افتراضي. يمكنك التبديل إلى أمر مباشر أو تنزيل نسخة محلية.

مراجعة ملفات المصدر

اقرأ SKILL.md وأي ملفات مرافقة يعرضها SkillsMP قبل أن تقرر التثبيت.

مستكشف الملفات
2 ملفات

عرض SKILL.md

SKILL.md
تعليمات المصدر · معاينة للقراءة فقط
name
fs-skill-authoring
description
End-to-end workflow for adding a new skill/plugin to founder-skills: scripts/add-skill.sh anatomy step-by-step, its verified hazards (manifest-schema-only validation, the rm -rf symlink swap of ~/.claude/skills/<name>, the maybe-absent ~/claude-code-plugins cross-list, the folded-description misparse), the conformance pass against the house style, and the sanitize-and-graduate gate for publishing a private skill. Use when a maintainer asks "how do I add a new skill", "scaffold a plugin", "run add-skill.sh", "publish this private skill", "graduate a skill from claude-skills", "is this safe to make public", or "why did add-skill.sh eat my skill directory".
# fs-skill-authoring — adding a new skill, end to end Announce at start: "Loading fs-skill-authoring — walking the add-skill.sh path with its hazards, then the conformance and sanitization gates." You are adding a plugin to `founder-skills` (public GitHub `craigm26/founder-skills`, MIT), a Claude Code marketplace (15 plugins as of 2026-07-02: 12 committed + 3 uncommitted skill-*) that **live-publishes**: every push to `master` is instantly installable by marketplace users. There is exactly one scaffolding tool, `scripts/add-skill.sh` (161 lines), and it has shipped a broken plugin once already because its validation step checks manifests, not content. This skill is the runbook that gets a new skill from idea to publish-ready without repeating that incident. All facts verified against the repo at HEAD `2e4c9dd` on 2026-07-02 unless stamped otherwise. The four hazards below were each reproduced or observed on the origin machine that day. ## When NOT to use this skill | Your job | Load instead | |---|---| | Understand what files a plugin needs, plugin.json/marketplace.json schema, .gitignore hazards in depth | `fs-plugin-anatomy` | | Write the SKILL.md *content* — frontmatter voice, phases, tables, closers | `fs-skill-style-guide` | | Get the spec/plan approved before you scaffold anything | `fs-change-control` | | Actually push to master (pre-push gate, publish sequence) | `fs-release-and-publish` | | Set up the venv / run the test suites | `fs-toolchain-and-tests` | | The no-oversell and sanitization *doctrine* and its rationale | `fs-doctrine-and-honesty` | | Full incident narratives (how build-options shipped broken and was rescued) | `fs-failure-archaeology` | | First orientation to the repo | `fs-orientation` | ## Terms (defined once) | Term | Meaning here | |---|---| | **Plugin** | A self-contained directory under `plugins/<name>/` with a `SKILL.md` and `.claude-plugin/plugin.json`, listed in the root marketplace manifest. | | **Marketplace** | The repo itself: root `.claude-plugin/marketplace.json` lists every installable plugin. | | **Cross-list** | A second registration of the same plugin in a *different* marketplace repo (`~/claude-code-plugins`, the RobotRegistryFoundation marketplace) via a `git-subdir` source pointing back at founder-skills master. | | **Graduate** | Move a skill from the private repo `claude-skills` into public founder-skills as a sanitized near-copy. | | **Sanitize-and-graduate gate** | The hard gate from `docs/superpowers/specs/2026-06-11-publish-private-six-design.md`: zero occurrences of client names, secret prefixes, personal paths, or infra layout in anything public. | | **Public-is-canonical** | Standing operator rule (2026-07-02): for graduated skills, the public copy is the source of truth; the private repo is a frozen historical archive (last commit `ab5bcb2`, 2026-06-11). Never backport. | ## Step 0 — Change control comes first Adding a skill is a publishable change to a live marketplace. Per `fs-change-control`, it needs an operator-approved spec (`docs/superpowers/specs/`, date-prefixed) and a checkbox plan before you scaffold. Do not route around this. Assistants never run mutating git commands — the script prints `git add/commit/push` lines at the end; those are **for the operator**, not for you. ## Step 1 — add-skill.sh anatomy Usage (from the script header, verified): ```bash scripts/add-skill.sh <name> [--desc "one-line description with triggers"] \ [--from <dir>] [--category <cat>] [--no-cross-list] ``` `<name>` must be a lowercase slug (`^[a-z0-9][a-z0-9-]*$`). `plugins/<name>` must not already exist (the script dies if it does — so re-runs fail early rather than double-register). What it does, in order (line numbers from the 161-line script at HEAD `2e4c9dd`): | # | Lines | Step | Detail | |---|---|---|---| | 1 | 50–57 | **Resolve source** | Three-way: `--from <dir>` (must contain `SKILL.md`) → else `~/.claude/skills/<name>` *if it is a real directory, not a symlink, with a SKILL.md* → else scaffold a stub SKILL.md you fill in. | | 2 | 59–76 | **Import or stub** | `rsync -a` the source into `plugins/<name>/`, excluding `.git`, `.pytest_cache`, `__pycache__`; any imported `.claude-plugin/` is wiped and recreated fresh. | | 3 | 79–93 | **Resolve description** | `--desc` wins; else a Python one-liner extracts the `description:` line from SKILL.md frontmatter. Dies if empty. **See Hazard 4 — this misparses folded `>-` descriptions.** | | 4 | 96–109 | **Write plugin.json** | Always `version: 0.1.0`, MIT, author Craig Merry, homepage = the GitHub repo. | | 5 | 132 | **Register locally** | Appends an entry to `.claude-plugin/marketplace.json` with source `./plugins/<name>`. Idempotent: skips if the name is already listed. | | 6 | 133–138 | **Cross-list** | If `~/claude-code-plugins/.claude-plugin/marketplace.json` exists (override path with `RRF_MARKETPLACE` env var), appends a `git-subdir` entry there too. **See Hazard 3.** | | 7 | 140–144 | **Symlink swap** | If `~/.claude/skills/<name>` is not already a symlink: `rm -rf` it, then symlink it to `plugins/<name>` ("one copy"). **See Hazard 2.** | | 8 | 146–150 | **Validate** | `claude plugin validate "$PLUGIN_DIR" --strict`, then the repo, then the cross-list repo if touched. **See Hazard 1.** | | 9 | 152–160 | **Print publish commands** | Echoes the `git add/commit/push` lines for both repos. It does NOT push for you. | Because the script runs `set -euo pipefail`, a failure at the validate step (step 8) exits **after** steps 4–7 already mutated things. Manual cleanup after a mid-run failure: `rm -rf plugins/<name>`, `git checkout -- .claude-plugin/marketplace.json`, and restore whatever was at `~/.claude/skills/<name>` from your backup (Hazard 2). ## Step 2 — The hazards ### Hazard 1 — validation is manifest-schema-only `claude plugin validate` (options: `--strict` only, verified via `--help` 2026-07-02) validates the **manifest**, not the plugin's content. It passed `build-options` while the plugin's core asset `assets/matrix.template.html` was missing from git — swallowed by a `*.html` line in the plugin's own `.gitignore` — so the plugin shipped broken and stayed broken until the 2026-07-02 rescue (full narrative: `fs-failure-archaeology`; the .gitignore mechanics: `fs-plugin-anatomy`). A green validate therefore proves almost nothing. Add these two checks for **every file your SKILL.md references** (both verified working 2026-07-02): ```bash # 1. Is the file actually tracked by git? (exits 1 + error if not) git ls-files --error-unmatch plugins/<name>/assets/<file> # 2. Which .gitignore rule, if any, would swallow files in this plugin? git check-ignore -v plugins/<name>/assets/* plugins/<name>/references/* ``` `git check-ignore -v` prints the exact `.gitignore` file and line that matches — this is the command that would have caught the `*.html` swallow before it shipped. If the plugin has executable assets, also run its tests (`fs-toolchain-and-tests`) before calling it done. ### Hazard 2 — the `rm -rf` symlink swap can destroy work Line 142: `rm -rf "$SKILLS_DIR/$NAME"; ln -s "$PLUGIN_DIR" "$SKILLS_DIR/$NAME"` — guarded only by "is it not already a symlink". Two data-loss paths: 1. **You pass `--from <dir>` while a real directory also exists at `~/.claude/skills/<name>`.** The `--from` source wins (source resolution never looks at the skills dir), so the existing real directory is `rm -rf`'d at step 7 *without ever being imported*. 2. **A real directory exists but has no `SKILL.md`** (work in progress, notes, drafts). Source resolution skips it (the elif requires `SKILL.md` to exist), the script scaffolds a stub — and then `rm -rf`s your work-in-progress directory anyway. The safe path (`~/.claude/skills/<name>` is a real dir **with** SKILL.md and you pass no `--from`) is fine: rsync copies it into the repo *before* the rm. But you cannot always know which case you are in, so: ```bash # ALWAYS back up before running, if anything exists at the target: [ -e ~/.claude/skills/<name> ] && cp -a ~/.claude/skills/<name> \ ~/.claude/skills-backup-$(date +%Y%m%d)-<name> ``` Note the inverse gotcha too: if `~/.claude/skills/<name>` is already a symlink pointing somewhere *else*, the script leaves it alone silently — your new plugin is then not the copy Claude loads. Check with `ls -l ~/.claude/skills/<name>` afterwards. These paths are environment-specific (the origin machine's home dir); the mechanism is the same anywhere the Claude Code skills dir lives. ### Hazard 3 — the cross-list repo may not exist The script cross-lists into `$HOME/claude-code-plugins` (the RobotRegistryFoundation marketplace) by default. Verified 2026-07-02: **that directory does not exist on the origin machine** — the script prints `(RRF marketplace not found at ... — skipped cross-list; set RRF_MARKETPLACE to override)` and continues harmlessly (`DID_RRF=0`). The hazard is on machines where it DOES exist: the script then mutates a **second git repo** whose new entry is a `git-subdir` source pointing at founder-skills master — meaning the cross-listed entry is broken for installers until founder-skills itself is pushed, and the second repo needs its own commit/push (the script prints that command line too). If you do not intend to maintain the cross-list, pass `--no-cross-list` explicitly rather than relying on the directory being absent. ### Hazard 4 — folded `>-` descriptions misparse to the literal string `>-` Verified 2026-07-02 by running the script's exact Python extractor against a house-style SKILL.md: when frontmatter uses the folded form the repo's own style guide mandates — ```yaml description: >- Multi-line description... ``` — the extractor's single-line regex captures `>-` and writes **`">-"` as the plugin description** into plugin.json and both marketplace entries. It is non-empty, so the script's empty-description guard does not fire; the garbage ships silently. Mitigation: **always pass `--desc "..."`** when the source SKILL.md uses a folded description (i.e., always, if you follow `fs-skill-style-guide`). After any run, eyeball the description fields in `plugins/<name>/.claude-plugin/plugin.json` and `.claude-plugin/marketplace.json`. This is an open defect candidate for a spec'd fix (route via `fs-change-control`); as of 2026-07-02 the script is unpatched. ## Step 3 — Conformance pass The script gives you a structurally registered plugin; it does nothing for quality. Before publish, check every row (detail lives in the named sibling — do not guess): | Check | Gate | Detail in | |---|---|---| | Frontmatter is EXACTLY `name` + `description`, folded `>-`, description opens with the job then "Use when …" with literal utterances | required | `fs-skill-style-guide` | | Body: imperative voice, numbered Phases/Steps, tables, house closers, `## References` list, honesty admissions ("syntax-checked only" where true) | required | `fs-skill-style-guide` | | Model style anchors: copy from the golden four only (market-validation, build-options, ecosystem-planning, fable-org-audit); NEVER from prd/tasks/tufte-viz | required | `fs-skill-style-guide` | | Directory layout, plugin.json fields, version semantics, per-plugin `.gitignore` audited for swallows | required | `fs-plugin-anatomy` | | Every referenced asset passes `git ls-files --error-unmatch`; `git check-ignore -v` on assets/references comes back clean or negated | required | this skill, Hazard 1 | | Executable assets have a `## Tests` section with exact commands, and the tests pass | required if assets exist | `fs-toolchain-and-tests` | | Per-plugin README follows the walkthrough template (Before you install / What it will ask you / What it produces / Cost / 60-second first run / Built on) | required for published plugins | `fs-plugin-anatomy` | | Every factual claim traces to a source verified this session; no oversell | required | `fs-doctrine-and-honesty` | ## Step 4 — The sanitize-and-graduate gate (publishing a private skill) Applies whenever content originates in the private repo `/home/craigm26/projects/craigm26/claude-skills` (environment-specific path; private GitHub `craigm26/claude-skills`; skills live under `skills/<name>/`) — or in ANY private context. The gate comes from the operator-approved spec `docs/superpowers/specs/2026-06-11-publish-private-six-design.md` and is a **hard gate**: zero hits, or each hit explicitly waived in a spec. ### 4a. Scripted grep scan This skill ships the scan (tested 2026-07-02: `plugins/` scans clean; planted secrets, home paths, and banned claims are all caught): ```bash .claude/skills/fs-skill-authoring/scripts/sanitize-scan.sh plugins/<name>/ # exit 0 = clean, 1 = hits (printed file:line) ``` Pattern list (mirror of the spec's hard-gate list; keep the script and this table in sync): | Pattern | Why | |---|---| | `sk_live`, `sk_test`, `cfut_`, inline `Bearer <token>` values | secret/token prefixes | | `/home/craigm26` | personal home-dir paths | | `reservoir` (case-insensitive) | real client org names — expect false positives on the English word; human-review every hit | | `parcelriskreport` | real-project example filenames → genericize | | "Anthropic('s) internal research" | banned claim class (`fs-doctrine-and-honesty`) | Grep cannot catch everything the spec bans. Manually check for: personal-machine infra layout (systemd unit paths, `/etc/` configs, venv locations), and reframe any hard third-party-tool dependency generically (the precedent: Codex became "an external executor **such as** Codex"). ### 4b. Near-copy divergence tracking Graduated skills are *near-copies*, not identical copies (sanitization changed them at birth, and v0.2.0 upgrades were public-only). Track divergence explicitly rather than assuming sync: ```bash # Per graduated skill (private path is environment-specific): diff -u /home/craigm26/projects/craigm26/claude-skills/skills/<name>/SKILL.md \ plugins/<name>/SKILL.md ``` Verified 2026-07-02: ecosystem-planning already diverges (diff exits 1) — that is expected and correct, because of: ### 4c. Public-is-canonical Standing operator rule (2026-07-02): the public founder-skills copy is canonical for all six graduated skills; private `claude-skills` is frozen as a historical archive at `ab5bcb2` (2026-06-11). Consequences: - Never edit the private copy to "keep it in sync". Never backport public changes. - When graduating a NEW skill, the private original becomes archive material the moment the sanitized copy lands on master — all future edits happen public-side only. - The divergence diff (4b) is archaeology, not a to-do list. ## Step 5 — Publish Hand off to `fs-release-and-publish` and run its full local pre-push gate (all three test suites + JSON parse + sanitization grep + URL sweep — mandatory, no CI exists to save you). The operator reviews and runs the `git add/commit/push` lines the script printed. After the push, installed users stay on their cached copy until they run `/plugin marketplace update founder-skills` — a push alone updates nobody. ## Common mistakes | Fix | Mistake | Fix |
عرض على GitHub
ملف SKILL.md هذا كبير جدا، لذلك يعرض SkillsMP القسم الاول فقط هنا. عرض على GitHub