@aipper/aiws-spec 0.0.12 → 0.0.15

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 (24) hide show
  1. package/package.json +1 -1
  2. package/templates/workspace/.agents/skills/ws-deliver/SKILL.md +22 -1
  3. package/templates/workspace/.agents/skills/ws-finish/SKILL.md +46 -23
  4. package/templates/workspace/.agents/skills/ws-preflight/SKILL.md +3 -0
  5. package/templates/workspace/.agents/skills/ws-pull/SKILL.md +4 -3
  6. package/templates/workspace/.agents/skills/ws-push/SKILL.md +105 -0
  7. package/templates/workspace/.agents/skills/ws-submodule-setup/SKILL.md +65 -0
  8. package/templates/workspace/.claude/commands/ws-deliver.md +1 -1
  9. package/templates/workspace/.claude/commands/ws-finish.md +25 -6
  10. package/templates/workspace/.claude/commands/ws-pull.md +2 -2
  11. package/templates/workspace/.claude/commands/ws-push.md +90 -0
  12. package/templates/workspace/.claude/commands/ws-submodule-setup.md +54 -0
  13. package/templates/workspace/.iflow/commands/ws-deliver.toml +1 -1
  14. package/templates/workspace/.iflow/commands/ws-finish.toml +6 -5
  15. package/templates/workspace/.iflow/commands/ws-pull.toml +1 -0
  16. package/templates/workspace/.iflow/commands/ws-push.toml +40 -0
  17. package/templates/workspace/.iflow/commands/ws-submodule-setup.toml +32 -0
  18. package/templates/workspace/.opencode/command/ws-deliver.md +1 -0
  19. package/templates/workspace/.opencode/command/ws-finish.md +25 -6
  20. package/templates/workspace/.opencode/command/ws-pull.md +2 -2
  21. package/templates/workspace/.opencode/command/ws-push.md +93 -0
  22. package/templates/workspace/.opencode/command/ws-submodule-setup.md +57 -0
  23. package/templates/workspace/AGENTS.md +1 -1
  24. package/templates/workspace/manifest.json +14 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aipper/aiws-spec",
3
- "version": "0.0.12",
3
+ "version": "0.0.15",
4
4
  "description": "AIWS spec and templates (single source of truth).",
5
5
  "type": "module",
6
6
  "files": [
@@ -27,6 +27,26 @@ description: 交付(submodules + superproject 分步提交,并安全合并
27
27
 
28
28
  建议流程(按顺序):
29
29
 
30
+ ## 0) submodule branch 真值检查(减少 detached 与人为差异)
31
+ 如果存在 `.gitmodules` 但缺少 `submodule.<name>.branch`,先运行 `$ws-submodule-setup` 并提交 `.gitmodules`,否则后续 `aiws validate .` 会失败,且 `ws-pull/ws-finish` 无法确定性工作。
32
+ ```bash
33
+ if [[ -f .gitmodules ]]; then
34
+ missing=0
35
+ while read -r key sub_path; do
36
+ name="${key#submodule.}"; name="${name%.path}"
37
+ b="$(git config --file .gitmodules --get "submodule.${name}.branch" 2>/dev/null || true)"
38
+ if [[ -z "${b:-}" ]]; then
39
+ echo "error: missing .gitmodules submodule.${name}.branch (path=${sub_path})"
40
+ missing=1
41
+ fi
42
+ done < <(git config --file .gitmodules --get-regexp '^submodule\\..*\\.path$' 2>/dev/null || true)
43
+ if [[ "$missing" -ne 0 ]]; then
44
+ echo "hint: run $ws-submodule-setup (and commit .gitmodules), then retry"
45
+ exit 2
46
+ fi
47
+ fi
48
+ ```
49
+
30
50
  ## A) 发现 submodules 清单(数量不固定)
31
51
  在 superproject 根目录执行:
32
52
  ```bash
@@ -43,7 +63,8 @@ git -C "$sub_path" branch --show-current
43
63
  git -C "$sub_path" status --porcelain
44
64
  ```
45
65
  2) 若 submodule 处于 detached HEAD(`branch --show-current` 为空):
46
- - 默认建议:在该 submodule 内创建并切到同名 change 分支(与 superproject 对齐),例如 `change/<change-id>`:
66
+ - 说明:这通常是因为 superproject gitlink checkout(例如 `git submodule update`)导致 detached。
67
+ - 若你要在该 submodule 里提交:在 submodule 内创建并切到同名 change 分支(与 superproject 对齐),例如 `change/<change-id>`:
47
68
  ```bash
48
69
  git -C "$sub_path" switch -c "change/<change-id>"
49
70
  ```
@@ -15,8 +15,27 @@ description: 收尾(门禁 + 安全合并 + submodule→主仓库顺序 push
15
15
  - 工作区是干净的:`git status --porcelain` 无输出(若有未提交改动:先 commit 或 stash)
16
16
  - change 分支已存在:`change/<change-id>`(也支持 `changes/`、`ws/`、`ws-change/`)
17
17
  - 若使用 worktree:在“目标分支所在 worktree”执行(`aiws change finish` 会提示正确的 worktree)
18
+ - 若存在 `.gitmodules`:必须为每个 submodule 配置 `submodule.<name>.branch`(否则无法确定性减少 detached;先运行 `$ws-submodule-setup` 并提交 `.gitmodules`)
18
19
 
19
20
  建议步骤:
21
+ 0) 若存在 `.gitmodules`,先检查 submodule branch 配置是否齐全(缺失则停止并提示 setup):
22
+ ```bash
23
+ if [[ -f .gitmodules ]]; then
24
+ missing=0
25
+ while read -r key sub_path; do
26
+ name="${key#submodule.}"; name="${name%.path}"
27
+ b="$(git config --file .gitmodules --get "submodule.${name}.branch" 2>/dev/null || true)"
28
+ if [[ -z "${b:-}" ]]; then
29
+ echo "error: missing .gitmodules submodule.${name}.branch (path=${sub_path})"
30
+ missing=1
31
+ fi
32
+ done < <(git config --file .gitmodules --get-regexp '^submodule\\..*\\.path$' 2>/dev/null || true)
33
+ if [[ "$missing" -ne 0 ]]; then
34
+ echo "hint: run $ws-submodule-setup (and commit .gitmodules), then retry"
35
+ exit 2
36
+ fi
37
+ fi
38
+ ```
20
39
  1) (推荐)先跑一次门禁并落盘证据:
