AgentStack
SKILL verified MIT Self-run

Gh Profile

skill-yasyf-cc-skills-gh-profile · by yasyf

Create or non-destructively refresh a fancy GitHub profile README — the special <username>/<username> public repo that renders at github.com/<username>. Driven by real GitHub data (repos, stars, pinned items, languages, releases, recent activity), never invented; marker-delimited sections stay fresh via a committed cron updater, flattery gates hide numbers that don't impress, and the default "fan…

No reviews yet
0 installs
13 views
0.0% view→install

Install

$ agentstack add skill-yasyf-cc-skills-gh-profile

✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access Used
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

Are you the author of Gh Profile? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Fancy GitHub Profile README

Build the page that renders at github.com/ from the user's real GitHub data — repos, stars, pinned items, languages, releases, recent events — styled creative-and-fancy by default, with cron GitHub Actions that keep the dynamic sections fresh without Claude. The whole skill is driven by a single command:

PROFILE="python3 ${CLAUDE_PLUGIN_ROOT}/skills/gh-profile/scripts/profile.py"
$PROFILE preflight | harvest [--login X] [--out F] | render --target DIR [--with metrics,claude] [--force]

The keystone: the updater you run at compose time (templates/github/scripts/update_profile.py) is the same file render commits into the profile repo as .github/scripts/update_profile.py, where a cron workflow runs it every 6 hours — first render == every cron render, by construction.

Work the phases below in order. Each ends with an Exit criteria line — don't advance until it holds.

The profile's prose is writing: it always runs on fable — never delegate it to a down-routed subagent; inherit the session model or pass model: fable.

Terminology

  • Profile repo — the public repo named exactly the user's login

(/); its README renders on the profile page. A private one renders nothing.

  • Dossier — the normalized JSON harvest of the user's GitHub data

(reference/data.md has the schema). The single source of truth: every name, number, and date on the page must trace to it.

  • Managed section — a block delimited by ``

/ ` with ids featured, shipped, activity, languages`. The committed updater owns and rewrites the interiors every cron run; everything outside is sacred content it never touches, byte for byte.

  • Intensity — how fancy: polished, fancy (default), max. See

Intensity levels below.

  • Flattery gates — pure threshold checks: a stat renders only when it

impresses. Defaults: star counts at ≥ 30 stars, contribution total at ≥ 750/yr, releases within 6 months. Hidden numbers are never explained.

  • Meta comment — README line 1: ``,

recording intensity, skill version, last refresh, and the gate thresholds (min_stars_badge, min_contributions, shipped_window_months). Thresholds persist; verdicts are re-judged against fresh data every run — a star count appears by itself the day a repo crosses 30.

  • Summaries sidecar.github/profile-summaries.json: Claude-written

one-line summaries keyed by event (PushEvent:owner/repo) and release (repo@tag). The updater appends them to activity/shipped lines while the file is fresh (≤ 10 days old); absent or stale, lines render plain. The machinery comes from the repo-summaries plugin (a sibling in this marketplace): its summaries.py template is committed next to the updater, which imports it, and .github/summaries.config.json describes the groups. The sibling refresh skill (the daily Claude workflow) maintains the sidecar; this skill seeds it at compose time.

Phase 0 — Preflight & mode

$PROFILE preflight

