Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions src/specify_cli/integrations/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@

from __future__ import annotations

import os
import re
import shlex
import shutil
from abc import ABC
from dataclasses import dataclass
Expand Down Expand Up @@ -138,6 +140,33 @@ def build_exec_args(
"""
return None

def _apply_extra_args_env_var(self, args: list[str]) -> None:
"""Append `SPECIFY_INTEGRATION_<KEY>_EXTRA_ARGS` env-var value to *args*.

Operators can inject extra CLI flags into the spawned agent
subprocess by setting an env var named for the integration key,
e.g. `SPECIFY_INTEGRATION_CLAUDE_EXTRA_ARGS="--dangerously-skip-permissions"`.
The `INTEGRATION` segment scopes the variable to this subsystem
so it does not collide with other Spec Kit env-var namespaces.
Hyphens in the integration key are replaced with underscores
and the key is uppercased
(e.g. `kiro-cli` → `SPECIFY_INTEGRATION_KIRO_CLI_EXTRA_ARGS`).

Useful in CI / non-interactive contexts where the spawned agent
needs flags that change its prompt-handling behaviour.
Default behaviour (env var unset or whitespace-only) is a no-op
— *args* is unchanged. Multi-token values are parsed via
`shlex.split`.

See issue #2595.
"""
env_name = (
f"SPECIFY_INTEGRATION_{self.key.upper().replace('-', '_')}_EXTRA_ARGS"
)
extra = os.environ.get(env_name, "").strip()
if extra:
args.extend(shlex.split(extra))
Comment thread
doquanghuy marked this conversation as resolved.

def build_command_invocation(self, command_name: str, args: str = "") -> str:
"""Build the native slash-command invocation for a Spec Kit command.

Expand Down Expand Up @@ -851,6 +880,7 @@ def build_exec_args(
if not self.config or not self.config.get("requires_cli"):
return None
args = [self.key, "-p", prompt]
self._apply_extra_args_env_var(args)
if model:
args.extend(["--model", model])
if output_json:
Expand Down Expand Up @@ -938,6 +968,7 @@ def build_exec_args(
if not self.config or not self.config.get("requires_cli"):
return None
args = [self.key, "-p", prompt]
self._apply_extra_args_env_var(args)
if model:
args.extend(["-m", model])
if output_json:
Expand Down Expand Up @@ -1356,6 +1387,7 @@ def build_exec_args(
if not self.config or not self.config.get("requires_cli"):
return None
args = [self.key, "-p", prompt]
self._apply_extra_args_env_var(args)
if model:
args.extend(["--model", model])
if output_json:
Expand Down
1 change: 1 addition & 0 deletions src/specify_cli/integrations/codex/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ def build_exec_args(
) -> list[str] | None:
# Codex uses ``codex exec "prompt"`` for non-interactive mode.
args: list[str] = ["codex", "exec", prompt]
self._apply_extra_args_env_var(args)
if model:
args.extend(["--model", model])
if output_json:
Expand Down
12 changes: 11 additions & 1 deletion src/specify_cli/integrations/copilot/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ def build_exec_args(
# (default: enabled). The deprecated SPECKIT_ALLOW_ALL_TOOLS
# is also honoured as a fallback.
args = ["copilot", "-p", prompt]
self._apply_extra_args_env_var(args)
if _allow_all():
args.append("--yolo")
if model:
Comment thread
doquanghuy marked this conversation as resolved.
Expand Down Expand Up @@ -207,6 +208,11 @@ def dispatch_command(
prompt = args or ""

cli_args = ["copilot", "-p", prompt]
# Honour SPECIFY_INTEGRATION_COPILOT_EXTRA_ARGS for real workflow runs.
# `dispatch_command` builds cli_args inline rather than going
# through `build_exec_args`, so the hook must be invoked here
# too — otherwise the env var is silently ignored.
self._apply_extra_args_env_var(cli_args)
if not skills_mode:
cli_args.extend(["--agent", agent_name])
if _allow_all():
Expand Down Expand Up @@ -358,7 +364,11 @@ def _setup_default(
created: list[Path] = []

script_type = opts.get("script_type", "sh")
arg_placeholder = self.registrar_config.get("args", "$ARGUMENTS")
arg_placeholder = (
self.registrar_config.get("args", "$ARGUMENTS")
if self.registrar_config
else "$ARGUMENTS"
)

# 1. Process and write command files as .agent.md
for src_file in templates:
Expand Down
1 change: 1 addition & 0 deletions src/specify_cli/integrations/devin/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ def build_exec_args(
kept on the integration for tool detection.
"""
args = [self.key, "-p", prompt]
self._apply_extra_args_env_var(args)
if model:
args.extend(["--model", model])
return args
Expand Down
5 changes: 5 additions & 0 deletions src/specify_cli/integrations/opencode/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ def build_exec_args(
output_json: bool = True,
) -> list[str] | None:
args = [self.key, "run"]
# Apply operator-injected extra args before the prompt-derived
# --command and the canonical --format/-m flags so Spec Kit's
# later appends remain authoritative under repeated-flag CLI
# semantics.
self._apply_extra_args_env_var(args)

message = prompt
if prompt.startswith("/"):
Expand Down
Loading