minor updates to installer multiselects

This commit is contained in:
Brian Madison 2026-01-14 23:48:50 -06:00
parent 3360666c2a
commit 2b7f7ff421
4 changed files with 77 additions and 66 deletions

View File

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

View File

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

View File

@ -184,6 +184,7 @@ async function groupMultiselect(options) {
options: options.options, options: options.options,
initialValues: options.initialValues, initialValues: options.initialValues,
required: options.required || false, required: options.required || false,
selectableGroups: options.selectableGroups || false,
}); });
await handleCancel(result); await handleCancel(result);

View File

@ -395,7 +395,7 @@ class UI {
const processedIdes = new Set(); const processedIdes = new Set();
const initialValues = []; const initialValues = [];
// First, add previously configured IDEs at the top, marked with ✅ // First, add previously configured IDEs, marked with ✅
if (configuredIdes.length > 0) { if (configuredIdes.length > 0) {
const configuredGroup = []; const configuredGroup = [];
for (const ideValue of configuredIdes) { for (const ideValue of configuredIdes) {
@ -447,42 +447,33 @@ class UI {
})); }));
} }
let selectedIdes = []; // Add standalone "None" option at the end
let userConfirmedNoTools = false; groupedOptions[' '] = [
{
label: '⚠ None - I am not installing any tools',
value: '__NONE__',
},
];
let selectedIdes = [];
// Loop until user selects at least one tool OR explicitly confirms no tools
while (!userConfirmedNoTools) {
selectedIdes = await prompts.groupMultiselect({ selectedIdes = await prompts.groupMultiselect({
message: `Select tools to configure ${chalk.dim('(↑/↓ navigate, SPACE select, ENTER confirm)')}:`, message: `Select tools to configure ${chalk.dim('(↑/↓ navigates multiselect, SPACE toggles, A to toggles All, ENTER confirm)')}:`,
options: groupedOptions, options: groupedOptions,
initialValues: initialValues.length > 0 ? initialValues : undefined, initialValues: initialValues.length > 0 ? initialValues : undefined,
required: false, required: true,
selectableGroups: false,
}); });
// If tools were selected, we're done // If user selected both "__NONE__" and other tools, honor the "None" choice
if (selectedIdes && selectedIdes.length > 0) { if (selectedIdes && selectedIdes.includes('__NONE__') && selectedIdes.length > 1) {
break;
}
// Warn that no tools were selected - users often miss the spacebar requirement
console.log(); console.log();
console.log(chalk.red.bold('⚠️ WARNING: No tools were selected!')); console.log(chalk.yellow('⚠️ "None - I am not installing any tools" was selected, so no tools will be configured.'));
console.log(chalk.red(' You must press SPACE to select items, then ENTER to confirm.'));
console.log(chalk.red(' Simply highlighting an item does NOT select it.'));
console.log(); console.log();
selectedIdes = [];
const goBack = await prompts.confirm({ } else if (selectedIdes && selectedIdes.includes('__NONE__')) {
message: chalk.yellow('Would you like to go back and select at least one tool?'), // Only "__NONE__" was selected
default: true, selectedIdes = [];
});
if (goBack) {
// Re-display a message before looping back
console.log();
} else {
// User explicitly chose to proceed without tools
userConfirmedNoTools = true;
}
} }
return { return {
@ -509,27 +500,6 @@ class UI {
return { backupFirst, preserveCustomizations }; return { backupFirst, preserveCustomizations };
} }
/**
* Prompt for module selection
* @param {Array} modules - Available modules
* @returns {Array} Selected modules
*/
async promptModules(modules) {
const choices = modules.map((mod) => ({
name: `${mod.name} - ${mod.description}`,
value: mod.id,
checked: false,
}));
const selectedModules = await prompts.multiselect({
message: `Select modules to add ${chalk.dim('(↑/↓ navigate, SPACE select, ENTER confirm)')}:`,
choices,
required: true,
});
return selectedModules;
}
/** /**
* Confirm action * Confirm action
* @param {string} message - Confirmation message * @param {string} message - Confirmation message
@ -697,20 +667,40 @@ class UI {
* @param {Array} moduleChoices - Available module choices * @param {Array} moduleChoices - Available module choices
* @returns {Array} Selected module IDs * @returns {Array} Selected module IDs
*/ */
async selectModules(moduleChoices, defaultSelections = []) { async selectModules(moduleChoices, defaultSelections = null) {
// Mark choices as checked based on defaultSelections // If defaultSelections is provided, use it to override checked state
// Otherwise preserve the checked state from moduleChoices (set by getModuleChoices)
const choicesWithDefaults = moduleChoices.map((choice) => ({ const choicesWithDefaults = moduleChoices.map((choice) => ({
...choice, ...choice,
checked: defaultSelections.includes(choice.value), ...(defaultSelections === null ? {} : { checked: defaultSelections.includes(choice.value) }),
})); }));
// Add a "None" option at the end for users who changed their mind
const choicesWithSkipOption = [
...choicesWithDefaults,
{
value: '__NONE__',
label: '⚠ None / I changed my mind - skip module installation',
checked: false,
},
];
const selected = await prompts.multiselect({ const selected = await prompts.multiselect({
message: `Select modules to install ${chalk.dim('(↑/↓ navigate, SPACE select, ENTER confirm)')}:`, message: `Select modules to install ${chalk.dim('(↑/↓ navigates multiselect, SPACE toggles, A to toggles All, ENTER confirm)')}:`,
choices: choicesWithDefaults, choices: choicesWithSkipOption,
required: false, required: true,
}); });
return selected || []; // If user selected both "__NONE__" and other items, honor the "None" choice
if (selected && selected.includes('__NONE__') && selected.length > 1) {
console.log();
console.log(chalk.yellow('⚠️ "None / I changed my mind" was selected, so no modules will be installed.'));
console.log();
return [];
}
// Filter out the special '__NONE__' value
return selected ? selected.filter((m) => m !== '__NONE__') : [];
} }
/** /**
@ -1255,12 +1245,32 @@ class UI {
checked: m.checked, checked: m.checked,
})); }));
// Add "None / I changed my mind" option at the end
const choicesWithSkip = [
...selectChoices,
{
name: '⚠ None / I changed my mind - keep no custom modules',
value: '__NONE__',
checked: false,
},
];
const keepModules = await prompts.multiselect({ const keepModules = await prompts.multiselect({
message: `Select custom modules to keep ${chalk.dim('(↑/↓ navigate, SPACE select, ENTER confirm)')}:`, message: `Select custom modules to keep ${chalk.dim('(↑/↓ navigates multiselect, SPACE toggles, A to toggles All, ENTER confirm)')}:`,
choices: selectChoices, choices: choicesWithSkip,
required: false, required: true,
}); });
result.selectedCustomModules = keepModules || [];
// If user selected both "__NONE__" and other modules, honor the "None" choice
if (keepModules && keepModules.includes('__NONE__') && keepModules.length > 1) {
console.log();
console.log(chalk.yellow('⚠️ "None / I changed my mind" was selected, so no custom modules will be kept.'));
console.log();
result.selectedCustomModules = [];
} else {
// Filter out the special '__NONE__' value
result.selectedCustomModules = keepModules ? keepModules.filter((m) => m !== '__NONE__') : [];
}
break; break;
} }