Browse Source

docs(skills): github-ops generates richer README intros (v2.4.8)

Adds references/readme-description.md — voice/structure guide with worked
example for the 2–3 paragraph repo intro. Wires into mode `new` (always
draft, surface for approval), mode `update` (only if thin or scope drifted),
mode `audit` (flag intros < 80 words). The `gh repo create --description`
one-liner now derives from the README intro draft rather than blindly
copying package metadata.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
0xDarkMatter 1 month ago
parent
commit
cc485b9150

+ 1 - 1
.claude-plugin/plugin.json

@@ -1,6 +1,6 @@
 {
   "name": "claude-mods",
-  "version": "2.4.7",
+  "version": "2.4.8",
   "description": "Custom commands, skills, and agents for Claude Code - session continuity, 23 expert agents, 71 skills, 3 commands, 6 rules, 4 hooks, 13 output styles, modern CLI tools",
   "author": "0xDarkMatter",
   "repository": "https://github.com/0xDarkMatter/claude-mods",

+ 3 - 0
README.md

@@ -22,6 +22,9 @@ From Python async patterns to Rust ownership models, from AWS Fargate deployment
 
 ## Recent Updates
 
+**v2.4.8** (April 2026)
+- 📝 **`github-ops` README intros** - Stopped shipping single-line taglines as "descriptions". Skill now drafts a proper 2–3 paragraph intro on first publish (what it is / why it exists / who it's for), reading package metadata, CHANGELOG, and the primary entry point before writing — and surfaces the draft for approval rather than committing one-shot. New `references/readme-description.md` codifies voice (developer-to-developer, concrete, occasional dry wit), structure, anti-patterns ("blazing fast", emoji walls, marketing fluff, "this project aims to..."), and ships a worked before/after example. Mode `update` proposes expansion only if intro is < 80 words or scope has drifted (no churning good prose); mode `audit` flags thin intros. The `gh repo create --description` one-liner now derives from the README intro draft rather than blindly copying `pyproject.toml.description`.
+
 **v2.4.7** (April 2026)
 - 🛡️ **`push-gate` first-push fix** - Detected on first publish to a new remote: gitleaks scan failed because `origin/main` doesn't exist yet. Now branches on remote-ref existence — full-branch scan when new, diff-range scan when incremental.
 

+ 31 - 12
skills/github-ops/SKILL.md

@@ -54,42 +54,54 @@ Triggered by: "publish to github", "create repo on github", "push to github" (wh
    - At least one tag exists (typically v0.1.0)?
    - CHANGELOG.md has an entry for the latest tag?
 
-2. Add "Recent Updates" section to README if missing
+2. Draft / refine README intro (2–3 paragraphs) — see references/readme-description.md
+   - If the README intro is just a tagline or < 80 words, draft a proper 2–3 paragraph
+     description: what it is, why it exists, who it's for. Read package metadata, CHANGELOG,
+     and the primary entry point first; do not fabricate.
+   - Voice: developer-to-developer, concrete, occasional dry wit (earned, never sprayed).
+     Anti-patterns ("blazing fast", emoji walls, marketing fluff) listed in the reference.
+   - Surface the draft to the user for approval before committing — this is the repo's
+     first impression and shouldn't be a one-shot.
+   - Commit via git-ops with: docs: Expand README intro
+
+3. Add "Recent Updates" section to README if missing
    - Use claude-mods style by default (see references/readme-recent-updates.md)
    - Place after Quickstart, before deep "why this exists" sections
    - For first release, single bullet block describing the initial extraction
    - Commit via git-ops with: docs: Add Recent Updates section
 
-3. Surface the publish plan to user, with visibility as a flippable line:
+4. Surface the publish plan to user, with visibility as a flippable line:
    "Creating as **private** at github.com/<org>/<repo> — say 'public' to flip"
    Wait for explicit confirmation.
 
-4. Create the repo:
+5. Create the repo:
    gh repo create <org>/<repo> --private --source=. --remote=origin \
-     --description "<derived from package metadata or asked>" \
+     --description "<one-line — distilled from the README intro draft in step 2, ≤ 350 chars>" \
      --homepage "<homepage URL or omit>"
    (NEVER pass --push; we want push-gate to run between)
+   Note: the GitHub `--description` is a single line and distinct from the README intro.
+   Derive it FROM the intro you just wrote, not from package metadata blindly.
 
-5. Run push-gate preflight:
+6. Run push-gate preflight:
    bash $HOME/.claude/skills/push-gate/scripts/preflight.sh --cwd <repo> origin main
    On any non-zero exit: stop, report, do not push.
 
-6. Push main + tags:
+7. Push main + tags:
    git -C <repo> push -u origin main
    git -C <repo> push origin --tags
 
-7. Set topics (derived from package keywords + language + frameworks):
+8. Set topics (derived from package keywords + language + frameworks):
    gh repo edit <org>/<repo> --add-topic <t1> --add-topic <t2> ...
    Aim for 6–12 topics. See references/metadata-checklist.md for derivation.
 
-8. Create the release for the latest tag:
+9. Create the release for the latest tag:
    gh release create <tag> --title "<tag> — <one-line headline>" \
      --notes "$(extract from CHANGELOG.md)"
 
-9. Verify:
-   gh repo view <org>/<repo>
-   gh release view <tag>
-   Report URL to user.
+10. Verify:
+    gh repo view <org>/<repo>
+    gh release view <tag>
+    Report URL to user.
 ```
 
 ### Mode `update` — subsequent release
@@ -120,6 +132,10 @@ Triggered by: "ship a release", "cut a release", "release v0.X.Y", "publish upda
               touch README body sections only if found.
    For patch: update Recent Updates ONLY (single bullet); no body changes unless asked.
 
+   Also: if the README intro is still < 80 words OR the repo's scope has drifted since
+   the intro was written, propose an expansion (see references/readme-description.md).
+   Don't churn good prose — only act if the intro is genuinely thin or stale.
+
 5. Commit README + CHANGELOG via git-ops:
    docs: Recent Updates + CHANGELOG for v<N>
 
@@ -153,6 +169,7 @@ Read-only — produces a report without making changes. See `references/metadata
 LOCAL FILE CHECKS
   [ ] LICENSE file present + matches metadata
   [ ] README has: tagline, install, quickstart, license link
+  [ ] README intro is ≥ 80 words (2–3 paragraphs orienting a cold reader)
   [ ] README has "Recent Updates" section near top
   [ ] CHANGELOG.md present and has entry for latest tag
   [ ] pyproject.toml / package.json: description, keywords, license, repository URL, homepage
@@ -175,6 +192,7 @@ Output: per-row pass/fail/warn, then a summary score and list of fixes. Fixes ar
 | Convention | File | Default |
 |---|---|---|
 | Release strategy | `references/release-strategy.md` | minor on `feat:`, patch on `fix:`-only, major requires approval |
+| README intro (2–3 paragraphs) | `references/readme-description.md` | what it is / why it exists / who it's for; concrete, dry, no marketing fluff |
 | README Recent Updates style | `references/readme-recent-updates.md` | claude-mods per-version blocks (alternate: flarecrawl table) |
 | Repo visibility default | `references/repo-visibility.md` | `--private` unless user says "public" |
 | Metadata audit checklist | `references/metadata-checklist.md` | full source-of-truth for mode `audit` |
@@ -251,6 +269,7 @@ When adding any of the above, keep the boundary discipline: anything talking to
 |---|---|
 | `SKILL.md` | This file — modes, rules, delegation |
 | `references/release-strategy.md` | Version bump policy |
+| `references/readme-description.md` | 2–3 paragraph README intro — voice, structure, anti-patterns |
 | `references/readme-recent-updates.md` | "Recent Updates" section format + emoji vocabulary |
 | `references/repo-visibility.md` | Private-by-default policy |
 | `references/metadata-checklist.md` | Audit checklist source of truth |

+ 1 - 0
skills/github-ops/references/metadata-checklist.md

@@ -10,6 +10,7 @@ Source of truth for mode `audit`. Ordered by criticality.
 | LICENSE matches package metadata | grep license field in pyproject.toml/package.json, compare to LICENSE header |
 | README.md present | `[ -f README.md ]` |
 | README has tagline | first non-empty paragraph is < 200 chars and not a heading |
+| README intro ≥ 80 words | word-count of prose between title and first `##` heading; see `readme-description.md` |
 | README has install section | `grep -iE '^##\\s+(install|installation|getting started|quickstart)' README.md` |
 | Package metadata file present | `pyproject.toml` (Python) OR `package.json` (Node) OR `Cargo.toml` (Rust) etc. |
 | Package metadata: description set | `jq -r .description` / `tomlq` equivalent |

+ 155 - 0
skills/github-ops/references/readme-description.md

@@ -0,0 +1,155 @@
+# README Description
+
+Guidance for the README intro that sits between the title and the first `##` heading. This is the bit a person reads when they land on the repo — not the GitHub one-line description (that's a separate, shorter beast; see `metadata-checklist.md`).
+
+The default tagline-only intro is too thin. Most published 0xDarkMatter repos deserve **2–3 substantial paragraphs** that orient a reader who landed cold from a search result or a link.
+
+## What it is, not what it does
+
+| Layer | Length | Purpose |
+|---|---|---|
+| Title (`# repo-name`) | ~3 words | Identity |
+| Tagline (one line, optional `>` blockquote) | ≤ 120 chars | The pitch |
+| Intro paragraphs (2–3) | ~150–300 words total | Orientation |
+| Then `## Install` etc. | — | The mechanics |
+
+The intro is *not* a feature list. Save bullets for later sections. This is prose, written like a developer explaining the project to a peer over coffee — concrete, slightly opinionated, not performative.
+
+## Structure (the three-paragraph shape)
+
+Not a rigid template — a default to vary from when the repo demands it.
+
+### Paragraph 1 — What it is
+
+- Concrete, specific noun phrase. Not "a powerful framework for..."
+- Name the actual category (CLI, library, plugin, daemon, skill collection, MCP server).
+- One sentence on the *shape* (single binary? plugin pack? long-running daemon? collection of scripts?).
+- One sentence on the *primary surface* (what command/import/endpoint does the user touch first).
+
+### Paragraph 2 — Why it exists / what it solves
+
+- The pain that prompted it. Real, specific, recognisable.
+- What the existing options were and why they didn't fit. (Tactful — no need to dunk.)
+- The shape of the solution, in one sentence.
+- This is where dry wit can land — naming a frustration accurately is itself a kind of joke.
+
+### Paragraph 3 — Who it's for / when it's handy
+
+- Who would reach for this tool. Be honest about scope.
+- A scenario or two where it shines.
+- A scenario where it's the wrong choice (this builds trust faster than any feature list).
+- Optional: how it fits alongside related tools the reader probably already knows.
+
+## Voice
+
+- **Developer-to-developer.** Assume technical literacy; don't explain what a CLI is.
+- **Concrete over abstract.** "Wraps `gh` and adds a confirm step before pushes" beats "streamlines GitHub workflows".
+- **Confidence without pomp.** State what it does. Don't sell.
+- **Occasional dry wit.** Earned, not sprayed. One well-placed observation > three jokes. British understatement scales better than zingers.
+- **Honest about scope.** "Handles the boring 80%" is more trustworthy than "comprehensive solution".
+
+### Wit calibration
+
+Good wit names something the reader has *also* felt:
+
+> "Because every project eventually needs the same six bash scripts, and writing them again at 11pm is no longer charming."
+
+Bad wit performs cleverness:
+
+> "Behold! A revolutionary new paradigm that will *blow your mind* 🤯"
+
+When in doubt, omit the joke. A clean, plain description is always better than a strained one.
+
+## Anti-patterns
+
+| Avoid | Why |
+|---|---|
+| "Blazing fast", "powerful", "cutting-edge", "robust" | Marketing words signal nothing. Show specifics. |
+| "Easy to use" | Decided by the reader, not you. |
+| Emoji walls (🚀✨🔥💯) at the top | Reads as AI slop. One contextual emoji is fine; a parade isn't. |
+| Feature bullets in the intro | Save for `## Features` or just let the structure speak. |
+| Comparison tables before saying what the thing is | Orient first, position later. |
+| "This project aims to..." | Just describe what it is, not what it aspires to be. |
+| Auto-generated boilerplate | A reader can spot it instantly. Trust collapses. |
+| Restating the title | "Foo is a tool called foo that does foo things." |
+| Hedging ("might be useful for", "could potentially help") | Either it's for them or it isn't. Say so. |
+
+## Process
+
+Before writing, read these in this order:
+
+1. **Existing README** — what's already there? Don't discard prior voice if it's good; refine it.
+2. **Package metadata** — `pyproject.toml` / `package.json` description + keywords. These were chosen for a reason.
+3. **CHANGELOG.md** — the v0.1.0 / first-publish entry often captures the original motivation cleanly.
+4. **Source layout** — top-level dirs and entry points reveal the actual shape.
+5. **Primary entry point file** — read the main script / `__init__.py` / `main.go` opening for any module docstring.
+6. **Tests** — test names often describe the contract more honestly than docs.
+
+Then draft 2–3 paragraphs, read them back as if you'd never seen the repo, and cut every sentence that doesn't add information. The final intro should be **dense** — a reader scanning it should come away knowing what the repo is, why it exists, and whether they should keep reading.
+
+## When to update vs leave alone
+
+| Situation | Action |
+|---|---|
+| Mode `new` (first publish) | Always draft the intro before publish. This is the reader's first impression. |
+| Mode `update` (subsequent release) | Touch only if scope drifted *or* the original intro was thin. Don't churn good prose. |
+| Mode `audit` | Flag if the intro is < 80 words OR is a single tagline. Suggest, don't auto-edit. |
+| Existing intro is already good | Leave it. Suggest a minor tweak if a release added a major capability. |
+
+## Worked example
+
+### Before (the thin version)
+
+```markdown
+# push-gate
+
+> Pre-push safety checks for git.
+
+## Install
+...
+```
+
+### After (the 3-paragraph version)
+
+```markdown
+# push-gate
+
+> Pre-push safety gate for any `git push` to a remote — secret scan, forbidden-file check, divergence check, explicit confirm.
+
+`push-gate` is a Claude Code skill that intercepts pushes to GitHub, GitLab,
+Bitbucket, or any other remote and runs a fast preflight before the bytes
+leave your machine. It layers `gitleaks` with a regex-based secret scan,
+checks for files that shouldn't be in the repo (private keys, `.env`, large
+binaries), confirms the local branch hasn't diverged unexpectedly from its
+upstream, and requires an explicit "yes" before the push proceeds.
+
+It exists because the worst time to discover a leaked AWS key is *after* it's
+in someone else's clone. Pre-commit hooks help, but they only run on commit
+and they're easy to bypass; CI scanners catch leaks too late. `push-gate`
+sits at the last useful checkpoint — the moment between "I've staged
+everything" and "the world has it" — and refuses to let a known-bad push
+through. Refusal is hard, not advisory: there's no `--force-anyway` flag,
+because if there were, you'd use it.
+
+It's most useful for solo developers who don't have org-level secret
+scanning, for repos that mix public and private code, and for the mid-pour
+late-night push where careful review has politely left the building. If you
+already run gitleaks pre-commit and have CI guards on every push, `push-gate`
+is redundant — go enjoy your weekend. If you don't, it's a small skill that
+will eventually save you from a very large incident.
+
+## Install
+...
+```
+
+The difference: a reader of the second version knows what the tool is, why it exists, when to use it, and when *not* to. That's the bar.
+
+## Length sanity check
+
+| Word count | Verdict |
+|---|---|
+| < 60 | Thin — expand. |
+| 60–150 | Borderline — fine for tiny utilities, light for anything substantial. |
+| 150–300 | The sweet spot. |
+| 300–500 | Acceptable for a complex/foundational repo; tighten if possible. |
+| > 500 | Too long for an intro — split into intro + a `## Why this exists` section. |