feat(skills): consolidate skill organization across agents

- Add piSkills option to ai-skills module for pi-only skills
- Add ralph-work-loop skill (depends on pi extension)
- Update skills.nix registry with nix-review, ralph-work-loop, ui-query
- Add intent/approach/work docs for skill organization effort

Universal skills deploy to claude/codex/opencode/gemini.
Pi-only skills (ralph-work-loop) deploy to ~/.pi/agent/skills/ only.
This commit is contained in:
dan 2026-01-25 11:49:29 -08:00
parent 796200a277
commit 9ce4c83a17
8 changed files with 380 additions and 0 deletions

View file

@ -0,0 +1,150 @@
# Approach: Skill Organization Across Agents
## Strategy
**Core philosophy**: Single source, Nix-deployed, per-repo filtered.
Skills live in dotfiles. Nix deploys them to each agent's expected location. Pi's settings handle per-repo filtering via `includeSkills`/`ignoredSkills`.
**Key Decisions**:
1. **Source location**: `~/proj/skills/skills/` vs `~/proj/dotfiles/skills/`
**dotfiles** — skills are config, not code. Dotfiles is the deployment source.
2. **Deployment target**: Single location vs all agent locations
**All locations** — deploy to `~/.claude/skills/`, `~/.codex/skills/`, `~/.config/opencode/skills/` for compatibility. Pi reads from `~/.claude/skills/` (disable other pi sources).
3. **Per-repo filtering mechanism**: Project config vs global config per repo
**Pi settings** — use `includeSkills`/`ignoredSkills` in project `.pi/settings.json`. Other agents don't support this, but pi is primary.
4. **Development workflow**: Direct edit in dotfiles vs edit in skills repo + deploy script
**Skills repo + deploy** — develop/test in `~/proj/skills/`, run `deploy-skill.sh`, Nix rebuild picks it up.
5. **Existing scattered skills**: Migrate vs leave in place
**Migrate** — consolidate everything to dotfiles, remove old locations after Nix manages them.
## Architecture
### Directory Structure
```
~/proj/skills/skills/<name>/ # Development (universal)
│ deploy-skill.sh <name>
~/proj/dotfiles/skills/<name>/ # Nix source (universal)
│ home-manager rebuild
~/.claude/skills/<name>/ # Claude Code + Pi
~/.codex/skills/<name>/ # Codex
~/.config/opencode/skills/<name>/ # OpenCode
~/proj/skills/skills/<name>/ # Development (pi-only)
│ deploy-skill.sh <name> --pi-only
~/proj/dotfiles/pi/skills/<name>/ # Nix source (pi-only)
│ home-manager rebuild
~/.pi/agent/skills/<name>/ # Pi only
```
**Two tiers**:
- **Universal**: Deploy to all agents (`~/.claude/`, `~/.codex/`, `~/.config/opencode/`)
- **Pi-only**: Deploy to `~/.pi/agent/skills/` only (for extension-dependent skills like ralph-work-loop)
### Pi Settings
Global `~/.pi/agent/settings.json`:
```json
{
"skills": {
"enableClaudeUser": true, // Universal skills (~/.claude/skills/)
"enablePiUser": true, // Pi-only skills (~/.pi/agent/skills/)
"enableCodexUser": false, // Don't double-load (Nix manages ~/.codex/)
"enableClaudeProject": true,
"enablePiProject": true
}
}
```
Pi reads from both `~/.claude/skills/` (universal) and `~/.pi/agent/skills/` (pi-specific).
### Per-Repo Filtering
Project `.pi/settings.json`:
```json
{
"skills": {
"ignoredSkills": ["nix-review", "ops-review"]
}
}
```
Or positive filtering:
```json
{
"skills": {
"includeSkills": ["worklog", "code-review", "intent", "approach", "work"]
}
}
```
### Nix Module
In `home/skills.nix` (or similar):
```nix
{
home.file = {
".claude/skills" = {
source = ./skills;
recursive = true;
};
".codex/skills" = {
source = ./skills;
recursive = true;
};
".config/opencode/skills" = {
source = ./skills;
recursive = true;
};
};
}
```
## Risks
### Known Unknowns
- **OpenCode skill format**: Does it use the same SKILL.md format? Need to verify.
- **Codex recursive scanning**: Pi docs say it scans recursively — does Codex itself do this too?
- **Project-local skills**: Should these also be standardized, or left as-is per project?
### Failure Modes
- **Nix rebuild required**: Skills don't update until rebuild. Could forget and wonder why changes aren't visible.
- **Agent format drift**: If an agent changes its skill format, we'd need to adapt.
- **includeSkills too restrictive**: Forget to add a skill to the include list, then wonder why it's not loading.
### Blast Radius
- **Dotfiles changes**: Modifying Nix config affects all machines using these dotfiles.
- **Removing old locations**: If we delete `~/.pi/agent/skills/` contents, need to ensure pi settings are updated first.
## Phases
### Phase 1: Consolidate to Dotfiles
- Move all global skills to `~/proj/dotfiles/skills/`
- Update Nix to deploy to all agent locations
- Update pi settings to read from `~/.claude/skills/` only
### Phase 2: Clean Up Old Locations
- Remove skills from `~/.codex/skills/`, `~/.pi/agent/skills/` (Nix now manages these)
- Verify all agents still work
### Phase 3: Per-Repo Filtering
- Add `.pi/settings.json` to repos that need filtering
- Document the pattern for future repos