21
40
  ```bash
22
41
  if [[ -x "./node_modules/.bin/aiws" ]]; then
@@ -49,39 +68,43 @@ base_branch="$(git branch --show-current)"
49
68
  git config --file .gitmodules --get-regexp '^submodule\..*\.path$' 2>/dev/null || true
50
69
 
51
70
  # 对每个 submodule.<name>.path <sub_path>:
52
- # 1) 计算 superproject 当前 HEAD 记录的 gitlink commit
71
+ # 说明:`git submodule update` 会把 submodule checkout 到固定 gitlink commit,导致 detached HEAD。
72
+ # 为减少“游离状态”的协作摩擦,本步骤采用“pin 分支”策略:
73
+ # - 仅在 `.gitmodules` 明确配置了 `submodule.<name>.branch` 时执行(避免 origin 多分支导致误判)
74
+ # - 不改动 submodule 现有分支指针(例如不强行移动 main/master)
75
+ # - 创建/更新本地 pin 分支:`aiws/pin/<target_branch>` 指向 gitlink commit,并将其 upstream 设为 `origin/<target_branch>`
53
76
  sub_sha="$(git rev-parse "HEAD:<sub_path>")"
54
- # 2) 推断目标分支(优先:.gitmodules 的 submodule.<name>.branch;否则 origin/HEAD;否则 main/master)
55
77
  cfg_branch="$(git config --file .gitmodules --get "submodule.<name>.branch" 2>/dev/null || true)"
56
78
  if [[ "${cfg_branch:-}" == "." ]]; then cfg_branch="$base_branch"; fi
57
- origin_head="$(git -C "<sub_path>" symbolic-ref --short refs/remotes/origin/HEAD 2>/dev/null | sed 's#^origin/##' || true)"
58
- target_branch="${cfg_branch:-${origin_head:-}}"
59
- if [[ -z "${target_branch:-}" ]]; then
60
- if git -C "<sub_path>" show-ref --verify --quiet refs/heads/main || git -C "<sub_path>" show-ref --verify --quiet refs/remotes/origin/main; then
61
- target_branch="main"
62
- else
63
- target_branch="master"
64
- fi
79
+ if [[ -z "${cfg_branch:-}" ]]; then
80
+ echo "[warn] <sub_path>: missing .gitmodules submodule.<name>.branch; keep detached and skip auto-push"
81
+ continue
65
82
  fi
83
+ target_branch="$cfg_branch"
84
+ pin_branch="aiws/pin/${target_branch}"
66
85
 
67
- # 3) 切到目标分支(若本地无分支则从 origin 跟踪创建)
68
- git -C "<sub_path>" fetch origin
69
- git -C "<sub_path>" switch "$target_branch" || git -C "<sub_path>" switch -c "$target_branch" --track "origin/$target_branch"
86
+ git -C "<sub_path>" fetch origin --prune
87
+ if ! git -C "<sub_path>" show-ref --verify --quiet "refs/remotes/origin/${target_branch}"; then
88
+ echo "[warn] <sub_path>: origin/${target_branch} not found; keep detached and skip auto-push"
89
+ continue
90
+ fi
70
91
 
71
- # 4) fast-forward 到 gitlink commit(非 ff 直接停止,避免错误合并)
72
- git -C "<sub_path>" status -sb
73
- if git -C "<sub_path>" merge-base --is-ancestor "$sub_sha" HEAD; then
74
- echo "[skip] <sub_path> already contains $sub_sha"
75
- else
76
- git -C "<sub_path>" merge --ff-only "$sub_sha"
92
+ # 仅当 gitlink commit 属于 origin/<target_branch> 的历史时才“挂回分支”
93
+ if ! git -C "<sub_path>" merge-base --is-ancestor "${sub_sha}" "origin/${target_branch}"; then
94
+ echo "[warn] <sub_path>: ${sub_sha} is not in origin/${target_branch}; keep detached and stop (need manual reconcile)"
95
+ exit 1
77
96
  fi
78
97
 
79
- # 5) push(若无 upstream 则首次 -u)
80
- git -C "<sub_path>" push || git -C "<sub_path>" push -u origin "$target_branch"
98
+ git -C "<sub_path>" checkout -B "${pin_branch}" "${sub_sha}"
99
+ git -C "<sub_path>" branch --set-upstream-to "origin/${target_branch}" "${pin_branch}" >/dev/null 2>&1 || true
100
+ git -C "<sub_path>" status -sb
101
+
102
+ # push:只允许 fast-forward(若远端分叉会被拒绝;此时必须人工处理)
103
+ git -C "<sub_path>" push origin "${sub_sha}:refs/heads/${target_branch}"
81
104
  ```
82
105
  规则:
83
- - 每个 submodule 必须先执行“切目标分支 + `merge --ff-only <gitlink-sha>`”,再 push;禁止在 detached HEAD 直接 push。
84
- - 若任一 submodule 不能 fast-forward(`merge --ff-only` 失败),立即停止,先人工处理冲突/分叉,再继续。
106
+ - 每个 submodule 必须先执行“pin 分支挂回 + fast-forward push”,再 push 主仓库。
107
+ - 若任一 submodule gitlink commit 不在 `origin/<target_branch>` 历史中:立即停止,先人工处理分叉,再继续。
85
108
 
86
109
  5) 仅当 submodules 全部成功后,再 push superproject 当前分支:
87
110
  ```bash
@@ -23,6 +23,9 @@ description: 预检(提交前快速检查与建议)
23
23
  - `Found:` <实际读取到的文件列表>
24
24
  - `Missing:` <缺失文件列表>
25
25
  - `Key rules:` 3–8 条 bullet(范围/禁止项/必须产物/必须验证命令)
26
+ 5) 若存在 `.gitmodules`:
27
+ - 输出 submodule 列表:`git config --file .gitmodules --get-regexp '^submodule\\..*\\.path$'`
28
+ - 检查每个 submodule 是否配置 `submodule.<name>.branch`(缺失则提示先运行 `$ws-submodule-setup`;否则 `aiws validate .` 会失败)
26
29
 
27
30
  安全:
28
31
  - 不打印 secrets;遇到疑似敏感值只提示“存在风险”但不要复述原文。
@@ -13,7 +13,7 @@ description: 拉取并对齐 submodules(避免 detached;减少人为差异
13
13
  - 安全拉取 superproject(默认只允许 fast-forward)
14
14
  - 初始化/同步 submodules(`--init --recursive`)
15
15
  - 在**不改动 superproject gitlink commit** 的前提下,尽量把每个 submodule 从 detached HEAD “挂回”到一个**确定的目标分支**(仅当该分支包含 gitlink commit 时)
16
- - 若需要长期稳定地“挂回分支”,给出一次性 `.gitmodules` branch 配置建议(方案 2)
16
+ - 若未配置 `.gitmodules` 的 `submodule.<name>.branch`:保持 detached,并提示先运行 `$ws-submodule-setup`
17
17
 
18
18
  安全约束(强制):
19
19
  - 不执行破坏性命令(不 `reset --hard` 主仓库;不改动远端)
@@ -50,7 +50,7 @@ fi
50
50
  说明:
51
51
  - 该步骤不会改变 superproject 的 gitlink commit(也不会让主仓库出现“submodule modified”)。
52
52
  - 只在目标分支包含该 gitlink commit 时才执行“挂回”;否则保持 detached 并提示原因。
53
- - 为了避免 origin 分支较多时“猜错分支”,本步骤**只在** `.gitmodules` 明确配置了 `submodule.<name>.branch` 时才执行“挂回分支”;否则保持 detached 并提示先对齐配置(见步骤 4)。
53
+ - 为了避免 origin 分支较多时“猜错分支”,本步骤**只在** `.gitmodules` 明确配置了 `submodule.<name>.branch` 时才执行“挂回分支”;否则保持 detached 并提示先运行 `$ws-submodule-setup` 对齐配置。
54
54
 
55
55
  ```bash
56
56
  if [[ -f .gitmodules ]]; then
@@ -71,7 +71,7 @@ if [[ -f .gitmodules ]]; then
71
71
  cfg_branch="$(git config --file .gitmodules --get "submodule.${name}.branch" 2>/dev/null || true)"
72
72
  if [[ "${cfg_branch:-}" == "." ]]; then cfg_branch="$base_branch"; fi
73
73
  if [[ -z "${cfg_branch:-}" ]]; then
74
- echo "[warn] ${sub_path}: missing .gitmodules submodule.${name}.branch; keep detached (run step 4 to set it)"
74
+ echo "[warn] ${sub_path}: missing .gitmodules submodule.${name}.branch; keep detached (run ws-submodule-setup to set it)"
75
75
  continue
76
76
  fi
77
77
 
@@ -112,6 +112,7 @@ git commit -m "chore(submodule): set tracking branch"
112
112
  建议:
113
113
  - 只有当团队明确希望“子模块按分支滚动”(而不是锁定固定 commit)时,才采用方案 2。
114
114
  - 若只是想避免 detached 但仍锁定 gitlink commit:优先使用步骤 3(不改 `.gitmodules`)。
115
+ - 推荐用 `$ws-submodule-setup` 交互式对齐所有 submodules 的 branch,并提交 `.gitmodules`。
115
116
 
116
117
  输出要求:
117
118
  - `Context:` 当前分支 + 是否存在 `.gitmodules` + submodule 列表
@@ -0,0 +1,105 @@
1
+ ---
2
+ name: ws-push
3
+ description: 推送(submodule 感知:先 submodules 后 superproject;fast-forward 安全)
4
+ ---
5
+
6
+ 用中文输出(命令/路径/代码标识符保持原样不翻译)。
7
+
8
+ 目标:
9
+ - 在不自动提交的前提下,安全 push 当前仓库
10
+ - 若仓库包含 submodules:按 `submodules -> superproject` 顺序 push,减少“别人拉取后子模块 detached/分叉”的协作摩擦
11
+ - 若不包含 submodules:按普通 git 仓库的规则 push
12
+
13
+ 安全约束(强制):
14
+ - 不自动提交、不自动 `git add -A`
15
+ - 不使用 `--force` / `--force-with-lease`
16
+ - 默认只允许 fast-forward push(发现分叉则停止并提示人工处理)
17
+ - 若工作区不干净:停止并要求先 commit 或 stash
18
+
19
+ 执行步骤(建议):
20
+ 0) 输出上下文:
21
+ ```bash
22
+ git branch --show-current
23
+ git status --porcelain
24
+ git status -sb
25
+ ```
26
+ 若 `git status --porcelain` 非空:停止。
27
+
28
+ 1) 判断是否存在 submodules:
29
+ ```bash
30
+ if [[ -f .gitmodules ]]; then
31
+ git config --file .gitmodules --get-regexp '^submodule\\..*\\.path$' || true
32
+ else
33
+ echo "[info] no .gitmodules"
34
+ fi
35
+ ```
36
+
37
+ 2) 若不存在 `.gitmodules` 或没有 submodule 条目:按普通仓库 push(仍需用户确认):
38
+ ```bash
39
+ git remote -v
40
+ git push
41
+ ```
42
+
43
+ 3) 若存在 submodules:先检查 `.gitmodules` 的 branch 真值是否齐全(缺失则停止并提示 setup):
44
+ ```bash
45
+ missing=0
46
+ while read -r key sub_path; do
47
+ name="${key#submodule.}"; name="${name%.path}"
48
+ b="$(git config --file .gitmodules --get "submodule.${name}.branch" 2>/dev/null || true)"
49
+ if [[ -z "${b:-}" ]]; then
50
+ echo "error: missing .gitmodules submodule.${name}.branch (path=${sub_path})"
51
+ missing=1
52
+ fi
53
+ done < <(git config --file .gitmodules --get-regexp '^submodule\\..*\\.path$' 2>/dev/null || true)
54
+ if [[ "$missing" -ne 0 ]]; then
55
+ echo "hint: run $ws-submodule-setup (and commit .gitmodules), then retry"
56
+ exit 2
57
+ fi
58
+ ```
59
+
60
+ 4) 逐个 push submodules(fast-forward only),再 push superproject:
61
+ ```bash
62
+ base_branch="$(git branch --show-current)"
63
+
64
+ git config --file .gitmodules --get-regexp '^submodule\\..*\\.path$' 2>/dev/null \
65
+ | while read -r key sub_path; do
66
+ name="${key#submodule.}"; name="${name%.path}"
67
+ echo "== submodule: ${sub_path} (${name}) =="
68
+
69
+ # submodule 工作区必须干净
70
+ if [[ -n "$(git -C "${sub_path}" status --porcelain 2>/dev/null || true)" ]]; then
71
+ echo "error: submodule dirty: ${sub_path}"
72
+ exit 2
73
+ fi
74
+
75
+ cfg_branch="$(git config --file .gitmodules --get "submodule.${name}.branch" 2>/dev/null || true)"
76
+ if [[ "${cfg_branch:-}" == "." ]]; then cfg_branch="$base_branch"; fi
77
+ target_branch="${cfg_branch}"
78
+
79
+ git -C "${sub_path}" fetch origin --prune
80
+ if ! git -C "${sub_path}" show-ref --verify --quiet "refs/remotes/origin/${target_branch}"; then
81
+ echo "error: missing origin/${target_branch} for ${sub_path}"
82
+ exit 2
83
+ fi
84
+
85
+ # fast-forward only: origin/<branch> 必须是 HEAD 的祖先
86
+ if ! git -C "${sub_path}" merge-base --is-ancestor "origin/${target_branch}" HEAD; then
87
+ echo "error: non-fast-forward (submodule=${sub_path}, branch=${target_branch})"
88
+ echo "hint: rebase/merge in submodule, then retry"
89
+ exit 2
90
+ fi
91
+
92
+ # push HEAD -> origin/<branch>(不 force)
93
+ git -C "${sub_path}" push origin "HEAD:refs/heads/${target_branch}"
94
+ done
95
+
96
+ # 最后 push superproject(仍需用户确认远端/分支)
97
+ git remote -v
98
+ git push
99
+ ```
100
+
101
+ 输出要求:
102
+ - `Context:` 当前分支 + 是否有 submodules
103
+ - `Submodules:` 每个 submodule push 的目标分支与结果(成功/阻断原因)
104
+ - `Superproject:` push 结果
105
+
@@ -0,0 +1,65 @@
1
+ ---
2
+ name: ws-submodule-setup
3
+ description: 子模块分支对齐(写入 .gitmodules 的 submodule.<name>.branch;减少 detached 与人为差异)
4
+ ---
5
+
6
+ 用中文输出(命令/路径/代码标识符保持原样不翻译)。
7
+
8
+ 目标:
9
+ - 为每个 submodule 写入 `.gitmodules` 的 `submodule.<name>.branch`,让 `ws-pull` / `ws-finish` 能确定性地“挂回分支/fast-forward push”,避免 origin 多分支时靠猜导致偏差。
10
+ - 该变更是 **superproject 的团队真值**:需要提交 `.gitmodules`。
11
+
12
+ 安全约束(强制):
13
+ - 不自动提交、不自动 push(必须先输出 diff 并让用户确认)
14
+ - 不在 submodule 中做破坏性操作(不 `reset --hard` / 不改动远端)
15
+
16
+ 步骤(建议):
17
+ 1) 确认工作区干净(否则停止):
18
+ ```bash
19
+ git status --porcelain
20
+ ```
21
+
22
+ 2) 列出 submodules(没有则停止并说明无需配置):
23
+ ```bash
24
+ test -f .gitmodules || { echo "no .gitmodules"; exit 0; }
25
+ git config --file .gitmodules --get-regexp '^submodule\\..*\\.path$'
26
+ ```
27
+
28
+ 3) 对每个 submodule,输出当前配置与建议分支(让用户确认每个 submodule 的 branch):
29
+ ```bash
30
+ while read -r key sub_path; do
31
+ name="${key#submodule.}"; name="${name%.path}"
32
+ echo "== submodule: ${name} path=${sub_path} =="
33
+ echo "[current] branch=$(git config --file .gitmodules --get submodule.${name}.branch || true)"
34
+ echo "[origin] HEAD=$(git -C \"${sub_path}\" symbolic-ref --short refs/remotes/origin/HEAD 2>/dev/null || true)"
35
+ git -C "${sub_path}" branch -r --list "origin/*" | sed -n '1,30p' || true
36
+ echo "[choose] set one of:"
37
+ echo " - a concrete branch, e.g. main / master / release/x.y"
38
+ echo " - '.' to follow superproject current branch name (only if your team uses matching branch names)"
39
+ done < <(git config --file .gitmodules --get-regexp '^submodule\\..*\\.path$')
40
+ ```
41
+
42
+ 4) 逐个写入分支配置(每次写完都回显,避免误配):
43
+ ```bash
44
+ # Example:
45
+ # git submodule set-branch --branch main path/to/submodule
46
+ # git submodule set-branch --branch . path/to/submodule
47
+ ```
48
+
49
+ 5) 输出变更并让用户确认是否提交:
50
+ ```bash
51
+ git diff -- .gitmodules
52
+ git status --porcelain
53
+ ```
54
+
55
+ 6) 若用户确认要提交:
56
+ ```bash
57
+ git add .gitmodules
58
+ git commit -m "chore(submodule): set tracking branches"
59
+ ```
60
+
61
+ 输出要求:
62
+ - `Submodules:` name/path + 选择的 branch(每个都列出)
63
+ - `Diff:` `.gitmodules` 的 diff(或至少 `git diff -- .gitmodules` 的摘要)
64
+ - `Next:` 提示后续用 `$ws-pull` 拉取可自动减少 detached;`aiws validate` 会检查该配置是否齐全
65
+
@@ -15,6 +15,7 @@
15
15
 
16
16
  建议流程(按顺序):
17
17
  1) 先运行 `/ws-preflight`。
18
+ 2) 如果存在 `.gitmodules` 但缺少 `submodule.<name>.branch`,先运行 `/ws-submodule-setup` 并提交 `.gitmodules`(否则 `aiws validate .` 会失败,且 submodule 工作流会产生人为差异)。
18
19
  2) 发现 submodules:
19
20
  - `git submodule status --recursive`
20
21
  3) 逐个提交 submodules(按上一步顺序):
