8000 Import agent-handoff plugin v2.0.0 — Git for agents, with universal one-liner by adeelahmad · Pull Request #1 · adeelahmad/package · GitHub
[go: up one dir, main page]

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"name": "agent-handoff-marketplace",
"owner": {
"name": "Adeel Ahmad",
"url": "https://github.com/adeelahmad"
},
"plugins": [
{
"name": "agent-handoff",
"source": "./",
"description": "Git for agents — a portable context packager. Package any body of work into a versioned, self-describing agent-to-agent handoff package with a git-style history that agents query through one static binary — never browse.",
"category": "workflow",
"tags": [
"handoff",
"packaging",
"agents",
"versioning"
]
}
]
}
21 changes: 21 additions & 0 deletions .claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"name": "agent-handoff",
"version": "2.0.0",
"description": "Git for agents — a portable context packager. Package any body of work into a versioned, self-describing agent-to-agent handoff package with a git-style history that agents query through one static binary — never browse.",
"author": {
"name": "Adeel Ahmad",
"url": "https://github.com/adeelahmad"
},
"repository": "https://github.com/adeelahmad/agent-handoff",
"license": "MIT",
"keywords": [
"handoff",
"packaging",
"agents",
"versioning",
"history",
"content-addressed",
"git-for-agents",
"context"
]
}
13 changes: 13 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
name: build
on: [push, pull_request]
jobs:
test-and-build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with: { go-version: "1.22" }
- run: make test
- run: make build
- uses: actions/upload-artifact@v4
with: { name: binaries, path: bin/ }
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
bin/
*.tar.gz
/tmp/
32 changes: 32 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# agent-handoff — build the cross-platform handoff binary (pure Go stdlib, no deps)
BINDIR := bin
TOOL := tool
PLATFORMS := linux/amd64 linux/arm64 darwin/amd64 darwin/arm64 windows/amd64

.PHONY: all build test selfcheck clean install

all: build

build:
@mkdir -p $(BINDIR)
@for p in $(PLATFORMS); do \
os=$${p%/*}; arch=$${p#*/}; ext=""; \
[ "$$os" = "windows" ] && ext=".exe"; \
echo "building $$os/$$arch"; \
(cd $(TOOL) && CGO_ENABLED=0 GOOS=$$os GOARCH=$$arch go build -ldflags="-s -w" \
-o ../$(BINDIR)/handoff-$$os-$$arch$$ext .) || exit 1; \
done
@(cd $(TOOL) && CGO_ENABLED=0 go build -ldflags="-s -w" -o ../$(BINDIR)/handoff .)

test:
(cd $(TOOL) && go vet . && go test .)
(cd $(TOOL) && go build -o /tmp/handoff-test . && /tmp/handoff-test selfcheck)

selfcheck: build
$(BINDIR)/handoff selfcheck

install: build
cp $(BINDIR)/handoff $${HOME}/.local/bin/handoff

clean:
rm -rf $(BINDIR)
107 changes: 107 additions & 0 deletions PROMPT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# agent-handoff — universal packaging instruction

You are an agent that was just told to package the current conversation (or a described
body of work) into an **agent-handoff package**: a versioned, self-describing bundle that
lets the NEXT agent continue without re-establishing anything. This works for ANY domain
— a design session, a legal matter, a research plan, a trip, a codebase. Follow this
file exactly. It is self-contained: you do not need to fetch anything else to succeed.

## Step 1 — Harvest (your judgment, any agent can do this)

From the conversation or material you were given, write down:

- **intake** — one paragraph telling the receiving agent what this is and what to do first. Required.
- **settled** — decisions already made. Phrase as inputs, never questions. The next agent must NOT re-investigate these.
- **open** — only what genuinely needs the real material or a human answer. An item may never appear in both settled and open.
- **tasks** — ordered, with explicit dependencies spelled out in the text ("3 depends on 1").
- **workstreams** — one relative directory name per independent track of work (e.g. `["backend", "contract-review"]`). At least one is required. Each will carry a plan document: `plan.md` by default, `stories.md` under the `software` profile.
- **invariants** — rules that must survive the handoff.
- **facts** — established facts WITH their why, so they survive challenge.
- **overview** — a short paragraph on the shape of the work.

