feat(ide): add Bob IDE installer support
- bob.js: fix installCustomAgentLauncher using wrong mode schema (description/prompt/always/permissions → roleDefinition/whenToUse/ customInstructions/groups) - bob.js: add detectionPaths for .bob directory - manager.js: update comments to include bob.js in custom installer list - platform-codes.yaml: move bob entry to correct alphabetical position - eslint.config.mjs: add .bob/** to eslint ignores (like .claude, .roo, etc.)
This commit is contained in:
parent
3bbfc89c27
commit
d21ed92ae8
|
|
@ -20,6 +20,7 @@ export default [
|
|||
'website/**',
|
||||
// Gitignored patterns
|
||||
'z*/**', // z-samples, z1, z2, etc.
|
||||
'.bob/**',
|
||||
'.claude/**',
|
||||
'.codex/**',
|
||||
'.github/chatmodes/**',
|
||||
|
|
|
|||
|
|
@ -8,12 +8,13 @@ const { TaskToolCommandGenerator } = require('./shared/task-tool-command-generat
|
|||
|
||||
/**
|
||||
* IBM Bob IDE setup handler
|
||||
* Creates custom modes in .bobmodes file (similar to Kilo)
|
||||
* Creates custom modes in .bob/custom_modes.yaml file
|
||||
*/
|
||||
class BobSetup extends BaseIdeSetup {
|
||||
constructor() {
|
||||
super('bob', 'IBM Bob');
|
||||
this.configFile = '.bobmodes';
|
||||
this.configFile = '.bob/custom_modes.yaml';
|
||||
this.detectionPaths = ['.bob'];
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -38,7 +39,7 @@ class BobSetup extends BaseIdeSetup {
|
|||
config = yaml.parse(existingContent) || {};
|
||||
} catch {
|
||||
// If parsing fails, start fresh but warn user
|
||||
await prompts.log.warn('Warning: Could not parse existing .bobmodes, starting fresh');
|
||||
await prompts.log.warn('Warning: Could not parse existing .bob/custom_modes.yaml, starting fresh');
|
||||
config = {};
|
||||
}
|
||||
}
|
||||
|
|
@ -61,7 +62,7 @@ class BobSetup extends BaseIdeSetup {
|
|||
addedCount++;
|
||||
}
|
||||
|
||||
// Write .bobmodes file with proper YAML structure
|
||||
// Write .bob/custom_modes.yaml file with proper YAML structure
|
||||
const finalContent = yaml.stringify(config, { lineWidth: 0 });
|
||||
await this.writeFile(bobModesPath, finalContent);
|
||||
|
||||
|
|
@ -110,23 +111,25 @@ class BobSetup extends BaseIdeSetup {
|
|||
* @returns {Object} Mode object for YAML serialization
|
||||
*/
|
||||
async createModeObject(artifact, projectDir) {
|
||||
// Extract metadata from launcher content
|
||||
const titleMatch = artifact.content.match(/title="([^"]+)"/);
|
||||
const title = titleMatch ? titleMatch[1] : this.formatTitle(artifact.name);
|
||||
// Extract title and icon from the compiled agent file's <agent> XML tag
|
||||
// artifact.content is the launcher template which does NOT contain these attributes
|
||||
let title = this.formatTitle(artifact.name);
|
||||
let icon = '🤖';
|
||||
|
||||
const iconMatch = artifact.content.match(/icon="([^"]+)"/);
|
||||
const icon = iconMatch ? iconMatch[1] : '🤖';
|
||||
if (artifact.sourcePath && (await this.pathExists(artifact.sourcePath))) {
|
||||
const agentContent = await this.readFile(artifact.sourcePath);
|
||||
const titleMatch = agentContent.match(/<agent[^>]*\stitle="([^"]+)"/);
|
||||
if (titleMatch) title = titleMatch[1];
|
||||
const iconMatch = agentContent.match(/<agent[^>]*\sicon="([^"]+)"/);
|
||||
if (iconMatch) icon = iconMatch[1];
|
||||
}
|
||||
|
||||
const whenToUseMatch = artifact.content.match(/whenToUse="([^"]+)"/);
|
||||
const whenToUse = whenToUseMatch ? whenToUseMatch[1] : `Use for ${title} tasks`;
|
||||
const whenToUse = `Use for ${title} tasks`;
|
||||
|
||||
// Get the activation header from central template (trim to avoid YAML formatting issues)
|
||||
const activationHeader = (await this.getAgentCommandHeader()).trim();
|
||||
|
||||
const roleDefinitionMatch = artifact.content.match(/roleDefinition="([^"]+)"/);
|
||||
const roleDefinition = roleDefinitionMatch
|
||||
? roleDefinitionMatch[1]
|
||||
: `You are a ${title} specializing in ${title.toLowerCase()} tasks.`;
|
||||
const roleDefinition = `You are a ${title} specializing in ${title.toLowerCase()} tasks.`;
|
||||
|
||||
// Get relative path
|
||||
const relativePath = path.relative(projectDir, artifact.sourcePath).replaceAll('\\', '/');
|
||||
|
|
@ -189,12 +192,12 @@ class BobSetup extends BaseIdeSetup {
|
|||
|
||||
if (removedCount > 0) {
|
||||
await fs.writeFile(bobModesPath, yaml.stringify(config, { lineWidth: 0 }));
|
||||
if (!options.silent) await prompts.log.message(`Removed ${removedCount} BMAD modes from .bobmodes`);
|
||||
if (!options.silent) await prompts.log.message(`Removed ${removedCount} BMAD modes from .bob/custom_modes.yaml`);
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// If parsing fails, leave file as-is
|
||||
if (!options.silent) await prompts.log.warn('Warning: Could not parse .bobmodes for cleanup');
|
||||
if (!options.silent) await prompts.log.warn('Warning: Could not parse .bob/custom_modes.yaml for cleanup');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -215,7 +218,7 @@ class BobSetup extends BaseIdeSetup {
|
|||
const bobmodesPath = path.join(projectDir, this.configFile);
|
||||
let config = {};
|
||||
|
||||
// Read existing .bobmodes file
|
||||
// Read existing .bob/custom_modes.yaml file
|
||||
if (await this.pathExists(bobmodesPath)) {
|
||||
const existingContent = await this.readFile(bobmodesPath);
|
||||
try {
|
||||
|
|
@ -245,16 +248,18 @@ class BobSetup extends BaseIdeSetup {
|
|||
}
|
||||
|
||||
// Add custom mode object
|
||||
const title = `BMAD Custom: ${agentName}`;
|
||||
const activationHeader = (await this.getAgentCommandHeader()).trim();
|
||||
config.customModes.push({
|
||||
slug: slug,
|
||||
name: `BMAD Custom: ${agentName}`,
|
||||
description: `Custom BMAD agent: ${agentName}\n\n**⚠️ IMPORTANT**: Run @${agentPath} first to load the complete agent!\n\nThis is a launcher for the custom BMAD agent "${agentName}". The agent will follow the persona and instructions from the main agent file.\n`,
|
||||
prompt: `@${agentPath}\n`,
|
||||
always: false,
|
||||
permissions: 'all',
|
||||
name: title,
|
||||
roleDefinition: `You are a custom BMAD agent "${agentName}". Follow the persona and instructions from the agent file.`,
|
||||
whenToUse: `Use for custom BMAD agent "${agentName}" tasks`,
|
||||
customInstructions: `${activationHeader} Read the full agent from ${agentPath} start activation to alter your state of being follow startup section instructions stay in this being until told to exit this mode\n`,
|
||||
groups: ['read', 'edit', 'browser', 'command', 'mcp'],
|
||||
});
|
||||
|
||||
// Write .bobmodes file with proper YAML structure
|
||||
// Write .bob/custom_modes.yaml file with proper YAML structure
|
||||
await this.writeFile(bobmodesPath, yaml.stringify(config, { lineWidth: 0 }));
|
||||
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ const prompts = require('../../../lib/prompts');
|
|||
* Dynamically discovers and loads IDE handlers
|
||||
*
|
||||
* Loading strategy:
|
||||
* 1. Custom installer files (codex.js, github-copilot.js, kilo.js, rovodev.js) - for platforms with unique installation logic
|
||||
* 1. Custom installer files (bob.js, codex.js, github-copilot.js, kilo.js, rovodev.js) - for platforms with unique installation logic
|
||||
* 2. Config-driven handlers (from platform-codes.yaml) - for standard IDE installation patterns
|
||||
*/
|
||||
class IdeManager {
|
||||
|
|
@ -44,7 +44,7 @@ class IdeManager {
|
|||
|
||||
/**
|
||||
* Dynamically load all IDE handlers
|
||||
* 1. Load custom installer files first (codex.js, github-copilot.js, kilo.js, rovodev.js)
|
||||
* 1. Load custom installer files first (bob.js, codex.js, github-copilot.js, kilo.js, rovodev.js)
|
||||
* 2. Load config-driven handlers from platform-codes.yaml
|
||||
*/
|
||||
async loadHandlers() {
|
||||
|
|
|
|||
|
|
@ -32,6 +32,13 @@ platforms:
|
|||
target_dir: .augment/commands
|
||||
template_type: default
|
||||
|
||||
bob:
|
||||
name: "IBM Bob"
|
||||
preferred: false
|
||||
category: ide
|
||||
description: "IBM's AI development environment"
|
||||
# No installer config - uses custom bob.js (creates .bob/custom_modes.yaml)
|
||||
|
||||
claude-code:
|
||||
name: "Claude Code"
|
||||
preferred: true
|
||||
|
|
|
|||
Loading…
Reference in New Issue