feat(cli): restore IDE grouping using groupMultiselect

Replace flat multiselect with native @clack/prompts groupMultiselect
component to restore visual grouping of IDE/tool options:
- "Previously Configured" - pre-selected IDEs from existing install
- "Recommended Tools" - starred preferred options
- "Additional Tools" - other available options

This restores the grouped UX that was lost during the Inquirer.js
to @clack/prompts migration.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Davor Racić 2026-01-14 12:21:40 +01:00
parent ae3dc2ce01
commit 1beaa0118b
2 changed files with 48 additions and 24 deletions

View File

@ -166,6 +166,30 @@ async function multiselect(options) {
return result;
}
/**
* Grouped multi-select prompt for categorized options
* @param {Object} options - Prompt options
* @param {string} options.message - The question to ask
* @param {Object} options.options - Object mapping group names to arrays of choices
* @param {Array} [options.initialValues] - Array of initially selected values
* @param {boolean} [options.required=false] - Whether at least one must be selected
* @param {boolean} [options.selectableGroups=false] - Whether groups can be selected as a whole
* @returns {Promise<Array>} Array of selected values
*/
async function groupMultiselect(options) {
const clack = await getClack();
const result = await clack.groupMultiselect({
message: options.message,
options: options.options,
initialValues: options.initialValues,
required: options.required || false,
});
await handleCancel(result);
return result;
}
/**
* Confirm prompt (replaces Inquirer 'confirm' type)
* @param {Object} options - Prompt options
@ -397,6 +421,7 @@ module.exports = {
spinner,
select,
multiselect,
groupMultiselect,
confirm,
text,
password,

View File

@ -442,13 +442,14 @@ class UI {
const preferredIdes = ideManager.getPreferredIdes();
const otherIdes = ideManager.getOtherIdes();
// Build IDE choices array with separators
const ideChoices = [];
// Build grouped options object for groupMultiselect
const groupedOptions = {};
const processedIdes = new Set();
const initialValues = [];
// First, add previously configured IDEs at the top, marked with ✅
if (configuredIdes.length > 0) {
ideChoices.push(new choiceUtils.Separator('── Previously Configured ──'));
const configuredGroup = [];
for (const ideValue of configuredIdes) {
// Skip empty or invalid IDE values
if (!ideValue || typeof ideValue !== 'string') {
@ -461,44 +462,41 @@ class UI {
const ide = preferredIde || otherIde;
if (ide) {
ideChoices.push({
name: `${ide.name}`,
configuredGroup.push({
label: `${ide.name}`,
value: ide.value,
checked: true, // Previously configured IDEs are checked by default
});
processedIdes.add(ide.value);
initialValues.push(ide.value); // Pre-select configured IDEs
} else {
// Warn about unrecognized IDE (but don't fail)
console.log(chalk.yellow(`⚠️ Previously configured IDE '${ideValue}' is no longer available`));
}
}
if (configuredGroup.length > 0) {
groupedOptions['Previously Configured'] = configuredGroup;
}
}
// Add preferred tools (excluding already processed)
const remainingPreferred = preferredIdes.filter((ide) => !processedIdes.has(ide.value));
if (remainingPreferred.length > 0) {
ideChoices.push(new choiceUtils.Separator('── Recommended Tools ──'));
for (const ide of remainingPreferred) {
ideChoices.push({
name: `${ide.name}`,
value: ide.value,
checked: false,
});
groupedOptions['Recommended Tools'] = remainingPreferred.map((ide) => {
processedIdes.add(ide.value);
}
return {
label: `${ide.name}`,
value: ide.value,
};
});
}
// Add other tools (excluding already processed)
const remainingOther = otherIdes.filter((ide) => !processedIdes.has(ide.value));
if (remainingOther.length > 0) {
ideChoices.push(new choiceUtils.Separator('── Additional Tools ──'));
for (const ide of remainingOther) {
ideChoices.push({
name: ide.name,
groupedOptions['Additional Tools'] = remainingOther.map((ide) => ({
label: ide.name,
value: ide.value,
checked: false,
});
}
}));
}
let selectedIdes = [];
@ -506,9 +504,10 @@ class UI {
// Loop until user selects at least one tool OR explicitly confirms no tools
while (!userConfirmedNoTools) {
selectedIdes = await prompts.multiselect({
selectedIdes = await prompts.groupMultiselect({
message: `Select tools to configure ${chalk.dim('(↑/↓ navigate, SPACE select, ENTER confirm)')}:`,
choices: ideChoices,
options: groupedOptions,
initialValues: initialValues.length > 0 ? initialValues : undefined,
required: false,
});