@imdeadpool/guardex 7.0.18 → 7.0.20

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.
@@ -0,0 +1,223 @@
1
+ function createToolchainApi(deps) {
2
+ const {
3
+ TOOL_NAME,
4
+ NPM_BIN,
5
+ NPX_BIN,
6
+ packageJson,
7
+ OPENSPEC_PACKAGE,
8
+ OPENSPEC_BIN,
9
+ GLOBAL_TOOLCHAIN_PACKAGES,
10
+ parseAutoApproval,
11
+ isInteractiveTerminal,
12
+ promptYesNoStrict,
13
+ run,
14
+ checkForGuardexUpdate,
15
+ printUpdateAvailableBanner,
16
+ readInstalledGuardexVersion,
17
+ restartIntoUpdatedGuardex,
18
+ checkForOpenSpecPackageUpdate,
19
+ printOpenSpecUpdateAvailableBanner,
20
+ resolveGlobalInstallApproval,
21
+ detectGlobalToolchainPackages,
22
+ detectOptionalLocalCompanionTools,
23
+ formatGlobalToolchainServiceName,
24
+ askGlobalInstallForMissing,
25
+ } = deps;
26
+
27
+ function maybeSelfUpdateBeforeStatus() {
28
+ const check = checkForGuardexUpdate();
29
+ if (!check.checked || !check.updateAvailable) {
30
+ return;
31
+ }
32
+
33
+ printUpdateAvailableBanner(check.current, check.latest);
34
+
35
+ const autoApproval = parseAutoApproval('GUARDEX_AUTO_UPDATE_APPROVAL');
36
+ const interactive = isInteractiveTerminal();
37
+
38
+ if (!interactive && autoApproval == null) {
39
+ console.log(`[${TOOL_NAME}] Non-interactive shell; skipping auto-update prompt.`);
40
+ return;
41
+ }
42
+
43
+ const shouldUpdate = interactive
44
+ ? promptYesNoStrict(
45
+ `Update now? (${NPM_BIN} i -g ${packageJson.name}@latest)`,
46
+ )
47
+ : autoApproval;
48
+
49
+ if (!shouldUpdate) {
50
+ console.log(`[${TOOL_NAME}] Skipped update.`);
51
+ return;
52
+ }
53
+
54
+ const installResult = run(NPM_BIN, ['i', '-g', `${packageJson.name}@latest`], { stdio: 'inherit' });
55
+ if (installResult.status !== 0) {
56
+ console.log(`[${TOOL_NAME}] ⚠️ Update failed. You can retry manually.`);
57
+ return;
58
+ }
59
+
60
+ const postInstallVersion = readInstalledGuardexVersion();
61
+ if (postInstallVersion != null && postInstallVersion !== check.latest) {
62
+ console.log(
63
+ `[${TOOL_NAME}] Installed version is still ${postInstallVersion} (expected ${check.latest}). ` +
64
+ `Retrying with pinned version ${check.latest}…`,
65
+ );
66
+ const pinnedResult = run(
67
+ NPM_BIN,
68
+ ['i', '-g', `${packageJson.name}@${check.latest}`],
69
+ { stdio: 'inherit' },
70
+ );
71
+ if (pinnedResult.status !== 0) {
72
+ console.log(
73
+ `[${TOOL_NAME}] ⚠️ Pinned retry failed. Run manually: ${NPM_BIN} i -g ${packageJson.name}@${check.latest}`,
74
+ );
75
+ return;
76
+ }
77
+ const pinnedVersion = readInstalledGuardexVersion();
78
+ if (pinnedVersion != null && pinnedVersion !== check.latest) {
79
+ console.log(
80
+ `[${TOOL_NAME}] ⚠️ On-disk version still ${pinnedVersion} after pinned retry. ` +
81
+ `Investigate: ${NPM_BIN} root -g && ${NPM_BIN} cache verify`,
82
+ );
83
+ return;
84
+ }
85
+ }
86
+
87
+ console.log(`[${TOOL_NAME}] ✅ Updated to latest published version.`);
88
+ restartIntoUpdatedGuardex(check.latest);
89
+ }
90
+
91
+ function maybeOpenSpecUpdateBeforeStatus() {
92
+ const check = checkForOpenSpecPackageUpdate();
93
+ if (!check.checked || !check.updateAvailable) {
94
+ return;
95
+ }
96
+
97
+ printOpenSpecUpdateAvailableBanner(check.current, check.latest);
98
+
99
+ const autoApproval = parseAutoApproval('GUARDEX_AUTO_OPENSPEC_UPDATE_APPROVAL');
100
+ const interactive = isInteractiveTerminal();
101
+
102
+ if (!interactive && autoApproval == null) {
103
+ console.log(`[${TOOL_NAME}] Non-interactive shell; skipping OpenSpec update prompt.`);
104
+ return;
105
+ }
106
+
107
+ const shouldUpdate = interactive
108
+ ? promptYesNoStrict(
109
+ `Update OpenSpec now? (${NPM_BIN} i -g ${OPENSPEC_PACKAGE}@latest && ${OPENSPEC_BIN} update)`,
110
+ )
111
+ : autoApproval;
112
+
113
+ if (!shouldUpdate) {
114
+ console.log(`[${TOOL_NAME}] Skipped OpenSpec update.`);
115
+ return;
116
+ }
117
+
118
+ const installResult = run(NPM_BIN, ['i', '-g', `${OPENSPEC_PACKAGE}@latest`], { stdio: 'inherit' });
119
+ if (installResult.status !== 0) {
120
+ console.log(`[${TOOL_NAME}] ⚠️ OpenSpec npm install failed. You can retry manually.`);
121
+ return;
122
+ }
123
+
124
+ const toolUpdateResult = run(OPENSPEC_BIN, ['update'], { stdio: 'inherit' });
125
+ if (toolUpdateResult.status !== 0) {
126
+ console.log(`[${TOOL_NAME}] ⚠️ OpenSpec tool update failed. Run '${OPENSPEC_BIN} update' manually.`);
127
+ return;
128
+ }
129
+
130
+ console.log(`[${TOOL_NAME}] ✅ OpenSpec updated to latest package and tool plugins refreshed.`);
131
+ }
132
+
133
+ function installGlobalToolchain(options) {
134
+ const approval = resolveGlobalInstallApproval(options);
135
+ if (approval.source === 'flag' && !approval.approved) {
136
+ return {
137
+ status: 'skipped',
138
+ reason: approval.source,
139
+ missingPackages: [],
140
+ missingLocalTools: [],
141
+ };
142
+ }
143
+
144
+ if (options.dryRun) {
145
+ return { status: 'dry-run-skip' };
146
+ }
147
+
148
+ const detection = detectGlobalToolchainPackages();
149
+ const localCompanionTools = detectOptionalLocalCompanionTools();
150
+ if (!detection.ok) {
151
+ console.log(`[${TOOL_NAME}] ⚠️ Could not detect global packages: ${detection.error}`);
152
+ } else {
153
+ if (detection.installed.length > 0) {
154
+ console.log(
155
+ `[${TOOL_NAME}] Already installed globally: ` +
156
+ `${detection.installed.map((pkg) => formatGlobalToolchainServiceName(pkg)).join(', ')}`,
157
+ );
158
+ }
159
+ const installedLocalTools = localCompanionTools
160
+ .filter((tool) => tool.status === 'active')
161
+ .map((tool) => tool.name);
162
+ if (installedLocalTools.length > 0) {
163
+ console.log(`[${TOOL_NAME}] Already installed locally: ${installedLocalTools.join(', ')}`);
164
+ }
165
+ if (detection.missing.length === 0 && localCompanionTools.every((tool) => tool.status === 'active')) {
166
+ return { status: 'already-installed' };
167
+ }
168
+ }
169
+
170
+ const missingPackages = detection.ok ? detection.missing : [...GLOBAL_TOOLCHAIN_PACKAGES];
171
+ const missingLocalTools = localCompanionTools.filter((tool) => tool.status !== 'active');
172
+ const installApproval = askGlobalInstallForMissing(options, missingPackages, missingLocalTools);
173
+ if (!installApproval.approved) {
174
+ return {
175
+ status: 'skipped',
176
+ reason: installApproval.source,
177
+ missingPackages,
178
+ missingLocalTools,
179
+ };
180
+ }
181
+
182
+ const installed = [];
183
+ if (missingPackages.length > 0) {
184
+ console.log(
185
+ `[${TOOL_NAME}] Installing global toolchain: npm i -g ${missingPackages.join(' ')}`,
186
+ );
187
+ const result = run(NPM_BIN, ['i', '-g', ...missingPackages], { stdio: 'inherit' });
188
+ if (result.status !== 0) {
189
+ const stderr = (result.stderr || '').trim();
190
+ return {
191
+ status: 'failed',
192
+ reason: stderr || 'npm global install failed',
193
+ };
194
+ }
195
+ installed.push(...missingPackages);
196
+ }
197
+
198
+ for (const tool of missingLocalTools) {
199
+ console.log(`[${TOOL_NAME}] Installing local companion tool: ${tool.installCommand}`);
200
+ const result = run(NPX_BIN, tool.installArgs, { stdio: 'inherit' });
201
+ if (result.status !== 0) {
202
+ const stderr = (result.stderr || '').trim();
203
+ return {
204
+ status: 'failed',
205
+ reason: stderr || `${tool.name} install failed`,
206
+ };
207
+ }
208
+ installed.push(tool.name);
209
+ }
210
+
211
+ return { status: 'installed', packages: installed };
212
+ }
213
+
214
+ return {
215
+ maybeSelfUpdateBeforeStatus,
216
+ maybeOpenSpecUpdateBeforeStatus,
217
+ installGlobalToolchain,
218
+ };
219
+ }
220
+
221
+ module.exports = {
222
+ createToolchainApi,
223
+ };
@@ -7,6 +7,9 @@
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
+ **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
+
10
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.
@@ -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.
@@ -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
@@ -191,11 +210,11 @@ 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:
@@ -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,14 +7,34 @@ 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:-}"
13
15
  OPENSPEC_CAPABILITY_SLUG_OVERRIDE="${GUARDEX_OPENSPEC_CAPABILITY_SLUG:-}"
14
16
  OPENSPEC_MASTERPLAN_LABEL_RAW="${GUARDEX_OPENSPEC_MASTERPLAN_LABEL-masterplan}"
17
+ OPENSPEC_TIER_RAW="${GUARDEX_OPENSPEC_TIER:-T3}"
15
18
  PRINT_NAME_ONLY=0
16
19
  POSITIONAL_ARGS=()
17
20
 
21
+ run_guardex_cli() {
22
+ if [[ -n "$CLI_ENTRY" ]]; then
23
+ "$NODE_BIN" "$CLI_ENTRY" "$@"
24
+ return $?
25
+ fi
26
+ if command -v gx >/dev/null 2>&1; then
27
+ gx "$@"
28
+ return $?
29
+ fi
30
+ if command -v gitguardex >/dev/null 2>&1; then
31
+ gitguardex "$@"
32
+ return $?
33
+ fi
34
+ echo "[agent-branch-start] Guardex CLI entrypoint unavailable; rerun via gx." >&2
35
+ return 127
36
+ }
37
+
18
38
  while [[ $# -gt 0 ]]; do
19
39
  case "$1" in
20
40
  --task)
@@ -35,8 +55,7 @@ while [[ $# -gt 0 ]]; do
35
55
  shift
36
56
  ;;
37
57
  --tier)
38
- # Accepted for CLAUDE.md compatibility; scaffold size is not yet wired
39
- # through this script. Consume the value so callers can pass it.
58
+ OPENSPEC_TIER_RAW="${2:-$OPENSPEC_TIER_RAW}"
40
59
  shift 2
41
60
  ;;
42
61
  --in-place|--allow-in-place)
@@ -227,11 +246,45 @@ normalize_bool() {
227
246
 
228
247
  OPENSPEC_AUTO_INIT="$(normalize_bool "$OPENSPEC_AUTO_INIT_RAW" "1")"
229
248
 
249
+ normalize_tier() {
250
+ local raw="${1:-}"
251
+ local fallback="${2:-T3}"
252
+ local upper
253
+ upper="$(printf '%s' "$raw" | tr '[:lower:]' '[:upper:]')"
254
+ case "$upper" in
255
+ T0|T1|T2|T3) printf '%s' "$upper" ;;
256
+ '') printf '%s' "$fallback" ;;
257
+ *) return 1 ;;
258
+ esac
259
+ }
260
+
261
+ if ! OPENSPEC_TIER="$(normalize_tier "$OPENSPEC_TIER_RAW" "T3")"; then
262
+ echo "[agent-branch-start] Unsupported OpenSpec tier: ${OPENSPEC_TIER_RAW}" >&2
263
+ exit 1
264
+ fi
265
+
266
+ OPENSPEC_SKIP_CHANGE=0
267
+ OPENSPEC_SKIP_PLAN=0
268
+ OPENSPEC_MINIMAL=0
269
+ case "$OPENSPEC_TIER" in
270
+ T0)
271
+ OPENSPEC_SKIP_CHANGE=1
272
+ OPENSPEC_SKIP_PLAN=1
273
+ ;;
274
+ T1)
275
+ OPENSPEC_SKIP_PLAN=1
276
+ OPENSPEC_MINIMAL=1
277
+ ;;
278
+ T2)
279
+ OPENSPEC_SKIP_PLAN=1
280
+ ;;
281
+ esac
282
+
230
283
  resolve_openspec_masterplan_label() {
231
284
  local raw="${OPENSPEC_MASTERPLAN_LABEL_RAW:-}"
232
285
  local label
233
286
 
234
- if [[ "$OPENSPEC_AUTO_INIT" -ne 1 ]] || [[ -z "$raw" ]]; then
287
+ if [[ "$OPENSPEC_AUTO_INIT" -ne 1 ]] || [[ "$OPENSPEC_SKIP_PLAN" -eq 1 ]] || [[ -z "$raw" ]]; then
235
288
  printf ''
236
289
  return 0
237
290
  fi
@@ -385,26 +438,14 @@ initialize_openspec_plan_workspace() {
385
438
  local worktree="$2"
386
439
  local plan_slug="$3"
387
440
 
388
- hydrate_local_helper_in_worktree "$repo" "$worktree" "scripts/openspec/init-plan-workspace.sh"
389
-
390
- if [[ "$OPENSPEC_AUTO_INIT" -ne 1 ]]; then
441
+ if [[ "$OPENSPEC_AUTO_INIT" -ne 1 ]] || [[ "$OPENSPEC_SKIP_PLAN" -eq 1 ]]; then
391
442
  return 0
392
443
  fi
393
444
 
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
445
  local init_output=""
405
446
  if ! init_output="$(
406
447
  cd "$worktree"
407
- bash "scripts/openspec/init-plan-workspace.sh" "$plan_slug" 2>&1
448
+ run_guardex_cli internal run-shell planInit "$plan_slug" 2>&1
408
449
  )"; then
409
450
  printf '%s\n' "$init_output" >&2
410
451
  echo "[agent-branch-start] OpenSpec workspace initialization failed for plan '${plan_slug}'." >&2
@@ -423,26 +464,15 @@ initialize_openspec_change_workspace() {
423
464
  local change_slug="$3"
424
465
  local capability_slug="$4"
425
466
 
426
- hydrate_local_helper_in_worktree "$repo" "$worktree" "scripts/openspec/init-change-workspace.sh"
427
-
428
- if [[ "$OPENSPEC_AUTO_INIT" -ne 1 ]]; then
467
+ if [[ "$OPENSPEC_AUTO_INIT" -ne 1 ]] || [[ "$OPENSPEC_SKIP_CHANGE" -eq 1 ]]; then
429
468
  return 0
430
469
  fi
431
470
 
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
471
  local init_output=""
443
472
  if ! init_output="$(
444
473
  cd "$worktree"
445
- bash "scripts/openspec/init-change-workspace.sh" "$change_slug" "$capability_slug" 2>&1
474
+ GUARDEX_OPENSPEC_MINIMAL="$OPENSPEC_MINIMAL" \
475
+ run_guardex_cli internal run-shell changeInit "$change_slug" "$capability_slug" 2>&1
446
476
  )"; then
447
477
  printf '%s\n' "$init_output" >&2
448
478
  echo "[agent-branch-start] OpenSpec workspace initialization failed for change '${change_slug}'." >&2
@@ -592,7 +622,6 @@ if [[ -n "$auto_transfer_stash_ref" ]]; then
592
622
  fi
593
623
  fi
594
624
 
595
- hydrate_local_helper_in_worktree "$repo_root" "$worktree_path" "scripts/codex-agent.sh"
596
625
  hydrate_dependency_dir_symlink_in_worktree "$repo_root" "$worktree_path" "node_modules"
597
626
  hydrate_dependency_dir_symlink_in_worktree "$repo_root" "$worktree_path" "apps/frontend/node_modules"
598
627
  hydrate_dependency_dir_symlink_in_worktree "$repo_root" "$worktree_path" "apps/backend/node_modules"
@@ -605,10 +634,19 @@ fi
605
634
 
606
635
  echo "[agent-branch-start] Created branch: ${branch_name}"
607
636
  echo "[agent-branch-start] Worktree: ${worktree_path}"
608
- echo "[agent-branch-start] OpenSpec change: openspec/changes/${openspec_change_slug}"
609
- echo "[agent-branch-start] OpenSpec plan: openspec/plan/${openspec_plan_slug}"
637
+ echo "[agent-branch-start] OpenSpec tier: ${OPENSPEC_TIER}"
638
+ if [[ "$OPENSPEC_SKIP_CHANGE" -eq 1 ]]; then
639
+ echo "[agent-branch-start] OpenSpec change: skipped by tier ${OPENSPEC_TIER}"
640
+ else
641
+ echo "[agent-branch-start] OpenSpec change: openspec/changes/${openspec_change_slug}"
642
+ fi
643
+ if [[ "$OPENSPEC_SKIP_PLAN" -eq 1 ]]; then
644
+ echo "[agent-branch-start] OpenSpec plan: skipped by tier ${OPENSPEC_TIER}"
645
+ else
646
+ echo "[agent-branch-start] OpenSpec plan: openspec/plan/${openspec_plan_slug}"
647
+ fi
610
648
  echo "[agent-branch-start] Next steps:"
611
649
  echo " cd \"${worktree_path}\""
612
- echo " python3 scripts/agent-file-locks.py claim --branch \"${branch_name}\" <file...>"
650
+ echo " gx locks claim --branch \"${branch_name}\" <file...>"
613
651
  echo " # implement + commit"
614
- echo " bash scripts/agent-branch-finish.sh --branch \"${branch_name}\" --base ${BASE_BRANCH} --via-pr --wait-for-merge"
652
+ echo " gx branch finish --branch \"${branch_name}\" --base ${BASE_BRANCH} --via-pr --wait-for-merge"