swain-sync

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese
<!-- swain-model-hint: sonnet, effort: low -->
Run through the following steps in order without pausing for confirmation unless a decision point is explicitly marked as requiring one.
Delegate this to a sub-agent so the main conversation thread stays clean. Include the full text of these instructions in the agent prompt, since sub-agents cannot read skill files directly.
<!-- swain-model-hint: sonnet, effort: low -->
按顺序执行以下步骤,除非明确标记为需要确认的决策点,否则无需暂停等待确认。
将此任务委托给子Agent,以保持主对话线程整洁。由于子Agent无法直接读取skill文件,请在Agent提示词中包含本说明的全文。

Step 1 — Detect worktree context and fetch/rebase upstream

步骤1 — 检测工作树上下文并拉取/变基上游代码

First, detect whether you are running in a git linked worktree:
bash
GIT_COMMON=$(git rev-parse --git-common-dir)
GIT_DIR=$(git rev-parse --git-dir)
IN_WORKTREE=$( [ "$GIT_COMMON" != "$GIT_DIR" ] && echo "yes" || echo "no" )
REPO_ROOT=$(git rev-parse --show-toplevel)
IN_WORKTREE=yes
means the current directory is inside a linked worktree (e.g.,
.claude/worktrees/agent-abc123
). Use this flag in Steps 3, 6, and the session bookmark step.
Next, check whether the current branch has an upstream tracking branch:
bash
git --no-pager rev-parse --abbrev-ref --symbolic-full-name @{u} 2>/dev/null
If there is an upstream, fetch and rebase to incorporate upstream changes BEFORE staging or committing:
bash
git fetch origin
If there are local changes (dirty working tree), stash them first:
bash
BRANCH=$(git rev-parse --abbrev-ref HEAD)
git stash push -m "swain-sync: auto-stash [$BRANCH]"
git --no-pager rebase origin/$BRANCH
git stash pop
If the rebase has conflicts after stash pop, abort and report:
bash
git rebase --abort  # if rebase itself conflicts
git stash pop       # recover stashed changes
Show the user the conflicting files and stop. Do not force-push or drop changes.
If there is no upstream (
@{u}
returns an error) and
IN_WORKTREE=yes
, the worktree branch has no remote tracking counterpart. Rebase onto
origin/main
so the commits apply cleanly as a fast-forward:
bash
git fetch origin
git rebase origin/main
If
origin
cannot be fetched, skip fetch/rebase and proceed to Step 2.
If there is no upstream and
IN_WORKTREE=no
(main worktree, new branch), skip this step entirely.
首先,检测当前是否运行在Git链接工作树中:
bash
GIT_COMMON=$(git rev-parse --git-common-dir)
GIT_DIR=$(git rev-parse --git-dir)
IN_WORKTREE=$( [ "$GIT_COMMON" != "$GIT_DIR" ] && echo "yes" || echo "no" )
REPO_ROOT=$(git rev-parse --show-toplevel)
IN_WORKTREE=yes
表示当前目录位于链接工作树内(例如
.claude/worktrees/agent-abc123
)。请在步骤3、步骤6和会话书签步骤中使用此标志。
接下来,检查当前分支是否有上游跟踪分支:
bash
git --no-pager rev-parse --abbrev-ref --symbolic-full-name @{u} 2>/dev/null
如果存在上游分支,请在暂存或提交之前先拉取并变基以合并上游变更:
bash
git fetch origin
如果存在本地变更(工作树未干净),请先暂存:
bash
BRANCH=$(git rev-parse --abbrev-ref HEAD)
git stash push -m "swain-sync: auto-stash [$BRANCH]"
git --no-pager rebase origin/$BRANCH
git stash pop
如果恢复暂存后变基出现冲突,请中止并报告:
bash
git rebase --abort  # 如果变基本身冲突
git stash pop       # 恢复暂存的变更
向用户显示冲突文件并停止操作。请勿强制推送或丢弃变更。
如果没有上游分支(
@{u}
返回错误)且
IN_WORKTREE=yes
,则该工作树分支没有远程跟踪对应分支。请变基到
origin/main
,以便提交可以作为快进合并干净应用:
bash
git fetch origin
git rebase origin/main
如果无法拉取
origin
,请跳过拉取/变基步骤,直接进入步骤2。
如果没有上游分支且
IN_WORKTREE=no
(主工作树,新分支),请完全跳过此步骤。

Step 2 — Survey the working tree

步骤2 — 检查工作树状态

