Compare commits

..

3 Commits

Author SHA1 Message Date
Alex Verkhovsky dc8293e5df
chore(sprint-planning): convert workflow.yaml to unified workflow.md (#1856)
* chore(sprint-planning): convert workflow.yaml + instructions.md to unified workflow.md

Step 4 of 9 in the yaml-to-md conversion plan. Merges config
and instructions into a single self-contained workflow.md,
dropping engine scaffolding (critical tags, invoke-protocol).

* fix(sprint-planning): address review findings from PR triage

- Update stale workflow.yaml references to workflow.md in sm.agent.yaml and module-help.csv
- Widen epics_pattern from epic*.md to *epic*.md to match documented input files
- Fix placeholder mismatch: epics_in_progress_count → in_progress_count

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 04:51:50 -06:00
Alex Verkhovsky 7f1a55ca8c
feat(skills): add type:skill manifest for verbatim directory copying (#1851)
* feat(skills): add type:skill manifest for verbatim skill directory copying

Introduce `type: skill` in bmad-skill-manifest.yaml to signal the
installer to copy entire skill directories verbatim into IDE skill
directories, replacing the launcher-based approach.

Changes:
- skill-manifest.js: fix single-entry detection for type-only manifests,
  add getArtifactType export
- manifest-generator.js: collect type:skill entries separately, write
  skill-manifest.csv, derive canonicalId from directory name
- _config-driven.js: add installVerbatimSkills with YAML-safe SKILL.md
  generation, stale file cleanup, and warning on parse failures
- Rename quick-dev-new-preview to bmad-quick-dev-new-preview so
  directory name is the canonical ID
- Update workflow.md installed_path to reference IDE skill base directory

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: replace {installed_path} with relative paths in quick-dev skill

Skills resolve paths relative to the skill root directory per the
open agent standard, so the installed_path variable is unnecessary.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(skills): add install_to_bmad flag and skill: help catalog reference

Add install_to_bmad flag to skill manifests (default true) enabling
skills to opt out of _bmad/ copy while retaining .claude/skills/
installation. Support skill:<canonicalId> references in module-help.csv
workflow-file column. Fix stale quick-dev-new-preview directory
references in agent YAML and help catalog.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* test: add install_to_bmad design contract tests

Unit tests against getInstallToBmad and loadSkillManifest that nail
down the 4 core design decisions for the install_to_bmad flag.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: reset skills array between runs and allow skill-only targets

- Reset this.skills and this.files in ManifestGenerator to prevent stale
  data when instance is reused across multiple manifest runs
- Allow targets with empty artifact_types to still install verbatim
  skills by checking skill_format before short-circuiting

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: resolve broken file references in quick-dev-new-preview workflow

- Fix step-02-plan.md templateFile path (./tech-spec-template.md → ../tech-spec-template.md)
- Teach validate-file-refs.js to skip skill: prefixed references in CSV

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 01:23:26 -07:00
Gani Mohamed Parakadhullah 8e5898e862
feat: add pi coding agent as supported platform (#1854)
* feat: add pi coding agent as supported platform

Add pi (provider-agnostic terminal-native AI coding agent) to
platform-codes.yaml with native skills format output to .pi/skills/.

Pi follows the open Agent Skills specification and uses the same
subdirectory/SKILL.md structure that BMAD already generates for
other platforms.

Fixes #1853

* fix: address PR review comments for Pi test suite

- Assert template_type === 'default' to pin config contract
- Verify Pi appears in getAvailableIdes() list
- Test detect() returns false before install, true after
- Parse frontmatter between --- delimiters instead of regex on full file
- Assert description is present and non-empty
- Assert frontmatter contains only name and description keys
- Validate body content is non-empty with expected activation instructions
- Add reinstall/upgrade coverage (rerun setup over existing output)
- Move temp directory cleanup to finally block
2026-03-08 00:51:26 -06:00
22 changed files with 565 additions and 102 deletions

View File

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

View File

@ -20,7 +20,7 @@ agent:
menu: menu:
- trigger: SP or fuzzy match on sprint-planning - trigger: SP or fuzzy match on sprint-planning
workflow: "{project-root}/_bmad/bmm/workflows/4-implementation/sprint-planning/workflow.yaml" workflow: "{project-root}/_bmad/bmm/workflows/4-implementation/sprint-planning/workflow.md"
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" 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 - 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,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 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,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,,_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,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,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,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,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", 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 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,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,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.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 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 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,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,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, 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 _bmad/bmm/workflows/bmad-quick-flow/quick-dev-new-preview/workflow.md 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
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.yaml _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
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,9 +1,57 @@
# Sprint Planning - Sprint Status Generator ---
name: sprint-planning
description: 'Generate sprint status tracking from epics. Use when the user says "run sprint planning" or "generate sprint plan"'
---
<critical>The workflow execution engine is governed by: {project-root}/_bmad/core/tasks/workflow.xml</critical> # Sprint Planning Workflow
<critical>You MUST have already loaded and processed: {project-root}/_bmad/bmm/workflows/4-implementation/sprint-planning/workflow.yaml</critical>
## 📚 Document Discovery - Full Epic Loading **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
**Strategy**: Sprint planning needs ALL epics and stories to build complete status tracking. **Strategy**: Sprint planning needs ALL epics and stories to build complete status tracking.
@ -44,11 +92,6 @@
<action>Build complete inventory of all epics and stories from all epic files</action> <action>Build complete inventory of all epics and stories from all epic files</action>
</step> </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"> <step n="2" goal="Build sprint status structure">
<action>For each epic found, create entries in this order:</action> <action>For each epic found, create entries in this order:</action>
@ -170,7 +213,7 @@ development_status:
- **File Location:** {status_file} - **File Location:** {status_file}
- **Total Epics:** {{epic_count}} - **Total Epics:** {{epic_count}}
- **Total Stories:** {{story_count}} - **Total Stories:** {{story_count}}
- **Epics In Progress:** {{epics_in_progress_count}} - **Epics In Progress:** {{in_progress_count}}
- **Stories Completed:** {{done_count}} - **Stories Completed:** {{done_count}}
**Next Steps:** **Next Steps:**

View File

@ -1,47 +0,0 @@
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

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

View File

@ -2,7 +2,7 @@
name: 'step-02-plan' name: 'step-02-plan'
description: 'Investigate, generate spec, present for approval' description: 'Investigate, generate spec, present for approval'
templateFile: '{installed_path}/tech-spec-template.md' templateFile: '../tech-spec-template.md'
wipFile: '{implementation_artifacts}/tech-spec-wip.md' wipFile: '{implementation_artifacts}/tech-spec-wip.md'
deferred_work_file: '{implementation_artifacts}/deferred-work.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 ## NEXT
Read fully and follow `{installed_path}/steps/step-03-implement.md` Read fully and follow `./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 ## NEXT
Read fully and follow `{installed_path}/steps/step-04-review.md` Read fully and follow `./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. - **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. 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. - **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 `{installed_path}/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 `./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. - **patch** — Auto-fix. These are the only findings that survive loopbacks.
- **defer** — Append to `{deferred_work_file}`. - **defer** — Append to `{deferred_work_file}`.
- **reject** — Drop silently. - **reject** — Drop silently.
@ -54,4 +54,4 @@ Do NOT `git add` anything — this is read-only inspection.
## NEXT ## NEXT
Read fully and follow `{installed_path}/steps/step-05-present.md` Read fully and follow `./steps/step-05-present.md`

View File

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

View File

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

View File

@ -21,6 +21,12 @@ description: 'Analyzes what is done and the users query and offers advice on wha
When `command` field has a value: When `command` field has a value:
- Show the command prefixed with `/` (e.g., `/bmad-bmm-create-prd`) - 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 ### Agent-Based Workflows
When `command` field is empty: When `command` field is empty:
- User loads agent first via `/agent-command` - User loads agent first via `/agent-command`

View File

@ -0,0 +1,154 @@
/**
* 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,6 +1500,96 @@ async function runTests() {
console.log(''); 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 // Summary
// ============================================================ // ============================================================

View File

@ -5,7 +5,12 @@ const crypto = require('node:crypto');
const csv = require('csv-parse/sync'); const csv = require('csv-parse/sync');
const { getSourcePath, getModulePath } = require('../../../lib/project-root'); const { getSourcePath, getModulePath } = require('../../../lib/project-root');
const prompts = require('../../../lib/prompts'); const prompts = require('../../../lib/prompts');
const { loadSkillManifest: loadSkillManifestShared, getCanonicalId: getCanonicalIdShared } = require('../ide/shared/skill-manifest'); const {
loadSkillManifest: loadSkillManifestShared,
getCanonicalId: getCanonicalIdShared,
getArtifactType: getArtifactTypeShared,
getInstallToBmad: getInstallToBmadShared,
} = require('../ide/shared/skill-manifest');
// Load package.json for version info // Load package.json for version info
const packageJson = require('../../../../../package.json'); const packageJson = require('../../../../../package.json');
@ -16,6 +21,7 @@ const packageJson = require('../../../../../package.json');
class ManifestGenerator { class ManifestGenerator {
constructor() { constructor() {
this.workflows = []; this.workflows = [];
this.skills = [];
this.agents = []; this.agents = [];
this.tasks = []; this.tasks = [];
this.tools = []; this.tools = [];
@ -34,6 +40,16 @@ class ManifestGenerator {
return getCanonicalIdShared(manifest, filename); 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. * Clean text for CSV output by normalizing whitespace.
* Note: Quote escaping is handled by escapeCsv() at write time. * Note: Quote escaping is handled by escapeCsv() at write time.
@ -89,6 +105,9 @@ class ManifestGenerator {
// Filter out any undefined/null values from IDE list // Filter out any undefined/null values from IDE list
this.selectedIdes = resolvedIdes.filter((ide) => ide && typeof ide === 'string'); 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 // Collect workflow data
await this.collectWorkflows(selectedModules); await this.collectWorkflows(selectedModules);
@ -105,6 +124,7 @@ class ManifestGenerator {
const manifestFiles = [ const manifestFiles = [
await this.writeMainManifest(cfgDir), await this.writeMainManifest(cfgDir),
await this.writeWorkflowManifest(cfgDir), await this.writeWorkflowManifest(cfgDir),
await this.writeSkillManifest(cfgDir),
await this.writeAgentManifest(cfgDir), await this.writeAgentManifest(cfgDir),
await this.writeTaskManifest(cfgDir), await this.writeTaskManifest(cfgDir),
await this.writeToolManifest(cfgDir), await this.writeToolManifest(cfgDir),
@ -127,6 +147,7 @@ class ManifestGenerator {
*/ */
async collectWorkflows(selectedModules) { async collectWorkflows(selectedModules) {
this.workflows = []; this.workflows = [];
this.skills = [];
// Use updatedModules which already includes deduplicated 'core' + selectedModules // Use updatedModules which already includes deduplicated 'core' + selectedModules
for (const moduleName of this.updatedModules) { for (const moduleName of this.updatedModules) {
@ -228,6 +249,25 @@ class ManifestGenerator {
? `${this.bmadFolderName}/core/workflows/${relativePath}/${entry.name}` ? `${this.bmadFolderName}/core/workflows/${relativePath}/${entry.name}`
: `${this.bmadFolderName}/${moduleName}/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 with standalone: false are filtered out above
workflows.push({ workflows.push({
name: workflow.name, name: workflow.name,
@ -793,6 +833,32 @@ class ManifestGenerator {
return csvPath; 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 * Write agent manifest CSV
* @returns {string} Path to the manifest file * @returns {string} Path to the manifest file

View File

@ -7,6 +7,7 @@ const prompts = require('../../../lib/prompts');
const { AgentCommandGenerator } = require('./shared/agent-command-generator'); const { AgentCommandGenerator } = require('./shared/agent-command-generator');
const { WorkflowCommandGenerator } = require('./shared/workflow-command-generator'); const { WorkflowCommandGenerator } = require('./shared/workflow-command-generator');
const { TaskToolCommandGenerator } = require('./shared/task-tool-command-generator'); const { TaskToolCommandGenerator } = require('./shared/task-tool-command-generator');
const csv = require('csv-parse/sync');
/** /**
* Config-driven IDE setup handler * Config-driven IDE setup handler
@ -116,39 +117,48 @@ class ConfigDrivenIdeSetup extends BaseIdeSetup {
async installToTarget(projectDir, bmadDir, config, options) { async installToTarget(projectDir, bmadDir, config, options) {
const { target_dir, template_type, artifact_types } = config; const { target_dir, template_type, artifact_types } = config;
// Skip targets with explicitly empty artifact_types array // Skip targets with explicitly empty artifact_types and no verbatim skills
// This prevents creating empty directories when no artifacts will be written // This prevents creating empty directories when no artifacts will be written
if (Array.isArray(artifact_types) && artifact_types.length === 0) { const skipStandardArtifacts = Array.isArray(artifact_types) && artifact_types.length === 0;
return { success: true, results: { agents: 0, workflows: 0, tasks: 0, tools: 0 } }; if (skipStandardArtifacts && !config.skill_format) {
return { success: true, results: { agents: 0, workflows: 0, tasks: 0, tools: 0, skills: 0 } };
} }
const targetPath = path.join(projectDir, target_dir); const targetPath = path.join(projectDir, target_dir);
await this.ensureDir(targetPath); await this.ensureDir(targetPath);
const selectedModules = options.selectedModules || []; const selectedModules = options.selectedModules || [];
const results = { agents: 0, workflows: 0, tasks: 0, tools: 0 }; const results = { agents: 0, workflows: 0, tasks: 0, tools: 0, skills: 0 };
// Install agents // Install standard artifacts (agents, workflows, tasks, tools)
if (!artifact_types || artifact_types.includes('agents')) { if (!skipStandardArtifacts) {
const agentGen = new AgentCommandGenerator(this.bmadFolderName); // Install agents
const { artifacts } = await agentGen.collectAgentArtifacts(bmadDir, selectedModules); if (!artifact_types || artifact_types.includes('agents')) {
results.agents = await this.writeAgentArtifacts(targetPath, artifacts, template_type, config); 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 workflows // Install verbatim skills (type: skill)
if (!artifact_types || artifact_types.includes('workflows')) { if (config.skill_format) {
const workflowGen = new WorkflowCommandGenerator(this.bmadFolderName); results.skills = await this.installVerbatimSkills(projectDir, bmadDir, targetPath, config);
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); await this.printSummary(results, target_dir, options);
@ -164,7 +174,7 @@ class ConfigDrivenIdeSetup extends BaseIdeSetup {
* @returns {Promise<Object>} Installation result * @returns {Promise<Object>} Installation result
*/ */
async installToMultipleTargets(projectDir, bmadDir, targets, options) { async installToMultipleTargets(projectDir, bmadDir, targets, options) {
const allResults = { agents: 0, workflows: 0, tasks: 0, tools: 0 }; const allResults = { agents: 0, workflows: 0, tasks: 0, tools: 0, skills: 0 };
for (const target of targets) { for (const target of targets) {
const result = await this.installToTarget(projectDir, bmadDir, target, options); const result = await this.installToTarget(projectDir, bmadDir, target, options);
@ -173,6 +183,7 @@ class ConfigDrivenIdeSetup extends BaseIdeSetup {
allResults.workflows += result.results.workflows || 0; allResults.workflows += result.results.workflows || 0;
allResults.tasks += result.results.tasks || 0; allResults.tasks += result.results.tasks || 0;
allResults.tools += result.results.tools || 0; allResults.tools += result.results.tools || 0;
allResults.skills += result.results.skills || 0;
} }
} }
@ -622,6 +633,94 @@ LOAD and execute from: {project-root}/{{bmadFolderName}}/{{path}}
return baseName.replace(/\.md$/, extension); 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 * Print installation summary
* @param {Object} results - Installation results * @param {Object} results - Installation results
@ -634,6 +733,7 @@ LOAD and execute from: {project-root}/{{bmadFolderName}}/{{path}}
if (results.workflows > 0) parts.push(`${results.workflows} workflows`); if (results.workflows > 0) parts.push(`${results.workflows} workflows`);
if (results.tasks > 0) parts.push(`${results.tasks} tasks`); if (results.tasks > 0) parts.push(`${results.tasks} tasks`);
if (results.tools > 0) parts.push(`${results.tools} tools`); 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}`); await prompts.log.success(`${this.name} configured: ${parts.join(', ')}${targetDir}`);
} }

View File

@ -192,6 +192,16 @@ platforms:
skill_format: true skill_format: true
ancestor_conflict_check: 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: qwen:
name: "QwenCoder" name: "QwenCoder"
preferred: false preferred: false

View File

@ -16,7 +16,7 @@ async function loadSkillManifest(dirPath) {
const content = await fs.readFile(manifestPath, 'utf8'); const content = await fs.readFile(manifestPath, 'utf8');
const parsed = yaml.parse(content); const parsed = yaml.parse(content);
if (!parsed || typeof parsed !== 'object') return null; if (!parsed || typeof parsed !== 'object') return null;
if (parsed.canonicalId) return { __single: parsed }; if (parsed.canonicalId || parsed.type) return { __single: parsed };
return parsed; return parsed;
} catch (error) { } catch (error) {
console.warn(`Warning: Failed to parse bmad-skill-manifest.yaml in ${dirPath}: ${error.message}`); console.warn(`Warning: Failed to parse bmad-skill-manifest.yaml in ${dirPath}: ${error.message}`);
@ -45,4 +45,46 @@ function getCanonicalId(manifest, filename) {
return ''; return '';
} }
module.exports = { loadSkillManifest, getCanonicalId }; /**
* 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 };

View File

@ -324,6 +324,8 @@ function extractCsvRefs(filePath, content) {
const raw = record['workflow-file']; const raw = record['workflow-file'];
if (!raw || raw.trim() === '') continue; if (!raw || raw.trim() === '') continue;
if (!isResolvable(raw)) 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 // Line = header (1) + data row index (0-based) + 1
const line = i + 2; const line = i + 2;