Skip to content

Commit 54ed736

Browse files
jawwad-aliclaude
andauthored
fix(agents): resolve skill placeholders in Goose (yaml) command output (#3374)
* fix(agents): resolve skill placeholders in Goose (yaml) command output CommandRegistrar.register_commands resolves {SCRIPT}/__AGENT__ and the $ARGUMENTS placeholder in the markdown and toml branches, but the yaml branch (Goose recipes) called render_yaml_command directly, skipping both. So extension/preset command bodies installed for Goose kept literal {SCRIPT}, __AGENT__, and repo-relative script paths in the generated .goose/recipes/*.yaml prompt. Mirror the markdown/toml branches: run resolve_skill_placeholders + _convert_argument_placeholder on the body before render_yaml_command. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(goose): assert positive placeholder replacements in recipe prompt Per review: parse the generated recipe with yaml.safe_load and assert the prompt contains the resolved values (.specify/scripts/, 'agent goose', {{args}}), not merely that the literal tokens are absent — a wrong output that happens to omit the exact strings would otherwise pass. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
1 parent 7b1065d commit 54ed736

2 files changed

Lines changed: 53 additions & 0 deletions

File tree

src/specify_cli/agents.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -701,6 +701,12 @@ def register_commands(
701701
)
702702
output = self.render_toml_command(frontmatter, body, source_id)
703703
elif agent_config["format"] == "yaml":
704+
body = self.resolve_skill_placeholders(
705+
agent_name, frontmatter, body, project_root
706+
)
707+
body = self._convert_argument_placeholder(
708+
body, "$ARGUMENTS", agent_config["args"]
709+
)
704710
output = self.render_yaml_command(
705711
frontmatter, body, source_id, cmd_name
706712
)

tests/integrations/test_integration_goose.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,50 @@ def test_setup_declares_args_parameter_for_args_prompt(self, tmp_path):
3636
param.get("key") == "args"
3737
for param in data.get("parameters", [])
3838
), f"{recipe_file} uses {{{{args}}}} but does not declare args"
39+
40+
41+
class TestGooseCommandPlaceholderResolution:
42+
"""register_commands must resolve skill placeholders for the yaml branch.
43+
44+
The yaml (Goose recipe) branch previously skipped
45+
resolve_skill_placeholders / _convert_argument_placeholder that the
46+
markdown and toml branches apply, so extension/preset command bodies
47+
kept literal {SCRIPT} / __AGENT__ / repo-relative paths.
48+
"""
49+
50+
def test_register_commands_resolves_placeholders_in_recipe(self, tmp_path):
51+
from specify_cli.agents import CommandRegistrar
52+
53+
ext_dir = tmp_path / "extension"
54+
cmd_dir = ext_dir / "commands"
55+
cmd_dir.mkdir(parents=True)
56+
cmd_file = cmd_dir / "example.md"
57+
cmd_file.write_text(
58+
"---\n"
59+
"description: Placeholder command\n"
60+
"scripts:\n"
61+
" sh: scripts/bash/do.sh\n"
62+
" ps: scripts/powershell/do.ps1\n"
63+
"---\n\n"
64+
"Run {SCRIPT} for agent __AGENT__ with $ARGUMENTS.\n",
65+
encoding="utf-8",
66+
)
67+
68+
registrar = CommandRegistrar()
69+
commands = [{"name": "speckit.example", "file": "commands/example.md"}]
70+
registrar.register_commands("goose", commands, "test-ext", ext_dir, tmp_path)
71+
72+
recipe = tmp_path / ".goose" / "recipes" / "speckit.example.yaml"
73+
assert recipe.exists(), "goose recipe should be generated"
74+
# Parse the recipe and assert the prompt actually got the correct
75+
# replacements — not merely that the literal tokens are absent (which
76+
# a wrong-but-token-free output could also satisfy).
77+
data = yaml.safe_load(recipe.read_text(encoding="utf-8"))
78+
prompt = data["prompt"]
79+
assert ".specify/scripts/" in prompt # {SCRIPT} -> resolved script path
80+
assert "agent goose" in prompt # __AGENT__ -> agent name
81+
assert "{{args}}" in prompt # $ARGUMENTS -> goose args token
82+
# And the raw placeholders must not survive.
83+
assert "{SCRIPT}" not in prompt
84+
assert "__AGENT__" not in prompt
85+
assert "$ARGUMENTS" not in prompt

0 commit comments

Comments
 (0)