{"slug": "i-built-an-efficient-graph-search-plugin-for-claude-code-skills", "title": "I built an efficient graph-search plugin for Claude Code skills", "summary": "Developer Daniel Lublinsky released Skill Atlas, a free, open-source graph-search plugin for Claude Code that adds a 'searchable' tier between enabled and disabled skills, cutting per-session token usage from 2,544 to 338 tokens (−86.7%) across 53 skills by keeping only 6 enabled. The plugin, installable via 'claude plugin marketplace add danielLublinsky/Skill_Atlas', builds a graph index and renders an HTML atlas, with a search costing about 2.4k tokens only when invoked.", "body_md": "**A third tier for Claude Code skills — dormant, zero tokens, still findable.**\nSearch a graph of your collection instead of preloading it.\n\n53 skills, 6 kept enabled: 2,544 → 338 tokens per session (−86.7%). The other\n47 cost nothing until asked for.\n\n```\nclaude plugin marketplace add danielLublinsky/Skill_Atlas\nclaude plugin install skill-atlas@skill-atlas\n```\n\nPython 3 only — no dependencies, no network (d3 is vendored). Later:\n`claude plugin update skill-atlas@skill-atlas`\n\n.\n\n## From a local clone (for hacking on it)\n\n```\ngit clone https://github.com/danielLublinsky/Skill_Atlas.git\nclaude plugin marketplace add ./Skill_Atlas\nclaude plugin install skill-atlas@skill-atlas\n```\n\n| Command | What it does |\n|---|---|\n`/skill-atlas` |\nBuild the graph, categorize what's new, render `atlas.html` . Run this once to set up. |\n`/skill-atlas:edit-searchable` |\nMove plugins in and out of the searchable tier, interactively. |\n`/skill-atlas:skill-search` |\nFind the right skill for a task across the whole library, dormant tier included. Usually you never type this — Claude calls it on its own before a nontrivial task. |\n(automatic) |\nA SessionStart hook keeps everything fresh after that. |\n\nThen open `./.claude/skill-atlas/atlas.html`\n\nin a browser, and just ask for a\ntask — the search runs itself, names the skill it picked in one line, and gets\non with the work.\n\n## Without the plugin (Makefile)\n\n```\nmake build    # graph.json + catalog/ from your live manifests\nmake render   # atlas.html\nmake check    # CI gate: exit 1 on any broken reference or dangling mention\nmake test     # unit suite against fixtures — never touches the real ~/.claude\n```\n\nClaude Code injects **every** enabled skill's description into **every** session —\n~48 tokens each. At 100+ skills that's thousands of tokens you pay for constantly,\nand near-duplicates quietly compete for the model's attention.\n\nDisabling fixes the cost and loses the skill. So Skill Atlas adds a tier in between:\n\n| Tier | In context? | Findable? | Cost / session |\n|---|---|---|---|\n🟢 enabled |\nyes | natively | ~48 tokens each |\n🟣 searchable |\nno |\nvia `skill-search` |\n0 |\n⚫ disabled |\nno | no | 0 |\n\nA **searchable** skill is dormant — its description never enters your context, but\nit stays discoverable on demand. All that advertises the whole dormant tier is one\n~50-token line at session start.\n\n```\nflowchart LR\n    A([\"🧭 a task arrives<br/><i>“resolve this merge conflict”</i>\"])\n\n    subgraph R1[\"1️⃣ read the index · ~1.6k tok\"]\n        I[\"<b>_index.md</b> — 14 lines<br/><i>name · count · what it covers · member tokens</i>\"]\n    end\n\n    subgraph R2[\"2️⃣ read one shard · ~0.6k tok\"]\n        S[\"<b>version-control.md</b><br/>using-git-worktrees<br/>resolving-merge-conflicts ← hit\"]\n    end\n\n    X[\"the other 13 shards<br/><i>~10k tok · never opened</i>\"]\n\n    A --> I\n    I -- \"pick ONE category\" --> S\n    I -. skipped .-> X\n    S --> E[\"🟢 <b>enabled</b><br/><i>invoke natively</i>\"]\n    S --> F[\"🟣 <b>searchable</b><br/><i>read its path,<br/>follow as instructions</i>\"]\n\n    classDef file stroke:#8a8578,stroke-width:1.5px\n    classDef ghost fill:transparent,stroke:#8a8578,stroke-width:1.5px\n    classDef hit fill:#1baf7a26,stroke:#1baf7a,stroke-width:2px\n    classDef dormant fill:#8a5cd626,stroke:#8a5cd6,stroke-width:2px\n    classDef muted fill:transparent,stroke:#8a8578,stroke-width:1px,stroke-dasharray:4 3\n    class I,S file\n    class A ghost\n    class E hit\n    class F dormant\n    class X muted\n    style R1 fill:#3987e514,stroke:#3987e5,stroke-width:1.5px\n    style R2 fill:#3987e514,stroke:#3987e5,stroke-width:1.5px\n```\n\n**A search costs ~2.4k tokens** — the index (1.6k), one shard (~0.6k) and the\nsearch skill itself (0.4k) — **and you pay it only when a search happens.** The\nalternative it replaces is every dormant description sitting in context from\nsession start, billed whether you search or not. The arbitrage is real when\nsearches are occasional; it is not free, and the numbers above are measured\nrather than estimated.\n\nOne shard is the norm. A second is opened only when the entry you found lists a\ncategory you haven't read — the catalog pointing, not the model guessing — and\nnever a third. Counts overlap on purpose, so the pick doesn't have to land on\n*the* right bucket, only *a* right one. Tier-off skills are excluded when shards\nare written, so a disabled plugin can never return through a side door. The\nresult is announced in one line before the work continues: search is a lookup\ninside a task, not a deliverable.\n\n**The model categorizes once.** The first run drafts 8–12 categories named for\n*user-intent task shapes*, not products, then freezes the taxonomy. Later runs only\nfile new skills and re-confirm the ones whose description changed — each assignment\ncarries a hash of the description it was made against.\n\nDiscovery is **manifest-driven** — `installed_plugins.json`\n\n→ each `plugin.json`\n\n→\nsettings — so marketplace catalogues and stale cached versions never pollute the\npicture. (The naive \"every directory with a SKILL.md\" count over-counts by ~60% on a\nreal machine: `build_graph.py --naive-count`\n\n.)\n\nSkills are nodes. The edges are the interesting part:\n\n| Edge | Caught | |\n|---|---|---|\n| 🔗 | references — a skill → its own bundled files |\nbroken when the file isn't there |\n| 💬 | mentions — a skill naming another skill |\ndangling when the target is `disabled` , `unregistered` or `absent` |\n\nMention matching is deliberately strict — only backticked, `skills/<name>`\n\n, or\nunambiguous hyphenated names, code fences stripped first. Loose matching produced 91\nedges on a 41-skill collection, almost all of them ordinary English words. You also\nget duplicate names, orphans, and an exit code to gate CI on.\n\n`atlas.html`\n\nis self-contained and opens from `file://`\n\nwith zero network requests:\n**scope** (origin, state, breakage in red) and **categories** (hub-and-spoke — solid\nedge to the home category, dashed to the rest; search `cat:<name>`\n\nto isolate).\n\n**What's really registered**— discovery is manifest-driven, so unregistered copies and stale plugin caches surface as their own nodes instead of quietly counting.**Broken bundles**— a`references`\n\nedge whose file isn't on disk draws red, so a typo'd`references/foo.md`\n\nis visible without opening anything.**Why it didn't trigger**— tier is the node's fill (enabled · searchable · disabled), and duplicate names are called out in the footer with both nodes drawn.**Read it in place**— pin any skill or bundled file and hit** open markdown**to read the source in a popup without leaving the graph.\n\n[ docs/](/danielLublinsky/Skill_Atlas/blob/main/docs) — component guide, numbered 1–9: start at\n\n[overview](/danielLublinsky/Skill_Atlas/blob/main/docs/1-overview.md), then jump to the component you're touching (discovery, graph build, categorization, catalog & search, rendering, hooks).\n\n[ DESIGN.md](/danielLublinsky/Skill_Atlas/blob/main/DESIGN.md) — the historical record for both phases: what was\nconsidered, what was chosen, what was dropped, and why\n\nApache-2.0 — see [LICENSE](/danielLublinsky/Skill_Atlas/blob/main/LICENSE).\n\nD3 is vendored in [vendor/d3.v7.min.js](/danielLublinsky/Skill_Atlas/blob/main/vendor/d3.v7.min.js) and inlined into\nevery generated `atlas.html`\n\n. It is ISC-licensed, © 2010-2023 Mike Bostock —\nsee [vendor/LICENSE-d3](/danielLublinsky/Skill_Atlas/blob/main/vendor/LICENSE-d3).", "url": "https://wpnews.pro/news/i-built-an-efficient-graph-search-plugin-for-claude-code-skills", "canonical_source": "https://github.com/danielLublinsky/Skill_Atlas", "published_at": "2026-08-14 16:44:06+00:00", "updated_at": "2026-08-14 17:12:18.079622+00:00", "lang": "en", "topics": ["developer-tools", "ai-tools", "large-language-models"], "entities": ["Daniel Lublinsky", "Skill Atlas", "Claude Code", "GitHub"], "alternates": {"html": "https://wpnews.pro/news/i-built-an-efficient-graph-search-plugin-for-claude-code-skills", "markdown": "https://wpnews.pro/news/i-built-an-efficient-graph-search-plugin-for-claude-code-skills.md", "text": "https://wpnews.pro/news/i-built-an-efficient-graph-search-plugin-for-claude-code-skills.txt", "jsonld": "https://wpnews.pro/news/i-built-an-efficient-graph-search-plugin-for-claude-code-skills.jsonld"}}