Skip to content

Commit 216146a

Browse files
authored
feat(init)!: make git extension opt-in and remove --no-git flag (#2168)
- Remove --no-git flag from specify init (was deprecated, now gone) - Remove auto-installation of the git extension during specify init - Remove git_default_notice deprecation panel - Update tracker step label: 'Install git extension' → 'Initialize git repository' - Git repository initialization still runs when git is available - Update docs: remove --no-git from core.md, upgrade.md, local-development.md - Add CHANGELOG entry for v0.10.0 breaking changes - Update tests: replace TestGitExtensionAutoInstall with TestGitExtensionOptIn
1 parent 8096ed9 commit 216146a

6 files changed

Lines changed: 63 additions & 238 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@
22

33
<!-- insert new changelog below this comment -->
44

5+
## [0.10.0] - TBD
6+
7+
### Breaking Changes
8+
9+
- **Git extension is now opt-in** — `specify init` no longer auto-installs the git extension. Use `specify extension add git` to install it, or pass `--extension git` during init.
10+
- **`--no-git` flag removed** — previously deprecated, this flag has been removed. Git repository initialization now always runs when git is available; the extension itself is no longer auto-enabled.
11+
512
## [0.9.4] - 2026-06-04
613

714
### Changed

‎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 is not installed or the directory is already a git repository |
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 as of v0.10.0. 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: 2 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -259,69 +259,19 @@ rm speckit.old-command-name.md
259259

260260
### Scenario 4: "I'm working on a project without Git"
261261

262-
If you initialized your project with `--no-git`, you can still upgrade:
262+
If your project directory does not use Git, Spec Kit will automatically detect this and skip repository initialization. You can still upgrade normally:
263263

264264
```bash
265265
# Manually back up files you customized
266266
cp .specify/memory/constitution.md /tmp/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
272272
mv /tmp/constitution-backup.md .specify/memory/constitution.md
273273
```
274274

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
291-
```
292-
293-
**During upgrade:**
294-
295-
```bash
296-
specify init --here --force --integration copilot --no-git
297-
```
298-
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:
312-
313-
```bash
314-
# Bash/Zsh
315-
export SPECIFY_FEATURE="001-my-feature"
316-
317-
# PowerShell
318-
$env:SPECIFY_FEATURE = "001-my-feature"
319-
```
320-
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-
325275
---
326276

327277
## Troubleshooting

‎src/specify_cli/commands/init.py‎

Lines changed: 25 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,6 @@ def init(
101101
ai_commands_dir: str = typer.Option(None, "--ai-commands-dir", help="Directory for agent command files (required with --ai generic, e.g. .myagent/commands/)"),
102102
script_type: str = typer.Option(None, "--script", help="Script type to use: sh or ps"),
103103
ignore_agent_tools: bool = typer.Option(False, "--ignore-agent-tools", help="Skip checks for coding agent tools like Claude Code"),
104-
no_git: bool = typer.Option(False, "--no-git", help="Skip git repository initialization"),
105104
here: bool = typer.Option(False, "--here", help="Initialize project in the current directory instead of creating a new one"),
106105
force: bool = typer.Option(False, "--force", help="Force merge/overwrite when using --here (skip confirmation)"),
107106
skip_tls: bool = typer.Option(False, "--skip-tls", help="Deprecated (no-op). Previously: skip SSL/TLS verification.", hidden=True),
@@ -127,13 +126,12 @@ def init(
127126
in non-interactive sessions
128127
3. Install bundled Spec Kit templates, scripts, workflow, and shared
129128
project infrastructure
130-
4. Initialize a fresh git repository (if not --no-git and no existing repo)
129+
4. Initialize a fresh git repository (if git is available and no existing repo)
131130
5. Set up coding agent integration commands and optional presets
132131
133132
Examples:
134133
specify init my-project
135134
specify init my-project --integration claude
136-
specify init my-project --integration copilot --no-git
137135
specify init --ignore-agent-tools my-project
138136
specify init . --integration claude # Initialize in current directory
139137
specify init . # Initialize in current directory (interactive integration selection)
@@ -223,13 +221,6 @@ def init(
223221
'use [bold]--integration generic --integration-options="--commands-dir <dir>"[/bold] instead.[/dim]'
224222
)
225223

226-
if no_git:
227-
console.print(
228-
"[yellow]⚠️ --no-git is deprecated and will be removed in v0.10.0.[/yellow]\n"
229-
"[yellow]The git extension will no longer be enabled by default "
230-
"— use the [bold]specify extension[/bold] commands to install or enable the git extension if needed.[/yellow]"
231-
)
232-
233224
if project_name == ".":
234225
here = True
235226
project_name = None
@@ -340,11 +331,9 @@ def init(
340331

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

343-
should_init_git = False
344-
if not no_git:
345-
should_init_git = check_tool("git")
346-
if not should_init_git:
347-
console.print("[yellow]Git not found - will skip repository initialization[/yellow]")
334+
should_init_git = check_tool("git")
335+
if not should_init_git:
336+
console.print("[yellow]Git not found - will skip repository initialization[/yellow]")
348337

349338
if not ignore_agent_tools:
350339
agent_config = AGENT_CONFIG.get(selected_ai)
@@ -395,15 +384,13 @@ def init(
395384
for key, label in [
396385
("chmod", "Ensure scripts executable"),
397386
("constitution", "Constitution setup"),
398-
("git", "Install git extension"),
387+
("git", "Initialize git repository"),
399388
("workflow", "Install bundled workflow"),
400389
("agent-context", "Install agent-context extension"),
401390
("final", "Finalize"),
402391
]:
403392
tracker.add(key, label)
404393

405-
git_default_notice = False
406-
407394
with Live(tracker.render(), console=console, refresh_per_second=8, transient=True) as live:
408395
tracker.attach_refresh(lambda: live.update(tracker.render()))
409396
try:
@@ -460,54 +447,29 @@ def init(
460447

461448
ensure_constitution_from_template(project_path, tracker=tracker)
462449

463-
if not no_git:
464-
tracker.start("git")
465-
git_messages = []
466-
git_has_error = False
467-
if is_git_repo(project_path):
468-
git_messages.append("existing repo detected")
469-
elif should_init_git:
470-
success, error_msg = init_git_repo(project_path, quiet=True)
471-
if success:
472-
git_messages.append("initialized")
473-
else:
474-
git_has_error = True
475-
if error_msg:
476-
sanitized = error_msg.replace('\n', ' ').strip()
477-
git_messages.append(f"init failed: {sanitized[:120]}")
478-
else:
479-
git_messages.append("init failed")
450+
tracker.start("git")
451+
git_messages = []
452+
git_has_error = False
453+
if is_git_repo(project_path):
454+
git_messages.append("existing repo detected")
455+
elif should_init_git:
456+
success, error_msg = init_git_repo(project_path, quiet=True)
457+
if success:
458+
git_messages.append("initialized")
480459
else:
481-
git_messages.append("git not available")
482-
try:
483-
from ..extensions import ExtensionManager
484-
bundled_path = _locate_bundled_extension("git")
485-
if bundled_path:
486-
manager = ExtensionManager(project_path)
487-
if manager.registry.is_installed("git"):
488-
git_messages.append("extension already installed")
489-
else:
490-
manager.install_from_directory(
491-
bundled_path, get_speckit_version()
492-
)
493-
git_default_notice = True
494-
git_messages.append("extension installed")
495-
else:
496-
git_has_error = True
497-
git_messages.append("bundled extension not found")
498-
except Exception as ext_err:
499460
git_has_error = True
500-
sanitized_ext = str(ext_err).replace('\n', ' ').strip()
501-
git_messages.append(
502-
f"extension install failed: {sanitized_ext[:120]}"
503-
)
504-
summary = "; ".join(git_messages)
505-
if git_has_error:
506-
tracker.error("git", summary)
507-
else:
508-
tracker.complete("git", summary)
461+
if error_msg:
462+
sanitized = error_msg.replace('\n', ' ').strip()
463+
git_messages.append(f"init failed: {sanitized[:120]}")
464+
else:
465+
git_messages.append("init failed")
466+
else:
467+
git_messages.append("git not available")
468+
summary = "; ".join(git_messages)
469+
if git_has_error:
470+
tracker.error("git", summary)
509471
else:
510-
tracker.skip("git", "--no-git flag")
472+
tracker.complete("git", summary)
511473

512474
try:
513475
bundled_wf = _locate_bundled_workflow("speckit")
@@ -697,18 +659,6 @@ def init(
697659
console.print()
698660
console.print(deprecation_notice)
699661

700-
if git_default_notice:
701-
default_change_notice = Panel(
702-
"The git extension is currently enabled by default during [bold]specify init[/bold].\n"
703-
"Starting in [bold]v0.10.0[/bold], this will require explicit opt-in.\n"
704-
"Use [bold]specify extension add git[/bold] after init when needed.",
705-
title="[yellow]Notice: Git Default Changing[/yellow]",
706-
border_style="yellow",
707-
padding=(1, 2),
708-
)
709-
console.print()
710-
console.print(default_change_notice)
711-
712662
steps_lines = []
713663
if not here:
714664
steps_lines.append(f"1. Go to the project folder: [cyan]cd {project_name}[/cyan]")

0 commit comments

Comments
 (0)