Skip to content

Commit 2b7f7ff

Browse files
Brian MadisonBrian Madison
authored andcommitted
minor updates to installer multiselects
1 parent 3360666 commit 2b7f7ff

File tree

4 files changed

+77
-66
lines changed

4 files changed

+77
-66
lines changed

tools/cli/installers/lib/ide/antigravity.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -345,7 +345,7 @@ class AntigravitySetup extends BaseIdeSetup {
345345
};
346346

347347
const selected = await prompts.multiselect({
348-
message: `Select subagents to install ${chalk.dim('(↑/↓ navigate, SPACE select, ENTER confirm)')}:`,
348+
message: `Select subagents to install ${chalk.dim('(↑/↓ navigates multiselect, SPACE toggles, A to toggles All, ENTER confirm)')}:`,
349349
choices: subagentConfig.files.map((file) => ({
350350
name: `${file.replace('.md', '')} - ${subagentInfo[file] || 'Specialized assistant'}`,
351351
value: file,

tools/cli/installers/lib/ide/claude-code.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -353,7 +353,7 @@ class ClaudeCodeSetup extends BaseIdeSetup {
353353
};
354354

355355
const selected = await prompts.multiselect({
356-
message: `Select subagents to install ${chalk.dim('(↑/↓ navigate, SPACE select, ENTER confirm)')}:`,
356+
message: `Select subagents to install ${chalk.dim('(↑/↓ navigates multiselect, SPACE toggles, A to toggles All, ENTER confirm)')}:`,
357357
options: subagentConfig.files.map((file) => ({
358358
label: `${file.replace('.md', '')} - ${subagentInfo[file] || 'Specialized assistant'}`,
359359
value: file,

tools/cli/lib/prompts.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ async function groupMultiselect(options) {
184184
options: options.options,
185185
initialValues: options.initialValues,
186186
required: options.required || false,
187+
selectableGroups: options.selectableGroups || false,
187188
});
188189

189190
await handleCancel(result);

tools/cli/lib/ui.js

Lines changed: 74 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -395,7 +395,7 @@ class UI {
395395
const processedIdes = new Set();
396396
const initialValues = [];
397397

398-
// First, add previously configured IDEs at the top, marked with ✅
398+
// First, add previously configured IDEs, marked with ✅
399399
if (configuredIdes.length > 0) {
400400
const configuredGroup = [];
401401
for (const ideValue of configuredIdes) {
@@ -447,42 +447,33 @@ class UI {
447447
}));
448448
}
449449

450+
// Add standalone "None" option at the end
451+
groupedOptions[' '] = [
452+
{
453+
label: '⚠ None - I am not installing any tools',
454+
value: '__NONE__',
455+
},
456+
];
457+
450458
let selectedIdes = [];
451-
let userConfirmedNoTools = false;
452-
453-
// Loop until user selects at least one tool OR explicitly confirms no tools
454-
while (!userConfirmedNoTools) {
455-
selectedIdes = await prompts.groupMultiselect({
456-
message: `Select tools to configure ${chalk.dim('(↑/↓ navigate, SPACE select, ENTER confirm)')}:`,
457-
options: groupedOptions,
458-
initialValues: initialValues.length > 0 ? initialValues : undefined,
459-
required: false,
460-
});
461459

462-
// If tools were selected, we're done
463-
if (selectedIdes && selectedIdes.length > 0) {
464-
break;
465-
}
460+
selectedIdes = await prompts.groupMultiselect({
461+
message: `Select tools to configure ${chalk.dim('(↑/↓ navigates multiselect, SPACE toggles, A to toggles All, ENTER confirm)')}:`,
462+
options: groupedOptions,
463+
initialValues: initialValues.length > 0 ? initialValues : undefined,
464+
required: true,
465+
selectableGroups: false,
466+
});
466467

467-
// Warn that no tools were selected - users often miss the spacebar requirement
468+
// If user selected both "__NONE__" and other tools, honor the "None" choice
469+
if (selectedIdes && selectedIdes.includes('__NONE__') && selectedIdes.length > 1) {
468470
console.log();
469-
console.log(chalk.red.bold('⚠️ WARNING: No tools were selected!'));
470-
console.log(chalk.red(' You must press SPACE to select items, then ENTER to confirm.'));
471-
console.log(chalk.red(' Simply highlighting an item does NOT select it.'));
471+
console.log(chalk.yellow('⚠️ "None - I am not installing any tools" was selected, so no tools will be configured.'));
472472
console.log();
473-
474-
const goBack = await prompts.confirm({
475-
message: chalk.yellow('Would you like to go back and select at least one tool?'),
476-
default: true,
477-
});
478-
479-
if (goBack) {
480-
// Re-display a message before looping back
481-
console.log();
482-
} else {
483-
// User explicitly chose to proceed without tools
484-
userConfirmedNoTools = true;
485-
}
473+
selectedIdes = [];
474+
} else if (selectedIdes && selectedIdes.includes('__NONE__')) {
475+
// Only "__NONE__" was selected
476+
selectedIdes = [];
486477
}
487478

488479
return {
@@ -509,27 +500,6 @@ class UI {
509500
return { backupFirst, preserveCustomizations };
510501
}
511502

512-
/**
513-
* Prompt for module selection
514-
* @param {Array} modules - Available modules
515-
* @returns {Array} Selected modules
516-
*/
517-
async promptModules(modules) {
518-
const choices = modules.map((mod) => ({
519-
name: `${mod.name} - ${mod.description}`,
520-
value: mod.id,
521-
checked: false,
522-
}));
523-
524-
const selectedModules = await prompts.multiselect({
525-
message: `Select modules to add ${chalk.dim('(↑/↓ navigate, SPACE select, ENTER confirm)')}:`,
526-
choices,
527-
required: true,
528-
});
529-
530-
return selectedModules;
531-
}
532-
533503
/**
534504
* Confirm action
535505
* @param {string} message - Confirmation message
@@ -697,20 +667,40 @@ class UI {
697667
* @param {Array} moduleChoices - Available module choices
698668
* @returns {Array} Selected module IDs
699669
*/
700-
async selectModules(moduleChoices, defaultSelections = []) {
701-
// Mark choices as checked based on defaultSelections
670+
async selectModules(moduleChoices, defaultSelections = null) {
671+
// If defaultSelections is provided, use it to override checked state
672+
// Otherwise preserve the checked state from moduleChoices (set by getModuleChoices)
702673
const choicesWithDefaults = moduleChoices.map((choice) => ({
703674
...choice,
704-
checked: defaultSelections.includes(choice.value),
675+
...(defaultSelections === null ? {} : { checked: defaultSelections.includes(choice.value) }),
705676
}));
706677

678+
// Add a "None" option at the end for users who changed their mind
679+
const choicesWithSkipOption = [
680+
...choicesWithDefaults,
681+
{
682+
value: '__NONE__',
683+
label: '⚠ None / I changed my mind - skip module installation',
684+
checked: false,
685+
},
686+
];
687+
707688
const selected = await prompts.multiselect({
708-
message: `Select modules to install ${chalk.dim('(↑/↓ navigate, SPACE select, ENTER confirm)')}:`,
709-
choices: choicesWithDefaults,
710-
required: false,
689+
message: `Select modules to install ${chalk.dim('(↑/↓ navigates multiselect, SPACE toggles, A to toggles All, ENTER confirm)')}:`,
690+
choices: choicesWithSkipOption,
691+
required: true,
711692
});
712693

713-
return selected || [];
694+
// If user selected both "__NONE__" and other items, honor the "None" choice
695+
if (selected && selected.includes('__NONE__') && selected.length > 1) {
696+
console.log();
697+
console.log(chalk.yellow('⚠️ "None / I changed my mind" was selected, so no modules will be installed.'));
698+
console.log();
699+
return [];
700+
}
701+
702+
// Filter out the special '__NONE__' value
703+
return selected ? selected.filter((m) => m !== '__NONE__') : [];
714704
}
715705

716706
/**
@@ -1255,12 +1245,32 @@ class UI {
12551245
checked: m.checked,
12561246
}));
12571247

1248+
// Add "None / I changed my mind" option at the end
1249+
const choicesWithSkip = [
1250+
...selectChoices,
1251+
{
1252+
name: '⚠ None / I changed my mind - keep no custom modules',
1253+
value: '__NONE__',
1254+
checked: false,
1255+
},
1256+
];
1257+
12581258
const keepModules = await prompts.multiselect({
1259-
message: `Select custom modules to keep ${chalk.dim('(↑/↓ navigate, SPACE select, ENTER confirm)')}:`,
1260-
choices: selectChoices,
1261-
required: false,
1259+
message: `Select custom modules to keep ${chalk.dim('(↑/↓ navigates multiselect, SPACE toggles, A to toggles All, ENTER confirm)')}:`,
1260+
choices: choicesWithSkip,
1261+
required: true,
12621262
});
1263-
result.selectedCustomModules = keepModules || [];
1263+
1264+
// If user selected both "__NONE__" and other modules, honor the "None" choice
1265+
if (keepModules && keepModules.includes('__NONE__') && keepModules.length > 1) {
1266+
console.log();
1267+
console.log(chalk.yellow('⚠️ "None / I changed my mind" was selected, so no custom modules will be kept.'));
1268+
console.log();
1269+
result.selectedCustomModules = [];
1270+
} else {
1271+
// Filter out the special '__NONE__' value
1272+
result.selectedCustomModules = keepModules ? keepModules.filter((m) => m !== '__NONE__') : [];
1273+
}
12641274
break;
12651275
}
12661276

0 commit comments

Comments
 (0)