mirror of
https://github.com/github/spec-kit.git
synced 2026-07-03 12:28:06 +08:00
fix: default non-interactive init to copilot integration (#2414)
* fix: default non-interactive init integration * chore: clarify non-interactive init default integration * Address non-interactive init review feedback * Fix interactive init test after fallback
This commit is contained in:
@@ -485,7 +485,7 @@ specify init --here --force
|
||||
|
||||

|
||||
|
||||
You will be prompted to select the coding agent integration you are using. You can also proactively specify it directly in the terminal:
|
||||
In an interactive terminal, you will be prompted to select the coding agent integration you are using. In non-interactive sessions, such as CI or piped runs, `specify init` defaults to GitHub Copilot unless you pass `--integration`. You can also proactively specify the integration directly in the terminal:
|
||||
|
||||
```bash
|
||||
specify init <project_name> --integration copilot
|
||||
|
||||
@@ -41,6 +41,8 @@ uvx --from git+https://github.com/github/spec-kit.git@vX.Y.Z specify init --here
|
||||
|
||||
### Specify Integration
|
||||
|
||||
Interactive terminals prompt you to choose a coding agent integration during initialization. Non-interactive sessions, such as CI or piped runs, default to GitHub Copilot unless you pass `--integration`.
|
||||
|
||||
You can proactively specify your coding agent integration during initialization:
|
||||
|
||||
```bash
|
||||
|
||||
@@ -28,6 +28,8 @@ Creates a new Spec Kit project with the necessary directory structure, templates
|
||||
|
||||
Use `<project_name>` to create a new directory, or `--here` (or `.`) to initialize in the current directory. If the directory already has files, use `--force` to merge without confirmation.
|
||||
|
||||
When `--integration` is omitted, interactive terminals prompt you to choose an integration. Non-interactive sessions, such as CI or piped runs, default to GitHub Copilot; pass `--integration <key>` to choose a different integration explicitly.
|
||||
|
||||
### Examples
|
||||
|
||||
```bash
|
||||
|
||||
@@ -90,6 +90,7 @@ def _build_agent_config() -> dict[str, dict[str, Any]]:
|
||||
return config
|
||||
|
||||
AGENT_CONFIG = _build_agent_config()
|
||||
DEFAULT_INIT_INTEGRATION = "copilot"
|
||||
|
||||
AI_ASSISTANT_ALIASES = {
|
||||
"kiro": "kiro-cli",
|
||||
@@ -152,6 +153,9 @@ def _build_ai_deprecation_warning(
|
||||
f"Use [bold]{replacement}[/bold] instead."
|
||||
)
|
||||
|
||||
def _stdin_is_interactive() -> bool:
|
||||
return sys.stdin.isatty()
|
||||
|
||||
SCRIPT_TYPE_CHOICES = {"sh": "POSIX Shell (bash/zsh)", "ps": "PowerShell"}
|
||||
|
||||
CLAUDE_LOCAL_PATH = Path.home() / ".claude" / "local" / "claude"
|
||||
@@ -995,7 +999,8 @@ def init(
|
||||
|
||||
This command will:
|
||||
1. Check that required tools are installed (git is optional)
|
||||
2. Let you choose your coding agent integration
|
||||
2. Let you choose your coding agent integration, or default to Copilot
|
||||
in non-interactive sessions
|
||||
3. Download template from GitHub (or use bundled assets with --offline)
|
||||
4. Initialize a fresh git repository (if not --no-git and no existing repo)
|
||||
5. Optionally set up coding agent integration commands
|
||||
@@ -1162,13 +1167,19 @@ def init(
|
||||
console.print(f"[red]Error:[/red] Invalid AI assistant '{ai_assistant}'. Choose from: {', '.join(AGENT_CONFIG.keys())}")
|
||||
raise typer.Exit(1)
|
||||
selected_ai = ai_assistant
|
||||
elif not _stdin_is_interactive():
|
||||
console.print(
|
||||
f"[dim]Non-interactive session detected: defaulting to '{DEFAULT_INIT_INTEGRATION}'. "
|
||||
"Use --integration to choose a different agent.[/dim]"
|
||||
)
|
||||
selected_ai = DEFAULT_INIT_INTEGRATION
|
||||
else:
|
||||
# Create options dict for selection (agent_key: display_name)
|
||||
ai_choices = {key: config["name"] for key, config in AGENT_CONFIG.items()}
|
||||
selected_ai = select_with_arrows(
|
||||
ai_choices,
|
||||
"Choose your coding agent integration:",
|
||||
"copilot"
|
||||
DEFAULT_INIT_INTEGRATION,
|
||||
)
|
||||
|
||||
# Auto-promote interactively selected agents to the integration path
|
||||
@@ -1233,7 +1244,7 @@ def init(
|
||||
else:
|
||||
default_script = "ps" if os.name == "nt" else "sh"
|
||||
|
||||
if sys.stdin.isatty():
|
||||
if _stdin_is_interactive():
|
||||
selected_script = select_with_arrows(SCRIPT_TYPE_CHOICES, "Choose script type (or press Enter)", default_script)
|
||||
else:
|
||||
selected_script = default_script
|
||||
|
||||
@@ -81,6 +81,29 @@ class TestInitIntegrationFlag:
|
||||
shared_manifest = project / ".specify" / "integrations" / "speckit.manifest.json"
|
||||
assert shared_manifest.exists()
|
||||
|
||||
def test_noninteractive_init_defaults_to_copilot(self, tmp_path, monkeypatch):
|
||||
from typer.testing import CliRunner
|
||||
from specify_cli import app
|
||||
import specify_cli
|
||||
|
||||
def fail_select(*_args, **_kwargs):
|
||||
raise AssertionError("non-interactive init should not open the integration picker")
|
||||
|
||||
monkeypatch.setattr(specify_cli, "select_with_arrows", fail_select)
|
||||
|
||||
runner = CliRunner()
|
||||
project = tmp_path / "noninteractive"
|
||||
result = runner.invoke(app, [
|
||||
"init", str(project), "--script", "sh", "--no-git", "--ignore-agent-tools",
|
||||
], catch_exceptions=False)
|
||||
|
||||
assert result.exit_code == 0, result.output
|
||||
assert f"defaulting to '{specify_cli.DEFAULT_INIT_INTEGRATION}'" in result.output
|
||||
assert (project / ".github" / "agents" / "speckit.plan.agent.md").exists()
|
||||
|
||||
data = json.loads((project / ".specify" / "integration.json").read_text(encoding="utf-8"))
|
||||
assert data["integration"] == specify_cli.DEFAULT_INIT_INTEGRATION
|
||||
|
||||
def test_ai_copilot_auto_promotes(self, tmp_path):
|
||||
from typer.testing import CliRunner
|
||||
from specify_cli import app
|
||||
|
||||
@@ -196,7 +196,10 @@ class TestClaudeIntegration:
|
||||
try:
|
||||
os.chdir(project)
|
||||
runner = CliRunner()
|
||||
with patch("specify_cli.select_with_arrows", return_value="claude"):
|
||||
with (
|
||||
patch("specify_cli._stdin_is_interactive", return_value=True),
|
||||
patch("specify_cli.select_with_arrows", return_value="claude"),
|
||||
):
|
||||
result = runner.invoke(
|
||||
app,
|
||||
[
|
||||
|
||||
Reference in New Issue
Block a user