ai-handoff-mcp 0.3.0 → 0.3.1
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/README.md +233 -231
- package/dist/src/server.js +2 -2
- package/dist/src/version.js +7 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,231 +1,233 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
|
16
|
-
|
|
17
|
-
|
|
|
18
|
-
|
|
|
19
|
-
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
|
32
|
-
|
|
33
|
-
| **
|
|
34
|
-
|
|
35
|
-
**
|
|
36
|
-
|
|
37
|
-
**
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
-
|
|
52
|
-
|
|
53
|
-
-
|
|
54
|
-
-
|
|
55
|
-
-
|
|
56
|
-
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
|
67
|
-
|
|
68
|
-
| **
|
|
69
|
-
| **
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
|
82
|
-
|
|
83
|
-
|
|
|
84
|
-
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
|
103
|
-
|
|
104
|
-
|
|
|
105
|
-
|
|
|
106
|
-
|
|
|
107
|
-
|
|
|
108
|
-
|
|
|
109
|
-
|
|
|
110
|
-
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
ai-handoff-mcp
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
|
141
|
-
|
|
142
|
-
| `
|
|
143
|
-
| `
|
|
144
|
-
| `
|
|
145
|
-
| `
|
|
146
|
-
| `
|
|
147
|
-
| `
|
|
148
|
-
| `
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
ai-handoff-mcp
|
|
158
|
-
ai-handoff-mcp
|
|
159
|
-
ai-handoff-mcp
|
|
160
|
-
ai-handoff-mcp
|
|
161
|
-
ai-handoff-mcp
|
|
162
|
-
ai-handoff-mcp
|
|
163
|
-
ai-handoff-mcp
|
|
164
|
-
ai-handoff-mcp
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
"
|
|
182
|
-
"
|
|
183
|
-
"
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
-
|
|
194
|
-
-
|
|
195
|
-
- **
|
|
196
|
-
-
|
|
197
|
-
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
-
|
|
204
|
-
-
|
|
205
|
-
-
|
|
206
|
-
-
|
|
207
|
-
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
npm
|
|
215
|
-
npm
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
1
|
+
<img src="assets/icon.svg" width="96" height="96" alt="AI Handoff icon">
|
|
2
|
+
|
|
3
|
+
# AI Handoff MCP
|
|
4
|
+
|
|
5
|
+
**Your AI coding agent hits its usage limit mid-task. The next agent, from any vendor, continues from the exact stopping point, without a model call, without reading old sessions and without re-exploring the code.**
|
|
6
|
+
|
|
7
|
+
Local-first MCP server + host hooks for Claude Code, VS Code (Copilot), Copilot CLI, Cursor, Gemini CLI, Windsurf / Devin Desktop and Visual Studio. No network, no telemetry, and session transcripts are never read. Two guards protect the same credits: a prompt gate and a loop guard, both running outside the model.
|
|
8
|
+
|
|
9
|
+
> In Arabic: a practical usage guide, [USAGE.ar.md](USAGE.ar.md), and the full guide, [DOCUMENTATION.ar.md](DOCUMENTATION.ar.md)
|
|
10
|
+
|
|
11
|
+
## Measured, not claimed
|
|
12
|
+
|
|
13
|
+
Every number below comes from [BENCHMARK.md](BENCHMARK.md) (`npm run bench`, no model, no network; tokens estimated at 3.5 characters per token).
|
|
14
|
+
|
|
15
|
+
| | Result |
|
|
16
|
+
|---|---|
|
|
17
|
+
| What the next agent reads to continue (fixture: this repo, 3 files edited after a checkpoint) | ~640 tokens, versus ~16,000 just to read `git status`, `git diff` and the changed files, which still don't say what the task or next steps were |
|
|
18
|
+
| Cost of the prompt gate, prompt strength, loop guard and file capture | 0 tokens: they run outside the model |
|
|
19
|
+
| Fixed cost per session | ~620 (`minimal`), ~2,200 (`lean`) or ~3,000 (`standard`) tokens of tool definitions, ~355 for the instructions block. `init` picks the smallest profile your hosts support |
|
|
20
|
+
| Prompt gate on its labelled corpus (88 prompts, Arabic and English) | 0 clear prompts stopped, 0 vague prompts missed, same decision in both languages for every pair |
|
|
21
|
+
| Hook latency | ~0.1-0.3 s per tool call with a global install (2-4 s through npx); ~0.4-0.7 s once at session start, also on a 20,000-file repository |
|
|
22
|
+
|
|
23
|
+
What it does not measure: how many tokens a real agent spends exploring without a handoff. That depends on the model and the task, and this benchmark runs no model.
|
|
24
|
+
|
|
25
|
+
## What makes it different
|
|
26
|
+
|
|
27
|
+
### 1. The stopping point survives a sudden limit, in three independent layers
|
|
28
|
+
|
|
29
|
+
A summary only helps if the agent writes it before it stops. Usage limits don't wait. `/handoff`-style commands need one more model turn, which a session that hit its limit no longer has.
|
|
30
|
+
|
|
31
|
+
| Layer | Who writes it | Survives a sudden limit? | What it knows |
|
|
32
|
+
|---|---|---|---|
|
|
33
|
+
| **Hooks** (post-tool, stop, pre-compact, session-end) | The host, after every tool call | Yes: the last *completed* tool call is always recorded | Files edited, tool count, last tool, whether the turn ended cleanly or was aborted |
|
|
34
|
+
| **Agent checkpoints** (`log_progress`, `update_handoff`) | The agent, after each step | Up to the last call | Intent: task, current step, ordered next steps, decisions, tests |
|
|
35
|
+
| **Git delta** | Computed at resume time | Yes, always, for any agent | Commits and files changed since the checkpoint, including uncommitted edits (per-file fingerprints, no repo writes) |
|
|
36
|
+
|
|
37
|
+
**The intent gap is closed by a nudge.** Hooks know *what* changed, not *why*. When an agent ends its turn after editing 3 or more files without a checkpoint, the stop hook asks it once to call `log_progress` (Claude Code, VS Code and Cursor, whose stop hooks can do that; Cursor's own forum reports its follow-ups as unreliable on Windows, and Gemini is left out because its hook would reject the agent's final answer). It costs ~120 tokens plus one more model step for that call (whose input is the conversation so far, mostly cache hits on hosts with prompt caching). That is the call the instructions already ask for after each step, so an agent that follows them never sees the nudge. It fires only when needed, never twice in a row, and never after the loop guard stopped the turn. `"checkpointNudge": { "mode": "off" }` or `"minFiles": 5` in `.ai-handoff.json`.
|
|
38
|
+
|
|
39
|
+
**Cloud agents can continue too.** With `"repoFile": true`, the checkpoint is also kept in `.ai-handoff/HANDOFF.md` inside the repository (credentials redacted), updated at every checkpoint and turn end. Cloud agents (Copilot coding agent, Claude Code on the web, Codex) and agents without MCP read it; the instructions block tells them where. Nothing is sent anywhere: committing the file is your choice. Off by default.
|
|
40
|
+
|
|
41
|
+
### 2. Minimal context on resume, paid once
|
|
42
|
+
|
|
43
|
+
One **compact brief** merges all three layers. The default budget is 6,000 characters (~2,250 in the benchmark), sections are ordered by importance, and the least important are dropped first. The session-start hook injects it automatically, so the new agent doesn't even need a tool call. It flags sessions that ended mid-step and lists exactly which files changed after the checkpoint, so the agent checks those instead of exploring the repo.
|
|
44
|
+
|
|
45
|
+
Old plans are flagged: when the checkpoint is 7 or more days old, or was written on another branch, the brief starts with a warning to check the plan against the code (about 30 tokens, only then).
|
|
46
|
+
|
|
47
|
+
It is not paid for twice: when the host already loads the instructions block (CLAUDE.md, AGENTS.md, ...), the brief leaves out the general rules (~80 tokens), and a resumed conversation that already contains the same brief is not given it again. It complements the hosts' own memory (Claude Code memory, Copilot Memory): those keep facts about a repository inside one vendor; this keeps the state of the task in progress, across vendors.
|
|
48
|
+
|
|
49
|
+
### 3. Prompt clarity gate: vague prompts are stopped before they cost credits
|
|
50
|
+
|
|
51
|
+
When you submit a prompt, a **local, rule-based check** (no model and no credits) scores how actionable it is, in English and Arabic. If it's too vague to act on without expensive exploration ("fix it", "صلحها", "refactor everything", "ليش ما بيشتغل؟"), the host **rejects it before the model runs** and tells you what's missing: target file/function, expected result, error message, or narrower scope.
|
|
52
|
+
|
|
53
|
+
- Replies and commands always pass: `yes`, `تمام`, `continue`, `كمل`, `/clear`, `!npm test`.
|
|
54
|
+
- Later in a session, short follow-ups like "fix it" pass, because they refer to the conversation. Only whole-project requests are still stopped.
|
|
55
|
+
- If a checkpoint already describes the task, short prompts pass.
|
|
56
|
+
- Override by adding `--go` anywhere in the prompt. Set `warn` mode to let the prompt through and tell the agent to ask one clarifying question instead of exploring.
|
|
57
|
+
- The prompt is analysed in memory and **never stored**. Only session ids and counters are kept.
|
|
58
|
+
- **Prompt strength**: a prompt that passes but is weak gets a one-line note *for you only* (it never enters the model's context, so it costs no tokens): `Prompt strength 60/100 (weak) · ✗ file/code named ✓ focused scope`, plus one tip. Set `showScore` to `always` to see it for every prompt, or `off`. Claude Code, VS Code and Gemini show it as a message; Windsurf and generic hosts get it on stderr; Cursor can't show a note on a passing prompt.
|
|
59
|
+
|
|
60
|
+
Try it: `ai-handoff-mcp check-prompt "fix it"`
|
|
61
|
+
|
|
62
|
+
### 4. Loop guard: an agent stuck in a loop is stopped
|
|
63
|
+
|
|
64
|
+
A pre-tool hook watches every tool call and catches four loop patterns:
|
|
65
|
+
|
|
66
|
+
| Rule | Catches | Not triggered by |
|
|
67
|
+
|---|---|---|
|
|
68
|
+
| **repeat** | The identical call (same tool, same arguments) a 3rd time with nothing changed in between, e.g. re-running `npm test` or re-reading a file | Fix-and-test cycles: any edit or state-changing command (`git commit`, `npm install`, `rm`, `>`, ...) in between resets it |
|
|
69
|
+
| **ping-pong** | Edits that undo the previous edit of a file, back and forth | A single revert |
|
|
70
|
+
| **failing** | The same command failing 4 times in a row even though the agent edits code in between | User interrupts; a success resets it |
|
|
71
|
+
| **runaway** | More than 150 tool calls in one turn without the user writing | Normal long tasks below the budget |
|
|
72
|
+
|
|
73
|
+
The first hit **denies that one call**, and the agent is told why and to change approach or ask you. If it hits the same rule again, the guard **stops the agent** and tells you, in your prompt's language, what loop it was in. On Cursor and Windsurf, which can't end a turn from a hook, every further call is denied instead. Your next prompt clears the counters. The stop is written to the handoff timeline (with credentials redacted), so the next agent knows not to repeat it. Only hashes and counters are stored, never commands or file content.
|
|
74
|
+
|
|
75
|
+
Settings: `"loopGuard": { "mode": "block", "repeatLimit": 3, "failLimit": 4, "turnBudget": 150 }` in `.ai-handoff.json` (`warn` only notifies you, `turnBudget: 0` disables the runaway rule), or `--guard` on the hook command.
|
|
76
|
+
|
|
77
|
+
### 5. Token budget: you choose how much the tool costs
|
|
78
|
+
|
|
79
|
+
Everything that guards your credits runs **outside the model and costs 0 tokens**: the prompt gate in block mode, the prompt strength note, the loop guard, file capture and the Git delta. What the model does pay for is the tool definitions, the brief injected at session start and the agent's own checkpoint calls. A profile in `.ai-handoff.json` controls those:
|
|
80
|
+
|
|
81
|
+
| | `standard` (default) | `lean` | `minimal` |
|
|
82
|
+
|---|---|---|---|
|
|
83
|
+
| Tools the model sees | all 9 (~3,000 tokens) | the 4 a handoff needs: `resume_task`, `log_progress`, `update_handoff`, `save_handoff` (~2,200 tokens) | only `log_progress`, which then also takes `task`, `keyFiles` and `blockers` (~600 tokens) |
|
|
84
|
+
| Brief at session start | up to 6,000 characters (~1,700 tokens) | up to 3,000 characters (~860 tokens) | up to 3,000 characters |
|
|
85
|
+
| Tool and field descriptions | unchanged | **unchanged**: they are what makes checkpoints useful to the next agent | unchanged |
|
|
86
|
+
| Works on | every host | every host | hosts that inject the brief at session start: Claude Code, VS Code, Cursor, Gemini CLI (not Windsurf or Visual Studio, which need `resume_task`) |
|
|
87
|
+
|
|
88
|
+
```json
|
|
89
|
+
{ "tokens": { "profile": "lean", "briefChars": 3000 } }
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
`briefChars` overrides the profile's brief budget (minimum 1,500). A brief over budget drops its least important sections first, and `resume_task` with `detail: "full"` still returns everything. Hooks read the setting from the project. The MCP server reads it when it starts, from the folder the host starts it in; if your host starts it elsewhere (user-level configs), set `AI_HANDOFF_TOKENS=lean` in the server's `env`. In `lean` and `minimal`, the CLI does what the hidden tools did: `show`, `prompt`, `export`, `clear`.
|
|
93
|
+
|
|
94
|
+
**`init` chooses the profile**: the smallest one every configured host supports (`minimal` when every host injects the brief, `lean` when `.vscode/mcp.json` is configured, because Visual Studio reads it without hooks, or with Windsurf). It keeps the default 6,000-character brief, so only the tool list changes, and it never replaces a profile you set. `--tokens <profile>` chooses one yourself.
|
|
95
|
+
|
|
96
|
+
**No profile sends the agent to a missing tool**: the brief, the nudge, the prompts and the tool definitions name only the tools that profile exposes, and one test checks every one of them for every profile. The instructions block is the same for all profiles, so tools that not every profile has are named "where available". In `minimal`, `nextSteps` is the whole remaining plan and replaces the list, so a changed plan can be corrected without `update_handoff`.
|
|
97
|
+
|
|
98
|
+
See the real numbers for a project: `ai-handoff-mcp tokens`
|
|
99
|
+
|
|
100
|
+
## Supported agents
|
|
101
|
+
|
|
102
|
+
| Agent | MCP tools | Auto-capture hooks | Prompt gate | Loop guard | Brief injected at start |
|
|
103
|
+
|---|---|---|---|---|---|
|
|
104
|
+
| VS Code + GitHub Copilot | ✅ `.vscode/mcp.json` | ✅ `.github/hooks/ai-handoff.json` (Preview) | ✅ | ✅ deny + stop | ✅ |
|
|
105
|
+
| GitHub Copilot CLI | user config | ✅ reads the same `.github/hooks` file | ❌ (host can't block) | ✅ | ✅ |
|
|
106
|
+
| Visual Studio 2022 17.14+ / 2026 | ✅ reads `.vscode/mcp.json` | ❌ (no hooks) | ❌ | ❌ | via `resume` prompt |
|
|
107
|
+
| Claude Code | ✅ `.mcp.json` | ✅ `.claude/settings.json` | ✅ | ✅ deny + stop | ✅ |
|
|
108
|
+
| Cursor | ✅ `.cursor/mcp.json` | ✅ `.cursor/hooks.json` | ✅ | ✅ deny | ✅ |
|
|
109
|
+
| Gemini CLI | ✅ `.gemini/settings.json` | ✅ same file | ✅ | ✅ deny + stop | ✅ |
|
|
110
|
+
| Windsurf / Devin Desktop | user config | ✅ `.windsurf/hooks.json` (or `.devin/`) | ✅ | ✅ deny | ❌ (no event; `AGENTS.md`) |
|
|
111
|
+
| Any other agent | via MCP | `ai-handoff-mcp hook … --host generic` | ✅ exit code 2 | ✅ exit code 2 | — |
|
|
112
|
+
| Agents without hooks or MCP | — | Git delta still works | — | — | `ai-handoff-mcp prompt` |
|
|
113
|
+
|
|
114
|
+
## Quick start
|
|
115
|
+
|
|
116
|
+
Requires Node.js 20.11+ and Git (optional, but needed for the Git delta).
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
npm install -g ai-handoff-mcp # once published; global = fastest hooks
|
|
120
|
+
cd path/to/your/project
|
|
121
|
+
ai-handoff-mcp init --mode global # add --all to configure every agent
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Without a global install: `npx -y ai-handoff-mcp init` (hooks then start through npx, 2-4 s per tool call in [BENCHMARK.md](BENCHMARK.md); `init` warns you). From a local checkout of this repository: `npm install && npm run build && node dist/src/index.js init <project> --mode local`.
|
|
125
|
+
|
|
126
|
+
`init` always writes the VS Code, Visual Studio, Claude Code and Copilot files plus `AGENTS.md`. It adds Cursor, Gemini and Windsurf when the project already has their folders, or everything with `--all`. It merges into existing files and keeps your own servers and hooks, skips JSON-with-comments files and tells you what to add, refreshes only its own marked blocks and hooks, and is safe to re-run. Use `--only <targets>` to choose: `vscode, claude, claude-hooks, copilot, copilot-hooks, agents, cursor, gemini, windsurf`.
|
|
127
|
+
|
|
128
|
+
Then reload the editor or agent and approve the server and hooks when asked.
|
|
129
|
+
|
|
130
|
+
## Daily use
|
|
131
|
+
|
|
132
|
+
1. Claude works and checkpoints with `log_progress`. Hooks record every edit.
|
|
133
|
+
2. Claude hits its limit in the middle of a step.
|
|
134
|
+
3. You open Copilot (or Cursor, Gemini, ...). The brief is injected and the agent continues from "Was doing when stopped".
|
|
135
|
+
4. No hooks? Use the MCP prompt: VS Code `/mcp.ai-handoff.resume`, Claude Code `/mcp__ai-handoff__resume`, Visual Studio **+ Add Reference → Prompts**. Or just say "resume the task".
|
|
136
|
+
5. Agent without MCP: `ai-handoff-mcp prompt --target cursor` prints a paste-ready prompt.
|
|
137
|
+
|
|
138
|
+
## MCP tools
|
|
139
|
+
|
|
140
|
+
| Tool | Purpose |
|
|
141
|
+
|---|---|
|
|
142
|
+
| `resume_task` | **Call first.** Brief = checkpoint + auto-capture + Git delta + rules. `detail: "full"` for everything |
|
|
143
|
+
| `log_progress` | Cheap frequent checkpoint: `completedStep` (moved out of `nextSteps`), `currentStep`, files, tests, decisions |
|
|
144
|
+
| `update_handoff` | Replace given fields; `append*` lists add without duplicates |
|
|
145
|
+
| `save_handoff` | Start a new checkpoint (replaces fields, keeps the timeline) |
|
|
146
|
+
| `get_handoff` | Raw JSON record |
|
|
147
|
+
| `get_project_status` | Read-only Git branch/HEAD/status/diff stat |
|
|
148
|
+
| `prepare_handoff` | Paste-ready prompt for another agent |
|
|
149
|
+
| `export_handoff_markdown` | Write `HANDOFF.md` (won't overwrite unless `overwrite=true`) |
|
|
150
|
+
| `clear_handoff` | Delete the checkpoint (`confirm: true` required) |
|
|
151
|
+
|
|
152
|
+
Prompts: `resume`, `checkpoint`. Every tool takes an absolute `projectPath`. Relative paths are rejected.
|
|
153
|
+
|
|
154
|
+
## CLI
|
|
155
|
+
|
|
156
|
+
```text
|
|
157
|
+
ai-handoff-mcp start the MCP server on stdio (hosts do this)
|
|
158
|
+
ai-handoff-mcp init [dir] --only <targets> --all --mode <npx|global|local> --force --tokens <profile>
|
|
159
|
+
ai-handoff-mcp show [dir] [--full] print the brief
|
|
160
|
+
ai-handoff-mcp prompt [dir] --target <agent>
|
|
161
|
+
ai-handoff-mcp export [dir] write HANDOFF.md (--overwrite to replace it)
|
|
162
|
+
ai-handoff-mcp clear [dir] --yes delete the checkpoint
|
|
163
|
+
ai-handoff-mcp stats [dir] what the prompt gate, loop guard and handoff did (counters only)
|
|
164
|
+
ai-handoff-mcp check-prompt "..." try the prompt gate (--later = mid-session)
|
|
165
|
+
ai-handoff-mcp tokens [dir] tokens the tool adds to the model's context, with the project's settings
|
|
166
|
+
ai-handoff-mcp hook <event> --agent <name> --host <claude|vscode|cursor|gemini|windsurf|generic>
|
|
167
|
+
--gate <block|warn|off> --guard <block|warn|off>
|
|
168
|
+
events: session-start, prompt-submit, pre-tool, post-tool, pre-compact, stop, session-end
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Hook commands read the host's JSON from stdin and answer in that host's dialect. They never fail the agent's session: errors go to stderr, the exit code is 0, and the prompt gate fails open. The only exception is an intentional block on hosts that use the exit-code-2 convention.
|
|
172
|
+
|
|
173
|
+
**Any other tool with command hooks** can use `--host generic`. Send `{"cwd", "tool_name", "tool_input"}` or `{"cwd", "session_id", "prompt"}` on stdin. A block returns `{"decision":"block","continue":false,"reason":…}` on stdout, the message on stderr, and exit code 2.
|
|
174
|
+
|
|
175
|
+
### Settings
|
|
176
|
+
|
|
177
|
+
Per project, in `.ai-handoff.json`:
|
|
178
|
+
|
|
179
|
+
```json
|
|
180
|
+
{
|
|
181
|
+
"promptGate": { "mode": "block", "threshold": 60, "showScore": "weak" },
|
|
182
|
+
"loopGuard": { "mode": "block", "repeatLimit": 3, "failLimit": 4, "turnBudget": 150 },
|
|
183
|
+
"tokens": { "profile": "standard" },
|
|
184
|
+
"checkpointNudge": { "mode": "on", "minFiles": 3 },
|
|
185
|
+
"repoFile": false
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
`mode` is `block` (default), `warn` or `off`. `threshold` is the vagueness score from 0 to 100 at which prompts are stopped, so raise it to be more lenient. A `--gate` flag on the hook command overrides `mode`. `showScore` is `weak` (default: prompts that pass with a vagueness of at least half the threshold), `always` or `off`. Strength is `100 - vagueness`; replies and commands are never rated.
|
|
190
|
+
|
|
191
|
+
## Privacy and data
|
|
192
|
+
|
|
193
|
+
- Checkpoints live in `~/.ai-handoff-mcp/<sha256-of-path>.json` (override with `AI_HANDOFF_DATA_DIR`). File mode is 0600, writes are atomic, and a cross-process lock lets the server and hooks write without losing updates. The same event reported by two hosts reading overlapping config files is recorded once.
|
|
194
|
+
- **Transcripts are never read.** Hook input is reduced to a whitelist before anything else sees it, and `transcript_path` is dropped. A test spies on the file system to prove the transcript is never opened.
|
|
195
|
+
- **Usage counters** for `stats` (prompts stopped, loop denials and stops, briefs injected and their size, checkpoints, nudges) are kept next to the checkpoint. Numbers only: no prompt, command or file content, and a test checks this.
|
|
196
|
+
- **`repoFile` is the one exception to "nothing in the repository"**: off by default; when on, it writes `.ai-handoff/HANDOFF.md` with credentials redacted, and you decide whether to commit it.
|
|
197
|
+
- **Prompts are never stored.** Only session ids and allowed/blocked counters are kept, and a test checks this. The loop guard stores only hashes and counters, not commands or file content, and a test checks this too.
|
|
198
|
+
- Only paths inside the project are recorded. Git is used read-only, and a test checks that no Git objects are written.
|
|
199
|
+
- No network calls and no telemetry.
|
|
200
|
+
|
|
201
|
+
## Limitations (stated honestly)
|
|
202
|
+
|
|
203
|
+
- Hooks capture the last **completed** tool call. Unfinished reasoning from the stopped session isn't recoverable, and the brief says so.
|
|
204
|
+
- Hooks know *what* changed, not *why*. Intent comes from the agent's `log_progress` calls.
|
|
205
|
+
- The prompt gate is a heuristic. On its labelled corpus (88 prompts, Arabic and English) it makes no mistakes, but it was tuned on that corpus, so that shows consistency, not accuracy on prompts it has never seen. It can misjudge, which is why `--go`, `warn` mode and `threshold` exist. It can't see earlier conversation, so it only applies strict rules to a session's first prompt.
|
|
206
|
+
- The loop guard runs a small process before every tool call. With a global install that takes ~0.1-0.2 s; through `npx` it adds 2-4 s per call (measured in [BENCHMARK.md](BENCHMARK.md)), so prefer `--mode global`. Its patterns are conservative, but a legitimate repeat can be denied once; the agent then gets an explanation and you can simply tell it to continue.
|
|
207
|
+
- VS Code hooks are **Preview** (VS Code 1.109+, `chat.useHooks`, workspace trust). Keep `chat.useClaudeHooks` off to avoid duplicate hooks. Hook formats of all hosts may change, and the adapters are covered by tests so changes are easy to spot.
|
|
208
|
+
- Gemini CLI has been succeeded by Antigravity CLI. The Gemini adapter follows the Gemini CLI documentation.
|
|
209
|
+
- Checkpoints are per machine. There's no sync, by design.
|
|
210
|
+
|
|
211
|
+
## Development
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
npm install
|
|
215
|
+
npm test # build + typecheck + 189 tests (unit, MCP in-memory, real stdio process, real shell hooks, every host dialect)
|
|
216
|
+
npm run dev # run from source
|
|
217
|
+
npm run bench # the measurements in BENCHMARK.md (-- --write updates the file, -- --large adds a 20,000-file repository)
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
CI (`.github/workflows/ci.yml`) runs `npm test` on Linux, macOS and Windows with Node 20 and 22 on every push and pull request.
|
|
221
|
+
|
|
222
|
+
## Publishing
|
|
223
|
+
|
|
224
|
+
See [DOCUMENTATION.ar.md](DOCUMENTATION.ar.md#النشر). In short:
|
|
225
|
+
|
|
226
|
+
1. Replace `YOUR-GITHUB-USERNAME` in `package.json` and `server.json`. `npm run check:publish` refuses to pass until you do.
|
|
227
|
+
2. Push to GitHub.
|
|
228
|
+
3. `npm publish --access public`. `prepublishOnly` runs every test and the publish check.
|
|
229
|
+
4. Run `mcp-publisher login github` and `mcp-publisher publish`. That lists it in the official MCP Registry, which feeds the GitHub MCP Registry, VS Code's `@mcp` gallery and Visual Studio's **Extensions → MCP Registries**.
|
|
230
|
+
|
|
231
|
+
## License
|
|
232
|
+
|
|
233
|
+
MIT. See `LICENSE`.
|
package/dist/src/server.js
CHANGED
|
@@ -8,7 +8,7 @@ import { applyProgress, describeProgress, mergeUpdate, normalizeAgent, renderBri
|
|
|
8
8
|
import { defaultHost, hasInstructionsBlock, readProjectConfig } from "./hooks.js";
|
|
9
9
|
import { isExposed, tokenSettings } from "./tokens.js";
|
|
10
10
|
import { REPO_FILE, writeRepoFile } from "./repo-file.js";
|
|
11
|
-
import { SERVER_NAME, SERVER_VERSION } from "./version.js";
|
|
11
|
+
import { SERVER_ICONS, SERVER_NAME, SERVER_VERSION } from "./version.js";
|
|
12
12
|
export { SERVER_NAME, SERVER_VERSION };
|
|
13
13
|
const projectPathSchema = z
|
|
14
14
|
.string()
|
|
@@ -99,7 +99,7 @@ async function buildBrief(store, root, detail = "compact", reader, minimalTools
|
|
|
99
99
|
return renderBrief({ projectPath: root, record, git, changes, detail, maxChars, rulesInInstructions, minimalTools });
|
|
100
100
|
}
|
|
101
101
|
export function createServer(store = new HandoffStore(), options = {}) {
|
|
102
|
-
const server = new McpServer({ name: SERVER_NAME, version: SERVER_VERSION });
|
|
102
|
+
const server = new McpServer({ name: SERVER_NAME, version: SERVER_VERSION, icons: SERVER_ICONS });
|
|
103
103
|
const exposed = (tool) => isExposed(tool, options.tools ?? "all");
|
|
104
104
|
const minimal = options.tools === "minimal";
|
|
105
105
|
if (exposed("resume_task"))
|
package/dist/src/version.js
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
/** Kept apart from server.ts so hook commands can print the version without loading the MCP SDK. */
|
|
2
2
|
export const SERVER_NAME = "ai-handoff-mcp";
|
|
3
|
-
export const SERVER_VERSION = "0.3.
|
|
3
|
+
export const SERVER_VERSION = "0.3.1";
|
|
4
|
+
/** Served from the repository; server.json lists the same icons for the MCP Registry (a test keeps them equal). */
|
|
5
|
+
const ICON_BASE = "https://raw.githubusercontent.com/EngHamzaAboJarad/ai-handoff-mcp/master/assets";
|
|
6
|
+
export const SERVER_ICONS = [
|
|
7
|
+
{ src: `${ICON_BASE}/icon.png`, mimeType: "image/png", sizes: ["256x256"] },
|
|
8
|
+
{ src: `${ICON_BASE}/icon.svg`, mimeType: "image/svg+xml", sizes: ["any"] }
|
|
9
|
+
];
|
|
4
10
|
//# sourceMappingURL=version.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai-handoff-mcp",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Local-first MCP server that lets one AI coding agent (Claude, Copilot, Cursor, ...) continue another agent's task from its last checkpoint",
|
|
5
5
|
"mcpName": "io.github.EngHamzaAboJarad/ai-handoff-mcp",
|
|
6
6
|
"type": "module",
|