diff --git a/docs/explanation/named-agents.md b/docs/explanation/named-agents.md index e5a92511c..c0063ba1c 100644 --- a/docs/explanation/named-agents.md +++ b/docs/explanation/named-agents.md @@ -36,7 +36,7 @@ BMad ships six named agents, each anchored to a phase of the BMad Method: | 📋 **John**, Product Manager | Planning | PRD creation, epic/story breakdown, implementation readiness | | 🎨 **Sally**, UX Designer | Planning | UX design specifications | | 🏗️ **Winston**, System Architect | Solutioning | technical architecture, alignment checks | -| 💻 **Amelia**, Senior Engineer | Implementation | story execution, quick-dev, code review, sprint planning | +| 💻 **Amelia**, Senior Engineer | Implementation | story execution, quick-dev, code review, sprint planning, [forensic investigation](./forensic-investigation.md) | They each have a hardcoded identity (name, title, domain) and a customizable layer (role, principles, communication style, icon, menu). You can rewrite Mary's principles or add menu items; you can't rename her — that's deliberate. Brand recognition survives customization so "hey Mary" always activates the analyst, regardless of how a team has shaped her behavior. diff --git a/docs/reference/agents.md b/docs/reference/agents.md index 4e05cde1b..bdc7d0871 100644 --- a/docs/reference/agents.md +++ b/docs/reference/agents.md @@ -20,7 +20,7 @@ This page lists the default BMM (Agile suite) agents that install with BMad Meth | Analyst (Mary) | `bmad-analyst` | `BP`, `MR`, `DR`, `TR`, `CB`, `WB`, `DP` | Brainstorm, Market Research, Domain Research, Technical Research, Create Brief, PRFAQ Challenge, Document Project | | Product Manager (John) | `bmad-pm` | `CP`, `VP`, `EP`, `CE`, `IR`, `CC` | Create/Validate/Edit PRD, Create Epics and Stories, Implementation Readiness, Correct Course | | Architect (Winston) | `bmad-architect` | `CA`, `IR` | Create Architecture, Implementation Readiness | -| Developer (Amelia) | `bmad-agent-dev` | `DS`, `QD`, `QA`, `CR`, `SP`, `CS`, `ER` | Dev Story, Quick Dev, QA Test Generation, Code Review, Sprint Planning, Create Story, Epic Retrospective | +| Developer (Amelia) | `bmad-agent-dev` | `DS`, `QD`, `QA`, `CR`, `SP`, `CS`, `ER`, `IN` | Dev Story, Quick Dev, QA Test Generation, Code Review, Sprint Planning, Create Story, Epic Retrospective, [Forensic Investigation](../explanation/forensic-investigation.md) | | UX Designer (Sally) | `bmad-ux-designer` | `CU` | Create UX Design | | Technical Writer (Paige) | `bmad-tech-writer` | `DP`, `WD`, `US`, `MG`, `VD`, `EC` | Document Project, Write Document, Update Standards, Mermaid Generate, Validate Doc, Explain Concept | diff --git a/src/scripts/resolve_customization.py b/src/scripts/resolve_customization.py index 28901ed0f..3299e1ade 100755 --- a/src/scripts/resolve_customization.py +++ b/src/scripts/resolve_customization.py @@ -177,6 +177,14 @@ def extract_key(data, dotted_key: str): return current +def write_json_stdout(output): + """Write JSON as UTF-8 so Windows cp1252 stdout can carry emoji icons.""" + reconfigure = getattr(sys.stdout, "reconfigure", None) + if reconfigure is not None: + reconfigure(encoding="utf-8") + sys.stdout.write(json.dumps(output, indent=2, ensure_ascii=False) + "\n") + + def main(): parser = argparse.ArgumentParser( description="Resolve customization for a BMad skill using three-layer TOML merge.", @@ -223,7 +231,7 @@ def main(): else: output = merged - sys.stdout.write(json.dumps(output, indent=2, ensure_ascii=False) + "\n") + write_json_stdout(output) if __name__ == "__main__": diff --git a/src/scripts/tests/test_resolve_customization.py b/src/scripts/tests/test_resolve_customization.py new file mode 100644 index 000000000..5ee112079 --- /dev/null +++ b/src/scripts/tests/test_resolve_customization.py @@ -0,0 +1,50 @@ +import json +import os +import subprocess +import sys +import tempfile +import unittest +from pathlib import Path + + +SCRIPT = Path(__file__).resolve().parents[1] / "resolve_customization.py" + + +class ResolveCustomizationStdoutTests(unittest.TestCase): + def test_writes_emoji_json_when_stdout_encoding_is_cp1252(self): + with tempfile.TemporaryDirectory() as temp_dir: + skill_dir = Path(temp_dir) / "emoji-agent" + skill_dir.mkdir() + (skill_dir / "customize.toml").write_text( + '[agent]\nname = "Emoji Agent"\nicon = "🧭"\n', + encoding="utf-8", + ) + + env = os.environ.copy() + env["PYTHONIOENCODING"] = "cp1252" + result = subprocess.run( + [ + sys.executable, + str(SCRIPT), + "--skill", + str(skill_dir), + "--key", + "agent", + ], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + env=env, + check=False, + ) + + stderr = result.stderr.decode("utf-8", errors="replace") + self.assertEqual(result.returncode, 0, msg=stderr) + + output = result.stdout.decode("utf-8") + self.assertIn("🧭", output) + resolved = json.loads(output) + self.assertEqual(resolved["agent"]["icon"], "🧭") + + +if __name__ == "__main__": + unittest.main()