Skills DirectorySkills Directory
SkillsLearnSecurityCategoriesDocsCommunityBlog
Sign InSubmit Skill
Skills Directory

Security-tested agent skills for Claude, coding agents, and AI workflows.

Directory

  • Browse Skills
  • All Skills A–Z
  • Claude Skills
  • Claude Code Skills
  • Agent Skills
  • Categories
  • Submit a Skill

Learn

  • Learn Hub
  • Install Claude Skills
  • Write SKILL.md
  • Skills vs MCP
  • Directories Compared

Security

  • Security
  • Methodology
  • Secure Claude Skills
  • Security Badges

Company

  • About
  • Community
  • Blog
  • API Docs
  • Advertise

2026 Skills Directory. All rights reserved.

Back to skills

Fork Skill Gap

BSecurity

Weekly cross-fork skill adoption digest — per-fork table of upstream skills the fork hasn't enabled, top forks by gap size called out, silent on small-gap weeks

6 stars
0 votes
0 copies
0 views
Added 6/6/2026
toolsgoshellbashgitapisecurity

Works with

api

Security Analysis

B85/100
highPerforms destructive filesystem operations

Scanned 6/6/2026

Install to Claude Code

$npx -y skills add anajuliabit/aeon --skill fork-skill-gap --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Fork Skill Gap?

Add the live security badge to your README — it updates automatically with every re-scan.

