@imdeadpool/guardex 7.0.19 → 7.0.21

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,129 @@ 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
+ task_requires_full_change_workspace() {
94
+ local text="$1"
95
+ string_contains_any "$text" \
96
+ "cleanup evidence" "merged cleanup" "merged state" "pr url" \
97
+ "cleanup pipeline" "finish pipeline" "sandbox cleanup" "tasks.md"
98
+ }
99
+
100
+ derive_task_mode_from_tier() {
101
+ case "$1" in
102
+ T0|T1) printf 'caveman' ;;
103
+ T2|T3) printf 'omx' ;;
104
+ *) return 1 ;;
105
+ esac
106
+ }
107
+
108
+ apply_openspec_tier() {
109
+ OPENSPEC_SKIP_CHANGE=0
110
+ OPENSPEC_SKIP_PLAN=0
111
+ OPENSPEC_MINIMAL=0
112
+ case "$1" in
113
+ T0)
114
+ OPENSPEC_SKIP_CHANGE=1
115
+ OPENSPEC_SKIP_PLAN=1
116
+ ;;
117
+ T1)
118
+ OPENSPEC_SKIP_PLAN=1
119
+ OPENSPEC_MINIMAL=1
120
+ ;;
121
+ T2)
122
+ OPENSPEC_SKIP_PLAN=1
123
+ ;;
124
+ esac
125
+ }
126
+
127
+ decide_task_routing() {
128
+ local task_lower
129
+ task_lower="$(printf '%s' "$TASK_NAME" | tr '[:upper:]' '[:lower:]')"
130
+
131
+ if [[ -n "$OPENSPEC_TIER_RAW" ]]; then
132
+ if ! OPENSPEC_TIER="$(normalize_tier "$OPENSPEC_TIER_RAW" "T2")"; then
133
+ echo "[codex-agent] Unsupported OpenSpec tier: ${OPENSPEC_TIER_RAW}" >&2
134
+ return 1
135
+ fi
136
+ TASK_ROUTING_REASON="explicit tier override"
137
+ elif string_has_lightweight_prefix "$task_lower"; then
138
+ if task_requires_full_change_workspace "$task_lower"; then
139
+ OPENSPEC_TIER="T2"
140
+ TASK_ROUTING_REASON="cleanup-evidence artifact wording overrides lightweight prefix"
141
+ else
142
+ OPENSPEC_TIER="T1"
143
+ TASK_ROUTING_REASON="explicit lightweight prefix"
144
+ fi
145
+ elif string_contains_any "$task_lower" \
146
+ "ralph" "autopilot" "ultrawork" "ultraqa" "ralplan" "deep interview" "ouroboros" \
147
+ "migration" "refactor" "architecture" "re-architect" "cross-cutting" "multi-agent" \
148
+ "multiagent" "parallel" "orchestr" "release" "zero-copy" "install surface" "workflow"
149
+ then
150
+ OPENSPEC_TIER="T3"
151
+ TASK_ROUTING_REASON="plan-heavy or orchestration-heavy task wording"
152
+ elif string_contains_any "$task_lower" \
153
+ "typo" "spelling" "comment-only" "comment only" "format-only" "format only" \
154
+ "whitespace" "one-liner" "one liner" "version bump" "bump version" \
155
+ "single-file" "single file"
156
+ then
157
+ OPENSPEC_TIER="T1"
158
+ TASK_ROUTING_REASON="small bounded maintenance wording"
159
+ else
160
+ OPENSPEC_TIER="T2"
161
+ TASK_ROUTING_REASON="default behavior-change route"
162
+ fi
163
+
164
+ if ! TASK_MODE="$(derive_task_mode_from_tier "$OPENSPEC_TIER")"; then
165
+ echo "[codex-agent] Unsupported task mode tier: ${OPENSPEC_TIER}" >&2
166
+ return 1
167
+ fi
168
+ apply_openspec_tier "$OPENSPEC_TIER"
169
+ }
170
+
171
+ describe_task_routing() {
172
+ case "$OPENSPEC_TIER" in
173
+ T0) printf 'caveman / T0 (no OpenSpec scaffold)' ;;
174
+ T1) printf 'caveman / T1 (notes-only OpenSpec)' ;;
175
+ T2) printf 'omx / T2 (change workspace only)' ;;
176
+ T3) printf 'omx / T3 (change plus plan workspace)' ;;
177
+ *) printf 'unknown / %s' "${OPENSPEC_TIER:-unset}" ;;
178
+ esac
179
+ }
180
+
51
181
  AUTO_FINISH="$(normalize_bool "$AUTO_FINISH_RAW" "1")"
52
182
  AUTO_REVIEW_ON_CONFLICT="$(normalize_bool "$AUTO_REVIEW_ON_CONFLICT_RAW" "1")"
53
183
  AUTO_CLEANUP="$(normalize_bool "$AUTO_CLEANUP_RAW" "1")"
