@aipper/aiws-spec 0.0.21 → 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 (89) hide show
  1. package/docs/spec-contract.md +5 -4
  2. package/package.json +1 -1
  3. package/templates/workspace/.agents/skills/{aiws-change-archive → p-aiws-change-archive}/SKILL.md +3 -2
  4. package/templates/workspace/.agents/skills/{aiws-change-finish → p-aiws-change-finish}/SKILL.md +2 -2
  5. package/templates/workspace/.agents/skills/{aiws-change-list → p-aiws-change-list}/SKILL.md +2 -2
  6. package/templates/workspace/.agents/skills/{aiws-change-new → p-aiws-change-new}/SKILL.md +2 -2
  7. package/templates/workspace/.agents/skills/{aiws-change-next → p-aiws-change-next}/SKILL.md +2 -2
  8. package/templates/workspace/.agents/skills/{aiws-change-start → p-aiws-change-start}/SKILL.md +2 -2
  9. package/templates/workspace/.agents/skills/{aiws-change-status → p-aiws-change-status}/SKILL.md +2 -2
  10. package/templates/workspace/.agents/skills/{aiws-change-sync → p-aiws-change-sync}/SKILL.md +2 -2
  11. package/templates/workspace/.agents/skills/{aiws-change-templates-init → p-aiws-change-templates-init}/SKILL.md +2 -2
  12. package/templates/workspace/.agents/skills/{aiws-change-templates-which → p-aiws-change-templates-which}/SKILL.md +2 -2
  13. package/templates/workspace/.agents/skills/{aiws-change-validate → p-aiws-change-validate}/SKILL.md +2 -2
  14. package/templates/workspace/.agents/skills/{aiws-hooks-install → p-aiws-hooks-install}/SKILL.md +2 -2
  15. package/templates/workspace/.agents/skills/{aiws-hooks-status → p-aiws-hooks-status}/SKILL.md +2 -2
  16. package/templates/workspace/.agents/skills/{aiws-init → p-aiws-init}/SKILL.md +2 -2
  17. package/templates/workspace/.agents/skills/{aiws-rollback → p-aiws-rollback}/SKILL.md +2 -2
  18. package/templates/workspace/.agents/skills/{aiws-update → p-aiws-update}/SKILL.md +2 -2
  19. package/templates/workspace/.agents/skills/{aiws-validate → p-aiws-validate}/SKILL.md +2 -2
  20. package/templates/workspace/.agents/skills/p-tasks-plan/SKILL.md +37 -0
  21. package/templates/workspace/.agents/skills/ws-bugfix/SKILL.md +30 -7
  22. package/templates/workspace/.agents/skills/ws-commit/SKILL.md +23 -6
  23. package/templates/workspace/.agents/skills/ws-deliver/SKILL.md +61 -17
  24. package/templates/workspace/.agents/skills/ws-dev/SKILL.md +102 -2
  25. package/templates/workspace/.agents/skills/ws-finish/SKILL.md +96 -65
  26. package/templates/workspace/.agents/skills/ws-handoff/SKILL.md +31 -0
  27. package/templates/workspace/.agents/skills/ws-plan/SKILL.md +68 -1
  28. package/templates/workspace/.agents/skills/ws-push/SKILL.md +17 -12
  29. package/templates/workspace/.claude/commands/{aiws-change-archive.md → p-aiws-change-archive.md} +3 -1
  30. package/templates/workspace/.claude/commands/{aiws-change-finish.md → p-aiws-change-finish.md} +2 -0
  31. package/templates/workspace/.claude/commands/{aiws-change-list.md → p-aiws-change-list.md} +2 -0
  32. package/templates/workspace/.claude/commands/{aiws-change-new.md → p-aiws-change-new.md} +2 -0
  33. package/templates/workspace/.claude/commands/{aiws-change-next.md → p-aiws-change-next.md} +2 -0
  34. package/templates/workspace/.claude/commands/{aiws-change-start.md → p-aiws-change-start.md} +2 -0
  35. package/templates/workspace/.claude/commands/{aiws-change-status.md → p-aiws-change-status.md} +2 -0
  36. package/templates/workspace/.claude/commands/{aiws-change-sync.md → p-aiws-change-sync.md} +2 -0
  37. package/templates/workspace/.claude/commands/{aiws-change-templates-init.md → p-aiws-change-templates-init.md} +2 -0
  38. package/templates/workspace/.claude/commands/{aiws-change-templates-which.md → p-aiws-change-templates-which.md} +2 -0
  39. package/templates/workspace/.claude/commands/{aiws-change-validate.md → p-aiws-change-validate.md} +2 -0
  40. package/templates/workspace/.claude/commands/{aiws-hooks-install.md → p-aiws-hooks-install.md} +2 -0
  41. package/templates/workspace/.claude/commands/{aiws-hooks-status.md → p-aiws-hooks-status.md} +2 -0
  42. package/templates/workspace/.claude/commands/{aiws-init.md → p-aiws-init.md} +2 -1
  43. package/templates/workspace/.claude/commands/{aiws-rollback.md → p-aiws-rollback.md} +2 -1
  44. package/templates/workspace/.claude/commands/{aiws-update.md → p-aiws-update.md} +2 -1
  45. package/templates/workspace/.claude/commands/{aiws-validate.md → p-aiws-validate.md} +2 -1
  46. package/templates/workspace/.claude/commands/ws-bugfix.md +11 -6
  47. package/templates/workspace/.claude/commands/ws-commit.md +2 -1
  48. package/templates/workspace/.claude/commands/ws-deliver.md +6 -2
  49. package/templates/workspace/.claude/commands/ws-dev.md +7 -1
  50. package/templates/workspace/.claude/commands/ws-finish.md +19 -19
  51. package/templates/workspace/.claude/commands/ws-handoff.md +22 -0
  52. package/templates/workspace/.claude/commands/ws-plan.md +9 -5
  53. package/templates/workspace/.codex/prompts/ws-dev.md +5 -1
  54. package/templates/workspace/.githooks/commit-msg +109 -0
  55. package/templates/workspace/.iflow/commands/ws-commit.toml +2 -1
  56. package/templates/workspace/.iflow/commands/ws-deliver.toml +6 -2
  57. package/templates/workspace/.iflow/commands/ws-finish.toml +19 -19
  58. package/templates/workspace/.opencode/command/{aiws-change-archive.md → p-aiws-change-archive.md} +4 -2
  59. package/templates/workspace/.opencode/command/{aiws-change-finish.md → p-aiws-change-finish.md} +3 -1
  60. package/templates/workspace/.opencode/command/{aiws-change-list.md → p-aiws-change-list.md} +3 -1
  61. package/templates/workspace/.opencode/command/{aiws-change-new.md → p-aiws-change-new.md} +3 -1
  62. package/templates/workspace/.opencode/command/{aiws-change-next.md → p-aiws-change-next.md} +3 -1
  63. package/templates/workspace/.opencode/command/{aiws-change-start.md → p-aiws-change-start.md} +3 -1
  64. package/templates/workspace/.opencode/command/{aiws-change-status.md → p-aiws-change-status.md} +3 -1
  65. package/templates/workspace/.opencode/command/{aiws-change-sync.md → p-aiws-change-sync.md} +3 -1
  66. package/templates/workspace/.opencode/command/{aiws-change-templates-init.md → p-aiws-change-templates-init.md} +3 -1
  67. package/templates/workspace/.opencode/command/{aiws-change-templates-which.md → p-aiws-change-templates-which.md} +3 -1
  68. package/templates/workspace/.opencode/command/{aiws-change-validate.md → p-aiws-change-validate.md} +3 -1
  69. package/templates/workspace/.opencode/command/{aiws-hooks-install.md → p-aiws-hooks-install.md} +3 -1
  70. package/templates/workspace/.opencode/command/{aiws-hooks-status.md → p-aiws-hooks-status.md} +3 -1
  71. package/templates/workspace/.opencode/command/{aiws-init.md → p-aiws-init.md} +3 -1
  72. package/templates/workspace/.opencode/command/{aiws-rollback.md → p-aiws-rollback.md} +3 -1
  73. package/templates/workspace/.opencode/command/{aiws-update.md → p-aiws-update.md} +3 -1
  74. package/templates/workspace/.opencode/command/{aiws-validate.md → p-aiws-validate.md} +3 -1
  75. package/templates/workspace/.opencode/command/ws-bugfix.md +11 -6
  76. package/templates/workspace/.opencode/command/ws-commit.md +3 -2
  77. package/templates/workspace/.opencode/command/ws-deliver.md +6 -2
  78. package/templates/workspace/.opencode/command/ws-dev.md +7 -1
  79. package/templates/workspace/.opencode/command/ws-finish.md +19 -19
  80. package/templates/workspace/.opencode/command/ws-handoff.md +25 -0
  81. package/templates/workspace/.opencode/command/ws-plan.md +9 -5
  82. package/templates/workspace/.opencode/command/ws-review.md +1 -1
  83. package/templates/workspace/AGENTS.md +8 -4
  84. package/templates/workspace/AI_WORKSPACE.md +4 -4
  85. package/templates/workspace/changes/README.md +8 -2
  86. package/templates/workspace/manifest.json +173 -103
  87. package/templates/workspace/tools/ws_change_check.py +99 -0
  88. package/templates/workspace/tools/ws_resolve_sub_target.sh +47 -0
  89. package/templates/workspace/tools/ws_tasks_plan.py +137 -0