bash
git --no-pager status
git --no-pager diff          # unstaged changes
git --no-pager diff --cached # already-staged changes
If the working tree is completely clean and there is nothing to push, report that and stop.
bash
git --no-pager status
git --no-pager diff          # 未暂存的变更
git --no-pager diff --cached # 已暂存的变更
如果工作树完全干净且没有需要推送的内容,请报告此情况并停止操作。

Step 3 — Stage changes

步骤3 — 暂存变更

Identify files that look like secrets (
.env
,
*.pem
,
*_rsa
,
credentials.*
,
secrets.*
). If any are present, warn the user and exclude them from staging.
If there are 10 or fewer changed files (excluding secrets), stage them individually:
bash
git add file1 file2 ...
If there are more than 10 changed files, stage everything and then unstage secrets:
bash
git add -A
git reset HEAD -- <secret-file-1> <secret-file-2> ...
识别看起来像敏感信息的文件(
.env
*.pem
*_rsa
credentials.*
secrets.*
)。如果存在此类文件,请警告用户并将其排除在暂存范围之外。
如果变更文件数量≤10个(排除敏感文件),请逐个暂存:
bash
git add file1 file2 ...
如果变更文件数量>10个,请暂存所有文件然后取消暂存敏感文件:
bash
git add -A
git reset HEAD -- <secret-file-1> <secret-file-2> ...

Step 3.5 — Gitignore check

步骤3.5 — Gitignore检查

Before committing, verify
.gitignore
hygiene. This step is blocking — if relevant patterns are missing, stop and require the user to fix
.gitignore
before proceeding.
提交前,请验证
.gitignore
的规范性。此步骤为阻塞式 — 如果缺少相关规则,请停止操作并要求用户在继续前修复
.gitignore

1. Check existence

1. 检查文件是否存在

If no
.gitignore
file exists in the repo root:
STOP: No
.gitignore
file found. Create one before committing — without it, secrets, build artifacts, and OS files can enter git history. Minimal starting point:
curl -sL https://www.toptal.com/developers/gitignore/api/macos,linux,node,python > .gitignore
Stop execution. Do not commit.
如果仓库根目录下没有
.gitignore
文件:
停止操作:未找到
.gitignore
文件。请在提交前创建该文件 — 没有它,敏感信息、构建产物和系统文件可能会进入Git历史。 最小化初始模板:
curl -sL https://www.toptal.com/developers/gitignore/api/macos,linux,node,python > .gitignore
停止执行,请勿提交。

2. Detect relevant patterns

2. 检测相关规则

