@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.
- package/README.md +7 -1
- package/bin/multiagent-safety.js +2 -7861
- package/package.json +2 -1
- package/src/cli/args.js +7 -0
- package/src/cli/dispatch.js +86 -0
- package/src/cli/main.js +7719 -0
- package/src/context.js +503 -0
- package/src/core/runtime.js +119 -0
- package/src/finish/index.js +425 -0
- package/src/git/index.js +112 -0
- package/src/hooks/index.js +74 -0
- package/src/output/index.js +398 -0
- package/src/sandbox/index.js +68 -0
- package/src/scaffold/index.js +169 -0
- package/src/toolchain/index.js +223 -0
- package/templates/scripts/agent-branch-start.sh +52 -8
- package/templates/scripts/codex-agent.sh +143 -7
- package/templates/vscode/guardex-active-agents/README.md +16 -11
- package/templates/vscode/guardex-active-agents/extension.js +876 -64
- package/templates/vscode/guardex-active-agents/package.json +61 -1
- package/templates/vscode/guardex-active-agents/session-schema.js +211 -17
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
|
603
|
-
|
|
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
|
-
|
|
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
|
-
"$
|
|
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
|
-
|
|
5
|
+
## Quick Start
|
|
6
6
|
|
|
7
|
-
|
|
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
|
-
|
|
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/`.
|