# f-27: Fast Reinstall — Skip PM When Package Dependencies Unchanged

- **Milestone:** M1
- **Complexity:** Low
- **Depends On:** #019 (strip devDependencies)

## 1. Problem Statement

`kley install <pkg>` always delegates to the native package manager (e.g. `npm install ./.kley/my-lib`), even when reinstalling a package whose `dependencies` and `peerDependencies` haven't changed since the last install. The PM call is the slowest part of the operation and is unnecessary in this case.

This is especially common during iterative development: a developer runs `kley install my-lib` after each `kley publish` in the library, but the library's dependency list rarely changes between iterations — only the source code does.

## 2. Proposed Solution

Before calling the PM, compare the package's `dependencies` and `peerDependencies` against the snapshot stored in `kley.lock`. If they are identical, skip the PM and copy files directly to `node_modules/<pkg>/`.

### Logic

1. **Copy package**: Copy from store to `.kley/<pkg>/` (existing behavior).
2. **Strip devDependencies**: Remove `devDependencies` from `.kley/<pkg>/package.json` (per #019, existing behavior).
3. **Compare dependencies**: Read `dependencies` and `peerDependencies` from `.kley/<pkg>/package.json` and compare against the snapshot in `kley.lock` for this package.
4. **Fast path** (dependencies unchanged):
   a. Copy `.kley/<pkg>/` content directly into `node_modules/<pkg>/` (overwriting existing files).
   b. Skip the PM call entirely.
5. **Slow path** (dependencies changed, or first install / no snapshot):
   a. Run the PM as usual (`npm install ./.kley/<pkg>/`, `pnpm add ./.kley/<pkg>/`, etc.).
   b. Update the dependency snapshot in `kley.lock`.

### Dependency Snapshot in `kley.lock`

Add dependency snapshots to `PackageInfo`:

```rust
pub struct PackageInfo {
    pub version: String,
    #[serde(default)]
    pub pinned: bool,
    #[serde(default)]
    pub retreated: bool,
    /// Snapshot of dependencies at last PM install, used for fast reinstall check
    #[serde(default)]
    pub dependencies: BTreeMap<String, String>,
    #[serde(default)]
    pub peer_dependencies: BTreeMap<String, String>,
}
```

When the PM is called (slow path), save the current `dependencies` and `peerDependencies` from `.kley/<pkg>/package.json` into `kley.lock`. On the next install, compare against this snapshot.

### Why This Works

- If `dependencies` and `peerDependencies` are the same as last time → the PM already resolved and installed all transitive deps → no need to run it again.
- Only the package's own source code changed → copying files to `node_modules/` is sufficient.
- This is a simple JSON object comparison — no semver parsing, no `npm list` output parsing, works with all package managers.

## 3. When Fast Path Applies

| Scenario | Snapshot exists? | Dependencies changed? | Path |
|----------|-----------------|----------------------|------|
| First `kley install` | No | N/A | **Slow** — run PM |
| Reinstall after source change | Yes | No | **Fast** — copy to node_modules |
| Reinstall after new dep added | Yes | Yes | **Slow** — run PM |
| `kley add` (first time) | No | N/A | **Slow** — run PM |
| `kley link` | N/A | N/A | **N/A** — symlink, no PM |

## 4. Acceptance Criteria

- When `kley install <pkg>` is called and the package's `dependencies` and `peerDependencies` match the snapshot in `kley.lock`, the PM call is skipped and files are copied directly to `node_modules/<pkg>/`.
- When `dependencies` or `peerDependencies` have changed, the PM is called as usual.
- `kley.lock` stores a snapshot of `dependencies` and `peerDependencies` after each PM install.
- First-time install (no snapshot in `kley.lock`) always runs the PM.
- `kley link` is unaffected (uses symlinks, no PM call).
- The optimization works with all package managers (npm, pnpm, yarn) — comparison is JSON-only, no PM-specific logic.
