Skip to content

Commit 655a3cb

Browse files
BenBtgCopilot
andauthored
fix(integrations): preserve native skill invocation prefixes (#3663)
* fix(integrations): use native dollar skill invocations Assisted-by: GitHub Copilot (model: GPT-5.6 Sol, autonomous) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 02f9e138-da58-4a60-93b9-eae659d2aa19 * fix(integrations): preserve skill post-process idempotence Assisted-by: GitHub Copilot (model: GPT-5.6 Sol, autonomous) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 02f9e138-da58-4a60-93b9-eae659d2aa19 * fix(integrations): preserve literal skill invocations Resolve generated command references with the active agent prefix instead of rewriting all slash-form text during post-processing. Assisted-by: GitHub Copilot (model: GPT-5.6 Sol, autonomous) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 02f9e138-da58-4a60-93b9-eae659d2aa19 * fix(integrations): preserve shared invocation prefix Assisted-by: GitHub Copilot (model: GPT-5.6 Sol, autonomous) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 02f9e138-da58-4a60-93b9-eae659d2aa19 * fix(integrations): preserve install invocation prefix Pass dollar-style skill prefixes through bare-project integration installation and cover the shared template output. Assisted-by: GitHub Copilot (model: GPT-5.6 Sol, autonomous) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 02f9e138-da58-4a60-93b9-eae659d2aa19 * fix(integrations): preserve dollar refs everywhere Use agent-native invocation prefixes in extension command registration and dynamic shared-script command hints. Assisted-by: GitHub Copilot (model: GPT-5.6 Sol, autonomous) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 02f9e138-da58-4a60-93b9-eae659d2aa19 * fix(shared-infra): preserve dollar command hints Escape dollar-prefixed commands embedded in Bash strings and propagate the native prefix into installed Python command helpers. Assisted-by: GitHub Copilot (model: GPT-5.6 Sol, autonomous) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 02f9e138-da58-4a60-93b9-eae659d2aa19 * fix(shared-infra): render native helper prefixes Rewrite installed Bash and PowerShell formatter return expressions so direct callers receive the selected integration's native prefix. Assisted-by: GitHub Copilot (model: GPT-5.6 Sol, autonomous) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 02f9e138-da58-4a60-93b9-eae659d2aa19 * fix(skills): use invocation-neutral hook guidance Describe hook-derived references as command invocations so dollar-prefixed skills do not receive contradictory slash-command terminology. Assisted-by: GitHub Copilot (model: GPT-5.6 Sol, autonomous) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 02f9e138-da58-4a60-93b9-eae659d2aa19 * test(integrations): expect native fallback invocation Assisted-by: GitHub Copilot (model: GPT-5.6 Sol, autonomous) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 02f9e138-da58-4a60-93b9-eae659d2aa19 * refactor(integrations): centralize invocation prefix selection Assisted-by: GitHub Copilot (model: GPT-5.6 Sol, autonomous) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 02f9e138-da58-4a60-93b9-eae659d2aa19 * fix(integrations): add Kimi /skill: prefix and fix docstrings - Add SKILL_COLON_AGENTS frozenset and get_invocation_prefix() to _invocation_style.py so Kimi resolves to '/skill:' in skills mode - Switch invoke_prefix_for_integration() to use get_invocation_prefix() instead of the binary dollar/slash check - Update post_process_skill_content docstring (base.py) to cover both slash and dollar native invocation forms - Update _resolve_command_refs_in_skill docstring (presets/__init__.py) to document the dollar-prefixed result alongside slash forms Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 65ef91d9-4c31-4f31-a009-ed2093fe7f28 Assisted-by: GitHub Copilot (model: claude-sonnet-4.6, autonomous) * fix(agents): use get_invocation_prefix for Kimi in register_commands Replace the binary is_dollar_skills_agent ternary with get_invocation_prefix so that __SPECKIT_COMMAND_*__ tokens in Kimi skill files resolve to /skill:speckit-<name> rather than /speckit-<name>. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 65ef91d9-4c31-4f31-a009-ed2093fe7f28 Assisted-by: GitHub Copilot (model: claude-sonnet-4.6, autonomous) * fix(agents): remove unused is_dollar_skills_agent import Leftover from replacing the inline ternary with get_invocation_prefix. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 65ef91d9-4c31-4f31-a009-ed2093fe7f28 Assisted-by: GitHub Copilot (model: claude-sonnet-4.6, autonomous) * fix(integrations): use get_invocation_prefix in post_process_skill_content Replaces the binary is_dollar_skills_agent ternary with get_invocation_prefix so that Kimi's hook-command note is injected as /skill:speckit-git-commit from the start. This keeps _inject_hook_command_note idempotent for Kimi: the previous note with its native prefix now matches on repeated passes, preventing duplicate note injection. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 65ef91d9-4c31-4f31-a009-ed2093fe7f28 Assisted-by: GitHub Copilot (model: claude-sonnet-4.6, autonomous) * fix(presets): use get_invocation_prefix in _resolve_skill_command_refs Replace the binary is_dollar_skills_agent ternary with get_invocation_prefix so Kimi tokens resolve to /skill:speckit-* directly rather than /speckit-* (which previously relied on the broad post-process body replacement). Also fix test_restore_skill_preserves_dollar_command_refs to write raw_core with the unresolved __SPECKIT_COMMAND_PLAN__ token, exercising the resolver rather than bypassing it with a pre-resolved string. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 65ef91d9-4c31-4f31-a009-ed2093fe7f28 Assisted-by: GitHub Copilot (model: claude-sonnet-4.6, autonomous) * docs(presets): document /skill: form in _resolve_skill_command_refs Add /skill:speckit-<cmd> to the docstring so the contract covers all three native prefix forms returned by get_invocation_prefix. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 65ef91d9-4c31-4f31-a009-ed2093fe7f28 Assisted-by: GitHub Copilot (model: claude-sonnet-4.6, autonomous) * test(integrations): add Kimi /skill: prefix coverage - test_skill_colon_prefix_core_command: resolve_command_refs with /skill: prefix - test_get_invocation_prefix_skill_colon: get_invocation_prefix returns /skill: for kimi (skills), / for kimi (non-skills), $ for codex, / for claude - test_kimi_skill_post_processing_is_idempotent: verifies Kimi's hook-command note is injected with /skill: prefix and does not duplicate on re-runs - test_installed_bash_formatter_uses_skill_colon_prefix: shared-infra bash formatter outputs /skill:speckit-plan when installed with /skill: prefix Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 65ef91d9-4c31-4f31-a009-ed2093fe7f28 Assisted-by: GitHub Copilot (model: claude-sonnet-4.6, autonomous) * fix(kimi): use get_invocation_prefix in process_template, remove broad replacement process_template() was still using a binary is_dollar_skills_agent ternary to select between dollar and slash prefix, so Kimi tokens were emitted as /speckit-* and then corrected by a broad .replace('/speckit-', '/skill:speckit-') in KimiIntegration.post_process_skill_content(). That broad replacement would also rewrite any literal /speckit-* text in generated skill content, contrary to the PR's token-only behavior. - Use get_invocation_prefix(agent_name, invoke_separator == '-') in process_template() so Kimi tokens are emitted as /skill:speckit-* directly. - Remove the broad .replace() from KimiIntegration.post_process_skill_content(); it is now a no-op (tokens are already correctly prefixed at source). - Add test_process_template_kimi_uses_skill_colon_prefix to guard the fix. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 65ef91d9-4c31-4f31-a009-ed2093fe7f28 Assisted-by: GitHub Copilot (model: claude-sonnet-4.6, autonomous) --------- Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 02f9e138-da58-4a60-93b9-eae659d2aa19 Copilot-Session: 65ef91d9-4c31-4f31-a009-ed2093fe7f28
1 parent 809b4c5 commit 655a3cb

21 files changed

Lines changed: 423 additions & 41 deletions

‎src/specify_cli/__init__.py‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ def _refresh_shared_templates(
114114
project_path: Path,
115115
*,
116116
invoke_separator: str,
117+
invoke_prefix: str = "/",
117118
force: bool = False,
118119
) -> None:
119120
"""Refresh default-sensitive shared templates without touching scripts."""
@@ -124,6 +125,7 @@ def _refresh_shared_templates(
124125
repo_root=_repo_root(),
125126
console=console,
126127
invoke_separator=invoke_separator,
128+
invoke_prefix=invoke_prefix,
127129
force=force,
128130
)
129131

@@ -134,6 +136,7 @@ def _install_shared_infra(
134136
tracker: StepTracker | None = None,
135137
force: bool = False,
136138
invoke_separator: str = ".",
139+
invoke_prefix: str = "/",
137140
refresh_managed: bool = False,
138141
refresh_hint: str | None = None,
139142
) -> bool:
@@ -177,6 +180,7 @@ def _install_shared_infra(
177180
console=console,
178181
force=force,
179182
invoke_separator=invoke_separator,
183+
invoke_prefix=invoke_prefix,
180184
refresh_managed=refresh_managed,
181185
refresh_hint=refresh_hint,
182186
)
@@ -188,6 +192,7 @@ def _install_shared_infra_or_exit(
188192
tracker: StepTracker | None = None,
189193
force: bool = False,
190194
invoke_separator: str = ".",
195+
invoke_prefix: str = "/",
191196
refresh_managed: bool = False,
192197
refresh_hint: str | None = None,
193198
) -> bool:
@@ -198,6 +203,7 @@ def _install_shared_infra_or_exit(
198203
tracker=tracker,
199204
force=force,
200205
invoke_separator=invoke_separator,
206+
invoke_prefix=invoke_prefix,
201207
refresh_managed=refresh_managed,
202208
refresh_hint=refresh_hint,
203209
)

‎src/specify_cli/_invocation_style.py‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@
2929
}
3030
)
3131

