Skip to content

Commit 90d1d69

Browse files
mnriemCopilot
andcommitted
feat(init)!: make git extension opt-in and remove --no-git at v0.10.0
- Remove --no-git parameter from specify init command - Remove git extension auto-installation from init flow - Git repository initialization (git init) still runs when git is available - Remove --no-git from all test invocations across the test suite - Update docs to reflect opt-in git extension behavior - Replace TestGitExtensionAutoInstall with TestGitExtensionOptIn tests BREAKING CHANGE: specify init no longer auto-installs the git extension. Use `specify extension add git` to install it explicitly. The --no-git flag has been removed. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 7106858 commit 90d1d69

25 files changed

Lines changed: 105 additions & 292 deletions

docs/local-development.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ rm -rf .venv dist build *.egg-info
162162
|---------|-----|
163163
| `ModuleNotFoundError: typer` | Run `uv pip install -e .` |
164164
| Scripts not executable (Linux) | Re-run init or `chmod +x scripts/*.sh` |
165-
| Git step skipped | You passed `--no-git` or Git not installed |
165+
| Git step skipped | Git not installed on your system |
166166
| Wrong script type downloaded | Pass `--script sh` or `--script ps` explicitly |
167167
| TLS errors on corporate network | Configure your environment's certificate store or proxy. The `--skip-tls` flag is deprecated and has no effect. |
168168

docs/reference/core.md

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,14 @@ specify init [<project_name>]
1515
| `--script sh\|ps` | Script type: `sh` (bash/zsh) or `ps` (PowerShell) |
1616
| `--here` | Initialize in the current directory instead of creating a new one |
1717
| `--force` | Force merge/overwrite when initializing in an existing directory |
18-
| `--no-git` | Skip git repository initialization |
1918
| `--ignore-agent-tools` | Skip checks for AI coding agent CLI tools |
2019
| `--preset <id>` | Install a preset during initialization |
2120
| `--branch-numbering` | Branch numbering strategy: `sequential` (default) or `timestamp` |
2221

2322
Creates a new Spec Kit project with the necessary directory structure, templates, scripts, and AI coding agent integration files.
2423

2524
> [!NOTE]
26-
> The git extension is currently enabled by default during `specify init`.
27-
> Starting in `v0.10.0`, it will require explicit opt-in. To add it after init, run `specify extension add git`.
25+
> The git extension is opt-in during `specify init`. To add it after init, run `specify extension add git`.
2826
2927
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.
3028

@@ -45,9 +43,6 @@ specify init --here --force --integration copilot
4543
# Use PowerShell scripts (Windows/cross-platform)
4644
specify init my-project --integration copilot --script ps
4745

48-
# Skip git initialization
49-
specify init my-project --integration copilot --no-git
50-
5146
# Install a preset during initialization
5247
specify init my-project --integration copilot --preset compliance
5348

docs/upgrade.md

Lines changed: 8 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -257,58 +257,28 @@ rm speckit.old-command-name.md
257257
# Restart your IDE
258258
```
259259

260-
### Scenario 4: "I'm working on a project without Git"
260+
### Scenario 4: "I don't want the git extension"
261261

262-
If you initialized your project with `--no-git`, you can still upgrade:
262+
The git extension is now opt-in, so upgrades do not install it unless you add it explicitly.
263263

264264
```bash
265265
# Manually back up files you customized
266-
cp .specify/memory/constitution.md /tmp/constitution-backup.md
266+
cp .specify/memory/constitution.md .specify/memory/constitution.backup.md
267267

268268
# Run upgrade
269-
specify init --here --force --integration copilot --no-git
269+
specify init --here --force --integration copilot
270270

271271
# Restore customizations
272-
mv /tmp/constitution-backup.md .specify/memory/constitution.md
273-
```
274-
275-
The `--no-git` flag skips git initialization but doesn't affect file updates.
276-
277-
---
278-
279-
## Using `--no-git` Flag
280-
281-
The `--no-git` flag tells Spec Kit to **skip git repository initialization**. This is useful when:
282-
283-
- You manage version control differently (Mercurial, SVN, etc.)
284-
- Your project is part of a larger monorepo with existing git setup
285-
- You're experimenting and don't want version control yet
286-
287-
**During initial setup:**
288-
289-
```bash
290-
specify init my-project --integration copilot --no-git
272+
mv .specify/memory/constitution.backup.md .specify/memory/constitution.md
291273
```
292274

293-
**During upgrade:**
275+
If you later decide you want the git extension's commands and hooks, install it explicitly:
294276

295277
```bash
296-
specify init --here --force --integration copilot --no-git
278+
specify extension add git
297279
```
298280

299-
### What `--no-git` does NOT do
300-
301-
❌ Does NOT prevent file updates
302-
❌ Does NOT skip slash command installation
303-
❌ Does NOT affect template merging
304-
305-
It **only** skips running `git init` and creating the initial commit.
306-
307-
### Working without Git
308-
309-
If you use `--no-git`, you'll need to manage feature directories manually:
310-
311-
**Set the `SPECIFY_FEATURE` environment variable** before using planning commands:
281+
Projects that do not use Git can still work with Spec Kit by setting `SPECIFY_FEATURE` manually before planning commands:
312282

313283
```bash
314284
# Bash/Zsh
@@ -318,10 +288,6 @@ export SPECIFY_FEATURE="001-my-feature"
318288
$env:SPECIFY_FEATURE = "001-my-feature"
319289
```
320290

321-
This tells Spec Kit which feature directory to use when creating specs, plans, and tasks.
322-
323-
**Why this matters:** Without git, Spec Kit can't detect your current branch name to determine the active feature. The environment variable provides that context manually.
324-
325291
---
326292

327293
## Troubleshooting

src/specify_cli/commands/init.py

Lines changed: 25 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,6 @@ def init(
7171
project_name: str = typer.Argument(None, help="Name for your new project directory (optional if using --here, or use '.' for current directory)"),
7272
script_type: str = typer.Option(None, "--script", help="Script type to use: sh or ps"),
7373
ignore_agent_tools: bool = typer.Option(False, "--ignore-agent-tools", help="Skip checks for coding agent tools like Claude Code"),
74-
no_git: bool = typer.Option(False, "--no-git", help="Skip git repository initialization"),
7574
here: bool = typer.Option(False, "--here", help="Initialize project in the current directory instead of creating a new one"),
7675
force: bool = typer.Option(False, "--force", help="Force merge/overwrite when using --here (skip confirmation)"),
7776
skip_tls: bool = typer.Option(False, "--skip-tls", help="Deprecated (no-op). Previously: skip SSL/TLS verification.", hidden=True),
@@ -96,13 +95,12 @@ def init(
9695
in non-interactive sessions
9796
3. Install bundled Spec Kit templates, scripts, workflow, and shared
9897
project infrastructure
99-
4. Initialize a fresh git repository (if not --no-git and no existing repo)
98+
4. Initialize a fresh git repository (if git is available and no existing repo)
10099
5. Set up coding agent integration commands and optional presets
101100
102101
Examples:
103102
specify init my-project
104103
specify init my-project --integration claude
105-
specify init my-project --integration copilot --no-git
106104
specify init --ignore-agent-tools my-project
107105
specify init . --integration claude # Initialize in current directory
108106
specify init . # Initialize in current directory (interactive integration selection)
@@ -142,13 +140,6 @@ def init(
142140
console.print(f"[yellow]Available integrations:[/yellow] {available}")
143141
raise typer.Exit(1)
144142

145-
if no_git:
146-
console.print(
147-
"[yellow]⚠️ --no-git is deprecated and will be removed in v0.10.0.[/yellow]\n"
148-
"[yellow]The git extension will no longer be enabled by default "
149-
"— use the [bold]specify extension[/bold] commands to install or enable the git extension if needed.[/yellow]"
150-
)
151-
152143
if project_name == ".":
153144
here = True
154145
project_name = None
@@ -253,11 +244,9 @@ def init(
253244

254245
console.print(Panel("\n".join(setup_lines), border_style="cyan", padding=(1, 2)))
255246

256-
should_init_git = False
257-
if not no_git:
258-
should_init_git = check_tool("git")
259-
if not should_init_git:
260-
console.print("[yellow]Git not found - will skip repository initialization[/yellow]")
247+
should_init_git = check_tool("git")
248+
if not should_init_git:
249+
console.print("[yellow]Git not found - will skip repository initialization[/yellow]")
261250

262251
if not ignore_agent_tools:
263252
agent_config = AGENT_CONFIG.get(selected_ai)
@@ -308,15 +297,13 @@ def init(
308297
for key, label in [
309298
("chmod", "Ensure scripts executable"),
310299
("constitution", "Constitution setup"),
311-
("git", "Install git extension"),
300+
("git", "Git repository setup"),
312301
("workflow", "Install bundled workflow"),
313302
("agent-context", "Install agent-context extension"),
314303
("final", "Finalize"),
315304
]:
316305
tracker.add(key, label)
317306

318-
git_default_notice = False
319-
320307
with Live(tracker.render(), console=console, refresh_per_second=8, transient=True) as live:
321308
tracker.attach_refresh(lambda: live.update(tracker.render()))
322309
try:
@@ -369,54 +356,29 @@ def init(
369356

370357
ensure_constitution_from_template(project_path, tracker=tracker)
371358

372-
if not no_git:
373-
tracker.start("git")
374-
git_messages = []
375-
git_has_error = False
376-
if is_git_repo(project_path):
377-
git_messages.append("existing repo detected")
378-
elif should_init_git:
379-
success, error_msg = init_git_repo(project_path, quiet=True)
380-
if success:
381-
git_messages.append("initialized")
382-
else:
383-
git_has_error = True
384-
if error_msg:
385-
sanitized = error_msg.replace('\n', ' ').strip()
386-
git_messages.append(f"init failed: {sanitized[:120]}")
387-
else:
388-
git_messages.append("init failed")
359+
tracker.start("git")
360+
git_messages = []
361+
git_has_error = False
362+
if is_git_repo(project_path):
363+
git_messages.append("existing repo detected")
364+
elif should_init_git:
365+
success, error_msg = init_git_repo(project_path, quiet=True)
366+
if success:
367+
git_messages.append("initialized")
389368
else:
390-
git_messages.append("git not available")
391-
try:
392-
from ..extensions import ExtensionManager
393-
bundled_path = _locate_bundled_extension("git")
394-
if bundled_path:
395-
manager = ExtensionManager(project_path)
396-
if manager.registry.is_installed("git"):
397-
git_messages.append("extension already installed")
398-
else:
399-
manager.install_from_directory(
400-
bundled_path, get_speckit_version()
401-
)
402-
git_default_notice = True
403-
git_messages.append("extension installed")
404-
else:
405-
git_has_error = True
406-
git_messages.append("bundled extension not found")
407-
except Exception as ext_err:
408369
git_has_error = True
409-
sanitized_ext = str(ext_err).replace('\n', ' ').strip()
410-
git_messages.append(
411-
f"extension install failed: {sanitized_ext[:120]}"
412-
)
413-
summary = "; ".join(git_messages)
414-
if git_has_error:
415-
tracker.error("git", summary)
416-
else:
417-
tracker.complete("git", summary)
370+
if error_msg:
371+
sanitized = error_msg.replace('\n', ' ').strip()
372+
git_messages.append(f"init failed: {sanitized[:120]}")
373+
else:
374+
git_messages.append("init failed")
418375
else:
419-
tracker.skip("git", "--no-git flag")
376+
git_messages.append("git not available")
377+
summary = "; ".join(git_messages)
378+
if git_has_error:
379+
tracker.error("git", summary)
380+
else:
381+
tracker.complete("git", summary)
420382

421383
try:
422384
bundled_wf = _locate_bundled_workflow("speckit")
@@ -596,18 +558,6 @@ def init(
596558
console.print()
597559
console.print(security_notice)
598560

599-
if git_default_notice:
600-
default_change_notice = Panel(
601-
"The git extension is currently enabled by default during [bold]specify init[/bold].\n"
602-
"Starting in [bold]v0.10.0[/bold], this will require explicit opt-in.\n"
603-
"Use [bold]specify extension add git[/bold] after init when needed.",
604-
title="[yellow]Notice: Git Default Changing[/yellow]",
605-
border_style="yellow",
606-
padding=(1, 2),
607-
)
608-
console.print()
609-
console.print(default_change_notice)
610-
611561
steps_lines = []
612562
if not here:
613563
steps_lines.append(f"1. Go to the project folder: [cyan]cd {project_name}[/cyan]")

0 commit comments

Comments
 (0)