@aipper/aiws-spec 0.0.29 → 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.
Files changed (96) hide show
  1. package/docs/workflow-delegation-contracts.json +40 -1
  2. package/docs/workflow-delegation-contracts.md +10 -0
  3. package/docs/workflow-delegation-contracts.schema.json +57 -0
  4. package/docs/workflow-governance-rules.md +1 -1
  5. package/docs/workflow-review-gates.json +6 -0
  6. package/docs/workflow-review-gates.md +14 -5
  7. package/docs/workflow-review-gates.schema.json +4 -0
  8. package/docs/workflow-router-rules.json +46 -14
  9. package/docs/workflow-router-rules.md +19 -16
  10. package/docs/workflow-router-rules.schema.json +4 -0
  11. package/docs/workflow-stage-contracts.json +22 -2
  12. package/docs/workflow-stage-contracts.md +15 -13
  13. package/docs/workflow-stage-contracts.schema.json +12 -1
  14. package/package.json +1 -1
  15. package/templates/workspace/.agents/skills/using-aiws/SKILL.md +10 -6
  16. package/templates/workspace/.agents/skills/ws-commit/SKILL.md +5 -5
  17. package/templates/workspace/.agents/skills/ws-deliver/SKILL.md +5 -5
  18. package/templates/workspace/.agents/skills/ws-dev/SKILL.md +30 -0
  19. package/templates/workspace/.agents/skills/ws-finish/SKILL.md +9 -5
  20. package/templates/workspace/.agents/skills/ws-handoff/SKILL.md +5 -5
  21. package/templates/workspace/.agents/skills/ws-migrate/SKILL.md +18 -0
  22. package/templates/workspace/.agents/skills/ws-plan-verify/SKILL.md +5 -5
  23. package/templates/workspace/.agents/skills/ws-pull/SKILL.md +18 -0
  24. package/templates/workspace/.agents/skills/ws-push/SKILL.md +18 -0
  25. package/templates/workspace/.agents/skills/ws-submodule-setup/SKILL.md +18 -0
  26. package/templates/workspace/.agents/skills/ws-verify-before-complete/SKILL.md +5 -5
  27. package/templates/workspace/{changes → .aiws/changes}/README.md +13 -13
  28. package/templates/workspace/{changes → .aiws/changes}/templates/proposal.md +12 -12
  29. package/templates/workspace/{changes → .aiws/changes}/templates/tasks.md +8 -8
  30. package/templates/workspace/{issues → .aiws/issues}/problem-issues.csv +1 -1
  31. package/templates/workspace/.aiws/journal/.gitkeep +1 -0
  32. package/templates/workspace/{requirements → .aiws/requirements}/requirements-issues.csv +1 -1
  33. package/templates/workspace/.aiws/tmp/.gitkeep +0 -0
  34. package/templates/workspace/{tools → .aiws/tools}/requirements_contract.py +2 -2
  35. package/templates/workspace/{tools → .aiws/tools}/requirements_contract_sync.py +2 -2
  36. package/templates/workspace/{tools → .aiws/tools}/requirements_flow_gen.py +1 -1
  37. package/templates/workspace/{tools → .aiws/tools}/server_test_runner.py +10 -10
  38. package/templates/workspace/{tools → .aiws/tools}/ws_change_check.py +23 -23
  39. package/templates/workspace/{tools → .aiws/tools}/ws_resolve_sub_target.sh +2 -2
  40. package/templates/workspace/{tools → .aiws/tools}/ws_tasks_plan.py +3 -3
  41. package/templates/workspace/.claude/skills/ws-commit/SKILL.md +5 -5
  42. package/templates/workspace/.claude/skills/ws-deliver/SKILL.md +5 -5
  43. package/templates/workspace/.claude/skills/ws-finish/SKILL.md +9 -5
  44. package/templates/workspace/.claude/skills/ws-handoff/SKILL.md +5 -5
  45. package/templates/workspace/.claude/skills/ws-migrate/SKILL.md +18 -0
  46. package/templates/workspace/.claude/skills/ws-plan-verify/SKILL.md +5 -5
  47. package/templates/workspace/.claude/skills/ws-pull/SKILL.md +18 -0
  48. package/templates/workspace/.claude/skills/ws-push/SKILL.md +18 -0
  49. package/templates/workspace/.claude/skills/ws-submodule-setup/SKILL.md +18 -0
  50. package/templates/workspace/.claude/skills/ws-verify-before-complete/SKILL.md +5 -5
  51. package/templates/workspace/.githooks/pre-commit +13 -0
  52. package/templates/workspace/.githooks/pre-push +7 -0
  53. package/templates/workspace/.opencode/commands/ws-finish.md +66 -1
  54. package/templates/workspace/.opencode/helpers/approval-whitelist-check.sh +1 -1
  55. package/templates/workspace/.opencode/helpers/approval-whitelist-run.sh +1 -1
  56. package/templates/workspace/.opencode/helpers/approval-whitelist-watchdog.sh +1 -1
  57. package/templates/workspace/.opencode/helpers/tmux-swarm-rescue.sh +1 -1
  58. package/templates/workspace/.opencode/helpers/tmux-swarm-scan.sh +1 -1
  59. package/templates/workspace/.opencode/lib/aiws-context.js +953 -0
  60. package/templates/workspace/.opencode/plugins/aiws-inject-context.js +201 -0
  61. package/templates/workspace/.opencode/plugins/aiws-session-start.js +86 -0
  62. package/templates/workspace/.opencode/plugins/aiws-workflow-state.js +116 -0
  63. package/templates/workspace/.opencode/scripts/ws-plan-gen-submodule-targets.sh +27 -0
  64. package/templates/workspace/.opencode/scripts/ws-plan-setup-worktree.sh +31 -0
  65. package/templates/workspace/.opencode/skills/using-aiws/SKILL.md +39 -2
  66. package/templates/workspace/.opencode/skills/ws-commit/SKILL.md +5 -5
  67. package/templates/workspace/.opencode/skills/ws-delegate/SKILL.md +22 -0
  68. package/templates/workspace/.opencode/skills/ws-deliver/SKILL.md +5 -5
  69. package/templates/workspace/.opencode/skills/ws-dev/SKILL.md +52 -4
  70. package/templates/workspace/.opencode/skills/ws-dev-lite/SKILL.md +4 -1
  71. package/templates/workspace/.opencode/skills/ws-finish/SKILL.md +9 -5
  72. package/templates/workspace/.opencode/skills/ws-handoff/SKILL.md +5 -5
  73. package/templates/workspace/.opencode/skills/ws-intake/SKILL.md +32 -2
  74. package/templates/workspace/.opencode/skills/ws-migrate/SKILL.md +5 -5
  75. package/templates/workspace/.opencode/skills/ws-plan/SKILL.md +11 -53
  76. package/templates/workspace/.opencode/skills/ws-plan-verify/SKILL.md +5 -5
  77. package/templates/workspace/.opencode/skills/ws-pull/SKILL.md +5 -5
  78. package/templates/workspace/.opencode/skills/ws-push/SKILL.md +5 -5
  79. package/templates/workspace/.opencode/skills/ws-quality-review/SKILL.md +6 -1
  80. package/templates/workspace/.opencode/skills/ws-review/SKILL.md +25 -5
  81. package/templates/workspace/.opencode/skills/ws-submodule-setup/SKILL.md +5 -5
  82. package/templates/workspace/.opencode/skills/ws-verify-before-complete/SKILL.md +36 -5
  83. package/templates/workspace/AGENTS.md +32 -87
  84. package/templates/workspace/AI_PROJECT.md +4 -4
  85. package/templates/workspace/AI_WORKSPACE.md +1 -1
  86. package/templates/workspace/REQUIREMENTS.md +1 -1
  87. package/templates/workspace/gitignore +1 -1
  88. package/templates/workspace/manifest.json +96 -334
  89. /package/templates/workspace/{changes → .aiws/changes}/templates/design.md +0 -0
  90. /package/templates/workspace/{memory-bank → .aiws/memory-bank}/README.md +0 -0
  91. /package/templates/workspace/{memory-bank → .aiws/memory-bank}/architecture.md +0 -0
  92. /package/templates/workspace/{memory-bank → .aiws/memory-bank}/implementation-plan.md +0 -0
  93. /package/templates/workspace/{memory-bank → .aiws/memory-bank}/progress.md +0 -0
  94. /package/templates/workspace/{memory-bank → .aiws/memory-bank}/tech-stack.md +0 -0
  95. /package/templates/workspace/{requirements → .aiws/requirements}/CHANGELOG.md +0 -0
  96. /package/templates/workspace/{secrets → .aiws/secrets}/test-accounts.example.json +0 -0