@@ -18,6 +18,7 @@
18
18
  - managed surface(可托管面):
19
19
  - `replace_file`:该文件由 aiws 全量托管(更新可整文件覆盖)。
20
20
  - `managed_blocks`:只更新文件内 `AIWS_MANAGED_BEGIN/END` 标记的托管块;块外内容保留给用户自定义。
21
+ - `remove`:该文件在本版本已废弃;`update` 会先备份再删除(可通过 `aiws rollback` 恢复)。仅用于跨版本改名/废弃场景。
21
22
  - unmanaged required(必需但不托管):
22
23
  - 模板 `manifest.json` 里的 `required` 仅表示“init/validate 必须存在”。
23
24
  - 若某个 required 文件不在 `update.replace_file` 且不在 `update.managed_blocks` 中:视为用户自主管理内容;`aiws update` 不应修改其内容(最多只补齐缺失文件)。
@@ -127,8 +128,8 @@ hash 规则(用于跨平台稳定性):
127
128
  - 默认覆盖:Claude Code / OpenCode / Codex / iFlow。
128
129
 
129
130
  约定路径(示例;最终以模板 manifest 为准):
130
- - Claude Code:`.claude/commands/aiws-*.md`(commands
131
- - OpenCode:`.opencode/command/aiws-*.md`(command
131
+ - Claude Code:`.claude/commands/p-aiws-*.md`(commands;私有原子入口)
132
+ - OpenCode:`.opencode/command/p-aiws-*.md`(command;私有原子入口)
132
133
  - Codex:`.agents/skills/*/SKILL.md`(skills)
133
134
  - iFlow:`.iflow/commands/aiws-*.toml`(commands)
134
135
 
@@ -160,13 +161,13 @@ hash 规则(用于跨平台稳定性):
160
161
 
161
162
  建议的默认取值(与 `workspace` 模板一致):
162
163
  - Claude Code
163
- - `path_pattern`:`.claude/commands/aiws-*.md`
164
+ - `path_pattern`:`.claude/commands/p-aiws-*.md`
164
165
  - `purpose`:提供“在 Claude 内可见的命令入口/说明”,引导用户运行 `npx @aipper/aiws ...`
165
166
  - `autodiscovery`:不作为契约要求(由工具决定;aiws 不做保证)
166
167
  - `reload_needed`:不作为契约要求
167
168
  - `fallback`:在 shell 直接运行 `npx @aipper/aiws init|update|validate|rollback`
168
169
  - OpenCode
169
- - `path_pattern`:`.opencode/command/aiws-*.md`
170
+ - `path_pattern`:`.opencode/command/p-aiws-*.md`
170
171
  - `purpose`:提供命令入口/说明(同上)
171
172
  - `autodiscovery`:不作为契约要求
172
173
  - `reload_needed`:不作为契约要求
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aipper/aiws-spec",
3
- "version": "0.0.21",
3
+ "version": "0.0.24",
4
4
  "description": "AIWS spec and templates (single source of truth).",
5
5
  "type": "module",
6
6
  "files": [
@@ -1,6 +1,6 @@
1
1
  ---
2
- name: aiws-change-archive
3
- description: 归档变更工件(会先做严格校验)
2
+ name: p-aiws-change-archive
3
+ description: 私有:归档变更工件(会先做严格校验并生成 handoff.md)
4
4
  ---
5
5
 
6
6
  目标:
@@ -21,3 +21,4 @@ fi
21
21
  说明:
22
22
  - `archive` 默认会先跑严格校验并要求 tasks 全部勾选
23
23
  - `--force` 会绕过部分门禁(不推荐)
24
+ - 归档后会生成交接文档:`changes/archive/<date>-<change-id>/handoff.md`
@@ -1,6 +1,6 @@
1
1
  ---
2
- name: aiws-change-finish
3
- description: 安全合并 change/<change-id> 回目标分支(默认 fast-forward)
2
+ name: p-aiws-change-finish
3
+ description: 私有:安全合并 change/<change-id> 回目标分支(默认 fast-forward)
4
4
  ---
5
5
 
6
6
  目标:
@@ -1,6 +1,6 @@
1
1
  ---
2
- name: aiws-change-list
3
- description: 列出 changes 工件(只读)
2
+ name: p-aiws-change-list
3
+ description: 私有:列出 changes 工件(只读)
4
4
  ---
5
5
 
6
6
  目标:
@@ -1,6 +1,6 @@
1
1
  ---
2
- name: aiws-change-new
3
- description: 创建 changes/<change-id> 工件
2
+ name: p-aiws-change-new
3
+ description: 私有:创建 changes/<change-id> 工件
4
4
  ---
5
5
 
6
6
  目标:
@@ -1,6 +1,6 @@
1
1
  ---
2
- name: aiws-change-next
3
- description: 给出下一步建议(只读)
2
+ name: p-aiws-change-next
3
+ description: 私有:给出下一步建议(只读)
4
4
  ---
5
5
 
6
6
  目标:
@@ -1,6 +1,6 @@
1
1
  ---
2
- name: aiws-change-start
3
- description: 切分支并初始化变更工件(可选安装 hooks)
2
+ name: p-aiws-change-start
3
+ description: 私有:切分支并初始化变更工件(可选安装 hooks)
4
4
  ---
5
5
 
6
6
  目标:
@@ -1,6 +1,6 @@
1
1
  ---
2
- name: aiws-change-status
3
- description: 查看单个变更工件状态(只读)
2
+ name: p-aiws-change-status
3
+ description: 私有:查看单个变更工件状态(只读)
4
4
  ---
5
5
 
6
6
  目标:
@@ -1,6 +1,6 @@
1
1
  ---
2
- name: aiws-change-sync
3
- description: 同步真值基线到 changes/<change-id>(写入 .ws-change.json)
2
+ name: p-aiws-change-sync
3
+ description: 私有:同步真值基线到 changes/<change-id>(写入 .ws-change.json)
4
4
  ---
5
5
 
6
6
  目标:
@@ -1,6 +1,6 @@
1
1
  ---
2
- name: aiws-change-templates-init
3
- description: 在仓库内初始化 change templates(写入 changes/templates)
2
+ name: p-aiws-change-templates-init
3
+ description: 私有:在仓库内初始化 change templates(写入 changes/templates)
4
4
  ---
5
5
 
6
6
  目标:
@@ -1,6 +1,6 @@
1
1
  ---
2
- name: aiws-change-templates-which
3
- description: 查看当前 change templates 来源(只读)
2
+ name: p-aiws-change-templates-which
3
+ description: 私有:查看当前 change templates 来源(只读)
4
4
  ---
5
5
 
6
6
  目标:
@@ -1,6 +1,6 @@
1
1
  ---
2
- name: aiws-change-validate
3
- description: 校验 changes 工件(可 strict)
2
+ name: p-aiws-change-validate
3
+ description: 私有:校验 changes 工件(可 strict)
4
4
  ---
5
5
 
6
6
  目标:
@@ -1,6 +1,6 @@
1
1
  ---
2
- name: aiws-hooks-install
3
- description: 启用 git hooks 门禁(core.hooksPath=.githooks)
2
+ name: p-aiws-hooks-install
3
+ description: 私有:启用 git hooks 门禁(core.hooksPath=.githooks)
4
4
  ---
5
5
 
6
6
  目标:
@@ -1,6 +1,6 @@
1
1
  ---
2
- name: aiws-hooks-status
3
- description: 查看当前仓库 hooks 状态(只读)
2
+ name: p-aiws-hooks-status
3
+ description: 私有:查看当前仓库 hooks 状态(只读)
4
4
  ---
5
5
 
6
6
  目标:
@@ -1,6 +1,6 @@
1
1
  ---
2
- name: aiws-init
3
- description: 初始化工作区(生成真值文件与门禁)
2
+ name: p-aiws-init
3
+ description: 私有:初始化工作区(生成真值文件与门禁)
4
4
  ---
5
5
 
6
6
  目标:
@@ -1,6 +1,6 @@
1
1
  ---
2
- name: aiws-rollback
3
- description: 回滚工作区(从备份恢复)
2
+ name: p-aiws-rollback
3
+ description: 私有:回滚工作区(从备份恢复)
4
4
  ---
5
5
 
6
6
  目标:
@@ -1,6 +1,6 @@
1
1
  ---
2
- name: aiws-update
3
- description: 更新工作区(刷新模板与托管块)
2
+ name: p-aiws-update
3
+ description: 私有:更新工作区(刷新模板与托管块)
4
4
  ---
5
5
 
6
6
  目标:
@@ -1,6 +1,6 @@
1
1
  ---
2
- name: aiws-validate
3
- description: 校验工作区(漂移检测 + 门禁)
2
+ name: p-aiws-validate
3
+ description: 私有:校验工作区(漂移检测 + 门禁)
4
4
  ---
5
5
 
6
6
  目标:
@@ -0,0 +1,37 @@
1
+ ---
2
+ name: p-tasks-plan
3
+ description: 原子:tasks 同步(从 changes/<id>/tasks.md 生成 update_plan payload)
4
+ ---
5
+
6
+ 用中文输出(命令/路径/代码标识符保持原样不翻译)。
7
+
8
+ 目标:
9
+ - 用 `changes/<change-id>/tasks.md` 的 checkbox 任务作为“步骤真值”
10
+ - 生成可直接用于 `update_plan` 的 JSON payload(`pending/in_progress/completed`)
11
+ - 输出当前进度摘要(done/total + 当前 in_progress)
12
+
13
+ 约束:
14
+ - 不写入任何 secrets(token、账号、内网端点等不得进入 git)
15
+ - 只读 `changes/<id>/tasks.md`(不自动改写 tasks 文件)
16
+ - 未运行不声称已运行
17
+
18
+ 执行步骤(建议):
19
+ 1) 先运行 `$ws-preflight`。
20
+ 2) 推断 `change-id`:
21
+ - 优先从当前分支名读取:`git rev-parse --abbrev-ref HEAD`(期望形如 `change/<change-id>`)
22
+ - 若无法推断:让用户明确本次要同步的 `<change-id>`
23
+ 3) 检查 tasks 文件存在:`test -f changes/<change-id>/tasks.md`
24
+ 4) 输出进度摘要:
25
+ ```bash
26
+ python3 tools/ws_tasks_plan.py status --file changes/<change-id>/tasks.md
27
+ ```
28
+ 5) 生成 `update_plan` payload(JSON 输出到 stdout):
29
+ ```bash
30
+ python3 tools/ws_tasks_plan.py plan --file changes/<change-id>/tasks.md --explanation "sync tasks.md -> update_plan"
31
+ ```
32
+ 6) 调用 `update_plan`,将上一步 JSON 原样作为入参(确保任意时刻最多 1 条 `in_progress`)。
33
+
34
+ 输出要求:
35
+ - `Change_ID:` <change-id>
36
+ - `Tasks:` `changes/<change-id>/tasks.md`
37
+ - `Next:` 推荐下一步(通常为继续完善 tasks 或进入 `$ws-dev`)
@@ -19,12 +19,34 @@ description: 缺陷修复(通过禅道 MCP 拉取 bug 与附件,下载图片
19
19
  前置:
20
20
  1) 先运行 `$ws-preflight`。
