- 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
415 lines
9.5 KiB
Markdown
415 lines
9.5 KiB
Markdown
# 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
|
|
|
|
- [x] Skills repo deployed to Forgejo: `http://192.168.1.108:3000/dan/skills`
|
|
- [x] Skills repo has working flake with ai-skills module
|
|
- [x] ops-dev VM running NixOS with flake-based configuration
|
|
- [x] 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)**:
|
|
```nix
|
|
{
|
|
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)**:
|
|
```nix
|
|
{
|
|
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 iteration
|
|
- **`git+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**:
|
|
```nix
|
|
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):
|
|
```nix
|
|
# 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):
|
|
```bash
|
|
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**:
|
|
```bash
|
|
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**:
|
|
```bash
|
|
cat flake.lock | jq '.nodes.skills'
|
|
```
|
|
|
|
Should show:
|
|
```json
|
|
{
|
|
"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)**:
|
|
```bash
|
|
# 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)**:
|
|
```bash
|
|
# 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**:
|
|
```bash
|
|
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**:
|
|
```bash
|
|
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):
|
|
```bash
|
|
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**:
|
|
```bash
|
|
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**:
|
|
```bash
|
|
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**:
|
|
```nix
|
|
services.ai-skills = {
|
|
enable = true;
|
|
selectedSkills = [
|
|
"tufte-press"
|
|
"worklog"
|
|
"screenshot-latest" # Add new skill
|
|
];
|
|
deployTargets = [ "claude" "opencode" ];
|
|
};
|
|
```
|
|
|
|
Then rebuild:
|
|
```bash
|
|
ssh root@192.168.1.73 "cd /home/dev/ops-dev && nixos-rebuild switch --flake .#dev"
|
|
```
|
|
|
|
## Rollback Plan
|
|
|
|
### If Migration Fails
|
|
|
|
**Restore local skills**:
|
|
```bash
|
|
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**:
|
|
```nix
|
|
services.ai-skills = {
|
|
enable = true;
|
|
selectedSkills = [
|
|
"tufte-press"
|
|
# "worklog" # Temporarily disabled
|
|
];
|
|
};
|
|
```
|
|
|
|
**Or pin skills input to older commit**:
|
|
```bash
|
|
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**:
|
|
```bash
|
|
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**:
|
|
```bash
|
|
# 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**:
|
|
```bash
|
|
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**:
|
|
```bash
|
|
# 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:
|
|
```bash
|
|
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 flake
|
|
- `CROSS-REPO-SKILL-COLLABORATION.md` - Collaboration patterns
|
|
- `modules/ai-skills.nix` - Module implementation details
|
|
- `flake.nix` - Skills repo flake definition
|
|
|
|
## Next Steps After Migration
|
|
|
|
1. **Test thoroughly** - Verify all skills work in both Claude and OpenCode
|
|
2. **Document for team** - Update ops-dev README with new pattern
|
|
3. **Apply to other VMs** - Use same pattern for other NixOS systems
|
|
4. **Establish update cadence** - How often to update skills input?
|
|
5. **Monitor for issues** - Watch for skill loading problems
|