@@ -42,4 +43,3 @@
42
43
  <!-- AIWS_MANAGED_END:claude:ws-deliver -->
43
44
 
44
45
  可在下方追加本项目对 Claude Code 的额外说明(托管块外内容会被保留)。
45
-
@@ -9,23 +9,42 @@
9
9
  前置(必须):
10
10
  - 工作区干净:`git status --porcelain` 无输出(否则先 commit 或 stash)
11
11
  - change 分支存在(`change/<change-id>`;也支持 `changes/`、`ws/`、`ws-change/`)
12
+ - 若存在 `.gitmodules`:必须为每个 submodule 配置 `submodule.<name>.branch`(否则先运行 `/ws-submodule-setup` 并提交 `.gitmodules`)
12
13
 
13
14
  步骤(建议):
15
+ 0) 若存在 `.gitmodules`,先检查 submodule branch 配置是否齐全(缺失则停止并提示 setup):
16
+ ```bash
17
+ if [[ -f .gitmodules ]]; then
18
+ missing=0
19
+ while read -r key sub_path; do
20
+ name="${key#submodule.}"; name="${name%.path}"
21
+ b="$(git config --file .gitmodules --get "submodule.${name}.branch" 2>/dev/null || true)"
22
+ if [[ -z "${b:-}" ]]; then
23
+ echo "error: missing .gitmodules submodule.${name}.branch (path=${sub_path})"
24
+ missing=1
25
+ fi
26
+ done < <(git config --file .gitmodules --get-regexp '^submodule\\..*\\.path$' 2>/dev/null || true)
27
+ if [[ "$missing" -ne 0 ]]; then
28
+ echo "hint: run /ws-submodule-setup (and commit .gitmodules), then retry"
29
+ exit 2
30
+ fi
31
+ fi
32
+ ```
14
33
  1) 先运行 `/ws-preflight`(确保真值文件齐全)。
15
34
  2) (推荐)门禁校验并落盘证据:`aiws validate . --stamp`(未安装全局 aiws 时可用 `npx @aipper/aiws validate . --stamp`)。
16
35
  3) 安全合并并切回目标分支:
17
36
  - 若当前就在 `change/<change-id>` 分支上,可直接执行:`aiws change finish`
18
37
  - 否则执行:`aiws change finish <change-id>`
19
38
  4) 若提示无法 fast-forward:先在 change 分支(或对应 worktree)里 `git rebase <target-branch>`,再重试 `aiws change finish`。
20
- 5) 合并成功后,按顺序处理每个 submodule(先“并回目标分支”,再 push):
39
+ 5) 合并成功后,按顺序处理每个 submodule(减少 detached;再 push):
21
40
  - 发现 submodules:`git config --file .gitmodules --get-regexp '^submodule\\..*\\.path$'`
22
41
  - 对每个 `<sub_path>`:
23
42
  - 读取 superproject 当前 gitlink:`git rev-parse "HEAD:<sub_path>"`
24
- - 推断目标分支:优先 `.gitmodules` `submodule.<name>.branch`(若为 `.` 则用当前主仓库分支),否则 `origin/HEAD`,再 fallback `main/master`
25
- - 切到目标分支(必要时创建跟踪分支):`git -C "<sub_path>" switch <target-branch> || git -C "<sub_path>" switch -c <target-branch> --track origin/<target-branch>`
26
- - 合并 gitlink commit:`git -C "<sub_path>" merge --ff-only <gitlink-sha>`
27
- - push:`git -C "<sub_path>" push`(无 upstream 则 `git -C "<sub_path>" push -u origin <target-branch>`)
28
- 6) 任一 submodule `merge --ff-only` 失败时立即停止(不要继续 push 主仓库)。
43
+ - 目标分支:必须在 `.gitmodules` 配置 `submodule.<name>.branch`(若为 `.` 则用当前主仓库分支;避免 origin 多分支时误判)
44
+ - pin 分支挂回(不改动现有 main/master 指针):`git -C "<sub_path>" checkout -B "aiws/pin/<target-branch>" <gitlink-sha>`
45
+ - 仅当 `<gitlink-sha>` 属于 `origin/<target-branch>` 历史时才允许 push;否则停止并人工处理分叉
46
+ - push(只允许 fast-forward):`git -C "<sub_path>" push origin "<gitlink-sha>:refs/heads/<target-branch>"`
47
+ 6) 任一 submodule 不满足 fast-forward 条件时立即停止(不要继续 push 主仓库)。
29
48
  7) submodules 全部成功后,再 push 主仓库当前分支:
30
49
  - `git branch --show-current`
31
50
  - `git status -sb`
@@ -36,7 +36,7 @@ fi
36
36
  ```
37
37
 
38
38
  4) (可选但推荐)把 submodule 从 detached HEAD 尽量“挂回分支”(不改动 gitlink commit):
39
- 为避免 origin 多分支时“猜错分支”,本步骤只在 `.gitmodules` 明确配置了 `submodule.<name>.branch` 时才执行;否则保持 detached 并提示先对齐配置。
39
+ 为避免 origin 多分支时“猜错分支”,本步骤只在 `.gitmodules` 明确配置了 `submodule.<name>.branch` 时才执行;否则保持 detached 并提示先运行 `/ws-submodule-setup` 对齐配置。
40
40
  ```bash
41
41
  if [[ -f .gitmodules ]]; then
42
42
  base_branch="$(git branch --show-current)"
@@ -51,7 +51,7 @@ if [[ -f .gitmodules ]]; then
51
51
  cfg_branch="$(git config --file .gitmodules --get "submodule.${name}.branch" 2>/dev/null || true)"
52
52
  if [[ "${cfg_branch:-}" == "." ]]; then cfg_branch="$base_branch"; fi
53
53
  if [[ -z "${cfg_branch:-}" ]]; then
54
- echo "[warn] ${sub_path}: missing .gitmodules submodule.${name}.branch; keep detached"
54
+ echo "[warn] ${sub_path}: missing .gitmodules submodule.${name}.branch; keep detached (run ws-submodule-setup)"
55
55
  continue
56
56
  fi
57
57
  target_branch="${cfg_branch}"