Check which patterns are relevant to this repo, based on what actually exists on disk:
PatternRelevant if
.env
.env.example
exists, OR any untracked/tracked
.env
or
.env.*
file is present (excluding
.env.example
), OR
dotenv
appears in
package.json
or
requirements.txt
node_modules/
package.json
exists in the repo root or any subdirectory
__pycache__/
any
*.py
file exists in the repo
*.pyc
same as
__pycache__/
.DS_Store
repo is on macOS (
uname
returns
Darwin
)
For each relevant pattern, check if
.gitignore
contains it (exact match or substring). Collect missing ones.
根据磁盘上实际存在的内容,检查哪些规则与当前仓库相关:
规则相关条件
.env
存在
.env.example
,或者存在任何未跟踪/已跟踪的
.env
.env.*
文件(排除
.env.example
),或者
package.json
requirements.txt
中包含
dotenv
node_modules/
仓库根目录或任何子目录中存在
package.json
__pycache__/
仓库中存在任何
*.py
文件
*.pyc
__pycache__/
条件相同
.DS_Store
仓库位于macOS系统(
uname
返回
Darwin
对于每个相关规则,检查
.gitignore
中是否包含它(完全匹配或子串匹配)。收集缺失的规则。

3. Decide whether to block

3. 决定是否阻塞

  • If no relevant patterns are missing: this step is silent. Continue to Step 3.7.
  • If any relevant patterns are missing: stop and report:
    STOP:
    .gitignore
    is missing patterns relevant to this repo:
    • .env
      .env.example
      found; without this, a local
      .env
      file could be committed
    • node_modules/
      package.json
      found
    Add the missing patterns before committing: echo ".env" >> .gitignore echo "node_modules/" >> .gitignore
    To permanently suppress a specific pattern check (intentional omission), add a comment to
    .gitignore
    :

    swain-sync: allow .env

    Stop execution. Do not commit until the user resolves this.
  • 如果没有缺失相关规则:此步骤无提示,继续执行步骤3.7。
  • 如果存在缺失的相关规则:停止操作并报告:
    停止操作:
    .gitignore
    缺少与当前仓库相关的规则:
    • .env
      — 检测到
      .env.example
      ;如果没有此规则,本地
      .env
      文件可能会被提交
    • node_modules/
      — 检测到
      package.json
    请在提交前添加缺失的规则: echo ".env" >> .gitignore echo "node_modules/" >> .gitignore
    如果要永久忽略特定规则检查(有意不添加),请在
    .gitignore
    中添加注释:

    swain-sync: allow .env

    停止执行,在用户解决此问题前请勿提交。

4. Skip logic

4. 跳过逻辑

If
.gitignore
contains
# swain-sync: allow <pattern>
for a given pattern, treat that pattern as intentionally omitted and do not flag it.
如果
.gitignore
中包含
# swain-sync: allow <pattern>
注释,则该规则被视为有意忽略,不会标记为缺失。

Step 3.7 — ADR compliance check

步骤3.7 — ADR合规性检查

If modified files include any swain artifacts (
docs/spec/
,
docs/epic/
,
docs/vision/
,
docs/research/
,
docs/journey/
,
docs/persona/
,
docs/runbook/
,
docs/design/
), run an ADR compliance check against each modified artifact:
bash
bash skills/swain-design/scripts/adr-check.sh <artifact-path>
For each artifact with findings (exit code 1 — DEAD_REF or STALE), collect the output and present a single consolidated warning after all checks complete:
ADR compliance: N artifact(s) have findings that may need attention. <condensed findings summary>
This step is advisory — it warns but never blocks the commit. Continue to Step 4 regardless.
If the
adr-check.sh
script is not found or fails with exit code 2, skip silently — the check is only available in repos with swain-design installed.
如果修改的文件包含任何swain工件(
docs/spec/
docs/epic/
docs/vision/
docs/research/
docs/journey/
docs/persona/
docs/runbook/
docs/design/
),请对每个修改的工件运行ADR合规性检查:
bash
bash skills/swain-design/scripts/adr-check.sh <artifact-path>
对于每个有问题的工件(退出码1 — DEAD_REF或STALE),收集输出结果,并在所有检查完成后呈现一个合并后的警告:
ADR合规性检查:N个工件存在需要关注的问题。 <精简的问题摘要>
此步骤为建议性 — 仅发出警告,不会阻止提交。无论结果如何,继续执行步骤4。
如果未找到
adr-check.sh
脚本或脚本以退出码2失败,请静默跳过此步骤 — 该检查仅在安装了swain-design的仓库中可用。

Step 4 — Generate a commit message

步骤4 — 生成提交信息

Read the staged diff (
git --no-pager diff --cached
) and write a commit message that:
  • Opens with a conventional-commit prefix matching the dominant change type:
    • feat
      — new feature or capability
    • fix
      — bug fix
    • docs
      — documentation only
    • chore
      — tooling, deps, config with no behavior change
    • refactor
      — restructuring without behavior change
    • test
      — test additions or fixes
  • Includes a concise imperative-mood subject line (≤ 72 chars).
  • Adds a short body (2–5 lines) summarising why, not just what, when the diff is non-trivial.
  • Appends a
    Co-Authored-By
    trailer identifying the model that generated the commit. Use the model name from your system prompt (e.g.,
    Claude Opus 4.6
    ,
    Gemini 2.5 Pro
    ). If you can't determine the model name, use
    AI Assistant
    as a fallback.
Example shape:
feat(terraform): add Cloudflare DNS module for hub provisioning

Operators can now point DNS at Cloudflare without migrating their zone.
Module is activated by dns_provider=cloudflare and requires only
CLOUDFLARE_API_TOKEN — no other provider credentials are validated.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
读取已暂存的差异(
git --no-pager diff --cached
)并撰写提交信息,要求:
  • 约定式提交前缀开头,匹配主要变更类型:
    • feat
      — 新功能或能力
    • fix
      — bug修复
    • docs
      — 仅文档变更
    • chore
      — 工具、依赖、配置变更,无行为变化
    • refactor
      — 代码重构,无行为变化
    • test
      — 测试用例添加或修复
  • 包含简洁的祈使语气主题行(≤72字符)。
  • 当差异非 trivial 时,添加简短的正文(2-5行),总结原因而非仅内容
  • 追加
    Co-Authored-By
    尾注,标识生成此提交的模型。使用系统提示词中的模型名称(例如
    Claude Opus 4.6
    Gemini 2.5 Pro
    )。如果无法确定模型名称,请使用
    AI Assistant
    作为备选。
示例格式:
feat(terraform): add Cloudflare DNS module for hub provisioning

Operators can now point DNS at Cloudflare without migrating their zone.
Module is activated by dns_provider=cloudflare and requires only
CLOUDFLARE_API_TOKEN — no other provider credentials are validated.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

Step 4.5 — Pre-commit hook check

步骤4.5 — 预提交钩子检查

Check if pre-commit hooks are configured:
bash
test -f .pre-commit-config.yaml && command -v pre-commit >/dev/null 2>&1 && echo "hooks-configured" || echo "no-hooks"
If
no-hooks
, emit a one-time warning (do not repeat if the same session already warned):
WARN: No pre-commit hooks configured. Run
/swain-init
to set up security scanning.
Continue to Step 5 regardless — hooks are recommended but not required.
检查是否配置了预提交钩子:
bash
test -f .pre-commit-config.yaml && command -v pre-commit >/dev/null 2>&1 && echo "hooks-configured" || echo "no-hooks"
如果结果为
no-hooks
,发出一次性警告(如果同一会话已警告过则不再重复):
警告:未配置预提交钩子。运行
/swain-init
以设置安全扫描。
无论结果如何,继续执行步骤5 — 钩子是推荐项而非必需项。

Step 5 — Commit

步骤5 — 提交

bash
git --no-pager commit -m "$(cat <<'EOF'
<generated message here>
EOF
)"
Use a heredoc so multi-line messages survive the shell without escaping issues.
IMPORTANT: Never use
--no-verify
. If pre-commit hooks are installed, they MUST run. There is no bypass.
If the commit fails because a pre-commit hook rejected it:
  1. Parse the output to identify which hook(s) failed and what was found
  2. Present findings clearly:
    Pre-commit hook failed: gitleaks: 2 findings (describe what was flagged)
    Fix the findings and run
    /swain-sync
    again. Suppress false positives: add to
    .gitleaksignore
  3. Stop execution — do not push. Do not retry automatically.
