- Transform tufte-press from reference guide to conversation-aware generator - Add JSON generation from conversation context following strict schema - Create build automation scripts with Nix environment handling - Integrate CUPS printing with duplex support - Add comprehensive workflow documentation Scripts added: - skills/tufte-press/scripts/generate-and-build.sh (242 lines) - skills/tufte-press/scripts/build-card.sh (23 lines) Documentation: - Updated SKILL.md with complete workflow instructions (370 lines) - Updated README.md with usage examples (340 lines) - Created SKILL-DEVELOPMENT-STRATEGY-tufte-press.md (450 lines) - Added worklog: 2025-11-10-tufte-press-skill-evolution.org Features: - Agent generates valid JSON from conversation - Schema validation before build (catches errors early) - Automatic Nix shell entry for dependencies - PDF build via tufte-press toolchain - Optional print with duplex support - Self-contained margin notes enforced - Complete end-to-end testing Workflow: Conversation → JSON → Validate → Build → Print Related: niri-window-capture, screenshot-latest, worklog skills
11 KiB
Best Practices: Skills as Single Source of Truth
Philosophy: This repository is the canonical source for all AI agent skills. All deployments consume via Nix flake inputs.
Core Principles
1. Skills Repo is Authoritative
What this means:
- All skill development happens in
dan/skillsrepository - No permanent local copies in consumer projects
- Changes must be committed here to be deployed
Why:
- Clear ownership and maintenance responsibility
- Version control provides audit trail
- Reproducible deployments across all environments
- Prevents drift and divergence
2. Modern Nix with Flakes
What this means:
- Use
flake.nixfor all configuration - Consumers add skills repo as flake input
- Leverage flake.lock for reproducibility
- Use NixOS modules for deployment
Why:
- Declarative configuration
- Version pinning with flake.lock
- Reproducible builds
- Composable modules
3. Temporary Local Copies Only During Development
What this means:
- OK to copy skill to consumer for testing during initial development
- Must switch to flake input once skill is stable
- Duration: Hours to 1-2 days max, not weeks
- Always transition to production pattern (flake input)
Why:
- Fast iteration during development
- Clean production deployments
- Prevents forgetting to sync changes
- Clear distinction between dev and prod patterns
Workflow Patterns
Adding a New Skill
Development phase (1-2 days):
# 1. Create skill in skills repo
cd ~/proj/skills
mkdir -p skills/new-skill/{scripts,examples,templates}
# ... create SKILL.md, README.md, etc ...
# 2. Commit early (even if WIP)
git add skills/new-skill/
git commit -m "WIP: Add new-skill (initial structure)"
# 3. Optional: Copy to consumer for rapid testing
cp -r skills/new-skill ~/proj/ops-dev/skills/
# Test, iterate, modify in place
# 4. When working, sync back to skills repo
cp -r ~/proj/ops-dev/skills/new-skill skills/
git add skills/new-skill/
git commit -m "Complete new-skill implementation"
Production phase (permanent):
# 5. Add to flake packages (if not already)
# Edit flake.nix, add to packages output
# 6. Consumer switches to flake input
cd ~/proj/ops-dev
# Edit flake.nix:
# services.ai-skills.selectedSkills = [ "new-skill" ];
nix flake lock --update-input skills
# Deploy to VM
# 7. Remove local copy
rm -rf ~/proj/ops-dev/skills/new-skill
git add -A
git commit -m "Switch new-skill to flake input"
Key point: Don't skip step 6-7. Leaving skills as local copies defeats the purpose.
Updating an Existing Skill
Always in skills repo:
# 1. Make changes in skills repo
cd ~/proj/skills
# Edit skills/tufte-press/SKILL.md or scripts
# 2. Commit with clear message
git commit -am "tufte-press: Add example for concurrent equations"
# 3. Push to remote (Forgejo)
git push origin master
# 4. Consumers update when ready
cd ~/proj/ops-dev
nix flake lock --update-input skills
# Deploy to VM
Never:
- ❌ Make changes in consumer's local copy
- ❌ Keep local modifications "just for us"
- ❌ Fork the skill for project-specific tweaks
Instead:
- ✅ Make changes in skills repo
- ✅ If change is project-specific, use configuration options
- ✅ If broadly useful, add to skills repo for everyone
Testing Changes Before Committing
Option A: Test in skills repo directly:
cd ~/proj/skills
# Make changes
vim skills/worklog/scripts/suggest-filename.sh
# Test locally (if skill has test scripts)
./skills/worklog/scripts/suggest-filename.sh test-input.org
# Commit when working
git commit -am "worklog: Fix filename suggestion for long titles"
Option B: Test in consumer with local override:
# Temporarily point to local path for testing
cd ~/proj/ops-dev
# Edit flake.nix:
# inputs.skills.url = "path:/home/dan/proj/skills";
nix flake lock --update-input skills
# Deploy to VM and test
# When working, revert to git URL
# Edit flake.nix:
# inputs.skills.url = "git+http://192.168.1.108:3000/dan/skills.git";
nix flake lock --update-input skills
Option C: Use a branch:
cd ~/proj/skills
# Create feature branch
git checkout -b feature/worklog-improvements
# Make changes, commit
git commit -am "worklog: Add weekly summary aggregation"
git push origin feature/worklog-improvements
# Consumer tests the branch
cd ~/proj/ops-dev
# Edit flake.nix:
# inputs.skills.url = "git+http://192.168.1.108:3000/dan/skills.git?ref=feature/worklog-improvements";
nix flake lock --update-input skills
# Test on VM
# When stable, merge to master
cd ~/proj/skills
git checkout master
git merge feature/worklog-improvements
git push origin master
# Consumer switches back to master
cd ~/proj/ops-dev
# Edit flake.nix: back to master URL
nix flake lock --update-input skills
Anti-Patterns (Don't Do This)
❌ Permanent Local Copies
Wrong:
# Consumer repo permanently has skills/ directory
ops-dev/
skills/
tufte-press/ # Local copy
worklog/ # Local copy
Why wrong:
- Must manually sync changes
- Risk of divergence
- Unclear which version is deployed
- Defeats purpose of single source of truth
Right:
# Consumer repo uses flake input
inputs.skills.url = "git+http://192.168.1.108:3000/dan/skills.git";
services.ai-skills.selectedSkills = [ "tufte-press" "worklog" ];
❌ Project-Specific Forks
Wrong:
# Creating modified copy for one project
ops-dev/skills/tufte-press-custom/
# Modified version just for ops-dev
Why wrong:
- Others can't benefit from improvements
- Maintenance burden (must merge upstream changes)
- Creates fragmentation
Right:
- Add configuration options to skill
- Make skill flexible via environment variables or config files
- Keep one version that works for everyone
❌ Manual File Copying
Wrong:
# Regularly doing this
scp skills/new-feature.sh root@vm:/etc/opencode/skills/worklog/scripts/
Why wrong:
- Bypasses version control
- Not reproducible
- No audit trail
- Breaks on next rebuild
Right:
# Commit to skills repo, update flake
cd ~/proj/skills
git commit -am "worklog: Add new feature"
git push
cd ~/proj/ops-dev
nix flake lock --update-input skills
# Deploy via nixos-rebuild
❌ Long-Lived Development Branches
Wrong:
# Branch exists for weeks/months
git checkout -b dan/experimental-features
# ... months pass ...
# Never merged, consumers stuck on old version
Why wrong:
- Others can't use improvements
- Becomes hard to merge later
- Defeats shared repository purpose
Right:
- Short-lived feature branches (days, not weeks)
- Merge to master frequently
- Use feature flags if needed for WIP features
- Or commit to master with "WIP" markers in docs
Version Control Practices
Commit Messages
Good examples:
tufte-press: Add support for margin figure citations
worklog: Fix filename generation for dates with slashes
screenshot-latest: Improve error handling when no screenshots found
niri-window-capture: Add security audit logging
Pattern: <skill-name>: <brief description>
Why: Clear which skill changed, easy to scan git log
When to Commit
Commit frequently:
- ✅ After adding new script
- ✅ After fixing bug
- ✅ After updating documentation
- ✅ After testing confirms it works
Don't wait for:
- ❌ "Perfect" state (commit early, improve later)
- ❌ All skills to be updated (commit per-skill changes separately)
- ❌ "Big batch" of changes (many small commits better)
Branching Strategy
Simple approach:
masterbranch is always deployable- Feature branches for significant changes
- Merge to master when working
- Delete branch after merge
Example:
# New skill
git checkout -b add-sql-formatter-skill
# ... work ...
git push origin add-sql-formatter-skill
# ... test ...
git checkout master
git merge add-sql-formatter-skill
git push origin master
git branch -d add-sql-formatter-skill
Deployment Best Practices
Pinning Versions
Default: Track latest:
inputs.skills.url = "git+http://192.168.1.108:3000/dan/skills.git";
# flake.lock tracks specific commit
# Update with: nix flake lock --update-input skills
When to pin: Production systems that need stability
inputs.skills.url = "git+http://192.168.1.108:3000/dan/skills.git?rev=abc123def";
# Locked to specific commit, won't change until manually updated
Update Cadence
Development VMs: Update frequently
# Daily or when skills change
nix flake lock --update-input skills
Production VMs: Update intentionally
# Weekly or after testing in dev
nix flake lock --update-input skills
# Test thoroughly before deploying
Rollback Strategy
If skill breaks:
# Option 1: Remove from selectedSkills temporarily
services.ai-skills.selectedSkills = [
"tufte-press"
# "worklog" # Disabled due to bug
];
# Option 2: Pin to older commit
cd ~/proj/ops-dev
nix flake lock --override-input skills git+http://.../skills.git?rev=<old-commit>
# Option 3: Fix in skills repo and update
cd ~/proj/skills
# Fix bug
git commit -am "worklog: Fix critical bug"
git push
cd ~/proj/ops-dev
nix flake lock --update-input skills
Multi-User Coordination
Communication
Before making breaking changes:
- Announce in team chat/IRC
- Create issue in Forgejo
- Use feature branch for testing
- Get feedback before merging
After making significant changes:
- Update CHANGELOG in skill directory
- Notify consumers
- Document migration steps if needed
Handling Conflicts
If two people modify same skill:
- Coordinate via git (branches, PRs)
- Use git merge/rebase to combine changes
- Test combined changes before deploying
- Modern git handles this well
Shared vs Personal Skills
Shared skills (in skills repo):
- General-purpose capabilities
- Useful to multiple people/projects
- Maintained collaboratively
- Examples: tufte-press, worklog, screenshot-latest
Personal skills (project-local):
- Truly project-specific
- Not useful to others
- Rapid iteration needed
- Can live in
.opencode/command/or.claude/skills/locally
Default: If in doubt, put in skills repo. Easy to share is better than hidden.
Summary Checklist
When developing new skill:
- Create in skills repo (even if WIP)
- Commit frequently
- OK to copy to consumer for testing (temporarily)
- Switch to flake input when stable (within 1-2 days)
- Remove local copy after switching
When updating existing skill:
- Make changes in skills repo only
- Commit with clear message
- Push to remote
- Consumers update via flake lock
- Never modify in consumer's local copy
When deploying:
- Use flake input (Model 1)
- Import ai-skills module
- Select skills via configuration
- Update with
nix flake lock --update-input skills - No manual file copying
Red flags:
- Permanent
skills/directory in consumer repo - Manual
scpof skill files - "Custom" versions of skills
- Long-lived local modifications
- Forgetting to sync changes back
Good signs:
- All skills in skills repo
- Consumers use flake inputs
- Clear git history
- Frequent small commits
- Fast update cycle (commit → push → update → deploy)
The goal: Make skills as easy to use and update as possible, while maintaining single source of truth and version control.