1818def _build_agent_configs () -> dict [str , Any ]:
1919 """Derive CommandRegistrar.AGENT_CONFIGS from INTEGRATION_REGISTRY."""
2020 from specify_cli .integrations import INTEGRATION_REGISTRY
21+
2122 configs : dict [str , dict [str , Any ]] = {}
2223 for key , integration in INTEGRATION_REGISTRY .items ():
2324 if key == "generic" :
@@ -75,7 +76,7 @@ def parse_frontmatter(content: str) -> tuple[dict, str]:
7576 return {}, content
7677
7778 frontmatter_str = content [3 :end_marker ].strip ()
78- body = content [end_marker + 3 :].strip ()
79+ body = content [end_marker + 3 :].strip ()
7980
8081 try :
8182 frontmatter = yaml .safe_load (frontmatter_str ) or {}
@@ -100,7 +101,9 @@ def render_frontmatter(fm: dict) -> str:
100101 if not fm :
101102 return ""
102103
103- yaml_str = yaml .dump (fm , default_flow_style = False , sort_keys = False , allow_unicode = True )
104+ yaml_str = yaml .dump (
105+ fm , default_flow_style = False , sort_keys = False , allow_unicode = True
106+ )
104107 return f"---\n { yaml_str } ---\n "
105108
106109 def _adjust_script_paths (self , frontmatter : dict ) -> dict :
@@ -146,16 +149,16 @@ def rewrite_project_relative_paths(text: str) -> str:
146149 # ".specify/extensions/<ext>/scripts/..." remain intact.
147150 text = re .sub (r'(^|[\s`"\'(])(?:\.?/)?memory/' , r"\1.specify/memory/" , text )
148151 text = re .sub (r'(^|[\s`"\'(])(?:\.?/)?scripts/' , r"\1.specify/scripts/" , text )
149- text = re .sub (r'(^|[\s`"\'(])(?:\.?/)?templates/' , r"\1.specify/templates/" , text )
152+ text = re .sub (
153+ r'(^|[\s`"\'(])(?:\.?/)?templates/' , r"\1.specify/templates/" , text
154+ )
150155
151- return text .replace (".specify/.specify/" , ".specify/" ).replace (".specify.specify/" , ".specify/" )
156+ return text .replace (".specify/.specify/" , ".specify/" ).replace (
157+ ".specify.specify/" , ".specify/"
158+ )
152159
153160 def render_markdown_command (
154- self ,
155- frontmatter : dict ,
156- body : str ,
157- source_id : str ,
158- context_note : str = None
161+ self , frontmatter : dict , body : str , source_id : str , context_note : str = None
159162 ) -> str :
160163 """Render command in Markdown format.
161164
@@ -172,12 +175,7 @@ def render_markdown_command(
172175 context_note = f"\n <!-- Source: { source_id } -->\n "
173176 return self .render_frontmatter (frontmatter ) + "\n " + context_note + body
174177
175- def render_toml_command (
176- self ,
177- frontmatter : dict ,
178- body : str ,
179- source_id : str
180- ) -> str :
178+ def render_toml_command (self , frontmatter : dict , body : str , source_id : str ) -> str :
181179 """Render command in TOML format.
182180
183181 Args:
@@ -192,7 +190,7 @@ def render_toml_command(
192190
193191 if "description" in frontmatter :
194192 toml_lines .append (
195- f' description = { self ._render_basic_toml_string (frontmatter [" description" ])} '
193+ f" description = { self ._render_basic_toml_string (frontmatter [' description' ])} "
196194 )
197195 toml_lines .append ("" )
198196
@@ -252,9 +250,13 @@ def render_skill_command(
252250 frontmatter = {}
253251
254252 if agent_name in {"codex" , "kimi" }:
255- body = self .resolve_skill_placeholders (agent_name , frontmatter , body , project_root )
253+ body = self .resolve_skill_placeholders (
254+ agent_name , frontmatter , body , project_root
255+ )
256256
257- description = frontmatter .get ("description" , f"Spec-kit workflow command: { skill_name } " )
257+ description = frontmatter .get (
258+ "description" , f"Spec-kit workflow command: { skill_name } "
259+ )
258260 skill_frontmatter = self .build_skill_frontmatter (
259261 agent_name ,
260262 skill_name ,
@@ -288,7 +290,9 @@ def build_skill_frontmatter(
288290 return skill_frontmatter
289291
290292 @staticmethod
291- def resolve_skill_placeholders (agent_name : str , frontmatter : dict , body : str , project_root : Path ) -> str :
293+ def resolve_skill_placeholders (
294+ agent_name : str , frontmatter : dict , body : str , project_root : Path
295+ ) -> str :
292296 """Resolve script placeholders for skills-backed agents."""
293297 try :
294298 from . import load_init_options
@@ -312,7 +316,9 @@ def resolve_skill_placeholders(agent_name: str, frontmatter: dict, body: str, pr
312316 script_variant = init_opts .get ("script" )
313317 if script_variant not in {"sh" , "ps" }:
314318 fallback_order = []
315- default_variant = "ps" if platform .system ().lower ().startswith ("win" ) else "sh"
319+ default_variant = (
320+ "ps" if platform .system ().lower ().startswith ("win" ) else "sh"
321+ )
316322 secondary_variant = "sh" if default_variant == "ps" else "ps"
317323
318324 if default_variant in scripts or default_variant in agent_scripts :
@@ -334,15 +340,19 @@ def resolve_skill_placeholders(agent_name: str, frontmatter: dict, body: str, pr
334340 script_command = script_command .replace ("{ARGS}" , "$ARGUMENTS" )
335341 body = body .replace ("{SCRIPT}" , script_command )
336342
337- agent_script_command = agent_scripts .get (script_variant ) if script_variant else None
343+ agent_script_command = (
344+ agent_scripts .get (script_variant ) if script_variant else None
345+ )
338346 if agent_script_command :
339347 agent_script_command = agent_script_command .replace ("{ARGS}" , "$ARGUMENTS" )
340348 body = body .replace ("{AGENT_SCRIPT}" , agent_script_command )
341349
342350 body = body .replace ("{ARGS}" , "$ARGUMENTS" ).replace ("__AGENT__" , agent_name )
343351 return CommandRegistrar .rewrite_project_relative_paths (body )
344352
345- def _convert_argument_placeholder (self , content : str , from_placeholder : str , to_placeholder : str ) -> str :
353+ def _convert_argument_placeholder (
354+ self , content : str , from_placeholder : str , to_placeholder : str
355+ ) -> str :
346356 """Convert argument placeholder format.
347357
348358 Args:
@@ -356,14 +366,16 @@ def _convert_argument_placeholder(self, content: str, from_placeholder: str, to_
356366 return content .replace (from_placeholder , to_placeholder )
357367
358368 @staticmethod
359- def _compute_output_name (agent_name : str , cmd_name : str , agent_config : Dict [str , Any ]) -> str :
369+ def _compute_output_name (
370+ agent_name : str , cmd_name : str , agent_config : Dict [str , Any ]
371+ ) -> str :
360372 """Compute the on-disk command or skill name for an agent."""
361373 if agent_config ["extension" ] != "/SKILL.md" :
362374 return cmd_name
363375
364376 short_name = cmd_name
365377 if short_name .startswith ("speckit." ):
366- short_name = short_name [len ("speckit." ):]
378+ short_name = short_name [len ("speckit." ) :]
367379 short_name = short_name .replace ("." , "-" )
368380
369381 return f"speckit-{ short_name } "
@@ -375,7 +387,7 @@ def register_commands(
375387 source_id : str ,
376388 source_dir : Path ,
377389 project_root : Path ,
378- context_note : str = None
390+ context_note : str = None ,
379391 ) -> List [str ]:
380392 """Register commands for a specific agent.
381393
@@ -422,6 +434,11 @@ def register_commands(
422434 if agent_config .get ("inject_name" ) and not frontmatter .get ("name" ):
423435 frontmatter ["name" ] = cmd_name
424436
437+ # Resolve {SCRIPT} and {AGENT_SCRIPT} placeholders for all agents
438+ body = self .resolve_skill_placeholders (
439+ agent_name , frontmatter , body , project_root
440+ )
441+
425442 body = self ._convert_argument_placeholder (
426443 body , "$ARGUMENTS" , agent_config ["args" ]
427444 )
@@ -430,10 +447,18 @@ def register_commands(
430447
431448 if agent_config ["extension" ] == "/SKILL.md" :
432449 output = self .render_skill_command (
433- agent_name , output_name , frontmatter , body , source_id , cmd_file , project_root
450+ agent_name ,
451+ output_name ,
452+ frontmatter ,
453+ body ,
454+ source_id ,
455+ cmd_file ,
456+ project_root ,
434457 )
435458 elif agent_config ["format" ] == "markdown" :
436- output = self .render_markdown_command (frontmatter , body , source_id , context_note )
459+ output = self .render_markdown_command (
460+ frontmatter , body , source_id , context_note
461+ )
437462 elif agent_config ["format" ] == "toml" :
438463 output = self .render_toml_command (frontmatter , body , source_id )
439464 else :
@@ -449,7 +474,9 @@ def register_commands(
449474 registered .append (cmd_name )
450475
451476 for alias in cmd_info .get ("aliases" , []):
452- alias_output_name = self ._compute_output_name (agent_name , alias , agent_config )
477+ alias_output_name = self ._compute_output_name (
478+ agent_name , alias , agent_config
479+ )
453480
454481 # For agents with inject_name, render with alias-specific frontmatter
455482 if agent_config .get ("inject_name" ):
@@ -458,23 +485,43 @@ def register_commands(
458485
459486 if agent_config ["extension" ] == "/SKILL.md" :
460487 alias_output = self .render_skill_command (
461- agent_name , alias_output_name , alias_frontmatter , body , source_id , cmd_file , project_root
488+ agent_name ,
489+ alias_output_name ,
490+ alias_frontmatter ,
491+ body ,
492+ source_id ,
493+ cmd_file ,
494+ project_root ,
462495 )
463496 elif agent_config ["format" ] == "markdown" :
464- alias_output = self .render_markdown_command (alias_frontmatter , body , source_id , context_note )
497+ alias_output = self .render_markdown_command (
498+ alias_frontmatter , body , source_id , context_note
499+ )
465500 elif agent_config ["format" ] == "toml" :
466- alias_output = self .render_toml_command (alias_frontmatter , body , source_id )
501+ alias_output = self .render_toml_command (
502+ alias_frontmatter , body , source_id
503+ )
467504 else :
468- raise ValueError (f"Unsupported format: { agent_config ['format' ]} " )
505+ raise ValueError (
506+ f"Unsupported format: { agent_config ['format' ]} "
507+ )
469508 else :
470509 # For other agents, reuse the primary output
471510 alias_output = output
472511 if agent_config ["extension" ] == "/SKILL.md" :
473512 alias_output = self .render_skill_command (
474- agent_name , alias_output_name , frontmatter , body , source_id , cmd_file , project_root
513+ agent_name ,
514+ alias_output_name ,
515+ frontmatter ,
516+ body ,
517+ source_id ,
518+ cmd_file ,
519+ project_root ,
475520 )
476521
477- alias_file = commands_dir / f"{ alias_output_name } { agent_config ['extension' ]} "
522+ alias_file = (
523+ commands_dir / f"{ alias_output_name } { agent_config ['extension' ]} "
524+ )
478525 alias_file .parent .mkdir (parents = True , exist_ok = True )
479526 alias_file .write_text (alias_output , encoding = "utf-8" )
480527 if agent_name == "copilot" :
@@ -502,7 +549,7 @@ def register_commands_for_all_agents(
502549 source_id : str ,
503550 source_dir : Path ,
504551 project_root : Path ,
505- context_note : str = None
552+ context_note : str = None ,
506553 ) -> Dict [str , List [str ]]:
507554 """Register commands for all detected agents in the project.
508555
@@ -525,8 +572,12 @@ def register_commands_for_all_agents(
525572 if agent_dir .exists ():
526573 try :
527574 registered = self .register_commands (
528- agent_name , commands , source_id , source_dir , project_root ,
529- context_note = context_note
575+ agent_name ,
576+ commands ,
577+ source_id ,
578+ source_dir ,
579+ project_root ,
580+ context_note = context_note ,
530581 )
531582 if registered :
532583 results [agent_name ] = registered
@@ -536,9 +587,7 @@ def register_commands_for_all_agents(
536587 return results
537588
538589 def unregister_commands (
539- self ,
540- registered_commands : Dict [str , List [str ]],
541- project_root : Path
590+ self , registered_commands : Dict [str , List [str ]], project_root : Path
542591 ) -> None :
543592 """Remove previously registered command files from agent directories.
544593
@@ -555,13 +604,17 @@ def unregister_commands(
555604 commands_dir = project_root / agent_config ["dir" ]
556605
557606 for cmd_name in cmd_names :
558- output_name = self ._compute_output_name (agent_name , cmd_name , agent_config )
607+ output_name = self ._compute_output_name (
608+ agent_name , cmd_name , agent_config
609+ )
559610 cmd_file = commands_dir / f"{ output_name } { agent_config ['extension' ]} "
560611 if cmd_file .exists ():
561612 cmd_file .unlink ()
562613
563614 if agent_name == "copilot" :
564- prompt_file = project_root / ".github" / "prompts" / f"{ cmd_name } .prompt.md"
615+ prompt_file = (
616+ project_root / ".github" / "prompts" / f"{ cmd_name } .prompt.md"
617+ )
565618 if prompt_file .exists ():
566619 prompt_file .unlink ()
567620
0 commit comments