handoff-cli 0.3.4__tar.gz → 0.3.6__tar.gz

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.
Files changed (47) hide show
  1. {handoff_cli-0.3.4 → handoff_cli-0.3.6}/.github/workflows/publish.yml +5 -0
  2. handoff_cli-0.3.6/Makefile +33 -0
  3. handoff_cli-0.3.6/PKG-INFO +217 -0
  4. {handoff_cli-0.3.4 → handoff_cli-0.3.6}/README.md +18 -17
  5. {handoff_cli-0.3.4 → handoff_cli-0.3.6}/README.zh-CN.md +4 -4
  6. {handoff_cli-0.3.4 → handoff_cli-0.3.6}/cli/commands/new.py +15 -3
  7. {handoff_cli-0.3.4 → handoff_cli-0.3.6}/cli/commands/resume.py +20 -2
  8. {handoff_cli-0.3.4 → handoff_cli-0.3.6}/cli/commands/run.py +13 -4
  9. {handoff_cli-0.3.4 → handoff_cli-0.3.6}/cli/main.py +5 -3
  10. handoff_cli-0.3.6/cli/skills/handoff-codex/SKILL.md +52 -0
  11. handoff_cli-0.3.6/cli/skills/handoff-ds/SKILL.md +52 -0
  12. handoff_cli-0.3.6/cli/skills/handoff-ds.toml +63 -0
  13. handoff_cli-0.3.6/cli/skills/handoff-opus/SKILL.md +52 -0
  14. {handoff_cli-0.3.4 → handoff_cli-0.3.6}/pyproject.toml +2 -1
  15. handoff_cli-0.3.4/PKG-INFO +0 -7
  16. handoff_cli-0.3.4/cli/skills/handoff-codex/SKILL.md +0 -83
  17. handoff_cli-0.3.4/cli/skills/handoff-ds/SKILL.md +0 -83
  18. handoff_cli-0.3.4/cli/skills/handoff-ds.toml +0 -52
  19. handoff_cli-0.3.4/cli/skills/handoff-opus/SKILL.md +0 -83
  20. {handoff_cli-0.3.4 → handoff_cli-0.3.6}/.gitignore +0 -0
  21. {handoff_cli-0.3.4 → handoff_cli-0.3.6}/CLAUDE.md +0 -0
  22. {handoff_cli-0.3.4 → handoff_cli-0.3.6}/cli/__init__.py +0 -0
  23. {handoff_cli-0.3.4 → handoff_cli-0.3.6}/cli/backend.py +0 -0
  24. {handoff_cli-0.3.4 → handoff_cli-0.3.6}/cli/backend_types.yaml +0 -0
  25. {handoff_cli-0.3.4 → handoff_cli-0.3.6}/cli/commands/__init__.py +0 -0
  26. {handoff_cli-0.3.4 → handoff_cli-0.3.6}/cli/commands/env.py +0 -0
  27. {handoff_cli-0.3.4 → handoff_cli-0.3.6}/cli/commands/init.py +0 -0
  28. {handoff_cli-0.3.4 → handoff_cli-0.3.6}/cli/commands/list.py +0 -0
  29. {handoff_cli-0.3.4 → handoff_cli-0.3.6}/cli/commands/tail.py +0 -0
  30. {handoff_cli-0.3.4 → handoff_cli-0.3.6}/cli/config.py +0 -0
  31. {handoff_cli-0.3.4 → handoff_cli-0.3.6}/cli/core.py +0 -0
  32. {handoff_cli-0.3.4 → handoff_cli-0.3.6}/cli/jsonl_parser.py +0 -0
  33. {handoff_cli-0.3.4 → handoff_cli-0.3.6}/cli/jsonl_viewer.py +0 -0
  34. {handoff_cli-0.3.4 → handoff_cli-0.3.6}/cli/stream.py +0 -0
  35. {handoff_cli-0.3.4 → handoff_cli-0.3.6}/cli/tui.py +0 -0
  36. {handoff_cli-0.3.4 → handoff_cli-0.3.6}/cli/user_config_template.yaml +0 -0
  37. {handoff_cli-0.3.4 → handoff_cli-0.3.6}/docs/TODO.md +0 -0
  38. {handoff_cli-0.3.4 → handoff_cli-0.3.6}/docs/assets/claude-code.jpg +0 -0
  39. {handoff_cli-0.3.4 → handoff_cli-0.3.6}/docs/assets/codex.jpg +0 -0
  40. {handoff_cli-0.3.4 → handoff_cli-0.3.6}/docs/assets/handoff-hero.jpg +0 -0
  41. {handoff_cli-0.3.4 → handoff_cli-0.3.6}/docs/assets/list-tui.jpg +0 -0
  42. {handoff_cli-0.3.4 → handoff_cli-0.3.6}/docs/assets/parallel.jpg +0 -0
  43. {handoff_cli-0.3.4 → handoff_cli-0.3.6}/docs/assets/shell.jpg +0 -0
  44. {handoff_cli-0.3.4 → handoff_cli-0.3.6}/docs/assets/tail.jpg +0 -0
  45. {handoff_cli-0.3.4 → handoff_cli-0.3.6}/docs/cli-reference.zh-CN.md +0 -0
  46. {handoff_cli-0.3.4 → handoff_cli-0.3.6}/docs/configuration.zh-CN.md +0 -0
  47. {handoff_cli-0.3.4 → handoff_cli-0.3.6}/docs/design.zh-CN.md +0 -0
@@ -21,6 +21,11 @@ jobs:
21
21
  - name: Build
22
22
  run: uv build
23
23
 
