Skip to content
← Selected projects

Agent skills

2026 · open source · claude code + codex

A skill is a markdown file that tells a coding agent when to act and how. I had the same eight copied into several repos, and each copy drifted. Now the shareable ones live in one public repo, ong6/skills. A small command links them into every repo I work in, and publishes my edits back.

Figure 01 · Link and publish · skills

One checkout per machine, linked into every repo

skills up runs at session start: it fast-forwards a clean checkout in the background and links what the machine's profile enables. skills publish guards, lints, commits and pushes an edit. The plugin path reads upstream one way.

  • fast-forward + link
  • guard · lint · push
UPSTREAMCONSUMERSong6/skillsskills/24 × SKILL.mdcatalog.yamlone category per skillbin/skillsup · doctor · guard · publish.claude-plugin/marketplace + pluginNotes repoproject scope · skills up → publishEvery repo, home machinesuser scope · skills up → publishDev boxpublic only · skills up → publishPlugin marketplaceread-onlyFAST-FORWARD + LINKGUARD · LINT · PUSH
fig. 1 — link and publish. The repo holds the skills, a catalog that regenerates the README, the bin/skills command and the plugin manifest. Each machine keeps one checkout. At session start, skills up fast-forwards it in the background when it is clean and symlinks the skills that machine's profile enables into the paths Claude Code and Codex read. An edit goes back through skills publish. The plugin marketplace installs the same repo read-only.

Editing skills where I use them

The plugin install is useful when I just want to use the skills. But I also want to edit them while working. Before the shared repo, a fix made in one repo stayed there and the other copies fell behind.

So the checkout is the canonical copy: one clone per machine, beside my other repos. Each repo gets symlinks into it, so an edit made from any repo lands in the same files. Publishing carries it upstream, and other machines pick it up at their next session start. Claude Code and Codex read the same files through the same links.

One command, run by a hook

skills up runs at SessionStart. It finds this machine in a machines.json registry, links the skills its profile enables into .claude/skills and .agents/skills, and removes links it no longer wants. Fetching runs in the background, and a checkout only fast-forwards when it is clean. The hook never prompts, always exits 0, and returns in well under a second when nothing changed.

A profile picks the scope. Project scope links into named repos only; user scope links into the home folders, so every repo sees the skills. enable and disable take skill names or a whole category. Only symlinks that point into the checkouts are ever created or removed; anything else in a link folder is left alone and reported.

skills publish is the way back. It takes a lock, runs the public-safety guard, lints the skills that changed, commits and pushes. If the push is rejected, it rebases once and tries again. skills doctor reports the profile, every link, dirty checkouts and the last fetch, and exits 1 when something is off.

a session start

$ skills up --repo . --machines machines.jsonskills: 24 linked (Laptop, project) · fetched just now$ skills doctor --repo . --machines machines.jsonmachine  Laptop (skills profile)scope    project · root ~/src · private noskills   24 selected of 24 availablelinks    ~/src/notes/.agents/skills: 24 oklinks    ~/src/notes/.claude/skills: 24 okdoctor: ok

a home path in a public skill

$ skills guard skills/skills/handoff/SKILL.md:19: home-pathguard: 1 hit(s) in 1 file(s); fix them or mark a  deliberate line with `public-guard: allow`$ echo $?2
fig. 2 — a session start as the hook sees it, then the guard on a home path added to a public skill. publish runs the same guard and refuses with exit 2, before anything is committed.

The tests build temporary homes, checkouts and bare remotes, then exercise linking, stale and foreign links, both scopes, category selectors, publishing and the guard. CI runs them on Python 3.12 and on 3.7, the oldest machine I link skills into. All of it runs locally, with no network.

Keeping the catalog current

catalog.yaml puts each skill in exactly one category and lists, per category, other people's skills I rate. A script regenerates the README tables from it plus each skill's frontmatter, and exits 1 if a folder on disk is missing from the catalog or the catalog names a folder that does not exist. This catches a missing or renamed skill when the catalog is built. bin/skills reads the same file, so a profile can switch a whole category on or off.

Five of the seven categories also link to other people's projects. I want the catalog to be useful even where I have not written a skill myself.

skills
24 · one SKILL.md each
catalog
7 categories · 18 links out
install paths
3 · one folder, bin/skills, plugin
agents
Claude Code, Codex
bin/skills
one file · stdlib Python 3.7+
cli tests
22 · CI on Python 3.12 and 3.7
catalog build
exits 1 when tree and catalog disagree
status
MIT · github.com/ong6/skills

What stays out

Only shareable skills go in. Anything naming my paths, accounts or voice lives in a separate private repo, which can use public skills by name; the public repo never points back. skills guard blocks home-directory paths, personal email addresses, phone numbers, keys and tokens, and skill code that launches another agent CLI. The 24 here cover research, writing, design, trips and shopping, interview practice, field-engineering decks and pilot evidence, and making and testing skills.

// AI TOOLCHAIN

How these tools fit together

Ong Jun Xiong

SOFTWARE ENGINEER · SINGAPORE

ContactHobbiesArchiveNotesUI PackGitHubLinkedInSource

© 2026 Ong Jun Xiong