@aipper/aiws-spec 0.0.23 → 0.0.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/package.json +1 -1
  2. package/templates/workspace/.agents/skills/ws-bugfix/SKILL.md +30 -7
  3. package/templates/workspace/.agents/skills/ws-commit/SKILL.md +3 -1
  4. package/templates/workspace/.agents/skills/ws-deliver/SKILL.md +24 -9
  5. package/templates/workspace/.agents/skills/ws-dev/SKILL.md +46 -5
  6. package/templates/workspace/.agents/skills/ws-finish/SKILL.md +77 -60
  7. package/templates/workspace/.agents/skills/ws-plan/SKILL.md +36 -1
  8. package/templates/workspace/.claude/commands/ws-bugfix.md +11 -6
  9. package/templates/workspace/.claude/commands/ws-commit.md +2 -1
  10. package/templates/workspace/.claude/commands/ws-deliver.md +6 -2
  11. package/templates/workspace/.claude/commands/ws-dev.md +5 -2
  12. package/templates/workspace/.claude/commands/ws-finish.md +19 -19
  13. package/templates/workspace/.claude/commands/ws-plan.md +9 -5
  14. package/templates/workspace/.codex/prompts/ws-dev.md +5 -1
  15. package/templates/workspace/.githooks/commit-msg +109 -0
  16. package/templates/workspace/.iflow/commands/ws-commit.toml +2 -1
  17. package/templates/workspace/.iflow/commands/ws-deliver.toml +6 -2
  18. package/templates/workspace/.iflow/commands/ws-finish.toml +19 -19
  19. package/templates/workspace/.opencode/command/ws-bugfix.md +11 -6
  20. package/templates/workspace/.opencode/command/ws-commit.md +2 -1
  21. package/templates/workspace/.opencode/command/ws-deliver.md +6 -2
  22. package/templates/workspace/.opencode/command/ws-dev.md +5 -2
  23. package/templates/workspace/.opencode/command/ws-finish.md +19 -19
  24. package/templates/workspace/.opencode/command/ws-plan.md +9 -5
  25. package/templates/workspace/AGENTS.md +1 -0
  26. package/templates/workspace/AI_WORKSPACE.md +4 -4
  27. package/templates/workspace/changes/README.md +8 -2
  28. package/templates/workspace/manifest.json +2 -0
  29. package/templates/workspace/tools/ws_change_check.py +1 -1
  30. package/templates/workspace/tools/ws_resolve_sub_target.sh +1 -0
@@ -34,34 +34,34 @@ fi
34
34
  2) (推荐)门禁校验并落盘证据:`aiws validate . --stamp`(未安装全局 aiws 时可用 `npx @aipper/aiws validate . --stamp`)。
35
35
  2.1) (强烈建议)收敛持久证据并回填 `Evidence_Path`:`aiws change evidence <change-id>`(未安装全局 aiws 时可用 `npx @aipper/aiws change evidence <change-id>`)。
36
36
  2.2) (可选)生成状态快照(建议):`aiws change state <change-id> --write`。
37
- 3) 安全合并并切回目标分支:
38
- - 若当前就在 `change/<change-id>` 分支上,可直接执行:`aiws change finish`
39
- - 否则执行:`aiws change finish <change-id>`
40
- 4) 若提示无法 fast-forward:先在 change 分支(或对应 worktree)里 `git rebase <target-branch>`,再重试 `aiws change finish`。
41
- 5) 合并成功后,按顺序处理每个 submodule(减少 detached;再 push):
37
+ 3) 若不存在 `.gitmodules`,或 submodules 已按顺序处理完成,优先直接执行最小收尾闭环:
38
+ - `aiws change finish <change-id> --push`
39
+ - 若当前就在 `change/<change-id>` 分支上,也可省略 `<change-id>`
40
+ - 该命令会在 fast-forward 合并成功后 push 目标分支;默认优先使用 upstream 配置(`branch.<name>.remote` + `branch.<name>.merge`),只有在你明确知道要推向别的 remote 时才追加 `--remote <name>`
41
+ - `change/<change-id>` 位于独立 worktree,且该 worktree 干净,则会在 push 成功后自动执行 `git worktree remove` + `git worktree prune`
42
+ - AI 入口执行前,应先向用户说明将要 push 的 remote/branch;显式传入 `--push` 即视为确认
43
+ 4) 若提示无法 fast-forward:先在 change 分支(或对应 worktree)里 `git rebase <target-branch>`,再重试 `aiws change finish --push`。
44
+ 5) 若需要先处理 submodules,则按顺序处理每个 submodule(减少 detached;再 push):
45
+ - 先显式解析 base branch,不要用当前分支名替代:
46
+ - `python3 - <<'PY'`
47
+ - `import json, pathlib; meta = pathlib.Path("changes") / "<change-id>" / ".ws-change.json"; print((json.loads(meta.read_text(encoding="utf-8")).get("base_branch") or "").strip())`
48
+ - `PY`
42
49
  - 发现 submodules:`git config --file .gitmodules --get-regexp '^submodule\\..*\\.path$'`
43
50
  - 对每个 `<sub_path>`:
44
51
  - 读取 superproject 当前 gitlink:`git rev-parse "HEAD:<sub_path>"`
45
- - 目标分支:必须在 `.gitmodules` 配置 `submodule.<name>.branch`(若为 `.` 则用当前主仓库分支;避免 origin 多分支时误判)
46
- - 不要直接切 `change/<change-id>` / `main` / `master` 来“解 detached”
52
+ - 目标分支真值:`changes/<change-id>/submodules.targets`;若条目里分支写 `.`,则展开为刚才解析出的 `base_branch`
53
+ - 生成/检查 `submodules.targets` 时,detached HEAD 默认建议取 `.gitmodules` `submodule.<name>.branch`;已附着在本地分支时默认建议取当前分支;这些都只是预填建议,不是 finish/push 的运行时真值
54
+ - 不要直接切 `change/<change-id>` / `main` / `master` 来解 detached
47
55
  - 用 pin 分支挂回(不改动现有 main/master 指针):`git -C "<sub_path>" checkout -B "aiws/pin/<target-branch>" <gitlink-sha>`
48
56
  - 仅当 `<gitlink-sha>` 属于 `origin/<target-branch>` 历史时才允许 push;否则停止并人工处理分叉
49
57
  - push(只允许 fast-forward):`git -C "<sub_path>" push origin "<gitlink-sha>:refs/heads/<target-branch>"`
50
58
  6) 任一 submodule 不满足 fast-forward 条件时立即停止(不要继续 push 主仓库)。
51
- 7) submodules 全部成功后,再 push 主仓库当前分支:
52
- - `git branch --show-current`
53
- - `git status -sb`
54
- - `git push`
55
- 8) push 成功后,清理 `change/<change-id>` 对应 worktree(若存在且不是当前 worktree):
56
- - `change_ref="refs/heads/change/<change-id>"`
57
- - `main_wt="$(git rev-parse --show-toplevel)"`
58
- - `change_wt="$(git worktree list --porcelain | awk -v ref="$change_ref" '$1=="worktree"{wt=substr($0,10)} $1=="branch"&&$2==ref{print wt; exit}')"`
59
- - 若 `change_wt` 非空且不等于 `main_wt`:先输出并让用户确认,再执行 `git worktree remove "$change_wt"`(不带 `--force`),最后 `git worktree prune`
60
- - 若 `git -C "$change_wt" status --porcelain` 非空:停止并提示先清理该 worktree
61
- 9) (可选)交付完成后归档变更工件:`aiws change archive <change-id>`。
59
+ 7) submodules 全部成功后,再回到主仓库执行:
60
+ - `aiws change finish <change-id> --push`
61
+ 8) (可选)交付完成后归档变更工件:`aiws change archive <change-id>`。
62
62
 
63
63
  安全:
64
- - push 前先输出状态并请用户确认远端/分支。
64
+ - push 前先输出状态并说明远端/分支。
65
65
  - 不执行破坏性命令。
66
66
  <!-- AIWS_MANAGED_END:claude:ws-finish -->
67
67
 
@@ -4,14 +4,18 @@
4
4
  用中文输出(命令/路径/代码标识符保持原样不翻译)。
5
5
 
6
6
  目标:
7
- - 生成可落盘执行计划(供 /ws-dev 执行)。
7
+ - 若尚未进入本次 change 的工作上下文:先建立 `change/<change-id>` 分支 / worktree,再生成可落盘执行计划(供 /ws-dev 执行)。
8
8
 
9
9
  执行建议:
10
10
  1) 先运行 `/ws-preflight`(对齐 `AI_PROJECT.md` / `REQUIREMENTS.md` / `AI_WORKSPACE.md`)。
11
- 2) 生成或更新计划文件:`plan/YYYY-MM-DD_HH-MM-SS-<slug>.md`。
12
- 3) 计划至少包含:`绑定信息(Bindings)`、`目标(Goal)`、`非目标(Non-goals)`、`范围(Scope)`、`执行计划(Plan)`、`验证(Verify)`、`风险与回滚(Risks & Rollback)`、`证据(Evidence)`。
13
- 4) 若已有 `changes/<change-id>/proposal.md`,对齐 `计划文件(Plan_File)` / `合同行(Contract_Row)` / `证据路径(Evidence_Path)`。
14
- 5) 完成后先运行 `/ws-plan-verify`,通过再进入 `/ws-dev`。
11
+ 2) 若当前不在 `change/<change-id>` 分支 / worktree,先调用 `aiws change start <change-id>` 建立上下文:
12
+ - 仓库已有提交:优先 `aiws change start <change-id> --hooks --worktree`;若声明了 submodules,加 `--submodules`
13
+ - 仓库尚无提交 / 不满足 worktree 前置条件:回退 `aiws change start <change-id> --hooks --no-switch`
14
+ 3) 若上一步创建了 worktree:切到输出的 `worktree:` 路径,后续所有计划文件都写在该 worktree 中。
15
+ 4) 生成或更新计划文件:`plan/YYYY-MM-DD_HH-MM-SS-<slug>.md`。
16
+ 5) 计划至少包含:`绑定信息(Bindings)`、`目标(Goal)`、`非目标(Non-goals)`、`范围(Scope)`、`执行计划(Plan)`、`验证(Verify)`、`风险与回滚(Risks & Rollback)`、`证据(Evidence)`。
17
+ 6) 若已有 `changes/<change-id>/proposal.md`,对齐 `计划文件(Plan_File)` / `合同行(Contract_Row)` / `证据路径(Evidence_Path)`。
18
+ 7) 完成后先运行 `/ws-plan-verify`,通过再进入 `/ws-dev`。
15
19
  <!-- AIWS_MANAGED_END:claude:ws-plan -->
16
20
 
17
21
  可在下方追加本项目对 Claude Code 的额外说明(托管块外内容会被保留)。
@@ -12,8 +12,12 @@ argument-hint: ""
12
12
 
13
13
  建议流程:
14
14
  1) 先运行 `/ws-preflight`(读真值文件并输出约束摘要)。
15
+ - 若 `$ws-plan` 刚创建了 `change/<change-id>` worktree:后续实现必须在该 worktree 中继续;不要回原工作区重复 `aiws change start ...`
15
16
  2) 建立变更归因(推荐):
16
- - 推荐一键:`aiws change start <change-id> --hooks`
17
+ - 若当前目录已经是 `change/<change-id>` worktree(例如由 `$ws-plan` 创建):直接在这里继续,不要再创建第二个 worktree,也不要回原工作区写代码。
18
+ - 若 `/ws-plan` 已生成 `plan/...` 或 `changes/<change-id>/...` 导致工作区非空,这是预期行为;此时不要直接再做 `--switch/--worktree`,优先:`aiws change start <change-id> --hooks --no-switch`,或先提交规划工件。
19
+ - 推荐一键(工作区干净时):`aiws change start <change-id> --hooks`
20
+ - 推荐更安全(默认):`aiws change start <change-id> --hooks --no-switch`
17
21
  - superproject + submodule(推荐):`aiws change start <change-id> --hooks --worktree --submodules`
18
22
  - 若后续需要在 detached submodule 内提交:先挂到 `aiws/pin/<target-branch>`;不要直接切 `change/<change-id>` / `main` / `master`
19
23
  - 或手工:`git switch -c change/<change-id>`,并创建 `changes/<change-id>/proposal.md` 与 `changes/<change-id>/tasks.md`(参考 `changes/README.md`)
