skills/docs/worklogs/2025-11-30-per-repo-skill-deployment-design.org
dan e921fd96df feat: add per-repo skill deployment pattern
- Add bin/use-skills.sh helper with use_skill and load_skills_from_manifest
- Add .skills manifest pattern for declarative skill configuration
- Fix ai-skills.nix: remove broken npm plugin code, update skill list
- Add update-opencode, web-search, web-research to flake.nix availableSkills
- Add RFC and documentation for team adoption

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-30 14:47:02 -08:00

9.7 KiB
Raw Blame History

Per-Repo Skill Deployment: Design and RFC

Session Summary

Date: 2025-11-30 (Sunday)

Focus Area: Designing and documenting per-repo skill deployment pattern

Accomplishments

  • Explored skills repository structure and identified 9 outstanding issues
  • Populated beads issue tracker with all identified work items
  • Set up dependency relationships between design decisions and documentation tasks
  • Fixed ai-skills.nix module - removed broken opencode-skills npm plugin code
  • Updated flake.nix to include all 8 skills (was missing update-opencode, web-search, web-research)
  • Designed per-repo skill deployment pattern using direnv + nix build
  • Created bin/use-skills.sh helper script with use_skill, use_skills, load_skills_from_manifest
  • Created docs/PER-REPO-SKILLS.md with quick start documentation
  • Created docs/RFC-SKILLS-MANIFEST.md with full pattern specification
  • Introduced .skills manifest file concept for agent-friendly skill configuration
  • Survey existing repos for .envrc patterns (next step)
  • Migrate pilot project to new pattern

Key Decisions

Decision 1: Per-repo skills via direnv + nix build (not devShell)

  • Context: User has Nix but wanted something lighter than full flake.nix in each consumer project
  • Options considered:

    1. Home Manager module - writes to global ~/.claude/skills/, no per-repo differentiation
    2. devShell symlinks - requires flake.nix in every consumer project
    3. direnv + nix build - lighter, just add to .envrc
  • Rationale: direnv already used in many repos, nix build caches builds, no new files needed
  • Impact: Each project can specify its own skill set via .envrc or .skills file

Decision 2: .skills manifest file for agent editing

  • Context: Agent needs to be able to add/query skills without bloating context
  • Options considered:

    1. Agent edits .envrc directly - fragile, .envrc has other content
    2. Central config mapping repos to skills - indirection, sync complexity
    3. Separate .skills file - clean for agent editing, .envrc reads it
  • Rationale: .skills is simple text (one skill per line), easy for agent to read/modify
  • Impact: Agent can answer "what skills do we have?" by reading .skills, can add skills by appending

Decision 3: Keep both Claude Code and OpenCode support

  • Context: Asked whether to simplify to Claude Code only
  • Decision: Keep dual-agent support
  • Rationale: Infrastructure already supports both, minimal extra complexity
  • Impact: Skills symlinked to both .claude/skills/ and .opencode/skills/

Decision 4: Always latest from skills repo (no version pinning)

  • Context: User preference for simplicity over reproducibility here
  • Decision: Default to latest, optional rev pinning if needed
  • Impact: nix build fetches latest, caches locally for performance

Problems & Solutions

Problem Solution Learning
ai-skills.nix had broken fetchFromNpm with empty sha256 Removed entire opencode-skills npm plugin section Keep modules simple, remove dead code
availableSkills list in flake.nix was stale Added update-opencode, web-search, web-research Need process to keep lists in sync
bd dep syntax confusion (from vs to) bd dep add A B means "A depends on B" not "A blocks B" Read help output carefully
Home Manager module writes to global paths, not per-repo Per-repo uses .claude/skills/ in project, not global Different deployment targets, different mechanisms

Technical Details

Code Changes

  • Total files modified: 2 existing + 3 new
  • Key files changed:

    • `modules/ai-skills.nix` - Removed 60 lines of broken npm plugin code, updated skill list in docs
    • `flake.nix` - Added 3 skills to availableSkills list
  • New files created:

    • `bin/use-skills.sh` - Helper script with use_skill, use_skills, load_skills_from_manifest functions
    • `docs/PER-REPO-SKILLS.md` - Quick start documentation for per-repo deployment
    • `docs/RFC-SKILLS-MANIFEST.md` - Full RFC documenting the .skills manifest pattern

Commands Used

# Beads workflow
bd ready                    # See available work
bd create --title="..." --type=task --description="..."
bd update <id> --status=in_progress
bd close <id> --reason="..."
bd dep add <from> <to>      # from depends on to
bd list
bd stats

# Nix validation
nix flake check             # Verified all packages build

# Skill deployment pattern
nix build --print-out-paths --no-link "git+ssh://...#worklog"
ln -sfn "$out" ".claude/skills/worklog"

Architecture Notes

  • Skills repo flake exports individual packages per skill
  • Consumer projects don't need flake.nix, just .envrc with nix build calls
  • Symlinks point to /nix/store paths - Claude Code follows them fine
  • .skills manifest is committed, .claude/skills/ symlinks are gitignored
  • Brief context blurb in project CLAUDE.md tells agent about skills (~4 lines)

