- 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
9.5 KiB
Migration Guide: ops-dev to Skills Flake Input
Objective: Migrate ops-dev VM from local skill copies to consuming skills via Nix flake input
Current State: tufte-press and worklog copied locally to ~/proj/ops-dev/skills/
Target State: All skills consumed from dan/skills flake input
Philosophy: Skills repo is single source of truth
Prerequisites
- Skills repo deployed to Forgejo:
http://192.168.1.108:3000/dan/skills - Skills repo has working flake with ai-skills module
- ops-dev VM running NixOS with flake-based configuration
- SSH access to ops-dev VM (192.168.1.73)
Migration Steps
Step 1: Add Skills Flake Input
File: ~/proj/ops-dev/flake.nix
Choose input URL based on your use case:
Option A: Local path (recommended for development):
{
description = "ops-dev NixOS configuration";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
# Local path - works offline, great for development
skills = {
url = "path:/home/dan/proj/skills";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs = { self, nixpkgs, skills }: {
# ... rest of config
};
}
Option B: Git URL (for remote deployment):
{
description = "ops-dev NixOS configuration";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
# Git URL - explicit versioning, works remotely
skills = {
url = "git+http://192.168.1.108:3000/dan/skills.git";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs = { self, nixpkgs, skills }: {
# ... rest of config
};
}
When to use which:
path:- Local development machine, offline work, fast iterationgit+http:- Remote VMs, explicit version control, shared deployments
Why inputs.nixpkgs.follows?
Ensures skills flake uses the same nixpkgs as ops-dev, avoiding duplicate dependencies and reducing closure size.
Network dependency note: Both approaches fetch to /nix/store once, then work offline. Use nix flake prefetch to pre-cache.
Step 2: Import ai-skills Module
File: ~/proj/ops-dev/flake.nix
In nixosConfigurations.dev.modules:
nixosConfigurations.dev = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
./configuration.nix
# Import skills module
skills.nixosModules.ai-skills
# Configure skills
{
services.ai-skills = {
enable = true;
selectedSkills = [
"tufte-press"
"worklog"
# Add more skills as needed:
# "screenshot-latest"
# "niri-window-capture"
];
deployTargets = [ "claude" "opencode" ];
};
}
];
};
Step 3: Remove Local Skills Configuration
File: ~/proj/ops-dev/flake.nix
Remove these sections (if present):
# DELETE: Old local skills deployment
environment.etc."opencode/skills" = {
source = ./skills;
};
environment.etc."claude/skills" = {
source = ./skills;
};
# DELETE: Old symlink activation script
system.activationScripts.skills-symlinks = {
text = ''
# ... old symlink code ...
'';
deps = [ "etc" ];
};
Why remove?
The ai-skills module handles all skill deployment. Keeping old config creates conflicts.
Step 4: Remove Local Skills Directory
Commands (on development machine):
cd ~/proj/ops-dev
# Backup first (just in case)
tar czf skills-backup-$(date +%Y%m%d).tar.gz skills/
# Remove local skills
rm -rf skills/
# Commit the change
git add -A
git commit -m "Switch to skills repo flake input
- Add skills flake input from Forgejo
- Import ai-skills NixOS module
- Remove local skill copies (now from flake)
- Enable tufte-press and worklog skills
"
Step 5: Update Flake Lock
Command:
cd ~/proj/ops-dev
nix flake lock
This creates/updates flake.lock with:
- Skills repo commit hash
- Input dependencies
- Reproducible versions
Check the lock file:
cat flake.lock | jq '.nodes.skills'
Should show:
{
"locked": {
"lastModified": 1234567890,
"narHash": "sha256-...",
"ref": "refs/heads/master",
"rev": "abc123...",
"type": "git",
"url": "http://192.168.1.108:3000/dan/skills.git"
}
}
Step 6: Deploy to VM
Option A: Via SCP (current method):
# Copy updated config to VM
scp -i ~/.ssh/id_ed25519_2025 flake.nix flake.lock root@192.168.1.73:/home/dev/ops-dev/
# Rebuild on VM
ssh root@192.168.1.73 "cd /home/dev/ops-dev && nixos-rebuild switch --flake .#dev"
Option B: Via Git (cleaner):
# Push to Forgejo
git push origin master
# Pull and rebuild on VM
ssh root@192.168.1.73 "cd /home/dev/ops-dev && git pull && nixos-rebuild switch --flake .#dev"
Step 7: Verify Deployment
Check skills are deployed:
ssh dev@192.168.1.73 "ls -la /etc/opencode/skills/"
# Should show: tufte-press, worklog
ssh dev@192.168.1.73 "ls -la /etc/claude/skills/"
# Should show: tufte-press, worklog
ssh dev@192.168.1.73 "cat /etc/opencode/skills/tufte-press/SKILL.md | head -5"
# Should show skill content
Check symlinks in user home:
ssh dev@192.168.1.73 "ls -la ~/.config/opencode/skills"
# Should be symlink to /etc/opencode/skills
ssh dev@192.168.1.73 "ls -la ~/.claude/skills"
# Should be symlink to /etc/claude/skills
Verify with agents (if opencode-skills plugin installed):
ssh dev@192.168.1.73
cd ~/some-project
opencode # or claude-code
# In agent, try:
# "What skills are available?"
# "Use tufte-press skill"
Updating Skills
When Skills Repo Changes
Update to latest:
cd ~/proj/ops-dev
# Update skills input to latest commit
nix flake lock --update-input skills
# Check what changed
git diff flake.lock
# Deploy to VM
ssh root@192.168.1.73 "cd /home/dev/ops-dev && nixos-rebuild switch --flake .#dev"
Pin to Specific Commit
Lock to a known-good version:
cd ~/proj/ops-dev
# Update flake.nix input to specific commit
nix flake lock --override-input skills git+http://192.168.1.108:3000/dan/skills.git?rev=abc123...
# Or edit flake.nix:
inputs.skills.url = "git+http://192.168.1.108:3000/dan/skills.git?rev=abc123...";
Add New Skills
Edit ops-dev flake.nix:
services.ai-skills = {
enable = true;
selectedSkills = [
"tufte-press"
"worklog"
"screenshot-latest" # Add new skill
];
deployTargets = [ "claude" "opencode" ];
};
Then rebuild:
ssh root@192.168.1.73 "cd /home/dev/ops-dev && nixos-rebuild switch --flake .#dev"
Rollback Plan
If Migration Fails
Restore local skills:
cd ~/proj/ops-dev
# Extract backup
tar xzf skills-backup-YYYYMMDD.tar.gz
# Revert flake.nix changes
git revert HEAD
# Rebuild with old config
ssh root@192.168.1.73 "cd /home/dev/ops-dev && nixos-rebuild switch --flake .#dev"
If Specific Skill Broken
Remove from selectedSkills:
services.ai-skills = {
enable = true;
selectedSkills = [
"tufte-press"
# "worklog" # Temporarily disabled
];
};
Or pin skills input to older commit:
nix flake lock --override-input skills git+http://192.168.1.108:3000/dan/skills.git?rev=<old-working-commit>
Troubleshooting
Issue: "error: getting status of '/nix/store/.../skills': No such file or directory"
Cause: Skills flake not properly fetched
Solution:
cd ~/proj/ops-dev
nix flake lock --update-input skills
ssh root@192.168.1.73 "cd /home/dev/ops-dev && nix flake update"
Issue: "error: attribute 'nixosModules.ai-skills' missing"
Cause: Skills repo doesn't export the module
Solution:
# Check skills repo exports
cd ~/proj/skills
nix flake show
# Should see:
# └───nixosModules
# └───ai-skills: NixOS module
Issue: Skills not appearing in /etc/opencode/skills
Cause: Module not properly enabled or paths wrong
Check:
ssh root@192.168.1.73 "systemctl status"
ssh root@192.168.1.73 "ls -la /etc/opencode/"
ssh root@192.168.1.73 "readlink -f /etc/opencode/skills/tufte-press"
# Should point to /nix/store/.../tufte-press
Debug:
# Check what the module evaluated to
ssh root@192.168.1.73 "nixos-option services.ai-skills"
Issue: Permission denied accessing skills
Cause: Wrong ownership on symlinks
Solution: Module should handle this, but manually fix:
ssh root@192.168.1.73 "chown -h dev:users ~/.config/opencode/skills ~/.claude/skills"
Benefits After Migration
Before (Local Copy)
- ❌ Manual sync between repos required
- ❌ Risk of divergence
- ❌ No version control of deployment
- ❌ Changes require copying files and rebuilding
- ❌ Unclear which version is deployed
After (Flake Input)
- ✅ Single source of truth (skills repo)
- ✅ Version controlled via flake.lock
- ✅ Automatic updates via
nix flake lock --update-input skills - ✅ Can pin to specific commits
- ✅ Declarative deployment
- ✅ Same pattern for all consuming projects
- ✅ Reproducible builds
Related Documentation
NIX-FLAKE-USAGE.md- How to consume the skills flakeCROSS-REPO-SKILL-COLLABORATION.md- Collaboration patternsmodules/ai-skills.nix- Module implementation detailsflake.nix- Skills repo flake definition
Next Steps After Migration
- Test thoroughly - Verify all skills work in both Claude and OpenCode
- Document for team - Update ops-dev README with new pattern
- Apply to other VMs - Use same pattern for other NixOS systems
- Establish update cadence - How often to update skills input?
- Monitor for issues - Watch for skill loading problems