@@ -0,0 +1,109 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ if [[ "${WS_CHANGE_HOOK_BYPASS:-0}" == "1" ]]; then
5
+ echo "warn: WS_CHANGE_HOOK_BYPASS=1; skip aiws commit-msg hook" >&2
6
+ exit 0
7
+ fi
8
+
9
+ msg_file="${1:-}"
10
+ [[ -n "${msg_file:-}" && -f "$msg_file" ]] || exit 0
11
+
12
+ policy="${AIWS_COMMIT_MSG_POLICY:-}"
13
+ if [[ -z "${policy:-}" ]]; then
14
+ policy="$(git config --get aiws.commitMessagePolicy 2>/dev/null || true)"
15
+ fi
16
+ policy="${policy:-warn}"
17
+
18
+ case "$policy" in
19
+ off|disabled|0)
20
+ exit 0
21
+ ;;
22
+ warn|prefer-zh)
23
+ policy="warn"
24
+ ;;
25
+ strict|require-zh|1)
26
+ policy="strict"
27
+ ;;
28
+ *)
29
+ echo "warn: unknown aiws commit message policy '${policy}', fallback to warn" >&2
30
+ policy="warn"
31
+ ;;
32
+ esac
33
+
34
+ first_line="$(sed -n '1{s/\r$//;p;q;}' "$msg_file")"
35
+ first_line="${first_line#"${first_line%%[![:space:]]*}"}"
36
+ first_line="${first_line%"${first_line##*[![:space:]]}"}"
37
+
38
+ [[ -n "${first_line:-}" ]] || exit 0
39
+
40
+ case "$first_line" in
41
+ Merge\ *|Revert\ *|fixup!\ *|squash!\ *)
42
+ exit 0
43
+ ;;
44
+ esac
45
+
46
+ has_cjk=1
47
+ if command -v python3 >/dev/null 2>&1; then
48
+ if python3 - "$first_line" <<'PY'
49
+ import re
50
+ import sys
51
+
52
+ line = sys.argv[1].strip()
53
+ sys.exit(0 if re.search(r"[\u3400-\u9fff]", line) else 1)
54
+ PY
55
+ then
56
+ has_cjk=0
57
+ fi
58
+ else
59
+ if printf '%s\n' "$first_line" | grep -q '[一-龥]'; then
60
+ has_cjk=0
61
+ fi
62
+ fi
63
+
64
+ if [[ "$has_cjk" -eq 0 ]]; then
65
+ exit 0
66
+ fi
67
+
68
+ if [[ "$policy" == "warn" ]]; then
69
+ cat >&2 <<'EOF'
70
+ warn: commit message is better in Chinese, but English is still allowed.
71
+
72
+ hint:
73
+ - preferred format: 修复: 登录页空指针
74
+ - or: 功能: 新增 submodule targets 校验
75
+ - if you are in AI tools, prefer: $ws-commit / /ws-commit / ws:commit
76
+ - set strict mode if needed: git config aiws.commitMessagePolicy strict
77
+ - disable this reminder: git config aiws.commitMessagePolicy off
78
+
79
+ allowed exceptions:
80
+ - Merge ...
81
+ - Revert ...
82
+ - fixup! ...
83
+ - squash! ...
84
+ EOF
85
+
86
+ echo "message: ${first_line}" >&2
87
+ exit 0
88
+ fi
89
+
90
+ cat >&2 <<'EOF'
91
+ error: commit message should be in Chinese (strict mode).
92
+
93
+ hint:
94
+ - use format: 修复: 登录页空指针
95
+ - or: 功能: 新增 submodule targets 校验
96
+ - if you are in AI tools, prefer: $ws-commit / /ws-commit / ws:commit
97
+ - downgrade to warn: git config aiws.commitMessagePolicy warn
98
+ - disable only when necessary: git config aiws.commitMessagePolicy off
99
+ - bypass only when necessary: WS_CHANGE_HOOK_BYPASS=1 git commit ...
100
+
101
+ allowed exceptions:
102
+ - Merge ...
103
+ - Revert ...
104
+ - fixup! ...
105
+ - squash! ...
106
+ EOF
107
+
108
+ echo "message: ${first_line}" >&2
109
+ exit 1
@@ -16,6 +16,7 @@ prompt = """
16
16
  - 不使用 `--no-verify` 绕过 hooks
17
17
  - 不自动 push
18
18
  - 不打印 secrets
19
+ - commit message 优先使用中文;若启用了 `.githooks/commit-msg`,默认仅提示,只有 strict 模式才会拒绝全英文首行(`Merge/Revert/fixup!/squash!` 例外)
19
20
 
20
21
  强制步骤:
21
22
  1) 读取真值文件:`AI_PROJECT.md`、`REQUIREMENTS.md`、`AI_WORKSPACE.md`(缺失则先运行 `/aiws-init` 或 `aiws init .`)。
@@ -36,7 +37,7 @@ prompt = """
36
37
  - `git status --porcelain`
37
38
  - `git diff --staged --submodule=short`
38
39
  8) 若没有 staged changes:停止并提示用户先明确要提交哪些文件(例如 `git add -p` 或 `git add <path>`)。
39
- 9) 让用户提供 commit message(必须确认后再执行)。
40
+ 9) 优先生成并确认中文 commit message(格式建议:`<类型>: <简述>`;若用户明确要求英文也可保留,但 strict 模式下会被 hook 拒绝)。
40
41
  10) 执行提交(不带 `--no-verify`):`git commit -m "<message>"`。
41
42
 
42
43
  输出必须包含:
@@ -8,7 +8,7 @@ description = "交付(submodules + superproject 分步提交,并安全合并
8
8
  prompt = """
9
9
  用中文输出(命令/路径/代码标识符保持原样不翻译)。
10
10
 
11
- 目标:适配 superproject + submodule(数量不固定)的交付收尾,降低“提交顺序/合并回 base 分支”出错概率:
11
+ 目标:适配 superproject + submodule(数量不固定)的交付收尾,降低提交顺序和合并回 base 分支的出错概率:
12
12
  1) 先逐个提交 submodule(每个 repo 单独确认 commit message;默认 `git add -p`)
13
13
  2) 再提交 superproject(包含 submodule gitlink 指针更新 + 自身改动/变更工件)
14
14
  3) 最后 fast-forward 合并回目标分支(复用 `aiws change finish`;建议用 `ws:finish`)
@@ -21,11 +21,15 @@ prompt = """
21
21
  建议流程(按顺序):
22
22
  1) 先确认真值文件存在并遵守:`AI_PROJECT.md` / `REQUIREMENTS.md` / `AI_WORKSPACE.md`。
