@imdeadpool/guardex 7.0.19 → 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
+ };
@@ -14,6 +14,7 @@ OPENSPEC_PLAN_SLUG_OVERRIDE="${GUARDEX_OPENSPEC_PLAN_SLUG:-}"
14
14
  OPENSPEC_CHANGE_SLUG_OVERRIDE="${GUARDEX_OPENSPEC_CHANGE_SLUG:-}"
15
15
  OPENSPEC_CAPABILITY_SLUG_OVERRIDE="${GUARDEX_OPENSPEC_CAPABILITY_SLUG:-}"
16
16
  OPENSPEC_MASTERPLAN_LABEL_RAW="${GUARDEX_OPENSPEC_MASTERPLAN_LABEL-masterplan}"
17
+ OPENSPEC_TIER_RAW="${GUARDEX_OPENSPEC_TIER:-T3}"
17
18
  PRINT_NAME_ONLY=0
18
19
  POSITIONAL_ARGS=()
19
20
 
@@ -54,8 +55,7 @@ while [[ $# -gt 0 ]]; do
54
55
  shift
55
56
  ;;
56
57
  --tier)
57
- # Accepted for CLAUDE.md compatibility; scaffold size is not yet wired
58
- # through this script. Consume the value so callers can pass it.
58
+ OPENSPEC_TIER_RAW="${2:-$OPENSPEC_TIER_RAW}"
59
59
  shift 2
60
60
  ;;
61
61
  --in-place|--allow-in-place)
@@ -246,11 +246,45 @@ normalize_bool() {
246
246
 
247
247
  OPENSPEC_AUTO_INIT="$(normalize_bool "$OPENSPEC_AUTO_INIT_RAW" "1")"
248
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
+
249
283
  resolve_openspec_masterplan_label() {
250
284
  local raw="${OPENSPEC_MASTERPLAN_LABEL_RAW:-}"
251
285
  local label
252
286
 
253
- if [[ "$OPENSPEC_AUTO_INIT" -ne 1 ]] || [[ -z "$raw" ]]; then
287
+ if [[ "$OPENSPEC_AUTO_INIT" -ne 1 ]] || [[ "$OPENSPEC_SKIP_PLAN" -eq 1 ]] || [[ -z "$raw" ]]; then
254
288
  printf ''
255
289
  return 0
256
290
  fi
@@ -404,7 +438,7 @@ initialize_openspec_plan_workspace() {
404
438
  local worktree="$2"
405
439
  local plan_slug="$3"
406
440
 
407
- if [[ "$OPENSPEC_AUTO_INIT" -ne 1 ]]; then
441
+ if [[ "$OPENSPEC_AUTO_INIT" -ne 1 ]] || [[ "$OPENSPEC_SKIP_PLAN" -eq 1 ]]; then
408
442
  return 0
409
443
  fi
410
444
 
@@ -430,14 +464,15 @@ initialize_openspec_change_workspace() {
430
464
  local change_slug="$3"
431
465
  local capability_slug="$4"
432
466
 
433
- if [[ "$OPENSPEC_AUTO_INIT" -ne 1 ]]; then
467
+ if [[ "$OPENSPEC_AUTO_INIT" -ne 1 ]] || [[ "$OPENSPEC_SKIP_CHANGE" -eq 1 ]]; then
434
468
  return 0
435
469
  fi
436
470
 
437
471
  local init_output=""
438
472
  if ! init_output="$(
439
473
  cd "$worktree"
440
- run_guardex_cli internal run-shell changeInit "$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
441
476
  )"; then
442
477
  printf '%s\n' "$init_output" >&2
443
478
  echo "[agent-branch-start] OpenSpec workspace initialization failed for change '${change_slug}'." >&2
@@ -599,8 +634,17 @@ fi
599
634
 
600
635
  echo "[agent-branch-start] Created branch: ${branch_name}"
601
636
  echo "[agent-branch-start] Worktree: ${worktree_path}"
602
- echo "[agent-branch-start] OpenSpec change: openspec/changes/${openspec_change_slug}"
603
- 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
604
648
  echo "[agent-branch-start] Next steps:"
605
649
  echo " cd \"${worktree_path}\""
606
650
  echo " gx locks claim --branch \"${branch_name}\" <file...>"
@@ -17,6 +17,13 @@ OPENSPEC_PLAN_SLUG_OVERRIDE="${GUARDEX_OPENSPEC_PLAN_SLUG:-}"
17
17
  OPENSPEC_CHANGE_SLUG_OVERRIDE="${GUARDEX_OPENSPEC_CHANGE_SLUG:-}"
18
18
  OPENSPEC_CAPABILITY_SLUG_OVERRIDE="${GUARDEX_OPENSPEC_CAPABILITY_SLUG:-}"
19
19
  OPENSPEC_MASTERPLAN_LABEL_RAW="${GUARDEX_OPENSPEC_MASTERPLAN_LABEL-masterplan}"
20
+ OPENSPEC_TIER_RAW="${GUARDEX_OPENSPEC_TIER:-}"
21
+ OPENSPEC_TIER=""
22
+ OPENSPEC_SKIP_CHANGE=0
23
+ OPENSPEC_SKIP_PLAN=0
24
+ OPENSPEC_MINIMAL=0
25
+ TASK_MODE=""
26
+ TASK_ROUTING_REASON=""
20
27
 
21
28
  run_guardex_cli() {
22
29
  if [[ -n "$CLI_ENTRY" ]]; then
@@ -48,6 +55,117 @@ normalize_bool() {
48
55
  esac
49
56
  }
50
57
 
58
+ normalize_tier() {
59
+ local raw="${1:-}"
60
+ local fallback="${2:-T2}"
61
+ local upper
62
+ upper="$(printf '%s' "$raw" | tr '[:lower:]' '[:upper:]')"
63
+ case "$upper" in
64
+ T0|T1|T2|T3) printf '%s' "$upper" ;;
65
+ '') printf '%s' "$fallback" ;;
66
+ *) return 1 ;;
67
+ esac
68
+ }
69
+
70
+ string_contains_any() {
71
+ local haystack="$1"
72
+ shift
73
+ local needle
74
+ for needle in "$@"; do
75
+ if [[ "$haystack" == *"$needle"* ]]; then
76
+ return 0
77
+ fi
78
+ done
79
+ return 1
80
+ }
81
+
82
+ string_has_lightweight_prefix() {
83
+ local text="$1"
84
+ local prefix
85
+ for prefix in "quick:" "simple:" "tiny:" "minor:" "small:" "just:" "only:"; do
86
+ if [[ "$text" == "$prefix"* ]]; then
87
+ return 0
88
+ fi
89
+ done
90
+ return 1
91
+ }
92
+
93
+ derive_task_mode_from_tier() {
94
+ case "$1" in
95
+ T0|T1) printf 'caveman' ;;
96
+ T2|T3) printf 'omx' ;;
97
+ *) return 1 ;;
98
+ esac
99
+ }
100
+
101
+ apply_openspec_tier() {
102
+ OPENSPEC_SKIP_CHANGE=0
103
+ OPENSPEC_SKIP_PLAN=0
104
+ OPENSPEC_MINIMAL=0
105
+ case "$1" in
106
+ T0)
107
+ OPENSPEC_SKIP_CHANGE=1
108
+ OPENSPEC_SKIP_PLAN=1
109
+ ;;
110
+ T1)
111
+ OPENSPEC_SKIP_PLAN=1
112
+ OPENSPEC_MINIMAL=1
113
+ ;;
114
+ T2)
115
+ OPENSPEC_SKIP_PLAN=1
116
+ ;;
117
+ esac
118
+ }
119
+
120
+ decide_task_routing() {
121
+ local task_lower
122
+ task_lower="$(printf '%s' "$TASK_NAME" | tr '[:upper:]' '[:lower:]')"
123
+
124
+ if [[ -n "$OPENSPEC_TIER_RAW" ]]; then
125
+ if ! OPENSPEC_TIER="$(normalize_tier "$OPENSPEC_TIER_RAW" "T2")"; then
126
+ echo "[codex-agent] Unsupported OpenSpec tier: ${OPENSPEC_TIER_RAW}" >&2
127
+ return 1
128
+ fi
129
+ TASK_ROUTING_REASON="explicit tier override"
130
+ elif string_has_lightweight_prefix "$task_lower"; then
131
+ OPENSPEC_TIER="T1"
132
+ TASK_ROUTING_REASON="explicit lightweight prefix"
133
+ elif string_contains_any "$task_lower" \
134
+ "ralph" "autopilot" "ultrawork" "ultraqa" "ralplan" "deep interview" "ouroboros" \
135
+ "migration" "refactor" "architecture" "re-architect" "cross-cutting" "multi-agent" \
136
+ "multiagent" "parallel" "orchestr" "release" "zero-copy" "install surface" "workflow"
137
+ then
138
+ OPENSPEC_TIER="T3"
139
+ TASK_ROUTING_REASON="plan-heavy or orchestration-heavy task wording"
140
+ elif string_contains_any "$task_lower" \
141
+ "typo" "spelling" "comment-only" "comment only" "format-only" "format only" \
142
+ "whitespace" "one-liner" "one liner" "version bump" "bump version" \
143
+ "single-file" "single file"
144
+ then
145
+ OPENSPEC_TIER="T1"
146
+ TASK_ROUTING_REASON="small bounded maintenance wording"
147
+ else
148
+ OPENSPEC_TIER="T2"
149
+ TASK_ROUTING_REASON="default behavior-change route"
150
+ fi
151
+
152
+ if ! TASK_MODE="$(derive_task_mode_from_tier "$OPENSPEC_TIER")"; then
153
+ echo "[codex-agent] Unsupported task mode tier: ${OPENSPEC_TIER}" >&2
154
+ return 1
155
+ fi
156
+ apply_openspec_tier "$OPENSPEC_TIER"
157
+ }
158
+
159
+ describe_task_routing() {
160
+ case "$OPENSPEC_TIER" in
161
+ T0) printf 'caveman / T0 (no OpenSpec scaffold)' ;;
162
+ T1) printf 'caveman / T1 (notes-only OpenSpec)' ;;
163
+ T2) printf 'omx / T2 (change workspace only)' ;;
164
+ T3) printf 'omx / T3 (change plus plan workspace)' ;;
165
+ *) printf 'unknown / %s' "${OPENSPEC_TIER:-unset}" ;;
166
+ esac
167
+ }
168
+
51
169
  AUTO_FINISH="$(normalize_bool "$AUTO_FINISH_RAW" "1")"
