Compare commits
10 Commits
3aec79d173
...
0bb07e50a3
| Author | SHA1 | Date |
|---|---|---|
|
|
0bb07e50a3 | |
|
|
df176d4206 | |
|
|
d8038d7848 | |
|
|
8043beef0c | |
|
|
cd541cfc00 | |
|
|
df103fc19b | |
|
|
2b27257110 | |
|
|
d83680838b | |
|
|
c7835413ad | |
|
|
82ce240234 |
|
|
@ -36,6 +36,7 @@ cursor
|
|||
CLAUDE.local.md
|
||||
.serena/
|
||||
.claude/settings.local.json
|
||||
.vibe
|
||||
|
||||
z*/
|
||||
|
||||
|
|
|
|||
|
|
@ -536,6 +536,11 @@ class BaseIdeSetup {
|
|||
content = content.replaceAll('_bmad', this.bmadFolderName);
|
||||
}
|
||||
|
||||
// Replace {bmad-folder} placeholder if present
|
||||
if (typeof content === 'string' && content.includes('{bmad-folder}')) {
|
||||
content = content.replaceAll('{bmad-folder}', this.bmadFolderName);
|
||||
}
|
||||
|
||||
// Replace escape sequence _bmad with literal _bmad
|
||||
if (typeof content === 'string' && content.includes('_bmad')) {
|
||||
content = content.replaceAll('_bmad', '_bmad');
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ class IdeManager {
|
|||
*/
|
||||
loadCustomInstallerFiles() {
|
||||
const ideDir = __dirname;
|
||||
const customFiles = ['codex.js', 'kilo.js', 'kiro-cli.js'];
|
||||
const customFiles = ['codex.js', 'kilo.js', 'kiro-cli.js', 'mistral-vibe.js'];
|
||||
|
||||
for (const file of customFiles) {
|
||||
const filePath = path.join(ideDir, file);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,372 @@
|
|||
const path = require('node:path');
|
||||
const fs = require('fs-extra');
|
||||
const os = require('node:os');
|
||||
const chalk = require('chalk');
|
||||
const { BaseIdeSetup } = require('./_base-ide');
|
||||
const { WorkflowCommandGenerator } = require('./shared/workflow-command-generator');
|
||||
const { AgentCommandGenerator } = require('./shared/agent-command-generator');
|
||||
const { getTasksFromBmad } = require('./shared/bmad-artifacts');
|
||||
|
||||
/**
|
||||
* Mistral Vibe setup handler (CLI mode)
|
||||
*/
|
||||
class MistralVibeSetup extends BaseIdeSetup {
|
||||
constructor() {
|
||||
super('mistral-vibe', 'Mistral Vibe CLI', false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup Mistral Vibe configuration
|
||||
* @param {string} projectDir - Project directory
|
||||
* @param {string} bmadDir - BMAD installation directory
|
||||
* @param {Object} options - Setup options
|
||||
*/
|
||||
async setup(projectDir, bmadDir, options = {}) {
|
||||
console.log(chalk.cyan(`Setting up ${this.name}...`));
|
||||
|
||||
// Always use CLI mode
|
||||
const mode = 'cli';
|
||||
|
||||
// Get the skills directory
|
||||
const skillsDir = path.join(projectDir, '.vibe', 'skills');
|
||||
await fs.ensureDir(skillsDir);
|
||||
await this.clearOldBmadFiles(skillsDir);
|
||||
|
||||
// Collect artifacts and write using the same pattern as Codex
|
||||
const agentGen = new AgentCommandGenerator(this.bmadFolderName);
|
||||
|
||||
// Collect core and BMM agents separately
|
||||
const { artifacts: coreAgents } = await agentGen.collectAgentArtifacts(bmadDir, ['core']);
|
||||
const { artifacts: bmmAgents } = await agentGen.collectAgentArtifacts(bmadDir, ['bmm']);
|
||||
|
||||
// Filter bmad-master from BMM agents (it appears in both)
|
||||
const filteredBmmAgents = bmmAgents.filter((a) => a.name !== 'bmad-master');
|
||||
|
||||
// Combine: core bmad-master + BMM agents
|
||||
const filteredAgentArtifacts = [...coreAgents, ...filteredBmmAgents];
|
||||
|
||||
// Write agent skills with "agent-" or "agent-bmm-" prefix
|
||||
const agentCount = await this.writeMistralVibeArtifacts(skillsDir, filteredAgentArtifacts, 'agent');
|
||||
|
||||
// Collect workflows
|
||||
const workflowGen = new WorkflowCommandGenerator(this.bmadFolderName);
|
||||
const workflows = await workflowGen.loadWorkflowManifest(bmadDir);
|
||||
|
||||
// Filter to only include BMM workflows (matching existing .vibe/skills/)
|
||||
const bmmWorkflows = workflows ? workflows.filter((wf) => wf.module === 'bmm') : [];
|
||||
|
||||
// Write workflow skills with "bmm-" prefix
|
||||
const workflowCount = await this.writeMistralVibeWorkflows(skillsDir, bmmWorkflows);
|
||||
|
||||
// Write other skills (brainstorming, help, party-mode)
|
||||
const otherCount = await this.writeOtherSkills(skillsDir);
|
||||
|
||||
const totalWritten = agentCount + workflowCount + otherCount;
|
||||
|
||||
console.log(chalk.green(`✓ ${this.name} configured:`));
|
||||
console.log(chalk.dim(` - ${totalWritten} skills generated`));
|
||||
console.log(chalk.dim(` - Destination: ${skillsDir}`));
|
||||
|
||||
return { success: true, written: totalWritten, destination: skillsDir };
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear old BMAD files from the destination directory
|
||||
*/
|
||||
async clearOldBmadFiles(destDir) {
|
||||
if (await fs.pathExists(destDir)) {
|
||||
const existingFiles = await fs.readdir(destDir);
|
||||
for (const file of existingFiles) {
|
||||
if (file !== 'README.md') {
|
||||
const filePath = path.join(destDir, file);
|
||||
const stat = await fs.lstat(filePath);
|
||||
if (stat.isDirectory()) {
|
||||
await fs.remove(filePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write Mistral Vibe agent artifacts
|
||||
*/
|
||||
async writeMistralVibeArtifacts(destDir, artifacts, prefix = '') {
|
||||
let count = 0;
|
||||
for (const artifact of artifacts) {
|
||||
const skillName =
|
||||
prefix === 'agent'
|
||||
? (artifact.module === 'bmm' ? `agent-bmm-${artifact.name}` : `agent-${artifact.name}`)
|
||||
: artifact.name;
|
||||
|
||||
const skillDir = path.join(destDir, skillName);
|
||||
await fs.ensureDir(skillDir);
|
||||
|
||||
const skillContent = await this.generateAgentSkillContent(artifact, skillName);
|
||||
await fs.writeFile(path.join(skillDir, 'SKILL.md'), skillContent);
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write Mistral Vibe workflow artifacts
|
||||
*/
|
||||
async writeMistralVibeWorkflows(destDir, workflows) {
|
||||
let count = 0;
|
||||
for (const workflow of workflows) {
|
||||
const skillName = `bmm-${workflow.name}`;
|
||||
const skillDir = path.join(destDir, skillName);
|
||||
await fs.ensureDir(skillDir);
|
||||
|
||||
const skillContent = await this.generateWorkflowSkillContent(workflow, skillName);
|
||||
await fs.writeFile(path.join(skillDir, 'SKILL.md'), skillContent);
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write other skills (brainstorming, help, party-mode)
|
||||
*/
|
||||
async writeOtherSkills(destDir) {
|
||||
const otherSkills = [
|
||||
{ name: 'brainstorming', description: 'Brainstorm Project Ideas with guided facilitation of a brainstorming coach' },
|
||||
{ name: 'help', description: 'Execute undefined' },
|
||||
{
|
||||
name: 'party-mode',
|
||||
description: 'Orchestrates group discussions between all installed BMAD agents, enabling natural multi-agent conversations',
|
||||
},
|
||||
];
|
||||
|
||||
let count = 0;
|
||||
for (const skill of otherSkills) {
|
||||
const skillDir = path.join(destDir, skill.name);
|
||||
await fs.ensureDir(skillDir);
|
||||
|
||||
const skillContent = `---
|
||||
name: ${skillName}
|
||||
description: ${skill.description}
|
||||
license: MIT
|
||||
compatibility: Mistral Vibe CLI
|
||||
user-invocable: True
|
||||
---
|
||||
|
||||
# ${skill.name.toUpperCase()}
|
||||
|
||||
IT IS CRITICAL THAT YOU FOLLOW THIS COMMAND: LOAD the FULL @.vibe/${this.bmadFolderName}/core/workflows/${skill.name}/workflow.md, READ its entire contents and follow its directions exactly!`;
|
||||
|
||||
await fs.writeFile(path.join(skillDir, 'SKILL.md'), skillContent);
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
async generateAgentSkills(skillsDir, bmadDir, options) {
|
||||
// Get agents from both core and bmm modules
|
||||
const agentGen = new AgentCommandGenerator(this.bmadFolderName);
|
||||
const { artifacts: coreAgents } = await agentGen.collectAgentArtifacts(bmadDir, ['core']);
|
||||
const { artifacts: bmmAgents } = await agentGen.collectAgentArtifacts(bmadDir, ['bmm']);
|
||||
const allAgents = [...coreAgents, ...bmmAgents];
|
||||
|
||||
let written = 0;
|
||||
for (const artifact of allAgents) {
|
||||
// Use "agent-" prefix for core, "agent-bmm-" for BMM agents to match existing format
|
||||
const skillName = artifact.module === 'bmm' ? `agent-bmm-${artifact.name}` : `agent-${artifact.name}`;
|
||||
const skillDir = path.join(skillsDir, skillName);
|
||||
await fs.ensureDir(skillDir);
|
||||
|
||||
const skillContent = this.addUserInvocableToContent(artifact.content, {
|
||||
...artifact,
|
||||
name: skillName,
|
||||
});
|
||||
const skillPath = path.join(skillDir, 'SKILL.md');
|
||||
await fs.writeFile(skillPath, skillContent);
|
||||
written++;
|
||||
}
|
||||
|
||||
return written;
|
||||
}
|
||||
|
||||
async generateWorkflowSkills(skillsDir, bmadDir) {
|
||||
const workflowGen = new WorkflowCommandGenerator(this.bmadFolderName);
|
||||
const workflows = await workflowGen.loadWorkflowManifest(bmadDir);
|
||||
|
||||
if (!workflows || workflows.length === 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let written = 0;
|
||||
for (const workflow of workflows) {
|
||||
// Add module prefix to workflow names (bmm- for BMM workflows)
|
||||
const skillName = workflow.module === 'bmm' ? `bmm-${workflow.name}` : workflow.name;
|
||||
const skillDir = path.join(skillsDir, skillName);
|
||||
await fs.ensureDir(skillDir);
|
||||
|
||||
// Generate workflow content in the same format as existing skills
|
||||
const workflowContent = await this.generateSimpleWorkflowSkillContent(workflow, bmadDir);
|
||||
const skillContent = this.addUserInvocableToContent(workflowContent, {
|
||||
name: skillName,
|
||||
description: workflow.description || `${workflow.name} workflow`,
|
||||
});
|
||||
|
||||
const skillPath = path.join(skillDir, 'SKILL.md');
|
||||
await fs.writeFile(skillPath, skillContent);
|
||||
written++;
|
||||
}
|
||||
|
||||
return written;
|
||||
}
|
||||
|
||||
async generateSimpleWorkflowSkillContent(workflow, bmadDir) {
|
||||
// Generate content in the same format as existing workflow skills
|
||||
return `IT IS CRITICAL THAT YOU FOLLOW THIS COMMAND: LOAD the FULL @${workflow.path}, READ its entire contents and follow its directions exactly!`;
|
||||
}
|
||||
|
||||
async generateOtherSkills(skillsDir, bmadDir) {
|
||||
// Generate additional skills that aren't agents or workflows
|
||||
// These are core workflows that don't have the bmm- prefix
|
||||
const otherSkills = [
|
||||
{ name: 'help', description: 'BMAD help system' },
|
||||
{ name: 'brainstorming', description: 'Brainstorming workflow' },
|
||||
{ name: 'party-mode', description: 'Multi-agent collaboration mode' },
|
||||
];
|
||||
|
||||
let written = 0;
|
||||
for (const skill of otherSkills) {
|
||||
const skillDir = path.join(skillsDir, skill.name);
|
||||
await fs.ensureDir(skillDir);
|
||||
|
||||
// Create basic skill content
|
||||
const skillContent = `---
|
||||
name: ${skill.name}
|
||||
description: ${skill.description}
|
||||
license: MIT
|
||||
compatibility: Mistral Vibe CLI
|
||||
user-invocable: True
|
||||
---
|
||||
|
||||
# ${skill.name.toUpperCase()} Skill
|
||||
|
||||
This skill provides ${skill.description.toLowerCase()} functionality.`;
|
||||
|
||||
const skillPath = path.join(skillDir, 'SKILL.md');
|
||||
await fs.writeFile(skillPath, skillContent);
|
||||
written++;
|
||||
}
|
||||
|
||||
return written;
|
||||
}
|
||||
|
||||
async generateAgentSkillContent(artifact, skillName) {
|
||||
const agentName = artifact.name;
|
||||
const isBmmAgent = artifact.module === 'bmm';
|
||||
const agentPath = isBmmAgent ? `${this.bmadFolderName}/bmm/agents/${agentName}.md` : `${this.bmadFolderName}/core/agents/${agentName}.md`;
|
||||
|
||||
return `---
|
||||
name: ${skillName}
|
||||
description: ${agentName} agent
|
||||
license: MIT
|
||||
compatibility: Mistral Vibe CLI
|
||||
user-invocable: True
|
||||
---
|
||||
|
||||
# Agent ${agentName.toUpperCase()}
|
||||
|
||||
You must fully embody this agent's persona and follow all activation instructions exactly as specified. NEVER break character until given an exit command.
|
||||
|
||||
<agent-activation CRITICAL="TRUE">
|
||||
1. LOAD the FULL agent file from @${agentPath}
|
||||
2. READ its entire contents - this contains the complete agent persona, menu, and instructions
|
||||
3. Execute ALL activation steps exactly as written in the agent file
|
||||
4. Follow the agent's persona and menu system precisely
|
||||
5. Stay in character throughout the session
|
||||
</agent-activation>`;
|
||||
}
|
||||
|
||||
async generateWorkflowSkillContent(workflow, skillName) {
|
||||
// Generate workflow skill content in the exact format
|
||||
const workflowName = skillName.replace('bmm-', '');
|
||||
|
||||
// Determine the workflow path based on the workflow name
|
||||
let workflowPath;
|
||||
switch (workflowName) {
|
||||
case 'brainstorming':
|
||||
case 'party-mode': {
|
||||
workflowPath = `${this.bmadFolderName}/core/workflows/${workflowName}/workflow.md`;
|
||||
break;
|
||||
}
|
||||
case 'help': {
|
||||
workflowPath = `${this.bmadFolderName}/core/tasks/help.md`;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
// BMM workflows are in various subdirectories
|
||||
workflowPath = `${this.bmadFolderName}/bmm/workflows/${workflowName}/workflow.md`;
|
||||
}
|
||||
}
|
||||
|
||||
return `---
|
||||
name: ${skillName}
|
||||
description: ${this.getWorkflowDescription(workflowName)}
|
||||
license: MIT
|
||||
compatibility: Mistral Vibe CLI
|
||||
user-invocable: True
|
||||
---
|
||||
|
||||
# ${workflowName.toUpperCase()}
|
||||
|
||||
IT IS CRITICAL THAT YOU FOLLOW THIS COMMAND: LOAD the FULL @${workflowPath}, READ its entire contents and follow its directions exactly!`;
|
||||
}
|
||||
|
||||
generateOtherSkillContent(skill) {
|
||||
// Generate other skill content
|
||||
const descriptions = {
|
||||
brainstorming: 'Brainstorm Project Ideas with guided facilitation of a brainstorming coach',
|
||||
help: 'Execute undefined',
|
||||
'party-mode': 'Orchestrates group discussions between all installed BMAD agents, enabling natural multi-agent conversations',
|
||||
};
|
||||
|
||||
return `---
|
||||
name: ${skill.name}
|
||||
description: ${descriptions[skill.name] || skill.name}
|
||||
license: MIT
|
||||
compatibility: Mistral Vibe CLI
|
||||
user-invocable: True
|
||||
---
|
||||
|
||||
# ${skill.name.toUpperCase()}
|
||||
|
||||
IT IS CRITICAL THAT YOU FOLLOW THIS COMMAND: LOAD the FULL @.vibe/${this.bmadFolderName}/core/workflows/${skill.name}/workflow.md, READ its entire contents and follow its directions exactly!`;
|
||||
}
|
||||
|
||||
getWorkflowDescription(workflowName) {
|
||||
const descriptions = {
|
||||
'check-implementation-readiness': 'Gate check before implementation',
|
||||
'code-review': 'Validate implementation quality',
|
||||
'correct-course': 'Handle significant mid-sprint changes',
|
||||
'create-architecture': 'Make technical decisions explicit',
|
||||
'create-epics-and-stories': 'Break requirements into implementable work',
|
||||
'create-excalidraw-dataflow': 'Create data flow diagrams',
|
||||
'create-excalidraw-diagram': 'Create diagrams',
|
||||
'create-excalidraw-flowchart': 'Create flowcharts',
|
||||
'create-excalidraw-wireframe': 'Create wireframes',
|
||||
'create-prd': 'Define requirements (FRs/NFRs)',
|
||||
'create-product-brief': 'Capture strategic vision',
|
||||
'create-story': 'Prepare next story for implementation',
|
||||
'create-ux-design': 'Design user experience (when UX matters)',
|
||||
'dev-story': 'Implement the story',
|
||||
'document-project': 'Document the project',
|
||||
'qa-automate': 'Generate tests for existing features',
|
||||
'quick-dev': 'Implement from spec or direct instructions',
|
||||
'quick-spec': 'Define an ad-hoc change',
|
||||
research: 'Validate market, technical, or domain assumptions',
|
||||
retrospective: 'Review after epic completion',
|
||||
'sprint-planning': 'Initialize tracking (once per project)',
|
||||
'sprint-status': 'Track sprint status',
|
||||
};
|
||||
return descriptions[workflowName] || workflowName;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { MistralVibeSetup };
|
||||
|
|
@ -84,6 +84,13 @@ platforms:
|
|||
target_dir: .gemini/commands
|
||||
template_type: gemini
|
||||
|
||||
mistral-vibe:
|
||||
name: "Mistral Vibe CLI"
|
||||
preferred: false
|
||||
category: cli
|
||||
description: "Mistral's vibe command-line interface"
|
||||
# No installer config - uses custom mistral-vibe.js
|
||||
|
||||
github-copilot:
|
||||
name: "GitHub Copilot"
|
||||
preferred: false
|
||||
|
|
|
|||
|
|
@ -435,95 +435,29 @@ class UI {
|
|||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// NEW INSTALL: Show recommended tools first with "Browse All" option
|
||||
// NEW INSTALL: Show all tools with search
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
const recommendedOptions = preferredIdes.map((ide) => {
|
||||
const isConfigured = configuredPreferred.includes(ide.value);
|
||||
const allTools = [...preferredIdes, ...otherIdes];
|
||||
|
||||
const allToolOptions = allTools.map((ide) => {
|
||||
const isPreferred = preferredIdes.some((p) => p.value === ide.value);
|
||||
let label = ide.name;
|
||||
if (isPreferred) label += ' ⭐';
|
||||
return {
|
||||
label: isConfigured ? `${ide.name} ⭐ ✅` : `${ide.name} ⭐`,
|
||||
label,
|
||||
value: ide.value,
|
||||
};
|
||||
});
|
||||
|
||||
// Add "browse all" option at the end if there are additional tools
|
||||
if (otherIdes.length > 0) {
|
||||
const totalTools = preferredIdes.length + otherIdes.length;
|
||||
recommendedOptions.push({
|
||||
label: `→ Browse all supported tools (${totalTools} total)...`,
|
||||
value: '__BROWSE_ALL__',
|
||||
});
|
||||
}
|
||||
|
||||
// Pre-select previously configured preferred tools
|
||||
const recommendedInitialValues = configuredPreferred.length > 0 ? configuredPreferred : undefined;
|
||||
|
||||
const recommendedSelected = await prompts.multiselect({
|
||||
message: `Integrate with ${chalk.dim('(↑/↓ to navigate • SPACE: select • ENTER: confirm)')}:`,
|
||||
options: recommendedOptions,
|
||||
initialValues: recommendedInitialValues,
|
||||
const selectedIdes = await prompts.autocompleteMultiselect({
|
||||
message: 'Select tools:',
|
||||
options: allToolOptions,
|
||||
initialValues: configuredIdes.length > 0 ? configuredIdes : undefined,
|
||||
required: false,
|
||||
maxItems: 8,
|
||||
});
|
||||
|
||||
const selectedRecommended = recommendedSelected || [];
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// STEP 2: Handle "Browse All" selection - show additional tools if requested
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
const wantsBrowseAll = selectedRecommended.includes('__BROWSE_ALL__');
|
||||
const filteredRecommended = selectedRecommended.filter((v) => v !== '__BROWSE_ALL__');
|
||||
|
||||
// Show additional tools if:
|
||||
// 1. User explicitly chose "Browse All", OR
|
||||
// 2. User has previously configured "other" tools, OR
|
||||
// 3. User selected no recommended tools (allow them to pick from other tools)
|
||||
const showAdditionalTools = wantsBrowseAll || configuredOther.length > 0 || filteredRecommended.length === 0;
|
||||
|
||||
let selectedAdditionalOrAll = [];
|
||||
|
||||
if (showAdditionalTools) {
|
||||
// Show ALL tools if:
|
||||
// - User explicitly chose "Browse All", OR
|
||||
// - User selected nothing from recommended (so they can pick from everything)
|
||||
// Otherwise, show only "other" tools as additional options
|
||||
const showAllTools = wantsBrowseAll || filteredRecommended.length === 0;
|
||||
const toolsToShow = showAllTools ? [...preferredIdes, ...otherIdes] : otherIdes;
|
||||
|
||||
if (toolsToShow.length > 0) {
|
||||
const allToolOptions = toolsToShow.map((ide) => {
|
||||
const isConfigured = configuredIdes.includes(ide.value);
|
||||
const isPreferred = preferredIdes.some((p) => p.value === ide.value);
|
||||
let label = ide.name;
|
||||
if (isPreferred) label += ' ⭐';
|
||||
if (isConfigured) label += ' ✅';
|
||||
return {
|
||||
label,
|
||||
value: ide.value,
|
||||
};
|
||||
});
|
||||
|
||||
// Pre-select: previously configured tools + any recommended tools already selected
|
||||
const initialValues = [...configuredIdes, ...filteredRecommended].filter((v, i, arr) => arr.indexOf(v) === i); // dedupe
|
||||
|
||||
// Use "additional" only if user already selected some recommended tools
|
||||
const isAdditional = !wantsBrowseAll && filteredRecommended.length > 0;
|
||||
|
||||
console.log('');
|
||||
const selected = await prompts.autocompleteMultiselect({
|
||||
message: isAdditional ? 'Select additional tools:' : 'Select tools:',
|
||||
options: allToolOptions,
|
||||
initialValues: initialValues.length > 0 ? initialValues : undefined,
|
||||
required: false,
|
||||
maxItems: 8,
|
||||
});
|
||||
|
||||
selectedAdditionalOrAll = selected || [];
|
||||
}
|
||||
}
|
||||
|
||||
// Combine selections:
|
||||
// - If "Browse All" was used, the second prompt contains ALL selections
|
||||
// - Otherwise, combine recommended + additional
|
||||
const allSelectedIdes = wantsBrowseAll ? selectedAdditionalOrAll : [...filteredRecommended, ...selectedAdditionalOrAll];
|
||||
const allSelectedIdes = selectedIdes || [];
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// STEP 3: Confirm if no tools selected
|
||||
|
|
@ -547,7 +481,6 @@ class UI {
|
|||
}
|
||||
|
||||
// Display selected tools
|
||||
const allTools = [...preferredIdes, ...otherIdes];
|
||||
this.displaySelectedTools(allSelectedIdes, preferredIdes, allTools);
|
||||
|
||||
return {
|
||||
|
|
|
|||
Loading…
Reference in New Issue