bmad-method-quarkus 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +53 -0
- package/AGENTS.md +12 -0
- package/LICENSE +30 -0
- package/README.md +94 -0
- package/README_CN.md +108 -0
- package/README_VN.md +109 -0
- package/bmad-modules.yaml +133 -0
- package/package.json +123 -0
- package/removals.txt +87 -0
- package/src/bmm-skills/agents/bmad-agent-analyst/SKILL.md +76 -0
- package/src/bmm-skills/agents/bmad-agent-analyst/customize.toml +105 -0
- package/src/bmm-skills/agents/bmad-agent-architect/SKILL.md +76 -0
- package/src/bmm-skills/agents/bmad-agent-architect/customize.toml +65 -0
- package/src/bmm-skills/agents/bmad-agent-dev/SKILL.md +76 -0
- package/src/bmm-skills/agents/bmad-agent-dev/customize.toml +83 -0
- package/src/bmm-skills/agents/bmad-agent-pm/SKILL.md +76 -0
- package/src/bmm-skills/agents/bmad-agent-pm/customize.toml +75 -0
- package/src/bmm-skills/agents/bmad-agent-ux-designer/SKILL.md +76 -0
- package/src/bmm-skills/agents/bmad-agent-ux-designer/customize.toml +60 -0
- package/src/bmm-skills/agents/bmad-quarkus-architect/.memlog.md +12 -0
- package/src/bmm-skills/agents/bmad-quarkus-architect/.memlog.md:Zone.Identifier +0 -0
- package/src/bmm-skills/agents/bmad-quarkus-architect/SKILL.md +86 -0
- package/src/bmm-skills/agents/bmad-quarkus-architect/SKILL.md:Zone.Identifier +0 -0
- package/src/bmm-skills/agents/bmad-quarkus-architect/customize.toml +37 -0
- package/src/bmm-skills/agents/bmad-quarkus-architect/customize.toml:Zone.Identifier +0 -0
- package/src/bmm-skills/agents/bmad-quarkus-architect/references/enrich-stories.md +19 -0
- package/src/bmm-skills/agents/bmad-quarkus-architect/references/enrich-stories.md:Zone.Identifier +0 -0
- package/src/bmm-skills/agents/bmad-quarkus-architect/references/prompt-quality-canon.md +79 -0
- package/src/bmm-skills/agents/bmad-quarkus-architect/references/prompt-quality-canon.md:Zone.Identifier +0 -0
- package/src/bmm-skills/agents/bmad-quarkus-architect/skills/bqa-setup/SKILL.md +80 -0
- package/src/bmm-skills/agents/bmad-quarkus-architect/skills/bqa-setup/SKILL.md:Zone.Identifier +0 -0
- package/src/bmm-skills/agents/bmad-quarkus-architect/skills/bqa-setup/assets/module-help.csv +9 -0
- package/src/bmm-skills/agents/bmad-quarkus-architect/skills/bqa-setup/assets/module-help.csv:Zone.Identifier +0 -0
- package/src/bmm-skills/agents/bmad-quarkus-architect/skills/bqa-setup/assets/module.yaml +16 -0
- package/src/bmm-skills/agents/bmad-quarkus-architect/skills/bqa-setup/assets/module.yaml:Zone.Identifier +0 -0
- package/src/bmm-skills/agents/bmad-quarkus-architect/skills/bqa-setup/scripts/cleanup-legacy.py +287 -0
- package/src/bmm-skills/agents/bmad-quarkus-architect/skills/bqa-setup/scripts/cleanup-legacy.py:Zone.Identifier +0 -0
- package/src/bmm-skills/agents/bmad-quarkus-architect/skills/bqa-setup/scripts/merge-config.py +441 -0
- package/src/bmm-skills/agents/bmad-quarkus-architect/skills/bqa-setup/scripts/merge-config.py:Zone.Identifier +0 -0
- package/src/bmm-skills/agents/bmad-quarkus-architect/skills/bqa-setup/scripts/merge-help-csv.py +246 -0
- package/src/bmm-skills/agents/bmad-quarkus-architect/skills/bqa-setup/scripts/merge-help-csv.py:Zone.Identifier +0 -0
- package/src/bmm-skills/agents/bmad-quarkus-architect/skills/quarkus-error-handling-i18n/SKILL.md +181 -0
- package/src/bmm-skills/agents/bmad-quarkus-architect/skills/quarkus-error-handling-i18n/SKILL.md:Zone.Identifier +0 -0
- package/src/bmm-skills/agents/bmad-quarkus-architect/skills/quarkus-grpc-services/SKILL.md +141 -0
- package/src/bmm-skills/agents/bmad-quarkus-architect/skills/quarkus-grpc-services/SKILL.md:Zone.Identifier +0 -0
- package/src/bmm-skills/agents/bmad-quarkus-architect/skills/quarkus-hexagonal-core/SKILL.md +392 -0
- package/src/bmm-skills/agents/bmad-quarkus-architect/skills/quarkus-hexagonal-core/SKILL.md:Zone.Identifier +0 -0
- package/src/bmm-skills/agents/bmad-quarkus-architect/skills/quarkus-kafka-messaging/SKILL.md +148 -0
- package/src/bmm-skills/agents/bmad-quarkus-architect/skills/quarkus-kafka-messaging/SKILL.md:Zone.Identifier +0 -0
- package/src/bmm-skills/agents/bmad-quarkus-architect/skills/quarkus-observability-otel/SKILL.md +180 -0
- package/src/bmm-skills/agents/bmad-quarkus-architect/skills/quarkus-observability-otel/SKILL.md:Zone.Identifier +0 -0
- package/src/bmm-skills/agents/bmad-quarkus-architect/skills/quarkus-openapi-tmforum/SKILL.md +128 -0
- package/src/bmm-skills/agents/bmad-quarkus-architect/skills/quarkus-openapi-tmforum/SKILL.md:Zone.Identifier +0 -0
- package/src/bmm-skills/agents/bmad-quarkus-architect/skills/quarkus-sql-jdbc-agroal/SKILL.md +245 -0
- package/src/bmm-skills/agents/bmad-quarkus-architect/skills/quarkus-sql-jdbc-agroal/SKILL.md:Zone.Identifier +0 -0
- package/src/bmm-skills/module-help.csv +19 -0
- package/src/bmm-skills/module.yaml +92 -0
- package/src/bmm-skills/plan/bmad-architecture/SKILL.md +85 -0
- package/src/bmm-skills/plan/bmad-architecture/assets/spine-template.md +79 -0
- package/src/bmm-skills/plan/bmad-architecture/customize.toml +102 -0
- package/src/bmm-skills/plan/bmad-architecture/references/headless.md +26 -0
- package/src/bmm-skills/plan/bmad-architecture/references/reviewer-gate.md +13 -0
- package/src/bmm-skills/plan/bmad-architecture/scripts/lint_spine.py +257 -0
- package/src/bmm-skills/plan/bmad-architecture/scripts/tests/test_lint_spine.py +270 -0
- package/src/bmm-skills/plan/bmad-create-epics-and-stories/SKILL.md +93 -0
- package/src/bmm-skills/plan/bmad-create-epics-and-stories/customize.toml +41 -0
- package/src/bmm-skills/plan/bmad-create-epics-and-stories/steps/step-01-validate-prerequisites.md +263 -0
- package/src/bmm-skills/plan/bmad-create-epics-and-stories/steps/step-02-design-epics.md +242 -0
- package/src/bmm-skills/plan/bmad-create-epics-and-stories/steps/step-03-create-stories.md +255 -0
- package/src/bmm-skills/plan/bmad-create-epics-and-stories/steps/step-04-final-validation.md +143 -0
- package/src/bmm-skills/plan/bmad-create-epics-and-stories/templates/epics-template.md +61 -0
- package/src/bmm-skills/plan/bmad-generate-project-context/SKILL.md +10 -0
- package/src/bmm-skills/plan/bmad-prd/SKILL.md +94 -0
- package/src/bmm-skills/plan/bmad-prd/assets/headless-schemas.md +76 -0
- package/src/bmm-skills/plan/bmad-prd/assets/prd-template.md +165 -0
- package/src/bmm-skills/plan/bmad-prd/assets/prd-validation-checklist.md +135 -0
- package/src/bmm-skills/plan/bmad-prd/assets/validation-report-template.html +325 -0
- package/src/bmm-skills/plan/bmad-prd/customize.toml +149 -0
- package/src/bmm-skills/plan/bmad-prd/references/headless.md +39 -0
- package/src/bmm-skills/plan/bmad-prd/references/validate.md +97 -0
- package/src/bmm-skills/plan/bmad-prfaq/SKILL.md +135 -0
- package/src/bmm-skills/plan/bmad-prfaq/agents/artifact-analyzer.md +60 -0
- package/src/bmm-skills/plan/bmad-prfaq/agents/web-researcher.md +49 -0
- package/src/bmm-skills/plan/bmad-prfaq/assets/prfaq-template.md +62 -0
- package/src/bmm-skills/plan/bmad-prfaq/bmad-manifest.json +16 -0
- package/src/bmm-skills/plan/bmad-prfaq/customize.toml +41 -0
- package/src/bmm-skills/plan/bmad-prfaq/references/customer-faq.md +55 -0
- package/src/bmm-skills/plan/bmad-prfaq/references/internal-faq.md +51 -0
- package/src/bmm-skills/plan/bmad-prfaq/references/press-release.md +60 -0
- package/src/bmm-skills/plan/bmad-prfaq/references/verdict.md +83 -0
- package/src/bmm-skills/plan/bmad-product-brief/SKILL.md +91 -0
- package/src/bmm-skills/plan/bmad-product-brief/assets/brief-template.md +41 -0
- package/src/bmm-skills/plan/bmad-product-brief/customize.toml +101 -0
- package/src/bmm-skills/plan/bmad-project-context/SKILL.md +110 -0
- package/src/bmm-skills/plan/bmad-project-context/customize.toml +24 -0
- package/src/bmm-skills/plan/bmad-project-context/references/best-practices.md +65 -0
- package/src/bmm-skills/plan/bmad-project-context/references/template.md +55 -0
- package/src/bmm-skills/plan/bmad-spec/SKILL.md +160 -0
- package/src/bmm-skills/plan/bmad-spec/assets/headless-schemas.md +33 -0
- package/src/bmm-skills/plan/bmad-spec/assets/spec-template.md +49 -0
- package/src/bmm-skills/plan/bmad-spec/assets/stories-schema.md +44 -0
- package/src/bmm-skills/plan/bmad-spec/customize.toml +52 -0
- package/src/bmm-skills/plan/bmad-sprint-planning/SKILL.md +62 -0
- package/src/bmm-skills/plan/bmad-sprint-planning/customize.toml +41 -0
- package/src/bmm-skills/plan/bmad-sprint-planning/references/fix-sprint-status.md +30 -0
- package/src/bmm-skills/plan/bmad-sprint-planning/references/generate-tracking.md +25 -0
- package/src/bmm-skills/plan/bmad-sprint-planning/references/readiness-gate.md +20 -0
- package/src/bmm-skills/plan/bmad-sprint-planning/references/status-view.md +14 -0
- package/src/bmm-skills/plan/bmad-sprint-planning/references/validate.md +10 -0
- package/src/bmm-skills/plan/bmad-sprint-planning/scripts/__pycache__/sprint_plan.cpython-311.pyc +0 -0
- package/src/bmm-skills/plan/bmad-sprint-planning/scripts/sprint_plan.py +697 -0
- package/src/bmm-skills/plan/bmad-sprint-planning/scripts/tests/__pycache__/test_sprint_plan.cpython-311-pytest-9.1.1.pyc +0 -0
- package/src/bmm-skills/plan/bmad-sprint-planning/scripts/tests/test_sprint_plan.py +524 -0
- package/src/bmm-skills/plan/bmad-sprint-planning/sprint-status-template.yaml +71 -0
- package/src/bmm-skills/plan/bmad-ux/SKILL.md +90 -0
- package/src/bmm-skills/plan/bmad-ux/assets/color-themes.md +9 -0
- package/src/bmm-skills/plan/bmad-ux/assets/design-directions.md +9 -0
- package/src/bmm-skills/plan/bmad-ux/assets/design-example-editorial.md +158 -0
- package/src/bmm-skills/plan/bmad-ux/assets/design-example-mobile.md +93 -0
- package/src/bmm-skills/plan/bmad-ux/assets/design-example-shadcn.md +109 -0
- package/src/bmm-skills/plan/bmad-ux/assets/excalidraw-wireframe.md +19 -0
- package/src/bmm-skills/plan/bmad-ux/assets/experience-example-mobile.md +112 -0
- package/src/bmm-skills/plan/bmad-ux/assets/experience-example-shadcn.md +133 -0
- package/src/bmm-skills/plan/bmad-ux/assets/headless-schemas.md +84 -0
- package/src/bmm-skills/plan/bmad-ux/assets/key-screens.md +29 -0
- package/src/bmm-skills/plan/bmad-ux/assets/validation-report-template.html +319 -0
- package/src/bmm-skills/plan/bmad-ux/customize.toml +102 -0
- package/src/bmm-skills/plan/bmad-ux/references/creative-tools.md +19 -0
- package/src/bmm-skills/plan/bmad-ux/references/design-md-spec.md +50 -0
- package/src/bmm-skills/plan/bmad-ux/references/headless.md +37 -0
- package/src/bmm-skills/plan/bmad-ux/references/validate.md +115 -0
- package/src/bmm-skills/ship/bmad-build/SKILL.md +13 -0
- package/src/bmm-skills/ship/bmad-build/compile-epic-context.md +62 -0
- package/src/bmm-skills/ship/bmad-build/customize.toml +164 -0
- package/src/bmm-skills/ship/bmad-build/references/deletion-check.md +14 -0
- package/src/bmm-skills/ship/bmad-build/review-prompts/edge-case-hunter.md +88 -0
- package/src/bmm-skills/ship/bmad-build/review-prompts/verification-gap.md +113 -0
- package/src/bmm-skills/ship/bmad-build/spec-template.md +89 -0
- package/src/bmm-skills/ship/bmad-build/step-01-clarify-and-route.md +105 -0
- package/src/bmm-skills/ship/bmad-build/step-02-plan.md +47 -0
- package/src/bmm-skills/ship/bmad-build/step-03-implement.md +49 -0
- package/src/bmm-skills/ship/bmad-build/step-04-review.md +55 -0
- package/src/bmm-skills/ship/bmad-build/step-05-present.md +75 -0
- package/src/bmm-skills/ship/bmad-build/step-oneshot.md +77 -0
- package/src/bmm-skills/ship/bmad-build/sync-sprint-status.md +19 -0
- package/src/bmm-skills/ship/bmad-build/workflow.md +84 -0
- package/src/bmm-skills/ship/bmad-build-auto/SKILL.md +13 -0
- package/src/bmm-skills/ship/bmad-build-auto/compile-epic-context.md +62 -0
- package/src/bmm-skills/ship/bmad-build-auto/customize.toml +121 -0
- package/src/bmm-skills/ship/bmad-build-auto/references/deletion-check.md +14 -0
- package/src/bmm-skills/ship/bmad-build-auto/review-prompts/edge-case-hunter.md +88 -0
- package/src/bmm-skills/ship/bmad-build-auto/review-prompts/verification-gap.md +113 -0
- package/src/bmm-skills/ship/bmad-build-auto/spec-template.md +99 -0
- package/src/bmm-skills/ship/bmad-build-auto/step-01-clarify-and-route.md +83 -0
- package/src/bmm-skills/ship/bmad-build-auto/step-02-plan.md +27 -0
- package/src/bmm-skills/ship/bmad-build-auto/step-03-implement.md +44 -0
- package/src/bmm-skills/ship/bmad-build-auto/step-04-review.md +96 -0
- package/src/bmm-skills/ship/bmad-build-auto/workflow.md +104 -0
- package/src/bmm-skills/ship/bmad-checkpoint-preview/SKILL.md +68 -0
- package/src/bmm-skills/ship/bmad-checkpoint-preview/customize.toml +41 -0
- package/src/bmm-skills/ship/bmad-checkpoint-preview/generate-trail.md +38 -0
- package/src/bmm-skills/ship/bmad-checkpoint-preview/step-01-orientation.md +105 -0
- package/src/bmm-skills/ship/bmad-checkpoint-preview/step-02-walkthrough.md +89 -0
- package/src/bmm-skills/ship/bmad-checkpoint-preview/step-03-detail-pass.md +106 -0
- package/src/bmm-skills/ship/bmad-checkpoint-preview/step-04-testing.md +74 -0
- package/src/bmm-skills/ship/bmad-checkpoint-preview/step-05-wrapup.md +30 -0
- package/src/bmm-skills/ship/bmad-code-review/SKILL.md +90 -0
- package/src/bmm-skills/ship/bmad-code-review/customize.toml +113 -0
- package/src/bmm-skills/ship/bmad-code-review/references/deletion-check.md +14 -0
- package/src/bmm-skills/ship/bmad-code-review/review-prompts/edge-case-hunter.md +88 -0
- package/src/bmm-skills/ship/bmad-code-review/review-prompts/verification-gap.md +113 -0
- package/src/bmm-skills/ship/bmad-code-review/steps/step-01-gather-context.md +87 -0
- package/src/bmm-skills/ship/bmad-code-review/steps/step-02-review.md +32 -0
- package/src/bmm-skills/ship/bmad-code-review/steps/step-03-triage.md +50 -0
- package/src/bmm-skills/ship/bmad-code-review/steps/step-04-present.md +132 -0
- package/src/bmm-skills/ship/bmad-correct-course/SKILL.md +300 -0
- package/src/bmm-skills/ship/bmad-correct-course/checklist.md +288 -0
- package/src/bmm-skills/ship/bmad-correct-course/customize.toml +41 -0
- package/src/bmm-skills/ship/bmad-qa-generate-e2e-tests/SKILL.md +176 -0
- package/src/bmm-skills/ship/bmad-qa-generate-e2e-tests/checklist.md +33 -0
- package/src/bmm-skills/ship/bmad-qa-generate-e2e-tests/customize.toml +41 -0
- package/src/bmm-skills/ship/bmad-retrospective/SKILL.md +94 -0
- package/src/bmm-skills/ship/bmad-retrospective/customize.toml +41 -0
- package/src/bmm-skills/ship/bmad-retrospective/references/acceptance-verdict.md +55 -0
- package/src/bmm-skills/ship/bmad-retrospective/references/aggregate-views.md +17 -0
- package/src/bmm-skills/ship/bmad-retrospective/references/evidence-gathering.md +30 -0
- package/src/bmm-skills/ship/bmad-retrospective/references/retro-document.md +84 -0
- package/src/bmm-skills/ship/bmad-retrospective/references/team-discussion.md +22 -0
- package/src/bmm-skills/ship/bmad-retrospective/scripts/__pycache__/sprint_status.cpython-311.pyc +0 -0
- package/src/bmm-skills/ship/bmad-retrospective/scripts/git_evidence.py +304 -0
- package/src/bmm-skills/ship/bmad-retrospective/scripts/sprint_status.py +746 -0
- package/src/bmm-skills/ship/bmad-retrospective/scripts/tests/__pycache__/test_git_evidence.cpython-311-pytest-9.1.1.pyc +0 -0
- package/src/bmm-skills/ship/bmad-retrospective/scripts/tests/__pycache__/test_sprint_status.cpython-311-pytest-9.1.1.pyc +0 -0
- package/src/bmm-skills/ship/bmad-retrospective/scripts/tests/fixtures/sprint-status-template.yaml +71 -0
- package/src/bmm-skills/ship/bmad-retrospective/scripts/tests/test_git_evidence.py +750 -0
- package/src/bmm-skills/ship/bmad-retrospective/scripts/tests/test_sprint_status.py +1579 -0
- package/src/bmm-skills/v6-shims/README.md +28 -0
- package/src/bmm-skills/v6-shims/bmad-create-architecture/SKILL.md +30 -0
- package/src/bmm-skills/v6-shims/bmad-create-architecture/customize.toml +41 -0
- package/src/bmm-skills/v6-shims/bmad-create-prd/SKILL.md +30 -0
- package/src/bmm-skills/v6-shims/bmad-create-prd/customize.toml +41 -0
- package/src/bmm-skills/v6-shims/bmad-create-story/SKILL.md +434 -0
- package/src/bmm-skills/v6-shims/bmad-create-story/checklist.md +357 -0
- package/src/bmm-skills/v6-shims/bmad-create-story/customize.toml +41 -0
- package/src/bmm-skills/v6-shims/bmad-create-story/discover-inputs.md +88 -0
- package/src/bmm-skills/v6-shims/bmad-create-story/template.md +49 -0
- package/src/bmm-skills/v6-shims/bmad-dev-auto/SKILL.md +19 -0
- package/src/bmm-skills/v6-shims/bmad-dev-story/SKILL.md +502 -0
- package/src/bmm-skills/v6-shims/bmad-dev-story/checklist.md +80 -0
- package/src/bmm-skills/v6-shims/bmad-dev-story/customize.toml +44 -0
- package/src/bmm-skills/v6-shims/bmad-document-project/SKILL.md +14 -0
- package/src/bmm-skills/v6-shims/bmad-domain-research/SKILL.md +14 -0
- package/src/bmm-skills/v6-shims/bmad-edit-prd/SKILL.md +30 -0
- package/src/bmm-skills/v6-shims/bmad-edit-prd/customize.toml +42 -0
- package/src/bmm-skills/v6-shims/bmad-market-research/SKILL.md +14 -0
- package/src/bmm-skills/v6-shims/bmad-quick-dev/SKILL.md +19 -0
- package/src/bmm-skills/v6-shims/bmad-sprint-status/SKILL.md +26 -0
- package/src/bmm-skills/v6-shims/bmad-sprint-status/customize.toml +41 -0
- package/src/bmm-skills/v6-shims/bmad-technical-research/SKILL.md +14 -0
- package/src/bmm-skills/v6-shims/bmad-validate-prd/SKILL.md +30 -0
- package/src/bmm-skills/v6-shims/bmad-validate-prd/customize.toml +42 -0
- package/src/core-skills/bmad-advanced-elicitation/SKILL.md +65 -0
- package/src/core-skills/bmad-advanced-elicitation/assets/methods.csv +72 -0
- package/src/core-skills/bmad-advanced-elicitation/customize.toml +54 -0
- package/src/core-skills/bmad-advanced-elicitation/scripts/pick_methods.py +233 -0
- package/src/core-skills/bmad-advanced-elicitation/scripts/tests/test_pick_methods.py +228 -0
- package/src/core-skills/bmad-brainstorming/SKILL.md +80 -0
- package/src/core-skills/bmad-brainstorming/assets/brain-icons.json +166 -0
- package/src/core-skills/bmad-brainstorming/assets/brain-methods.csv +109 -0
- package/src/core-skills/bmad-brainstorming/assets/brain-selector.html +328 -0
- package/src/core-skills/bmad-brainstorming/customize.toml +84 -0
- package/src/core-skills/bmad-brainstorming/references/converge.md +24 -0
- package/src/core-skills/bmad-brainstorming/references/finalize.md +26 -0
- package/src/core-skills/bmad-brainstorming/references/headless.md +54 -0
- package/src/core-skills/bmad-brainstorming/references/in-chat-techniques.md +18 -0
- package/src/core-skills/bmad-brainstorming/references/mode-autonomous.md +10 -0
- package/src/core-skills/bmad-brainstorming/references/mode-facilitator.md +11 -0
- package/src/core-skills/bmad-brainstorming/references/mode-partner.md +16 -0
- package/src/core-skills/bmad-brainstorming/references/resume.md +5 -0
- package/src/core-skills/bmad-brainstorming/scripts/brain.py +770 -0
- package/src/core-skills/bmad-brainstorming/scripts/tests/test_brain.py +239 -0
- package/src/core-skills/bmad-customize/SKILL.md +111 -0
- package/src/core-skills/bmad-customize/scripts/list_customizable_skills.py +231 -0
- package/src/core-skills/bmad-customize/scripts/tests/test_list_customizable_skills.py +249 -0
- package/src/core-skills/bmad-deep-recon/SKILL.md +82 -0
- package/src/core-skills/bmad-deep-recon/assets/research.template.md +18 -0
- package/src/core-skills/bmad-deep-recon/customize.toml +212 -0
- package/src/core-skills/bmad-deep-recon/references/draft.md +8 -0
- package/src/core-skills/bmad-deep-recon/references/finalize.md +11 -0
- package/src/core-skills/bmad-deep-recon/references/html-briefing.md +16 -0
- package/src/core-skills/bmad-deep-recon/references/lifecycle.md +11 -0
- package/src/core-skills/bmad-deep-recon/references/process.md +10 -0
- package/src/core-skills/bmad-deep-recon/references/run.md +73 -0
- package/src/core-skills/bmad-deep-recon/references/selection.md +13 -0
- package/src/core-skills/bmad-deep-recon/references/synthesis.md +16 -0
- package/src/core-skills/bmad-deep-recon/references/verification.md +29 -0
- package/src/core-skills/bmad-deep-recon/scripts/recon_kit.py +322 -0
- package/src/core-skills/bmad-deep-recon/scripts/tests/test_recon_kit.py +144 -0
- package/src/core-skills/bmad-deep-recon/types/academic-lit.md +19 -0
- package/src/core-skills/bmad-deep-recon/types/competitive.md +19 -0
- package/src/core-skills/bmad-deep-recon/types/domain.md +19 -0
- package/src/core-skills/bmad-deep-recon/types/market.md +19 -0
- package/src/core-skills/bmad-deep-recon/types/technical.md +19 -0
- package/src/core-skills/bmad-deep-recon/types/user-voice.md +19 -0
- package/src/core-skills/bmad-forge-idea/SKILL.md +107 -0
- package/src/core-skills/bmad-forge-idea/customize.toml +42 -0
- package/src/core-skills/bmad-forge-idea/scripts/resolve_personas.py +275 -0
- package/src/core-skills/bmad-forge-idea/scripts/tests/test_resolve_personas.py +138 -0
- package/src/core-skills/bmad-help/SKILL.md +75 -0
- package/src/core-skills/bmad-party-mode/SKILL.md +60 -0
- package/src/core-skills/bmad-party-mode/customize.toml +211 -0
- package/src/core-skills/bmad-party-mode/references/create-party.md +70 -0
- package/src/core-skills/bmad-party-mode/references/mode-agent-team.md +13 -0
- package/src/core-skills/bmad-party-mode/references/mode-auto.md +13 -0
- package/src/core-skills/bmad-party-mode/references/mode-subagent.md +31 -0
- package/src/core-skills/bmad-party-mode/references/party-memory.md +51 -0
- package/src/core-skills/bmad-party-mode/scripts/resolve_party.py +282 -0
- package/src/core-skills/bmad-party-mode/scripts/tests/test_resolve_party.py +146 -0
- package/src/core-skills/bmad-review/SKILL.md +49 -0
- package/src/core-skills/bmad-review/customize.toml +141 -0
- package/src/core-skills/bmad-review/references/editorial-common.md +56 -0
- package/src/core-skills/bmad-review/references/lens-adversarial.md +19 -0
- package/src/core-skills/bmad-review/references/lens-edge-case-hunter.md +54 -0
- package/src/core-skills/bmad-review/references/lens-prose.md +7 -0
- package/src/core-skills/bmad-review/references/lens-structure.md +9 -0
- package/src/core-skills/bmad-review/references/lens-verification-gap.md +92 -0
- package/src/core-skills/bmad-review/references/structure-models.md +44 -0
- package/src/core-skills/bmad-review/scripts/tests/test_word_metrics.py +62 -0
- package/src/core-skills/bmad-review/scripts/word_metrics.py +102 -0
- package/src/core-skills/module-help.csv +10 -0
- package/src/core-skills/module.yaml +38 -0
- package/src/core-skills/v6-shims/README.md +25 -0
- package/src/core-skills/v6-shims/bmad-editorial-review/SKILL.md +6 -0
- package/src/core-skills/v6-shims/bmad-editorial-review/customize.toml +31 -0
- package/src/core-skills/v6-shims/bmad-editorial-review-prose/SKILL.md +6 -0
- package/src/core-skills/v6-shims/bmad-editorial-review-structure/SKILL.md +6 -0
- package/src/core-skills/v6-shims/bmad-review-adversarial-general/SKILL.md +6 -0
- package/src/core-skills/v6-shims/bmad-review-edge-case-hunter/SKILL.md +6 -0
- package/src/core-skills/v6-shims/bmad-review-verification-gap/SKILL.md +6 -0
- package/src/scripts/__pycache__/config_utils.cpython-311.pyc +0 -0
- package/src/scripts/config_utils.py +119 -0
- package/src/scripts/memlog.py +224 -0
- package/src/scripts/render_skill.py +401 -0
- package/src/scripts/resolve_config.py +74 -0
- package/src/scripts/resolve_customization.py +99 -0
- package/src/scripts/tests/__pycache__/test_config_utils.cpython-311.pyc +0 -0
- package/src/scripts/tests/__pycache__/test_resolve_config.cpython-311.pyc +0 -0
- package/src/scripts/tests/__pycache__/test_resolve_customization.cpython-311.pyc +0 -0
- package/src/scripts/tests/test_config_utils.py +85 -0
- package/src/scripts/tests/test_memlog.py +306 -0
- package/src/scripts/tests/test_resolve_config.py +89 -0
- package/src/scripts/tests/test_resolve_customization.py +77 -0
- package/tools/bundle-web-bundles.js +117 -0
- package/tools/format-workflow-md.js +263 -0
- package/tools/installer/README.md +60 -0
- package/tools/installer/bmad-cli.js +105 -0
- package/tools/installer/cli-utils.js +57 -0
- package/tools/installer/commands/install.js +149 -0
- package/tools/installer/commands/status.js +65 -0
- package/tools/installer/commands/uninstall.js +167 -0
- package/tools/installer/core/config.js +73 -0
- package/tools/installer/core/existing-install.js +121 -0
- package/tools/installer/core/install-paths.js +132 -0
- package/tools/installer/core/installer.js +1767 -0
- package/tools/installer/core/legacy-warnings.js +151 -0
- package/tools/installer/core/manifest-generator.js +859 -0
- package/tools/installer/core/manifest.js +433 -0
- package/tools/installer/core/uv-check.js +195 -0
- package/tools/installer/core/wsl-node-check.js +109 -0
- package/tools/installer/file-ops.js +204 -0
- package/tools/installer/fs-native.js +116 -0
- package/tools/installer/ide/_config-driven.js +972 -0
- package/tools/installer/ide/manager.js +324 -0
- package/tools/installer/ide/platform-codes.js +80 -0
- package/tools/installer/ide/platform-codes.yaml +343 -0
- package/tools/installer/ide/shared/installed-skills.js +50 -0
- package/tools/installer/ide/shared/path-utils.js +219 -0
- package/tools/installer/ide/shared/skill-manifest.js +57 -0
- package/tools/installer/install-messages.yaml +37 -0
- package/tools/installer/list-options.js +210 -0
- package/tools/installer/message-loader.js +83 -0
- package/tools/installer/modules/channel-plan.js +203 -0
- package/tools/installer/modules/channel-resolver.js +241 -0
- package/tools/installer/modules/custom-module-manager.js +924 -0
- package/tools/installer/modules/external-manager.js +671 -0
- package/tools/installer/modules/git-env.js +47 -0
- package/tools/installer/modules/module-help-schema.js +13 -0
- package/tools/installer/modules/official-modules.js +2257 -0
- package/tools/installer/modules/plugin-resolver.js +398 -0
- package/tools/installer/modules/version-resolver.js +336 -0
- package/tools/installer/project-root.js +224 -0
- package/tools/installer/prompts.js +791 -0
- package/tools/installer/set-overrides.js +330 -0
- package/tools/installer/ui.js +2167 -0
- package/tools/installer/yaml-format.js +245 -0
- package/tools/javascript-conventions.md +5 -0
- package/tools/migrate-custom-module-paths.js +124 -0
- package/tools/skill-validator.md +378 -0
- package/tools/validate-published-implementation-model.mjs +68 -0
- package/tools/validate-sidebar-order.js +388 -0
- package/tools/validate-skills.js +735 -0
- package/tools/validate-svg-changes.sh +356 -0
- package/web-bundles/README.md +46 -0
- package/web-bundles/brainstorming-coach/INSTRUCTIONS.md +86 -0
- package/web-bundles/brainstorming-coach/SKILL.md +83 -0
- package/web-bundles/brainstorming-coach/brain-methods.csv +62 -0
- package/web-bundles/bundles.json +139 -0
- package/web-bundles/market-and-industry-research/INSTRUCTIONS.md +88 -0
- package/web-bundles/market-and-industry-research/SKILL.md +59 -0
- package/web-bundles/prd-coach/INSTRUCTIONS.md +86 -0
- package/web-bundles/prd-coach/SKILL.md +101 -0
- package/web-bundles/prd-coach/prd-template.md +165 -0
- package/web-bundles/prd-coach/prd-validation-checklist.md +135 -0
- package/web-bundles/prfaq-coach/INSTRUCTIONS.md +86 -0
- package/web-bundles/prfaq-coach/SKILL.md +139 -0
- package/web-bundles/product-brief-coach/INSTRUCTIONS.md +86 -0
- package/web-bundles/product-brief-coach/SKILL.md +113 -0
- package/web-bundles/ux-coach/INSTRUCTIONS.md +92 -0
- package/web-bundles/ux-coach/SKILL.md +187 -0
- package/web-bundles/ux-coach/ux-validation.md +100 -0
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
# /// script
|
|
2
|
+
# requires-python = ">=3.10"
|
|
3
|
+
# dependencies = ["pytest>=8.0"]
|
|
4
|
+
# ///
|
|
5
|
+
"""Tests for brain.py. Run: uv run -m pytest scripts/tests/test_brain.py"""
|
|
6
|
+
import json
|
|
7
|
+
import sys
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
import pytest
|
|
11
|
+
|
|
12
|
+
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
|
|
13
|
+
import brain # noqa: E402
|
|
14
|
+
|
|
15
|
+
CSV = """category,technique_name,description,detail
|
|
16
|
+
collaborative,Yes And Building,Build on every idea with "yes and" to keep momentum,
|
|
17
|
+
wild,Quantum Superposition,Hold contradictory ideas as simultaneously true,techniques/quantum.md
|
|
18
|
+
structured,SCAMPER Method,Run the idea through seven transformation lenses,
|
|
19
|
+
wild,Anti-Solution,Brainstorm how to make the problem worse then invert,
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
DETAIL = "# Quantum Superposition\nFull multi-step instructions for the complex technique."
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@pytest.fixture
|
|
26
|
+
def lib(tmp_path):
|
|
27
|
+
csv_path = tmp_path / "brain-methods.csv"
|
|
28
|
+
csv_path.write_text(CSV, encoding="utf-8")
|
|
29
|
+
(tmp_path / "techniques").mkdir()
|
|
30
|
+
(tmp_path / "techniques" / "quantum.md").write_text(DETAIL, encoding="utf-8")
|
|
31
|
+
return csv_path
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def test_load_normalizes_detail(lib):
|
|
35
|
+
rows = brain.load(lib)
|
|
36
|
+
assert len(rows) == 4
|
|
37
|
+
assert rows[0]["detail"] == ""
|
|
38
|
+
assert rows[1]["detail"] == "techniques/quantum.md"
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def test_categories_counts_sorted(lib):
|
|
42
|
+
assert brain.categories(brain.load(lib)) == [("collaborative", 1), ("structured", 1), ("wild", 2)]
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def test_filter_is_case_insensitive(lib):
|
|
46
|
+
rows = brain.filter_cats(brain.load(lib), ["WILD"])
|
|
47
|
+
assert {r["technique_name"] for r in rows} == {"Quantum Superposition", "Anti-Solution"}
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def test_filter_none_returns_all(lib):
|
|
51
|
+
assert len(brain.filter_cats(brain.load(lib), None)) == 4
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def test_find_hits_and_misses(lib):
|
|
55
|
+
found, missing = brain.find(brain.load(lib), ["scamper method", "Nope"])
|
|
56
|
+
assert [r["technique_name"] for r in found] == ["SCAMPER Method"]
|
|
57
|
+
assert missing == ["Nope"]
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def test_resolve_detail_present(lib):
|
|
61
|
+
row = next(r for r in brain.load(lib) if r["detail"])
|
|
62
|
+
assert "multi-step instructions" in brain.resolve_detail(row, lib.parent)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def test_resolve_detail_absent_is_none(lib):
|
|
66
|
+
row = next(r for r in brain.load(lib) if not r["detail"])
|
|
67
|
+
assert brain.resolve_detail(row, lib.parent) is None
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def test_resolve_detail_missing_file_warns_not_fatal(lib, capsys):
|
|
71
|
+
rows = brain.load(lib)
|
|
72
|
+
rows[1]["detail"] = "techniques/gone.md"
|
|
73
|
+
assert brain.resolve_detail(rows[1], lib.parent) is None
|
|
74
|
+
assert "not found" in capsys.readouterr().err
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def test_show_inlines_detail(lib, capsys):
|
|
78
|
+
assert brain.main(["--file", str(lib), "show", "Quantum Superposition"]) == 0
|
|
79
|
+
out = capsys.readouterr().out
|
|
80
|
+
assert "multi-step instructions" in out and "[wild]" in out
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def test_show_simple_has_no_detail(lib, capsys):
|
|
84
|
+
brain.main(["--file", str(lib), "show", "SCAMPER Method"])
|
|
85
|
+
out = capsys.readouterr().out
|
|
86
|
+
assert "transformation lenses" in out
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def test_show_all_missing_returns_1(lib):
|
|
90
|
+
assert brain.main(["--file", str(lib), "show", "Ghost"]) == 1
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def test_list_filtered_text(lib, capsys):
|
|
94
|
+
brain.main(["--file", str(lib), "list", "--category", "structured"])
|
|
95
|
+
out = capsys.readouterr().out.strip().splitlines()
|
|
96
|
+
assert len(out) == 1 and out[0].startswith("structured\tSCAMPER Method\t")
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def test_list_bare_is_refused(lib, capsys):
|
|
100
|
+
# the footgun: bare `list` must NOT dump the catalog into context
|
|
101
|
+
assert brain.main(["--file", str(lib), "list"]) == 2
|
|
102
|
+
captured = capsys.readouterr()
|
|
103
|
+
assert captured.out == "" # nothing leaked to stdout
|
|
104
|
+
assert "--category" in captured.err and "--all" in captured.err
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def test_list_all_dumps_everything(lib, capsys):
|
|
108
|
+
assert brain.main(["--file", str(lib), "list", "--all"]) == 0
|
|
109
|
+
out = capsys.readouterr().out.strip().splitlines()
|
|
110
|
+
assert len(out) == 4 # the deliberate full-catalog escape hatch
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def test_json_output(lib, capsys):
|
|
114
|
+
import json
|
|
115
|
+
brain.main(["--file", str(lib), "--json", "categories"])
|
|
116
|
+
data = json.loads(capsys.readouterr().out)
|
|
117
|
+
assert {"category": "wild", "count": 2} in data
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def test_random_respects_n_and_category(lib, capsys):
|
|
121
|
+
brain.main(["--file", str(lib), "random", "--category", "wild", "-n", "5"])
|
|
122
|
+
lines = capsys.readouterr().out.strip().splitlines()
|
|
123
|
+
assert len(lines) == 2 # only 2 wild exist, n capped
|
|
124
|
+
assert all(line.startswith("wild\t") for line in lines)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def test_random_negative_n_does_not_crash(lib, capsys):
|
|
128
|
+
# a negative -n is clamped to 0, not passed to random.sample (which would raise)
|
|
129
|
+
assert brain.main(["--file", str(lib), "random", "-n", "-1"]) == 0
|
|
130
|
+
assert capsys.readouterr().out.strip() == ""
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def test_missing_file_returns_2(tmp_path):
|
|
134
|
+
assert brain.main(["--file", str(tmp_path / "nope.csv"), "categories"]) == 2
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
# --- html selection page ------------------------------------------------
|
|
138
|
+
|
|
139
|
+
def test_html_requires_out(lib, capsys):
|
|
140
|
+
# never dump the catalog to stdout — writing to a file is the whole point
|
|
141
|
+
assert brain.main(["--file", str(lib), "html"]) == 2
|
|
142
|
+
assert "--out" in capsys.readouterr().err
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def test_html_writes_selection_page(lib, tmp_path):
|
|
146
|
+
out = tmp_path / "sel.html"
|
|
147
|
+
assert brain.main(["--file", str(lib), "html", "--out", str(out)]) == 0
|
|
148
|
+
doc = out.read_text(encoding="utf-8")
|
|
149
|
+
assert doc.startswith("<!DOCTYPE html>")
|
|
150
|
+
assert "BMad Method Brainstorming Selection" in doc
|
|
151
|
+
for r in brain.load(lib):
|
|
152
|
+
assert r["technique_name"] in doc # every technique is selectable
|
|
153
|
+
assert ""yes and"" in doc # quotes in a description are escaped, not raw
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def test_html_creates_missing_parent(lib, tmp_path):
|
|
157
|
+
out = tmp_path / "nested" / "deep" / "sel.html"
|
|
158
|
+
assert brain.main(["--file", str(lib), "html", "--out", str(out)]) == 0
|
|
159
|
+
assert out.is_file()
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
# --- --extra overlay (customize.toml additional_techniques) -------------
|
|
163
|
+
|
|
164
|
+
EXTRA = (
|
|
165
|
+
'[{"category": "domain-specific", "technique_name": "Regulatory Inversion", '
|
|
166
|
+
'"description": "Start from the compliance constraint and brainstorm what it unlocks."}, '
|
|
167
|
+
'{"category": "wild", "technique_name": "Extra Wild One", "description": "An added wild method."}]'
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
@pytest.fixture
|
|
172
|
+
def extra(tmp_path):
|
|
173
|
+
p = tmp_path / "extra.json"
|
|
174
|
+
p.write_text(EXTRA, encoding="utf-8")
|
|
175
|
+
return p
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def test_extra_merges_into_categories(lib, extra, capsys):
|
|
179
|
+
brain.main(["--file", str(lib), "--extra", str(extra), "categories"])
|
|
180
|
+
out = capsys.readouterr().out
|
|
181
|
+
assert "domain-specific\t1" in out # a brand-new category appears
|
|
182
|
+
assert "wild\t3" in out # the extra wild one is counted alongside the shipped two
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def test_extra_appears_in_list_and_random(lib, extra, capsys):
|
|
186
|
+
brain.main(["--file", str(lib), "--extra", str(extra), "list", "--category", "domain-specific"])
|
|
187
|
+
assert "Regulatory Inversion" in capsys.readouterr().out
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def test_extra_replaces_shipped_row_by_name(lib, extra, tmp_path, capsys):
|
|
191
|
+
shipped = brain.load(Path(lib))[0]
|
|
192
|
+
overlay = tmp_path / "replace.json"
|
|
193
|
+
overlay.write_text(
|
|
194
|
+
json.dumps([{"category": shipped["category"], "technique_name": shipped["technique_name"], "description": "RETUNED"}]),
|
|
195
|
+
encoding="utf-8",
|
|
196
|
+
)
|
|
197
|
+
brain.main(["--file", str(lib), "--extra", str(overlay), "list", "--all"])
|
|
198
|
+
out = capsys.readouterr().out
|
|
199
|
+
assert "RETUNED" in out
|
|
200
|
+
assert out.count(shipped["technique_name"]) == 1 # replaced, not duplicated
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def test_extra_malformed_exits_cleanly(lib, tmp_path, capsys):
|
|
204
|
+
bad = tmp_path / "bad.json"
|
|
205
|
+
for content in ('{not json', '{"a": 1}', '["not-an-object"]'):
|
|
206
|
+
bad.write_text(content, encoding="utf-8")
|
|
207
|
+
assert brain.main(["--file", str(lib), "--extra", str(bad), "categories"]) == 2
|
|
208
|
+
assert "could not read --extra" in capsys.readouterr().err
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def test_extra_is_first_class_in_html(lib, extra, tmp_path):
|
|
212
|
+
out = tmp_path / "sel.html"
|
|
213
|
+
assert brain.main(["--file", str(lib), "--extra", str(extra), "html", "--out", str(out)]) == 0
|
|
214
|
+
doc = out.read_text(encoding="utf-8")
|
|
215
|
+
# custom technique is selectable and its new category renders without crashing (fallback glyph/hue)
|
|
216
|
+
assert "Regulatory Inversion" in doc
|
|
217
|
+
assert "Domain Specific" in doc
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def test_extra_missing_file_returns_2(lib, tmp_path):
|
|
221
|
+
assert brain.main(["--file", str(lib), "--extra", str(tmp_path / "nope.json"), "categories"]) == 2
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
def test_unknown_category_style_uses_fallback_glyph():
|
|
225
|
+
hue, glyph = brain.category_style("totally-made-up-category")
|
|
226
|
+
assert hue.startswith("#") and len(hue) == 7 # valid derived hex
|
|
227
|
+
assert glyph == brain._FALLBACK_GLYPH
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def test_shipped_selector_is_in_sync_with_catalog():
|
|
231
|
+
# foolproofing: if someone edits brain-methods.csv they must regenerate the page.
|
|
232
|
+
# Regenerate with: uv run brain.py html --out assets/brain-selector.html
|
|
233
|
+
asset = brain.DEFAULT_FILE.parent / "brain-selector.html"
|
|
234
|
+
assert asset.is_file(), "missing assets/brain-selector.html — generate it"
|
|
235
|
+
expected = brain.html_doc(brain.load(brain.DEFAULT_FILE))
|
|
236
|
+
assert asset.read_text(encoding="utf-8") == expected, (
|
|
237
|
+
"assets/brain-selector.html is stale; regenerate: "
|
|
238
|
+
"uv run brain.py html --out assets/brain-selector.html"
|
|
239
|
+
)
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: bmad-customize
|
|
3
|
+
description: Authors and updates customization overrides for installed BMad skills. Use when the user says 'customize bmad', 'override a skill', 'change agent behavior', or 'customize a workflow'.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# BMad Customize
|
|
7
|
+
|
|
8
|
+
Translate the user's intent into a correctly-placed TOML override file under `{project-root}/_bmad/custom/` for a customizable agent or workflow skill. Discover, route, author, write, verify.
|
|
9
|
+
|
|
10
|
+
Scope v1: per-skill `[agent]` overrides (`bmad-agent-<role>.toml` / `.user.toml`) and per-skill `[workflow]` overrides (`bmad-<workflow>.toml` / `.user.toml`). Central config (`{project-root}/_bmad/custom/config.toml`) is out of scope — point users at the [How to Customize BMad guide](https://docs.bmad-method.org/how-to/customize-bmad/).
|
|
11
|
+
|
|
12
|
+
When the target's `customize.toml` doesn't expose what the user wants, say so plainly. Don't invent fields.
|
|
13
|
+
|
|
14
|
+
## Preflight
|
|
15
|
+
|
|
16
|
+
- No `{project-root}/_bmad/` → BMad isn't installed. Say so, stop.
|
|
17
|
+
- `{project-root}/_bmad/scripts/resolve_customization.py` missing → continue, but Step 6 verify falls back to manual merge.
|
|
18
|
+
- Both present → proceed.
|
|
19
|
+
|
|
20
|
+
## Activation
|
|
21
|
+
|
|
22
|
+
Load `_bmad/config.toml` and `_bmad/config.user.toml` from `{project-root}` for `user_name` (default `BMad`) and `communication_language` (default `English`). Greet. If the user's invocation already names a target skill AND a specific change, jump to Step 3.
|
|
23
|
+
|
|
24
|
+
## Step 1: Classify intent
|
|
25
|
+
|
|
26
|
+
- **Directed** — specific skill + specific change → Step 3.
|
|
27
|
+
- **Exploratory** — "what can I customize?" → Step 2.
|
|
28
|
+
- **Audit/iterate** — wants to review or change something already customized → Step 2, lead with skills that have existing overrides; read the existing override in Step 3 before composing.
|
|
29
|
+
- **Cross-cutting** — could live on multiple surfaces → Step 3, choose agent vs workflow explicitly with the user.
|
|
30
|
+
|
|
31
|
+
## Step 2: Discovery
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
uv run {skill-root}/scripts/list_customizable_skills.py --project-root {project-root}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Use `--extra-root <path>` (repeatable) if the user has skills installed in additional locations.
|
|
38
|
+
|
|
39
|
+
Group the returned `agents` and `workflows` for the user; for each show name, description, whether `has_team_override` or `has_user_override` is true. Surface any `errors[]`. For audit/iterate intents, lead with already-overridden entries.
|
|
40
|
+
|
|
41
|
+
Empty list: show `scanned_roots`, ask whether skills live elsewhere (offer `--extra-root`); otherwise stop.
|
|
42
|
+
|
|
43
|
+
## Step 3: Determine the right surface
|
|
44
|
+
|
|
45
|
+
Read the target's `customize.toml`. Top-level `[agent]` or `[workflow]` block defines the surface.
|
|
46
|
+
|
|
47
|
+
If a team or user override already exists, read it first and summarize what's already overridden before composing.
|
|
48
|
+
|
|
49
|
+
**Cross-cutting intent — walk both surfaces with the user:**
|
|
50
|
+
- Every workflow a given agent runs → agent surface (e.g. `bmad-agent-pm.toml` with `persistent_facts`, `principles`).
|
|
51
|
+
- One workflow only → workflow surface (e.g. `bmad-prd.toml` with `activation_steps_prepend`).
|
|
52
|
+
- Several specific workflows → multiple workflow overrides in sequence, not an agent override.
|
|
53
|
+
|
|
54
|
+
**Single-surface heuristic:**
|
|
55
|
+
- Workflow-level: template swap, output path, step-specific behavior, or a named scalar already exposed (`*_template`, `on_complete`). Surgical, reliable.
|
|
56
|
+
- Agent-level: persona, communication style, org-wide facts, menu changes, behavior that should apply to every workflow the agent dispatches.
|
|
57
|
+
|
|
58
|
+
When ambiguous, present both with tradeoff, recommend one, let the user decide.
|
|
59
|
+
|
|
60
|
+
Intent outside the exposed surface (step logic, ordering, anything not in `customize.toml`): say so; offer `activation_steps_prepend`/`append` or `persistent_facts` as approximations, or recommend `bmad-builder` to create a custom skill.
|
|
61
|
+
|
|
62
|
+
## Step 4: Compose the override
|
|
63
|
+
|
|
64
|
+
Translate plain-English into TOML against the target's `customize.toml` fields. If an existing override was read, frame the change as additive.
|
|
65
|
+
|
|
66
|
+
Merge semantics:
|
|
67
|
+
- **Scalars** (`icon`, `role`, `*_template`, `on_complete`) — override wins.
|
|
68
|
+
- **Append arrays** (`persistent_facts`, `activation_steps_prepend`/`append`, `principles`) — team/user entries append in order.
|
|
69
|
+
- **Keyed arrays of tables** (menu items with `code` or `id`) — matching keys replace, new keys append.
|
|
70
|
+
|
|
71
|
+
Overrides are sparse: only the fields being changed. Never copy the whole `customize.toml`.
|
|
72
|
+
|
|
73
|
+
**Template swap** (`*_template` scalar): offer to copy the default template to `{project-root}/_bmad/custom/{skill-name}-{purpose}-template.md`, point the override at the new path, offer to help edit it.
|
|
74
|
+
|
|
75
|
+
## Step 5: Team or user placement
|
|
76
|
+
|
|
77
|
+
Under `{project-root}/_bmad/custom/`:
|
|
78
|
+
- `{skill-name}.toml` — team, committed. Policies, org conventions, compliance.
|
|
79
|
+
- `{skill-name}.user.toml` — user, gitignored. Personal tone, private facts, shortcuts.
|
|
80
|
+
|
|
81
|
+
Default by character (policy → team, personal → user), confirm before writing.
|
|
82
|
+
|
|
83
|
+
## Step 6: Show, confirm, write, verify
|
|
84
|
+
|
|
85
|
+
1. Show the full TOML. If the file exists, show a diff. Never silently overwrite.
|
|
86
|
+
2. Wait for explicit yes.
|
|
87
|
+
3. Write. Create `{project-root}/_bmad/custom/` if needed.
|
|
88
|
+
4. Verify:
|
|
89
|
+
```
|
|
90
|
+
uv run {project-root}/_bmad/scripts/resolve_customization.py --skill <install-path> --key <agent-or-workflow>
|
|
91
|
+
```
|
|
92
|
+
Show the merged output, point out the changed fields.
|
|
93
|
+
|
|
94
|
+
**Resolver missing or fails:** read whichever layers exist — `<install-path>/customize.toml` (base), `{project-root}/_bmad/custom/{skill-name}.toml` (team), `{project-root}/_bmad/custom/{skill-name}.user.toml` (user) — apply base → team → user with the same merge rules (scalars override, tables deep-merge, `code`/`id`-keyed arrays merge by key, all other arrays append), describe how the changed fields resolve.
|
|
95
|
+
|
|
96
|
+
**Verify shows override didn't land** (field unchanged, merge conflict, file not picked up): re-enter Step 4 with the verify output as context. Usually wrong field name, wrong merge mode (scalar vs array), or wrong scope.
|
|
97
|
+
5. Summarize what changed, where the file lives, how to iterate. Remind the user to commit team overrides.
|
|
98
|
+
|
|
99
|
+
## Complete when
|
|
100
|
+
|
|
101
|
+
- Override file written (or user explicitly aborted).
|
|
102
|
+
- User has seen resolver output (or manual fallback merge summary).
|
|
103
|
+
- User has acknowledged the summary.
|
|
104
|
+
|
|
105
|
+
Otherwise the skill isn't done — finish or tell the user they're exiting incomplete.
|
|
106
|
+
|
|
107
|
+
## When this skill can't help
|
|
108
|
+
|
|
109
|
+
- **Central config** (`{project-root}/_bmad/custom/config.toml`) — see the [How to Customize BMad guide](https://docs.bmad-method.org/how-to/customize-bmad/).
|
|
110
|
+
- **Step logic, ordering, behavior not in `customize.toml`** — open a feature request, or use `bmad-builder` to create a custom skill. Offer to help with either.
|
|
111
|
+
- **Skills without a `customize.toml`** — not customizable.
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# /// script
|
|
3
|
+
# requires-python = ">=3.11"
|
|
4
|
+
# ///
|
|
5
|
+
"""Enumerate customizable BMad skills installed alongside this one.
|
|
6
|
+
|
|
7
|
+
Scans a skills directory (by default: the directory this script's own skill
|
|
8
|
+
lives in, derived from __file__), finds every sibling directory containing a
|
|
9
|
+
`customize.toml`, classifies each as agent and/or workflow based on its
|
|
10
|
+
top-level blocks, reads the skill's SKILL.md frontmatter description for a
|
|
11
|
+
one-liner, and checks whether override files already exist in
|
|
12
|
+
`{project-root}/_bmad/custom/`.
|
|
13
|
+
|
|
14
|
+
Skills in BMad are loaded either from a project-local location (e.g. the
|
|
15
|
+
project's `.claude/skills/` or `.cursor/skills/`) or from a user-global
|
|
16
|
+
location (e.g. `~/.claude/skills/`). We do not hardcode those paths — the
|
|
17
|
+
running skill's own location is the source of truth for sibling discovery.
|
|
18
|
+
`--extra-root` is available for the rare case where skills live in multiple
|
|
19
|
+
locations on the same machine.
|
|
20
|
+
|
|
21
|
+
Output: JSON to stdout. Non-empty `errors[]` in the payload is non-fatal
|
|
22
|
+
by contract — the scanner surfaces malformed TOML, missing roots, and
|
|
23
|
+
skills with no customization block as data for the caller to display,
|
|
24
|
+
and still exits 0. Exit 2 is reserved for invocation errors (e.g.
|
|
25
|
+
missing or unreadable `--project-root`) where no useful payload can be
|
|
26
|
+
produced.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
from __future__ import annotations
|
|
30
|
+
|
|
31
|
+
import argparse
|
|
32
|
+
import json
|
|
33
|
+
import re
|
|
34
|
+
import sys
|
|
35
|
+
import tomllib
|
|
36
|
+
from pathlib import Path
|
|
37
|
+
|
|
38
|
+
# Top-level TOML blocks that indicate a customization surface.
|
|
39
|
+
SURFACE_KEYS = ("agent", "workflow")
|
|
40
|
+
|
|
41
|
+
FRONTMATTER_RE = re.compile(r"^---\s*\n(.*?)\n---\s*\n", re.DOTALL)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def default_skills_root() -> Path:
|
|
45
|
+
"""Derive the skills root from this script's location.
|
|
46
|
+
|
|
47
|
+
Layout assumption: {skills_root}/bmad-customize/scripts/list_customizable_skills.py.
|
|
48
|
+
So the skills root is three parents up from this file.
|
|
49
|
+
"""
|
|
50
|
+
return Path(__file__).resolve().parent.parent.parent
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def read_frontmatter_description(skill_md: Path) -> str:
|
|
54
|
+
"""Extract the `description:` value from a SKILL.md YAML frontmatter block.
|
|
55
|
+
|
|
56
|
+
Returns an empty string if the file is missing, unreadable, or has no
|
|
57
|
+
description field. Intentionally permissive — this is metadata for a
|
|
58
|
+
human-facing list, not a validation target.
|
|
59
|
+
"""
|
|
60
|
+
if not skill_md.is_file():
|
|
61
|
+
return ""
|
|
62
|
+
try:
|
|
63
|
+
text = skill_md.read_text(encoding="utf-8")
|
|
64
|
+
except (OSError, UnicodeDecodeError):
|
|
65
|
+
return ""
|
|
66
|
+
m = FRONTMATTER_RE.match(text)
|
|
67
|
+
if not m:
|
|
68
|
+
return ""
|
|
69
|
+
for line in m.group(1).splitlines():
|
|
70
|
+
stripped = line.strip()
|
|
71
|
+
if stripped.startswith("description:"):
|
|
72
|
+
value = stripped[len("description:") :].strip()
|
|
73
|
+
# Strip surrounding quotes if present.
|
|
74
|
+
if (value.startswith("'") and value.endswith("'")) or (
|
|
75
|
+
value.startswith('"') and value.endswith('"')
|
|
76
|
+
):
|
|
77
|
+
value = value[1:-1]
|
|
78
|
+
return value
|
|
79
|
+
return ""
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def load_customize(toml_path: Path) -> dict | None:
|
|
83
|
+
"""Return the parsed TOML, or None if unreadable."""
|
|
84
|
+
try:
|
|
85
|
+
with toml_path.open("rb") as f:
|
|
86
|
+
return tomllib.load(f)
|
|
87
|
+
except (OSError, tomllib.TOMLDecodeError):
|
|
88
|
+
return None
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def scan_skills(
|
|
92
|
+
skills_roots: list[Path],
|
|
93
|
+
project_root: Path,
|
|
94
|
+
) -> dict:
|
|
95
|
+
"""Scan each skills root for directories that contain a customize.toml."""
|
|
96
|
+
agents: list[dict] = []
|
|
97
|
+
workflows: list[dict] = []
|
|
98
|
+
errors: list[str] = []
|
|
99
|
+
scanned_roots: list[str] = []
|
|
100
|
+
seen_names: set[str] = set()
|
|
101
|
+
custom_dir = project_root / "_bmad" / "custom"
|
|
102
|
+
|
|
103
|
+
for root in skills_roots:
|
|
104
|
+
if not root.is_dir():
|
|
105
|
+
errors.append(f"skills root does not exist: {root}")
|
|
106
|
+
continue
|
|
107
|
+
scanned_roots.append(str(root))
|
|
108
|
+
|
|
109
|
+
for skill_dir in sorted(p for p in root.iterdir() if p.is_dir()):
|
|
110
|
+
customize_toml = skill_dir / "customize.toml"
|
|
111
|
+
if not customize_toml.is_file():
|
|
112
|
+
continue
|
|
113
|
+
|
|
114
|
+
data = load_customize(customize_toml)
|
|
115
|
+
if data is None:
|
|
116
|
+
errors.append(f"failed to parse {customize_toml}")
|
|
117
|
+
continue
|
|
118
|
+
|
|
119
|
+
skill_name = skill_dir.name
|
|
120
|
+
# If a skill with this name was already found in an earlier
|
|
121
|
+
# root, skip it — roots are scanned in the order provided, so
|
|
122
|
+
# the first occurrence wins.
|
|
123
|
+
if skill_name in seen_names:
|
|
124
|
+
continue
|
|
125
|
+
seen_names.add(skill_name)
|
|
126
|
+
|
|
127
|
+
description = read_frontmatter_description(skill_dir / "SKILL.md")
|
|
128
|
+
team_override = custom_dir / f"{skill_name}.toml"
|
|
129
|
+
user_override = custom_dir / f"{skill_name}.user.toml"
|
|
130
|
+
|
|
131
|
+
entry_base = {
|
|
132
|
+
"name": skill_name,
|
|
133
|
+
"install_path": str(skill_dir),
|
|
134
|
+
"skills_root": str(root),
|
|
135
|
+
"description": description,
|
|
136
|
+
"has_team_override": team_override.is_file(),
|
|
137
|
+
"has_user_override": user_override.is_file(),
|
|
138
|
+
"team_override_path": str(team_override),
|
|
139
|
+
"user_override_path": str(user_override),
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
# A skill may expose an agent surface, a workflow surface, or
|
|
143
|
+
# both. Emit one entry per surface so the caller can group cleanly.
|
|
144
|
+
surfaces_found = [k for k in SURFACE_KEYS if k in data]
|
|
145
|
+
if not surfaces_found:
|
|
146
|
+
errors.append(
|
|
147
|
+
f"no [agent] or [workflow] block in {customize_toml}"
|
|
148
|
+
)
|
|
149
|
+
continue
|
|
150
|
+
for surface in surfaces_found:
|
|
151
|
+
entry = dict(entry_base)
|
|
152
|
+
entry["surface"] = surface
|
|
153
|
+
if surface == "agent":
|
|
154
|
+
agents.append(entry)
|
|
155
|
+
else:
|
|
156
|
+
workflows.append(entry)
|
|
157
|
+
|
|
158
|
+
return {
|
|
159
|
+
"project_root": str(project_root),
|
|
160
|
+
"scanned_roots": scanned_roots,
|
|
161
|
+
"custom_dir": str(custom_dir),
|
|
162
|
+
"agents": agents,
|
|
163
|
+
"workflows": workflows,
|
|
164
|
+
"errors": errors,
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def parse_args(argv: list[str]) -> argparse.Namespace:
|
|
169
|
+
parser = argparse.ArgumentParser(
|
|
170
|
+
description=(
|
|
171
|
+
"List customizable BMad skills installed alongside this one, "
|
|
172
|
+
"grouped by surface (agent vs workflow), with override status "
|
|
173
|
+
"looked up against {project-root}/_bmad/custom/."
|
|
174
|
+
)
|
|
175
|
+
)
|
|
176
|
+
parser.add_argument(
|
|
177
|
+
"--project-root",
|
|
178
|
+
required=True,
|
|
179
|
+
help="Absolute path to the project root (the folder containing _bmad/).",
|
|
180
|
+
)
|
|
181
|
+
parser.add_argument(
|
|
182
|
+
"--skills-root",
|
|
183
|
+
default=None,
|
|
184
|
+
help=(
|
|
185
|
+
"Override the primary skills directory to scan. Defaults to the "
|
|
186
|
+
"directory this script's own skill lives in."
|
|
187
|
+
),
|
|
188
|
+
)
|
|
189
|
+
parser.add_argument(
|
|
190
|
+
"--extra-root",
|
|
191
|
+
action="append",
|
|
192
|
+
default=[],
|
|
193
|
+
metavar="PATH",
|
|
194
|
+
help=(
|
|
195
|
+
"Additional skills directory to include (repeatable). Useful "
|
|
196
|
+
"when skills live in multiple locations on the same machine "
|
|
197
|
+
"(e.g. project-local plus a user-global install)."
|
|
198
|
+
),
|
|
199
|
+
)
|
|
200
|
+
return parser.parse_args(argv)
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def main(argv: list[str]) -> int:
|
|
204
|
+
args = parse_args(argv)
|
|
205
|
+
project_root = Path(args.project_root).expanduser().resolve()
|
|
206
|
+
if not project_root.is_dir():
|
|
207
|
+
print(
|
|
208
|
+
f"error: project-root does not exist or is not a directory: {project_root}",
|
|
209
|
+
file=sys.stderr,
|
|
210
|
+
)
|
|
211
|
+
return 2
|
|
212
|
+
|
|
213
|
+
primary = (
|
|
214
|
+
Path(args.skills_root).expanduser().resolve()
|
|
215
|
+
if args.skills_root
|
|
216
|
+
else default_skills_root()
|
|
217
|
+
)
|
|
218
|
+
extras = [Path(p).expanduser().resolve() for p in args.extra_root]
|
|
219
|
+
# Deduplicate in order of appearance.
|
|
220
|
+
roots: list[Path] = []
|
|
221
|
+
for root in [primary, *extras]:
|
|
222
|
+
if root not in roots:
|
|
223
|
+
roots.append(root)
|
|
224
|
+
|
|
225
|
+
result = scan_skills(roots, project_root)
|
|
226
|
+
print(json.dumps(result, indent=2, sort_keys=True))
|
|
227
|
+
return 0
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
if __name__ == "__main__":
|
|
231
|
+
sys.exit(main(sys.argv[1:]))
|