mcpField
S
swain-teardown

swain-teardown

github.com/cristoslcAgent 工作流236 次安装25 周前更新
代码仓库

关于 swain-teardown

swain-teardown 是用于结束会话的技能,可检查仓库和工作树状态、补做未完成的收尾步骤,并协调交接与清理。它会在未提交更改、未合并分支和工作树移除方面执行安全检查。

主要功能

  1. 01检查孤立工作树
  2. 02提示未提交的更改
  3. 03补做缺失的完成步骤
  4. 04提示同步工单
  5. 05生成会话交接摘要

适用场景

  1. 01结束会话前检查仓库状态
  2. 02查找缺少会话书签的工作树
  3. 03交接前补做测试或回顾

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/cristoslc/swain --skill swain-teardown

安装后在 Claude Code 中输入/swain-teardown确认已加载。

详细介绍

会话拆解

结束会话的统一入口。运行完整的关停流程:总结、复盘、合并、清理、关闭、提交。取代原有的 swain-session 关闭处理程序(ADR-023)。

REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
bash "$REPO_ROOT/.agents/bin/swain-session-check.sh" 2>/dev/null

如果未传入 session-chain,且 JSON 输出中的 "status" 不是 "active",请告知操作人员:“没有活动会话可拆解。”并以代码 0 正常退出。


此技能的作用

会话拆解会在会话关闭前运行一系列卫生检查:

  1. 孤立工作树检测 — 查找没有对应会话书签的工作树(SPEC-233:带安全检查的可操作项)
  2. Git 脏状态检查 — 在操作人员离开前,显示未提交的更改
  3. 完成流水线检查 — 验证 BDD、冒烟测试和复盘是否已运行;执行缺失步骤(SPEC-258)
  4. 工单同步提示 — 提示操作人员确认工单与实际完成的工作一致(SPEC-234:无会话时记录降级状态)
  5. 交接摘要 — 将会话摘要追加到 SESSION-ROADMAP.md

**关于复盘的说明:**SPEC 级复盘现在作为完成流水线的一部分运行(第 3 步 / swain-do 第 2d 步)。EPIC 级复盘由 swain-session 的关闭处理程序调用。仅当流水线的复盘步骤遗漏时,拆解流程才会调用复盘来补做。


第 0 步 — 释放当前锁文件声明(SPEC-247)

如果当前会话在具有活动锁文件的工作树中运行,请释放该声明。此步骤最先运行,以便后续的孤立工作树检测不会将当前工作树标记为活动状态。

REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
LOCKFILE_SCRIPT="$REPO_ROOT/.agents/bin/swain-lockfile.sh"
GIT_COMMON=$(git rev-parse --git-common-dir 2>/dev/null)
GIT_DIR=$(git rev-parse --git-dir 2>/dev/null)

if [ "$GIT_COMMON" != "$GIT_DIR" ] && [ -f "$LOCKFILE_SCRIPT" ]; then
  BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null)
  bash "$LOCKFILE_SCRIPT" release "$BRANCH" 2>/dev/null || true
fi

第 1 步 — 孤立工作树检查(SPEC-233、SPEC-247)

此步骤从 session.json 的 worktrees 数组中读取工作树书签,并交叉核对锁文件状态(SPEC-247)。对于孤立工作树,流程会在安全检查后征求操作人员确认是否移除。此步骤始终运行,不受 --session-chain 影响。

REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
SESSION_FILE="$REPO_ROOT/.agents/session.json"
LOCKFILE_SCRIPT="$REPO_ROOT/.agents/bin/swain-lockfile.sh"

# Build a list of bookmarked worktree paths from session.json
bookmarked_wts=$(jq -r '(.worktrees // [])[] | .path' "$SESSION_FILE" 2>/dev/null)

orphan_candidates=()
orphan_results=()
ready_candidates=()