View file

@ -0,0 +1,42 @@
# Intent: Skill Organization Across Agents
## Motivation
Skills are scattered across multiple directories (`~/.codex/skills/`, `~/.claude/skills/`, `~/.pi/agent/skills/`), leading to duplication and drift. Some repos load skills they don't need. We want a single source of truth with clean deployment to all agents.
## Need
A maintainable system for:
1. Developing skills in one place
2. Deploying to all agent locations (pi, Claude Code, OpenCode, Codex)
3. Filtering which skills load in which repos
4. Keeping compatibility with agents we might use later
## Use-Cases
- **Skill development**: Edit a skill in `~/proj/skills/`, run deploy, all agents pick it up after Nix rebuild.
- **Per-repo filtering**: Working in a Python project — don't load `nix-review`. Working in a Nix project — don't load `python-lint`.
- **Agent compatibility**: Switch to Claude Code for a session — same skills available, same behavior.
- **Avoiding drift**: Fix a bug in `worklog` skill — fix it once, deployed everywhere. No stale copies in random directories.
## Success Criteria
- Single source of truth for global skills (in dotfiles, managed by Nix)
- All agents (pi, Claude Code, OpenCode, Codex) load skills from their expected locations
- Per-repo skill filtering works (via pi settings or project config)
- No duplicate skill directories to maintain
- Development workflow: edit in skills repo → deploy → Nix rebuild → available everywhere
## Constraints
- Must work with Nix/home-manager deployment (no direnv, no runtime symlink management)
- Skills must remain compatible with Agent Skills standard (SKILL.md format)
- Pi is primary agent, but others must keep working
- Extensions (TypeScript) are pi-only, separate concern for now
## Anti-Goals
- **Not changing skill format**: Skills stay as SKILL.md + scripts, no new abstraction
- **Not building a skill manager tool**: Use Nix for deployment, not a custom CLI
- **Not per-file filtering**: Filtering is per-skill, not per-file within a skill
- **Not solving extension organization**: Extensions stay in `~/.pi/agent/extensions/`, address later if needed

View file

