@imdeadpool/guardex 7.0.43 → 7.1.0
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 +26 -0
- package/package.json +2 -1
- package/skills/gx-act/SKILL.md +82 -0
- package/src/agents/inspect.js +17 -4
- package/src/agents/launch.js +10 -1
- package/src/agents/status.js +9 -6
- package/src/budget/index.js +2 -1
- package/src/cli/args.js +52 -2
- package/src/cli/commands/agents.js +364 -0
- package/src/cli/commands/bootstrap.js +92 -0
- package/src/cli/commands/branch.js +127 -0
- package/src/cli/commands/claude.js +674 -0
- package/src/cli/commands/doctor.js +268 -0
- package/src/cli/commands/finish.js +26 -0
- package/src/cli/commands/mcp.js +122 -0
- package/src/cli/commands/misc.js +304 -0
- package/src/cli/commands/pr.js +439 -0
- package/src/cli/commands/prompt.js +92 -0
- package/src/cli/commands/release.js +305 -0
- package/src/cli/commands/report.js +244 -0
- package/src/cli/commands/review.js +32 -0
- package/src/cli/commands/setup.js +242 -0
- package/src/cli/commands/status.js +338 -0
- package/src/cli/commands/watch.js +234 -0
- package/src/cli/main.js +68 -3726
- package/src/cli/shared/repo-env.js +161 -0
- package/src/cli/shared/sandbox.js +417 -0
- package/src/cli/shared/scaffolding.js +535 -0
- package/src/cli/shared/toolchain-shims.js +420 -0
- package/src/context.js +229 -11
- package/src/core/runtime.js +6 -1
- package/src/doctor/index.js +42 -13
- package/src/finish/index.js +147 -5
- package/src/finish/preflight.js +177 -0
- package/src/finish/review-gate.js +182 -0
- package/src/git/index.js +446 -4
- package/src/hooks/index.js +0 -64
- package/src/mcp/collect.js +370 -0
- package/src/mcp/server.js +157 -0
- package/src/output/index.js +67 -1
- package/src/pr-review.js +23 -0
- package/src/pr.js +381 -0
- package/src/sandbox/index.js +13 -2
- package/src/scaffold/agent-worktree-prep.js +213 -0
- package/src/scaffold/index.js +108 -10
- package/src/speckit/index.js +226 -0
- package/src/terminal/index.js +1 -76
- package/src/terminal/tmux.js +0 -1
- package/src/toolchain/index.js +20 -0
- package/templates/AGENTS.monorepo-apps.md +26 -0
- package/templates/AGENTS.multiagent-safety.md +61 -347
- package/templates/AGENTS.multiagent-safety.min.md +11 -0
- package/templates/codex/skills/gx-act/SKILL.md +82 -0
- package/templates/githooks/pre-commit +22 -19
- package/templates/scripts/agent-branch-finish.sh +8 -30
- package/templates/scripts/agent-branch-merge.sh +4 -1
- package/templates/scripts/agent-branch-start.sh +88 -3
- package/templates/scripts/agent-preflight.sh +31 -5
- package/templates/scripts/agent-worktree-prune.sh +1 -1
- package/templates/scripts/codex-agent.sh +0 -91
- package/src/agents/detect.js +0 -160
- package/src/cockpit/keybindings.js +0 -224
- package/src/cockpit/layout.js +0 -224
|
@@ -0,0 +1,420 @@
|
|
|
1
|
+
// Legacy in-file copies of toolchain wrappers that previously coexisted with
|
|
2
|
+
// `toolchainModule.*` in src/cli/main.js. These are unreachable from the live
|
|
3
|
+
// dispatch table (every caller already routes through `toolchainModule`), but
|
|
4
|
+
// the original file kept them inline; they are preserved here so that the
|
|
5
|
+
// module-load smoke test (`require('./src/cli/main.js')`) still imports an
|
|
6
|
+
// identical set of declarations and downstream tools that may rely on the
|
|
7
|
+
// dead-code lookup keep working without surprise.
|
|
8
|
+
const {
|
|
9
|
+
fs,
|
|
10
|
+
path,
|
|
11
|
+
cp,
|
|
12
|
+
packageJson,
|
|
13
|
+
TOOL_NAME,
|
|
14
|
+
SHORT_TOOL_NAME,
|
|
15
|
+
OPENSPEC_PACKAGE,
|
|
16
|
+
NPX_BIN,
|
|
17
|
+
GUARDEX_HOME_DIR,
|
|
18
|
+
GLOBAL_TOOLCHAIN_SERVICES,
|
|
19
|
+
GLOBAL_TOOLCHAIN_PACKAGES,
|
|
20
|
+
OPTIONAL_LOCAL_COMPANION_TOOLS,
|
|
21
|
+
REQUIRED_SYSTEM_TOOLS,
|
|
22
|
+
NPM_BIN,
|
|
23
|
+
OPENSPEC_BIN,
|
|
24
|
+
envFlagIsTruthy,
|
|
25
|
+
} = require('../../context');
|
|
26
|
+
const toolchainModule = require('../../toolchain');
|
|
27
|
+
const { run } = require('../../core/runtime');
|
|
28
|
+
const { isNewerVersion } = require('../../core/versions');
|
|
29
|
+
const { readSingleLineFromStdin } = require('../../core/stdin');
|
|
30
|
+
const { colorize } = require('../../output');
|
|
31
|
+
const { isInteractiveTerminal } = require('./repo-env');
|
|
32
|
+
|
|
33
|
+
function parseNpmVersionOutput(stdout) {
|
|
34
|
+
const trimmed = String(stdout || '').trim();
|
|
35
|
+
if (!trimmed) return '';
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
const parsed = JSON.parse(trimmed);
|
|
39
|
+
if (Array.isArray(parsed)) {
|
|
40
|
+
return String(parsed[parsed.length - 1] || '').trim();
|
|
41
|
+
}
|
|
42
|
+
return String(parsed || '').trim();
|
|
43
|
+
} catch {
|
|
44
|
+
const firstLine = trimmed.split('\n').map((line) => line.trim()).find(Boolean);
|
|
45
|
+
return firstLine || '';
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function checkForGuardexUpdate() {
|
|
50
|
+
if (envFlagIsTruthy(process.env.GUARDEX_SKIP_UPDATE_CHECK)) {
|
|
51
|
+
return { checked: false, reason: 'disabled' };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const forceCheck = envFlagIsTruthy(process.env.GUARDEX_FORCE_UPDATE_CHECK);
|
|
55
|
+
if (!forceCheck && !isInteractiveTerminal()) {
|
|
56
|
+
return { checked: false, reason: 'non-interactive' };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const result = run(NPM_BIN, ['view', packageJson.name, 'version', '--json'], { timeout: 5000 });
|
|
60
|
+
if (result.status !== 0) {
|
|
61
|
+
return { checked: false, reason: 'lookup-failed' };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const latest = parseNpmVersionOutput(result.stdout);
|
|
65
|
+
if (!latest) {
|
|
66
|
+
return { checked: false, reason: 'invalid-latest-version' };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
checked: true,
|
|
71
|
+
current: packageJson.version,
|
|
72
|
+
latest,
|
|
73
|
+
updateAvailable: isNewerVersion(latest, packageJson.version),
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function printUpdateAvailableBanner(current, latest) {
|
|
78
|
+
const title = colorize('UPDATE AVAILABLE', '1;33');
|
|
79
|
+
console.log(`[${TOOL_NAME}] ${title}`);
|
|
80
|
+
console.log(`[${TOOL_NAME}] Current: ${current}`);
|
|
81
|
+
console.log(`[${TOOL_NAME}] Latest : ${latest}`);
|
|
82
|
+
console.log(`[${TOOL_NAME}] Command: ${NPM_BIN} i -g ${packageJson.name}@latest`);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function maybeSelfUpdateBeforeStatus() {
|
|
86
|
+
return toolchainModule.maybeSelfUpdateBeforeStatus();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function readInstalledGuardexVersion() {
|
|
90
|
+
const installInfo = readInstalledGuardexInstallInfo();
|
|
91
|
+
return installInfo ? installInfo.version : null;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function readInstalledGuardexInstallInfo() {
|
|
95
|
+
try {
|
|
96
|
+
const rootResult = run(NPM_BIN, ['root', '-g'], { timeout: 5000 });
|
|
97
|
+
if (rootResult.status !== 0) {
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
const globalRoot = String(rootResult.stdout || '').trim();
|
|
101
|
+
if (!globalRoot) {
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
const installedPkgPath = path.join(globalRoot, packageJson.name, 'package.json');
|
|
105
|
+
if (!fs.existsSync(installedPkgPath)) {
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
const parsed = JSON.parse(fs.readFileSync(installedPkgPath, 'utf8'));
|
|
109
|
+
if (parsed && typeof parsed.version === 'string') {
|
|
110
|
+
let binRelative = null;
|
|
111
|
+
if (typeof parsed.bin === 'string') {
|
|
112
|
+
binRelative = parsed.bin;
|
|
113
|
+
} else if (parsed.bin && typeof parsed.bin === 'object') {
|
|
114
|
+
const invokedName = path.basename(process.argv[1] || '');
|
|
115
|
+
binRelative =
|
|
116
|
+
parsed.bin[invokedName] ||
|
|
117
|
+
parsed.bin[SHORT_TOOL_NAME] ||
|
|
118
|
+
Object.values(parsed.bin).find((value) => typeof value === 'string') ||
|
|
119
|
+
null;
|
|
120
|
+
}
|
|
121
|
+
const packageRoot = path.dirname(installedPkgPath);
|
|
122
|
+
const binPath = binRelative ? path.join(packageRoot, binRelative) : null;
|
|
123
|
+
return {
|
|
124
|
+
version: parsed.version,
|
|
125
|
+
packageRoot,
|
|
126
|
+
binPath,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
} catch (error) {
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function restartIntoUpdatedGuardex(expectedVersion) {
|
|
136
|
+
const installInfo = readInstalledGuardexInstallInfo();
|
|
137
|
+
if (!installInfo || installInfo.version !== expectedVersion || installInfo.version === packageJson.version) {
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
if (!installInfo.binPath || !fs.existsSync(installInfo.binPath)) {
|
|
141
|
+
console.log(`[${TOOL_NAME}] Restart required to use ${installInfo.version}. Rerun ${SHORT_TOOL_NAME}.`);
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
console.log(`[${TOOL_NAME}] Restarting into ${installInfo.version}…`);
|
|
146
|
+
const restartResult = cp.spawnSync(
|
|
147
|
+
process.execPath,
|
|
148
|
+
[installInfo.binPath, ...process.argv.slice(2)],
|
|
149
|
+
{
|
|
150
|
+
cwd: process.cwd(),
|
|
151
|
+
env: {
|
|
152
|
+
...process.env,
|
|
153
|
+
GUARDEX_SKIP_UPDATE_CHECK: '1',
|
|
154
|
+
},
|
|
155
|
+
stdio: 'inherit',
|
|
156
|
+
},
|
|
157
|
+
);
|
|
158
|
+
if (restartResult.error) {
|
|
159
|
+
console.log(
|
|
160
|
+
`[${TOOL_NAME}] Restart into ${installInfo.version} failed. Rerun ${SHORT_TOOL_NAME}.`,
|
|
161
|
+
);
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
process.exit(restartResult.status == null ? 0 : restartResult.status);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function checkForOpenSpecPackageUpdate() {
|
|
168
|
+
if (envFlagIsTruthy(process.env.GUARDEX_SKIP_OPENSPEC_UPDATE_CHECK)) {
|
|
169
|
+
return { checked: false, reason: 'disabled' };
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const forceCheck = envFlagIsTruthy(process.env.GUARDEX_FORCE_OPENSPEC_UPDATE_CHECK);
|
|
173
|
+
if (!forceCheck && !isInteractiveTerminal()) {
|
|
174
|
+
return { checked: false, reason: 'non-interactive' };
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const detection = detectGlobalToolchainPackages();
|
|
178
|
+
if (!detection.ok) {
|
|
179
|
+
return { checked: false, reason: 'package-detect-failed' };
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const current = String((detection.installedVersions || {})[OPENSPEC_PACKAGE] || '').trim();
|
|
183
|
+
if (!current) {
|
|
184
|
+
return { checked: false, reason: 'not-installed' };
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const latestResult = run(NPM_BIN, ['view', OPENSPEC_PACKAGE, 'version', '--json'], { timeout: 5000 });
|
|
188
|
+
if (latestResult.status !== 0) {
|
|
189
|
+
return { checked: false, reason: 'lookup-failed' };
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const latest = parseNpmVersionOutput(latestResult.stdout);
|
|
193
|
+
if (!latest) {
|
|
194
|
+
return { checked: false, reason: 'invalid-latest-version' };
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return {
|
|
198
|
+
checked: true,
|
|
199
|
+
current,
|
|
200
|
+
latest,
|
|
201
|
+
updateAvailable: isNewerVersion(latest, current),
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function printOpenSpecUpdateAvailableBanner(current, latest) {
|
|
206
|
+
const title = colorize('OPENSPEC UPDATE AVAILABLE', '1;33');
|
|
207
|
+
console.log(`[${TOOL_NAME}] ${title}`);
|
|
208
|
+
console.log(`[${TOOL_NAME}] Current: ${current}`);
|
|
209
|
+
console.log(`[${TOOL_NAME}] Latest : ${latest}`);
|
|
210
|
+
console.log(`[${TOOL_NAME}] Command: ${NPM_BIN} i -g ${OPENSPEC_PACKAGE}@latest`);
|
|
211
|
+
console.log(`[${TOOL_NAME}] Then : ${OPENSPEC_BIN} update`);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function maybeOpenSpecUpdateBeforeStatus() {
|
|
215
|
+
return toolchainModule.maybeOpenSpecUpdateBeforeStatus();
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function promptYesNoStrict(question) {
|
|
219
|
+
while (true) {
|
|
220
|
+
process.stdout.write(`${question} [y/n] `);
|
|
221
|
+
const answer = readSingleLineFromStdin().trim().toLowerCase();
|
|
222
|
+
|
|
223
|
+
if (answer === 'y' || answer === 'yes') {
|
|
224
|
+
process.stdout.write('\n');
|
|
225
|
+
return true;
|
|
226
|
+
}
|
|
227
|
+
if (answer === 'n' || answer === 'no') {
|
|
228
|
+
process.stdout.write('\n');
|
|
229
|
+
return false;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
process.stdout.write('Please answer with y or n.\n');
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function resolveGlobalInstallApproval(options) {
|
|
237
|
+
if (options.yesGlobalInstall && options.noGlobalInstall) {
|
|
238
|
+
throw new Error('Cannot use both --yes-global-install and --no-global-install');
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
if (options.yesGlobalInstall) {
|
|
242
|
+
return { approved: true, source: 'flag' };
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
if (options.noGlobalInstall) {
|
|
246
|
+
return { approved: false, source: 'flag' };
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
if (!isInteractiveTerminal()) {
|
|
250
|
+
return { approved: false, source: 'non-interactive-default' };
|
|
251
|
+
}
|
|
252
|
+
return { approved: true, source: 'prompt' };
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function getGlobalToolchainService(packageName) {
|
|
256
|
+
const service = GLOBAL_TOOLCHAIN_SERVICES.find(
|
|
257
|
+
(candidate) => candidate.packageName === packageName,
|
|
258
|
+
);
|
|
259
|
+
return service || { name: packageName, packageName };
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function formatGlobalToolchainServiceName(packageName) {
|
|
263
|
+
return getGlobalToolchainService(packageName).name;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function describeMissingGlobalDependencyWarnings(packageNames) {
|
|
267
|
+
return packageNames
|
|
268
|
+
.map((packageName) => getGlobalToolchainService(packageName))
|
|
269
|
+
.filter((service) => service.dependencyUrl)
|
|
270
|
+
.map(
|
|
271
|
+
(service) =>
|
|
272
|
+
`Guardex needs ${service.name} as a dependency: ${service.dependencyUrl}`,
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
function buildMissingCompanionInstallPrompt(missingPackages, missingLocalTools) {
|
|
277
|
+
const dependencyWarnings = describeMissingGlobalDependencyWarnings(missingPackages);
|
|
278
|
+
const installCommands = describeCompanionInstallCommands(missingPackages, missingLocalTools);
|
|
279
|
+
const dependencyPrefix = dependencyWarnings.length > 0
|
|
280
|
+
? `${dependencyWarnings.join(' ')} `
|
|
281
|
+
: '';
|
|
282
|
+
return `${dependencyPrefix}Install missing companion tools now? (${installCommands.join(' && ')})`;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function detectGlobalToolchainPackages() {
|
|
286
|
+
const result = run(NPM_BIN, ['list', '-g', '--depth=0', '--json']);
|
|
287
|
+
if (result.status !== 0) {
|
|
288
|
+
const stderr = (result.stderr || '').trim();
|
|
289
|
+
return {
|
|
290
|
+
ok: false,
|
|
291
|
+
error: stderr || 'Unable to detect globally installed npm packages',
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
let parsed;
|
|
296
|
+
try {
|
|
297
|
+
parsed = JSON.parse(result.stdout || '{}');
|
|
298
|
+
} catch (error) {
|
|
299
|
+
return {
|
|
300
|
+
ok: false,
|
|
301
|
+
error: `Failed to parse npm list output: ${error.message}`,
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
const dependencyMap = parsed && parsed.dependencies && typeof parsed.dependencies === 'object'
|
|
306
|
+
? parsed.dependencies
|
|
307
|
+
: {};
|
|
308
|
+
const installedSet = new Set(Object.keys(dependencyMap));
|
|
309
|
+
|
|
310
|
+
const installed = [];
|
|
311
|
+
const missing = [];
|
|
312
|
+
const installedVersions = {};
|
|
313
|
+
for (const pkg of GLOBAL_TOOLCHAIN_PACKAGES) {
|
|
314
|
+
if (installedSet.has(pkg)) {
|
|
315
|
+
installed.push(pkg);
|
|
316
|
+
const rawVersion = dependencyMap[pkg] && dependencyMap[pkg].version;
|
|
317
|
+
const version = String(rawVersion || '').trim();
|
|
318
|
+
if (version) {
|
|
319
|
+
installedVersions[pkg] = version;
|
|
320
|
+
}
|
|
321
|
+
} else {
|
|
322
|
+
missing.push(pkg);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
return { ok: true, installed, missing, installedVersions };
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
function detectRequiredSystemTools() {
|
|
330
|
+
const services = [];
|
|
331
|
+
for (const tool of REQUIRED_SYSTEM_TOOLS) {
|
|
332
|
+
const result = run(tool.command, ['--version']);
|
|
333
|
+
const active = result.status === 0;
|
|
334
|
+
const rawReason = result.error && result.error.code
|
|
335
|
+
? result.error.code
|
|
336
|
+
: (result.stderr || '').trim();
|
|
337
|
+
const reason = rawReason.split('\n')[0] || '';
|
|
338
|
+
services.push({
|
|
339
|
+
name: tool.name,
|
|
340
|
+
displayName: tool.displayName || tool.name,
|
|
341
|
+
command: tool.command,
|
|
342
|
+
installHint: tool.installHint,
|
|
343
|
+
status: active ? 'active' : 'inactive',
|
|
344
|
+
reason,
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
return services;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
function detectOptionalLocalCompanionTools() {
|
|
351
|
+
return OPTIONAL_LOCAL_COMPANION_TOOLS.map((tool) => {
|
|
352
|
+
const detectedPath = tool.candidatePaths
|
|
353
|
+
.map((relativePath) => path.join(GUARDEX_HOME_DIR, relativePath))
|
|
354
|
+
.find((candidatePath) => fs.existsSync(candidatePath));
|
|
355
|
+
return {
|
|
356
|
+
name: tool.name,
|
|
357
|
+
displayName: tool.displayName || tool.name,
|
|
358
|
+
installCommand: tool.installCommand,
|
|
359
|
+
installArgs: [...tool.installArgs],
|
|
360
|
+
status: detectedPath ? 'active' : 'inactive',
|
|
361
|
+
detectedPath: detectedPath || null,
|
|
362
|
+
};
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
function describeCompanionInstallCommands(missingPackages, missingLocalTools) {
|
|
367
|
+
const commands = [];
|
|
368
|
+
if (missingPackages.length > 0) {
|
|
369
|
+
commands.push(`${NPM_BIN} i -g ${missingPackages.join(' ')}`);
|
|
370
|
+
}
|
|
371
|
+
for (const tool of missingLocalTools) {
|
|
372
|
+
commands.push(tool.installCommand);
|
|
373
|
+
}
|
|
374
|
+
return commands;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
function askGlobalInstallForMissing(options, missingPackages, missingLocalTools) {
|
|
378
|
+
const approval = resolveGlobalInstallApproval(options);
|
|
379
|
+
if (!approval.approved) {
|
|
380
|
+
return approval;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
if (approval.source === 'prompt') {
|
|
384
|
+
const approved = promptYesNoStrict(
|
|
385
|
+
buildMissingCompanionInstallPrompt(missingPackages, missingLocalTools),
|
|
386
|
+
);
|
|
387
|
+
return { approved, source: 'prompt' };
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
return approval;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
function installGlobalToolchain(options) {
|
|
394
|
+
return toolchainModule.installGlobalToolchain(options);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
module.exports = {
|
|
398
|
+
parseNpmVersionOutput,
|
|
399
|
+
checkForGuardexUpdate,
|
|
400
|
+
printUpdateAvailableBanner,
|
|
401
|
+
maybeSelfUpdateBeforeStatus,
|
|
402
|
+
readInstalledGuardexVersion,
|
|
403
|
+
readInstalledGuardexInstallInfo,
|
|
404
|
+
restartIntoUpdatedGuardex,
|
|
405
|
+
checkForOpenSpecPackageUpdate,
|
|
406
|
+
printOpenSpecUpdateAvailableBanner,
|
|
407
|
+
maybeOpenSpecUpdateBeforeStatus,
|
|
408
|
+
promptYesNoStrict,
|
|
409
|
+
resolveGlobalInstallApproval,
|
|
410
|
+
getGlobalToolchainService,
|
|
411
|
+
formatGlobalToolchainServiceName,
|
|
412
|
+
describeMissingGlobalDependencyWarnings,
|
|
413
|
+
buildMissingCompanionInstallPrompt,
|
|
414
|
+
detectGlobalToolchainPackages,
|
|
415
|
+
detectRequiredSystemTools,
|
|
416
|
+
detectOptionalLocalCompanionTools,
|
|
417
|
+
describeCompanionInstallCommands,
|
|
418
|
+
askGlobalInstallForMissing,
|
|
419
|
+
installGlobalToolchain,
|
|
420
|
+
};
|