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>
This commit is contained in:
parent
0e098ba0bb
commit
e921fd96df
3
.gitattributes
vendored
Normal file
3
.gitattributes
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
|
||||
# Use bd merge for beads JSONL files
|
||||
.beads/issues.jsonl merge=beads
|
||||
68
bin/use-skills.sh
Executable file
68
bin/use-skills.sh
Executable file
|
|
@ -0,0 +1,68 @@
|
|||
#!/usr/bin/env bash
|
||||
# Helper for per-repo skill deployment via direnv
|
||||
# Source this from your .envrc or copy the functions
|
||||
#
|
||||
# Usage in .envrc:
|
||||
# source /path/to/skills/bin/use-skills.sh
|
||||
# use_skills worklog web-search
|
||||
#
|
||||
# Or without sourcing (copy-paste into .envrc):
|
||||
# SKILLS_REPO="git+ssh://git@forgejo.example/dan/skills.git"
|
||||
# use_skill() { ... }
|
||||
# use_skill worklog
|
||||
|
||||
# Default repo - uses local git, override with SKILLS_REPO for remote
|
||||
# Local: git+file:///home/dan/proj/skills (default, works offline)
|
||||
# Network: git+http://192.168.1.108:3000/dan/skills.git
|
||||
SKILLS_REPO="${SKILLS_REPO:-git+file://$HOME/proj/skills}"
|
||||
|
||||
# Install a single skill via nix build + symlink
|
||||
use_skill() {
|
||||
local skill="$1"
|
||||
local out
|
||||
|
||||
out=$(nix build --print-out-paths --no-link "${SKILLS_REPO}#${skill}" 2>/dev/null)
|
||||
if [[ -z "$out" ]]; then
|
||||
echo "use_skill: failed to build ${skill}" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Claude Code
|
||||
if [[ -d .claude ]] || [[ -n "${SKILLS_CLAUDE:-}" ]]; then
|
||||
mkdir -p .claude/skills
|
||||
ln -sfn "$out" ".claude/skills/${skill}"
|
||||
fi
|
||||
|
||||
# OpenCode
|
||||
if [[ -d .opencode ]] || [[ -n "${SKILLS_OPENCODE:-}" ]]; then
|
||||
mkdir -p .opencode/skills
|
||||
ln -sfn "$out" ".opencode/skills/${skill}"
|
||||
fi
|
||||
|
||||
echo "use_skill: ${skill} -> ${out}"
|
||||
}
|
||||
|
||||
# Install multiple skills
|
||||
use_skills() {
|
||||
# Ensure at least one target exists
|
||||
mkdir -p .claude/skills .opencode/skills
|
||||
|
||||
for skill in "$@"; do
|
||||
use_skill "$skill"
|
||||
done
|
||||
}
|
||||
|
||||
# Load skills from .skills manifest file
|
||||
load_skills_from_manifest() {
|
||||
[[ ! -f .skills ]] && return 0
|
||||
mkdir -p .claude/skills .opencode/skills
|
||||
|
||||
while IFS= read -r skill || [[ -n "$skill" ]]; do
|
||||
# Skip empty lines and comments
|
||||
[[ -z "$skill" || "$skill" =~ ^[[:space:]]*# ]] && continue
|
||||
# Strip inline comments and whitespace
|
||||
skill="${skill%%#*}"
|
||||
skill="${skill// /}"
|
||||
[[ -n "$skill" ]] && use_skill "$skill"
|
||||
done < .skills
|
||||
}
|
||||
144
docs/PER-REPO-SKILLS.md
Normal file
144
docs/PER-REPO-SKILLS.md
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
# Per-Repo Skill Deployment
|
||||
|
||||
Deploy selected skills to individual projects using direnv + Nix.
|
||||
|
||||
## Overview
|
||||
|
||||
Each project can declare which skills it needs. When team members enter the directory (via direnv), skills are symlinked from the Nix store into `.claude/skills/` and `.opencode/skills/`.
|
||||
|
||||
```
|
||||
teammate clones repo
|
||||
↓
|
||||
direnv allow
|
||||
↓
|
||||
nix builds skills (cached)
|
||||
↓
|
||||
symlinks created in .claude/skills/
|
||||
↓
|
||||
Claude Code sees project-local skills
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
### 1. Add to `.envrc`
|
||||
|
||||
**Option A: Source the helper** (if skills repo is accessible)
|
||||
```bash
|
||||
source ~/proj/skills/bin/use-skills.sh
|
||||
use_skills worklog web-search
|
||||
```
|
||||
|
||||
**Option B: Self-contained** (copy-paste, no external dependency)
|
||||
```bash
|
||||
# AI Agent Skills
|
||||
SKILLS_REPO="git+file://$HOME/proj/skills" # or git+http://... for remote
|
||||
|
||||
use_skill() {
|
||||
local skill="$1"
|
||||
local out
|
||||
out=$(nix build --print-out-paths --no-link "${SKILLS_REPO}#${skill}" 2>/dev/null)
|
||||
if [[ -n "$out" ]]; then
|
||||
mkdir -p .claude/skills .opencode/skills
|
||||
ln -sfn "$out" ".claude/skills/${skill}"
|
||||
ln -sfn "$out" ".opencode/skills/${skill}"
|
||||
echo "use_skill: ${skill}"
|
||||
fi
|
||||
}
|
||||
|
||||
use_skill worklog
|
||||
use_skill web-search
|
||||
```
|
||||
|
||||
### 2. Add to `.gitignore`
|
||||
|
||||
```
|
||||
.claude/skills/
|
||||
.opencode/skills/
|
||||
```
|
||||
|
||||
### 3. Done
|
||||
|
||||
Team members clone and run `direnv allow`. Skills appear.
|
||||
|
||||
## Available Skills
|
||||
|
||||
Run `nix flake show git+ssh://git@forgejo.delmore.io/dan/skills.git` to see all available skills.
|
||||
|
||||
Current list:
|
||||
- `worklog` - Create org-mode worklogs
|
||||
- `web-search` - Search the web via Claude
|
||||
- `web-research` - Deep research with multiple backends
|
||||
- `update-opencode` - Update OpenCode via Nix
|
||||
- `update-spec-kit` - Update spec-kit ecosystem
|
||||
- `niri-window-capture` - Capture window screenshots
|
||||
- `screenshot-latest` - Find latest screenshots
|
||||
- `tufte-press` - Generate study card JSON
|
||||
|
||||
## How It Works
|
||||
|
||||
1. **direnv** triggers on directory entry
|
||||
2. **nix build** fetches/builds the skill package (cached locally)
|
||||
3. **symlink** points `.claude/skills/<name>` to `/nix/store/xxx-ai-skill-<name>`
|
||||
4. **Claude Code** reads skills from `.claude/skills/` when in that directory
|
||||
|
||||
Skills are always fetched from the latest commit on the skills repo. Nix caches builds locally, so subsequent loads are fast.
|
||||
|
||||
## Customization
|
||||
|
||||
### Different skills per project
|
||||
|
||||
```bash
|
||||
# Project A - .envrc
|
||||
use_skill worklog
|
||||
use_skill web-search
|
||||
|
||||
# Project B - .envrc
|
||||
use_skill worklog
|
||||
use_skill tufte-press
|
||||
```
|
||||
|
||||
### Claude Code only (no OpenCode)
|
||||
|
||||
```bash
|
||||
SKILLS_REPO="git+file://$HOME/proj/skills"
|
||||
mkdir -p .claude/skills
|
||||
for skill in worklog web-search; do
|
||||
ln -sfn $(nix build --print-out-paths --no-link "${SKILLS_REPO}#${skill}") ".claude/skills/${skill}"
|
||||
done
|
||||
```
|
||||
|
||||
### Pin to specific version
|
||||
|
||||
```bash
|
||||
SKILLS_REPO="git+file://$HOME/proj/skills?rev=abc123def" # pin to commit
|
||||
use_skill worklog # uses pinned revision
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Skills not appearing
|
||||
|
||||
1. Check `direnv allow` was run
|
||||
2. Check `.claude/skills/` exists and has symlinks
|
||||
3. Restart Claude Code (it loads skills at startup)
|
||||
|
||||
### nix build fails
|
||||
|
||||
1. Check network access to skills repo
|
||||
2. Check `nix flake show $SKILLS_REPO` works
|
||||
3. Check skill name is correct
|
||||
|
||||
### Symlinks broken after nix-collect-garbage
|
||||
|
||||
Re-run `direnv reload` to rebuild and re-link.
|
||||
|
||||
## Comparison with Global Skills
|
||||
|
||||
| Aspect | Global (~/.claude/skills/) | Per-Repo (.claude/skills/) |
|
||||
|--------|---------------------------|---------------------------|
|
||||
| Scope | All projects | Single project |
|
||||
| Deployment | Nix Home Manager | direnv + nix build |
|
||||
| Team sharing | Via dotfiles | Via project .envrc |
|
||||
| Selection | User-wide | Per-project |
|
||||
|
||||
Use global for personal defaults. Use per-repo for project-specific or team-shared skills.
|
||||
234
docs/RFC-SKILLS-MANIFEST.md
Normal file
234
docs/RFC-SKILLS-MANIFEST.md
Normal file
|
|
@ -0,0 +1,234 @@
|
|||
# RFC: .skills Manifest Pattern
|
||||
|
||||
**Status**: Draft
|
||||
**Created**: 2024-11-30
|
||||
**Author**: dan + claude
|
||||
|
||||
## Summary
|
||||
|
||||
A lightweight pattern for per-repo skill deployment where projects declare desired skills in a `.skills` manifest file. This enables:
|
||||
- Team members get skills on clone + `direnv allow`
|
||||
- Agents can query installed skills without loading full docs
|
||||
- Agents can install skills by editing the manifest
|
||||
- Central skills repo remains single source of truth
|
||||
|
||||
## Motivation
|
||||
|
||||
### Current State
|
||||
- Skills live in central `skills` repo
|
||||
- Global deployment via Nix Home Manager to `~/.claude/skills/`
|
||||
- Per-repo deployment undocumented and manual
|
||||
|
||||
### Problems
|
||||
1. **No per-repo differentiation** - all projects get same global skills
|
||||
2. **Agent context bloat** - loading full skill docs is expensive
|
||||
3. **No standard pattern** - each project reinvents .envrc setup
|
||||
4. **Agent can't help** - no standard way for agent to install skills
|
||||
|
||||
### Goals
|
||||
1. Projects declare which skills they need
|
||||
2. Team members get skills automatically (via direnv)
|
||||
3. Agents can answer "what skills do we have?"
|
||||
4. Agents can add skills with minimal context
|
||||
|
||||
## Design
|
||||
|
||||
### The `.skills` Manifest
|
||||
|
||||
A simple text file in project root:
|
||||
|
||||
```
|
||||
# .skills - AI agent skills for this project
|
||||
# Run `direnv reload` after editing
|
||||
|
||||
worklog
|
||||
web-search
|
||||
```
|
||||
|
||||
**Format**:
|
||||
- One skill name per line
|
||||
- Lines starting with `#` are comments
|
||||
- Empty lines ignored
|
||||
- Skill names match flake package names
|
||||
|
||||
### The `.envrc` Integration
|
||||
|
||||
Projects add to their `.envrc`:
|
||||
|
||||
```bash
|
||||
# AI Agent Skills
|
||||
if [[ -f .skills ]]; then
|
||||
SKILLS_REPO="${SKILLS_REPO:-git+file://$HOME/proj/skills}"
|
||||
mkdir -p .claude/skills .opencode/skills
|
||||
while IFS= read -r skill || [[ -n "$skill" ]]; do
|
||||
[[ -z "$skill" || "$skill" =~ ^[[:space:]]*# ]] && continue
|
||||
skill="${skill%%#*}" # strip inline comments
|
||||
skill="${skill// /}" # strip whitespace
|
||||
out=$(nix build --print-out-paths --no-link "${SKILLS_REPO}#${skill}" 2>/dev/null)
|
||||
if [[ -n "$out" ]]; then
|
||||
ln -sfn "$out" ".claude/skills/${skill}"
|
||||
ln -sfn "$out" ".opencode/skills/${skill}"
|
||||
fi
|
||||
done < .skills
|
||||
fi
|
||||
```
|
||||
|
||||
Or source the helper:
|
||||
|
||||
```bash
|
||||
if [[ -f .skills ]]; then
|
||||
source ~/proj/skills/bin/use-skills.sh
|
||||
load_skills_from_manifest
|
||||
fi
|
||||
```
|
||||
|
||||
### The `.gitignore` Entries
|
||||
|
||||
```
|
||||
.claude/skills/
|
||||
.opencode/skills/
|
||||
```
|
||||
|
||||
The manifest (`.skills`) IS committed. The symlinks are not.
|
||||
|
||||
### Agent Context Blurb
|
||||
|
||||
Add to project's `CLAUDE.md` or `AGENTS.md`:
|
||||
|
||||
```markdown
|
||||
## Skills
|
||||
|
||||
This project uses AI agent skills. Skills are declared in `.skills` and installed via direnv.
|
||||
|
||||
**Installed**: See `.skills`
|
||||
**Available**: worklog, web-search, web-research, update-opencode, update-spec-kit, niri-window-capture, screenshot-latest, tufte-press
|
||||
**To add**: Edit `.skills`, then run `direnv reload`
|
||||
**Docs**: See ~/proj/skills for full skill documentation
|
||||
```
|
||||
|
||||
This gives the agent enough context (~4 lines) to:
|
||||
- Answer "what skills do we have?" → read `.skills`
|
||||
- Add a skill → edit `.skills`, tell user to `direnv reload`
|
||||
- Know where to find full docs if needed
|
||||
|
||||
## Workflow
|
||||
|
||||
### Initial Setup (one-time per project)
|
||||
|
||||
1. Create `.skills` with desired skills
|
||||
2. Add skills loader to `.envrc`
|
||||
3. Add `.claude/skills/` to `.gitignore`
|
||||
4. Add brief context to project's CLAUDE.md
|
||||
5. Commit
|
||||
|
||||
### Team Member Onboarding
|
||||
|
||||
1. Clone repo
|
||||
2. Run `direnv allow`
|
||||
3. Skills are installed automatically
|
||||
|
||||
### Adding a Skill
|
||||
|
||||
**Human workflow**:
|
||||
1. Edit `.skills`, add skill name
|
||||
2. Run `direnv reload`
|
||||
|
||||
**Agent-assisted workflow**:
|
||||
1. Human: "add the worklog skill"
|
||||
2. Agent: edits `.skills`, adds `worklog`
|
||||
3. Agent: "Added worklog to .skills. Run `direnv reload` to install."
|
||||
|
||||
### Querying Skills
|
||||
|
||||
**"What skills do we have?"**
|
||||
- Agent reads `.skills` file
|
||||
- Or runs `ls .claude/skills/`
|
||||
|
||||
**"What skills are available?"**
|
||||
- Agent references the list in CLAUDE.md context
|
||||
- Or runs `nix flake show $SKILLS_REPO`
|
||||
|
||||
## File Locations
|
||||
|
||||
```
|
||||
project/
|
||||
├── .skills # Manifest (committed)
|
||||
├── .envrc # Loads skills from manifest (committed)
|
||||
├── .gitignore # Ignores .claude/skills/ (committed)
|
||||
├── CLAUDE.md # Agent context blurb (committed)
|
||||
├── .claude/
|
||||
│ └── skills/ # Symlinks to nix store (NOT committed)
|
||||
│ ├── worklog -> /nix/store/xxx
|
||||
│ └── web-search -> /nix/store/yyy
|
||||
└── .opencode/
|
||||
└── skills/ # Same symlinks (NOT committed)
|
||||
```
|
||||
|
||||
## Helper Script
|
||||
|
||||
`skills/bin/use-skills.sh` provides:
|
||||
|
||||
```bash
|
||||
# Load skills from .skills manifest
|
||||
load_skills_from_manifest() {
|
||||
[[ ! -f .skills ]] && return
|
||||
mkdir -p .claude/skills .opencode/skills
|
||||
while IFS= read -r skill || [[ -n "$skill" ]]; do
|
||||
[[ -z "$skill" || "$skill" =~ ^[[:space:]]*# ]] && continue
|
||||
use_skill "${skill%%#*}"
|
||||
done < .skills
|
||||
}
|
||||
```
|
||||
|
||||
## Migration
|
||||
|
||||
### Existing Projects
|
||||
|
||||
1. Survey current `.envrc` files for skill-related setup
|
||||
2. Extract skill list to `.skills`
|
||||
3. Replace custom logic with standard pattern
|
||||
4. Add CLAUDE.md context blurb
|
||||
|
||||
### New Projects
|
||||
|
||||
Use the template:
|
||||
```bash
|
||||
cp ~/proj/skills/templates/skills-envrc-snippet.sh .envrc.skills
|
||||
cat .envrc.skills >> .envrc
|
||||
echo "worklog" > .skills
|
||||
echo ".claude/skills/" >> .gitignore
|
||||
```
|
||||
|
||||
## Alternatives Considered
|
||||
|
||||
### Full flake.nix in each project
|
||||
- Heavier than needed
|
||||
- Not all projects want/have flake.nix
|
||||
- Decided: .envrc + nix build is lighter
|
||||
|
||||
### Central config mapping repos to skills
|
||||
- Single view of all projects
|
||||
- But adds indirection and sync complexity
|
||||
- Decided: per-repo manifest is simpler
|
||||
|
||||
### Agent edits .envrc directly
|
||||
- Fragile - .envrc has other content
|
||||
- Decided: separate .skills file is cleaner for agent editing
|
||||
|
||||
## Open Questions
|
||||
|
||||
1. **Should we validate skill names?** Currently fails silently if skill doesn't exist.
|
||||
2. **Should we support skill versions?** e.g., `worklog@v1.2.0`
|
||||
3. **Should there be a `skills` CLI?** e.g., `skills add worklog`, `skills list`
|
||||
|
||||
## Implementation Checklist
|
||||
|
||||
- [x] Create `bin/use-skills.sh` helper
|
||||
- [x] Create `docs/PER-REPO-SKILLS.md` documentation
|
||||
- [x] Create this RFC
|
||||
- [ ] Update `bin/use-skills.sh` with `load_skills_from_manifest`
|
||||
- [ ] Create template `.skills` file
|
||||
- [ ] Create template CLAUDE.md blurb
|
||||
- [ ] Survey existing projects for migration
|
||||
- [ ] Migrate pilot project
|
||||
- [ ] Update AGENTS.md with pattern reference
|
||||
133
docs/RFC-TEAM-SKILLS-ADOPTION.md
Normal file
133
docs/RFC-TEAM-SKILLS-ADOPTION.md
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
# RFC: Per-Project AI Agent Skills
|
||||
|
||||
**Status**: Proposal
|
||||
**Date**: 2025-11-30
|
||||
**Audience**: All project teams using Claude Code or OpenCode
|
||||
|
||||
## Summary
|
||||
|
||||
A standard pattern for projects to declare which AI agent skills they need. Team members automatically get the right skills when they clone and enter the project directory.
|
||||
|
||||
## Problem
|
||||
|
||||
Currently, AI agent skills are deployed globally to everyone's machine. This means:
|
||||
- All projects get all skills, whether they need them or not
|
||||
- No way to share project-specific skills with teammates
|
||||
- No visibility into what skills a project uses
|
||||
|
||||
## Solution
|
||||
|
||||
Projects declare their skills in a `.skills` file. When you enter the directory (via direnv), the skills are automatically installed.
|
||||
|
||||
```
|
||||
# .skills
|
||||
worklog
|
||||
web-search
|
||||
```
|
||||
|
||||
That's it. Clone the repo, run `direnv allow`, and you have the skills.
|
||||
|
||||
## How It Works
|
||||
|
||||
```
|
||||
You clone a repo with .skills file
|
||||
↓
|
||||
Run `direnv allow`
|
||||
↓
|
||||
direnv reads .skills, builds each skill via Nix
|
||||
↓
|
||||
Skills symlinked to .claude/skills/ and .opencode/skills/
|
||||
↓
|
||||
Claude Code / OpenCode sees the skills
|
||||
```
|
||||
|
||||
Skills come from the central skills repo (`~/proj/skills`), so everyone gets the same version.
|
||||
|
||||
## Available Skills
|
||||
|
||||
| Skill | Description |
|
||||
|-------|-------------|
|
||||
| `worklog` | Create org-mode worklogs documenting sessions |
|
||||
| `web-search` | Search the web via Claude subprocess |
|
||||
| `web-research` | Deep research with multiple backends |
|
||||
| `update-opencode` | Update OpenCode version via Nix |
|
||||
| `niri-window-capture` | Capture window screenshots (security-sensitive) |
|
||||
| `tufte-press` | Generate Tufte-style study cards |
|
||||
|
||||
## Adopting This Pattern
|
||||
|
||||
### 1. Create `.skills` file
|
||||
|
||||
```bash
|
||||
echo "worklog" > .skills
|
||||
```
|
||||
|
||||
### 2. Update `.envrc`
|
||||
|
||||
Add to your project's `.envrc`:
|
||||
|
||||
```bash
|
||||
# AI Agent Skills
|
||||
if [[ -f .skills ]]; then
|
||||
source ~/proj/skills/bin/use-skills.sh
|
||||
load_skills_from_manifest
|
||||
fi
|
||||
```
|
||||
|
||||
### 3. Update `.gitignore`
|
||||
|
||||
```bash
|
||||
echo ".claude/skills/" >> .gitignore
|
||||
echo ".opencode/skills/" >> .gitignore
|
||||
```
|
||||
|
||||
### 4. (Optional) Add agent context
|
||||
|
||||
Add to your `AGENTS.md` or `CLAUDE.md`:
|
||||
|
||||
```markdown
|
||||
## Skills
|
||||
|
||||
This project uses AI agent skills declared in `.skills`.
|
||||
- **Installed**: See `.skills`
|
||||
- **To add**: Edit `.skills`, run `direnv reload`
|
||||
```
|
||||
|
||||
### 5. Commit and share
|
||||
|
||||
```bash
|
||||
git add .skills .envrc .gitignore
|
||||
git commit -m "Add AI agent skills support"
|
||||
```
|
||||
|
||||
Teammates will get the skills when they `direnv allow`.
|
||||
|
||||
## FAQ
|
||||
|
||||
**Q: Do I need Nix?**
|
||||
A: Yes. Skills are built via Nix for reproducibility.
|
||||
|
||||
**Q: What if I'm offline?**
|
||||
A: Works fine. Skills build from the local `~/proj/skills` repo.
|
||||
|
||||
**Q: Can I pin a specific version?**
|
||||
A: Yes. Use `SKILLS_REPO="git+file://$HOME/proj/skills?rev=abc123"` in your `.envrc`.
|
||||
|
||||
**Q: What if a skill doesn't exist?**
|
||||
A: The build will fail silently for that skill. Check the name matches a skill in the repo.
|
||||
|
||||
**Q: How do I see what skills are installed?**
|
||||
A: `ls .claude/skills/` or `cat .skills`
|
||||
|
||||
**Q: How do I add a skill after setup?**
|
||||
A: Edit `.skills`, then run `direnv reload`.
|
||||
|
||||
## Future Improvements
|
||||
|
||||
1. **Global direnv helper** - Add `load_project_skills` to `~/.config/direnv/direnvrc` so `.envrc` only needs one line
|
||||
2. **CLI tool** - `skills init` to set up a project, `skills add <name>` to add skills
|
||||
3. **Validation** - Warn if a skill name doesn't exist
|
||||
|
||||
## Questions / Feedback
|
||||
|
||||
Open an issue in the skills repo or ask in the team channel.
|
||||
211
docs/worklogs/2025-11-30-per-repo-skill-deployment-design.org
Normal file
211
docs/worklogs/2025-11-30-per-repo-skill-deployment-design.org
Normal file
|
|
@ -0,0 +1,211 @@
|
|||
#+TITLE: Per-Repo Skill Deployment: Design and RFC
|
||||
#+DATE: 2025-11-30
|
||||
#+KEYWORDS: skills, per-repo, direnv, nix, deployment, rfc, beads, manifest
|
||||
#+COMMITS: 0 (uncommitted work)
|
||||
#+COMPRESSION_STATUS: uncompressed
|
||||
|
||||
* Session Summary
|
||||
** Date: 2025-11-30 (Sunday)
|
||||
** Focus Area: Designing and documenting per-repo skill deployment pattern
|
||||
|
||||
* Accomplishments
|
||||
- [X] Explored skills repository structure and identified 9 outstanding issues
|
||||
- [X] Populated beads issue tracker with all identified work items
|
||||
- [X] Set up dependency relationships between design decisions and documentation tasks
|
||||
- [X] Fixed ai-skills.nix module - removed broken opencode-skills npm plugin code
|
||||
- [X] Updated flake.nix to include all 8 skills (was missing update-opencode, web-search, web-research)
|
||||
- [X] Designed per-repo skill deployment pattern using direnv + nix build
|
||||
- [X] Created bin/use-skills.sh helper script with use_skill, use_skills, load_skills_from_manifest
|
||||
- [X] Created docs/PER-REPO-SKILLS.md with quick start documentation
|
||||
- [X] Created docs/RFC-SKILLS-MANIFEST.md with full pattern specification
|
||||
- [X] 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
|
||||
#+begin_src bash
|
||||
# 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"
|
||||
#+end_src
|
||||
|
||||
** 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: [[file:2025-11-09-nix-flake-module-development-opencode-skills-integration.org][Nix Flake Module Development]] - original HM module design
|
||||
- Previous: [[file:2025-11-22-create-web-search-skill.org][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+file://$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
|
||||
#+begin_src
|
||||
orch/.claude/skills/worklog -> /nix/store/...-ai-skill-worklog
|
||||
orch/.opencode/skills/worklog -> /nix/store/...-ai-skill-worklog
|
||||
#+end_src
|
||||
|
||||
** 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
|
||||
|
|
@ -18,6 +18,9 @@
|
|||
"tufte-press"
|
||||
"worklog"
|
||||
"update-spec-kit"
|
||||
"update-opencode"
|
||||
"web-search"
|
||||
"web-research"
|
||||
];
|
||||
in
|
||||
flake-utils.lib.eachDefaultSystem
|
||||
|
|
|
|||
|
|
@ -4,22 +4,6 @@ with lib;
|
|||
|
||||
let
|
||||
cfg = config.services.ai-skills;
|
||||
|
||||
# Helper to install opencode-skills npm package
|
||||
opencodeSkillsPlugin = pkgs.buildNpmPackage rec {
|
||||
pname = "opencode-skills";
|
||||
version = "0.1.0";
|
||||
|
||||
src = pkgs.fetchFromNpm {
|
||||
name = pname;
|
||||
version = version;
|
||||
sha256 = ""; # TODO: Get actual hash
|
||||
};
|
||||
|
||||
# Alternative: install from npm directly at runtime
|
||||
# This is a placeholder - actual implementation would fetch from npm
|
||||
};
|
||||
|
||||
in {
|
||||
options.services.ai-skills = {
|
||||
enable = mkEnableOption "AI agent skills for Claude Code and OpenCode";
|
||||
|
|
@ -34,8 +18,11 @@ in {
|
|||
- tufte-press: Generate study card JSON
|
||||
- worklog: Create org-mode worklogs
|
||||
- update-spec-kit: Update spec-kit ecosystem
|
||||
- update-opencode: Update OpenCode via Nix
|
||||
- web-search: Search the web via Claude
|
||||
- web-research: Deep web research with multiple backends
|
||||
'';
|
||||
example = [ "worklog" "screenshot-latest" ];
|
||||
example = [ "worklog" "web-search" ];
|
||||
};
|
||||
|
||||
skillsPath = mkOption {
|
||||
|
|
@ -55,12 +42,6 @@ in {
|
|||
default = true;
|
||||
description = "Deploy skills to OpenCode (~/.config/opencode/skills/)";
|
||||
};
|
||||
|
||||
installOpencodePlugin = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = "Install opencode-skills npm plugin";
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
|
|
@ -91,43 +72,6 @@ in {
|
|||
}) cfg.skills
|
||||
)
|
||||
))
|
||||
|
||||
# OpenCode plugin installation
|
||||
(mkIf (cfg.enableOpenCode && cfg.installOpencodePlugin) {
|
||||
".config/opencode/package.json" = {
|
||||
text = builtins.toJSON {
|
||||
dependencies = {
|
||||
"@opencode-ai/plugin" = "1.0.44";
|
||||
"opencode-skills" = "^0.1.0";
|
||||
};
|
||||
};
|
||||
};
|
||||
})
|
||||
];
|
||||
|
||||
# Ensure opencode-skills plugin is in config
|
||||
home.activation.opencodeSkillsPlugin = mkIf (cfg.enableOpenCode && cfg.installOpencodePlugin) (
|
||||
lib.hm.dag.entryAfter [ "writeBoundary" ] ''
|
||||
# Install npm dependencies for OpenCode
|
||||
if [ -f "$HOME/.config/opencode/package.json" ]; then
|
||||
cd "$HOME/.config/opencode"
|
||||
if command -v bun &> /dev/null; then
|
||||
${pkgs.bun}/bin/bun install
|
||||
elif command -v npm &> /dev/null; then
|
||||
${pkgs.nodejs}/bin/npm install
|
||||
fi
|
||||
fi
|
||||
|
||||
# Ensure plugin is enabled in config
|
||||
CONFIG_FILE="$HOME/.config/opencode/config.json"
|
||||
if [ -f "$CONFIG_FILE" ]; then
|
||||
# Check if plugin array includes opencode-skills
|
||||
if ! ${pkgs.jq}/bin/jq -e '.plugin | index("opencode-skills")' "$CONFIG_FILE" &> /dev/null; then
|
||||
echo "Warning: opencode-skills plugin not in config.json plugin array"
|
||||
echo "Add it manually: { \"plugin\": [\"opencode-skills\"] }"
|
||||
fi
|
||||
fi
|
||||
''
|
||||
);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue