Compare commits

..

No commits in common. "dc8293e5df46cba0c6adc70aff7d5d44a88a1a6c" and "44ba15f9a1bff9ac5419a7247f4234d2cf877d22" have entirely different histories.

22 changed files with 102 additions and 565 deletions

View File

@ -27,8 +27,8 @@ agent:
exec: "{project-root}/_bmad/bmm/workflows/bmad-quick-flow/quick-dev/workflow.md"
description: "[QD] Quick-flow Develop: Implement a story tech spec end-to-end (Core of Quick Flow)"
- trigger: QQ or fuzzy match on bmad-quick-dev-new-preview
exec: "{project-root}/_bmad/bmm/workflows/bmad-quick-flow/bmad-quick-dev-new-preview/workflow.md"
- trigger: QQ or fuzzy match on quick-dev-new-preview
exec: "{project-root}/_bmad/bmm/workflows/bmad-quick-flow/quick-dev-new-preview/workflow.md"
description: "[QQ] Quick Dev New (Preview): Unified quick flow — clarify intent, plan, implement, review, present (experimental)"
- trigger: CR or fuzzy match on code-review

View File

@ -20,7 +20,7 @@ agent:
menu:
- trigger: SP or fuzzy match on sprint-planning
workflow: "{project-root}/_bmad/bmm/workflows/4-implementation/sprint-planning/workflow.md"
workflow: "{project-root}/_bmad/bmm/workflows/4-implementation/sprint-planning/workflow.yaml"
description: "[SP] Sprint Planning: Generate or update the record that will sequence the tasks to complete the full project that the dev agent will follow"
- trigger: CS or fuzzy match on create-story

View File

@ -3,7 +3,7 @@ bmm,anytime,Document Project,DP,,_bmad/bmm/workflows/document-project/workflow.y
bmm,anytime,Generate Project Context,GPC,,_bmad/bmm/workflows/generate-project-context/workflow.md,bmad-bmm-generate-project-context,false,analyst,Create Mode,"Scan existing codebase to generate a lean LLM-optimized project-context.md containing critical implementation rules patterns and conventions for AI agents. Essential for brownfield projects and quick-flow.",output_folder,"project context",
bmm,anytime,Quick Spec,QS,,_bmad/bmm/workflows/bmad-quick-flow/quick-spec/workflow.md,bmad-bmm-quick-spec,false,quick-flow-solo-dev,Create Mode,"Do not suggest for potentially very complex things unless requested or if the user complains that they do not want to follow the extensive planning of the bmad method. Quick one-off tasks small changes simple apps brownfield additions to well established patterns utilities without extensive planning",planning_artifacts,"tech spec",
bmm,anytime,Quick Dev,QD,,_bmad/bmm/workflows/bmad-quick-flow/quick-dev/workflow.md,bmad-bmm-quick-dev,false,quick-flow-solo-dev,Create Mode,"Quick one-off tasks small changes simple apps utilities without extensive planning - Do not suggest for potentially very complex things unless requested or if the user complains that they do not want to follow the extensive planning of the bmad method, unless the user is already working through the implementation phase and just requests a 1 off things not already in the plan",,,
bmm,anytime,Quick Dev New Preview,QQ,,skill:bmad-quick-dev-new-preview,bmad-bmm-quick-dev-new-preview,false,quick-flow-solo-dev,Create Mode,"Unified quick flow (experimental): clarify intent plan implement review and present in a single workflow",implementation_artifacts,"tech spec implementation",
bmm,anytime,Quick Dev New Preview,QQ,,_bmad/bmm/workflows/bmad-quick-flow/quick-dev-new-preview/workflow.md,bmad-bmm-quick-dev-new-preview,false,quick-flow-solo-dev,Create Mode,"Unified quick flow (experimental): clarify intent plan implement review and present in a single workflow",implementation_artifacts,"tech spec implementation",
bmm,anytime,Correct Course,CC,,_bmad/bmm/workflows/4-implementation/correct-course/workflow.yaml,bmad-bmm-correct-course,false,sm,Create Mode,"Anytime: Navigate significant changes. May recommend start over update PRD redo architecture sprint planning or correct epics and stories",planning_artifacts,"change proposal",
bmm,anytime,Write Document,WD,,_bmad/bmm/agents/tech-writer/tech-writer.agent.yaml,,false,tech-writer,,"Describe in detail what you want, and the agent will follow the documentation best practices defined in agent memory. Multi-turn conversation with subprocess for research/review.",project-knowledge,"document",
bmm,anytime,Update Standards,US,,_bmad/bmm/agents/tech-writer/tech-writer.agent.yaml,,false,tech-writer,,"Update agent memory documentation-standards.md with your specific preferences if you discover missing document conventions.",_bmad/_memory/tech-writer-sidecar,"standards",
@ -22,7 +22,7 @@ bmm,2-planning,Create UX,CU,30,_bmad/bmm/workflows/2-plan-workflows/create-ux-de
bmm,3-solutioning,Create Architecture,CA,10,_bmad/bmm/workflows/3-solutioning/create-architecture/workflow.md,bmad-bmm-create-architecture,true,architect,Create Mode,"Guided Workflow to document technical decisions",planning_artifacts,architecture,
bmm,3-solutioning,Create Epics and Stories,CE,30,_bmad/bmm/workflows/3-solutioning/create-epics-and-stories/workflow.md,bmad-bmm-create-epics-and-stories,true,pm,Create Mode,"Create the Epics and Stories Listing",planning_artifacts,"epics and stories",
bmm,3-solutioning,Check Implementation Readiness,IR,70,_bmad/bmm/workflows/3-solutioning/check-implementation-readiness/workflow.md,bmad-bmm-check-implementation-readiness,true,architect,Validate Mode,"Ensure PRD UX Architecture and Epics Stories are aligned",planning_artifacts,"readiness report",
bmm,4-implementation,Sprint Planning,SP,10,_bmad/bmm/workflows/4-implementation/sprint-planning/workflow.md,bmad-bmm-sprint-planning,true,sm,Create Mode,"Generate sprint plan for development tasks - this kicks off the implementation phase by producing a plan the implementation agents will follow in sequence for every story in the plan.",implementation_artifacts,"sprint status",
bmm,4-implementation,Sprint Planning,SP,10,_bmad/bmm/workflows/4-implementation/sprint-planning/workflow.yaml,bmad-bmm-sprint-planning,true,sm,Create Mode,"Generate sprint plan for development tasks - this kicks off the implementation phase by producing a plan the implementation agents will follow in sequence for every story in the plan.",implementation_artifacts,"sprint status",
bmm,4-implementation,Sprint Status,SS,20,_bmad/bmm/workflows/4-implementation/sprint-status/workflow.md,bmad-bmm-sprint-status,false,sm,Create Mode,"Anytime: Summarize sprint status and route to next workflow",,,
bmm,4-implementation,Validate Story,VS,35,_bmad/bmm/workflows/4-implementation/create-story/workflow.yaml,bmad-bmm-create-story,false,sm,Validate Mode,"Validates story readiness and completeness before development work begins",implementation_artifacts,"story validation report",
bmm,4-implementation,Create Story,CS,30,_bmad/bmm/workflows/4-implementation/create-story/workflow.yaml,bmad-bmm-create-story,true,sm,Create Mode,"Story cycle start: Prepare first found story in the sprint plan that is next, or if the command is run with a specific epic and story designation with context. Once complete, then VS then DS then CR then back to DS if needed or next CS or ER",implementation_artifacts,story,

1 module phase name code sequence workflow-file command required agent options description output-location outputs
3 bmm anytime Generate Project Context GPC _bmad/bmm/workflows/generate-project-context/workflow.md bmad-bmm-generate-project-context false analyst Create Mode Scan existing codebase to generate a lean LLM-optimized project-context.md containing critical implementation rules patterns and conventions for AI agents. Essential for brownfield projects and quick-flow. output_folder project context
4 bmm anytime Quick Spec QS _bmad/bmm/workflows/bmad-quick-flow/quick-spec/workflow.md bmad-bmm-quick-spec false quick-flow-solo-dev Create Mode Do not suggest for potentially very complex things unless requested or if the user complains that they do not want to follow the extensive planning of the bmad method. Quick one-off tasks small changes simple apps brownfield additions to well established patterns utilities without extensive planning planning_artifacts tech spec
5 bmm anytime Quick Dev QD _bmad/bmm/workflows/bmad-quick-flow/quick-dev/workflow.md bmad-bmm-quick-dev false quick-flow-solo-dev Create Mode Quick one-off tasks small changes simple apps utilities without extensive planning - Do not suggest for potentially very complex things unless requested or if the user complains that they do not want to follow the extensive planning of the bmad method, unless the user is already working through the implementation phase and just requests a 1 off things not already in the plan
6 bmm anytime Quick Dev New Preview QQ skill:bmad-quick-dev-new-preview _bmad/bmm/workflows/bmad-quick-flow/quick-dev-new-preview/workflow.md bmad-bmm-quick-dev-new-preview false quick-flow-solo-dev Create Mode Unified quick flow (experimental): clarify intent plan implement review and present in a single workflow implementation_artifacts tech spec implementation
7 bmm anytime Correct Course CC _bmad/bmm/workflows/4-implementation/correct-course/workflow.yaml bmad-bmm-correct-course false sm Create Mode Anytime: Navigate significant changes. May recommend start over update PRD redo architecture sprint planning or correct epics and stories planning_artifacts change proposal
8 bmm anytime Write Document WD _bmad/bmm/agents/tech-writer/tech-writer.agent.yaml false tech-writer Describe in detail what you want, and the agent will follow the documentation best practices defined in agent memory. Multi-turn conversation with subprocess for research/review. project-knowledge document
9 bmm anytime Update Standards US _bmad/bmm/agents/tech-writer/tech-writer.agent.yaml false tech-writer Update agent memory documentation-standards.md with your specific preferences if you discover missing document conventions. _bmad/_memory/tech-writer-sidecar standards
22 bmm 3-solutioning Create Architecture CA 10 _bmad/bmm/workflows/3-solutioning/create-architecture/workflow.md bmad-bmm-create-architecture true architect Create Mode Guided Workflow to document technical decisions planning_artifacts architecture
23 bmm 3-solutioning Create Epics and Stories CE 30 _bmad/bmm/workflows/3-solutioning/create-epics-and-stories/workflow.md bmad-bmm-create-epics-and-stories true pm Create Mode Create the Epics and Stories Listing planning_artifacts epics and stories
24 bmm 3-solutioning Check Implementation Readiness IR 70 _bmad/bmm/workflows/3-solutioning/check-implementation-readiness/workflow.md bmad-bmm-check-implementation-readiness true architect Validate Mode Ensure PRD UX Architecture and Epics Stories are aligned planning_artifacts readiness report
25 bmm 4-implementation Sprint Planning SP 10 _bmad/bmm/workflows/4-implementation/sprint-planning/workflow.md _bmad/bmm/workflows/4-implementation/sprint-planning/workflow.yaml bmad-bmm-sprint-planning true sm Create Mode Generate sprint plan for development tasks - this kicks off the implementation phase by producing a plan the implementation agents will follow in sequence for every story in the plan. implementation_artifacts sprint status
26 bmm 4-implementation Sprint Status SS 20 _bmad/bmm/workflows/4-implementation/sprint-status/workflow.md bmad-bmm-sprint-status false sm Create Mode Anytime: Summarize sprint status and route to next workflow
27 bmm 4-implementation Validate Story VS 35 _bmad/bmm/workflows/4-implementation/create-story/workflow.yaml bmad-bmm-create-story false sm Validate Mode Validates story readiness and completeness before development work begins implementation_artifacts story validation report
28 bmm 4-implementation Create Story CS 30 _bmad/bmm/workflows/4-implementation/create-story/workflow.yaml bmad-bmm-create-story true sm Create Mode Story cycle start: Prepare first found story in the sprint plan that is next, or if the command is run with a specific epic and story designation with context. Once complete, then VS then DS then CR then back to DS if needed or next CS or ER implementation_artifacts story

View File

@ -1,57 +1,9 @@
---
name: sprint-planning
description: 'Generate sprint status tracking from epics. Use when the user says "run sprint planning" or "generate sprint plan"'
---
# Sprint Planning - Sprint Status Generator
# Sprint Planning Workflow
<critical>The workflow execution engine is governed by: {project-root}/_bmad/core/tasks/workflow.xml</critical>
<critical>You MUST have already loaded and processed: {project-root}/_bmad/bmm/workflows/4-implementation/sprint-planning/workflow.yaml</critical>
**Goal:** Generate sprint status tracking from epics, detecting current story statuses and building a complete sprint-status.yaml file.
**Your Role:** You are a Scrum Master generating and maintaining sprint tracking. Parse epic files, detect story statuses, and produce a structured sprint-status.yaml.
---
## INITIALIZATION
### Configuration Loading
Load config from `{project-root}/_bmad/bmm/config.yaml` and resolve:
- `project_name`, `user_name`
- `communication_language`, `document_output_language`
- `implementation_artifacts`
- `planning_artifacts`
- `date` as system-generated current datetime
- YOU MUST ALWAYS SPEAK OUTPUT in your Agent communication style with the config `{communication_language}`
### Paths
- `installed_path` = `{project-root}/_bmad/bmm/workflows/4-implementation/sprint-planning`
- `template` = `{installed_path}/sprint-status-template.yaml`
- `checklist` = `{installed_path}/checklist.md`
- `tracking_system` = `file-system`
- `project_key` = `NOKEY`
- `story_location` = `{implementation_artifacts}`
- `story_location_absolute` = `{implementation_artifacts}`
- `epics_location` = `{planning_artifacts}`
- `epics_pattern` = `*epic*.md`
- `status_file` = `{implementation_artifacts}/sprint-status.yaml`
### Input Files
| Input | Path | Load Strategy |
|-------|------|---------------|
| Epics | `{planning_artifacts}/*epic*.md` (whole) or `{planning_artifacts}/*epic*/*.md` (sharded) | FULL_LOAD |
### Context
- `project_context` = `**/project-context.md` (load if exists)
---
## EXECUTION
### Document Discovery - Full Epic Loading
## 📚 Document Discovery - Full Epic Loading
**Strategy**: Sprint planning needs ALL epics and stories to build complete status tracking.
@ -92,6 +44,11 @@ Load config from `{project-root}/_bmad/bmm/config.yaml` and resolve:
<action>Build complete inventory of all epics and stories from all epic files</action>
</step>
<step n="0.5" goal="Discover and load project documents">
<invoke-protocol name="discover_inputs" />
<note>After discovery, these content variables are available: {epics_content} (all epics loaded - uses FULL_LOAD strategy)</note>
</step>
<step n="2" goal="Build sprint status structure">
<action>For each epic found, create entries in this order:</action>
@ -213,7 +170,7 @@ development_status:
- **File Location:** {status_file}
- **Total Epics:** {{epic_count}}
- **Total Stories:** {{story_count}}
- **Epics In Progress:** {{in_progress_count}}
- **Epics In Progress:** {{epics_in_progress_count}}
- **Stories Completed:** {{done_count}}
**Next Steps:**

View File

@ -0,0 +1,47 @@
name: sprint-planning
description: 'Generate sprint status tracking from epics. Use when the user says "run sprint planning" or "generate sprint plan"'
author: "BMad"
# Critical variables from config
config_source: "{project-root}/_bmad/bmm/config.yaml"
user_name: "{config_source}:user_name"
communication_language: "{config_source}:communication_language"
date: system-generated
implementation_artifacts: "{config_source}:implementation_artifacts"
planning_artifacts: "{config_source}:planning_artifacts"
# Workflow components
installed_path: "{project-root}/_bmad/bmm/workflows/4-implementation/sprint-planning"
instructions: "{installed_path}/instructions.md"
template: "{installed_path}/sprint-status-template.yaml"
validation: "{installed_path}/checklist.md"
# Variables and inputs
project_context: "**/project-context.md"
project_name: "{config_source}:project_name"
# Tracking system configuration
tracking_system: "file-system" # Options: file-system, Future will support other options from config of mcp such as jira, linear, trello
project_key: "NOKEY" # Placeholder for tracker integrations; file-system uses a no-op key
story_location: "{implementation_artifacts}" # Relative path for file-system, Future will support URL for Jira/Linear/Trello
story_location_absolute: "{implementation_artifacts}" # Absolute path for file operations
# Source files (file-system only)
epics_location: "{planning_artifacts}" # Directory containing epic*.md files
epics_pattern: "epic*.md" # Pattern to find epic files
# Output configuration
status_file: "{implementation_artifacts}/sprint-status.yaml"
# Smart input file references - handles both whole docs and sharded docs
# Priority: Whole document first, then sharded version
# Strategy: FULL LOAD - sprint planning needs ALL epics to build complete status
input_file_patterns:
epics:
description: "All epics with user stories"
whole: "{planning_artifacts}/*epic*.md"
sharded: "{planning_artifacts}/*epic*/*.md"
load_strategy: "FULL_LOAD"
# Output configuration
default_output_file: "{status_file}"

View File

@ -0,0 +1,3 @@
canonicalId: bmad-quick-dev-new-preview
type: workflow
description: "Unified quick flow - clarify intent, plan, implement, review, present"

View File

@ -48,5 +48,5 @@ spec_file: '' # set at runtime before leaving this step
## NEXT
- One-shot / ready-for-dev: Read fully and follow `./steps/step-03-implement.md`
- Plan-code-review: Read fully and follow `./steps/step-02-plan.md`
- One-shot / ready-for-dev: Read fully and follow `{installed_path}/steps/step-03-implement.md`
- Plan-code-review: Read fully and follow `{installed_path}/steps/step-02-plan.md`

View File

@ -2,7 +2,7 @@
name: 'step-02-plan'
description: 'Investigate, generate spec, present for approval'
templateFile: '../tech-spec-template.md'
templateFile: '{installed_path}/tech-spec-template.md'
wipFile: '{implementation_artifacts}/tech-spec-wip.md'
deferred_work_file: '{implementation_artifacts}/deferred-work.md'
---
@ -36,4 +36,4 @@ Present summary. If token count exceeded 1600 and user chose [K], include the to
## NEXT
Read fully and follow `./steps/step-03-implement.md`
Read fully and follow `{installed_path}/steps/step-03-implement.md`