@@ -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|issues/problem-issues\.csv)\s*[:#]\s*", "", token)
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` / `changes/{change_id}/submodules.targets`, or move the submodule gitlink onto a commit reachable from {remote}/{target_branch}"
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
 
@@ -1,18 +1,18 @@
1
1
  ---
2
2
  name: ws-commit
3
- description: `aiws ws-commit` 的薄包装入口
3
+ description: `aiws commit` 的薄包装入口
4
4
  ---
5
5
 
6
6
  # ws-commit
7
7
 
8
- `aiws ws-commit` 的薄包装入口。
8
+ `aiws commit` 的薄包装入口。
9
9
 
10
10
  ```bash
11
11
  if [[ -x "./node_modules/.bin/aiws" ]]; then
12
- ./node_modules/.bin/aiws ws-commit
12
+ ./node_modules/.bin/aiws commit
13
13
  elif command -v aiws >/dev/null 2>&1; then
14
- aiws ws-commit
14
+ aiws commit
15
15
  else
16
- npx @aipper/aiws ws-commit
16
+ npx @aipper/aiws commit
17
17
  fi
18
18
  ```
@@ -1,18 +1,18 @@
1
1
  ---
2
2
  name: ws-deliver
3
- description: `aiws ws-deliver` 的薄包装入口
3
+ description: `aiws deliver` 的薄包装入口
4
4
  ---
5
5
 
6
6
  # ws-deliver
7
7
 
8
- `aiws ws-deliver` 的薄包装入口。
8
+ `aiws deliver` 的薄包装入口。
9
9
 
10
10
  ```bash
11
11
  if [[ -x "./node_modules/.bin/aiws" ]]; then
12
- ./node_modules/.bin/aiws ws-deliver
12
+ ./node_modules/.bin/aiws deliver
13
13
  elif command -v aiws >/dev/null 2>&1; then
14
- aiws ws-deliver
14
+ aiws deliver
15
15
  else
16
- npx @aipper/aiws ws-deliver
16
+ npx @aipper/aiws deliver
17
17
  fi
18
18
  ```
@@ -1,18 +1,22 @@
1
1
  ---
2
2
  name: ws-finish
3
- description: `aiws ws-finish` 的薄包装入口
3
+ description: `aiws finish` 的收尾入口(finish_resume_required)
4
4
  ---
5
5
 
6
6
  # ws-finish
7
7
 
8
- `aiws ws-finish` 的薄包装入口。
8
+ `aiws finish` 的收尾入口。
9
+
10
+ 关键契约:
11
+ - 若 `aiws change status <change-id>` 输出 `governance_rule: finish_resume_required`,继续执行 `aiws change finish <change-id> --push`
12
+ - 普通 finish 的 `validate/evidence/state` 仍应在 `change/<change-id>` worktree 完成,不要在目标 worktree 里跑 `aiws validate . --stamp`
9
13
 
10
14
  ```bash
11
15
  if [[ -x "./node_modules/.bin/aiws" ]]; then
12
- ./node_modules/.bin/aiws ws-finish
16
+ ./node_modules/.bin/aiws finish
13
17
  elif command -v aiws >/dev/null 2>&1; then
14
- aiws ws-finish
18
+ aiws finish
15
19
  else
16
- npx @aipper/aiws ws-finish
20
+ npx @aipper/aiws finish
17
21
  fi
18
22
  ```
@@ -1,18 +1,18 @@
1
1
  ---
2
2
  name: ws-handoff
3
- description: `aiws ws-handoff` 的薄包装入口
3
+ description: `aiws handoff` 的薄包装入口
4
4
  ---
5
5
 
6
6
  # ws-handoff
7
7
 
8
- `aiws ws-handoff` 的薄包装入口。
8
+ `aiws handoff` 的薄包装入口。
9
9
 
10
10
  ```bash
11
11
  if [[ -x "./node_modules/.bin/aiws" ]]; then
12
- ./node_modules/.bin/aiws ws-handoff
12
+ ./node_modules/.bin/aiws handoff
13
13
  elif command -v aiws >/dev/null 2>&1; then
14
- aiws ws-handoff
14
+ aiws handoff
15
15
  else
16
- npx @aipper/aiws ws-handoff
16
+ npx @aipper/aiws handoff
17
17
  fi
18
18
  ```
@@ -0,0 +1,18 @@
1
+ ---
2
+ name: ws-migrate
3
+ description: `aiws migrate` 的薄包装入口
4
+ ---
5
+
6
+ # ws-migrate
7
+
8
+ `aiws migrate` 的薄包装入口。
9
+
10
+ ```bash
11
+ if [[ -x "./node_modules/.bin/aiws" ]]; then
12
+ ./node_modules/.bin/aiws migrate
13
+ elif command -v aiws >/dev/null 2>&1; then
14
+ aiws migrate
15
+ else
16
+ npx @aipper/aiws migrate
17
+ fi
18
+ ```
@@ -1,18 +1,18 @@
1
1
  ---
2
2
  name: ws-plan-verify
3
- description: `aiws ws-plan-verify` 的薄包装入口
3
+ description: `aiws plan-verify` 的薄包装入口
4
4
  ---
5
5
 
6
6
  # ws-plan-verify
7
7
 
8
- `aiws ws-plan-verify` 的薄包装入口。
8
+ `aiws plan-verify` 的薄包装入口。
9
9
 
10
10
  ```bash
11
11
  if [[ -x "./node_modules/.bin/aiws" ]]; then
12
- ./node_modules/.bin/aiws ws-plan-verify
12
+ ./node_modules/.bin/aiws plan-verify
13
13
  elif command -v aiws >/dev/null 2>&1; then
14
- aiws ws-plan-verify
14
+ aiws plan-verify
15
15
  else
16
- npx @aipper/aiws ws-plan-verify
16
+ npx @aipper/aiws plan-verify
17
17
  fi
18
18
  ```
@@ -0,0 +1,18 @@
1
+ ---
2
+ name: ws-pull
3
+ description: `aiws pull` 的薄包装入口
4
+ ---
5
+
6
+ # ws-pull
7
+
8
+ `aiws pull` 的薄包装入口。
9
+
10
+ ```bash
11
+ if [[ -x "./node_modules/.bin/aiws" ]]; then
12
+ ./node_modules/.bin/aiws pull
13
+ elif command -v aiws >/dev/null 2>&1; then
14
+ aiws pull
15
+ else
16
+ npx @aipper/aiws pull
17
+ fi
18
+ ```
@@ -0,0 +1,18 @@
1
+ ---
2
+ name: ws-push
3
+ description: `aiws push` 的薄包装入口
4
+ ---
5
+
6
+ # ws-push
7
+
8
+ `aiws push` 的薄包装入口。
9
+
10
+ ```bash
11
+ if [[ -x "./node_modules/.bin/aiws" ]]; then
12
+ ./node_modules/.bin/aiws push
13
+ elif command -v aiws >/dev/null 2>&1; then
14
+ aiws push
15
+ else
16
+ npx @aipper/aiws push
17
+ fi
18
+ ```
@@ -0,0 +1,18 @@
1
+ ---
2
+ name: ws-submodule-setup
3
+ description: `aiws submodule-setup` 的薄包装入口
4
+ ---
5
+
6
+ # ws-submodule-setup
7
+
8
+ `aiws submodule-setup` 的薄包装入口。
9
+
10
+ ```bash
11
+ if [[ -x "./node_modules/.bin/aiws" ]]; then
12
+ ./node_modules/.bin/aiws submodule-setup
13
+ elif command -v aiws >/dev/null 2>&1; then
14
+ aiws submodule-setup
15
+ else
16
+ npx @aipper/aiws submodule-setup
17
+ fi
18
+ ```
@@ -1,18 +1,18 @@
1
1
  ---
2
2
  name: ws-verify-before-complete
3
- description: `aiws ws-verify-before-complete` 的薄包装入口
3
+ description: `aiws verify-bc` 的薄包装入口
4
4
  ---
5
5
 
6
6
  # ws-verify-before-complete
7
7
 
8
- `aiws ws-verify-before-complete` 的薄包装入口。
8
+ `aiws verify-bc` 的薄包装入口。
9
9
 
10
10
  ```bash
11
11
  if [[ -x "./node_modules/.bin/aiws" ]]; then
12
- ./node_modules/.bin/aiws ws-verify-before-complete
12
+ ./node_modules/.bin/aiws verify-bc
13
13
  elif command -v aiws >/dev/null 2>&1; then
14
- aiws ws-verify-before-complete
14
+ aiws verify-bc
15
15
  else
16
- npx @aipper/aiws ws-verify-before-complete
16
+ npx @aipper/aiws verify-bc
17
17
  fi
18
18
  ```
@@ -30,3 +30,16 @@ if [[ -z "${aiws_bin:-}" ]]; then
30
30
  fi
31
31
 
32
32
  "$aiws_bin" validate .
33
+
34
+ # Quality gates (non-blocking warnings)
35
+ if [[ -f "package.json" ]] && grep -q '"lint"' package.json 2>/dev/null; then
36
+ npm run lint >/dev/null 2>&1 || echo "warn: lint found issues (non-blocking)" >&2
37
+ fi
38
+
39
+ if [[ -f "packages/aiws/package.json" ]] && grep -q '"build"' packages/aiws/package.json 2>/dev/null; then
40
+ npm run --workspace packages/aiws build >/dev/null 2>&1 || echo "warn: build/typecheck found issues (non-blocking)" >&2
41
+ fi
42
+
43
+ if [[ -f "package.json" ]] && grep -q '"test"' package.json 2>/dev/null; then
44
+ npm test >/dev/null 2>&1 || echo "warn: tests found issues (non-blocking)" >&2
45
+ fi
@@ -30,3 +30,10 @@ if [[ -z "${aiws_bin:-}" ]]; then
30
30
  fi
31
31
 
32
32
  "$aiws_bin" validate .
33
+
34
+ # Quality gates — non-blocking (failures downgraded to warning to avoid false positives)
35
+ if [[ -f "$root/package.json" ]]; then
36
+ npm --prefix "$root" run lint --if-present 2>/dev/null || echo "[aiws] lint: skipped or failed (non-blocking)" >&2
37
+ npm --prefix "$root" run typecheck --if-present 2>/dev/null || echo "[aiws] typecheck: skipped or failed (non-blocking)" >&2
38
+ npm --prefix "$root" test --if-present 2>/dev/null || echo "[aiws] test: skipped or failed (non-blocking)" >&2
39
+ fi
@@ -4,7 +4,72 @@ description: 收尾:fast-forward 合并并把 submodule 并回目标分支后
4
4
  <!-- AIWS_MANAGED_BEGIN:opencode:ws-finish -->
5
5
  # ws finish
6
6
 
7
- Thin CLI wrapper. Delegates to `aiws finish`.
7
+ 用中文输出(命令/路径/代码标识符保持原样不翻译)。
8
+
9
+ 目标:安全把 `change/<change-id>` fast-forward 合并回目标分支,避免手输分支名导致的错误。
10
+ 补充:若团队希望减少 submodule detached 的人为差异,建议在 `.gitmodules` 配置 `submodule.<name>.branch`,并在日常拉取时使用 `/ws-pull`。
11
+
12
+ 前置(必须):
13
+ - 工作区干净:`git status --porcelain` 无输出(否则先 commit 或 stash)
14
+ - change 分支存在(`change/<change-id>`;也支持 `changes/`、`ws/`、`ws-change/`)
15
+ - 普通 finish 的 `validate/evidence/state` 应先在 `change/<change-id>` worktree 完成;真正执行 `aiws change finish` 时再切到目标分支所在 worktree
16
+ - 若 `aiws change status <change-id>` 输出 `governance_rule: finish_resume_required`:说明 merge 已发生,只需继续 push / archive / cleanup closeout;直接在提示的 worktree 运行 `aiws change finish <change-id> --push`
17
+ - 若存在 `.gitmodules`:必须为每个 submodule 配置 `submodule.<name>.branch`(否则先运行 `/ws-submodule-setup` 并提交 `.gitmodules`)
18
+
19
+ 步骤(建议):
20
+ 0) 若存在 `.gitmodules`,先检查 submodule branch 配置是否齐全(缺失则停止并提示 setup):
21
+ ```bash
22
+ if [[ -f .gitmodules ]]; then
23
+ missing=0
24
+ while read -r key sub_path; do
25
+ name="${key#submodule.}"; name="${name%.path}"
26
+ b="$(git config --file .gitmodules --get "submodule.${name}.branch" 2>/dev/null || true)"
27
+ if [[ -z "${b:-}" ]]; then
28
+ echo "error: missing .gitmodules submodule.${name}.branch (path=${sub_path})"
29
+ missing=1
30
+ fi
31
+ done < <(git config --file .gitmodules --get-regexp '^submodule\\..*\\.path$' 2>/dev/null || true)
32
+ if [[ "$missing" -ne 0 ]]; then
33
+ echo "hint: run /ws-submodule-setup (and commit .gitmodules), then retry"
34
+ exit 2
35
+ fi
36
+ fi
37
+ ```
38
+ 1) 先运行 `/ws-preflight`(确保真值文件齐全)。
39
+ 2) 先运行 `aiws change status <change-id>`,优先看稳定字段 `governance_rule:`。
40
+ - 若为 `finish_resume_required`:直接跳到步骤 3 执行 `aiws change finish <change-id> --push`
41
+ - 若不是该值:按普通 finish 继续;不要依赖自然语言提示句做分支判断
42
+ 2.1) 普通 finish 时,`aiws validate . --stamp` / `aiws change evidence <change-id>` / `aiws change state <change-id> --write` 应在 `change/<change-id>` worktree 完成。
43
+ - 如果已经在目标分支 worktree,且 `governance_rule` 不是 `finish_resume_required`,不要在这里跑 `aiws validate . --stamp`;先回 `change/<change-id>` worktree,或先跑 `/ws-verify-before-complete`
44
+ 3) 若不存在 `.gitmodules`,或 submodules 已按顺序处理完成,优先直接执行最小收尾闭环:
45
+ - `aiws change finish <change-id> --push`
46
+ - 若当前就在 `change/<change-id>` 分支上,也可省略 `<change-id>`
47
+ - 该命令会在 fast-forward 合并成功后 push 目标分支;默认优先使用 upstream 配置(`branch.<name>.remote` + `branch.<name>.merge`),只有在你明确知道要推向别的 remote 时才追加 `--remote <name>`
48
+ - 若 `change/<change-id>` 位于独立 worktree,且该 worktree 干净,则会在 push 成功后自动执行 `git worktree remove` + `git worktree prune`
49
+ - AI 入口执行前,应先向用户说明将要 push 的 remote/branch;显式传入 `--push` 即视为确认
50
+ 4) 若提示无法 fast-forward:先在 change 分支(或对应 worktree)里 `git rebase <target-branch>`,再重试 `aiws change finish --push`。
51
+ 5) 若需要先处理 submodules,则按顺序处理每个 submodule(减少 detached;再 push):
52
+ - 先显式解析 base branch,不要用当前分支名替代:
53
+ - `python3 - <<'PY'`
54
+ - `import json, pathlib; meta = pathlib.Path("changes") / "<change-id>" / ".ws-change.json"; print((json.loads(meta.read_text(encoding="utf-8")).get("base_branch") or "").strip())`
55
+ - `PY`
56
+ - 发现 submodules:`git config --file .gitmodules --get-regexp '^submodule\\..*\\.path$'`
57
+ - 对每个 `<sub_path>`:
58
+ - 读取 superproject 当前 gitlink:`git rev-parse "HEAD:<sub_path>"`
59
+ - 目标分支真值:`changes/<change-id>/submodules.targets`;若条目里分支写 `.`,则展开为刚才解析出的 `base_branch`
60
+ - 生成/检查 `submodules.targets` 时,detached HEAD 默认建议取 `.gitmodules` 的 `submodule.<name>.branch`;已附着在本地分支时默认建议取当前分支;这些都只是预填建议,不是 finish/push 的运行时真值
61
+ - 不要直接切 `change/<change-id>` / `main` / `master` 来解 detached
62
+ - 用 pin 分支挂回(不改动现有 main/master 指针):`git -C "<sub_path>" checkout -B "aiws/pin/<target-branch>" <gitlink-sha>`
63
+ - 仅当 `<gitlink-sha>` 属于 `origin/<target-branch>` 历史时才允许 push;否则停止并人工处理分叉
64
+ - push(只允许 fast-forward):`git -C "<sub_path>" push origin "<gitlink-sha>:refs/heads/<target-branch>"`
65
+ 6) 任一 submodule 不满足 fast-forward 条件时立即停止(不要继续 push 主仓库)。
66
+ 7) submodules 全部成功后,再回到主仓库执行:
67
+ - `aiws change finish <change-id> --push`
68
+ 8) `aiws change finish --push` 成功后会自动归档并生成 handoff;只有历史/异常场景才需要手工运行 `aiws change archive <change-id>`。
69
+
70
+ 安全:
71
+ - push 前先输出状态并说明远端/分支。
72
+ - 不执行破坏性命令。
8
73
 
9
74
  ```bash
10
75
  if [[ -x "./node_modules/.bin/aiws" ]]; then
@@ -44,7 +44,7 @@ decision_kind = sys.argv[3]
44
44
  paths = sys.argv[4:]
45
45
 
46
46
  config_path = workspace_root / ".opencode" / "oh-my-opencode.json"
47
- out_dir = Path(os.environ.get("AIWS_OPENCODE_AUTONOMY_DIR", workspace_root / ".agentdocs" / "tmp" / "opencode-autonomy"))
47
+ out_dir = Path(os.environ.get("AIWS_OPENCODE_AUTONOMY_DIR", workspace_root / ".aiws" / "tmp" / "opencode-autonomy"))
48
48
  out_dir.mkdir(parents=True, exist_ok=True)
49
49
  log_file = out_dir / "approval-whitelist.log"
50
50
  result_file = out_dir / "approval-whitelist-last.json"
@@ -26,7 +26,7 @@ from pathlib import Path
26
26
  workspace_root = Path(sys.argv[1]).resolve()
27
27
  payload = json.loads(sys.argv[2])
28
28
 
29
- out_dir = Path(os.environ.get("AIWS_OPENCODE_AUTONOMY_DIR", workspace_root / ".agentdocs" / "tmp" / "opencode-autonomy"))
29
+ out_dir = Path(os.environ.get("AIWS_OPENCODE_AUTONOMY_DIR", workspace_root / ".aiws" / "tmp" / "opencode-autonomy"))
30
30
  out_dir.mkdir(parents=True, exist_ok=True)
31
31
  log_file = out_dir / "approval-whitelist-exec.log"
32
32
  result_file = out_dir / "approval-whitelist-exec-last.json"
@@ -4,7 +4,7 @@ set -euo pipefail
4
4
  workspace_root="${1:-.}"
5
5
  shift || true
6
6
 
7
- out_dir="${AIWS_OPENCODE_AUTONOMY_DIR:-${workspace_root}/.agentdocs/tmp/opencode-autonomy}"
7
+ out_dir="${AIWS_OPENCODE_AUTONOMY_DIR:-${workspace_root}/.aiws/tmp/opencode-autonomy}"
8
8
  queue_file="${out_dir}/approval-watchdog-queue.jsonl"
9
9
  once=false
10
10
  poll_ms=2000
@@ -6,7 +6,7 @@ if ! command -v tmux >/dev/null 2>&1; then
6
6
  exit 1
7
7
  fi
8
8
 
9
- out_dir="${AIWS_OPENCODE_AUTONOMY_DIR:-.agentdocs/tmp/opencode-autonomy}"
9
+ out_dir="${AIWS_OPENCODE_AUTONOMY_DIR:-.aiws/tmp/opencode-autonomy}"
10
10
  scan_file="${1:-${out_dir}/tmux-scan.json}"
11
11
  log_file="${out_dir}/tmux-rescue.log"
12
12
  mkdir -p "${out_dir}"
@@ -6,7 +6,7 @@ if ! command -v tmux >/dev/null 2>&1; then
6
6
  exit 1
7
7
  fi
8
8
 
9
- out_dir="${AIWS_OPENCODE_AUTONOMY_DIR:-.agentdocs/tmp/opencode-autonomy}"
9
+ out_dir="${AIWS_OPENCODE_AUTONOMY_DIR:-.aiws/tmp/opencode-autonomy}"
10
10
  out_file="${out_dir}/tmux-scan.json"
11
11
  mkdir -p "${out_dir}"
12
12