Commit Graph

3 Commits

Author SHA1 Message Date
Pascal THUET
38fd1f6cc2 Support controlled multi-install for safe AI agent integrations (#2389)
* support controlled multi-install integrations

* fix: harden multi-install integration state

* refactor: isolate integration runtime helpers

* fix: address copilot review feedback

* fix: address follow-up copilot feedback

* fix: tighten integration switch semantics

* fix: address final copilot review feedback

* fix: harden integration manifest read errors

* fix: refuse symlinked shared infra paths

* test: filter expected self-test preset warning

* test: address copilot review nits

* refactor: centralize safe shared infra writes

* fix: use no-follow writes for shared infra

* fix: keep default integration atomic on template refresh

* fix: harden shared infra error paths

* fix: preflight shared infra and future state schemas

* fix: support nested shared scripts during preflight

* test: tolerate wrapped schema error output

* fix: use safe default mode for shared text writes

* fix: use posix paths in shared skip output

* fix: share project guard for integration use

* fix: centralize spec-kit project guards

* fix: use posix project paths in cli output

* fix: harden shared manifest and upgrade refresh
2026-05-01 11:54:41 -05:00
Chengyou Liu
5edc9a5358 fix: migrate extension commands on integration switch (#2404)
* fix: migrate extension commands on integration switch

When switching integrations (e.g. kimi → opencode), extension commands
were not re-registered for the new agent, leaving the new agent without
extension support and orphaning files in the old agent's directory.

Changes:
- Add ExtensionManager.unregister_agent_artifacts() to clean up old
  agent extension files and registry entries during switch
- Add ExtensionManager.register_enabled_extensions_for_agent() to
  re-register all enabled extensions for the new agent
- Wire both into integration_switch() after uninstall/install phases
- Handle skills mode (Copilot --skills) correctly
- Add tests for kimi→opencode→claude migration, Copilot skills mode,
  and disabled extension handling

Fixes extension commands not appearing after integration switch.

* Update src/specify_cli/extensions.py

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-05-01 07:41:38 -05:00
Copilot
94ba857b78 Add specify integration subcommand for post-init integration management (#2083)
* Initial plan

* Add specify integration subcommand (list, install, uninstall, switch)

Implements the `specify integration` subcommand group for managing
integrations in existing projects after initial setup:

- `specify integration list` — shows available integrations and installed status
- `specify integration install <key>` — installs an integration into existing project
- `specify integration uninstall [key]` — hash-safe removal preserving modified files
- `specify integration switch <target>` — uninstalls current, installs target

Follows the established `specify <noun> <verb>` CLI pattern used by
extensions and presets. Shared infrastructure (scripts, templates) is
preserved during uninstall and switch operations.

Agent-Logs-Url: https://github.com/github/spec-kit/sessions/1cca6c84-3e12-465d-88b8-a646d3504f63

Co-authored-by: mnriem <15701806+mnriem@users.noreply.github.com>

* Address review feedback: extract helper, fix return type annotation

- Extract _update_init_options_for_integration() to deduplicate init-options
  update logic between install and switch commands
- Fix _parse_integration_options return type to dict[str, Any] | None

Agent-Logs-Url: https://github.com/github/spec-kit/sessions/1cca6c84-3e12-465d-88b8-a646d3504f63

Co-authored-by: mnriem <15701806+mnriem@users.noreply.github.com>

* Potential fix for pull request finding 'Unused import'

Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>

* Address review feedback: validate script type, handle --flag=value, fix metadata cleanup

- Add _normalize_script_type() to validate script type against SCRIPT_TYPE_CHOICES
- Handle --name=value syntax in _parse_integration_options()
- Clear init-options.json keys in no-manifest uninstall early-return path
- Clear stale metadata between switch teardown and install phases
- Add 5 tests covering the new edge cases

* Block --force with different integration, persist script type in init-options

- --force on install now rejects overwriting a different integration; users must
  use 'specify integration switch' instead
- _update_init_options_for_integration() now accepts and persists script_type
- Fix misleading test docstring for switch metadata test
- Add test_force_blocked_with_different_integration

* Remove --force from integration install, ensure shared infra on install/switch

- Remove --force parameter entirely from integration install; users must
  uninstall before reinstalling to prevent orphaned files
- Auto-install shared infrastructure (.specify/scripts/, .specify/templates/)
  when missing during install or switch
- Add test for shared infra creation on bare project install

* Remove redundant installed_key != key check

The == key case already returns above, so the != key guard is always true
at this point. Simplify to just 'if installed_key:'.

* Run shared infra unconditionally, defer metadata removal in switch

- Call _install_shared_infra() unconditionally on install and switch since it
  merges without overwriting existing files
- Remove premature metadata cleanup between switch phases; metadata is now
  only updated after successful Phase 2 install

* Add install rollback, graceful manifest errors, clear switch metadata

- Attempt teardown rollback on install/switch failure to avoid orphaned files
- Catch ValueError/FileNotFoundError on IntegrationManifest.load() in uninstall
  with user-friendly recovery guidance
- Clear metadata immediately after switch teardown so failed Phase 2 doesn't
  leave stale references to the removed integration

* Log rollback failures instead of silently suppressing them

* Handle corrupt manifest in switch, distinguish unknown vs missing manifest

- Wrap IntegrationManifest.load() in switch with ValueError/FileNotFoundError
  handling, matching the pattern used in uninstall
- Split else branch to report 'unknown integration' vs 'no manifest' separately

* Clean up metadata on rollback, broaden init-options match in uninstall

- Remove integration.json in install/switch rollback paths so failed installs
  don't leave stale metadata
- Match on both 'integration' and 'ai' keys when clearing init-options.json
  during uninstall to handle partially-written metadata

* Fix recovery guidance for unreadable manifests, fix type annotations

- Recovery instructions now guide users through delete manifest → uninstall →
  reinstall workflow that actually works
- Type annotations for optional CLI parameters changed from str to str | None

* Allow manifest-only uninstall for unknown/removed integrations

- Uninstall no longer requires the integration to be in the registry; falls back
  to manifest.uninstall() directly when get_integration() returns None
- Switch Phase 1 similarly uses manifest-only uninstall for unknown integrations
  instead of skipping teardown, preventing orphaned files

* Fail fast on corrupt integration.json, validate integration options

- _read_integration_json() now exits with an actionable error when
  integration.json exists but is corrupt/unreadable
- _parse_integration_options() rejects unknown options, validates flag usage,
  and requires values for non-flag options

* Validate integration.json is a dict, fail fast on missing manifest in switch

- _read_integration_json() validates parsed JSON is a dict, not a list/string
- Switch fails fast with recovery guidance when manifest is missing instead
  of silently skipping teardown and risking co-existing integration files

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: mnriem <15701806+mnriem@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
2026-04-03 17:35:04 -05:00