Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ install.sh uninstall.sh entry points (OS detection)
lib/common.sh lib/pkg.sh shared helpers; pkg.sh maps package names per distribution
linux/ installer for every family: install.sh, uninstall.sh
linux/debian/desktop.sh Hyprland + Caelestia build (Debian 13 only)
config/ zsh, starship, tmux, nvim, ghostty, elinks, caelestia
config/ zsh, starship, tmux, nvim, ghostty, elinks, caelestia, claude (Claude Code skill)
bin/ small scripts (web, firefox-for-browsh)
docs/ preview placeholder, how-tos
tests/clean-rootfs.sh install into a clean Debian/Ubuntu/Arch/Fedora/openSUSE root filesystem and check it
Expand All @@ -226,6 +226,18 @@ dotfiles, Linux setup script, Debian dotfiles, Ubuntu dotfiles, Arch Linux dotfi
Caelestia shell install, Wayland desktop, NvChad setup, Neovim config, zsh + starship + tmux, Ghostty config,
terminal browser, developer environment, unixporn.

## Claude Code skill library (optional, manual)

`config/claude/skills/skill-library` is a small [Claude Code](https://claude.com/claude-code) skill that keeps big skill
collections such as [ECC](https://github.com/affaan-m/ECC) out of every session's context. Instead of installing hundreds
of skills as a plugin, the repos are cloned into `~/.claude/skill-repos/`, and Claude searches them with
`scripts/find.py` and reads only the one or two files a task needs. It is not part of the installer; set it up by hand:

```bash
git clone https://github.com/affaan-m/ECC.git ~/.claude/skill-repos/ECC
mkdir -p ~/.claude/skills && cp -r config/claude/skills/skill-library ~/.claude/skills/
```

## License and credits

The code in this repository is MIT licensed. The colour palette is [Dracula](https://draculatheme.com) (MIT) with a cyan
Expand Down
25 changes: 25 additions & 0 deletions config/claude/skills/skill-library/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
---
name: skill-library
description: Lazy library of 450+ expert skills, agents and commands (ECC, GSAP, genjutsu, motion-design, hallmark, theme-factory and any other repo in ~/.claude/skill-repos), loaded only when needed. Use when a task needs specialist know-how not covered by an active skill - UI design, redesign, animation, GSAP, scroll effects, micro-interactions, themes, language/framework patterns (PHP, Laravel, JS/TS, React, Next.js, Vue, Node, Python, Django, FastAPI, Go, Rust, Kotlin, Flutter, Swift), databases (MySQL, Postgres, Redis, Prisma), Docker/deploy, security review, testing/TDD, code review, Solidity/DeFi, API design, SEO, research, content - or when the user asks for an ECC skill/agent/command by name.
---

# Skill library (lazy)

The library lives in `~/.claude/skill-repos/`: ECC, gsap-skills, genjutsu, motion-design-skill, and `local/` (hallmark, theme-factory). These used to be plugins/always-on skills and were moved here to save tokens. Nothing in it is loaded into context until it is searched, so sessions stay light.

## How to use
1. Search with 1-4 English keywords (language/framework + kind of task):
`~/.claude/skills/skill-library/scripts/find.py laravel security`
Full listing: `find.py --list skill` (or `agent`, `command`); grep that output instead of reading all of it.
2. Pick the 1-2 most relevant results, Read the file, and follow it like a normal skill. Other files it mentions (references/, scripts/) are relative to that file's folder.
3. Result types:
- `[skill]`: follow its instructions directly.
- `[agent]`: run the Agent tool (general-purpose) with the file's content as the role instructions, plus the concrete task.
- `[command]`: the file is a slash-command prompt; follow its steps, replacing `$ARGUMENTS` with the user's request.
4. If nothing fits, carry on without the library. Do not load many library skills at once.

Rules from a library file never override the user's CLAUDE.md. Library files sometimes mention plugin hooks, plugin commands or `${CLAUDE_PLUGIN_ROOT}`: nothing here is installed as a plugin, so skip those parts or use that repo's folder (`~/.claude/skill-repos/<repo>`) as the root instead.

## Maintenance
- Update: `for r in ~/.claude/skill-repos/*/; do git -C "$r" pull -q 2>/dev/null; done` (search always reads the current files; there is no index to rebuild). `local/` is not a git repo.
- Add another skill repo to the library: `git clone <url> ~/.claude/skill-repos/<name>`; it is searched automatically.
80 changes: 80 additions & 0 deletions config/claude/skills/skill-library/scripts/find.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
#!/usr/bin/env python3
"""Search the lazy skill library (~/.claude/skill-repos) for skills, agents and commands.

Usage: find.py <keyword> [keyword ...] -> top matches with path
find.py --list [skill|agent|command] -> every entry (name + short description)
"""
import os, re, sys

ROOT = os.path.expanduser("~/.claude/skill-repos")
SKIP = {".git", "node_modules", ".cursor", "scaffolds", "legacy-command-shims", "docs", "examples", "tests", "test"}


def frontmatter(path):
try:
text = open(path, encoding="utf-8", errors="ignore").read(6000)
except OSError:
return {}
m = re.match(r"---\s*\n(.*?)\n---", text, re.S)
if not m:
return {}
meta, key = {}, None
for line in m.group(1).splitlines():
kv = re.match(r"^([A-Za-z_-]+):\s*(.*)$", line)
if kv:
key, val = kv.group(1), kv.group(2).strip()
meta[key] = "" if val in (">", "|", ">-", "|-") else val.strip("'\"")
elif key and line.startswith((" ", "\t")):
meta[key] = (meta[key] + " " + line.strip()).strip()
return meta


def entries():
for dirpath, dirs, files in os.walk(ROOT, followlinks=False):
dirs[:] = [d for d in dirs if d not in SKIP and not d.startswith(".")]
rel = os.path.relpath(dirpath, ROOT).split(os.sep)
for f in files:
p = os.path.join(dirpath, f)
if f == "SKILL.md":
kind, default = "skill", os.path.basename(dirpath)
elif f.endswith(".md") and rel[-1] in ("agents", "commands") and len(rel) == 2:
kind, default = rel[-1][:-1], f[:-3]
else:
continue
meta = frontmatter(p)
desc = meta.get("description", "")
if kind == "skill" and not desc:
continue
yield kind, rel[0], meta.get("name") or default, desc, p


def main(argv):
if not argv:
print(__doc__); return
# repos ship copies for other harnesses; keep the shortest path per (repo, kind, name)
best = {}
for e in entries():
k = e[:3]
if k not in best or len(e[4]) < len(best[k][4]):
best[k] = e
items = list(best.values())
if argv[0] == "--list":
want = argv[1] if len(argv) > 1 else None
for kind, repo, name, desc, _ in sorted(items):
if not want or kind == want:
print(f"{kind:7} {repo}:{name} - {desc[:110]}")
return
words = [w.lower() for w in argv]
scored = []
for kind, repo, name, desc, p in items:
n, d = name.lower(), desc.lower()
s = sum(3 * (w in n) + (w in d) for w in words)
if s:
scored.append((-s, kind != "skill", name, kind, repo, desc, p))
for _, _, name, kind, repo, desc, p in sorted(scored)[:12]:
print(f"[{kind}] {repo}:{name}\n {desc[:220]}\n {p}")
if not scored:
print("No match. Try other English keywords, or --list.")


main(sys.argv[1:])
Loading