Skip to content

feat(init): support extension install during initialization#2180

Open
officialasishkumar wants to merge 3 commits intogithub:mainfrom
officialasishkumar:feat/init-extension-transition
Open

feat(init): support extension install during initialization#2180
officialasishkumar wants to merge 3 commits intogithub:mainfrom
officialasishkumar:feat/init-extension-transition

Conversation

@officialasishkumar
Copy link
Copy Markdown

Description

Closes #2165
Closes #2166
Closes #2167
Closes #2169

Adds the init-time extension transition path:

  • Adds repeatable specify init --extension <value> support for bundled extension IDs, local extension directories, and extension archive URLs.
  • Persists requested extensions in .specify/init-options.json and installs them before the default git-extension auto-install, so explicit --extension git does not double-install.
  • Emits visible deprecation warnings for --ai and --no-git while preserving current behavior.
  • Shows a non-blocking notice when the git extension is auto-enabled by default, pointing users to future explicit opt-in via --extension git or specify extension add git.
  • Updates README init option documentation and examples for the integration/extension path.

Testing

  • Tested locally with .venv/bin/specify --help (uv is not installed in this environment)
  • Ran existing tests with .venv/bin/python -m pytest -q
  • Tested with a sample project (covered by CLI integration tests that initialize temp projects with local extensions, repeated extensions, explicit --extension git, and default git auto-install)

Additional checks:

  • .venv/bin/ruff check src/
  • npx --yes markdownlint-cli2 README.md
  • git diff --check

AI Disclosure

  • I did not use AI assistance for this contribution
  • I did use AI assistance (describe below)

I used AI assistance for codebase analysis, implementation support, test updates, and PR drafting. I reviewed the resulting changes and validated them locally with the commands listed above.

Copilot AI review requested due to automatic review settings April 11, 2026 20:17
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds support for installing extensions during specify init, including a transition path away from legacy init flags and toward explicit extension opt-in.

Changes:

  • Introduces repeatable specify init --extension <value> for bundled IDs, local extension directories, and archive URLs, and persists selected extensions in .specify/init-options.json.
  • Ensures init-time extension installs occur before the default git-extension auto-install, and adds user-visible notices/deprecation warnings (--ai, --no-git).
  • Expands CLI integration tests and updates README init documentation/examples.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
src/specify_cli/__init__.py Adds init-time extension installation flow (name/path/URL), persistence of init options, and deprecation/notice messaging.
tests/integrations/test_cli.py Adds integration tests covering local extension install, repeatable --extension, explicit --extension git with --no-git, and new warnings/notices.
README.md Updates init option docs to include --extension and deprecations; adjusts headings and examples.
Comments suppressed due to low confidence (1)

README.md:424

  • The examples section mixes the new recommended --integration flag with several --ai examples (e.g. qodercli/windsurf/kiro-cli/etc.), even though --ai is now documented as deprecated above. To avoid encouraging deprecated usage, update these remaining examples to use --integration <key> (or explicitly label --ai examples as legacy/deprecated).
# Initialize with Cursor support
specify init my-project --integration cursor-agent

# Initialize with Qoder support
specify init my-project --ai qodercli


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/specify_cli/__init__.py
Comment thread src/specify_cli/__init__.py Outdated
Comment thread README.md Outdated
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds init-time extension installation support to specify init, including persistence of requested extensions and updated user-facing messaging around upcoming deprecations/default behavior changes.

Changes:

  • Added repeatable specify init --extension ... support (bundled IDs, local extension dirs, and archive URLs) and persisted selections in .specify/init-options.json.
  • Installed requested extensions during init ahead of default git-extension auto-install, plus added deprecation warnings for --ai and --no-git and a post-init notice about the git extension default changing in v1.0.0.
  • Updated CLI integration tests and README documentation to cover the new init/extension workflow.
Show a summary per file
File Description
src/specify_cli/__init__.py Implements init-time extension install flow, URL/archive handling, deprecation warnings, and git-default notice.
tests/integrations/test_cli.py Adds CLI/integration tests for --extension (local dirs, repeatable values, explicit git, TAR safety, bounded URL downloads) and deprecation output assertions.
README.md Documents --integration as primary, adds --extension flag docs/examples, and adjusts related init documentation.

Copilot's findings

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comments suppressed due to low confidence (1)

README.md:424

  • In the Examples section, most agent examples still use --ai ... even though the README now introduces --integration as the recommended flag and --ai as a deprecated alias. Consider switching these examples to --integration to align with the new guidance (keeping one --ai example only if you want to explicitly demonstrate the deprecated alias).
# Initialize with Cursor support
specify init my-project --integration cursor-agent

# Initialize with Qoder support
specify init my-project --ai qodercli

  • Files reviewed: 3/3 changed files
  • Comments generated: 4

Comment thread src/specify_cli/__init__.py Outdated
Comment thread src/specify_cli/__init__.py
Comment thread README.md
Comment thread README.md Outdated
Copy link
Copy Markdown
Collaborator

@mnriem mnriem left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please address Copilot feedback. If not applicable, please explain why

@mnriem mnriem self-assigned this Apr 13, 2026
@officialasishkumar officialasishkumar force-pushed the feat/init-extension-transition branch from 4ddb1cb to 9889c0b Compare April 16, 2026 04:47
@mnriem mnriem requested a review from Copilot April 16, 2026 13:10
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds init-time extension installation and messaging to support the transition away from the git extension being enabled by default, while preserving legacy init flags with deprecation warnings.

Changes:

  • Adds repeatable specify init --extension ... to install bundled extensions, local extension directories, and extension archive URLs during initialization (and persists choices to .specify/init-options.json).
  • Adds visible deprecation warnings for --ai and --no-git, plus a non-blocking notice when the git extension is auto-enabled by default.
  • Updates CLI integration tests and README documentation to reflect the init/integration/extension workflow.
Show a summary per file
File Description
src/specify_cli/__init__.py Implements init-time extension installation flow, URL download/install helpers, deprecation warnings, and git-default-change notice; redacts URLs in extension add.
tests/integrations/test_cli.py Adds coverage for init --extension (local/bundled/URL), URL redaction, TAR safety checks, and bounded-download behavior.
README.md Updates init option docs/examples to center --integration and document --extension and deprecations.

Copilot's findings

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 3/3 changed files
  • Comments generated: 2

Comment thread src/specify_cli/__init__.py Outdated
)

if not tarfile.is_tarfile(archive_path):
raise ValidationError("Extension archive must be a ZIP, .tar.gz, or .tgz file")
Copy link

Copilot AI Apr 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error message here says archives must be ZIP, .tar.gz, or .tgz, but the implementation accepts plain .tar as well (tarfile.is_tarfile + tarfile.open('r:*')). Either update the message to include .tar, or enforce the intended restriction by rejecting .tar files explicitly so users aren’t misled.

Suggested change
raise ValidationError("Extension archive must be a ZIP, .tar.gz, or .tgz file")
raise ValidationError(
"Extension archive must be a ZIP, .tar, .tar.gz, or .tgz file"
)

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated the validation message to include .tar, matching the archive formats the implementation accepts.

Comment thread src/specify_cli/__init__.py Outdated
Comment on lines 3555 to 3559
console.print("Only install extensions from sources you trust.\n")
console.print(f"Downloading from {from_url}...")
console.print(f"Downloading from {display_url}...")

# Download ZIP to temp location
download_dir = project_root / ".specify" / "extensions" / ".cache" / "downloads"
Copy link

Copilot AI Apr 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the --from URL install branch, the download implementation later uses response.read() to pull the entire archive into memory before writing it out. This can cause large memory spikes and lacks the MAX_EXTENSION_ARCHIVE_BYTES safeguard you added for init-time URL installs. Consider reusing the same bounded streaming helper (and shared URL validation) here so specify extension add --from ... also downloads in chunks with a size limit.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reworked this path to use the shared bounded streaming URL download helper instead of reading the response into memory.

@mnriem
Copy link
Copy Markdown
Collaborator

mnriem commented Apr 16, 2026

Can you please address each of the issues separately?

Add repeatable --extension support to specify init for bundled extension IDs, local extension directories, and archive URLs. Persist requested extensions in init options and install them before the default git-extension auto-install so explicit git opt-in does not double-install.

Warn when legacy --ai and --no-git flags are used, and show a visible notice when the git extension is still auto-enabled by default ahead of the v1.0.0 opt-in change.

Update README init option docs and add CLI regression coverage for init-time extension installation and deprecation messaging.

Signed-off-by: Asish Kumar <officialasishkumar@gmail.com>
Signed-off-by: Asish Kumar <officialasishkumar@gmail.com>
Signed-off-by: Asish Kumar <officialasishkumar@gmail.com>
@officialasishkumar officialasishkumar force-pushed the feat/init-extension-transition branch from 9889c0b to ab5e033 Compare April 16, 2026 14:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

3 participants