cursor:do — Single-Task Write Delegate to Cursor Composer
1 件の実装タスクを Cursor Composer (composer-2.5-fast) に専用 worktree 内で委譲し、Lead が diff をレビューしてから main へ cherry-pick する skill。breezing の team フローを起こさず、1 タスク 1 cherry-pick を最短経路で回す。
封じ込めは Cursor 側にはない (references/cursor-cli-only.md)。専用 .git を持つ worktree + Lead diff review + cherry-pick (R01-R13 経路) の 3 点だけが実効的な境界。cursor の出力は Lead レビューまで untrusted として扱う。
Step 0 — NARRATION RULES (UX Contract)
敵は 冗長さ であって進捗報告ではない。breezing と同じ契約。起動時に banner + 実行計画を簡潔に明示してから実行する。見やすい進捗報告は歓迎、冗長な繰り返しのみ禁止。
起動時に必ず出すもの (banner + plan、合計 5 行以内)
🚀 cursor / composer-2.5-fast / feat/foo-bar / Add login form validation これから: 1. pre-check (branch / cursor-agent) → 専用 worktree 作成 2. composer に実装委譲 (--write) 3. diff レビュー → cherry-pick → Plans.md 更新
banner 1 行 (🚀 cursor / composer-2.5-fast / <branch> / <task>) + 計画 2-4 行。1 秒以内に出し、即 Step 1 へ。
進捗報告は出してよい (見やすい範囲で)
- 各ステップの開始・完了を 1 行ステータスで (
✓ worktree 作成: .claude/worktrees/cursor-do-...) - pre-check / resolve の要点、cherry-pick した SHA
- なぜこの分岐を取るかの理由を 1 行で
禁止 (= 冗長さ)
- 同じ事実の 2 回言い換え: pre-check 結果を後段で再説明しない
- 中身のない前置き: tool call で自明な宣言だけの行
- 3 行以上の経緯振り返り: 必要なら 1 行に圧縮
- 起動シーケンス中の ★ Insight ブロック: Insight は最終 report で 1 回のみ
違反例 (冗長):
× 「composer 2.5 で実装する流れですね、まず確認します」(中身のない前置き) × 「Cursor を呼ぶ前に branch を見ます」 → bash → 「branch を確認しました」(言い換え) × ★ Insight ──── Cursor の強みは…
正常例 (簡潔 + 計画明示):
🚀 cursor / composer-2.5-fast / feat/foo-bar / Add login form validation これから: worktree 作成 → composer に実装委譲 → diff レビュー → cherry-pick
Step 1 — banner + plan を出し切る (1 秒以内)
引数 $ARGUMENTS をタスク説明として受ける。空なら直前の明示依頼と選択済み Plans.md を読み、対象を回収する。
それでも対象を特定できない場合だけ、以下のマーカーを出力してユーザーに 1 行タスクを要求し、入力後に Step 2 へ進む:
CURSOR_DO_AWAITING_TASK: provide a one-line task description as $ARGUMENTS
引数があれば、即 1 行 echo:
🚀 cursor / composer-2.5-fast / <current-branch> / <task-first-60-chars>
<current-branch> は Step 2 で取得する値だが、Step 1 では未取得のため … でも可。Step 2 直後に確定値を 1 行で再出力する。Step 0 の banner + 実行計画 (5 行以内) はここで出し切り、以降は各ステップの 1 行ステータスで進捗を見せる。冗長な繰り返しのみ避ける。
Step 2 — 並列 pre-check (1 bash)
1 つの bash 呼び出しで以下を並列に取り、結果だけを 1 ブロックで受ける。個別の説明は出さない。
bashbash -c ' set +e echo "==BRANCH=="; git branch --show-current echo "==VERSION=="; cat VERSION 2>/dev/null echo "==PLANS_TAIL=="; tail -n 12 Plans.md 2>/dev/null echo "==CURSOR_AGENT==" CURSOR_AGENT_BIN="${CURSOR_AGENT_BIN:-}" if [ -z "$CURSOR_AGENT_BIN" ]; then # `agent`(公式の新表記)→ `cursor-agent`(legacy alias)の順に探す。 # `agent` は汎用的な名前なので、symlink 解決後の実パスの「成分」に # cursor-agent が完全一致で現れる場合だけ採用する(部分文字列一致だと # /x/cursor-agent-not-really/agent のようなディレクトリ名で突破される)。 for _c in agent cursor-agent; do _p="$(command -v "$_c" 2>/dev/null)" || continue [ -n "$_p" ] || continue _r="$(realpath "$_p" 2>/dev/null || readlink -f "$_p" 2>/dev/null || echo "$_p")" case "/$_r/" in */cursor-agent/*) CURSOR_AGENT_BIN="$_p"; break ;; esac done if [ -z "$CURSOR_AGENT_BIN" ] && [ -x "$HOME/.local/bin/cursor-agent" ]; then CURSOR_AGENT_BIN="$HOME/.local/bin/cursor-agent" fi fi if [ -z "$CURSOR_AGENT_BIN" ]; then echo "NOT_INSTALLED" else "$CURSOR_AGENT_BIN" --version 2>/dev/null || echo "NOT_INSTALLED" fi '
判定:
CURSOR_AGENT=NOT_INSTALLED→ERROR: cursor-agent not found (exit 3 expected from companion). Install via setup-cursor.sh.を出し終了。BRANCHがmain/master→WARN: on protected branch — cherry-pick target is HEAD of this branch. Confirm intent or switch.を出し継続。
Step 3 — plugin root + backend + model resolve (1 bash)
HARNESS_PLUGIN_ROOT / CLAUDE_PLUGIN_ROOT が未設定だと :-. fallback が consumer repo の cwd に解決し、scripts/cursor-companion.sh が見えず起動不能になる (Issue #193 §2)。hooks.json と同じ valid_root パターンで堅牢に解決する。
bashbash -c ' set -euo pipefail valid_root() { [ -n "${1:-}" ] && [ -f "$1/scripts/cursor-companion.sh" ] && { [ -f "$1/.claude-plugin/plugin.json" ] || [ -f "$1/.codex-plugin/plugin.json" ] || [ -f "$1/.cursor-plugin/plugin.json" ]; } } HARNESS_PLUGIN_ROOT="${HARNESS_PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT:-}}" ROOT="$HARNESS_PLUGIN_ROOT" if ! valid_root "$ROOT"; then ROOT="" if [ -n "${CLAUDE_SKILL_DIR:-}" ]; then probe="$(cd "${CLAUDE_SKILL_DIR}" && pwd)" while [ "$probe" != "/" ] && ! valid_root "$probe"; do probe="$(cd "$probe/.." && pwd)" done valid_root "$probe" && ROOT="$probe" fi fi if ! valid_root "$ROOT"; then ROOT="" for c in "${CLAUDE_PROJECT_DIR:-}" "$PWD" \ "$HOME/.claude/plugins/marketplaces/claude-code-harness-marketplace" \ "$HOME/.claude/plugins/cache/claude-code-harness-marketplace/claude-code-harness/"*; do if valid_root "$c"; then ROOT="$c"; break; fi done fi if ! valid_root "$ROOT"; then echo "ERROR: claude-code-harness plugin root not found (no scripts/cursor-companion.sh)" >&2 exit 2 fi HARNESS_PLUGIN_ROOT="$ROOT" BACKEND=$(bash "${HARNESS_PLUGIN_ROOT}/scripts/resolve-impl-backend.sh" --backend cursor --role worker) MODEL=$(bash "${HARNESS_PLUGIN_ROOT}/scripts/model-routing.sh" --host cursor --role worker --field model) echo "PLUGIN_ROOT=${HARNESS_PLUGIN_ROOT}" echo "BACKEND=$BACKEND" echo "MODEL=$MODEL" '
返却値: PLUGIN_ROOT (Step 5 で使う) / BACKEND (必ず cursor) / MODEL (通常 composer-2.5-fast)。BACKEND または MODEL が空なら ERROR: backend/model resolution failed を 1 行で出して終了。PLUGIN_ROOT 解決失敗は上記スクリプトが exit 2 で報告する。
Step 4 — 専用 worktree 作成
衝突しない id を作って worktree を切る。main tree や $HOME を指してはならない (companion 側 guard で exit 2 になる)。WT_DIR は絶対パスで作る (Step 5 の --workspace は companion の is not a directory ガードで相対パスを exit 2 にすることがあるため、Issue #193 §4)。
bashbash -c ' set -euo pipefail REPO_ROOT="$(git rev-parse --show-toplevel)" cd "$REPO_ROOT" ID="$(date +%Y%m%d-%H%M%S)-$$" WT_DIR="$REPO_ROOT/.claude/worktrees/cursor-do-${ID}" BASE_REF="$(git rev-parse HEAD)" BASE_BRANCH="$(git branch --show-current)" WT_BRANCH="cursor-do/${ID}" mkdir -p "$REPO_ROOT/.claude/worktrees" git worktree add -b "${WT_BRANCH}" "${WT_DIR}" "${BASE_REF}" echo "REPO_ROOT=${REPO_ROOT}" echo "WT_DIR=${WT_DIR}" echo "WT_BRANCH=${WT_BRANCH}" echo "BASE_REF=${BASE_REF}" echo "BASE_BRANCH=${BASE_BRANCH}" '
返却された WT_DIR / WT_BRANCH / BASE_REF / BASE_BRANCH を以降の Step で使う。失敗時 (branch 名衝突等) は ID を作り直して 1 回だけ retry。2 回連続失敗で ERROR: worktree creation failed を出し終了。
Step 5 — cursor-companion.sh task --write で委譲
Lead が直接 companion を呼ぶ (references/cursor-cli-only.md Topology 節 — 非 claude backend では Worker 介在なし)。プロンプトは引数の task そのまま + 必要な追補のみ。冗長な前置きは付けない。
bashbash -c ' set -euo pipefail valid_root() { [ -n "${1:-}" ] && [ -f "$1/scripts/cursor-companion.sh" ] && { [ -f "$1/.claude-plugin/plugin.json" ] || [ -f "$1/.codex-plugin/plugin.json" ] || [ -f "$1/.cursor-plugin/plugin.json" ]; } } HARNESS_PLUGIN_ROOT="${HARNESS_PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT:-}}" ROOT="${PLUGIN_ROOT:-$HARNESS_PLUGIN_ROOT}" if ! valid_root "$ROOT"; then ROOT="" if [ -n "${CLAUDE_SKILL_DIR:-}" ]; then probe="$(cd "${CLAUDE_SKILL_DIR}" && pwd)" while [ "$probe" != "/" ] && ! valid_root "$probe"; do probe="$(cd "$probe/.." && pwd)" done valid_root "$probe" && ROOT="$probe" fi fi if ! valid_root "$ROOT"; then ROOT="" for c in "${CLAUDE_PROJECT_DIR:-}" "$PWD" \ "$HOME/.claude/plugins/marketplaces/claude-code-harness-marketplace" \ "$HOME/.claude/plugins/cache/claude-code-harness-marketplace/claude-code-harness/"*; do if valid_root "$c"; then ROOT="$c"; break; fi done fi if ! valid_root "$ROOT"; then echo "ERROR: claude-code-harness plugin root not found (no scripts/cursor-companion.sh)" >&2 exit 2 fi HARNESS_PLUGIN_ROOT="$ROOT" PROMPT="<task-description> Purpose and why: <expected outcome and user need> Owned paths and non-goals: <assigned files/modules and excluded work> Definition of done: <observable conditions and required checks> Contract and authorization references: <selected plan/spec paths and original user instruction or applicable approval record> Available evidence: <observed state, prior attempts, findings and artifact paths> Constraints: - Choose the implementation method within the assigned scope. Other workers may be active; preserve their edits. - Recover missing context from the supplied contracts and read-only inspection first. State minor assumptions; stop only the action that needs a material decision or missing authorization. - Complete authorized reversible work without asking again. Inferred scope is not authorization for protected operations. - Preserve required TDD and validation. After required checks pass, add checks only for new changes, failures, or unresolved concerns. - Match existing code style and naming. - Create exactly one git commit if your environment supports it; otherwise leave one dirty changeset for Lead auto-commit. - Do not touch .claude-plugin/settings*, .claude/settings*, .eslintrc*, biome.json, tsconfig*.json. - Return the result, decision reasons, changed paths, check commands/results and remaining limitations. A success claim alone is not evidence." bash "${HARNESS_PLUGIN_ROOT}/scripts/cursor-companion.sh" task \ --write \ --workspace "${WT_DIR}" \ "${PROMPT}" ' 2>&1
判定:
- exit 0 + result text → Step 6 へ
- exit 1 (result-error) → companion stderr を 1 行要約して
ERROR: cursor returned is_error/empty resultを出し終了。worktree は Step 8 のクリーンアップで削除 - exit 2 (bad-guard) → 設定不備。原因 (workspace 指定誤り等) を 1 行で示し終了
- exit 3 (not-found) → Step 2 で検出済みのはずだが、再度遭遇したら同様に終了
Step 6 — Lead diff review
worktree 内で Composer が作成した変更を読み、目視レビュー + contract grep の二段ゲートを通す (harness-work 「Lead の cherry-pick 前ゲート」と同じ契約)。
注意 (Issue #193 §1): Cursor Composer は --write でファイル編集を行うが commit は作らないことがある。worktree が dirty なまま放置すると Step 7 の cherry-pick 対象から漏れ、ユーザー視点で「完了したのに main に何も入らない」状態になる。本 Step 冒頭で dirty なら、既存 commit がある場合は amend、commit がない場合は Lead 側で 1 commit にまとめる。
bashbash -c ' set -euo pipefail cd "${WT_DIR}" # Composer は dirty changeset を返すことがあるため、既存 commit に fold する if [ -n "$(git status --porcelain)" ]; then git add -A if [ "$(git rev-list --count "${BASE_REF}..HEAD")" -gt 0 ]; then git -c user.name="cursor-composer" -c user.email="cursor-composer@local" \ commit --amend --no-edit --no-verify echo "==AUTO_AMENDED==" else git -c user.name="cursor-composer" -c user.email="cursor-composer@local" \ commit --no-verify -m "cursor: ${TASK_SUMMARY:-cursor-do delegated change}" echo "==AUTO_COMMITTED==" fi git log -1 --oneline fi echo "==LOG==" git log --oneline "${BASE_REF}..HEAD" echo "==STAT==" git diff --stat "${BASE_REF}..HEAD" echo "==DIFF==" git diff "${BASE_REF}..HEAD" '
TASK_SUMMARY は引数 task の先頭 60 文字以内に圧縮した文字列を Lead が事前に export しておく (例: TASK_SUMMARY="Add login form validation")。未設定なら fallback メッセージ cursor-do delegated change を使う。--no-verify を付けるのは worktree 内の編集を「Lead レビュー前の中間 commit」として扱うため (cherry-pick 後の main commit で R01-R13 と pre-commit hook を通す)。
Lead は diff 全文を Read し、以下を確認する:
- 変更が依頼タスクの範囲内か (関係ないファイルを触っていないか)
- protected path (
.claude-plugin/settings*,.eslintrc*, etc.) を変更していないか - secret /
.env/ 認証情報を含まないか - 公開 support tier 表記を破壊していないか。contract gates は必ず candidate worktree (
WT_DIR) 内で実行する:bash[ ! -f "${WT_DIR}/tests/test-support-claim-wording.sh" ] || (cd "${WT_DIR}" && bash tests/test-support-claim-wording.sh) [ ! -f "${WT_DIR}/scripts/ci/check-consistency.sh" ] || (cd "${WT_DIR}" && bash scripts/ci/check-consistency.sh) [ ! -f "${WT_DIR}/tests/validate-plugin.sh" ] || (cd "${WT_DIR}" && bash tests/validate-plugin.sh)
判定:
- 問題なし → Step 7 へ
- 範囲外変更あり → 該当 commit を
git resetで巻き戻すか、Cursor に再委譲 (Step 5 を 1 回だけ retry)。2 回失敗でREQUEST_CHANGES: <理由>を出し、worktree を残したまま終了 - protected path / secret 検出 → 即 abort。
ABORT: protected path violationを出し worktree 削除
Step 7 — cherry-pick + Plans.md cc:done 更新
worktree から main tree に cherry-pick する。Cursor prompt は exactly one commit 契約だが、Composer は dirty changeset を返すことがあるため Step 6 で commit/amend する。複数 commit が残った場合は main tree を触る前に停止する。
さらに cherry-pick 前に Plans.md の staged / unstaged 差分がないことを確認する。理由: 既存差分がある状態で後続の git add Plans.md && git commit --amend を行うと、task と無関係な plan/status 編集を Cursor の code commit に巻き込むため。
SHA 直接指定 (branch 名経由ではない) で reviewer state drift を避ける (MEMORY: reviewer_state_drift)。
bashbash -c ' set -euo pipefail COMMIT_COUNT="$(cd "${WT_DIR}" && git rev-list --count "${BASE_REF}..HEAD")" if [ "${COMMIT_COUNT}" -eq 0 ]; then echo "ERROR: no commits to cherry-pick" exit 1 fi if [ "${COMMIT_COUNT}" -ne 1 ]; then echo "ERROR: cursor returned ${COMMIT_COUNT} commits; expected exactly one. Keep worktree for manual review: ${WT_DIR}" exit 1 fi if ! git diff --quiet -- Plans.md || ! git diff --cached --quiet -- Plans.md; then echo "ERROR: Plans.md has pre-existing local edits; refusing to cherry-pick before the cursor:do marker amend" exit 1 fi SHA="$(cd "${WT_DIR}" && git rev-parse HEAD)" git cherry-pick "${SHA}" echo "==CHERRY_PICKED==" git log --oneline -n 1 '
cherry-pick で conflict が出たら git cherry-pick --abort し、CHERRY_PICK_CONFLICT: <files> を 1 行で示して終了 (worktree は残す、ユーザー判断)。
cherry-pick 後、Plans.md に対応行があれば該当タスクのマーカーを更新する。 上記の clean precondition を通過しているため、ここで発生する Plans.md 差分は marker-only diff として扱う:
| <task> | ... | cc:done [<merged-sha>] |
該当行の特定: 引数 task 文字列の先頭 40 文字で grep し、ヒットした最初の cc:TODO / cc:WIP / cc:todo 行を cc:done [<sha>] に置換する。ヒットなしなら更新スキップ (Plans.md 外のタスクとして扱う)。
マーカーを更新した場合、その編集は cherry-pick commit に含める。未コミットの Plans.md を残して cleanup / 完了報告へ進んではならない。
以下の amend block は、上記 precondition 通過後に実行した marker-only diff だけを対象にする:
bashbash -c ' set -euo pipefail if git diff --quiet -- Plans.md; then echo "PLANS_UPDATED=0" else git add Plans.md git commit --amend --no-edit echo "PLANS_UPDATED=1" echo "MERGED_SHA=$(git rev-parse HEAD)" fi '
Step 8 — worktree cleanup + 完了報告 (1 ブロック)
cherry-pick 成功後、worktree を delete する。失敗 path では呼ばれない(worktree を残してユーザー判断)。
bashbash -c ' set -euo pipefail git worktree remove --force "${WT_DIR}" git branch -D "${WT_BRANCH}" 2>/dev/null || true echo "==CLEANUP==" git worktree list | grep -v "cursor-do-" || true '
完了報告は 1 ブロック で出す。中間ナレーションなし:
cursor:do completed task: <task-first-60-chars> commits: <count> base: <BASE_REF> → cherry-picked into <BASE_BRANCH> plans: <updated|skipped (no match)> files: <changed-file-count> changed, +<inserts> -<deletes> checks: <commands, results and evidence paths> unverified: <remaining limitations or none observed within the checked scope>
Full Containment (write mode 必須)
| 層 | 役割 | skip 可否 |
|---|---|---|
専用 .git worktree | cursor の書込を main tree から隔離 | 不可(必須) |
| Lead diff review | untrusted cursor 出力の品質ゲート | 不可(必須) |
| contract-grep ゲート | docs / locale / matrix 固定文字列の保護 | 不可(必須) |
| cherry-pick → main | R01-R13 guard rail を通す唯一の経路 | 不可(必須) |
| Plans.md cc:done 更新 | 台帳との sync | 該当行なければ skip 可 |
Prohibited
--force/--yoloを companion に渡す(Cursor 公式 "Never use")- cursor 出力を Lead レビュー前に main へ直接 commit する
- protected path (
.claude-plugin/settings*,.eslintrc*,tsconfig*.json, etc.) を Step 5 の prompt で許可する $HOME/// main tree を--workspaceに指定する- Plans.md の
cc:*マーカーを task と無関係に書き換える
Related Skills / Rules
cursor:ask— 読取専用の質問・調査・敵対的視点 (worktree 不要)breezing --cursor— 複数タスクを team フローで cursor 委譲する場合harness-work— claude backend の default フロー (Worker agent 経由)references/cursor-cli-only.md— Cursor backend governance + Topology (旧.claude/rules/cursor-cli-only.mdの全文がここに移設済み)


