# 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
