Add IDE handler system with slash command support
Implement BMad-style IDE handler architecture for WDS: - Base class (_base-ide.js) with template method pattern - Dynamic handler discovery via IdeManager - Priority handlers: Windsurf, Cursor, Claude Code, Cline, GitHub Copilot - Installer integration after agent compilation step - Saga activation now branches on starting_point config (pitch vs brief) - Remove parenthetical hint from learning material prompt Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
b120dd9327
commit
4596fad5d6
|
|
@ -36,12 +36,13 @@ agent:
|
||||||
prompts:
|
prompts:
|
||||||
- id: activation
|
- id: activation
|
||||||
content: |
|
content: |
|
||||||
Hi {{user_name}}, I'm Saga, your strategic analyst! 👋
|
Hi {user_name}, I'm Saga, your strategic analyst! 👋
|
||||||
|
|
||||||
I'll help you create a Product Brief and Trigger Map for {{project_name}}.
|
I'll help you create a Product Brief and Trigger Map for {project_name}.
|
||||||
|
|
||||||
Let's start with the Product Brief. Tell me in your own words:
|
Check {starting_point} from config:
|
||||||
**What are you building?**
|
- If "pitch": Say "Before we dive into formal documentation, let's talk about your idea! Tell me in your own words — **what's the big idea? What problem are you solving and for whom?**" Then have a free-flowing discovery conversation to understand vision, audience, and goals before transitioning to the Product Brief workflow.
|
||||||
|
- If "brief": Say "Let's start with the Product Brief. Tell me in your own words: **What are you building?**" Then proceed directly with the [PB] Product Brief workflow.
|
||||||
|
|
||||||
menu:
|
menu:
|
||||||
- trigger: AS or fuzzy match on alignment-signoff
|
- trigger: AS or fuzzy match on alignment-signoff
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,266 @@
|
||||||
|
const path = require('node:path');
|
||||||
|
const fs = require('fs-extra');
|
||||||
|
const chalk = require('chalk');
|
||||||
|
const yaml = require('yaml');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for IDE-specific setup
|
||||||
|
* All IDE handlers should extend this class
|
||||||
|
*/
|
||||||
|
class BaseIdeSetup {
|
||||||
|
constructor(name, displayName = null, preferred = false) {
|
||||||
|
this.name = name;
|
||||||
|
this.displayName = displayName || name; // Human-readable name for UI
|
||||||
|
this.preferred = preferred; // Whether this IDE should be shown in preferred list
|
||||||
|
this.configDir = null; // Override in subclasses (e.g., '.windsurf/workflows/wds')
|
||||||
|
this.configFile = null; // Override in subclasses when detection is file-based
|
||||||
|
this.detectionPaths = []; // Additional paths that indicate the IDE is configured
|
||||||
|
this.wdsFolderName = '_wds'; // Default, can be overridden
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the WDS folder name for placeholder replacement
|
||||||
|
* @param {string} wdsFolderName - The WDS folder name
|
||||||
|
*/
|
||||||
|
setWdsFolderName(wdsFolderName) {
|
||||||
|
this.wdsFolderName = wdsFolderName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main setup method - must be implemented by subclasses
|
||||||
|
* @param {string} projectDir - Project directory
|
||||||
|
* @param {string} wdsDir - WDS installation directory
|
||||||
|
* @param {Object} options - Setup options
|
||||||
|
*/
|
||||||
|
async setup(projectDir, wdsDir, options = {}) {
|
||||||
|
throw new Error(`setup() must be implemented by ${this.name} handler`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cleanup IDE configuration
|
||||||
|
* @param {string} projectDir - Project directory
|
||||||
|
*/
|
||||||
|
async cleanup(projectDir) {
|
||||||
|
// Default implementation - can be overridden
|
||||||
|
if (this.configDir) {
|
||||||
|
const configPath = path.join(projectDir, this.configDir);
|
||||||
|
if (await fs.pathExists(configPath)) {
|
||||||
|
await fs.remove(configPath);
|
||||||
|
console.log(chalk.dim(`Removed ${this.name} WDS configuration`));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detect whether this IDE already has configuration in the project
|
||||||
|
* Subclasses can override for custom logic
|
||||||
|
* @param {string} projectDir - Project directory
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
async detect(projectDir) {
|
||||||
|
const pathsToCheck = [];
|
||||||
|
|
||||||
|
if (this.configDir) {
|
||||||
|
pathsToCheck.push(path.join(projectDir, this.configDir));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.configFile) {
|
||||||
|
pathsToCheck.push(path.join(projectDir, this.configFile));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(this.detectionPaths)) {
|
||||||
|
for (const candidate of this.detectionPaths) {
|
||||||
|
if (!candidate) continue;
|
||||||
|
const resolved = path.isAbsolute(candidate) ? candidate : path.join(projectDir, candidate);
|
||||||
|
pathsToCheck.push(resolved);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const candidate of pathsToCheck) {
|
||||||
|
if (await fs.pathExists(candidate)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get list of agents from WDS installation
|
||||||
|
* @param {string} wdsDir - WDS installation directory
|
||||||
|
* @returns {Array} List of agent files with metadata
|
||||||
|
*/
|
||||||
|
async getAgents(wdsDir) {
|
||||||
|
const agents = [];
|
||||||
|
const agentsPath = path.join(wdsDir, 'agents');
|
||||||
|
|
||||||
|
if (!(await fs.pathExists(agentsPath))) {
|
||||||
|
return agents;
|
||||||
|
}
|
||||||
|
|
||||||
|
const files = await fs.readdir(agentsPath);
|
||||||
|
|
||||||
|
for (const file of files) {
|
||||||
|
if (!file.endsWith('.md')) continue;
|
||||||
|
|
||||||
|
const filePath = path.join(agentsPath, file);
|
||||||
|
const agentName = file.replace('.md', '');
|
||||||
|
|
||||||
|
// Extract metadata from agent file
|
||||||
|
const metadata = await this.extractAgentMetadata(filePath);
|
||||||
|
|
||||||
|
// Create slug from agent name (e.g., 'saga-analyst' -> 'saga')
|
||||||
|
const slug = metadata.slug || agentName.split('-')[0];
|
||||||
|
|
||||||
|
agents.push({
|
||||||
|
name: agentName,
|
||||||
|
slug: slug,
|
||||||
|
path: filePath,
|
||||||
|
relativePath: path.relative(wdsDir, filePath),
|
||||||
|
filename: file,
|
||||||
|
metadata: metadata,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return agents;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract agent metadata from compiled agent markdown file
|
||||||
|
* Reads YAML frontmatter or fallback to defaults
|
||||||
|
* @param {string} filePath - Path to agent markdown file
|
||||||
|
* @returns {Object} Agent metadata
|
||||||
|
*/
|
||||||
|
async extractAgentMetadata(filePath) {
|
||||||
|
try {
|
||||||
|
const content = await fs.readFile(filePath, 'utf8');
|
||||||
|
|
||||||
|
// Try to extract YAML frontmatter
|
||||||
|
const frontmatterMatch = content.match(/^---\s*\n([\s\S]*?)\n---/);
|
||||||
|
if (frontmatterMatch) {
|
||||||
|
const frontmatter = yaml.parse(frontmatterMatch[1]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: frontmatter.name || path.basename(filePath, '.md'),
|
||||||
|
description: frontmatter.description || frontmatter.role || '',
|
||||||
|
icon: frontmatter.icon || '📋',
|
||||||
|
slug: frontmatter.id ? path.basename(frontmatter.id, '.md').split('-')[0] : null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback: extract from filename
|
||||||
|
const agentName = path.basename(filePath, '.md');
|
||||||
|
return {
|
||||||
|
name: this.formatTitle(agentName),
|
||||||
|
description: agentName.includes('saga') ? 'Strategic Analyst' :
|
||||||
|
agentName.includes('freya') ? 'UX Designer' :
|
||||||
|
agentName.includes('idunn') ? 'Product Manager' : '',
|
||||||
|
icon: '📋',
|
||||||
|
slug: agentName.split('-')[0],
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
// Fallback metadata on error
|
||||||
|
const agentName = path.basename(filePath, '.md');
|
||||||
|
return {
|
||||||
|
name: this.formatTitle(agentName),
|
||||||
|
description: '',
|
||||||
|
icon: '📋',
|
||||||
|
slug: agentName.split('-')[0],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format agent launcher content that references the compiled agent
|
||||||
|
* @param {string} agentName - Agent name (e.g., 'saga-analyst')
|
||||||
|
* @param {string} agentPath - Relative path to agent file (e.g., 'agents/saga-analyst.md')
|
||||||
|
* @returns {string} Launcher content
|
||||||
|
*/
|
||||||
|
formatAgentLauncher(agentName, agentPath) {
|
||||||
|
const relativePath = path.relative(process.cwd(), agentPath)
|
||||||
|
.replace(/\\/g, '/'); // Convert Windows paths to forward slashes
|
||||||
|
|
||||||
|
return `<!-- WDS Agent Launcher -->
|
||||||
|
<!-- This file references the compiled agent. Do not edit directly. -->
|
||||||
|
<!-- Source: ${this.wdsFolderName}/agents/${agentName}.md -->
|
||||||
|
|
||||||
|
@include(${this.wdsFolderName}/agents/${agentName}.md)
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process content with IDE-specific frontmatter
|
||||||
|
* Subclasses must override this to add IDE-specific headers
|
||||||
|
* @param {string} content - Launcher content
|
||||||
|
* @param {Object} metadata - Agent metadata
|
||||||
|
* @returns {string} Processed content with IDE-specific frontmatter
|
||||||
|
*/
|
||||||
|
processContent(content, metadata = {}) {
|
||||||
|
// Default implementation - subclasses should override
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensure directory exists
|
||||||
|
* @param {string} dirPath - Directory path
|
||||||
|
*/
|
||||||
|
async ensureDir(dirPath) {
|
||||||
|
await fs.ensureDir(dirPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write file with content (replaces _wds placeholder)
|
||||||
|
* @param {string} filePath - File path
|
||||||
|
* @param {string} content - File content
|
||||||
|
*/
|
||||||
|
async writeFile(filePath, content) {
|
||||||
|
// Replace _wds placeholder if present
|
||||||
|
if (typeof content === 'string' && content.includes('_wds')) {
|
||||||
|
content = content.replaceAll('_wds', this.wdsFolderName);
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.ensureDir(path.dirname(filePath));
|
||||||
|
await fs.writeFile(filePath, content, 'utf8');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if path exists
|
||||||
|
* @param {string} pathToCheck - Path to check
|
||||||
|
* @returns {boolean} True if path exists
|
||||||
|
*/
|
||||||
|
async exists(pathToCheck) {
|
||||||
|
return await fs.pathExists(pathToCheck);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Alias for exists method
|
||||||
|
* @param {string} pathToCheck - Path to check
|
||||||
|
* @returns {boolean} True if path exists
|
||||||
|
*/
|
||||||
|
async pathExists(pathToCheck) {
|
||||||
|
return await fs.pathExists(pathToCheck);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read file content
|
||||||
|
* @param {string} filePath - File path
|
||||||
|
* @returns {string} File content
|
||||||
|
*/
|
||||||
|
async readFile(filePath) {
|
||||||
|
return await fs.readFile(filePath, 'utf8');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format name as title
|
||||||
|
* @param {string} name - Name to format (e.g., 'saga-analyst')
|
||||||
|
* @returns {string} Formatted title (e.g., 'Saga Analyst')
|
||||||
|
*/
|
||||||
|
formatTitle(name) {
|
||||||
|
return name
|
||||||
|
.split('-')
|
||||||
|
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
||||||
|
.join(' ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { BaseIdeSetup };
|
||||||
|
|
@ -0,0 +1,114 @@
|
||||||
|
const path = require('node:path');
|
||||||
|
const { BaseIdeSetup } = require('./_base-ide');
|
||||||
|
const chalk = require('chalk');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Claude Code IDE setup handler for WDS
|
||||||
|
*/
|
||||||
|
class ClaudeCodeSetup extends BaseIdeSetup {
|
||||||
|
constructor() {
|
||||||
|
super('claude-code', 'Claude Code', true); // preferred IDE
|
||||||
|
this.configDir = '.claude/skills/wds';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setup Claude Code IDE configuration
|
||||||
|
* @param {string} projectDir - Project directory
|
||||||
|
* @param {string} wdsDir - WDS installation directory
|
||||||
|
* @param {Object} options - Setup options
|
||||||
|
*/
|
||||||
|
async setup(projectDir, wdsDir, options = {}) {
|
||||||
|
// Create .claude/skills/wds directory
|
||||||
|
const targetDir = path.join(projectDir, this.configDir);
|
||||||
|
await this.ensureDir(targetDir);
|
||||||
|
|
||||||
|
// Get all WDS agents
|
||||||
|
const agents = await this.getAgents(wdsDir);
|
||||||
|
|
||||||
|
if (agents.length === 0) {
|
||||||
|
throw new Error('No agents found in WDS installation');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create launcher file for each agent
|
||||||
|
let agentCount = 0;
|
||||||
|
for (const agent of agents) {
|
||||||
|
// Create launcher content that references the compiled agent
|
||||||
|
const launcher = this.formatAgentLauncher(agent.name, agent.path);
|
||||||
|
|
||||||
|
// Add Claude Code-specific YAML frontmatter
|
||||||
|
const content = this.processContent(launcher, agent.metadata);
|
||||||
|
|
||||||
|
// Write launcher file
|
||||||
|
const filePath = path.join(targetDir, `${agent.slug}.md`);
|
||||||
|
await this.writeFile(filePath, content);
|
||||||
|
agentCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.logger) {
|
||||||
|
options.logger.log(chalk.dim(` - ${agentCount} agent(s) configured for Claude Code`));
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
agents: agentCount,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process content with Claude Code-specific YAML frontmatter
|
||||||
|
* @param {string} content - Launcher content
|
||||||
|
* @param {Object} metadata - Agent metadata
|
||||||
|
* @returns {string} Processed content with Claude Code YAML frontmatter
|
||||||
|
*/
|
||||||
|
processContent(content, metadata = {}) {
|
||||||
|
// Strip any existing frontmatter from launcher
|
||||||
|
const frontmatterRegex = /^---\s*\n[\s\S]*?\n---\s*\n/;
|
||||||
|
const contentWithoutFrontmatter = content.replace(frontmatterRegex, '');
|
||||||
|
|
||||||
|
const name = metadata.name || 'WDS Agent';
|
||||||
|
const description = metadata.description || 'Agent';
|
||||||
|
|
||||||
|
// Create Claude Code YAML metadata header
|
||||||
|
const yamlHeader = `---
|
||||||
|
name: ${name}
|
||||||
|
description: ${description}
|
||||||
|
---
|
||||||
|
|
||||||
|
`;
|
||||||
|
|
||||||
|
return yamlHeader + contentWithoutFrontmatter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cleanup Claude Code WDS configuration
|
||||||
|
* @param {string} projectDir - Project directory
|
||||||
|
*/
|
||||||
|
async cleanup(projectDir) {
|
||||||
|
const wdsPath = path.join(projectDir, this.configDir);
|
||||||
|
|
||||||
|
if (await this.exists(wdsPath)) {
|
||||||
|
await this.remove(wdsPath);
|
||||||
|
console.log(chalk.dim(`Removed Claude Code WDS configuration`));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detect if Claude Code is configured in project
|
||||||
|
* @param {string} projectDir - Project directory
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
async detect(projectDir) {
|
||||||
|
return await this.exists(path.join(projectDir, '.claude'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove directory helper
|
||||||
|
* @param {string} dirPath - Directory to remove
|
||||||
|
*/
|
||||||
|
async remove(dirPath) {
|
||||||
|
const fs = require('fs-extra');
|
||||||
|
await fs.remove(dirPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { ClaudeCodeSetup };
|
||||||
|
|
@ -0,0 +1,118 @@
|
||||||
|
const path = require('node:path');
|
||||||
|
const { BaseIdeSetup } = require('./_base-ide');
|
||||||
|
const chalk = require('chalk');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cline IDE setup handler for WDS
|
||||||
|
*/
|
||||||
|
class ClineSetup extends BaseIdeSetup {
|
||||||
|
constructor() {
|
||||||
|
super('cline', 'Cline', false);
|
||||||
|
this.configDir = '.cline';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setup Cline IDE configuration
|
||||||
|
* @param {string} projectDir - Project directory
|
||||||
|
* @param {string} wdsDir - WDS installation directory
|
||||||
|
* @param {Object} options - Setup options
|
||||||
|
*/
|
||||||
|
async setup(projectDir, wdsDir, options = {}) {
|
||||||
|
// Create .cline directory
|
||||||
|
const targetDir = path.join(projectDir, this.configDir);
|
||||||
|
await this.ensureDir(targetDir);
|
||||||
|
|
||||||
|
// Get all WDS agents
|
||||||
|
const agents = await this.getAgents(wdsDir);
|
||||||
|
|
||||||
|
if (agents.length === 0) {
|
||||||
|
throw new Error('No agents found in WDS installation');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create launcher file for each agent
|
||||||
|
let agentCount = 0;
|
||||||
|
for (const agent of agents) {
|
||||||
|
// Create launcher content that references the compiled agent
|
||||||
|
const launcher = this.formatAgentLauncher(agent.name, agent.path);
|
||||||
|
|
||||||
|
// Add Cline-specific formatting (flat markdown, no frontmatter)
|
||||||
|
const content = this.processContent(launcher, agent.metadata);
|
||||||
|
|
||||||
|
// Write launcher file
|
||||||
|
const filePath = path.join(targetDir, `${agent.slug}.md`);
|
||||||
|
await this.writeFile(filePath, content);
|
||||||
|
agentCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.logger) {
|
||||||
|
options.logger.log(chalk.dim(` - ${agentCount} agent(s) configured for Cline`));
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
agents: agentCount,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process content with Cline-specific formatting
|
||||||
|
* Cline uses flat markdown with no frontmatter
|
||||||
|
* @param {string} content - Launcher content
|
||||||
|
* @param {Object} metadata - Agent metadata
|
||||||
|
* @returns {string} Processed content without frontmatter
|
||||||
|
*/
|
||||||
|
processContent(content, metadata = {}) {
|
||||||
|
// Strip any existing frontmatter from launcher
|
||||||
|
const frontmatterRegex = /^---\s*\n[\s\S]*?\n---\s*\n/;
|
||||||
|
const contentWithoutFrontmatter = content.replace(frontmatterRegex, '');
|
||||||
|
|
||||||
|
// Add title header for Cline
|
||||||
|
const title = metadata.name ?
|
||||||
|
`${metadata.name} - ${metadata.description}` :
|
||||||
|
metadata.description || 'WDS Agent';
|
||||||
|
|
||||||
|
return `# ${title}
|
||||||
|
|
||||||
|
${contentWithoutFrontmatter}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cleanup Cline WDS configuration
|
||||||
|
* @param {string} projectDir - Project directory
|
||||||
|
*/
|
||||||
|
async cleanup(projectDir) {
|
||||||
|
const wdsPath = path.join(projectDir, this.configDir);
|
||||||
|
|
||||||
|
if (await this.exists(wdsPath)) {
|
||||||
|
// Only remove WDS agent files, not entire .cline directory
|
||||||
|
const agents = ['saga.md', 'freya.md', 'idunn.md'];
|
||||||
|
for (const agentFile of agents) {
|
||||||
|
const filePath = path.join(wdsPath, agentFile);
|
||||||
|
if (await this.exists(filePath)) {
|
||||||
|
await this.removeFile(filePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log(chalk.dim(`Removed Cline WDS configuration`));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detect if Cline is configured in project
|
||||||
|
* @param {string} projectDir - Project directory
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
async detect(projectDir) {
|
||||||
|
return await this.exists(path.join(projectDir, '.cline'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove file helper
|
||||||
|
* @param {string} filePath - File to remove
|
||||||
|
*/
|
||||||
|
async removeFile(filePath) {
|
||||||
|
const fs = require('fs-extra');
|
||||||
|
await fs.remove(filePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { ClineSetup };
|
||||||
|
|
@ -0,0 +1,116 @@
|
||||||
|
const path = require('node:path');
|
||||||
|
const { BaseIdeSetup } = require('./_base-ide');
|
||||||
|
const chalk = require('chalk');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cursor IDE setup handler for WDS
|
||||||
|
*/
|
||||||
|
class CursorSetup extends BaseIdeSetup {
|
||||||
|
constructor() {
|
||||||
|
super('cursor', 'Cursor', true); // preferred IDE
|
||||||
|
this.configDir = '.cursor/rules/wds';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setup Cursor IDE configuration
|
||||||
|
* @param {string} projectDir - Project directory
|
||||||
|
* @param {string} wdsDir - WDS installation directory
|
||||||
|
* @param {Object} options - Setup options
|
||||||
|
*/
|
||||||
|
async setup(projectDir, wdsDir, options = {}) {
|
||||||
|
// Create .cursor/rules/wds directory
|
||||||
|
const targetDir = path.join(projectDir, this.configDir);
|
||||||
|
await this.ensureDir(targetDir);
|
||||||
|
|
||||||
|
// Get all WDS agents
|
||||||
|
const agents = await this.getAgents(wdsDir);
|
||||||
|
|
||||||
|
if (agents.length === 0) {
|
||||||
|
throw new Error('No agents found in WDS installation');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create launcher file for each agent
|
||||||
|
let agentCount = 0;
|
||||||
|
for (const agent of agents) {
|
||||||
|
// Create launcher content that references the compiled agent
|
||||||
|
const launcher = this.formatAgentLauncher(agent.name, agent.path);
|
||||||
|
|
||||||
|
// Add Cursor-specific MDC frontmatter
|
||||||
|
const content = this.processContent(launcher, agent.metadata);
|
||||||
|
|
||||||
|
// Write launcher file with .mdc extension
|
||||||
|
const filePath = path.join(targetDir, `${agent.slug}.mdc`);
|
||||||
|
await this.writeFile(filePath, content);
|
||||||
|
agentCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.logger) {
|
||||||
|
options.logger.log(chalk.dim(` - ${agentCount} agent(s) configured for Cursor`));
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
agents: agentCount,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process content with Cursor-specific MDC frontmatter
|
||||||
|
* @param {string} content - Launcher content
|
||||||
|
* @param {Object} metadata - Agent metadata
|
||||||
|
* @returns {string} Processed content with Cursor MDC frontmatter
|
||||||
|
*/
|
||||||
|
processContent(content, metadata = {}) {
|
||||||
|
// Strip any existing frontmatter from launcher
|
||||||
|
const frontmatterRegex = /^---\s*\n[\s\S]*?\n---\s*\n/;
|
||||||
|
const contentWithoutFrontmatter = content.replace(frontmatterRegex, '');
|
||||||
|
|
||||||
|
const description = metadata.name ?
|
||||||
|
`WDS Agent: ${metadata.name} - ${metadata.description}` :
|
||||||
|
`WDS Agent: ${metadata.description || 'Agent'}`;
|
||||||
|
|
||||||
|
// Create Cursor MDC metadata header
|
||||||
|
const mdcHeader = `---
|
||||||
|
description: ${description}
|
||||||
|
globs:
|
||||||
|
alwaysApply: false
|
||||||
|
---
|
||||||
|
|
||||||
|
`;
|
||||||
|
|
||||||
|
return mdcHeader + contentWithoutFrontmatter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cleanup Cursor WDS configuration
|
||||||
|
* @param {string} projectDir - Project directory
|
||||||
|
*/
|
||||||
|
async cleanup(projectDir) {
|
||||||
|
const wdsPath = path.join(projectDir, this.configDir);
|
||||||
|
|
||||||
|
if (await this.exists(wdsPath)) {
|
||||||
|
await this.remove(wdsPath);
|
||||||
|
console.log(chalk.dim(`Removed Cursor WDS configuration`));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detect if Cursor is configured in project
|
||||||
|
* @param {string} projectDir - Project directory
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
async detect(projectDir) {
|
||||||
|
return await this.exists(path.join(projectDir, '.cursor'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove directory helper
|
||||||
|
* @param {string} dirPath - Directory to remove
|
||||||
|
*/
|
||||||
|
async remove(dirPath) {
|
||||||
|
const fs = require('fs-extra');
|
||||||
|
await fs.remove(dirPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { CursorSetup };
|
||||||
|
|
@ -0,0 +1,106 @@
|
||||||
|
const path = require('node:path');
|
||||||
|
const { BaseIdeSetup } = require('./_base-ide');
|
||||||
|
const chalk = require('chalk');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GitHub Copilot IDE setup handler for WDS
|
||||||
|
* Note: GitHub Copilot doesn't support includes/references, so we must copy full agent content
|
||||||
|
*/
|
||||||
|
class GitHubCopilotSetup extends BaseIdeSetup {
|
||||||
|
constructor() {
|
||||||
|
super('github-copilot', 'GitHub Copilot', false);
|
||||||
|
this.configFile = '.github/copilot-instructions.md';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setup GitHub Copilot IDE configuration
|
||||||
|
* @param {string} projectDir - Project directory
|
||||||
|
* @param {string} wdsDir - WDS installation directory
|
||||||
|
* @param {Object} options - Setup options
|
||||||
|
*/
|
||||||
|
async setup(projectDir, wdsDir, options = {}) {
|
||||||
|
// Ensure .github directory exists
|
||||||
|
const githubDir = path.join(projectDir, '.github');
|
||||||
|
await this.ensureDir(githubDir);
|
||||||
|
|
||||||
|
// Get all WDS agents
|
||||||
|
const agents = await this.getAgents(wdsDir);
|
||||||
|
|
||||||
|
if (agents.length === 0) {
|
||||||
|
throw new Error('No agents found in WDS installation');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build content to append
|
||||||
|
let content = '\n\n## WDS Agents\n\n';
|
||||||
|
content += '<!-- Generated by WDS installer - Do not edit manually -->\n\n';
|
||||||
|
|
||||||
|
// For each agent, read the full compiled agent content
|
||||||
|
let agentCount = 0;
|
||||||
|
for (const agent of agents) {
|
||||||
|
// Read the full agent content
|
||||||
|
const agentContent = await this.readFile(agent.path);
|
||||||
|
|
||||||
|
// Add section header
|
||||||
|
content += `### ${agent.metadata.name} - ${agent.metadata.description}\n\n`;
|
||||||
|
|
||||||
|
// Add full agent content
|
||||||
|
content += agentContent + '\n\n';
|
||||||
|
content += '---\n\n';
|
||||||
|
|
||||||
|
agentCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append to copilot-instructions.md
|
||||||
|
const filePath = path.join(projectDir, this.configFile);
|
||||||
|
|
||||||
|
if (await this.exists(filePath)) {
|
||||||
|
// File exists, append to it
|
||||||
|
const fs = require('fs-extra');
|
||||||
|
await fs.appendFile(filePath, content);
|
||||||
|
} else {
|
||||||
|
// File doesn't exist, create it
|
||||||
|
await this.writeFile(filePath, content);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.logger) {
|
||||||
|
options.logger.log(chalk.dim(` - ${agentCount} agent(s) configured for GitHub Copilot`));
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
agents: agentCount,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cleanup GitHub Copilot WDS configuration
|
||||||
|
* @param {string} projectDir - Project directory
|
||||||
|
*/
|
||||||
|
async cleanup(projectDir) {
|
||||||
|
const filePath = path.join(projectDir, this.configFile);
|
||||||
|
|
||||||
|
if (await this.exists(filePath)) {
|
||||||
|
// Read file, remove WDS Agents section
|
||||||
|
const fs = require('fs-extra');
|
||||||
|
const content = await fs.readFile(filePath, 'utf8');
|
||||||
|
|
||||||
|
// Remove WDS Agents section (everything from "## WDS Agents" to the end or next ## section)
|
||||||
|
const wdsAgentsRegex = /\n\n## WDS Agents\n\n[\s\S]*?(?=\n\n##\s|$)/;
|
||||||
|
const cleanedContent = content.replace(wdsAgentsRegex, '');
|
||||||
|
|
||||||
|
await fs.writeFile(filePath, cleanedContent);
|
||||||
|
console.log(chalk.dim(`Removed GitHub Copilot WDS configuration`));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detect if GitHub Copilot is configured in project
|
||||||
|
* @param {string} projectDir - Project directory
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
async detect(projectDir) {
|
||||||
|
return await this.exists(path.join(projectDir, '.github'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { GitHubCopilotSetup };
|
||||||
|
|
@ -0,0 +1,222 @@
|
||||||
|
const fs = require('fs-extra');
|
||||||
|
const path = require('node:path');
|
||||||
|
const chalk = require('chalk');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IDE Manager - handles IDE-specific setup for WDS
|
||||||
|
* Dynamically discovers and loads IDE handlers
|
||||||
|
*/
|
||||||
|
class IdeManager {
|
||||||
|
constructor() {
|
||||||
|
this.handlers = new Map();
|
||||||
|
this.loadHandlers();
|
||||||
|
this.wdsFolderName = '_wds'; // Default, can be overridden
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the WDS folder name for all IDE handlers
|
||||||
|
* @param {string} wdsFolderName - The WDS folder name
|
||||||
|
*/
|
||||||
|
setWdsFolderName(wdsFolderName) {
|
||||||
|
this.wdsFolderName = wdsFolderName;
|
||||||
|
// Update all loaded handlers
|
||||||
|
for (const handler of this.handlers.values()) {
|
||||||
|
if (typeof handler.setWdsFolderName === 'function') {
|
||||||
|
handler.setWdsFolderName(wdsFolderName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dynamically load all IDE handlers from directory
|
||||||
|
*/
|
||||||
|
loadHandlers() {
|
||||||
|
const ideDir = __dirname;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Get all JS files in the IDE directory
|
||||||
|
const files = fs.readdirSync(ideDir).filter((file) => {
|
||||||
|
// Skip base class, manager, and utility files (starting with _)
|
||||||
|
return (
|
||||||
|
file.endsWith('.js') &&
|
||||||
|
!file.startsWith('_') &&
|
||||||
|
file !== 'manager.js'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Sort alphabetically for consistent ordering
|
||||||
|
files.sort();
|
||||||
|
|
||||||
|
for (const file of files) {
|
||||||
|
const moduleName = path.basename(file, '.js');
|
||||||
|
|
||||||
|
try {
|
||||||
|
const modulePath = path.join(ideDir, file);
|
||||||
|
const HandlerModule = require(modulePath);
|
||||||
|
|
||||||
|
// Get the first exported class (handles various export styles)
|
||||||
|
const HandlerClass = HandlerModule.default || HandlerModule[Object.keys(HandlerModule)[0]];
|
||||||
|
|
||||||
|
if (HandlerClass) {
|
||||||
|
const instance = new HandlerClass();
|
||||||
|
// Use the name property from the instance (set in constructor)
|
||||||
|
// Only add if the instance has a valid name
|
||||||
|
if (instance.name && typeof instance.name === 'string') {
|
||||||
|
this.handlers.set(instance.name, instance);
|
||||||
|
} else {
|
||||||
|
console.log(chalk.yellow(` Warning: ${moduleName} handler missing valid 'name' property`));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log(chalk.yellow(` Warning: Could not load ${moduleName}: ${error.message}`));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(chalk.red('Failed to load IDE handlers:'), error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all available IDEs with their metadata
|
||||||
|
* @returns {Array} Array of IDE information objects
|
||||||
|
*/
|
||||||
|
getAvailableIdes() {
|
||||||
|
const ides = [];
|
||||||
|
|
||||||
|
for (const [key, handler] of this.handlers) {
|
||||||
|
// Skip handlers without valid names
|
||||||
|
const name = handler.displayName || handler.name || key;
|
||||||
|
|
||||||
|
// Filter out invalid entries
|
||||||
|
if (!key || !name || typeof key !== 'string' || typeof name !== 'string') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ides.push({
|
||||||
|
value: key,
|
||||||
|
name: name,
|
||||||
|
preferred: handler.preferred || false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort: preferred first, then alphabetical
|
||||||
|
ides.sort((a, b) => {
|
||||||
|
if (a.preferred && !b.preferred) return -1;
|
||||||
|
if (!a.preferred && b.preferred) return 1;
|
||||||
|
return a.name.localeCompare(b.name);
|
||||||
|
});
|
||||||
|
|
||||||
|
return ides;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setup IDE integrations for selected IDEs
|
||||||
|
* Main method called by installer
|
||||||
|
* @param {string} projectDir - Project directory
|
||||||
|
* @param {string} wdsDir - WDS installation directory
|
||||||
|
* @param {Array<string>} selectedIdes - List of IDE names to setup
|
||||||
|
* @param {Object} options - Setup options (logger, etc.)
|
||||||
|
* @returns {Object} Results object with success/failure counts
|
||||||
|
*/
|
||||||
|
async setup(projectDir, wdsDir, selectedIdes, options = {}) {
|
||||||
|
const results = {
|
||||||
|
success: [],
|
||||||
|
failed: [],
|
||||||
|
skipped: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
const logger = options.logger || console;
|
||||||
|
|
||||||
|
// Set WDS folder name if provided
|
||||||
|
if (options.wdsFolderName) {
|
||||||
|
this.setWdsFolderName(options.wdsFolderName);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const ideName of selectedIdes) {
|
||||||
|
const handler = this.handlers.get(ideName.toLowerCase());
|
||||||
|
|
||||||
|
if (!handler) {
|
||||||
|
logger.warn(chalk.yellow(` ⚠ IDE '${ideName}' is not yet supported`));
|
||||||
|
results.skipped.push({ ide: ideName, reason: 'unsupported' });
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
logger.log(chalk.dim(` Setting up ${handler.displayName || ideName}...`));
|
||||||
|
await handler.setup(projectDir, wdsDir, options);
|
||||||
|
results.success.push(ideName);
|
||||||
|
logger.log(chalk.green(` ✓ ${handler.displayName || ideName} configured`));
|
||||||
|
} catch (error) {
|
||||||
|
logger.warn(chalk.yellow(` ⚠ Failed to setup ${ideName}: ${error.message}`));
|
||||||
|
results.failed.push({ ide: ideName, error: error.message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log summary
|
||||||
|
if (results.success.length > 0) {
|
||||||
|
logger.log(chalk.dim(` Configured ${results.success.length} IDE(s)`));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (results.failed.length > 0) {
|
||||||
|
logger.warn(chalk.yellow(` ${results.failed.length} IDE(s) failed to configure`));
|
||||||
|
}
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cleanup IDE configurations
|
||||||
|
* @param {string} projectDir - Project directory
|
||||||
|
* @returns {Array} Results for each IDE
|
||||||
|
*/
|
||||||
|
async cleanup(projectDir) {
|
||||||
|
const results = [];
|
||||||
|
|
||||||
|
for (const [name, handler] of this.handlers) {
|
||||||
|
try {
|
||||||
|
await handler.cleanup(projectDir);
|
||||||
|
results.push({ ide: name, success: true });
|
||||||
|
} catch (error) {
|
||||||
|
results.push({ ide: name, success: false, error: error.message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get list of supported IDEs
|
||||||
|
* @returns {Array} List of supported IDE names
|
||||||
|
*/
|
||||||
|
getSupportedIdes() {
|
||||||
|
return [...this.handlers.keys()];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if an IDE is supported
|
||||||
|
* @param {string} ideName - Name of the IDE
|
||||||
|
* @returns {boolean} True if IDE is supported
|
||||||
|
*/
|
||||||
|
isSupported(ideName) {
|
||||||
|
return this.handlers.has(ideName.toLowerCase());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detect installed IDEs in project
|
||||||
|
* @param {string} projectDir - Project directory
|
||||||
|
* @returns {Array} List of detected IDEs
|
||||||
|
*/
|
||||||
|
async detectInstalledIdes(projectDir) {
|
||||||
|
const detected = [];
|
||||||
|
|
||||||
|
for (const [name, handler] of this.handlers) {
|
||||||
|
if (typeof handler.detect === 'function' && (await handler.detect(projectDir))) {
|
||||||
|
detected.push(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return detected;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { IdeManager };
|
||||||
|
|
@ -0,0 +1,108 @@
|
||||||
|
const path = require('node:path');
|
||||||
|
const { BaseIdeSetup } = require('./_base-ide');
|
||||||
|
const chalk = require('chalk');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Windsurf IDE setup handler for WDS
|
||||||
|
*/
|
||||||
|
class WindsurfSetup extends BaseIdeSetup {
|
||||||
|
constructor() {
|
||||||
|
super('windsurf', 'Windsurf', true); // preferred IDE
|
||||||
|
this.configDir = '.windsurf/workflows/wds';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setup Windsurf IDE configuration
|
||||||
|
* @param {string} projectDir - Project directory
|
||||||
|
* @param {string} wdsDir - WDS installation directory
|
||||||
|
* @param {Object} options - Setup options
|
||||||
|
*/
|
||||||
|
async setup(projectDir, wdsDir, options = {}) {
|
||||||
|
// Create .windsurf/workflows/wds directory
|
||||||
|
const targetDir = path.join(projectDir, this.configDir);
|
||||||
|
await this.ensureDir(targetDir);
|
||||||
|
|
||||||
|
// Get all WDS agents
|
||||||
|
const agents = await this.getAgents(wdsDir);
|
||||||
|
|
||||||
|
if (agents.length === 0) {
|
||||||
|
throw new Error('No agents found in WDS installation');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create launcher file for each agent
|
||||||
|
let agentCount = 0;
|
||||||
|
for (const agent of agents) {
|
||||||
|
// Create launcher content that references the compiled agent
|
||||||
|
const launcher = this.formatAgentLauncher(agent.name, agent.path);
|
||||||
|
|
||||||
|
// Add Windsurf-specific frontmatter
|
||||||
|
const content = this.processContent(launcher, agent.metadata);
|
||||||
|
|
||||||
|
// Write launcher file
|
||||||
|
const filePath = path.join(targetDir, `${agent.slug}.md`);
|
||||||
|
await this.writeFile(filePath, content);
|
||||||
|
agentCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.logger) {
|
||||||
|
options.logger.log(chalk.dim(` - ${agentCount} agent(s) configured for Windsurf`));
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
agents: agentCount,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process content with Windsurf-specific frontmatter
|
||||||
|
* @param {string} content - Launcher content
|
||||||
|
* @param {Object} metadata - Agent metadata
|
||||||
|
* @returns {string} Processed content with Windsurf YAML frontmatter
|
||||||
|
*/
|
||||||
|
processContent(content, metadata = {}) {
|
||||||
|
const description = metadata.name ?
|
||||||
|
`${metadata.name} - ${metadata.description}` :
|
||||||
|
metadata.description || 'WDS Agent';
|
||||||
|
|
||||||
|
return `---
|
||||||
|
description: ${description}
|
||||||
|
auto_execution_mode: 3
|
||||||
|
---
|
||||||
|
|
||||||
|
${content}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cleanup Windsurf WDS configuration
|
||||||
|
* @param {string} projectDir - Project directory
|
||||||
|
*/
|
||||||
|
async cleanup(projectDir) {
|
||||||
|
const wdsPath = path.join(projectDir, this.configDir);
|
||||||
|
|
||||||
|
if (await this.exists(wdsPath)) {
|
||||||
|
await this.remove(wdsPath);
|
||||||
|
console.log(chalk.dim(`Removed Windsurf WDS configuration`));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detect if Windsurf is configured in project
|
||||||
|
* @param {string} projectDir - Project directory
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
async detect(projectDir) {
|
||||||
|
return await this.exists(path.join(projectDir, '.windsurf'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove directory helper
|
||||||
|
* @param {string} dirPath - Directory to remove
|
||||||
|
*/
|
||||||
|
async remove(dirPath) {
|
||||||
|
const fs = require('fs-extra');
|
||||||
|
await fs.remove(dirPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { WindsurfSetup };
|
||||||
|
|
@ -232,7 +232,7 @@ function buildActivationBlock(agent, wdsFolder) {
|
||||||
<step n="1">Load persona from this current agent file (already in context)</step>
|
<step n="1">Load persona from this current agent file (already in context)</step>
|
||||||
<step n="2">IMMEDIATE ACTION REQUIRED - BEFORE ANY OUTPUT:
|
<step n="2">IMMEDIATE ACTION REQUIRED - BEFORE ANY OUTPUT:
|
||||||
- Load and read {project-root}/${wdsFolder}/config.yaml NOW
|
- Load and read {project-root}/${wdsFolder}/config.yaml NOW
|
||||||
- Store ALL fields as session variables: {user_name}, {communication_language}, {output_folder}
|
- Store ALL fields as session variables: {user_name}, {communication_language}, {output_folder}, {starting_point}, {project_name}
|
||||||
- VERIFY: If config not loaded, STOP and report error to user
|
- VERIFY: If config not loaded, STOP and report error to user
|
||||||
- DO NOT PROCEED to step 3 until config is successfully loaded and variables stored
|
- DO NOT PROCEED to step 3 until config is successfully loaded and variables stored
|
||||||
</step>
|
</step>
|
||||||
|
|
|
||||||
|
|
@ -100,6 +100,43 @@ class Installer {
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Step 3.5: Setup IDE integrations
|
||||||
|
if (config.ides && config.ides.length > 0) {
|
||||||
|
const ideSpinner = ora('Setting up IDE integrations...').start();
|
||||||
|
try {
|
||||||
|
const { IdeManager } = require('../installers/lib/ide/manager');
|
||||||
|
const ideManager = new IdeManager();
|
||||||
|
|
||||||
|
const results = await ideManager.setup(
|
||||||
|
projectDir,
|
||||||
|
wdsDir,
|
||||||
|
config.ides,
|
||||||
|
{
|
||||||
|
logger: {
|
||||||
|
log: (msg) => {}, // Suppress detailed logs during spinner
|
||||||
|
warn: (msg) => console.log(msg),
|
||||||
|
},
|
||||||
|
wdsFolderName: wdsFolder,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const successCount = results.success.length;
|
||||||
|
const failedCount = results.failed.length;
|
||||||
|
const skippedCount = results.skipped.length;
|
||||||
|
|
||||||
|
if (successCount > 0) {
|
||||||
|
ideSpinner.succeed(`IDE integrations configured (${successCount} IDE${successCount > 1 ? 's' : ''})`);
|
||||||
|
} else if (failedCount > 0 || skippedCount > 0) {
|
||||||
|
ideSpinner.warn(`IDE setup completed with ${failedCount} failed, ${skippedCount} skipped`);
|
||||||
|
} else {
|
||||||
|
ideSpinner.succeed('IDE integrations configured');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
ideSpinner.warn(`IDE setup encountered issues: ${error.message}`);
|
||||||
|
console.log(chalk.dim(' You can still use WDS by manually activating agents'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Step 4: Create work products folder structure
|
// Step 4: Create work products folder structure
|
||||||
const rootFolder = root_folder || 'design-process';
|
const rootFolder = root_folder || 'design-process';
|
||||||
const docsSpinner = ora(`Creating project folders in ${rootFolder}/...`).start();
|
const docsSpinner = ora(`Creating project folders in ${rootFolder}/...`).start();
|
||||||
|
|
|
||||||
|
|
@ -94,7 +94,7 @@ class UI {
|
||||||
{
|
{
|
||||||
type: 'confirm',
|
type: 'confirm',
|
||||||
name: 'install_learning',
|
name: 'install_learning',
|
||||||
message: 'Install learning & reference material? (You can remove it later)',
|
message: 'Install learning & reference material?',
|
||||||
default: true,
|
default: true,
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue