@imdeadpool/guardex 5.0.0
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/CONTRIBUTING.md +25 -0
- package/LICENSE +21 -0
- package/README.md +428 -0
- package/SECURITY.md +28 -0
- package/bin/multiagent-safety.js +2478 -0
- package/package.json +68 -0
- package/templates/AGENTS.multiagent-safety.md +60 -0
- package/templates/claude/commands/guardex.md +18 -0
- package/templates/codex/skills/guardex/SKILL.md +36 -0
- package/templates/githooks/pre-commit +178 -0
- package/templates/githooks/pre-push +57 -0
- package/templates/scripts/agent-branch-finish.sh +389 -0
- package/templates/scripts/agent-branch-start.sh +289 -0
- package/templates/scripts/agent-file-locks.py +406 -0
- package/templates/scripts/agent-worktree-prune.sh +155 -0
- package/templates/scripts/codex-agent.sh +94 -0
- package/templates/scripts/install-agent-git-hooks.sh +21 -0
- package/templates/scripts/openspec/init-plan-workspace.sh +118 -0
package/package.json
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@imdeadpool/guardex",
|
|
3
|
+
"version": "5.0.0",
|
|
4
|
+
"description": "GuardeX: the Guardian T-Rex for your repo, with hardened multi-agent git guardrails.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"preferGlobal": true,
|
|
7
|
+
"bin": {
|
|
8
|
+
"guardex": "bin/multiagent-safety.js",
|
|
9
|
+
"gx": "bin/multiagent-safety.js",
|
|
10
|
+
"musafety": "bin/multiagent-safety.js",
|
|
11
|
+
"multiagent-safety": "bin/multiagent-safety.js"
|
|
12
|
+
},
|
|
13
|
+
"scripts": {
|
|
14
|
+
"test": "node --test test/*.test.js",
|
|
15
|
+
"agent:codex": "bash ./scripts/codex-agent.sh",
|
|
16
|
+
"agent:branch:start": "bash ./scripts/agent-branch-start.sh",
|
|
17
|
+
"agent:branch:finish": "bash ./scripts/agent-branch-finish.sh",
|
|
18
|
+
"agent:cleanup": "bash ./scripts/agent-worktree-prune.sh --base dev",
|
|
19
|
+
"agent:hooks:install": "bash ./scripts/install-agent-git-hooks.sh",
|
|
20
|
+
"agent:locks:claim": "python3 ./scripts/agent-file-locks.py claim",
|
|
21
|
+
"agent:locks:allow-delete": "python3 ./scripts/agent-file-locks.py allow-delete",
|
|
22
|
+
"agent:locks:release": "python3 ./scripts/agent-file-locks.py release",
|
|
23
|
+
"agent:locks:status": "python3 ./scripts/agent-file-locks.py status",
|
|
24
|
+
"agent:plan:init": "bash ./scripts/openspec/init-plan-workspace.sh",
|
|
25
|
+
"agent:protect:list": "gx protect list",
|
|
26
|
+
"agent:branch:sync": "gx sync",
|
|
27
|
+
"agent:branch:sync:check": "gx sync --check",
|
|
28
|
+
"agent:safety:setup": "gx setup",
|
|
29
|
+
"agent:safety:scan": "gx scan",
|
|
30
|
+
"agent:safety:fix": "gx fix",
|
|
31
|
+
"agent:safety:doctor": "gx doctor"
|
|
32
|
+
},
|
|
33
|
+
"engines": {
|
|
34
|
+
"node": ">=18"
|
|
35
|
+
},
|
|
36
|
+
"files": [
|
|
37
|
+
"bin",
|
|
38
|
+
"templates",
|
|
39
|
+
"README.md",
|
|
40
|
+
"LICENSE",
|
|
41
|
+
"SECURITY.md",
|
|
42
|
+
"CONTRIBUTING.md"
|
|
43
|
+
],
|
|
44
|
+
"keywords": [
|
|
45
|
+
"guardex",
|
|
46
|
+
"multi-agent",
|
|
47
|
+
"git-hooks",
|
|
48
|
+
"branch-guard",
|
|
49
|
+
"agent-safety",
|
|
50
|
+
"codex"
|
|
51
|
+
],
|
|
52
|
+
"author": "recodeecom",
|
|
53
|
+
"repository": {
|
|
54
|
+
"type": "git",
|
|
55
|
+
"url": "git+https://github.com/recodeecom/multiagent-safety.git"
|
|
56
|
+
},
|
|
57
|
+
"bugs": {
|
|
58
|
+
"url": "https://github.com/recodeecom/multiagent-safety/issues"
|
|
59
|
+
},
|
|
60
|
+
"homepage": "https://github.com/recodeecom/multiagent-safety#readme",
|
|
61
|
+
"funding": "https://github.com/sponsors/recodeecom",
|
|
62
|
+
"publishConfig": {
|
|
63
|
+
"access": "public"
|
|
64
|
+
},
|
|
65
|
+
"devDependencies": {
|
|
66
|
+
"fast-check": "^3.23.2"
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
<!-- multiagent-safety:START -->
|
|
2
|
+
## Multi-Agent Execution Contract (multiagent-safety)
|
|
3
|
+
|
|
4
|
+
0. Session plan comment + read gate (required)
|
|
5
|
+
|
|
6
|
+
- Before editing, each agent must post a short session comment/handoff note that includes:
|
|
7
|
+
- plan/change name (or checkpoint id),
|
|
8
|
+
- owned files/scope,
|
|
9
|
+
- intended action.
|
|
10
|
+
- Before deleting/replacing code, each agent must read the latest session comments/handoffs first and confirm the target code is in their owned scope.
|
|
11
|
+
- If ownership is unclear or overlaps, stop that edit, post a blocker comment, and let the leader/integrator reassign scope.
|
|
12
|
+
- For git isolation, each agent must start on a dedicated branch via `scripts/agent-branch-start.sh "<task-or-plan>" "<agent-name>"`.
|
|
13
|
+
- Agent completion must use `scripts/agent-branch-finish.sh` (direct merge to base when allowed; auto PR fallback for protected bases, then cleanup after merge).
|
|
14
|
+
|
|
15
|
+
1. Explicit ownership before edits
|
|
16
|
+
|
|
17
|
+
- Assign each agent clear file/module ownership.
|
|
18
|
+
- Do not edit files outside your assigned scope unless the leader reassigns ownership.
|
|
19
|
+
|
|
20
|
+
2. Preserve parallel safety
|
|
21
|
+
|
|
22
|
+
- Assume other agents are editing nearby code concurrently.
|
|
23
|
+
- Never revert unrelated changes authored by others.
|
|
24
|
+
- If another change conflicts with your approach, adapt and report the conflict in handoff.
|
|
25
|
+
|
|
26
|
+
3. Verify before completion
|
|
27
|
+
|
|
28
|
+
- Run required local checks for the area you changed.
|
|
29
|
+
- Do not mark work complete without command output evidence.
|
|
30
|
+
|
|
31
|
+
4. Required handoff format (every agent)
|
|
32
|
+
|
|
33
|
+
- Files changed
|
|
34
|
+
- Behavior touched
|
|
35
|
+
- Verification commands + results
|
|
36
|
+
- Risks / follow-ups
|
|
37
|
+
|
|
38
|
+
## OpenSpec Plan Workspace (recommended)
|
|
39
|
+
|
|
40
|
+
When work needs a durable planning phase, scaffold a plan workspace before implementation:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
bash scripts/openspec/init-plan-workspace.sh "<plan-slug>"
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Expected shape:
|
|
47
|
+
|
|
48
|
+
```text
|
|
49
|
+
openspec/plan/<plan-slug>/
|
|
50
|
+
summary.md
|
|
51
|
+
checkpoints.md
|
|
52
|
+
planner/plan.md
|
|
53
|
+
planner/tasks.md
|
|
54
|
+
architect/tasks.md
|
|
55
|
+
critic/tasks.md
|
|
56
|
+
executor/tasks.md
|
|
57
|
+
writer/tasks.md
|
|
58
|
+
verifier/tasks.md
|
|
59
|
+
```
|
|
60
|
+
<!-- multiagent-safety:END -->
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# /guardex
|
|
2
|
+
|
|
3
|
+
Run a GuardeX check-and-repair workflow for the current repository.
|
|
4
|
+
|
|
5
|
+
## Steps
|
|
6
|
+
|
|
7
|
+
1. Run `gx status`.
|
|
8
|
+
2. If status is degraded, run `gx doctor`.
|
|
9
|
+
3. If still degraded, run `gx scan` and summarize each finding with a fix.
|
|
10
|
+
4. Report final verdict as one of:
|
|
11
|
+
- `Repo is guarded`
|
|
12
|
+
- `Repo is not guarded` (include blockers)
|
|
13
|
+
|
|
14
|
+
## Style
|
|
15
|
+
|
|
16
|
+
- Keep output short and operational.
|
|
17
|
+
- Include exact commands you executed.
|
|
18
|
+
- Prefer concrete next actions over generic advice.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: guardex
|
|
3
|
+
description: "Use when you need to check, repair, or bootstrap multi-agent safety guardrails in this repository."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# GuardeX (Codex skill)
|
|
7
|
+
|
|
8
|
+
Use this skill whenever branch safety, lock ownership, or guardrail setup may be broken.
|
|
9
|
+
|
|
10
|
+
## Fast path
|
|
11
|
+
|
|
12
|
+
1. Run `gx status`.
|
|
13
|
+
2. If repo safety is degraded, run `gx doctor`.
|
|
14
|
+
3. If issues remain, run `gx scan` and address the findings.
|
|
15
|
+
|
|
16
|
+
## Setup path
|
|
17
|
+
|
|
18
|
+
If guardrails are missing entirely, run:
|
|
19
|
+
|
|
20
|
+
```sh
|
|
21
|
+
gx setup
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Then verify:
|
|
25
|
+
|
|
26
|
+
```sh
|
|
27
|
+
gx status
|
|
28
|
+
gx scan
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Operator notes
|
|
32
|
+
|
|
33
|
+
- Prefer `gx doctor` for one-step repair + verification.
|
|
34
|
+
- Keep agent work isolated (`agent/*` branches + lock claims).
|
|
35
|
+
- For one-command Codex sandbox startup, use `bash scripts/codex-agent.sh "<task>" "<agent-name>"`.
|
|
36
|
+
- Do not bypass protected branch safeguards unless explicitly required.
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
branch="$(git rev-parse --abbrev-ref HEAD 2>/dev/null || true)"
|
|
5
|
+
if [[ -z "$branch" || "$branch" == "HEAD" ]]; then
|
|
6
|
+
branch="$(git symbolic-ref --quiet --short HEAD 2>/dev/null || true)"
|
|
7
|
+
fi
|
|
8
|
+
if [[ -z "$branch" ]]; then
|
|
9
|
+
exit 0
|
|
10
|
+
fi
|
|
11
|
+
|
|
12
|
+
if [[ "${ALLOW_COMMIT_ON_PROTECTED_BRANCH:-0}" == "1" ]]; then
|
|
13
|
+
exit 0
|
|
14
|
+
fi
|
|
15
|
+
|
|
16
|
+
is_unborn_branch=0
|
|
17
|
+
if ! git rev-parse --verify HEAD >/dev/null 2>&1; then
|
|
18
|
+
is_unborn_branch=1
|
|
19
|
+
fi
|
|
20
|
+
|
|
21
|
+
is_codex_session=0
|
|
22
|
+
if [[ -n "${CODEX_THREAD_ID:-}" || -n "${OMX_SESSION_ID:-}" || "${CODEX_CI:-0}" == "1" ]]; then
|
|
23
|
+
is_codex_session=1
|
|
24
|
+
fi
|
|
25
|
+
|
|
26
|
+
protected_branches_raw="${MUSAFETY_PROTECTED_BRANCHES:-$(git config --get multiagent.protectedBranches || true)}"
|
|
27
|
+
if [[ -z "$protected_branches_raw" ]]; then
|
|
28
|
+
protected_branches_raw="dev main master"
|
|
29
|
+
fi
|
|
30
|
+
protected_branches_raw="${protected_branches_raw//,/ }"
|
|
31
|
+
|
|
32
|
+
is_protected_branch=0
|
|
33
|
+
for protected_branch in $protected_branches_raw; do
|
|
34
|
+
if [[ "$branch" == "$protected_branch" ]]; then
|
|
35
|
+
is_protected_branch=1
|
|
36
|
+
break
|
|
37
|
+
fi
|
|
38
|
+
done
|
|
39
|
+
|
|
40
|
+
codex_require_agent_branch_raw="${MUSAFETY_CODEX_REQUIRE_AGENT_BRANCH:-$(git config --get multiagent.codexRequireAgentBranch || true)}"
|
|
41
|
+
if [[ -z "$codex_require_agent_branch_raw" ]]; then
|
|
42
|
+
codex_require_agent_branch_raw="true"
|
|
43
|
+
fi
|
|
44
|
+
codex_require_agent_branch="$(printf '%s' "$codex_require_agent_branch_raw" | tr '[:upper:]' '[:lower:]')"
|
|
45
|
+
|
|
46
|
+
should_require_codex_agent_branch=0
|
|
47
|
+
case "$codex_require_agent_branch" in
|
|
48
|
+
1|true|yes|on) should_require_codex_agent_branch=1 ;;
|
|
49
|
+
0|false|no|off) should_require_codex_agent_branch=0 ;;
|
|
50
|
+
*) should_require_codex_agent_branch=1 ;;
|
|
51
|
+
esac
|
|
52
|
+
|
|
53
|
+
if [[ "$should_require_codex_agent_branch" == "1" && "${MUSAFETY_ALLOW_CODEX_ON_NON_AGENT:-0}" != "1" ]]; then
|
|
54
|
+
if [[ "$is_codex_session" == "1" && "$branch" != agent/* ]]; then
|
|
55
|
+
if [[ "$is_protected_branch" == "1" ]]; then
|
|
56
|
+
cat >&2 <<'MSG'
|
|
57
|
+
[guardex-preedit-guard] Codex edit/commit detected on a protected branch.
|
|
58
|
+
GuardeX requires Codex work to run from an isolated agent/* branch.
|
|
59
|
+
Start the sub-branch/worktree with:
|
|
60
|
+
bash scripts/codex-agent.sh "<task-or-plan>" "<agent-name>"
|
|
61
|
+
Or manually:
|
|
62
|
+
bash scripts/agent-branch-start.sh "<task-or-plan>" "<agent-name>"
|
|
63
|
+
Then commit from the created agent/* branch.
|
|
64
|
+
|
|
65
|
+
Temporary bypass (not recommended):
|
|
66
|
+
MUSAFETY_ALLOW_CODEX_ON_NON_AGENT=1 git commit ...
|
|
67
|
+
MSG
|
|
68
|
+
exit 1
|
|
69
|
+
fi
|
|
70
|
+
|
|
71
|
+
cat >&2 <<'MSG'
|
|
72
|
+
[codex-branch-guard] Codex agent commit blocked on non-agent branch.
|
|
73
|
+
Use isolated branch/worktree first:
|
|
74
|
+
bash scripts/agent-branch-start.sh "<task-or-plan>" "<agent-name>"
|
|
75
|
+
Then commit from the created agent/* branch.
|
|
76
|
+
|
|
77
|
+
Temporary bypass (not recommended):
|
|
78
|
+
MUSAFETY_ALLOW_CODEX_ON_NON_AGENT=1 git commit ...
|
|
79
|
+
Disable this rule for a repo (not recommended):
|
|
80
|
+
git config multiagent.codexRequireAgentBranch false
|
|
81
|
+
MSG
|
|
82
|
+
exit 1
|
|
83
|
+
fi
|
|
84
|
+
fi
|
|
85
|
+
|
|
86
|
+
if [[ "$is_protected_branch" == "1" ]]; then
|
|
87
|
+
if [[ "$is_unborn_branch" == "1" && "$is_codex_session" != "1" ]]; then
|
|
88
|
+
exit 0
|
|
89
|
+
fi
|
|
90
|
+
|
|
91
|
+
git_dir="$(git rev-parse --git-dir)"
|
|
92
|
+
if [[ -f "$git_dir/MERGE_HEAD" ]]; then
|
|
93
|
+
exit 0
|
|
94
|
+
fi
|
|
95
|
+
|
|
96
|
+
cat >&2 <<'MSG'
|
|
97
|
+
[agent-branch-guard] Direct commits on protected branches are blocked.
|
|
98
|
+
Use an agent branch first:
|
|
99
|
+
bash scripts/agent-branch-start.sh "<task-or-plan>" "<agent-name>"
|
|
100
|
+
After finishing work:
|
|
101
|
+
bash scripts/agent-branch-finish.sh
|
|
102
|
+
|
|
103
|
+
Temporary bypass (not recommended):
|
|
104
|
+
ALLOW_COMMIT_ON_PROTECTED_BRANCH=1 git commit ...
|
|
105
|
+
MSG
|
|
106
|
+
exit 1
|
|
107
|
+
fi
|
|
108
|
+
|
|
109
|
+
if [[ "$branch" == agent/* ]]; then
|
|
110
|
+
if ! python3 scripts/agent-file-locks.py validate --branch "$branch" --staged; then
|
|
111
|
+
cat >&2 <<'MSG'
|
|
112
|
+
[agent-branch-guard] Agent branch commits require file ownership locks.
|
|
113
|
+
Claim files first:
|
|
114
|
+
python3 scripts/agent-file-locks.py claim --branch "$(git rev-parse --abbrev-ref HEAD)" <file...>
|
|
115
|
+
MSG
|
|
116
|
+
exit 1
|
|
117
|
+
fi
|
|
118
|
+
|
|
119
|
+
require_sync_before_commit_raw="$(git config --get multiagent.sync.requireBeforeCommit || true)"
|
|
120
|
+
if [[ -z "$require_sync_before_commit_raw" ]]; then
|
|
121
|
+
require_sync_before_commit_raw="false"
|
|
122
|
+
fi
|
|
123
|
+
require_sync_before_commit="$(printf '%s' "$require_sync_before_commit_raw" | tr '[:upper:]' '[:lower:]')"
|
|
124
|
+
|
|
125
|
+
should_require_sync=0
|
|
126
|
+
case "$require_sync_before_commit" in
|
|
127
|
+
1|true|yes|on) should_require_sync=1 ;;
|
|
128
|
+
0|false|no|off) should_require_sync=0 ;;
|
|
129
|
+
*) should_require_sync=0 ;;
|
|
130
|
+
esac
|
|
131
|
+
|
|
132
|
+
if [[ "$should_require_sync" == "1" ]]; then
|
|
133
|
+
base_branch="$(git config --get multiagent.baseBranch || true)"
|
|
134
|
+
if [[ -z "$base_branch" ]]; then
|
|
135
|
+
base_branch="dev"
|
|
136
|
+
fi
|
|
137
|
+
|
|
138
|
+
max_behind_raw="$(git config --get multiagent.sync.maxBehindCommits || true)"
|
|
139
|
+
if [[ -z "$max_behind_raw" ]]; then
|
|
140
|
+
max_behind_raw="0"
|
|
141
|
+
fi
|
|
142
|
+
if [[ ! "$max_behind_raw" =~ ^[0-9]+$ ]]; then
|
|
143
|
+
echo "[agent-sync-guard] Invalid multiagent.sync.maxBehindCommits value: ${max_behind_raw}" >&2
|
|
144
|
+
echo "[agent-sync-guard] Expected non-negative integer. Example: git config multiagent.sync.maxBehindCommits 0" >&2
|
|
145
|
+
exit 1
|
|
146
|
+
fi
|
|
147
|
+
|
|
148
|
+
if ! git fetch origin "$base_branch" --quiet >/dev/null 2>&1; then
|
|
149
|
+
echo "[agent-sync-guard] Unable to fetch origin/${base_branch} while commit sync gate is enabled." >&2
|
|
150
|
+
echo "[agent-sync-guard] Disable gate temporarily with: git config multiagent.sync.requireBeforeCommit false" >&2
|
|
151
|
+
exit 1
|
|
152
|
+
fi
|
|
153
|
+
|
|
154
|
+
if ! git show-ref --verify --quiet "refs/remotes/origin/${base_branch}"; then
|
|
155
|
+
echo "[agent-sync-guard] Remote base branch not found: origin/${base_branch}" >&2
|
|
156
|
+
exit 1
|
|
157
|
+
fi
|
|
158
|
+
|
|
159
|
+
behind_count="$(git rev-list --left-right --count "${branch}...origin/${base_branch}" 2>/dev/null | awk '{print $2}')"
|
|
160
|
+
behind_count="${behind_count:-0}"
|
|
161
|
+
max_behind="${max_behind_raw}"
|
|
162
|
+
|
|
163
|
+
if [[ "$behind_count" -gt "$max_behind" ]]; then
|
|
164
|
+
cat >&2 <<MSG
|
|
165
|
+
[agent-sync-guard] Commit blocked: '${branch}' is behind origin/${base_branch} by ${behind_count} commit(s) (max allowed: ${max_behind}).
|
|
166
|
+
Run:
|
|
167
|
+
gx sync --base ${base_branch}
|
|
168
|
+
Or relax threshold:
|
|
169
|
+
git config multiagent.sync.maxBehindCommits <n>
|
|
170
|
+
MSG
|
|
171
|
+
exit 1
|
|
172
|
+
fi
|
|
173
|
+
fi
|
|
174
|
+
fi
|
|
175
|
+
|
|
176
|
+
if command -v pre-commit >/dev/null 2>&1 && [[ -f .pre-commit-config.yaml ]]; then
|
|
177
|
+
pre-commit run --hook-stage pre-commit
|
|
178
|
+
fi
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
if [[ "${ALLOW_PUSH_ON_PROTECTED_BRANCH:-0}" == "1" || "${ALLOW_COMMIT_ON_PROTECTED_BRANCH:-0}" == "1" ]]; then
|
|
5
|
+
exit 0
|
|
6
|
+
fi
|
|
7
|
+
|
|
8
|
+
is_vscode_git_context=0
|
|
9
|
+
if [[ -n "${VSCODE_GIT_IPC_HANDLE:-}" || -n "${VSCODE_GIT_ASKPASS_NODE:-}" || -n "${VSCODE_IPC_HOOK_CLI:-}" ]]; then
|
|
10
|
+
is_vscode_git_context=1
|
|
11
|
+
fi
|
|
12
|
+
|
|
13
|
+
if [[ "$is_vscode_git_context" == "1" ]]; then
|
|
14
|
+
exit 0
|
|
15
|
+
fi
|
|
16
|
+
|
|
17
|
+
protected_branches_raw="${MUSAFETY_PROTECTED_BRANCHES:-$(git config --get multiagent.protectedBranches || true)}"
|
|
18
|
+
if [[ -z "$protected_branches_raw" ]]; then
|
|
19
|
+
protected_branches_raw="dev main master"
|
|
20
|
+
fi
|
|
21
|
+
protected_branches_raw="${protected_branches_raw//,/ }"
|
|
22
|
+
|
|
23
|
+
is_protected_branch() {
|
|
24
|
+
local branch="$1"
|
|
25
|
+
for protected_branch in $protected_branches_raw; do
|
|
26
|
+
if [[ "$branch" == "$protected_branch" ]]; then
|
|
27
|
+
return 0
|
|
28
|
+
fi
|
|
29
|
+
done
|
|
30
|
+
return 1
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
blocked_refs=()
|
|
34
|
+
while IFS=' ' read -r local_ref local_sha remote_ref remote_sha; do
|
|
35
|
+
if [[ -z "${remote_ref:-}" || "$remote_ref" != refs/heads/* ]]; then
|
|
36
|
+
continue
|
|
37
|
+
fi
|
|
38
|
+
|
|
39
|
+
remote_branch="${remote_ref#refs/heads/}"
|
|
40
|
+
if is_protected_branch "$remote_branch"; then
|
|
41
|
+
blocked_refs+=("$remote_branch")
|
|
42
|
+
fi
|
|
43
|
+
done
|
|
44
|
+
|
|
45
|
+
if [[ "${#blocked_refs[@]}" -gt 0 ]]; then
|
|
46
|
+
{
|
|
47
|
+
echo "[agent-branch-guard] Push to protected branch blocked outside VS Code Git context."
|
|
48
|
+
echo "[agent-branch-guard] Protected target(s): ${blocked_refs[*]}"
|
|
49
|
+
echo "[agent-branch-guard] Use VS Code Source Control for protected-branch push, or push from an agent branch and merge via PR."
|
|
50
|
+
echo
|
|
51
|
+
echo "Temporary bypass (not recommended):"
|
|
52
|
+
echo " ALLOW_PUSH_ON_PROTECTED_BRANCH=1 git push ..."
|
|
53
|
+
} >&2
|
|
54
|
+
exit 1
|
|
55
|
+
fi
|
|
56
|
+
|
|
57
|
+
exit 0
|