fix: complete non-interactive installation support
- Fix validation checks using truthy instead of !== true - Add skipPrompts flag to skip module config prompts with --yes - Add getDefaultModules() for automatic module selection with --yes - Fix IDE selection to use array check instead of length check Co-Authored-By: AiderDesk <https://github.com/hotovo/aider-desk>
This commit is contained in:
parent
8688e3eb13
commit
4aec04437e
|
|
@ -136,10 +136,12 @@ class ConfigCollector {
|
||||||
* @param {string} projectDir - Target project directory
|
* @param {string} projectDir - Target project directory
|
||||||
* @param {Object} options - Additional options
|
* @param {Object} options - Additional options
|
||||||
* @param {Map} options.customModulePaths - Map of module ID to source path for custom modules
|
* @param {Map} options.customModulePaths - Map of module ID to source path for custom modules
|
||||||
|
* @param {boolean} options.skipPrompts - Skip prompts and use defaults (for --yes flag)
|
||||||
*/
|
*/
|
||||||
async collectAllConfigurations(modules, projectDir, options = {}) {
|
async collectAllConfigurations(modules, projectDir, options = {}) {
|
||||||
// Store custom module paths for use in collectModuleConfig
|
// Store custom module paths for use in collectModuleConfig
|
||||||
this.customModulePaths = options.customModulePaths || new Map();
|
this.customModulePaths = options.customModulePaths || new Map();
|
||||||
|
this.skipPrompts = options.skipPrompts || false;
|
||||||
await this.loadExistingConfig(projectDir);
|
await this.loadExistingConfig(projectDir);
|
||||||
|
|
||||||
// Check if core was already collected (e.g., in early collection phase)
|
// Check if core was already collected (e.g., in early collection phase)
|
||||||
|
|
@ -583,47 +585,60 @@ class ConfigCollector {
|
||||||
// If there are questions to ask, prompt for accepting defaults vs customizing
|
// If there are questions to ask, prompt for accepting defaults vs customizing
|
||||||
if (questions.length > 0) {
|
if (questions.length > 0) {
|
||||||
const moduleDisplayName = moduleConfig.header || `${moduleName.toUpperCase()} Module`;
|
const moduleDisplayName = moduleConfig.header || `${moduleName.toUpperCase()} Module`;
|
||||||
console.log();
|
|
||||||
console.log(chalk.cyan('?') + ' ' + chalk.magenta(moduleDisplayName));
|
// Skip prompts mode: use all defaults without asking
|
||||||
let customize = true;
|
if (this.skipPrompts) {
|
||||||
if (moduleName === 'core') {
|
console.log(chalk.cyan('Using default configuration for'), chalk.magenta(moduleDisplayName));
|
||||||
// Core module: no confirm prompt, so add spacing manually to match visual style
|
// Use defaults for all questions
|
||||||
console.log(chalk.gray('│'));
|
for (const question of questions) {
|
||||||
|
const hasDefault = question.default !== undefined && question.default !== null && question.default !== '';
|
||||||
|
if (hasDefault && typeof question.default !== 'function') {
|
||||||
|
allAnswers[question.name] = question.default;
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Non-core modules: show "Accept Defaults?" confirm prompt (clack adds spacing)
|
console.log();
|
||||||
const customizeAnswer = await prompts.prompt([
|
console.log(chalk.cyan('?') + ' ' + chalk.magenta(moduleDisplayName));
|
||||||
{
|
let customize = true;
|
||||||
type: 'confirm',
|
if (moduleName === 'core') {
|
||||||
name: 'customize',
|
// Core module: no confirm prompt, so add spacing manually to match visual style
|
||||||
message: 'Accept Defaults (no to customize)?',
|
console.log(chalk.gray('│'));
|
||||||
default: true,
|
} else {
|
||||||
},
|
// Non-core modules: show "Accept Defaults?" confirm prompt (clack adds spacing)
|
||||||
]);
|
const customizeAnswer = await prompts.prompt([
|
||||||
customize = customizeAnswer.customize;
|
{
|
||||||
}
|
type: 'confirm',
|
||||||
|
name: 'customize',
|
||||||
|
message: 'Accept Defaults (no to customize)?',
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
customize = customizeAnswer.customize;
|
||||||
|
}
|
||||||
|
|
||||||
if (customize && moduleName !== 'core') {
|
if (customize && moduleName !== 'core') {
|
||||||
// Accept defaults - only ask questions that have NO default value
|
// Accept defaults - only ask questions that have NO default value
|
||||||
const questionsWithoutDefaults = questions.filter((q) => q.default === undefined || q.default === null || q.default === '');
|
const questionsWithoutDefaults = questions.filter((q) => q.default === undefined || q.default === null || q.default === '');
|
||||||
|
|
||||||
if (questionsWithoutDefaults.length > 0) {
|
if (questionsWithoutDefaults.length > 0) {
|
||||||
console.log(chalk.dim(`\n Asking required questions for ${moduleName.toUpperCase()}...`));
|
console.log(chalk.dim(`\n Asking required questions for ${moduleName.toUpperCase()}...`));
|
||||||
const promptedAnswers = await prompts.prompt(questionsWithoutDefaults);
|
const promptedAnswers = await prompts.prompt(questionsWithoutDefaults);
|
||||||
|
Object.assign(allAnswers, promptedAnswers);
|
||||||
|
}
|
||||||
|
|
||||||
|
// For questions with defaults that weren't asked, we need to process them with their default values
|
||||||
|
const questionsWithDefaults = questions.filter((q) => q.default !== undefined && q.default !== null && q.default !== '');
|
||||||
|
for (const question of questionsWithDefaults) {
|
||||||
|
// Skip function defaults - these are dynamic and will be evaluated later
|
||||||
|
if (typeof question.default === 'function') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
allAnswers[question.name] = question.default;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const promptedAnswers = await prompts.prompt(questions);
|
||||||
Object.assign(allAnswers, promptedAnswers);
|
Object.assign(allAnswers, promptedAnswers);
|
||||||
}
|
}
|
||||||
|
|
||||||
// For questions with defaults that weren't asked, we need to process them with their default values
|
|
||||||
const questionsWithDefaults = questions.filter((q) => q.default !== undefined && q.default !== null && q.default !== '');
|
|
||||||
for (const question of questionsWithDefaults) {
|
|
||||||
// Skip function defaults - these are dynamic and will be evaluated later
|
|
||||||
if (typeof question.default === 'function') {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
allAnswers[question.name] = question.default;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const promptedAnswers = await prompts.prompt(questions);
|
|
||||||
Object.assign(allAnswers, promptedAnswers);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -353,11 +353,13 @@ class Installer {
|
||||||
const modulesWithoutCore = allModulesForConfig.filter((m) => m !== 'core');
|
const modulesWithoutCore = allModulesForConfig.filter((m) => m !== 'core');
|
||||||
moduleConfigs = await this.configCollector.collectAllConfigurations(modulesWithoutCore, path.resolve(config.directory), {
|
moduleConfigs = await this.configCollector.collectAllConfigurations(modulesWithoutCore, path.resolve(config.directory), {
|
||||||
customModulePaths,
|
customModulePaths,
|
||||||
|
skipPrompts: config.skipPrompts,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// Core not collected yet, include it
|
// Core not collected yet, include it
|
||||||
moduleConfigs = await this.configCollector.collectAllConfigurations(allModulesForConfig, path.resolve(config.directory), {
|
moduleConfigs = await this.configCollector.collectAllConfigurations(allModulesForConfig, path.resolve(config.directory), {
|
||||||
customModulePaths,
|
customModulePaths,
|
||||||
|
skipPrompts: config.skipPrompts,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -680,7 +682,8 @@ class Installer {
|
||||||
} else {
|
} else {
|
||||||
// Pass pre-selected IDEs from early prompt (if available)
|
// Pass pre-selected IDEs from early prompt (if available)
|
||||||
// This allows IDE selection to happen before file copying, improving UX
|
// This allows IDE selection to happen before file copying, improving UX
|
||||||
const preSelectedIdes = config.ides && config.ides.length > 0 ? config.ides : null;
|
// Use config.ides if it's an array (even if empty), null means prompt
|
||||||
|
const preSelectedIdes = Array.isArray(config.ides) ? config.ides : null;
|
||||||
toolSelection = await this.collectToolConfigurations(
|
toolSelection = await this.collectToolConfigurations(
|
||||||
path.resolve(config.directory),
|
path.resolve(config.directory),
|
||||||
config.modules,
|
config.modules,
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ class UI {
|
||||||
// Use provided directory from command-line
|
// Use provided directory from command-line
|
||||||
const expandedDir = this.expandUserPath(options.directory);
|
const expandedDir = this.expandUserPath(options.directory);
|
||||||
const validation = this.validateDirectorySync(expandedDir);
|
const validation = this.validateDirectorySync(expandedDir);
|
||||||
if (validation !== true) {
|
if (validation) {
|
||||||
throw new Error(`Invalid directory: ${validation}`);
|
throw new Error(`Invalid directory: ${validation}`);
|
||||||
}
|
}
|
||||||
confirmedDirectory = expandedDir;
|
confirmedDirectory = expandedDir;
|
||||||
|
|
@ -308,7 +308,7 @@ class UI {
|
||||||
for (const customPath of paths) {
|
for (const customPath of paths) {
|
||||||
const expandedPath = this.expandUserPath(customPath);
|
const expandedPath = this.expandUserPath(customPath);
|
||||||
const validation = this.validateCustomContentPathSync(expandedPath);
|
const validation = this.validateCustomContentPathSync(expandedPath);
|
||||||
if (validation !== true) {
|
if (validation) {
|
||||||
console.log(chalk.yellow(`⚠️ Skipping invalid custom content path: ${customPath} - ${validation}`));
|
console.log(chalk.yellow(`⚠️ Skipping invalid custom content path: ${customPath} - ${validation}`));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -405,6 +405,10 @@ class UI {
|
||||||
.map((m) => m.trim())
|
.map((m) => m.trim())
|
||||||
.filter(Boolean);
|
.filter(Boolean);
|
||||||
console.log(chalk.cyan('Using modules from command-line:'), chalk.bold(selectedModules.join(', ')));
|
console.log(chalk.cyan('Using modules from command-line:'), chalk.bold(selectedModules.join(', ')));
|
||||||
|
} else if (options.yes) {
|
||||||
|
// Use default modules when --yes flag is set
|
||||||
|
selectedModules = await this.getDefaultModules(installedModuleIds);
|
||||||
|
console.log(chalk.cyan('Using default modules (--yes flag):'), chalk.bold(selectedModules.join(', ')));
|
||||||
} else {
|
} else {
|
||||||
selectedModules = await this.selectAllModules(installedModuleIds);
|
selectedModules = await this.selectAllModules(installedModuleIds);
|
||||||
}
|
}
|
||||||
|
|
@ -425,7 +429,7 @@ class UI {
|
||||||
for (const customPath of paths) {
|
for (const customPath of paths) {
|
||||||
const expandedPath = this.expandUserPath(customPath);
|
const expandedPath = this.expandUserPath(customPath);
|
||||||
const validation = this.validateCustomContentPathSync(expandedPath);
|
const validation = this.validateCustomContentPathSync(expandedPath);
|
||||||
if (validation !== true) {
|
if (validation) {
|
||||||
console.log(chalk.yellow(`⚠️ Skipping invalid custom content path: ${customPath} - ${validation}`));
|
console.log(chalk.yellow(`⚠️ Skipping invalid custom content path: ${customPath} - ${validation}`));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -487,6 +491,7 @@ class UI {
|
||||||
skipIde: toolSelection.skipIde,
|
skipIde: toolSelection.skipIde,
|
||||||
coreConfig: coreConfig,
|
coreConfig: coreConfig,
|
||||||
customContent: customContentConfig,
|
customContent: customContentConfig,
|
||||||
|
skipPrompts: options.yes || false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1124,6 +1129,33 @@ class UI {
|
||||||
return selected ? selected.filter((m) => m !== '__NONE__') : [];
|
return selected ? selected.filter((m) => m !== '__NONE__') : [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get default modules for non-interactive mode
|
||||||
|
* @param {Set} installedModuleIds - Already installed module IDs
|
||||||
|
* @returns {Array} Default module codes
|
||||||
|
*/
|
||||||
|
async getDefaultModules(installedModuleIds = new Set()) {
|
||||||
|
const { ModuleManager } = require('../installers/lib/modules/manager');
|
||||||
|
const moduleManager = new ModuleManager();
|
||||||
|
const { modules: localModules } = await moduleManager.listAvailable();
|
||||||
|
|
||||||
|
const defaultModules = [];
|
||||||
|
|
||||||
|
// Add default-selected local modules (typically BMM)
|
||||||
|
for (const mod of localModules) {
|
||||||
|
if (mod.defaultSelected === true || installedModuleIds.has(mod.id)) {
|
||||||
|
defaultModules.push(mod.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no defaults found, use 'bmm' as the fallback default
|
||||||
|
if (defaultModules.length === 0) {
|
||||||
|
defaultModules.push('bmm');
|
||||||
|
}
|
||||||
|
|
||||||
|
return defaultModules;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prompt for directory selection
|
* Prompt for directory selection
|
||||||
* @returns {Object} Directory answer from prompt
|
* @returns {Object} Directory answer from prompt
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue