@aipper/aiws-spec 0.0.28 → 0.0.30
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.
- package/docs/cli-interface.md +10 -12
- package/docs/opencode-autonomous-swarm.md +178 -0
- package/docs/opencode-omo-adapter.md +123 -4
- package/docs/opencode-omo-validation-checklist.md +47 -0
- package/docs/opencode-subagent-first.md +187 -0
- package/docs/workflow-delegation-context-injection.md +217 -0
- package/docs/workflow-delegation-contracts.json +108 -2
- package/docs/workflow-delegation-contracts.md +13 -0
- package/docs/workflow-delegation-contracts.schema.json +152 -0
- package/docs/workflow-governance-rules.json +47 -6
- package/docs/workflow-governance-rules.md +8 -7
- package/docs/workflow-governance-rules.schema.json +39 -1
- package/docs/workflow-review-gates.json +6 -0
- package/docs/workflow-review-gates.md +14 -5
- package/docs/workflow-review-gates.schema.json +4 -0
- package/docs/workflow-router-rules.json +72 -8
- package/docs/workflow-router-rules.md +21 -14
- package/docs/workflow-router-rules.schema.json +4 -0
- package/docs/workflow-stage-contracts.json +24 -5
- package/docs/workflow-stage-contracts.md +16 -15
- package/docs/workflow-stage-contracts.schema.json +12 -1
- package/package.json +1 -1
- package/templates/workspace/.agents/skills/using-aiws/SKILL.md +16 -10
- package/templates/workspace/.agents/skills/ws-commit/SKILL.md +6 -118
- package/templates/workspace/.agents/skills/ws-deliver/SKILL.md +6 -218
- package/templates/workspace/.agents/skills/ws-dev/SKILL.md +82 -141
- package/templates/workspace/.agents/skills/ws-finish/SKILL.md +9 -204
- package/templates/workspace/.agents/skills/ws-handoff/SKILL.md +10 -44
- package/templates/workspace/.agents/skills/ws-migrate/SKILL.md +6 -42
- package/templates/workspace/.agents/skills/ws-plan-verify/SKILL.md +6 -58
- package/templates/workspace/.agents/skills/ws-pull/SKILL.md +8 -109
- package/templates/workspace/.agents/skills/ws-push/SKILL.md +8 -100
- package/templates/workspace/.agents/skills/ws-review/SKILL.md +6 -1
- package/templates/workspace/.agents/skills/ws-submodule-setup/SKILL.md +10 -57
- package/templates/workspace/.agents/skills/ws-verify-before-complete/SKILL.md +12 -53
- package/templates/workspace/{changes → .aiws/changes}/README.md +20 -23
- package/templates/workspace/{changes → .aiws/changes}/templates/proposal.md +12 -12
- package/templates/workspace/{changes → .aiws/changes}/templates/tasks.md +8 -8
- package/templates/workspace/{issues → .aiws/issues}/problem-issues.csv +1 -1
- package/templates/workspace/.aiws/journal/.gitkeep +1 -0
- package/templates/workspace/{requirements → .aiws/requirements}/requirements-issues.csv +1 -1
- package/templates/workspace/.aiws/tmp/.gitkeep +0 -0
- package/templates/workspace/{tools → .aiws/tools}/requirements_contract.py +2 -2
- package/templates/workspace/{tools → .aiws/tools}/requirements_contract_sync.py +2 -2
- package/templates/workspace/{tools → .aiws/tools}/requirements_flow_gen.py +1 -1
- package/templates/workspace/{tools → .aiws/tools}/server_test_runner.py +10 -10
- package/templates/workspace/{tools → .aiws/tools}/ws_change_check.py +23 -23
- package/templates/workspace/{tools → .aiws/tools}/ws_resolve_sub_target.sh +2 -2
- package/templates/workspace/{tools → .aiws/tools}/ws_tasks_plan.py +3 -3
- package/templates/workspace/.claude/commands/ws-review.md +5 -1
- package/templates/workspace/.claude/settings.json.example +26 -0
- package/templates/workspace/.claude/skills/ws-commit/SKILL.md +6 -118
- package/templates/workspace/.claude/skills/ws-deliver/SKILL.md +6 -218
- package/templates/workspace/.claude/skills/ws-dev/SKILL.md +52 -141
- package/templates/workspace/.claude/skills/ws-finish/SKILL.md +9 -204
- package/templates/workspace/.claude/skills/ws-handoff/SKILL.md +10 -44
- package/templates/workspace/.claude/skills/ws-migrate/SKILL.md +6 -42
- package/templates/workspace/.claude/skills/ws-plan-verify/SKILL.md +6 -49
- package/templates/workspace/.claude/skills/ws-pull/SKILL.md +8 -109
- package/templates/workspace/.claude/skills/ws-push/SKILL.md +8 -100
- package/templates/workspace/.claude/skills/ws-review/SKILL.md +6 -1
- package/templates/workspace/.claude/skills/ws-submodule-setup/SKILL.md +10 -57
- package/templates/workspace/.claude/skills/ws-verify-before-complete/SKILL.md +12 -53
- package/templates/workspace/.githooks/pre-commit +13 -0
- package/templates/workspace/.githooks/pre-push +7 -0
- package/templates/workspace/.opencode/command/ws-auto.md +33 -0
- package/templates/workspace/.opencode/command/ws-autonomy.md +25 -0
- package/templates/workspace/.opencode/command/ws-review.md +5 -1
- package/templates/workspace/.opencode/commands/ws-auto.md +33 -0
- package/templates/workspace/.opencode/commands/ws-autonomy.md +25 -0
- package/templates/workspace/.opencode/commands/ws-commit.md +4 -56
- package/templates/workspace/.opencode/commands/ws-deliver.md +10 -50
- package/templates/workspace/.opencode/commands/ws-finish.md +11 -3
- package/templates/workspace/.opencode/commands/ws-handoff.md +9 -17
- package/templates/workspace/.opencode/commands/ws-migrate.md +10 -17
- package/templates/workspace/.opencode/commands/ws-plan-verify.md +5 -15
- package/templates/workspace/.opencode/commands/ws-pull.md +6 -75
- package/templates/workspace/.opencode/commands/ws-push.md +7 -82
- package/templates/workspace/.opencode/commands/ws-review.md +5 -1
- package/templates/workspace/.opencode/commands/ws-submodule-setup.md +8 -47
- package/templates/workspace/.opencode/commands/ws-verify-before-complete.md +10 -19
- package/templates/workspace/.opencode/helpers/approval-whitelist-check.sh +148 -0
- package/templates/workspace/.opencode/helpers/approval-whitelist-run.sh +82 -0
- package/templates/workspace/.opencode/helpers/approval-whitelist-watchdog.sh +144 -0
- package/templates/workspace/.opencode/helpers/tmux-swarm-rescue.sh +56 -0
- package/templates/workspace/.opencode/helpers/tmux-swarm-scan.sh +46 -0
- package/templates/workspace/.opencode/lib/aiws-context.js +953 -0
- package/templates/workspace/.opencode/oh-my-opencode.json.example +64 -4
- package/templates/workspace/.opencode/plugins/aiws-inject-context.js +201 -0
- package/templates/workspace/.opencode/plugins/aiws-session-start.js +86 -0
- package/templates/workspace/.opencode/plugins/aiws-workflow-state.js +116 -0
- package/templates/workspace/.opencode/scripts/ws-plan-gen-submodule-targets.sh +27 -0
- package/templates/workspace/.opencode/scripts/ws-plan-setup-worktree.sh +31 -0
- package/templates/workspace/.opencode/skills/using-aiws/SKILL.md +130 -77
- package/templates/workspace/.opencode/skills/ws-analyze/SKILL.md +1 -1
- package/templates/workspace/.opencode/skills/ws-auto/SKILL.md +46 -0
- package/templates/workspace/.opencode/skills/ws-autonomy/SKILL.md +62 -0
- package/templates/workspace/.opencode/skills/ws-bugfix/SKILL.md +1 -1
- package/templates/workspace/.opencode/skills/ws-commit/SKILL.md +6 -118
- package/templates/workspace/.opencode/skills/ws-delegate/SKILL.md +115 -40
- package/templates/workspace/.opencode/skills/ws-deliver/SKILL.md +6 -218
- package/templates/workspace/.opencode/skills/ws-dev/SKILL.md +105 -146
- package/templates/workspace/.opencode/skills/ws-dev-lite/SKILL.md +23 -7
- package/templates/workspace/.opencode/skills/ws-finish/SKILL.md +9 -204
- package/templates/workspace/.opencode/skills/ws-frontend-design/SKILL.md +1 -1
- package/templates/workspace/.opencode/skills/ws-handoff/SKILL.md +10 -44
- package/templates/workspace/.opencode/skills/ws-intake/SKILL.md +43 -4
- package/templates/workspace/.opencode/skills/ws-migrate/SKILL.md +6 -42
- package/templates/workspace/.opencode/skills/ws-plan/SKILL.md +15 -55
- package/templates/workspace/.opencode/skills/ws-plan-verify/SKILL.md +6 -49
- package/templates/workspace/.opencode/skills/ws-preflight/SKILL.md +1 -1
- package/templates/workspace/.opencode/skills/ws-pull/SKILL.md +8 -109
- package/templates/workspace/.opencode/skills/ws-push/SKILL.md +8 -100
- package/templates/workspace/.opencode/skills/ws-quality-review/SKILL.md +7 -2
- package/templates/workspace/.opencode/skills/ws-req-change/SKILL.md +1 -1
- package/templates/workspace/.opencode/skills/ws-req-contract-sync/SKILL.md +1 -1
- package/templates/workspace/.opencode/skills/ws-req-contract-validate/SKILL.md +1 -1
- package/templates/workspace/.opencode/skills/ws-req-flow-sync/SKILL.md +1 -1
- package/templates/workspace/.opencode/skills/ws-req-review/SKILL.md +1 -1
- package/templates/workspace/.opencode/skills/ws-review/SKILL.md +35 -4
- package/templates/workspace/.opencode/skills/ws-rule/SKILL.md +1 -1
- package/templates/workspace/.opencode/skills/ws-spec-review/SKILL.md +1 -1
- package/templates/workspace/.opencode/skills/ws-submodule-setup/SKILL.md +10 -57
- package/templates/workspace/.opencode/skills/ws-verify-before-complete/SKILL.md +45 -55
- package/templates/workspace/AGENTS.md +32 -84
- package/templates/workspace/AI_PROJECT.md +5 -5
- package/templates/workspace/AI_WORKSPACE.md +1 -1
- package/templates/workspace/REQUIREMENTS.md +1 -1
- package/templates/workspace/gitignore +1 -1
- package/templates/workspace/manifest.json +215 -395
- /package/templates/workspace/{changes → .aiws/changes}/templates/design.md +0 -0
- /package/templates/workspace/{memory-bank → .aiws/memory-bank}/README.md +0 -0
- /package/templates/workspace/{memory-bank → .aiws/memory-bank}/architecture.md +0 -0
- /package/templates/workspace/{memory-bank → .aiws/memory-bank}/implementation-plan.md +0 -0
- /package/templates/workspace/{memory-bank → .aiws/memory-bank}/progress.md +0 -0
- /package/templates/workspace/{memory-bank → .aiws/memory-bank}/tech-stack.md +0 -0
- /package/templates/workspace/{requirements → .aiws/requirements}/CHANGELOG.md +0 -0
- /package/templates/workspace/{secrets → .aiws/secrets}/test-accounts.example.json +0 -0
|
@@ -9,16 +9,16 @@
|
|
|
9
9
|
- [ ] 0.1 阅读并遵守 `AI_PROJECT.md` / `AI_WORKSPACE.md` / `REQUIREMENTS.md`
|
|
10
10
|
- [ ] 0.2 运行门禁校验:`aiws validate .`(或 `npx -y @aipper/aiws validate .`)
|
|
11
11
|
- [ ] 0.3 若真值文件发生变化(例如你更新了 REQUIREMENTS.md),同步基线:`aiws change sync {{CHANGE_ID}}`
|
|
12
|
-
- [ ] 0.4 在
|
|
13
|
-
- [ ] 0.5 生成
|
|
12
|
+
- [ ] 0.4 在 `.aiws/changes/{{CHANGE_ID}}/proposal.md` 填写主索引绑定:`Change_ID` / (`Req_ID` or `Problem_ID`) / `Contract_Row` / `Plan_File` / `Evidence_Path`
|
|
13
|
+
- [ ] 0.5 生成 `.aiws/plan/...` 后,确认计划文件中的绑定字段与 proposal 一致
|
|
14
14
|
- [ ] 0.6 执行计划质检:在 AI 工具运行 `$ws-plan-verify`(或按同等清单手工检查“章节/步骤粒度/验证命令与预期”)
|
|
15
15
|
- [ ] 0.7 严格校验:`aiws change validate {{CHANGE_ID}} --strict`
|
|
16
16
|
|
|
17
17
|
## 1. 需求/问题合同(如适用)
|
|
18
18
|
|
|
19
19
|
- [ ] 1.1 需求交付:补齐/更新 `REQUIREMENTS.md` 验收条款(或确认不需要)
|
|
20
|
-
- [ ] 1.2 同步
|
|
21
|
-
- [ ] 1.3 记录到
|
|
20
|
+
- [ ] 1.2 同步 `.aiws/requirements/requirements-issues.csv`(或更新 `.aiws/issues/problem-issues.csv`)
|
|
21
|
+
- [ ] 1.3 记录到 `.aiws/requirements/CHANGELOG.md`(如需求发生变化)
|
|
22
22
|
|
|
23
23
|
## 2. 实现
|
|
24
24
|
|
|
@@ -27,9 +27,9 @@
|
|
|
27
27
|
|
|
28
28
|
## 2A. 协同(可选)
|
|
29
29
|
|
|
30
|
-
- [ ] 2A.1 若使用委托分析:把结果落盘到
|
|
31
|
-
- [ ] 2A.2 若使用 patch 草案:把结果落盘到
|
|
32
|
-
- [ ] 2A.3 若存在多审查者:把审查结果落盘到
|
|
30
|
+
- [ ] 2A.1 若使用委托分析:把结果落盘到 `.aiws/changes/{{CHANGE_ID}}/analysis/`
|
|
31
|
+
- [ ] 2A.2 若使用 patch 草案:把结果落盘到 `.aiws/changes/{{CHANGE_ID}}/patches/`,并记录是否采用
|
|
32
|
+
- [ ] 2A.3 若存在多审查者:把审查结果落盘到 `.aiws/changes/{{CHANGE_ID}}/review/`
|
|
33
33
|
|
|
34
34
|
## 3. 验证(必须可复现)
|
|
35
35
|
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
|
|
39
39
|
## 4. 交付与归档
|
|
40
40
|
|
|
41
|
-
- [ ] 4.1 证据落盘到 `.
|
|
41
|
+
- [ ] 4.1 证据落盘到 `.aiws/tmp/...`(报告/日志/请求响应等)
|
|
42
42
|
- [ ] 4.2 生成持久证据:`aiws change evidence {{CHANGE_ID}}`
|
|
43
43
|
- [ ] 4.3 交叉审计(可选但推荐):在 AI 工具内运行 `/ws-review`(或按 `AI_PROJECT.md` 手工审计)
|
|
44
44
|
- [ ] 4.4 收尾:`aiws change finish {{CHANGE_ID}} --push`(成功后自动归档并生成 handoff)
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
Problem_ID,Type,Severity,Title,Symptom,Repro,Root_Cause,Fix_Plan,Files,Tests,Evidence,Status,Owner,Created_At,Updated_At,Notes
|
|
2
|
-
PROB-000,BUG,P2,示例:/api/system/menu/routes 返回 500,"访问 routes 接口报 500;日志提示参数解析错误","curl -sS -H 'Authorization: Bearer <token>' 'http://localhost:8080/api/system/menu/routes?x=1'","Controller 入参类型与前端实际 query 不一致","对齐入参定义并补充单测;复测 runner 端点状态更新为 DONE","server/src/...","uv run tools/server_test_runner.py --workspace . --service backend --max-endpoints 10",".
|
|
2
|
+
PROB-000,BUG,P2,示例:/api/system/menu/routes 返回 500,"访问 routes 接口报 500;日志提示参数解析错误","curl -sS -H 'Authorization: Bearer <token>' 'http://localhost:8080/api/system/menu/routes?x=1'","Controller 入参类型与前端实际 query 不一致","对齐入参定义并补充单测;复测 runner 端点状态更新为 DONE","server/src/...","uv run .aiws/tools/server_test_runner.py --workspace . --service backend --max-endpoints 10",".aiws/tmp/server-test/report.json",TODO,ab,YYYY-MM-DDTHH:MM:SSZ,YYYY-MM-DDTHH:MM:SSZ,"若该问题阻塞某个需求交付,在 .aiws/requirements/requirements-issues.csv 的 Notes 里关联 Problem_ID"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
Req_ID,Title,Change_Type,Module,CRUD,Actor,Scenario,Preconditions,Inputs,Outputs,Data_Model,Business_Logic,API_Impact,NonFunctional,Spec_Status,Impl_Status,Tests,Evidence,Owner,Created_At,Updated_At,Notes
|
|
2
|
-
REQ-000,示例:用户列表分页支持按部门筛选,ADD,system/user,R,admin,管理员在用户管理页按部门筛选列表,已登录且有 system:user:list 权限,"deptId=10&page=1&pageSize=20",返回包含 deptId=10 的用户列表,"user,dept 表(字段:dept_id)","查询条件增加 deptId;默认不传不筛选;分页参数边界:page>=1&pageSize<=100",GET /api/system/user/list 新增 query 参数 deptId(可选),响应字段不变,性能:分页查询需走索引;安全:权限不变,READY,TODO,"uv run tools/server_test_runner.py --workspace . --service demo-spring --max-endpoints 10",.
|
|
2
|
+
REQ-000,示例:用户列表分页支持按部门筛选,ADD,system/user,R,admin,管理员在用户管理页按部门筛选列表,已登录且有 system:user:list 权限,"deptId=10&page=1&pageSize=20",返回包含 deptId=10 的用户列表,"user,dept 表(字段:dept_id)","查询条件增加 deptId;默认不传不筛选;分页参数边界:page>=1&pageSize<=100",GET /api/system/user/list 新增 query 参数 deptId(可选),响应字段不变,性能:分页查询需走索引;安全:权限不变,READY,TODO,"uv run .aiws/tools/server_test_runner.py --workspace . --service demo-spring --max-endpoints 10",.aiws/tmp/server-test/report.json,ab,YYYY-MM-DDTHH:MM:SSZ,YYYY-MM-DDTHH:MM:SSZ,
|
|
File without changes
|
|
@@ -198,9 +198,9 @@ def validate_rows(rows: List[Dict[str, str]]) -> List[ValidateProblem]:
|
|
|
198
198
|
|
|
199
199
|
|
|
200
200
|
def main(argv: List[str]) -> int:
|
|
201
|
-
p = argparse.ArgumentParser(description="Manage requirements execution contract CSV (requirements/requirements-issues.csv).")
|
|
201
|
+
p = argparse.ArgumentParser(description="Manage requirements execution contract CSV (.aiws/requirements/requirements-issues.csv).")
|
|
202
202
|
p.add_argument("--workspace", default=".", help="workspace root")
|
|
203
|
-
p.add_argument("--csv", default="requirements/requirements-issues.csv", help="contract CSV path (relative to workspace)")
|
|
203
|
+
p.add_argument("--csv", default=".aiws/requirements/requirements-issues.csv", help="contract CSV path (relative to workspace)")
|
|
204
204
|
sub = p.add_subparsers(dest="cmd", required=True)
|
|
205
205
|
|
|
206
206
|
sub.add_parser("init", help="create CSV with header if missing")
|
|
@@ -243,7 +243,7 @@ def main(argv: List[str]) -> int:
|
|
|
243
243
|
p = argparse.ArgumentParser(description="Backfill requirements contract CSV from REQUIREMENTS.md FlowSpec.")
|
|
244
244
|
p.add_argument("--workspace", default=".", help="workspace root")
|
|
245
245
|
p.add_argument("--requirements", default="REQUIREMENTS.md", help="requirements markdown path (relative to workspace)")
|
|
246
|
-
p.add_argument("--out-csv", default="requirements/requirements-issues.csv", help="output contract CSV path")
|
|
246
|
+
p.add_argument("--out-csv", default=".aiws/requirements/requirements-issues.csv", help="output contract CSV path")
|
|
247
247
|
p.add_argument("--owner", default="", help="default Owner for new rows")
|
|
248
248
|
p.add_argument(
|
|
249
249
|
"--default-tests",
|
|
@@ -281,7 +281,7 @@ def main(argv: List[str]) -> int:
|
|
|
281
281
|
print(f"- flows: {stats['total_flows']}")
|
|
282
282
|
print(f"- created: {stats['created']}")
|
|
283
283
|
print(f"- updated: {stats['updated']}")
|
|
284
|
-
print("next: fill missing fields, then run: python3 tools/requirements_contract.py validate")
|
|
284
|
+
print("next: fill missing fields, then run: python3 .aiws/tools/requirements_contract.py validate")
|
|
285
285
|
return 0
|
|
286
286
|
|
|
287
287
|
|
|
@@ -222,7 +222,7 @@ def main(argv: List[str]) -> int:
|
|
|
222
222
|
p.add_argument("--workspace", default=".", help="workspace root")
|
|
223
223
|
p.add_argument("--requirements", default="REQUIREMENTS.md", help="requirements markdown path (relative to workspace)")
|
|
224
224
|
p.add_argument("--out-mermaid", default="docs/api-flow.mmd", help="output mermaid file (relative to workspace)")
|
|
225
|
-
p.add_argument("--out-csv", default="issues/server-scenario-issues.csv", help="output scenario CSV (relative to workspace)")
|
|
225
|
+
p.add_argument("--out-csv", default=".aiws/issues/server-scenario-issues.csv", help="output scenario CSV (relative to workspace)")
|
|
226
226
|
args = p.parse_args(argv)
|
|
227
227
|
|
|
228
228
|
root = Path(args.workspace).resolve()
|
|
@@ -114,7 +114,7 @@ class WorkspaceConfig:
|
|
|
114
114
|
health_path: str = "/health"
|
|
115
115
|
openapi_path: str = "docs/openapi.json"
|
|
116
116
|
openapi_url: str = "/openapi.json"
|
|
117
|
-
log_path: str = ".
|
|
117
|
+
log_path: str = ".aiws/tmp/server-test/app.log"
|
|
118
118
|
request_id_header: str = "X-Request-Id"
|
|
119
119
|
server_dirs: Tuple[str, ...] = ()
|
|
120
120
|
build_cmd: str = ""
|
|
@@ -359,7 +359,7 @@ def parse_ai_workspace_md(path: Path) -> WorkspaceConfig:
|
|
|
359
359
|
health_path = find_scalar("health_path") or "/health"
|
|
360
360
|
openapi_path = find_scalar("openapi_path") or "docs/openapi.json"
|
|
361
361
|
openapi_url = find_scalar("openapi_url") or "/openapi.json"
|
|
362
|
-
log_path = find_scalar("log_path") or ".
|
|
362
|
+
log_path = find_scalar("log_path") or ".aiws/tmp/server-test/app.log"
|
|
363
363
|
request_id_header = find_scalar("request_id_header") or "X-Request-Id"
|
|
364
364
|
|
|
365
365
|
server_dirs = tuple(find_list("server_dirs"))
|
|
@@ -703,9 +703,9 @@ def render_cmd(template: str, *, service_dir: Path, service_name: str) -> str:
|
|
|
703
703
|
|
|
704
704
|
def resolve_log_file(log_path: str, *, workspace_root: Path, service_name: str) -> Path:
|
|
705
705
|
"""
|
|
706
|
-
Resolve log file path with optional {service} placeholder; defaults to per-service log under .
|
|
706
|
+
Resolve log file path with optional {service} placeholder; defaults to per-service log under .aiws/tmp/server-test.
|
|
707
707
|
"""
|
|
708
|
-
path_str = (log_path or "").strip() or ".
|
|
708
|
+
path_str = (log_path or "").strip() or ".aiws/tmp/server-test/{service}.log"
|
|
709
709
|
path_str = path_str.replace("{service}", service_name)
|
|
710
710
|
return (workspace_root / path_str).resolve()
|
|
711
711
|
|
|
@@ -1278,7 +1278,7 @@ def write_fix_issues_csv(
|
|
|
1278
1278
|
)
|
|
1279
1279
|
|
|
1280
1280
|
write_fix_csv_rows(fix_csv_path, out_rows)
|
|
1281
|
-
triage_csv_path = (workspace_root / "issues" / "server-triage-issues.csv").resolve()
|
|
1281
|
+
triage_csv_path = (workspace_root / ".aiws" / "issues" / "server-triage-issues.csv").resolve()
|
|
1282
1282
|
write_triage_issues_csv(triage_csv_path=triage_csv_path, fix_rows=out_rows, generated_at=generated_at)
|
|
1283
1283
|
|
|
1284
1284
|
|
|
@@ -1564,9 +1564,9 @@ def main(argv: List[str]) -> int:
|
|
|
1564
1564
|
parser = argparse.ArgumentParser(description="Workspace server API test runner (OpenAPI -> CSV -> requests -> log correlation)")
|
|
1565
1565
|
parser.add_argument("--workspace", default=".", help="workspace root (directory A)")
|
|
1566
1566
|
parser.add_argument("--service", default="", help="optional: only run one service directory name")
|
|
1567
|
-
parser.add_argument("--csv", default="issues/server-api-issues.csv", help="execution contract CSV path (relative to workspace)")
|
|
1568
|
-
parser.add_argument("--out-dir", default=".
|
|
1569
|
-
parser.add_argument("--base-url", default="", help="override base_url (otherwise use secrets/test-accounts.json or AI_WORKSPACE.md)")
|
|
1567
|
+
parser.add_argument("--csv", default=".aiws/issues/server-api-issues.csv", help="execution contract CSV path (relative to workspace)")
|
|
1568
|
+
parser.add_argument("--out-dir", default=".aiws/tmp/server-test", help="output dir for responses (relative to workspace)")
|
|
1569
|
+
parser.add_argument("--base-url", default="", help="override base_url (otherwise use .aiws/secrets/test-accounts.json or AI_WORKSPACE.md)")
|
|
1570
1570
|
parser.add_argument("--health-timeout", type=int, default=60, help="seconds to wait for health endpoint")
|
|
1571
1571
|
parser.add_argument("--no-wait-health", action="store_true", help="skip health check wait")
|
|
1572
1572
|
parser.add_argument("--refresh-openapi", action="store_true", help="always export OpenAPI from openapi_url into docs/openapi.json")
|
|
@@ -1582,7 +1582,7 @@ def main(argv: List[str]) -> int:
|
|
|
1582
1582
|
ai_workspace_md = workspace_root / "AI_WORKSPACE.md"
|
|
1583
1583
|
cfg = parse_ai_workspace_md(ai_workspace_md)
|
|
1584
1584
|
|
|
1585
|
-
secrets_path = workspace_root / "secrets" / "test-accounts.json"
|
|
1585
|
+
secrets_path = workspace_root / ".aiws" / "secrets" / "test-accounts.json"
|
|
1586
1586
|
secrets = load_secrets(secrets_path)
|
|
1587
1587
|
|
|
1588
1588
|
csv_path = (workspace_root / args.csv).resolve()
|
|
@@ -1850,7 +1850,7 @@ def main(argv: List[str]) -> int:
|
|
|
1850
1850
|
if openapi_used:
|
|
1851
1851
|
report["openapi_used"] = openapi_used
|
|
1852
1852
|
write_text_if_changed(report_path, json.dumps(report, ensure_ascii=False, indent=2) + "\n")
|
|
1853
|
-
fix_csv_path = (workspace_root / "issues" / "server-fix-issues.csv").resolve()
|
|
1853
|
+
fix_csv_path = (workspace_root / ".aiws" / "issues" / "server-fix-issues.csv").resolve()
|
|
1854
1854
|
write_fix_issues_csv(
|
|
1855
1855
|
fix_csv_path=fix_csv_path,
|
|
1856
1856
|
endpoint_rows=updated_rows,
|
|
@@ -213,7 +213,7 @@ def parse_archived_change_dir_name(entry_name: str) -> Optional[Tuple[str, str,
|
|
|
213
213
|
|
|
214
214
|
|
|
215
215
|
def find_archived_change(root: Path, change_id: str) -> Optional[Path]:
|
|
216
|
-
archive_root = root / "changes" / "archive"
|
|
216
|
+
archive_root = root / ".aiws" / "changes" / "archive"
|
|
217
217
|
if not archive_root.exists():
|
|
218
218
|
return None
|
|
219
219
|
matches: List[Path] = []
|
|
@@ -283,7 +283,7 @@ def summarize_finish_state(change_dir: Path) -> str:
|
|
|
283
283
|
|
|
284
284
|
|
|
285
285
|
def terminated_change_message(root: Path, change_id: str) -> Optional[str]:
|
|
286
|
-
change_dir = root / "changes" / change_id
|
|
286
|
+
change_dir = root / ".aiws" / "changes" / change_id
|
|
287
287
|
if change_dir.exists() and summarize_finish_state(change_dir) == "done":
|
|
288
288
|
return (
|
|
289
289
|
f"change/{change_id} already reached finish, but archive/push closeout is still pending; "
|
|
@@ -335,7 +335,7 @@ def split_declared_values(value: str) -> List[str]:
|
|
|
335
335
|
|
|
336
336
|
def normalize_contract_ref(raw: str) -> Tuple[str, str]:
|
|
337
337
|
token = raw.strip().strip("`").strip()
|
|
338
|
-
token = re.sub(r"^(requirements/requirements-issues\.csv
|
|
338
|
+
token = re.sub(r"^(\.aiws/requirements/requirements-issues\.csv|\.aiws/issues/problem-issues\.csv)\s*[:#]\s*", "", token)
|
|
339
339
|
m = re.match(r"(?i)^(req[_-]?id|problem[_-]?id)\s*=\s*(.+)$", token)
|
|
340
340
|
if m:
|
|
341
341
|
key = m.group(1).lower()
|
|
@@ -560,7 +560,7 @@ def validate_change(
|
|
|
560
560
|
check_evidence: bool,
|
|
561
561
|
check_scope: bool,
|
|
562
562
|
) -> int:
|
|
563
|
-
change_dir = root / "changes" / change_id
|
|
563
|
+
change_dir = root / ".aiws" / "changes" / change_id
|
|
564
564
|
required_files = ["proposal.md", "tasks.md"]
|
|
565
565
|
|
|
566
566
|
errors: List[str] = []
|
|
@@ -572,7 +572,7 @@ def validate_change(
|
|
|
572
572
|
if subs:
|
|
573
573
|
targets_path = change_dir / "submodules.targets"
|
|
574
574
|
if not targets_path.exists() or targets_path.stat().st_size == 0:
|
|
575
|
-
msg = "missing/empty: changes/<change-id>/submodules.targets (required when .gitmodules declares submodules)"
|
|
575
|
+
msg = "missing/empty: .aiws/changes/<change-id>/submodules.targets (required when .gitmodules declares submodules)"
|
|
576
576
|
hint = f"hint: create it under {change_dir} (format: <sub_path> <target_branch> [remote])"
|
|
577
577
|
if strict:
|
|
578
578
|
errors.append(msg)
|
|
@@ -685,7 +685,7 @@ def validate_change(
|
|
|
685
685
|
f"{sub_path}: gitlink commit {gitlink_sha} is not contained in declared target branch {remote}/{target_branch}"
|
|
686
686
|
)
|
|
687
687
|
errors.append(
|
|
688
|
-
f"hint: fix `.gitmodules` /
|
|
688
|
+
f"hint: fix `.gitmodules` / `.aiws/changes/{change_id}/submodules.targets`, or move the submodule gitlink onto a commit reachable from {remote}/{target_branch}"
|
|
689
689
|
)
|
|
690
690
|
|
|
691
691
|
def placeholder_scan(rel: str, text: str) -> None:
|
|
@@ -696,7 +696,7 @@ def validate_change(
|
|
|
696
696
|
|
|
697
697
|
def evidence_is_persistent(p: str) -> bool:
|
|
698
698
|
p2 = p.replace("\\", "/")
|
|
699
|
-
return p2.startswith(f"changes/{change_id}/evidence/") or p2.startswith(f"changes/{change_id}/review/")
|
|
699
|
+
return p2.startswith(f".aiws/changes/{change_id}/evidence/") or p2.startswith(f".aiws/changes/{change_id}/review/")
|
|
700
700
|
|
|
701
701
|
def parse_scope_patterns_from_plan(plan_text: str) -> List[str]:
|
|
702
702
|
# Extract allow-list bullet items from a plan's scope section.
|
|
@@ -777,7 +777,7 @@ def validate_change(
|
|
|
777
777
|
has_persistent = any(evidence_is_persistent(p) for p in paths)
|
|
778
778
|
if not has_persistent:
|
|
779
779
|
(errors if strict else warnings).append(
|
|
780
|
-
f"{rel} Evidence_Path should include at least one persistent path under changes/<id>/evidence or changes/<id>/review"
|
|
780
|
+
f"{rel} Evidence_Path should include at least one persistent path under .aiws/changes/<id>/evidence or .aiws/changes/<id>/review"
|
|
781
781
|
)
|
|
782
782
|
for raw in paths:
|
|
783
783
|
p = raw.strip()
|
|
@@ -819,7 +819,7 @@ def validate_change(
|
|
|
819
819
|
base_branch = ""
|
|
820
820
|
if not base_branch:
|
|
821
821
|
(errors if strict else warnings).append(
|
|
822
|
-
"scope check requires base_branch recorded in changes/<id>/.ws-change.json (run `aiws change start <id>` to record it)"
|
|
822
|
+
"scope check requires base_branch recorded in .aiws/changes/<id>/.ws-change.json (run `aiws change start <id>` to record it)"
|
|
823
823
|
)
|
|
824
824
|
return
|
|
825
825
|
|
|
@@ -879,11 +879,11 @@ def validate_change(
|
|
|
879
879
|
|
|
880
880
|
# Always allow core workflow artifacts.
|
|
881
881
|
always_allow = [
|
|
882
|
-
f"changes/{change_id}/**",
|
|
883
|
-
"plan/**",
|
|
882
|
+
f".aiws/changes/{change_id}/**",
|
|
883
|
+
".aiws/plan/**",
|
|
884
884
|
"REQUIREMENTS.md",
|
|
885
|
-
"requirements/requirements-issues.csv",
|
|
886
|
-
"issues/problem-issues.csv",
|
|
885
|
+
".aiws/requirements/requirements-issues.csv",
|
|
886
|
+
".aiws/issues/problem-issues.csv",
|
|
887
887
|
]
|
|
888
888
|
all_patterns = patterns + always_allow
|
|
889
889
|
|
|
@@ -898,8 +898,8 @@ def validate_change(
|
|
|
898
898
|
contract_row_decl = ""
|
|
899
899
|
plan_file_decl = ""
|
|
900
900
|
evidence_path_decl = ""
|
|
901
|
-
req_csv = root / "requirements" / "requirements-issues.csv"
|
|
902
|
-
prob_csv = root / "issues" / "problem-issues.csv"
|
|
901
|
+
req_csv = root / ".aiws" / "requirements" / "requirements-issues.csv"
|
|
902
|
+
prob_csv = root / ".aiws" / "issues" / "problem-issues.csv"
|
|
903
903
|
|
|
904
904
|
if proposal_path:
|
|
905
905
|
t = read_text(proposal_path)
|
|
@@ -936,18 +936,18 @@ def validate_change(
|
|
|
936
936
|
try:
|
|
937
937
|
ok = csv_has_id(req_csv, "Req_ID", req_id)
|
|
938
938
|
except Exception as e:
|
|
939
|
-
warnings.append(f"failed to read requirements/requirements-issues.csv: {e}")
|
|
939
|
+
warnings.append(f"failed to read .aiws/requirements/requirements-issues.csv: {e}")
|
|
940
940
|
if not ok:
|
|
941
|
-
(errors if strict else warnings).append(f"Req_ID not found in requirements/requirements-issues.csv: {req_id}")
|
|
941
|
+
(errors if strict else warnings).append(f"Req_ID not found in .aiws/requirements/requirements-issues.csv: {req_id}")
|
|
942
942
|
|
|
943
943
|
if prob_id and prob_csv.exists():
|
|
944
944
|
ok = False
|
|
945
945
|
try:
|
|
946
946
|
ok = csv_has_id(prob_csv, "Problem_ID", prob_id)
|
|
947
947
|
except Exception as e:
|
|
948
|
-
warnings.append(f"failed to read issues/problem-issues.csv: {e}")
|
|
948
|
+
warnings.append(f"failed to read .aiws/issues/problem-issues.csv: {e}")
|
|
949
949
|
if not ok:
|
|
950
|
-
(errors if strict else warnings).append(f"Problem_ID not found in issues/problem-issues.csv: {prob_id}")
|
|
950
|
+
(errors if strict else warnings).append(f"Problem_ID not found in .aiws/issues/problem-issues.csv: {prob_id}")
|
|
951
951
|
|
|
952
952
|
normalized_contract_refs: List[Tuple[str, str]] = []
|
|
953
953
|
for raw_ref in split_declared_values(contract_row_decl):
|
|
@@ -969,7 +969,7 @@ def validate_change(
|
|
|
969
969
|
except Exception as e:
|
|
970
970
|
warnings.append(f"failed to resolve Contract_Row reference {raw_ref}: {e}")
|
|
971
971
|
if not found:
|
|
972
|
-
(errors if strict else warnings).append(f"Contract_Row not found in requirements/problem CSVs: {raw_ref}")
|
|
972
|
+
(errors if strict else warnings).append(f"Contract_Row not found in .aiws/requirements/problem CSVs: {raw_ref}")
|
|
973
973
|
continue
|
|
974
974
|
normalized_contract_refs.append((resolved_kind, value))
|
|
975
975
|
|
|
@@ -995,8 +995,8 @@ def validate_change(
|
|
|
995
995
|
plan_rel = plan_rel[2:]
|
|
996
996
|
if os.path.isabs(plan_rel):
|
|
997
997
|
(errors if strict else warnings).append(f"Plan_File must be workspace-relative, got absolute path: {plan_rel}")
|
|
998
|
-
if not plan_rel.startswith("plan/"):
|
|
999
|
-
(errors if strict else warnings).append(f"Plan_File should be under plan/: {plan_rel}")
|
|
998
|
+
if not plan_rel.startswith(".aiws/plan/"):
|
|
999
|
+
(errors if strict else warnings).append(f"Plan_File should be under .aiws/plan/: {plan_rel}")
|
|
1000
1000
|
|
|
1001
1001
|
plan_abs = (root / plan_rel).resolve()
|
|
1002
1002
|
try:
|
|
@@ -1178,7 +1178,7 @@ def main(argv: Optional[List[str]] = None) -> int:
|
|
|
1178
1178
|
eprint(f"error: change-id does not match branch (branch={branch_arg}, change_id={change_id})")
|
|
1179
1179
|
return 2
|
|
1180
1180
|
|
|
1181
|
-
change_dir = root / "changes" / change_id
|
|
1181
|
+
change_dir = root / ".aiws" / "changes" / change_id
|
|
1182
1182
|
terminated = terminated_change_message(root, change_id) if (inferred_from_branch or bool(branch_arg)) else None
|
|
1183
1183
|
if terminated:
|
|
1184
1184
|
eprint(f"error: {terminated}")
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
# ws_resolve_sub_target.sh — Resolve the target branch & remote for a submodule path.
|
|
3
3
|
#
|
|
4
4
|
# Usage:
|
|
5
|
-
# source tools/ws_resolve_sub_target.sh
|
|
5
|
+
# source .aiws/tools/ws_resolve_sub_target.sh
|
|
6
6
|
# ws_resolve_sub_target <sub_path> <sub_name> <targets_file> <base_branch>
|
|
7
7
|
#
|
|
8
8
|
# Outputs two variables: _resolved_branch and _resolved_remote.
|
|
@@ -13,7 +13,7 @@ ws_resolve_sub_target() {
|
|
|
13
13
|
_resolved_branch=""
|
|
14
14
|
_resolved_remote=""
|
|
15
15
|
|
|
16
|
-
# Priority 1: changes/<id>/submodules.targets (per-change override)
|
|
16
|
+
# Priority 1: .aiws/changes/<id>/submodules.targets (per-change override)
|
|
17
17
|
local override=""
|
|
18
18
|
if [[ -f "${targets}" ]]; then
|
|
19
19
|
override="$(awk -v p="${sub_path}" '
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
"""
|
|
3
|
-
Generate an update_plan JSON payload from changes/<id>/tasks.md checkbox tasks.
|
|
3
|
+
Generate an update_plan JSON payload from .aiws/changes/<id>/tasks.md checkbox tasks.
|
|
4
4
|
|
|
5
5
|
This script is intended to be used by the `p-tasks-plan` Codex skill.
|
|
6
6
|
It reads tasks.md only and does not modify it.
|
|
@@ -116,11 +116,11 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
116
116
|
sub = p.add_subparsers(dest="cmd", required=True)
|
|
117
117
|
|
|
118
118
|
p_status = sub.add_parser("status", help="Print progress summary from tasks.md")
|
|
119
|
-
p_status.add_argument("--file", required=True, help="Path to changes/<id>/tasks.md")
|
|
119
|
+
p_status.add_argument("--file", required=True, help="Path to .aiws/changes/<id>/tasks.md")
|
|
120
120
|
p_status.set_defaults(fn=cmd_status)
|
|
121
121
|
|
|
122
122
|
p_plan = sub.add_parser("plan", help="Generate update_plan JSON payload from tasks.md")
|
|
123
|
-
p_plan.add_argument("--file", required=True, help="Path to changes/<id>/tasks.md")
|
|
123
|
+
p_plan.add_argument("--file", required=True, help="Path to .aiws/changes/<id>/tasks.md")
|
|
124
124
|
p_plan.add_argument("--explanation", default="", help="Explanation for update_plan payload")
|
|
125
125
|
p_plan.set_defaults(fn=cmd_plan)
|
|
126
126
|
|
|
@@ -14,7 +14,11 @@
|
|
|
14
14
|
3) 将审计落盘到(目录不存在则创建):
|
|
15
15
|
- 默认:`changes/<change-id>/review/codex-review.md`
|
|
16
16
|
- 回退:`.agentdocs/tmp/review/codex-review.md`(仅在无法确定 `change-id` 时使用)
|
|
17
|
-
4)
|
|
17
|
+
4) 若当前任务已进入“准备提交/交付/finish”的语境,继续补齐 dual review gate:
|
|
18
|
+
- 运行/收敛 `/ws-spec-review`,落盘 `changes/<change-id>/review/spec-review.md`
|
|
19
|
+
- 运行/收敛 `/ws-quality-review`,落盘 `changes/<change-id>/review/quality-review.md`
|
|
20
|
+
- 不要把单个 `codex-review.md` 误当成 finish gate 已完成
|
|
21
|
+
5) 回复中输出:
|
|
18
22
|
- `证据(Evidence):` 证据文件路径
|
|
19
23
|
- `主要风险(Top risks):` 3–8 条(高→低)
|
|
20
24
|
- `下一步(Next):` 最小修复清单 + 最小验证命令
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"hooks": {
|
|
3
|
+
"UserPromptSubmit": [
|
|
4
|
+
{
|
|
5
|
+
"matcher": "*",
|
|
6
|
+
"hooks": [
|
|
7
|
+
{
|
|
8
|
+
"type": "command",
|
|
9
|
+
"command": "mkdir -p .agentdocs/tmp/opencode-autonomy && printf '[user-prompt-submit] %s\\n' \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\" >> .agentdocs/tmp/opencode-autonomy/claude-hooks.log"
|
|
10
|
+
}
|
|
11
|
+
]
|
|
12
|
+
}
|
|
13
|
+
],
|
|
14
|
+
"Stop": [
|
|
15
|
+
{
|
|
16
|
+
"matcher": "*",
|
|
17
|
+
"hooks": [
|
|
18
|
+
{
|
|
19
|
+
"type": "command",
|
|
20
|
+
"command": "mkdir -p .agentdocs/tmp/opencode-autonomy && printf '[stop] %s\\n' \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\" >> .agentdocs/tmp/opencode-autonomy/claude-hooks.log"
|
|
21
|
+
}
|
|
22
|
+
]
|
|
23
|
+
}
|
|
24
|
+
]
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -1,130 +1,18 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: ws-commit
|
|
3
|
-
description:
|
|
3
|
+
description: `aiws commit` 的薄包装入口
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
# ws-commit
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
- 支持在**当前分支直接提交**(不要求必须先切 `change/<change-id>`)
|
|
10
|
-
- 提交前审计与证据落盘(`$ws-review`)
|
|
11
|
-
- 提交前门禁校验与证据落盘(`aiws validate . --stamp`)
|
|
12
|
-
- 最后执行 `git commit`(commit 前必须让用户确认 message;不使用 `--no-verify` 绕过 hooks)
|
|
13
|
-
- 若仓库含 submodule:提交前识别并提示正确顺序(先 submodule,再 superproject)
|
|
14
|
-
- 若你经常遇到 submodule detached:建议日常拉取使用 `$ws-pull`(尽量把 submodule “挂回分支”且不改变 gitlink commit)
|
|
8
|
+
`aiws commit` 的薄包装入口。
|
|
15
9
|
|
|
16
|
-
安全约束(强制):
|
|
17
|
-
- 不自动 `git add -A`(避免误提交);只在用户明确指示时才执行 staging 命令
|
|
18
|
-
- 不自动 push
|
|
19
|
-
- 不写入任何 secrets
|
|
20
|
-
- 检测到 submodule 有未提交改动时,不允许直接提交 superproject(先处理 submodule)
|
|
21
|
-
- commit message 优先使用中文(命令/路径/代码标识符保持原样不翻译);格式建议:`<类型>: <简述>`(例如 `修复: 登录页空指针`、`功能: 新增 submodule targets 校验`、`重构: 提取共享脚本`)
|
|
22
|
-
- 若启用了 `.githooks/commit-msg`:默认按仓库角色区分
|
|
23
|
-
- superproject / root 仓库(存在 `.gitmodules`)默认允许自由 commit message
|
|
24
|
-
- submodule / 普通仓库默认提示优先中文
|
|
25
|
-
- 只有在 `git config aiws.commitMessagePolicy strict` 时才会拒绝全英文首行(`Merge/Revert/fixup!/squash!` 例外)
|
|
26
|
-
|
|
27
|
-
阶段定位:
|
|
28
|
-
- commit gate;负责在提交前收敛 review、validate 和 message 确认,不允许跳过 hooks。
|
|
29
|
-
|
|
30
|
-
必需输入:
|
|
31
|
-
- 当前分支与 staged diff
|
|
32
|
-
- `$ws-review` 生成的审计证据
|
|
33
|
-
- `aiws validate . --stamp` 生成的校验证据
|
|
34
|
-
- 用户确认后的 commit message
|
|
35
|
-
|
|
36
|
-
必需输出:
|
|
37
|
-
- `证据(Evidence):` review 文件路径 + validate stamp 路径
|
|
38
|
-
- `上下文(Context):` 当前分支、submodule 检测结果、阻断原因(若有)
|
|
39
|
-
- `提交信息(Commit):` 最终提交信息
|
|
40
|
-
|
|
41
|
-
阻断条件:
|
|
42
|
-
- 没有 staged changes
|
|
43
|
-
- submodule 工作区不干净或 detached 处理不完整
|
|
44
|
-
- 用户未确认 commit message
|
|
45
|
-
- validate 失败
|
|
46
|
-
|
|
47
|
-
完成判定:
|
|
48
|
-
- 提交已完成,证据路径明确,且没有通过 `--no-verify` 绕过门禁。
|
|
49
|
-
|
|
50
|
-
执行步骤(建议):
|
|
51
|
-
1) 运行 `$ws-preflight`(确保真值文件就绪)。
|
|
52
|
-
2) 运行 `$ws-review`(优先生成审计证据:`changes/<change-id>/review/codex-review.md`;无 `change-id` 时回退 `.agentdocs/tmp/review/codex-review.md`)。
|
|
53
|
-
3) 运行门禁校验并写 stamp:
|
|
54
10
|
```bash
|
|
55
11
|
if [[ -x "./node_modules/.bin/aiws" ]]; then
|
|
56
|
-
./node_modules/.bin/aiws
|
|
12
|
+
./node_modules/.bin/aiws commit
|
|
57
13
|
elif command -v aiws >/dev/null 2>&1; then
|
|
58
|
-
aiws
|
|
14
|
+
aiws commit
|
|
59
15
|
else
|
|
60
|
-
npx @aipper/aiws
|
|
61
|
-
fi
|
|
62
|
-
```
|
|
63
|
-
4) 输出当前提交上下文(必须输出给用户确认):
|
|
64
|
-
```bash
|
|
65
|
-
git branch --show-current
|
|
66
|
-
git status --porcelain
|
|
67
|
-
```
|
|
68
|
-
5) 检测是否存在 submodule(有则进入 submodule 感知模式):
|
|
69
|
-
```bash
|
|
70
|
-
if [[ -f .gitmodules ]]; then
|
|
71
|
-
git config --file .gitmodules --get-regexp '^submodule\..*\.path$' || true
|
|
72
|
-
else
|
|
73
|
-
echo "[info] no .gitmodules"
|
|
74
|
-
fi
|
|
75
|
-
```
|
|
76
|
-
6) 若存在 submodule,逐个检查子仓库工作区是否干净:
|
|
77
|
-
```bash
|
|
78
|
-
while read -r _ sub_path; do
|
|
79
|
-
[[ -z "${sub_path:-}" ]] && continue
|
|
80
|
-
echo "== submodule: ${sub_path} =="
|
|
81
|
-
git -C "${sub_path}" rev-parse --abbrev-ref HEAD 2>/dev/null || true
|
|
82
|
-
git -C "${sub_path}" status --porcelain || true
|
|
83
|
-
done < <(git config --file .gitmodules --get-regexp '^submodule\..*\.path$' 2>/dev/null || true)
|
|
84
|
-
```
|
|
85
|
-
判定规则(强制):
|
|
86
|
-
- 任一 submodule `git status --porcelain` 非空:停止 superproject commit,先在对应 submodule 完成 commit,再回到 superproject 更新并提交 gitlink。
|
|
87
|
-
- 若该 submodule 当前为 detached HEAD:先按 `.gitmodules` 的目标分支挂到 `aiws/pin/<target_branch>`;不要直接切 `change/<change-id>` / `main` / `master` 来“解 detached”。
|
|
88
|
-
处理指引(detached submodule):
|
|
89
|
-
```bash
|
|
90
|
-
cur_branch="$(git branch --show-current)"
|
|
91
|
-
change_id="$(echo "${cur_branch}" | sed -n 's|^change/||p')"
|
|
92
|
-
targets="changes/${change_id}/submodules.targets"
|
|
93
|
-
|
|
94
|
-
source tools/ws_resolve_sub_target.sh
|
|
95
|
-
ws_resolve_sub_target "${sub_path}" "${sub_name}" "${targets}" "${cur_branch}" || exit 2
|
|
96
|
-
target_branch="${_resolved_branch}"
|
|
97
|
-
remote="${_resolved_remote}"
|
|
98
|
-
|
|
99
|
-
git -C "${sub_path}" fetch "${remote}" --prune
|
|
100
|
-
if ! git -C "${sub_path}" show-ref --verify --quiet "refs/remotes/${remote}/${target_branch}"; then
|
|
101
|
-
echo "error: missing ${remote}/${target_branch} for submodule path=${sub_path}"
|
|
102
|
-
exit 2
|
|
16
|
+
npx @aipper/aiws commit
|
|
103
17
|
fi
|
|
104
|
-
git -C "${sub_path}" checkout -B "aiws/pin/${target_branch}" HEAD
|
|
105
|
-
git -C "${sub_path}" branch --set-upstream-to "${remote}/${target_branch}" "aiws/pin/${target_branch}" >/dev/null 2>&1 || true
|
|
106
18
|
```
|
|
107
|
-
7) 检查当前 staging 内容(必须输出给用户确认):
|
|
108
|
-
```bash
|
|
109
|
-
git status --porcelain
|
|
110
|
-
git diff --staged --submodule=short
|
|
111
|
-
```
|
|
112
|
-
8) 若没有 staged changes:停止并提示用户先明确要提交哪些文件(例如 `git add -p` 或 `git add <path>`)。
|
|
113
|
-
9) 生成中文 commit message 草案(格式:`<类型>: <简述>`),输出给用户确认后再执行。
|
|
114
|
-
- 类型参考:`功能` / `修复` / `重构` / `文档` / `测试` / `构建` / `杂项`
|
|
115
|
-
- 简述用一句话概括本次改动的"为什么"而非"改了什么"
|
|
116
|
-
- 命令/路径/代码标识符保持原样不翻译
|
|
117
|
-
- 若用户给出全英文 message:优先改写成中文;若用户明确要求保留英文,也可以提交(但 strict 模式下会被 hook 拒绝)
|
|
118
|
-
10) 执行提交(不带 `--no-verify`):
|
|
119
|
-
```bash
|
|
120
|
-
git commit -m "<message>"
|
|
121
|
-
```
|
|
122
|
-
11) 输出提交结果(可选):
|
|
123
|
-
```bash
|
|
124
|
-
git show --stat --oneline -1
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
输出要求:
|
|
128
|
-
- `证据(Evidence):` `changes/<change-id>/review/codex-review.md`(无 `change-id` 时回退 `.agentdocs/tmp/review/codex-review.md`) + `.agentdocs/tmp/aiws-validate/*.json`
|
|
129
|
-
- `上下文(Context):` 当前分支 + 是否检测到 submodule + 若阻断则给出阻断原因
|
|
130
|
-
- `提交信息(Commit):` 最终使用的 commit message(仅当用户确认后)
|