feat(integrations): support SPECIFY_<KEY>_EXTRA_ARGS env var for agent subprocess flags#2596
feat(integrations): support SPECIFY_<KEY>_EXTRA_ARGS env var for agent subprocess flags#2596doquanghuy wants to merge 2 commits into
Conversation
…t subprocess flags Read a per-integration env var (SPECIFY_<KEY>_EXTRA_ARGS) inside `SkillsIntegration.build_exec_args`, `MarkdownIntegration.build_exec_args`, and `TomlIntegration.build_exec_args` and append the parsed flags to the spawned agent's argv, gated per integration key. Operators can now opt into extra CLI flags (e.g. `SPECIFY_CLAUDE_EXTRA_ARGS=--dangerously-skip-permissions`) without modifying any SKILL or workflow YAML. Useful in CI / non-interactive contexts where the spawned `<agent> -p ...` would otherwise hang on an internal permission or input prompt invisible to the parent `specify workflow run` process. Key normalization: `kiro-cli` → `SPECIFY_KIRO_CLI_EXTRA_ARGS` (hyphen replaced with underscore, then uppercased). Default (env var unset or whitespace-only) is byte-identical to previous behaviour. Extra args are inserted between `-p prompt` and the model / output-format flags so they cannot clobber canonical Spec Kit args. Implementation: a single helper `IntegrationBase._apply_extra_args_env_var` encapsulates the env-var read + shlex parsing; each of the three concrete `build_exec_args` implementations calls it. Closes github#2595 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds a generic env-var hook SPECIFY_<KEY>_EXTRA_ARGS so operators can append CLI flags to the spawned agent subprocess (e.g. --dangerously-skip-permissions for Claude in CI/non-interactive contexts) without editing skills, workflows, or integration code. The hook is implemented once on IntegrationBase and invoked from the three concrete build_exec_args implementations in base.py.
Changes:
- New
IntegrationBase._apply_extra_args_env_varhelper that readsSPECIFY_<KEY>_EXTRA_ARGS, normalizes the key (hyphens→underscores, uppercased), andshlex.splits the value intoargs. MarkdownIntegration,TomlIntegration, andSkillsIntegrationbuild_exec_argsnow call the helper between[key, -p, prompt]and the--model/--output-formatflags.- Adds
tests/integrations/test_extra_args.pycovering no-op default, single/multi-token parsing, whitespace handling, per-integration scoping, key normalization, andrequires_cli=Falseshort-circuit.
Show a summary per file
| File | Description |
|---|---|
| src/specify_cli/integrations/base.py | Adds _apply_extra_args_env_var helper and calls it from the three concrete build_exec_args implementations. |
| tests/integrations/test_extra_args.py | New test module exercising the env-var hook through stub SkillsIntegration subclasses. |
Copilot's findings
Tip
Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Files reviewed: 2/2 changed files
- Comments generated: 2
| def _apply_extra_args_env_var(self, args: list[str]) -> None: | ||
| """Append `SPECIFY_<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_CLAUDE_EXTRA_ARGS="--dangerously-skip-permissions"`. | ||
| Hyphens in the integration key are replaced with underscores | ||
| and the key is uppercased | ||
| (e.g. `kiro-cli` → `SPECIFY_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_{self.key.upper().replace('-', '_')}_EXTRA_ARGS" | ||
| ) | ||
| extra = os.environ.get(env_name, "").strip() | ||
| if extra: | ||
| args.extend(shlex.split(extra)) |
| def _clean_extra_args_env(monkeypatch): | ||
| """Strip any leaked SPECIFY_*_EXTRA_ARGS from the test env so a | ||
| developer's shell setting doesn't pollute results.""" | ||
| for key in list(__import__("os").environ): |
…ncode/Copilot
Four integrations override `build_exec_args` and were silently
ignoring the env-var hook introduced in the previous commit:
- CodexIntegration (`codex exec ...`)
- DevinIntegration (`devin -p ...`)
- OpencodeIntegration (`opencode run ...`)
- CopilotIntegration (`copilot -p ...`)
Each now calls `self._apply_extra_args_env_var(args)` between the
base argv and the canonical Spec Kit flags (matching the placement
in `MarkdownIntegration`, `TomlIntegration`, and `SkillsIntegration`),
so operator-injected flags cannot clobber `--model` / `--output-format`
/ `--json`.
Adds 4 parameterized override-integration tests locking the wiring
per agent. Also cleans up an inline `__import__("os").environ` in the
fixture to a top-of-file `import os`.
Drive-by typing fix: guard `self.registrar_config.get(...)` in
`CopilotIntegration.add_commands` against the `None` case, matching
the pattern already used in `base.py` for the same access.
Addresses Copilot review on github#2596.
|
Pushed 8341c12 addressing both Copilot findings: 1. Override integrations wired up. 2. Test fixture cleanup. Replaced inline Coverage. Added 4 parameterized override-integration tests locking the wiring per agent — full suite is now 2947 passed (was 2943), 35 skipped, no regressions. Drive-by. Pyright surfaced a pre-existing latent AI disclosure: drafted with Claude Opus, human-reviewed. |
Description
Closes #2595.
Adds a per-integration env-var hook
(
SPECIFY_<KEY>_EXTRA_ARGS) so operators can inject extra CLIflags into the spawned agent subprocess without modifying any
SKILL or workflow YAML. The motivating use case is non-interactive
contexts (CI, batch, sandboxed eval) where the spawned
<agent> -p ...hangs silently on an internal permission orinput prompt invisible to the parent
specify workflow runprocess.
What changed
IntegrationBase._apply_extra_args_env_var(args)—reads
SPECIFY_<KEY>_EXTRA_ARGSfrom env (key normalized:-→
_, uppercased), parses withshlex.split, and appends toargs. No-op when the env var is unset or whitespace-only.
MarkdownIntegration.build_exec_args,TomlIntegration.build_exec_args, andSkillsIntegration.build_exec_argseach call the helper betweenargs = [self.key, "-p", prompt]and the model / output-formatappends — so extra args sit between
-p promptand thecanonical Spec Kit flags and cannot clobber them.
tests/integrations/test_extra_args.pywith 7 testscovering: default no-op, single-token, multi-token via shlex,
whitespace-only treated as unset, other-integration env var
ignored (per-key scoping), key normalization
(
kiro-cli→KIRO_CLI),requires_cli: Falseshort-circuit.
Default behaviour preserved
When
SPECIFY_<KEY>_EXTRA_ARGSis unset for the activeintegration,
build_exec_argsreturns byte-identical argv tobefore this change. Existing operators see no difference.
Examples
Testing
uv run specify --helppytest tests/ -q→2943 passed, 35 skipped (was 2936 before; +7 new tests
added in this PR).
specify workflow runagainst a Claude command step with
SPECIFY_CLAUDE_EXTRA_ARGS=--dangerously-skip-permissionsset — flag reaches the spawned
claude -p ...subprocessand the run completes non-interactively. Same workflow
without the env var still hangs as before (consistent with
the pre-PR behaviour).
Manual test results
Agent: Claude Code (CLI) | OS/Shell: macOS 14 / zsh
--dangerously-skip-permissionsappears between-p promptand--modelSPECIFY_GEMINI_EXTRA_ARGS=...does not affect Claude argv"--flag-a --flag-b 3"splits into 3 tokens" "→ no-opkiro-cli→KIRO_CLISPECIFY_KIRO_CLI_EXTRA_ARGScorrectly readrequires_cli: Falseshort-circuitbuild_exec_argsreturns None; env-var hook never reachedAI Disclosure
Used Claude Opus to draft the implementation (env-var hook +
helper extraction), the test suite, the issue body for #2595, and
this PR body. The reproducer scenario (non-interactive
claude -phang on permission prompts) was a real-world problem encountered
in operator practice. Code, tests, and design decisions were
human-reviewed before submission.