An open API service indexing awesome lists of open source software.

https://github.com/givepro91/cc-handoff

Never lose context between Claude Code sessions — hook-enforced session handoff to a git-tracked HANDOFF.md, portable across machines.
https://github.com/givepro91/cc-handoff

ai claude-code claude-code-plugin context-management developer-tools handoff hooks llm

Last synced: 15 days ago
JSON representation

Never lose context between Claude Code sessions — hook-enforced session handoff to a git-tracked HANDOFF.md, portable across machines.

Awesome Lists containing this project

README

          

# cc-handoff

> Never lose context between Claude Code sessions — even across machines.

A [Claude Code](https://docs.claude.com/en/docs/claude-code) plugin that automatically preserves a session's **decisions, open work, and next steps** into a git-tracked, **per-branch** handoff (`docs/handoff/.md`), so the next session — on this machine or another, on the same branch — picks up exactly where you left off.

## The one-line design

> **Tools live machine-global. Data lives in the repo (git).**

`--resume`, auto memory, and `/rewind` checkpoints all live under `~/.claude` on a *single machine* — they don't follow a `git push` to your other laptop. So the canonical handoff is a file that travels with the repo.

## Why this exists

- **`/compact` is lossy.** Anthropic states it plainly: *"overly aggressive compaction can result in the loss of subtle but critical context whose importance only becomes apparent later."* Decisions and the *why* behind them are exactly what gets dropped.
- **Manual and model-discretion handoffs forget.** If you have to remember to run `/handoff`, you eventually won't — and a crashed/closed session takes everything with it.
- **Hooks don't forget.** A `Stop` hook can return `decision: "block"` with a `reason` that becomes the model's next instruction — the one robust way to *force* the handoff to be written when work happened but wasn't recorded.

## How it works — a hybrid of hooks

| Hook | When | What it does |
|------|------|--------------|
| `SessionStart` | startup / resume / clear / compact | Injects **the current branch's** handoff into context — **auto-restore** (blocked ones prominently; resolved/deleted ones not at all; auto-migrates a legacy `HANDOFF.md` on first run) |
| `PreCompact` | right before compaction | Backs up the raw transcript (per-branch) + a git breadcrumb to `docs/handoff/.snapshots/` — **loss protection** |
| `Stop` | end of a turn | If ≥N files changed *and* the branch handoff is stale, blocks **once** and asks the model to write it — **never-miss enforcement** |
| `/handoff` skill | manual | Write this branch's handoff (`blocked` to flag it), or `/handoff status` · `/handoff resolve` (delete when done) · `/handoff prune` (clear orphans) |

**Honest limitation:** there is no way to make the model write an intelligent handoff at the exact instant you close the terminal — `SessionEnd` hook output is ignored. Instead this catches turn-end and pre-compaction, which covers virtually every real case.

## Parallel branches & worktrees

Run many sessions at once (Boris-style: several local + a few on the web) and a single shared handoff falls apart — it gets overwritten and you can't tell which one is latest or already resolved. v2 fixes that:

- **One handoff per branch** — `docs/handoff/.md` (slug-sanitized, e.g. `feat/login` → `feat-login.md`). Each worktree only ever writes its own branch file, so there's **no disk or merge conflict**.
- **`SessionStart` injects only the current branch's** handoff — no cross-branch noise.
- **Derived status board** — `/handoff status` scans the files and shows status/age/issue per branch (🟢 active · ⛔ blocked), marking the current branch, plus any **prunable** orphans. There is **no committed index file** (that would conflict on merge) — the board is always derived on demand.
- **Ephemeral by design** — a handoff exists only while work is open. **`/handoff resolve`** deletes a finished branch's handoff (git history keeps it); **`/handoff prune`** clears handoffs whose branches were merged/deleted — so files never pile up on `main`.

```
🟢 feat-login (current) 2h ago #42
⛔ fix-cache BLOCKED 1d ago #51

prunable: chore-old (branch merged) — run /handoff prune
```

Upgrading from v1? The legacy single `docs/handoff/HANDOFF.md` is **auto-migrated** to the current branch's file on first run (lossless — the legacy file is left in place; delete it once you've confirmed).

## Install

```sh
# Add this repo as a marketplace, then install (user scope = available in every project)
/plugin marketplace add givepro91/cc-handoff
/plugin install handoff@cc-handoff
```

Or from a local clone:

```sh
/plugin marketplace add /path/to/cc-handoff
/plugin install handoff@cc-handoff
```

## Enable per project (opt-in)

Installing the plugin does **not** disturb any project on its own. A project opts in by having a `docs/handoff/` directory — without it, every hook stays silent.

```sh
/handoff # creates docs/handoff/ + this branch's handoff → hooks activate from then on
```

Optionally, pin it in your project `CLAUDE.md` / `AGENTS.md` so new sessions read it first:

```
At session start, if a handoff for the current branch exists under docs/handoff/, read it first and continue from "Next steps".
```

## Configuration

| Env var | Default | Meaning |
|---------|---------|---------|
| `HANDOFF_MIN_CHANGED_FILES` | `3` | Minimum changed files before the `Stop` hook will block |
| `HANDOFF_STOP_ENFORCE` | _(on)_ | Set to `0` to disable `Stop` enforcement (snapshot + restore still run) |

## Data layout

- `docs/handoff/.md` — **git-tracked.** One canonical handoff per branch; travels across machines via commit + push.
- `docs/handoff/HANDOFF.md` — legacy single file (v1). Auto-migrated to the branch file on first run; safe to delete afterwards.
- `docs/handoff/.snapshots/` — **git-ignored** (added automatically). Lossless per-branch pre-compaction backups, local to the machine.

The status board is **derived** (`/handoff status`) — there is no index file to commit or conflict on.

Keep the detailed *why* of each decision in your **commit messages** (`git log` is portable too); keep open work and next steps in the branch handoff.

## What a handoff looks like

```markdown
---
branch: feat/login
status: active # active | blocked (done = file deleted)
updated: 2026-06-08T12:00:00.000Z
issue: 42 # optional
pr: 51 # optional
---

# Handoff — · ·

## Restore in 30s
What you were doing / where you got to / what you just finished.

## Next steps
- [ ] Concrete next action — down to files & commands
- [ ] Blocker + what was already tried
- [ ] Parked item + why

## Touch points
- path/to/file:line — what / why · verify: `command` → expected

## Decisions
- → detailed why in commit ``
```

## Project structure

```
cc-handoff/
├── .claude-plugin/marketplace.json # marketplace manifest
└── plugins/handoff/
├── .claude-plugin/plugin.json # plugin manifest
├── hooks/
│ ├── hooks.json # registers SessionStart · PreCompact · Stop
│ ├── _lib.mjs # shared helpers
│ ├── session-start.mjs # restore
│ ├── pre-compact.mjs # snapshot
│ └── stop.mjs # never-miss enforcement (medium)
└── skills/handoff/SKILL.md # /handoff manual capture
```

Hooks are plain Node ESM (no dependencies). Requires a recent Node on `PATH`.

## License

MIT © 2026 Jay (Spacewalk)