Skip to content

Commit 0e9131b

Browse files
NagyViktNagyViktclaude
authored
feat(setup): offer VS Code extension install during gx setup (#416)
After the existing global companion-install prompt, `gx setup` now probes for the `code` CLI and, when present, offers to install the GitGuardex Active Agents VS Code extension from the Marketplace (publisher `Recodee`, id `Recodee.gitguardex-active-agents`). Behavior rules: - If `code --version` fails (no VS Code, or editor is Cursor / VSCodium / WebStorm), silently skip — Guardex does not assume an editor. - If the extension is already present in `code --list-extensions`, surface a one-line `already installed` confirmation and move on. - Interactive TTY: use the existing `promptYesNoStrict` helper for an explicit Y/N question before doing anything. - Non-interactive: respect `--yes-global-install` / `--no-global-install`. With neither flag, print a one-line install hint and skip. - Dry-run: report the offer without calling `code --install-extension`. - `GUARDEX_SKIP_VSCODE_EXT_PROMPT=1` silences the prompt entirely for users who never want it (matches the existing `GUARDEX_SKIP_*` opt-out family). - Install failure is non-fatal; we keep the rest of setup running and print the exact `code --install-extension Recodee.gitguardex-active-agents` retry command. Unrelated pre-existing setup.test.js failures (canonical-bundle hash drift, missing `git worktree add --orphan`, harness slug-prefix regex) are not addressed here; the 39 pass / 3 fail ratio is unchanged by this commit. Co-authored-by: NagyVikt <nagy.viktordp@gmail.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 1027a0d commit 0e9131b

1 file changed

Lines changed: 81 additions & 0 deletions

File tree

src/cli/main.js

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1234,6 +1234,84 @@ function promptYesNoStrict(question) {
12341234
}
12351235
}
12361236

1237+
const VSCODE_EXTENSION_ID = 'Recodee.gitguardex-active-agents';
1238+
const VSCODE_EXTENSION_DISPLAY_NAME = 'GitGuardex Active Agents';
1239+
1240+
function maybePromptInstallVscodeExtension(options) {
1241+
if (options.dryRun) {
1242+
console.log(
1243+
`[${TOOL_NAME}] (dry-run) Would offer to install VS Code extension '${VSCODE_EXTENSION_ID}'.`,
1244+
);
1245+
return;
1246+
}
1247+
1248+
if (envFlagIsTruthy(process.env.GUARDEX_SKIP_VSCODE_EXT_PROMPT)) {
1249+
return;
1250+
}
1251+
1252+
const codeProbe = cp.spawnSync('code', ['--version'], { stdio: 'ignore' });
1253+
if (codeProbe.error || codeProbe.status !== 0) {
1254+
return;
1255+
}
1256+
1257+
const listProbe = cp.spawnSync('code', ['--list-extensions'], {
1258+
encoding: 'utf8',
1259+
stdio: ['ignore', 'pipe', 'ignore'],
1260+
});
1261+
if (!listProbe.error && listProbe.status === 0) {
1262+
const alreadyInstalled = String(listProbe.stdout || '')
1263+
.split('\n')
1264+
.some((line) => line.trim().toLowerCase() === VSCODE_EXTENSION_ID.toLowerCase());
1265+
if (alreadyInstalled) {
1266+
console.log(
1267+
`[${TOOL_NAME}] ✅ VS Code extension '${VSCODE_EXTENSION_ID}' already installed.`,
1268+
);
1269+
return;
1270+
}
1271+
}
1272+
1273+
let approved;
1274+
if (options.yesGlobalInstall) {
1275+
approved = true;
1276+
} else if (options.noGlobalInstall) {
1277+
approved = false;
1278+
} else if (!isInteractiveTerminal()) {
1279+
console.log(
1280+
`[${TOOL_NAME}] Optional VS Code extension '${VSCODE_EXTENSION_ID}' ` +
1281+
`(${VSCODE_EXTENSION_DISPLAY_NAME}) not installed. ` +
1282+
`Install later: code --install-extension ${VSCODE_EXTENSION_ID}`,
1283+
);
1284+
return;
1285+
} else {
1286+
approved = promptYesNoStrict(
1287+
`Install VS Code extension '${VSCODE_EXTENSION_ID}' (${VSCODE_EXTENSION_DISPLAY_NAME}) now?`,
1288+
);
1289+
}
1290+
1291+
if (!approved) {
1292+
console.log(
1293+
`[${TOOL_NAME}] ⚠️ VS Code extension skipped. ` +
1294+
`Set GUARDEX_SKIP_VSCODE_EXT_PROMPT=1 to silence or run 'code --install-extension ${VSCODE_EXTENSION_ID}' later.`,
1295+
);
1296+
return;
1297+
}
1298+
1299+
const install = cp.spawnSync('code', ['--install-extension', VSCODE_EXTENSION_ID], {
1300+
stdio: 'inherit',
1301+
});
1302+
if (install.error || install.status !== 0) {
1303+
console.log(
1304+
`[${TOOL_NAME}] ⚠️ VS Code extension install failed. ` +
1305+
`Retry manually: code --install-extension ${VSCODE_EXTENSION_ID}`,
1306+
);
1307+
return;
1308+
}
1309+
console.log(
1310+
`[${TOOL_NAME}] ✅ VS Code extension '${VSCODE_EXTENSION_ID}' installed. ` +
1311+
`Reload the VS Code window to activate it.`,
1312+
);
1313+
}
1314+
12371315
function resolveGlobalInstallApproval(options) {
12381316
if (options.yesGlobalInstall && options.noGlobalInstall) {
12391317
throw new Error('Cannot use both --yes-global-install and --no-global-install');
@@ -2835,6 +2913,9 @@ function setup(rawArgs) {
28352913
console.log(`[${TOOL_NAME}] ⚠️ ${warning}`);
28362914
}
28372915
}
2916+
2917+
maybePromptInstallVscodeExtension(options);
2918+
28382919
const requiredSystemTools = toolchainModule.detectRequiredSystemTools();
28392920
const missingSystemTools = requiredSystemTools.filter((tool) => tool.status !== 'active');
28402921
if (missingSystemTools.length === 0) {

0 commit comments

Comments
 (0)