21
21
  2) 准备 `change-id`(建议:`bug-<bug-id>` 或 `bugfix-<bug-id>-<slug>`)。
22
- 3) 建立变更工件(推荐):
22
+ 3) 建立 change 上下文(推荐先于任何落盘):
23
+ - 若当前还不在 `change/<change-id>` 分支 / worktree,先调用 `aiws change start`
24
+ - 工作区必须先干净;否则不要先写 `changes/<change-id>/bug/` 或 `issues/fix_bus_issues.csv`,避免后续切 worktree 时工件留在原工作区
25
+ - 仓库已有提交:优先 `--worktree`
26
+ - superproject + submodule:优先 `--worktree --submodules`
27
+ - 仓库尚无提交 / 不满足 worktree 前置条件:回退 `--no-switch`
23
28
  ```bash
24
- aiws change start <change-id> --hooks
25
- # superproject + submodule 推荐:
26
- aiws change start <change-id> --hooks --worktree --submodules
29
+ if [[ -n "$(git status --porcelain)" ]]; then
30
+ echo "error: working tree dirty before ws-bugfix creates change context"
31
+ exit 2
32
+ fi
33
+
34
+ if git rev-parse --verify HEAD >/dev/null 2>&1; then
35
+ if [[ -f .gitmodules ]] && git config --file .gitmodules --get-regexp '^submodule\\..*\\.path$' >/dev/null 2>&1; then
36
+ aiws change start <change-id> --hooks --worktree --submodules
37
+ else
38
+ aiws change start <change-id> --hooks --worktree
39
+ fi
40
+ else
41
+ aiws change start <change-id> --hooks --no-switch
42
+ fi
27
43
  ```