# Get all physical worktrees
git worktree list --porcelain 2>/dev/null | grep "^worktree " | while read -r line; do
  wt_path="${line#worktree }"
  wt_branch="$(git -C "$wt_path" rev-parse --abbrev-ref HEAD 2>/dev/null)"

  # Skip trunk (protected)
  if [ "$wt_path" = "$REPO_ROOT" ]; then
    echo "protected: $wt_path (trunk)"
    continue
  fi

  # Check lockfile state (SPEC-247)
  if [ -f "$LOCKFILE_SCRIPT" ]; then
    lock_branch="${wt_branch##*/}"  # strip refs/heads/ prefix
    lockfile="$REPO_ROOT/.agents/worktrees/${lock_branch}.lock"
    if [ -f "$lockfile" ]; then
      # Check if ready_for_cleanup — safe to remove without confirmation
      if grep -q 'ready_for_cleanup=true' "$lockfile" 2>/dev/null; then
        echo "ready: $wt_path ($wt_branch) — marked ready_for_cleanup"
        ready_candidates+=("$wt_path|$wt_branch|$lockfile")
        continue
      fi
      # Check if actively claimed by another session
      if ! bash "$LOCKFILE_SCRIPT" is-stale "$lock_branch" >/dev/null 2>&1; then
        echo "claimed: $wt_path ($wt_branch) — active lockfile, skip"
        continue
      fi
    fi
  fi

  # Check if bookmarked in session.json
  is_orphan=true
  reason=""
  if echo "$bookmarked_wts" | grep -qF "$wt_path"; then
    is_orphan=false
    echo "linked: $wt_path ($wt_branch)"
  fi

  if [ "$is_orphan" = true ]; then
    # Safety checks per SPEC-233 AC1-AC3
    if [ "$wt_path" = "$(pwd)" ]; then
      reason="current directory"
    elif [ -n "$(git -C "$wt_path" status --porcelain 2>/dev/null)" ]; then
      reason="uncommitted changes"
    elif ! git merge-base --is-ancestor "$wt_branch" trunk 2>/dev/null; then
      reason="branch not fully merged"
    else
      reason="safe to remove"
      orphan_candidates+=("$wt_path|$wt_branch")
    fi
    echo "orphan: $wt_path ($wt_branch) — $reason"
    orphan_results+=("$wt_path|$wt_branch|$reason")
  fi
done

如果发现孤立工作树,请显示消息:“以下工作树没有活动会话书签 — 请考虑将其移除。”

自动移除 ready_for_cleanup 工作树(SPEC-247)

标记为 ready_for_cleanup 的工作树已合并并推送 — 可安全移除,无需操作人员确认。请先归档 session.json(SPEC-248):

ARCHIVE_SCRIPT="$REPO_ROOT/.agents/bin/swain-session-archive.sh"
for candidate in "${ready_candidates[@]}"; do
  IFS='|' read -r wt_path wt_branch lockfile <<< "$candidate"

  # Archive session.json before deletion
  if [ -f "$ARCHIVE_SCRIPT" ]; then
    bash "$ARCHIVE_SCRIPT" save "$wt_path" 2>/dev/null || true
  fi

  if git worktree remove "$wt_path" 2>/dev/null; then
    rm -f "$lockfile"  # Clean up lockfile (SPEC-247 AC3)
    bash "$REPO_ROOT/.agents/bin/swain-bookmark.sh" worktree remove "$wt_path" 2>/dev/null || true
    echo "Auto-removed (ready_for_cleanup): $wt_path"
  fi
done
针对安全孤立工作树征求操作人员确认

仅提供 reason="safe to remove" 的孤立工作树供移除。对于每个安全的孤立工作树:

removed_worktrees=()
for candidate in "${orphan_candidates[@]}"; do
  wt_path="${candidate%%|*}"
  wt_branch="${candidate##*|}"

  echo "> Remove orphan worktree $wt_path ($wt_branch)? [y/N]"
  read -r response
  if [ "$response" = "y" ] || [ "$response" = "Y" ]; then
    # Archive session.json before deletion (SPEC-248)
    if [ -f "$ARCHIVE_SCRIPT" ]; then
      bash "$ARCHIVE_SCRIPT" save "$wt_path" 2>/dev/null || true
    fi
    if git worktree remove "$wt_path" 2>/dev/null; then
      removed_worktrees+=("$wt_path|$wt_branch")
      # Clean up lockfile if present (SPEC-247 AC3)
      lock_branch="${wt_branch##*/}"
      rm -f "$REPO_ROOT/.agents/worktrees/${lock_branch}.lock" 2>/dev/null || true
      # Clear the bookmark from session.json
      bash "$REPO_ROOT/.agents/bin/swain-bookmark.sh" worktree remove "$wt_path"
      echo "Removed: $wt_path"
    else
      echo "ERROR: Failed to remove $wt_path — skipping bookmark cleanup"
    fi
  fi
