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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -478,7 +478,7 @@ specify init --here --force

![Specify CLI bootstrapping a new project in the terminal](./media/specify_cli.gif)

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
Expand Down
2 changes: 2 additions & 0 deletions docs/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions docs/reference/core.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,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
Expand Down
12 changes: 10 additions & 2 deletions src/specify_cli/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,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",
Expand Down Expand Up @@ -997,7 +998,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
Expand Down Expand Up @@ -1164,13 +1166,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 sys.stdin.isatty():
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
Expand Down
23 changes: 23 additions & 0 deletions tests/integrations/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,29 @@ def test_integration_copilot_creates_files(self, tmp_path):
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
Expand Down