80 lines
3.7 KiB
Bash
Executable File
80 lines
3.7 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
# 用法:start.sh <owner/repo> <N>
|
||
# 在目前 clone 旁建 worktree、開 Herdr tab、啟動 claude、送提示詞並確認 agent 有反應、掛 in-progress 標籤。
|
||
# 送出提示詞後 15 秒內沒觀察到 working 或 blocked 就視為失敗並回滾(關 tab、移除 worktree、刪本次新建的分支)。
|
||
# 最後一行印出 JSON:{"tab":..,"pane":..,"path":..,"resumed":..,"state":"working"|"blocked"}
|
||
source "$(dirname "$0")/lib.sh"
|
||
REPO="${1:?}"; N="${2:?用法:start.sh <owner/repo> <N>}"
|
||
LINK="https://gitea.jsc.idv.tw/$REPO/issues/$N"
|
||
test "${HERDR_ENV:-}" = 1 || die "不在 Herdr 裡(HERDR_ENV≠1)"
|
||
CLONE="$(clone_root_for "$REPO")"
|
||
BRANCH="issue-$N"
|
||
WT="$(worktree_path "$N" "$CLONE")"
|
||
|
||
if [ "$(agent_status "$N")" != missing ]; then
|
||
die "issue-$N 的 agent 還活著(tab $(tab_for_issue "$N")),不重複啟動"
|
||
fi
|
||
[ "$(issue_state "$REPO" "$N")" = open ] || die "#$N 不是 open"
|
||
BLOCKERS="$(open_blockers "$REPO" "$N")" || exit 1
|
||
[ -z "$BLOCKERS" ] || die "#$N 被還沒關閉的議題擋住:$(echo "$BLOCKERS" | tr '\n' ' ')(Gitea 依賴設定)。等它們關閉,或到 Gitea 拿掉依賴再啟動"
|
||
|
||
git -C "$CLONE" fetch --quiet origin
|
||
RESUMED=false
|
||
if git -C "$CLONE" show-ref --verify --quiet "refs/heads/$BRANCH"; then
|
||
RESUMED=true
|
||
if ! git -C "$CLONE" worktree list --porcelain | grep -qx "worktree $WT"; then
|
||
git -C "$CLONE" worktree add --quiet "$WT" "$BRANCH"
|
||
fi
|
||
else
|
||
git -C "$CLONE" worktree add --quiet -b "$BRANCH" "$WT" origin/main
|
||
fi
|
||
|
||
TAB=""
|
||
# 回滾到跟沒跑過一樣:關 tab、移除 worktree;分支只在本次新建時刪除。
|
||
rollback() {
|
||
[ -n "$TAB" ] && herdr tab close "$TAB" >/dev/null 2>&1 || true
|
||
if git -C "$CLONE" worktree list --porcelain | grep -qx "worktree $WT"; then
|
||
git -C "$CLONE" worktree remove --force "$WT" >/dev/null 2>&1 || true
|
||
fi
|
||
$RESUMED || git -C "$CLONE" branch -D "$BRANCH" >/dev/null 2>&1 || true
|
||
}
|
||
|
||
TAB_JSON="$(herdr tab create --workspace "${HERDR_WORKSPACE_ID:?}" --cwd "$WT" --label "#$N" --no-focus)"
|
||
TAB="$(jq_py 'print(find(d,"tab")["tab_id"])' <<<"$TAB_JSON")"
|
||
PANE="$(jq_py 'print(find(d,"root_pane")["pane_id"])' <<<"$TAB_JSON")"
|
||
|
||
START_ERR="$(mktemp)"
|
||
for _ in 1 2 3 4 5 6; do
|
||
if herdr agent start "issue-$N" --kind claude --pane "$PANE" --timeout 90000 -- --model "$MODEL" >/dev/null 2>"$START_ERR"; then
|
||
rm -f "$START_ERR"; break
|
||
fi
|
||
sleep 2
|
||
done
|
||
if [ -f "$START_ERR" ]; then
|
||
cat "$START_ERR" >&2; rm -f "$START_ERR"
|
||
rollback
|
||
die "agent 啟動失敗,已回滾"
|
||
fi
|
||
|
||
if $RESUMED; then RESUME_LINE="分支已有先前進度,先看 git log 和議題核取方塊再接續。"; else RESUME_LINE=""; fi
|
||
PROMPT="$(sed -e "s|{LINK}|$LINK|g" -e "s|{REPO}|$REPO|g" -e "s|{N}|$N|g" -e "s|{S}|$SCRIPTS|g" -e "s|{RESUME}|$RESUME_LINE|" "$SCRIPTS/prompt.txt")"
|
||
|
||
# 送出後必須觀察到 agent 進入 working 或 blocked;否則 Herdr 回非 0(agent_prompt_stalled、timeout、agent_blocked 等)。
|
||
PROMPT_ERR="$(mktemp)"
|
||
if PROMPT_OUT="$(herdr agent prompt "issue-$N" "$PROMPT" --wait --until working --until blocked --timeout 15000 2>"$PROMPT_ERR")"; then
|
||
rm -f "$PROMPT_ERR"
|
||
else
|
||
echo "herdr-issue: 送出提示詞後 agent 沒有反應,已回滾" >&2
|
||
cat "$PROMPT_ERR" >&2; rm -f "$PROMPT_ERR"
|
||
rollback
|
||
exit 1
|
||
fi
|
||
STATE="$(jq_py 'print(find(d,"agent_status") or "")' <<<"$PROMPT_OUT" 2>/dev/null || true)"
|
||
case "$STATE" in working|blocked) ;; *) STATE="$(agent_status "$N")" ;; esac
|
||
|
||
ensure_label "$REPO"
|
||
tea issues edit "$N" --repo "$REPO" --remove-labels "$LABEL" >/dev/null 2>&1 || true
|
||
tea issues edit "$N" --repo "$REPO" --add-labels "$LABEL" >/dev/null
|
||
|
||
printf '{"tab":"%s","pane":"%s","path":"%s","resumed":%s,"state":"%s"}\n' "$TAB" "$PANE" "$WT" "$RESUMED" "$STATE"
|