44
+ - 若上一步创建了 worktree:后续 bug 证据、CSV 更新、`$ws-dev` 修复都必须在该 worktree 中继续;不要回原工作区重复创建 change
45
+ - 若该 change 涉及 submodule:
46
+ - 优先复用 `$ws-dev` 的 `submodules.targets` 生成/确认流程
47
+ - detached HEAD 时默认建议取 `.gitmodules` 声明的分支
48
+ - 已附着在某个本地分支时默认建议取当前分支
49
+ - 以上都只是建议值,最终必须显式写入 `changes/<change-id>/submodules.targets`
28
50
 
29
51
  建议流程(按顺序):
30
52
 
@@ -36,7 +58,7 @@ aiws change start <change-id> --hooks --worktree --submodules
36
58
  - 若当前环境没有 zentao MCP 工具:立即停止并提示用户先配置,不要猜数据。
37
59
 
38
60
  ## 2) 证据落盘(强制)
39
- `changes/<change-id>/bug/` 下落盘:
61
+ 在当前 active change 上下文的 `changes/<change-id>/bug/` 下落盘:
40
62
  - `zentao-bug-<bug-id>.json`:原始字段快照(避免信息丢失)
41
63
  - `zentao-bug-<bug-id>.md`:人类可读摘要(复现步骤/期望/实际/风险)
42
64
  - `images/<bug-id>/...`:下载的图片附件(保留原扩展名)
@@ -50,7 +72,7 @@ changes/<change-id>/bug/
50
72
  ```
51
73
 
52
74
  ## 3) 汇总到 issues/fix_bus_issues.csv(upsert)
53
- - 目标文件:`issues/fix_bus_issues.csv`
75
+ - 目标文件:当前 active change 上下文中的 `issues/fix_bus_issues.csv`
54
76
  - 若文件不存在,先创建表头:
55
77
  ```csv
