fix(integrations): Cline overrides post_process_command_content (correct hook name) (#3657)

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) <noreply@anthropic.com>
This commit is contained in:
Ali jawwad
2026-07-23 02:26:24 +05:00
committed by GitHub
parent 37041087dd
commit 38eb2fcc4b
2 changed files with 40 additions and 3 deletions

View File

@@ -138,8 +138,14 @@ class ClineIntegration(MarkdownIntegration):
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 @@ class ClineIntegration(MarkdownIntegration):
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"))

View File

@@ -243,3 +243,34 @@ class TestRegressionPlainTemplate:
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