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
15 changes: 14 additions & 1 deletion src/specify_cli/commands/init.py
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,20 @@ def init(
"[cyan]--force supplied: skipping confirmation and proceeding with merge[/cyan]"
)
else:
response = typer.confirm("Do you want to continue?")
try:
response = typer.confirm("Do you want to continue?")
except (typer.Abort, EOFError):
# No confirmation input available (non-interactive session
# with empty stdin): fail fast with actionable guidance
# instead of the bare "Aborted." Piped input (e.g. "y") is
# still honored above. Mirrors the named-project path,
# which already points to --force.
console.print(
"[red]Error:[/red] Current directory is not empty and no "
"confirmation input is available. Re-run with "
"[bold]--force[/bold] to merge into it."
)
raise typer.Exit(1) from None
Comment on lines +233 to +246
if not response:
console.print("[yellow]Operation cancelled[/yellow]")
raise typer.Exit(0)
Expand Down
26 changes: 26 additions & 0 deletions tests/integrations/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,32 @@ def fail_select(*_args, **_kwargs):
data = json.loads((project / ".specify" / "integration.json").read_text(encoding="utf-8"))
assert data["integration"] == specify_cli.DEFAULT_INIT_INTEGRATION

def test_init_here_nonempty_noninteractive_errors_with_force_guidance(self, tmp_path):
"""`init --here` on a non-empty directory with no confirmation input (empty
stdin) must fail fast with guidance to use --force, instead of the bare
'Aborted.' from an EOF on typer.confirm. CliRunner with no `input=` provides
empty stdin, so typer.confirm raises Abort, which the command converts to the
actionable error."""
from typer.testing import CliRunner
from specify_cli import app

project = tmp_path / "nonempty-here"
project.mkdir()
(project / "existing.txt").write_text("keep me", encoding="utf-8")
old_cwd = os.getcwd()
try:
os.chdir(project)
result = CliRunner().invoke(app, [
"init", "--here", "--integration", "copilot", "--script", "sh", "--ignore-agent-tools",
], catch_exceptions=False)
finally:
os.chdir(old_cwd)

assert result.exit_code == 1, result.output
assert "--force" in result.output
# Aborted before scaffolding: the pre-existing file is untouched.
assert (project / "existing.txt").read_text(encoding="utf-8") == "keep me"

def test_integration_copilot_auto_promotes(self, tmp_path):
from typer.testing import CliRunner
from specify_cli import app
Expand Down