@imdeadpool/guardex 7.0.16 → 7.0.19

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@imdeadpool/guardex",
3
- "version": "7.0.16",
4
- "description": "GitGuardex: hardened multi-agent git guardrails for parallel agent work.",
3
+ "version": "7.0.19",
4
+ "description": "Guardian T-Rex for your multi-agent repo. Isolated worktrees, file locks, and PR-only merges stop parallel Codex & Claude agents from overwriting each other's work. Auto-wires Oh My Codex, Oh My Claude, OpenSpec, and Caveman.",
5
5
  "license": "MIT",
6
6
  "preferGlobal": true,
7
7
  "bin": {
@@ -7,22 +7,25 @@
7
7
  `GUARDEX_ON=0` disables Guardex for that repo.
8
8
  `GUARDEX_ON=1` explicitly enables Guardex for that repo again.
9
9
 
10
- **Isolation.** Every task runs on a dedicated `agent/*` branch + worktree. Start with `scripts/agent-branch-start.sh "<task>" "<agent-name>"`. Treat the base branch (`main`/`dev`) as read-only while an agent branch is active. Never `git checkout <branch>` on a primary working tree (including nested repos); use `git worktree add` instead. The `.githooks/post-checkout` hook auto-reverts primary-branch switches during agent sessions - bypass only with `GUARDEX_ALLOW_PRIMARY_BRANCH_SWITCH=1`.
10
+ **Task-size routing.** Small tasks stay in direct caveman-only mode. For typos, single-file tweaks, one-liners, version bumps, or similarly bounded asks, solve directly and do not escalate into heavy OMX orchestration just because a keyword appears. Treat `quick:`, `simple:`, `tiny:`, `minor:`, `small:`, `just:`, and `only:` as explicit lightweight escape hatches.
11
+ Promote to OMX orchestration only when the task is medium/large: multi-file behavior changes, API/schema work, refactors, migrations, architecture, cross-cutting scope, or long prompts. Heavy OMX modes (`ralph`, `autopilot`, `team`, `ultrawork`, `swarm`, `ralplan`) are for that larger scope. If the task grows while working, upgrade then.
12
+
13
+ **Isolation.** Every task runs on a dedicated `agent/*` branch + worktree. Start with `gx branch start "<task>" "<agent-name>"`. Treat the base branch (`main`/`dev`) as read-only while an agent branch is active. Never `git checkout <branch>` on a primary working tree (including nested repos); use `git worktree add` instead. The `.githooks/post-checkout` hook auto-reverts primary-branch switches during agent sessions - bypass only with `GUARDEX_ALLOW_PRIMARY_BRANCH_SWITCH=1`.
11
14
  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 instead of creating a fresh lane unless the user explicitly redirects scope.
12
15
  Never implement directly on the local/base branch checkout; keep it unchanged and perform all edits in the agent sub-branch/worktree.
13
16
 
14
- **Ownership.** Before editing, claim files: `scripts/agent-file-locks.py claim --branch "<agent-branch>" <file...>`. Before deleting, confirm the path is in your claim. Don't edit outside your scope unless reassigned.
17
+ **Ownership.** Before editing, claim files: `gx locks claim --branch "<agent-branch>" <file...>`. Before deleting, confirm the path is in your claim. Don't edit outside your scope unless reassigned.
15
18
 
16
19
  **Handoff gate.** Post a one-line handoff note (plan/change, owned scope, intended action) before editing. Re-read the latest handoffs before replacing others' code.
17
20
 
18
- **Completion.** Finish with `scripts/agent-branch-finish.sh --branch "<agent-branch>" --via-pr --wait-for-merge --cleanup` (or `gx finish --all`). Task is only complete when: commit pushed, PR URL recorded, state = `MERGED`, sandbox worktree pruned. If anything blocks, append a `BLOCKED:` note and stop - don't half-finish.
21
+ **Completion.** Finish with `gx branch finish --branch "<agent-branch>" --via-pr --wait-for-merge --cleanup` (or `gx finish --all`). Task is only complete when: commit pushed, PR URL recorded, state = `MERGED`, sandbox worktree pruned. If anything blocks, append a `BLOCKED:` note and stop - don't half-finish.
19
22
  OMX completion policy: when a task is done, the agent must commit the task changes, push the agent branch, and create/update a PR before considering the branch complete.
20
23
 
21
24
  **Parallel safety.** Assume other agents edit nearby. Never revert unrelated changes. Report conflicts in the handoff.
22
25
 
23
26
  **Reporting.** Every completion handoff includes: files changed, behavior touched, verification commands + results, risks/follow-ups.
24
27
 
25
- **OpenSpec (when change-driven).** Keep `openspec/changes/<slug>/tasks.md` checkboxes current during work, not batched at the end. Task scaffolds and manual task edits must include an explicit final completion/cleanup section that ends with PR merge + sandbox cleanup (`gx finish --via-pr --wait-for-merge --cleanup` or `scripts/agent-branch-finish.sh ... --cleanup`) and records PR URL + final `MERGED` evidence. Verify specs with `openspec validate --specs` before archive. Don't archive unverified.
28
+ **OpenSpec (when change-driven).** Keep `openspec/changes/<slug>/tasks.md` checkboxes current during work, not batched at the end. Task scaffolds and manual task edits must include an explicit final completion/cleanup section that ends with PR merge + sandbox cleanup (`gx finish --via-pr --wait-for-merge --cleanup` or `gx branch finish ... --cleanup`) and records PR URL + final `MERGED` evidence. Verify specs with `openspec validate --specs` before archive. Don't archive unverified.
26
29
 
27
30
  **Version bumps.** If a change bumps a published version, the same PR updates release notes/changelog.
28
31
  <!-- multiagent-safety:END -->
@@ -8,4 +8,4 @@ Use when repo safety may be broken.
8
8
  `gx status` -> `gx doctor` -> `gx status --strict`
9
9
 
10
10
  Bootstrap: `gx setup`
11
- Ops: `bash scripts/codex-agent.sh "<task>" "<agent>"`, `gx finish --all`, `gx cleanup`
11
+ Ops: `gx branch start "<task>" "<agent>"`, `gx locks claim --branch "<agent-branch>" <file...>`, `gx branch finish --branch "<agent-branch>" --base <base> --via-pr --wait-for-merge --cleanup`, `gx finish --all`, `gx cleanup`
@@ -24,7 +24,7 @@ echo "$BASE_BRANCH"
24
24
  2. Start a dedicated integration sandbox from base:
25
25
 
26
26
  ```sh
27
- bash scripts/agent-branch-start.sh "merge-skill-files-to-${BASE_BRANCH}" "skill-merge" "$BASE_BRANCH"
27
+ gx branch start "merge-skill-files-to-${BASE_BRANCH}" "skill-merge" "$BASE_BRANCH"
28
28
  ```
29
29
 
30
30
  3. Enter the sandbox worktree printed by the command above.
@@ -48,11 +48,11 @@ git diff --name-only
48
48
  ```sh
49
49
  git add .codex/skills templates/codex/skills
50
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
51
+ gx branch finish --branch "$(git rev-parse --abbrev-ref HEAD)" --base "$BASE_BRANCH" --via-pr --wait-for-merge --cleanup
52
52
  ```
53
53
 
54
54
  ## Notes
55
55
 
56
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`.
57
+ - If merge conflicts occur, resolve only within the skill files, then rerun `gx branch finish`.
58
58
  - Do not commit directly on `dev`/`main`; always merge through an agent branch/worktree.
@@ -64,7 +64,7 @@ echo "[agent-primary-branch-guard] Primary checkout switched branches." >&2
64
64
  echo "[agent-primary-branch-guard] from: $prev_branch (protected)" >&2
65
65
  echo "[agent-primary-branch-guard] to: $new_branch" >&2
66
66
  echo "[agent-primary-branch-guard] The primary working tree must stay on its base/protected branch." >&2
67
- echo "[agent-primary-branch-guard] Use 'git worktree add' (or scripts/agent-branch-start.sh) for feature work." >&2
67
+ echo "[agent-primary-branch-guard] Use 'git worktree add' (or gx branch start) for feature work." >&2
68
68
 
69
69
  if [[ "$is_agent" == "1" ]]; then
70
70
  echo "[agent-primary-branch-guard] Agent session detected — reverting to '$prev_branch'." >&2
@@ -32,17 +32,30 @@ if [[ "$branch" != "$base_branch" ]]; then
32
32
  exit 0
33
33
  fi
34
34
 
35
- cli_path="$repo_root/bin/multiagent-safety.js"
36
- if [[ ! -f "$cli_path" ]]; then
35
+ if [[ -n "${GUARDEX_CLI_ENTRY:-}" ]]; then
36
+ node_bin="${GUARDEX_NODE_BIN:-node}"
37
+ if command -v "$node_bin" >/dev/null 2>&1; then
38
+ "$node_bin" "$GUARDEX_CLI_ENTRY" cleanup \
39
+ --target "$repo_root" \
40
+ --base "$base_branch" \
41
+ --include-pr-merged \
42
+ --keep-clean-worktrees >/dev/null 2>&1 || true
43
+ fi
37
44
  exit 0
38
45
  fi
39
46
 
40
- node_bin="${GUARDEX_NODE_BIN:-node}"
41
- if ! command -v "$node_bin" >/dev/null 2>&1; then
42
- exit 0
47
+ cli_bin="${GUARDEX_CLI_BIN:-}"
48
+ if [[ -z "$cli_bin" ]]; then
49
+ if command -v gx >/dev/null 2>&1; then
50
+ cli_bin="gx"
51
+ elif command -v gitguardex >/dev/null 2>&1; then
52
+ cli_bin="gitguardex"
53
+ else
54
+ exit 0
55
+ fi
43
56
  fi
44
57
 
45
- "$node_bin" "$cli_path" cleanup \
58
+ "$cli_bin" cleanup \
46
59
  --target "$repo_root" \
47
60
  --base "$base_branch" \
48
61
  --include-pr-merged \
@@ -13,6 +13,8 @@ repo_root="$(git rev-parse --show-toplevel 2>/dev/null || true)"
13
13
  if [[ -z "$repo_root" ]]; then
14
14
  exit 0
15
15
  fi
16
+ NODE_BIN="${GUARDEX_NODE_BIN:-node}"
17
+ CLI_ENTRY="${GUARDEX_CLI_ENTRY:-}"
16
18
  guardex_env_helper="${repo_root}/scripts/guardex-env.sh"
17
19
  if [[ -f "$guardex_env_helper" ]]; then
18
20
  # shellcheck source=/dev/null
@@ -22,6 +24,23 @@ if declare -F guardex_repo_is_enabled >/dev/null 2>&1 && ! guardex_repo_is_enabl
22
24
  exit 0
23
25
  fi
24
26
 
27
+ run_guardex_cli() {
28
+ if [[ -n "$CLI_ENTRY" ]]; then
29
+ "$NODE_BIN" "$CLI_ENTRY" "$@"
30
+ return $?
31
+ fi
32
+ if command -v gx >/dev/null 2>&1; then
33
+ gx "$@"
34
+ return $?
35
+ fi
36
+ if command -v gitguardex >/dev/null 2>&1; then
37
+ gitguardex "$@"
38
+ return $?
39
+ fi
40
+ echo "[agent-branch-guard] Guardex CLI entrypoint unavailable; rerun via gx." >&2
41
+ return 127
42
+ }
43
+
25
44
  if [[ "${ALLOW_COMMIT_ON_PROTECTED_BRANCH:-0}" == "1" ]]; then
26
45
  exit 0
27
46
  fi
@@ -116,11 +135,11 @@ if [[ "$should_require_codex_agent_branch" == "1" && "${GUARDEX_ALLOW_CODEX_ON_N
116
135
 
117
136
  cat >&2 <<'MSG'
118
137
  [guardex-preedit-guard] Codex edit/commit detected on a protected branch.
119
- GuardeX requires Codex work to run from an isolated agent/* branch.
138
+ GitGuardex requires Codex work to run from an isolated agent/* branch.
120
139
  Start the sub-branch/worktree with:
121
- bash scripts/codex-agent.sh "<task-or-plan>" "<agent-name>"
140
+ gx branch start "<task-or-plan>" "<agent-name>"
122
141
  Or manually:
123
- bash scripts/agent-branch-start.sh "<task-or-plan>" "<agent-name>"
142
+ gx branch start "<task-or-plan>" "<agent-name>"
124
143
  Then commit from the created agent/* branch.
125
144
 
126
145
  Temporary bypass (not recommended):
@@ -132,7 +151,7 @@ MSG
132
151
  cat >&2 <<'MSG'
133
152
  [codex-branch-guard] Codex agent commit blocked on non-agent branch.
134
153
  Use isolated branch/worktree first:
135
- bash scripts/agent-branch-start.sh "<task-or-plan>" "<agent-name>"
154
+ gx branch start "<task-or-plan>" "<agent-name>"
136
155
  Then commit from the created agent/* branch.
137
156
 
138
157
  Temporary bypass (not recommended):
@@ -163,9 +182,9 @@ if [[ "$is_protected_branch" == "1" ]]; then
163
182
  cat >&2 <<'MSG'
164
183
  [agent-branch-guard] Direct commits on protected branches are blocked.
165
184
  Use an agent branch first:
166
- bash scripts/agent-branch-start.sh "<task-or-plan>" "<agent-name>"
185
+ gx branch start "<task-or-plan>" "<agent-name>"
167
186
  After finishing work:
168
- bash scripts/agent-branch-finish.sh
187
+ gx branch finish
169
188
 
170
189
  Temporary bypass (not recommended):
171
190
  ALLOW_COMMIT_ON_PROTECTED_BRANCH=1 git commit ...
@@ -177,7 +196,7 @@ if [[ "$is_agent_session" == "1" && "$branch" != agent/* ]]; then
177
196
  cat >&2 <<'MSG'
178
197
  [agent-branch-guard] Agent commits must run on dedicated agent/* branches.
179
198
  Start an agent branch first:
180
- bash scripts/agent-branch-start.sh "<task-or-plan>" "<agent-name>"
199
+ gx branch start "<task-or-plan>" "<agent-name>"
181
200
  Then commit on that branch.
182
201
 
183
202
  Temporary bypass (not recommended):
@@ -191,15 +210,15 @@ if [[ "$branch" == agent/* ]]; then
191
210
  while IFS= read -r staged_file; do
192
211
  [[ -z "$staged_file" ]] && continue
193
212
  [[ "$staged_file" == ".omx/state/agent-file-locks.json" ]] && continue
194
- python3 scripts/agent-file-locks.py claim --branch "$branch" "$staged_file" >/dev/null 2>&1 || true
213
+ run_guardex_cli locks claim --branch "$branch" "$staged_file" >/dev/null 2>&1 || true
195
214
  done < <(git diff --cached --name-only --diff-filter=ACMRDTUXB)
196
215
  fi
197
216
 
198
- if ! python3 scripts/agent-file-locks.py validate --branch "$branch" --staged; then
217
+ if ! run_guardex_cli locks validate --branch "$branch" --staged; then
199
218
  cat >&2 <<'MSG'
200
219
  [agent-branch-guard] Agent branch commits require file ownership locks.
201
220
  Claim files first:
202
- python3 scripts/agent-file-locks.py claim --branch "$(git rev-parse --abbrev-ref HEAD)" <file...>
221
+ gx locks claim --branch "$(git rev-parse --abbrev-ref HEAD)" <file...>
203
222
  MSG
204
223
  exit 1
205
224
  fi
@@ -9,11 +9,30 @@ DELETE_REMOTE_BRANCH=0
9
9
  DELETE_REMOTE_BRANCH_EXPLICIT=0
10
10
  MERGE_MODE="auto"
11
11
  GH_BIN="${GUARDEX_GH_BIN:-gh}"
12
+ NODE_BIN="${GUARDEX_NODE_BIN:-node}"
13
+ CLI_ENTRY="${GUARDEX_CLI_ENTRY:-}"
12
14
  CLEANUP_AFTER_MERGE_RAW="${GUARDEX_FINISH_CLEANUP:-false}"
13
15
  WAIT_FOR_MERGE_RAW="${GUARDEX_FINISH_WAIT_FOR_MERGE:-false}"
14
16
  WAIT_TIMEOUT_SECONDS_RAW="${GUARDEX_FINISH_WAIT_TIMEOUT_SECONDS:-1800}"
15
17
  WAIT_POLL_SECONDS_RAW="${GUARDEX_FINISH_WAIT_POLL_SECONDS:-10}"
16
18
 
19
+ run_guardex_cli() {
20
+ if [[ -n "$CLI_ENTRY" ]]; then
21
+ "$NODE_BIN" "$CLI_ENTRY" "$@"
22
+ return $?
23
+ fi
24
+ if command -v gx >/dev/null 2>&1; then
25
+ gx "$@"
26
+ return $?
27
+ fi
28
+ if command -v gitguardex >/dev/null 2>&1; then
29
+ gitguardex "$@"
30
+ return $?
31
+ fi
32
+ echo "[agent-branch-finish] Guardex CLI entrypoint unavailable; rerun via gx." >&2
33
+ return 127
34
+ }
35
+
17
36
  normalize_bool() {
18
37
  local raw="${1:-}"
19
38
  local fallback="${2:-0}"
@@ -431,7 +450,7 @@ run_pr_flow() {
431
450
  if [[ -z "$pr_title" ]]; then
432
451
  pr_title="Merge ${SOURCE_BRANCH} into ${BASE_BRANCH}"
433
452
  fi
434
- pr_body="Automated by scripts/agent-branch-finish.sh (PR flow)."
453
+ pr_body="Automated by gx branch finish (PR flow)."
435
454
 
436
455
  "$GH_BIN" pr create \
437
456
  --base "$BASE_BRANCH" \
@@ -517,9 +536,7 @@ if [[ "$PUSH_ENABLED" -eq 1 ]]; then
517
536
  fi
518
537
  fi
519
538
 
520
- if [[ -x "${repo_root}/scripts/agent-file-locks.py" ]]; then
521
- python3 "${repo_root}/scripts/agent-file-locks.py" release --branch "$SOURCE_BRANCH" >/dev/null 2>&1 || true
522
- fi
539
+ run_guardex_cli locks release --branch "$SOURCE_BRANCH" >/dev/null 2>&1 || true
523
540
 
524
541
  base_worktree="$(get_worktree_for_branch "$BASE_BRANCH")"
525
542
  if [[ -n "$base_worktree" ]] && is_clean_worktree "$base_worktree" && [[ "$PUSH_ENABLED" -eq 1 ]]; then
@@ -555,29 +572,25 @@ if [[ "$CLEANUP_AFTER_MERGE" -eq 1 ]]; then
555
572
  fi
556
573
  fi
557
574
 
558
- if [[ -x "${repo_root}/scripts/agent-worktree-prune.sh" ]]; then
559
- prune_args=(--base "$BASE_BRANCH" --only-dirty-worktrees --delete-branches)
560
- if [[ "$DELETE_REMOTE_BRANCH" -eq 1 ]]; then
561
- prune_args+=(--delete-remote-branches)
562
- fi
563
- if ! bash "${repo_root}/scripts/agent-worktree-prune.sh" "${prune_args[@]}"; then
564
- echo "[agent-branch-finish] Warning: automatic worktree prune failed." >&2
565
- echo "[agent-branch-finish] You can run manual cleanup: bash scripts/agent-worktree-prune.sh --base ${BASE_BRANCH} --delete-branches" >&2
566
- fi
575
+ prune_args=(--base "$BASE_BRANCH" --only-dirty-worktrees --delete-branches)
576
+ if [[ "$DELETE_REMOTE_BRANCH" -eq 1 ]]; then
577
+ prune_args+=(--delete-remote-branches)
578
+ fi
579
+ if ! run_guardex_cli worktree prune "${prune_args[@]}"; then
580
+ echo "[agent-branch-finish] Warning: automatic worktree prune failed." >&2
581
+ echo "[agent-branch-finish] You can run manual cleanup: gx cleanup --base ${BASE_BRANCH}" >&2
567
582
  fi
568
583
 
569
584
  echo "[agent-branch-finish] Merged '${SOURCE_BRANCH}' into '${BASE_BRANCH}' via ${merge_status} flow and cleaned source branch/worktree."
570
585
  if [[ "$source_worktree" == "$current_worktree" && "$source_worktree" == "${agent_worktree_root}"/* ]]; then
571
586
  echo "[agent-branch-finish] Current worktree '${source_worktree}' still exists because it is the active shell cwd." >&2
572
- echo "[agent-branch-finish] Leave this directory, then run: bash scripts/agent-worktree-prune.sh --base ${BASE_BRANCH} --delete-branches" >&2
587
+ echo "[agent-branch-finish] Leave this directory, then run: gx cleanup --base ${BASE_BRANCH}" >&2
573
588
  fi
574
589
  else
575
- if [[ -x "${repo_root}/scripts/agent-worktree-prune.sh" ]]; then
576
- if ! bash "${repo_root}/scripts/agent-worktree-prune.sh" --base "$BASE_BRANCH"; then
577
- echo "[agent-branch-finish] Warning: temporary worktree prune failed." >&2
578
- fi
590
+ if ! run_guardex_cli worktree prune --base "$BASE_BRANCH"; then
591
+ echo "[agent-branch-finish] Warning: temporary worktree prune failed." >&2
579
592
  fi
580
593
 
581
594
  echo "[agent-branch-finish] Merged '${SOURCE_BRANCH}' into '${BASE_BRANCH}' via ${merge_status} flow and kept source branch/worktree."
582
- echo "[agent-branch-finish] Cleanup later with: bash scripts/agent-worktree-prune.sh --base ${BASE_BRANCH} --delete-branches --delete-remote-branches"
595
+ echo "[agent-branch-finish] Cleanup later with: gx cleanup --base ${BASE_BRANCH}"
583
596
  fi
@@ -6,18 +6,37 @@ BASE_BRANCH_EXPLICIT=0
6
6
  TARGET_BRANCH=""
7
7
  TASK_NAME=""
8
8
  AGENT_NAME="${GUARDEX_MERGE_AGENT_NAME:-codex}"
9
+ NODE_BIN="${GUARDEX_NODE_BIN:-node}"
10
+ CLI_ENTRY="${GUARDEX_CLI_ENTRY:-}"
9
11
  declare -a SOURCE_BRANCHES=()
10
12
 
11
13
  usage() {
12
14
  cat <<'EOF'
13
- Usage: scripts/agent-branch-merge.sh --branch <agent/...> [--branch <agent/...> ...] [--into <agent/...>] [--task <task>] [--agent <agent>] [--base <branch>]
15
+ Usage: gx branch merge --branch <agent/...> [--branch <agent/...> ...] [--into <agent/...>] [--task <task>] [--agent <agent>] [--base <branch>]
14
16
 
15
17
  Examples:
16
- bash scripts/agent-branch-merge.sh --branch agent/codex/ui-a --branch agent/codex/ui-b
17
- bash scripts/agent-branch-merge.sh --into agent/codex/owner-lane --branch agent/codex/helper-a --branch agent/codex/helper-b
18
+ gx branch merge --branch agent/codex/ui-a --branch agent/codex/ui-b
19
+ gx branch merge --into agent/codex/owner-lane --branch agent/codex/helper-a --branch agent/codex/helper-b
18
20
  EOF
19
21
  }
20
22
 
23
+ run_guardex_cli() {
24
+ if [[ -n "$CLI_ENTRY" ]]; then
25
+ "$NODE_BIN" "$CLI_ENTRY" "$@"
26
+ return $?
27
+ fi
28
+ if command -v gx >/dev/null 2>&1; then
29
+ gx "$@"
30
+ return $?
31
+ fi
32
+ if command -v gitguardex >/dev/null 2>&1; then
33
+ gitguardex "$@"
34
+ return $?
35
+ fi
36
+ echo "[agent-branch-merge] Guardex CLI entrypoint unavailable; rerun via gx." >&2
37
+ return 127
38
+ }
39
+
21
40
  sanitize_slug() {
22
41
  local raw="$1"
23
42
  local fallback="${2:-merge-agent-branches}"
@@ -262,7 +281,7 @@ if [[ -z "$TARGET_BRANCH" ]]; then
262
281
  start_output=""
263
282
  if ! start_output="$(
264
283
  cd "$repo_root"
265
- env GUARDEX_OPENSPEC_AUTO_INIT=1 bash "scripts/agent-branch-start.sh" "$TASK_NAME" "$AGENT_NAME" "$BASE_BRANCH" 2>&1
284
+ GUARDEX_OPENSPEC_AUTO_INIT=1 run_guardex_cli branch start "$TASK_NAME" "$AGENT_NAME" "$BASE_BRANCH" 2>&1
266
285
  )"; then
267
286
  printf '%s\n' "$start_output" >&2
268
287
  exit 1
@@ -418,4 +437,4 @@ echo "[agent-branch-merge] Merge sequence complete for '${TARGET_BRANCH}'."
418
437
  if [[ "$target_created" -eq 1 ]]; then
419
438
  echo "[agent-branch-merge] Review and verify in '${target_worktree}', then finish the integration branch when ready."
420
439
  fi
421
- echo "[agent-branch-merge] Next step: bash scripts/agent-branch-finish.sh --branch \"${TARGET_BRANCH}\" --base \"${BASE_BRANCH}\" --via-pr --wait-for-merge --cleanup"
440
+ echo "[agent-branch-merge] Next step: gx branch finish --branch \"${TARGET_BRANCH}\" --base \"${BASE_BRANCH}\" --via-pr --wait-for-merge --cleanup"
@@ -7,6 +7,8 @@ BASE_BRANCH=""
7
7
  BASE_BRANCH_EXPLICIT=0
8
8
  WORKTREE_ROOT_REL=""
9
9
  WORKTREE_ROOT_EXPLICIT=0
10
+ NODE_BIN="${GUARDEX_NODE_BIN:-node}"
11
+ CLI_ENTRY="${GUARDEX_CLI_ENTRY:-}"
10
12
  OPENSPEC_AUTO_INIT_RAW="${GUARDEX_OPENSPEC_AUTO_INIT:-false}"
11
13
  OPENSPEC_PLAN_SLUG_OVERRIDE="${GUARDEX_OPENSPEC_PLAN_SLUG:-}"
12
14
  OPENSPEC_CHANGE_SLUG_OVERRIDE="${GUARDEX_OPENSPEC_CHANGE_SLUG:-}"
@@ -15,6 +17,23 @@ OPENSPEC_MASTERPLAN_LABEL_RAW="${GUARDEX_OPENSPEC_MASTERPLAN_LABEL-masterplan}"
15
17
  PRINT_NAME_ONLY=0
16
18
  POSITIONAL_ARGS=()
17
19
 
20
+ run_guardex_cli() {
21
+ if [[ -n "$CLI_ENTRY" ]]; then
22
+ "$NODE_BIN" "$CLI_ENTRY" "$@"
23
+ return $?
24
+ fi
25
+ if command -v gx >/dev/null 2>&1; then
26
+ gx "$@"
27
+ return $?
28
+ fi
29
+ if command -v gitguardex >/dev/null 2>&1; then
30
+ gitguardex "$@"
31
+ return $?
32
+ fi
33
+ echo "[agent-branch-start] Guardex CLI entrypoint unavailable; rerun via gx." >&2
34
+ return 127
35
+ }
36
+
18
37
  while [[ $# -gt 0 ]]; do
19
38
  case "$1" in
20
39
  --task)
@@ -385,26 +404,14 @@ initialize_openspec_plan_workspace() {
385
404
  local worktree="$2"
386
405
  local plan_slug="$3"
387
406
 
388
- hydrate_local_helper_in_worktree "$repo" "$worktree" "scripts/openspec/init-plan-workspace.sh"
389
-
390
407
  if [[ "$OPENSPEC_AUTO_INIT" -ne 1 ]]; then
391
408
  return 0
392
409
  fi
393
410
 
394
- local openspec_script="${worktree}/scripts/openspec/init-plan-workspace.sh"
395
- if [[ ! -f "$openspec_script" ]]; then
396
- echo "[agent-branch-start] OpenSpec init script is missing in sandbox worktree." >&2
397
- echo "[agent-branch-start] Run 'gx setup --target \"$repo\"' to repair templates, then retry." >&2
398
- return 1
399
- fi
400
- if [[ ! -x "$openspec_script" ]]; then
401
- chmod +x "$openspec_script" 2>/dev/null || true
402
- fi
403
-
404
411
  local init_output=""
405
412
  if ! init_output="$(
406
413
  cd "$worktree"
407
- bash "scripts/openspec/init-plan-workspace.sh" "$plan_slug" 2>&1
414
+ run_guardex_cli internal run-shell planInit "$plan_slug" 2>&1
408
415
  )"; then
409
416
  printf '%s\n' "$init_output" >&2
410
417
  echo "[agent-branch-start] OpenSpec workspace initialization failed for plan '${plan_slug}'." >&2
@@ -423,26 +430,14 @@ initialize_openspec_change_workspace() {
423
430
  local change_slug="$3"
424
431
  local capability_slug="$4"
425
432
 
426
- hydrate_local_helper_in_worktree "$repo" "$worktree" "scripts/openspec/init-change-workspace.sh"
427
-
428
433
  if [[ "$OPENSPEC_AUTO_INIT" -ne 1 ]]; then
429
434
  return 0
430
435
  fi
431
436
 
432
- local openspec_script="${worktree}/scripts/openspec/init-change-workspace.sh"
433
- if [[ ! -f "$openspec_script" ]]; then
434
- echo "[agent-branch-start] OpenSpec change init script is missing in sandbox worktree." >&2
435
- echo "[agent-branch-start] Run 'gx setup --target \"$repo\"' to repair templates, then retry." >&2
436
- return 1
437
- fi
438
- if [[ ! -x "$openspec_script" ]]; then
439
- chmod +x "$openspec_script" 2>/dev/null || true
440
- fi
441
-
442
437
  local init_output=""
443
438
  if ! init_output="$(
444
439
  cd "$worktree"
445
- bash "scripts/openspec/init-change-workspace.sh" "$change_slug" "$capability_slug" 2>&1
440
+ run_guardex_cli internal run-shell changeInit "$change_slug" "$capability_slug" 2>&1
446
441
  )"; then
447
442
  printf '%s\n' "$init_output" >&2
448
443
  echo "[agent-branch-start] OpenSpec workspace initialization failed for change '${change_slug}'." >&2
@@ -592,7 +587,6 @@ if [[ -n "$auto_transfer_stash_ref" ]]; then
592
587
  fi
593
588
  fi
594
589
 
595
- hydrate_local_helper_in_worktree "$repo_root" "$worktree_path" "scripts/codex-agent.sh"
596
590
  hydrate_dependency_dir_symlink_in_worktree "$repo_root" "$worktree_path" "node_modules"
597
591
  hydrate_dependency_dir_symlink_in_worktree "$repo_root" "$worktree_path" "apps/frontend/node_modules"
598
592
  hydrate_dependency_dir_symlink_in_worktree "$repo_root" "$worktree_path" "apps/backend/node_modules"
@@ -609,6 +603,6 @@ echo "[agent-branch-start] OpenSpec change: openspec/changes/${openspec_change_s
609
603
  echo "[agent-branch-start] OpenSpec plan: openspec/plan/${openspec_plan_slug}"
610
604
  echo "[agent-branch-start] Next steps:"
611
605
  echo " cd \"${worktree_path}\""
612
- echo " python3 scripts/agent-file-locks.py claim --branch \"${branch_name}\" <file...>"
606
+ echo " gx locks claim --branch \"${branch_name}\" <file...>"
613
607
  echo " # implement + commit"
614
- echo " bash scripts/agent-branch-finish.sh --branch \"${branch_name}\" --base ${BASE_BRANCH} --via-pr --wait-for-merge"
608
+ echo " gx branch finish --branch \"${branch_name}\" --base ${BASE_BRANCH} --via-pr --wait-for-merge"
@@ -2,11 +2,11 @@
2
2
  """Per-file lock registry for concurrent agent branches.
3
3
 
4
4
  Usage examples:
5
- python3 scripts/agent-file-locks.py claim --branch agent/a path/to/file1 path/to/file2
6
- python3 scripts/agent-file-locks.py claim --branch agent/a --allow-delete path/to/obsolete-file
7
- python3 scripts/agent-file-locks.py allow-delete --branch agent/a path/to/obsolete-file
8
- python3 scripts/agent-file-locks.py validate --branch agent/a --staged
9
- python3 scripts/agent-file-locks.py release --branch agent/a
5
+ gx locks claim --branch agent/a path/to/file1 path/to/file2
6
+ gx locks claim --branch agent/a --allow-delete path/to/obsolete-file
7
+ gx locks allow-delete --branch agent/a path/to/obsolete-file
8
+ gx locks validate --branch agent/a --staged
9
+ gx locks release --branch agent/a
10
10
  """
11
11
 
12
12
  from __future__ import annotations
@@ -27,9 +27,9 @@ CRITICAL_GUARDRAIL_PATHS = {
27
27
  'AGENTS.md',
28
28
  '.githooks/pre-commit',
29
29
  '.githooks/pre-push',
30
- 'scripts/agent-branch-start.sh',
31
- 'scripts/agent-branch-finish.sh',
32
- 'scripts/agent-file-locks.py',
30
+ '.githooks/post-merge',
31
+ '.githooks/post-checkout',
32
+ 'scripts/guardex-env.sh',
33
33
  }
34
34
  ALLOW_GUARDRAIL_DELETE_ENV = 'AGENT_ALLOW_GUARDRAIL_DELETE'
35
35
 
@@ -326,11 +326,11 @@ def cmd_validate(args: argparse.Namespace, repo_root: Path) -> int:
326
326
  print(f' - {file_path}', file=sys.stderr)
327
327
  print(' Approve explicit deletions with one of:', file=sys.stderr)
328
328
  print(
329
- f' python3 scripts/agent-file-locks.py claim --branch "{args.branch}" --allow-delete <file...>',
329
+ f' gx locks claim --branch "{args.branch}" --allow-delete <file...>',
330
330
  file=sys.stderr,
331
331
  )
332
332
  print(
333
- f' python3 scripts/agent-file-locks.py allow-delete --branch "{args.branch}" <file...>',
333
+ f' gx locks allow-delete --branch "{args.branch}" <file...>',
334
334
  file=sys.stderr,
335
335
  )
336
336
  if guardrail_delete_blocked:
@@ -343,7 +343,7 @@ def cmd_validate(args: argparse.Namespace, repo_root: Path) -> int:
343
343
  )
344
344
 
345
345
  print('\nClaim files with:', file=sys.stderr)
346
- print(f' python3 scripts/agent-file-locks.py claim --branch "{args.branch}" <file...>', file=sys.stderr)
346
+ print(f' gx locks claim --branch "{args.branch}" <file...>', file=sys.stderr)
347
347
  return 1
348
348
 
349
349
 
@@ -0,0 +1,110 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require('node:fs');
4
+ const path = require('node:path');
5
+
6
+ function resolveSessionSchemaModule() {
7
+ const candidates = [
8
+ path.resolve(__dirname, '..', 'vscode', 'guardex-active-agents', 'session-schema.js'),
9
+ path.resolve(__dirname, '..', 'templates', 'vscode', 'guardex-active-agents', 'session-schema.js'),
10
+ ];
11
+
12
+ for (const candidate of candidates) {
13
+ if (fs.existsSync(candidate)) {
14
+ return require(candidate);
15
+ }
16
+ }
17
+
18
+ throw new Error('Could not resolve Guardex active-agent session schema module.');
19
+ }
20
+
21
+ const sessionSchema = resolveSessionSchemaModule();
22
+
23
+ function usage() {
24
+ return (
25
+ 'Usage:\n' +
26
+ ' node scripts/agent-session-state.js start --repo <path> --branch <name> --task <task> --agent <agent> --worktree <path> --pid <pid> --cli <name>\n' +
27
+ ' node scripts/agent-session-state.js stop --repo <path> --branch <name>\n'
28
+ );
29
+ }
30
+
31
+ function parseOptions(argv) {
32
+ const options = {};
33
+ for (let index = 0; index < argv.length; index += 1) {
34
+ const token = argv[index];
35
+ if (!token.startsWith('--')) {
36
+ throw new Error(`Unexpected argument: ${token}`);
37
+ }
38
+ const key = token.slice(2);
39
+ const value = argv[index + 1];
40
+ if (!value || value.startsWith('--')) {
41
+ throw new Error(`Missing value for --${key}`);
42
+ }
43
+ options[key] = value;
44
+ index += 1;
45
+ }
46
+ return options;
47
+ }
48
+
49
+ function requireOption(options, key) {
50
+ const value = options[key];
51
+ if (!value) {
52
+ throw new Error(`Missing required option --${key}`);
53
+ }
54
+ return value;
55
+ }
56
+
57
+ function writeSessionRecord(options) {
58
+ const repoRoot = requireOption(options, 'repo');
59
+ const branch = requireOption(options, 'branch');
60
+ const record = sessionSchema.buildSessionRecord({
61
+ repoRoot,
62
+ branch,
63
+ taskName: requireOption(options, 'task'),
64
+ agentName: requireOption(options, 'agent'),
65
+ worktreePath: requireOption(options, 'worktree'),
66
+ pid: requireOption(options, 'pid'),
67
+ cliName: requireOption(options, 'cli'),
68
+ });
69
+
70
+ const targetPath = sessionSchema.sessionFilePathForBranch(repoRoot, branch);
71
+ fs.mkdirSync(path.dirname(targetPath), { recursive: true });
72
+ fs.writeFileSync(targetPath, `${JSON.stringify(record, null, 2)}\n`, 'utf8');
73
+ }
74
+
75
+ function removeSessionRecord(options) {
76
+ const repoRoot = requireOption(options, 'repo');
77
+ const branch = requireOption(options, 'branch');
78
+ const targetPath = sessionSchema.sessionFilePathForBranch(repoRoot, branch);
79
+ if (fs.existsSync(targetPath)) {
80
+ fs.unlinkSync(targetPath);
81
+ }
82
+ }
83
+
84
+ function main() {
85
+ const [command, ...rest] = process.argv.slice(2);
86
+ if (!command || ['-h', '--help', 'help'].includes(command)) {
87
+ process.stdout.write(usage());
88
+ return;
89
+ }
90
+
91
+ const options = parseOptions(rest);
92
+ if (command === 'start') {
93
+ writeSessionRecord(options);
94
+ return;
95
+ }
96
+ if (command === 'stop') {
97
+ removeSessionRecord(options);
98
+ return;
99
+ }
100
+
101
+ throw new Error(`Unknown subcommand: ${command}`);
102
+ }
103
+
104
+ try {
105
+ main();
106
+ } catch (error) {
107
+ process.stderr.write(`[guardex-active-session] ${error.message}\n`);
108
+ process.stderr.write(usage());
109
+ process.exitCode = 1;
110
+ }