@@ -0,0 +1,90 @@
1
+ <!-- AIWS_MANAGED_BEGIN:claude:ws-push -->
2
+ # ws push
3
+
4
+ 用中文输出(命令/路径/代码标识符保持原样不翻译)。
5
+
6
+ 目标:安全 push 当前仓库;若仓库包含 submodules,则先 push submodules,再 push superproject(默认 fast-forward;不 force)。
7
+
8
+ 强制约束:
9
+ - 不自动提交、不自动 `git add -A`
10
+ - 不使用 `--force` / `--force-with-lease`
11
+ - 若工作区不干净:停止并要求先 commit 或 stash
12
+
13
+ 步骤(建议):
14
+ 1) 输出上下文并检查工作区干净:
15
+ ```bash
16
+ git branch --show-current
17
+ git status --porcelain
18
+ git status -sb
19
+ ```
20
+ 若 `git status --porcelain` 非空:停止。
21
+
22
+ 2) 判断是否存在 submodules:
23
+ ```bash
24
+ if [[ -f .gitmodules ]]; then
25
+ git config --file .gitmodules --get-regexp '^submodule\\..*\\.path$' || true
26
+ fi
27
+ ```
28
+
29
+ 3) 若没有 submodules:正常 push(仍需用户确认远端/分支):
30
+ ```bash
31
+ git remote -v
32
+ git push
33
+ ```
34
+
35
+ 4) 若有 submodules:先检查 `.gitmodules` 的 `submodule.<name>.branch` 是否齐全(缺失则停止并提示 `/ws-submodule-setup`):
36
+ ```bash
37
+ missing=0
38
+ while read -r key sub_path; do
39
+ name="${key#submodule.}"; name="${name%.path}"
40
+ b="$(git config --file .gitmodules --get "submodule.${name}.branch" 2>/dev/null || true)"
41
+ if [[ -z "${b:-}" ]]; then
42
+ echo "error: missing .gitmodules submodule.${name}.branch (path=${sub_path})"
43
+ missing=1
44
+ fi
45
+ done < <(git config --file .gitmodules --get-regexp '^submodule\\..*\\.path$' 2>/dev/null || true)
46
+ if [[ "$missing" -ne 0 ]]; then
47
+ echo "hint: run /ws-submodule-setup (and commit .gitmodules), then retry"
48
+ exit 2
49
+ fi
50
+ ```
51
+
52
+ 5) 逐个 push submodules(fast-forward only),再 push superproject:
53
+ ```bash
54
+ base_branch="$(git branch --show-current)"
55
+
56
+ git config --file .gitmodules --get-regexp '^submodule\\..*\\.path$' 2>/dev/null \
57
+ | while read -r key sub_path; do
58
+ name="${key#submodule.}"; name="${name%.path}"
59
+ echo "== submodule: ${sub_path} (${name}) =="
60
+
61
+ if [[ -n "$(git -C "${sub_path}" status --porcelain 2>/dev/null || true)" ]]; then
62
+ echo "error: submodule dirty: ${sub_path}"
63
+ exit 2
64
+ fi
65
+
66
+ cfg_branch="$(git config --file .gitmodules --get "submodule.${name}.branch" 2>/dev/null || true)"
67
+ if [[ "${cfg_branch:-}" == "." ]]; then cfg_branch="$base_branch"; fi
68
+ target_branch="${cfg_branch}"
69
+
70
+ git -C "${sub_path}" fetch origin --prune
71
+ if ! git -C "${sub_path}" show-ref --verify --quiet "refs/remotes/origin/${target_branch}"; then
72
+ echo "error: missing origin/${target_branch} for ${sub_path}"
73
+ exit 2
74
+ fi
75
+
76
+ if ! git -C "${sub_path}" merge-base --is-ancestor "origin/${target_branch}" HEAD; then
77
+ echo "error: non-fast-forward (submodule=${sub_path}, branch=${target_branch})"
78
+ exit 2
79
+ fi
80
+
81
+ git -C "${sub_path}" push origin "HEAD:refs/heads/${target_branch}"
82
+ done
83
+
84
+ git remote -v
85
+ git push
86
+ ```
87
+ <!-- AIWS_MANAGED_END:claude:ws-push -->
88
+
89
+ 可在下方追加本项目对 Claude Code 的额外说明(托管块外内容会被保留)。
90
+
@@ -0,0 +1,54 @@
1
+ <!-- AIWS_MANAGED_BEGIN:claude:ws-submodule-setup -->
2
+ # ws submodule setup
3
+
4
+ 用中文输出(命令/路径/代码标识符保持原样不翻译)。
5
+
6
+ 目标:为每个 submodule 写入 `.gitmodules` 的 `submodule.<name>.branch`(团队真值),让 `/ws-pull`、`/ws-finish` 能确定性地减少 detached 与人为差异;并使 `aiws validate` 的 submodule 分支门禁通过。
7
+
8
+ 约束:
9
+ - 不自动提交、不自动 push(必须先输出 diff 并让用户确认)
10
+ - 不做破坏性命令
11
+
12
+ 步骤(建议):
13
+ 1) 确认工作区干净:
14
+ ```bash
15
+ git status --porcelain
16
+ ```
17
+ 非空则停止。
18
+
19
+ 2) 列出 submodules:
20
+ ```bash
21
+ test -f .gitmodules || { echo "no .gitmodules"; exit 0; }
22
+ git config --file .gitmodules --get-regexp '^submodule\\..*\\.path$'
23
+ ```
24
+
25
+ 3) 对每个 submodule 让用户选择目标分支(推荐具体分支名;可选 `.` 表示跟随 superproject 分支名):
26
+ ```bash
27
+ while read -r key sub_path; do
28
+ name="${key#submodule.}"; name="${name%.path}"
29
+ echo "== submodule: ${name} path=${sub_path} =="
30
+ echo "[current] branch=$(git config --file .gitmodules --get submodule.${name}.branch || true)"
31
+ echo "[origin] HEAD=$(git -C \"${sub_path}\" symbolic-ref --short refs/remotes/origin/HEAD 2>/dev/null || true)"
32
+ done < <(git config --file .gitmodules --get-regexp '^submodule\\..*\\.path$')
33
+ ```
34
+
35
+ 4) 写入分支配置:
36
+ ```bash
37
+ git submodule set-branch --branch <branch-or-dot> <sub_path>
38
+ ```
39
+
40
+ 5) 输出 diff 并让用户确认是否提交:
41
+ ```bash
42
+ git diff -- .gitmodules
43
+ git status --porcelain
44
+ ```
45
+
46
+ 6) 用户确认后提交:
47
+ ```bash
48
+ git add .gitmodules
49
+ git commit -m "chore(submodule): set tracking branches"
50
+ ```
51
+ <!-- AIWS_MANAGED_END:claude:ws-submodule-setup -->
52
+
53
+ 可在下方追加本项目对 Claude Code 的额外说明(托管块外内容会被保留)。
54
+
@@ -20,6 +20,7 @@ prompt = """
20
20
 
21
21
  建议流程(按顺序):
22
22
  1) 先确认真值文件存在并遵守:`AI_PROJECT.md` / `REQUIREMENTS.md` / `AI_WORKSPACE.md`。
23
+ 2) 如果存在 `.gitmodules` 但缺少 `submodule.<name>.branch`,先运行 `ws:submodule-setup` 并提交 `.gitmodules`(否则 `aiws validate .` 会失败,且 submodule 工作流会产生人为差异)。
23
24
  2) 发现 submodules:
24
25
  - `git submodule status --recursive`
25
26
  3) 逐个提交 submodules(按上一步顺序):
@@ -45,4 +46,3 @@ prompt = """
45
46
  - `Merge:` `ws:finish`/`aiws change finish` 输出(into/from)
46
47
  - `Evidence:` `.agentdocs/tmp/aiws-validate/*.json`(若使用 --stamp)
47
48
  """
48
-
@@ -18,6 +18,7 @@ prompt = """
18
18
  - `git status --porcelain`
19
19
  - `git branch --show-current`(或 `git rev-parse --abbrev-ref HEAD`)
20
20
  4) 若工作区不干净:停止,并要求先 commit 或 stash(不要尝试自动处理)。
21
+ 4.1) 若存在 `.gitmodules`:必须为每个 submodule 配置 `submodule.<name>.branch`(否则先运行 `ws:submodule-setup` 并提交 `.gitmodules`)。
21
22
  5) (推荐)门禁校验并落盘证据:`aiws validate . --stamp`(未安装全局 aiws 时可用 `npx @aipper/aiws validate . --stamp`)。
22
23
  6) 安全合并(默认 fast-forward):
23
24
  - 在 `change/<change-id>` 分支上:`aiws change finish`(会尝试读 `changes/<change-id>/.ws-change.json` 的 `base_branch` 并切回目标分支)
@@ -27,11 +28,11 @@ prompt = """
27
28
  - 发现 submodules:`git config --file .gitmodules --get-regexp '^submodule\\..*\\.path$'`
28
29
  - 对每个 `<sub_path>`:
29
30
  - 读取 superproject 当前 gitlink:`git rev-parse "HEAD:<sub_path>"`
30
- - 推断目标分支:优先 `.gitmodules` `submodule.<name>.branch`(若为 `.` 则用当前主仓库分支),否则 `origin/HEAD`,再 fallback `main/master`
31
- - 切到目标分支(必要时创建跟踪分支):`git -C "<sub_path>" switch <target-branch> || git -C "<sub_path>" switch -c <target-branch> --track origin/<target-branch>`
32
- - 合并 gitlink commit:`git -C "<sub_path>" merge --ff-only <gitlink-sha>`
33
- - push:`git -C "<sub_path>" push`(无 upstream 则 `git -C "<sub_path>" push -u origin <target-branch>`)
34
- 9) 任一 submodule `merge --ff-only` 失败时立即停止(不要继续 push 主仓库)。
31
+ - 目标分支:必须在 `.gitmodules` 配置 `submodule.<name>.branch`(若为 `.` 则用当前主仓库分支;避免 origin 多分支时误判)
32
+ - pin 分支挂回(不改动现有 main/master 指针):`git -C "<sub_path>" checkout -B "aiws/pin/<target-branch>" <gitlink-sha>`
33
+ - 仅当 `<gitlink-sha>` 属于 `origin/<target-branch>` 历史时才允许 push;否则停止并人工处理分叉
34
+ - push(只允许 fast-forward):`git -C "<sub_path>" push origin "<gitlink-sha>:refs/heads/<target-branch>"`
35
+ 9) 任一 submodule 不满足 fast-forward 条件时立即停止(不要继续 push 主仓库)。
35
36
  10) submodules push 完成后,再 push 主仓库当前分支:
36
37
  - `git branch --show-current`
37
38
  - `git status -sb`
@@ -43,4 +43,5 @@ prompt = """
43
43
  可选(方案 2,一次性设置 submodule 跟踪分支,会改 `.gitmodules`,需提交):