Prints KEY=VALUE lines: GH_VERSION, AUTH, LOGIN, SCOPES, SCOPE_WORKFLOW=ok|MISSING|UNKNOWN, PROFILE_REPO=exists|absent, VISIBILITY, DEFAULT_BRANCH, HAS_MARKERS, RATE_REMAINING — and exits 1 with a MISSING: line on stderr per problem, each carrying its fix. Resolve every MISSING first: pushing .github/workflows/*.yml fails without the workflow token scope. SCOPE_WORKFLOW=UNKNOWN means a fine-grained token whose scopes gh can't report — proceed, but the push may fail (Common issues).

LOGIN comes from preflight — never ask the user for their username.

Decide the mode from the output:

| Mode | Condition | Meaning | |---|---|---| | CREATE | PROFILE_REPO=absent | No profile repo yet; build from scratch | | UPDATE-managed | exists + HAS_MARKERS=true | This skill ran before; refresh it | | UPDATE-foreign | exists + HAS_MARKERS=false | Hand-written README; migrate carefully |

Set up the workspace — always a temp dir, always git -C (never cd):

WORK=$(mktemp -d)
gh repo clone "$LOGIN/$LOGIN" "$WORK/profile"     # UPDATE modes
git init -b main "$WORK/profile"                  # CREATE

CREATE defers gh repo create to Phase 5 push time — nothing public appears until the content is ready.

Exit criteria: preflight clean (or every MISSING resolved), LOGIN known, mode decided, working clone or fresh repo under $WORK/profile.

Phase 1 — Harvest

$PROFILE harvest --out "$WORK/dossier.json"

About 20 gh api calls, all cached (--cache 3600s, events 900s) — cheap to re-run. Read the dossier, then show the user a one-screen "here's what your data says about you" summary: top-starred repos, inferred project categories (cluster by topics and language), top languages, recently shipped releases, the shape of the last 90 days — and what the flattery gates will hide and why ("hiding star counts: your top repo has 12; the page reads better without them"), so nothing in Phase 5's diff is a surprise.

Check excluded too: it lists quality-floor drops with reasons (fork, archived, no description). A great repo dropped for a missing description deserves a fix at the source (gh repo edit -d "...", then re-harvest), not a silent loss.

Exit criteria: dossier on disk; summary shown, including what the gates hide.

Phase 2 — Interview (the only decision phase)

One AskUserQuestion round, every question pre-filled from the dossier as confirm-or-correct:

  • Tagline — draft 2–3 options from their bio and top repos.
  • Now / current-focus bullets — drafted from the last 90 days of events.
  • Badge links — multiSelect, prefilled from user.blog, user.company,

and obvious socials; max 5.

  • Fun facts — optional; skip the section entirely if they pass.
  • Philosophy / footer line — one sentence they'd put on a t-shirt.
  • Intensitypolished / fancy (default) / max.
  • Claude-refresh opt-in — default off (needs an ANTHROPIC_API_KEY

repo secret — real friction). Daily pass via claude-code-action@v1 that installs this plugin fresh from the marketplace and runs the sibling refresh skill: commit/release summaries on the activity and shipped lines, prose refresh when activity warrants it; reference/actions.md.

UPDATE-managed re-run: collapse the round to one question — "refresh data only, or revisit voice/intensity?". Data-only means no interview and no prose edits.

UPDATE-foreign adds a migration choice:

  • Annex (default) — keep every byte of their prose; insert marker-wrapped

data sections around it.

  • Remix — full blueprint restructure; their old prose pre-fills the

interview defaults; full diff shown in Phase 5; the old version stays in git history.

  • Sections-only — render the managed sections into a scratch file for them

to splice by hand; push nothing.

Exit criteria: every answer recorded; intensity fixed; for UPDATE-foreign, the migration choice fixed.

Phase 3 — Compose

Write $WORK/profile/README.md section by section per the Content blueprint below (worked markup per section in reference/blueprint.md). Rules:

  • Every fact traces to the dossier. If a number, name, or date isn't in

dossier.json, it doesn't go on the page.

  • Rewrite repo one-liners in the user's voice — punchy, specific, theirs.

Never copy description strings verbatim into prose.

  • Markers from day one. Lay down all four marker pairs (empty interiors)

in their blueprint positions, even in Annex mode.

  • Meta comment on line 1. Choose gate thresholds now (defaults are right

for almost everyone) and record them with intensity and skill version: ``

  • Run the updater once so the dynamic sections render — gates included —

through the same code path as cron:

UPDATER="python3 ${CLAUDE_PLUGIN_ROOT}/skills/gh-profile/templates/github/scripts/update_profile.py"
$UPDATER update --readme "$WORK/profile/README.md" --login "$LOGIN"
$UPDATER update --readme "$WORK/profile/README.md" --login "$LOGIN" --check   # idempotence: must exit 0

WROTE means sections populated; NOMARKER means that pair is missing or typo'd — fix and re-run (nothing was touched for that id).

  • Seed the summaries sidecar so the activity and shipped lines launch

with real summaries instead of bare event lines: write $WORK/profile/.github/profile-summaries.json following the sibling refresh skill (${CLAUDE_PLUGIN_ROOT}/skills/refresh/SKILL.md) — the shared schema, style core, and flattery law it points to, with the groups and raw-material recipes from ${CLAUDE_PLUGIN_ROOT}/skills/gh-profile/templates/github/summaries.config.json (fetch commit subjects per push-repo, summarize releases from their real content, every word traceable, omit entries with uninformative material). Then re-run the updater + --check: the activity/shipped lines should now carry suffixes and --check must still exit 0. Without the Claude-refresh opt-in the sidecar ages out after 10 days and lines degrade to plain — by design.

Prose gates: apply the writing-docs skill's voice to everything a human reads, then slop-cop check README.md and triage — widget markup is exempt, prose is not.

Exit criteria: README composed with all four marker pairs; summaries sidecar seeded; updater run once (WROTE, no NOMARKER) and --check exits 0; slop-cop triaged.

Phase 4 — Assets & Actions (gated by intensity)

Banner (fancy+, default-on) — invoke the gen-image skill (a sibling plugin in this marketplace; if it's not installed, install gen-image@skills from marketplace yasyf/cc-skills or apply the no-banner escape hatch):

banner --name $LOGIN --tagline "$TAGLINE" --variant both --out-dir $WORK/profile/assets/

It writes assets/banner-dark.webp + assets/banner-light.webp, each under 1 MiB. gen-image owns the key chain: OPENAI_API_KEY env → 1Password op read "op://OpenClaw/OpenAI API Key/notesPlain" → codex $imagegen. If the whole chain comes up empty, fall back to the typing-SVG-only hero — remove the `` block entirely so nothing dangles. View both banners with Read before accepting them.

Workflows (fancy+):

$PROFILE render --target "$WORK/profile"

Copies the committed updater (update_profile.py plus its summaries.py sidecar module and summaries.config.json) and profile-snake.yml and profile-refresh.yml into .github/, substituting a random {{CRON_MINUTE}} per file (no thundering herd) and failing on any leftover {{...}} token. Prints WROTE/SKIP per file; CONFLICT writes nothing — resolve per file or re-run with --force. Add-ons:

  • --with claude (if opted in) — adds profile-claude-refresh.yml (daily;

installs this plugin and repo-summaries fresh from the skills marketplace each run and runs /gh-profile:refresh, so the canonical instructions live here, not frozen into the profile repo) plus PROFILE_GUIDE.md at the repo root (per-user overrides only). Then set the secret: gh secret set ANTHROPIC_API_KEY -R "$LOGIN/$LOGIN" (CREATE: defer until the repo exists in Phase 5).

  • --with metrics (max only) — adds profile-metrics.yml; needs a classic

PAT as METRICS_TOKEN (reference/actions.md walks through both secrets).

Writing section — only with a real blog feed; wire blog-post-workflow per reference/blueprint.md §8. No feed, no section.

Stat-widget gate: trophies and metrics render only when the numbers flatter — a C-rank trophy case hurts more than it helps; skip and say so. Hard rule: never embed the public Vercel instances of github-readme-stats, github-profile-trophy, or github-readme-activity-graph (rate-limited, with outages as of Jan 2026). Actions-generated or static (shields.io, skillicons.dev) only; the featured section already covers the stats-card use case. Full green/red-light table: reference/widgets.md.

Exit criteria: per the chosen intensity — banners exist and reviewed (or escape hatch applied), render exited 0 with no CONFLICT, secrets set for every opted-in workflow (or explicitly deferred to Phase 5 for CREATE).

Phase 5 — Push & verify

  1. Show the diff, always. git -C "$WORK/profile" diff (CREATE: the full

README and file list). State which managed sections changed. On a data-only UPDATE run, state — and verify in the diff — that zero bytes outside marker interiors changed. The user confirms before push, always.

  1. CREATE only: now make the repo public and wire the remote:

``bash gh repo create "$LOGIN" --public --description "GitHub profile" git -C "$WORK/profile" remote add origin "https://github.com/$LOGIN/$LOGIN.git" ``

Then set any deferred secrets from Phase 4.

  1. Commit and push immediately (commit+push+verify is one atomic step)

with git -C "$WORK/profile":

  • CREATE: feat: bootstrap GitHub profile README, then push -u origin main
  • UPDATE: chore: refresh profile README sections, then push
  1. Seed the workflows (fancy+) — cron hasn't fired yet, so kick each

installed workflow and watch it to green (full loop in reference/actions.md):

``bash gh workflow run profile-snake.yml -R "$LOGIN/$LOGIN" # ditto profile-refresh.yml sleep 5 # dispatched runs take a moment to appear run_id=$(gh run list -R "$LOGIN/$LOGIN" --workflow profile-snake.yml -L 1 --json databaseId -q '.[0].databaseId') gh run watch "$run_id" -R "$LOGIN/$LOGIN" --exit-status ``

Then confirm the snake landed: gh api "repos/$LOGIN/$LOGIN/contents/github-snake.svg?ref=output" -q .name.

  1. Render check: gh api "repos/$LOGIN/$LOGIN/readme" -H "Accept: application/vnd.github.html"

must return rendered HTML. Then extract every image URL from the raw README — src="https://..." attributes and ` markdown — and curl -sIL -o /dev/null -w '%{http_code}' each: **all must be 200** (catches typo'd skillicons names). Relative srcs (assets/...) check via https://raw.githubusercontent.com/$LOGIN/$LOGIN/main/...`.

  1. Finish with open "https://github.com/$LOGIN" so the user sees the live

page.

Exit criteria: pushed; seeded runs green and the snake SVG on the output branch; every image URL returns 200; profile page opened.

Content blueprint

Fixed order — omit sections (2-item minimum), never reorder. Worked markup per section in reference/blueprint.md.

| # | Section | Source | Dynamic? | |---|---|---|---| | 1 | Header — banner ` dark/light **or** typing-SVG hero, never both | gen-image / interview tagline | static | | 2 | Social badges — shields for-the-badge, max 5 | interview | static | | 3 | Now — current-focus bullets; recent-activity digest in beneath | interview + dossier | managed activity | | 4 | Start Here — 3–5 flagship repos (pinned ∪ top-starred), gated star counts, punched-up one-liners | dossier | managed featured | | 5 | More things I built — topic/language clusters, 4–8 per category | dossier | static prose | | 6 | Recently shipped — dated release lines | dossier | managed shipped | | 7 | Toolbox — skillicons grid (cap 16); language histogram in beneath | dossier | managed languages | | 8 | Writing — blog-post-workflow, **only if a feed exists** | feed | action-managed | | 9 | Random facts | interview | static | | 10 | Footer — philosophy line + snake ` | interview + snake Action | static |

Taste budget & flattery law

The anti-widget-soup law — every README obeys all of it:

  • ≤ 1 animated element above the fold (banner or typing-SVG, not both).
  • ≤ 2 stat widgets total, snake bottom-only.
  • Every section clears a 2-item minimum or is omitted entirely.
  • One emoji per heading, at most.
  • Personality lives in exactly four places: the tagline, the repo

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet — be the first.

Versions

  • v0.1.0 Imported from the upstream source.