23
23
  2) 如果存在 `.gitmodules` 但缺少 `submodule.<name>.branch`,先运行 `ws:submodule-setup` 并提交 `.gitmodules`(否则 `aiws validate .` 会失败,且 submodule 工作流会产生人为差异)。
24
+ 2.1) 若存在 submodule:必须准备 `changes/<change-id>/submodules.targets`,并覆盖所有 submodule path;这是本次交付的目标分支真值。
25
+ - 生成该文件时可先做默认预填:detached HEAD 默认建议取 `.gitmodules` 声明分支;已附着在本地分支时默认建议取当前分支。
26
+ - 上述都只是建议值;真正的 deliver/finish 只认 `submodules.targets`。
24
27
  2) 发现 submodules:
25
28
  - `git submodule status --recursive`
26
29
  3) 逐个提交 submodules(按上一步顺序):
27
30
  - `git -C "<sub_path>" status --porcelain`
28
- - 若当前为 detached HEAD:不要直接切 `change/<change-id>` / `main` / `master`;先按 `.gitmodules` 的目标分支挂到 `aiws/pin/<target-branch>`
31
+ - 先说明该 submodule 目标分支的来源:attached branch 默认建议取当前分支;detached HEAD 默认建议取 `.gitmodules`;若与 `submodules.targets` 已落盘值冲突,则以 `submodules.targets` 为准
32
+ - 若当前为 detached HEAD:不要直接切 `change/<change-id>` / `main` / `master`;先按 `submodules.targets`(若分支为 `.` 则展开为 `.ws-change.json` 的 `base_branch`)挂到 `aiws/pin/<target-branch>`
29
33
  - `git -C "<sub_path>" add -p`
30
34
  - `git -C "<sub_path>" diff --staged --stat`
31
35
  - 生成并让用户确认该 submodule 的 commit message(每个 repo 单独确认)
@@ -22,33 +22,33 @@ prompt = """
22
22
  5) (推荐)门禁校验并落盘证据:`aiws validate . --stamp`(未安装全局 aiws 时可用 `npx @aipper/aiws validate . --stamp`)。
23
23
  5.1) (强烈建议)收敛持久证据并回填 `Evidence_Path`:`aiws change evidence <change-id>`(未安装全局 aiws 时可用 `npx @aipper/aiws change evidence <change-id>`)。
24
24
  5.2) (可选)生成状态快照(建议):`aiws change state <change-id> --write`。
25
- 6) 安全合并(默认 fast-forward):
26
- - `change/<change-id>` 分支上:`aiws change finish`(会尝试读 `changes/<change-id>/.ws-change.json` `base_branch` 并切回目标分支)
27
- - 否则:`aiws change finish <change-id>`
28
- 7) 若提示无法 fast-forward:先在 change 分支(或对应 worktree)里执行 `git rebase <target-branch>`,再重试 `aiws change finish`。
29
- 8) 合并成功后,按顺序处理每个 submodule(先“并回目标分支”,再 push):
25
+ 6) 若不存在 `.gitmodules`,或 submodules 已按顺序处理完成,优先直接执行最小收尾闭环:
26
+ - `aiws change finish <change-id> --push`
27
+ - `change/<change-id>` 分支上也可省略 `<change-id>`;命令会尝试读取 `changes/<change-id>/.ws-change.json` 的 `base_branch`
28
+ - 该命令会在 fast-forward 合并成功后 push 目标分支;默认优先使用 upstream 配置(`branch.<name>.remote` + `branch.<name>.merge`),只有在你明确知道要推向别的 remote 时才追加 `--remote <name>`
29
+ - `change/<change-id>` 位于独立 worktree,且该 worktree 干净,则会在 push 成功后自动执行 `git worktree remove` + `git worktree prune`
30
+ - AI 入口执行前,应先向用户说明将要 push 的 remote/branch;显式传入 `--push` 即视为确认
31
+ 7) 若提示无法 fast-forward:先在 change 分支(或对应 worktree)里执行 `git rebase <target-branch>`,再重试 `aiws change finish --push`。
32
+ 8) 若需要先处理 submodules,则按顺序处理每个 submodule(先并回目标分支,再 push):
33
+ - 先显式解析 base branch,不要用当前分支名替代:
34
+ - `python3 - <<'PY'`
35
+ - `import json, pathlib; meta = pathlib.Path("changes") / "<change-id>" / ".ws-change.json"; print((json.loads(meta.read_text(encoding="utf-8")).get("base_branch") or "").strip())`
36
+ - `PY`
30
37
  - 发现 submodules:`git config --file .gitmodules --get-regexp '^submodule\\..*\\.path$'`
31
38
  - 对每个 `<sub_path>`:
32
39
  - 读取 superproject 当前 gitlink:`git rev-parse "HEAD:<sub_path>"`
33
- - 目标分支:必须在 `.gitmodules` 配置 `submodule.<name>.branch`(若为 `.` 则用当前主仓库分支;避免 origin 多分支时误判)
34
- - 不要直接切 `change/<change-id>` / `main` / `master` 来“解 detached”
40
+ - 目标分支真值:`changes/<change-id>/submodules.targets`;若条目里分支写 `.`,则展开为刚才解析出的 `base_branch`
41
+ - 生成/检查 `submodules.targets` 时,detached HEAD 默认建议取 `.gitmodules` `submodule.<name>.branch`;已附着在本地分支时默认建议取当前分支;这些都只是预填建议,不是 finish/push 的运行时真值
42
+ - 不要直接切 `change/<change-id>` / `main` / `master` 来解 detached
35
43
  - 用 pin 分支挂回(不改动现有 main/master 指针):`git -C "<sub_path>" checkout -B "aiws/pin/<target-branch>" <gitlink-sha>`
36
44
  - 仅当 `<gitlink-sha>` 属于 `origin/<target-branch>` 历史时才允许 push;否则停止并人工处理分叉
37
45
  - push(只允许 fast-forward):`git -C "<sub_path>" push origin "<gitlink-sha>:refs/heads/<target-branch>"`
38
46
  9) 任一 submodule 不满足 fast-forward 条件时立即停止(不要继续 push 主仓库)。
39
- 10) submodules push 完成后,再 push 主仓库当前分支:
40
- - `git branch --show-current`
41
- - `git status -sb`
42
- - `git push`
43
- 11) push 成功后,清理 `change/<change-id>` 对应 worktree(若存在且不是当前 worktree):
44
- - `change_ref="refs/heads/change/<change-id>"`
45
- - `main_wt="$(git rev-parse --show-toplevel)"`
46
- - `change_wt="$(git worktree list --porcelain | awk -v ref="$change_ref" '$1=="worktree"{wt=substr($0,10)} $1=="branch"&&$2==ref{print wt; exit}')"`
47
- - 若 `change_wt` 非空且不等于 `main_wt`:先输出并让用户确认,再执行 `git worktree remove "$change_wt"`(不带 `--force`),最后 `git worktree prune`
48
- - 若 `git -C "$change_wt" status --porcelain` 非空:停止并提示先清理该 worktree
49
- 12) (可选)交付完成后归档变更工件:`aiws change archive <change-id>`。
47
+ 10) submodules push 完成后,再回到主仓库执行:
48
+ - `aiws change finish <change-id> --push`
49
+ 11) (可选)交付完成后归档变更工件:`aiws change archive <change-id>`。
50
50
 
51
51
  边界:
52
- - push 前先输出状态并请用户确认远端/分支。
52
+ - push 前先输出状态并说明远端/分支。
53
53
  - 不执行破坏性命令。
54
54
  """