44
44
  - `git submodule set-branch --branch main <sub_path>`
45
45
  - `git add .gitmodules && git commit -m "chore(submodule): set tracking branch"`
46
+ 推荐:优先使用 `ws:submodule-setup` 交互式对齐所有 submodules 的 branch,并提交 `.gitmodules`。
46
47
  """
@@ -0,0 +1,40 @@
1
+ # Command: ws:push
2
+ # Description: 推送(submodule 感知:先 submodules 后 superproject;fast-forward;不 force)
3
+ # Category: workspace
4
+ # Version: 1
5
+
6
+ description = "推送(submodule 感知:先 submodules 后 superproject;fast-forward;不 force)"
7
+
8
+ prompt = """
9
+ 用中文输出(命令/路径/代码标识符保持原样不翻译)。
10
+
11
+ 目标:安全 push 当前仓库;若仓库包含 submodules,则先 push submodules,再 push superproject(默认 fast-forward;不 force)。
12
+
13
+ 约束:
14
+ - 不自动提交、不自动 `git add -A`
15
+ - 不使用 `--force` / `--force-with-lease`
16
+ - 若工作区不干净:停止并要求先 commit 或 stash
17
+
18
+ 步骤(建议):
19
+ 1) 输出上下文并检查工作区干净:
20
+ - `git branch --show-current`
21
+ - `git status --porcelain`
22
+ - `git status -sb`
23
+ - 非空则停止。
24
+
25
+ 2) 判断是否存在 submodules:若存在 `.gitmodules`,输出 submodule 列表:
26
+ - `git config --file .gitmodules --get-regexp '^submodule\\..*\\.path$'`
27
+
28
+ 3) 若没有 submodules:正常 push(仍需用户确认远端/分支):
29
+ - `git remote -v`
30
+ - `git push`
31
+
32
+ 4) 若有 submodules:先检查 `.gitmodules` 的 `submodule.<name>.branch` 是否齐全(缺失则停止并提示 `ws:submodule-setup`)。
33
+
34
+ 5) 逐个 push submodules(fast-forward only),再 push superproject:
35
+ - `git -C "<sub_path>" fetch origin --prune`
36
+ - 若 `origin/<target_branch>` 不是 `HEAD` 的祖先:停止并提示先在 submodule 做 rebase/merge
37
+ - `git -C "<sub_path>" push origin "HEAD:refs/heads/<target_branch>"`
38
+ - 最后 `git push`
39
+ """
40
+
@@ -0,0 +1,32 @@
1
+ # Command: ws:submodule-setup
2
+ # Description: 子模块分支对齐(写入 .gitmodules 的 submodule.<name>.branch)
3
+ # Category: workspace
4
+ # Version: 1
5
+
6
+ description = "子模块分支对齐(写入 .gitmodules 的 submodule.<name>.branch)"
7
+
8
+ prompt = """
9
+ 用中文输出(命令/路径/代码标识符保持原样不翻译)。
10
+
11
+ 目标:为每个 submodule 写入 `.gitmodules` 的 `submodule.<name>.branch`(团队真值),让 `ws:pull`、`ws:finish` 能确定性地减少 detached 与人为差异;并使 `aiws validate` 的 submodule 分支门禁通过。
12
+
13
+ 约束:
14
+ - 不自动提交、不自动 push(必须先输出 diff 并让用户确认)
15
+ - 不做破坏性命令
16
+
17
+ 步骤(建议):
18
+ 1) 确认工作区干净:`git status --porcelain`(非空则停止)。
19
+ 2) 列出 submodules:
20
+ - `test -f .gitmodules || { echo "no .gitmodules"; exit 0; }`
21
+ - `git config --file .gitmodules --get-regexp '^submodule\\..*\\.path$'`
22
+ 3) 对每个 submodule 让用户选择目标分支(推荐具体分支名;可选 `.` 表示跟随 superproject 分支名)。
23
+ 4) 写入分支配置:
24
+ - `git submodule set-branch --branch <branch-or-dot> <sub_path>`
25
+ 5) 输出 diff 并让用户确认是否提交:
26
+ - `git diff -- .gitmodules`
27
+ - `git status --porcelain`
28
+ 6) 用户确认后提交:
29
+ - `git add .gitmodules`
30
+ - `git commit -m "chore(submodule): set tracking branches"`
31
+ """
32
+
@@ -18,6 +18,7 @@ description: 交付:submodules+superproject 分步提交并安全合并回 bas
18
18
 
19
19
  建议流程(按顺序):
20
20
  1) 先运行 `/ws-preflight`。
21
+ 2) 如果存在 `.gitmodules` 但缺少 `submodule.<name>.branch`,先运行 `/ws-submodule-setup` 并提交 `.gitmodules`(否则 `aiws validate .` 会失败,且 submodule 工作流会产生人为差异)。
21
22
  2) 发现 submodules:
22
23
  - `git submodule status --recursive`
23
24
  3) 逐个提交 submodules(按上一步顺序):
@@ -12,23 +12,42 @@ description: 收尾:fast-forward 合并并把 submodule 并回目标分支后
12
12
  前置(必须):
13
13
  - 工作区干净:`git status --porcelain` 无输出(否则先 commit 或 stash)
14
14
  - change 分支存在(`change/<change-id>`;也支持 `changes/`、`ws/`、`ws-change/`)
15
+ - 若存在 `.gitmodules`:必须为每个 submodule 配置 `submodule.<name>.branch`(否则先运行 `/ws-submodule-setup` 并提交 `.gitmodules`)
15
16
 
16
17
  步骤(建议):
18
+ 0) 若存在 `.gitmodules`,先检查 submodule branch 配置是否齐全(缺失则停止并提示 setup):
19
+ ```bash
20
+ if [[ -f .gitmodules ]]; then
21
+ missing=0
22
+ while read -r key sub_path; do
23
+ name="${key#submodule.}"; name="${name%.path}"
24
+ b="$(git config --file .gitmodules --get "submodule.${name}.branch" 2>/dev/null || true)"
25
+ if [[ -z "${b:-}" ]]; then
26
+ echo "error: missing .gitmodules submodule.${name}.branch (path=${sub_path})"
27
+ missing=1
28
+ fi
29
+ done < <(git config --file .gitmodules --get-regexp '^submodule\\..*\\.path$' 2>/dev/null || true)
30
+ if [[ "$missing" -ne 0 ]]; then
31
+ echo "hint: run /ws-submodule-setup (and commit .gitmodules), then retry"
32
+ exit 2
33
+ fi
34
+ fi
35
+ ```
17
36
  1) 先运行 `/ws-preflight`(确保真值文件齐全)。
18
37
  2) (推荐)门禁校验并落盘证据:`aiws validate . --stamp`(未安装全局 aiws 时可用 `npx @aipper/aiws validate . --stamp`)。
19
38
  3) 安全合并并切回目标分支:
20
39
  - 若当前就在 `change/<change-id>` 分支上,可直接执行:`aiws change finish`
21
40
  - 否则执行:`aiws change finish <change-id>`
22
41
  4) 若提示无法 fast-forward:先在 change 分支(或对应 worktree)里 `git rebase <target-branch>`,再重试 `aiws change finish`。
23
- 5) 合并成功后,按顺序处理每个 submodule(先“并回目标分支”,再 push):
42
+ 5) 合并成功后,按顺序处理每个 submodule(减少 detached;再 push):
24
43
  - 发现 submodules:`git config --file .gitmodules --get-regexp '^submodule\\..*\\.path$'`
25
44
  - 对每个 `<sub_path>`:
26
45
  - 读取 superproject 当前 gitlink:`git rev-parse "HEAD:<sub_path>"`
27
- - 推断目标分支:优先 `.gitmodules` `submodule.<name>.branch`(若为 `.` 则用当前主仓库分支),否则 `origin/HEAD`,再 fallback `main/master`
28
- - 切到目标分支(必要时创建跟踪分支):`git -C "<sub_path>" switch <target-branch> || git -C "<sub_path>" switch -c <target-branch> --track origin/<target-branch>`
29
- - 合并 gitlink commit:`git -C "<sub_path>" merge --ff-only <gitlink-sha>`
30
- - push:`git -C "<sub_path>" push`(无 upstream 则 `git -C "<sub_path>" push -u origin <target-branch>`)
31
- 6) 任一 submodule `merge --ff-only` 失败时立即停止(不要继续 push 主仓库)。
46
+ - 目标分支:必须在 `.gitmodules` 配置 `submodule.<name>.branch`(若为 `.` 则用当前主仓库分支;避免 origin 多分支时误判)
47
+ - pin 分支挂回(不改动现有 main/master 指针):`git -C "<sub_path>" checkout -B "aiws/pin/<target-branch>" <gitlink-sha>`
48
+ - 仅当 `<gitlink-sha>` 属于 `origin/<target-branch>` 历史时才允许 push;否则停止并人工处理分叉
49
+ - push(只允许 fast-forward):`git -C "<sub_path>" push origin "<gitlink-sha>:refs/heads/<target-branch>"`
50
+ 6) 任一 submodule 不满足 fast-forward 条件时立即停止(不要继续 push 主仓库)。
32
51
  7) submodules 全部成功后,再 push 主仓库当前分支:
33
52
  - `git branch --show-current`
34
53
  - `git status -sb`
@@ -39,7 +39,7 @@ fi
39
39
  ```
40
40
 
41
41
  4) (可选但推荐)把 submodule 从 detached HEAD 尽量“挂回分支”(不改动 gitlink commit):
42
- 为避免 origin 多分支时“猜错分支”,本步骤只在 `.gitmodules` 明确配置了 `submodule.<name>.branch` 时才执行;否则保持 detached 并提示先对齐配置。
42
+ 为避免 origin 多分支时“猜错分支”,本步骤只在 `.gitmodules` 明确配置了 `submodule.<name>.branch` 时才执行;否则保持 detached 并提示先运行 `/ws-submodule-setup` 对齐配置。
43
43
  ```bash
44
44
  if [[ -f .gitmodules ]]; then
45
45
  base_branch="$(git branch --show-current)"
@@ -54,7 +54,7 @@ if [[ -f .gitmodules ]]; then
54
54
  cfg_branch="$(git config --file .gitmodules --get "submodule.${name}.branch" 2>/dev/null || true)"
55
55
  if [[ "${cfg_branch:-}" == "." ]]; then cfg_branch="$base_branch"; fi
56
56
  if [[ -z "${cfg_branch:-}" ]]; then
57
- echo "[warn] ${sub_path}: missing .gitmodules submodule.${name}.branch; keep detached"
57
+ echo "[warn] ${sub_path}: missing .gitmodules submodule.${name}.branch; keep detached (run ws-submodule-setup)"
58
58
  continue
59
59
  fi
60
60
  target_branch="${cfg_branch}"
@@ -0,0 +1,93 @@
1
+ ---
2
+ description: 推送:submodule 感知(先 submodules 后 superproject;fast-forward;不 force)
3
+ ---
4
+ <!-- AIWS_MANAGED_BEGIN:opencode:ws-push -->
5
+ # ws push
6
+
7
+ 用中文输出(命令/路径/代码标识符保持原样不翻译)。
8
+
9
+ 目标:安全 push 当前仓库;若仓库包含 submodules,则先 push submodules,再 push superproject(默认 fast-forward;不 force)。
10
+
11
+ 强制约束:
12
+ - 不自动提交、不自动 `git add -A`
13
+ - 不使用 `--force` / `--force-with-lease`
14
+ - 若工作区不干净:停止并要求先 commit 或 stash
15
+
16
+ 步骤(建议):
17
+ 1) 输出上下文并检查工作区干净:
18
+ ```bash
19
+ git branch --show-current
20
+ git status --porcelain
21
+ git status -sb
22
+ ```
23
+ 若 `git status --porcelain` 非空:停止。
24
+
25
+ 2) 判断是否存在 submodules:
26
+ ```bash
27
+ if [[ -f .gitmodules ]]; then
28
+ git config --file .gitmodules --get-regexp '^submodule\\..*\\.path$' || true
29
+ fi
30
+ ```
31
+
32
+ 3) 若没有 submodules:正常 push(仍需用户确认远端/分支):
33
+ ```bash
34
+ git remote -v
35
+ git push
36
+ ```
37
+
38
+ 4) 若有 submodules:先检查 `.gitmodules` 的 `submodule.<name>.branch` 是否齐全(缺失则停止并提示 `/ws-submodule-setup`):
39
+ ```bash
40
+ missing=0
41
+ while read -r key sub_path; do
42
+ name="${key#submodule.}"; name="${name%.path}"
43
+ b="$(git config --file .gitmodules --get "submodule.${name}.branch" 2>/dev/null || true)"
44
+ if [[ -z "${b:-}" ]]; then
45
+ echo "error: missing .gitmodules submodule.${name}.branch (path=${sub_path})"
46
+ missing=1
47
+ fi
48
+ done < <(git config --file .gitmodules --get-regexp '^submodule\\..*\\.path$' 2>/dev/null || true)
49
+ if [[ "$missing" -ne 0 ]]; then
50
+ echo "hint: run /ws-submodule-setup (and commit .gitmodules), then retry"
51
+ exit 2
52
+ fi
53
+ ```
54
+
55
+ 5) 逐个 push submodules(fast-forward only),再 push superproject:
56
+ ```bash
57
+ base_branch="$(git branch --show-current)"
58
+
59
+ git config --file .gitmodules --get-regexp '^submodule\\..*\\.path$' 2>/dev/null \
60
+ | while read -r key sub_path; do
61
+ name="${key#submodule.}"; name="${name%.path}"
62
+ echo "== submodule: ${sub_path} (${name}) =="
63
+
64
+ if [[ -n "$(git -C "${sub_path}" status --porcelain 2>/dev/null || true)" ]]; then
65
+ echo "error: submodule dirty: ${sub_path}"
66
+ exit 2
67
+ fi
68
+
69
+ cfg_branch="$(git config --file .gitmodules --get "submodule.${name}.branch" 2>/dev/null || true)"
70
+ if [[ "${cfg_branch:-}" == "." ]]; then cfg_branch="$base_branch"; fi
71
+ target_branch="${cfg_branch}"
72
+
73
+ git -C "${sub_path}" fetch origin --prune
74
+ if ! git -C "${sub_path}" show-ref --verify --quiet "refs/remotes/origin/${target_branch}"; then
75
+ echo "error: missing origin/${target_branch} for ${sub_path}"
76
+ exit 2
77
+ fi
78
+
79
+ if ! git -C "${sub_path}" merge-base --is-ancestor "origin/${target_branch}" HEAD; then
80
+ echo "error: non-fast-forward (submodule=${sub_path}, branch=${target_branch})"
81
+ exit 2
82
+ fi
83
+
84
+ git -C "${sub_path}" push origin "HEAD:refs/heads/${target_branch}"
85
+ done
86
+
87
+ git remote -v
88
+ git push
89
+ ```
90
+ <!-- AIWS_MANAGED_END:opencode:ws-push -->
91
+
92
+ 可在下方追加本项目对 OpenCode 的额外说明(托管块外内容会被保留)。
93
+
@@ -0,0 +1,57 @@
1
+ ---
2
+ description: 子模块分支对齐(写入 .gitmodules 的 submodule.<name>.branch)
3
+ ---
4
+ <!-- AIWS_MANAGED_BEGIN:opencode:ws-submodule-setup -->
5
+ # ws submodule setup
6
+
7
+ 用中文输出(命令/路径/代码标识符保持原样不翻译)。
8
+
9
+ 目标:为每个 submodule 写入 `.gitmodules` 的 `submodule.<name>.branch`(团队真值),让 `/ws-pull`、`/ws-finish` 能确定性地减少 detached 与人为差异;并使 `aiws validate` 的 submodule 分支门禁通过。
10
+
11
+ 约束:
12
+ - 不自动提交、不自动 push(必须先输出 diff 并让用户确认)
13
+ - 不做破坏性命令
14
+
15
+ 步骤(建议):
16
+ 1) 确认工作区干净:
17
+ ```bash
18
+ git status --porcelain
19
+ ```
20
+ 非空则停止。
21
+
22
+ 2) 列出 submodules:
23
+ ```bash
24
+ test -f .gitmodules || { echo "no .gitmodules"; exit 0; }
25
+ git config --file .gitmodules --get-regexp '^submodule\\..*\\.path$'
26
+ ```
27
+
28
+ 3) 对每个 submodule 让用户选择目标分支(推荐具体分支名;可选 `.` 表示跟随 superproject 分支名):
29
+ ```bash
30
+ while read -r key sub_path; do
31
+ name="${key#submodule.}"; name="${name%.path}"
32
+ echo "== submodule: ${name} path=${sub_path} =="
33
+ echo "[current] branch=$(git config --file .gitmodules --get submodule.${name}.branch || true)"
34
+ echo "[origin] HEAD=$(git -C \"${sub_path}\" symbolic-ref --short refs/remotes/origin/HEAD 2>/dev/null || true)"
35
+ done < <(git config --file .gitmodules --get-regexp '^submodule\\..*\\.path$')
36
+ ```
37
+
38
+ 4) 写入分支配置:
39
+ ```bash
40
+ git submodule set-branch --branch <branch-or-dot> <sub_path>
41
+ ```
42
+
43
+ 5) 输出 diff 并让用户确认是否提交:
44
+ ```bash
45
+ git diff -- .gitmodules
46
+ git status --porcelain
47
+ ```
48
+
49
+ 6) 用户确认后提交:
50
+ ```bash
51
+ git add .gitmodules
52
+ git commit -m "chore(submodule): set tracking branches"
53
+ ```
54
+ <!-- AIWS_MANAGED_END:opencode:ws-submodule-setup -->
55
+
56
+ 可在下方追加本项目对 OpenCode 的额外说明(托管块外内容会被保留)。
57
+
@@ -10,7 +10,7 @@
10
10
  - 启用本机门禁(推荐):`aiws hooks install .`(或手工:`git config core.hooksPath .githooks`;`git commit`/`git push` 会自动跑 `aiws validate .`)
11
11
  - 提交前校验(强制门禁):`aiws validate .`(包含:漂移检测 + `ws_change_check` + `requirements_contract`)
12
12
  - Codex(推荐):本仓库内置 repo skills:`.agents/skills/`(可显式 `$ws-dev`,也可隐式套用工作流)
13
- - Codex skills(常用):`$ws-preflight` / `$ws-plan` / `$ws-plan-verify` / `$ws-dev` / `$ws-pull` / `$ws-review` / `$ws-commit` / `$aiws-init` / `$aiws-validate` / `$aiws-hooks-install` / `$aiws-change-new`
13
+ - Codex skills(常用):`$ws-preflight` / `$ws-submodule-setup` / `$ws-plan` / `$ws-plan-verify` / `$ws-dev` / `$ws-pull` / `$ws-push` / `$ws-review` / `$ws-commit` / `$aiws-init` / `$aiws-validate` / `$aiws-hooks-install` / `$aiws-change-new`
14
14
  - Codex CLI(推荐,可选):安装全局 skills:`npx @aipper/aiws codex install-skills`(写入 `~/.codex/skills/` 或 `$CODEX_HOME/skills`)
15
15
  - Codex CLI(遗留,可选):安装全局 prompts:`npx @aipper/aiws codex install-prompts`(写入 `~/.codex/prompts/` 或 `$CODEX_HOME/prompts`;prompts 已 deprecated)
16
16
  - 不要把敏感信息写入 git:`secrets/test-accounts.json`、`.env*`、token、内网地址等
@@ -31,7 +31,9 @@
31
31
  ".claude/commands/ws-deliver.md",
32
32
  ".claude/commands/ws-analyze.md",
33
33
  ".claude/commands/ws-pull.md",
34
+ ".claude/commands/ws-push.md",
34
35
  ".claude/commands/ws-finish.md",
36
+ ".claude/commands/ws-submodule-setup.md",
35
37
  ".claude/commands/ws-review.md",
36
38
  ".claude/commands/ws-commit.md",
37
39
  ".claude/commands/ws-rule.md",
@@ -50,7 +52,9 @@
50
52
  ".opencode/command/ws-deliver.md",
51
53
  ".opencode/command/ws-analyze.md",
52
54
  ".opencode/command/ws-pull.md",
55
+ ".opencode/command/ws-push.md",
53
56
  ".opencode/command/ws-finish.md",
57
+ ".opencode/command/ws-submodule-setup.md",
54
58
  ".opencode/command/ws-review.md",
55
59
  ".opencode/command/ws-commit.md",
56
60
  ".opencode/command/ws-rule.md",
@@ -78,7 +82,9 @@
78
82
  ".iflow/commands/ws-analyze.toml",
79
83
  ".iflow/commands/ws-deliver.toml",
80
84
  ".iflow/commands/ws-pull.toml",
85
+ ".iflow/commands/ws-push.toml",
81
86
  ".iflow/commands/ws-finish.toml",
87
+ ".iflow/commands/ws-submodule-setup.toml",
82
88
  ".iflow/commands/ws-review.toml",
83
89
  ".iflow/commands/ws-commit.toml",
84
90
  ".iflow/commands/ws-rule.toml",
@@ -141,6 +147,8 @@
141
147
  ".agents/skills/ws-plan/SKILL.md",
142
148
  ".agents/skills/ws-plan-verify/SKILL.md",
143
149
  ".agents/skills/ws-pull/SKILL.md",
150
+ ".agents/skills/ws-push/SKILL.md",
151
+ ".agents/skills/ws-submodule-setup/SKILL.md",
144
152
  ".agents/skills/ws-preflight/SKILL.md",
145
153
  ".agents/skills/ws-req-change/SKILL.md",
146
154
  ".agents/skills/ws-req-contract-sync/SKILL.md",
@@ -168,7 +176,9 @@
168
176
  ".iflow/commands/ws-analyze.toml",
169
177
  ".iflow/commands/ws-deliver.toml",
170
178
  ".iflow/commands/ws-pull.toml",
179
+ ".iflow/commands/ws-push.toml",
171
180
  ".iflow/commands/ws-finish.toml",
181
+ ".iflow/commands/ws-submodule-setup.toml",
172
182
  ".iflow/commands/ws-review.toml",
173
183
  ".iflow/commands/ws-commit.toml",
174
184
  ".iflow/commands/ws-rule.toml",
@@ -201,7 +211,9 @@
201
211
  ".claude/commands/ws-deliver.md": ["claude:ws-deliver"],
202
212
  ".claude/commands/ws-analyze.md": ["claude:ws-analyze"],
203
213
  ".claude/commands/ws-pull.md": ["claude:ws-pull"],
214
+ ".claude/commands/ws-push.md": ["claude:ws-push"],
204
215
  ".claude/commands/ws-finish.md": ["claude:ws-finish"],
216
+ ".claude/commands/ws-submodule-setup.md": ["claude:ws-submodule-setup"],
205
217
  ".claude/commands/ws-review.md": ["claude:ws-review"],
206
218
  ".claude/commands/ws-commit.md": ["claude:ws-commit"],
207
219
  ".claude/commands/ws-rule.md": ["claude:ws-rule"],
@@ -220,7 +232,9 @@
220
232
  ".opencode/command/ws-deliver.md": ["opencode:ws-deliver"],
221
233
  ".opencode/command/ws-analyze.md": ["opencode:ws-analyze"],
222
234
  ".opencode/command/ws-pull.md": ["opencode:ws-pull"],
235
+ ".opencode/command/ws-push.md": ["opencode:ws-push"],
223
236
  ".opencode/command/ws-finish.md": ["opencode:ws-finish"],
237
+ ".opencode/command/ws-submodule-setup.md": ["opencode:ws-submodule-setup"],
224
238
  ".opencode/command/ws-review.md": ["opencode:ws-review"],
225
239
  ".opencode/command/ws-commit.md": ["opencode:ws-commit"],
226
240
  ".opencode/command/ws-rule.md": ["opencode:ws-rule"],