Framework: Zola
Hosting: Github Pages
Theme: Consoler Dark
Title: parkerjones.dev

Shipping Claude Skills with Nix: a Reproducible Agent Toolkit Across My Fleet

2026-06-26
Parker Jones and Claude Opus 4.8 in software
#nix , #claude-code , #ai , #agents , #skills , #home-manager and #reproducibility
5 minute read

A coding agent is only as good as the procedures you hand it. Out of the box, a model improvises every task from scratch; give it a skill — a written procedure for "do TDD," "diagnose a hard bug," "turn this into issues" — and it stops guessing and starts following a method you trust. I've built up a collection of these, and once you have more than a handful the real problem isn't writing them, it's distribution: getting the same skills onto every machine I work from, version-pinned, without copy-pasting Markdown around.

I solved that with Nix. Here's the setup.

What a skill is

My skills live in a repo (a fork of Matt Pocock's skills, credit where it's due), organized into buckets — engineering, qa, productivity, personal, misc. Each skill is a directory with a SKILL.md: YAML frontmatter naming it and describing when to use it, then the procedure itself.

---
name: diagnose
description: Disciplined diagnosis loop for hard bugs and performance
  regressions. Reproduce → minimise → hypothesise → instrument → fix →
  regression-test. Use when user says "diagnose this" / "debug this"...
---

# Diagnose
...

The description is load-bearing — it's what the agent matches against to decide whether the skill is relevant. A few I reach for constantly: tdd (red-green-refactor), diagnose (the loop above), to-prd / to-issues / triage (turning a vague ask into tracked work), and write-a-skill (the skill that writes more skills). Small, composable, model-agnostic. No framework owning my process — just procedures I can read, edit, and trust.

Three ways to install them

The repo supports three distribution paths, in increasing order of how much I actually rely on them.

1. npx, for anyone. The zero-commitment path:

npx skills@latest add parallaxisjones/skills

Pick the skills and agents you want, and you're set. Great for trying them on a machine that isn't mine.

2. A symlink script, for a local clone. If I've cloned the repo, link-skills.sh symlinks every SKILL.md into ~/.claude/skills/ so the CLI picks them up directly. It's idempotent — re-run after pulling — and it specifically guards against the footgun where ~/.claude/skills is itself a symlink back into the repo, which would write per-skill symlinks into my own working copy:

if [ -L "$DEST" ]; then
  resolved="$(readlink -f "$DEST")"
  case "$resolved" in
    "$REPO"/*) echo "refusing to pollute the repo"; exit 1 ;;
  esac
fi

That defensive check is the kind of thing you write after the first time a script eats its own tail.

3. Nix, for my actual fleet. This is the one that matters. My skills repo is a flake input in my system config, and a home-manager module materializes them declaratively on every machine.

The Nix wiring

Two inputs do the work — my skills repo, and agent-skills-nix, a home-manager module that knows how to turn a skills repo into materialized files:

# flake.nix
inputs = {
  agent-skills-nix = {
    url = "github:Kyure-A/agent-skills-nix";
    inputs.nixpkgs.follows = "nixpkgs";
    inputs.home-manager.follows = "home-manager";
  };
  my-skills.url = "github:parallaxisjones/skills";
};

Then in home-manager I point the module at my repo, filter to the buckets I want live, and enable everything:

agent-skills = {
  enable = true;
  sources.mine = {
    input  = "my-skills";
    subdir = "skills";
    filter.nameRegex = "^(engineering|misc|personal|productivity)/.*";
  };
  skills.enableAll = true;
  targets.claude.enable = true;
};

That's the whole thing. On nixos-rebuild switch (or darwin-rebuild on the Mac), every SKILL.md matching the regex gets written into Claude's skills directory. The deprecated/ bucket is excluded by the filter, so retiring a skill is a one-line regex change, not a manual delete on five machines.

Why bother — what Nix actually buys here

You could argue the symlink script is simpler, and for one machine it is. The fleet is where Nix earns it:

There's a framing I lean on for deciding what to manage this way — think of three zones. Zone 1 is stable, declarative config (the home-manager module enabling skills). Zone 3 is runtime state that should never touch Nix (per-conversation agent memory). Skills sit in Zone 2: authored content, edited as plain files in their repo, but materialized onto each machine by Nix. Nix owns where they land and which version; I own what they say.

The honest caveat

Nix doesn't validate skill content — a SKILL.md with a bad description will deploy just as reliably as a good one. This pipeline guarantees distribution and pinning, not quality. The quality comes from treating skills like code: review them, iterate on the descriptions when the agent picks the wrong one, and delete the ones that stop earning their place (that's what deprecated/ is for).

But that's the right division of labor. Writing a good procedure is human work. Making sure that procedure is identically present on every machine I touch is exactly the kind of toil Nix exists to kill. Treat your agent's skills as part of your declarative system, not as dotfiles you sync by hand.

— Parker Jones, parkerjones.dev