mirror of
https://github.com/github/spec-kit.git
synced 2026-07-03 12:28:06 +08:00
VS Code Copilot Agent Skills do not support the `mode:` frontmatter field. The generated SKILL.md files included `mode: speckit.<stem>` injected by CopilotIntegration.post_process_skill_content(), which had no effect in VS Code and could cause confusion. Simplify post_process_skill_content to delegate directly to _CopilotSkillsHelper without injecting mode:. Update tests to assert mode: is absent from generated skill frontmatter. Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -283,58 +283,13 @@ class CopilotIntegration(IntegrationBase):
|
||||
return f"speckit.{template_name}.agent.md"
|
||||
|
||||
def post_process_skill_content(self, content: str) -> str:
|
||||
"""Inject shared hook guidance and Copilot ``mode:`` frontmatter.
|
||||
"""Inject shared hook guidance into Copilot skill content.
|
||||
|
||||
Inserts ``mode: speckit.<stem>`` before the closing ``---`` so
|
||||
Copilot can associate the skill with its agent mode.
|
||||
Delegates to :class:`_CopilotSkillsHelper` for shared post-processing.
|
||||
The ``mode:`` frontmatter field is intentionally omitted: VS Code
|
||||
Copilot Agent Skills do not support it (see issue #2799).
|
||||
"""
|
||||
updated = _CopilotSkillsHelper().post_process_skill_content(content)
|
||||
lines = updated.splitlines(keepends=True)
|
||||
|
||||
# Extract skill name from frontmatter to derive the mode value
|
||||
dash_count = 0
|
||||
skill_name = ""
|
||||
for line in lines:
|
||||
stripped = line.rstrip("\n\r")
|
||||
if stripped == "---":
|
||||
dash_count += 1
|
||||
if dash_count == 2:
|
||||
break
|
||||
continue
|
||||
if dash_count == 1:
|
||||
if stripped.startswith("mode:"):
|
||||
return updated # already present
|
||||
if stripped.startswith("name:"):
|
||||
# Parse: name: "speckit-plan" → speckit.plan
|
||||
val = stripped.split(":", 1)[1].strip().strip('"').strip("'")
|
||||
# Convert speckit-plan → speckit.plan
|
||||
if val.startswith("speckit-"):
|
||||
skill_name = "speckit." + val[len("speckit-"):]
|
||||
else:
|
||||
skill_name = val
|
||||
|
||||
if not skill_name:
|
||||
return updated
|
||||
|
||||
# Inject mode: before the closing --- of frontmatter
|
||||
out: list[str] = []
|
||||
dash_count = 0
|
||||
injected = False
|
||||
for line in lines:
|
||||
stripped = line.rstrip("\n\r")
|
||||
if stripped == "---":
|
||||
dash_count += 1
|
||||
if dash_count == 2 and not injected:
|
||||
if line.endswith("\r\n"):
|
||||
eol = "\r\n"
|
||||
elif line.endswith("\n"):
|
||||
eol = "\n"
|
||||
else:
|
||||
eol = ""
|
||||
out.append(f"mode: {skill_name}{eol}")
|
||||
injected = True
|
||||
out.append(line)
|
||||
return "".join(out)
|
||||
return _CopilotSkillsHelper().post_process_skill_content(content)
|
||||
|
||||
def setup(
|
||||
self,
|
||||
|
||||
@@ -426,8 +426,8 @@ class TestCopilotSkillsMode:
|
||||
|
||||
# -- Copilot-specific post-processing ---------------------------------
|
||||
|
||||
def test_post_process_skill_content_injects_mode(self):
|
||||
"""post_process_skill_content() should inject mode: field."""
|
||||
def test_post_process_skill_content_does_not_inject_mode(self):
|
||||
"""post_process_skill_content() must NOT inject mode: — VS Code Copilot does not support it."""
|
||||
copilot = self._make_copilot()
|
||||
content = (
|
||||
"---\n"
|
||||
@@ -437,10 +437,10 @@ class TestCopilotSkillsMode:
|
||||
"\nBody content\n"
|
||||
)
|
||||
updated = copilot.post_process_skill_content(content)
|
||||
assert "mode: speckit.plan" in updated
|
||||
assert "mode:" not in updated
|
||||
|
||||
def test_post_process_skill_content_injects_hook_note(self):
|
||||
"""post_process_skill_content() should inject shared hook guidance."""
|
||||
"""post_process_skill_content() should inject shared hook guidance but not mode:."""
|
||||
copilot = self._make_copilot()
|
||||
content = (
|
||||
"---\n"
|
||||
@@ -451,7 +451,7 @@ class TestCopilotSkillsMode:
|
||||
)
|
||||
updated = copilot.post_process_skill_content(content)
|
||||
assert "replace dots" in updated
|
||||
assert "mode: speckit.specify" in updated
|
||||
assert "mode:" not in updated
|
||||
|
||||
def test_post_process_idempotent(self):
|
||||
"""post_process_skill_content() must be idempotent."""
|
||||
@@ -467,8 +467,8 @@ class TestCopilotSkillsMode:
|
||||
second = copilot.post_process_skill_content(first)
|
||||
assert first == second
|
||||
|
||||
def test_skills_have_mode_in_frontmatter(self, tmp_path):
|
||||
"""Generated SKILL.md files should have mode: field from post-processing."""
|
||||
def test_skills_do_not_have_mode_in_frontmatter(self, tmp_path):
|
||||
"""Generated SKILL.md files must NOT contain mode: — VS Code Copilot does not support it."""
|
||||
copilot = self._make_copilot()
|
||||
created, _ = self._setup_skills(copilot, tmp_path)
|
||||
skill_files = [f for f in created if f.name == "SKILL.md"]
|
||||
@@ -477,11 +477,7 @@ class TestCopilotSkillsMode:
|
||||
content = f.read_text(encoding="utf-8")
|
||||
parts = content.split("---", 2)
|
||||
fm = yaml.safe_load(parts[1])
|
||||
assert "mode" in fm, f"{f} frontmatter missing 'mode'"
|
||||
# mode should be speckit.<stem>
|
||||
skill_dir_name = f.parent.name
|
||||
stem = skill_dir_name.removeprefix("speckit-")
|
||||
assert fm["mode"] == f"speckit.{stem}"
|
||||
assert "mode" not in fm, f"{f} frontmatter must not contain unsupported 'mode' field"
|
||||
|
||||
def test_skills_hook_sections_explain_dotted_command_conversion(self, tmp_path):
|
||||
"""Generated skills with hook sections should include shared hook guidance."""
|
||||
|
||||
@@ -898,11 +898,10 @@ class TestIntegrationSwitch:
|
||||
assert copilot_git_feature.exists(), "Git extension skill should exist for Copilot skills mode"
|
||||
assert not copilot_agent_file.exists(), "Copilot skills mode should not create extension .agent.md files"
|
||||
|
||||
# Verify Copilot-specific frontmatter: mode field should map from
|
||||
# skill name (speckit-git-feature) back to dot notation (speckit.git-feature)
|
||||
# Verify Copilot skill frontmatter does NOT contain mode: — VS Code Copilot does not support it
|
||||
skill_content = copilot_git_feature.read_text(encoding="utf-8")
|
||||
assert "mode: speckit.git-feature" in skill_content, (
|
||||
"Copilot skill frontmatter should contain mode mapped from skill name"
|
||||
assert "mode:" not in skill_content, (
|
||||
"Copilot skill frontmatter must not contain unsupported 'mode' field"
|
||||
)
|
||||
|
||||
registry = json.loads(
|
||||
|
||||
Reference in New Issue
Block a user