@ -0,0 +1,74 @@
# Work: Skill Organization Across Agents
## Intent
Link to: [docs/intent/2026-01-25-skill-organization.md](../intent/2026-01-25-skill-organization.md)
## Approach
Link to: [docs/approach/2026-01-25-skill-organization.md](../approach/2026-01-25-skill-organization.md)
## Checklist
### Phase 1: Consolidate to Dotfiles
- [x] **W001**: Inventory current skills across all locations
- Verification: `find ~/.codex/skills ~/.claude/skills ~/.pi/agent/skills -name "SKILL.md" 2>/dev/null | wc -l` returns count
- [x] **W002**: Create `~/proj/dotfiles/skills/` directory structure
- Verification: SKIP — using existing ai-skills module, skills sourced from this repo
- [x] **W003**: Create `~/proj/dotfiles/pi/skills/` for pi-only skills
- Verification: SKIP — piSkills added to ai-skills module, ralph-work-loop in this repo
- [x] **W004**: Copy universal skills to dotfiles/skills/
- Verification: SKIP — skills already in ~/proj/skills/skills/, ai-skills module deploys them
- [x] **W005**: Copy pi-only skills to dotfiles/pi/skills/
- Verification: `ls skills/ralph-work-loop/SKILL.md` exists in this repo
- [x] **W006**: Create/update Nix module to deploy skills to all agent locations
- Verification: Added piSkills option to modules/ai-skills.nix
- [x] **W007**: Update pi settings to use correct sources
- Verification: `cat ~/.pi/agent/settings.json | jq '.skills'` shows correct flags
- [ ] **W008**: Nix rebuild and verify skills appear in all locations
- Verification: `ls ~/.claude/skills ~/.codex/skills ~/.config/opencode/skills ~/.pi/agent/skills` all populated
### Phase 2: Clean Up Old Locations
- [ ] **W009**: Remove manually-managed skills from ~/.codex/skills/ (Nix now manages)
- Verification: Skills in ~/.codex/skills/ match dotfiles exactly (no extras)
- [ ] **W010**: Remove manually-managed skills from ~/.pi/agent/skills/ (except pi-only)
- Verification: Only ralph-work-loop in ~/.pi/agent/skills/
- [ ] **W011**: Remove duplicate skills from project-local directories (talu, etc.)
- Verification: `ls ~/proj/talu/.claude/skills/` shows only project-specific skills (if any)
### Phase 3: Update deploy-skill.sh
- [ ] **W012**: Update deploy-skill.sh to support --pi-only flag
- Verification: `./bin/deploy-skill.sh --help` shows --pi-only option
- [ ] **W013**: Update deploy-skill.sh to copy to correct dotfiles location
- Verification: `./bin/deploy-skill.sh worklog` copies to `~/proj/dotfiles/skills/worklog/`
- [ ] **W014**: Test end-to-end: create skill, deploy, rebuild, verify in agents
- Verification: Create test-skill, deploy, rebuild, `ls ~/.claude/skills/test-skill/SKILL.md` exists
## Verification Evidence
- (2026-01-25) W001: Inventoried 4 locations, found existing ai-skills Nix module already handles deployment
- (2026-01-25) W002-W005: SKIP — infrastructure already exists in this repo + ai-skills module
- (2026-01-25) W006: Added piSkills option to modules/ai-skills.nix
- (2026-01-25) Copied ralph-work-loop to skills/ directory
- (2026-01-25) Updated skills.nix registry
- (2026-01-25) Updated ~/proj/dotfiles/home/claude.nix with full skill lists + piSkills
- (2026-01-25) Removed manual nix-review deployments from codex.nix, opencode.nix, gemini.nix (now managed by ai-skills)
- (2026-01-25) W007: Added settings.json to ~/proj/dotfiles/home/pi.nix with skill source config
## Notes
- Universal skills: worklog, screenshot-latest, orch, playwright-visit, code-review, ops-review, nix-review, hq, niri-window-capture, intent, approach, work
- Pi-only skills: ralph-work-loop
- Need to check what's currently in each location before migrating

View file