56
78
  Bug_ID,Title,Severity,Module,Status,Assigned_To,Change_ID,Image_Count,Image_Paths,Evidence_Path,Verify_Command,Fix_Status,Updated_At,Notes
@@ -66,7 +88,7 @@ Bug_ID,Title,Severity,Module,Status,Assigned_To,Change_ID,Image_Count,Image_Path
66
88
  - `Fix_Status`:`TODO|DOING|DONE|BLOCKED`
67
89
 
68
90
  ## 4) 修复执行与回填
69
- - 进入 `$ws-dev` 做最小改动修复。
91
+ - 进入 `$ws-dev` 做最小改动修复;若 `ws-bugfix` 创建了 worktree,则必须在该 worktree 中继续。
70
92
  - 完成后回填 `issues/fix_bus_issues.csv`:
71
93
  - `Fix_Status`
72
94
  - `Verify_Command`
@@ -83,6 +105,7 @@ aiws validate . --stamp
83
105
 
84
106
  输出要求:
85
107
  - `Change_ID:` `<change-id>`
108
+ - `Change context:` `<当前分支或 worktree 路径>`
86
109
  - `CSV:` `issues/fix_bus_issues.csv` 中对应 `Bug_ID` 行的关键字段
87
110
  - `Evidence:` `changes/<change-id>/bug/zentao-bug-<bug-id>.md` + 图片目录
88
111
  - `Verify:` 实际运行命令与结果(未运行不声称已运行)
@@ -18,6 +18,8 @@ description: 提交(当前分支可直提;submodule 感知;先审计/门
18
18
  - 不自动 push
19
19
  - 不写入任何 secrets
20
20
  - 检测到 submodule 有未提交改动时,不允许直接提交 superproject(先处理 submodule)
21
+ - commit message 优先使用中文(命令/路径/代码标识符保持原样不翻译);格式建议:`<类型>: <简述>`(例如 `修复: 登录页空指针`、`功能: 新增 submodule targets 校验`、`重构: 提取共享脚本`)
22
+ - 若启用了 `.githooks/commit-msg`:默认会提示优先中文;只有在 `git config aiws.commitMessagePolicy strict` 时才会拒绝全英文首行(`Merge/Revert/fixup!/squash!` 例外)
21
23
 
22
24
  执行步骤(建议):
23
25
  1) 运行 `$ws-preflight`(确保真值文件就绪)。
@@ -59,11 +61,22 @@ done < <(git config --file .gitmodules --get-regexp '^submodule\..*\.path$' 2>/d
59
61
  - 若该 submodule 当前为 detached HEAD:先按 `.gitmodules` 的目标分支挂到 `aiws/pin/<target_branch>`;不要直接切 `change/<change-id>` / `main` / `master` 来“解 detached”。
60
62
  处理指引(detached submodule):
61
63
  ```bash
62
- sub_name="<submodule-name>"
63
- base_branch="$(git branch --show-current)"
64
- cfg_branch="$(git config --file .gitmodules --get "submodule.${sub_name}.branch" 2>/dev/null || true)"
65
- if [[ "${cfg_branch:-}" == "." ]]; then cfg_branch="$base_branch"; fi
66
- git -C "${sub_path}" checkout -B "aiws/pin/${cfg_branch}" HEAD
64
+ cur_branch="$(git branch --show-current)"
65
+ change_id="$(echo "${cur_branch}" | sed -n 's|^change/||p')"
66
+ targets="changes/${change_id}/submodules.targets"
67
+
68
+ source tools/ws_resolve_sub_target.sh
69
+ ws_resolve_sub_target "${sub_path}" "${sub_name}" "${targets}" "${cur_branch}" || exit 2
70
+ target_branch="${_resolved_branch}"
71
+ remote="${_resolved_remote}"
72
+
73
+ git -C "${sub_path}" fetch "${remote}" --prune
74
+ if ! git -C "${sub_path}" show-ref --verify --quiet "refs/remotes/${remote}/${target_branch}"; then
75
+ echo "error: missing ${remote}/${target_branch} for submodule path=${sub_path}"
76
+ exit 2
77
+ fi
78
+ git -C "${sub_path}" checkout -B "aiws/pin/${target_branch}" HEAD
79
+ git -C "${sub_path}" branch --set-upstream-to "${remote}/${target_branch}" "aiws/pin/${target_branch}" >/dev/null 2>&1 || true
67
80
  ```
68
81
  7) 检查当前 staging 内容(必须输出给用户确认):
69
82
  ```bash
@@ -71,7 +84,11 @@ git status --porcelain
71
84
  git diff --staged --submodule=short
72
85
  ```
73
86
  8) 若没有 staged changes:停止并提示用户先明确要提交哪些文件(例如 `git add -p` 或 `git add <path>`)。
74
- 9) 让用户提供 commit message(必须确认后再执行)。
87
+ 9) 生成中文 commit message 草案(格式:`<类型>: <简述>`),输出给用户确认后再执行。
88
+ - 类型参考:`功能` / `修复` / `重构` / `文档` / `测试` / `构建` / `杂项`
89
+ - 简述用一句话概括本次改动的"为什么"而非"改了什么"
90
+ - 命令/路径/代码标识符保持原样不翻译
91
+ - 若用户给出全英文 message:优先改写成中文;若用户明确要求保留英文,也可以提交(但 strict 模式下会被 hook 拒绝)
75
92
  10) 执行提交(不带 `--no-verify`):