@@ -58,7 +188,7 @@ resolve_openspec_masterplan_label() {
58
188
  local raw="${OPENSPEC_MASTERPLAN_LABEL_RAW:-}"
59
189
  local label
60
190
 
61
- if [[ "$OPENSPEC_AUTO_INIT" -ne 1 ]] || [[ -z "$raw" ]]; then
191
+ if [[ "$OPENSPEC_AUTO_INIT" -ne 1 ]] || [[ "$OPENSPEC_SKIP_PLAN" -eq 1 ]] || [[ -z "$raw" ]]; then
62
192
  printf ''
63
193
  return 0
64
194
  fi
@@ -86,6 +216,10 @@ while [[ $# -gt 0 ]]; do
86
216
  BASE_BRANCH_EXPLICIT=1
87
217
  shift 2
88
218
  ;;
219
+ --tier)
220
+ OPENSPEC_TIER_RAW="${2:-$OPENSPEC_TIER_RAW}"
221
+ shift 2
222
+ ;;
89
223
  --codex-bin)
90
224
  CODEX_BIN="${2:-$CODEX_BIN}"
91
225
  shift 2
@@ -151,6 +285,10 @@ if [[ "$BASE_BRANCH_EXPLICIT" -eq 1 && -z "$BASE_BRANCH" ]]; then
151
285
  exit 1
152
286
  fi
153
287
 
288
+ if ! decide_task_routing; then
289
+ exit 1
290
+ fi
291
+
154
292
  if ! command -v "$CODEX_BIN" >/dev/null 2>&1; then
155
293
  echo "[codex-agent] Missing Codex CLI command: $CODEX_BIN" >&2
156
294
  echo "[codex-agent] Install Codex first, then retry." >&2
@@ -393,7 +531,7 @@ start_sandbox_fallback() {
393
531
  printf '[agent-branch-start] Worktree: %s\n' "$worktree_path"
394
532
  }
395
533
 
396
- start_args=("$TASK_NAME" "$AGENT_NAME")
534
+ start_args=(--tier "$OPENSPEC_TIER" "$TASK_NAME" "$AGENT_NAME")
397
535
  if [[ "$BASE_BRANCH_EXPLICIT" -eq 1 ]]; then
398
536
  start_args+=("$BASE_BRANCH")
399
537
  fi
@@ -487,7 +625,10 @@ record_active_session_state() {
487
625
  --agent "$AGENT_NAME" \
488
626
  --worktree "$wt" \
489
627
  --pid "$$" \
490
- --cli "$CODEX_BIN"
628
+ --cli "$CODEX_BIN" \
629
+ --task-mode "$TASK_MODE" \
630
+ --openspec-tier "$OPENSPEC_TIER" \
631
+ --routing-reason "$TASK_ROUTING_REASON"
491
632
  }
492
633
 
493
634
  clear_active_session_state() {
@@ -545,6 +686,7 @@ print_takeover_prompt() {
545
686
  finish_cmd="gx branch finish --branch \"${branch}\" --base ${base_branch} --via-pr --wait-for-merge --cleanup"
546
687
 
547
688
  echo "[codex-agent] Takeover sandbox: ${wt}"
689
+ echo "[codex-agent] Takeover routing: $(describe_task_routing) (${TASK_ROUTING_REASON})"
548
690
  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
691
  }
550
692
 
@@ -594,7 +736,7 @@ ensure_openspec_plan_workspace() {
594
736
  local wt="$1"
595
737
  local branch="$2"
596
738
 
597
- if [[ "$OPENSPEC_AUTO_INIT" -ne 1 ]]; then
739
+ if [[ "$OPENSPEC_AUTO_INIT" -ne 1 ]] || [[ "$OPENSPEC_SKIP_PLAN" -eq 1 ]]; then
598
740
  return 0
599
741
  fi
600
742
 
@@ -619,7 +761,7 @@ ensure_openspec_change_workspace() {
619
761
  local wt="$1"
620
762
  local branch="$2"
621
763
 
622
- if [[ "$OPENSPEC_AUTO_INIT" -ne 1 ]]; then
764
+ if [[ "$OPENSPEC_AUTO_INIT" -ne 1 ]] || [[ "$OPENSPEC_SKIP_CHANGE" -eq 1 ]]; then
623
765
  return 0
624
766
  fi
625
767
 
@@ -628,7 +770,8 @@ ensure_openspec_change_workspace() {
628
770
  capability_slug="$(resolve_openspec_capability_slug)"
629
771
  if ! init_output="$(
630
772
  cd "$wt"
631
- run_guardex_cli internal run-shell changeInit "$change_slug" "$capability_slug" 2>&1
773
+ GUARDEX_OPENSPEC_MINIMAL="$OPENSPEC_MINIMAL" \
774
+ run_guardex_cli internal run-shell changeInit "$change_slug" "$capability_slug" 2>&1
632
775
  )"; then
633
776
  printf '%s\n' "$init_output" >&2
634
777
  echo "[codex-agent] OpenSpec workspace initialization failed for change '${change_slug}'." >&2
@@ -878,6 +1021,8 @@ if ! ensure_openspec_plan_workspace "$worktree_path" "$worktree_branch"; then
878
1021
  exit 1
879
1022
  fi
880
1023
 
1024
+ echo "[codex-agent] Task routing: $(describe_task_routing) (${TASK_ROUTING_REASON})"
1025
+
881
1026
  active_session_recorded=0
882
1027
  cleanup_active_session_state_on_exit() {
883
1028
  set +e
@@ -894,7 +1039,10 @@ trap cleanup_active_session_state_on_exit EXIT INT TERM
894
1039
  echo "[codex-agent] Launching ${CODEX_BIN} in sandbox: $worktree_path"
895
1040
  cd "$worktree_path"
896
1041
  set +e
897
- "$CODEX_BIN" "$@"
1042
+ GUARDEX_TASK_MODE="$TASK_MODE" \
1043
+ GUARDEX_OPENSPEC_TIER="$OPENSPEC_TIER" \
1044
+ GUARDEX_TASK_ROUTING_REASON="$TASK_ROUTING_REASON" \
1045
+ "$CODEX_BIN" "$@"
898
1046
  codex_exit="$?"
899
1047
  set -e
900
1048
 
@@ -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/` and falls back to managed worktree-root `AGENT.lock` telemetry when the launcher session file is absent.