bash
git --no-pager commit -m "$(cat <<'EOF'
<generated message here>
EOF
)"
使用here-document确保多行消息在shell中无需转义即可正常使用。
重要提示: 请勿使用
--no-verify
。如果安装了预提交钩子,必须运行。无法绕过。
如果提交因预提交钩子拒绝而失败:
  1. 解析输出以确定哪些钩子失败以及发现了什么问题
  2. 清晰呈现问题:
    预提交钩子失败: gitleaks: 2个问题(描述被标记的内容)
    请修复问题后重新运行
    /swain-sync
    。 抑制误报:添加到
    .gitleaksignore
  3. 停止执行 — 请勿推送,请勿自动重试。

Step 6 — Push

步骤6 — 推送

If
IN_WORKTREE=yes
:
push the worktree's commits directly to
main
rather than creating a remote worktree branch:
bash
git push origin HEAD:main
If this push is rejected with a non-fast-forward error:
  • Check whether the rejection message mentions branch protection rules or required reviews.
    • If branch protection is the cause, open a PR instead:
      bash
      BRANCH=$(git rev-parse --abbrev-ref HEAD)
      SUBJECT=$(git log -1 --pretty=format:'%s')
      BODY=$(git log -1 --pretty=format:'%b')
      gh pr create --base main --head "$BRANCH" --title "$SUBJECT" --body "$BODY"
      Report the PR URL. Do not retry the push. Proceed to worktree pruning below.
    • If diverged history is the cause (not branch protection), report the conflict and stop. Do not force-push.
