@imdeadpool/guardex 6.0.0 → 6.0.1
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/package.json +3 -1
- package/templates/githooks/post-merge +3 -39
- package/templates/githooks/pre-commit +193 -27
- package/templates/githooks/pre-push +0 -0
- package/templates/scripts/agent-branch-finish.sh +702 -70
- package/templates/scripts/agent-branch-start.sh +877 -76
- package/templates/scripts/agent-worktree-prune.sh +353 -65
- package/templates/scripts/codex-agent.sh +238 -626
- package/templates/scripts/install-agent-git-hooks.sh +27 -4
- package/templates/scripts/openspec/init-change-workspace.sh +50 -4
- package/templates/scripts/openspec/init-plan-workspace.sh +495 -48
- package/templates/scripts/review-bot-watch.sh +11 -11
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@imdeadpool/guardex",
|
|
3
|
-
"version": "6.0.
|
|
3
|
+
"version": "6.0.1",
|
|
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,
|
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
"multiagent-safety": "bin/multiagent-safety.js"
|
|
11
11
|
},
|
|
12
12
|
"scripts": {
|
|
13
|
+
"prepack": "node pack-helpers/dereference-templates.mjs",
|
|
14
|
+
"postpack": "git checkout -- templates",
|
|
13
15
|
"test": "node --test test/*.test.js",
|
|
14
16
|
"agent:codex": "bash ./scripts/codex-agent.sh",
|
|
15
17
|
"agent:branch:start": "bash ./scripts/agent-branch-start.sh",
|
|
@@ -1,43 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
set -euo pipefail
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
# Auto-sync agent worktrees when the base branch is updated in this worktree.
|
|
5
|
+
if [[ -x "scripts/agent-sync-on-base-update.sh" ]]; then
|
|
6
|
+
bash scripts/agent-sync-on-base-update.sh --quiet || true
|
|
6
7
|
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="${GUARDEX_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="${GUARDEX_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
|
|
@@ -9,6 +9,12 @@ if [[ -z "$branch" ]]; then
|
|
|
9
9
|
exit 0
|
|
10
10
|
fi
|
|
11
11
|
|
|
12
|
+
git_dir="$(git rev-parse --git-dir 2>/dev/null || true)"
|
|
13
|
+
is_linked_worktree=0
|
|
14
|
+
if [[ -n "$git_dir" && "$git_dir" == *"/worktrees/"* ]]; then
|
|
15
|
+
is_linked_worktree=1
|
|
16
|
+
fi
|
|
17
|
+
|
|
12
18
|
if [[ "${ALLOW_COMMIT_ON_PROTECTED_BRANCH:-0}" == "1" ]]; then
|
|
13
19
|
exit 0
|
|
14
20
|
fi
|
|
@@ -24,7 +30,7 @@ if [[ -n "${CODEX_THREAD_ID:-}" || -n "${OMX_SESSION_ID:-}" || "${CODEX_CI:-0}"
|
|
|
24
30
|
fi
|
|
25
31
|
|
|
26
32
|
is_vscode_git_context=0
|
|
27
|
-
if [[ -n "${VSCODE_GIT_IPC_HANDLE:-}" || -n "${VSCODE_GIT_ASKPASS_NODE:-}" || -n "${VSCODE_IPC_HOOK_CLI:-}" ]]; then
|
|
33
|
+
if [[ -n "${VSCODE_GIT_IPC_HANDLE:-}" || -n "${VSCODE_GIT_ASKPASS_NODE:-}" || -n "${VSCODE_IPC_HOOK_CLI:-}" || "${TERM_PROGRAM:-}" == "vscode" ]]; then
|
|
28
34
|
is_vscode_git_context=1
|
|
29
35
|
fi
|
|
30
36
|
|
|
@@ -68,6 +74,163 @@ case "$codex_require_agent_branch" in
|
|
|
68
74
|
*) should_require_codex_agent_branch=1 ;;
|
|
69
75
|
esac
|
|
70
76
|
|
|
77
|
+
sanitize_slug() {
|
|
78
|
+
local raw="$1"
|
|
79
|
+
local fallback="${2:-task}"
|
|
80
|
+
local slug
|
|
81
|
+
slug="$(printf '%s' "$raw" | tr '[:upper:]' '[:lower:]' | sed -E 's/[^a-z0-9]+/-/g; s/^-+//; s/-+$//; s/-{2,}/-/g')"
|
|
82
|
+
if [[ -z "$slug" ]]; then
|
|
83
|
+
slug="$fallback"
|
|
84
|
+
fi
|
|
85
|
+
printf '%s' "$slug"
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
resolve_agent_branch_base() {
|
|
89
|
+
local branch_name="$1"
|
|
90
|
+
git config --get "branch.${branch_name}.guardexBase" || true
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
is_helper_agent_branch() {
|
|
94
|
+
local branch_name="$1"
|
|
95
|
+
local base_branch=""
|
|
96
|
+
base_branch="$(resolve_agent_branch_base "$branch_name")"
|
|
97
|
+
[[ "$base_branch" == agent/* ]]
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
ensure_agent_branch_openspec_workspace() {
|
|
101
|
+
local branch_name="$1"
|
|
102
|
+
local change_slug change_dir specs_dir capability_slug branch_base
|
|
103
|
+
local missing_workspace=0
|
|
104
|
+
local openspec_script="scripts/openspec/init-change-workspace.sh"
|
|
105
|
+
|
|
106
|
+
branch_base="$(git config --get "branch.${branch_name}.guardexBase" || true)"
|
|
107
|
+
if [[ "$branch_base" == agent/* ]]; then
|
|
108
|
+
echo "[agent-openspec-guard] Skipping OpenSpec change workspace bootstrap for helper branch '${branch_name}' (base '${branch_base}')."
|
|
109
|
+
return 0
|
|
110
|
+
fi
|
|
111
|
+
|
|
112
|
+
change_slug="$(sanitize_slug "${branch_name//\//-}" "change")"
|
|
113
|
+
change_dir="openspec/changes/${change_slug}"
|
|
114
|
+
specs_dir="${change_dir}/specs"
|
|
115
|
+
|
|
116
|
+
if [[ ! -f "${change_dir}/.openspec.yaml" || ! -f "${change_dir}/proposal.md" || ! -f "${change_dir}/tasks.md" ]]; then
|
|
117
|
+
missing_workspace=1
|
|
118
|
+
elif [[ ! -d "$specs_dir" ]] || ! find "$specs_dir" -mindepth 2 -maxdepth 2 -type f -name spec.md | grep -q .; then
|
|
119
|
+
missing_workspace=1
|
|
120
|
+
fi
|
|
121
|
+
|
|
122
|
+
if [[ "$missing_workspace" -ne 1 ]]; then
|
|
123
|
+
return 0
|
|
124
|
+
fi
|
|
125
|
+
|
|
126
|
+
if [[ ! -f "$openspec_script" ]]; then
|
|
127
|
+
cat >&2 <<MSG
|
|
128
|
+
[agent-openspec-guard] Missing OpenSpec change workspace for '${branch_name}'.
|
|
129
|
+
Expected path:
|
|
130
|
+
${change_dir}
|
|
131
|
+
Cannot auto-initialize because '${openspec_script}' is missing.
|
|
132
|
+
Run:
|
|
133
|
+
gx setup --target "$(git rev-parse --show-toplevel)"
|
|
134
|
+
bash scripts/openspec/init-change-workspace.sh "${change_slug}" "<capability-slug>"
|
|
135
|
+
MSG
|
|
136
|
+
exit 1
|
|
137
|
+
fi
|
|
138
|
+
|
|
139
|
+
if [[ ! -x "$openspec_script" ]]; then
|
|
140
|
+
chmod +x "$openspec_script" 2>/dev/null || true
|
|
141
|
+
fi
|
|
142
|
+
|
|
143
|
+
capability_slug="$(sanitize_slug "${branch_name##*/}" "general-behavior")"
|
|
144
|
+
init_output=""
|
|
145
|
+
if ! init_output="$(bash "$openspec_script" "$change_slug" "$capability_slug" 2>&1)"; then
|
|
146
|
+
printf '%s\n' "$init_output" >&2
|
|
147
|
+
cat >&2 <<MSG
|
|
148
|
+
[agent-openspec-guard] OpenSpec auto-init failed for '${branch_name}'.
|
|
149
|
+
Run manually:
|
|
150
|
+
bash scripts/openspec/init-change-workspace.sh "${change_slug}" "${capability_slug}"
|
|
151
|
+
MSG
|
|
152
|
+
exit 1
|
|
153
|
+
fi
|
|
154
|
+
|
|
155
|
+
if [[ -n "$init_output" ]]; then
|
|
156
|
+
printf '%s\n' "$init_output"
|
|
157
|
+
fi
|
|
158
|
+
|
|
159
|
+
git add "$change_dir"
|
|
160
|
+
|
|
161
|
+
if [[ -x scripts/agent-file-locks.py ]]; then
|
|
162
|
+
staged_openspec="$(git diff --cached --name-only -- "$change_dir" | sed '/^$/d' || true)"
|
|
163
|
+
if [[ -n "$staged_openspec" ]]; then
|
|
164
|
+
mapfile -t openspec_files < <(printf '%s\n' "$staged_openspec")
|
|
165
|
+
python3 scripts/agent-file-locks.py claim --branch "$branch_name" "${openspec_files[@]}" >/dev/null 2>&1 || true
|
|
166
|
+
fi
|
|
167
|
+
fi
|
|
168
|
+
|
|
169
|
+
echo "[agent-openspec-guard] Bootstrapped OpenSpec change workspace: ${change_dir}"
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
should_auto_reroute_protected_branch() {
|
|
173
|
+
local raw="${GUARDEX_AUTO_REROUTE_PROTECTED_BRANCH:-$(git config --get multiagent.autoRerouteProtectedBranch || true)}"
|
|
174
|
+
local lowered=""
|
|
175
|
+
if [[ -z "$raw" ]]; then
|
|
176
|
+
raw="true"
|
|
177
|
+
fi
|
|
178
|
+
lowered="$(printf '%s' "$raw" | tr '[:upper:]' '[:lower:]')"
|
|
179
|
+
case "$lowered" in
|
|
180
|
+
1|true|yes|on) return 0 ;;
|
|
181
|
+
*) return 1 ;;
|
|
182
|
+
esac
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
auto_reroute_protected_branch_commit() {
|
|
186
|
+
local branch_name="$1"
|
|
187
|
+
local starter_script="scripts/agent-branch-start.sh"
|
|
188
|
+
local task_name="${GUARDEX_AUTO_REROUTE_TASK_NAME:-protected-branch-commit-reroute}"
|
|
189
|
+
local agent_name="${GUARDEX_AUTO_REROUTE_AGENT_NAME:-auto-reroute}"
|
|
190
|
+
local changed_paths=""
|
|
191
|
+
local start_output=""
|
|
192
|
+
local start_status=0
|
|
193
|
+
local new_branch=""
|
|
194
|
+
local worktree_path=""
|
|
195
|
+
|
|
196
|
+
changed_paths="$({
|
|
197
|
+
git diff --name-only
|
|
198
|
+
git diff --cached --name-only
|
|
199
|
+
git ls-files --others --exclude-standard
|
|
200
|
+
} | sed '/^$/d' | sort -u)"
|
|
201
|
+
|
|
202
|
+
if [[ -z "$changed_paths" ]]; then
|
|
203
|
+
return 1
|
|
204
|
+
fi
|
|
205
|
+
|
|
206
|
+
if [[ ! -x "$starter_script" ]]; then
|
|
207
|
+
return 1
|
|
208
|
+
fi
|
|
209
|
+
|
|
210
|
+
set +e
|
|
211
|
+
start_output="$(bash "$starter_script" "$task_name" "$agent_name" "$branch_name" 2>&1)"
|
|
212
|
+
start_status=$?
|
|
213
|
+
set -e
|
|
214
|
+
|
|
215
|
+
if [[ "$start_status" -ne 0 ]]; then
|
|
216
|
+
printf '%s\n' "$start_output" >&2
|
|
217
|
+
return 1
|
|
218
|
+
fi
|
|
219
|
+
|
|
220
|
+
new_branch="$(printf '%s\n' "$start_output" | sed -n 's/^\[agent-branch-start\] Created branch: //p' | tail -n 1)"
|
|
221
|
+
worktree_path="$(printf '%s\n' "$start_output" | sed -n 's/^\[agent-branch-start\] Worktree: //p' | tail -n 1)"
|
|
222
|
+
|
|
223
|
+
printf '%s\n' "$start_output" >&2
|
|
224
|
+
cat >&2 <<MSG
|
|
225
|
+
[agent-branch-guard] Protected-branch commit rerouted automatically.
|
|
226
|
+
Changes from '${branch_name}' were moved to:
|
|
227
|
+
branch: ${new_branch:-<see output>}
|
|
228
|
+
worktree: ${worktree_path:-<see output>}
|
|
229
|
+
Continue work and commit from that agent worktree.
|
|
230
|
+
MSG
|
|
231
|
+
return 0
|
|
232
|
+
}
|
|
233
|
+
|
|
71
234
|
is_codex_managed_only_commit_on_protected=0
|
|
72
235
|
if [[ "$is_codex_session" == "1" && "$is_protected_branch" == "1" ]]; then
|
|
73
236
|
deleted_paths="$(git diff --cached --name-only --diff-filter=D)"
|
|
@@ -123,17 +286,32 @@ MSG
|
|
|
123
286
|
fi
|
|
124
287
|
fi
|
|
125
288
|
|
|
126
|
-
if [[ "$
|
|
127
|
-
if [[ "$
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
289
|
+
if [[ "$is_codex_session" == "1" && "$branch" == agent/* ]]; then
|
|
290
|
+
if [[ "$is_linked_worktree" != "1" && "${GUARDEX_ALLOW_CODEX_ON_PRIMARY_WORKTREE:-0}" != "1" ]]; then
|
|
291
|
+
cat >&2 <<'MSG'
|
|
292
|
+
[codex-worktree-guard] Codex agent commits are blocked from the primary checkout.
|
|
293
|
+
Use a linked agent worktree for agent/* branches:
|
|
294
|
+
bash scripts/agent-branch-start.sh "<task-or-plan>" "<agent-name>"
|
|
295
|
+
Then commit from the printed worktree path.
|
|
296
|
+
|
|
297
|
+
Temporary bypass (not recommended):
|
|
298
|
+
GUARDEX_ALLOW_CODEX_ON_PRIMARY_WORKTREE=1 git commit ...
|
|
299
|
+
MSG
|
|
300
|
+
exit 1
|
|
131
301
|
fi
|
|
302
|
+
fi
|
|
132
303
|
|
|
133
|
-
|
|
304
|
+
if [[ "$is_protected_branch" == "1" ]]; then
|
|
305
|
+
if [[ "$is_codex_session" != "1" && "$is_vscode_git_context" == "1" && "$allow_vscode_protected_branch_writes" == "1" ]]; then
|
|
134
306
|
exit 0
|
|
135
307
|
fi
|
|
136
308
|
|
|
309
|
+
if should_auto_reroute_protected_branch; then
|
|
310
|
+
if auto_reroute_protected_branch_commit "$branch"; then
|
|
311
|
+
exit 1
|
|
312
|
+
fi
|
|
313
|
+
fi
|
|
314
|
+
|
|
137
315
|
git_dir="$(git rev-parse --git-dir)"
|
|
138
316
|
if [[ -f "$git_dir/MERGE_HEAD" ]]; then
|
|
139
317
|
exit 0
|
|
@@ -145,8 +323,10 @@ Use an agent branch first:
|
|
|
145
323
|
bash scripts/agent-branch-start.sh "<task-or-plan>" "<agent-name>"
|
|
146
324
|
After finishing work:
|
|
147
325
|
bash scripts/agent-branch-finish.sh
|
|
326
|
+
Auto-reroute can be disabled (not recommended):
|
|
327
|
+
GUARDEX_AUTO_REROUTE_PROTECTED_BRANCH=0 git commit ...
|
|
148
328
|
|
|
149
|
-
Optional repo
|
|
329
|
+
Optional repo override for manual VS Code protected-branch commits:
|
|
150
330
|
git config multiagent.allowVscodeProtectedBranchWrites true
|
|
151
331
|
|
|
152
332
|
Temporary bypass (not recommended):
|
|
@@ -155,26 +335,12 @@ MSG
|
|
|
155
335
|
exit 1
|
|
156
336
|
fi
|
|
157
337
|
|
|
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.
|
|
164
|
-
|
|
165
|
-
Temporary bypass (not recommended):
|
|
166
|
-
ALLOW_COMMIT_ON_PROTECTED_BRANCH=1 git commit ...
|
|
167
|
-
MSG
|
|
168
|
-
exit 1
|
|
169
|
-
fi
|
|
170
|
-
|
|
171
338
|
if [[ "$branch" == agent/* ]]; then
|
|
172
|
-
if
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
done < <(git diff --cached --name-only --diff-filter=ACMRDTUXB)
|
|
339
|
+
if is_helper_agent_branch "$branch"; then
|
|
340
|
+
helper_base="$(resolve_agent_branch_base "$branch")"
|
|
341
|
+
echo "[agent-openspec-guard] Skipping OpenSpec change workspace bootstrap for helper branch '${branch}' (base '${helper_base}')."
|
|
342
|
+
else
|
|
343
|
+
ensure_agent_branch_openspec_workspace "$branch"
|
|
178
344
|
fi
|
|
179
345
|
|
|
180
346
|
if ! python3 scripts/agent-file-locks.py validate --branch "$branch" --staged; then
|
|
File without changes
|