76
93
  ```bash
77
94
  git commit -m "<message>"
@@ -29,6 +29,8 @@ description: 交付(submodules + superproject 分步提交,并安全合并
29
29
 
30
30
  ## 0) submodule branch 真值检查(减少 detached 与人为差异)
31
31
  如果存在 `.gitmodules` 但缺少 `submodule.<name>.branch`,先运行 `$ws-submodule-setup` 并提交 `.gitmodules`,否则后续 `aiws validate .` 会失败,且 `ws-pull/ws-finish` 无法确定性工作。
32
+ > 说明:若同一 superproject 分支内存在多渠道 submodule 目标分支的交付需求,可在 `changes/<change-id>/submodules.targets` 额外声明本次 change 的目标分支;交付时会优先使用该文件(不改变 `.gitmodules` 的团队默认真值)。
33
+ > 生成该文件时,可以按当前状态做默认预填,但必须显式说明来源并让用户确认:detached HEAD 默认建议取 `.gitmodules` 声明分支;已附着在某个本地分支时默认建议取当前分支;finish/push 最终只认 `submodules.targets`。
32
34
  ```bash
33
35
  if [[ -f .gitmodules ]]; then
34
36
  missing=0
@@ -44,6 +46,28 @@ if [[ -f .gitmodules ]]; then
44
46
  echo "hint: run $ws-submodule-setup (and commit .gitmodules), then retry"
45
47
  exit 2
46
48
  fi
49
+
50
+ # 强约束:当 .gitmodules 声明 submodules 时,要求本次 change 存在 submodules.targets 且覆盖所有 submodule path
51
+ if git config --file .gitmodules --get-regexp '^submodule\\..*\\.path$' >/dev/null 2>&1; then
52
+ change_id="$(git branch --show-current | sed -n 's|^change/||p')"
53
+ targets="changes/${change_id}/submodules.targets"
54
+ if [[ ! -f "${targets}" ]]; then
55
+ echo "error: missing ${targets} (required when .gitmodules declares submodules)"
56
+ exit 2
57
+ fi
58
+ t_missing=0
59
+ while read -r _ sub_path; do
60
+ [[ -z "${sub_path:-}" ]] && continue
61
+ if ! awk -v p="${sub_path}" '$1==p && $0 !~ /^[[:space:]]*#/ && $2!="" { found=1 } END { exit(found?0:1) }' "${targets}" 2>/dev/null; then
62
+ echo "error: ${targets} missing entry for submodule path=${sub_path}"
63
+ t_missing=1
64
+ fi
65
+ done < <(git config --file .gitmodules --get-regexp '^submodule\\..*\\.path$' 2>/dev/null || true)
66
+ if [[ "${t_missing}" -ne 0 ]]; then
67
+ echo "hint: fill ${targets} as: <path> <target_branch> [remote]"
68
+ exit 2
69
+ fi
70
+ fi
47
71
  fi
48
72
  ```
49
73
 
@@ -62,36 +86,56 @@ sub_path="<path>"
62
86
  git -C "$sub_path" branch --show-current
63
87
  git -C "$sub_path" status --porcelain
64
88
  ```
65
- 2) submodule 处于 detached HEAD(`branch --show-current` 为空):
89
+ 2) 先确定该 submodule 的目标分支来源,并显式说明给用户:
90
+ - `branch --show-current` 非空:默认建议用当前分支
91
+ - `branch --show-current` 为空(detached HEAD):默认建议用 `.gitmodules` 的 `submodule.<name>.branch`
92
+ - 若建议值与 `changes/<change-id>/submodules.targets` 已落盘值不一致:以 `submodules.targets` 为准,并先提示差异
93
+ 3) 若 submodule 处于 detached HEAD(`branch --show-current` 为空):
66
94
  - 说明:这通常是因为 superproject 的 gitlink checkout(例如 `git submodule update`)导致 detached。
67
- - 不要直接切 `change/<change-id>` / `main` / `master` 来“解 detached”。
68
- - 若你要在该 submodule 里提交:先按 `.gitmodules` 的目标分支挂到 pin 分支 `aiws/pin/<target-branch>`,再在其上提交:
95
+ - 不要直接切 `change/<change-id>` / `main` / `master` 来解 detached
96
+ - 若你要在该 submodule 里提交:先按目标分支挂到 pin 分支 `aiws/pin/<target-branch>`,再在其上提交:
97
+ - 目标分支真值优先级:`changes/<change-id>/submodules.targets`(若存在)> `.gitmodules submodule.<name>.branch`
69
98
  ```bash
70
- sub_name="<submodule-name>"
71
- base_branch="$(git branch --show-current)"
72
- cfg_branch="$(git config --file .gitmodules --get "submodule.${sub_name}.branch" 2>/dev/null || true)"
73
- if [[ "${cfg_branch:-}" == "." ]]; then cfg_branch="$base_branch"; fi
74
- if [[ -z "${cfg_branch:-}" ]]; then
75
- echo "error: missing .gitmodules submodule.${sub_name}.branch (path=${sub_path})"
99
+ change_id="<change-id>"
100
+ targets="changes/${change_id}/submodules.targets"
101
+ sub_name="$(git config --file .gitmodules --get-regexp '^submodule\\..*\\.path$' 2>/dev/null | awk -v p="${sub_path}" '$2==p { name=$1; sub(/^submodule\\./,"",name); sub(/\\.path$/,"",name); print name; exit }')"
102
+ base_branch="$(python3 - <<'PY'
103
+ import json, pathlib
104
+ change_id = "<change-id>"
105
+ meta = pathlib.Path("changes") / change_id / ".ws-change.json"
106
+ data = json.loads(meta.read_text(encoding="utf-8"))
107
+ print((data.get("base_branch") or "").strip())
108
+ PY
109
+ )"
110
+ test -n "${sub_name}"
111
+ test -n "${base_branch}"
112
+
113
+ source tools/ws_resolve_sub_target.sh
114
+ ws_resolve_sub_target "${sub_path}" "${sub_name}" "${targets}" "${base_branch}" || exit 2
115
+ target_branch="${_resolved_branch}"
116
+ remote="${_resolved_remote}"
117
+
118
+ git -C "$sub_path" fetch "${remote}" --prune
119
+ if ! git -C "$sub_path" show-ref --verify --quiet "refs/remotes/${remote}/${target_branch}"; then
120
+ echo "error: missing ${remote}/${target_branch} for submodule path=${sub_path}"
76
121
  exit 2
77
122
  fi
78
- git -C "$sub_path" fetch origin --prune
79
- git -C "$sub_path" checkout -B "aiws/pin/${cfg_branch}" HEAD
80
- git -C "$sub_path" branch --set-upstream-to "origin/${cfg_branch}" "aiws/pin/${cfg_branch}" >/dev/null 2>&1 || true
123
+ git -C "$sub_path" checkout -B "aiws/pin/${target_branch}" HEAD
124
+ git -C "$sub_path" branch --set-upstream-to "${remote}/${target_branch}" "aiws/pin/${target_branch}" >/dev/null 2>&1 || true
81
125
  ```
