@@ -87,6 +87,26 @@ class IntegrationBase(ABC):
8787 invoke_separator : str = "."
8888 """Separator used in slash-command invocations (``"."`` → ``/speckit.plan``)."""
8989
90+ # -- Declarative batch-mode attributes --------------------------------
91+
92+ exec_mode : str = "flag"
93+ """How the CLI accepts a prompt: ``"flag"`` (``-p "prompt"``),
94+ ``"subcommand"`` (``<subcmd> "prompt"``), or ``"none"`` (no CLI dispatch)."""
95+
96+ exec_prompt_flag : str = "-p"
97+ """Flag used to pass the prompt when ``exec_mode == "flag"``."""
98+
99+ exec_subcommand : str = ""
100+ """Subcommand inserted before the prompt when ``exec_mode == "subcommand"``."""
101+
102+ exec_model_flag : str = "--model"
103+ """Flag for model selection (e.g. ``"--model"``, ``"-m"``).
104+ Set to ``""`` to omit model passing entirely."""
105+
106+ exec_json_args : tuple [str , ...] = ("--output-format" , "json" )
107+ """Arguments appended when JSON output is requested.
108+ Set to ``()`` if the CLI has no structured-output flag."""
109+
90110 # -- Markers for managed context section ------------------------------
91111
92112 CONTEXT_MARKER_START = "<!-- SPECKIT START -->"
@@ -124,9 +144,31 @@ def build_exec_args(
124144 non-interactively using this integration's CLI tool, or ``None``
125145 if the integration does not support CLI dispatch.
126146
127- Subclasses for CLI-based integrations should override this.
147+ The default implementation uses the declarative ``exec_*`` class
148+ attributes. Integrations with complex dispatch logic (e.g.
149+ dynamic flags) can still override this method directly.
128150 """
129- return None
151+ if not self .config or not self .config .get ("requires_cli" ):
152+ return None
153+ if self .exec_mode == "none" :
154+ return None
155+
156+ args = [self .key ]
157+
158+ if self .exec_mode == "subcommand" and self .exec_subcommand :
159+ args .append (self .exec_subcommand )
160+
161+ if self .exec_mode == "flag" :
162+ args .extend ([self .exec_prompt_flag , prompt ])
163+ elif self .exec_mode == "subcommand" :
164+ args .append (prompt )
165+
166+ if model and self .exec_model_flag :
167+ args .extend ([self .exec_model_flag , model ])
168+ if output_json and self .exec_json_args :
169+ args .extend (self .exec_json_args )
170+
171+ return args
130172
131173 def build_command_invocation (self , command_name : str , args : str = "" ) -> str :
132174 """Build the native slash-command invocation for a Spec Kit command.
@@ -830,22 +872,6 @@ class MarkdownIntegration(IntegrationBase):
830872 managed context section into the agent context file.
831873 """
832874
833- def build_exec_args (
834- self ,
835- prompt : str ,
836- * ,
837- model : str | None = None ,
838- output_json : bool = True ,
839- ) -> list [str ] | None :
840- if not self .config or not self .config .get ("requires_cli" ):
841- return None
842- args = [self .key , "-p" , prompt ]
843- if model :
844- args .extend (["--model" , model ])
845- if output_json :
846- args .extend (["--output-format" , "json" ])
847- return args
848-
849875 def setup (
850876 self ,
851877 project_root : Path ,
@@ -917,21 +943,7 @@ class TomlIntegration(IntegrationBase):
917943 TOML format (``description`` key + ``prompt`` multiline string).
918944 """
919945
920- def build_exec_args (
921- self ,
922- prompt : str ,
923- * ,
924- model : str | None = None ,
925- output_json : bool = True ,
926- ) -> list [str ] | None :
927- if not self .config or not self .config .get ("requires_cli" ):
928- return None
929- args = [self .key , "-p" , prompt ]
930- if model :
931- args .extend (["-m" , model ])
932- if output_json :
933- args .extend (["--output-format" , "json" ])
934- return args
946+ exec_model_flag = "-m"
935947
936948 def command_filename (self , template_name : str ) -> str :
937949 """TOML commands use ``.toml`` extension."""
@@ -1315,22 +1327,6 @@ class SkillsIntegration(IntegrationBase):
13151327
13161328 invoke_separator = "-"
13171329
1318- def build_exec_args (
1319- self ,
1320- prompt : str ,
1321- * ,
1322- model : str | None = None ,
1323- output_json : bool = True ,
1324- ) -> list [str ] | None :
1325- if not self .config or not self .config .get ("requires_cli" ):
1326- return None
1327- args = [self .key , "-p" , prompt ]
1328- if model :
1329- args .extend (["--model" , model ])
1330- if output_json :
1331- args .extend (["--output-format" , "json" ])
1332- return args
1333-
13341330 def skills_dest (self , project_root : Path ) -> Path :
13351331 """Return the absolute path to the skills output directory.
13361332
0 commit comments