done
安全规则(SPEC-233 AC1-AC3、AC7)
  • **当前目录:**绝不提供移除选项。标记为 reason="current directory"。
  • **未提交的更改:**绝不提供移除选项。标记为 reason="uncommitted changes"。
  • **未合并的分支:**绝不提供移除选项。标记为 reason="branch not fully merged"。
  • **Trunk:**绝不提供移除选项。始终显示为 protected: <repo_root> (trunk)。
  • **必须确认:**始终提示操作人员。绝不自动移除。

第 2 步 — Git 脏状态检查

REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"

cd "$REPO_ROOT"
git_status=$(git status --porcelain 2>/dev/null)

if [ -n "$git_status" ]; then
  echo "WARN: Dirty git working tree — uncommitted changes detected."
  echo "Changes:"
  echo "$git_status" | head -20
else
  echo "OK: Working tree is clean."
fi

报告检查结果,并给出可执行的建议。


第 3 步 — 完成流水线检查(SPEC-258)

继续进行工单同步前,请确认此工作树中任何已完成工作的完成流水线均已运行。如果有遗漏的步骤,现在就运行。

3a — 检测流水线状态
REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
STATE_FILE="$REPO_ROOT/.agents/completion-state.json"
SWAIN_TEST="$REPO_ROOT/.agents/bin/swain-test.sh"

**如果 completion-state.json 存在:**读取该文件并查找未完成的步骤:

PENDING_STEPS=$(jq -r '.steps | to_entries[] | select(.value.status == "pending" or .value.status == "failed") | .key' "$STATE_FILE" 2>/dev/null)
SPEC_ID=$(jq -r '.spec_id // empty' "$STATE_FILE" 2>/dev/null)
  • 如果 PENDING_STEPS 为空 → 所有步骤均已通过或已跳过。显示“完成流水线:所有步骤均已完成。”并继续。
  • 如果 PENDING_STEPS 不为空 → 显示需要处理的步骤,并运行这些步骤(见 3b)。

**如果 completion-state.json 不存在:**检查工作树中是否有已关闭的任务:

export PATH="$REPO_ROOT/.agents/bin:$PATH"
CLOSED_COUNT=$(ticket-query '.status == "closed"' 2>/dev/null | wc -l | tr -d ' ')
  • 如果 CLOSED_COUNT > 0 → 已完成工作,但流水线从未启动。创建状态文件,将所有步骤设为 pending,并运行这些步骤:
    # Try to find the SPEC ID from the plan epic
    EPIC_ID=$(ticket-query '.type == "epic"' 2>/dev/null | head -1 | jq -r '.id // empty')
    SPEC_ID=$(tk show "$EPIC_ID" 2>/dev/null | grep -i 'external_ref' | awk '{print $NF}')
    mkdir -p "$REPO_ROOT/.agents"
    jq -n --arg spec "${SPEC_ID:-unknown}" --arg branch "$(git branch --show-current)" \
      '{spec_id: $spec, branch: $branch, pipeline_started: (now | todate), steps: {bdd_tests: {status: "pending", timestamp: null, detail: null}, smoke_test: {status: "pending", timestamp: null, detail: null}, retro: {status: "pending", timestamp: null, detail: null}}}' \
      > "$STATE_FILE.tmp" && mv "$STATE_FILE.tmp" "$STATE_FILE"
    
  • 如果 CLOSED_COUNT == 0 → 没有已完成的工作,静默跳过此步骤。
3b — 运行缺失步骤

按流水线顺序(bdd_tests → smoke_test → retro)检查每个步骤的状态。跳过状态为 passed 或 skipped 的步骤。运行状态为 pending 或 failed 的步骤。

bdd_tests:

BDD_STATUS=$(jq -r '.steps.bdd_tests.status' "$STATE_FILE")

如果 BDD_STATUS 为 pending 或 failed:

  • 如果 swain-test.sh 存在:

    BDD_OUTPUT=$(bash "$SWAIN_TEST" --artifacts "$SPEC_ID" 2>&1)
    BDD_EXIT=$?
    
    • 退出码为 0 → 将状态更新为 passed,详情填写输出的前 5 行。
    • 非零 → 将状态更新为 failed,详情填写最后 10 行。询问:“BDD 失败。retry、skip 还是 abort?”
      • retry → 重新运行此步骤
      • skip → 将状态设为 skipped,详情填写“操作员在 teardown 期间跳过”
      • abort → 完全停止 teardown,不继续同步
  • **如果 swain-test.sh 不存在:**将状态设为 skipped,详情填写“swain-test.sh 不可用”。显示警告。