82
126
  - 若 `origin/<target-branch>` 不存在,或用户明确不想使用 pin 分支:停止,解释风险(提交可能不可追溯/难以推送)。
83
- 3) 选择性 staging(默认用 `-p` 更安全):
127
+ 4) 选择性 staging(默认用 `-p` 更安全):
84
128
  ```bash
85
129
  git -C "$sub_path" add -p
86
130
  git -C "$sub_path" diff --staged --stat
87
131
  git -C "$sub_path" diff --staged
88
132
  ```
89
- 4) AI 生成该 submodule 的 commit message(标题+可选 body),并让用户确认(每个 repo 单独确认)。
90
- 5) 执行提交(不带 `--no-verify`):
133
+ 5) AI 生成该 submodule 的 commit message(标题+可选 body),并让用户确认(每个 repo 单独确认)。
134
+ 6) 执行提交(不带 `--no-verify`):
91
135
  ```bash
92
136
  git -C "$sub_path" commit -m "<message>"
93
137
  ```
94
- 6) 若该 submodule 没有 staged changes:跳过(不要硬提交空 commit)。
138
+ 7) 若该 submodule 没有 staged changes:跳过(不要硬提交空 commit)。
95
139
 
96
140
  ## C) 提交 superproject(更新 gitlinks + 自身改动 + changes 工件)
97
141
  1) 先检查 submodule 指针差异(gitlinks):
@@ -109,7 +153,7 @@ git add -p
109
153
  git diff --staged --stat
110
154
  git diff --staged
111
155
  ```
112
- 3) AI 生成 superproject 的 commit message(应包含 bump submodule <name> -> <sha>” 等关键信息),并让用户确认。
156
+ 3) AI 生成 superproject 的 commit message(应包含 `bump submodule <name> -> <sha>` 等关键信息),并让用户确认。
113
157
  4) 提交:
114
158
  ```bash
115
159
  git commit -m "<message>"
@@ -11,10 +11,110 @@ description: 开发(按需求实现并验证;适用于任何需要修改代
11
11
  1) 先做 preflight:定位项目根目录,读取 `AI_PROJECT.md` / `REQUIREMENTS.md` / `AI_WORKSPACE.md`,输出约束摘要。
12
12
  - 若是中大型任务:建议先用 `$ws-plan` 生成 `plan/` 工件,再进入实现(便于可回放与对齐验证入口)。
13
13
  - 若已有 `plan/` 工件:先执行 `$ws-plan-verify`;通过后再进入实现(防止计划过长/跑偏)。
14
+ - 若 `$ws-plan` 刚创建了 `change/<change-id>` worktree:后续实现必须在该 worktree 中继续;不要回到原工作区重复 `aiws change start ...`
14
15
  2) 建立变更归因(推荐):
15
- - 推荐一键:`aiws change start <change-id> --hooks`(切分支 + 初始化变更工件 + 启用 hooks)
16
- - superproject + submodule(推荐):`aiws change start <change-id> --hooks --worktree --submodules`(创建独立 worktree;当前目录分支保持不变)
16
+ - ⚠️ 若你准备执行 `aiws change start ... --switch/--worktree` 或手工 `git switch ...` 改变 checkout 上下文,先确认工作区状态:`git status --porcelain`。否则切分支/创建 worktree 后,未提交改动可能看起来丢了(worktree 只从 `HEAD` checkout,未提交内容会留在原目录)。
17
+ - 若当前目录已经是 `change/<change-id>` worktree(例如由 `$ws-plan` 创建):直接在这里继续,不要再创建第二个 worktree,也不要回原工作区写代码。
18
+ - 若 `git status --porcelain` 非空仅因为上一轮 `$ws-plan` / `aiws change new|sync` 生成了 `plan/...`、`changes/<change-id>/proposal.md`、`tasks.md`、`design.md`、`submodules.targets`,这是预期行为,不等于流程出错。处理方式:
19
+ - 已经在 `change/<change-id>` 分支:直接继续实现,并把这些文件作为计划/证据工件保留。
20
+ - 还没进入 `change/<change-id>`:先运行 `aiws change start <change-id> --hooks --no-switch` 建立 change 上下文;若你仍要 `--switch/--worktree`,先提交这些规划工件,再切换上下文。
21
+ - 强制(当你准备执行 `aiws change start ... --worktree/--switch` 创建新 change 时):先同步线上代码(含 submodules),避免基线过旧导致的 rebase/冲突与别人已更新但本地没拉的协作摩擦。
22
+ - 若你已经在 `change/<change-id>` 上继续开发:不要在此处强制 pull(避免把远端变动拉进变更分支);改为按需 `git fetch`/rebase,并保持门禁与验证可复现。
23
+ - 在 AI 工具中运行:`$ws-pull`(推荐;会在工作区不干净时阻断)
24
+ - 或等价手工(必须工作区干净;失败则停止并人工处理):
25
+ ```bash
26
+ cur="$(git branch --show-current)"
27
+ if [[ "${cur}" =~ ^(change|changes|ws|ws-change)/ ]]; then
28
+ echo "info: already on change branch (${cur}); skip pull here"
29
+ else
30
+ git status --porcelain
31
+ git pull --ff-only
32
+ if [[ -f .gitmodules ]]; then
33
+ git submodule sync --recursive
34
+ git submodule update --init --recursive
35
+ fi
36
+ fi
37
+ ```
38
+ - 推荐更安全(默认):`aiws change start <change-id> --hooks --no-switch`(只创建分支/工件 + 启用 hooks;不切分支)
39
+ - 准备进入实现时:若当前已在 `change/<change-id>` 直接继续;若需切换到该分支,先确认除规划工件外无额外未提交改动,再执行:`git switch change/<change-id>`
40
+ - 若你明确要一键切分支(不推荐,且 dirty 会被拦截):`aiws change start <change-id> --hooks --switch`
41
+ - superproject + submodule(推荐):`aiws change start <change-id> --hooks --worktree --submodules`(创建独立 worktree;当前目录分支保持不变;会在新 worktree 内初始化 submodules;若忘了 `--submodules` 也会强制初始化)
17
42
  - 若后续需要在 detached submodule 内提交:先挂到 `aiws/pin/<target-branch>`;不要直接切 `change/<change-id>` / `main` / `master`
