import subprocess import sys import tempfile import unittest from pathlib import Path ROOT = Path(__file__).resolve().parents[2] SCRIPT = ROOT / "scripts" / "playbook.py" def run_cli(*args): return subprocess.run( [sys.executable, str(SCRIPT), *args], capture_output=True, text=True, ) class PlaybookCliTests(unittest.TestCase): def test_help_shows_usage(self): result = run_cli("-h") self.assertEqual(result.returncode, 0) self.assertIn("Usage:", result.stdout + result.stderr) def test_missing_config_is_error(self): result = run_cli() self.assertNotEqual(result.returncode, 0) self.assertIn("-config", result.stdout + result.stderr) def test_action_order(self): config_body = """ [playbook] project_root = "." [format_md] [sync_standards] langs = ["tsl"] """ with tempfile.TemporaryDirectory() as tmp_dir: config_path = Path(tmp_dir) / "playbook.toml" config_path.write_text(config_body, encoding="utf-8") result = run_cli("-config", str(config_path)) self.assertEqual(result.returncode, 0) output = result.stdout + result.stderr self.assertIn("sync_standards", output) self.assertIn("format_md", output) def test_vendor_creates_snapshot(self): with tempfile.TemporaryDirectory() as tmp_dir: config_body = f""" [playbook] project_root = "{tmp_dir}" [vendor] langs = ["tsl"] """ config_path = Path(tmp_dir) / "playbook.toml" config_path.write_text(config_body, encoding="utf-8") result = run_cli("-config", str(config_path)) snapshot = Path(tmp_dir) / "docs/standards/playbook/SOURCE.md" self.assertEqual(result.returncode, 0) self.assertTrue(snapshot.is_file()) def test_sync_memory_bank_creates_memory_bank(self): with tempfile.TemporaryDirectory() as tmp_dir: config_body = f""" [playbook] project_root = "{tmp_dir}" [sync_memory_bank] project_name = "Demo" """ config_path = Path(tmp_dir) / "playbook.toml" config_path.write_text(config_body, encoding="utf-8") result = run_cli("-config", str(config_path)) memory_bank = Path(tmp_dir) / "memory-bank/project-brief.md" self.assertEqual(result.returncode, 0) self.assertTrue(memory_bank.is_file()) def test_sync_standards_creates_agents(self): with tempfile.TemporaryDirectory() as tmp_dir: config_body = f""" [playbook] project_root = "{tmp_dir}" [sync_standards] langs = ["tsl"] """ config_path = Path(tmp_dir) / "playbook.toml" config_path.write_text(config_body, encoding="utf-8") result = run_cli("-config", str(config_path)) agents_index = Path(tmp_dir) / ".agents/tsl/index.md" self.assertEqual(result.returncode, 0) self.assertTrue(agents_index.is_file()) def test_sync_standards_agents_block_has_blank_lines(self): with tempfile.TemporaryDirectory() as tmp_dir: config_body = f""" [playbook] project_root = "{tmp_dir}" [sync_standards] langs = ["tsl"] """ config_path = Path(tmp_dir) / "playbook.toml" config_path.write_text(config_body, encoding="utf-8") result = run_cli("-config", str(config_path)) self.assertEqual(result.returncode, 0) agents_md = Path(tmp_dir) / "AGENTS.md" lines = agents_md.read_text(encoding="utf-8").splitlines() start_idx = lines.index("") end_idx = lines.index("") block = lines[start_idx : end_idx + 1] self.assertEqual(block[1], "") bullet_idx = next(i for i, line in enumerate(block) if line.startswith("- ")) self.assertEqual(block[bullet_idx - 1], "") def test_install_skills(self): with tempfile.TemporaryDirectory() as tmp_dir: target = Path(tmp_dir) / "codex" config_body = f""" [playbook] project_root = "{tmp_dir}" [install_skills] codex_home = "{target}" mode = "list" skills = ["brainstorming"] """ config_path = Path(tmp_dir) / "playbook.toml" config_path.write_text(config_body, encoding="utf-8") result = run_cli("-config", str(config_path)) skill_file = target / "skills/brainstorming/SKILL.md" self.assertEqual(result.returncode, 0) self.assertTrue(skill_file.is_file()) if __name__ == "__main__": unittest.main()