@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.
- package/docs/workflow-delegation-contracts.json +40 -1
- package/docs/workflow-delegation-contracts.md +10 -0
- package/docs/workflow-delegation-contracts.schema.json +57 -0
- package/docs/workflow-governance-rules.md +1 -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 +46 -14
- package/docs/workflow-router-rules.md +19 -16
- package/docs/workflow-router-rules.schema.json +4 -0
- package/docs/workflow-stage-contracts.json +22 -2
- package/docs/workflow-stage-contracts.md +15 -13
- package/docs/workflow-stage-contracts.schema.json +12 -1
- package/package.json +1 -1
- package/templates/workspace/.agents/skills/using-aiws/SKILL.md +10 -6
- package/templates/workspace/.agents/skills/ws-commit/SKILL.md +5 -5
- package/templates/workspace/.agents/skills/ws-deliver/SKILL.md +5 -5
- package/templates/workspace/.agents/skills/ws-dev/SKILL.md +30 -0
- package/templates/workspace/.agents/skills/ws-finish/SKILL.md +9 -5
- package/templates/workspace/.agents/skills/ws-handoff/SKILL.md +5 -5
- package/templates/workspace/.agents/skills/ws-migrate/SKILL.md +18 -0
- package/templates/workspace/.agents/skills/ws-plan-verify/SKILL.md +5 -5
- package/templates/workspace/.agents/skills/ws-pull/SKILL.md +18 -0
- package/templates/workspace/.agents/skills/ws-push/SKILL.md +18 -0
- package/templates/workspace/.agents/skills/ws-submodule-setup/SKILL.md +18 -0
- package/templates/workspace/.agents/skills/ws-verify-before-complete/SKILL.md +5 -5
- package/templates/workspace/{changes → .aiws/changes}/README.md +13 -13
- 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/skills/ws-commit/SKILL.md +5 -5
- package/templates/workspace/.claude/skills/ws-deliver/SKILL.md +5 -5
- package/templates/workspace/.claude/skills/ws-finish/SKILL.md +9 -5
- package/templates/workspace/.claude/skills/ws-handoff/SKILL.md +5 -5
- package/templates/workspace/.claude/skills/ws-migrate/SKILL.md +18 -0
- package/templates/workspace/.claude/skills/ws-plan-verify/SKILL.md +5 -5
- package/templates/workspace/.claude/skills/ws-pull/SKILL.md +18 -0
- package/templates/workspace/.claude/skills/ws-push/SKILL.md +18 -0
- package/templates/workspace/.claude/skills/ws-submodule-setup/SKILL.md +18 -0
- package/templates/workspace/.claude/skills/ws-verify-before-complete/SKILL.md +5 -5
- package/templates/workspace/.githooks/pre-commit +13 -0
- package/templates/workspace/.githooks/pre-push +7 -0
- package/templates/workspace/.opencode/commands/ws-finish.md +66 -1
- package/templates/workspace/.opencode/helpers/approval-whitelist-check.sh +1 -1
- package/templates/workspace/.opencode/helpers/approval-whitelist-run.sh +1 -1
- package/templates/workspace/.opencode/helpers/approval-whitelist-watchdog.sh +1 -1
- package/templates/workspace/.opencode/helpers/tmux-swarm-rescue.sh +1 -1
- package/templates/workspace/.opencode/helpers/tmux-swarm-scan.sh +1 -1
- package/templates/workspace/.opencode/lib/aiws-context.js +953 -0
- 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 +39 -2
- package/templates/workspace/.opencode/skills/ws-commit/SKILL.md +5 -5
- package/templates/workspace/.opencode/skills/ws-delegate/SKILL.md +22 -0
- package/templates/workspace/.opencode/skills/ws-deliver/SKILL.md +5 -5
- package/templates/workspace/.opencode/skills/ws-dev/SKILL.md +52 -4
- package/templates/workspace/.opencode/skills/ws-dev-lite/SKILL.md +4 -1
- package/templates/workspace/.opencode/skills/ws-finish/SKILL.md +9 -5
- package/templates/workspace/.opencode/skills/ws-handoff/SKILL.md +5 -5
- package/templates/workspace/.opencode/skills/ws-intake/SKILL.md +32 -2
- package/templates/workspace/.opencode/skills/ws-migrate/SKILL.md +5 -5
- package/templates/workspace/.opencode/skills/ws-plan/SKILL.md +11 -53
- package/templates/workspace/.opencode/skills/ws-plan-verify/SKILL.md +5 -5
- package/templates/workspace/.opencode/skills/ws-pull/SKILL.md +5 -5
- package/templates/workspace/.opencode/skills/ws-push/SKILL.md +5 -5
- package/templates/workspace/.opencode/skills/ws-quality-review/SKILL.md +6 -1
- package/templates/workspace/.opencode/skills/ws-review/SKILL.md +25 -5
- package/templates/workspace/.opencode/skills/ws-submodule-setup/SKILL.md +5 -5
- package/templates/workspace/.opencode/skills/ws-verify-before-complete/SKILL.md +36 -5
- package/templates/workspace/AGENTS.md +32 -87
- package/templates/workspace/AI_PROJECT.md +4 -4
- 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 +96 -334
- /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
|
@@ -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
|
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: ws-commit
|
|
3
|
-
description: `aiws
|
|
3
|
+
description: `aiws commit` 的薄包装入口
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# ws-commit
|
|
7
7
|
|
|
8
|
-
`aiws
|
|
8
|
+
`aiws commit` 的薄包装入口。
|
|
9
9
|
|
|
10
10
|
```bash
|
|
11
11
|
if [[ -x "./node_modules/.bin/aiws" ]]; then
|
|
12
|
-
./node_modules/.bin/aiws
|
|
12
|
+
./node_modules/.bin/aiws commit
|
|
13
13
|
elif command -v aiws >/dev/null 2>&1; then
|
|
14
|
-
aiws
|
|
14
|
+
aiws commit
|
|
15
15
|
else
|
|
16
|
-
npx @aipper/aiws
|
|
16
|
+
npx @aipper/aiws commit
|
|
17
17
|
fi
|
|
18
18
|
```
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: ws-deliver
|
|
3
|
-
description: `aiws
|
|
3
|
+
description: `aiws deliver` 的薄包装入口
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# ws-deliver
|
|
7
7
|
|
|
8
|
-
`aiws
|
|
8
|
+
`aiws deliver` 的薄包装入口。
|
|
9
9
|
|
|
10
10
|
```bash
|
|
11
11
|
if [[ -x "./node_modules/.bin/aiws" ]]; then
|
|
12
|
-
./node_modules/.bin/aiws
|
|
12
|
+
./node_modules/.bin/aiws deliver
|
|
13
13
|
elif command -v aiws >/dev/null 2>&1; then
|
|
14
|
-
aiws
|
|
14
|
+
aiws deliver
|
|
15
15
|
else
|
|
16
|
-
npx @aipper/aiws
|
|
16
|
+
npx @aipper/aiws deliver
|
|
17
17
|
fi
|
|
18
18
|
```
|
|
@@ -1,18 +1,22 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: ws-finish
|
|
3
|
-
description: `aiws
|
|
3
|
+
description: `aiws finish` 的收尾入口(finish_resume_required)
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# ws-finish
|
|
7
7
|
|
|
8
|
-
`aiws
|
|
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
|
|
16
|
+
./node_modules/.bin/aiws finish
|
|
13
17
|
elif command -v aiws >/dev/null 2>&1; then
|
|
14
|
-
aiws
|
|
18
|
+
aiws finish
|
|
15
19
|
else
|
|
16
|
-
npx @aipper/aiws
|
|
20
|
+
npx @aipper/aiws finish
|
|
17
21
|
fi
|
|
18
22
|
```
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: ws-handoff
|
|
3
|
-
description: `aiws
|
|
3
|
+
description: `aiws handoff` 的薄包装入口
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# ws-handoff
|
|
7
7
|
|
|
8
|
-
`aiws
|
|
8
|
+
`aiws handoff` 的薄包装入口。
|
|
9
9
|
|
|
10
10
|
```bash
|
|
11
11
|
if [[ -x "./node_modules/.bin/aiws" ]]; then
|
|
12
|
-
./node_modules/.bin/aiws
|
|
12
|
+
./node_modules/.bin/aiws handoff
|
|
13
13
|
elif command -v aiws >/dev/null 2>&1; then
|
|
14
|
-
aiws
|
|
14
|
+
aiws handoff
|
|
15
15
|
else
|
|
16
|
-
npx @aipper/aiws
|
|
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
|
|
3
|
+
description: `aiws plan-verify` 的薄包装入口
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# ws-plan-verify
|
|
7
7
|
|
|
8
|
-
`aiws
|
|
8
|
+
`aiws plan-verify` 的薄包装入口。
|
|
9
9
|
|
|
10
10
|
```bash
|
|
11
11
|
if [[ -x "./node_modules/.bin/aiws" ]]; then
|
|
12
|
-
./node_modules/.bin/aiws
|
|
12
|
+
./node_modules/.bin/aiws plan-verify
|
|
13
13
|
elif command -v aiws >/dev/null 2>&1; then
|
|
14
|
-
aiws
|
|
14
|
+
aiws plan-verify
|
|
15
15
|
else
|
|
16
|
-
npx @aipper/aiws
|
|
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
|
|
3
|
+
description: `aiws verify-bc` 的薄包装入口
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# ws-verify-before-complete
|
|
7
7
|
|
|
8
|
-
`aiws
|
|
8
|
+
`aiws verify-bc` 的薄包装入口。
|
|
9
9
|
|
|
10
10
|
```bash
|
|
11
11
|
if [[ -x "./node_modules/.bin/aiws" ]]; then
|
|
12
|
-
./node_modules/.bin/aiws
|
|
12
|
+
./node_modules/.bin/aiws verify-bc
|
|
13
13
|
elif command -v aiws >/dev/null 2>&1; then
|
|
14
|
-
aiws
|
|
14
|
+
aiws verify-bc
|
|
15
15
|
else
|
|
16
|
-
npx @aipper/aiws
|
|
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
|
-
|
|
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 / ".
|
|
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 / ".
|
|
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}/.
|
|
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:-.
|
|
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:-.
|
|
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
|
|