# Plan: Patch-Package Style Feature for Template File Modifications

## Summary

Add a patch system that preserves user modifications to template files across upgrades. When users edit built-in skills, commands, agents, or tools, their changes are saved as patches and automatically reapplied after template updates.

## Problem

```
User edits .opencode/skill/beads/skill.md (built-in template file)
User runs `ock upgrade`
→ Current behavior: Template file overwrites OR user version preserved forever (never gets template updates)
→ User loses customizations OR misses template improvements
```

## Solution

Save user modifications as unified diff patches. On upgrade:

1. Apply new template files
2. Automatically reapply user patches
3. Handle conflicts gracefully

---

## File Structure

```
.opencode/
├── patches/
│   ├── .patches.json                 # Metadata index (hashes, versions)
│   ├── skill-beads-skill.md.patch    # Unified diff patches
│   ├── command-ship.md.patch
│   └── *.patch.rej                   # Failed patches (conflicts)
├── .version                          # Already exists
└── ... (other template files)
```

---

## New Commands

### `ock patch save <file>`

Save current modifications as a patch.

```bash
$ ock patch save .opencode/skill/beads/skill.md

✓ Saved patch: .opencode/patches/skill-beads-skill.md.patch
  Original: a1b2c3d4 → Current: e5f6g7h8
```

### `ock patch list`

List all saved patches with status.

```bash
$ ock patch list

Active Patches (3):
  skill/beads/skill.md    ✓ Clean
  command/ship.md         ⚠ Stale (template changed)
  agent/build.md          ✗ Conflict
```

### `ock patch apply` (automatic during upgrade)

Reapply patches after template update.

### `ock patch remove <file>`

Remove a saved patch.

---

## `.patches.json` Schema

```json
{
  "version": "1.0.0",
  "patches": {
    "skill/beads/skill.md": {
      "originalHash": "sha256:a1b2c3d4...",
      "currentHash": "sha256:e5f6g7h8...",
      "patchFile": "skill-beads-skill.md.patch",
      "createdAt": "2026-02-03T10:00:00Z",
      "templateVersion": "1.2.3"
    }
  }
}
```

---

## Upgrade Flow (Modified)

```
1. User runs: ock upgrade
2. Check version (existing logic)
3. For PRESERVE_DIRS files that exist in template:
   - Copy template file (overwrite user's version)
   - Check if patch exists in .patches.json
   - If patch exists, apply it
   - If patch fails, save conflict to .patch.rej
4. Report: "✓ Updated X files, reapplied Y patches, Z conflicts"
```

---

## Conflict Resolution

| Scenario                        | Action                                    |
| ------------------------------- | ----------------------------------------- |
| Template unchanged              | Auto-apply patch                          |
| Template changed, patch applies | Auto-apply with info message              |
| Template changed, patch fails   | Save `.patch.rej`, skip file, notify user |

**Conflict UI:**

```
⚠ CONFLICT: skill/beads/skill.md

Template changed in v1.3.0 and your patch no longer applies.

Options:
  [1] View diff between versions
  [2] Keep new template (discard patch)
  [3] Keep your version (skip update)
  [4] Manually merge (edit patch.rej)

Saved: .opencode/patches/skill-beads-skill.md.patch.rej
```

---

## Implementation Phases

### Phase 1: Core Patch Infrastructure

**Files to create:**

- `src/commands/patch.ts` - Patch save/list/apply/remove commands
- `src/utils/patch.ts` - Diff generation, hash calculation, patch application

**Files to modify:**

- `src/index.ts` - Register `patch` command

**Key functions:**

```typescript
// src/utils/patch.ts
export function generatePatch(templatePath: string, userPath: string): string;
export function applyPatch(filePath: string, patchContent: string): boolean;
export function calculateHash(content: string): string;
export function loadPatchMetadata(opencodeDir: string): PatchMetadata;
export function savePatchMetadata(opencodeDir: string, metadata: PatchMetadata): void;
```

### Phase 2: Upgrade Integration

**Files to modify:**

- `src/commands/upgrade.ts` - Integrate patch apply after template copy

**Changes:**

1. After `copyDirWithPreserve()`, call `applyPatches()`
2. Modify `PRESERVE_DIRS` logic: still preserve, but ALSO check for patches
3. Add patch status to upgrade summary

### Phase 3: CLI Polish

- Interactive conflict resolution
- `--dry-run` flag for patch apply
- Pretty diff viewer for conflicts

---

## Dependencies

Use existing Node.js capabilities:

- `crypto.createHash('sha256')` - Hash calculation
- **diff** library (`npm:diff`) - Generate unified diffs
- **patch-package** or custom parser - Apply patches

Alternative: Shell out to `git diff` and `git apply` (simpler, but requires git).

---

## Critical Files

| File                      | Purpose                    |
| ------------------------- | -------------------------- |
| `src/commands/patch.ts`   | New - Patch commands       |
| `src/utils/patch.ts`      | New - Patch utilities      |
| `src/commands/upgrade.ts` | Modify - Integrate patches |
| `src/index.ts`            | Modify - Register command  |

---

## Verification

1. **Unit tests**: `src/commands/patch.test.ts`
   - Test patch generation from diff
   - Test patch application
   - Test conflict detection

2. **Integration test**:

   ```bash
   # Setup
   ock init
   echo "# Custom section" >> .opencode/skill/beads/skill.md
   ock patch save .opencode/skill/beads/skill.md

   # Simulate upgrade
   ock upgrade --force

   # Verify
   grep "Custom section" .opencode/skill/beads/skill.md  # Should exist
   ```

3. **Conflict test**:
   - Modify template file in dist/template
   - Run upgrade
   - Verify conflict detection and `.patch.rej` creation

---

## Complexity Estimate

| Component           | Effort | Notes                          |
| ------------------- | ------ | ------------------------------ |
| Hash/diff utilities | 2h     | Use `diff` library             |
| Patch save command  | 2h     | Generate + store patch         |
| Patch list command  | 1h     | Read metadata, show status     |
| Patch apply logic   | 3h     | Apply patches, handle failures |
| Upgrade integration | 2h     | Hook into existing flow        |
| Conflict UI         | 2h     | Interactive resolution         |
| Tests               | 2h     | Unit + integration             |

**Total: ~14 hours (2 days)**

---

## Open Questions

1. **Git dependency?** Should we require git for `git diff`/`git apply` or use pure JS?
   - Recommendation: Pure JS (`diff` library) for portability

2. **Auto-save patches?** Should we auto-detect modifications on upgrade?
   - Recommendation: Manual `ock patch save` for now (explicit is better)

3. **Nested directories?** How to handle `skill/beads/skill.md` path → filename conversion?
   - Recommendation: Replace `/` with `-` → `skill-beads-skill.md.patch`
