agents-handoff 0.0.0-stage → 2.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +192 -0
- package/LICENSE +21 -0
- package/README.md +150 -2
- package/SKILL.md +147 -0
- package/capability-registry.json +27 -0
- package/docs/ARCHITECTURE.md +187 -0
- package/docs/CHANGELOG.md +196 -0
- package/docs/CLI.md +299 -0
- package/docs/COMPATIBILITY.md +124 -0
- package/docs/CONTRIBUTING.md +134 -0
- package/docs/FORMAT.md +185 -0
- package/docs/INSTALL.md +394 -0
- package/docs/INTEGRATION.md +188 -0
- package/docs/LEVEL4.md +202 -0
- package/docs/LEVEL5.md +96 -0
- package/docs/PERMISSIONS.md +145 -0
- package/docs/PROVENANCE.md +110 -0
- package/docs/SECURITY.md +93 -0
- package/docs/SESSIONS.md +97 -0
- package/docs/TROUBLESHOOTING.md +158 -0
- package/docs/UNINSTALL.md +148 -0
- package/docs/UPGRADE.md +177 -0
- package/docs/_config.yml +18 -0
- package/docs/_data/nav.yml +36 -0
- package/docs/_layouts/default.html +31 -0
- package/docs/assets/style.css +88 -0
- package/docs/index.md +92 -0
- package/docs/sessions.json +34 -0
- package/handoff.config.example.json +35 -0
- package/handoff.config.schema.json +117 -0
- package/install/CHANGELOG.md +48 -0
- package/install/README.md +76 -0
- package/install/install.mjs +1455 -0
- package/install/package.json +39 -0
- package/package.json +66 -4
- package/permission-policy.json +33 -0
- package/refs/ADAPTERS.md +33 -0
- package/refs/bootstrap.md +59 -0
- package/refs/brief-checklist.md +79 -0
- package/refs/handbook.md +58 -0
- package/refs/protocol.md +117 -0
- package/refs/roles.md +75 -0
- package/refs/validator.md +73 -0
- package/schemas/handoff.schema.json +275 -0
- package/skill.json +147 -0
- package/templates/HANDOFF.llm.schema.json +144 -0
- package/templates/HANDOFF.template.md +40 -0
- package/tests/acceptance/acceptance.yaml +209 -0
- package/tests/fixtures/minimal-transcript.jsonl +2 -0
- package/tools/agent-handoff.mjs +22 -0
- package/tools/agents-handoff.mjs +410 -0
- package/tools/capability-registry.mjs +120 -0
- package/tools/handoff.mjs +398 -0
- package/tools/handoff.test.mjs +668 -0
- package/tools/lib/handoff-root.mjs +161 -0
- package/tools/runtime-engine.mjs +330 -0
package/docs/FORMAT.md
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Handoff format
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Handoff format
|
|
6
|
+
|
|
7
|
+
One handoff is a directory of files generated from a session transcript. The source
|
|
8
|
+
transcript is read, never written: every handoff file is a render of it, and the
|
|
9
|
+
renders carry the digests that let you prove which source bytes produced them.
|
|
10
|
+
|
|
11
|
+
Everything below describes the behaviour of `tools/handoff.mjs`.
|
|
12
|
+
|
|
13
|
+
## Where handoffs live
|
|
14
|
+
|
|
15
|
+
`tools/lib/handoff-root.mjs` is the single implementation of this order. First match wins:
|
|
16
|
+
|
|
17
|
+
| Order | Source | Notes |
|
|
18
|
+
|---|---|---|
|
|
19
|
+
| 1 | `HANDOFFS_ROOT` | Environment override. Always wins. The test suite uses it to stay hermetic. |
|
|
20
|
+
| 2 | `handoff.config.json` | Found by walking up from the current directory, at most 10 levels. `storage.path` beats `handoff_dir`; a relative `handoff_dir` resolves against the directory holding the config. |
|
|
21
|
+
| 3 | `<dir>/handoffs/` | Zero-config convention, checked at each level of the same upward walk. |
|
|
22
|
+
| 4 | the skill directory | Default store when no environment variable, config or `handoffs/` directory is found. |
|
|
23
|
+
|
|
24
|
+
A `handoff.config.json` that is present but invalid fails the run with exit 2 instead of
|
|
25
|
+
falling back, because falling back would write the session to a store other than the one
|
|
26
|
+
that was configured. The schema for that file is `handoff.config.schema.json`.
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
node tools/handoff.mjs config
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
prints the resolved root, which rule chose it (`env`, `config`, `discover`, `default`),
|
|
33
|
+
the config file used, the schema path, the configured project name and whether
|
|
34
|
+
cross-project linking is enabled.
|
|
35
|
+
|
|
36
|
+
## Directory layout
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
<root>/
|
|
40
|
+
INDEX.json index of every session, rewritten on each build
|
|
41
|
+
links/<other-project>.md cross-project relation notes
|
|
42
|
+
projects/<project>/
|
|
43
|
+
PROJECT.md project file: one line per session
|
|
44
|
+
<session>/ one handoff
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
A legacy layout — session directories directly under `<root>` — is migrated on the next
|
|
48
|
+
build: any directory holding a `manifest.json` is moved to `projects/<project>/`, using the
|
|
49
|
+
project recorded in its manifest.
|
|
50
|
+
|
|
51
|
+
## Files in a session directory
|
|
52
|
+
|
|
53
|
+
| File | Written by | Contents |
|
|
54
|
+
|---|---|---|
|
|
55
|
+
| `HANDOFF.md` | `build`, `retitle`, `rename` | Human render: objective, current state, open loops, the last 40 turns as a table, tool-call digest, where the other files are, provenance. |
|
|
56
|
+
| `HANDOFF.summary.json` | `build`, `retitle`, `rename` | Compact machine payload, `schema_version` `2.0.0-summary`. Objective, `state_now` head (600 chars), open loops, counts, artifact names, provenance. |
|
|
57
|
+
| `HANDOFF.llm.json` | `build`, `retitle`, `rename` | Full machine payload, `schema_version` `2.0.0`. The summary fields plus the complete `timeline[]` and `tool_calls[]` arrays, with the turn class stored as `class`. |
|
|
58
|
+
| `timeline.jsonl` | `build` (append-only) | One JSON object per turn: `{seq, ts, class, text}`. New turns are appended; existing lines are never rewritten. |
|
|
59
|
+
| `TOOLS.md` | `build` | Every tool turn in full, untruncated, as `## [seq] <ts>` sections. The table in `HANDOFF.md` is a 120-character digest of the same turns. |
|
|
60
|
+
| `manifest.json` | `build`, `retitle`, `rename` | The record the other files are verified against. See below. |
|
|
61
|
+
|
|
62
|
+
Renders (`HANDOFF.md`, the two JSON payloads) are rewritten on every build; `timeline.jsonl`
|
|
63
|
+
is not.
|
|
64
|
+
|
|
65
|
+
## Installed-copy artifacts (not part of a session)
|
|
66
|
+
|
|
67
|
+
Two files belong to an INSTALLATION rather than to a session, and neither is written by the
|
|
68
|
+
capture engine. They sit in the directory the installer copied into, beside `SKILL.md`, and
|
|
69
|
+
nothing above in this document describes them.
|
|
70
|
+
|
|
71
|
+
| File | Written by | Contents |
|
|
72
|
+
|---|---|---|
|
|
73
|
+
| `package.json` | `install`, `update` | Only when the directory has none. A minimal manifest marked `private`, so the copy can report its own version and cannot be published to npm by accident. |
|
|
74
|
+
| `.agents-handoff-install.json` | `install`, `update` | The install record: what version landed, from which source, and a sha256 over the installed file set. |
|
|
75
|
+
|
|
76
|
+
The install record:
|
|
77
|
+
|
|
78
|
+
| Field | Meaning |
|
|
79
|
+
|---|---|
|
|
80
|
+
| `schema_version` | `1.0-install-provenance`. |
|
|
81
|
+
| `product`, `version` | The skill this is a copy of, and the version that landed, read back out of the installed `SKILL.md`. |
|
|
82
|
+
| `installer_version`, `installed_at` | The installer that wrote the record, and when. |
|
|
83
|
+
| `harness`, `target` | The harness the copy went to (`claude`, `codex`, `agents`) when one was named, else `null`, and the absolute target path. |
|
|
84
|
+
| `source` | `{kind: 'tree', path}` for a copy made from a tree beside the installer, or `{kind: 'archive', ref, label, archive_url, archive_sha256}` when the copy was fetched instead. |
|
|
85
|
+
| `file_count`, `files_sha256` | How many manifest files the copy holds, and one sha256 over all of them. |
|
|
86
|
+
| `files` | Per-path sha256 values, so a mismatch names the file that changed. |
|
|
87
|
+
|
|
88
|
+
`verify` re-hashes the manifest files, recomputes `files_sha256` and fails when one changed or
|
|
89
|
+
went missing. An installation made before this record existed has none: `verify` says so and
|
|
90
|
+
checks everything else, and treats the absence as neither a pass nor a failure.
|
|
91
|
+
[PROVENANCE.md](PROVENANCE.md) states what the record proves and what it cannot.
|
|
92
|
+
|
|
93
|
+
## manifest.json
|
|
94
|
+
|
|
95
|
+
| Field | Meaning |
|
|
96
|
+
|---|---|
|
|
97
|
+
| `session` | Session id as resolved for this handoff. |
|
|
98
|
+
| `project` | Project slug. |
|
|
99
|
+
| `harness`, `model` | From `--harness` / `--model`, from the source's own fields, or `unknown` / `""`. |
|
|
100
|
+
| `created_at`, `updated_at` | ISO 8601. `created_at` is set once on creation. |
|
|
101
|
+
| `source_paths` | Every source path this session was ever built from. |
|
|
102
|
+
| `watermark` | Highest `seq` already emitted. Turn `seq <= watermark` is already in `timeline.jsonl`. |
|
|
103
|
+
| `raw_sha256` | SHA-256 of the source bytes at the last build. |
|
|
104
|
+
| `revisions` | Number of writes. Increments on every build, `retitle` and `rename`. |
|
|
105
|
+
| `turn_count` | Number of turns in `timeline.jsonl` after the build. |
|
|
106
|
+
| `counts` | Turn counts by class: `USER`, `AGENT`, `THOUGHT`, `TOOL`. |
|
|
107
|
+
| `manifest_sha256` | Self-hash: SHA-256 of the manifest JSON with this field removed. |
|
|
108
|
+
| `prev_project` | Set when `rename` moves the session to another project. |
|
|
109
|
+
| `titled_from` | Previous directory names, set by `retitle`. |
|
|
110
|
+
|
|
111
|
+
## Turn classes
|
|
112
|
+
|
|
113
|
+
`classify()` assigns each turn one class, in this order:
|
|
114
|
+
|
|
115
|
+
| Class | Rule |
|
|
116
|
+
|---|---|
|
|
117
|
+
| `TOOL` | `kind` contains `tool`, or `role` is `tool`. |
|
|
118
|
+
| `THOUGHT` | `kind` contains `reason` or `think`. |
|
|
119
|
+
| `USER` | `role` is `user`, or `kind` is `human`. |
|
|
120
|
+
| `AGENT` | `role` is `assistant`, or `kind` is `ai`. |
|
|
121
|
+
| `OTHER` | Everything else. Kept in `timeline.jsonl`, omitted from the tables in `HANDOFF.md`. |
|
|
122
|
+
|
|
123
|
+
Turn text is taken from `text`, then `content`, then `parts[].text`.
|
|
124
|
+
|
|
125
|
+
## Accepted input
|
|
126
|
+
|
|
127
|
+
A source file ending in `.jsonl` is read line by line. Each line is parsed
|
|
128
|
+
independently; a line that does not parse, or whose text is blank, is skipped. `seq` is used
|
|
129
|
+
when it is a finite number, and the line index otherwise.
|
|
130
|
+
|
|
131
|
+
Any other extension is parsed as text: a line matching `user:`, `human:`, `assistant:`,
|
|
132
|
+
`ai:`, `system:` or `tool:` (optionally prefixed with `#`) starts a turn, and following
|
|
133
|
+
lines are appended to it. The role marker decides the class. Adapters that produce either
|
|
134
|
+
shape are listed in [ADAPTERS.md](https://github.com/Alot1z/agent-handoff/blob/main/refs/ADAPTERS.md).
|
|
135
|
+
|
|
136
|
+
If no turn parses, the build fails with exit 4.
|
|
137
|
+
|
|
138
|
+
## Session id and directory name
|
|
139
|
+
|
|
140
|
+
1. `--session <id>`, if given.
|
|
141
|
+
2. else the `session` field of the first JSONL line, if present.
|
|
142
|
+
3. else the source file name without its `.jsonl`, `.txt` or `.md` extension.
|
|
143
|
+
|
|
144
|
+
The directory name replaces every character outside `[\w.-]` with `_`. `INDEX.json` is
|
|
145
|
+
checked first: if a session with that id or with the same session UUID already exists, its
|
|
146
|
+
project and directory name are reused, so a rebuild lands in the same place.
|
|
147
|
+
|
|
148
|
+
`retitle <id-prefix> <new-name>` renames the directory to the slugified name, records the
|
|
149
|
+
old name in `titled_from`, increments `revisions`, re-seals the manifest and refreshes every
|
|
150
|
+
render. `rename <id-prefix> <new-project>` moves the session under another project and sets
|
|
151
|
+
`prev_project`.
|
|
152
|
+
|
|
153
|
+
## Revisions, watermark and idempotence
|
|
154
|
+
|
|
155
|
+
Each build appends only the turns with `seq > watermark`, then sets `watermark` to the
|
|
156
|
+
highest `seq` seen and increments `revisions`. A rebuild with no new turns and an unchanged
|
|
157
|
+
`raw_sha256` prints `handoff: up-to-date` and writes nothing.
|
|
158
|
+
|
|
159
|
+
## Provenance and verification
|
|
160
|
+
|
|
161
|
+
| Value | Definition |
|
|
162
|
+
|---|---|
|
|
163
|
+
| `raw_sha256` | SHA-256 of the exact source bytes read. |
|
|
164
|
+
| `manifest_sha256` | SHA-256 of the manifest with `manifest_sha256` removed. |
|
|
165
|
+
| `provenance` block in both JSON payloads | `sources`, `raw_sha256`, `manifest_sha256`, `revision`, `watermark`, `total_turns`. |
|
|
166
|
+
|
|
167
|
+
```
|
|
168
|
+
node tools/handoff.mjs verify <id-prefix>
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
recomputes the manifest hash, requires `timeline.jsonl` to exist, compares its line count
|
|
172
|
+
with `turn_count`, and parses `HANDOFF.llm.json`. It prints `PASS <id> [...]` and exits 0, or
|
|
173
|
+
`FAIL <id>: ...` and exits 1. Exit 2 is a usage error, 3 an ambiguous prefix, 4 no match.
|
|
174
|
+
|
|
175
|
+
## Payload schemas
|
|
176
|
+
|
|
177
|
+
`schemas/handoff.schema.json` is the portable handoff payload contract, version `1.0`: a
|
|
178
|
+
single JSON object with required keys `schema_version`, `handoff_id`, `mission_id`,
|
|
179
|
+
`task_id`, `created_at`, `updated_at`, `source`, `state` and `next_action`, and optional
|
|
180
|
+
blocks for capabilities, permissions, checkpoint, artifacts, evidence, decisions, errors and
|
|
181
|
+
the next action.
|
|
182
|
+
|
|
183
|
+
The engine does not emit that payload. Its own outputs are the session files listed above,
|
|
184
|
+
and the JSON it writes is validated by consumption, not by that schema. The schema is the
|
|
185
|
+
interchange contract for a consumer that wants a single structured document.
|
package/docs/INSTALL.md
ADDED
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Installation
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Installation
|
|
6
|
+
|
|
7
|
+
agents-handoff turns a working session into a portable handoff folder, and verifies that folder
|
|
8
|
+
later. It is a Node.js command-line skill with no runtime dependencies.
|
|
9
|
+
|
|
10
|
+
## Requirements
|
|
11
|
+
|
|
12
|
+
| Requirement | Notes |
|
|
13
|
+
|---|---|
|
|
14
|
+
| Node.js >= 18.0.0 | The engine uses ES modules and `node:fs`. |
|
|
15
|
+
| `unzip` | Only needed to extract a release archive by hand. |
|
|
16
|
+
| `tar` | Only needed when the installer fetches an archive instead of copying the tree beside it. |
|
|
17
|
+
| No network at run time | The engine never makes a network call. `npx` and `--verify-package` do. |
|
|
18
|
+
|
|
19
|
+
## Quick start
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npx agents-handoff --all # every harness found on this machine
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
One run covers one harness, several, or a directory of your own; the table in
|
|
26
|
+
[Install into the harness you use](#install-into-the-harness-you-use) has the whole set. The
|
|
27
|
+
installer copies the skill files into the resolved global root (or the harness directories
|
|
28
|
+
named), then reports how many files it copied. The published package carries the whole tree, so
|
|
29
|
+
this needs no download; only a bare copy of `install/` falls back to fetching the archive for
|
|
30
|
+
the requested version.
|
|
31
|
+
|
|
32
|
+
Confirm the installation:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
node "<install-path>/tools/handoff.mjs" config
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
That prints the handoff root the engine will use:
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
handoff: config root=<dir>
|
|
42
|
+
handoff: config source=default
|
|
43
|
+
handoff: config file=none
|
|
44
|
+
handoff: config schema=<dir>/handoff.config.schema.json
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Installing from GitHub
|
|
48
|
+
|
|
49
|
+
Two ways, and both install the same tree:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
# npm runs the repository's own package straight from GitHub — no registry copy involved
|
|
53
|
+
npx github:Alot1z/agent-handoff --all
|
|
54
|
+
|
|
55
|
+
# or clone it and run the installer from the checkout
|
|
56
|
+
git clone https://github.com/Alot1z/agent-handoff.git
|
|
57
|
+
cd agent-handoff
|
|
58
|
+
node install/install.mjs --all
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
The clone is also the way to install a version that is not on npm at all: a branch, a commit or
|
|
62
|
+
a tag. The installer accepts the same flags either way, and a checkout installs the tree in front
|
|
63
|
+
of it.
|
|
64
|
+
|
|
65
|
+
## Install into the harness you use
|
|
66
|
+
|
|
67
|
+
With no harness flag the installer uses the resolved global root (next section). A harness flag
|
|
68
|
+
puts the skill where that tool reads its skills, and several flags cover several tools in one
|
|
69
|
+
run:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
npx agents-handoff --claude # Claude Code
|
|
73
|
+
npx agents-handoff --codex # Codex CLI
|
|
74
|
+
npx agents-handoff --agents # the harness-neutral store
|
|
75
|
+
npx agents-handoff --all # every harness found on this machine
|
|
76
|
+
npx agents-handoff --claude --codex # exactly these two, one run
|
|
77
|
+
npx agents-handoff --harness claude,codex
|
|
78
|
+
npx agents-handoff --skills-dir ~/.config/mytool/skills
|
|
79
|
+
npx agents-handoff --project --claude # this repository only (./.claude/skills)
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
| Flag | Harness | User-level directory | Per-repository directory, with `--project` |
|
|
83
|
+
|---|---|---|---|
|
|
84
|
+
| `--claude` | Claude Code | `~/.claude/skills` | `./.claude/skills` |
|
|
85
|
+
| `--codex` | Codex CLI | `~/.codex/skills` | `./.codex/skills` |
|
|
86
|
+
| `--agents` | the harness-neutral store | `~/.agents/skills` | `./.agents/skills` |
|
|
87
|
+
| `--harness <a,b>` | named harness(es), comma separated, repeatable | as above | as above |
|
|
88
|
+
| `--skills-dir <dir>` | any other stack, exactly | `<dir>` | `<dir>` |
|
|
89
|
+
| `--all` | every harness whose configuration directory exists here | as above | — |
|
|
90
|
+
|
|
91
|
+
Each target gets the skill folder `agents-handoff/` below that directory, so Claude Code finds
|
|
92
|
+
`~/.claude/skills/agents-handoff/`. Targets are installed, reported and recorded one by one,
|
|
93
|
+
and the run ends with a summary:
|
|
94
|
+
|
|
95
|
+
```
|
|
96
|
+
── target 1 of 2: claude ──
|
|
97
|
+
✓ Installed agents-handoff v<version> to <dir>/agents-handoff
|
|
98
|
+
Provenance: the tree beside the installer (.) · file-set sha256 3dab5c0d754a27fd… (.agents-handoff-install.json)
|
|
99
|
+
|
|
100
|
+
── target 2 of 2: custom dir <dir> ──
|
|
101
|
+
…
|
|
102
|
+
|
|
103
|
+
Summary
|
|
104
|
+
✓ installed <dir> — v<version>
|
|
105
|
+
✓ installed <dir> — v<version>
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
`--all` installs into the harnesses whose configuration directory exists in your home directory
|
|
109
|
+
and names the ones it skipped; `--claude` and its siblings install there whether or not the
|
|
110
|
+
directory exists yet. Detection is a suggestion, never a decision.
|
|
111
|
+
|
|
112
|
+
Ask what this machine has, and whether what is installed is still intact:
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
npx agents-handoff doctor
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
```
|
|
119
|
+
agents-handoff doctor
|
|
120
|
+
product v<version> · installer v<version> · node v<node version>
|
|
121
|
+
|
|
122
|
+
Harnesses
|
|
123
|
+
✓ claude present <home>/.claude/skills
|
|
124
|
+
v<version> · 39/39 files · provenance OK
|
|
125
|
+
· codex not found <home>/.codex/skills
|
|
126
|
+
✓ agents present <home>/.agents/skills
|
|
127
|
+
|
|
128
|
+
Global resolution
|
|
129
|
+
<dir> — <reason>
|
|
130
|
+
|
|
131
|
+
Store (where handoffs are written)
|
|
132
|
+
handoff: config root=<dir>
|
|
133
|
+
|
|
134
|
+
Verdict
|
|
135
|
+
1 harness installation(s) found.
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
`doctor` reads and reports. It never installs, updates or removes anything.
|
|
139
|
+
|
|
140
|
+
## Where a global install goes
|
|
141
|
+
|
|
142
|
+
`--location global` does not point at a fixed directory. The installer searches for a store and
|
|
143
|
+
reports the reason it chose one. The order is:
|
|
144
|
+
|
|
145
|
+
| # | Condition | Result |
|
|
146
|
+
|---|---|---|
|
|
147
|
+
| 1 | `AGENT_HANDOFF_GLOBAL_DIR` is set | that directory |
|
|
148
|
+
| 2 | A skill store already holds an `agents-handoff` install | the newest such location |
|
|
149
|
+
| 3 | `~/.agents/skills` exists | `~/.agents/skills` |
|
|
150
|
+
| 4 | A skill store exists | its first account-skill root |
|
|
151
|
+
| 5 | Nothing found | the default store, created on install |
|
|
152
|
+
|
|
153
|
+
An account-skill store keeps skills two identifier levels below the store itself:
|
|
154
|
+
|
|
155
|
+
```
|
|
156
|
+
<store>/<account-id>/<profile-id>/agents-handoff/
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
`<store>` is `%APPDATA%\<client>\account-skills` on Windows, and `~/.<client>/account-skills` or
|
|
160
|
+
`~/.config/<client>/account-skills` on macOS and Linux, for whichever desktop client keeps
|
|
161
|
+
skills there. The installer searches every store it can find and never assumes one of them.
|
|
162
|
+
|
|
163
|
+
Inspect the decision before installing anything:
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
npx agents-handoff where
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
```
|
|
170
|
+
Global install root: <dir>
|
|
171
|
+
chosen because: <reason>
|
|
172
|
+
override with: AGENT_HANDOFF_GLOBAL_DIR=<dir> or --path <dir>
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
`--list` shows the resolved root, the local location, every candidate root, each harness
|
|
176
|
+
directory (whether or not it exists on this machine), and — inside a git repository — the
|
|
177
|
+
project location. Each installation found is printed with its version, the number of manifest
|
|
178
|
+
files present, and whether it still matches its install record.
|
|
179
|
+
|
|
180
|
+
## Locations
|
|
181
|
+
|
|
182
|
+
| Location | Target directory |
|
|
183
|
+
|---|---|
|
|
184
|
+
| `global` (default) | `<resolved global root>/agents-handoff` |
|
|
185
|
+
| `local` | `./local/skills/agents-handoff` |
|
|
186
|
+
| `project` | `./skills/agents-handoff` |
|
|
187
|
+
| `--path <dir>` | exactly `<dir>` |
|
|
188
|
+
|
|
189
|
+
## Options
|
|
190
|
+
|
|
191
|
+
| Option | Default | Effect |
|
|
192
|
+
|---|---|---|
|
|
193
|
+
| `--location <global\|local\|project>` | `global` | Which location to install, update, remove or verify. |
|
|
194
|
+
| `--path <dir>` | none | Use this directory instead of a resolved location. |
|
|
195
|
+
| `--version <v>` | `latest` | Request a version. Confirm what landed with `--verify`, which prints the installed version. |
|
|
196
|
+
| `--force`, `-f` | off | Skip confirmations and overwrite an existing installation. |
|
|
197
|
+
| `--claude`, `--codex`, `--agents` | none | Install into that harness's skills directory (table above). Repeatable, and combinable. |
|
|
198
|
+
| `--harness <a,b>` | none | Named harness(es), comma separated. Repeatable. |
|
|
199
|
+
| `--all` | none | Every harness whose configuration directory exists on this machine. |
|
|
200
|
+
| `--skills-dir <dir>` | none | Any other stack, exactly. Repeatable. |
|
|
201
|
+
| `--project` | off | With a harness flag: use the per-repository directory instead of the user-level one. |
|
|
202
|
+
| `--provenance` | off | With `verify`: print the install record the verification was checked against. |
|
|
203
|
+
| `--record` | off | With `verify-package`: store the tarball hashes in the install record. |
|
|
204
|
+
| `--help`, `-h` | — | Print the installer usage text. |
|
|
205
|
+
|
|
206
|
+
The installer accepts both bare verbs and flag forms: `install`/`--install`, `update`/`--update`,
|
|
207
|
+
`remove`/`--remove`, `verify`/`--verify`, `verify-package`/`--verify-package`, `list`/`--list`,
|
|
208
|
+
`doctor`/`--doctor`.
|
|
209
|
+
|
|
210
|
+
## Commands
|
|
211
|
+
|
|
212
|
+
| Command | Description |
|
|
213
|
+
|---|---|
|
|
214
|
+
| `install`, `i` (default) | Copy the skill files to the target. |
|
|
215
|
+
| `update`, `u` | Update every installation found, or the harnesses named. See [UPGRADE.md](UPGRADE.md). |
|
|
216
|
+
| `remove`, `rm` | Remove an installation, keeping everything the manifest does not own. See [UNINSTALL.md](UNINSTALL.md). |
|
|
217
|
+
| `verify`, `v` | Check every installation found: each manifest file, the skill metadata, that the engine runs, and the install record. |
|
|
218
|
+
| `verify-package`, `vp` | Check an installation against the published npm tarball for its version. |
|
|
219
|
+
| `list`, `ls` | List installed locations with version, manifest file count and provenance state. |
|
|
220
|
+
| `where` | Print the global root and why it was chosen. |
|
|
221
|
+
| `doctor` | Which harnesses are present here, what is installed where, and whether each installation still matches its record. |
|
|
222
|
+
|
|
223
|
+
## Verify an installation
|
|
224
|
+
|
|
225
|
+
```bash
|
|
226
|
+
npx agents-handoff --verify
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
Verification runs four kinds of check:
|
|
230
|
+
|
|
231
|
+
1. every file named in the installer manifest exists in the target;
|
|
232
|
+
2. `SKILL.md` declares both `name` and `version`;
|
|
233
|
+
3. `node tools/handoff.mjs config` exits 0 and prints the `handoff: config root=` marker — this
|
|
234
|
+
exercises the engine's module graph, so a missing module fails here;
|
|
235
|
+
4. the installation still matches the provenance record written when it was installed.
|
|
236
|
+
|
|
237
|
+
Each check is printed with a pass or fail mark. On success the installer also prints the
|
|
238
|
+
installed location and version; on failure it exits non-zero and names the files that changed.
|
|
239
|
+
|
|
240
|
+
With no harness flag, `--verify` verifies **every installation found on this machine**, not just
|
|
241
|
+
the one the global resolver would pick: a machine that holds the skill in `~/.claude/skills` and
|
|
242
|
+
in `~/.agents/skills` has two copies, and both are checked. A harness flag or `--skills-dir`
|
|
243
|
+
scopes the run to those targets. The run fails if any installation fails.
|
|
244
|
+
|
|
245
|
+
There is no `--verbose` flag.
|
|
246
|
+
|
|
247
|
+
## Prove an installation matches the published package
|
|
248
|
+
|
|
249
|
+
`--verify` compares an installation with its own record. `--verify-package` compares it with the
|
|
250
|
+
artifact npm is actually serving for its version — the strongest check available, and it works
|
|
251
|
+
whichever way the install happened: from a tree, from a GitHub archive, or from `npx`.
|
|
252
|
+
|
|
253
|
+
```bash
|
|
254
|
+
npx agents-handoff --verify-package
|
|
255
|
+
npx agents-handoff --verify-package --record
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
It runs three checks, in this order, because each one makes the next meaningful:
|
|
259
|
+
|
|
260
|
+
1. the downloaded tarball matches the hashes the **registry declares** for that version
|
|
261
|
+
(`dist.integrity` and `dist.shasum`) — otherwise "the published package" would be whatever
|
|
262
|
+
the network handed over;
|
|
263
|
+
2. every file named in the installer manifest is byte-identical to the file of that name in the
|
|
264
|
+
published tarball — each difference is named, with `not installed`, `not in the published
|
|
265
|
+
package` or `content differs`;
|
|
266
|
+
3. the tarball's sha256 matches the one recorded for this installation, when one was recorded.
|
|
267
|
+
|
|
268
|
+
It prints the package name and version, the tarball URL and the tarball's sha256. `--record`
|
|
269
|
+
stores that sha256 (plus sha512, the integrity string and the shasum) in the `package` block of
|
|
270
|
+
the install record, so every later run compares against a stored value instead of re-deriving
|
|
271
|
+
one.
|
|
272
|
+
|
|
273
|
+
This check needs the network. When the registry cannot be reached, or the installed version is
|
|
274
|
+
not published, it fails and says so — it never passes by default.
|
|
275
|
+
|
|
276
|
+
## The install record (provenance)
|
|
277
|
+
|
|
278
|
+
Every install writes `.agents-handoff-install.json` inside the installed copy. It records what
|
|
279
|
+
landed and what it was made from:
|
|
280
|
+
|
|
281
|
+
| Field | Meaning |
|
|
282
|
+
|---|---|
|
|
283
|
+
| `product`, `version`, `installer_version` | What was installed, and which installer did it. |
|
|
284
|
+
| `installed_at` | When. |
|
|
285
|
+
| `harness`, `target` | Which harness flag selected the target, and its absolute path. |
|
|
286
|
+
| `source` | `the tree beside the installer`, or the tag archive with its URL and sha256. |
|
|
287
|
+
| `package` | The npm identity this installation should match: `name`, `version`, `registry`, `tarball`, and — once `--verify-package --record` has run — `sha256`, `sha512`, `integrity`, `shasum` and `verified_at`. |
|
|
288
|
+
| `file_count`, `files_sha256` | The installed file set, folded into one hash. |
|
|
289
|
+
| `files` | Every manifest path with its own sha256. |
|
|
290
|
+
|
|
291
|
+
`verify` re-hashes the same file set and compares it with the record, so a changed, missing or
|
|
292
|
+
renamed file fails the check and is named. That is the difference between an installation being
|
|
293
|
+
present and being checkable: the file that changed is reported, not just `verification failed`.
|
|
294
|
+
|
|
295
|
+
```bash
|
|
296
|
+
npx agents-handoff --verify --provenance
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
`--provenance` prints the record it checked against, so the verification can be read without
|
|
300
|
+
opening the file:
|
|
301
|
+
|
|
302
|
+
```
|
|
303
|
+
provenance record
|
|
304
|
+
installed_at: <iso timestamp>
|
|
305
|
+
source: the tree beside the installer (.)
|
|
306
|
+
harness: claude
|
|
307
|
+
files: 39 · file-set sha256 3dab5c0d754a27fd…
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
An installation made before the record existed reports
|
|
311
|
+
`· no provenance record — installed before 2.0.3; reinstall to record one` and is otherwise
|
|
312
|
+
verified as before. Reinstall to write one.
|
|
313
|
+
|
|
314
|
+
The `package` block is the hook for the published-tarball check: installing from a tree cannot
|
|
315
|
+
know the hash of a tarball it did not download, so the hash is recorded the first time
|
|
316
|
+
`npx agents-handoff --verify-package --record` runs, and compared on every run after that.
|
|
317
|
+
|
|
318
|
+
## What `remove` keeps
|
|
319
|
+
|
|
320
|
+
`remove` deletes exactly what the install manifest owns — the skill files, and the two files the
|
|
321
|
+
installer writes for itself — and nothing else. Everything the manifest does not own is kept and
|
|
322
|
+
listed:
|
|
323
|
+
|
|
324
|
+
```
|
|
325
|
+
Kept — not the installer's to delete:
|
|
326
|
+
.agent-handoff/
|
|
327
|
+
handoff-session.md
|
|
328
|
+
handoff.config.json
|
|
329
|
+
handoffs/
|
|
330
|
+
projects/
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
The rule is stated as a removal set rather than a keep list on purpose: a keep list deletes
|
|
334
|
+
whatever nobody remembered to name, so a store called anything other than the expected
|
|
335
|
+
directories would have gone with the skill. A `package.json` you wrote yourself is kept too; only
|
|
336
|
+
the installer's own private stub is removed.
|
|
337
|
+
|
|
338
|
+
## Installing again
|
|
339
|
+
|
|
340
|
+
Installing over an existing installation does nothing by default when the requested version is
|
|
341
|
+
`latest`: the installer reports the version already present, then stops. It tells you to use
|
|
342
|
+
`--update` to upgrade, or `--force` to reinstall the same files.
|
|
343
|
+
|
|
344
|
+
## Manual installation
|
|
345
|
+
|
|
346
|
+
Use the release archive when you cannot run `npx`.
|
|
347
|
+
|
|
348
|
+
1. Download the archive the release attaches: `agents-handoff-v<version>.zip` from the
|
|
349
|
+
[releases page](https://github.com/Alot1z/agent-handoff/releases) (there is no
|
|
350
|
+
`latest` asset — the newest release carries its own version in the file name).
|
|
351
|
+
2. Extract it into the target directory with `unzip`.
|
|
352
|
+
3. Confirm the engine runs: `node "<target>/tools/handoff.mjs" config`.
|
|
353
|
+
|
|
354
|
+
A complete installation contains `SKILL.md`, `skill.json`, the manifest JSON files,
|
|
355
|
+
`tools/` (the engine and its runtime), `tools/lib/`, `schemas/`, `refs/`, `templates/`, `docs/`,
|
|
356
|
+
and `tests/`. That list is not a description: it is the installer's manifest, and a run that
|
|
357
|
+
cannot resolve any entry fails rather than reporting an incomplete installation as a success.
|
|
358
|
+
|
|
359
|
+
## Handoff storage is separate
|
|
360
|
+
|
|
361
|
+
Two environment variables decide two different things, and they are not interchangeable:
|
|
362
|
+
|
|
363
|
+
| Variable | Decides |
|
|
364
|
+
|---|---|
|
|
365
|
+
| `AGENT_HANDOFF_GLOBAL_DIR` | Where the installer puts the skill. |
|
|
366
|
+
| `HANDOFFS_ROOT` | Where the engine stores handoff data. |
|
|
367
|
+
|
|
368
|
+
By default the engine stores handoff data under its own root, inside the installation. Set
|
|
369
|
+
`HANDOFFS_ROOT` when you want handoffs somewhere else, for example in a versioned directory.
|
|
370
|
+
|
|
371
|
+
## Troubleshooting
|
|
372
|
+
|
|
373
|
+
| Symptom | Cause and fix |
|
|
374
|
+
|---|---|
|
|
375
|
+
| `Cannot write to <dir>` | The target is not writable. Pick another location with `--path`, or fix permissions. |
|
|
376
|
+
| `Not installed at <dir>` | `--path` or a harness flag named a directory that holds no installation. Drop the flag to act on every installation found, or run `install` first. |
|
|
377
|
+
| `Nothing to update:` / `Nothing to verify:` | No installation was found anywhere the installer looks. Install one, or name a target with `--path`. |
|
|
378
|
+
| `verify-package` fails with `is not on the npm registry, or the registry is unreachable` | The installed version has no published tarball to compare against (a checkout install, or no network). The installation itself is untouched by that result. |
|
|
379
|
+
| `verify-package` fails with `does NOT match ... as published` | The check names each differing file. Reinstall with `--force`, then run it again; a file you edited on purpose will keep failing until it is reverted. |
|
|
380
|
+
| `Incomplete install: <n> of <m> file(s) missing — …` | The tree the installer reads from is not a complete one. Install from a fresh clone, or from a freshly downloaded archive. |
|
|
381
|
+
| `no published release found — fetching the main branch` | Not an error: `--version latest` found no release object, so the archive of `main` is used instead. Pass `--version <x>` to install a released tag. |
|
|
382
|
+
| `Cannot extract the archive (tar exited …)` | `tar` is missing, or the download did not arrive intact. The message prints the `curl` and `tar` commands that do the same job by hand. |
|
|
383
|
+
| The wrong root was chosen | Run `where` to see the reason, then set `AGENT_HANDOFF_GLOBAL_DIR` or pass `--path`. |
|
|
384
|
+
| `remove` did nothing | Removal asks for confirmation, and refuses in a non-interactive shell. Pass `--force`. |
|
|
385
|
+
| Verification fails | The output names the failing check. Fix it, or reinstall with `--force`. |
|
|
386
|
+
| `provenance: file-set sha256 matches the install record` fails | A file in the installation changed or went missing after it was installed. The changed paths are listed under the check; reinstall with `--force` to restore them, or keep the edit knowingly. |
|
|
387
|
+
| `no provenance record` | The installation predates the record. That is not a failure; reinstall to write one. |
|
|
388
|
+
| A harness install went somewhere unexpected | Harness directories are listed in the table above and by `doctor`/`list`. Use `--path` or `--skills-dir` to name the directory yourself. |
|
|
389
|
+
|
|
390
|
+
## See also
|
|
391
|
+
|
|
392
|
+
- [UPGRADE.md](UPGRADE.md)
|
|
393
|
+
- [UNINSTALL.md](UNINSTALL.md)
|
|
394
|
+
- [../README.md](https://github.com/Alot1z/agent-handoff/blob/main/README.md)
|