24
+ - name: Check long description rendering
25
+ run: |
26
+ pip install twine
27
+ twine check dist/*
28
+
24
29
  - name: Publish to PyPI
25
30
  run: uv publish --token ${{ secrets.PYPI }}
26
31
 
@@ -0,0 +1,33 @@
1
+ .PHONY: render skills
2
+
3
+ render: ## Preview PyPI long description rendering
4
+ pip install -q "readme_renderer[md]" 2>/dev/null
5
+ python -m readme_renderer README.md > /tmp/handoff-pypi-preview.html
6
+ @echo "✅ /tmp/handoff-pypi-preview.html ($(shell wc -c < /tmp/handoff-pypi-preview.html | tr -d ' ') bytes)"
7
+ open /tmp/handoff-pypi-preview.html
8
+
9
+ # ── skill 文档同步 ──────────────────────────────────────────────────
10
+ # handoff-ds/SKILL.md 是主文档(master),只改它。
11
+ # 它的 frontmatter(顶部的 `---...---` 块)不同步;其下的正文会被复制到
12
+ # 其它 backend 的 SKILL.md,并把 backend 名/缩写替换成对应值。占位符在
13
+ # 「构建时」由 sed 替换,落盘的都是具体值——LLM 永远读不到占位符。
14
+ SKILLS := cli/skills
15
+ MASTER := $(SKILLS)/handoff-ds/SKILL.md
16
+
17
+ skills: ## 把 handoff-ds/SKILL.md 正文同步到其它 backend 的 SKILL.md
18
+ @$(call sync_skill,handoff-codex,codex,cx)
19
+ @$(call sync_skill,handoff-opus,opus,op)
20
+ @echo "done."
21
+
22
+ # $(1)=目标目录 $(2)=backend 名 $(3)=run_id 缩写
23
+ define sync_skill
24
+ target="$(SKILLS)/$(1)/SKILL.md"; \
25
+ tmp=$$(mktemp); \
26
+ awk '{print} /^---[[:space:]]*$$/{n++; if(n==2) exit}' "$$target" > "$$tmp"; \
27
+ awk 'body{print} /^---[[:space:]]*$$/{n++; if(n==2) body=1}' "$(MASTER)" \
28
+ | sed -e 's/deepseek/$(2)/g' \
29
+ -e 's/handoff-ds/handoff-$(2)/g' \
30
+ -e 's/0613-ds-/0613-$(3)-/g' >> "$$tmp"; \
31
+ mv "$$tmp" "$$target"; \
32
+ echo "synced $$target (backend=$(2))";
33
+ endef
@@ -0,0 +1,217 @@
1
+ Metadata-Version: 2.4
2
+ Name: handoff-cli
3
+ Version: 0.3.6
4
+ Summary: Multi coding-agent task dispatcher — a CLI proxy for claude that sends coding tasks to configurable AI backends
5
+ Requires-Python: >=3.9
6
+ Requires-Dist: pyyaml<7,>=6
7
+ Requires-Dist: textual<3,>=2
8
+ Description-Content-Type: text/markdown
9
+
10
+ <div align="center">
11
+ <img src="https://raw.githubusercontent.com/dazuiba/handoff/main/docs/assets/handoff-hero.jpg" width="100%" alt="hero">
12
+
13
+ # With **Handoff**, your coding agents can finally work together.
14
+
15
+
16
+ | You're in | Hand off to | Why |
17
+ | :-- | :-- | :-- |
18
+ | Claude Code / Codex | **DeepSeek** | It does the simple work fast and cheap; save the expensive quota for decisions |
19
+ | DeepSeek | **Codex / Opus** | Borrow a brain for hard problems, bring the answer back to your session |
20
+
21
+ No tool-switching, no lost context.
22
+
23
+ **English** · [简体中文](README.zh-CN.md)
24
+
25
+ </div>
26
+
27
+ ## Why handoff
28
+
29
+ If you use more than one coding agent, these will sound familiar:
30
+
31
+ - 💸 **"Claude / Codex: the $20 plan never lasts. The $100 plan costs too much."**<br>
32
+ — Just say: *"Give this task to `/handoff-ds`."* DeepSeek does the work fast and cheap. Save your expensive quota for decisions.
33
+ - 🤔 **"DeepSeek is stuck. I want a second opinion from Codex."**<br>
34
+ — Just say: *"Ask `/handoff-codex` what it thinks."* No new terminal. No re-explaining. The answer comes back to your current session.
35
+ - 🔁 **"`/handoff-ds` finished that task. Now I have a follow-up task for it."**<br>
36
+ — Just say: *"Resume the last `/handoff-ds` session, then do X."* It just sends one more message in the old conversation — all the old context is still there.
37
+
38
+
39
+ **The math is simple:** DeepSeek V4 is as capable as Sonnet, and on [OpenCode Go](https://opencode.ai/go?ref=D5926WCTD8) the same money buys **18× the work**:
40
+
41
+ | Option | Relative cost for the same work |
42
+ | --- | --- |
43
+ | Claude Sonnet (subscription) | 1× (baseline) |
44
+ | DeepSeek official API | **1/3** |
45
+ | [OpenCode Go](https://opencode.ai/go?ref=D5926WCTD8) (includes DeepSeek V4) | **1/18** |
46
+
47
+ So: **only pay for the SOTA model** (Opus / GPT-5.5) — use it to plan and review. Everything else goes to DeepSeek. With handoff, **$20 Claude Code (plan + dispatch) + $5 OpenCode Go (execution) ≈ the work of a $200 Claude Code Max.**
48
+
49
+ ## Quick start
50
+
51
+ > **Before you start:** handoff works inside Claude Code (CLI or desktop app) or Codex. You need at least one of them installed and logged in.
52
+
53
+ ### 1. Install
54
+
55
+ ```bash
56
+ uv tool install handoff-cli
57
+ handoff init # creates the config, links skill / agent files
58
+ ```
59
+
60
+ Upgrade with `uv tool upgrade handoff-cli`.
61
+
62
+ ### 2. Set your token
63
+
64
+ The `opus` and `codex` backends reuse your existing Claude Code / Codex logins — zero config. **Only DeepSeek needs a token.**
65
+
66
+ For DeepSeek, we recommend the [OpenCode Go plan](https://opencode.ai/go?ref=D5926WCTD8) (lowest cost, includes DeepSeek V4). Once you have a key, edit `~/.handoff/config.yaml` and change just the `ANTHROPIC_AUTH_TOKEN` line:
67
+
68
+ ```yaml
69
+ # ~/.handoff/config.yaml — handoff init generates this for you
70
+ backends:
71
+ deepseek: # ← first = default
72
+ type: claude
73
+ model: deepseek-v4-flash
74
+ pro_model: "deepseek-v4-pro[1m]"
75
+ env:
76
+ ANTHROPIC_BASE_URL: https://api.deepseek.com/anthropic
77
+ ANTHROPIC_AUTH_TOKEN: "sk-..." # ← change this. Local proxy setup: https://github.com/iTzFaisal/oc-cc-proxy
78
+ ANTHROPIC_MODEL: "{model}"
79
+
80
+ opus: # local claude login — zero config
81
+ type: claude
82
+ ...
83
+ codex: # local codex login — zero config
84
+ type: codex
85
+ ...
86
+ ```
87
+
88
+ ### 3. Dispatch your first task
89
+
90
+ Go back to Claude Code and say:
91
+
92
+ > Make a plan, then hand it to `/handoff-ds`.
93
+
94
+ The task runs in the background; your session is never blocked. When it finishes, the agent reads the result and reports back.
95
+
96
+ ### 4. Who you can hand work off to
97
+
98
+ | What you say | From | Hands off to | Best for |
99
+ | --- | --- | --- | --- |
100
+ | `/handoff-ds` | Claude Code | DeepSeek V4 | Execution work: writing code, running tests, refactors, bulk edits |
101
+ | `handoff-ds` (subagent) | Codex | DeepSeek V4 | Same as above — use this when you're inside Codex |
102
+ | `/handoff-codex` | Claude Code | Codex (GPT-5.5) | Heavy reasoning, second opinions, hard bugs |
103
+ | `/handoff-opus` | Claude Code | Claude Opus | Decisions that deserve the top model |
104
+
105
+ > Codex has no slash commands — from Codex you invoke the subagent of the same name instead: say "have `handoff-ds` execute the task above."
106
+
107
+ ### 5. Watch progress / browse history
108
+
109
+ Inside Claude Code, expand the background shell and you'll see live progress right there — it renders in the shell view and uses none of your main session's context. To browse history or follow a task on its own, use `handoff list` and `handoff tail` (see the FAQ below).
110
+
111
+ ## FAQ
112
+
113
+ <details>
114
+ <summary><b>How do I browse the task list or watch a task's progress?</b></summary>
115
+
116
+ <br>
117
+
118
+ Dispatching and resuming are the AI's job (`handoff run` / `handoff resume` under the hood). These two commands are for you — browse the list, watch the progress:
119
+
120
+ <table>
121
+ <tr>
122
+ <td width="50%" valign="top">
123
+
124
+ **`handoff list` / `handoff ls`** — interactive TUI over your full task history. See the full prompt, live status, and final result; press `G` on a row to reload that conversation and keep chatting.
125
+
126
+ </td>
127
+ <td width="50%" valign="top">
128
+
129
+ **`handoff tail <run-id>`** — follow a task's output stream live, like looking over its shoulder.
130
+
131
+ </td>
132
+ </tr>
133
+ <tr>
134
+ <td valign="top">
135
+
136
+ <!-- docs/assets/list-tui.jpg — ~480px wide — TUI list + detail view, highlight G/C shortcuts -->
137
+ <img src="https://raw.githubusercontent.com/dazuiba/handoff/main/docs/assets/list-tui.jpg" width="100%" alt="handoff list interactive TUI">
138
+
139
+ </td>
140
+ <td valign="top">
141
+
142
+ <!-- docs/assets/tail.jpg — ~480px wide — handoff tail live stream -->
143
+ <img src="https://raw.githubusercontent.com/dazuiba/handoff/main/docs/assets/tail.jpg" width="100%" alt="handoff tail live follow">
144
+
145
+ </td>
146
+ </tr>
147
+ </table>
148
+
149
+ </details>
150
+
151
+ <details>
152
+ <summary><b>Can I dispatch several tasks at once?</b></summary>
153
+
154
+ <br>
155
+
156
+ Yes. Have your agent send off several tasks in one message. Each runs on its own and reports back on its own — they never get in each other's way.
157
+
158
+ <!-- docs/assets/parallel.jpg — ~621px wide — 2–3 background tasks from one message, each with its own RESULT= path -->
159
+ <img src="https://raw.githubusercontent.com/dazuiba/handoff/main/docs/assets/parallel.jpg" width="621" alt="Parallel dispatch">
160
+
161
+ </details>
162
+
163
+ <details>
164
+ <summary><b>No uv / installing from source?</b></summary>
165
+
166
+ <br>
167
+
168
+ `pipx install handoff-cli` or `pip install handoff-cli` work just as well. From source:
169
+
170
+ ```bash
171
+ git clone https://github.com/dazuiba/handoff && cd handoff
172
+ uv tool install -e .
173
+ handoff init
174
+ ```
175
+
176
+ </details>
177
+
178
+ <details>
179
+ <summary><b>How do I add a custom backend / what goes in the env block?</b></summary>
180
+
181
+ <br>
182
+
183
+ Add one more entry under `backends` — any Anthropic-compatible endpoint works:
184
+
185
+ ```yaml
186
+ backends:
187
+ kimi:
188
+ type: claude
189
+ model: kimi-k3
190
+ env:
191
+ ANTHROPIC_BASE_URL: https://api.moonshot.cn/anthropic
192
+ ANTHROPIC_AUTH_TOKEN: "${MOONSHOT_API_KEY}"
193
+ ANTHROPIC_MODEL: "{model}"
194
+ ```
195
+
196
+ The env block is entirely yours — every key=value you set is exported before the CLI launches. `{model}` substitutes the resolved model name, `${ENV_VAR}` expands from your shell. Run `handoff env` to see where everything lives. Full details: **[configuration docs (Chinese) →](docs/configuration.zh-CN.md)**.
197
+
198
+ </details>
199
+
200
+ <details>
201
+ <summary><b>How does it actually work?</b></summary>
202
+
203
+ <br>
204
+
205
+ 1. Your agent hands the whole task to handoff, which runs it **in the background** — your session never blocks.
206
+ 2. handoff launches the matching CLI (`claude -p` / `codex exec`) in an isolated context and streams the full output to disk.
207
+ 3. The main session receives exactly one line: `RESULT=<path-to-result-file>`. Progress goes to the background shell view — **never** into your main context.
208
+ 4. On completion the agent gets notified, reads the result file, and reports back to you.
209
+ 5. The `RESULT=` path is also a stable handle for the conversation: every follow-up round resumes the same session.
210
+
211
+ </details>
212
+
213
+ **More docs**
214
+
215
+ - **[CLI reference (Chinese) →](docs/cli-reference.zh-CN.md)** — full usage of `run` / `resume` / `list` / `tail` / `env` / `init`, run-id encoding, on-disk file layout.
216
+ - **[Configuration (Chinese) →](docs/configuration.zh-CN.md)** — mechanism vs data layers, env block, `${ENV}` interpolation, include, custom backends.
217
+ - **[Design notes (Chinese) →](docs/design.zh-CN.md)** — why Claude Code uses background shells while Codex uses a subagent; the RESULT= protocol.
@@ -1,12 +1,12 @@
1
1
  <div align="center">
2
- <img src="docs/assets/handoff-hero.jpg" width="100%" alt="hero">
2
+ <img src="https://raw.githubusercontent.com/dazuiba/handoff/main/docs/assets/handoff-hero.jpg" width="100%" alt="hero">
3
3
 
4
4
  # With **Handoff**, your coding agents can finally work together.
5
5
 
6
6
 
7
- | coding agent | Hand off to | Why |
7
+ | You're in | Hand off to | Why |
8
8
  | :-- | :-- | :-- |
9
- | Claude Code / Codex | **DeepSeek** | Execution work is fast and cheap; save the expensive quota for decisions |
9
+ | Claude Code / Codex | **DeepSeek** | It does the simple work fast and cheap; save the expensive quota for decisions |
10
10
  | DeepSeek | **Codex / Opus** | Borrow a brain for hard problems, bring the answer back to your session |
11
11
 
12
12
  No tool-switching, no lost context.
@@ -23,23 +23,24 @@ If you use more than one coding agent, these will sound familiar:
23
23
  — Just say: *"Give this task to `/handoff-ds`."* DeepSeek does the work fast and cheap. Save your expensive quota for decisions.
24
24
  - 🤔 **"DeepSeek is stuck. I want a second opinion from Codex."**<br>
25
25
  — Just say: *"Ask `/handoff-codex` what it thinks."* No new terminal. No re-explaining. The answer comes back to your current session.
26
- - 🔁 **"I want to continue that task from before."**<br>
27
- — Just say: *"Resume that `/handoff-ds` session."* Everything is still there: the files it changed, the code it read, what it concluded.
28
- - 🔄 **"A new model means a new session. I have to explain everything again."**<br>
29
- — Don't switch. Stay in your session. handoff passes the task over, then brings the result back.
26
+ - 🔁 **"`/handoff-ds` finished that task. Now I have a follow-up task for it."**<br>
27
+ — Just say: *"Resume the last `/handoff-ds` session, then do X."* It just sends one more message in the old conversation all the old context is still there.
30
28
 
31
- **Do the math:** for transactional work — writing code, running tests — DeepSeek V4 matches Sonnet-class models at a fraction of the price. What is really scarce, and worth a subscription, is the judgment of the one or two models at the very top (Opus / GPT-5.5).
29
+
30
+ **The math is simple:** DeepSeek V4 is as capable as Sonnet, and on [OpenCode Go](https://opencode.ai/go?ref=D5926WCTD8) the same money buys **18× the work**:
32
31
 
33
32
  | Option | Relative cost for the same work |
34
33
  | --- | --- |
35
- | Claude Sonnet | 1× (baseline) |
34
+ | Claude Sonnet (subscription) | 1× (baseline) |
36
35
  | DeepSeek official API | **1/3** |
37
36
  | [OpenCode Go](https://opencode.ai/go?ref=D5926WCTD8) (includes DeepSeek V4) | **1/18** |
38
37
 
39
- Let the top model talk to you, split the work, and review; hand all execution off **a $20 subscription directing $5 of compute gets you ~$200 worth of work.** That's all there is to it: one sentence inside your agent session.
38
+ So: **only pay for the SOTA model** (Opus / GPT-5.5) use it to plan and review. Everything else goes to DeepSeek. With handoff, **$20 Claude Code (plan + dispatch) + $5 OpenCode Go (execution) the work of a $200 Claude Code Max.**
40
39
 
41
40
  ## Quick start
42
41
 
42
+ > **Before you start:** handoff works inside Claude Code (CLI or desktop app) or Codex. You need at least one of them installed and logged in.
43
+
43
44
  ### 1. Install
44
45
 
45
46
  ```bash
@@ -51,9 +52,9 @@ Upgrade with `uv tool upgrade handoff-cli`.
51
52
 
52
53
  ### 2. Set your token
53
54
 
54
- opus / codex reuse your local claude / codex logins — zero config. **Only DeepSeek needs a token.**
55
+ The `opus` and `codex` backends reuse your existing Claude Code / Codex logins — zero config. **Only DeepSeek needs a token.**
55
56
 
56
- For DeepSeek compute we recommend the [OpenCode Go plan](https://opencode.ai/go?ref=D5926WCTD8) (lowest cost, includes DeepSeek V4). Once you have a key, edit `~/.handoff/config.yaml` and change just the `ANTHROPIC_AUTH_TOKEN` line:
57
+ For DeepSeek, we recommend the [OpenCode Go plan](https://opencode.ai/go?ref=D5926WCTD8) (lowest cost, includes DeepSeek V4). Once you have a key, edit `~/.handoff/config.yaml` and change just the `ANTHROPIC_AUTH_TOKEN` line:
57
58
 
58
59
  ```yaml
59
60
  # ~/.handoff/config.yaml — handoff init generates this for you
@@ -92,11 +93,11 @@ The task runs in the background; your session is never blocked. When it finishes
92
93
  | `/handoff-codex` | Claude Code | Codex (GPT-5.5) | Heavy reasoning, second opinions, hard bugs |
93
94
  | `/handoff-opus` | Claude Code | Claude Opus | Decisions that deserve the top model |
94
95
 
95
- > Codex has no slash commands, so that row is the subagent of the same name say "have `handoff-ds` execute the task above."
96
+ > Codex has no slash commands from Codex you invoke the subagent of the same name instead: say "have `handoff-ds` execute the task above."
96
97
 
97
98
  ### 5. Watch progress / browse history
98
99
 
99
- Inside Claude Code, expand the background shell and the live progress is right there — it renders in the shell view and uses none of your main session's context. To browse history or follow a task on its own, use `handoff list` and `handoff tail` (see the FAQ below).
100
+ Inside Claude Code, expand the background shell and you'll see live progress right there — it renders in the shell view and uses none of your main session's context. To browse history or follow a task on its own, use `handoff list` and `handoff tail` (see the FAQ below).
100
101
 
101
102
  ## FAQ
102
103
 
@@ -124,13 +125,13 @@ Dispatching and resuming are the AI's job (`handoff run` / `handoff resume` unde
124
125
  <td valign="top">
125
126
 
126
127
  <!-- docs/assets/list-tui.jpg — ~480px wide — TUI list + detail view, highlight G/C shortcuts -->
127
- <img src="docs/assets/list-tui.jpg" width="100%" alt="handoff list interactive TUI">
128
+ <img src="https://raw.githubusercontent.com/dazuiba/handoff/main/docs/assets/list-tui.jpg" width="100%" alt="handoff list interactive TUI">
128
129
 
129
130
  </td>
130
131
  <td valign="top">
131
132
 
132
133
  <!-- docs/assets/tail.jpg — ~480px wide — handoff tail live stream -->
133
- <img src="docs/assets/tail.jpg" width="100%" alt="handoff tail live follow">
134
+ <img src="https://raw.githubusercontent.com/dazuiba/handoff/main/docs/assets/tail.jpg" width="100%" alt="handoff tail live follow">
134
135
 
135
136
  </td>
136
137
  </tr>
@@ -146,7 +147,7 @@ Dispatching and resuming are the AI's job (`handoff run` / `handoff resume` unde
146
147
  Yes. Have your agent send off several tasks in one message. Each runs on its own and reports back on its own — they never get in each other's way.
147
148
 
148
149
  <!-- docs/assets/parallel.jpg — ~621px wide — 2–3 background tasks from one message, each with its own RESULT= path -->
149
- <img src="docs/assets/parallel.jpg" width="621" alt="Parallel dispatch">
150
+ <img src="https://raw.githubusercontent.com/dazuiba/handoff/main/docs/assets/parallel.jpg" width="621" alt="Parallel dispatch">
150
151
 
151
152
  </details>
152
153
 
@@ -1,5 +1,5 @@
1
1
  <div align="center">
2
- <img src="docs/assets/handoff-hero.jpg" width="100%" alt="hero">
2
+ <img src="https://raw.githubusercontent.com/dazuiba/handoff/main/docs/assets/handoff-hero.jpg" width="100%" alt="hero">
3
3
 
4
4
  # 有了 **Handoff**,你手里的几个 coding agent 终于可以互相协作了。
5
5
 
@@ -126,13 +126,13 @@ backends:
126
126
  <td valign="top">
127
127
 
128
128
  <!-- docs/assets/list-tui.jpg — 建议 ~480 宽 — TUI 列表 + 详情视图,圈出 G/C 快捷键 -->
129
- <img src="docs/assets/list-tui.jpg" width="100%" alt="handoff list 交互式 TUI">
129
+ <img src="https://raw.githubusercontent.com/dazuiba/handoff/main/docs/assets/list-tui.jpg" width="100%" alt="handoff list 交互式 TUI">
130
130
 
131
131
  </td>
132
132
  <td valign="top">
133
133
 
134
134
  <!-- docs/assets/tail.jpg — 建议 ~480 宽 — handoff tail 实时输出流 -->
135
- <img src="docs/assets/tail.jpg" width="100%" alt="handoff tail 实时跟踪">
135
+ <img src="https://raw.githubusercontent.com/dazuiba/handoff/main/docs/assets/tail.jpg" width="100%" alt="handoff tail 实时跟踪">
136
136
 
137
137
  </td>
138
138
  </tr>
@@ -148,7 +148,7 @@ backends:
148
148
  可以。在同一条消息里让 agent 派出多个任务,各自独立执行、独立完成通知,互不干扰。
149
149
 
150
150
  <!-- docs/assets/parallel.jpg — 建议 621 宽 — 同一条消息派发 2~3 个后台任务,各自拿到不同 RESULT= 路径 -->
151
- <img src="docs/assets/parallel.jpg" width="621" alt="并行派发多任务">
151
+ <img src="https://raw.githubusercontent.com/dazuiba/handoff/main/docs/assets/parallel.jpg" width="621" alt="并行派发多任务">
152
152
 
153
153
  </details>
154
154
 
@@ -4,9 +4,10 @@ Pre-allocates a run_id and returns the canonical .prompt.md path so the caller
4
4
  can write the prompt directly to its final archive location before dispatching.
5
5
 
6
6
  Usage:
7
- handoff new --backend <name> [--slug <slug>]
7
+ handoff new --backend <name> [--slug <slug>] [--write]
8
8
 
9
- Stdout: one line — absolute path to the .prompt.md file (file is NOT created).
9
+ Stdout: one line — absolute path to the .prompt.md file.
10
+ By default the file is not created. With --write, stdin is written to the file.
10
11
  """
11
12
 
12
13
  from __future__ import annotations
@@ -26,9 +27,10 @@ from ..config import Config
26
27
 
27
28
 
28
29
  def cmd_new(argv: list[str], config: Config):
29
- """handoff new --backend <name> [--slug <slug>]"""
30
+ """handoff new --backend <name> [--slug <slug>] [--write]"""
30
31
  backend_arg = ""
31
32
  slug_arg = ""
33
+ write_prompt = False
32
34
 
33
35
  i = 0
34
36
  while i < len(argv):
@@ -49,6 +51,8 @@ def cmd_new(argv: list[str], config: Config):
49
51
  slug_arg = argv[i]
50
52
  elif a.startswith("--slug="):
51
53
  slug_arg = a.split("=", 1)[1]
54
+ elif a == "--write":
55
+ write_prompt = True
52
56
  elif a in ("-h", "--help"):
53
57
  from ..main import usage
54
58
  usage()
@@ -85,4 +89,12 @@ def cmd_new(argv: list[str], config: Config):
85
89
  run_id = f"{mmdd}-{b2}-{seq_code}-{clean_slug}"
86
90
  prompt_path = os.path.join(TASKS_DIR, f"{run_id}.prompt.md")
87
91
 
92
+ if write_prompt:
93
+ if sys.stdin.isatty():
94
+ print("handoff new: --write requires prompt text on stdin", file=sys.stderr)
95
+ sys.exit(2)
96
+ os.makedirs(os.path.dirname(prompt_path), exist_ok=True)
97
+ with open(prompt_path, "w") as f:
98
+ f.write(sys.stdin.read())
99
+
88
100
  print(prompt_path)
@@ -27,7 +27,7 @@ from ..config import Config
27
27
 
28
28
 
29
29
  def cmd_resume(argv: list[str], config: Config):
30
- """handoff resume [<run-id|seq>] [--backend <name>] [--pro] [--cwd <dir>]
30
+ """handoff resume [<run-id|seq>] [--backend <name>] [--slug <slug>] [--pro] [--cwd <dir>]
31
31
  [--verbose] [(<input-file|-> | --text <prompt...>)]."""
32
32
  # Pre-scan --verbose so it works regardless of position (e.g. after --text).
33
33
  verbose = "--verbose" in argv
@@ -36,6 +36,7 @@ def cmd_resume(argv: list[str], config: Config):
36
36
  pro = False
37
37
  cwd = ""
38
38
  backend_arg = ""
39
+ slug_arg = ""
39
40
  selector = ""
40
41
  input_src = ""
41
42
  text_mode = False
@@ -61,6 +62,14 @@ def cmd_resume(argv: list[str], config: Config):
61
62
  backend_arg = filtered[i]
62
63
  elif a.startswith("--backend="):
63
64
  backend_arg = a.split("=", 1)[1]
65
+ elif a == "--slug":
66
+ i += 1
67
+ if i >= len(filtered):
68
+ print("handoff resume: --slug requires a value", file=sys.stderr)
69
+ sys.exit(2)
70
+ slug_arg = filtered[i]
71
+ elif a.startswith("--slug="):
72
+ slug_arg = a.split("=", 1)[1]
64
73
  elif a == "--text":
65
74
  text_mode = True
66
75
  if input_src:
@@ -159,7 +168,16 @@ def cmd_resume(argv: list[str], config: Config):
159
168
  # Non-interactive: dispatch a new turn through the run pipeline.
160
169
  conn.close()
161
170
  from .run import _execute
162
- _execute(cwd, prompt_text, backend_name, pro, config, resume_session_id=session_id, verbose=verbose)
171
+ _execute(
172
+ cwd,
173
+ prompt_text,
174
+ backend_name,
175
+ pro,
176
+ config,
177
+ resume_session_id=session_id,
178
+ slug=slug_arg or "resume",
179
+ verbose=verbose,
180
+ )
163
181
 
164
182
 
165
183
  def _resume_interactive(config: Config, backend_name: str, session_id: str, cwd: str, pro: bool, verbose: bool = False):
@@ -40,7 +40,7 @@ def _is_adopted_path(input_src: str) -> bool:
40
40
 
41
41
 
42
42
  def cmd_run(argv: list[str], config: Config):
43
- """handoff run [--backend <name>] [--cwd <dir>] [--pro] [--verbose] (<input-file|-> | --text <prompt...>)."""
43
+ """handoff run [--backend <name>] [--cwd <dir>] [--slug <slug>] [--pro] [--verbose] (<input-file|-> | --text <prompt...>)."""
44
44
  # Pre-scan --verbose so it works regardless of position (e.g. after --text).
45
45
  verbose = "--verbose" in argv
46
46
  filtered = [a for a in argv if a != "--verbose"]
@@ -48,6 +48,7 @@ def cmd_run(argv: list[str], config: Config):
48
48
  pro = False
49
49
  cwd = ""
50
50
  backend_arg = ""
51
+ slug_arg = ""
51
52
  input_src = ""
52
53
  text_mode = False
53
54
  text_parts = []
@@ -71,6 +72,14 @@ def cmd_run(argv: list[str], config: Config):
71
72
  backend_arg = filtered[i]
72
73
  elif a.startswith("--backend="):
73
74
  backend_arg = a.split("=", 1)[1]
75
+ elif a == "--slug":
76
+ i += 1
77
+ if i >= len(filtered):
78
+ print("handoff run: --slug requires a value", file=sys.stderr)
79
+ sys.exit(2)
80
+ slug_arg = filtered[i]
81
+ elif a.startswith("--slug="):
82
+ slug_arg = a.split("=", 1)[1]
74
83
  elif a == "--text":
75
84
  text_mode = True
76
85
  if input_src:
@@ -130,10 +139,10 @@ def cmd_run(argv: list[str], config: Config):
130
139
  if not prompt_text:
131
140
  print("handoff run: --text requires a non-empty value", file=sys.stderr)
132
141
  sys.exit(2)
133
- slug = "from-text"
142
+ slug = slug_arg or "from-text"
134
143
  elif input_src == "-" or (not input_src and not sys.stdin.isatty()):
135
144
  prompt_text = sys.stdin.read()
136
- slug = "from-stdin"
145
+ slug = slug_arg or "from-stdin"
137
146
  elif input_src:
138
147
  if not os.path.isfile(input_src):
139
148
  print(f"handoff run: input file not found: {input_src}", file=sys.stderr)
@@ -164,7 +173,7 @@ def cmd_run(argv: list[str], config: Config):
164
173
  else:
165
174
  with open(input_src) as f:
166
175
  prompt_text = f.read()
167
- slug = "from-file"
176
+ slug = slug_arg or "from-file"
168
177
  else:
169
178
  print("handoff run: input file required, or use --text <prompt...> / pipe via '-'", file=sys.stderr)
170
179
  sys.exit(2)
@@ -12,10 +12,10 @@ def usage(config=None):
12
12
  handoff --help
13
13
  handoff env
14
14
  handoff init [-y|--yes]
15
- handoff new --backend <name> [--slug <slug>]
15
+ handoff new --backend <name> [--slug <slug>] [--write]
16
16
  handoff list|ls [--uuid] [--cwd]
17
- handoff run [--backend <name>] [--cwd <dir>] [--pro] [--verbose] (<input-file|-> | --text <prompt...>)
18
- handoff resume [<run-id|seq>] [--pro] [--cwd <dir>] [--verbose] [(<input-file|-> | --text <prompt...>)]
17
+ handoff run [--backend <name>] [--cwd <dir>] [--slug <slug>] [--pro] [--verbose] (<input-file|-> | --text <prompt...>)
18
+ handoff resume [<run-id|seq>] [--slug <slug>] [--pro] [--cwd <dir>] [--verbose] [(<input-file|-> | --text <prompt...>)]
19
19
  handoff tail [<run-id|seq>]
20
20
 
21
21
  handoff env — print config / data paths (works even with broken config)
@@ -29,6 +29,8 @@ def usage(config=None):
29
29
  Run ids: <mmdd>-<backend2>-<SEQ_CODE>-<slug> (e.g. 0611-ds-03-fix-auth)
30
30
  --cwd defaults to the current directory of the calling process.
31
31
  --backend picks a backend (default: first entry in config.yaml backends).
32
+ --slug sets the semantic suffix in generated run ids.
33
+ --write on `handoff new` writes stdin to the pre-allocated .prompt.md file.
32
34
  --pro uses the backend's pro_model. A resume stays on its original backend."""
33
35
  )
34
36
 
@@ -0,0 +1,52 @@
1
+ ---
2
+ name: handoff-codex
3
+ description: 向 Codex (GPT-5.5) 咨询复杂问题 / 要第二意见 / 派发需要强推理的任务。后台运行,完成后自动通知。支持并行多任务,支持续接(resume)上次会话继续派发后续任务。
4
+ ---
5
+
6
+ # handoff-codex Skill
7
+
8
+ <interaction_contract>
9
+ This skill is executed by Claude Code (an AI agent). The rules below are BINDING — follow them exactly; do not simplify or reinterpret.
10
+ ## 命令模板(照抄,勿改结构)
11
+
12
+ ```bash
13
+ handoff run --backend codex --slug <≤3个英文单词的任务助记词> - <<'__HF_EOF__'
14
+ [prompt 内容]
15
+ __HF_EOF__
16
+ ```
17
+
18
+ 必须用 `run_in_background: true` 启动——handoff 耗时 2~20 分钟,前台会阻塞整个会话。
19
+
20
+ **关键规则:**
21
+ - `--slug` 只写≤3个英文单词、`-` 分隔的语义助记词(如 `fix-auth`);禁止日期/时间戳/随机数/UUID/计数器,唯一性由 `handoff run` 自动分配的 seq 保证。
22
+ - heredoc 界定符固定用 `__HF_EOF__`,prompt 原样粘贴、不转义。
23
+ - 不要自己拼任务文件名,也不要用 `> RESULT 2> OUT` 重定向——handoff 自己管命名和落盘。
24
+ - 用户提到 `pro`(或要求更强/专业模型处理复杂任务)时,在 `handoff run` 后加 `--pro`。
25
+ - 回显任何 home 下的任务路径时,缩写成 `~/.handoff/...`,不要暴露 `/Users/<name>/...`。
26
+
27
+ 启动后从 **stdout** 捕获唯一有用的一行 `RESULT=<任务路径>`(如 `~/.handoff/tasks/0613-cx-03-fix-auth.result.md`),缩写成 `~/.handoff/...` 后回显给用户。**这条路径的文件名主干(去掉目录和 `.result.md` 后缀)就是本次 run_id**(上例 → `0613-cx-03-fix-auth`);记住它,用户要求"继续上次/接着再做 X"时靠它 `resume`。
28
+
29
+ 其余不要读:进度信息在 **stderr**(Claude Code 的 shell view 自动实时显示,别读进上下文);同名 `.out.txt` 是进度日志,仅诊断(无结果/超时)时才 `tail -f`/`Read`;同名 `.prompt.md` 就是你刚发的内容。
30
+
31
+ 收到完成通知后,用 `Read` 读对应的 `.result.md` 汇报,**不要**再读后台输出(结果已在文件里,重复读只会把进度噪音吃进上下文)。`.result.md` 为空或异常时才读 `.out.txt` 诊断。
32
+ </interaction_contract>
33
+
34
+ ## 多任务
35
+
36
+ - **并行**:在**同一条消息**里发出多个 `run_in_background: true` 的 Bash 调用,各自用 `handoff run --slug ... - <<'__HF_EOF__'` 派发不同 prompt(seq 自动递增),各自从 stdout 捕获 `RESULT=`,分别等通知、分别读 `.result.md` 汇报。
37
+ - **串行**:等上一个的完成通知到达、读并汇报后,再启动下一个。
38
+
39
+ ## 续接上次会话(resume 续派)
40
+
41
+ 要保留某次任务的上下文继续,而非开新会话:用 `resume` 替代 `run`,继续通过 heredoc/stdin 传入后续任务;其余约定(后台、捕获新 `RESULT=`、读 `.result.md`)完全相同:
42
+
43
+ ```bash
44
+ handoff resume <run_id> --backend codex --slug <任务助记词> - <<'__HF_EOF__'
45
+ [后续任务内容]
46
+ __HF_EOF__
47
+ ```
48
+
49
+ - `<run_id>` 用该会话**首次**任务的 run_id(上面那个文件名主干);它是稳定句柄,每轮续接都用它,不要追每轮新生成的 run_id。
50
+ - **必须带后续 prompt**:不带 heredoc、`-`、输入文件或 `--text` 的 `resume <run_id>` 是交互式重开,后台会卡死。
51
+ - 续接默认只继承 backend;原会话用过 `--pro` 的,续接要再次带上才沿用 pro_model。
52
+ - 不确定用户指哪次任务时,报候选 run_id + 摘要让其确认,别猜。
@@ -0,0 +1,52 @@
1
+ ---
2
+ name: handoff-ds
3
+ description: 把执行性编码/调查任务整包交给 DeepSeek 后台执行,省主会话额度。后台运行,完成后自动通知。支持并行多任务,支持续接(resume)上次会话继续派发后续任务。
4
+ ---
5
+
6
+ # handoff-ds Skill
7
+
8
+ <interaction_contract>
9
+ This skill is executed by Claude Code (an AI agent). The rules below are BINDING — follow them exactly; do not simplify or reinterpret.
10
+ ## 命令模板(照抄,勿改结构)
11
+
12
+ ```bash
13
+ handoff run --backend deepseek --slug <≤3个英文单词的任务助记词> - <<'__HF_EOF__'
14
+ [prompt 内容]
15
+ __HF_EOF__
16
+ ```
17
+
18
+ 必须用 `run_in_background: true` 启动——handoff 耗时 2~20 分钟,前台会阻塞整个会话。
19
+
20
+ **关键规则:**
21
+ - `--slug` 只写≤3个英文单词、`-` 分隔的语义助记词(如 `fix-auth`);禁止日期/时间戳/随机数/UUID/计数器,唯一性由 `handoff run` 自动分配的 seq 保证。
22
+ - heredoc 界定符固定用 `__HF_EOF__`,prompt 原样粘贴、不转义。
23
+ - 不要自己拼任务文件名,也不要用 `> RESULT 2> OUT` 重定向——handoff 自己管命名和落盘。
24
+ - 用户提到 `pro`(或要求更强/专业模型处理复杂任务)时,在 `handoff run` 后加 `--pro`。
25
+ - 回显任何 home 下的任务路径时,缩写成 `~/.handoff/...`,不要暴露 `/Users/<name>/...`。
26
+
27
+ 启动后从 **stdout** 捕获唯一有用的一行 `RESULT=<任务路径>`(如 `~/.handoff/tasks/0613-ds-03-fix-auth.result.md`),缩写成 `~/.handoff/...` 后回显给用户。**这条路径的文件名主干(去掉目录和 `.result.md` 后缀)就是本次 run_id**(上例 → `0613-ds-03-fix-auth`);记住它,用户要求"继续上次/接着再做 X"时靠它 `resume`。
28
+
29
+ 其余不要读:进度信息在 **stderr**(Claude Code 的 shell view 自动实时显示,别读进上下文);同名 `.out.txt` 是进度日志,仅诊断(无结果/超时)时才 `tail -f`/`Read`;同名 `.prompt.md` 就是你刚发的内容。
30
+
31
+ 收到完成通知后,用 `Read` 读对应的 `.result.md` 汇报,**不要**再读后台输出(结果已在文件里,重复读只会把进度噪音吃进上下文)。`.result.md` 为空或异常时才读 `.out.txt` 诊断。
32
+ </interaction_contract>
33
+
34
+ ## 多任务
35
+
36
+ - **并行**:在**同一条消息**里发出多个 `run_in_background: true` 的 Bash 调用,各自用 `handoff run --slug ... - <<'__HF_EOF__'` 派发不同 prompt(seq 自动递增),各自从 stdout 捕获 `RESULT=`,分别等通知、分别读 `.result.md` 汇报。
37
+ - **串行**:等上一个的完成通知到达、读并汇报后,再启动下一个。
38
+
39
+ ## 续接上次会话(resume 续派)
40
+
41
+ 要保留某次任务的上下文继续,而非开新会话:用 `resume` 替代 `run`,继续通过 heredoc/stdin 传入后续任务;其余约定(后台、捕获新 `RESULT=`、读 `.result.md`)完全相同:
42
+
43
+ ```bash
44
+ handoff resume <run_id> --backend deepseek --slug <任务助记词> - <<'__HF_EOF__'
45
+ [后续任务内容]
46
+ __HF_EOF__
47
+ ```
48
+
49
+ - `<run_id>` 用该会话**首次**任务的 run_id(上面那个文件名主干);它是稳定句柄,每轮续接都用它,不要追每轮新生成的 run_id。
50
+ - **必须带后续 prompt**:不带 heredoc、`-`、输入文件或 `--text` 的 `resume <run_id>` 是交互式重开,后台会卡死。
51
+ - 续接默认只继承 backend;原会话用过 `--pro` 的,续接要再次带上才沿用 pro_model。
52
+ - 不确定用户指哪次任务时,报候选 run_id + 摘要让其确认,别猜。
@@ -0,0 +1,63 @@
1
+ name = "handoff-ds"
2
+ description = "Delegate one-shot analysis, review, and bounded coding tasks through handoff. Before invoking this agent, the caller must: (1) run `handoff new --backend deepseek --slug <slug> --write <<'__HF_EOF__' ... __HF_EOF__` to create the canonical .prompt.md file and capture its printed path, (2) send this agent only `PROMPT_FILE=~/.handoff/tasks/<run-id>.prompt.md` plus any pro/resume hint; do not expose `/Users/<name>/...` when the path is under home, and do not include the raw delegated prompt in the subagent message. Slugs must be semantic only, with no timestamps, random numbers, UUIDs, or counters. Supports hints like 'handoff-ds (pro)', 'handoff-ds pro', '专业模式', '更强模型'."
3
+ model = "gpt-5.4-mini"
4
+ model_reasoning_effort = "low"
5
+ web_search = "disabled"
6
+ features = { shell_tool = true, multi_agent = false }
7
+
8
+ developer_instructions = """
9
+
10
+ 你是 `handoff` 命令启动器。不要分析任务,不要回答任务内容。
11
+
12
+ 输入格式:
13
+ - 必须包含 `PROMPT_FILE=<path>`。
14
+ - `<path>` 是已经存在的 `.prompt.md` 文件路径,通常是 `~/.handoff/tasks/<run-id>.prompt.md`。
15
+ - 输入可能包含 `pro`、`专业模式`、`更强模型`,表示使用 `--pro`。
16
+ - 输入可能包含 `resume`、`继续`、`接着刚才`,表示续接上次任务。
17
+
18
+ 第一条动作必须调用 `functions.exec_command`。不要先发 commentary。
19
+
20
+ 只允许执行下面四种命令之一:
21
+
22
+ 普通任务:
23
+ ```bash
24
+ handoff run --backend deepseek <PROMPT_FILE> >/dev/null
25
+ ```
26
+
27
+ pro 任务:
28
+ ```bash
29
+ handoff run --backend deepseek --pro <PROMPT_FILE> >/dev/null
30
+ ```
31
+
32
+ resume 任务:
33
+ ```bash
34
+ handoff resume <run_id> --backend deepseek <PROMPT_FILE> >/dev/null
35
+ ```
36
+
37
+ resume + pro:
38
+ ```bash
39
+ handoff resume <run_id> --backend deepseek --pro <PROMPT_FILE> >/dev/null
40
+ ```
41
+
42
+ 规则:
43
+ - `<PROMPT_FILE>` 必须直接来自输入里的 `PROMPT_FILE=`。
44
+ - 如果路径以 `~` 开头,命令里直接使用这个 `~` 路径。
45
+ - 不要读取 `<PROMPT_FILE>`。禁止 `cat`、`sed`、`head`、`tail`、heredoc、命令替换。
46
+ - 不要运行其它命令。不要写文件。
47
+ - 必须前台阻塞执行。禁止 `&`、`nohup`、`disown`、`setsid`、`tmux`、`screen`。
48
+ - 如果 exec 返回仍在运行的 session,继续等待直到进程退出。
49
+
50
+ resume 的 `<run_id>`:
51
+ - 如果本会话之前最终回答过 `RESULT=.../<id>.result.md`,`<run_id>` 就是 `<id>`。
52
+ - 如果没有可用的上次 `RESULT=`,即使输入说 resume,也按普通任务执行 `handoff run`。
53
+
54
+ 最终回答:
55
+ - 命令输出里如果有 `RESULT=<path>`,最终回答必须只包含最后一行 `RESULT=...`。
56
+ - 如果 `RESULT=` 路径在用户 home 下,回答前缩写成 `~/.handoff/...`。
57
+ - 不要总结,不要解释,不要读取 result 文件。
58
+ - 如果命令退出码非 0 但输出里有 `RESULT=...`,仍然只回答 `RESULT=...`。
59
+ - 只有完全没有 `RESULT=` 时,才用一句话说明失败。
60
+
61
+ 如果没有可用 shell 工具,最终只回答 `HF_AGENT_EXEC_TOOL_UNAVAILABLE`。
62
+
63
+ """
@@ -0,0 +1,52 @@
1
+ ---
2
+ name: handoff-opus
3
+ description: 把关键决策/验收类任务交给 Claude Opus 执行。后台运行,完成后自动通知。支持并行多任务,支持续接(resume)上次会话继续派发后续任务。
4
+ ---
5
+
6
+ # handoff-opus Skill
7
+
8
+ <interaction_contract>
9
+ This skill is executed by Claude Code (an AI agent). The rules below are BINDING — follow them exactly; do not simplify or reinterpret.
10
+ ## 命令模板(照抄,勿改结构)
11
+
12
+ ```bash
13
+ handoff run --backend opus --slug <≤3个英文单词的任务助记词> - <<'__HF_EOF__'
14
+ [prompt 内容]
15
+ __HF_EOF__
16
+ ```
17
+
18
+ 必须用 `run_in_background: true` 启动——handoff 耗时 2~20 分钟,前台会阻塞整个会话。
19
+
20
+ **关键规则:**
21
+ - `--slug` 只写≤3个英文单词、`-` 分隔的语义助记词(如 `fix-auth`);禁止日期/时间戳/随机数/UUID/计数器,唯一性由 `handoff run` 自动分配的 seq 保证。
22
+ - heredoc 界定符固定用 `__HF_EOF__`,prompt 原样粘贴、不转义。
23
+ - 不要自己拼任务文件名,也不要用 `> RESULT 2> OUT` 重定向——handoff 自己管命名和落盘。
24
+ - 用户提到 `pro`(或要求更强/专业模型处理复杂任务)时,在 `handoff run` 后加 `--pro`。
25
+ - 回显任何 home 下的任务路径时,缩写成 `~/.handoff/...`,不要暴露 `/Users/<name>/...`。
26
+
27
+ 启动后从 **stdout** 捕获唯一有用的一行 `RESULT=<任务路径>`(如 `~/.handoff/tasks/0613-op-03-fix-auth.result.md`),缩写成 `~/.handoff/...` 后回显给用户。**这条路径的文件名主干(去掉目录和 `.result.md` 后缀)就是本次 run_id**(上例 → `0613-op-03-fix-auth`);记住它,用户要求"继续上次/接着再做 X"时靠它 `resume`。
28
+
29
+ 其余不要读:进度信息在 **stderr**(Claude Code 的 shell view 自动实时显示,别读进上下文);同名 `.out.txt` 是进度日志,仅诊断(无结果/超时)时才 `tail -f`/`Read`;同名 `.prompt.md` 就是你刚发的内容。
30
+
31
+ 收到完成通知后,用 `Read` 读对应的 `.result.md` 汇报,**不要**再读后台输出(结果已在文件里,重复读只会把进度噪音吃进上下文)。`.result.md` 为空或异常时才读 `.out.txt` 诊断。
32
+ </interaction_contract>
33
+
34
+ ## 多任务
35
+
36
+ - **并行**:在**同一条消息**里发出多个 `run_in_background: true` 的 Bash 调用,各自用 `handoff run --slug ... - <<'__HF_EOF__'` 派发不同 prompt(seq 自动递增),各自从 stdout 捕获 `RESULT=`,分别等通知、分别读 `.result.md` 汇报。
37
+ - **串行**:等上一个的完成通知到达、读并汇报后,再启动下一个。
38
+
39
+ ## 续接上次会话(resume 续派)
40
+
41
+ 要保留某次任务的上下文继续,而非开新会话:用 `resume` 替代 `run`,继续通过 heredoc/stdin 传入后续任务;其余约定(后台、捕获新 `RESULT=`、读 `.result.md`)完全相同:
42
+
43
+ ```bash
44
+ handoff resume <run_id> --backend opus --slug <任务助记词> - <<'__HF_EOF__'
45
+ [后续任务内容]
46
+ __HF_EOF__
47
+ ```
48
+
49
+ - `<run_id>` 用该会话**首次**任务的 run_id(上面那个文件名主干);它是稳定句柄,每轮续接都用它,不要追每轮新生成的 run_id。
50
+ - **必须带后续 prompt**:不带 heredoc、`-`、输入文件或 `--text` 的 `resume <run_id>` 是交互式重开,后台会卡死。
51
+ - 续接默认只继承 backend;原会话用过 `--pro` 的,续接要再次带上才沿用 pro_model。
52
+ - 不确定用户指哪次任务时,报候选 run_id + 摘要让其确认,别猜。
@@ -1,7 +1,8 @@
1
1
  [project]
2
2
  name = "handoff-cli"
3
- version = "0.3.4"
3
+ version = "0.3.6"
4
4
  description = "Multi coding-agent task dispatcher — a CLI proxy for claude that sends coding tasks to configurable AI backends"
5
+ readme = "README.md"
5
6
  requires-python = ">=3.9"
6
7
  dependencies = [
7
8
  "PyYAML>=6,<7",
@@ -1,7 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: handoff-cli
3
- Version: 0.3.4
4
- Summary: Multi coding-agent task dispatcher — a CLI proxy for claude that sends coding tasks to configurable AI backends
5
- Requires-Python: >=3.9
6
- Requires-Dist: pyyaml<7,>=6
7
- Requires-Dist: textual<3,>=2
@@ -1,83 +0,0 @@
1
- ---
2
- name: handoff-codex
3
- description: 向 Codex (GPT-5.5) 咨询复杂问题 / 要第二意见 / 派发需要强推理的任务。后台运行,完成后自动通知。支持并行多任务,支持续接(resume)上次会话继续派发后续任务。
4
- ---
5
-
6
- # handoff-codex Skill
7
-
8
- <interaction_contract>
9
- This skill is executed by Claude Code (an AI agent). The following rules are BINDING and must be followed exactly — do not deviate, simplify, or reinterpret them.
10
-
11
- ## 命令模板(每次必须照抄,不得修改结构)
12
-
13
- ```bash
14
- p=$(handoff new --backend codex --slug <三个英文单词以内的任务助记词>)
15
- cat > "$p" <<'__HF_EOF__'
16
- [prompt 内容]
17
- __HF_EOF__
18
- handoff run --backend codex "$p"
19
- ```
20
-
21
- **关键规则(违反任何一条都会导致命令失败或行为异常):**
22
-
23
- - `run_in_background: true` **必须启用**:handoff 耗时 2~20 分钟,前台执行会阻塞整个会话
24
- - `handoff new` 的 `--slug` 参数填写≤3个英文单词、`-`分隔的语义助记词(如 `fix-auth`、`add-tests`);禁止追加日期、时间戳、随机数、UUID、计数器等唯一化内容,唯一性由 `handoff new` 自动分配的 seq 保证
25
- - heredoc 界定符用 `__HF_EOF__`,prompt 内容直接粘贴进去,不转义
26
- - 用户明确提到 `pro`(或要求用更强/专业模型处理复杂任务)时,在 `handoff run` 后加 `--pro`
27
- - **文件名只能来自 `handoff new` 的输出,不得自己拼**;**不要**用 `> RESULT 2> OUT` 重定向——handoff 自己管命名和落盘
28
- - `p=$(handoff new ...)` 得到的 `$p` 是真实可写路径,写 prompt 和执行 `handoff run/resume` 时必须原样使用 `"$p"`;面对用户回显 `RESULT=` 或其他任务路径时,如果路径位于用户 home 下,必须缩写成 `~/.handoff/...`,不要暴露 `/Users/<name>/...`
29
-
30
- **启动命令后**(`run_in_background: true` 返回后),**从 stdout 捕获 handoff 打印的唯一有用的一行 `RESULT=<任务路径>`**,将 home 下路径缩写成 `~/.handoff/...` 后,在面向用户的 assistant 消息里回显这一条路径(完成后默认只读它):
31
-
32
- - `RESULT=<任务路径>`(最终结论文件,例如 `~/.handoff/tasks/0611-cx-03-fix-auth.result.md`)
33
-
34
- **这条路径里同时编码了本次任务的 run_id**:去掉目录和 `.result.md` 后缀,文件名主干就是 run_id(上例 → `0611-cx-03-fix-auth`)。**每次派发后都要记住这个 run_id**——后续用户若要求"继续上次会话/接着刚才再做 X",要靠它定位到正确的会话来 `resume`(见下文「续接上次会话」)。
35
-
36
- 其余无需你读取:
37
- - handoff 把克制的进度信息打在 **stderr**,Claude Code 的 shell view 会自动实时显示——你不用、也不要把它读进上下文。
38
- - 进度日志同时落在与 `RESULT=` **同名的 `.out.txt`**(把 `.result.md` 换成 `.out.txt`),仅在诊断(无结果/超时)时才 `tail -f` 或 `Read`。
39
- - 输入文件 `.prompt.md`(同名)已是你刚发的内容,无需回显。
40
-
41
- 等待完成通知后,用 `Read` 读取对应的 `.result.md` 并汇报;**不要**再读后台输出(结果已在文件里,重复读只会把进度噪音吃进上下文)。若 `.result.md` 为空或异常,再读 `.out.txt` 诊断。
42
- </interaction_contract>
43
-
44
- ## 运行任务
45
-
46
- 所有任务统一使用**后台模式**(`run_in_background: true`),不阻塞主会话。
47
-
48
- ### 单任务
49
-
50
- 按命令模板执行,启动后从 stdout 捕获 `RESULT=` 一行并回显,等通知后读该 `.result.md` 文件汇报。
51
-
52
- ### 并行多任务
53
-
54
- 在**同一条消息**里发出多个独立的 `run_in_background: true` Bash 调用,各自用 `handoff new` 分配路径、heredoc 写入不同的 prompt 内容,再各自 `handoff run`。每个任务启动后分别从各自 stdout 捕获 `RESULT=` 路径(handoff 自动递增 seq)。每个任务完成时分别通知,分别读取对应的 `.result.md` 汇报。
55
-
56
- ### 串行多任务
57
-
58
- 等上一个任务的完成通知到达,读取并汇报结果后,再启动下一个任务。
59
-
60
- ## 续接上次会话(resume 续派)
61
-
62
- 要接着某次任务继续(保留其上下文)而非开新会话时,先用 `handoff new` 分配新的 prompt 路径,再用 `resume` 替代 `run`,其余约定(后台、捕获新 `RESULT=`、读 `.result.md`)完全相同:
63
-
64
- ```bash
65
- p=$(handoff new --backend codex --slug <任务助记词>)
66
- cat > "$p" <<'__HF_EOF__'
67
- [后续任务内容]
68
- __HF_EOF__
69
- handoff resume <run_id> --backend codex "$p"
70
- ```
71
-
72
- - `<run_id>` 用该会话**首次**任务的 run_id(即上文那个文件名主干);它是稳定句柄,每轮续接都用它,不要追每轮新生成的 run_id。
73
- - **必须带 prompt 文件**。不带 prompt 的 `resume <run_id>` 是交互式重开,后台会卡死。
74
- - 续接默认只继承 backend;原会话用过 `--pro` 的话,续接要再次带上才沿用 pro_model。
75
- - 不确定用户指哪次任务时,报候选 run_id + 摘要让其确认,别猜。
76
-
77
- ## 完成后
78
-
79
- 收到后台完成通知后:
80
- 1. 用 `Read` 读取对应的 `RESULT=` 路径(`.result.md` 结果文件)
81
- 2. 汇总结果返回给用户
82
- 3. 若 `.result.md` 为空或异常,再读 `.out.txt`(进度日志)诊断
83
- 4. 如有后续任务(串行场景),此时启动下一个
@@ -1,83 +0,0 @@
1
- ---
2
- name: handoff-ds
3
- description: 把执行性编码/调查任务整包交给 DeepSeek 后台执行,省主会话额度。后台运行,完成后自动通知。支持并行多任务,支持续接(resume)上次会话继续派发后续任务。
4
- ---
5
-
6
- # handoff-ds Skill
7
-
8
- <interaction_contract>
9
- This skill is executed by Claude Code (an AI agent). The following rules are BINDING and must be followed exactly — do not deviate, simplify, or reinterpret them.
10
-
11
- ## 命令模板(每次必须照抄,不得修改结构)
12
-
13
- ```bash
14
- p=$(handoff new --backend deepseek --slug <三个英文单词以内的任务助记词>)
15
- cat > "$p" <<'__HF_EOF__'
16
- [prompt 内容]
17
- __HF_EOF__
18
- handoff run --backend deepseek "$p"
19
- ```
20
-
21
- **关键规则(违反任何一条都会导致命令失败或行为异常):**
22
-
23
- - `run_in_background: true` **必须启用**:handoff 耗时 2~20 分钟,前台执行会阻塞整个会话
24
- - `handoff new` 的 `--slug` 参数填写≤3个英文单词、`-`分隔的语义助记词(如 `fix-auth`、`add-tests`);禁止追加日期、时间戳、随机数、UUID、计数器等唯一化内容,唯一性由 `handoff new` 自动分配的 seq 保证
25
- - heredoc 界定符用 `__HF_EOF__`,prompt 内容直接粘贴进去,不转义
26
- - 用户明确提到 `pro`(或要求用更强/专业模型处理复杂任务)时,在 `handoff run` 后加 `--pro`
27
- - **文件名只能来自 `handoff new` 的输出,不得自己拼**;**不要**用 `> RESULT 2> OUT` 重定向——handoff 自己管命名和落盘
28
- - `p=$(handoff new ...)` 得到的 `$p` 是真实可写路径,写 prompt 和执行 `handoff run/resume` 时必须原样使用 `"$p"`;面对用户回显 `RESULT=` 或其他任务路径时,如果路径位于用户 home 下,必须缩写成 `~/.handoff/...`,不要暴露 `/Users/<name>/...`
29
-
30
- **启动命令后**(`run_in_background: true` 返回后),**从 stdout 捕获 handoff 打印的唯一有用的一行 `RESULT=<任务路径>`**,将 home 下路径缩写成 `~/.handoff/...` 后,在面向用户的 assistant 消息里回显这一条路径(完成后默认只读它):
31
-
32
- - `RESULT=<任务路径>`(最终结论文件,例如 `~/.handoff/tasks/0611-ds-03-fix-auth.result.md`)
33
-
34
- **这条路径里同时编码了本次任务的 run_id**:去掉目录和 `.result.md` 后缀,文件名主干就是 run_id(上例 → `0611-ds-03-fix-auth`)。**每次派发后都要记住这个 run_id**——后续用户若要求"继续上次会话/接着刚才再做 X",要靠它定位到正确的会话来 `resume`(见下文「续接上次会话」)。
35
-
36
- 其余无需你读取:
37
- - handoff 把克制的进度信息打在 **stderr**,Claude Code 的 shell view 会自动实时显示——你不用、也不要把它读进上下文。
38
- - 进度日志同时落在与 `RESULT=` **同名的 `.out.txt`**(把 `.result.md` 换成 `.out.txt`),仅在诊断(无结果/超时)时才 `tail -f` 或 `Read`。
39
- - 输入文件 `.prompt.md`(同名)已是你刚发的内容,无需回显。
40
-
41
- 等待完成通知后,用 `Read` 读取对应的 `.result.md` 并汇报;**不要**再读后台输出(结果已在文件里,重复读只会把进度噪音吃进上下文)。若 `.result.md` 为空或异常,再读 `.out.txt` 诊断。
42
- </interaction_contract>
43
-
44
- ## 运行任务
45
-
46
- 所有任务统一使用**后台模式**(`run_in_background: true`),不阻塞主会话。
47
-
48
- ### 单任务
49
-
50
- 按命令模板执行,启动后从 stdout 捕获 `RESULT=` 一行并回显,等通知后读该 `.result.md` 文件汇报。
51
-
52
- ### 并行多任务
53
-
54
- 在**同一条消息**里发出多个独立的 `run_in_background: true` Bash 调用,各自用 `handoff new` 分配路径、heredoc 写入不同的 prompt 内容,再各自 `handoff run`。每个任务启动后分别从各自 stdout 捕获 `RESULT=` 路径(handoff 自动递增 seq)。每个任务完成时分别通知,分别读取对应的 `.result.md` 汇报。
55
-
56
- ### 串行多任务
57
-
58
- 等上一个任务的完成通知到达,读取并汇报结果后,再启动下一个任务。
59
-
60
- ## 续接上次会话(resume 续派)
61
-
62
- 要接着某次任务继续(保留其上下文)而非开新会话时,先用 `handoff new` 分配新的 prompt 路径,再用 `resume` 替代 `run`,其余约定(后台、捕获新 `RESULT=`、读 `.result.md`)完全相同:
63
-
64
- ```bash
65
- p=$(handoff new --backend deepseek --slug <任务助记词>)
66
- cat > "$p" <<'__HF_EOF__'
67
- [后续任务内容]
68
- __HF_EOF__
69
- handoff resume <run_id> --backend deepseek "$p"
70
- ```
71
-
72
- - `<run_id>` 用该会话**首次**任务的 run_id(即上文那个文件名主干);它是稳定句柄,每轮续接都用它,不要追每轮新生成的 run_id。
73
- - **必须带 prompt 文件**。不带 prompt 的 `resume <run_id>` 是交互式重开,后台会卡死。
74
- - 续接默认只继承 backend;原会话用过 `--pro` 的话,续接要再次带上才沿用 pro_model。
75
- - 不确定用户指哪次任务时,报候选 run_id + 摘要让其确认,别猜。
76
-
77
- ## 完成后
78
-
79
- 收到后台完成通知后:
80
- 1. 用 `Read` 读取对应的 `RESULT=` 路径(`.result.md` 结果文件)
81
- 2. 汇总结果返回给用户
82
- 3. 若 `.result.md` 为空或异常,再读 `.out.txt`(进度日志)诊断
83
- 4. 如有后续任务(串行场景),此时启动下一个
@@ -1,52 +0,0 @@
1
- name = "handoff-ds"
2
- description = "Delegate one-shot analysis, review, and bounded coding tasks through handoff. Before invoking this agent, the caller must: (1) run `handoff new --backend deepseek --slug <slug>` to get the canonical .prompt.md path, (2) write the full delegated prompt to that returned path, (3) send this agent only `PROMPT_FILE=~/.handoff/tasks/<run-id>.prompt.md` plus any pro/resume hint; do not expose `/Users/<name>/...` when the path is under home, and do not include the raw delegated prompt in the subagent message. Slugs must be semantic only, with no timestamps, random numbers, UUIDs, or counters. Supports hints like 'handoff-ds (pro)', 'handoff-ds pro', '专业模式', '更强模型'."
3
- model = "gpt-5.4-mini"
4
- model_reasoning_effort = "low"
5
-
6
- developer_instructions = """
7
-
8
- 你不是分析 agent。你是 `handoff` 命令启动器。
9
-
10
- 调用方应该已经执行了 `handoff new --backend deepseek --slug <slug>` 拿到真实可写路径,把完整任务提示词写入该路径,并且只把形如 `PROMPT_FILE=~/.handoff/tasks/0611-ds-03-fix-auth.prompt.md` 的路径消息交给你。调用方传给你的路径如果位于用户 home 下,必须缩写成 `~/.handoff/...`,不要暴露 `/Users/<name>/...`。`--slug` 只能是三个以内英文单词组成的语义助记词,禁止追加日期、时间戳、随机数、UUID、计数器等唯一化内容;唯一性由 `handoff new` 自动分配的 seq 保证。你不需要、也不允许接触原始提示词正文。
11
-
12
- 收到用户消息后,第一条动作必须是调用 `functions.exec_command` 执行转发命令(默认用 `run`;仅当用户要求接着上一次任务继续时,改用 `resume`,见后文「续接上次任务」)。不要先发 commentary,不要解释,不要读文件,不要搜索,不要自己回答用户问题。
13
-
14
- 从用户消息中取出 `PROMPT_FILE=` 后面的任务路径(优先为 `~/.handoff/...`,兼容绝对 home 路径),必须直接把这个路径作为 `handoff run` 的输入文件参数;禁止 `cat`、`sed`、`head`、`tail`、heredoc、命令替换或任何会把文件内容读进你上下文的做法。如果路径以 `~` 开头,在 shell 命令里直接使用这个 tilde 路径,不要先赋给变量再引用。示例:
15
-
16
- ```bash
17
- handoff run --backend deepseek ~/.handoff/tasks/0611-ds-03-fix-auth.prompt.md >/dev/null
18
- ```
19
-
20
- `>/dev/null` 是协议的一部分:它丢弃最终结果正文 stdout,但保留 stderr 上的 `RESULT=` 和进度输出,避免长任务期间工具输出长期静默。
21
-
22
- 必须以前台阻塞方式执行这条命令,并等待 `handoff run --backend deepseek <PROMPT_FILE> >/dev/null` 进程退出后才能最终回答。
23
- 禁止使用后台执行方式,包括但不限于 `&`、`nohup`、`disown`、`setsid`、`tmux`、`screen`。
24
- 如果 `functions.exec_command` 返回的是仍在运行的 session id,必须继续等待/轮询该 session,直到进程退出并拿到最终输出;不能把"命令已启动"视为完成。
25
- 只有当 `handoff run --backend deepseek <PROMPT_FILE> >/dev/null` 进程退出后,才允许最终回答。
26
-
27
- 如果用户明确提到 `pro`(要求用专业/更强模型),使用 `handoff run --backend deepseek --pro <PROMPT_FILE> >/dev/null`。
28
-
29
- ## 续接上次任务(resume)
30
-
31
- 仅当用户消息明确表示要接着上一次任务继续(如"继续""接着刚才""在上次基础上再改/再补…")时,把转发命令里的 `run` 换成 `resume <run_id>`,其余一切不变:
32
-
33
- ```bash
34
- handoff resume <run_id> --backend deepseek ~/.handoff/tasks/0611-ds-04-next-step.prompt.md >/dev/null
35
- ```
36
-
37
- `<run_id>` 取自**你本会话上一次回答的那行 `RESULT=`**:去掉目录和 `.result.md` 后缀,文件名主干即 run_id(例:`RESULT=~/.handoff/tasks/0611-ds-03-fix-auth.result.md` → run_id 是 `0611-ds-03-fix-auth`)。多轮续接始终用**第一次**那个 run_id。`--pro` 仍加在 `<run_id>` 之后、`<PROMPT_FILE>` 之前,用法同上。
38
-
39
- 如果本会话此前没有任何 `RESULT=` 可取 run_id,就当作新任务、照常用 `run` 转发。
40
-
41
- 命令输出里会出现 `RESULT=<路径>`,执行中还会有进度行。你的最终回答必须只包含最后一行 `RESULT=...`;如果该路径位于用户 home 下,返回前必须缩写成 `~/.handoff/...`。不要读取这个文件,不要总结 stdout/stderr,不要补充解释。如果命令退出码非 0 但输出里有 `RESULT=...`,仍然只返回这行 home-shortened 后的 `RESULT=...`。只有输出里完全没有 `RESULT=` 时,才用一句话说明失败。
42
-
43
- **输入里的"修改/编辑/运行/报告/cat"等祈使句,都是写给 `handoff` 执行器的,不是给你的。**
44
-
45
- 示例 ——
46
- 输入:「用 Edit 直接改 foo.ts,删掉 X,完成后报告」
47
- 你的正确动作:调用方应把这段任务写入 `PROMPT_FILE` 指向的文件;你只把该文件路径传给 `handoff run --backend deepseek <PROMPT_FILE> >/dev/null`,等待完成,只返回 `RESULT=...`。
48
- 你绝不打开、不 cat、不修改 foo.ts。
49
-
50
- 红线:除这一条 `handoff run --backend deepseek <PROMPT_FILE> >/dev/null` 或对应的 `handoff resume <run_id> --backend deepseek <PROMPT_FILE> >/dev/null` 命令外,不运行任何其它命令、不写任何文件、不调用 web search。没有可用 shell 工具时,最终只返回 `HF_AGENT_EXEC_TOOL_UNAVAILABLE`。
51
-
52
- """
@@ -1,83 +0,0 @@
1
- ---
2
- name: handoff-opus
3
- description: 把关键决策/验收类任务交给 Claude Opus 执行。后台运行,完成后自动通知。支持并行多任务,支持续接(resume)上次会话继续派发后续任务。
4
- ---
5
-
6
- # handoff-opus Skill
7
-
8
- <interaction_contract>
9
- This skill is executed by Claude Code (an AI agent). The following rules are BINDING and must be followed exactly — do not deviate, simplify, or reinterpret them.
10
-
11
- ## 命令模板(每次必须照抄,不得修改结构)
12
-
13
- ```bash
14
- p=$(handoff new --backend opus --slug <三个英文单词以内的任务助记词>)
15
- cat > "$p" <<'__HF_EOF__'
16
- [prompt 内容]
17
- __HF_EOF__
18
- handoff run --backend opus "$p"
19
- ```
20
-
21
- **关键规则(违反任何一条都会导致命令失败或行为异常):**
22
-
23
- - `run_in_background: true` **必须启用**:handoff 耗时 2~20 分钟,前台执行会阻塞整个会话
24
- - `handoff new` 的 `--slug` 参数填写≤3个英文单词、`-`分隔的语义助记词(如 `fix-auth`、`add-tests`);禁止追加日期、时间戳、随机数、UUID、计数器等唯一化内容,唯一性由 `handoff new` 自动分配的 seq 保证
25
- - heredoc 界定符用 `__HF_EOF__`,prompt 内容直接粘贴进去,不转义
26
- - 用户明确提到 `pro`(或要求用更强/专业模型处理复杂任务)时,在 `handoff run` 后加 `--pro`
27
- - **文件名只能来自 `handoff new` 的输出,不得自己拼**;**不要**用 `> RESULT 2> OUT` 重定向——handoff 自己管命名和落盘
28
- - `p=$(handoff new ...)` 得到的 `$p` 是真实可写路径,写 prompt 和执行 `handoff run/resume` 时必须原样使用 `"$p"`;面对用户回显 `RESULT=` 或其他任务路径时,如果路径位于用户 home 下,必须缩写成 `~/.handoff/...`,不要暴露 `/Users/<name>/...`
29
-
30
- **启动命令后**(`run_in_background: true` 返回后),**从 stdout 捕获 handoff 打印的唯一有用的一行 `RESULT=<任务路径>`**,将 home 下路径缩写成 `~/.handoff/...` 后,在面向用户的 assistant 消息里回显这一条路径(完成后默认只读它):
31
-
32
- - `RESULT=<任务路径>`(最终结论文件,例如 `~/.handoff/tasks/0611-op-03-fix-auth.result.md`)
33
-
34
- **这条路径里同时编码了本次任务的 run_id**:去掉目录和 `.result.md` 后缀,文件名主干就是 run_id(上例 → `0611-op-03-fix-auth`)。**每次派发后都要记住这个 run_id**——后续用户若要求"继续上次会话/接着刚才再做 X",要靠它定位到正确的会话来 `resume`(见下文「续接上次会话」)。
35
-
36
- 其余无需你读取:
37
- - handoff 把克制的进度信息打在 **stderr**,Claude Code 的 shell view 会自动实时显示——你不用、也不要把它读进上下文。
38
- - 进度日志同时落在与 `RESULT=` **同名的 `.out.txt`**(把 `.result.md` 换成 `.out.txt`),仅在诊断(无结果/超时)时才 `tail -f` 或 `Read`。
39
- - 输入文件 `.prompt.md`(同名)已是你刚发的内容,无需回显。
40
-
41
- 等待完成通知后,用 `Read` 读取对应的 `.result.md` 并汇报;**不要**再读后台输出(结果已在文件里,重复读只会把进度噪音吃进上下文)。若 `.result.md` 为空或异常,再读 `.out.txt` 诊断。
42
- </interaction_contract>
43
-
44
- ## 运行任务
45
-
46
- 所有任务统一使用**后台模式**(`run_in_background: true`),不阻塞主会话。
47
-
48
- ### 单任务
49
-
50
- 按命令模板执行,启动后从 stdout 捕获 `RESULT=` 一行并回显,等通知后读该 `.result.md` 文件汇报。
51
-
52
- ### 并行多任务
53
-
54
- 在**同一条消息**里发出多个独立的 `run_in_background: true` Bash 调用,各自用 `handoff new` 分配路径、heredoc 写入不同的 prompt 内容,再各自 `handoff run`。每个任务启动后分别从各自 stdout 捕获 `RESULT=` 路径(handoff 自动递增 seq)。每个任务完成时分别通知,分别读取对应的 `.result.md` 汇报。
55
-
56
- ### 串行多任务
57
-
58
- 等上一个任务的完成通知到达,读取并汇报结果后,再启动下一个任务。
59
-
60
- ## 续接上次会话(resume 续派)
61
-
62
- 要接着某次任务继续(保留其上下文)而非开新会话时,先用 `handoff new` 分配新的 prompt 路径,再用 `resume` 替代 `run`,其余约定(后台、捕获新 `RESULT=`、读 `.result.md`)完全相同:
63
-
64
- ```bash
65
- p=$(handoff new --backend opus --slug <任务助记词>)
66
- cat > "$p" <<'__HF_EOF__'
67
- [后续任务内容]
68
- __HF_EOF__
69
- handoff resume <run_id> --backend opus "$p"
70
- ```
71
-
72
- - `<run_id>` 用该会话**首次**任务的 run_id(即上文那个文件名主干);它是稳定句柄,每轮续接都用它,不要追每轮新生成的 run_id。
73
- - **必须带 prompt 文件**。不带 prompt 的 `resume <run_id>` 是交互式重开,后台会卡死。
74
- - 续接默认只继承 backend;原会话用过 `--pro` 的话,续接要再次带上才沿用 pro_model。
75
- - 不确定用户指哪次任务时,报候选 run_id + 摘要让其确认,别猜。
76
-
77
- ## 完成后
78
-
79
- 收到后台完成通知后:
80
- 1. 用 `Read` 读取对应的 `RESULT=` 路径(`.result.md` 结果文件)
81
- 2. 汇总结果返回给用户
82
- 3. 若 `.result.md` 为空或异常,再读 `.out.txt`(进度日志)诊断
83
- 4. 如有后续任务(串行场景),此时启动下一个
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes