Allow file syncing during conflict resolution#595
Conversation
Enables non-conflicted files to sync bidirectionally while the conflict prompt is open, and updates conflict data with latest content as changes arrive. When both sides converge to matching content, that conflict auto-resolves without user intervention. Plugin reducer guards prevent mode dismissal during active conflicts, and non-conflicted deletes/renames are deferred to avoid prompt collisions.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
Autofix Details
Bugbot Autofix prepared fixes for both issues found in the latest run.
- ✅ Fixed: Plugin UI stuck after all conflicts auto-resolve
- The plugin now clears conflicts on
sync-complete, so auto-resolve flows can always exit conflict resolution and transition to idle.
- The plugin now clears conflicts on
- ✅ Fixed: Guard blocks plugin close on tab replacement
- The reducer guard now permits
set-mode: replacedduring conflict resolution, allowing the replacement close path to proceed correctly.
- The reducer guard now permits
Or push these changes by commenting:
@cursor push 0a9cfca857
Preview (0a9cfca857)
diff --git a/plugins/code-link/src/App.tsx b/plugins/code-link/src/App.tsx
--- a/plugins/code-link/src/App.tsx
+++ b/plugins/code-link/src/App.tsx
@@ -58,7 +58,7 @@
}
case "set-mode":
// Don't dismiss conflict resolution while conflicts are pending
- if (state.mode === "conflict_resolution" && state.conflicts.length > 0) {
+ if (state.mode === "conflict_resolution" && state.conflicts.length > 0 && action.mode !== "replaced") {
return state
}
return {
diff --git a/plugins/code-link/src/messages.ts b/plugins/code-link/src/messages.ts
--- a/plugins/code-link/src/messages.ts
+++ b/plugins/code-link/src/messages.ts
@@ -13,6 +13,7 @@
| { type: "set-mode"; mode: Mode }
| { type: "pending-deletes"; files: PendingDelete[] }
| { type: "conflicts"; conflicts: ConflictSummary[] }
+ | { type: "clear-conflicts" }
export function createMessageHandler({
dispatch,
@@ -95,7 +96,7 @@
}
case "sync-complete":
log.debug("Sync complete, transitioning to idle")
- dispatch({ type: "set-mode", mode: "idle" })
+ dispatch({ type: "clear-conflicts" })
break
default:
log.warn("Unknown message type:", (message as unknown as { type: string }).type)This Bugbot Autofix run was free. To enable autofix for future PRs, go to the Cursor dashboard.
The guard unconditionally blocked all mode transitions during conflict_resolution, including "replaced" (tab takeover) and "idle" (conflicts resolved). This left the plugin stuck with no active socket. Allow these transitions through and clear stale conflicts on idle.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Comment @cursor review or bugbot run to trigger another review on this PR
…n sync-complete The idle exemption allowed file-change/file-rename to prematurely exit conflict resolution. Instead, the sync-complete message now dispatches clear-conflicts (reusing the existing action) to properly exit conflict resolution when all conflicts auto-resolve.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: fc4fc04976
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Remove the deferral workaround for non-conflicted deletes and renames during conflict resolution. The pending-deletes guard already queues them correctly; the clear-conflicts reducer now surfaces queued deletes by transitioning to delete_confirmation instead of idle.
There was a problem hiding this comment.
Pull request overview
This PR updates Code Link’s plugin + CLI controller state machines to keep syncing non-conflicted files while the conflict resolution UI is open, and to stream live conflict-content updates (including auto-resolving conflicts when contents converge).
Changes:
- Add a
clear-conflictsaction in the plugin and use it onsync-completeto centralize post-sync/conflict cleanup. - Introduce a CLI effect
UPDATE_CONFLICT_DATAand logic to live-updatependingConflictsduring conflict resolution (with auto-resolution on convergence). - Expand controller unit tests to cover syncing behaviors during conflict resolution and auto-resolution.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| plugins/code-link/src/messages.ts | Routes sync-complete to a new clear-conflicts action rather than forcing set-mode: idle. |
| plugins/code-link/src/App.tsx | Adds reducer guards to prevent conflict UI dismissal while conflicts exist; queues pending deletes during conflicts; adds clear-conflicts reducer path. |
| packages/code-link-cli/src/controller.ts | Adds UPDATE_CONFLICT_DATA effect + applyConflictUpdate helper; updates watcher/remote handlers to apply non-conflicted sync while updating conflicts live; adds auto-resolution. |
| packages/code-link-cli/src/controller.test.ts | Adds test coverage for sync paths during conflict resolution and auto-resolution scenarios. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Update debug message to reflect that sync-complete transitions out of conflict mode, which may lead to delete_confirmation when pendingDeletes is queued, not just idle state.
When a file is deleted locally during conflict resolution, convert it to a ConflictSummary and merge it into the existing conflict panel instead of queuing it as a separate DeletePanel afterwards. Reuses pendingDeletes as the marker for delete-originated conflicts, sending delete-confirmed or delete-cancelled before conflicts-resolved to cleanly resolve the CLI's awaiting promises.
Reformat reduce call to match biome's formatting expectations by consolidating the function arguments onto a single line.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Prevents sync-complete from clearing the plugin UI while delete confirmations are still in-flight during conflict resolution.


Description
Enables non-conflicted files to sync bidirectionally while the conflict resolution prompt is open, and updates conflict data with latest content as changes arrive. When both sides converge to matching content, that conflict auto-resolves without user intervention. Plugin reducer guards prevent mode dismissal during active conflicts, and non-conflicted deletes/renames are deferred to avoid prompt collisions.
Changes
set-modeandpending-deletesno longer leaveconflict_resolutionwhile conflicts remainUPDATE_CONFLICT_DATAsends updated conflicts to plugin directly (avoids orphaned promises)add/changesyncs; conflicted files update inpendingConflicts;delete/renamedeferred during conflict resolutionpendingConflictslocalContent === remoteContentafter a live update, that conflict is removed and prompt skips if none remainTesting