Skip to content

Fix duplicate command registration crash on .vswift load/reload - #7

Merged
CommunityPoke merged 1 commit into
mainfrom
devin/1789815371-cmd-registry-fix
Sep 19, 2026
Merged

CommunityPoke merged 1 commit into
mainfrom
devin/1789815371-cmd-registry-fix

Conversation

@CommunityPoke

Copy link
Copy Markdown
Contributor

Summary

Fixes Failed to load .vswift bundle: Error: command 'swiftCopilot.generate' already exists.

Root causes:

  1. SwiftScriptHost.start() pushed command Disposables into this.disposables, but if registerCommand threw partway (duplicate id), the host was never added to hosts, so dispose() never ran → leaked registrations made every subsequent loadAll() fail permanently.
  2. The same .vswift discovered via both the workspace glob and vswift-installed/ spawned two hosts registering identical command ids.

New CommandRegistry (commands.ts): tracks registrations by command id and owning script (name@binaryPath). registerFor() disposes the previous registration for an id before registering (idempotent reload), unregisterOwner() cleans a script's whole command set on stop/remove/deactivate. SwiftScriptHost.start() rolls back partial registrations on failure; loadAll dedupes bundles by name@version; registry is disposed on deactivate.

test/commands.test.mjs (12 checks, runs against compiled out/): idempotent re-register, duplicate-source takeover, owner cleanup, dispose — with a fake vscode.commands that throws on live duplicates like the real API. Added to CI extension job.

Root cause: command Disposables were tracked per-host but a host whose
start() threw partway through registration leaked its already-registered
commands and was never added to , so dispose() never ran — every
subsequent loadAll() failed permanently with 'command already exists'.
The same bundle discovered via workspace glob AND vswift-installed also
double-registered identical command ids.

- New CommandRegistry (commands.ts): tracks registrations by command id
  and by owning script; registerFor() disposes a previous registration
  for the same id before registering (idempotent), unregisterOwner()
  cleans up all of a script's commands on stop/reload/deactivate.
- SwiftScriptHost uses the shared registry via owner key
  name@binaryPath; failed start() rolls back partial registrations.
- loadAll dedupes bundles by manifest name@version across sources.
- Registry disposed on deactivate; added to context.subscriptions.
- test/commands.test.mjs: 12 checks (idempotent re-register, duplicate
  source takeover, owner cleanup, dispose) with a fake vscode that
  throws on live duplicates like the real API.

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@CommunityPoke
CommunityPoke merged commit 989f575 into main Sep 19, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant