From 8916211ba993d2a63797644d00532c900599d133 Mon Sep 17 00:00:00 2001 From: Brian Madison Date: Thu, 12 Jun 2025 22:38:24 -0500 Subject: [PATCH] installer functional --- .gitignore | 1 + .vscode/extensions.json | 6 - .vscode/settings.json | 40 - .../web-bundles}/agents/analyst.txt | 0 .../web-bundles}/agents/architect.txt | 0 .../web-bundles}/agents/bmad-master.txt | 0 .../web-bundles}/agents/bmad-orchestrator.txt | 0 .../web-bundles}/agents/dev.txt | 0 .../web-bundles}/agents/pm.txt | 0 .../web-bundles}/agents/po.txt | 0 .../web-bundles}/agents/qa.txt | 0 .../web-bundles}/agents/sm.txt | 0 .../web-bundles}/agents/ux-expert.txt | 0 .../web-bundles}/teams/team-all.txt | 0 .../web-bundles}/teams/team-fullstack.txt | 0 .../web-bundles}/teams/team-no-ui.txt | 0 ide-tool-rules/.claude/commands/agentbmad.md | 110 --- ide-tool-rules/.claude/commands/exit-agent.md | 25 - ide-tool-rules/.claude/commands/ide-agent.md | 25 - .../.cursor/rules/000-ide-agent-commands.mdc | 17 - ide-tool-rules/.windsurf/rules/ide-agent.md | 7 - installer/config/install.config.yml | 129 ---- installer/package.json | 50 -- package-lock.json | 3 - package.json | 4 +- tools/builders/web-builder.js | 2 +- tools/cli.js | 23 +- {installer => tools/installer}/README.md | 0 tools/installer/bin/bmad.js | 158 ++++ tools/installer/config/install.config.yml | 167 +++++ tools/installer/lib/config-loader.js | 62 ++ tools/installer/lib/file-manager.js | 154 ++++ tools/installer/lib/ide-setup.js | 189 +++++ tools/installer/lib/installer.js | 269 +++++++ tools/installer/package-lock.json | 704 ++++++++++++++++++ tools/installer/package.json | 43 ++ .../installer}/templates/claude-commands.md | 0 .../installer}/templates/cursor-rules.md | 0 .../installer}/templates/windsurf-rules.md | 0 39 files changed, 1769 insertions(+), 419 deletions(-) delete mode 100644 .vscode/extensions.json delete mode 100644 .vscode/settings.json rename {web-build => bmad-core/web-bundles}/agents/analyst.txt (100%) rename {web-build => bmad-core/web-bundles}/agents/architect.txt (100%) rename {web-build => bmad-core/web-bundles}/agents/bmad-master.txt (100%) rename {web-build => bmad-core/web-bundles}/agents/bmad-orchestrator.txt (100%) rename {web-build => bmad-core/web-bundles}/agents/dev.txt (100%) rename {web-build => bmad-core/web-bundles}/agents/pm.txt (100%) rename {web-build => bmad-core/web-bundles}/agents/po.txt (100%) rename {web-build => bmad-core/web-bundles}/agents/qa.txt (100%) rename {web-build => bmad-core/web-bundles}/agents/sm.txt (100%) rename {web-build => bmad-core/web-bundles}/agents/ux-expert.txt (100%) rename {web-build => bmad-core/web-bundles}/teams/team-all.txt (100%) rename {web-build => bmad-core/web-bundles}/teams/team-fullstack.txt (100%) rename {web-build => bmad-core/web-bundles}/teams/team-no-ui.txt (100%) delete mode 100644 ide-tool-rules/.claude/commands/agentbmad.md delete mode 100644 ide-tool-rules/.claude/commands/exit-agent.md delete mode 100644 ide-tool-rules/.claude/commands/ide-agent.md delete mode 100644 ide-tool-rules/.cursor/rules/000-ide-agent-commands.mdc delete mode 100644 ide-tool-rules/.windsurf/rules/ide-agent.md delete mode 100644 installer/config/install.config.yml delete mode 100644 installer/package.json rename {installer => tools/installer}/README.md (100%) create mode 100755 tools/installer/bin/bmad.js create mode 100644 tools/installer/config/install.config.yml create mode 100644 tools/installer/lib/config-loader.js create mode 100644 tools/installer/lib/file-manager.js create mode 100644 tools/installer/lib/ide-setup.js create mode 100644 tools/installer/lib/installer.js create mode 100644 tools/installer/package-lock.json create mode 100644 tools/installer/package.json rename {installer => tools/installer}/templates/claude-commands.md (100%) rename {installer => tools/installer}/templates/cursor-rules.md (100%) rename {installer => tools/installer}/templates/windsurf-rules.md (100%) diff --git a/.gitignore b/.gitignore index 2e1afc86..ef4b5660 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,4 @@ Thumbs.db CLAUDE.md .ai/* +test-project-install/* \ No newline at end of file diff --git a/.vscode/extensions.json b/.vscode/extensions.json deleted file mode 100644 index aef996fd..00000000 --- a/.vscode/extensions.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "recommendations": [ - "davidanson.vscode-markdownlint", - "streetsidesoftware.code-spell-checker" - ] -} diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 3d76cbb5..00000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "cSpell.words": [ - "agentic", - "Axios", - "BMAD", - "Centricity", - "dataclass", - "docstrings", - "emergently", - "explorative", - "frontends", - "golint", - "Goroutines", - "HSTS", - "httpx", - "Immer", - "implementability", - "Inclusivity", - "Luxon", - "pasteable", - "Pino", - "Polyrepo", - "Pydantic", - "pyproject", - "rescope", - "roadmaps", - "roleplay", - "runbooks", - "Serilog", - "shadcn", - "structlog", - "Systemization", - "taskroot", - "Testcontainers", - "tmpl", - "VARCHAR", - "venv", - "WCAG" - ] -} diff --git a/web-build/agents/analyst.txt b/bmad-core/web-bundles/agents/analyst.txt similarity index 100% rename from web-build/agents/analyst.txt rename to bmad-core/web-bundles/agents/analyst.txt diff --git a/web-build/agents/architect.txt b/bmad-core/web-bundles/agents/architect.txt similarity index 100% rename from web-build/agents/architect.txt rename to bmad-core/web-bundles/agents/architect.txt diff --git a/web-build/agents/bmad-master.txt b/bmad-core/web-bundles/agents/bmad-master.txt similarity index 100% rename from web-build/agents/bmad-master.txt rename to bmad-core/web-bundles/agents/bmad-master.txt diff --git a/web-build/agents/bmad-orchestrator.txt b/bmad-core/web-bundles/agents/bmad-orchestrator.txt similarity index 100% rename from web-build/agents/bmad-orchestrator.txt rename to bmad-core/web-bundles/agents/bmad-orchestrator.txt diff --git a/web-build/agents/dev.txt b/bmad-core/web-bundles/agents/dev.txt similarity index 100% rename from web-build/agents/dev.txt rename to bmad-core/web-bundles/agents/dev.txt diff --git a/web-build/agents/pm.txt b/bmad-core/web-bundles/agents/pm.txt similarity index 100% rename from web-build/agents/pm.txt rename to bmad-core/web-bundles/agents/pm.txt diff --git a/web-build/agents/po.txt b/bmad-core/web-bundles/agents/po.txt similarity index 100% rename from web-build/agents/po.txt rename to bmad-core/web-bundles/agents/po.txt diff --git a/web-build/agents/qa.txt b/bmad-core/web-bundles/agents/qa.txt similarity index 100% rename from web-build/agents/qa.txt rename to bmad-core/web-bundles/agents/qa.txt diff --git a/web-build/agents/sm.txt b/bmad-core/web-bundles/agents/sm.txt similarity index 100% rename from web-build/agents/sm.txt rename to bmad-core/web-bundles/agents/sm.txt diff --git a/web-build/agents/ux-expert.txt b/bmad-core/web-bundles/agents/ux-expert.txt similarity index 100% rename from web-build/agents/ux-expert.txt rename to bmad-core/web-bundles/agents/ux-expert.txt diff --git a/web-build/teams/team-all.txt b/bmad-core/web-bundles/teams/team-all.txt similarity index 100% rename from web-build/teams/team-all.txt rename to bmad-core/web-bundles/teams/team-all.txt diff --git a/web-build/teams/team-fullstack.txt b/bmad-core/web-bundles/teams/team-fullstack.txt similarity index 100% rename from web-build/teams/team-fullstack.txt rename to bmad-core/web-bundles/teams/team-fullstack.txt diff --git a/web-build/teams/team-no-ui.txt b/bmad-core/web-bundles/teams/team-no-ui.txt similarity index 100% rename from web-build/teams/team-no-ui.txt rename to bmad-core/web-bundles/teams/team-no-ui.txt diff --git a/ide-tool-rules/.claude/commands/agentbmad.md b/ide-tool-rules/.claude/commands/agentbmad.md deleted file mode 100644 index 44ea1be6..00000000 --- a/ide-tool-rules/.claude/commands/agentbmad.md +++ /dev/null @@ -1,110 +0,0 @@ -# Role: BMAD Master Orchestrator IDE Agent - -## File References - -`taskroot`: `bmad-core/tasks/` -`templates`: `bmad-core/templates/` -`checklists`: `bmad-core/checklists/` -`ide-agents`: `bmad-core/ide-agents/` -`agents`: `bmad-core/agents/` -`personas`: `bmad-core/personas/` -`workflows`: `bmad-core/workflows/` -`knowledge-base`: `bmad-core/data/bmad-kb.md` -`create-doc`: `taskroot/create-doc` - -## Persona - -- **Name:** BMad -- **Role:** Master Orchestrator & Technical Expert -- **Identity:** The unified interface to all BMAD-METHOD capabilities, able to dynamically transform into any specialized agent or execute any task -- **Focus:** Orchestrating the right agent or capability for each user need, maintaining efficiency by loading resources only when needed -- **Style:** Helpful, encouraging, technically brilliant yet approachable. Breaks down complex topics while maintaining professional friendliness - -## Core Principles (Always Active) - -- **Dynamic Transformation:** Can become any IDE agent or full agent (with persona) on demand, loading files only when needed -- **Efficient Resource Management:** Never pre-load agents, templates, or knowledge base - discover and load at runtime -- **Intelligent Routing:** Assess user needs and recommend the best approach, agent, or workflow -- **Runtime Discovery:** Dynamically discover available resources (agents, templates, tasks) from file system when needed -- **Context Awareness:** Track current state and guide users to next logical steps -- **Numbered Options Protocol:** When presenting multiple options, always use numbered lists for easy selection -- **Lazy Loading:** Only load the knowledge base when explicitly requested via \*kb-mode command - -## Critical Startup Operating Instructions - -1. Announce your name and role: "Hey! I'm BMad, your BMAD-METHOD orchestrator. I can become any specialized agent or help you with any BMAD task. You can type `*help` at any time to see available options." -2. Assess what the user wants to accomplish -3. If request matches a specific agent's expertise, suggest becoming that agent -4. If request is generic, offer numbered options or execute directly -5. Only load specific resources (agents, templates, KB) when actually needed - -## Commands - -### Core Commands - -- `*help` - Show these available commands as a numbered list offering selection -- `*chat-mode` - Enter conversational mode, staying in character while offering `taskroot/advanced-elicitation` when providing advice or multiple options. Ends if other task or command is given -- `*kb-mode` - Load knowledge base and enter full BMAD-METHOD help mode -- `*status` - Show current context, active agent (if any), and progress - -### Agent Management - -- `*ide-agent {name/role}` - Transform into specified IDE agent (fuzzy match supported) -- `*agent {name/role}` - Load full agent with persona (uses more context) -- `*agent-exit` - Return to BMAD orchestrator mode -- `*list-agents` - Show available IDE agents and agents (Name and Role) for numbered list choice selection - -### Dynamic Task Execution - -- `*create {template}` - Create document using specified template with `create-doc` task (fuzzy match) -- `*run {checklist}` - Execute specified checklist validation with `taskroot/execute-checklist` -- `*task {task-name}` - Run any task from taskroot (fuzzy match), if none specified, offer numbered list of tasks from `taskroot` -- `*workflow {type}` - Start specified workflow or list available workflows for selection - -### Discovery Commands - -- `*list-templates` - Discover and show numbered list of available templates for selection to create -- `*list-tasks` - Discover and show numbered list of available tasks for selection to execute -- `*list-checklists` - Discover and show numbered list of available checklists for selection to run -- `*list-workflows` - Discover and show numbered list of available workflows for selection to activate - -## Agent Transformation Protocol - -When user requests agent transformation: - -1. Fuzzy match the requested name/role against available agents -2. For IDE agents: Load the `ide-agents` file and fully become that agent -3. For full agents: Load both the `agents` file and any references files in the agent such as `personas`, merge capabilities -4. Announce the transformation clearly -5. Operate as that agent until \*agent-exit command - -## Runtime Discovery Protocol - -Instead of hard-coding lists, generate lists from folders when requested and user asked or was not specific. - -Use Fuzzy Matching with 85% confidence. If unsure offer the list of whats in a folder. Examples of fuzzy matching: - -- "create prd" → matches "prd-tmpl.md" -- "become architect" → matches "architect.ide.md" -- "run po checklist" → matches "po-master-checklist.md" - -## Knowledge Base Protocol - -The knowledge base is only loaded when: - -1. User explicitly runs \*kb-mode command -2. User asks detailed questions about BMAD methodology when in chat mode -3. User requests comprehensive help beyond basic commands that is not clear already or embedded in a workflow - -This keeps context usage minimal for normal operations. ALWAYS indicate KB has been loaded if loaded. - -## Workflow Guidance - -When user needs guidance: - -1. Ask about project type (greenfield/brownfield) -2. Ask about scope (UI/service/fullstack) -3. Recommend appropriate workflow -4. Guide through workflow stages with appropriate agents - -Remember: As BMAD orchestrator, you have access to ALL capabilities but load them intelligently based on user needs. Always provide clear next steps and maintain efficiency by loading only what's needed. diff --git a/ide-tool-rules/.claude/commands/exit-agent.md b/ide-tool-rules/.claude/commands/exit-agent.md deleted file mode 100644 index 3ede34b3..00000000 --- a/ide-tool-rules/.claude/commands/exit-agent.md +++ /dev/null @@ -1,25 +0,0 @@ -# /exit-persona - -## Description - -Returns Claude to default assistant mode, exiting any active IDE agent persona. - -**Usage:** `/exit-agent` - -## Behavior - -- Immediately exits the current agent persona if one is active -- Returns to standard Claude Code assistant capabilities -- Clears any agent-specific context or workflows -- Confirms the exit to the user - -## Example - -```text -User: /exit-agent -Claude: Exited IDE agent mode. I'm now back to my standard Claude Code assistant capabilities. -``` - -## Related Commands - -- `/ide-agent` - Switch to a specific IDE agent persona diff --git a/ide-tool-rules/.claude/commands/ide-agent.md b/ide-tool-rules/.claude/commands/ide-agent.md deleted file mode 100644 index f3ba6f41..00000000 --- a/ide-tool-rules/.claude/commands/ide-agent.md +++ /dev/null @@ -1,25 +0,0 @@ -# /ide-agent - -## Description - -Switches Claude to embody a specific IDE agent persona from the BMAD-METHOD framework. - -**Usage:** `/ide-agent ` or `/ide-agent` (to list available agents) - -**Instructions:** - -IMPORTANT: When an agent name is provided (e.g., `/ide-agent pm`): - -1. IMMEDIATELY read the file at `bmad-core/ide-agents/{agent-name}.ide.md` -2. DO NOT search for related files or use the Task tool -3. Start operating as that agent persona right away - -For complete behavior details, see `bmad-core/utils/agent-switcher.ide.md`. - -## Related Commands - -### /exit-agent - -Returns Claude to default assistant mode, exiting any active IDE agent persona. - -**Usage:** `/exit-agent` or `/exit` diff --git a/ide-tool-rules/.cursor/rules/000-ide-agent-commands.mdc b/ide-tool-rules/.cursor/rules/000-ide-agent-commands.mdc deleted file mode 100644 index 45cf6d0f..00000000 --- a/ide-tool-rules/.cursor/rules/000-ide-agent-commands.mdc +++ /dev/null @@ -1,17 +0,0 @@ ---- -description: -globs: -alwaysApply: true ---- - -# IDE Agent Slash Commands - -## Slash Commands - -### `/ide-agent` or `/ide-agent ` - -When the user types `/ide-agent` or `/ide-agent `, read and follow the complete instructions in `bmad-core/utils/agent-switcher.ide.md`. - -### `/exit-agent`, `/exit-persona`, or `/exit` - -When the user types any of these exit commands, follow the "Exiting Agent Mode" instructions in `bmad-core/utils/agent-switcher.ide.md`. diff --git a/ide-tool-rules/.windsurf/rules/ide-agent.md b/ide-tool-rules/.windsurf/rules/ide-agent.md deleted file mode 100644 index 251e89f3..00000000 --- a/ide-tool-rules/.windsurf/rules/ide-agent.md +++ /dev/null @@ -1,7 +0,0 @@ -# IDE Agent Slash Commands - -## Commands - -When the user types at the start of a line `ide-agent` or `ide-agent `, read and follow the complete instructions in `bmad-core/utils/agent-switcher.ide.md`. - -When the user types at the start of a line `exit-agent`, follow the "Exiting Agent Mode" instructions in `bmad-core/utils/agent-switcher.ide.md`. diff --git a/installer/config/install.config.yml b/installer/config/install.config.yml deleted file mode 100644 index 183bb32f..00000000 --- a/installer/config/install.config.yml +++ /dev/null @@ -1,129 +0,0 @@ -# BMAD Method Installation Configuration -# This file defines what files are included in each installation profile - -installation-profiles: - minimal: - name: "IDE Agents Only" - description: "Minimal setup for IDE usage (recommended for beginners)" - files: - - "bmad-core/ide-agents/*.ide.md" - - "bmad-core/utils/agent-switcher.ide.md" - - "bmad-core/data/bmad-kb.md" - - "bmad-core/data/technical-preferences.md" - - "bmad-core/utils/template-format.md" - - core: - extends: minimal - name: "IDE + Web Agents" - description: "Includes web agent configurations for building web-compatible bundles" - files: - - "bmad-core/agents/*.yml" - - "bmad-core/templates/web-agent-startup-instructions-template.md" - - teams: - extends: core - name: "Teams & Workflows" - description: "Full team collaboration setup with pre-configured workflows" - files: - - "bmad-core/agent-teams/*.yml" - - "bmad-core/workflows/*.yml" - - "bmad-core/utils/workflow-management.md" - - developer: - extends: teams - name: "Full Developer Kit" - description: "Everything including agent creation tools and schemas" - files: - - "bmad-core/tasks/create-*.md" - - "bmad-core/schemas/*.yml" - - "bmad-core/tasks/shard-doc.md" - - "bmad-core/tasks/index-docs.md" - -# IDE-specific configuration for generating rules/commands -ide-configurations: - cursor: - name: "Cursor" - rule-file: ".cursorrules" - format: "single-file" - command-prefix: "" - instructions: | - # To use BMAD agents in Cursor: - # 1. Press Ctrl+L (Cmd+L on Mac) to open the chat - # 2. Type the agent name (e.g., "dev", "pm", "architect") - # 3. The agent will adopt that persona for the conversation - - claude-code: - name: "Claude Code" - rule-dir: ".claude/commands/" - format: "multi-file" - command-suffix: ".md" - instructions: | - # To use BMAD agents in Claude Code: - # 1. Type /agent-name (e.g., "/dev", "/pm", "/architect") - # 2. Claude will switch to that agent's persona - - windsurf: - name: "Windsurf" - rule-dir: ".windsurf/rules/" - format: "single-file" - rule-file: "bmad-agents.md" - instructions: | - # To use BMAD agents in Windsurf: - # 1. Type /agent-name (e.g., "/dev", "/pm") - # 2. Windsurf will adopt that agent's persona - - roo: - name: "Roo" - # Configuration TBD - needs research - format: "unknown" - instructions: | - # Roo configuration coming soon - # Manual setup: Copy IDE agent files to your Roo configuration - - cline: - name: "Cline" - # Configuration TBD - needs research - format: "unknown" - instructions: | - # Cline configuration coming soon - # Manual setup: Copy IDE agent files to your Cline configuration - -# Web agent export configuration -web-agents: - source-dir: "web-build" - available-agents: - - name: "analyst" - file: "analyst.md" - description: "Business Analyst agent for requirements gathering" - - name: "pm" - file: "pm.md" - description: "Product Manager agent for product strategy" - - name: "architect" - file: "architect.md" - description: "Solution Architect agent for technical design" - - name: "po" - file: "po.md" - description: "Product Owner agent for backlog management" - - name: "sm" - file: "sm.md" - description: "Scrum Master agent for Agile processes" - - name: "dev" - file: "dev.md" - description: "Developer agent for implementation" - - name: "qa" - file: "qa.md" - description: "QA Engineer agent for testing" - - name: "ux" - file: "ux-expert.md" - description: "UX Expert agent for user experience" - - teams: - - name: "fullstack" - file: "team-fullstack.md" - description: "Full-stack development team" - - name: "no-ui" - file: "team-no-ui.md" - description: "Backend/service development team" - - name: "all" - file: "team-all.md" - description: "Complete Agile team with all roles" diff --git a/installer/package.json b/installer/package.json deleted file mode 100644 index fa53697f..00000000 --- a/installer/package.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "name": "bmad-method", - "version": "4.0.0", - "description": "BMAD Method installer - Easy setup for AI-powered Agile development agents", - "bin": { - "bmad": "./bin/bmad.js" - }, - "main": "lib/installer.js", - "scripts": { - "test": "jest", - "lint": "eslint lib/**/*.js bin/**/*.js" - }, - "keywords": [ - "bmad", - "agile", - "ai", - "agents", - "development", - "methodology", - "installer" - ], - "author": "Brian (BMad) Madison", - "license": "MIT", - "repository": { - "type": "git", - "url": "https://github.com/your-org/bmad-method.git" - }, - "engines": { - "node": ">=14.0.0" - }, - "dependencies": { - "chalk": "^4.1.2", - "commander": "^9.4.1", - "fs-extra": "^11.1.0", - "glob": "^8.0.3", - "inquirer": "^8.2.5", - "js-yaml": "^4.1.0", - "ora": "^5.4.1" - }, - "devDependencies": { - "eslint": "^8.0.0", - "jest": "^29.5.0" - }, - "files": [ - "bin/", - "lib/", - "config/", - "templates/" - ] -} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 5da362f2..4bdcb203 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,9 +12,6 @@ "commander": "^9.4.1", "js-yaml": "^4.1.0" }, - "bin": { - "bmad-build": "tools/cli.js" - }, "devDependencies": { "jest": "^29.5.0" }, diff --git a/package.json b/package.json index f8e4991e..4eee5845 100644 --- a/package.json +++ b/package.json @@ -6,11 +6,11 @@ "bin": {}, "scripts": { "build": "node tools/cli.js build", + "install": "node tools/installer/bin/bmad.js install", "build:agents": "node tools/cli.js build --agents-only", "build:teams": "node tools/cli.js build --teams-only", "list:agents": "node tools/cli.js list:agents", - "validate": "node tools/cli.js validate", - "analyze:deps": "echo 'Dependency analysis not yet implemented'" + "validate": "node tools/cli.js validate" }, "dependencies": { "commander": "^9.4.1", diff --git a/tools/builders/web-builder.js b/tools/builders/web-builder.js index 5ab2a9cc..4d5239d5 100644 --- a/tools/builders/web-builder.js +++ b/tools/builders/web-builder.js @@ -7,7 +7,7 @@ class WebBuilder { this.rootDir = options.rootDir || process.cwd(); this.outputDirs = options.outputDirs || [ path.join(this.rootDir, 'dist'), - path.join(this.rootDir, 'web-build') + path.join(this.rootDir, 'bmad-core', 'web-bundles') ]; this.resolver = new DependencyResolver(this.rootDir); this.templatePath = path.join(this.rootDir, 'bmad-core', 'templates', 'web-agent-startup-instructions-template.md'); diff --git a/tools/cli.js b/tools/cli.js index 20dde6a4..006427b6 100644 --- a/tools/cli.js +++ b/tools/cli.js @@ -48,9 +48,9 @@ program program .command('list:agents') .description('List all available agents') - .action(() => { + .action(async () => { const builder = new WebBuilder({ rootDir: process.cwd() }); - const agents = builder.listAgents(); + const agents = await builder.resolver.listAgents(); console.log('Available agents:'); agents.forEach(agent => console.log(` - ${agent}`)); }); @@ -61,8 +61,23 @@ program .action(async () => { const builder = new WebBuilder({ rootDir: process.cwd() }); try { - await builder.validate(); - console.log('All configurations are valid!'); + // Validate by attempting to build all agents and teams + const agents = await builder.resolver.listAgents(); + const teams = await builder.resolver.listTeams(); + + console.log('Validating agents...'); + for (const agent of agents) { + await builder.resolver.resolveAgentDependencies(agent); + console.log(` ✓ ${agent}`); + } + + console.log('\nValidating teams...'); + for (const team of teams) { + await builder.resolver.resolveTeamDependencies(team); + console.log(` ✓ ${team}`); + } + + console.log('\nAll configurations are valid!'); } catch (error) { console.error('Validation failed:', error.message); process.exit(1); diff --git a/installer/README.md b/tools/installer/README.md similarity index 100% rename from installer/README.md rename to tools/installer/README.md diff --git a/tools/installer/bin/bmad.js b/tools/installer/bin/bmad.js new file mode 100755 index 00000000..8f9df53a --- /dev/null +++ b/tools/installer/bin/bmad.js @@ -0,0 +1,158 @@ +#!/usr/bin/env node + +const { program } = require('commander'); +const inquirer = require('inquirer'); +const chalk = require('chalk'); +const path = require('path'); +const { version } = require('../package.json'); +const installer = require('../lib/installer'); + +program + .version(version) + .description('BMAD Method installer - AI-powered Agile development framework'); + +program + .command('install') + .description('Install BMAD Method agents and tools') + .option('-f, --full', 'Install complete bmad-core folder') + .option('-a, --agent ', 'Install specific agent with dependencies') + .option('-d, --directory ', 'Installation directory (default: ./bmad-core)') + .option('-i, --ide ', 'Configure for specific IDE (cursor, claude-code, windsurf)') + .action(async (options) => { + try { + if (!options.full && !options.agent) { + // Interactive mode + const answers = await promptInstallation(options); + await installer.install(answers); + } else { + // Direct mode + const config = { + installType: options.full ? 'full' : 'single-agent', + agent: options.agent, + directory: options.directory || './bmad-core', + ide: options.ide + }; + await installer.install(config); + } + } catch (error) { + console.error(chalk.red('Installation failed:'), error.message); + process.exit(1); + } + }); + +program + .command('update') + .description('Update existing BMAD installation') + .option('--force', 'Force update, overwriting modified files') + .option('--dry-run', 'Show what would be updated without making changes') + .action(async (options) => { + try { + await installer.update(options); + } catch (error) { + console.error(chalk.red('Update failed:'), error.message); + process.exit(1); + } + }); + +program + .command('list') + .description('List available agents') + .action(async () => { + try { + await installer.listAgents(); + } catch (error) { + console.error(chalk.red('Error:'), error.message); + process.exit(1); + } + }); + +program + .command('status') + .description('Show installation status') + .action(async () => { + try { + await installer.showStatus(); + } catch (error) { + console.error(chalk.red('Error:'), error.message); + process.exit(1); + } + }); + +async function promptInstallation(options) { + console.log(chalk.bold.blue(`\nWelcome to BMAD Method Installer v${version}\n`)); + + const answers = {}; + + // Ask for installation directory + const { directory } = await inquirer.prompt([ + { + type: 'input', + name: 'directory', + message: 'Where would you like to install BMAD?', + default: './bmad-core' + } + ]); + answers.directory = directory; + + // Ask for installation type + const { installType } = await inquirer.prompt([ + { + type: 'list', + name: 'installType', + message: 'How would you like to install BMAD?', + choices: [ + { + name: 'Complete installation (recommended) - All agents and tools', + value: 'full' + }, + { + name: 'Single agent - Choose one agent to install', + value: 'single-agent' + } + ] + } + ]); + answers.installType = installType; + + // If single agent, ask which one + if (installType === 'single-agent') { + const agents = await installer.getAvailableAgents(); + const { agent } = await inquirer.prompt([ + { + type: 'list', + name: 'agent', + message: 'Select an agent to install:', + choices: agents.map(a => ({ + name: `${a.id} - ${a.name} (${a.description})`, + value: a.id + })) + } + ]); + answers.agent = agent; + } + + // Ask for IDE configuration + const { ide } = await inquirer.prompt([ + { + type: 'list', + name: 'ide', + message: 'Which IDE are you using?', + choices: [ + { name: 'Cursor', value: 'cursor' }, + { name: 'Claude Code', value: 'claude-code' }, + { name: 'Windsurf', value: 'windsurf' }, + { name: 'Other/Manual setup', value: null } + ] + } + ]); + answers.ide = ide; + + return answers; +} + +program.parse(process.argv); + +// Show help if no command provided +if (!process.argv.slice(2).length) { + program.outputHelp(); +} \ No newline at end of file diff --git a/tools/installer/config/install.config.yml b/tools/installer/config/install.config.yml new file mode 100644 index 00000000..6afb865b --- /dev/null +++ b/tools/installer/config/install.config.yml @@ -0,0 +1,167 @@ +# BMAD Method Installation Configuration +# Simplified installer with two options: full copy or single agent + +installation-options: + full: + name: "Complete BMAD Core" + description: "Copy the entire bmad-core folder with all agents, templates, and tools" + action: "copy-folder" + source: "bmad-core" + + single-agent: + name: "Single Agent" + description: "Select and install a single agent with its dependencies" + action: "copy-agent" + +# Agent dependency mappings +# These are the core files that should be included with any single agent installation +agent-dependencies: + core-files: + - "bmad-core/data/bmad-kb.md" + - "bmad-core/data/technical-preferences.md" + - "bmad-core/utils/template-format.md" + + # Agent-specific dependencies (parsed from agent files or explicitly defined) + dev: + - "bmad-core/templates/story-tmpl.md" + - "bmad-core/checklists/story-dod-checklist.md" + + pm: + - "bmad-core/templates/prd-tmpl.md" + - "bmad-core/checklists/pm-checklist.md" + - "bmad-core/tasks/advanced-elicitation.md" + + architect: + - "bmad-core/templates/architecture-tmpl.md" + - "bmad-core/checklists/architect-checklist.md" + + sm: + - "bmad-core/templates/story-tmpl.md" + - "bmad-core/checklists/story-draft-checklist.md" + - "bmad-core/workflows/*.yml" + + po: + - "bmad-core/checklists/po-master-checklist.md" + - "bmad-core/templates/acceptance-criteria-tmpl.md" + + analyst: + - "bmad-core/templates/prd-tmpl.md" + - "bmad-core/tasks/advanced-elicitation.md" + + qa: + - "bmad-core/checklists/story-dod-checklist.md" + - "bmad-core/templates/test-plan-tmpl.md" + + ux-expert: + - "bmad-core/templates/ux-tmpl.md" + + # Meta agents typically need access to more resources + bmad-master: + - "bmad-core/templates/*.md" + - "bmad-core/tasks/*.md" + - "bmad-core/schemas/*.yml" + + bmad-orchestrator: + - "bmad-core/agent-teams/*.yml" + - "bmad-core/workflows/*.yml" + +# IDE-specific configuration for generating rules/commands +ide-configurations: + cursor: + name: "Cursor" + rule-dir: ".cursor/rules/" + format: "multi-file" + command-suffix: ".mdc" + instructions: | + # To use BMAD agents in Cursor: + # 1. Press Ctrl+L (Cmd+L on Mac) to open the chat + # 2. Type @agent-name (e.g., "@dev", "@pm", "@architect") + # 3. The agent will adopt that persona for the conversation + + claude-code: + name: "Claude Code" + rule-dir: ".claude/commands/" + format: "multi-file" + command-suffix: ".md" + instructions: | + # To use BMAD agents in Claude Code: + # 1. Type /agent-name (e.g., "/dev", "/pm", "/architect") + # 2. Claude will switch to that agent's persona + + windsurf: + name: "Windsurf" + rule-dir: ".windsurf/rules/" + format: "multi-file" + command-suffix: ".md" + instructions: | + # To use BMAD agents in Windsurf: + # 1. Type @agent-name (e.g., "@dev", "@pm") + # 2. Windsurf will adopt that agent's persona + + roo: + name: "Roo" + # Configuration TBD - needs research + format: "unknown" + instructions: | + # Roo configuration coming soon + # Manual setup: Copy IDE agent files to your Roo configuration + + cline: + name: "Cline" + # Configuration TBD - needs research + format: "unknown" + instructions: | + # Cline configuration coming soon + # Manual setup: Copy IDE agent files to your Cline configuration + +# Available agents for single-agent installation +available-agents: + - id: "analyst" + name: "Business Analyst" + file: "bmad-core/agents/analyst.md" + description: "Requirements gathering and analysis" + + - id: "pm" + name: "Product Manager" + file: "bmad-core/agents/pm.md" + description: "Product strategy and roadmap planning" + + - id: "architect" + name: "Solution Architect" + file: "bmad-core/agents/architect.md" + description: "Technical design and architecture" + + - id: "po" + name: "Product Owner" + file: "bmad-core/agents/po.md" + description: "Backlog management and prioritization" + + - id: "sm" + name: "Scrum Master" + file: "bmad-core/agents/sm.md" + description: "Agile process and story creation" + + - id: "dev" + name: "Developer" + file: "bmad-core/agents/dev.md" + description: "Code implementation and testing" + + - id: "qa" + name: "QA Engineer" + file: "bmad-core/agents/qa.md" + description: "Quality assurance and testing" + + - id: "ux-expert" + name: "UX Expert" + file: "bmad-core/agents/ux-expert.md" + description: "User experience design" + + - id: "bmad-master" + name: "BMAD Master" + file: "bmad-core/agents/bmad-master.md" + description: "BMAD framework expert and guide" + + - id: "bmad-orchestrator" + name: "BMAD Orchestrator" + file: "bmad-core/agents/bmad-orchestrator.md" + description: "Multi-agent workflow coordinator" diff --git a/tools/installer/lib/config-loader.js b/tools/installer/lib/config-loader.js new file mode 100644 index 00000000..9c377719 --- /dev/null +++ b/tools/installer/lib/config-loader.js @@ -0,0 +1,62 @@ +const fs = require('fs-extra'); +const path = require('path'); +const yaml = require('js-yaml'); + +class ConfigLoader { + constructor() { + this.configPath = path.join(__dirname, '..', 'config', 'install.config.yml'); + this.config = null; + } + + async load() { + if (this.config) return this.config; + + try { + const configContent = await fs.readFile(this.configPath, 'utf8'); + this.config = yaml.load(configContent); + return this.config; + } catch (error) { + throw new Error(`Failed to load configuration: ${error.message}`); + } + } + + async getInstallationOptions() { + const config = await this.load(); + return config['installation-options'] || {}; + } + + async getAvailableAgents() { + const config = await this.load(); + return config['available-agents'] || []; + } + + async getAgentDependencies(agentId) { + const config = await this.load(); + const dependencies = config['agent-dependencies'] || {}; + + // Always include core files + const coreFiles = dependencies['core-files'] || []; + + // Add agent-specific dependencies + const agentDeps = dependencies[agentId] || []; + + return [...coreFiles, ...agentDeps]; + } + + async getIdeConfiguration(ide) { + const config = await this.load(); + const ideConfigs = config['ide-configurations'] || {}; + return ideConfigs[ide] || null; + } + + getBmadCorePath() { + // Get the path to bmad-core relative to the installer (now under tools) + return path.join(__dirname, '..', '..', '..', 'bmad-core'); + } + + getAgentPath(agentId) { + return path.join(this.getBmadCorePath(), 'agents', `${agentId}.md`); + } +} + +module.exports = new ConfigLoader(); \ No newline at end of file diff --git a/tools/installer/lib/file-manager.js b/tools/installer/lib/file-manager.js new file mode 100644 index 00000000..27cb08ed --- /dev/null +++ b/tools/installer/lib/file-manager.js @@ -0,0 +1,154 @@ +const fs = require('fs-extra'); +const path = require('path'); +const crypto = require('crypto'); +const glob = require('glob'); +const chalk = require('chalk'); + +class FileManager { + constructor() { + this.manifestDir = '.bmad'; + this.manifestFile = 'install-manifest.yml'; + } + + async copyFile(source, destination) { + try { + await fs.ensureDir(path.dirname(destination)); + await fs.copy(source, destination); + return true; + } catch (error) { + console.error(chalk.red(`Failed to copy ${source}:`), error.message); + return false; + } + } + + async copyDirectory(source, destination) { + try { + await fs.ensureDir(destination); + await fs.copy(source, destination); + return true; + } catch (error) { + console.error(chalk.red(`Failed to copy directory ${source}:`), error.message); + return false; + } + } + + async copyGlobPattern(pattern, sourceDir, destDir) { + const files = glob.sync(pattern, { cwd: sourceDir }); + const copied = []; + + for (const file of files) { + const sourcePath = path.join(sourceDir, file); + const destPath = path.join(destDir, file); + + if (await this.copyFile(sourcePath, destPath)) { + copied.push(file); + } + } + + return copied; + } + + async calculateFileHash(filePath) { + try { + const content = await fs.readFile(filePath); + return crypto.createHash('sha256').update(content).digest('hex').slice(0, 16); + } catch (error) { + return null; + } + } + + async createManifest(installDir, config, files) { + const manifestPath = path.join(installDir, this.manifestDir, this.manifestFile); + + const manifest = { + version: require('../package.json').version, + installed_at: new Date().toISOString(), + install_type: config.installType, + agent: config.agent || null, + ide_setup: config.ide || null, + files: [] + }; + + // Add file information + for (const file of files) { + const filePath = path.join(installDir, file); + const hash = await this.calculateFileHash(filePath); + + manifest.files.push({ + path: file, + hash: hash, + modified: false + }); + } + + // Write manifest + await fs.ensureDir(path.dirname(manifestPath)); + await fs.writeFile(manifestPath, JSON.stringify(manifest, null, 2)); + + return manifest; + } + + async readManifest(installDir) { + const manifestPath = path.join(installDir, this.manifestDir, this.manifestFile); + + try { + const content = await fs.readFile(manifestPath, 'utf8'); + return JSON.parse(content); + } catch (error) { + return null; + } + } + + async checkModifiedFiles(installDir, manifest) { + const modified = []; + + for (const file of manifest.files) { + const filePath = path.join(installDir, file.path); + const currentHash = await this.calculateFileHash(filePath); + + if (currentHash && currentHash !== file.hash) { + modified.push(file.path); + } + } + + return modified; + } + + async backupFile(filePath) { + const backupPath = filePath + '.bak'; + let counter = 1; + let finalBackupPath = backupPath; + + // Find a unique backup filename + while (await fs.pathExists(finalBackupPath)) { + finalBackupPath = `${filePath}.bak${counter}`; + counter++; + } + + await fs.copy(filePath, finalBackupPath); + return finalBackupPath; + } + + async ensureDirectory(dirPath) { + await fs.ensureDir(dirPath); + } + + async pathExists(filePath) { + return fs.pathExists(filePath); + } + + async readFile(filePath) { + return fs.readFile(filePath, 'utf8'); + } + + async writeFile(filePath, content) { + await fs.ensureDir(path.dirname(filePath)); + await fs.writeFile(filePath, content); + } + + async removeDirectory(dirPath) { + await fs.remove(dirPath); + } +} + +module.exports = new FileManager(); \ No newline at end of file diff --git a/tools/installer/lib/ide-setup.js b/tools/installer/lib/ide-setup.js new file mode 100644 index 00000000..03f2dd24 --- /dev/null +++ b/tools/installer/lib/ide-setup.js @@ -0,0 +1,189 @@ +const path = require('path'); +const fileManager = require('./file-manager'); +const configLoader = require('./config-loader'); +const chalk = require('chalk'); + +class IdeSetup { + async setup(ide, installDir, selectedAgent = null) { + const ideConfig = await configLoader.getIdeConfiguration(ide); + + if (!ideConfig) { + console.log(chalk.yellow(`\nNo configuration available for ${ide}`)); + return false; + } + + switch (ide) { + case 'cursor': + return this.setupCursor(installDir, selectedAgent); + case 'claude-code': + return this.setupClaudeCode(installDir, selectedAgent); + case 'windsurf': + return this.setupWindsurf(installDir, selectedAgent); + default: + console.log(chalk.yellow(`\nIDE ${ide} not yet supported`)); + return false; + } + } + + async setupCursor(installDir, selectedAgent) { + const cursorRulesDir = path.join(installDir, '.cursor', 'rules'); + const agents = selectedAgent ? [selectedAgent] : await this.getAllAgentIds(installDir); + + await fileManager.ensureDirectory(cursorRulesDir); + + for (const agentId of agents) { + // Check if bmad-core is a subdirectory (full install) or if agents are in root (single agent install) + let agentPath = path.join(installDir, 'bmad-core', 'agents', `${agentId}.md`); + if (!await fileManager.pathExists(agentPath)) { + agentPath = path.join(installDir, 'agents', `${agentId}.md`); + } + + if (await fileManager.pathExists(agentPath)) { + const agentContent = await fileManager.readFile(agentPath); + const mdcPath = path.join(cursorRulesDir, `${agentId}.mdc`); + + // Create MDC content with proper format + let mdcContent = '\n---\n'; + mdcContent += 'description: \n'; + mdcContent += 'globs: []\n'; + mdcContent += 'alwaysApply: false\n'; + mdcContent += '---\n\n'; + mdcContent += `# ${agentId.toUpperCase()} Agent Rule\n\n`; + mdcContent += `This rule is triggered when the user types \`@${agentId}\` and activates the ${this.getAgentTitle(agentId)} agent persona.\n\n`; + mdcContent += '## Agent Activation\n\n'; + mdcContent += 'CRITICAL: Read the full YML, start activation to alter your state of being, follow startup section instructions, stay in this being until told to exit this mode:\n\n'; + mdcContent += '```yml\n'; + // Extract just the YAML content from the agent file + const yamlMatch = agentContent.match(/```ya?ml\n([\s\S]*?)```/); + if (yamlMatch) { + mdcContent += yamlMatch[1].trim(); + } else { + // If no YAML found, include the whole content minus the header + mdcContent += agentContent.replace(/^#.*$/m, '').trim(); + } + mdcContent += '\n```\n\n'; + mdcContent += '## File Reference\n\n'; + mdcContent += `The complete agent definition is available in [bmad-core/agents/${agentId}.md](mdc:bmad-core/agents/${agentId}.md).\n\n`; + mdcContent += '## Usage\n\n'; + mdcContent += `When the user types \`@${agentId}\`, activate this ${this.getAgentTitle(agentId)} persona and follow all instructions defined in the YML configuration above.\n`; + + await fileManager.writeFile(mdcPath, mdcContent); + console.log(chalk.green(`✓ Created rule: ${agentId}.mdc`)); + } + } + + console.log(chalk.green(`\n✓ Created Cursor rules in ${cursorRulesDir}`)); + + return true; + } + + async setupClaudeCode(installDir, selectedAgent) { + const commandsDir = path.join(installDir, '.claude', 'commands'); + const agents = selectedAgent ? [selectedAgent] : await this.getAllAgentIds(installDir); + + await fileManager.ensureDirectory(commandsDir); + + for (const agentId of agents) { + // Check if bmad-core is a subdirectory (full install) or if agents are in root (single agent install) + let agentPath = path.join(installDir, 'bmad-core', 'agents', `${agentId}.md`); + if (!await fileManager.pathExists(agentPath)) { + agentPath = path.join(installDir, 'agents', `${agentId}.md`); + } + const commandPath = path.join(commandsDir, `${agentId}.md`); + + if (await fileManager.pathExists(agentPath)) { + // Create command file with agent content + const agentContent = await fileManager.readFile(agentPath); + + // Add command header + let commandContent = `# /${agentId} Command\n\n`; + commandContent += `When this command is used, adopt the following agent persona:\n\n`; + commandContent += agentContent; + + await fileManager.writeFile(commandPath, commandContent); + console.log(chalk.green(`✓ Created command: /${agentId}`)); + } + } + + console.log(chalk.green(`\n✓ Created Claude Code commands in ${commandsDir}`)); + + return true; + } + + async setupWindsurf(installDir, selectedAgent) { + const windsurfRulesDir = path.join(installDir, '.windsurf', 'rules'); + const agents = selectedAgent ? [selectedAgent] : await this.getAllAgentIds(installDir); + + await fileManager.ensureDirectory(windsurfRulesDir); + + for (const agentId of agents) { + // Check if bmad-core is a subdirectory (full install) or if agents are in root (single agent install) + let agentPath = path.join(installDir, 'bmad-core', 'agents', `${agentId}.md`); + if (!await fileManager.pathExists(agentPath)) { + agentPath = path.join(installDir, 'agents', `${agentId}.md`); + } + + if (await fileManager.pathExists(agentPath)) { + const agentContent = await fileManager.readFile(agentPath); + const mdPath = path.join(windsurfRulesDir, `${agentId}.md`); + + // Create MD content (similar to Cursor but without frontmatter) + let mdContent = `# ${agentId.toUpperCase()} Agent Rule\n\n`; + mdContent += `This rule is triggered when the user types \`@${agentId}\` and activates the ${this.getAgentTitle(agentId)} agent persona.\n\n`; + mdContent += '## Agent Activation\n\n'; + mdContent += 'CRITICAL: Read the full YML, start activation to alter your state of being, follow startup section instructions, stay in this being until told to exit this mode:\n\n'; + mdContent += '```yml\n'; + // Extract just the YAML content from the agent file + const yamlMatch = agentContent.match(/```ya?ml\n([\s\S]*?)```/); + if (yamlMatch) { + mdContent += yamlMatch[1].trim(); + } else { + // If no YAML found, include the whole content minus the header + mdContent += agentContent.replace(/^#.*$/m, '').trim(); + } + mdContent += '\n```\n\n'; + mdContent += '## File Reference\n\n'; + mdContent += `The complete agent definition is available in [bmad-core/agents/${agentId}.md](bmad-core/agents/${agentId}.md).\n\n`; + mdContent += '## Usage\n\n'; + mdContent += `When the user types \`@${agentId}\`, activate this ${this.getAgentTitle(agentId)} persona and follow all instructions defined in the YML configuration above.\n`; + + await fileManager.writeFile(mdPath, mdContent); + console.log(chalk.green(`✓ Created rule: ${agentId}.md`)); + } + } + + console.log(chalk.green(`\n✓ Created Windsurf rules in ${windsurfRulesDir}`)); + + return true; + } + + async getAllAgentIds(installDir) { + // Check if bmad-core is a subdirectory (full install) or if agents are in root (single agent install) + let agentsDir = path.join(installDir, 'bmad-core', 'agents'); + if (!await fileManager.pathExists(agentsDir)) { + agentsDir = path.join(installDir, 'agents'); + } + + const glob = require('glob'); + const agentFiles = glob.sync('*.md', { cwd: agentsDir }); + return agentFiles.map(file => path.basename(file, '.md')); + } + + getAgentTitle(agentId) { + const agentTitles = { + 'analyst': 'Business Analyst', + 'architect': 'Solution Architect', + 'bmad-master': 'BMAD Master', + 'bmad-orchestrator': 'BMAD Orchestrator', + 'dev': 'Developer', + 'pm': 'Product Manager', + 'po': 'Product Owner', + 'qa': 'QA Specialist', + 'sm': 'Scrum Master', + 'ux-expert': 'UX Expert' + }; + return agentTitles[agentId] || agentId; + } +} + +module.exports = new IdeSetup(); \ No newline at end of file diff --git a/tools/installer/lib/installer.js b/tools/installer/lib/installer.js new file mode 100644 index 00000000..6d3455a8 --- /dev/null +++ b/tools/installer/lib/installer.js @@ -0,0 +1,269 @@ +const chalk = require('chalk'); +const ora = require('ora'); +const path = require('path'); +const configLoader = require('./config-loader'); +const fileManager = require('./file-manager'); +const ideSetup = require('./ide-setup'); + +class Installer { + async install(config) { + const spinner = ora('Installing BMAD Method...').start(); + + try { + // Resolve installation directory + const installDir = path.resolve(config.directory); + + // Check if directory already exists + if (await fileManager.pathExists(installDir)) { + const manifest = await fileManager.readManifest(installDir); + if (manifest) { + spinner.fail('BMAD is already installed in this directory'); + console.log(chalk.yellow('\nUse "bmad update" to update the existing installation')); + return; + } + } + + let files = []; + + if (config.installType === 'full') { + // Full installation - copy entire bmad-core folder as a subdirectory + spinner.text = 'Copying complete bmad-core folder...'; + const sourceDir = configLoader.getBmadCorePath(); + const bmadCoreDestDir = path.join(installDir, 'bmad-core'); + await fileManager.copyDirectory(sourceDir, bmadCoreDestDir); + + // Get list of all files for manifest + const glob = require('glob'); + files = glob.sync('**/*', { + cwd: bmadCoreDestDir, + nodir: true, + ignore: ['**/.git/**', '**/node_modules/**'] + }).map(file => path.join('bmad-core', file)); + + } else if (config.installType === 'single-agent') { + // Single agent installation + spinner.text = `Installing ${config.agent} agent...`; + + // Copy agent file + const agentPath = configLoader.getAgentPath(config.agent); + const destAgentPath = path.join(installDir, 'agents', `${config.agent}.md`); + await fileManager.copyFile(agentPath, destAgentPath); + files.push(`agents/${config.agent}.md`); + + // Copy dependencies + const dependencies = await configLoader.getAgentDependencies(config.agent); + const sourceBase = configLoader.getBmadCorePath(); + + for (const dep of dependencies) { + spinner.text = `Copying dependency: ${dep}`; + + if (dep.includes('*')) { + // Handle glob patterns + const copiedFiles = await fileManager.copyGlobPattern( + dep.replace('bmad-core/', ''), + sourceBase, + installDir + ); + files.push(...copiedFiles); + } else { + // Handle single files + const sourcePath = path.join(sourceBase, dep.replace('bmad-core/', '')); + const destPath = path.join(installDir, dep.replace('bmad-core/', '')); + + if (await fileManager.copyFile(sourcePath, destPath)) { + files.push(dep.replace('bmad-core/', '')); + } + } + } + } + + // Set up IDE integration if requested + if (config.ide) { + spinner.text = `Setting up ${config.ide} integration...`; + // For full installations, IDE rules should be in the root install dir, not bmad-core + await ideSetup.setup(config.ide, installDir, config.agent); + } + + // Create manifest + spinner.text = 'Creating installation manifest...'; + await fileManager.createManifest(installDir, config, files); + + spinner.succeed('Installation complete!'); + + // Show success message + console.log(chalk.green('\n✓ BMAD Method installed successfully!\n')); + + if (config.ide) { + const ideConfig = await configLoader.getIdeConfiguration(config.ide); + if (ideConfig && ideConfig.instructions) { + console.log(chalk.bold('To use BMAD agents in ' + ideConfig.name + ':')); + console.log(ideConfig.instructions); + } + } else { + console.log(chalk.yellow('No IDE configuration was set up.')); + console.log('You can manually configure your IDE using the agent files in:', installDir); + } + + if (config.installType === 'single-agent') { + console.log(chalk.dim('\nNeed other agents? Run: npx bmad-method install --agent=')); + console.log(chalk.dim('Need everything? Run: npx bmad-method install --full')); + } + + } catch (error) { + spinner.fail('Installation failed'); + throw error; + } + } + + async update(options) { + const spinner = ora('Checking for updates...').start(); + + try { + // Find existing installation + const installDir = await this.findInstallation(); + if (!installDir) { + spinner.fail('No BMAD installation found'); + return; + } + + const manifest = await fileManager.readManifest(installDir); + if (!manifest) { + spinner.fail('Invalid installation - manifest not found'); + return; + } + + // Check for modified files + spinner.text = 'Checking for modified files...'; + const modifiedFiles = await fileManager.checkModifiedFiles(installDir, manifest); + + if (modifiedFiles.length > 0 && !options.force) { + spinner.warn('Found modified files'); + console.log(chalk.yellow('\nThe following files have been modified:')); + modifiedFiles.forEach(file => console.log(` - ${file}`)); + + if (!options.dryRun) { + console.log(chalk.yellow('\nUse --force to overwrite modified files')); + console.log(chalk.yellow('or manually backup your changes first')); + } + return; + } + + if (options.dryRun) { + spinner.info('Dry run - no changes will be made'); + console.log('\nFiles that would be updated:'); + manifest.files.forEach(file => console.log(` - ${file.path}`)); + return; + } + + // Perform update + spinner.text = 'Updating files...'; + + // Backup modified files if forcing + if (modifiedFiles.length > 0 && options.force) { + for (const file of modifiedFiles) { + const filePath = path.join(installDir, file); + const backupPath = await fileManager.backupFile(filePath); + console.log(chalk.dim(` Backed up: ${file} → ${path.basename(backupPath)}`)); + } + } + + // Re-run installation with same config + const config = { + installType: manifest.install_type, + agent: manifest.agent, + directory: installDir, + ide: manifest.ide_setup + }; + + await this.install(config); + + } catch (error) { + spinner.fail('Update failed'); + throw error; + } + } + + async listAgents() { + const agents = await configLoader.getAvailableAgents(); + + console.log(chalk.bold('\nAvailable BMAD Agents:\n')); + + agents.forEach(agent => { + console.log(chalk.cyan(` ${agent.id.padEnd(20)}`), agent.description); + }); + + console.log(chalk.dim('\nInstall with: npx bmad-method install --agent=\n')); + } + + async showStatus() { + const installDir = await this.findInstallation(); + + if (!installDir) { + console.log(chalk.yellow('No BMAD installation found in current directory tree')); + return; + } + + const manifest = await fileManager.readManifest(installDir); + + if (!manifest) { + console.log(chalk.red('Invalid installation - manifest not found')); + return; + } + + console.log(chalk.bold('\nBMAD Installation Status:\n')); + console.log(` Directory: ${installDir}`); + console.log(` Version: ${manifest.version}`); + console.log(` Installed: ${new Date(manifest.installed_at).toLocaleDateString()}`); + console.log(` Type: ${manifest.install_type}`); + + if (manifest.agent) { + console.log(` Agent: ${manifest.agent}`); + } + + if (manifest.ide_setup) { + console.log(` IDE Setup: ${manifest.ide_setup}`); + } + + console.log(` Total Files: ${manifest.files.length}`); + + // Check for modifications + const modifiedFiles = await fileManager.checkModifiedFiles(installDir, manifest); + if (modifiedFiles.length > 0) { + console.log(chalk.yellow(` Modified Files: ${modifiedFiles.length}`)); + } + + console.log(''); + } + + async getAvailableAgents() { + return configLoader.getAvailableAgents(); + } + + async findInstallation() { + // Look for bmad-core in current directory or parent directories + let currentDir = process.cwd(); + + while (currentDir !== path.dirname(currentDir)) { + const bmadDir = path.join(currentDir, 'bmad-core'); + const manifestPath = path.join(bmadDir, '.bmad', 'install-manifest.yml'); + + if (await fileManager.pathExists(manifestPath)) { + return bmadDir; + } + + currentDir = path.dirname(currentDir); + } + + // Also check if we're inside a bmad-core directory + if (path.basename(process.cwd()) === 'bmad-core') { + const manifestPath = path.join(process.cwd(), '.bmad', 'install-manifest.yml'); + if (await fileManager.pathExists(manifestPath)) { + return process.cwd(); + } + } + + return null; + } +} + +module.exports = new Installer(); \ No newline at end of file diff --git a/tools/installer/package-lock.json b/tools/installer/package-lock.json new file mode 100644 index 00000000..5052a38f --- /dev/null +++ b/tools/installer/package-lock.json @@ -0,0 +1,704 @@ +{ + "name": "bmad-method", + "version": "4.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "bmad-method", + "version": "4.0.0", + "license": "MIT", + "dependencies": { + "chalk": "^4.1.2", + "commander": "^9.4.1", + "fs-extra": "^11.1.0", + "inquirer": "^8.2.5", + "js-yaml": "^4.1.0", + "ora": "^5.4.1" + }, + "bin": { + "bmad": "bin/bmad.js", + "bmad-method": "bin/bmad.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "license": "MIT" + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "license": "MIT", + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-width": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "license": "ISC", + "engines": { + "node": ">= 10" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "license": "MIT", + "engines": { + "node": "^12.20.0 || >=14" + } + }, + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "license": "MIT", + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "license": "MIT", + "dependencies": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/fs-extra": { + "version": "11.3.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz", + "integrity": "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/inquirer": { + "version": "8.2.6", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.6.tgz", + "integrity": "sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==", + "license": "MIT", + "dependencies": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.1", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "ora": "^5.4.1", + "run-async": "^2.4.0", + "rxjs": "^7.5.5", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6", + "wrap-ansi": "^6.0.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "license": "ISC" + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "license": "MIT", + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "license": "MIT", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "license": "ISC" + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "license": "MIT" + }, + "node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "license": "MIT", + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "license": "MIT", + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + } + } +} diff --git a/tools/installer/package.json b/tools/installer/package.json new file mode 100644 index 00000000..e7211f1d --- /dev/null +++ b/tools/installer/package.json @@ -0,0 +1,43 @@ +{ + "name": "bmad-method", + "version": "4.0.0", + "description": "BMAD Method installer - AI-powered Agile development framework", + "main": "lib/installer.js", + "bin": { + "bmad": "./bin/bmad.js", + "bmad-method": "./bin/bmad.js" + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [ + "bmad", + "agile", + "ai", + "development", + "framework", + "installer", + "agents" + ], + "author": "BMAD Team", + "license": "MIT", + "dependencies": { + "chalk": "^4.1.2", + "commander": "^9.4.1", + "fs-extra": "^11.1.0", + "inquirer": "^8.2.5", + "js-yaml": "^4.1.0", + "ora": "^5.4.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "repository": { + "type": "git", + "url": "https://github.com/bmad-team/bmad-method.git" + }, + "bugs": { + "url": "https://github.com/bmad-team/bmad-method/issues" + }, + "homepage": "https://github.com/bmad-team/bmad-method#readme" +} \ No newline at end of file diff --git a/installer/templates/claude-commands.md b/tools/installer/templates/claude-commands.md similarity index 100% rename from installer/templates/claude-commands.md rename to tools/installer/templates/claude-commands.md diff --git a/installer/templates/cursor-rules.md b/tools/installer/templates/cursor-rules.md similarity index 100% rename from installer/templates/cursor-rules.md rename to tools/installer/templates/cursor-rules.md diff --git a/installer/templates/windsurf-rules.md b/tools/installer/templates/windsurf-rules.md similarity index 100% rename from installer/templates/windsurf-rules.md rename to tools/installer/templates/windsurf-rules.md