diff --git a/src/bmm-skills/1-analysis/bmad-agent-analyst/SKILL.md b/src/bmm-skills/1-analysis/bmad-agent-analyst/SKILL.md index 4653171df3..daa69616f7 100644 --- a/src/bmm-skills/1-analysis/bmad-agent-analyst/SKILL.md +++ b/src/bmm-skills/1-analysis/bmad-agent-analyst/SKILL.md @@ -46,7 +46,7 @@ Treat every entry in `{agent.persistent_facts}` as foundational context you carr ### Step 5: Load Config -Load config from `{project-root}/_bmad/bmm/config.yaml` and resolve: +Load config by running `python3 {project-root}/_bmad/scripts/resolve_config.py --project-root {project-root}` (requires Python 3.11+). If the command fails, read the merge logic in `{project-root}/_bmad/scripts/resolve_config.py` and apply it yourself to resolve the config variables. Resolve: - Use `{user_name}` for greeting - Use `{communication_language}` for all communications - Use `{document_output_language}` for output documents diff --git a/src/bmm-skills/1-analysis/bmad-agent-tech-writer/SKILL.md b/src/bmm-skills/1-analysis/bmad-agent-tech-writer/SKILL.md index ff6430d93b..8b57491636 100644 --- a/src/bmm-skills/1-analysis/bmad-agent-tech-writer/SKILL.md +++ b/src/bmm-skills/1-analysis/bmad-agent-tech-writer/SKILL.md @@ -46,7 +46,7 @@ Treat every entry in `{agent.persistent_facts}` as foundational context you carr ### Step 5: Load Config -Load config from `{project-root}/_bmad/bmm/config.yaml` and resolve: +Load config by running `python3 {project-root}/_bmad/scripts/resolve_config.py --project-root {project-root}` (requires Python 3.11+). If the command fails, read the merge logic in `{project-root}/_bmad/scripts/resolve_config.py` and apply it yourself to resolve the config variables. Resolve: - Use `{user_name}` for greeting - Use `{communication_language}` for all communications - Use `{document_output_language}` for output documents diff --git a/src/bmm-skills/1-analysis/bmad-document-project/SKILL.md b/src/bmm-skills/1-analysis/bmad-document-project/SKILL.md index 112732031c..0a628e2911 100644 --- a/src/bmm-skills/1-analysis/bmad-document-project/SKILL.md +++ b/src/bmm-skills/1-analysis/bmad-document-project/SKILL.md @@ -40,7 +40,7 @@ Treat every entry in `{workflow.persistent_facts}` as foundational context you c ### Step 4: Load Config -Load config from `{project-root}/_bmad/bmm/config.yaml` and resolve: +Load config by running `python3 {project-root}/_bmad/scripts/resolve_config.py --project-root {project-root}` (requires Python 3.11+). If the command fails, read the merge logic in `{project-root}/_bmad/scripts/resolve_config.py` and apply it yourself to resolve the config variables. Resolve: - Use `{user_name}` for greeting - Use `{communication_language}` for all communications - Use `{document_output_language}` for output documents diff --git a/src/bmm-skills/1-analysis/bmad-document-project/workflows/deep-dive-workflow.md b/src/bmm-skills/1-analysis/bmad-document-project/workflows/deep-dive-workflow.md index c55f036a7d..1796ea8d80 100644 --- a/src/bmm-skills/1-analysis/bmad-document-project/workflows/deep-dive-workflow.md +++ b/src/bmm-skills/1-analysis/bmad-document-project/workflows/deep-dive-workflow.md @@ -11,7 +11,7 @@ ### Configuration Loading -Load config from `{project-root}/_bmad/bmm/config.yaml` and resolve: +Load config by running `python3 {project-root}/_bmad/scripts/resolve_config.py --project-root {project-root}` (requires Python 3.11+). If the command fails, read the merge logic in `{project-root}/_bmad/scripts/resolve_config.py` and apply it yourself to resolve the config variables. Resolve: - `project_knowledge` - `user_name` diff --git a/src/bmm-skills/1-analysis/bmad-document-project/workflows/full-scan-workflow.md b/src/bmm-skills/1-analysis/bmad-document-project/workflows/full-scan-workflow.md index 5aaf4a5800..367179ec38 100644 --- a/src/bmm-skills/1-analysis/bmad-document-project/workflows/full-scan-workflow.md +++ b/src/bmm-skills/1-analysis/bmad-document-project/workflows/full-scan-workflow.md @@ -10,7 +10,7 @@ ### Configuration Loading -Load config from `{project-root}/_bmad/bmm/config.yaml` and resolve: +Load config by running `python3 {project-root}/_bmad/scripts/resolve_config.py --project-root {project-root}` (requires Python 3.11+). If the command fails, read the merge logic in `{project-root}/_bmad/scripts/resolve_config.py` and apply it yourself to resolve the config variables. Resolve: - `project_knowledge` - `user_name` diff --git a/src/bmm-skills/1-analysis/bmad-prfaq/SKILL.md b/src/bmm-skills/1-analysis/bmad-prfaq/SKILL.md index 6ce2d33ed6..55338d6f99 100644 --- a/src/bmm-skills/1-analysis/bmad-prfaq/SKILL.md +++ b/src/bmm-skills/1-analysis/bmad-prfaq/SKILL.md @@ -50,7 +50,7 @@ Treat every entry in `{workflow.persistent_facts}` as foundational context you c ### Step 4: Load Config -Load config from `{project-root}/_bmad/bmm/config.yaml` and resolve: +Load config by running `python3 {project-root}/_bmad/scripts/resolve_config.py --project-root {project-root}` (requires Python 3.11+). If the command fails, read the merge logic in `{project-root}/_bmad/scripts/resolve_config.py` and apply it yourself to resolve the config variables. Resolve: - Use `{user_name}` for greeting - Use `{communication_language}` for all communications - Use `{document_output_language}` for output documents diff --git a/src/bmm-skills/1-analysis/bmad-product-brief/SKILL.md b/src/bmm-skills/1-analysis/bmad-product-brief/SKILL.md index 8d697259ec..773ba80215 100644 --- a/src/bmm-skills/1-analysis/bmad-product-brief/SKILL.md +++ b/src/bmm-skills/1-analysis/bmad-product-brief/SKILL.md @@ -59,7 +59,7 @@ Treat every entry in `{workflow.persistent_facts}` as foundational context you c ### Step 4: Load Config -Load config from `{project-root}/_bmad/bmm/config.yaml` and resolve: +Load config by running `python3 {project-root}/_bmad/scripts/resolve_config.py --project-root {project-root}` (requires Python 3.11+). If the command fails, read the merge logic in `{project-root}/_bmad/scripts/resolve_config.py` and apply it yourself to resolve the config variables. Resolve: - Use `{user_name}` for greeting - Use `{communication_language}` for all communications - Use `{document_output_language}` for output documents diff --git a/src/bmm-skills/1-analysis/research/bmad-domain-research/SKILL.md b/src/bmm-skills/1-analysis/research/bmad-domain-research/SKILL.md index be364aa2f9..d4fa24e058 100644 --- a/src/bmm-skills/1-analysis/research/bmad-domain-research/SKILL.md +++ b/src/bmm-skills/1-analysis/research/bmad-domain-research/SKILL.md @@ -44,7 +44,7 @@ Treat every entry in `{workflow.persistent_facts}` as foundational context you c ### Step 4: Load Config -Load config from `{project-root}/_bmad/bmm/config.yaml` and resolve: +Load config by running `python3 {project-root}/_bmad/scripts/resolve_config.py --project-root {project-root}` (requires Python 3.11+). If the command fails, read the merge logic in `{project-root}/_bmad/scripts/resolve_config.py` and apply it yourself to resolve the config variables. Resolve: - Use `{user_name}` for greeting - Use `{communication_language}` for all communications - Use `{document_output_language}` for output documents diff --git a/src/bmm-skills/1-analysis/research/bmad-market-research/SKILL.md b/src/bmm-skills/1-analysis/research/bmad-market-research/SKILL.md index 9640490859..eb96b59f84 100644 --- a/src/bmm-skills/1-analysis/research/bmad-market-research/SKILL.md +++ b/src/bmm-skills/1-analysis/research/bmad-market-research/SKILL.md @@ -44,7 +44,7 @@ Treat every entry in `{workflow.persistent_facts}` as foundational context you c ### Step 4: Load Config -Load config from `{project-root}/_bmad/bmm/config.yaml` and resolve: +Load config by running `python3 {project-root}/_bmad/scripts/resolve_config.py --project-root {project-root}` (requires Python 3.11+). If the command fails, read the merge logic in `{project-root}/_bmad/scripts/resolve_config.py` and apply it yourself to resolve the config variables. Resolve: - Use `{user_name}` for greeting - Use `{communication_language}` for all communications - Use `{document_output_language}` for output documents diff --git a/src/bmm-skills/1-analysis/research/bmad-technical-research/SKILL.md b/src/bmm-skills/1-analysis/research/bmad-technical-research/SKILL.md index 582a05c60b..e46a82239e 100644 --- a/src/bmm-skills/1-analysis/research/bmad-technical-research/SKILL.md +++ b/src/bmm-skills/1-analysis/research/bmad-technical-research/SKILL.md @@ -44,7 +44,7 @@ Treat every entry in `{workflow.persistent_facts}` as foundational context you c ### Step 4: Load Config -Load config from `{project-root}/_bmad/bmm/config.yaml` and resolve: +Load config by running `python3 {project-root}/_bmad/scripts/resolve_config.py --project-root {project-root}` (requires Python 3.11+). If the command fails, read the merge logic in `{project-root}/_bmad/scripts/resolve_config.py` and apply it yourself to resolve the config variables. Resolve: - Use `{user_name}` for greeting - Use `{communication_language}` for all communications - Use `{document_output_language}` for output documents diff --git a/src/bmm-skills/2-plan-workflows/bmad-agent-pm/SKILL.md b/src/bmm-skills/2-plan-workflows/bmad-agent-pm/SKILL.md index 6930726032..9ab429a273 100644 --- a/src/bmm-skills/2-plan-workflows/bmad-agent-pm/SKILL.md +++ b/src/bmm-skills/2-plan-workflows/bmad-agent-pm/SKILL.md @@ -46,7 +46,7 @@ Treat every entry in `{agent.persistent_facts}` as foundational context you carr ### Step 5: Load Config -Load config from `{project-root}/_bmad/bmm/config.yaml` and resolve: +Load config by running `python3 {project-root}/_bmad/scripts/resolve_config.py --project-root {project-root}` (requires Python 3.11+). If the command fails, read the merge logic in `{project-root}/_bmad/scripts/resolve_config.py` and apply it yourself to resolve the config variables. Resolve: - Use `{user_name}` for greeting - Use `{communication_language}` for all communications - Use `{document_output_language}` for output documents diff --git a/src/bmm-skills/2-plan-workflows/bmad-agent-ux-designer/SKILL.md b/src/bmm-skills/2-plan-workflows/bmad-agent-ux-designer/SKILL.md index cb261c3fbd..2e666a9b80 100644 --- a/src/bmm-skills/2-plan-workflows/bmad-agent-ux-designer/SKILL.md +++ b/src/bmm-skills/2-plan-workflows/bmad-agent-ux-designer/SKILL.md @@ -46,7 +46,7 @@ Treat every entry in `{agent.persistent_facts}` as foundational context you carr ### Step 5: Load Config -Load config from `{project-root}/_bmad/bmm/config.yaml` and resolve: +Load config by running `python3 {project-root}/_bmad/scripts/resolve_config.py --project-root {project-root}` (requires Python 3.11+). If the command fails, read the merge logic in `{project-root}/_bmad/scripts/resolve_config.py` and apply it yourself to resolve the config variables. Resolve: - Use `{user_name}` for greeting - Use `{communication_language}` for all communications - Use `{document_output_language}` for output documents diff --git a/src/bmm-skills/2-plan-workflows/bmad-create-prd/SKILL.md b/src/bmm-skills/2-plan-workflows/bmad-create-prd/SKILL.md index 1ad02d01d9..c32eff2fe6 100644 --- a/src/bmm-skills/2-plan-workflows/bmad-create-prd/SKILL.md +++ b/src/bmm-skills/2-plan-workflows/bmad-create-prd/SKILL.md @@ -73,7 +73,7 @@ Treat every entry in `{workflow.persistent_facts}` as foundational context you c ### Step 4: Load Config -Load config from `{project-root}/_bmad/bmm/config.yaml` and resolve: +Load config by running `python3 {project-root}/_bmad/scripts/resolve_config.py --project-root {project-root}` (requires Python 3.11+). If the command fails, read the merge logic in `{project-root}/_bmad/scripts/resolve_config.py` and apply it yourself to resolve the config variables. Resolve: - Use `{user_name}` for greeting - Use `{communication_language}` for all communications - Use `{document_output_language}` for output documents diff --git a/src/bmm-skills/2-plan-workflows/bmad-create-ux-design/SKILL.md b/src/bmm-skills/2-plan-workflows/bmad-create-ux-design/SKILL.md index 496473b1e4..76f8656812 100644 --- a/src/bmm-skills/2-plan-workflows/bmad-create-ux-design/SKILL.md +++ b/src/bmm-skills/2-plan-workflows/bmad-create-ux-design/SKILL.md @@ -47,7 +47,7 @@ Treat every entry in `{workflow.persistent_facts}` as foundational context you c ### Step 4: Load Config -Load config from `{project-root}/_bmad/bmm/config.yaml` and resolve: +Load config by running `python3 {project-root}/_bmad/scripts/resolve_config.py --project-root {project-root}` (requires Python 3.11+). If the command fails, read the merge logic in `{project-root}/_bmad/scripts/resolve_config.py` and apply it yourself to resolve the config variables. Resolve: - Use `{user_name}` for greeting - Use `{communication_language}` for all communications - Use `{document_output_language}` for output documents diff --git a/src/bmm-skills/2-plan-workflows/bmad-edit-prd/SKILL.md b/src/bmm-skills/2-plan-workflows/bmad-edit-prd/SKILL.md index e209df3407..3b5c7526c2 100644 --- a/src/bmm-skills/2-plan-workflows/bmad-edit-prd/SKILL.md +++ b/src/bmm-skills/2-plan-workflows/bmad-edit-prd/SKILL.md @@ -73,7 +73,7 @@ Treat every entry in `{workflow.persistent_facts}` as foundational context you c ### Step 4: Load Config -Load config from `{project-root}/_bmad/bmm/config.yaml` and resolve: +Load config by running `python3 {project-root}/_bmad/scripts/resolve_config.py --project-root {project-root}` (requires Python 3.11+). If the command fails, read the merge logic in `{project-root}/_bmad/scripts/resolve_config.py` and apply it yourself to resolve the config variables. Resolve: - Use `{user_name}` for greeting - Use `{communication_language}` for all communications - Use `{document_output_language}` for output documents diff --git a/src/bmm-skills/2-plan-workflows/bmad-validate-prd/SKILL.md b/src/bmm-skills/2-plan-workflows/bmad-validate-prd/SKILL.md index 90ec68f17d..28c2797eae 100644 --- a/src/bmm-skills/2-plan-workflows/bmad-validate-prd/SKILL.md +++ b/src/bmm-skills/2-plan-workflows/bmad-validate-prd/SKILL.md @@ -73,7 +73,7 @@ Treat every entry in `{workflow.persistent_facts}` as foundational context you c ### Step 4: Load Config -Load config from `{project-root}/_bmad/bmm/config.yaml` and resolve: +Load config by running `python3 {project-root}/_bmad/scripts/resolve_config.py --project-root {project-root}` (requires Python 3.11+). If the command fails, read the merge logic in `{project-root}/_bmad/scripts/resolve_config.py` and apply it yourself to resolve the config variables. Resolve: - Use `{user_name}` for greeting - Use `{communication_language}` for all communications - Use `{document_output_language}` for output documents diff --git a/src/bmm-skills/3-solutioning/bmad-agent-architect/SKILL.md b/src/bmm-skills/3-solutioning/bmad-agent-architect/SKILL.md index 1650aee095..52e9829b4f 100644 --- a/src/bmm-skills/3-solutioning/bmad-agent-architect/SKILL.md +++ b/src/bmm-skills/3-solutioning/bmad-agent-architect/SKILL.md @@ -46,7 +46,7 @@ Treat every entry in `{agent.persistent_facts}` as foundational context you carr ### Step 5: Load Config -Load config from `{project-root}/_bmad/bmm/config.yaml` and resolve: +Load config by running `python3 {project-root}/_bmad/scripts/resolve_config.py --project-root {project-root}` (requires Python 3.11+). If the command fails, read the merge logic in `{project-root}/_bmad/scripts/resolve_config.py` and apply it yourself to resolve the config variables. Resolve: - Use `{user_name}` for greeting - Use `{communication_language}` for all communications - Use `{document_output_language}` for output documents diff --git a/src/bmm-skills/3-solutioning/bmad-check-implementation-readiness/SKILL.md b/src/bmm-skills/3-solutioning/bmad-check-implementation-readiness/SKILL.md index 1d5133f90a..725ce3e317 100644 --- a/src/bmm-skills/3-solutioning/bmad-check-implementation-readiness/SKILL.md +++ b/src/bmm-skills/3-solutioning/bmad-check-implementation-readiness/SKILL.md @@ -69,7 +69,7 @@ Treat every entry in `{workflow.persistent_facts}` as foundational context you c ### Step 4: Load Config -Load config from `{project-root}/_bmad/bmm/config.yaml` and resolve: +Load config by running `python3 {project-root}/_bmad/scripts/resolve_config.py --project-root {project-root}` (requires Python 3.11+). If the command fails, read the merge logic in `{project-root}/_bmad/scripts/resolve_config.py` and apply it yourself to resolve the config variables. Resolve: - Use `{user_name}` for greeting - Use `{communication_language}` for all communications - Use `{document_output_language}` for output documents diff --git a/src/bmm-skills/3-solutioning/bmad-create-architecture/SKILL.md b/src/bmm-skills/3-solutioning/bmad-create-architecture/SKILL.md index ca89a71cf5..ebe478f73a 100644 --- a/src/bmm-skills/3-solutioning/bmad-create-architecture/SKILL.md +++ b/src/bmm-skills/3-solutioning/bmad-create-architecture/SKILL.md @@ -50,7 +50,7 @@ Treat every entry in `{workflow.persistent_facts}` as foundational context you c ### Step 4: Load Config -Load config from `{project-root}/_bmad/bmm/config.yaml` and resolve: +Load config by running `python3 {project-root}/_bmad/scripts/resolve_config.py --project-root {project-root}` (requires Python 3.11+). If the command fails, read the merge logic in `{project-root}/_bmad/scripts/resolve_config.py` and apply it yourself to resolve the config variables. Resolve: - Use `{user_name}` for greeting - Use `{communication_language}` for all communications - Use `{document_output_language}` for output documents diff --git a/src/bmm-skills/3-solutioning/bmad-create-epics-and-stories/SKILL.md b/src/bmm-skills/3-solutioning/bmad-create-epics-and-stories/SKILL.md index a3f0f61c81..c535de75e8 100644 --- a/src/bmm-skills/3-solutioning/bmad-create-epics-and-stories/SKILL.md +++ b/src/bmm-skills/3-solutioning/bmad-create-epics-and-stories/SKILL.md @@ -71,7 +71,7 @@ Treat every entry in `{workflow.persistent_facts}` as foundational context you c ### Step 4: Load Config -Load config from `{project-root}/_bmad/bmm/config.yaml` and resolve: +Load config by running `python3 {project-root}/_bmad/scripts/resolve_config.py --project-root {project-root}` (requires Python 3.11+). If the command fails, read the merge logic in `{project-root}/_bmad/scripts/resolve_config.py` and apply it yourself to resolve the config variables. Resolve: - Use `{user_name}` for greeting - Use `{communication_language}` for all communications - Use `{document_output_language}` for output documents diff --git a/src/bmm-skills/3-solutioning/bmad-generate-project-context/SKILL.md b/src/bmm-skills/3-solutioning/bmad-generate-project-context/SKILL.md index 42fd2e8fcf..13e712fcdd 100644 --- a/src/bmm-skills/3-solutioning/bmad-generate-project-context/SKILL.md +++ b/src/bmm-skills/3-solutioning/bmad-generate-project-context/SKILL.md @@ -50,7 +50,7 @@ Treat every entry in `{workflow.persistent_facts}` as foundational context you c ### Step 4: Load Config -Load config from `{project-root}/_bmad/bmm/config.yaml` and resolve: +Load config by running `python3 {project-root}/_bmad/scripts/resolve_config.py --project-root {project-root}` (requires Python 3.11+). If the command fails, read the merge logic in `{project-root}/_bmad/scripts/resolve_config.py` and apply it yourself to resolve the config variables. Resolve: - Use `{user_name}` for greeting - Use `{communication_language}` for all communications - Use `{document_output_language}` for output documents diff --git a/src/bmm-skills/4-implementation/bmad-agent-dev/SKILL.md b/src/bmm-skills/4-implementation/bmad-agent-dev/SKILL.md index 95a3b95947..4d2ec3711d 100644 --- a/src/bmm-skills/4-implementation/bmad-agent-dev/SKILL.md +++ b/src/bmm-skills/4-implementation/bmad-agent-dev/SKILL.md @@ -46,7 +46,7 @@ Treat every entry in `{agent.persistent_facts}` as foundational context you carr ### Step 5: Load Config -Load config from `{project-root}/_bmad/bmm/config.yaml` and resolve: +Load config by running `python3 {project-root}/_bmad/scripts/resolve_config.py --project-root {project-root}` (requires Python 3.11+). If the command fails, read the merge logic in `{project-root}/_bmad/scripts/resolve_config.py` and apply it yourself to resolve the config variables. Resolve: - Use `{user_name}` for greeting - Use `{communication_language}` for all communications - Use `{document_output_language}` for output documents diff --git a/src/bmm-skills/4-implementation/bmad-checkpoint-preview/SKILL.md b/src/bmm-skills/4-implementation/bmad-checkpoint-preview/SKILL.md index 101dcf2bc8..8439e4d6ca 100644 --- a/src/bmm-skills/4-implementation/bmad-checkpoint-preview/SKILL.md +++ b/src/bmm-skills/4-implementation/bmad-checkpoint-preview/SKILL.md @@ -40,7 +40,7 @@ Treat every entry in `{workflow.persistent_facts}` as foundational context you c ### Step 4: Load Config -Load config from `{project-root}/_bmad/bmm/config.yaml` and resolve: +Load config by running `python3 {project-root}/_bmad/scripts/resolve_config.py --project-root {project-root}` (requires Python 3.11+). If the command fails, read the merge logic in `{project-root}/_bmad/scripts/resolve_config.py` and apply it yourself to resolve the config variables. Resolve: - `implementation_artifacts` - `planning_artifacts` @@ -63,6 +63,18 @@ Activation is complete. Begin the workflow below. - **Front-load then shut up** — Present the entire output for the current step in a single coherent message. Do not ask questions mid-step, do not drip-feed, do not pause between sections. - **Language** — Speak in `{communication_language}`. Write any file output in `{document_output_language}`. +<<<<<<< HEAD +======= +## INITIALIZATION + +Load config by running `python3 {project-root}/_bmad/scripts/resolve_config.py --project-root {project-root}` (requires Python 3.11+). If the command fails, read the merge logic in `{project-root}/_bmad/scripts/resolve_config.py` and apply it yourself to resolve the config variables. Resolve: + +- `implementation_artifacts` +- `planning_artifacts` +- `communication_language` +- `document_output_language` + +>>>>>>> 3846e184 (fix(skills): use resolve_config.py instead of reading config.yaml directly) ## FIRST STEP Read fully and follow `./step-01-orientation.md` to begin. diff --git a/src/bmm-skills/4-implementation/bmad-code-review/SKILL.md b/src/bmm-skills/4-implementation/bmad-code-review/SKILL.md index 44223f11a9..909a86477b 100644 --- a/src/bmm-skills/4-implementation/bmad-code-review/SKILL.md +++ b/src/bmm-skills/4-implementation/bmad-code-review/SKILL.md @@ -40,7 +40,7 @@ Treat every entry in `{workflow.persistent_facts}` as foundational context you c ### Step 4: Load Config -Load config from `{project-root}/_bmad/bmm/config.yaml` and resolve: +Load config by running `python3 {project-root}/_bmad/scripts/resolve_config.py --project-root {project-root}` (requires Python 3.11+). If the command fails, read the merge logic in `{project-root}/_bmad/scripts/resolve_config.py` and apply it yourself to resolve the config variables. Resolve: - `project_name`, `planning_artifacts`, `implementation_artifacts`, `user_name` - `communication_language`, `document_output_language`, `user_skill_level` diff --git a/src/bmm-skills/4-implementation/bmad-correct-course/SKILL.md b/src/bmm-skills/4-implementation/bmad-correct-course/SKILL.md index adea0bda04..0456d63dd3 100644 --- a/src/bmm-skills/4-implementation/bmad-correct-course/SKILL.md +++ b/src/bmm-skills/4-implementation/bmad-correct-course/SKILL.md @@ -40,7 +40,7 @@ Treat every entry in `{workflow.persistent_facts}` as foundational context you c ### Step 4: Load Config -Load config from `{project-root}/_bmad/bmm/config.yaml` and resolve: +Load config by running `python3 {project-root}/_bmad/scripts/resolve_config.py --project-root {project-root}` (requires Python 3.11+). If the command fails, read the merge logic in `{project-root}/_bmad/scripts/resolve_config.py` and apply it yourself to resolve the config variables. Resolve: - `project_name`, `user_name` - `communication_language`, `document_output_language` diff --git a/src/bmm-skills/4-implementation/bmad-create-story/SKILL.md b/src/bmm-skills/4-implementation/bmad-create-story/SKILL.md index cf14039c10..516095eeec 100644 --- a/src/bmm-skills/4-implementation/bmad-create-story/SKILL.md +++ b/src/bmm-skills/4-implementation/bmad-create-story/SKILL.md @@ -47,7 +47,7 @@ Treat every entry in `{workflow.persistent_facts}` as foundational context you c ### Step 4: Load Config -Load config from `{project-root}/_bmad/bmm/config.yaml` and resolve: +Load config by running `python3 {project-root}/_bmad/scripts/resolve_config.py --project-root {project-root}` (requires Python 3.11+). If the command fails, read the merge logic in `{project-root}/_bmad/scripts/resolve_config.py` and apply it yourself to resolve the config variables. Resolve: - `project_name`, `user_name` - `communication_language`, `document_output_language` diff --git a/src/bmm-skills/4-implementation/bmad-dev-story/SKILL.md b/src/bmm-skills/4-implementation/bmad-dev-story/SKILL.md index 218b234ab6..3db2e99549 100644 --- a/src/bmm-skills/4-implementation/bmad-dev-story/SKILL.md +++ b/src/bmm-skills/4-implementation/bmad-dev-story/SKILL.md @@ -47,7 +47,7 @@ Treat every entry in `{workflow.persistent_facts}` as foundational context you c ### Step 4: Load Config -Load config from `{project-root}/_bmad/bmm/config.yaml` and resolve: +Load config by running `python3 {project-root}/_bmad/scripts/resolve_config.py --project-root {project-root}` (requires Python 3.11+). If the command fails, read the merge logic in `{project-root}/_bmad/scripts/resolve_config.py` and apply it yourself to resolve the config variables. Resolve: - `project_name`, `user_name` - `communication_language`, `document_output_language` diff --git a/src/bmm-skills/4-implementation/bmad-qa-generate-e2e-tests/SKILL.md b/src/bmm-skills/4-implementation/bmad-qa-generate-e2e-tests/SKILL.md index ef9d7e87a3..655130c712 100644 --- a/src/bmm-skills/4-implementation/bmad-qa-generate-e2e-tests/SKILL.md +++ b/src/bmm-skills/4-implementation/bmad-qa-generate-e2e-tests/SKILL.md @@ -40,7 +40,7 @@ Treat every entry in `{workflow.persistent_facts}` as foundational context you c ### Step 4: Load Config -Load config from `{project-root}/_bmad/bmm/config.yaml` and resolve: +Load config by running `python3 {project-root}/_bmad/scripts/resolve_config.py --project-root {project-root}` (requires Python 3.11+). If the command fails, read the merge logic in `{project-root}/_bmad/scripts/resolve_config.py` and apply it yourself to resolve the config variables. Resolve: - `project_name`, `user_name` - `communication_language`, `document_output_language` diff --git a/src/bmm-skills/4-implementation/bmad-quick-dev/SKILL.md b/src/bmm-skills/4-implementation/bmad-quick-dev/SKILL.md index f5326fc3f9..c0ba1b8a1e 100644 --- a/src/bmm-skills/4-implementation/bmad-quick-dev/SKILL.md +++ b/src/bmm-skills/4-implementation/bmad-quick-dev/SKILL.md @@ -59,7 +59,7 @@ Treat every entry in `{workflow.persistent_facts}` as foundational context you c ### Step 4: Load Config -Load config from `{project-root}/_bmad/bmm/config.yaml` and resolve: +Load config by running `python3 {project-root}/_bmad/scripts/resolve_config.py --project-root {project-root}` (requires Python 3.11+). If the command fails, read the merge logic in `{project-root}/_bmad/scripts/resolve_config.py` and apply it yourself to resolve the config variables. Resolve: - `project_name`, `planning_artifacts`, `implementation_artifacts`, `user_name` - `communication_language`, `document_output_language`, `user_skill_level` diff --git a/src/bmm-skills/4-implementation/bmad-retrospective/SKILL.md b/src/bmm-skills/4-implementation/bmad-retrospective/SKILL.md index b6d0c96c6f..e1cdb27644 100644 --- a/src/bmm-skills/4-implementation/bmad-retrospective/SKILL.md +++ b/src/bmm-skills/4-implementation/bmad-retrospective/SKILL.md @@ -56,7 +56,7 @@ Treat every entry in `{workflow.persistent_facts}` as foundational context you c ### Step 4: Load Config -Load config from `{project-root}/_bmad/bmm/config.yaml` and resolve: +Load config by running `python3 {project-root}/_bmad/scripts/resolve_config.py --project-root {project-root}` (requires Python 3.11+). If the command fails, read the merge logic in `{project-root}/_bmad/scripts/resolve_config.py` and apply it yourself to resolve the config variables. Resolve: - `project_name`, `user_name` - `communication_language`, `document_output_language` diff --git a/src/bmm-skills/4-implementation/bmad-sprint-planning/SKILL.md b/src/bmm-skills/4-implementation/bmad-sprint-planning/SKILL.md index 25266d716a..887bd45bf7 100644 --- a/src/bmm-skills/4-implementation/bmad-sprint-planning/SKILL.md +++ b/src/bmm-skills/4-implementation/bmad-sprint-planning/SKILL.md @@ -40,7 +40,7 @@ Treat every entry in `{workflow.persistent_facts}` as foundational context you c ### Step 4: Load Config -Load config from `{project-root}/_bmad/bmm/config.yaml` and resolve: +Load config by running `python3 {project-root}/_bmad/scripts/resolve_config.py --project-root {project-root}` (requires Python 3.11+). If the command fails, read the merge logic in `{project-root}/_bmad/scripts/resolve_config.py` and apply it yourself to resolve the config variables. Resolve: - `project_name`, `user_name` - `communication_language`, `document_output_language` diff --git a/src/bmm-skills/4-implementation/bmad-sprint-status/SKILL.md b/src/bmm-skills/4-implementation/bmad-sprint-status/SKILL.md index c52a849474..57273ba05b 100644 --- a/src/bmm-skills/4-implementation/bmad-sprint-status/SKILL.md +++ b/src/bmm-skills/4-implementation/bmad-sprint-status/SKILL.md @@ -40,7 +40,7 @@ Treat every entry in `{workflow.persistent_facts}` as foundational context you c ### Step 4: Load Config -Load config from `{project-root}/_bmad/bmm/config.yaml` and resolve: +Load config by running `python3 {project-root}/_bmad/scripts/resolve_config.py --project-root {project-root}` (requires Python 3.11+). If the command fails, read the merge logic in `{project-root}/_bmad/scripts/resolve_config.py` and apply it yourself to resolve the config variables. Resolve: - `project_name`, `user_name` - `communication_language`, `document_output_language` diff --git a/src/core-skills/bmad-brainstorming/workflow.md b/src/core-skills/bmad-brainstorming/workflow.md index 168dab93ec..7eccef9b73 100644 --- a/src/core-skills/bmad-brainstorming/workflow.md +++ b/src/core-skills/bmad-brainstorming/workflow.md @@ -32,7 +32,7 @@ This uses **micro-file architecture** for disciplined execution: ### Configuration Loading -Load config from `{project-root}/_bmad/core/config.yaml` and resolve: +Load config by running `python3 {project-root}/_bmad/scripts/resolve_config.py --project-root {project-root}` (requires Python 3.11+). If the command fails, read the merge logic in `{project-root}/_bmad/scripts/resolve_config.py` and apply it yourself to resolve the config variables. Resolve: - `project_name`, `output_folder`, `user_name` - `communication_language`, `document_output_language`, `user_skill_level` diff --git a/src/core-skills/bmad-help/SKILL.md b/src/core-skills/bmad-help/SKILL.md index e829543cfc..82ddcf72e0 100644 --- a/src/core-skills/bmad-help/SKILL.md +++ b/src/core-skills/bmad-help/SKILL.md @@ -23,7 +23,7 @@ When this skill completes, the user should: ## Data Sources - **Catalog**: `{project-root}/_bmad/_config/bmad-help.csv` — assembled manifest of all installed module skills -- **Config**: `config.yaml` and `user-config.yaml` files in `{project-root}/_bmad/` and its subfolders — resolve `output-location` variables, provide `communication_language` and `project_knowledge` +- **Config**: Run `python3 {project-root}/_bmad/scripts/resolve_config.py --project-root {project-root}` to get merged config. If that fails, read the merge logic in `{project-root}/_bmad/scripts/resolve_config.py` and apply it yourself — resolve `output-location` variables, provide `communication_language` and `project_knowledge` - **Artifacts**: Files matching `outputs` patterns at resolved `output-location` paths reveal which steps are possibly completed; their content may also provide grounding context for recommendations - **Project knowledge**: If `project_knowledge` resolves to an existing path, read it for grounding context. Never fabricate project-specific details. - **Module docs**: Rows with `_meta` in the `skill` column carry a URL or path in `output-location` pointing to the module's documentation (e.g., llms.txt). Fetch and use these to answer general questions about that module. diff --git a/src/core-skills/bmad-party-mode/SKILL.md b/src/core-skills/bmad-party-mode/SKILL.md index 6f4ee3e63b..6f3906da8a 100644 --- a/src/core-skills/bmad-party-mode/SKILL.md +++ b/src/core-skills/bmad-party-mode/SKILL.md @@ -22,7 +22,7 @@ Party mode accepts optional arguments when invoked: 1. **Parse arguments** — check for `--model` and `--solo` flags from the user's invocation. -2. Load config from `{project-root}/_bmad/core/config.yaml` and resolve: +2. Load config by running `python3 {project-root}/_bmad/scripts/resolve_config.py --project-root {project-root}` (requires Python 3.11+). If the command fails, read the merge logic in `{project-root}/_bmad/scripts/resolve_config.py` and apply it yourself to resolve the config variables. Resolve: - Use `{user_name}` for greeting - Use `{communication_language}` for all communications diff --git a/src/scripts/resolve_config.py b/src/scripts/resolve_config.py index eb9e20288a..5f6c8d7bd3 100644 --- a/src/scripts/resolve_config.py +++ b/src/scripts/resolve_config.py @@ -1,12 +1,13 @@ #!/usr/bin/env python3 """ -Resolve BMad's central config using four-layer TOML merge. +Resolve BMad's central config using five-layer TOML merge. -Reads from four layers (highest priority last): +Reads from five layers (highest priority last): 1. {project-root}/_bmad/config.toml (installer-owned team) 2. {project-root}/_bmad/config.user.toml (installer-owned user) - 3. {project-root}/_bmad/custom/config.toml (human-authored team, committed) - 4. {project-root}/_bmad/custom/config.user.toml (human-authored user, gitignored) + 3. ~/.bmad/config/config.user.toml (global user preferences) + 4. {project-root}/_bmad/custom/config.toml (human-authored team, committed) + 5. {project-root}/_bmad/custom/config.user.toml (human-authored user, gitignored) Outputs merged JSON to stdout. Errors go to stderr. @@ -40,6 +41,7 @@ _MISSING = object() _KEYED_MERGE_FIELDS = ("code", "id") +GLOBAL_DIR = Path.home() / ".bmad" / "config" def load_toml(file_path: Path, required: bool = False) -> dict: @@ -136,7 +138,7 @@ def extract_key(data, dotted_key: str): def main(): parser = argparse.ArgumentParser( - description="Resolve BMad central config using four-layer TOML merge.", + description="Resolve BMad central config using five-layer TOML merge.", ) parser.add_argument( "--project-root", "-p", required=True, @@ -153,10 +155,12 @@ def main(): base_team = load_toml(bmad_dir / "config.toml", required=True) base_user = load_toml(bmad_dir / "config.user.toml") + global_user = load_toml(GLOBAL_DIR / "config.user.toml") custom_team = load_toml(bmad_dir / "custom" / "config.toml") custom_user = load_toml(bmad_dir / "custom" / "config.user.toml") merged = deep_merge(base_team, base_user) + merged = deep_merge(merged, global_user) merged = deep_merge(merged, custom_team) merged = deep_merge(merged, custom_user) diff --git a/src/scripts/resolve_customization.py b/src/scripts/resolve_customization.py index 28901ed0f1..d8af2d26f1 100755 --- a/src/scripts/resolve_customization.py +++ b/src/scripts/resolve_customization.py @@ -1,11 +1,12 @@ #!/usr/bin/env python3 """ -Resolve customization for a BMad skill using three-layer TOML merge. +Resolve customization for a BMad skill using four-layer TOML merge. -Reads customization from three layers (highest priority first): +Reads customization from four layers (highest priority first): 1. {project-root}/_bmad/custom/{name}.user.toml (personal, gitignored) 2. {project-root}/_bmad/custom/{name}.toml (team/org, committed) - 3. {skill-root}/customize.toml (skill defaults) + 3. ~/.bmad/config/{name}.user.toml (global user preferences) + 4. {skill-root}/customize.toml (skill defaults) Skill name is derived from the basename of the skill directory. @@ -51,6 +52,7 @@ _MISSING = object() _KEYED_MERGE_FIELDS = ("code", "id") +GLOBAL_DIR = Path.home() / ".bmad" / "config" def find_project_root(start: Path): @@ -179,7 +181,7 @@ def extract_key(data, dotted_key: str): def main(): parser = argparse.ArgumentParser( - description="Resolve customization for a BMad skill using three-layer TOML merge.", + description="Resolve customization for a BMad skill using four-layer TOML merge.", add_help=True, ) parser.add_argument( @@ -211,7 +213,10 @@ def main(): team = load_toml(custom_dir / f"{skill_name}.toml") user = load_toml(custom_dir / f"{skill_name}.user.toml") - merged = deep_merge(defaults, team) + global_user = load_toml(GLOBAL_DIR / f"{skill_name}.user.toml") + + merged = deep_merge(defaults, global_user) + merged = deep_merge(merged, team) merged = deep_merge(merged, user) if args.key: diff --git a/test/test-config-resolution.js b/test/test-config-resolution.js new file mode 100644 index 0000000000..e19baf103b --- /dev/null +++ b/test/test-config-resolution.js @@ -0,0 +1,341 @@ +/** + * Config Resolution Tests + * + * Tests the Python config resolution scripts by invoking them as subprocesses + * with temporary TOML fixtures. Validates: + * - Global user layer is loaded and merged with correct priority + * - Project layers override global layers + * - Missing global dir doesn't break anything (backward compat) + * - resolve_customization.py global skill layer + * + * Usage: node test/test-config-resolution.js + */ + +const path = require('node:path'); +const os = require('node:os'); +const fs = require('node:fs/promises'); +const { execSync } = require('node:child_process'); + +const SCRIPTS_DIR = path.resolve(__dirname, '..', 'src', 'scripts'); + +const colors = { + reset: '', + green: '', + red: '', + dim: '', +}; + +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++; + } +} + +function writeToml(filePath, data) { + const lines = []; + for (const [key, val] of Object.entries(data)) { + if (typeof val === 'string') { + lines.push(`${key} = "${val}"`); + } else if (typeof val === 'number' || typeof val === 'boolean') { + lines.push(`${key} = ${val}`); + } + } + return fs.writeFile(filePath, lines.join('\n') + '\n'); +} + +async function withTempDir(fn) { + const dir = await fs.mkdtemp(path.join(os.tmpdir(), 'bmad-config-test-')); + try { + return await fn(dir); + } finally { + await fs.rm(dir, { recursive: true, force: true }); + } +} + +async function testResolveConfig() { + console.log('\n--- resolve_config.py ---\n'); + + // Test 1: global user overrides installer defaults + await withTempDir(async (tmpDir) => { + const globalDir = path.join(tmpDir, '.bmad', 'config'); + await fs.mkdir(globalDir, { recursive: true }); + await writeToml(path.join(globalDir, 'config.user.toml'), { + user_name: 'GlobalAlice', + communication_language: 'en', + }); + + const projectDir = path.join(tmpDir, 'project'); + const bmadDir = path.join(projectDir, '_bmad'); + await fs.mkdir(path.join(bmadDir, 'custom'), { recursive: true }); + await writeToml(path.join(bmadDir, 'config.toml'), { + project_name: 'TestProject', + user_name: 'InstallerDefault', + }); + await writeToml(path.join(bmadDir, 'config.user.toml'), { + user_name: 'InstallerUserDefault', + }); + + const origHome = process.env.HOME; + process.env.HOME = tmpDir; + try { + const result = JSON.parse(execSync(`python3 ${SCRIPTS_DIR}/resolve_config.py --project-root ${projectDir}`, { encoding: 'utf-8' })); + + assert( + result.user_name === 'GlobalAlice', + 'global user overrides installer defaults', + `Expected "GlobalAlice", got "${result.user_name}"`, + ); + assert( + result.communication_language === 'en', + 'global communication_language preserved when installer has no override', + `Expected "en", got "${result.communication_language}"`, + ); + assert( + result.project_name === 'TestProject', + 'installer team config values preserved', + `Expected "TestProject", got "${result.project_name}"`, + ); + } finally { + process.env.HOME = origHome; + } + }); + + // Test 2: missing global dir — backward compat + await withTempDir(async (tmpDir) => { + const projectDir = path.join(tmpDir, 'project'); + const bmadDir = path.join(projectDir, '_bmad'); + await fs.mkdir(path.join(bmadDir, 'custom'), { recursive: true }); + await writeToml(path.join(bmadDir, 'config.toml'), { + project_name: 'NoGlobalProject', + }); + + const origHome = process.env.HOME; + process.env.HOME = tmpDir; + try { + const result = JSON.parse(execSync(`python3 ${SCRIPTS_DIR}/resolve_config.py --project-root ${projectDir}`, { encoding: 'utf-8' })); + + assert( + result.project_name === 'NoGlobalProject', + 'works fine without global config dir', + `Expected "NoGlobalProject", got "${result.project_name}"`, + ); + } finally { + process.env.HOME = origHome; + } + }); + + // Test 3: full priority chain — base_team < base_user < global < custom_team < custom_user + await withTempDir(async (tmpDir) => { + const globalDir = path.join(tmpDir, '.bmad', 'config'); + await fs.mkdir(globalDir, { recursive: true }); + await writeToml(path.join(globalDir, 'config.user.toml'), { + user_name: 'L2-Global', + }); + + const projectDir = path.join(tmpDir, 'project'); + const bmadDir = path.join(projectDir, '_bmad'); + await fs.mkdir(path.join(bmadDir, 'custom'), { recursive: true }); + await writeToml(path.join(bmadDir, 'config.toml'), { user_name: 'L0-BaseTeam' }); + await writeToml(path.join(bmadDir, 'config.user.toml'), { user_name: 'L1-BaseUser' }); + await writeToml(path.join(bmadDir, 'custom', 'config.toml'), { user_name: 'L3-CustomTeam' }); + await writeToml(path.join(bmadDir, 'custom', 'config.user.toml'), { user_name: 'L4-CustomUser' }); + + const origHome = process.env.HOME; + process.env.HOME = tmpDir; + try { + const result = JSON.parse(execSync(`python3 ${SCRIPTS_DIR}/resolve_config.py --project-root ${projectDir}`, { encoding: 'utf-8' })); + + assert( + result.user_name === 'L4-CustomUser', + 'highest priority layer (custom user) wins', + `Expected "L4-CustomUser", got "${result.user_name}"`, + ); + } finally { + process.env.HOME = origHome; + } + }); + + // Test 4: --key flag works with global layer + await withTempDir(async (tmpDir) => { + const globalDir = path.join(tmpDir, '.bmad', 'config'); + await fs.mkdir(globalDir, { recursive: true }); + await writeToml(path.join(globalDir, 'config.user.toml'), { + user_name: 'KeyTestGlobal', + communication_language: 'fr', + }); + + const projectDir = path.join(tmpDir, 'project'); + const bmadDir = path.join(projectDir, '_bmad'); + await fs.mkdir(path.join(bmadDir, 'custom'), { recursive: true }); + await writeToml(path.join(bmadDir, 'config.toml'), { + project_name: 'KeyTestProject', + }); + + const origHome = process.env.HOME; + process.env.HOME = tmpDir; + try { + const result = JSON.parse( + execSync(`python3 ${SCRIPTS_DIR}/resolve_config.py --project-root ${projectDir} --key user_name --key communication_language`, { + encoding: 'utf-8', + }), + ); + + assert( + Object.keys(result).length === 2, + '--key returns only requested keys', + `Expected 2 keys, got ${Object.keys(result).length}: ${JSON.stringify(Object.keys(result))}`, + ); + assert( + result.user_name === 'KeyTestGlobal', + '--key user_name returns global value', + `Expected "KeyTestGlobal", got "${result.user_name}"`, + ); + assert( + result.communication_language === 'fr', + '--key communication_language returns global value', + `Expected "fr", got "${result.communication_language}"`, + ); + } finally { + process.env.HOME = origHome; + } + }); +} + +async function testResolveCustomization() { + console.log('\n--- resolve_customization.py ---\n'); + + // Test 1: global skill user overrides skill defaults + await withTempDir(async (tmpDir) => { + const globalDir = path.join(tmpDir, '.bmad', 'config'); + await fs.mkdir(globalDir, { recursive: true }); + await writeToml(path.join(globalDir, 'test-skill.user.toml'), { + agent: 'global-agent-prompt', + }); + + const skillDir = path.join(tmpDir, 'skill', 'test-skill'); + await fs.mkdir(skillDir, { recursive: true }); + await writeToml(path.join(skillDir, 'customize.toml'), { + agent: 'default-agent-prompt', + version: '1.0.0', + }); + + const origHome = process.env.HOME; + process.env.HOME = tmpDir; + try { + const result = JSON.parse(execSync(`python3 ${SCRIPTS_DIR}/resolve_customization.py --skill ${skillDir}`, { encoding: 'utf-8' })); + + assert( + result.agent === 'global-agent-prompt', + 'global user overrides skill defaults', + `Expected "global-agent-prompt", got "${result.agent}"`, + ); + assert(result.version === '1.0.0', 'skill default values preserved', `Expected "1.0.0", got "${result.version}"`); + } finally { + process.env.HOME = origHome; + } + }); + + // Test 2: global skill layer provides value not in defaults + await withTempDir(async (tmpDir) => { + const globalDir = path.join(tmpDir, '.bmad', 'config'); + await fs.mkdir(globalDir, { recursive: true }); + await writeToml(path.join(globalDir, 'test-skill.user.toml'), { + extra_global_key: 'from-global', + }); + + const skillDir = path.join(tmpDir, 'skill', 'test-skill'); + await fs.mkdir(skillDir, { recursive: true }); + await writeToml(path.join(skillDir, 'customize.toml'), { + version: '2.0.0', + }); + + const origHome = process.env.HOME; + process.env.HOME = tmpDir; + try { + const result = JSON.parse(execSync(`python3 ${SCRIPTS_DIR}/resolve_customization.py --skill ${skillDir}`, { encoding: 'utf-8' })); + + assert( + result.extra_global_key === 'from-global', + 'global key not in defaults is preserved', + `Expected "from-global", got "${result.extra_global_key}"`, + ); + assert(result.version === '2.0.0', 'skill defaults still present', `Expected "2.0.0", got "${result.version}"`); + } finally { + process.env.HOME = origHome; + } + }); + + // Test 3: missing global dir — backward compat + await withTempDir(async (tmpDir) => { + const skillDir = path.join(tmpDir, 'skill', 'test-skill'); + await fs.mkdir(skillDir, { recursive: true }); + await writeToml(path.join(skillDir, 'customize.toml'), { + version: '3.0.0', + }); + + const origHome = process.env.HOME; + process.env.HOME = tmpDir; + try { + const result = JSON.parse(execSync(`python3 ${SCRIPTS_DIR}/resolve_customization.py --skill ${skillDir}`, { encoding: 'utf-8' })); + + assert(result.version === '3.0.0', 'works without global config dir', `Expected "3.0.0", got "${result.version}"`); + } finally { + process.env.HOME = origHome; + } + }); + + // Test 4: full priority chain — defaults < global < team < user + await withTempDir(async (tmpDir) => { + const globalDir = path.join(tmpDir, '.bmad', 'config'); + await fs.mkdir(globalDir, { recursive: true }); + await writeToml(path.join(globalDir, 'test-skill.user.toml'), { + agent: 'L1-Global', + }); + + const skillDir = path.join(tmpDir, 'project', '_bmad', 'skills', 'test-skill'); + await fs.mkdir(skillDir, { recursive: true }); + await writeToml(path.join(skillDir, 'customize.toml'), { agent: 'L0-Defaults' }); + + const customDir = path.join(tmpDir, 'project', '_bmad', 'custom'); + await fs.mkdir(customDir, { recursive: true }); + await writeToml(path.join(customDir, 'test-skill.toml'), { agent: 'L2-Team' }); + await writeToml(path.join(customDir, 'test-skill.user.toml'), { agent: 'L3-User' }); + + const origHome = process.env.HOME; + process.env.HOME = tmpDir; + try { + const result = JSON.parse(execSync(`python3 ${SCRIPTS_DIR}/resolve_customization.py --skill ${skillDir}`, { encoding: 'utf-8' })); + + assert(result.agent === 'L3-User', 'highest priority layer (project user) wins', `Expected "L3-User", got "${result.agent}"`); + } finally { + process.env.HOME = origHome; + } + }); +} + +async function main() { + console.log('Config Resolution Tests\n'); + + try { + await testResolveConfig(); + await testResolveCustomization(); + } catch (error) { + console.error(`${colors.red}Fatal error: ${error.message}${colors.reset}`); + process.exit(1); + } + + console.log(`\n${colors.green}${passed} passed${colors.reset}, ${colors.red}${failed} failed${colors.reset}`); + process.exit(failed > 0 ? 1 : 0); +} + +main();