Security grade badge for Fork Skill Gap
[![Security: B — Skills Directory](https://www.skillsdirectory.com/api/skills/anajuliabit-fork-skill-gap/badge)](https://www.skillsdirectory.com/skills/anajuliabit-fork-skill-gap)

More formats (shields.io, HTML) on the badges page.

Download Zip
Files
SKILL.md
---
name: fork-skill-gap
description: Weekly cross-fork skill adoption digest — per-fork table of upstream skills the fork hasn't enabled, top forks by gap size called out, silent on small-gap weeks
var: ""
tags: [meta, community]
---
> **${var}** — Optional. Pass `dry-run` to skip notify (state and article still write). Pass `owner/repo` to override the parent repo. Combine with a space (`dry-run owner/repo`) for both.

Today is ${today}. Three fork-intelligence skills already exist: `fork-cohort` answers *is the fork alive?* (workflow runs in 7d), `fork-release-tracker` answers *has any fork shipped a versioned artifact?*, `contributor-spotlight` answers *who's pushing the most code?* None of them answer the obvious operator question: **"what's in upstream that I haven't adopted yet?"** This skill closes that layer.

## Why this exists

A fork that activates the agent on day one and never re-syncs accumulates an invisible drift — upstream keeps shipping skills, the fork stays at its activation-day skill count, and the operator has no surface that flags the gap. Skill drift is silent. The first sign is usually a fork operator noticing six months later that everyone else's agent is doing something theirs isn't.

The gap also tells upstream something: which new skills are getting picked up by the fleet, and which are launching into silence. A skill that ships and is never adopted by any fork in 8 weeks is a skill worth re-examining.

## Scope and inputs

Reads from two places, with graceful degradation if either is missing:

1. **`memory/topics/fork-cohort-state.json`** (optional accelerator) — gives the POWER + ACTIVE fork list, classification, and `enabled_count`. When present, this skill targets only POWER + ACTIVE forks (the audience that actually cares about gaps — STALE/COLD forks aren't running anything anyway).
2. **`gh api repos/{parent}/forks`** (always called as fallback / first run) — when fork-cohort state is absent, missing the forks list, or older than 8 days, this skill builds its own POWER + ACTIVE list from scratch using the same activation rule (≥1 workflow run in last 7d).

The intent is: when fork-cohort is enabled and runs Sunday 19:00, fork-skill-gap at 21:00 reuses its work. When fork-cohort hasn't been enabled yet, fork-skill-gap still works, it's just slower.

## Steps

### 0. Bootstrap

```bash
mkdir -p memory/topics articles
[ -f memory/topics/fork-skill-gap-state.json ] || cat > memory/topics/fork-skill-gap-state.json <<'EOF'
{"parent":null,"last_run":null,"last_status":null,"upstream_skill_count":null,"forks":{}}
EOF
```

`forks` is a map keyed by `owner/repo`. Each entry holds `{missing_count, missing_slugs (cap 50), top_missing_categories, last_seen, classification_source}`. Old entries (fork no longer in POWER+ACTIVE) are evicted on each run.

### 1. Parse var

- Split `${var}` on whitespace. Tokens: `dry-run`, anything matching `^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+$` (treated as `PARENT_OVERRIDE`), anything else.
- If any unknown token is present → log `FORK_SKILL_GAP_BAD_VAR: ${var}` and exit (no notify).
- `MODE=dry-run` if `dry-run` token present, else `execute`.

### 2. Resolve parent repo

```bash
if [ -n "$PARENT_OVERRIDE" ]; then
  PARENT_REPO="$PARENT_OVERRIDE"
else
  PARENT_REPO=$(gh api "repos/$(gh repo view --json nameWithOwner -q .nameWithOwner)" --jq '.parent.full_name // .full_name')
fi
PARENT_OWNER="${PARENT_REPO%%/*}"
```

If `state.parent` is set and differs from the resolved `PARENT_REPO` → log `FORK_SKILL_GAP_PARENT_CHANGED`, reset `forks` to `{}`, update `state.parent`.

### 3. Read upstream skills.json (parent baseline)

```bash
gh api "repos/${PARENT_REPO}/contents/skills.json" \
  --jq '.content' 2>/dev/null | base64 -d > /tmp/fsg-upstream.json
UPSTREAM_SLUGS=$(jq -r '.skills[].slug' /tmp/fsg-upstream.json | sort -u)
UPSTREAM_COUNT=$(echo "$UPSTREAM_SLUGS" | wc -l | tr -d ' ')
```

Also pull a slug→category map for the missing-categories rollup:

```bash
jq -r '.skills[] | "\(.slug)\t\(.category // "other")"' /tmp/fsg-upstream.json > /tmp/fsg-categories.tsv
```

If skills.json is missing from upstream (vanishingly unlikely on an Aeon-shaped parent) → log `FORK_SKILL_GAP_NO_UPSTREAM_MANIFEST`, exit (no notify).

### 4. Build the POWER + ACTIVE fork list

Try the cached path first:

```bash
COHORT_STATE=memory/topics/fork-cohort-state.json
COHORT_FRESH=false
if [ -f "$COHORT_STATE" ]; then
  COHORT_DATE=$(jq -r '.last_run // empty' "$COHORT_STATE")
  if [ -n "$COHORT_DATE" ]; then
    # within 8 days = fresh enough (handles weekly Sunday cadence + 1d grace)
    AGE_DAYS=$(( ($(date -u +%s) - $(date -u -d "$COHORT_DATE" +%s)) / 86400 ))
    [ "$AGE_DAYS" -le 8 ] && COHORT_FRESH=true
  fi
fi
```

- If `COHORT_FRESH=true`: read POWER + ACTIVE forks from `state.forks` (`jq -r '.forks | to_entries[] | select(.value.bucket == "POWER" or .value.bucket == "ACTIVE") | .key'`). Set `classification_source=cohort`.
- If `COHORT_FRESH=false`: fall back to live API. For each fork in `gh api "repos/${PARENT_REPO}/forks" --paginate`, check `gh api "repos/${FORK}/actions/runs?per_page=1" --jq '.workflow_runs[0].updated_at // empty'`. Include forks with a run in the last 7 days. Set `classification_source=live`. Apply retry-once-then-skip on 403/5xx, same policy as `fork-cohort`.

Cap at 80 forks per run. If exceeded, sort by stargazers desc and trim (log `truncated_at=80`).

If the resulting list is empty:
- If `classification_source=cohort` and the cohort state has zero POWER+ACTIVE forks → exit `FORK_SKILL_GAP_NO_ACTIVE` (no notify, log only).
- If `classification_source=live` and the live check found zero active forks → exit `FORK_SKILL_GAP_NO_ACTIVE` (no notify).
- If the forks listing itself failed → exit `FORK_SKILL_GAP_API_FAIL` (single-line error notify).

### 5. Per-fork: read fork's skills.json

For each fork in the active list:

```bash
gh api "repos/${FORK_FULL_NAME}/contents/skills.json?ref=${FORK_DEFAULT_BRANCH:-main}" \
  --jq '.content' 2>/dev/null | base64 -d > /tmp/fsg-fork.json
```

If the call returns 404 or the file is missing/empty/invalid JSON: the fork has stripped `skills.json` or is on a non-default branch we couldn't infer. Mark `unreadable=true` for that fork. **Do not** assume zero skills — that would inflate the gap on every fork that simply renamed the manifest.

Compute:

```bash
FORK_SLUGS=$(jq -r '.skills[].slug' /tmp/fsg-fork.json 2>/dev/null | sort -u)
MISSING_SLUGS=$(comm -23 <(echo "$UPSTREAM_SLUGS") <(echo "$FORK_SLUGS"))
MISSING_COUNT=$(echo "$MISSING_SLUGS" | grep -c .)
```

For each missing slug, look up its category from `/tmp/fsg-categories.tsv`. Roll up: top 3 categories by missing-slug count.

Error handling per fork:
- 404 on contents endpoint → `unreadable` (fork has no skills.json at default branch).
- 403 → retry once after 60s, then `unreadable`.
- 5xx → retry once after 10s, then `unreadable`.

### 6. Compute fleet-level rollup

```
MISSING_PER_FORK = sorted (desc) list of (fork, missing_count)
READABLE_FORKS   = forks where unreadable=false
GAP_DISTRIBUTION = histogram of missing_count across readable forks
TOP_MISSING_SLUGS = slugs missing on the most readable forks (slug → fork-count)
                    capped at top 10
```

Quiet-week gate (skip notify; still write article + state):

- All readable forks have `missing_count ≤ 5` AND
- There is a prior state record AND
- The previous run was also `FORK_SKILL_GAP_QUIET` or `FORK_SKILL_GAP_OK` with no new top-missing slugs

Otherwise, gate is open and notify fires.

### 7. Pick the verdict (one-line lede)

Priority order:
1. `WIDE_GAP: {N} forks each missing {M}+ upstream skills` — when ≥3 forks have `missing_count ≥ 20` (signals fleet-wide drift).
2. `BIG_FORK_GAP: @{owner} missing {N} skills` — when the top fork by missing_count is missing ≥15 skills (single-fork laggard worth a direct check-in).
3. `NEW_UPSTREAM_UNCLAIMED: {N} fresh skills with zero fleet adoption` — when ≥1 upstream skill shipped in the last 14 days has 0 fork adoption (read `updated` field from `skills.json`).
4. `STEADY: fleet within {N} skills of upstream` — typical week, max-gap-fork is within tolerance.
5. `COLD START: first scan — {N} active forks, median gap {M}` — first ever run, no prior state.

### 8. Write the article

Path: `articles/fork-skill-gap-${today}.md`

```markdown
# Fork Skill Gap — ${today}

**Verdict:** {one-line verdict from step 7}

**Parent:** {PARENT_REPO} · **Upstream skills:** {UPSTREAM_COUNT}
**Active forks audited:** {N_AUDITED} (POWER + ACTIVE) · **Readable manifests:** {N_READABLE}/{N_AUDITED}
**Median gap:** {M_MEDIAN} · **Max gap:** {M_MAX} · **Min gap:** {M_MIN}

---

## Forks by gap size

(Cap table at 20 rows by missing_count desc. Footer "... and N more" if truncated.)

| Fork | Owner | Source | Total upstream | Missing | Top missing categories |
|------|-------|--------|----------------|---------|------------------------|
| {full_name} | @{owner} | cohort\|live | {UPSTREAM_COUNT} | {missing_count} | {cat1} ({n}), {cat2} ({n}), {cat3} ({n}) |

---

## Top 10 unadopted upstream skills

(Slugs missing on the most forks — the inverse view. Helps upstream see which new skills aren't catching on.)

| Slug | Category | Shipped | Forks missing it |
|------|----------|---------|------------------|
| {slug} | {category} | {updated} | {fork_count} / {N_READABLE} |

---

## Unreadable forks

(Only render if any. Forks where skills.json was 404 / parse-failed / rate-limited.)

| Fork | Owner | Reason |
|------|-------|--------|

---

## Source status

`active_list_source={cohort|live} · forks_audited=N · skills_json_lookup=N/M · unreadable=N · truncated=true|false · cohort_state_age_days=N`
```

Cap article at ~400 lines. Sort forks descending by `missing_count`; ties broken by stargazers desc, then alphabetical.

### 9. Update state

Write `memory/topics/fork-skill-gap-state.json`:

```json
{
  "parent": "{PARENT_REPO}",
  "last_run": "${today}",
  "last_status": "FORK_SKILL_GAP_OK|FORK_SKILL_GAP_QUIET|...",
  "upstream_skill_count": N,
  "top_missing_slugs": [
    {"slug": "name", "fork_count": N, "category": "..."}
  ],
  "forks": {
    "owner/repo": {
      "missing_count": N,
      "missing_slugs": ["..."],          // cap 50
      "top_missing_categories": [["dev", 8], ["social", 3]],
      "unreadable": false,
      "last_seen": "${today}",
      "classification_source": "cohort|live"
    }
  }
}
```

Evict entries whose `last_seen` is more than 35 days old (covers ~5 missed weekly runs before purge).

### 10. Append to memory log

```
## fork-skill-gap
- Status: FORK_SKILL_GAP_OK | FORK_SKILL_GAP_QUIET | FORK_SKILL_GAP_NO_ACTIVE | FORK_SKILL_GAP_API_FAIL | FORK_SKILL_GAP_BAD_VAR | FORK_SKILL_GAP_PARENT_CHANGED | FORK_SKILL_GAP_DRY_RUN
- Verdict: {one-line verdict}
- Active forks audited: {N_AUDITED} (readable: {N_READABLE})
- Median gap: M_MEDIAN · Max gap: M_MAX
- Article: articles/fork-skill-gap-${today}.md
- Source status: active_list_source={cohort|live} · skills_json_lookup=N/M · unreadable=N
```

### 11. Notify — gated

**Skip notify entirely** when:
- `MODE=dry-run`, OR
- Status is `FORK_SKILL_GAP_NO_ACTIVE`, `FORK_SKILL_GAP_QUIET`, or `FORK_SKILL_GAP_BAD_VAR`, OR
- Quiet-week gate from step 6 is closed.

Otherwise send via `./notify` (keep ≤900 chars total — Telegram/Discord/Slack render):

```
*Fork Skill Gap — ${today} — {PARENT_REPO}*
{verdict line}

{N_READABLE} of {N_AUDITED} active forks audited. Upstream ships {UPSTREAM_COUNT} skills; the median fork is missing {M_MEDIAN}.

Top 3 forks by gap:
- @{owner1} — {short_name1} missing {N1} ({top_cat1})
- @{owner2} — {short_name2} missing {N2} ({top_cat2})
- @{owner3} — {short_name3} missing {N3} ({top_cat3})

{If TOP_MISSING_SLUGS has any with fork_count == N_READABLE — i.e. nobody has it:}
Universally unadopted upstream skills: {slug1}, {slug2}, {slug3}

Full report: articles/fork-skill-gap-${today}.md
```

## Exit taxonomy

| Status | Meaning | Notify? |
|--------|---------|---------|
| `FORK_SKILL_GAP_OK` | Run succeeded; verdict triggered notify gate | Yes |
| `FORK_SKILL_GAP_QUIET` | All forks within 5 skills of upstream + prior state existed + no new top-missing | No (log only) |
| `FORK_SKILL_GAP_DRY_RUN` | `MODE=dry-run`; state + article wrote, notify skipped | No |
| `FORK_SKILL_GAP_NO_ACTIVE` | Zero POWER+ACTIVE forks found | No (log only) |
| `FORK_SKILL_GAP_NO_UPSTREAM_MANIFEST` | Parent has no skills.json | No (log only) |
| `FORK_SKILL_GAP_PARENT_CHANGED` | Resolved parent differs from stored — fork-history reset | No (log only) |
| `FORK_SKILL_GAP_API_FAIL` | Forks listing failed after retry | Yes (single-line error notify) |
| `FORK_SKILL_GAP_BAD_VAR` | `${var}` parse failed | No |

## Constraints

- **Read-only across the fleet.** This skill never writes to fork repos, never opens issues on forks, never PRs against forks. The original idea suggested "shout-out" notifications; that's an upstream-channel announcement, not a fork-side write.
- **Never treat missing skills.json as zero skills.** Manifest absence → `unreadable=true`. A fork that renamed `skills.json` to `manifest.json` should not be reported as missing 118 skills.
- **Compare on slug, not on enabled state.** `enabled: true` vs `enabled: false` is `fork-skill-digest`'s job — this skill only answers "is the skill *present in the fork's skills.json* at all?" An enabled-but-stale skill is still a present skill here.
- **Bot owner allowlist:** `dependabot[bot]`, `github-actions[bot]`, `aeonframework[bot]` — skip from cohort rendering and from totals (they're never running the agent themselves; counting them inflates "active fork" numbers).
- **Cap fork processing at 80 per run.** At current ~50-fork scale this is a guard for viral days.
- **`MISSING_SLUGS` is recorded but bounded.** Per-fork state stores up to 50 missing slugs verbatim — past that, only the count and category rollup persist (state file size guard).

## Sandbox note

Uses `gh api` for everything — no `curl`, no env-var-in-headers. Authenticates via `GITHUB_TOKEN` automatically. The contents endpoint returns base64-encoded payloads; the `--jq '.content' | base64 -d` chain works in the sandbox because `gh api` handles auth internally and `base64 -d` is pure local.

Persistent 403 on a fork's contents endpoint marks it `unreadable` — the skill never lies about coverage. Persistent 403 on the forks listing → `FORK_SKILL_GAP_API_FAIL` with one error notify, then exit.

## Security

- The fork's `skills.json` is parsed as JSON only — never executed, never interpolated into shell commands. Slug values pass through `jq -r` extraction directly to `comm` for diffing. A malicious fork that ships `{"slug": "$(rm -rf /)"}` in their manifest produces a benign weird-looking row in the gap table.
- Skill descriptions and category strings from fork manifests are **not** rendered in the notification or article — only slugs (verified against the upstream manifest as the canonical source) and the upstream category mapping. This means a fork can't smuggle an attacker-controlled description into the operator's Telegram feed.
- The 200-char body truncation pattern from `fork-release-tracker` does not apply here — we don't read release bodies, only slug lists.

Attribution

anajuliabitanajuliabit
View sourceMore from anajuliabit →
SSkills DirectorySkills Directory

Your tool, in front of Claude Code builders.

3 founder slots · $299/mo · GSC-verified traffic · sponsors can never buy grades.

See placements

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments (0)

No comments yet. Be the first to comment!

SSkills DirectorySkills Directory

Your tool, in front of Claude Code builders.

3 founder slots · $299/mo · GSC-verified traffic · sponsors can never buy grades.

See placements

Related Skills

ucoz-landing-skill

Playbook for creating and editing uCoz landing pages via MCP tools (`templates_tool`, `ftp_tool`, `modules_tool`). Use for tasks such as: "build a landing page", "update the homepage as a landing page", "create a promo page on the homepage", "add a lead form / menu / SEO to the homepage". Homepage: `page_list`, `page_get`; first publish — `page_update` with full `page_tmpl`; HTML edits after generation — `patch_template` (module_id=2, template_id=1), not `update_template`. Activate the mail f...

107 votes

Paperclip

Interact with the Paperclip control plane API for task coordination and governance. Use when checking assignments, updating issue status, posting comments, delegating work, managing routines, or calling Paperclip API endpoints.

805541 votes

Instantly Rdsthomas Mission Control

Instantly.ai cold email outreach API - manage campaigns, leads, accounts, and analytics. Use for cold email automation, lead management, campaign creation/monitoring, and email account warmup.

761 votes

Daw Music

Digital Audio Workstation usage, music composition, interactive music systems, and game audio implementation for immersive soundscapes.

761 votes

Caveman Compress

Compress natural language memory files (CLAUDE.md, todos, preferences) into caveman format to save input tokens. Preserves all technical substance, code, URLs, and structure. Compressed version overwrites the original file. Human-readable backup saved as FILE.original.md. Trigger: /caveman-compress FILEPATH or "compress memory file"

1023330 votes
View all in tools →