Process and Workflow

What Worked Well

  • Beads for tracking issues - good visibility into work items
  • Plan mode for thinking through architecture before coding
  • Iterative questioning to clarify user requirements (Nix available? Source of truth?)
  • "Ultrathink" prompt to go deeper on design questions

What Was Challenging

  • Initial confusion about what "per-repo" meant (global HM module vs project-local)
  • Getting beads dependency direction right (had to remove and re-add)
  • Balancing thoroughness with "don't overengineer" guidance

Learning and Insights

Technical Insights

  • Claude Code loads skills from both ~/.claude/skills/ (global) AND .claude/skills/ (project-local)
  • Project-local skills add to or shadow global - good for per-repo customization
  • nix build print-out-paths gives store path without building if cached
  • direnv + nix build is lighter than devShell for simple cases

Process Insights

  • "Ultrathink" is a good trigger for deeper analysis
  • Questioning assumptions early ("do you need flake.nix in every project?") saves design churn
  • Agent-friendly design (like .skills manifest) is worth thinking about explicitly

Architectural Insights

  • Separation of concerns: manifest (.skills) vs loader (.envrc) vs runtime (symlinks)
  • Agent context efficiency: brief blurb in CLAUDE.md, not full skill docs
  • The .skills file serves multiple purposes: human config, agent query, agent edit

Context for Future Work

Open Questions

  • Should we validate skill names? Currently fails silently if skill doesn't exist
  • Should we support skill versions? e.g., `worklog@v1.2.0`
  • Should there be a `skills` CLI? e.g., `skills add worklog`, `skills list`
  • How to handle skills that need secrets (like web-research with KAGI_API_KEY)?

Next Steps

  • Survey existing repos for .envrc patterns
  • Migrate pilot project to new .skills pattern
  • Clean up beads.left.jsonl merge artifact
  • Commit and push this session's work

Related Work

  • Previous: Nix Flake Module Development - original HM module design
  • Previous: Create Web Search Skill - skill that motivated this work
  • Related: docs/CROSS-REPO-SKILL-COLLABORATION.md - Nix flake input pattern (for HM deployments)
  • Created: docs/RFC-SKILLS-MANIFEST.md - this session's main artifact

Raw Notes

Beads status at session end:

  • 4 closed: skills-3o7 (nix fix), skills-pu4 (cleanup), skills-cnc (helper), skills-39g (RFC)
  • 7 open: README updates, design decisions, code review items

Key insight: The question "how does an agent help configure skills?" led to the .skills manifest design. Agent-friendly != human-friendly, need both.

The pattern is intentionally minimal:

  1. .skills file lists skill names
  2. .envrc reads .skills and symlinks
  3. CLAUDE.md tells agent about available skills
  4. Agent can read/edit .skills, tell user to direnv reload

Session Continuation: Pilot Migration and Flow Design

Pilot Migration: orch

Successfully migrated orch project as pilot for .skills pattern:

  • Created `.skills` manifest with `worklog`
  • Updated `.envrc` to source helper and call `load_skills_from_manifest`
  • Updated `.gitignore` to exclude `.claude/skills/` and `.opencode/skills/`
  • Added Skills section to `AGENTS.md`

Technical Fix: Local Nix Build

  • Original default URL `git+ssh://git@forgejo.delmore.io/dan/skills.git` didn't resolve
  • Changed to `git+/$HOME/proj/skills` for offline/local development
  • `path:` scheme failed due to `.beads/bd.sock` (unsupported file type)
  • `git+file://` only includes git-tracked files, works correctly

Verified Working

orch/.claude/skills/worklog -> /nix/store/...-ai-skill-worklog
orch/.opencode/skills/worklog -> /nix/store/...-ai-skill-worklog

Dotfiles Analysis

Explored how dotfiles manages Claude Code:

  • `home/claude.nix` deploys 4 skills via Home Manager
  • `~/.claude/settings.json` has `bd prime` hooks (global, not Nix-managed)
  • Drift: dotfiles has 4 skills, skills repo has 8
  • Global skills come from dotfiles copy, per-repo from skills repo directly

Flow Design Discussion

Identified need to simplify per-repo flow. Current requires 4 file changes. Options discussed:

  1. Global direnv helper (add `load_project_skills` to direnvrc)
  2. CLI tool (`skills init`, `skills add`)
  3. Convention over configuration (auto-load if `.skills` exists)

Direction: Simplify with global direnv helper + team-facing RFC.

Session Metrics

  • Commits made: 0 (uncommitted)
  • Files touched: 7+ (skills repo + orch pilot)
  • Lines added/removed: ~500 lines new content
  • New documentation: RFC-SKILLS-MANIFEST.md, PER-REPO-SKILLS.md
  • Beads created: 9 issues
  • Beads closed: 4 issues
  • Pilot project: orch migrated successfully