Compare commits
22 Commits
cf8a9b7db0
...
6496b6e002
| Author | SHA1 | Date |
|---|---|---|
|
|
6496b6e002 | |
|
|
1782e97731 | |
|
|
9247146397 | |
|
|
c8481c21c3 | |
|
|
f0ad64efc4 | |
|
|
decf15b5da | |
|
|
64e5a9c696 | |
|
|
b318d9242e | |
|
|
6db629278a | |
|
|
94666bd05b | |
|
|
dfd961944c | |
|
|
d0ef58b421 | |
|
|
4bd43ec8b9 | |
|
|
7f81518896 | |
|
|
43672d33c1 | |
|
|
26dd80e021 | |
|
|
746bd50d19 | |
|
|
fcf781fa39 | |
|
|
44f07bf341 | |
|
|
79855b2c4c | |
|
|
62e0b0ba52 | |
|
|
18a4a489c8 |
|
|
@ -36,6 +36,7 @@ cursor
|
|||
CLAUDE.local.md
|
||||
.serena/
|
||||
.claude/settings.local.json
|
||||
.agents
|
||||
|
||||
z*/
|
||||
|
||||
|
|
|
|||
|
|
@ -41,14 +41,14 @@ _bmad/_config/agents/
|
|||
|
||||
Open the `.customize.yaml` file for the agent you want to modify. Every section is optional -- customize only what you need.
|
||||
|
||||
| Section | Behavior | Purpose |
|
||||
| ------------------- | ------------ | ---------------------------------------------- |
|
||||
| `agent.metadata` | Replaces | Override the agent's display name |
|
||||
| `persona` | Replaces | Set role, identity, style, and principles |
|
||||
| `memories` | Appends | Add persistent context the agent always recalls |
|
||||
| `menu` | Appends | Add custom menu items for workflows or prompts |
|
||||
| `critical_actions` | Appends | Define startup instructions for the agent |
|
||||
| `prompts` | Appends | Create reusable prompts for menu actions |
|
||||
| Section | Behavior | Purpose |
|
||||
| ------------------ | -------- | ----------------------------------------------- |
|
||||
| `agent.metadata` | Replaces | Override the agent's display name |
|
||||
| `persona` | Replaces | Set role, identity, style, and principles |
|
||||
| `memories` | Appends | Add persistent context the agent always recalls |
|
||||
| `menu` | Appends | Add custom menu items for workflows or prompts |
|
||||
| `critical_actions` | Appends | Define startup instructions for the agent |
|
||||
| `prompts` | Appends | Create reusable prompts for menu actions |
|
||||
|
||||
Sections marked **Replaces** overwrite the agent's defaults entirely. Sections marked **Appends** add to the existing configuration.
|
||||
|
||||
|
|
@ -96,7 +96,7 @@ Add custom entries to the agent's display menu. Each item needs a `trigger`, a t
|
|||
```yaml
|
||||
menu:
|
||||
- trigger: my-workflow
|
||||
workflow: '{project-root}/my-custom/workflows/my-workflow.yaml'
|
||||
workflow: 'my-custom/workflows/my-workflow.yaml'
|
||||
description: My custom workflow
|
||||
- trigger: deploy
|
||||
action: '#deploy-prompt'
|
||||
|
|
@ -136,11 +136,11 @@ npx bmad-method install
|
|||
|
||||
The installer detects the existing installation and offers these options:
|
||||
|
||||
| Option | What It Does |
|
||||
| --------------------- | ------------------------------------------------------------------- |
|
||||
| **Quick Update** | Updates all modules to the latest version and recompiles all agents |
|
||||
| **Recompile Agents** | Applies customizations only, without updating module files |
|
||||
| **Modify BMad Installation** | Full installation flow for adding or removing modules |
|
||||
| Option | What It Does |
|
||||
| ---------------------------- | ------------------------------------------------------------------- |
|
||||
| **Quick Update** | Updates all modules to the latest version and recompiles all agents |
|
||||
| **Recompile Agents** | Applies customizations only, without updating module files |
|
||||
| **Modify BMad Installation** | Full installation flow for adding or removing modules |
|
||||
|
||||
For customization-only changes, **Recompile Agents** is the fastest option.
|
||||
|
||||
|
|
|
|||
|
|
@ -59,9 +59,21 @@ For complex projects, consider using the `document-project` workflow. It offers
|
|||
|
||||
## Step 3: Get Help
|
||||
|
||||
Get help to know what to do next based on your unique needs
|
||||
### BMad-Help: Your Starting Point
|
||||
|
||||
Run `bmad-help` to get guidance when you are not sure what to do next.
|
||||
**Run `/bmad-help` anytime you're unsure what to do next.** This intelligent guide:
|
||||
|
||||
- Inspects your project to see what's already been done
|
||||
- Shows options based on your installed modules
|
||||
- Understands natural language queries
|
||||
|
||||
```
|
||||
/bmad-help I have an existing Rails app, where should I start?
|
||||
/bmad-help What's the difference between quick-flow and full method?
|
||||
/bmad-help Show me what workflows are available
|
||||
```
|
||||
|
||||
BMad-Help also **automatically runs at the end of every workflow**, providing clear guidance on exactly what to do next.
|
||||
|
||||
### Choosing Your Approach
|
||||
|
||||
|
|
|
|||
|
|
@ -5,17 +5,48 @@ sidebar:
|
|||
order: 4
|
||||
---
|
||||
|
||||
If you have successfully installed BMad and the BMad Method (+ other modules as needed) - the first step in getting answers is `/bmad-help`. This will answer upwards of 80% of all questions and is available to you in the IDE as you are working.
|
||||
## Start Here: BMad-Help
|
||||
|
||||
## When to Use This
|
||||
**The fastest way to get answers about BMad is `/bmad-help`.** This intelligent guide will answer upwards of 80% of all questions and is available to you directly in your IDE as you work.
|
||||
|
||||
- You have a question about how BMad works or what to do next with BMad
|
||||
- You want to understand a specific agent or workflow
|
||||
- You need quick answers without waiting for Discord
|
||||
BMad-Help is more than a lookup tool — it:
|
||||
- **Inspects your project** to see what's already been completed
|
||||
- **Understands natural language** — ask questions in plain English
|
||||
- **Varies based on your installed modules** — shows relevant options
|
||||
- **Auto-runs after workflows** — tells you exactly what to do next
|
||||
- **Recommends the first required task** — no guessing where to start
|
||||
|
||||
:::note[Prerequisites]
|
||||
An AI tool (Claude Code, Cursor, ChatGPT, Claude.ai, etc.) and either BMad installed in your project or access to the GitHub repo.
|
||||
:::
|
||||
### How to Use BMad-Help
|
||||
|
||||
Run it with just the slash command:
|
||||
|
||||
```
|
||||
/bmad-help
|
||||
```
|
||||
|
||||
Or combine it with a natural language query:
|
||||
|
||||
```
|
||||
/bmad-help I have a SaaS idea and know all the features. Where do I start?
|
||||
/bmad-help What are my options for UX design?
|
||||
/bmad-help I'm stuck on the PRD workflow
|
||||
/bmad-help Show me what's been done so far
|
||||
```
|
||||
|
||||
BMad-Help responds with:
|
||||
- What's recommended for your situation
|
||||
- What the first required task is
|
||||
- What the rest of the process looks like
|
||||
|
||||
---
|
||||
|
||||
## When to Use This Guide
|
||||
|
||||
Use this section when:
|
||||
- You want to understand BMad's architecture or internals
|
||||
- You need answers outside of what BMad-Help provides
|
||||
- You're researching BMad before installing
|
||||
- You want to explore the source code directly
|
||||
|
||||
## Steps
|
||||
|
||||
|
|
|
|||
|
|
@ -79,7 +79,18 @@ your-project/
|
|||
|
||||
## Verify Installation
|
||||
|
||||
Run the `help` workflow (`/bmad-help` on most platforms) to verify everything works and see what to do next.
|
||||
Run `/bmad-help` to verify everything works and see what to do next.
|
||||
|
||||
**BMad-Help is your intelligent guide** that will:
|
||||
- Confirm your installation is working
|
||||
- Show what's available based on your installed modules
|
||||
- Recommend your first step
|
||||
|
||||
You can also ask it questions:
|
||||
```
|
||||
/bmad-help I just installed, what should I do first?
|
||||
/bmad-help What are my options for a SaaS project?
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,11 @@ If you're comfortable working with AI coding assistants like Claude, Cursor, or
|
|||
The fastest way to understand BMad is to try it.
|
||||
|
||||
- **[Get Started with BMad](./tutorials/getting-started.md)** — Install and understand how BMad works
|
||||
- **[Workflow Map](./reference/workflow-map.md)** — Visual overview of BMM phases, workflows, and context management.
|
||||
- **[Workflow Map](./reference/workflow-map.md)** — Visual overview of BMM phases, workflows, and context management
|
||||
|
||||
:::tip[Just Want to Dive In?]
|
||||
Install BMad and run `/bmad-help` — it will guide you through everything based on your project and installed modules.
|
||||
:::
|
||||
|
||||
## How to Use These Docs
|
||||
|
||||
|
|
|
|||
|
|
@ -103,9 +103,29 @@ See [Workflow Map](./workflow-map.md) for the complete workflow reference organi
|
|||
|
||||
Tasks and tools are standalone operations that do not require an agent or workflow context.
|
||||
|
||||
#### BMad-Help: Your Intelligent Guide
|
||||
|
||||
**`/bmad-help`** is your primary interface for discovering what to do next. It's not just a lookup tool — it's an intelligent assistant that:
|
||||
|
||||
- **Inspects your project** to see what's already been done
|
||||
- **Understands natural language queries** — ask questions in plain English
|
||||
- **Varies by installed modules** — shows options based on what you have
|
||||
- **Auto-invokes after workflows** — every workflow ends with clear next steps
|
||||
- **Recommends the first required task** — no guessing where to start
|
||||
|
||||
**Examples:**
|
||||
|
||||
```
|
||||
/bmad-help
|
||||
/bmad-help I have a SaaS idea and know all the features. Where do I start?
|
||||
/bmad-help What are my options for UX design?
|
||||
/bmad-help I'm stuck on the PRD workflow
|
||||
```
|
||||
|
||||
#### Other Tasks and Tools
|
||||
|
||||
| Example command | Purpose |
|
||||
| --- | --- |
|
||||
| `/bmad-help` | Context-aware guidance and next-step recommendations |
|
||||
| `/bmad-shard-doc` | Split a large markdown file into smaller sections |
|
||||
| `/bmad-index-docs` | Index project documentation |
|
||||
| `/bmad-editorial-review-prose` | Review document prose quality |
|
||||
|
|
|
|||
|
|
@ -23,55 +23,53 @@ Final important note: Every workflow below can be run directly with your tool of
|
|||
|
||||
Explore the problem space and validate ideas before committing to planning.
|
||||
|
||||
| Workflow | Purpose | Produces |
|
||||
| ---------------------- | -------------------------------------------------------------------------- | ------------------------- |
|
||||
| `brainstorming` | Brainstorm Project Ideas with guided facilitation of a brainstorming coach | `brainstorming-report.md` |
|
||||
| `research` | Validate market, technical, or domain assumptions | Research findings |
|
||||
| `create-product-brief` | Capture strategic vision | `product-brief.md` |
|
||||
| Workflow | Purpose | Produces |
|
||||
| ------------------------------- | -------------------------------------------------------------------------- | ------------------------- |
|
||||
| `bmad-brainstorming` | Brainstorm Project Ideas with guided facilitation of a brainstorming coach | `brainstorming-report.md` |
|
||||
| `bmad-bmm-research` | Validate market, technical, or domain assumptions | Research findings |
|
||||
| `bmad-bmm-create-product-brief` | Capture strategic vision | `product-brief.md` |
|
||||
|
||||
## Phase 2: Planning
|
||||
|
||||
Define what to build and for whom.
|
||||
|
||||
| Workflow | Purpose | Produces |
|
||||
| ------------------ | ---------------------------------------- | ------------ |
|
||||
| `create-prd` | Define requirements (FRs/NFRs) | `PRD.md` |
|
||||
| `create-ux-design` | Design user experience (when UX matters) | `ux-spec.md` |
|
||||
| Workflow | Purpose | Produces |
|
||||
| --------------------------- | ---------------------------------------- | ------------ |
|
||||
| `bmad-bmm-create-prd` | Define requirements (FRs/NFRs) | `PRD.md` |
|
||||
| `bmad-bmm-create-ux-design` | Design user experience (when UX matters) | `ux-spec.md` |
|
||||
|
||||
## Phase 3: Solutioning
|
||||
|
||||
Decide how to build it and break work into stories.
|
||||
|
||||
| Workflow | Purpose | Produces |
|
||||
| -------------------------------- | ------------------------------------------ | --------------------------- |
|
||||
| `create-architecture` | Make technical decisions explicit | `architecture.md` with ADRs |
|
||||
| `create-epics-and-stories` | Break requirements into implementable work | Epic files with stories |
|
||||
| `check-implementation-readiness` | Gate check before implementation | PASS/CONCERNS/FAIL decision |
|
||||
| Workflow | Purpose | Produces |
|
||||
| ----------------------------------------- | ------------------------------------------ | --------------------------- |
|
||||
| `bmad-bmm-create-architecture` | Make technical decisions explicit | `architecture.md` with ADRs |
|
||||
| `bmad-bmm-create-epics-and-stories` | Break requirements into implementable work | Epic files with stories |
|
||||
| `bmad-bmm-check-implementation-readiness` | Gate check before implementation | PASS/CONCERNS/FAIL decision |
|
||||
|
||||
## Phase 4: Implementation
|
||||
|
||||
Build it, one story at a time.
|
||||
Build it, one story at a time. Coming soon, full phase 4 automation!
|
||||
|
||||
| Workflow | Purpose | Produces |
|
||||
| ----------------- | -------------------------------------- | ----------------------------- |
|
||||
| `sprint-planning` | Initialize tracking (once per project) | `sprint-status.yaml` |
|
||||
| `create-story` | Prepare next story for implementation | `story-[slug].md` |
|
||||
| `dev-story` | Implement the story | Working code + tests |
|
||||
| `automate` (QA) | Generate tests for existing features | Test suite |
|
||||
| `code-review` | Validate implementation quality | Approved or changes requested |
|
||||
| `correct-course` | Handle significant mid-sprint changes | Updated plan or re-routing |
|
||||
| `retrospective` | Review after epic completion | Lessons learned |
|
||||
|
||||
**Quinn (QA Agent):** Built-in QA agent for test automation. Trigger with `QA` or `bmad-bmm-qa-automate`. Generates standard API and E2E tests using your project's test framework. Beginner-friendly, no configuration needed. For advanced test strategy, install [Test Architect (TEA)](https://bmad-code-org.github.io/bmad-method-test-architecture-enterprise/) module.
|
||||
| Workflow | Purpose | Produces |
|
||||
| -------------------------- | ------------------------------------------------------------------------ | -------------------------------- |
|
||||
| `bmad-bmm-sprint-planning` | Initialize tracking (once per project to sequence the dev cycle) | `sprint-status.yaml` |
|
||||
| `bmad-bmm-create-story` | Prepare next story for implementation | `story-[slug].md` |
|
||||
| `bmad-bmm-dev-story` | Implement the story | Working code + tests |
|
||||
| `bmad-bmm-code-review` | Validate implementation quality | Approved or changes requested |
|
||||
| `bmad-bmm-correct-course` | Handle significant mid-sprint changes | Updated plan or re-routing |
|
||||
| `bmad-bmm-automate` | Generate tests for existing features - Use after a full epic is complete | End to End UI Focused Test suite |
|
||||
| `bmad-bmm-retrospective` | Review after epic completion | Lessons learned |
|
||||
|
||||
## Quick Flow (Parallel Track)
|
||||
|
||||
Skip phases 1-3 for small, well-understood work.
|
||||
|
||||
| Workflow | Purpose | Produces |
|
||||
| ------------ | ------------------------------------------ | --------------------------------------------- |
|
||||
| `quick-spec` | Define an ad-hoc change | `tech-spec.md` (story file for small changes) |
|
||||
| `quick-dev` | Implement from spec or direct instructions | Working code + tests |
|
||||
| Workflow | Purpose | Produces |
|
||||
| --------------------- | ------------------------------------------ | --------------------------------------------- |
|
||||
| `bmad-bmm-quick-spec` | Define an ad-hoc change | `tech-spec.md` (story file for small changes) |
|
||||
| `bmad-bmm-quick-dev` | Implement from spec or direct instructions | Working code + tests |
|
||||
|
||||
## Context Management
|
||||
|
||||
|
|
@ -80,43 +78,12 @@ Each document becomes context for the next phase. The PRD tells the architect wh
|
|||
### Project Context
|
||||
|
||||
:::tip[Recommended]
|
||||
Create `project-context.md` to ensure AI agents follow your project's rules and preferences. This file works like a constitution for your project — it guides implementation decisions across all workflows.
|
||||
Create `project-context.md` to ensure AI agents follow your project's rules and preferences. This file works like a constitution for your project — it guides implementation decisions across all workflows. This optional file can be generated at the end of Architecture Creation, or in an existing project it can be generated also to capture whats important to keep aligned with current conventions.
|
||||
:::
|
||||
|
||||
**When to create it:**
|
||||
|
||||
| Scenario | Approach |
|
||||
|----------|----------|
|
||||
| Before architecture (manual) | Document technical preferences you want the architect to respect |
|
||||
| After architecture | Generate it to capture decisions made during solutioning |
|
||||
| Existing projects | Run `generate-project-context` to discover established patterns |
|
||||
| Quick Flow | Create before `quick-dev` to ensure consistent implementation |
|
||||
|
||||
**How to create it:**
|
||||
|
||||
- **Manually** — Create `_bmad-output/project-context.md` with your technology stack and implementation rules
|
||||
- **Generate it** — Run `/bmad-bmm-generate-project-context` to auto-generate from your architecture or codebase
|
||||
|
||||
**What workflows load it:**
|
||||
|
||||
| Workflow | Purpose |
|
||||
|----------|---------|
|
||||
| `create-architecture` | Respects technical preferences when designing |
|
||||
| `create-story` | Informs story creation with project patterns |
|
||||
| `dev-story` | Guides implementation decisions |
|
||||
| `code-review` | Validates against project standards |
|
||||
| `quick-dev` | Applies patterns when implementing |
|
||||
|
||||
[**Learn more about project-context.md**](../explanation/project-context.md)
|
||||
|
||||
### Additional Context by Workflow
|
||||
|
||||
Beyond `project-context.md`, each workflow loads specific documents:
|
||||
|
||||
| Workflow | Also Loads |
|
||||
|----------|------------|
|
||||
| `create-story` | epics, PRD, architecture, UX |
|
||||
| `dev-story` | story file |
|
||||
| `code-review` | architecture, story file |
|
||||
| `quick-spec` | planning docs (if exist) |
|
||||
| `quick-dev` | tech-spec |
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ Build software faster using AI-powered workflows with specialized agents that gu
|
|||
## What You'll Learn
|
||||
|
||||
- Install and initialize BMad Method for a new project
|
||||
- Use **BMad-Help** — your intelligent guide that knows what to do next
|
||||
- Choose the right planning track for your project size
|
||||
- Progress through phases from requirements to working code
|
||||
- Use agents and workflows effectively
|
||||
|
|
@ -19,11 +20,46 @@ Build software faster using AI-powered workflows with specialized agents that gu
|
|||
- **A project idea** — Even a simple one works for learning
|
||||
:::
|
||||
|
||||
:::tip[Quick Path]
|
||||
:::tip[The Easiest Path]
|
||||
**Install** → `npx bmad-method install`
|
||||
**Plan** → PM creates PRD, Architect creates architecture
|
||||
**Build** → SM manages sprints, DEV implements stories
|
||||
**Fresh chats** for each workflow to avoid context issues.
|
||||
**Ask** → `/bmad-help what should I do first?`
|
||||
**Build** → Let BMad-Help guide you workflow by workflow
|
||||
:::
|
||||
|
||||
## Meet BMad-Help: Your Intelligent Guide
|
||||
|
||||
**BMad-Help is the fastest way to get started with BMad.** You don't need to memorize workflows or phases — just ask, and BMad-Help will:
|
||||
|
||||
- **Inspect your project** to see what's already been done
|
||||
- **Show your options** based on which modules you have installed
|
||||
- **Recommend what's next** — including the first required task
|
||||
- **Answer questions** like "I have a SaaS idea, where do I start?"
|
||||
|
||||
### How to Use BMad-Help
|
||||
|
||||
Run it in your AI IDE with just the slash command:
|
||||
|
||||
```
|
||||
/bmad-help
|
||||
```
|
||||
|
||||
Or combine it with a question for context-aware guidance:
|
||||
|
||||
```
|
||||
/bmad-help I have an idea for a SaaS product, I already know all the features I want. where do I get started?
|
||||
```
|
||||
|
||||
BMad-Help will respond with:
|
||||
- What's recommended for your situation
|
||||
- What the first required task is
|
||||
- What the rest of the process looks like
|
||||
|
||||
### It Powers Workflows Too
|
||||
|
||||
BMad-Help doesn't just answer questions — **it automatically runs at the end of every workflow** to tell you exactly what to do next. No guessing, no searching docs — just clear guidance on the next required workflow.
|
||||
|
||||
:::tip[Start Here]
|
||||
After installing BMad, run `/bmad-help` immediately. It will detect what modules you have installed and guide you to the right starting point for your project.
|
||||
:::
|
||||
|
||||
## Understanding BMad
|
||||
|
|
@ -65,7 +101,15 @@ The installer creates two folders:
|
|||
- `_bmad/` — agents, workflows, tasks, and configuration
|
||||
- `_bmad-output/` — empty for now, but this is where your artifacts will be saved
|
||||
|
||||
Open your AI IDE in the project folder. Run the `help` workflow (`/bmad-help`) to see what to do next — it detects what you've completed and recommends the next step.
|
||||
:::tip[Your Next Step]
|
||||
Open your AI IDE in the project folder and run:
|
||||
|
||||
```
|
||||
/bmad-help
|
||||
```
|
||||
|
||||
BMad-Help will detect what you've completed and recommend exactly what to do next. You can also ask it questions like "What are my options?" or "I have a SaaS idea, where should I start?"
|
||||
:::
|
||||
|
||||
:::note[How to Load Agents and Run Workflows]
|
||||
Each workflow has a **slash command** you run in your IDE (e.g., `/bmad-bmm-create-prd`). Running a workflow command automatically loads the appropriate agent — you don't need to load agents separately. You can also load an agent directly for general conversation (e.g., `/bmad-agent-bmm-pm` for the PM agent).
|
||||
|
|
@ -175,12 +219,12 @@ your-project/
|
|||
|
||||
## Quick Reference
|
||||
|
||||
| Workflow | Command | Agent | Purpose |
|
||||
| -------------------------------- | ------------------------------------------ | --------- | ------------------------------------ |
|
||||
| `help` | `/bmad-help` | Any | Get guidance on what to do next |
|
||||
| `prd` | `/bmad-bmm-create-prd` | PM | Create Product Requirements Document |
|
||||
| `create-architecture` | `/bmad-bmm-create-architecture` | Architect | Create architecture document |
|
||||
| `generate-project-context` | `/bmad-bmm-generate-project-context` | Analyst | Create project context file |
|
||||
| Workflow | Command | Agent | Purpose |
|
||||
| ------------------------------------- | ------------------------------------------ | --------- | ----------------------------------------------- |
|
||||
| **`help`** ⭐ | `/bmad-help` | Any | **Your intelligent guide — ask anything!** |
|
||||
| `prd` | `/bmad-bmm-create-prd` | PM | Create Product Requirements Document |
|
||||
| `create-architecture` | `/bmad-bmm-create-architecture` | Architect | Create architecture document |
|
||||
| `generate-project-context` | `/bmad-bmm-generate-project-context` | Analyst | Create project context file |
|
||||
| `create-epics-and-stories` | `/bmad-bmm-create-epics-and-stories` | PM | Break down PRD into epics |
|
||||
| `check-implementation-readiness` | `/bmad-bmm-check-implementation-readiness` | Architect | Validate planning cohesion |
|
||||
| `sprint-planning` | `/bmad-bmm-sprint-planning` | SM | Initialize sprint tracking |
|
||||
|
|
@ -204,16 +248,26 @@ Not strictly. Once you learn the flow, you can run workflows directly using the
|
|||
|
||||
## Getting Help
|
||||
|
||||
:::tip[First Stop: BMad-Help]
|
||||
**Run `/bmad-help` anytime** — it's the fastest way to get unstuck. Ask it anything:
|
||||
- "What should I do after installing?"
|
||||
- "I'm stuck on workflow X"
|
||||
- "What are my options for Y?"
|
||||
- "Show me what's been done so far"
|
||||
|
||||
BMad-Help inspects your project, detects what you've completed, and tells you exactly what to do next.
|
||||
:::
|
||||
|
||||
- **During workflows** — Agents guide you with questions and explanations
|
||||
- **Community** — [Discord](https://discord.gg/gk8jAdXWmj) (#bmad-method-help, #report-bugs-and-issues)
|
||||
- **Stuck?** — Run `help` (`/bmad-help`) to see what to do next
|
||||
|
||||
## Key Takeaways
|
||||
|
||||
:::tip[Remember These]
|
||||
- **Start with `/bmad-help`** — Your intelligent guide that knows your project and options
|
||||
- **Always use fresh chats** — Start a new chat for each workflow
|
||||
- **Track matters** — Quick Flow uses quick-spec; Method/Enterprise need PRD and architecture
|
||||
- **Use `help` (`/bmad-help`) when stuck** — It detects your progress and suggests next steps
|
||||
- **BMad-Help runs automatically** — Every workflow ends with guidance on what's next
|
||||
:::
|
||||
|
||||
Ready to start? Install BMad and let the agents guide you through your first project.
|
||||
Ready to start? Install BMad, run `/bmad-help`, and let your intelligent guide lead the way.
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ description: Get unstuck by showing what workflow steps come next or answering q
|
|||
|
||||
- **Empty `phase` = anytime** — Universal tools work regardless of workflow state
|
||||
- **Numbered phases indicate sequence** — Phases like `1-discover` → `2-define` → `3-build` → `4-ship` flow in order (naming varies by module)
|
||||
- **Phase with no Required Steps** - If an entire phase has no required, true items, the entire phase is optional. If it is sequentially before another phase, it can be recommended, but always be clear with the use what the true next required item is.
|
||||
- **Stay in module** — Guide through the active module's workflow based on phase+sequence ordering
|
||||
- **Descriptions contain routing** — Read for alternate paths (e.g., "back to previous if fixes needed")
|
||||
- **`required=true` blocks progress** — Required workflows must complete before proceeding to later phases
|
||||
|
|
@ -52,7 +53,7 @@ Determine what was just completed:
|
|||
|
||||
## EXECUTION
|
||||
|
||||
1. **Load catalog** — Load `{project-root}/_bmad/_config/bmad-help.csv`
|
||||
1. **Load catalog** — Load `_bmad/_config/bmad-help.csv`
|
||||
|
||||
2. **Resolve output locations and config** — Scan each folder under `_bmad/` (except `_config`) for `config.yaml`. For each workflow row, resolve its `output-location` variables against that module's config so artifact paths can be searched. Also extract `communication_language` and `project_knowledge` from each scanned module's config.
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,145 @@
|
|||
/**
|
||||
* Tests for CodexSetup.transformToSkillFormat
|
||||
*
|
||||
* Validates that descriptions round-trip correctly through parse/stringify,
|
||||
* producing valid YAML regardless of input quoting style.
|
||||
*
|
||||
* Usage: node test/test-codex-transform.js
|
||||
*/
|
||||
|
||||
const path = require('node:path');
|
||||
const yaml = require('yaml');
|
||||
|
||||
// ANSI colors
|
||||
const colors = {
|
||||
reset: '\u001B[0m',
|
||||
green: '\u001B[32m',
|
||||
red: '\u001B[31m',
|
||||
cyan: '\u001B[36m',
|
||||
dim: '\u001B[2m',
|
||||
};
|
||||
|
||||
let passed = 0;
|
||||
let failed = 0;
|
||||
|
||||
function assert(condition, testName, detail) {
|
||||
if (condition) {
|
||||
console.log(` ${colors.green}PASS${colors.reset} ${testName}`);
|
||||
passed++;
|
||||
} else {
|
||||
console.log(` ${colors.red}FAIL${colors.reset} ${testName}`);
|
||||
if (detail) console.log(` ${colors.dim}${detail}${colors.reset}`);
|
||||
failed++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the output frontmatter and return the description value.
|
||||
* Validates the output is well-formed YAML that parses back correctly.
|
||||
*/
|
||||
function parseOutputDescription(output) {
|
||||
const match = output.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
||||
if (!match) return null;
|
||||
const parsed = yaml.parse(match[1]);
|
||||
return parsed?.description;
|
||||
}
|
||||
|
||||
// Import the class under test
|
||||
const { CodexSetup } = require(path.join(__dirname, '..', 'tools', 'cli', 'installers', 'lib', 'ide', 'codex.js'));
|
||||
|
||||
const setup = new CodexSetup();
|
||||
|
||||
console.log(`\n${colors.cyan}CodexSetup.transformToSkillFormat tests${colors.reset}\n`);
|
||||
|
||||
// --- Simple description, no quotes ---
|
||||
{
|
||||
const input = `---\ndescription: A simple description\n---\n\nBody content here.`;
|
||||
const result = setup.transformToSkillFormat(input, 'my-skill');
|
||||
const desc = parseOutputDescription(result);
|
||||
assert(desc === 'A simple description', 'simple description round-trips', `got description: ${JSON.stringify(desc)}`);
|
||||
assert(result.includes('\nBody content here.'), 'body preserved for simple description');
|
||||
}
|
||||
|
||||
// --- Description with embedded single quotes (from double-quoted YAML input) ---
|
||||
{
|
||||
const input = `---\ndescription: "can't stop won't stop"\n---\n\nBody content here.`;
|
||||
const result = setup.transformToSkillFormat(input, 'my-skill');
|
||||
const desc = parseOutputDescription(result);
|
||||
assert(desc === "can't stop won't stop", 'description with apostrophes round-trips', `got description: ${JSON.stringify(desc)}`);
|
||||
assert(result.includes('\nBody content here.'), 'body preserved for quoted description');
|
||||
}
|
||||
|
||||
// --- Description with embedded single quote ---
|
||||
{
|
||||
const input = `---\ndescription: "it's a test"\n---\n\nBody.`;
|
||||
const result = setup.transformToSkillFormat(input, 'test-skill');
|
||||
const desc = parseOutputDescription(result);
|
||||
assert(desc === "it's a test", 'description with apostrophe round-trips', `got description: ${JSON.stringify(desc)}`);
|
||||
}
|
||||
|
||||
// --- Single-quoted input with pre-escaped apostrophe (YAML '' escape) ---
|
||||
{
|
||||
const input = `---\ndescription: 'don''t panic'\n---\n\nBody.`;
|
||||
const result = setup.transformToSkillFormat(input, 'test-skill');
|
||||
const desc = parseOutputDescription(result);
|
||||
assert(desc === "don't panic", 'single-quoted escaped apostrophe round-trips', `got description: ${JSON.stringify(desc)}`);
|
||||
}
|
||||
|
||||
// --- Verify name is set correctly ---
|
||||
{
|
||||
const input = `---\ndescription: test\n---\n\nBody.`;
|
||||
const result = setup.transformToSkillFormat(input, 'my-custom-skill');
|
||||
const match = result.match(/^---\n([\s\S]*?)\n---/);
|
||||
const parsed = yaml.parse(match[1]);
|
||||
assert(parsed.name === 'my-custom-skill', 'name field matches skillName argument', `got name: ${JSON.stringify(parsed.name)}`);
|
||||
}
|
||||
|
||||
// --- Extra frontmatter keys are stripped ---
|
||||
{
|
||||
const input = `---\ndescription: foo\ndisable-model-invocation: true\ncustom-field: bar\n---\n\nBody.`;
|
||||
const result = setup.transformToSkillFormat(input, 'strip-extra');
|
||||
const desc = parseOutputDescription(result);
|
||||
assert(desc === 'foo', 'description preserved when extra keys present', `got description: ${JSON.stringify(desc)}`);
|
||||
const match = result.match(/^---\n([\s\S]*?)\n---/);
|
||||
const parsed = yaml.parse(match[1]);
|
||||
assert(parsed.name === 'strip-extra', 'name equals skillName after stripping extras', `got name: ${JSON.stringify(parsed.name)}`);
|
||||
assert(!('disable-model-invocation' in parsed), 'disable-model-invocation stripped', `keys: ${Object.keys(parsed).join(', ')}`);
|
||||
assert(!('custom-field' in parsed), 'custom-field stripped', `keys: ${Object.keys(parsed).join(', ')}`);
|
||||
const keys = Object.keys(parsed).sort();
|
||||
assert(
|
||||
keys.length === 2 && keys[0] === 'description' && keys[1] === 'name',
|
||||
'only name and description remain',
|
||||
`keys: ${keys.join(', ')}`,
|
||||
);
|
||||
}
|
||||
|
||||
// --- No frontmatter wraps content ---
|
||||
{
|
||||
const input = 'Just some content without frontmatter.';
|
||||
const result = setup.transformToSkillFormat(input, 'bare-skill');
|
||||
const desc = parseOutputDescription(result);
|
||||
assert(desc === 'bare-skill', 'no-frontmatter fallback uses skillName as description', `got description: ${JSON.stringify(desc)}`);
|
||||
assert(result.includes('Just some content without frontmatter.'), 'body preserved when no frontmatter');
|
||||
}
|
||||
|
||||
// --- No frontmatter with single-quote in skillName ---
|
||||
{
|
||||
const input = 'Body content for the skill.';
|
||||
const result = setup.transformToSkillFormat(input, "it's-a-task");
|
||||
const desc = parseOutputDescription(result);
|
||||
assert(desc === "it's-a-task", 'no-frontmatter skillName with single quote round-trips', `got description: ${JSON.stringify(desc)}`);
|
||||
assert(result.includes('Body content for the skill.'), 'body preserved for single-quote skillName');
|
||||
}
|
||||
|
||||
// --- CRLF frontmatter is parsed correctly (Windows line endings) ---
|
||||
{
|
||||
const input = '---\r\ndescription: windows line endings\r\n---\r\n\r\nBody.';
|
||||
const result = setup.transformToSkillFormat(input, 'crlf-skill');
|
||||
const desc = parseOutputDescription(result);
|
||||
assert(desc === 'windows line endings', 'CRLF frontmatter parses correctly', `got description: ${JSON.stringify(desc)}`);
|
||||
assert(result.includes('Body.'), 'body preserved for CRLF input');
|
||||
}
|
||||
|
||||
// --- Summary ---
|
||||
console.log(`\n${passed} passed, ${failed} failed\n`);
|
||||
process.exit(failed > 0 ? 1 : 0);
|
||||
|
|
@ -0,0 +1,216 @@
|
|||
/**
|
||||
* Tests for CodexSetup.writeSkillArtifacts
|
||||
*
|
||||
* Validates directory creation, SKILL.md file writing, type filtering,
|
||||
* and integration with transformToSkillFormat.
|
||||
*
|
||||
* Usage: node test/test-codex-write-skills.js
|
||||
*/
|
||||
|
||||
const path = require('node:path');
|
||||
const fs = require('fs-extra');
|
||||
const os = require('node:os');
|
||||
const yaml = require('yaml');
|
||||
|
||||
// ANSI colors
|
||||
const colors = {
|
||||
reset: '\u001B[0m',
|
||||
green: '\u001B[32m',
|
||||
red: '\u001B[31m',
|
||||
cyan: '\u001B[36m',
|
||||
dim: '\u001B[2m',
|
||||
};
|
||||
|
||||
let passed = 0;
|
||||
let failed = 0;
|
||||
|
||||
function assert(condition, testName, detail) {
|
||||
if (condition) {
|
||||
console.log(` ${colors.green}PASS${colors.reset} ${testName}`);
|
||||
passed++;
|
||||
} else {
|
||||
console.log(` ${colors.red}FAIL${colors.reset} ${testName}`);
|
||||
if (detail) console.log(` ${colors.dim}${detail}${colors.reset}`);
|
||||
failed++;
|
||||
}
|
||||
}
|
||||
|
||||
// Import the class under test
|
||||
const { CodexSetup } = require(path.join(__dirname, '..', 'tools', 'cli', 'installers', 'lib', 'ide', 'codex.js'));
|
||||
|
||||
const setup = new CodexSetup();
|
||||
|
||||
// Create a temp directory for each test run
|
||||
let tmpDir;
|
||||
|
||||
async function createTmpDir() {
|
||||
tmpDir = path.join(os.tmpdir(), `bmad-test-skills-${Date.now()}`);
|
||||
await fs.ensureDir(tmpDir);
|
||||
return tmpDir;
|
||||
}
|
||||
|
||||
async function cleanTmpDir() {
|
||||
if (tmpDir) {
|
||||
await fs.remove(tmpDir);
|
||||
}
|
||||
}
|
||||
|
||||
async function runTests() {
|
||||
console.log(`\n${colors.cyan}CodexSetup.writeSkillArtifacts tests${colors.reset}\n`);
|
||||
|
||||
// --- Writes a single artifact as a skill directory with SKILL.md ---
|
||||
{
|
||||
const destDir = await createTmpDir();
|
||||
const artifacts = [
|
||||
{
|
||||
type: 'task',
|
||||
relativePath: 'bmm/tasks/create-story.md',
|
||||
content: '---\ndescription: Create a user story\n---\n\nStory creation instructions.',
|
||||
},
|
||||
];
|
||||
const count = await setup.writeSkillArtifacts(destDir, artifacts, 'task');
|
||||
assert(count === 1, 'single artifact returns count 1');
|
||||
|
||||
const skillDir = path.join(destDir, 'bmad-bmm-create-story');
|
||||
assert(await fs.pathExists(skillDir), 'skill directory created');
|
||||
|
||||
const skillFile = path.join(skillDir, 'SKILL.md');
|
||||
assert(await fs.pathExists(skillFile), 'SKILL.md file created');
|
||||
|
||||
const content = await fs.readFile(skillFile, 'utf8');
|
||||
const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
||||
assert(fmMatch !== null, 'SKILL.md has frontmatter');
|
||||
|
||||
const parsed = yaml.parse(fmMatch[1]);
|
||||
assert(parsed.name === 'bmad-bmm-create-story', 'name matches skill directory name', `got: ${parsed.name}`);
|
||||
assert(parsed.description === 'Create a user story', 'description preserved', `got: ${parsed.description}`);
|
||||
assert(content.includes('Story creation instructions.'), 'body content preserved');
|
||||
await cleanTmpDir();
|
||||
}
|
||||
|
||||
// --- Filters artifacts by type ---
|
||||
{
|
||||
const destDir = await createTmpDir();
|
||||
const artifacts = [
|
||||
{
|
||||
type: 'task',
|
||||
relativePath: 'bmm/tasks/create-story.md',
|
||||
content: '---\ndescription: A task\n---\n\nTask body.',
|
||||
},
|
||||
{
|
||||
type: 'workflow-command',
|
||||
relativePath: 'bmm/workflows/plan-project.md',
|
||||
content: '---\ndescription: A workflow\n---\n\nWorkflow body.',
|
||||
},
|
||||
{
|
||||
type: 'agent-launcher',
|
||||
relativePath: 'bmm/agents/pm.md',
|
||||
content: '---\ndescription: An agent\n---\n\nAgent body.',
|
||||
},
|
||||
];
|
||||
const count = await setup.writeSkillArtifacts(destDir, artifacts, 'task');
|
||||
assert(count === 1, 'only matching type is written when filtering for task');
|
||||
|
||||
const entries = await fs.readdir(destDir);
|
||||
assert(entries.length === 1, 'only one skill directory created', `got ${entries.length}: ${entries.join(', ')}`);
|
||||
assert(entries[0] === 'bmad-bmm-create-story', 'correct artifact was written', `got: ${entries[0]}`);
|
||||
await cleanTmpDir();
|
||||
}
|
||||
|
||||
// --- Writes multiple artifacts of the same type ---
|
||||
{
|
||||
const destDir = await createTmpDir();
|
||||
const artifacts = [
|
||||
{
|
||||
type: 'workflow-command',
|
||||
relativePath: 'bmm/workflows/plan-project.md',
|
||||
content: '---\ndescription: Plan\n---\n\nPlan body.',
|
||||
},
|
||||
{
|
||||
type: 'workflow-command',
|
||||
relativePath: 'core/workflows/review.md',
|
||||
content: '---\ndescription: Review\n---\n\nReview body.',
|
||||
},
|
||||
];
|
||||
const count = await setup.writeSkillArtifacts(destDir, artifacts, 'workflow-command');
|
||||
assert(count === 2, 'two artifacts written');
|
||||
|
||||
const entries = new Set((await fs.readdir(destDir)).sort());
|
||||
assert(entries.has('bmad-bmm-plan-project'), 'first skill directory exists');
|
||||
assert(entries.has('bmad-review'), 'second skill directory exists (core module)');
|
||||
await cleanTmpDir();
|
||||
}
|
||||
|
||||
// --- Returns 0 when no artifacts match type ---
|
||||
{
|
||||
const destDir = await createTmpDir();
|
||||
const artifacts = [
|
||||
{
|
||||
type: 'agent-launcher',
|
||||
relativePath: 'bmm/agents/pm.md',
|
||||
content: '---\ndescription: An agent\n---\n\nBody.',
|
||||
},
|
||||
];
|
||||
const count = await setup.writeSkillArtifacts(destDir, artifacts, 'task');
|
||||
assert(count === 0, 'returns 0 when no types match');
|
||||
|
||||
const entries = await fs.readdir(destDir);
|
||||
assert(entries.length === 0, 'no directories created when no types match');
|
||||
await cleanTmpDir();
|
||||
}
|
||||
|
||||
// --- Handles empty artifacts array ---
|
||||
{
|
||||
const destDir = await createTmpDir();
|
||||
const count = await setup.writeSkillArtifacts(destDir, [], 'task');
|
||||
assert(count === 0, 'returns 0 for empty artifacts array');
|
||||
await cleanTmpDir();
|
||||
}
|
||||
|
||||
// --- Artifacts without type field are always written ---
|
||||
{
|
||||
const destDir = await createTmpDir();
|
||||
const artifacts = [
|
||||
{
|
||||
relativePath: 'bmm/tasks/no-type.md',
|
||||
content: '---\ndescription: No type field\n---\n\nBody.',
|
||||
},
|
||||
];
|
||||
const count = await setup.writeSkillArtifacts(destDir, artifacts, 'task');
|
||||
assert(count === 1, 'artifact without type field is written (no filtering)');
|
||||
await cleanTmpDir();
|
||||
}
|
||||
|
||||
// --- Content without frontmatter gets minimal frontmatter added ---
|
||||
{
|
||||
const destDir = await createTmpDir();
|
||||
const artifacts = [
|
||||
{
|
||||
type: 'task',
|
||||
relativePath: 'bmm/tasks/bare.md',
|
||||
content: 'Just plain content, no frontmatter.',
|
||||
},
|
||||
];
|
||||
const count = await setup.writeSkillArtifacts(destDir, artifacts, 'task');
|
||||
assert(count === 1, 'bare content artifact written');
|
||||
|
||||
const skillFile = path.join(destDir, 'bmad-bmm-bare', 'SKILL.md');
|
||||
const content = await fs.readFile(skillFile, 'utf8');
|
||||
const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
||||
assert(fmMatch !== null, 'frontmatter added to bare content');
|
||||
|
||||
const parsed = yaml.parse(fmMatch[1]);
|
||||
assert(parsed.name === 'bmad-bmm-bare', 'name set for bare content', `got: ${parsed.name}`);
|
||||
assert(content.includes('Just plain content, no frontmatter.'), 'original content preserved');
|
||||
await cleanTmpDir();
|
||||
}
|
||||
|
||||
// --- Summary ---
|
||||
console.log(`\n${passed} passed, ${failed} failed\n`);
|
||||
process.exit(failed > 0 ? 1 : 0);
|
||||
}
|
||||
|
||||
runTests().catch((error) => {
|
||||
console.error('Test runner error:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
|
|
@ -7,10 +7,13 @@ const { AgentCommandGenerator } = require('./shared/agent-command-generator');
|
|||
const { TaskToolCommandGenerator } = require('./shared/task-tool-command-generator');
|
||||
const { getTasksFromBmad } = require('./shared/bmad-artifacts');
|
||||
const { toDashPath, customAgentDashName } = require('./shared/path-utils');
|
||||
const yaml = require('yaml');
|
||||
const prompts = require('../../../lib/prompts');
|
||||
|
||||
/**
|
||||
* Codex setup handler (CLI mode)
|
||||
* Writes BMAD artifacts as Agent Skills (agentskills.io format)
|
||||
* into .agents/skills/ directories.
|
||||
*/
|
||||
class CodexSetup extends BaseIdeSetup {
|
||||
constructor() {
|
||||
|
|
@ -23,35 +26,35 @@ class CodexSetup extends BaseIdeSetup {
|
|||
* @returns {Object} Collected configuration
|
||||
*/
|
||||
async collectConfiguration(options = {}) {
|
||||
// Non-interactive mode: use default (global)
|
||||
// Non-interactive mode: use default (project)
|
||||
if (options.skipPrompts) {
|
||||
return { installLocation: 'global' };
|
||||
return { installLocation: 'project' };
|
||||
}
|
||||
|
||||
let confirmed = false;
|
||||
let installLocation = 'global';
|
||||
let installLocation = 'project';
|
||||
|
||||
while (!confirmed) {
|
||||
installLocation = await prompts.select({
|
||||
message: 'Where would you like to install Codex CLI prompts?',
|
||||
message: 'Where would you like to install Codex CLI skills?',
|
||||
choices: [
|
||||
{
|
||||
name: 'Global - Simple for single project ' + '(~/.codex/prompts, but references THIS project only)',
|
||||
value: 'global',
|
||||
},
|
||||
{
|
||||
name: `Project-specific - Recommended for real work (requires CODEX_HOME=<project-dir>${path.sep}.codex)`,
|
||||
name: 'Project-specific - Recommended (<project>/.agents/skills)',
|
||||
value: 'project',
|
||||
},
|
||||
{
|
||||
name: 'Global - ($HOME/.agents/skills)',
|
||||
value: 'global',
|
||||
},
|
||||
],
|
||||
default: 'global',
|
||||
default: 'project',
|
||||
});
|
||||
|
||||
// Show brief confirmation hint (detailed instructions available via verbose)
|
||||
if (installLocation === 'project') {
|
||||
await prompts.log.info('Prompts installed to: <project>/.codex/prompts (requires CODEX_HOME)');
|
||||
await prompts.log.info('Skills installed to: <project>/.agents/skills');
|
||||
} else {
|
||||
await prompts.log.info('Prompts installed to: ~/.codex/prompts');
|
||||
await prompts.log.info('Skills installed to: $HOME/.agents/skills');
|
||||
}
|
||||
|
||||
// Confirm the choice
|
||||
|
|
@ -80,20 +83,21 @@ class CodexSetup extends BaseIdeSetup {
|
|||
// Always use CLI mode
|
||||
const mode = 'cli';
|
||||
|
||||
// Get installation location from pre-collected config or default to global
|
||||
const installLocation = options.preCollectedConfig?.installLocation || 'global';
|
||||
// Get installation location from pre-collected config or default to project
|
||||
const installLocation = options.preCollectedConfig?.installLocation || 'project';
|
||||
|
||||
const { artifacts, counts } = await this.collectClaudeArtifacts(projectDir, bmadDir, options);
|
||||
|
||||
const destDir = this.getCodexPromptDir(projectDir, installLocation);
|
||||
const destDir = this.getCodexSkillsDir(projectDir, installLocation);
|
||||
await fs.ensureDir(destDir);
|
||||
await this.clearOldBmadFiles(destDir, options);
|
||||
await this.clearOldBmadSkills(destDir, options);
|
||||
|
||||
// Collect artifacts and write using underscore format
|
||||
// Collect and write agent skills
|
||||
const agentGen = new AgentCommandGenerator(this.bmadFolderName);
|
||||
const { artifacts: agentArtifacts } = await agentGen.collectAgentArtifacts(bmadDir, options.selectedModules || []);
|
||||
const agentCount = await agentGen.writeDashArtifacts(destDir, agentArtifacts);
|
||||
const agentCount = await this.writeSkillArtifacts(destDir, agentArtifacts, 'agent-launcher');
|
||||
|
||||
// Collect and write task skills
|
||||
const tasks = await getTasksFromBmad(bmadDir, options.selectedModules || []);
|
||||
const taskArtifacts = [];
|
||||
for (const task of tasks) {
|
||||
|
|
@ -117,19 +121,23 @@ class CodexSetup extends BaseIdeSetup {
|
|||
});
|
||||
}
|
||||
|
||||
const ttGen = new TaskToolCommandGenerator(this.bmadFolderName);
|
||||
const taskSkillArtifacts = taskArtifacts.map((artifact) => ({
|
||||
...artifact,
|
||||
content: ttGen.generateCommandContent(artifact, artifact.type),
|
||||
}));
|
||||
const tasksWritten = await this.writeSkillArtifacts(destDir, taskSkillArtifacts, 'task');
|
||||
|
||||
// Collect and write workflow skills
|
||||
const workflowGenerator = new WorkflowCommandGenerator(this.bmadFolderName);
|
||||
const { artifacts: workflowArtifacts } = await workflowGenerator.collectWorkflowArtifacts(bmadDir);
|
||||
const workflowCount = await workflowGenerator.writeDashArtifacts(destDir, workflowArtifacts);
|
||||
|
||||
// Also write tasks using underscore format
|
||||
const ttGen = new TaskToolCommandGenerator(this.bmadFolderName);
|
||||
const tasksWritten = await ttGen.writeDashArtifacts(destDir, taskArtifacts);
|
||||
const workflowCount = await this.writeSkillArtifacts(destDir, workflowArtifacts, 'workflow-command');
|
||||
|
||||
const written = agentCount + workflowCount + tasksWritten;
|
||||
|
||||
if (!options.silent) {
|
||||
await prompts.log.success(
|
||||
`${this.name} configured: ${counts.agents} agents, ${counts.workflows} workflows, ${counts.tasks} tasks, ${written} files → ${destDir}`,
|
||||
`${this.name} configured: ${counts.agents} agents, ${counts.workflows} workflows, ${counts.tasks} tasks, ${written} skills → ${destDir}`,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -145,13 +153,82 @@ class CodexSetup extends BaseIdeSetup {
|
|||
}
|
||||
|
||||
/**
|
||||
* Detect Codex installation by checking for BMAD prompt exports
|
||||
* Write artifacts as Agent Skills (agentskills.io format).
|
||||
* Each artifact becomes a directory containing SKILL.md.
|
||||
* @param {string} destDir - Base skills directory
|
||||
* @param {Array} artifacts - Artifacts to write
|
||||
* @param {string} artifactType - Type filter (e.g., 'agent-launcher', 'workflow-command', 'task')
|
||||
* @returns {number} Number of skills written
|
||||
*/
|
||||
async writeSkillArtifacts(destDir, artifacts, artifactType) {
|
||||
let writtenCount = 0;
|
||||
|
||||
for (const artifact of artifacts) {
|
||||
// Filter by type if the artifact has a type field
|
||||
if (artifact.type && artifact.type !== artifactType) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get the dash-format name (e.g., bmad-bmm-create-prd.md) and remove .md
|
||||
const flatName = toDashPath(artifact.relativePath);
|
||||
const skillName = flatName.replace(/\.md$/, '');
|
||||
|
||||
// Create skill directory
|
||||
const skillDir = path.join(destDir, skillName);
|
||||
await fs.ensureDir(skillDir);
|
||||
|
||||
// Transform content: rewrite frontmatter for skills format
|
||||
const skillContent = this.transformToSkillFormat(artifact.content, skillName);
|
||||
|
||||
// Write SKILL.md
|
||||
await fs.writeFile(path.join(skillDir, 'SKILL.md'), skillContent);
|
||||
writtenCount++;
|
||||
}
|
||||
|
||||
return writtenCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform artifact content from Codex prompt format to Agent Skills format.
|
||||
* Removes disable-model-invocation, ensures name matches directory.
|
||||
* @param {string} content - Original content with YAML frontmatter
|
||||
* @param {string} skillName - Skill name (must match directory name)
|
||||
* @returns {string} Transformed content
|
||||
*/
|
||||
transformToSkillFormat(content, skillName) {
|
||||
// Parse frontmatter
|
||||
const fmMatch = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/);
|
||||
if (!fmMatch) {
|
||||
// No frontmatter -- wrap with minimal frontmatter
|
||||
const fm = yaml.stringify({ name: skillName, description: skillName }).trimEnd();
|
||||
return `---\n${fm}\n---\n\n${content}`;
|
||||
}
|
||||
|
||||
const frontmatter = fmMatch[1];
|
||||
const body = fmMatch[2];
|
||||
|
||||
// Parse frontmatter with yaml library to handle all quoting variants
|
||||
let description;
|
||||
try {
|
||||
const parsed = yaml.parse(frontmatter);
|
||||
description = parsed?.description || `${skillName} skill`;
|
||||
} catch {
|
||||
description = `${skillName} skill`;
|
||||
}
|
||||
|
||||
// Build new frontmatter with only skills-spec fields, let yaml handle quoting
|
||||
const newFrontmatter = yaml.stringify({ name: skillName, description }).trimEnd();
|
||||
return `---\n${newFrontmatter}\n---\n${body}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect Codex installation by checking for BMAD skills
|
||||
*/
|
||||
async detect(projectDir) {
|
||||
// Check both global and project-specific locations
|
||||
const globalDir = this.getCodexPromptDir(null, 'global');
|
||||
const globalDir = this.getCodexSkillsDir(null, 'global');
|
||||
const projectDir_local = projectDir || process.cwd();
|
||||
const projectSpecificDir = this.getCodexPromptDir(projectDir_local, 'project');
|
||||
const projectSpecificDir = this.getCodexSkillsDir(projectDir_local, 'project');
|
||||
|
||||
// Check global location
|
||||
if (await fs.pathExists(globalDir)) {
|
||||
|
|
@ -240,27 +317,20 @@ class CodexSetup extends BaseIdeSetup {
|
|||
};
|
||||
}
|
||||
|
||||
getCodexPromptDir(projectDir = null, location = 'global') {
|
||||
getCodexSkillsDir(projectDir = null, location = 'project') {
|
||||
if (location === 'project' && projectDir) {
|
||||
return path.join(projectDir, '.codex', 'prompts');
|
||||
return path.join(projectDir, '.agents', 'skills');
|
||||
}
|
||||
return path.join(os.homedir(), '.codex', 'prompts');
|
||||
if (location === 'project' && !projectDir) {
|
||||
throw new Error('projectDir is required for project-scoped skill installation');
|
||||
}
|
||||
return path.join(os.homedir(), '.agents', 'skills');
|
||||
}
|
||||
|
||||
async flattenAndWriteArtifacts(artifacts, destDir) {
|
||||
let written = 0;
|
||||
|
||||
for (const artifact of artifacts) {
|
||||
const flattenedName = this.flattenFilename(artifact.relativePath);
|
||||
const targetPath = path.join(destDir, flattenedName);
|
||||
await fs.writeFile(targetPath, artifact.content);
|
||||
written++;
|
||||
}
|
||||
|
||||
return written;
|
||||
}
|
||||
|
||||
async clearOldBmadFiles(destDir, options = {}) {
|
||||
/**
|
||||
* Remove existing BMAD skill directories from the skills directory.
|
||||
*/
|
||||
async clearOldBmadSkills(destDir, options = {}) {
|
||||
if (!(await fs.pathExists(destDir))) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -299,7 +369,8 @@ class CodexSetup extends BaseIdeSetup {
|
|||
}
|
||||
|
||||
async readAndProcessWithProject(filePath, metadata, projectDir) {
|
||||
const content = await fs.readFile(filePath, 'utf8');
|
||||
const rawContent = await fs.readFile(filePath, 'utf8');
|
||||
const content = rawContent.replaceAll('\r\n', '\n').replaceAll('\r', '\n');
|
||||
return super.processContent(content, metadata, projectDir);
|
||||
}
|
||||
|
||||
|
|
@ -311,14 +382,14 @@ class CodexSetup extends BaseIdeSetup {
|
|||
const lines = [
|
||||
'IMPORTANT: Codex Configuration',
|
||||
'',
|
||||
'/prompts installed globally to your HOME DIRECTORY.',
|
||||
'Skills installed globally to your HOME DIRECTORY ($HOME/.agents/skills).',
|
||||
'',
|
||||
'These prompts reference a specific _bmad path.',
|
||||
'These skills reference a specific _bmad path.',
|
||||
"To use with other projects, you'd need to copy the _bmad dir.",
|
||||
'',
|
||||
'You can now use /commands in Codex CLI',
|
||||
' Example: /bmad_bmm_pm',
|
||||
' Type / to see all available commands',
|
||||
'Skills are available in Codex CLI automatically.',
|
||||
' Use /skills to see available skills',
|
||||
' Skills can also be invoked implicitly based on task description',
|
||||
];
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
|
@ -330,40 +401,15 @@ class CodexSetup extends BaseIdeSetup {
|
|||
* @returns {string} Instructions text
|
||||
*/
|
||||
getProjectSpecificInstructions(projectDir = null, destDir = null) {
|
||||
const isWindows = os.platform() === 'win32';
|
||||
|
||||
const commonLines = [
|
||||
const lines = [
|
||||
'Project-Specific Codex Configuration',
|
||||
'',
|
||||
`Prompts will be installed to: ${destDir || '<project>/.codex/prompts'}`,
|
||||
'',
|
||||
'REQUIRED: You must set CODEX_HOME to use these prompts',
|
||||
`Skills installed to: ${destDir || '<project>/.agents/skills'}`,
|
||||
'',
|
||||
'Codex automatically discovers skills in .agents/skills/ at and above the current directory and in your home directory.',
|
||||
'No additional configuration is needed.',
|
||||
];
|
||||
|
||||
const windowsLines = [
|
||||
'Create a codex.cmd file in your project root:',
|
||||
'',
|
||||
' @echo off',
|
||||
' set CODEX_HOME=%~dp0.codex',
|
||||
' codex %*',
|
||||
'',
|
||||
String.raw`Then run: .\codex instead of codex`,
|
||||
'(The %~dp0 gets the directory of the .cmd file)',
|
||||
];
|
||||
|
||||
const unixLines = [
|
||||
'Add this alias to your ~/.bashrc or ~/.zshrc:',
|
||||
'',
|
||||
' alias codex=\'CODEX_HOME="$PWD/.codex" codex\'',
|
||||
'',
|
||||
'After adding, run: source ~/.bashrc (or source ~/.zshrc)',
|
||||
'(The $PWD uses your current working directory)',
|
||||
];
|
||||
const closingLines = ['', 'This tells Codex CLI to use prompts from this project instead of ~/.codex'];
|
||||
|
||||
const lines = [...commonLines, ...(isWindows ? windowsLines : unixLines), ...closingLines];
|
||||
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
|
|
@ -372,31 +418,34 @@ class CodexSetup extends BaseIdeSetup {
|
|||
*/
|
||||
async cleanup(projectDir = null) {
|
||||
// Clean both global and project-specific locations
|
||||
const globalDir = this.getCodexPromptDir(null, 'global');
|
||||
await this.clearOldBmadFiles(globalDir);
|
||||
const globalDir = this.getCodexSkillsDir(null, 'global');
|
||||
await this.clearOldBmadSkills(globalDir);
|
||||
|
||||
if (projectDir) {
|
||||
const projectSpecificDir = this.getCodexPromptDir(projectDir, 'project');
|
||||
await this.clearOldBmadFiles(projectSpecificDir);
|
||||
const projectSpecificDir = this.getCodexSkillsDir(projectDir, 'project');
|
||||
await this.clearOldBmadSkills(projectSpecificDir);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Install a custom agent launcher for Codex
|
||||
* @param {string} projectDir - Project directory (not used, Codex installs to home)
|
||||
* Install a custom agent launcher for Codex as an Agent Skill
|
||||
* @param {string} projectDir - Project directory
|
||||
* @param {string} agentName - Agent name (e.g., "fred-commit-poet")
|
||||
* @param {string} agentPath - Path to compiled agent (relative to project root)
|
||||
* @param {Object} metadata - Agent metadata
|
||||
* @returns {Object|null} Info about created command
|
||||
* @returns {Object|null} Info about created skill
|
||||
*/
|
||||
async installCustomAgentLauncher(projectDir, agentName, agentPath, metadata) {
|
||||
const destDir = this.getCodexPromptDir(projectDir, 'project');
|
||||
await fs.ensureDir(destDir);
|
||||
const destDir = this.getCodexSkillsDir(projectDir, 'project');
|
||||
|
||||
const launcherContent = `---
|
||||
name: '${agentName}'
|
||||
description: '${agentName} agent'
|
||||
disable-model-invocation: true
|
||||
// Skill name from the dash name (without .md)
|
||||
const skillName = customAgentDashName(agentName).replace(/\.md$/, '');
|
||||
const skillDir = path.join(destDir, skillName);
|
||||
await fs.ensureDir(skillDir);
|
||||
|
||||
const fm = yaml.stringify({ name: skillName, description: `${agentName} agent` }).trimEnd();
|
||||
const skillContent = `---
|
||||
${fm}
|
||||
---
|
||||
|
||||
You must fully embody this agent's persona and follow all activation instructions exactly as specified. NEVER break character until given an exit command.
|
||||
|
|
@ -411,14 +460,12 @@ You must fully embody this agent's persona and follow all activation instruction
|
|||
</agent-activation>
|
||||
`;
|
||||
|
||||
// Use underscore format: bmad_custom_fred-commit-poet.md
|
||||
const fileName = customAgentDashName(agentName);
|
||||
const launcherPath = path.join(destDir, fileName);
|
||||
await fs.writeFile(launcherPath, launcherContent, 'utf8');
|
||||
const skillPath = path.join(skillDir, 'SKILL.md');
|
||||
await fs.writeFile(skillPath, skillContent, 'utf8');
|
||||
|
||||
return {
|
||||
path: path.relative(projectDir, launcherPath),
|
||||
command: `/${fileName.replace('.md', '')}`,
|
||||
path: path.relative(projectDir, skillPath),
|
||||
command: `$${skillName}`,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue