fix(sync): address code review issues in manual conflict resolution

Fixes from code review:
- Fix duplicate check in merge logic (use else clause)
- Handle io.EOF gracefully (treat as quit)
- Add quit (q) option to abort resolution early
- Add accept-all (a) option to auto-merge remaining conflicts
- Fix skipped conflicts to keep local version (not auto-merge)
- Handle json.MarshalIndent errors properly
- Fix truncateText to use rune count for UTF-8 safety
- Update help text with new options
- Add UTF-8 and emoji test cases

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
jane
2026-01-19 11:44:09 -08:00
committed by Steve Yegge
parent 5d68e6b61a
commit 80cd1f35c0
3 changed files with 202 additions and 39 deletions

View File

@@ -45,7 +45,9 @@ The --manual flag shows a diff for each conflict and prompts you to choose:
l/local - Keep local version
r/remote - Keep remote version
m/merge - Auto-merge (LWW for scalars, union for collections)
s/skip - Skip and leave unresolved
s/skip - Skip (keep local, conflict remains for later)
a/all - Accept auto-merge for all remaining conflicts
q/quit - Quit and skip all remaining conflicts
d/diff - Show full JSON diff
The --full flag provides the legacy full sync behavior for backwards compatibility.`,
@@ -1149,22 +1151,14 @@ func resolveSyncConflictsManually(ctx context.Context, jsonlPath, beadsDir strin
var mergedIssues []*beads.Issue
for id := range allIDSet {
if conflictIDSet[id] {
// This was a conflict - use the resolved version if available
// This was a conflict
if resolved, ok := resolvedMap[id]; ok {
// User resolved this conflict - use their choice
mergedIssues = append(mergedIssues, resolved)
}
// If not in resolvedMap, it was skipped - use the automatic merge result
if _, ok := resolvedMap[id]; !ok {
// Fall back to field-level merge for skipped conflicts
local := localMap[id]
remote := remoteMap[id]
base := baseMap[id]
if local != nil && remote != nil {
mergedIssues = append(mergedIssues, mergeFieldLevel(base, local, remote))
} else if local != nil {
} else {
// Skipped - keep local version in output, conflict remains for later
if local := localMap[id]; local != nil {
mergedIssues = append(mergedIssues, local)
} else if remote != nil {
mergedIssues = append(mergedIssues, remote)
}
}
} else {