After a successful push or PR creation, remove the worktree:
bash
WORKTREE_PATH=$(git worktree list --porcelain | grep -B2 "HEAD" | awk '/worktree/{print $2}' | grep -v "$(git rev-parse --git-common-dir | sed 's|/.git$||')")
git -C "$(git rev-parse --show-toplevel 2>/dev/null || git rev-parse --git-common-dir | sed 's|/.git||')" worktree remove --force "$WORKTREE_PATH" 2>/dev/null || true
git -C "$(git rev-parse --git-common-dir | sed 's|/.git||')" worktree prune 2>/dev/null || true
If
IN_WORKTREE=no
(main worktree, normal case):
bash
git push          # or: git push -u origin HEAD (if no upstream)
If push fails due to divergent history (shouldn't happen after Step 1 rebase, but as a safety net):
bash
git --no-pager pull --rebase
git push
如果
IN_WORKTREE=yes
将工作树的提交直接推送到
main
分支,而不是创建远程工作树分支:
bash
git push origin HEAD:main
如果推送因非快进错误被拒绝:
  • 检查拒绝消息是否提到分支保护规则或必需的审核。
    • 如果分支保护是原因,请改为创建PR:
      bash
      BRANCH=$(git rev-parse --abbrev-ref HEAD)
      SUBJECT=$(git log -1 --pretty=format:'%s')
      BODY=$(git log -1 --pretty=format:'%b')
      gh pr create --base main --head "$BRANCH" --title "$SUBJECT" --body "$BODY"
      报告PR链接。请勿重试推送。继续执行下方的工作树清理步骤。
    • 如果历史分歧是原因(非分支保护),请报告冲突并停止。请勿强制推送。
推送成功或PR创建完成后,删除工作树:
bash
WORKTREE_PATH=$(git worktree list --porcelain | grep -B2 "HEAD" | awk '/worktree/{print $2}' | grep -v "$(git rev-parse --git-common-dir | sed 's|/.git$||')")
git -C "$(git rev-parse --show-toplevel 2>/dev/null || git rev-parse --git-common-dir | sed 's|/.git||')" worktree remove --force "$WORKTREE_PATH" 2>/dev/null || true
git -C "$(git rev-parse --git-common-dir | sed 's|/.git||')" worktree prune 2>/dev/null || true
如果
IN_WORKTREE=no
(主工作树,常规情况):
bash
git push          # 或:git push -u origin HEAD(如果没有上游分支)
如果推送因历史分歧失败(步骤1的变基后不应发生,但作为安全措施):
bash
git --no-pager pull --rebase
git push

Step 7 — Verify

步骤7 — 验证

Run
git --no-pager status
and
git --no-pager log --oneline -3
to verify the push landed and show the user the final state. Do not prompt for confirmation — just report the result.
运行
git --no-pager status
git --no-pager log --oneline -3
以验证推送是否成功,并向用户显示最终状态。无需等待确认,直接报告结果。

Index rebuild (SPEC-047)

索引重建(SPEC-047)

Before committing (after staging, before Step 5), check whether any artifact index files (
list-*.md
) are stale. If
skills/swain-design/scripts/rebuild-index.sh
exists, run it for each artifact type that had changes staged:
bash
REBUILD_SCRIPT="$REPO_ROOT/skills/swain-design/scripts/rebuild-index.sh"
if [[ -x "$REBUILD_SCRIPT" ]]; then
    # Detect which types had staged changes
    for type in spec epic spike adr persona runbook design vision journey; do
        if git diff --cached --name-only | grep -q "^docs/$type/"; then
            bash "$REBUILD_SCRIPT" "$type"
            git add "docs/$type/list-${type}.md" 2>/dev/null || true
        fi
    done
fi
This ensures the index is current when the session's commits land.
提交前(暂存后,步骤5之前),检查是否有工件索引文件(
list-*.md
)已过期。如果存在
skills/swain-design/scripts/rebuild-index.sh
,请为每个有变更暂存的工件类型运行该脚本:
bash
REBUILD_SCRIPT="$REPO_ROOT/skills/swain-design/scripts/rebuild-index.sh"
if [[ -x "$REBUILD_SCRIPT" ]]; then
    # 检测哪些类型有暂存的变更
    for type in spec epic spike adr persona runbook design vision journey; do
        if git diff --cached --name-only | grep -q "^docs/$type/"; then
            bash "$REBUILD_SCRIPT" "$type"
            git add "docs/$type/list-${type}.md" 2>/dev/null || true
        fi
    done
fi
这可以确保会话的提交完成后索引是最新的。

Session bookmark

会话书签

After a successful push, update the bookmark. Use
$REPO_ROOT
(set in Step 1) as the search root so this works from both main and linked worktrees:
bash
bash "$(find "$REPO_ROOT" -path '*/swain-session/scripts/swain-bookmark.sh' -print -quit 2>/dev/null)" "Pushed {n} commits to {branch}"
推送成功后,更新书签。使用步骤1中设置的
$REPO_ROOT
作为搜索根,以便在主工作树和链接工作树中都能正常工作:
bash
bash "$(find "$REPO_ROOT" -path '*/swain-session/scripts/swain-bookmark.sh' -print -quit 2>/dev/null)" "Pushed {n} commits to {branch}"