43
+ - 若仓库存在 submodule(`.gitmodules` 声明了 submodule 条目):进入编码前必须准备好 `changes/<change-id>/submodules.targets`,并把每个 submodule 挂到对应的 `aiws/pin/<target_branch>`(必要时切到 `<remote>/<target_branch>`;这可能会改变 superproject 的 gitlink 指针,属于预期的选渠道行为;缺失该文件会被门禁阻断)
44
+ - 允许用当前 submodule 状态来做默认预填,但必须显式说明来源,并最终落盘到 `submodules.targets`:
45
+ - detached HEAD:默认建议取 `.gitmodules` 中声明的 `submodule.<name>.branch`
46
+ - 已附着在某个本地分支:默认建议取该 submodule 的当前分支名
47
+ - 上述两条都只是建议值,不是运行时真值;真正的 finish/push 只认 `changes/<change-id>/submodules.targets`
48
+ ```bash
49
+ change_id="<change-id>"
50
+ targets="changes/${change_id}/submodules.targets"
51
+ has_submodules=0
52
+ if [[ -f .gitmodules ]]; then
53
+ if git config --file .gitmodules --get-regexp '^submodule\\..*\\.path$' >/dev/null 2>&1; then
54
+ has_submodules=1
55
+ fi
56
+ fi
57
+ if [[ "${has_submodules}" -eq 1 && ! -f "${targets}" ]]; then
58
+ echo "error: missing ${targets} (required when .gitmodules declares submodules)"
59
+ echo "hint: create it first; defaults may be inferred, but the final truth must be written explicitly"
60
+ exit 2
61
+ fi
62
+
63
+ if [[ -f "${targets}" ]]; then
64
+ echo "info: applying submodule targets: ${targets}"
65
+ while read -r sub_path target_branch remote; do
66
+ [[ -z "${sub_path:-}" ]] && continue
67
+ [[ "${sub_path}" == \#* ]] && continue
68
+ [[ -z "${target_branch:-}" ]] && { echo "error: missing target_branch for path=${sub_path}"; exit 2; }
69
+ remote="${remote:-origin}"
70
+
71
+ echo "== submodule: ${sub_path} target=${remote}/${target_branch} =="
72
+ if [[ -n "$(git -C "${sub_path}" status --porcelain 2>/dev/null)" ]]; then
73
+ echo "error: submodule dirty: ${sub_path} (commit/stash first)"
74
+ exit 2
75
+ fi
76
+ git -C "${sub_path}" fetch "${remote}" --prune
77
+ # 选渠道:默认切到远端目标分支,并用 pin 分支承载本地提交(避免 detached)
78
+ git -C "${sub_path}" checkout -B "aiws/pin/${target_branch}" "${remote}/${target_branch}"
79
+ git -C "${sub_path}" branch --set-upstream-to "${remote}/${target_branch}" "aiws/pin/${target_branch}" >/dev/null 2>&1 || true
80
+ git -C "${sub_path}" status -sb
81
+ done < "${targets}"
82
+
83
+ # 检查 superproject 的 gitlink 是否发生变化(预期:若切了不同渠道,会看到差异)
84
+ git diff --submodule
85
+ fi
86
+ ```
87
+ - 若你还没写 `submodules.targets`,可按下面方式先生成建议值,再人工确认后落盘:
88
+ ```bash
89
+ change_id="<change-id>"
90
+ targets="changes/${change_id}/submodules.targets"
91
+ : > "${targets}"
92
+
93
+ git config --file .gitmodules --get-regexp '^submodule\\..*\\.path$' 2>/dev/null | while read -r key sub_path; do
94
+ name="${key#submodule.}"; name="${name%.path}"
95
+ current_branch="$(git -C "${sub_path}" branch --show-current 2>/dev/null || true)"
96
+ declared_branch="$(git config --file .gitmodules --get "submodule.${name}.branch" 2>/dev/null || true)"
97
+
98
+ if [[ -n "${current_branch}" ]]; then
99
+ inferred_branch="${current_branch}"
100
+ inferred_from="current branch"
101
+ else
102
+ inferred_branch="${declared_branch}"
103
+ inferred_from=".gitmodules"
104
+ fi
105
+
106
+ if [[ -z "${inferred_branch}" ]]; then
107
+ echo "error: cannot infer target branch for ${sub_path}; set it manually"
108
+ exit 2
109
+ fi
110
+
111
+ printf "%s %s\n" "${sub_path}" "${inferred_branch}" >> "${targets}"
112
+ echo "info: ${sub_path} -> ${inferred_branch} (inferred from ${inferred_from}; remote defaults to origin unless you edit ${targets})"
113
+ done
114
+
115
+ echo "review required: ${targets}"
116
+ cat "${targets}"
117
+ ```
18
118
  - 若你明确要在 superproject 直接切分支:`aiws change start <change-id> --hooks --switch`(仅在存在 `.gitmodules` 时有意义;会尝试让 submodules 工作区跟随 superproject 指针)
19
119
  - 或手工:`git switch -c change/<change-id>`,并创建 `changes/<change-id>/proposal.md` 与 `changes/<change-id>/tasks.md`(参考 `changes/README.md`)
20
120
  3) 如涉及需求调整:先做需求评审(可用 `$ws-req-review`)→ 用户确认后再做需求落盘(可用 `$ws-req-change`)(避免需求漂移)。