@@ -11,15 +11,20 @@ description: 缺陷修复:禅道 MCP 拉单、图片证据落盘并汇总 fix_
11
11
 
12
12
  建议流程:
13
13
  1) 先运行 `/ws-preflight`。
14
- 2) 建立变更工件(推荐):`aiws change start <change-id> --hooks`(superproject+submodule 可用 `--worktree --submodules`)。
15
- 3) 通过已配置 zentao MCP 拉取 bug 字段与附件列表。
16
- 4) 落盘证据到 `changes/<change-id>/bug/`:
14
+ 2) 若当前不在 `change/<change-id>` 分支 / worktree,先建立 change 上下文:
15
+ - 工作区先保持干净
16
+ - 仓库已有提交:优先 `aiws change start <change-id> --hooks --worktree`
17
+ - superproject + submodule:优先 `aiws change start <change-id> --hooks --worktree --submodules`
18
+ - 仓库尚无提交 / 不满足 worktree 前置条件:回退 `aiws change start <change-id> --hooks --no-switch`
19
+ 3) 若上一步创建了 worktree:后续 bug 证据、CSV 更新、`/ws-dev` 修复都必须在该 worktree 中继续。
20
+ 4) 通过已配置 zentao MCP 拉取 bug 字段与附件列表。
21
+ 5) 落盘证据到当前 active change 上下文的 `changes/<change-id>/bug/`:
17
22
  - `zentao-bug-<bug-id>.json`
18
23
  - `zentao-bug-<bug-id>.md`
19
24
  - `images/<bug-id>/...`
20
- 5) upsert `issues/fix_bus_issues.csv`(主键 `Bug_ID`)。
21
- 6) 进入 `/ws-dev` 修复并回填 `Fix_Status/Verify_Command/Updated_At`。
22
- 7) 质量门:`aiws change validate <change-id> --strict` + `aiws validate . --stamp`。
25
+ 6) upsert 当前 active change 上下文中的 `issues/fix_bus_issues.csv`(主键 `Bug_ID`)。
26
+ 7) 进入 `/ws-dev` 修复并回填 `Fix_Status/Verify_Command/Updated_At`。
27
+ 8) 质量门:`aiws change validate <change-id> --strict` + `aiws validate . --stamp`。
23
28
 
24
29
  强制约束:
25
30
  - 不自动 commit / push。
@@ -14,6 +14,7 @@ description: 提交:门禁/审计后提交(submodule 感知)
14
14
  - 不使用 `--no-verify` 绕过 hooks
15
15
  - 不自动 push
16
16
  - 不打印 secrets
17
+ - commit message 优先使用中文;若启用了 `.githooks/commit-msg`,默认仅提示,只有 strict 模式才会拒绝全英文首行(`Merge/Revert/fixup!/squash!` 例外)
17
18
 
18
19
  步骤(建议):
19
20
  1) 先运行 `/ws-preflight`。
@@ -54,7 +55,7 @@ git status --porcelain
54
55
  git diff --staged --submodule=short
55
56
  ```
56
57
  7) 若没有 staged changes:停止并提示用户先明确要提交哪些文件(例如 `git add -p` 或 `git add <path>`)。
57
- 8) 让用户提供 commit message(必须确认后再执行)。
58
+ 8) 优先生成并确认中文 commit message(格式建议:`<类型>: <简述>`;若用户明确要求英文也可保留,但 strict 模式下会被 hook 拒绝)。
58
59
  9) 执行提交(不带 `--no-verify`):
59
60
  ```bash
60
61
  git commit -m "<message>"
@@ -6,7 +6,7 @@ description: 交付:submodules+superproject 分步提交并安全合并回 bas
6
6
 
7
7
  用中文输出(命令/路径/代码标识符保持原样不翻译)。
8
8
 
9
- 目标:适配 superproject + submodule(数量不固定)的交付收尾,降低“提交顺序/合并回 base 分支”出错概率:
9
+ 目标:适配 superproject + submodule(数量不固定)的交付收尾,降低提交顺序和合并回 base 分支的出错概率:
10
10
  1) 先逐个提交 submodule(每个 repo 单独确认 commit message;默认 `git add -p`)
11
11
  2) 再提交 superproject(包含 submodule gitlink 指针更新 + 自身改动/变更工件)
12
12
  3) 最后 fast-forward 合并回目标分支(复用 `aiws change finish`;建议用 `/ws-finish`)
@@ -19,11 +19,15 @@ description: 交付:submodules+superproject 分步提交并安全合并回 bas
19
19
  建议流程(按顺序):
20
20
  1) 先运行 `/ws-preflight`。
21
21
  2) 如果存在 `.gitmodules` 但缺少 `submodule.<name>.branch`,先运行 `/ws-submodule-setup` 并提交 `.gitmodules`(否则 `aiws validate .` 会失败,且 submodule 工作流会产生人为差异)。
22
+ 2.1) 若存在 submodule:必须准备 `changes/<change-id>/submodules.targets`,并覆盖所有 submodule path;这是本次交付的目标分支真值。
23
+ - 生成该文件时可先做默认预填:detached HEAD 默认建议取 `.gitmodules` 声明分支;已附着在本地分支时默认建议取当前分支。
24
+ - 上述都只是建议值;真正的 deliver/finish 只认 `submodules.targets`。
22
25
  2) 发现 submodules:
23
26
  - `git submodule status --recursive`
24
27
  3) 逐个提交 submodules(按上一步顺序):
25
28
  - `git -C "<sub_path>" status --porcelain`
26
- - 若当前为 detached HEAD:不要直接切 `change/<change-id>` / `main` / `master`;先按 `.gitmodules` 的目标分支挂到 `aiws/pin/<target-branch>`
29
+ - 先说明该 submodule 目标分支的来源:attached branch 默认建议取当前分支;detached HEAD 默认建议取 `.gitmodules`;若与 `submodules.targets` 已落盘值冲突,则以 `submodules.targets` 为准
30
+ - 若当前为 detached HEAD:不要直接切 `change/<change-id>` / `main` / `master`;先按 `submodules.targets`(若分支为 `.` 则展开为 `.ws-change.json` 的 `base_branch`)挂到 `aiws/pin/<target-branch>`
27
31
  - `git -C "<sub_path>" add -p`
28
32
  - `git -C "<sub_path>" diff --staged --stat`
29
33
  - 生成并让用户确认该 submodule 的 commit message(每个 repo 单独确认)
@@ -10,10 +10,13 @@ description: 开发:在 AIWS 约束下完成小步交付
10
10
 
11
11
  建议流程:
12
12
  1) 先运行 `/ws-preflight`(读真值文件并输出约束摘要)。
13
+ - 若 `/ws-plan` 刚创建了 `change/<change-id>` worktree:后续实现必须在该 worktree 中继续;不要回原工作区重复 `aiws change start ...`
13
14
  2) 建立变更归因(推荐):
14
- - ⚠️ 开始前先确认工作区干净:`git status --porcelain` 为空;否则切分支/创建 worktree 后,未提交改动可能“看起来丢了”(worktree 只从 `HEAD` checkout,未提交内容会留在原目录)。
15
+ - ⚠️ 若准备切分支/创建 worktree,先看 `git status --porcelain`;否则切换上下文后,未提交改动可能“看起来丢了”。
16
+ - 若当前目录已经是 `change/<change-id>` worktree(例如由 `/ws-plan` 创建):直接在这里继续,不要再创建第二个 worktree,也不要回原工作区写代码。
17
+ - 若非空仅因为 `/ws-plan` 生成了 `plan/...` 或 `changes/<change-id>/...`,这是预期行为;此时优先 `aiws change start <change-id> --hooks --no-switch`,若仍要 `--switch/--worktree`,先提交这些规划工件。
15
18
  - 推荐更安全(默认):`aiws change start <change-id> --hooks --no-switch`(只创建分支/工件 + 启用 hooks;不切分支)
16
- - 准备进入实现(且工作区干净)后再切换:`git switch change/<change-id>`
19
+ - 准备进入实现时:若当前已在 `change/<change-id>` 直接继续;若需切换到该分支,先确认除规划工件外无额外未提交改动,再执行:`git switch change/<change-id>`
17
20
  - 若你明确要“一键切分支”(不推荐,且 dirty 会被拦截):`aiws change start <change-id> --hooks --switch`
18
21
  - superproject + submodule(推荐):`aiws change start <change-id> --hooks --worktree --submodules`
19
22
  - 若后续需要在 detached submodule 内提交:先挂到 `aiws/pin/<target-branch>`;不要直接切 `change/<change-id>` / `main` / `master`
@@ -37,34 +37,34 @@ fi
37
37
  2) (推荐)门禁校验并落盘证据:`aiws validate . --stamp`(未安装全局 aiws 时可用 `npx @aipper/aiws validate . --stamp`)。
38
38
  2.1) (强烈建议)收敛持久证据并回填 `Evidence_Path`:`aiws change evidence <change-id>`(未安装全局 aiws 时可用 `npx @aipper/aiws change evidence <change-id>`)。
39
39
  2.2) (可选)生成状态快照(建议):`aiws change state <change-id> --write`。
40
- 3) 安全合并并切回目标分支:
41
- - 若当前就在 `change/<change-id>` 分支上,可直接执行:`aiws change finish`
42
- - 否则执行:`aiws change finish <change-id>`
43
- 4) 若提示无法 fast-forward:先在 change 分支(或对应 worktree)里 `git rebase <target-branch>`,再重试 `aiws change finish`。
44
- 5) 合并成功后,按顺序处理每个 submodule(减少 detached;再 push):
40
+ 3) 若不存在 `.gitmodules`,或 submodules 已按顺序处理完成,优先直接执行最小收尾闭环:
41
+ - `aiws change finish <change-id> --push`
42
+ - 若当前就在 `change/<change-id>` 分支上,也可省略 `<change-id>`
43
+ - 该命令会在 fast-forward 合并成功后 push 目标分支;默认优先使用 upstream 配置(`branch.<name>.remote` + `branch.<name>.merge`),只有在你明确知道要推向别的 remote 时才追加 `--remote <name>`
44
+ - `change/<change-id>` 位于独立 worktree,且该 worktree 干净,则会在 push 成功后自动执行 `git worktree remove` + `git worktree prune`
45
+ - AI 入口执行前,应先向用户说明将要 push 的 remote/branch;显式传入 `--push` 即视为确认
46
+ 4) 若提示无法 fast-forward:先在 change 分支(或对应 worktree)里 `git rebase <target-branch>`,再重试 `aiws change finish --push`。
47
+ 5) 若需要先处理 submodules,则按顺序处理每个 submodule(减少 detached;再 push):
48
+ - 先显式解析 base branch,不要用当前分支名替代:
49
+ - `python3 - <<'PY'`
50
+ - `import json, pathlib; meta = pathlib.Path("changes") / "<change-id>" / ".ws-change.json"; print((json.loads(meta.read_text(encoding="utf-8")).get("base_branch") or "").strip())`
51
+ - `PY`
45
52
  - 发现 submodules:`git config --file .gitmodules --get-regexp '^submodule\\..*\\.path$'`
46
53
  - 对每个 `<sub_path>`:
47
54
  - 读取 superproject 当前 gitlink:`git rev-parse "HEAD:<sub_path>"`