32+
# Agents that render /skill:<name> (skill-colon invocation) when in skills mode.
33+
SKILL_COLON_AGENTS: frozenset[str] = frozenset({"kimi"})
34+
3235

3336
def is_dollar_skills_agent(selected_ai: str | None, ai_skills_enabled: bool) -> bool:
3437
"""Return ``True`` if *selected_ai* uses ``$speckit-<name>`` invocations.
@@ -41,6 +44,21 @@ def is_dollar_skills_agent(selected_ai: str | None, ai_skills_enabled: bool) ->
4144
return selected_ai in DOLLAR_SKILLS_AGENTS and ai_skills_enabled
4245

4346

47+
def get_invocation_prefix(selected_ai: str | None, ai_skills_enabled: bool) -> str:
48+
"""Return the native invocation prefix for *selected_ai* in skills mode.
49+
50+
Returns ``"$"`` for dollar-skills agents (Codex, ZCode),
51+
``"/skill:"`` for skill-colon agents (Kimi), and ``"/"`` for all others.
52+
"""
53+
if not isinstance(selected_ai, str):
54+
return "/"
55+
if selected_ai in DOLLAR_SKILLS_AGENTS and ai_skills_enabled:
56+
return "$"
57+
if selected_ai in SKILL_COLON_AGENTS and ai_skills_enabled:
58+
return "/skill:"
59+
return "/"
60+
61+
4462
def is_slash_skills_agent(selected_ai: str | None, ai_skills_enabled: bool) -> bool:
4563
"""Return ``True`` if *selected_ai* uses ``/speckit-<name>`` invocations.
4664

