Files
jiantw83 03e59c69bd feat(狀態回報): 收尾寫一筆 skill-end 事件
現行紀錄只記「被叫用」,沒有成敗也沒有結束碼。跑完整輪的技能與開場就
中止的技能,在紀錄裡長得一模一樣。

start 由技能用量 hook 順手發,不必改技能文件。end 只能由技能自己在收尾
步驟寫——hook 接在技能工具呼叫上,而實際工作發生在之後的模型輪次,它在
原理上看不到成敗。有 start 沒有配對的 end,就是那一輪中止了。

status 五選一,每支技能各自寫明什麼情況選哪一個。找不到回報腳本就安靜
跳過,回報失敗一律不改變技能自己的結論。
2026-09-02 16:01:14 +08:00

122 lines
10 KiB
Markdown

---
name: delegate
description: Delegate a single bounded task to another installed AI agent CLI as a subagent, with an explicit target CLI, required capability tags, or a forced model. Use when another CLI should do the work and return structured results; not for model inventory, plugin deployment, or tasks that must stay in the current agent.
---
# delegate — hand a task to another CLI
Use this skill when the work should move to another installed AI agent CLI instead of staying in the current agent.
Detection and tag filtering are decided in code, not in prose: `jsc-cli/tools/detect-clis.sh` owns the CLI list and `jsc-cli/tools/model-tags.sh` owns the capability table. This skill only says when to call them and what each exit code means.
## Steps
1. Bound the goal. Write one sentence for the goal, and a numbered list of acceptance criteria that a reader can mark pass or fail without asking a follow-up question.
Two or more independent goals → split them and run this skill once per goal. A goal is not bounded enough when it has no acceptance criterion that can be checked from the subagent's returned text alone.
Done when exactly one goal sentence and at least one pass-or-fail acceptance criterion are written down.
2. Collect the CLI list and the model list. They read different files and share no state, so **start both at once and wait for both**. Step 3 has no other source for a model id: without the second script there is nothing to hand to `model-tags.sh gate`.
`jsc-cli/tools/detect-clis.sh` prints `{name}<TAB>{path}<TAB>{version}` per installed CLI.
| Result | Action |
| --- | --- |
| exit 0, at least one row | Take the candidate CLIs from those rows only |
| exit 0, no row | Stop. Report that no AI agent CLI is installed, and name the five it probes: claude, codex, copilot, antigravity, kiro |
| any non-zero exit | Stop. Report the exit code and the stderr text. Never fall back to a guessed CLI list |
`jsc-cli/tools/list-models.sh` prints `{cli}<TAB>{model}<TAB>{in-use}` per model, read from each CLI's own config, and always exits 0. It stays silent for a CLI whose config it cannot read, so a CLI with no row is a CLI with no known model, not a failure.
| Result | Action |
| --- | --- |
| exit 0 | Take the candidate models for the target CLI from that CLI's own rows, the `in-use` row first |
| any non-zero exit | The script itself failed. Stop and report the exit code and the stderr text; never invent a model id |
Done when the candidate CLI list comes from the first TSV and holds at least one name, the model rows from the second are in hand, or the skill has stopped with the reason.
3. Pick the target CLI, then resolve its model against the requirement. Both read step 2's two TSVs and nothing else, so they belong to one pass over that data.
**Pick the target CLI first.** The user naming a CLI keeps only that CLI; a name absent from step 2's TSV stops the skill with the message that the CLI is not installed on this machine. No name from the user → keep every detected CLI as a candidate, and settle on one before any model is checked: the candidate model list is defined by the target CLI.
**Then resolve the model.** Never let a model judge its own tags — the verdict comes from the script's exit code.
The candidate models are the step 2 `list-models.sh` rows whose first column is the chosen target CLI, checked one at a time in that order, `in-use` first. A user-forced model replaces that list with itself alone. No row for the target CLI and no forced model → stop, and say the fix is to record a model for that CLI through `/jsc-cli:models`; a guessed model id would be gated against a table entry that has nothing to do with the CLI that will actually run the task.
Requirement stated as an SDLC stage → run `jsc-cli/tools/model-tags.sh gate {stage} {model-id}` per candidate. Exit 2 covers three different faults, so read the output line to tell them apart:
| Exit | Output | Action |
| --- | --- | --- |
| 0 | `PASS` | Keep the model and stop checking further candidates |
| 1 | `FAIL:{tags}` | Drop that model, name the missing tags in the report, and move to the next candidate |
| 2 | `UNKNOWN-MODEL` | Drop that model and move to the next candidate. Do not guess its tags. Say the fix is to add it through `/jsc-cli:models` |
| 2 | `UNKNOWN-STAGE` | Stop. The stage name is wrong; name the four valid ones: plan, analyze, implement, maintain |
| 2 | usage text on stderr, no verdict line | Stop. The call itself is malformed — report it as a defect in this skill, and never read it as a model that failed the requirement |
| other | — | Stop. Report the exit code and the stderr text |
Every candidate exhausted without a `PASS` → stop, and report each candidate with its own verdict.
Requirement stated as raw capability tags → run `jsc-cli/tools/model-tags.sh model {model-id}`, which always exits 0 and prints the model's tags, or prints nothing when the model is not on the table. Keep the model only when its tags cover every required tag. Empty output gets the same treatment as `UNKNOWN-MODEL` above.
A forced model goes through the same check. Failing it stops the delegation rather than downgrading the requirement.
Done when exactly one target CLI is chosen and one model id from that CLI has cleared the requirement — a `PASS` from `gate`, or tags covering every required tag — or the skill has stopped with the reason.
4. Build the subagent prompt. It carries the goal, the acceptance criteria from step 1, the target CLI, the model id, the minimum context needed, the write scope, and the output contract below.
The write scope is a list of paths the subagent may write, one per line, each an absolute path or a path relative to the current working directory. An empty list means read-only, and the prompt says so in those words. Anything outside the list is out of scope, including temporary files.
The output contract is a TSV block, and the prompt states it verbatim:
| Line | Meaning |
| --- | --- |
| `result<TAB>{ok\|fail\|needs-input}` | Exactly one, and the last line |
| `summary<TAB>{one line}` | Exactly one |
| `criterion<TAB>{n}<TAB>{pass\|fail}<TAB>{evidence}` | One per acceptance criterion from step 1 |
| `wrote<TAB>{path}` | One per written path, zero when read-only |
| `error<TAB>{message}` | One per failure, zero on success |
Done when the prompt holds all seven parts and the write scope is either a path list or the read-only sentence.
5. Spawn one subagent for the goal, targeted at the CLI and the model from step 3. One goal, one subagent. Done when the subagent has returned and its exit status has been captured.
6. Verify the returned result before reporting it. Every check below must pass:
| Check | Failure handling |
| --- | --- |
| Exit status is 0 | Non-zero → record a failure carrying the target CLI, the exit code and the stderr text |
| Exit status was obtained at all | Not obtainable → treat it as a failure, exactly as a non-zero exit |
| Output holds one `result` line and one `summary` line | Missing either → record a failure reading 回傳格式不符 |
| One `criterion` line per acceptance criterion, all `pass` | Any `fail` or missing line → report the outcome as failure, naming the criteria |
| Every `wrote` path is inside the step 4 write scope | Any path outside → report it as an out-of-scope write and name the path |
Done when every row above has a verdict, and a failing row has produced a recorded failure.
7. Report the verified result: the target CLI, the model id, the capability requirement and how it was met, and the outcome. Keep success, failure and 需要使用者補充 in three separate sections, so a partial result is never read as a finished one.
Done when the CLI, the model, the requirement and the per-criterion verdicts are all in the report, and every failure recorded in step 6 appears in the failure section.
8. **Record how the run ended.** This is the last thing this skill does, and it runs on every path out of the skill, the ones that stop at step 1 or step 3 included. Call
`jsc-hooks/tools/report-status.sh skill-end jsc-cli:delegate {status} {exit code} [detail]`
`{exit code}` is the exit code of whatever decided the outcome — the subagent's own status, or the `detect-clis.sh` or `model-tags.sh` call that ruled the run — and `0` when nothing failed. `{detail}` is one short line, no more than 200 characters: the target CLI and the model id fit there, the subagent's output does not. **If the script is not on this machine, skip this step in silence and finish the run as it stood** — missing infrastructure is not a failure, and a reporting call may never change what this skill returns or reports.
| status | When this skill uses it |
| --- | --- |
| `ok` | The subagent exited 0, its output held one `result` and one `summary` line, every acceptance criterion came back `pass`, and every `wrote` path sat inside the write scope |
| `blocked` | The capability gate stopped the delegation before it started, so no subagent ran: every candidate model returned `FAIL` or `UNKNOWN-MODEL` from `model-tags.sh gate`, `detect-clis.sh` exited 0 with no row, the CLI the user named is not on this machine, or the target CLI has no model row and no forced model |
| `failed` | The delegation ran and broke: the subagent exited non-zero, its exit status could not be obtained at all, its output was missing the `result` or `summary` line, a criterion came back `fail`, or a `wrote` path landed outside the write scope. `detect-clis.sh` or `list-models.sh` exiting non-zero sits here too |
| `degraded` | The subagent returned `result needs-input`: part of the goal is done and the rest waits on the user, so the report has a 需要使用者補充 section that is not empty. The delegation happened, the goal did not close |
| `aborted` | The premise did not hold, so the skill stopped on its own: step 1 could give the goal no pass-or-fail acceptance criterion, or the request carried two or more independent goals and has to be split. Also used when the user stops the run before step 5 spawns the subagent |
Done when exactly one `skill-end` line was recorded for this run, or the script was absent and the run finished without it.
## Do not use this skill
- Do not use it for model inventory. That is `/jsc-cli:models`.
- Do not use it for plugin deployment. That is `/jsc-cli:deploy`.
- Do not use it for tasks that must stay inside the current agent.
- Do not use it for a goal that step 1 could not give a pass-or-fail acceptance criterion.