Skip to content

Commit 3d8429a

Browse files
mnriemelTorres
authored andcommitted
fix(copilot): use --yolo to grant all permissions in non-interactive mode (github#2298)
* fix(copilot): use --yolo to grant all permissions in non-interactive mode The Copilot CLI's --allow-all-tools flag only covers tool execution permissions but does not grant path or URL access. When the Copilot agent autonomously runs shell commands (e.g. npm run build) during workflow execution, the CLI blocks path access and cannot prompt for approval in non-interactive mode, producing: Permission denied and could not request permission from user Replace --allow-all-tools with --yolo (equivalent to --allow-all-tools --allow-all-paths --allow-all-urls) to grant all three permission types. Rename the opt-out env var from SPECKIT_ALLOW_ALL_TOOLS to SPECKIT_COPILOT_ALLOW_ALL to match the formal --allow-all alias and scope it to the Copilot integration. Fixes github#2294 * review: deprecate SPECKIT_ALLOW_ALL_TOOLS, rename to SPECKIT_COPILOT_ALLOW_ALL_TOOLS Address Copilot review feedback: - Honour the old SPECKIT_ALLOW_ALL_TOOLS env var as a fallback with a DeprecationWarning so existing opt-outs are not silently ignored. - Rename the new canonical env var to SPECKIT_COPILOT_ALLOW_ALL_TOOLS. - New var takes precedence when both are set. - Use monkeypatch in tests to avoid flakiness from ambient env vars. - Add tests for deprecation warning, precedence, and opt-out paths. * review: use UserWarning instead of DeprecationWarning DeprecationWarning is suppressed by default in Python, so users relying on the old SPECKIT_ALLOW_ALL_TOOLS env var would never see the deprecation notice during normal CLI runs. Switch to UserWarning which is always shown. Update test to also assert the warning category.
1 parent 64c12f6 commit 3d8429a

2 files changed

Lines changed: 72 additions & 11 deletions

File tree

src/specify_cli/integrations/copilot/__init__.py

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,40 @@
1010
from __future__ import annotations
1111

1212
import json
13+
import os
1314
import shutil
15+
import warnings
1416
from pathlib import Path
1517
from typing import Any
1618

1719
from ..base import IntegrationBase
1820
from ..manifest import IntegrationManifest
1921

2022

23+
def _allow_all() -> bool:
24+
"""Return True if the Copilot CLI should run with full permissions.
25+
26+
Checks ``SPECKIT_COPILOT_ALLOW_ALL_TOOLS`` first (new canonical name).
27+
Falls back to the deprecated ``SPECKIT_ALLOW_ALL_TOOLS`` if set,
28+
emitting a deprecation warning. Default when neither is set: enabled.
29+
"""
30+
new_var = os.environ.get("SPECKIT_COPILOT_ALLOW_ALL_TOOLS")
31+
if new_var is not None:
32+
return new_var != "0"
33+
34+
old_var = os.environ.get("SPECKIT_ALLOW_ALL_TOOLS")
35+
if old_var is not None:
36+
warnings.warn(
37+
"SPECKIT_ALLOW_ALL_TOOLS is deprecated; "
38+
"use SPECKIT_COPILOT_ALLOW_ALL_TOOLS instead.",
39+
UserWarning,
40+
stacklevel=2,
41+
)
42+
return old_var != "0"
43+
44+
return True
45+
46+
2147
class CopilotIntegration(IntegrationBase):
2248
"""Integration for GitHub Copilot (VS Code IDE + CLI).
2349
@@ -50,13 +76,15 @@ def build_exec_args(
5076
output_json: bool = True,
5177
) -> list[str] | None:
5278
# GitHub Copilot CLI uses ``copilot -p "prompt"`` for
53-
# non-interactive mode. --allow-all-tools is required for the
54-
# agent to perform file edits and shell commands. Controlled
55-
# by SPECKIT_ALLOW_ALL_TOOLS env var (default: enabled).
56-
import os
79+
# non-interactive mode. --yolo enables all permissions
80+
# (tools, paths, and URLs) so the agent can perform file
81+
# edits and shell commands without interactive prompts.
82+
# Controlled by SPECKIT_COPILOT_ALLOW_ALL_TOOLS env var
83+
# (default: enabled). The deprecated SPECKIT_ALLOW_ALL_TOOLS
84+
# is also honoured as a fallback.
5785
args = ["copilot", "-p", prompt]
58-
if os.environ.get("SPECKIT_ALLOW_ALL_TOOLS", "1") != "0":
59-
args.append("--allow-all-tools")
86+
if _allow_all():
87+
args.append("--yolo")
6088
if model:
6189
args.extend(["--model", model])
6290
if output_json:
@@ -91,13 +119,12 @@ def dispatch_command(
91119
agent_name = f"speckit.{stem}"
92120

93121
prompt = args or ""
94-
import os
95122
cli_args = [
96123
"copilot", "-p", prompt,
97124
"--agent", agent_name,
98125
]
99-
if os.environ.get("SPECKIT_ALLOW_ALL_TOOLS", "1") != "0":
100-
cli_args.append("--allow-all-tools")
126+
if _allow_all():
127+
cli_args.append("--yolo")
101128
if model:
102129
cli_args.extend(["--model", model])
103130
if not stream:

tests/test_workflows.py

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -367,15 +367,49 @@ def test_codex_exec_args(self):
367367
assert args[2] == "do stuff"
368368
assert "--json" in args
369369

370-
def test_copilot_exec_args(self):
370+
def test_copilot_exec_args(self, monkeypatch):
371+
monkeypatch.delenv("SPECKIT_COPILOT_ALLOW_ALL_TOOLS", raising=False)
372+
monkeypatch.delenv("SPECKIT_ALLOW_ALL_TOOLS", raising=False)
371373
from specify_cli.integrations.copilot import CopilotIntegration
372374
impl = CopilotIntegration()
373375
args = impl.build_exec_args("do stuff", model="claude-sonnet-4-20250514")
374376
assert args[0] == "copilot"
375377
assert "-p" in args
376-
assert "--allow-all-tools" in args
378+
assert "--yolo" in args
377379
assert "--model" in args
378380

381+
def test_copilot_new_env_var_disables_yolo(self, monkeypatch):
382+
monkeypatch.setenv("SPECKIT_COPILOT_ALLOW_ALL_TOOLS", "0")
383+
monkeypatch.delenv("SPECKIT_ALLOW_ALL_TOOLS", raising=False)
384+
from specify_cli.integrations.copilot import CopilotIntegration
385+
impl = CopilotIntegration()
386+
args = impl.build_exec_args("do stuff")
387+
assert "--yolo" not in args
388+
389+
def test_copilot_deprecated_env_var_still_honoured(self, monkeypatch):
390+
monkeypatch.delenv("SPECKIT_COPILOT_ALLOW_ALL_TOOLS", raising=False)
391+
monkeypatch.setenv("SPECKIT_ALLOW_ALL_TOOLS", "0")
392+
import warnings
393+
from specify_cli.integrations.copilot import CopilotIntegration
394+
impl = CopilotIntegration()
395+
with warnings.catch_warnings(record=True) as w:
396+
warnings.simplefilter("always")
397+
args = impl.build_exec_args("do stuff")
398+
assert "--yolo" not in args
399+
assert any(
400+
"SPECKIT_ALLOW_ALL_TOOLS is deprecated" in str(x.message)
401+
and issubclass(x.category, UserWarning)
402+
for x in w
403+
)
404+
405+
def test_copilot_new_env_var_takes_precedence(self, monkeypatch):
406+
monkeypatch.setenv("SPECKIT_COPILOT_ALLOW_ALL_TOOLS", "1")
407+
monkeypatch.setenv("SPECKIT_ALLOW_ALL_TOOLS", "0")
408+
from specify_cli.integrations.copilot import CopilotIntegration
409+
impl = CopilotIntegration()
410+
args = impl.build_exec_args("do stuff")
411+
assert "--yolo" in args
412+
379413
def test_ide_only_returns_none(self):
380414
from specify_cli.integrations.windsurf import WindsurfIntegration
381415
impl = WindsurfIntegration()

0 commit comments

Comments
 (0)