48
- - 目标分支:必须在 `.gitmodules` 配置 `submodule.<name>.branch`(若为 `.` 则用当前主仓库分支;避免 origin 多分支时误判)
49
- - 不要直接切 `change/<change-id>` / `main` / `master` 来“解 detached”
55
+ - 目标分支真值:`changes/<change-id>/submodules.targets`;若条目里分支写 `.`,则展开为刚才解析出的 `base_branch`
56
+ - 生成/检查 `submodules.targets` 时,detached HEAD 默认建议取 `.gitmodules` `submodule.<name>.branch`;已附着在本地分支时默认建议取当前分支;这些都只是预填建议,不是 finish/push 的运行时真值
57
+ - 不要直接切 `change/<change-id>` / `main` / `master` 来解 detached
50
58
  - 用 pin 分支挂回(不改动现有 main/master 指针):`git -C "<sub_path>" checkout -B "aiws/pin/<target-branch>" <gitlink-sha>`
51
59
  - 仅当 `<gitlink-sha>` 属于 `origin/<target-branch>` 历史时才允许 push;否则停止并人工处理分叉
52
60
  - push(只允许 fast-forward):`git -C "<sub_path>" push origin "<gitlink-sha>:refs/heads/<target-branch>"`
53
61
  6) 任一 submodule 不满足 fast-forward 条件时立即停止(不要继续 push 主仓库)。
54
- 7) submodules 全部成功后,再 push 主仓库当前分支:
55
- - `git branch --show-current`
56
- - `git status -sb`
57
- - `git push`
58
- 8) push 成功后,清理 `change/<change-id>` 对应 worktree(若存在且不是当前 worktree):
59
- - `change_ref="refs/heads/change/<change-id>"`
60
- - `main_wt="$(git rev-parse --show-toplevel)"`
61
- - `change_wt="$(git worktree list --porcelain | awk -v ref="$change_ref" '$1=="worktree"{wt=substr($0,10)} $1=="branch"&&$2==ref{print wt; exit}')"`
62
- - 若 `change_wt` 非空且不等于 `main_wt`:先输出并让用户确认,再执行 `git worktree remove "$change_wt"`(不带 `--force`),最后 `git worktree prune`
63
- - 若 `git -C "$change_wt" status --porcelain` 非空:停止并提示先清理该 worktree
64
- 9) (可选)交付完成后归档变更工件:`aiws change archive <change-id>`。
62
+ 7) submodules 全部成功后,再回到主仓库执行:
63
+ - `aiws change finish <change-id> --push`
64
+ 8) (可选)交付完成后归档变更工件:`aiws change archive <change-id>`。
65
65
 
66
66
  安全:
67
- - push 前先输出状态并请用户确认远端/分支。
67
+ - push 前先输出状态并说明远端/分支。
68
68
  - 不执行破坏性命令。
69
69
  <!-- AIWS_MANAGED_END:opencode:ws-finish -->
70
70
 
@@ -7,14 +7,18 @@ description: 规划:生成可落盘 plan 工件
7
7
  用中文输出(命令/路径/代码标识符保持原样不翻译)。
8
8
 
9
9
  目标:
10
- - 生成可落盘执行计划(供 /ws-dev 执行)。
10
+ - 若尚未进入本次 change 的工作上下文:先建立 `change/<change-id>` 分支 / worktree,再生成可落盘执行计划(供 /ws-dev 执行)。
11
11
 
12
12
  执行建议:
13
13
  1) 先运行 `/ws-preflight`(对齐 `AI_PROJECT.md` / `REQUIREMENTS.md` / `AI_WORKSPACE.md`)。
14
- 2) 生成或更新计划文件:`plan/YYYY-MM-DD_HH-MM-SS-<slug>.md`。
15
- 3) 计划至少包含:`Bindings`、`Goal`、`Non-goals`、`Scope`、`Plan`、`Verify`、`Risks & Rollback`、`Evidence`。
16
- 4) 若已有 `changes/<change-id>/proposal.md`,对齐 `Plan_File` / `Contract_Row` / `Evidence_Path`。
17
- 5) 完成后先运行 `/ws-plan-verify`,通过再进入 `/ws-dev`。
14
+ 2) 若当前不在 `change/<change-id>` 分支 / worktree,先调用 `aiws change start <change-id>` 建立上下文:
15
+ - 仓库已有提交:优先 `aiws change start <change-id> --hooks --worktree`;若声明了 submodules,加 `--submodules`
16
+ - 仓库尚无提交 / 不满足 worktree 前置条件:回退 `aiws change start <change-id> --hooks --no-switch`
17
+ 3) 若上一步创建了 worktree:切到输出的 `worktree:` 路径,后续所有计划文件都写在该 worktree 中。
18
+ 4) 生成或更新计划文件:`plan/YYYY-MM-DD_HH-MM-SS-<slug>.md`。
19
+ 5) 计划至少包含:`Bindings`、`Goal`、`Non-goals`、`Scope`、`Plan`、`Verify`、`Risks & Rollback`、`Evidence`。
20
+ 6) 若已有 `changes/<change-id>/proposal.md`,对齐 `Plan_File` / `Contract_Row` / `Evidence_Path`。
21
+ 7) 完成后先运行 `/ws-plan-verify`,通过再进入 `/ws-dev`。
18
22
  <!-- AIWS_MANAGED_END:opencode:ws-plan -->
19
23
 
20
24
  可在下方追加本项目对 OpenCode 的额外说明(托管块外内容会被保留)。
@@ -8,6 +8,7 @@
8
8
  协作约束(建议最小集):
9
9
  - 每次变更使用分支 `change/<change-id>`,并维护 `changes/<change-id>/proposal.md`、`tasks.md`(可选 `design.md`)
10
10
  - 启用本机门禁(推荐):`aiws hooks install .`(或手工:`git config core.hooksPath .githooks`;`git commit`/`git push` 会自动跑 `aiws validate .`)
11
+ - 启用 hooks 后,`.githooks/commit-msg` 默认提示优先使用中文 commit message;如需强制,可执行:`git config aiws.commitMessagePolicy strict`
11
12
  - 提交前校验(强制门禁):`aiws validate .`(包含:漂移检测 + `ws_change_check` + `requirements_contract`)
12
13
  - Codex(推荐):本仓库内置 repo skills:`.agents/skills/`(可显式 `$ws-dev`,也可隐式套用工作流)
13
14
  - Codex skills 命名约定:`ws-*` 为常用链路入口;`p-*` 为私有原子入口(一般不需要直接调用)。
@@ -80,10 +80,10 @@
80
80
  - 先 `/server-test-plan` → `/server-test` 跑到验收通过