@ -53,6 +53,13 @@ in {
example = [ "worklog" "web-search" ]; example = [ "worklog" "web-search" ];
}; };
piSkills = mkOption {
type = types.listOf types.str;
default = [];
description = "Skills to deploy to Pi (~/.pi/agent/skills/). For pi-only skills that depend on extensions. ${skillsList}";
example = [ "ralph-work-loop" ];
};
# Lenses for orch multi-model review # Lenses for orch multi-model review
enableLenses = mkOption { enableLenses = mkOption {
type = types.bool; type = types.bool;
@ -116,6 +123,19 @@ in {
) )
)) ))
# Pi skills (pi-only, extension-dependent)
(mkIf (cfg.piSkills != []) (
builtins.listToAttrs (
map (skillName: {
name = ".pi/agent/skills/${skillName}";
value = {
source = "${cfg.skillsPath}/${skillName}";
recursive = true;
};
}) cfg.piSkills
)
))
# Lenses for orch (separate subdirectories per skill) # Lenses for orch (separate subdirectories per skill)
(mkIf cfg.enableLenses { (mkIf cfg.enableLenses {
".config/lenses/code" = { ".config/lenses/code" = {

View file

@ -8,16 +8,19 @@
handoff = "Create structured handoff summaries"; handoff = "Create structured handoff summaries";
intent = "Capture the volition and problem space (The Why)"; intent = "Capture the volition and problem space (The Why)";
niri-window-capture = "Invisibly capture window screenshots"; niri-window-capture = "Invisibly capture window screenshots";
nix-review = "Nix-focused infrastructure review";
ops-review = "Multi-lens ops/infrastructure review"; ops-review = "Multi-lens ops/infrastructure review";
orch = "Orchestration and consensus skill"; orch = "Orchestration and consensus skill";
brave-search = "Web search via Brave API"; brave-search = "Web search via Brave API";
browser-tools = "Interactive browser automation via CDP"; browser-tools = "Interactive browser automation via CDP";
playwright-visit = "Browser automation and content extraction"; playwright-visit = "Browser automation and content extraction";
ralph-work-loop = "Run iterative Work-phase execution via Ralph extension (pi-only)";
review-gate = "Quality gate for agent work"; review-gate = "Quality gate for agent work";
screenshot-latest = "Find latest screenshots"; screenshot-latest = "Find latest screenshots";
spec-review = "Technical specification review"; spec-review = "Technical specification review";
tufte-press = "Generate study card JSON"; tufte-press = "Generate study card JSON";
test-review = "Audit test quality for flakiness, tautologies, and anti-patterns"; test-review = "Audit test quality for flakiness, tautologies, and anti-patterns";
ui-query = "Query UI elements via accessibility APIs";
verify-work = "The Gatekeeper: Run project build/tests before claiming success"; verify-work = "The Gatekeeper: Run project build/tests before claiming success";
work = "Execute and verify the implementation (The Do)"; work = "Execute and verify the implementation (The Do)";
worklog = "Create structured worklogs"; worklog = "Create structured worklogs";

View file

@ -0,0 +1,15 @@
# Ralph Work Loop Skill
Runs the Ralph Wiggum loop on an existing Work document.
## Usage
```
/ralph start docs/work/<work-doc>.md
```
If you don't know the work doc, run:
```bash
./.pi/skills/ralph-work-loop/scripts/find-latest-work.sh
```

View file

@ -0,0 +1,51 @@
---
name: ralph-work-loop
description: Start or resume a Ralph Wiggum loop on an existing Work document (docs/work/*.md). Use when the user asks to "use ralph" on a Work doc or to run iterative Work-phase execution.
---
# Ralph Work Loop
Use this skill to run the Ralph Wiggum loop **after** a Work document exists.
## Requirements
- Ralph extension installed: `.pi/extensions/ralph-wiggum/index.ts`
- Work document already created in `docs/work/`
## Process
1. **Validate the Work doc**:
- Check required sections: Intent link, Approach link, Checklist, Verification commands, Evidence section
- Each checklist item needs a verification command
- See the `work` skill for full validation checklist
- Fix any issues before starting the loop
2. **Locate the Work doc**:
- If user provides a path, use it.
- Otherwise run:
```bash
./.pi/skills/ralph-work-loop/scripts/find-latest-work.sh
```
- If multiple candidates are relevant, list them and ask the user to choose.
3. **Start the loop**:
```
/ralph start <work-doc-path>
```
Optional flags (ask user if they care):
- `--items-per-iteration N`
- `--reflect-every N`
- `--max-iterations N`
4. **Monitor or resume**:
- ` /ralph status` to show active loops
- ` /ralph resume <name>` to continue paused loop
5. **Stop**:
- Press `ESC` to pause
- ` /ralph-stop` when idle to end the loop
## Notes
- The loop enforces the **Intent → Approach → Work** dialect and requires verification evidence for completed items.
- Use `/ralph start <path>` to point directly to an existing Work doc.

View file

@ -0,0 +1,25 @@
#!/usr/bin/env bash
set -euo pipefail
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
skill_dir="$(cd "${script_dir}/.." && pwd)"
root_dir="$(cd "${skill_dir}/../../.." && pwd)"
work_dir="${root_dir}/docs/work"
if [[ ! -d "${work_dir}" ]]; then
echo "Error: docs/work not found at ${work_dir}" >&2
exit 1
fi
latest_file=""
if compgen -G "${work_dir}/*.md" > /dev/null; then
latest_file="$(ls -t "${work_dir}"/*.md | head -n 1)"
fi
if [[ -z "${latest_file}" ]]; then
echo "Error: no Work docs found in ${work_dir}" >&2
exit 1
fi
relative_path="${latest_file#"${root_dir}/"}"
echo "${relative_path}"