52
170
  AUTO_REVIEW_ON_CONFLICT="$(normalize_bool "$AUTO_REVIEW_ON_CONFLICT_RAW" "1")"
53
171
  AUTO_CLEANUP="$(normalize_bool "$AUTO_CLEANUP_RAW" "1")"
@@ -58,7 +176,7 @@ resolve_openspec_masterplan_label() {
58
176
  local raw="${OPENSPEC_MASTERPLAN_LABEL_RAW:-}"
59
177
  local label
60
178
 
61
- if [[ "$OPENSPEC_AUTO_INIT" -ne 1 ]] || [[ -z "$raw" ]]; then
179
+ if [[ "$OPENSPEC_AUTO_INIT" -ne 1 ]] || [[ "$OPENSPEC_SKIP_PLAN" -eq 1 ]] || [[ -z "$raw" ]]; then
62
180
  printf ''
63
181
  return 0
64
182
  fi
@@ -86,6 +204,10 @@ while [[ $# -gt 0 ]]; do
86
204
  BASE_BRANCH_EXPLICIT=1
87
205
  shift 2
88
206
  ;;
207
+ --tier)
208
+ OPENSPEC_TIER_RAW="${2:-$OPENSPEC_TIER_RAW}"
209
+ shift 2
210
+ ;;
89
211
  --codex-bin)
90
212
  CODEX_BIN="${2:-$CODEX_BIN}"
91
213
  shift 2
@@ -151,6 +273,10 @@ if [[ "$BASE_BRANCH_EXPLICIT" -eq 1 && -z "$BASE_BRANCH" ]]; then
151
273
  exit 1
152
274
  fi
153
275
 
276
+ if ! decide_task_routing; then
277
+ exit 1
278
+ fi
279
+
154
280
  if ! command -v "$CODEX_BIN" >/dev/null 2>&1; then
155
281
  echo "[codex-agent] Missing Codex CLI command: $CODEX_BIN" >&2
156
282
  echo "[codex-agent] Install Codex first, then retry." >&2
@@ -393,7 +519,7 @@ start_sandbox_fallback() {
393
519
  printf '[agent-branch-start] Worktree: %s\n' "$worktree_path"
394
520
  }
395
521
 
396
- start_args=("$TASK_NAME" "$AGENT_NAME")
522
+ start_args=(--tier "$OPENSPEC_TIER" "$TASK_NAME" "$AGENT_NAME")
397
523
  if [[ "$BASE_BRANCH_EXPLICIT" -eq 1 ]]; then
398
524
  start_args+=("$BASE_BRANCH")
399
525
  fi
@@ -487,7 +613,10 @@ record_active_session_state() {
487
613
  --agent "$AGENT_NAME" \
488
614
  --worktree "$wt" \
489
615
  --pid "$$" \
490
- --cli "$CODEX_BIN"
616
+ --cli "$CODEX_BIN" \
617
+ --task-mode "$TASK_MODE" \
618
+ --openspec-tier "$OPENSPEC_TIER" \
619
+ --routing-reason "$TASK_ROUTING_REASON"
491
620
  }
492
621
 
493
622
  clear_active_session_state() {
@@ -545,6 +674,7 @@ print_takeover_prompt() {
545
674
  finish_cmd="gx branch finish --branch \"${branch}\" --base ${base_branch} --via-pr --wait-for-merge --cleanup"
546
675
 
547
676
  echo "[codex-agent] Takeover sandbox: ${wt}"
677
+ echo "[codex-agent] Takeover routing: $(describe_task_routing) (${TASK_ROUTING_REASON})"
548
678
  echo "[codex-agent] Takeover prompt: Continue \`${change_slug}\` on branch \`${branch}\`. Work inside \`${wt}\`, review \`${change_artifact}\`, continue from the current state instead of creating a new sandbox, and when the work is done run \`${finish_cmd}\`."
549
679
  }
550
680
 
@@ -594,7 +724,7 @@ ensure_openspec_plan_workspace() {
594
724
  local wt="$1"
595
725
  local branch="$2"
596
726
 
597
- if [[ "$OPENSPEC_AUTO_INIT" -ne 1 ]]; then
727
+ if [[ "$OPENSPEC_AUTO_INIT" -ne 1 ]] || [[ "$OPENSPEC_SKIP_PLAN" -eq 1 ]]; then
598
728
  return 0
599
729
  fi
600
730
 
@@ -619,7 +749,7 @@ ensure_openspec_change_workspace() {
619
749
  local wt="$1"
620
750
  local branch="$2"
621
751
 
622
- if [[ "$OPENSPEC_AUTO_INIT" -ne 1 ]]; then
752
+ if [[ "$OPENSPEC_AUTO_INIT" -ne 1 ]] || [[ "$OPENSPEC_SKIP_CHANGE" -eq 1 ]]; then
623
753
  return 0
624
754
  fi
625
755
 
@@ -628,7 +758,8 @@ ensure_openspec_change_workspace() {
628
758
  capability_slug="$(resolve_openspec_capability_slug)"
629
759
  if ! init_output="$(
630
760
  cd "$wt"
631
- run_guardex_cli internal run-shell changeInit "$change_slug" "$capability_slug" 2>&1
761
+ GUARDEX_OPENSPEC_MINIMAL="$OPENSPEC_MINIMAL" \
762
+ run_guardex_cli internal run-shell changeInit "$change_slug" "$capability_slug" 2>&1
632
763
  )"; then
633
764
  printf '%s\n' "$init_output" >&2
634
765
  echo "[codex-agent] OpenSpec workspace initialization failed for change '${change_slug}'." >&2
@@ -878,6 +1009,8 @@ if ! ensure_openspec_plan_workspace "$worktree_path" "$worktree_branch"; then
878
1009
  exit 1
879
1010
  fi
880
1011
 
1012
+ echo "[codex-agent] Task routing: $(describe_task_routing) (${TASK_ROUTING_REASON})"
1013
+
881
1014
  active_session_recorded=0
882
1015
  cleanup_active_session_state_on_exit() {
883
1016
  set +e
@@ -894,7 +1027,10 @@ trap cleanup_active_session_state_on_exit EXIT INT TERM
894
1027
  echo "[codex-agent] Launching ${CODEX_BIN} in sandbox: $worktree_path"
895
1028
  cd "$worktree_path"
896
1029
  set +e
897
- "$CODEX_BIN" "$@"
1030
+ GUARDEX_TASK_MODE="$TASK_MODE" \
1031
+ GUARDEX_OPENSPEC_TIER="$OPENSPEC_TIER" \
1032
+ GUARDEX_TASK_ROUTING_REASON="$TASK_ROUTING_REASON" \
1033
+ "$CODEX_BIN" "$@"
898
1034
  codex_exit="$?"
899
1035
  set -e
900
1036
 
@@ -2,21 +2,26 @@
2
2
 
3
3
  Local VS Code companion for Guardex-managed repos.
4
4
 
5
- What it does:
5
+ ## Quick Start
6
6
 
7
- - Adds an `Active Agents` view to the Source Control container.
8
- - Renders one repo node per live Guardex workspace with grouped `ACTIVE AGENTS` and `CHANGES` sections.
9
- - Splits live sessions inside `ACTIVE AGENTS` into `WORKING NOW` and `THINKING` groups so active edit lanes stand out immediately.
10
- - Shows one row per live Guardex sandbox session inside those activity groups.
11
- - Shows repo-root git changes in a sibling `CHANGES` section when the guarded repo itself is dirty.
12
- - Derives `thinking` versus `working` from the live sandbox worktree, surfaces working counts in the repo/header summary, and shows changed-file counts for active edits.
13
- - Uses VS Code's native animated `loading~spin` icon for the running-state affordance.
14
- - Reads repo-local presence files from `.omx/state/active-sessions/`.
7
+ Use the welcome view in Source Control to create or inspect Guardex sandboxes quickly.
15
8
 
16
- Install from a Guardex-wired repo:
9
+ 1. Install from a Guardex-wired repo:
17
10
 
18
11
  ```sh
19
12
  node scripts/install-vscode-active-agents-extension.js
20
13
  ```
21
14
 
22
- Then reload the VS Code window.
15
+ 2. Reload the VS Code window.
16
+ 3. In Source Control -> `Active Agents`, use `Start agent` to enter a task + agent name and run `gx branch start`.
17
+
18
+ What it does:
19
+
20
+ - Adds an `Active Agents` view to the Source Control container.
21
+ - Renders one repo node per live Guardex workspace with grouped `ACTIVE AGENTS` and `CHANGES` sections.
22
+ - Splits live sessions inside `ACTIVE AGENTS` into `BLOCKED`, `WORKING NOW`, `IDLE`, `STALLED`, and `DEAD` groups so stuck, active, and inactive lanes stand out immediately.
23
+ - Shows one row per live Guardex sandbox session inside those activity groups.
24
+ - Shows repo-root git changes in a sibling `CHANGES` section when the guarded repo itself is dirty.
25
+ - Derives session state from dirty worktree status, git conflict markers, PID liveness, and recent file mtimes, surfaces working/dead counts in the repo/header summary, and shows changed-file counts for active edits.
26
+ - Uses distinct VS Code codicons for each session state: `warning`, `edit`, `loading~spin`, `clock`, and `error`.
27
+ - Reads repo-local presence files from `.omx/state/active-sessions/`.