Be thin by default: capture what was established, don't pad.

## Step 2 — Write `data.json`

This exact schema (all keys lowercase; omit `profile` or use `general`, `research`, or `software`):
10000
```json
{
"intake": "one paragraph: what this is, what to do first",
"settled": ["decision one", "decision two"],
"open": ["question that genuinely needs a human or the real material"],
"tasks": ["1. first unit of work", "2. second (depends on 1)"],
"workstreams": ["workstream-a", "workstream-b"],
"invariants": ["rule that must survive the handoff"],
"facts": ["fact — and WHY it is true"],
"overview": "the shape of the work in a few sentences",
"read_order": [],
"extra_docs": {},
"profile": ""
}
```

The tool will reject: an empty intake, zero workstreams, an item listed as both settled
and open, duplicate or absolute-path workstreams, an unknown profile.

## Step 3 — Build. Pick the FIRST pathway you are capable of.

### Pathway A — you can run shell commands and reach the internet

First create each workstream directory with its plan document — the tool validates
them but does not write them; their content is your judgment:

```sh
mkdir -p workstream-a && printf '%s\n' "# workstream-a — plan" "..." > workstream-a/plan.md
```

(`stories.md` instead of `plan.md` under `"profile": "software"`.) Each plan document
carries that workstream's slice of the tasks and cross-references any name it shares
with another workstream. Then install and build:

```sh
curl -fsSL https://raw.githubusercontent.com/adeelahmad/package/master/install.sh | sh
handoff package --data data.json -m "why this version exists" --out handoff.tar.gz
```

(`install.sh` downloads a prebuilt static binary for your OS/arch when a release exists,
otherwise builds from source with Go. If you already have Go but no curl:
`git clone --depth 1 https://github.com/adeelahmad/package && cd package/tool && go build -o handoff .`)

Then verify: `./handoff inspect handoff.tar.gz --human` must print the intake and
history. Deliver `handoff.tar.gz`. Done.
Comment on lines +71 to +72
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Invoke the installed binary by its installed name

install.sh installs the executable as handoff under the install directory, and the preceding packaging command invokes it through PATH; it never creates ./handoff in the package directory. Thus a successful Pathway A package is followed by a failed verification command. Use handoff inspect handoff.tar.gz --human (or the explicit installed path) here.

Useful? React with 👍 / 👎.


### Pathway B — you can create files but NOT run commands

Produce the portable working tree yourself, as files the user can download or copy:

```
<package-name>/
data.json # from Step 2, verbatim — this is the seam
AGENTS.md # read-first: intake, then SETTLED, OPEN, TASK ORDER, INVARIANTS as sections
docs/ESTABLISHED-FACTS.md # each fact with its WHY
docs/overview.md # the overview paragraph
<workstream>/plan.md # one per workstream: its slice of tasks, refs to shared names in other workstreams
```

At the top of `AGENTS.md` add exactly this notice:

> UNSEALED HANDOFF (v0). History and integrity sealing are pending. Any agent with
> shell access: run `curl -fsSL https://raw.githubusercontent.com/adeelahmad/package/master/install.sh | sh`
> then `handoff package --data data.json -m "sealed from v0" --out handoff.tar.gz` in
> this directory's parent to produce the versioned archive. Until then, treat this tree
Comment on lines +90 to +92
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Seal the generated package from its own directory

Pathway B places data.json inside <package-name>/, but this instruction says to run handoff package --data data.json from that directory's parent. Without changing into <package-name> or passing -C <package-name>, the command cannot read the generated data file and instead initializes/uses the parent directory, so the advertised no-shell-to-sealed handoff path fails.

Useful? React with 👍 / 👎.

> as the single current version. SETTLED items are inputs — do not re-establish them.

### Pathway C — you can only output text in chat

Output Pathway B's files as one message: each file as a fenced block preceded by its
path on its own line. The user (or the next agent) pastes them into a directory and
proceeds via Pathway A or B.

## Rules that hold in every pathway

