✨ feat(workflow): adopt Matt Pocock ticket workflow
Replace the Superpowers plan pipeline with grill-with-docs, specs, local tickets, and ticket-native execution. BREAKING CHANGE: Remove the legacy Plan CLI, prompt templates, and Superpowers skills.
This commit is contained in:
+181
-17
@@ -76,9 +76,6 @@ no_backup = true
|
||||
project_name = "Demo"
|
||||
no_backup = true
|
||||
|
||||
[sync_prompts]
|
||||
no_backup = true
|
||||
|
||||
[sync_standards]
|
||||
langs = ["tsl", "markdown"]
|
||||
gitattr_mode = "overwrite"
|
||||
@@ -101,18 +98,128 @@ def seed_custom_files(project_root: Path) -> None:
|
||||
custom_memory.parent.mkdir(parents=True)
|
||||
custom_memory.write_text("custom memory\n", encoding="utf-8", newline="\n")
|
||||
|
||||
custom_prompt = project_root / "docs" / "prompts" / "custom.md"
|
||||
custom_prompt.parent.mkdir(parents=True)
|
||||
custom_prompt.write_text("custom prompt\n", encoding="utf-8", newline="\n")
|
||||
|
||||
(project_root / "AGENTS.md").write_text(
|
||||
"# Existing Agents\n\nKeep this agent note.\n\nSee `.agents/index.md`.\n",
|
||||
encoding="utf-8",
|
||||
newline="\n",
|
||||
)
|
||||
(project_root / "CLAUDE.md").write_text(
|
||||
"# Existing Claude\n\nKeep this.\n",
|
||||
"# Existing Claude\n\nKeep this Claude note.\n\n@AGENTS.md\n",
|
||||
encoding="utf-8",
|
||||
newline="\n",
|
||||
)
|
||||
|
||||
|
||||
class PlaybookDeploymentTests(unittest.TestCase):
|
||||
def test_help_lists_only_current_cli_options(self):
|
||||
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||
result = subprocess.run(
|
||||
[sys.executable, str(SCRIPT), "--help"],
|
||||
cwd=Path(tmp_dir),
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
|
||||
self.assertEqual(result.returncode, 0, msg=result.stderr)
|
||||
self.assertIn("-config PATH", result.stdout)
|
||||
self.assertIn("-h, --help", result.stdout)
|
||||
self.assertNotIn("-h, -help", result.stdout)
|
||||
|
||||
def test_install_all_excludes_legacy_superpowers_skills(self):
|
||||
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||
project_root = Path(tmp_dir) / "project"
|
||||
project_root.mkdir()
|
||||
config = project_root / "playbook.toml"
|
||||
config.write_text(
|
||||
"""
|
||||
[playbook]
|
||||
project_root = "."
|
||||
playbook_root = "custom/playbook"
|
||||
install_mode = "snapshot"
|
||||
|
||||
[install_skills]
|
||||
agents_home = ".test-agents"
|
||||
mode = "all"
|
||||
no_backup = true
|
||||
""".lstrip(),
|
||||
encoding="utf-8",
|
||||
newline="\n",
|
||||
)
|
||||
|
||||
result = run_playbook(SCRIPT, config, project_root)
|
||||
|
||||
self.assertEqual(
|
||||
result.returncode,
|
||||
0,
|
||||
msg=f"all-skills install failed\n{result.stdout}{result.stderr}",
|
||||
)
|
||||
installed = {
|
||||
path.name
|
||||
for path in (project_root / ".test-agents" / "skills").iterdir()
|
||||
if path.is_dir()
|
||||
}
|
||||
self.assertIn("grill-with-docs", installed)
|
||||
self.assertIn("to-tickets", installed)
|
||||
self.assertTrue(
|
||||
{
|
||||
"using-superpowers",
|
||||
"brainstorming",
|
||||
"writing-plans",
|
||||
"executing-plans",
|
||||
}.isdisjoint(installed)
|
||||
)
|
||||
|
||||
def test_install_skills_list_requires_explicit_skills(self):
|
||||
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||
project_root = Path(tmp_dir) / "project"
|
||||
project_root.mkdir()
|
||||
config = project_root / "playbook.toml"
|
||||
config.write_text(
|
||||
"""
|
||||
[playbook]
|
||||
project_root = "."
|
||||
playbook_root = "custom/playbook"
|
||||
install_mode = "snapshot"
|
||||
|
||||
[install_skills]
|
||||
agents_home = ".test-agents"
|
||||
mode = "list"
|
||||
bundles = ["matt-pocock-workflow"]
|
||||
no_backup = true
|
||||
""".lstrip(),
|
||||
encoding="utf-8",
|
||||
newline="\n",
|
||||
)
|
||||
|
||||
result = run_playbook(SCRIPT, config, project_root)
|
||||
|
||||
self.assertEqual(
|
||||
result.returncode,
|
||||
2,
|
||||
msg=f"bundles unexpectedly supported\n{result.stdout}{result.stderr}",
|
||||
)
|
||||
self.assertIn("ERROR: skills is required", result.stderr)
|
||||
|
||||
def test_invalid_toml_is_reported_without_a_traceback(self):
|
||||
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||
project_root = Path(tmp_dir) / "project"
|
||||
project_root.mkdir()
|
||||
config = project_root / "playbook.toml"
|
||||
config.write_text(
|
||||
r"""
|
||||
[playbook]
|
||||
project_root = "C:\workspace\project"
|
||||
""".lstrip(),
|
||||
encoding="utf-8",
|
||||
newline="\n",
|
||||
)
|
||||
|
||||
result = run_playbook(SCRIPT, config, project_root)
|
||||
|
||||
self.assertEqual(result.returncode, 2)
|
||||
self.assertIn("ERROR: invalid TOML", result.stderr)
|
||||
self.assertNotIn("Traceback", result.stderr)
|
||||
|
||||
def test_playbook_deployment_modes(self):
|
||||
for install_mode, playbook_root in MODE_ROOTS.items():
|
||||
with self.subTest(install_mode=install_mode):
|
||||
@@ -149,8 +256,7 @@ class PlaybookDeploymentTests(unittest.TestCase):
|
||||
"CLAUDE.md",
|
||||
".gitattributes",
|
||||
"memory-bank/project-brief.md",
|
||||
"memory-bank/active-context.md",
|
||||
"docs/prompts/system/agent-behavior.md",
|
||||
"memory-bank/system-patterns.md",
|
||||
".agents/index.md",
|
||||
".agents/tsl/index.md",
|
||||
".agents/markdown/index.md",
|
||||
@@ -171,17 +277,18 @@ class PlaybookDeploymentTests(unittest.TestCase):
|
||||
),
|
||||
"custom memory\n",
|
||||
)
|
||||
self.assertEqual(
|
||||
(project_root / "docs/prompts/custom.md").read_text(
|
||||
encoding="utf-8"
|
||||
),
|
||||
"custom prompt\n",
|
||||
agents_text = (project_root / "AGENTS.md").read_text(
|
||||
encoding="utf-8"
|
||||
)
|
||||
self.assertIn("Keep this agent note.", agents_text)
|
||||
self.assertEqual(
|
||||
agents_text.count("<!-- playbook:framework:start -->"), 1
|
||||
)
|
||||
|
||||
claude_text = (project_root / "CLAUDE.md").read_text(
|
||||
encoding="utf-8"
|
||||
)
|
||||
self.assertIn("Keep this.", claude_text)
|
||||
self.assertIn("Keep this Claude note.", claude_text)
|
||||
self.assertIn("@AGENTS.md", claude_text)
|
||||
self.assertEqual(
|
||||
claude_text.count("<!-- playbook:claude:start -->"), 1
|
||||
)
|
||||
@@ -232,10 +339,67 @@ class PlaybookDeploymentTests(unittest.TestCase):
|
||||
self.assertTrue(
|
||||
(snapshot_root / "scripts/playbook.py").is_file()
|
||||
)
|
||||
self.assertTrue(
|
||||
(snapshot_root / "scripts/main_loop.py").is_file()
|
||||
)
|
||||
self.assertTrue(
|
||||
(snapshot_root / "playbook.example.toml").is_file()
|
||||
)
|
||||
self.assertFalse(
|
||||
(snapshot_root / "playbook.toml.example").exists()
|
||||
)
|
||||
self.assertFalse(
|
||||
(snapshot_root / "scripts/ticket_loop.py").exists()
|
||||
)
|
||||
self.assertTrue((snapshot_root / "skills").is_dir())
|
||||
else:
|
||||
self.assertFalse((source_root / "SOURCE.md").exists())
|
||||
|
||||
def test_sync_without_standards_keeps_language_rules(self):
|
||||
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||
project_root = Path(tmp_dir) / "project"
|
||||
project_root.mkdir()
|
||||
playbook_root = MODE_ROOTS["snapshot"]
|
||||
|
||||
full_config = write_config(project_root, "snapshot", playbook_root)
|
||||
result = run_playbook(SCRIPT, full_config, project_root)
|
||||
self.assertEqual(
|
||||
result.returncode,
|
||||
0,
|
||||
msg=f"seed run failed\n{result.stdout}{result.stderr}",
|
||||
)
|
||||
|
||||
agents_md = project_root / "AGENTS.md"
|
||||
seeded = agents_md.read_text(encoding="utf-8")
|
||||
self.assertIn("`.agents/tsl/index.md`", seeded)
|
||||
self.assertIn("`.agents/markdown/index.md`", seeded)
|
||||
|
||||
partial_config = project_root / "playbook-partial.toml"
|
||||
partial_config.write_text(
|
||||
f"""
|
||||
[playbook]
|
||||
project_root = "."
|
||||
playbook_root = "{playbook_root.as_posix()}"
|
||||
install_mode = "snapshot"
|
||||
|
||||
[sync_memory_bank]
|
||||
project_name = "Demo"
|
||||
no_backup = true
|
||||
""".lstrip(),
|
||||
encoding="utf-8",
|
||||
newline="\n",
|
||||
)
|
||||
result = run_playbook(SCRIPT, partial_config, project_root)
|
||||
self.assertEqual(
|
||||
result.returncode,
|
||||
0,
|
||||
msg=f"partial run failed\n{result.stdout}{result.stderr}",
|
||||
)
|
||||
|
||||
after = agents_md.read_text(encoding="utf-8")
|
||||
self.assertIn("`.agents/tsl/index.md`", after)
|
||||
self.assertIn("`.agents/markdown/index.md`", after)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
Reference in New Issue
Block a user