‎src/specify_cli/agents.py‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import yaml
1616

1717
from ._init_options import is_ai_skills_enabled, load_init_options
18+
from ._invocation_style import get_invocation_prefix
1819
from ._toml_string import escape_toml_basic as _escape_toml_basic
1920
from ._toml_string import has_illegal_toml_control as _has_illegal_toml_control
2021
from ._utils import relative_extension_path_violation
@@ -659,17 +660,16 @@ def register_commands(
659660
# correct when a stale ``.bob/skills`` directory coexists with
660661
# ``.bob/commands``.
661662
_sep = agent_config.get("invoke_separator", ".")
663+
registrar_writes_skills = agent_config.get("extension") == "/SKILL.md"
662664
try:
663665
from specify_cli.integrations import get_integration # noqa: PLC0415
664666

665667
_integ = get_integration(agent_name)
666668
if _integ is not None:
667-
registrar_writes_skills = (
668-
agent_config.get("extension") == "/SKILL.md"
669-
)
670669
_sep = _integ.invoke_separator_for_mode(registrar_writes_skills)
671670
except Exception:
672671
pass
672+
_prefix = get_invocation_prefix(agent_name, registrar_writes_skills)
673673

674674
for cmd_info in commands:
675675
cmd_name = cmd_info["name"]
@@ -772,7 +772,7 @@ def register_commands(
772772
# (base.py itself imports CommandRegistrar lazily).
773773
from specify_cli.integrations.base import IntegrationBase # noqa: PLC0415
774774

775-
body = IntegrationBase.resolve_command_refs(body, _sep)
775+
body = IntegrationBase.resolve_command_refs(body, _sep, _prefix)
776776

777777
output_name = self._compute_output_name(agent_name, cmd_name, agent_config)
778778

‎src/specify_cli/commands/init.py‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ def init(
183183
save_init_options,
184184
)
185185
from ..integration_runtime import (
186+
invoke_prefix_for_integration as _invoke_prefix_for_integration,
186187
with_integration_setting as _with_integration_setting,
187188
)
188189
from ..integrations._commands import (
@@ -481,6 +482,12 @@ def init(
481482
invoke_separator=resolved_integration.effective_invoke_separator(
482483
integration_parsed_options, project_root=project_path
483484
),
485+
invoke_prefix=_invoke_prefix_for_integration(
486+
resolved_integration,
487+
resolved_integration.key,
488+
integration_parsed_options,
489+
project_path,
490+
),
484491
)
485492
tracker.complete(
486493
"shared-infra", f"scripts ({selected_script}) + templates"

‎src/specify_cli/integration_runtime.py‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from collections.abc import Callable
66
from typing import Any
77

8+
from ._invocation_style import get_invocation_prefix
89
from .integration_state import integration_setting, integration_settings
910

1011

@@ -99,3 +100,14 @@ def invoke_separator_for_integration(
99100
return integration.effective_invoke_separator(stored_parsed, project_root)
100101

101102
return integration.effective_invoke_separator(None, project_root)
103+
104+
105+
def invoke_prefix_for_integration(
106+
integration: Any,
107+
key: str,
108+
parsed_options: dict[str, Any] | None = None,
109+
project_root: Any = None,
110+
) -> str:
111+
"""Resolve the native invocation prefix for an integration's output mode."""
112+
skills_mode = integration.is_skills_mode(parsed_options, project_root)
113+
return get_invocation_prefix(key, skills_mode)

‎src/specify_cli/integrations/_helpers.py‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from .._agent_config import SCRIPT_TYPE_CHOICES
1212
from .._console import console
1313
from ..integration_runtime import (
14+
invoke_prefix_for_integration as _invoke_prefix_for_integration,
1415
invoke_separator_for_integration as _invoke_separator_for_integration,
1516
resolve_integration_options as _resolve_integration_options_impl,
1617
with_integration_setting as _with_integration_setting,
@@ -333,6 +334,9 @@ def _set_default_integration(
333334
integration, {"integration_settings": settings}, key, parsed_options,
334335
project_root=project_root,
335336
),
337+
invoke_prefix=_invoke_prefix_for_integration(
338+
integration, key, parsed_options, project_root
339+
),
336340
force=refresh_templates_force,
337341
refresh_managed=True,
338342
refresh_hint=refresh_hint,

‎src/specify_cli/integrations/_install_commands.py‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from .._console import console
99
from .._utils import _display_project_path
1010
from ..integration_runtime import (
11+
invoke_prefix_for_integration as _invoke_prefix_for_integration,
1112
invoke_separator_for_integration as _invoke_separator_for_integration,
1213
with_integration_setting as _with_integration_setting,
1314
)
@@ -130,6 +131,9 @@ def integration_install(
130131
infra_integration, current, infra_key, infra_parsed,
131132
project_root=project_root,
132133
),
134+
invoke_prefix=_invoke_prefix_for_integration(
135+
infra_integration, infra_key, infra_parsed, project_root
136+
),
133137
)
134138
if os.name != "nt":
135139
from .. import ensure_executable_scripts

‎src/specify_cli/integrations/_migrate_commands.py‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
from .._console import console
1111
from ..integration_runtime import (
12+
invoke_prefix_for_integration as _invoke_prefix_for_integration,
1213
invoke_separator_for_integration as _invoke_separator_for_integration,
1314
with_integration_setting as _with_integration_setting,
1415
)
@@ -447,6 +448,9 @@ def integration_switch(
447448
target_integration, current, target, parsed_options,
448449
project_root=project_root,
449450
),
451+
invoke_prefix=_invoke_prefix_for_integration(
452+
target_integration, target, parsed_options, project_root
453+
),
450454
refresh_hint=(
451455
"To overwrite customizations, re-run with "
452456
"[cyan]specify integration switch ... --refresh-shared-infra[/cyan]."
@@ -747,6 +751,9 @@ def integration_upgrade(
747751
infra_integration, current, infra_key, infra_parsed,
748752
project_root=project_root,
749753
),
754+
invoke_prefix=_invoke_prefix_for_integration(
755+
infra_integration, infra_key, infra_parsed, project_root
756+
),
750757
)
751758
if os.name != "nt":
752759
from .. import ensure_executable_scripts
@@ -782,6 +789,9 @@ def integration_upgrade(
782789
integration, {"integration_settings": settings}, key, parsed_options,
783790
project_root=project_root,
784791
),
792+
invoke_prefix=_invoke_prefix_for_integration(
793+
integration, key, parsed_options, project_root
794+
),
785795
force=force,
786796
refresh_managed=True,
787797
)

‎src/specify_cli/integrations/base.py‎

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,15 @@
2727

2828
import yaml
2929

30+
from .._invocation_style import get_invocation_prefix, is_dollar_skills_agent
3031
from .._toml_string import escape_toml_basic as _escape_toml_basic
3132
from .._toml_string import has_illegal_toml_control as _has_illegal_toml_control
3233

3334
if TYPE_CHECKING:
3435
from .manifest import IntegrationManifest
3536

3637
_HOOK_COMMAND_NOTE = (
37-
"- When constructing slash commands from hook command names, "
38+
"- When constructing command invocations from hook command names, "
3839
"replace dots (`.`) with hyphens (`-`). "
3940
"For example, `speckit.git.commit` → `/speckit-git-commit`.\n"
4041
)
@@ -601,7 +602,9 @@ def install_scripts(
601602
return created
602603

603604
@staticmethod
604-
def resolve_command_refs(content: str, separator: str = ".") -> str:
605+
def resolve_command_refs(
606+
content: str, separator: str = ".", prefix: str = "/"
607+
) -> str:
605608
"""Replace ``__SPECKIT_COMMAND_<NAME>__`` placeholders with invocations.
606609
607610
Each placeholder encodes a command name in upper-case with
@@ -611,10 +614,16 @@ def resolve_command_refs(content: str, separator: str = ".") -> str:
611614
612615
* ``separator="."`` → ``/speckit.plan``, ``/speckit.git.commit``
613616
* ``separator="-"`` → ``/speckit-plan``, ``/speckit-git-commit``
617+
618+
*prefix* defaults to ``"/"`` but may be ``"$"`` for agents whose
619+
native skills invocation uses dollar-prefixed chat commands.
614620
"""
615621
return re.sub(
616622
r"__SPECKIT_COMMAND_([A-Z][A-Z0-9_]*)__",
617-
lambda m: "/speckit" + separator + m.group(1).lower().replace("_", separator),
623+
lambda m: prefix
624+
+ "speckit"
625+
+ separator
626+
+ m.group(1).lower().replace("_", separator),
618627
content,
619628
)
620629

@@ -838,7 +847,12 @@ def process_template(
838847
content = CommandRegistrar.rewrite_project_relative_paths(content)
839848

840849
# 8. Replace __SPECKIT_COMMAND_<NAME>__ with invocation strings
841-
content = IntegrationBase.resolve_command_refs(content, invoke_separator)
850+
invocation_prefix = get_invocation_prefix(
851+
agent_name, invoke_separator == "-"
852+
)
853+
content = IntegrationBase.resolve_command_refs(
854+
content, invoke_separator, invocation_prefix
855+
)
842856

843857
return content
844858

@@ -1520,18 +1534,21 @@ def skills_dest(self, project_root: Path) -> Path:
15201534
return project_root / folder / subdir
15211535

15221536
def build_command_invocation(self, command_name: str, args: str = "") -> str:
1523-
"""Skills use ``/speckit-<stem>`` (hyphenated directory name)."""
1537+
"""Build the agent's native invocation for a hyphenated skill name."""
15241538
stem = command_name
15251539
if stem.startswith("speckit."):
15261540
stem = stem[len("speckit."):]
15271541

1528-
invocation = "/speckit-" + stem.replace(".", "-")
1542+
prefix = "$" if is_dollar_skills_agent(self.key, True) else "/"
1543+
invocation = prefix + "speckit-" + stem.replace(".", "-")
15291544
if args:
15301545
invocation = f"{invocation} {args}"
15311546
return invocation
15321547

15331548
@staticmethod
1534-
def _inject_hook_command_note(content: str) -> str:
1549+
def _inject_hook_command_note(
1550+
content: str, invocation_prefix: str = "/"
1551+
) -> str:
15351552
"""Insert a dot-to-hyphen note before each hook output instruction.
15361553
15371554
Targets the line ``- For each executable hook, output the following``
@@ -1540,6 +1557,11 @@ def _inject_hook_command_note(content: str) -> str:
15401557
above them.
15411558
"""
15421559
note = _HOOK_COMMAND_NOTE.rstrip("\n")
1560+
if invocation_prefix != "/":
1561+
note = note.replace(
1562+
"`/speckit-git-commit`",
1563+
f"`{invocation_prefix}speckit-git-commit`",
1564+
)
15431565

15441566
def repl(m: re.Match[str]) -> str:
15451567
indent = m.group(1)
@@ -1573,10 +1595,13 @@ def post_process_skill_content(self, content: str) -> str:
15731595
Called by external skill generators (presets, extensions) to let
15741596
the integration inject agent-specific frontmatter or body
15751597
transformations. The base implementation injects shared skills
1576-
guidance for converting dotted hook command names to hyphenated
1577-
slash commands. Subclasses may override — see ``ClaudeIntegration``.
1598+
guidance for converting dotted hook command names to the agent-native
1599+
hyphenated command invocation (e.g. ``/speckit-git-commit`` or
1600+
``$speckit-git-commit``). Subclasses may override -- see
1601+
``ClaudeIntegration``.
15781602
"""
1579-
return self._inject_hook_command_note(content)
1603+
invocation_prefix = get_invocation_prefix(self.key, True)
1604+
return self._inject_hook_command_note(content, invocation_prefix)
15801605

15811606
def setup(
15821607
self,

‎src/specify_cli/integrations/kimi/__init__.py‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,7 @@ def build_command_invocation(self, command_name: str, args: str = "") -> str:
5959

6060
def post_process_skill_content(self, content: str) -> str:
6161
"""Ensure in-skill cross-command references use Kimi's `/skill:` syntax."""
62-
content = super().post_process_skill_content(content)
63-
return content.replace("/speckit-", "/skill:speckit-")
62+
return super().post_process_skill_content(content)
6463

6564
@classmethod
6665
def options(cls) -> list[IntegrationOption]:

0 commit comments

Comments
 (0)