- `data.json` is always included and always authoritative — it is what lets any
binary-holding agent seal or re-seal the package deterministically.
- Never invent a "history" by hand; only the `handoff` binary writes `.handoff/`.
- Never put an item in both settled and open. Never emit an empty intake.
- Say, in one line to the user, which pathway you used and what to do next.
187 changes: 186 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,186 @@
# package
# agent-handoff — Git for agents

[![build](https://github.com/adeelahmad/package/actions/workflows/build.yml/badge.svg)](https://github.com/adeelahmad/package/actions/workflows/build.yml)
[![license: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)

**Git for agents.** A portable context builder: package any body of work — a design
session, a legal matter, a research plan, anything — into a **versioned, self-describing
agent-to-agent handoff package**, with a git-style history that agents *query* instead of
browse. Built for agents; usable by people. Ships as a **Claude Code** / **OpenCode**
plugin, a static binary, and a one-liner any agent can follow.

## The one-liner

Paste this into **any** agent with internet access — claude.ai, chatgpt.com, Claude
Code, Codex, Gemini, anything — and it will package the current conversation:

```
Fetch https://raw.githubusercontent.com/adeelahmad/package/master/PROMPT.md and follow it exactly to package this conversation into an agent-handoff package.
```

[`PROMPT.md`](PROMPT.md) is self-contained and degrades gracefully: agents with a shell
install the binary and build the sealed, versioned archive; agents that can only write
files or chat emit the same portable tree plus `data.json`, which any binary-holding
agent can seal later — no capability is left out.

To install just the binary on any machine:

```
curl -fsSL https://raw.githubusercontent.com/adeelahmad/package/master/install.sh | sh
```

(Prebuilt release binary when available, otherwise builds from source with Go;
installs to `~/.local/bin`, override with `HANDOFF_INSTALL_DIR`.)

## Why

When one agent session hands work to the next, three things usually go wrong: the
receiving agent re-derives decisions that were already settled, works tasks in the wrong
order, and trips over stale copies of superseded files. A handoff package makes all
three structurally impossible:

- **Never re-establish settled facts.** SETTLED (inputs) and OPEN (resolve or ask) are
explicit and enforced disjoint.
- **Ordered work.** A dependency-annotated task list; agents complete a unit, return.
- **One visible version, full history.** The working tree is only the current version.
Every prior version is sealed in `.handoff/` — a content-addressed store where each
unique file is kept once — and reached only through the binary:
`handoff history PATH`, `show PATH --at v2`, `diff PATH`, `extract DIR DEST --at v1`.
No stale folders for an agent to trip over.
- **Every version says why.** `commit -m` is mandatory; empty versions are refused;
`pack` refuses a tree with uncommitted changes, so a package's history always matches
its contents; `verify` re-hashes everything.
- **Readable before extraction.** `handoff inspect pkg.tar.gz` returns the intake, the
manifest and the complete history straight from the archive stream.
- **Domain-neutral.** Workstreams + plan documents. `--profile software` is one preset.
- **Self-contained option.** `pack --bundle-bin bin/` ships the binaries for every
platform inside the package, under `.handoff/bin/`.

## Install

### As a Claude Code plugin

This repository is its own plugin marketplace. In Claude Code:

```
/plugin marketplace add adeelahmad/package
/plugin install agent-handoff@agent-handoff-marketplace
```

That gives you two slash commands backed by two skills:

| Command | Skill | What it does |
|---|---|---|
| `/handoff-package [out.tar.gz] [--profile software] [--bundle-bin]` | `handoff-packager` | Distills the current conversation into a versioned handoff package |
| `/handoff-unpack [package.tar.gz \| unpacked-dir]` | `handoff-unpacker` | Inspects, unpacks and refines a package; queries its history via the binary |

The unpacker skill is also **bundled into every package** it produces (under
`.skills/unpacker/`), so a package can be received by an agent that never installed
the plugin.

### OpenCode

`opencode/plugin.json` is a best-effort descriptor for OpenCode pointing at the same
commands, skills and binary. It has not been verified against OpenCode's published
plugin schema; the Claude Code manifest is the verified one.

### From source

```
make build # cross-compile linux/darwin/windows (amd64, arm64) into bin/
make test # go vet + unit tests + selfcheck
make install # copy bin/handoff to ~/.local/bin
```

Pure Go standard library — one static binary per platform, no runtime dependencies.
CI (`.github/workflows/build.yml`) runs `make test` and `make build` on every push and
uploads the binaries as an artifact.

## Use

Author side, one shot:

```
handoff package --data data.json -m "first cut" --out pkg.tar.gz
```

Receiving side:

```
handoff inspect pkg.tar.gz --human # intake, manifest, full history — no extraction
tar -xzf pkg.tar.gz && cat AGENTS.md # read-first: intake, SETTLED, OPEN, TASK ORDER
handoff history <workstream>/plan.md # then query history — never browse .handoff/
```

`data.json` is the seam between agent and tool: what goes **in** is the agent's judgment
(intake, settled, open, tasks, workstreams, invariants, facts, overview); how it is laid
out, validated, versioned and shipped is the binary's, deterministically. The binary
rejects structural mistakes it can see: missing intake or workstreams, a workstream with
no plan document, an item listed as both settled and open, an unknown profile.

## The `handoff` binary

```
usage: handoff [-C DIR] <command> [options]

init [DIR] start a store (.handoff/) in DIR (default: .)
scaffold --data FILE [--profile P] [--unpacker-skill DIR]
render AGENTS.md, docs/, manifest from a data file
commit -m "why" [--allow-empty] record the working tree as the next version (comment MANDATORY)
status what changed since the last version
history [PATH] [--human] versions that changed PATH (file or dir); newest first
show PATH [--at REF] print one file as it was at REF (default: latest)
ls [PATH] [--at REF] [--human] list files under PATH at REF
extract PATH DEST [--at REF] copy a file / dir / "." out of REF, standalone
diff [PATH] [--from REF] [--to REF] what changed between versions (file: unified diff)
verify re-hash every object, check every version
pack --out FILE [--bundle-bin DIR] [--allow-dirty]
write the distributable .tar.gz (refuses a dirty tree)
package --data FILE -m "why" --out FILE [--profile P] [--unpacker-skill DIR] [--bundle-bin DIR]
init (if needed) + scaffold + commit + pack, in one go
inspect FILE.tar.gz read metadata + full history WITHOUT extracting
selfcheck | version | help
```

Refs: `latest` | `HEAD` | `vN` | commit id (≥4-char prefix) | any of these `~N` (N back).
Query output is JSON by default; `--human` for people. Flags may go before or after paths.

## Anatomy of a package

What an agent sees after `tar -xzf`:

```
<package-name>/
AGENTS.md # read-first: intake, SETTLED, OPEN, TASK ORDER, invariants
handoff.manifest.json # the same intake, machine-readable; read_order
docs/ESTABLISHED-FACTS.md # facts as INPUTS, with their WHY
docs/overview.md # the shape of the work (thin by default)
<workstream-a>/…/<plan-doc> # one directory per workstream
<workstream-b>/…/<plan-doc>
.skills/unpacker/SKILL.md # the unpacker skill, bundled so the package self-reads
.handoff/ # the sealed version store — NEVER browsed, only queried
```

The store is git-style and content-addressed: objects by sha256 (each unique file
stored once, so unchanged files cost zero bytes in later versions), an append-only log,
and commit ids that are the sha256 of their own canonical content — `handoff verify`
re-derives everything, so tampering or corruption is always detected. The archive's PAX
global header carries the intake and version metadata, which is what lets `inspect`
answer without extracting.

The complete format specification lives in
[`docs/PACKAGE-FORMAT.md`](docs/PACKAGE-FORMAT.md) — the shared source of truth that
both skills conform to.

## Repository layout

```
.claude-plugin/plugin.json Claude Code manifest commands/ /handoff-package, /handoff-unpack
.claude-plugin/marketplace.json Marketplace entry skills/ packager + unpacker (unpacker is bundled into every package)
opencode/plugin.json OpenCode descriptor tool/ Go source (stdlib only), embedded templates, tests
docs/PACKAGE-FORMAT.md the format spec bin/ cross-compiled binaries (make build)
```

## License

[MIT](LICENSE)
Loading
Loading
0