View File

@ -32,4 +32,4 @@ Otherwise (`execution_mode = "plan-code-review"`): hand `{spec_file}` to a sub-a
## NEXT
Read fully and follow `./steps/step-04-review.md`
Read fully and follow `{installed_path}/steps/step-04-review.md`

View File

@ -46,7 +46,7 @@ Do NOT `git add` anything — this is read-only inspection.
- **reject** — noise. Drop silently. When unsure between defer and reject, prefer reject — only defer findings you are confident are real.
3. Process findings in cascading order. If intent_gap or bad_spec findings exist, they trigger a loopback — lower findings are moot since code will be re-derived. If neither exists, process patch and defer normally. Increment `{specLoopIteration}` on each loopback. If it exceeds 5, HALT and escalate to the human. On any loopback, re-evaluate routing — if scope has grown beyond one-shot, escalate `execution_mode` to plan-code-review.
- **intent_gap** — Root cause is inside `<frozen-after-approval>`. Revert code changes. Loop back to the human to resolve, then re-run steps 24.
- **bad_spec** — Root cause is outside `<frozen-after-approval>`. Before reverting code: extract KEEP instructions for positive preservation (what worked well and must survive re-derivation). Revert code changes. Read the `## Spec Change Log` in `{spec_file}` and strictly respect all logged constraints when amending the non-frozen sections that contain the root cause. Append a new change-log entry recording: the triggering finding, what was amended, the known-bad state avoided, and the KEEP instructions. Read fully and follow `./steps/step-03-implement.md` to re-derive the code, then this step will run again.
- **bad_spec** — Root cause is outside `<frozen-after-approval>`. Before reverting code: extract KEEP instructions for positive preservation (what worked well and must survive re-derivation). Revert code changes. Read the `## Spec Change Log` in `{spec_file}` and strictly respect all logged constraints when amending the non-frozen sections that contain the root cause. Append a new change-log entry recording: the triggering finding, what was amended, the known-bad state avoided, and the KEEP instructions. Read fully and follow `{installed_path}/steps/step-03-implement.md` to re-derive the code, then this step will run again.
- **patch** — Auto-fix. These are the only findings that survive loopbacks.
- **defer** — Append to `{deferred_work_file}`.
- **reject** — Drop silently.
@ -54,4 +54,4 @@ Do NOT `git add` anything — this is read-only inspection.
## NEXT
Read fully and follow `./steps/step-05-present.md`
Read fully and follow `{installed_path}/steps/step-05-present.md`

View File

