From 866d3421bf7bb249c9477046af664aa57117f4c6 Mon Sep 17 00:00:00 2001 From: jawwad-ali Date: Wed, 22 Jul 2026 21:18:02 +0500 Subject: [PATCH] fix(integrations): Cline overrides post_process_command_content (correct hook name) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ClineIntegration defined its command-content transform as post_process_content, but the overridable base hook is IntegrationBase.post_process_command_content, which CommandRegistrar.register_commands() dispatches to for every non-skills integration. Because the names differed, Cline's method never overrode the base hook, so extension/preset command files registered for Cline silently ran the base no-op and never received Cline's dot-to-hyphen hook-command note (_inject_hook_command_note) — the note that tells the agent to replace dots with hyphens when invoking hook commands. (Handoff references are already hyphenated independently by the registrar's _hyphenate_body_refs, so that transform was unaffected; renaming simply makes Cline's own copy run too, harmlessly, since both are idempotent.) Rename to post_process_command_content (matching the base hook and the post_process_skill_content convention used by claude/copilot/agy/kimi/ droid/vibe) and update the single internal caller in setup(). Cline's own setup() post-processing of core commands is unchanged. No test referenced the old name. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.8 (1M context) --- .../integrations/cline/__init__.py | 12 +++++-- tests/test_post_process.py | 31 +++++++++++++++++++ 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/src/specify_cli/integrations/cline/__init__.py b/src/specify_cli/integrations/cline/__init__.py index 248f44d54d..c3ea3cc409 100644 --- a/src/specify_cli/integrations/cline/__init__.py +++ b/src/specify_cli/integrations/cline/__init__.py @@ -138,8 +138,14 @@ def _rewrite_handoff_references(content: str) -> str: content, ) - def post_process_content(self, content: str) -> str: - """Apply Cline-specific transformations to command content.""" + def post_process_command_content(self, content: str) -> str: + """Apply Cline-specific transformations to command content. + + Overrides the ``IntegrationBase`` hook of the same name so that + ``CommandRegistrar.register_commands()`` (which dispatches to + ``post_process_command_content``) applies these transforms to + extension/preset command files too, not just core commands. + """ updated = self._inject_hook_command_note(content) updated = self._rewrite_handoff_references(updated) return updated @@ -169,7 +175,7 @@ def setup( content_bytes = path.read_bytes() content = content_bytes.decode("utf-8") - updated = self.post_process_content(content) + updated = self.post_process_command_content(content) if updated != content: path.write_bytes(updated.encode("utf-8")) diff --git a/tests/test_post_process.py b/tests/test_post_process.py index 34e947e9fb..12003f6a07 100644 --- a/tests/test_post_process.py +++ b/tests/test_post_process.py @@ -243,3 +243,34 @@ def test_plain_template_unchanged( assert output_file.exists(), f"Output file missing for {agent}" content = output_file.read_text(encoding="utf-8") assert body_text.strip() in content, f"Body text missing in {agent} output" + + +class TestClineRealPostProcess: + """Cline's real command-content transforms (hook-command note + handoff + dot->hyphen rewrite) must run for extension/preset commands registered via + CommandRegistrar. This exercises the REAL method (not a monkeypatched + marker), so it fails if Cline's override does not match the base hook name + the registrar dispatches to (post_process_command_content).""" + + def test_cline_transforms_applied_via_registrar( + self, tmp_path, registrar, ext_dir + ): + ext, cmd_dir = ext_dir + body = ( + "- For each executable hook, output the following:\n" + "agent: speckit.foo\n" + ) + _write_cmd(cmd_dir, body=body) + + commands = [{"name": "speckit.test.review", "file": "commands/review.md"}] + registrar.register_commands("cline", commands, "test-ext", ext, tmp_path) + + outputs = list((tmp_path / ".clinerules" / "workflows").rglob("*.md")) + assert outputs, "no cline command file was written" + content = outputs[0].read_text(encoding="utf-8") + + # _inject_hook_command_note fired (its note text contains "replace dots") + assert "replace dots" in content + # _rewrite_handoff_references rewrote the dotted agent handoff + assert "agent: speckit-foo" in content + assert "agent: speckit.foo" not in content