Compare commits
No commits in common. "dc8293e5df46cba0c6adc70aff7d5d44a88a1a6c" and "44ba15f9a1bff9ac5419a7247f4234d2cf877d22" have entirely different histories.
dc8293e5df
...
44ba15f9a1
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,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:**
|
||||
|
|
@ -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}"
|
||||
|
|
@ -1 +0,0 @@
|
|||
type: skill
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
canonicalId: bmad-quick-dev-new-preview
|
||||
type: workflow
|
||||
description: "Unified quick flow - clarify intent, plan, implement, review, present"
|
||||
|
|
@ -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`
|
||||
|
|
@ -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`
|
||||
|
|
@ -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`
|
||||
|
|
@ -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 2–4.
|
||||
- **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`
|
||||
|
|
@ -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.
|
||||
|
|
@ -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`
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
});
|
||||
|
|
@ -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
|
||||
// ============================================================
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,21 +116,18 @@ 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);
|
||||
|
|
@ -154,12 +150,6 @@ class ConfigDrivenIdeSetup extends BaseIdeSetup {
|
|||
results.tasks = taskToolResult.tasks || 0;
|
||||
results.tools = taskToolResult.tools || 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Install verbatim skills (type: skill)
|
||||
if (config.skill_format) {
|
||||
results.skills = await this.installVerbatimSkills(projectDir, bmadDir, targetPath, config);
|
||||
}
|
||||
|
||||
await this.printSummary(results, target_dir, options);
|
||||
return { success: true, results };
|
||||
|
|
@ -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}`);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 };
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in New Issue