所有状态写入均使用 DESIGN-018 中的原子 jq 更新模式:

jq --arg step "$STEP" --arg status "$STATUS" --arg detail "$DETAIL" \
  '.steps[$step].status = $status | .steps[$step].timestamp = (now | todate) | .steps[$step].detail = $detail' \
  "$STATE_FILE" > "$STATE_FILE.tmp" && mv "$STATE_FILE.tmp" "$STATE_FILE"

smoke_test:

如果状态为 pending 或 failed:

  • **如果 swain-test.sh 存在:**从 swain-test 输出中提取 ## SMOKE 部分并展示给操作员。询问:“冒烟测试是否通过?(yes / no / skip)”

    • yes → 将状态设为 passed,详情填写“操作员在 teardown 期间确认”
    • no → 将状态设为 failed。询问:“retry、skip 还是 abort?”
    • skip → 将状态设为 skipped,详情填写“操作员在 teardown 期间跳过”
  • **如果 swain-test.sh 不存在:**将状态设为 skipped,详情填写“swain-test.sh 不可用”。

retro:

如果状态为 pending 或 failed:

使用 Skill 工具调用 swain-retro:

使用 Skill 工具:调用 swain-retro,参数为:"Teardown catch-up — run retro for before sync."

  • 成功时 → 将状态设为 passed,详情填写“在 teardown 期间记录了 retro”
  • 失败时 → 询问:“Retro 失败。retry 还是 abort?”(retro 不能跳过)
3c — 强制绕过

如果操作员调用 teardown 时使用了 --force(通过调用参数中包含 "force" 或 "--force" 检测):

# Mark all pending steps as skipped
for step in bdd_tests smoke_test retro; do
  STEP_STATUS=$(jq -r ".steps.$step.status" "$STATE_FILE")
  if [ "$STEP_STATUS" = "pending" ] || [ "$STEP_STATUS" = "failed" ]; then
    jq --arg step "$step" \
      '.steps[$step].status = "skipped" | .steps[$step].timestamp = (now | todate) | .steps[$step].detail = "operator forced bypass"' \
      "$STATE_FILE" > "$STATE_FILE.tmp" && mv "$STATE_FILE.tmp" "$STATE_FILE"
  fi
done

显示:“强制绕过 — 所有待处理的流水线步骤均标记为已跳过。”继续执行步骤 4。

3d — 流水线门控

运行或跳过所有步骤后,进行验证:

REMAINING=$(jq -r '.steps | to_entries[] | select(.value.status == "pending" or .value.status == "failed") | .key' "$STATE_FILE")
  • 如果为空 → 继续执行步骤 4
  • 如果不为空 → teardown 被阻止。显示仍未完成的步骤并停止。

步骤 4 — 工单同步提示(SPEC-234 AC3,由步骤 3 重新编号而来)

始终运行此步骤。如果没有活动会话,则注明状态降级:

REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
session_check=$(bash "$REPO_ROOT/.agents/bin/swain-session-check.sh" 2>/dev/null)
session_status=$(echo "$session_check" | jq -r '.status // "none"')

如果 session_status 不为 "active":

  • 如果传入了 --session-chain,则继续执行(调用方已确认)。
  • 否则,告知操作员:“没有活动会话。运行 /swain-init 启动会话,还是仅继续清理?”如果他们选择继续,则跳过步骤 1-2(摘要/retro 需要会话状态),并跳转到步骤 3(合并)。

步骤 1 — 会话摘要

在会话仍处于活动状态时生成会话摘要。

REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
SESSION_ID="$(jq -r '.session_id // empty' "$REPO_ROOT/.agents/session-state.json" 2>/dev/null)"

if [ -n "$SESSION_ID" ]; then
  bash "$REPO_ROOT/.agents/bin/swain-session-digest.sh" \
    --session-id "$SESSION_ID" \
    --output "$REPO_ROOT/.agents/session-log.jsonl" 2>/dev/null
fi

如果摘要脚本缺失或运行失败,记录警告并继续。摘要很有用,但不是阻塞条件。


步骤 2 — Retro(会话仍处于活动状态)

**关键:**运行 retro 时会话必须仍处于活动状态,以便读取会话状态。在此步骤之前不要关闭会话。

调用 swain-retro:

Skill("swain-retro", "Session is closing. Run retro to capture learnings before session state is cleared.")

如果 swain-retro 不可用,记录“未找到 Retro 技能 — 正在跳过”并继续。不能因为缺少技能而导致关机序列失败。


步骤 3 — 合并工作树分支

对每个活动工作树(不包括 trunk),询问是否将其分支合并到 trunk。这是旧版 teardown 中缺失的步骤——如果不执行此步骤,工作树的分支就永远不会合并,因此工作树永远无法达到“可安全移除”的状态。

3.1 — 枚举工作树
REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
TRUNK=$(bash "$REPO_ROOT/.agents/bin/swain-trunk.sh" 2>/dev/null || echo "trunk")

git worktree list --porcelain 2>/dev/null | grep "^worktree " | while read -r line; do
  wt_path="${line#worktree }"
  # Skip trunk
  [ "$wt_path" = "$REPO_ROOT" ] && continue
  wt_branch="$(git -C "$wt_path" rev-parse --abbrev-ref HEAD 2>/dev/null)"
  echo "$wt_path|$wt_branch"
done
3.2 — 逐个检查工作树

对于每个工作树,判断其状态:

状态条件操作
已合并git merge-base --is-ancestor "$wt_branch" "$TRUNK"跳过合并,标记为可安全清理
干净,未合并没有未提交的更改,分支尚未合并提议合并
有未提交更改git -C "$wt_path" status --porcelain 有输出警告操作员,提议先提交
当前目录"$wt_path" = "$(pwd)"必须先通过 ExitWorktree 退出
3.3 — 合并未合并的分支

对于每个“干净,未合并”的工作树,询问操作员:

工作树 {path}(分支 {branch})有尚未合并的更改。选项:

  1. 合并 — 现在将分支合并到 {trunk}
  2. PR — 推送分支并创建拉取请求
  3. 跳过 — 保留分支现状(工作树将予以保留)

如果选择合并:

REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
TRUNK=$(bash "$REPO_ROOT/.agents/bin/swain-trunk.sh" 2>/dev/null || echo "trunk")

# Link safety — scan for worktree-specific path links before merging
DETECT_SCRIPT="$REPO_ROOT/.agents/bin/detect-worktree-links.sh"
RESOLVE_SCRIPT="$REPO_ROOT/.agents/bin/resolve-worktree-links.sh"
if [ -x "$DETECT_SCRIPT" ]; then
  MERGE_BASE=$(git -C "$wt_path" merge-base HEAD "origin/$TRUNK" 2>/dev/null || true)
  CHANGED_FILES=$([ -n "$MERGE_BASE" ] && git -C "$wt_path" diff --name-only "$MERGE_BASE" HEAD 2>/dev/null || true)
  if [ -n "$CHANGED_FILES" ]; then
    if ! echo "$CHANGED_FILES" | xargs "$DETECT_SCRIPT" --repo-root "$REPO_ROOT" > /dev/null 2>&1; then
      echo "[link-safety] Found suspicious links in $wt_branch. Resolving..."
      echo "$CHANGED_FILES" | xargs "$RESOLVE_SCRIPT" --repo-root "$REPO_ROOT" 2>/dev/null || true
    fi
  fi
fi

# Merge into trunk from the main worktree
cd "$REPO_ROOT"
git fetch origin 2>/dev/null
git merge "$wt_branch" --no-edit

如果合并发生冲突,向操作员报告并跳过此工作树。不要强行解决冲突。

如果选择 PR:

cd "$wt_path"
git push -u origin "$wt_branch" 2>/dev/null
SUBJECT=$(git log -1 --pretty=format:'%s')
gh pr create --base "$TRUNK" --head "$wt_branch" --title "$SUBJECT" --body "Teardown PR — branch from session teardown."

报告 PR URL。

如果选择跳过: 将工作树标记为保留。第 4 步不会移除它。

3.4 — 处理“当前目录”工作树

如果操作员当前位于某个工作树中,最后处理该工作树。处理完所有其他工作树后:

  1. 询问他们是否也要合并此分支(选项与 3.3 相同)。
  2. 如果选择合并或 PR:先提交所有未提交的更改,然后调用 ExitWorktree 返回主干。
  3. 如果选择跳过:调用 ExitWorktree 并设置 action: "keep",以保留该分支。

第 4 步 — 清理工作树

完成合并后,清理分支现已合并的工作树。