81
81
  - 再 `/server-commit` 对 `server_dirs` 指定的 submodule 做 commit,并在工作区根仓库提交 submodule 指针更新
82
82
 
83
- 建议 commit message 保持通用简洁(Conventional Commits 风格),例如:
84
- - `fix(api): ...`
85
- - `chore(server): api test fixes`
86
- - `chore(workspace): bump server submodules`
83
+ 建议 commit message 保持通用简洁,优先使用中文;英文也可以,例如:
84
+ - `修复: API 健康检查空指针`
85
+ - `构建: 调整 server 测试脚本`
86
+ - `杂项: 更新 workspace submodule 指针`
87
87
 
88
88
  ## 3) 测试入口
89
89
 
@@ -28,6 +28,7 @@ changes/
28
28
  - 可选:`--worktree-dir <path>` 覆盖 worktree 目录
29
29
  - 可选:`--submodules` 在 worktree 内执行 `git submodule update --init --recursive`
30
30
  - `aiws change finish <change-id>`(安全合并:fast-forward 合并回目标分支;在 `change/<change-id>` 分支上执行时会尝试使用 `.ws-change.json` 的 `base_branch` 作为目标分支)
31
+ - `aiws change finish <change-id> --push [--remote <name>]`(最小收尾闭环:若存在 `.gitmodules` 则先按 `changes/<id>/submodules.targets` 顺序 push submodule,再 push 目标分支;默认优先遵循 upstream 配置;若 `change/<change-id>` 位于独立 worktree,且该 worktree 干净,则在 push 成功后自动执行 worktree cleanup;含 submodule 的 worktree 会因 Git 原生限制在 clean-check 后内部走 `git worktree remove --force`)
31
32
  - `aiws change new <change-id>`
32
33
  - `aiws change list`
33
34
  - `aiws change status <change-id>`
@@ -41,9 +42,10 @@ Active change(推荐,团队共享):
41
42
  - 切到该分支后,可省略 `<change-id>` 执行:`aiws change status|next|validate|sync|archive`
42
43
 
43
44
  计划质量门(推荐,执行前):
44
- - 先生成计划:`$ws-plan`
45
+ - 先生成计划:`$ws-plan`(若尚未进入 `change/<change-id>` 上下文,应先由它调用 `aiws change start <change-id>` 建立分支 / worktree,再在该上下文内写 `plan/...`)
45
46
  - 再执行计划质检:`$ws-plan-verify`
46
47
  - 最后进入实现:`$ws-dev`
48
+ - 若 `$ws-plan` 创建了独立 worktree,后续 `$ws-plan-verify` / `$ws-dev` / `$ws-finish` 都应优先在该 worktree 内继续
47
49
  - 目标:确保计划具备主索引绑定、步骤不过长、验证命令可复现且有预期结果
48
50
 
49
51
  模板覆盖(可选):
@@ -59,7 +61,10 @@ Active change(推荐,团队共享):
59
61
  - 若真值文件(`AI_PROJECT.md` / `AI_WORKSPACE.md` / `REQUIREMENTS.md`)在变更期间发生变化,严格校验/归档会要求先运行 `aiws change sync <change-id>` 确认基线。
60
62
 
61
63
  Hooks/CI(推荐,硬约束):
62
- - 工作区会安装 `.githooks/{pre-commit,pre-push}`(默认执行 `aiws validate .`)与门禁脚本(`tools/ws_change_check.py`、`tools/requirements_contract.py`)。
64
+ - 工作区会安装 `.githooks/{commit-msg,pre-commit,pre-push}`:
65
+ - `commit-msg`:默认提示优先使用中文 commit message;若执行 `git config aiws.commitMessagePolicy strict`,则会拒绝全英文首行(`Merge/Revert/fixup!/squash!` 例外)
66
+ - `pre-commit` / `pre-push`:默认执行 `aiws validate .`
67
+ - 还会安装门禁脚本(`tools/ws_change_check.py`、`tools/requirements_contract.py`)。
63
68
  - **启用 hooks 需要本地配置**(不会自动提交到 git):
64
69
  - 直接启用:`git config core.hooksPath .githooks`
65
70
  - 或使用:`aiws hooks install .`(等价)
@@ -69,4 +74,5 @@ Hooks/CI(推荐,硬约束):
69
74
  合并建议(减少人为 merge 引入新问题):
70
75
  - 优先使用 worktree + fast-forward 合并:
71
76
  - 完成后在主工作区(例如 `main`)执行:`aiws change finish <change-id>`(等价于 `git merge --ff-only change/<change-id>`)
77
+ - 若还要顺手 push 并清理独立 change worktree:`aiws change finish <change-id> --push`
72
78
  - 若失败:先在 worktree 里 `git rebase main`(或更新 main 后再 rebase),再重试 `--ff-only`
@@ -17,6 +17,7 @@
17
17
  "REQUIREMENTS.md",
18
18
  ".gitignore",
19
19
  ".aiws/manifest.json",
20
+ ".githooks/commit-msg",
20
21
  ".githooks/pre-commit",
21
22
  ".githooks/pre-push",
22
23
  "changes/README.md",
@@ -212,6 +213,7 @@
212
213
  ],
213
214
  "replace_file": [
214
215
  ".aiws/manifest.json",
216
+ ".githooks/commit-msg",
215
217
  ".githooks/pre-commit",
216
218
  ".githooks/pre-push",
217
219
  "tools/ws_change_check.py",
@@ -53,7 +53,7 @@ def list_submodules_from_gitmodules(root: Path) -> List[Tuple[str, str]]:
53
53
  continue
54
54
  key = line[:idx].strip()
55
55
  sub_path = line[idx + 1 :].strip()
56
- m = re.match(r"^submodule\.([^.]+)\.path$", key)
56
+ m = re.match(r"^submodule\.(.+)\.path$", key)
57
57
  if not m:
58
58
  continue
59
59
  name = (m.group(1) or "").strip()
@@ -28,6 +28,7 @@ ws_resolve_sub_target() {
28
28
  if [[ -n "${override:-}" ]]; then
29
29
  _resolved_branch="${override%% *}"
30
30
  _resolved_remote="${override#* }"
31
+ if [[ "${_resolved_branch:-}" == "." ]]; then _resolved_branch="${base_branch}"; fi
31
32
  else
32
33
  # Priority 2: .gitmodules submodule.<name>.branch
33
34
  local cfg_branch