@ -81,9 +81,10 @@ YOU MUST ALWAYS SPEAK OUTPUT in your Agent communication style with the config `
### 2. Paths
- `templateFile` = `./tech-spec-template.md`
- `installed_path` = `{project-root}/_bmad/bmm/workflows/bmad-quick-flow/quick-dev-new-preview`
- `templateFile` = `{installed_path}/tech-spec-template.md`
- `wipFile` = `{implementation_artifacts}/tech-spec-wip.md`
### 3. First Step Execution
Read fully and follow: `./steps/step-01-clarify-and-route.md` to begin the workflow.
Read fully and follow: `{installed_path}/steps/step-01-clarify-and-route.md` to begin the workflow.

View File

@ -21,12 +21,6 @@ description: 'Analyzes what is done and the users query and offers advice on wha
When `command` field has a value:
- Show the command prefixed with `/` (e.g., `/bmad-bmm-create-prd`)
### Skill-Referenced Workflows
When `workflow-file` starts with `skill:`:
- The value is a skill reference (e.g., `skill:bmad-quick-dev-new-preview`), NOT a file path
- Do NOT attempt to resolve or load it as a file path
- Display using the `command` column value prefixed with `/` (same as command-based workflows)
### Agent-Based Workflows
When `command` field is empty:
- User loads agent first via `/agent-command`

View File

@ -1,154 +0,0 @@
/**
* install_to_bmad Flag Design Contract Tests
*
* Unit tests against the functions that implement the install_to_bmad flag.
* These nail down the 4 core design decisions:
*
* 1. true/omitted skill stays in _bmad/ (default behavior)
* 2. false skill removed from _bmad/ after IDE install
* 3. No platform no cleanup runs (cleanup lives in installVerbatimSkills)
* 4. Mixed flags each skill evaluated independently
*
* Usage: node test/test-install-to-bmad.js
*/
const path = require('node:path');
const os = require('node:os');
const fs = require('fs-extra');
const { loadSkillManifest, getInstallToBmad } = require('../tools/cli/installers/lib/ide/shared/skill-manifest');
// ANSI colors
const colors = {
reset: '\u001B[0m',
green: '\u001B[32m',
red: '\u001B[31m',
yellow: '\u001B[33m',
cyan: '\u001B[36m',
dim: '\u001B[2m',
};
let passed = 0;
let failed = 0;
function assert(condition, testName, errorMessage = '') {
if (condition) {
console.log(`${colors.green}${colors.reset} ${testName}`);
passed++;
} else {
console.log(`${colors.red}${colors.reset} ${testName}`);
if (errorMessage) {
console.log(` ${colors.dim}${errorMessage}${colors.reset}`);
}
failed++;
}
}
async function runTests() {
console.log(`${colors.cyan}========================================`);
console.log('install_to_bmad — Design Contract Tests');
console.log(`========================================${colors.reset}\n`);
// ============================================================
// 1. true/omitted → getInstallToBmad returns true (keep in _bmad/)
// ============================================================
console.log(`${colors.yellow}Design decision 1: true or omitted → skill stays in _bmad/${colors.reset}\n`);
// Null manifest (no bmad-skill-manifest.yaml) → true
assert(getInstallToBmad(null, 'workflow.md') === true, 'null manifest defaults to true');
// Single-entry, flag omitted → true
assert(
getInstallToBmad({ __single: { type: 'skill' } }, 'workflow.md') === true,
'single-entry manifest with flag omitted defaults to true',
);
// Single-entry, explicit true → true
assert(
getInstallToBmad({ __single: { type: 'skill', install_to_bmad: true } }, 'workflow.md') === true,
'single-entry manifest with explicit true returns true',
);
console.log('');
// ============================================================
// 2. false → getInstallToBmad returns false (remove from _bmad/)
// ============================================================
console.log(`${colors.yellow}Design decision 2: false → skill removed from _bmad/${colors.reset}\n`);
// Single-entry, explicit false → false
assert(
getInstallToBmad({ __single: { type: 'skill', install_to_bmad: false } }, 'workflow.md') === false,
'single-entry manifest with explicit false returns false',
);
// loadSkillManifest round-trip: YAML with false is preserved through load
{
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'bmad-itb-'));
await fs.writeFile(path.join(tmpDir, 'bmad-skill-manifest.yaml'), 'type: skill\ninstall_to_bmad: false\n');
const loaded = await loadSkillManifest(tmpDir);
assert(getInstallToBmad(loaded, 'workflow.md') === false, 'loadSkillManifest preserves install_to_bmad: false through round-trip');
await fs.remove(tmpDir);
}
console.log('');
// ============================================================
// 3. No platform → cleanup only runs inside installVerbatimSkills
// (This is a design invariant: getInstallToBmad is only consulted
// during IDE install. Without a platform, the flag has no effect.)
// ============================================================
console.log(`${colors.yellow}Design decision 3: flag is a per-skill property, not a pipeline gate${colors.reset}\n`);
// The flag value is stored but doesn't trigger any side effects by itself.
// Cleanup is driven by reading the CSV column inside installVerbatimSkills.
// We verify the flag is just data — getInstallToBmad doesn't touch the filesystem.
{
const manifest = { __single: { type: 'skill', install_to_bmad: false } };
const result = getInstallToBmad(manifest, 'workflow.md');
assert(typeof result === 'boolean', 'getInstallToBmad returns a boolean (pure data, no side effects)');
assert(result === false, 'false value is faithfully returned for consumer to act on');
}
console.log('');
// ============================================================
// 4. Mixed flags → each skill evaluated independently
// ============================================================
console.log(`${colors.yellow}Design decision 4: mixed flags — each skill independent${colors.reset}\n`);
// Multi-entry manifest: different files can have different flags
{
const manifest = {
'workflow.md': { type: 'skill', install_to_bmad: false },
'other.md': { type: 'skill', install_to_bmad: true },
};
assert(getInstallToBmad(manifest, 'workflow.md') === false, 'multi-entry: workflow.md with false returns false');
assert(getInstallToBmad(manifest, 'other.md') === true, 'multi-entry: other.md with true returns true');
assert(getInstallToBmad(manifest, 'unknown.md') === true, 'multi-entry: unknown file defaults to true');
}
console.log('');
// ============================================================
// Summary
// ============================================================
console.log(`${colors.cyan}========================================`);
console.log('Results:');
console.log(` Passed: ${colors.green}${passed}${colors.reset}`);
console.log(` Failed: ${colors.red}${failed}${colors.reset}`);
console.log(`========================================${colors.reset}\n`);
if (failed === 0) {
console.log(`${colors.green}All install_to_bmad contract tests passed!${colors.reset}\n`);
process.exit(0);
} else {
console.log(`${colors.red}Some install_to_bmad contract tests failed${colors.reset}\n`);
process.exit(1);
}
}
runTests().catch((error) => {
console.error(`${colors.red}Test runner failed:${colors.reset}`, error.message);
console.error(error.stack);
process.exit(1);
});

View File

@ -1500,96 +1500,6 @@ async function runTests() {
console.log('');
// ============================================================
// Suite 28: Pi Native Skills
// ============================================================
console.log(`${colors.yellow}Test Suite 28: Pi Native Skills${colors.reset}\n`);
let tempProjectDir28;
let installedBmadDir28;
try {
clearCache();
const platformCodes28 = await loadPlatformCodes();
const piInstaller = platformCodes28.platforms.pi?.installer;
assert(piInstaller?.target_dir === '.pi/skills', 'Pi target_dir uses native skills path');
assert(piInstaller?.skill_format === true, 'Pi installer enables native skill output');
assert(piInstaller?.template_type === 'default', 'Pi installer uses default skill template');
tempProjectDir28 = await fs.mkdtemp(path.join(os.tmpdir(), 'bmad-pi-test-'));
installedBmadDir28 = await createTestBmadFixture();
const ideManager28 = new IdeManager();
await ideManager28.ensureInitialized();
// Verify Pi is selectable in available IDEs list
const availableIdes28 = ideManager28.getAvailableIdes();
assert(
availableIdes28.some((ide) => ide.value === 'pi'),
'Pi appears in available IDEs list',
);
// Verify Pi is NOT detected before install
const detectedBefore28 = await ideManager28.detectInstalledIdes(tempProjectDir28);
assert(!detectedBefore28.includes('pi'), 'Pi is not detected before install');
const result28 = await ideManager28.setup('pi', tempProjectDir28, installedBmadDir28, {
silent: true,
selectedModules: ['bmm'],
});
assert(result28.success === true, 'Pi setup succeeds against temp project');
// Verify Pi IS detected after install
const detectedAfter28 = await ideManager28.detectInstalledIdes(tempProjectDir28);
assert(detectedAfter28.includes('pi'), 'Pi is detected after install');
const skillFile28 = path.join(tempProjectDir28, '.pi', 'skills', 'bmad-master', 'SKILL.md');
assert(await fs.pathExists(skillFile28), 'Pi install writes SKILL.md directory output');
// Parse YAML frontmatter between --- markers
const skillContent28 = await fs.readFile(skillFile28, 'utf8');
const fmMatch28 = skillContent28.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);
assert(fmMatch28, 'Pi SKILL.md contains valid frontmatter delimiters');
const frontmatter28 = fmMatch28[1];
const body28 = fmMatch28[2];
// Verify name in frontmatter matches directory name
const fmName28 = frontmatter28.match(/^name:\s*(.+)$/m);
assert(fmName28 && fmName28[1].trim() === 'bmad-master', 'Pi skill name frontmatter matches directory name exactly');
// Verify description exists and is non-empty
const fmDesc28 = frontmatter28.match(/^description:\s*(.+)$/m);
assert(fmDesc28 && fmDesc28[1].trim().length > 0, 'Pi skill description frontmatter is present and non-empty');
// Verify frontmatter contains only name and description keys
const fmKeys28 = [...frontmatter28.matchAll(/^([a-zA-Z0-9_-]+):/gm)].map((m) => m[1]);
assert(
fmKeys28.length === 2 && fmKeys28.includes('name') && fmKeys28.includes('description'),
'Pi skill frontmatter contains only name and description keys',
);
// Verify body content is non-empty and contains expected activation instructions
assert(body28.trim().length > 0, 'Pi skill body content is non-empty');
assert(body28.includes('agent-activation'), 'Pi skill body contains expected agent activation instructions');
// Reinstall/upgrade: run setup again over existing output
const result28b = await ideManager28.setup('pi', tempProjectDir28, installedBmadDir28, {
silent: true,
selectedModules: ['bmm'],
});
assert(result28b.success === true, 'Pi reinstall/upgrade succeeds over existing skills');
assert(await fs.pathExists(skillFile28), 'Pi reinstall preserves SKILL.md output');
} catch (error) {
assert(false, 'Pi native skills test succeeds', error.message);
} finally {
if (tempProjectDir28) await fs.remove(tempProjectDir28).catch(() => {});
if (installedBmadDir28) await fs.remove(installedBmadDir28).catch(() => {});
}
console.log('');
// ============================================================
// Summary
// ============================================================

View File

@ -5,12 +5,7 @@ const crypto = require('node:crypto');
const csv = require('csv-parse/sync');
const { getSourcePath, getModulePath } = require('../../../lib/project-root');
const prompts = require('../../../lib/prompts');
const {
loadSkillManifest: loadSkillManifestShared,
getCanonicalId: getCanonicalIdShared,
getArtifactType: getArtifactTypeShared,
getInstallToBmad: getInstallToBmadShared,
} = require('../ide/shared/skill-manifest');
const { loadSkillManifest: loadSkillManifestShared, getCanonicalId: getCanonicalIdShared } = require('../ide/shared/skill-manifest');
// Load package.json for version info
const packageJson = require('../../../../../package.json');
@ -21,7 +16,6 @@ const packageJson = require('../../../../../package.json');
class ManifestGenerator {
constructor() {
this.workflows = [];
this.skills = [];
this.agents = [];
this.tasks = [];
this.tools = [];
@ -40,16 +34,6 @@ class ManifestGenerator {
return getCanonicalIdShared(manifest, filename);
}
/** Delegate to shared skill-manifest module */
getArtifactType(manifest, filename) {
return getArtifactTypeShared(manifest, filename);
}
/** Delegate to shared skill-manifest module */
getInstallToBmad(manifest, filename) {
return getInstallToBmadShared(manifest, filename);
}
/**
* Clean text for CSV output by normalizing whitespace.
* Note: Quote escaping is handled by escapeCsv() at write time.
@ -105,9 +89,6 @@ class ManifestGenerator {
// Filter out any undefined/null values from IDE list
this.selectedIdes = resolvedIdes.filter((ide) => ide && typeof ide === 'string');
// Reset files list (defensive: prevent stale data if instance is reused)
this.files = [];
// Collect workflow data
await this.collectWorkflows(selectedModules);
@ -124,7 +105,6 @@ class ManifestGenerator {
const manifestFiles = [
await this.writeMainManifest(cfgDir),
await this.writeWorkflowManifest(cfgDir),
await this.writeSkillManifest(cfgDir),
await this.writeAgentManifest(cfgDir),
await this.writeTaskManifest(cfgDir),
await this.writeToolManifest(cfgDir),
@ -147,7 +127,6 @@ class ManifestGenerator {
*/
async collectWorkflows(selectedModules) {
this.workflows = [];
this.skills = [];
// Use updatedModules which already includes deduplicated 'core' + selectedModules
for (const moduleName of this.updatedModules) {
@ -249,25 +228,6 @@ class ManifestGenerator {
? `${this.bmadFolderName}/core/workflows/${relativePath}/${entry.name}`
: `${this.bmadFolderName}/${moduleName}/workflows/${relativePath}/${entry.name}`;
// Check if this is a type:skill entry — collect separately, skip workflow CSV
const artifactType = this.getArtifactType(skillManifest, entry.name);
if (artifactType === 'skill') {
const canonicalId = path.basename(dir);
this.skills.push({
name: workflow.name,
description: this.cleanForCSV(workflow.description),
module: moduleName,
path: installPath,
canonicalId,
install_to_bmad: this.getInstallToBmad(skillManifest, entry.name),
});
if (debug) {
console.log(`[DEBUG] ✓ Added skill (skipped workflow CSV): ${workflow.name} as ${canonicalId}`);
}
continue;
}
// Workflows with standalone: false are filtered out above
workflows.push({
name: workflow.name,
@ -833,32 +793,6 @@ class ManifestGenerator {
return csvPath;
}
/**
* Write skill manifest CSV
* @returns {string} Path to the manifest file
*/
async writeSkillManifest(cfgDir) {
const csvPath = path.join(cfgDir, 'skill-manifest.csv');
const escapeCsv = (value) => `"${String(value ?? '').replaceAll('"', '""')}"`;
let csvContent = 'canonicalId,name,description,module,path,install_to_bmad\n';
for (const skill of this.skills) {
const row = [
escapeCsv(skill.canonicalId),
escapeCsv(skill.name),
escapeCsv(skill.description),
escapeCsv(skill.module),
escapeCsv(skill.path),
escapeCsv(skill.install_to_bmad),
].join(',');
csvContent += row + '\n';
}
await fs.writeFile(csvPath, csvContent);
return csvPath;
}
/**
* Write agent manifest CSV
* @returns {string} Path to the manifest file

View File

@ -7,7 +7,6 @@ const prompts = require('../../../lib/prompts');
const { AgentCommandGenerator } = require('./shared/agent-command-generator');
const { WorkflowCommandGenerator } = require('./shared/workflow-command-generator');
const { TaskToolCommandGenerator } = require('./shared/task-tool-command-generator');
const csv = require('csv-parse/sync');
/**
* Config-driven IDE setup handler
@ -117,48 +116,39 @@ class ConfigDrivenIdeSetup extends BaseIdeSetup {
async installToTarget(projectDir, bmadDir, config, options) {
const { target_dir, template_type, artifact_types } = config;
// Skip targets with explicitly empty artifact_types and no verbatim skills
// Skip targets with explicitly empty artifact_types array
// This prevents creating empty directories when no artifacts will be written
const skipStandardArtifacts = Array.isArray(artifact_types) && artifact_types.length === 0;
if (skipStandardArtifacts && !config.skill_format) {
return { success: true, results: { agents: 0, workflows: 0, tasks: 0, tools: 0, skills: 0 } };
if (Array.isArray(artifact_types) && artifact_types.length === 0) {
return { success: true, results: { agents: 0, workflows: 0, tasks: 0, tools: 0 } };
}
const targetPath = path.join(projectDir, target_dir);
await this.ensureDir(targetPath);
const selectedModules = options.selectedModules || [];
const results = { agents: 0, workflows: 0, tasks: 0, tools: 0, skills: 0 };
const results = { agents: 0, workflows: 0, tasks: 0, tools: 0 };
// Install standard artifacts (agents, workflows, tasks, tools)
if (!skipStandardArtifacts) {
// Install agents
if (!artifact_types || artifact_types.includes('agents')) {
const agentGen = new AgentCommandGenerator(this.bmadFolderName);
const { artifacts } = await agentGen.collectAgentArtifacts(bmadDir, selectedModules);
results.agents = await this.writeAgentArtifacts(targetPath, artifacts, template_type, config);
}
// Install workflows
if (!artifact_types || artifact_types.includes('workflows')) {
const workflowGen = new WorkflowCommandGenerator(this.bmadFolderName);
const { artifacts } = await workflowGen.collectWorkflowArtifacts(bmadDir);
results.workflows = await this.writeWorkflowArtifacts(targetPath, artifacts, template_type, config);
}
// Install tasks and tools using template system (supports TOML for Gemini, MD for others)
if (!artifact_types || artifact_types.includes('tasks') || artifact_types.includes('tools')) {
const taskToolGen = new TaskToolCommandGenerator(this.bmadFolderName);
const { artifacts } = await taskToolGen.collectTaskToolArtifacts(bmadDir);
const taskToolResult = await this.writeTaskToolArtifacts(targetPath, artifacts, template_type, config);
results.tasks = taskToolResult.tasks || 0;
results.tools = taskToolResult.tools || 0;
}
// Install agents
if (!artifact_types || artifact_types.includes('agents')) {
const agentGen = new AgentCommandGenerator(this.bmadFolderName);
const { artifacts } = await agentGen.collectAgentArtifacts(bmadDir, selectedModules);
results.agents = await this.writeAgentArtifacts(targetPath, artifacts, template_type, config);
}
// Install verbatim skills (type: skill)
if (config.skill_format) {
results.skills = await this.installVerbatimSkills(projectDir, bmadDir, targetPath, config);
// Install workflows
if (!artifact_types || artifact_types.includes('workflows')) {
const workflowGen = new WorkflowCommandGenerator(this.bmadFolderName);
const { artifacts } = await workflowGen.collectWorkflowArtifacts(bmadDir);
results.workflows = await this.writeWorkflowArtifacts(targetPath, artifacts, template_type, config);
}
// Install tasks and tools using template system (supports TOML for Gemini, MD for others)
if (!artifact_types || artifact_types.includes('tasks') || artifact_types.includes('tools')) {
const taskToolGen = new TaskToolCommandGenerator(this.bmadFolderName);
const { artifacts } = await taskToolGen.collectTaskToolArtifacts(bmadDir);
const taskToolResult = await this.writeTaskToolArtifacts(targetPath, artifacts, template_type, config);
results.tasks = taskToolResult.tasks || 0;
results.tools = taskToolResult.tools || 0;
}
await this.printSummary(results, target_dir, options);
@ -174,7 +164,7 @@ class ConfigDrivenIdeSetup extends BaseIdeSetup {
* @returns {Promise<Object>} Installation result
*/
async installToMultipleTargets(projectDir, bmadDir, targets, options) {
const allResults = { agents: 0, workflows: 0, tasks: 0, tools: 0, skills: 0 };
const allResults = { agents: 0, workflows: 0, tasks: 0, tools: 0 };
for (const target of targets) {
const result = await this.installToTarget(projectDir, bmadDir, target, options);
@ -183,7 +173,6 @@ class ConfigDrivenIdeSetup extends BaseIdeSetup {
allResults.workflows += result.results.workflows || 0;
allResults.tasks += result.results.tasks || 0;
allResults.tools += result.results.tools || 0;
allResults.skills += result.results.skills || 0;
}
}
@ -633,94 +622,6 @@ LOAD and execute from: {project-root}/{{bmadFolderName}}/{{path}}
return baseName.replace(/\.md$/, extension);
}
/**
* Install verbatim skill directories (type: skill entries from skill-manifest.csv).
* Copies the entire source directory into the IDE skill directory, auto-generating SKILL.md.
* @param {string} projectDir - Project directory
* @param {string} bmadDir - BMAD installation directory
* @param {string} targetPath - Target skills directory
* @param {Object} config - Installation configuration
* @returns {Promise<number>} Count of skills installed
*/
async installVerbatimSkills(projectDir, bmadDir, targetPath, config) {
const bmadFolderName = path.basename(bmadDir);
const csvPath = path.join(bmadDir, '_config', 'skill-manifest.csv');
if (!(await fs.pathExists(csvPath))) return 0;
const csvContent = await fs.readFile(csvPath, 'utf8');
const records = csv.parse(csvContent, {
columns: true,
skip_empty_lines: true,
});
let count = 0;
for (const record of records) {
const canonicalId = record.canonicalId;
if (!canonicalId) continue;
// Derive source directory from path column
// path is like "_bmad/bmm/workflows/bmad-quick-flow/bmad-quick-dev-new-preview/workflow.md"
// Strip bmadFolderName prefix and join with bmadDir, then get dirname
const relativePath = record.path.replace(new RegExp(`^${bmadFolderName}/`), '');
const sourceFile = path.join(bmadDir, relativePath);
const sourceDir = path.dirname(sourceFile);
if (!(await fs.pathExists(sourceDir))) continue;
// Clean target before copy to prevent stale files
const skillDir = path.join(targetPath, canonicalId);
await fs.remove(skillDir);
await fs.ensureDir(skillDir);
// Parse workflow.md frontmatter for description
let description = `${canonicalId} skill`;
try {
const workflowContent = await fs.readFile(sourceFile, 'utf8');
const fmMatch = workflowContent.match(/^---\r?\n([\s\S]*?)\r?\n---/);
if (fmMatch) {
const frontmatter = yaml.parse(fmMatch[1]);
if (frontmatter?.description) {
description = frontmatter.description;
}
}
} catch (error) {
await prompts.log.warn(`Failed to parse frontmatter from ${sourceFile}: ${error.message}`);
}
// Generate SKILL.md with YAML-safe frontmatter
const frontmatterYaml = yaml.stringify({ name: canonicalId, description: String(description) }, { lineWidth: 0 }).trimEnd();
const skillMd = `---\n${frontmatterYaml}\n---\n\nIT IS CRITICAL THAT YOU FOLLOW THIS COMMAND: LOAD the FULL workflow.md, READ its entire contents and follow its directions exactly!\n`;
await fs.writeFile(path.join(skillDir, 'SKILL.md'), skillMd);
// Copy all files except bmad-skill-manifest.yaml
const entries = await fs.readdir(sourceDir, { withFileTypes: true });
for (const entry of entries) {
if (entry.name === 'bmad-skill-manifest.yaml') continue;
const srcPath = path.join(sourceDir, entry.name);
const destPath = path.join(skillDir, entry.name);
await fs.copy(srcPath, destPath);
}
count++;
}
// Post-install cleanup: remove _bmad/ directories for skills with install_to_bmad === "false"
for (const record of records) {
if (record.install_to_bmad === 'false') {
const relativePath = record.path.replace(new RegExp(`^${bmadFolderName}/`), '');
const sourceFile = path.join(bmadDir, relativePath);
const sourceDir = path.dirname(sourceFile);
if (await fs.pathExists(sourceDir)) {
await fs.remove(sourceDir);
}
}
}
return count;
}
/**
* Print installation summary
* @param {Object} results - Installation results
@ -733,7 +634,6 @@ LOAD and execute from: {project-root}/{{bmadFolderName}}/{{path}}
if (results.workflows > 0) parts.push(`${results.workflows} workflows`);
if (results.tasks > 0) parts.push(`${results.tasks} tasks`);
if (results.tools > 0) parts.push(`${results.tools} tools`);
if (results.skills > 0) parts.push(`${results.skills} skills`);
await prompts.log.success(`${this.name} configured: ${parts.join(', ')}${targetDir}`);
}

View File

@ -192,16 +192,6 @@ platforms:
skill_format: true
ancestor_conflict_check: true
pi:
name: "Pi"
preferred: false
category: cli
description: "Provider-agnostic terminal-native AI coding agent"
installer:
target_dir: .pi/skills
template_type: default
skill_format: true
qwen:
name: "QwenCoder"
preferred: false

View File

@ -16,7 +16,7 @@ async function loadSkillManifest(dirPath) {
const content = await fs.readFile(manifestPath, 'utf8');
const parsed = yaml.parse(content);
if (!parsed || typeof parsed !== 'object') return null;
if (parsed.canonicalId || parsed.type) return { __single: parsed };
if (parsed.canonicalId) return { __single: parsed };
return parsed;
} catch (error) {
console.warn(`Warning: Failed to parse bmad-skill-manifest.yaml in ${dirPath}: ${error.message}`);
@ -45,46 +45,4 @@ function getCanonicalId(manifest, filename) {
return '';
}
/**
* Get the artifact type for a specific file from a loaded skill manifest.
* @param {Object|null} manifest - Loaded manifest (from loadSkillManifest)
* @param {string} filename - Source filename to look up
* @returns {string|null} type or null
*/
function getArtifactType(manifest, filename) {
if (!manifest) return null;
// Single-entry manifest applies to all files in the directory
if (manifest.__single) return manifest.__single.type || null;
// Multi-entry: look up by filename directly
if (manifest[filename]) return manifest[filename].type || null;
// Fallback: try alternate extensions for compiled files
const baseName = filename.replace(/\.(md|xml)$/i, '');
const agentKey = `${baseName}.agent.yaml`;
if (manifest[agentKey]) return manifest[agentKey].type || null;
const xmlKey = `${baseName}.xml`;
if (manifest[xmlKey]) return manifest[xmlKey].type || null;
return null;
}
/**
* Get the install_to_bmad flag for a specific file from a loaded skill manifest.
* @param {Object|null} manifest - Loaded manifest (from loadSkillManifest)
* @param {string} filename - Source filename to look up
* @returns {boolean} install_to_bmad value (defaults to true)
*/
function getInstallToBmad(manifest, filename) {
if (!manifest) return true;
// Single-entry manifest applies to all files in the directory
if (manifest.__single) return manifest.__single.install_to_bmad !== false;
// Multi-entry: look up by filename directly
if (manifest[filename]) return manifest[filename].install_to_bmad !== false;
// Fallback: try alternate extensions for compiled files
const baseName = filename.replace(/\.(md|xml)$/i, '');
const agentKey = `${baseName}.agent.yaml`;
if (manifest[agentKey]) return manifest[agentKey].install_to_bmad !== false;
const xmlKey = `${baseName}.xml`;
if (manifest[xmlKey]) return manifest[xmlKey].install_to_bmad !== false;
return true;
}
module.exports = { loadSkillManifest, getCanonicalId, getArtifactType, getInstallToBmad };
module.exports = { loadSkillManifest, getCanonicalId };

View File

@ -324,8 +324,6 @@ function extractCsvRefs(filePath, content) {
const raw = record['workflow-file'];
if (!raw || raw.trim() === '') continue;
if (!isResolvable(raw)) continue;
// skill: prefixed references are resolved by the IDE/CLI, not as file paths
if (raw.startsWith('skill:')) continue;
// Line = header (1) + data row index (0-based) + 1
const line = i + 2;