2727
2828import yaml
2929
30- from .._invocation_style import get_invocation_prefix , is_dollar_skills_agent
3130from .._toml_string import escape_toml_basic as _escape_toml_basic
3231from .._toml_string import has_illegal_toml_control as _has_illegal_toml_control
33- from ..events import install_integration_events , remove_integration_events
3432
3533if TYPE_CHECKING :
3634 from .manifest import IntegrationManifest
3735
3836_HOOK_COMMAND_NOTE = (
39- "- When constructing command invocations from hook command names, "
37+ "- When constructing slash commands from hook command names, "
4038 "replace dots (`.`) with hyphens (`-`). "
4139 "For example, `speckit.git.commit` → `/speckit-git-commit`.\n "
4240)
@@ -160,17 +158,7 @@ def post_process_command_content(self, content: str) -> str:
160158 @classmethod
161159 def options (cls ) -> list [IntegrationOption ]:
162160 """Return options this integration accepts. Default: none."""
163- opts = []
164- if bool (getattr (cls , "CANONICAL_TO_NATIVE" , None ) and getattr (cls , "events_config_file" , None )):
165- opts .append (
166- IntegrationOption (
167- "--events" ,
168- is_flag = False ,
169- default = "true" ,
170- help = "Enable/disable runtime events (true|false, default: true)" ,
171- )
172- )
173- return opts
161+ return []
174162
175163 def effective_invoke_separator (
176164 self ,
@@ -491,11 +479,7 @@ def stale_cleanup_exclusions(self) -> set[str]:
491479 tracking) would otherwise be deleted even though they are still
492480 managed. Subclasses list such paths here to protect them.
493481 """
494- exclusions = set ()
495- if self .supports_events ():
496- from ..events import events_stale_exclusions
497- exclusions .update (events_stale_exclusions (self .key ))
498- return exclusions
482+ return set ()
499483
500484 def commands_dest (self , project_root : Path ) -> Path :
501485 """Return the absolute path to the commands output directory.
@@ -617,9 +601,7 @@ def install_scripts(
617601 return created
618602
619603 @staticmethod
620- def resolve_command_refs (
621- content : str , separator : str = "." , prefix : str = "/"
622- ) -> str :
604+ def resolve_command_refs (content : str , separator : str = "." ) -> str :
623605 """Replace ``__SPECKIT_COMMAND_<NAME>__`` placeholders with invocations.
624606
625607 Each placeholder encodes a command name in upper-case with
@@ -629,16 +611,10 @@ def resolve_command_refs(
629611
630612 * ``separator="."`` → ``/speckit.plan``, ``/speckit.git.commit``
631613 * ``separator="-"`` → ``/speckit-plan``, ``/speckit-git-commit``
632-
633- *prefix* defaults to ``"/"`` but may be ``"$"`` for agents whose
634- native skills invocation uses dollar-prefixed chat commands.
635614 """
636615 return re .sub (
637616 r"__SPECKIT_COMMAND_([A-Z][A-Z0-9_]*)__" ,
638- lambda m : prefix
639- + "speckit"
640- + separator
641- + m .group (1 ).lower ().replace ("_" , separator ),
617+ lambda m : "/speckit" + separator + m .group (1 ).lower ().replace ("_" , separator ),
642618 content ,
643619 )
644620
@@ -862,12 +838,7 @@ def process_template(
862838 content = CommandRegistrar .rewrite_project_relative_paths (content )
863839
864840 # 8. Replace __SPECKIT_COMMAND_<NAME>__ with invocation strings
865- invocation_prefix = get_invocation_prefix (
866- agent_name , invoke_separator == "-"
867- )
868- content = IntegrationBase .resolve_command_refs (
869- content , invoke_separator , invocation_prefix
870- )
841+ content = IntegrationBase .resolve_command_refs (content , invoke_separator )
871842
872843 return content
873844
@@ -931,32 +902,8 @@ def teardown(
931902
932903 Returns ``(removed, skipped)`` file lists.
933904 """
934- self .remove_events (project_root , manifest )
935905 return manifest .uninstall (project_root , force = force )
936906
937- def emit_events (
938- self ,
939- project_root : Path ,
940- manifest : IntegrationManifest ,
941- events : dict [str , dict [str , Any ]] | None = None ,
942- parsed_options : dict [str , Any ] | None = None ,
943- ** opts : Any ,
944- ) -> list [Path ]:
945- """Emit native event configuration for this integration."""
946- return install_integration_events (self , project_root , manifest , events or {})
947-
948- def remove_events (
949- self ,
950- project_root : Path ,
951- manifest : IntegrationManifest ,
952- ) -> None :
953- """Remove Specify-authored event entries from native config."""
954- remove_integration_events (self , project_root , manifest )
955-
956- def supports_events (self ) -> bool :
957- """Return True if this integration supports agent-native events."""
958- return bool (getattr (self , "CANONICAL_TO_NATIVE" , None ) and getattr (self , "events_config_file" , None ))
959-
960907 # -- Convenience helpers for subclasses -------------------------------
961908
962909 def install (
@@ -1061,12 +1008,6 @@ def setup(
10611008 created .append (dst_file )
10621009
10631010
1064- # Install agent runtime events
1065- event_files = self .emit_events (
1066- project_root , manifest , events = opts .get ("events" ), parsed_options = parsed_options
1067- )
1068- created .extend (event_files )
1069-
10701011 return created
10711012
10721013
@@ -1274,12 +1215,6 @@ def setup(
12741215 created .append (dst_file )
12751216
12761217
1277- # Install agent runtime events
1278- event_files = self .emit_events (
1279- project_root , manifest , events = opts .get ("events" ), parsed_options = parsed_options
1280- )
1281- created .extend (event_files )
1282-
12831218 return created
12841219
12851220
@@ -1516,12 +1451,6 @@ def setup(
15161451 created .append (dst_file )
15171452
15181453
1519- # Install agent runtime events
1520- event_files = self .emit_events (
1521- project_root , manifest , events = opts .get ("events" ), parsed_options = parsed_options
1522- )
1523- created .extend (event_files )
1524-
15251454 return created
15261455
15271456
@@ -1591,21 +1520,18 @@ def skills_dest(self, project_root: Path) -> Path:
15911520 return project_root / folder / subdir
15921521
15931522 def build_command_invocation (self , command_name : str , args : str = "" ) -> str :
1594- """Build the agent's native invocation for a hyphenated skill name."""
1523+ """Skills use ``/speckit-<stem>`` ( hyphenated directory name) ."""
15951524 stem = command_name
15961525 if stem .startswith ("speckit." ):
15971526 stem = stem [len ("speckit." ):]
15981527
1599- prefix = "$" if is_dollar_skills_agent (self .key , True ) else "/"
1600- invocation = prefix + "speckit-" + stem .replace ("." , "-" )
1528+ invocation = "/speckit-" + stem .replace ("." , "-" )
16011529 if args :
16021530 invocation = f"{ invocation } { args } "
16031531 return invocation
16041532
16051533 @staticmethod
1606- def _inject_hook_command_note (
1607- content : str , invocation_prefix : str = "/"
1608- ) -> str :
1534+ def _inject_hook_command_note (content : str ) -> str :
16091535 """Insert a dot-to-hyphen note before each hook output instruction.
16101536
16111537 Targets the line ``- For each executable hook, output the following``
@@ -1614,11 +1540,6 @@ def _inject_hook_command_note(
16141540 above them.
16151541 """
16161542 note = _HOOK_COMMAND_NOTE .rstrip ("\n " )
1617- if invocation_prefix != "/" :
1618- note = note .replace (
1619- "`/speckit-git-commit`" ,
1620- f"`{ invocation_prefix } speckit-git-commit`" ,
1621- )
16221543
16231544 def repl (m : re .Match [str ]) -> str :
16241545 indent = m .group (1 )
@@ -1652,13 +1573,10 @@ def post_process_skill_content(self, content: str) -> str:
16521573 Called by external skill generators (presets, extensions) to let
16531574 the integration inject agent-specific frontmatter or body
16541575 transformations. The base implementation injects shared skills
1655- guidance for converting dotted hook command names to the agent-native
1656- hyphenated command invocation (e.g. ``/speckit-git-commit`` or
1657- ``$speckit-git-commit``). Subclasses may override -- see
1658- ``ClaudeIntegration``.
1576+ guidance for converting dotted hook command names to hyphenated
1577+ slash commands. Subclasses may override — see ``ClaudeIntegration``.
16591578 """
1660- invocation_prefix = get_invocation_prefix (self .key , True )
1661- return self ._inject_hook_command_note (content , invocation_prefix )
1579+ return self ._inject_hook_command_note (content )
16621580
16631581 def setup (
16641582 self ,
@@ -1709,16 +1627,10 @@ def setup(
17091627 command_name = src_file .stem # e.g. "plan"
17101628 skill_name = f"speckit-{ command_name .replace ('.' , '-' )} "
17111629
1712- # Parse frontmatter for description. Locate the closing ``---`` on
1713- # its own line rather than with ``raw.split("---", 2)`` — a bare
1714- # substring split stops at the first ``---`` *anywhere*, including
1715- # one inside a value such as ``description: Separate sections
1716- # with ---``, which truncates the frontmatter and drops later keys.
1717- # The block between the delimiters is parsed unstripped so trailing
1718- # newlines in literal (``|``) block scalars survive.
1630+ # Parse frontmatter for description
17191631 frontmatter : dict [str , Any ] = {}
17201632 if raw .startswith ("---" ):
1721- fm_lines = raw .splitlines ( keepends = True )
1633+ fm_lines = raw .split ( " \n " )
17221634 fm_close = next (
17231635 (
17241636 i
@@ -1729,7 +1641,7 @@ def setup(
17291641 )
17301642 if fm_close is not None :
17311643 try :
1732- fm = yaml .safe_load ("" .join (fm_lines [1 :fm_close ]))
1644+ fm = yaml .safe_load ("\n " .join (fm_lines [1 :fm_close ]))
17331645 if isinstance (fm , dict ):
17341646 frontmatter = fm
17351647 except yaml .YAMLError :
@@ -1744,27 +1656,19 @@ def setup(
17441656 # Strip the processed frontmatter — we rebuild it for skills.
17451657 # Preserve leading whitespace in the body to match release ZIP
17461658 # output byte-for-byte (the template body starts with \n after
1747- # the closing ---). Scan for the closing ``---`` on its own line
1748- # rather than ``split("---", 2)`` so a ``---`` embedded in a value
1749- # does not truncate the frontmatter and spill it into the body.
1659+ # the closing ---).
17501660 if processed_body .startswith ("---" ):
1751- body_lines = processed_body .splitlines ( keepends = True )
1752- close_idx = next (
1661+ body_lines = processed_body .split ( " \n " )
1662+ body_close = next (
17531663 (
17541664 i
17551665 for i in range (1 , len (body_lines ))
17561666 if body_lines [i ].rstrip () == "---"
17571667 ),
17581668 None ,
17591669 )
1760- if close_idx is not None :
1761- # Keep whatever trails the ``---`` marker on the closing
1762- # line (normally just the newline) so the body stays
1763- # byte-for-byte identical to ``split("---", 2)[2]``. The
1764- # line-anchored check guarantees ``---`` sits at index 0.
1765- processed_body = body_lines [close_idx ][3 :] + "" .join (
1766- body_lines [close_idx + 1 :]
1767- )
1670+ if body_close is not None :
1671+ processed_body = "\n " .join (body_lines [body_close + 1 :])
17681672
17691673 # Select description — use the original template description
17701674 # to stay byte-for-byte identical with release ZIP output.
@@ -1798,10 +1702,4 @@ def setup(
17981702 created .append (dst )
17991703
18001704
1801- # Install agent runtime events
1802- event_files = self .emit_events (
1803- project_root , manifest , events = opts .get ("events" ), parsed_options = parsed_options
1804- )
1805- created .extend (event_files )
1806-
18071705 return created
0 commit comments