mirror of
https://github.com/github/spec-kit.git
synced 2026-07-06 14:01:01 +08:00
fix(presets): preserve argument-hint in preset SKILL.md generation (#2978)
* fix(presets): preserve argument-hint in preset SKILL.md generation Preset-provided and extension-override commands that declare `argument-hint:` in their frontmatter had it dropped from the generated Claude SKILL.md, and it was re-dropped when a preset was removed and its overridden skill restored. This is the preset-side analog of the extension fix in #2903 / #2916. Factor the argument-hint carry-over into a shared CommandRegistrar.apply_argument_hint() helper and apply it at the four preset skill-generation sites (register, reconcile override-restore, and the core/extension unregister-restore paths). The extension path from The helper writes argument-hint into the frontmatter dict before serialization (so a folded multi-line description cannot be split into invalid YAML) and only for integrations that support it (those exposing inject_argument_hint -- currently Claude), leaving build_skill_frontmatter's shared shape unchanged for every other agent. Core templates carry no argument-hint, so the core-restore path is a no-op. No behavior change for non-Claude agents or the core path. Add regression tests covering a folding description (Claude) and the non-Claude gate (codex). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(presets): address review - guard skill_frontmatter type and tighten apply_argument_hint annotations Add a symmetric isinstance(skill_frontmatter, dict) guard so the helper stays a safe no-op if a caller passes a non-dict, and annotate the parameters as Dict[str, Any] with an optional integration to match real call-site usage. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -356,6 +356,33 @@ class CommandRegistrar:
|
||||
}
|
||||
return skill_frontmatter
|
||||
|
||||
@staticmethod
|
||||
def apply_argument_hint(
|
||||
source_frontmatter: Dict[str, Any],
|
||||
skill_frontmatter: Dict[str, Any],
|
||||
integration: Optional[object] = None,
|
||||
) -> None:
|
||||
"""Carry a command's ``argument-hint`` into its generated skill frontmatter.
|
||||
|
||||
Copies ``argument-hint`` from the parsed source command frontmatter into
|
||||
*skill_frontmatter* (mutated in place) before serialization, so that a
|
||||
folded multi-line ``description`` cannot be split into invalid YAML. Only
|
||||
integrations that support the field — those exposing
|
||||
``inject_argument_hint`` (currently Claude) — receive the key, leaving
|
||||
:meth:`build_skill_frontmatter`'s shared shape unchanged for every other
|
||||
agent. Built-in templates carry no ``argument-hint``, so this is a no-op
|
||||
for the core path.
|
||||
"""
|
||||
if not isinstance(source_frontmatter, dict) or not isinstance(skill_frontmatter, dict):
|
||||
return
|
||||
argument_hint = source_frontmatter.get("argument-hint")
|
||||
if (
|
||||
argument_hint
|
||||
and integration is not None
|
||||
and hasattr(integration, "inject_argument_hint")
|
||||
):
|
||||
skill_frontmatter["argument-hint"] = str(argument_hint)
|
||||
|
||||
@staticmethod
|
||||
def resolve_skill_placeholders(
|
||||
agent_name: str, frontmatter: dict, body: str, project_root: Path
|
||||
|
||||
@@ -1061,20 +1061,10 @@ class ExtensionManager:
|
||||
)
|
||||
# Preserve the command's argument-hint in the generated skill,
|
||||
# mirroring the core template path (ClaudeIntegration.setup injects
|
||||
# it for built-in commands). The value is added to the frontmatter
|
||||
# dict before serialization — rather than via the string-based
|
||||
# inject_argument_hint helper — so that a folded multi-line
|
||||
# description cannot be split by the inserted line. Gated on the
|
||||
# integration exposing inject_argument_hint so only argument-hint
|
||||
# aware agents receive the key, leaving build_skill_frontmatter's
|
||||
# shared shape unchanged for every other agent.
|
||||
argument_hint = frontmatter.get("argument-hint")
|
||||
if (
|
||||
argument_hint
|
||||
and integration is not None
|
||||
and hasattr(integration, "inject_argument_hint")
|
||||
):
|
||||
frontmatter_data["argument-hint"] = str(argument_hint)
|
||||
# it for built-in commands). See CommandRegistrar.apply_argument_hint
|
||||
# for why the value is added to the dict before serialization rather
|
||||
# than via the string-based inject_argument_hint helper.
|
||||
registrar.apply_argument_hint(frontmatter, frontmatter_data, integration)
|
||||
frontmatter_text = dump_frontmatter(frontmatter_data)
|
||||
|
||||
# Derive a human-friendly title from the command name
|
||||
|
||||
@@ -1064,11 +1064,14 @@ class PresetManager:
|
||||
body = self._resolve_skill_command_refs(
|
||||
body, registrar, selected_ai
|
||||
)
|
||||
from ..integrations import get_integration
|
||||
integration = get_integration(selected_ai) if isinstance(selected_ai, str) else None
|
||||
fm_data = registrar.build_skill_frontmatter(
|
||||
selected_ai if isinstance(selected_ai, str) else "",
|
||||
skill_name, desc,
|
||||
f"override:{cmd_name}",
|
||||
)
|
||||
registrar.apply_argument_hint(fm, fm_data, integration)
|
||||
fm_text = dump_frontmatter(fm_data)
|
||||
skill_title = self._skill_title_from_command(cmd_name)
|
||||
skill_content = (
|
||||
@@ -1076,8 +1079,6 @@ class PresetManager:
|
||||
f"# Speckit {skill_title} Skill\n\n{body}\n"
|
||||
)
|
||||
# Apply integration post-processing (e.g. Claude flags)
|
||||
from ..integrations import get_integration
|
||||
integration = get_integration(selected_ai) if isinstance(selected_ai, str) else None
|
||||
if integration is not None and hasattr(integration, "post_process_skill_content"):
|
||||
skill_content = integration.post_process_skill_content(skill_content)
|
||||
skill_file.write_text(skill_content, encoding="utf-8")
|
||||
@@ -1346,6 +1347,7 @@ class PresetManager:
|
||||
enhanced_desc,
|
||||
f"preset:{manifest.id}",
|
||||
)
|
||||
registrar.apply_argument_hint(frontmatter, frontmatter_data, integration)
|
||||
frontmatter_text = dump_frontmatter(frontmatter_data)
|
||||
skill_content = (
|
||||
f"---\n"
|
||||
@@ -1442,6 +1444,7 @@ class PresetManager:
|
||||
enhanced_desc,
|
||||
f"templates/commands/{short_name}.md",
|
||||
)
|
||||
registrar.apply_argument_hint(frontmatter, frontmatter_data, integration)
|
||||
frontmatter_text = dump_frontmatter(frontmatter_data)
|
||||
skill_title = self._skill_title_from_command(short_name)
|
||||
skill_content = (
|
||||
@@ -1479,6 +1482,7 @@ class PresetManager:
|
||||
frontmatter.get("description", f"Extension command: {command_name}"),
|
||||
extension_restore["source"],
|
||||
)
|
||||
registrar.apply_argument_hint(frontmatter, frontmatter_data, integration)
|
||||
frontmatter_text = dump_frontmatter(frontmatter_data)
|
||||
skill_content = (
|
||||
f"---\n"
|
||||
|
||||
Reference in New Issue
Block a user