4.1 — 重新枚举并检查合并状态
REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
TRUNK=$(bash "$REPO_ROOT/.agents/bin/swain-trunk.sh" 2>/dev/null || echo "trunk")

git worktree list --porcelain 2>/dev/null | grep "^worktree " | while read -r line; do
  wt_path="${line#worktree }"
  [ "$wt_path" = "$REPO_ROOT" ] && continue
  wt_branch="$(git -C "$wt_path" rev-parse --abbrev-ref HEAD 2>/dev/null)"

  # Safety checks
  if [ "$wt_path" = "$(pwd)" ]; then
    echo "SKIP: $wt_path — current directory"
    continue
  fi
  if [ -n "$(git -C "$wt_path" status --porcelain 2>/dev/null)" ]; then
    echo "SKIP: $wt_path — uncommitted changes"
    continue
  fi
  if ! git merge-base --is-ancestor "$wt_branch" "$TRUNK" 2>/dev/null; then
    echo "SKIP: $wt_path — branch not merged"
    continue
  fi

  echo "SAFE: $wt_path|$wt_branch"
done
4.2 — 移除可安全清理的工作树

对每个可安全清理的工作树,无需询问即可将其移除。该分支已合并,没有未提交的更改,而且它不是当前目录。操作员已在第 3 步批准合并。

git worktree remove "$wt_path" 2>/dev/null && echo "Removed: $wt_path ($wt_branch)"
# Clear bookmark
bash "$REPO_ROOT/.agents/bin/swain-bookmark.sh" worktree remove "$wt_path" 2>/dev/null

如果移除失败,请报告并继续。

4.3 — 清理过期的工作树引用
git worktree prune 2>/dev/null
bash "$REPO_ROOT/.agents/bin/swain-bookmark.sh" worktree prune 2>/dev/null

第 5 步 — 关闭会话状态

REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
WALKAWAY_NOTE="${WALKAWAY:-Session closed via teardown}"

bash "$REPO_ROOT/.agents/bin/swain-session-state.sh" close \
  --walkaway "$WALKAWAY_NOTE" \
  --session-roadmap "$(pwd)/SESSION-ROADMAP.md" 2>/dev/null

收尾记录应总结已完成的工作。根据会话工作内容推断(已合并的分支、已关闭的任务、回顾输出),或请操作员用一句话概括。


第 6 步 — 提交交接内容

暂存并提交拆解过程中发生更改的所有会话产物。

REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"

# Stage session artifacts
git add SESSION-ROADMAP.md 2>/dev/null
git add docs/swain-retro/ 2>/dev/null
git add .agents/session-state.json 2>/dev/null
git add .agents/session-log.jsonl 2>/dev/null

# Check if there's anything to commit
# Resolve model name from the system prompt. When subagents ran under a
# different model, include one Co-Authored-By trailer per model (see
# AGENTS.md Model attribution).
if [ -n "$(git diff --cached --name-only 2>/dev/null)" ]; then
  git commit -m "chore: session teardown handoff

Co-Authored-By: <model-name-from-system-prompt> <noreply@unknown>"
fi

摘要报告

完成所有步骤后,显示摘要:

=== Session Teardown Complete ===

Retro:          {fired / skipped / not available}
Branches:       {N merged, M PRs created, K preserved}
Worktrees:      {N removed, M preserved}
Session state:  closed
Handoff commit: {hash or "nothing to commit"}

除非发生严重错误,否则以代码 0 退出。


错误处理

情况响应
没有活动会话(未使用 --session-chain)提议仅清理模式(步骤 3–4),跳过摘要/回顾
摘要脚本缺失发出警告并继续
回顾技能缺失发出警告并继续
合并冲突报告冲突文件,跳过该工作树
工作树移除失败报告并继续
会话状态脚本缺失发出警告,跳过关闭会话
第 6 步没有可提交内容报告“干净”并退出
Git 不可用严重错误 — 无法继续

集成

由 swain-init 调用(决策预算)

会话达到决策预算时,swain-init 可以调用拆解流程:

Skill("swain-teardown", "Decision budget reached. Run teardown sequence.")
由 meta-router 调用

swain meta-router 会将“完成”“收尾”“关闭会话”“拆解”等请求路由到此技能。

独立调用

操作员可以随时运行 /swain-teardown。如果没有活动会话,则会提供仅清理模式。

评论

支持 Markdown