@imdeadpool/guardex 5.0.11 → 5.0.13
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/README.md +60 -13
- package/bin/multiagent-safety.js +485 -60
- package/package.json +4 -4
- package/templates/AGENTS.multiagent-safety.md +19 -7
- package/templates/codex/skills/guardex/SKILL.md +48 -0
- package/templates/codex/skills/guardex-merge-skills-to-dev/SKILL.md +58 -0
- package/templates/githooks/post-merge +43 -0
- package/templates/githooks/pre-commit +24 -15
- package/templates/githooks/pre-push +3 -3
- package/templates/github/pull.yml.example +6 -0
- package/templates/github/workflows/cr.yml +21 -0
- package/templates/scripts/agent-branch-finish.sh +0 -22
- package/templates/scripts/agent-branch-start.sh +66 -1
- package/templates/scripts/codex-agent.sh +82 -27
- package/templates/scripts/openspec/init-change-workspace.sh +87 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@imdeadpool/guardex",
|
|
3
|
-
"version": "5.0.
|
|
3
|
+
"version": "5.0.13",
|
|
4
4
|
"description": "GuardeX: the Guardian T-Rex for your repo, with hardened multi-agent git guardrails.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"preferGlobal": true,
|
|
@@ -54,12 +54,12 @@
|
|
|
54
54
|
"author": "recodeecom",
|
|
55
55
|
"repository": {
|
|
56
56
|
"type": "git",
|
|
57
|
-
"url": "git+https://github.com/
|
|
57
|
+
"url": "git+https://github.com/recodeee/guardex.git"
|
|
58
58
|
},
|
|
59
59
|
"bugs": {
|
|
60
|
-
"url": "https://github.com/
|
|
60
|
+
"url": "https://github.com/recodeee/guardex/issues"
|
|
61
61
|
},
|
|
62
|
-
"homepage": "https://github.com/
|
|
62
|
+
"homepage": "https://github.com/recodeee/guardex#readme",
|
|
63
63
|
"funding": "https://github.com/sponsors/recodeecom",
|
|
64
64
|
"publishConfig": {
|
|
65
65
|
"access": "public"
|
|
@@ -16,10 +16,10 @@
|
|
|
16
16
|
- OMX completion policy: when a task is done, the agent must commit the task changes, push the agent branch, and create/update a PR for those changes (via `codex-agent` or `agent-branch-finish`).
|
|
17
17
|
- Auto-finish now waits for required checks/merge and then cleans merged sandbox branch/worktree by default.
|
|
18
18
|
- Use `--no-cleanup` only when you explicitly need to keep a merged sandbox for audit/debug follow-up.
|
|
19
|
-
- If codex-agent auto-finish cannot complete, immediately run `scripts/agent-branch-finish.sh --branch "<agent-branch>" --via-pr --wait-for-merge` and keep the branch open until checks/review pass.
|
|
20
|
-
- If merge/rebase conflicts block auto-finish, run a conflict-resolution review pass in that sandbox branch, then rerun `agent-branch-finish.sh --via-pr` until merged.
|
|
19
|
+
- If codex-agent auto-finish cannot complete, immediately run `scripts/agent-branch-finish.sh --branch "<agent-branch>" --base dev --via-pr --wait-for-merge` and keep the branch open until checks/review pass.
|
|
20
|
+
- If merge/rebase conflicts block auto-finish, run a conflict-resolution review pass in that sandbox branch, then rerun `agent-branch-finish.sh --base dev --via-pr --wait-for-merge` until merged.
|
|
21
21
|
- Completion is not valid until these are true: commit exists on the agent branch, branch is pushed to `origin`, and PR/merge status is produced by `agent-branch-finish.sh` or `codex-agent`.
|
|
22
|
-
- For every new task, if an assigned agent sub-branch/worktree is already open, continue in that sub-branch; otherwise create a fresh one from the current local base snapshot with `scripts/agent-branch-start.sh`.
|
|
22
|
+
- For every new task, including follow-up work in the same chat/session, if an assigned agent sub-branch/worktree is already open, continue in that sub-branch; otherwise create a fresh one from the current local base snapshot with `scripts/agent-branch-start.sh`.
|
|
23
23
|
- Never implement directly on the local/base branch checkout; keep it unchanged and perform all edits in the agent sub-branch/worktree.
|
|
24
24
|
- If the change publishes or bumps a version, the same change must also update release notes/changelog entries.
|
|
25
25
|
|
|
@@ -46,12 +46,13 @@
|
|
|
46
46
|
- Verification commands + results
|
|
47
47
|
- Risks / follow-ups
|
|
48
48
|
|
|
49
|
-
## OpenSpec
|
|
49
|
+
## OpenSpec Workspaces (required for agent sub-branch changes)
|
|
50
50
|
|
|
51
|
-
OMX Codex execution flows must use OpenSpec. `scripts/codex-agent.sh` bootstraps
|
|
52
|
-
per-branch
|
|
51
|
+
OMX Codex execution flows must use OpenSpec. `scripts/codex-agent.sh` bootstraps
|
|
52
|
+
per-branch OpenSpec workspaces automatically:
|
|
53
53
|
|
|
54
54
|
```text
|
|
55
|
+
openspec/changes/<agent-branch-slug>/
|
|
55
56
|
openspec/plan/<agent-branch-slug>/
|
|
56
57
|
```
|
|
57
58
|
|
|
@@ -59,10 +60,21 @@ For manual `scripts/agent-branch-start.sh` usage, enable auto-bootstrap with
|
|
|
59
60
|
`MUSAFETY_OPENSPEC_AUTO_INIT=true` or scaffold manually before implementation:
|
|
60
61
|
|
|
61
62
|
```bash
|
|
63
|
+
bash scripts/openspec/init-change-workspace.sh "<change-slug>" "<capability-slug>"
|
|
62
64
|
bash scripts/openspec/init-plan-workspace.sh "<plan-slug>"
|
|
63
65
|
```
|
|
64
66
|
|
|
65
|
-
Expected shape:
|
|
67
|
+
Expected change shape:
|
|
68
|
+
|
|
69
|
+
```text
|
|
70
|
+
openspec/changes/<change-slug>/
|
|
71
|
+
.openspec.yaml
|
|
72
|
+
proposal.md
|
|
73
|
+
tasks.md
|
|
74
|
+
specs/<capability-slug>/spec.md
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Expected plan shape:
|
|
66
78
|
|
|
67
79
|
```text
|
|
68
80
|
openspec/plan/<plan-slug>/
|
|
@@ -38,4 +38,52 @@ gx scan
|
|
|
38
38
|
- For one-command Codex sandbox startup, use `bash scripts/codex-agent.sh "<task>" "<agent-name>"`.
|
|
39
39
|
- `scripts/codex-agent.sh` auto-syncs the sandbox branch against base before each task and auto-finishes merge/PR flow after Codex exits.
|
|
40
40
|
- Auto-finish keeps the branch/worktree by default; remove merged branches explicitly with `gx cleanup` (or `gx cleanup --branch "<agent-branch>"`).
|
|
41
|
+
- For skill-file-only merges into the local base branch (`dev` by default), use `$guardex-merge-skills-to-dev`.
|
|
41
42
|
- Do not bypass protected branch safeguards unless explicitly required.
|
|
43
|
+
|
|
44
|
+
## Bulk merge runbook (changed agent branches)
|
|
45
|
+
|
|
46
|
+
Use this when a repo has many `agent/*` branches/worktrees with pending changes and you need them merged into the base branch quickly.
|
|
47
|
+
|
|
48
|
+
1. Confirm base and guardrails are healthy:
|
|
49
|
+
|
|
50
|
+
```sh
|
|
51
|
+
git status --short --branch
|
|
52
|
+
git pull --ff-only origin "$(git config --get multiagent.baseBranch || echo dev)"
|
|
53
|
+
gx scan
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
2. Run bulk finish first:
|
|
57
|
+
|
|
58
|
+
```sh
|
|
59
|
+
gx finish --all
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
3. If a branch fails with `already used by worktree` or stale rebase hints, clear the stale state in that worktree, then retry targeted finish:
|
|
63
|
+
|
|
64
|
+
```sh
|
|
65
|
+
git -C "<worktree>" rebase --abort || true
|
|
66
|
+
gx finish --branch "<agent-branch>" --base "$(git config --get multiagent.baseBranch || echo dev)" --no-wait-for-merge --cleanup
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
4. If `gh pr merge` exits non-zero due local branch deletion but PR is already merged, treat it as merged and verify with:
|
|
70
|
+
|
|
71
|
+
```sh
|
|
72
|
+
gh pr view "<pr-number>" --json state,mergedAt,url
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
5. If a branch is still ahead of base with no open PR, create and merge a follow-up PR manually:
|
|
76
|
+
|
|
77
|
+
```sh
|
|
78
|
+
gh pr create --base "<base-branch>" --head "<agent-branch>" --title "Auto-finish: <agent-branch>" --body "Follow-up merge for pending branch commits."
|
|
79
|
+
gh pr merge "<pr-number>" --squash --delete-branch
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
6. Final verification:
|
|
83
|
+
|
|
84
|
+
```sh
|
|
85
|
+
gh pr list --state open --search "head:agent/ base:<base-branch>"
|
|
86
|
+
git pull --ff-only origin "<base-branch>"
|
|
87
|
+
gx cleanup
|
|
88
|
+
gx scan
|
|
89
|
+
```
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: guardex-merge-skills-to-dev
|
|
3
|
+
description: "Use when you need to merge SKILL.md updates from agent branches/worktrees into the local base branch (default: dev) with the multiagent-safety flow."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# GuardeX Merge Skills to dev
|
|
7
|
+
|
|
8
|
+
Use this skill when you only want to promote Codex skill file updates into the base branch (normally `dev`) without editing the visible base checkout directly.
|
|
9
|
+
|
|
10
|
+
## What this merges
|
|
11
|
+
|
|
12
|
+
- `.codex/skills/**/SKILL.md`
|
|
13
|
+
- `templates/codex/skills/**/SKILL.md`
|
|
14
|
+
|
|
15
|
+
## Merge runbook (safe path)
|
|
16
|
+
|
|
17
|
+
1. Resolve the base branch:
|
|
18
|
+
|
|
19
|
+
```sh
|
|
20
|
+
BASE_BRANCH="$(git config --get multiagent.baseBranch || echo dev)"
|
|
21
|
+
echo "$BASE_BRANCH"
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
2. Start a dedicated integration sandbox from base:
|
|
25
|
+
|
|
26
|
+
```sh
|
|
27
|
+
bash scripts/agent-branch-start.sh "merge-skill-files-to-${BASE_BRANCH}" "skill-merge" "$BASE_BRANCH"
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
3. Enter the sandbox worktree printed by the command above.
|
|
31
|
+
|
|
32
|
+
4. Pull only skill files from each source agent branch:
|
|
33
|
+
|
|
34
|
+
```sh
|
|
35
|
+
SOURCE_BRANCH="<agent-branch>"
|
|
36
|
+
git checkout "$SOURCE_BRANCH" -- ':(glob).codex/skills/**/SKILL.md' ':(glob)templates/codex/skills/**/SKILL.md'
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
5. Verify scope before commit:
|
|
40
|
+
|
|
41
|
+
```sh
|
|
42
|
+
git status --short
|
|
43
|
+
git diff --name-only
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
6. Commit and merge back to base using guardex finish flow:
|
|
47
|
+
|
|
48
|
+
```sh
|
|
49
|
+
git add .codex/skills templates/codex/skills
|
|
50
|
+
git commit -m "Merge skill file updates into ${BASE_BRANCH}"
|
|
51
|
+
bash scripts/agent-branch-finish.sh --branch "$(git rev-parse --abbrev-ref HEAD)" --base "$BASE_BRANCH" --via-pr --wait-for-merge --cleanup
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Notes
|
|
55
|
+
|
|
56
|
+
- If a source branch has non-skill changes, this runbook keeps them out of the merge.
|
|
57
|
+
- If merge conflicts occur, resolve only within the skill files, then rerun `agent-branch-finish.sh`.
|
|
58
|
+
- Do not commit directly on `dev`/`main`; always merge through an agent branch/worktree.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
if [[ "${MUSAFETY_DISABLE_POST_MERGE_CLEANUP:-0}" == "1" ]]; then
|
|
5
|
+
exit 0
|
|
6
|
+
fi
|
|
7
|
+
|
|
8
|
+
repo_root="$(git rev-parse --show-toplevel 2>/dev/null || true)"
|
|
9
|
+
if [[ -z "$repo_root" ]]; then
|
|
10
|
+
exit 0
|
|
11
|
+
fi
|
|
12
|
+
|
|
13
|
+
branch="$(git -C "$repo_root" rev-parse --abbrev-ref HEAD 2>/dev/null || true)"
|
|
14
|
+
if [[ -z "$branch" || "$branch" == "HEAD" ]]; then
|
|
15
|
+
exit 0
|
|
16
|
+
fi
|
|
17
|
+
|
|
18
|
+
base_branch="${MUSAFETY_BASE_BRANCH:-$(git -C "$repo_root" config --get multiagent.baseBranch || true)}"
|
|
19
|
+
if [[ -z "$base_branch" ]]; then
|
|
20
|
+
base_branch="dev"
|
|
21
|
+
fi
|
|
22
|
+
|
|
23
|
+
if [[ "$branch" != "$base_branch" ]]; then
|
|
24
|
+
exit 0
|
|
25
|
+
fi
|
|
26
|
+
|
|
27
|
+
cli_path="$repo_root/bin/multiagent-safety.js"
|
|
28
|
+
if [[ ! -f "$cli_path" ]]; then
|
|
29
|
+
exit 0
|
|
30
|
+
fi
|
|
31
|
+
|
|
32
|
+
node_bin="${MUSAFETY_NODE_BIN:-node}"
|
|
33
|
+
if ! command -v "$node_bin" >/dev/null 2>&1; then
|
|
34
|
+
exit 0
|
|
35
|
+
fi
|
|
36
|
+
|
|
37
|
+
"$node_bin" "$cli_path" cleanup \
|
|
38
|
+
--target "$repo_root" \
|
|
39
|
+
--base "$base_branch" \
|
|
40
|
+
--include-pr-merged \
|
|
41
|
+
--keep-clean-worktrees >/dev/null 2>&1 || true
|
|
42
|
+
|
|
43
|
+
exit 0
|
|
@@ -30,7 +30,7 @@ fi
|
|
|
30
30
|
|
|
31
31
|
allow_vscode_protected_raw="${MUSAFETY_ALLOW_VSCODE_PROTECTED_BRANCH_WRITES:-$(git config --get multiagent.allowVscodeProtectedBranchWrites || true)}"
|
|
32
32
|
if [[ -z "$allow_vscode_protected_raw" ]]; then
|
|
33
|
-
allow_vscode_protected_raw="
|
|
33
|
+
allow_vscode_protected_raw="false"
|
|
34
34
|
fi
|
|
35
35
|
allow_vscode_protected="$(printf '%s' "$allow_vscode_protected_raw" | tr '[:upper:]' '[:lower:]')"
|
|
36
36
|
|
|
@@ -55,15 +55,6 @@ for protected_branch in $protected_branches_raw; do
|
|
|
55
55
|
fi
|
|
56
56
|
done
|
|
57
57
|
|
|
58
|
-
is_local_only_branch=0
|
|
59
|
-
if [[ "$is_protected_branch" == "1" ]]; then
|
|
60
|
-
upstream_ref="$(git for-each-ref --format='%(upstream:short)' "refs/heads/${branch}" | head -n 1)"
|
|
61
|
-
remote_branch_ref="$(git for-each-ref --format='%(refname:short)' "refs/remotes/*/${branch}" | head -n 1)"
|
|
62
|
-
if [[ -z "$upstream_ref" && -z "$remote_branch_ref" ]]; then
|
|
63
|
-
is_local_only_branch=1
|
|
64
|
-
fi
|
|
65
|
-
fi
|
|
66
|
-
|
|
67
58
|
codex_require_agent_branch_raw="${MUSAFETY_CODEX_REQUIRE_AGENT_BRANCH:-$(git config --get multiagent.codexRequireAgentBranch || true)}"
|
|
68
59
|
if [[ -z "$codex_require_agent_branch_raw" ]]; then
|
|
69
60
|
codex_require_agent_branch_raw="true"
|
|
@@ -134,7 +125,7 @@ fi
|
|
|
134
125
|
|
|
135
126
|
if [[ "$is_protected_branch" == "1" ]]; then
|
|
136
127
|
if [[ "$is_codex_session" != "1" && "$is_vscode_git_context" == "1" ]]; then
|
|
137
|
-
if [[ "$allow_vscode_protected_branch_writes" == "1"
|
|
128
|
+
if [[ "$allow_vscode_protected_branch_writes" == "1" ]]; then
|
|
138
129
|
exit 0
|
|
139
130
|
fi
|
|
140
131
|
fi
|
|
@@ -155,11 +146,21 @@ Use an agent branch first:
|
|
|
155
146
|
After finishing work:
|
|
156
147
|
bash scripts/agent-branch-finish.sh
|
|
157
148
|
|
|
158
|
-
Optional repo
|
|
159
|
-
git config multiagent.allowVscodeProtectedBranchWrites
|
|
149
|
+
Optional repo opt-in for VS Code protected-branch commits:
|
|
150
|
+
git config multiagent.allowVscodeProtectedBranchWrites true
|
|
151
|
+
|
|
152
|
+
Temporary bypass (not recommended):
|
|
153
|
+
ALLOW_COMMIT_ON_PROTECTED_BRANCH=1 git commit ...
|
|
154
|
+
MSG
|
|
155
|
+
exit 1
|
|
156
|
+
fi
|
|
160
157
|
|
|
161
|
-
|
|
162
|
-
|
|
158
|
+
if [[ "$is_agent_context" == "1" && "$branch" != agent/* ]]; then
|
|
159
|
+
cat >&2 <<'MSG'
|
|
160
|
+
[agent-branch-guard] Agent commits must run on dedicated agent/* branches.
|
|
161
|
+
Start an agent branch first:
|
|
162
|
+
bash scripts/agent-branch-start.sh "<task-or-plan>" "<agent-name>"
|
|
163
|
+
Then commit on that branch.
|
|
163
164
|
|
|
164
165
|
Temporary bypass (not recommended):
|
|
165
166
|
ALLOW_COMMIT_ON_PROTECTED_BRANCH=1 git commit ...
|
|
@@ -168,6 +169,14 @@ MSG
|
|
|
168
169
|
fi
|
|
169
170
|
|
|
170
171
|
if [[ "$branch" == agent/* ]]; then
|
|
172
|
+
if [[ "${MUSAFETY_AUTOCLAIM_STAGED_LOCKS:-1}" == "1" ]]; then
|
|
173
|
+
while IFS= read -r staged_file; do
|
|
174
|
+
[[ -z "$staged_file" ]] && continue
|
|
175
|
+
[[ "$staged_file" == ".omx/state/agent-file-locks.json" ]] && continue
|
|
176
|
+
python3 scripts/agent-file-locks.py claim --branch "$branch" "$staged_file" >/dev/null 2>&1 || true
|
|
177
|
+
done < <(git diff --cached --name-only --diff-filter=ACMRDTUXB)
|
|
178
|
+
fi
|
|
179
|
+
|
|
171
180
|
if ! python3 scripts/agent-file-locks.py validate --branch "$branch" --staged; then
|
|
172
181
|
cat >&2 <<'MSG'
|
|
173
182
|
[agent-branch-guard] Agent branch commits require file ownership locks.
|
|
@@ -12,7 +12,7 @@ fi
|
|
|
12
12
|
|
|
13
13
|
allow_vscode_protected_raw="${MUSAFETY_ALLOW_VSCODE_PROTECTED_BRANCH_WRITES:-$(git config --get multiagent.allowVscodeProtectedBranchWrites || true)}"
|
|
14
14
|
if [[ -z "$allow_vscode_protected_raw" ]]; then
|
|
15
|
-
allow_vscode_protected_raw="
|
|
15
|
+
allow_vscode_protected_raw="false"
|
|
16
16
|
fi
|
|
17
17
|
allow_vscode_protected="$(printf '%s' "$allow_vscode_protected_raw" | tr '[:upper:]' '[:lower:]')"
|
|
18
18
|
|
|
@@ -77,8 +77,8 @@ if [[ "${#blocked_refs[@]}" -gt 0 ]]; then
|
|
|
77
77
|
echo "[agent-branch-guard] Push to protected branch blocked."
|
|
78
78
|
echo "[agent-branch-guard] Protected target(s): ${blocked_refs[*]}"
|
|
79
79
|
echo "[agent-branch-guard] Use an agent branch and merge via PR."
|
|
80
|
-
echo "[agent-branch-guard] Optional repo
|
|
81
|
-
echo " git config multiagent.allowVscodeProtectedBranchWrites
|
|
80
|
+
echo "[agent-branch-guard] Optional repo opt-in for VS Code protected-branch push:"
|
|
81
|
+
echo " git config multiagent.allowVscodeProtectedBranchWrites true"
|
|
82
82
|
echo
|
|
83
83
|
echo "Temporary bypass (not recommended):"
|
|
84
84
|
echo " ALLOW_PUSH_ON_PROTECTED_BRANCH=1 git push ..."
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
name: Code Review
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
types: [opened, reopened, synchronize]
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
contents: read
|
|
9
|
+
pull-requests: write
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
review:
|
|
13
|
+
if: ${{ secrets.OPENAI_API_KEY != '' }}
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
steps:
|
|
16
|
+
- uses: anc95/ChatGPT-CodeReview@main
|
|
17
|
+
env:
|
|
18
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
19
|
+
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
|
20
|
+
OPENAI_API_ENDPOINT: https://api.openai.com/v1
|
|
21
|
+
MODEL: gpt-4o-mini
|
|
@@ -162,28 +162,6 @@ if [[ "$BASE_BRANCH_EXPLICIT" -eq 0 ]]; then
|
|
|
162
162
|
fi
|
|
163
163
|
fi
|
|
164
164
|
|
|
165
|
-
if [[ -z "$BASE_BRANCH" ]]; then
|
|
166
|
-
branch_stored_base="$(git -C "$repo_root" config --get "branch.${SOURCE_BRANCH}.musafetyBase" || true)"
|
|
167
|
-
if [[ -n "$branch_stored_base" ]]; then
|
|
168
|
-
BASE_BRANCH="$branch_stored_base"
|
|
169
|
-
fi
|
|
170
|
-
fi
|
|
171
|
-
|
|
172
|
-
if [[ -z "$BASE_BRANCH" ]]; then
|
|
173
|
-
source_upstream="$(git -C "$repo_root" for-each-ref --format='%(upstream:short)' "refs/heads/${SOURCE_BRANCH}" | head -n 1)"
|
|
174
|
-
source_upstream="${source_upstream:-}"
|
|
175
|
-
if [[ "$source_upstream" == */* ]]; then
|
|
176
|
-
BASE_BRANCH="${source_upstream#*/}"
|
|
177
|
-
fi
|
|
178
|
-
fi
|
|
179
|
-
|
|
180
|
-
if [[ -z "$BASE_BRANCH" ]]; then
|
|
181
|
-
current_branch="$(git -C "$repo_root" rev-parse --abbrev-ref HEAD 2>/dev/null || true)"
|
|
182
|
-
if [[ -n "$current_branch" && "$current_branch" != "HEAD" && "$current_branch" != "$SOURCE_BRANCH" ]]; then
|
|
183
|
-
BASE_BRANCH="$current_branch"
|
|
184
|
-
fi
|
|
185
|
-
fi
|
|
186
|
-
|
|
187
165
|
if [[ -z "$BASE_BRANCH" ]]; then
|
|
188
166
|
BASE_BRANCH="dev"
|
|
189
167
|
fi
|
|
@@ -8,6 +8,8 @@ BASE_BRANCH_EXPLICIT=0
|
|
|
8
8
|
WORKTREE_ROOT_REL=".omx/agent-worktrees"
|
|
9
9
|
OPENSPEC_AUTO_INIT_RAW="${MUSAFETY_OPENSPEC_AUTO_INIT:-false}"
|
|
10
10
|
OPENSPEC_PLAN_SLUG_OVERRIDE="${MUSAFETY_OPENSPEC_PLAN_SLUG:-}"
|
|
11
|
+
OPENSPEC_CHANGE_SLUG_OVERRIDE="${MUSAFETY_OPENSPEC_CHANGE_SLUG:-}"
|
|
12
|
+
OPENSPEC_CAPABILITY_SLUG_OVERRIDE="${MUSAFETY_OPENSPEC_CAPABILITY_SLUG:-}"
|
|
11
13
|
POSITIONAL_ARGS=()
|
|
12
14
|
|
|
13
15
|
while [[ $# -gt 0 ]]; do
|
|
@@ -109,6 +111,25 @@ resolve_openspec_plan_slug() {
|
|
|
109
111
|
sanitize_slug "${branch_name//\//-}" "$task_slug"
|
|
110
112
|
}
|
|
111
113
|
|
|
114
|
+
resolve_openspec_change_slug() {
|
|
115
|
+
local branch_name="$1"
|
|
116
|
+
local task_slug="$2"
|
|
117
|
+
if [[ -n "$OPENSPEC_CHANGE_SLUG_OVERRIDE" ]]; then
|
|
118
|
+
sanitize_slug "$OPENSPEC_CHANGE_SLUG_OVERRIDE" "$task_slug"
|
|
119
|
+
return 0
|
|
120
|
+
fi
|
|
121
|
+
sanitize_slug "${branch_name//\//-}" "$task_slug"
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
resolve_openspec_capability_slug() {
|
|
125
|
+
local task_slug="$1"
|
|
126
|
+
if [[ -n "$OPENSPEC_CAPABILITY_SLUG_OVERRIDE" ]]; then
|
|
127
|
+
sanitize_slug "$OPENSPEC_CAPABILITY_SLUG_OVERRIDE" "$task_slug"
|
|
128
|
+
return 0
|
|
129
|
+
fi
|
|
130
|
+
sanitize_slug "$task_slug" "general-behavior"
|
|
131
|
+
}
|
|
132
|
+
|
|
112
133
|
resolve_active_codex_snapshot_name() {
|
|
113
134
|
local override="${MUSAFETY_CODEX_AUTH_SNAPSHOT:-}"
|
|
114
135
|
if [[ -n "$override" ]]; then
|
|
@@ -250,6 +271,44 @@ initialize_openspec_plan_workspace() {
|
|
|
250
271
|
echo "[agent-branch-start] OpenSpec plan workspace: ${worktree}/openspec/plan/${plan_slug}"
|
|
251
272
|
}
|
|
252
273
|
|
|
274
|
+
initialize_openspec_change_workspace() {
|
|
275
|
+
local repo="$1"
|
|
276
|
+
local worktree="$2"
|
|
277
|
+
local change_slug="$3"
|
|
278
|
+
local capability_slug="$4"
|
|
279
|
+
|
|
280
|
+
hydrate_local_helper_in_worktree "$repo" "$worktree" "scripts/openspec/init-change-workspace.sh"
|
|
281
|
+
|
|
282
|
+
if [[ "$OPENSPEC_AUTO_INIT" -ne 1 ]]; then
|
|
283
|
+
return 0
|
|
284
|
+
fi
|
|
285
|
+
|
|
286
|
+
local openspec_script="${worktree}/scripts/openspec/init-change-workspace.sh"
|
|
287
|
+
if [[ ! -f "$openspec_script" ]]; then
|
|
288
|
+
echo "[agent-branch-start] OpenSpec change init script is missing in sandbox worktree." >&2
|
|
289
|
+
echo "[agent-branch-start] Run 'gx setup --target \"$repo\"' to repair templates, then retry." >&2
|
|
290
|
+
return 1
|
|
291
|
+
fi
|
|
292
|
+
if [[ ! -x "$openspec_script" ]]; then
|
|
293
|
+
chmod +x "$openspec_script" 2>/dev/null || true
|
|
294
|
+
fi
|
|
295
|
+
|
|
296
|
+
local init_output=""
|
|
297
|
+
if ! init_output="$(
|
|
298
|
+
cd "$worktree"
|
|
299
|
+
bash "scripts/openspec/init-change-workspace.sh" "$change_slug" "$capability_slug" 2>&1
|
|
300
|
+
)"; then
|
|
301
|
+
printf '%s\n' "$init_output" >&2
|
|
302
|
+
echo "[agent-branch-start] OpenSpec workspace initialization failed for change '${change_slug}'." >&2
|
|
303
|
+
return 1
|
|
304
|
+
fi
|
|
305
|
+
|
|
306
|
+
if [[ -n "$init_output" ]]; then
|
|
307
|
+
printf '%s\n' "$init_output"
|
|
308
|
+
fi
|
|
309
|
+
echo "[agent-branch-start] OpenSpec change workspace: ${worktree}/openspec/changes/${change_slug}"
|
|
310
|
+
}
|
|
311
|
+
|
|
253
312
|
if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
|
254
313
|
echo "[agent-branch-start] Not inside a git repository." >&2
|
|
255
314
|
exit 1
|
|
@@ -312,6 +371,8 @@ worktree_root="${repo_root}/${WORKTREE_ROOT_REL}"
|
|
|
312
371
|
mkdir -p "$worktree_root"
|
|
313
372
|
worktree_path="${worktree_root}/${branch_name//\//__}"
|
|
314
373
|
openspec_plan_slug="$(resolve_openspec_plan_slug "$branch_name" "$task_slug")"
|
|
374
|
+
openspec_change_slug="$(resolve_openspec_change_slug "$branch_name" "$task_slug")"
|
|
375
|
+
openspec_capability_slug="$(resolve_openspec_capability_slug "$task_slug")"
|
|
315
376
|
|
|
316
377
|
if [[ -e "$worktree_path" ]]; then
|
|
317
378
|
echo "[agent-branch-start] Worktree path already exists: ${worktree_path}" >&2
|
|
@@ -364,15 +425,19 @@ hydrate_local_helper_in_worktree "$repo_root" "$worktree_path" "scripts/codex-ag
|
|
|
364
425
|
hydrate_dependency_dir_symlink_in_worktree "$repo_root" "$worktree_path" "node_modules"
|
|
365
426
|
hydrate_dependency_dir_symlink_in_worktree "$repo_root" "$worktree_path" "apps/frontend/node_modules"
|
|
366
427
|
hydrate_dependency_dir_symlink_in_worktree "$repo_root" "$worktree_path" "apps/backend/node_modules"
|
|
428
|
+
if ! initialize_openspec_change_workspace "$repo_root" "$worktree_path" "$openspec_change_slug" "$openspec_capability_slug"; then
|
|
429
|
+
exit 1
|
|
430
|
+
fi
|
|
367
431
|
if ! initialize_openspec_plan_workspace "$repo_root" "$worktree_path" "$openspec_plan_slug"; then
|
|
368
432
|
exit 1
|
|
369
433
|
fi
|
|
370
434
|
|
|
371
435
|
echo "[agent-branch-start] Created branch: ${branch_name}"
|
|
372
436
|
echo "[agent-branch-start] Worktree: ${worktree_path}"
|
|
437
|
+
echo "[agent-branch-start] OpenSpec change: openspec/changes/${openspec_change_slug}"
|
|
373
438
|
echo "[agent-branch-start] OpenSpec plan: openspec/plan/${openspec_plan_slug}"
|
|
374
439
|
echo "[agent-branch-start] Next steps:"
|
|
375
440
|
echo " cd \"${worktree_path}\""
|
|
376
441
|
echo " python3 scripts/agent-file-locks.py claim --branch \"${branch_name}\" <file...>"
|
|
377
442
|
echo " # implement + commit"
|
|
378
|
-
echo " bash scripts/agent-branch-finish.sh --branch \"${branch_name}\""
|
|
443
|
+
echo " bash scripts/agent-branch-finish.sh --branch \"${branch_name}\" --base dev --via-pr --wait-for-merge"
|