@sudocode-ai/local-server 0.1.9 → 0.1.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (146) hide show
  1. package/dist/execution/executors/agent-executor-wrapper.d.ts.map +1 -1
  2. package/dist/execution/executors/agent-executor-wrapper.js +57 -2
  3. package/dist/execution/executors/agent-executor-wrapper.js.map +1 -1
  4. package/dist/execution/process/builders/claude.d.ts.map +1 -1
  5. package/dist/execution/process/builders/claude.js +32 -1
  6. package/dist/execution/process/builders/claude.js.map +1 -1
  7. package/dist/execution/worktree/config.js +1 -1
  8. package/dist/execution/worktree/config.js.map +1 -1
  9. package/dist/execution/worktree/git-cli.d.ts +48 -0
  10. package/dist/execution/worktree/git-cli.d.ts.map +1 -1
  11. package/dist/execution/worktree/git-cli.js +81 -0
  12. package/dist/execution/worktree/git-cli.js.map +1 -1
  13. package/dist/execution/worktree/types.d.ts.map +1 -1
  14. package/dist/execution/worktree/types.js.map +1 -1
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +17 -4
  17. package/dist/index.js.map +1 -1
  18. package/dist/public/assets/index-Nz4IjDwB.css +1 -0
  19. package/dist/public/assets/index-Z8yftXvD.js +824 -0
  20. package/dist/public/assets/index-Z8yftXvD.js.map +1 -0
  21. package/dist/public/assets/{react-vendor-DiL5hC7l.js → react-vendor-5f1Wq1qs.js} +5 -5
  22. package/dist/public/assets/{react-vendor-DiL5hC7l.js.map → react-vendor-5f1Wq1qs.js.map} +1 -1
  23. package/dist/public/assets/{ui-vendor-B4WMPEfa.js → ui-vendor-BDDPoYki.js} +2 -2
  24. package/dist/public/assets/{ui-vendor-B4WMPEfa.js.map → ui-vendor-BDDPoYki.js.map} +1 -1
  25. package/dist/public/index.html +4 -4
  26. package/dist/routes/executions.d.ts.map +1 -1
  27. package/dist/routes/executions.js +3 -1
  28. package/dist/routes/executions.js.map +1 -1
  29. package/dist/routes/issues.d.ts.map +1 -1
  30. package/dist/routes/issues.js +13 -0
  31. package/dist/routes/issues.js.map +1 -1
  32. package/dist/routes/specs.d.ts.map +1 -1
  33. package/dist/routes/specs.js +14 -0
  34. package/dist/routes/specs.js.map +1 -1
  35. package/dist/routes/workflows.d.ts +8 -0
  36. package/dist/routes/workflows.d.ts.map +1 -0
  37. package/dist/routes/workflows.js +1729 -0
  38. package/dist/routes/workflows.js.map +1 -0
  39. package/dist/services/execution-event-callbacks.d.ts +73 -0
  40. package/dist/services/execution-event-callbacks.d.ts.map +1 -0
  41. package/dist/services/execution-event-callbacks.js +82 -0
  42. package/dist/services/execution-event-callbacks.js.map +1 -0
  43. package/dist/services/execution-lifecycle.d.ts +38 -2
  44. package/dist/services/execution-lifecycle.d.ts.map +1 -1
  45. package/dist/services/execution-lifecycle.js +94 -23
  46. package/dist/services/execution-lifecycle.js.map +1 -1
  47. package/dist/services/execution-service.d.ts +31 -3
  48. package/dist/services/execution-service.d.ts.map +1 -1
  49. package/dist/services/execution-service.js +161 -34
  50. package/dist/services/execution-service.js.map +1 -1
  51. package/dist/services/executions.d.ts +1 -0
  52. package/dist/services/executions.d.ts.map +1 -1
  53. package/dist/services/executions.js +4 -0
  54. package/dist/services/executions.js.map +1 -1
  55. package/dist/services/project-context.d.ts +25 -0
  56. package/dist/services/project-context.d.ts.map +1 -1
  57. package/dist/services/project-context.js +53 -3
  58. package/dist/services/project-context.js.map +1 -1
  59. package/dist/services/project-manager.d.ts +7 -0
  60. package/dist/services/project-manager.d.ts.map +1 -1
  61. package/dist/services/project-manager.js +108 -13
  62. package/dist/services/project-manager.js.map +1 -1
  63. package/dist/services/websocket.d.ts +10 -2
  64. package/dist/services/websocket.d.ts.map +1 -1
  65. package/dist/services/websocket.js +18 -0
  66. package/dist/services/websocket.js.map +1 -1
  67. package/dist/services/workflow-broadcast-service.d.ts +43 -0
  68. package/dist/services/workflow-broadcast-service.d.ts.map +1 -0
  69. package/dist/services/workflow-broadcast-service.js +145 -0
  70. package/dist/services/workflow-broadcast-service.js.map +1 -0
  71. package/dist/services/worktree-sync-service.d.ts +76 -4
  72. package/dist/services/worktree-sync-service.d.ts.map +1 -1
  73. package/dist/services/worktree-sync-service.js +264 -23
  74. package/dist/services/worktree-sync-service.js.map +1 -1
  75. package/dist/workflow/base-workflow-engine.d.ts +186 -0
  76. package/dist/workflow/base-workflow-engine.d.ts.map +1 -0
  77. package/dist/workflow/base-workflow-engine.js +549 -0
  78. package/dist/workflow/base-workflow-engine.js.map +1 -0
  79. package/dist/workflow/dependency-analyzer.d.ts +78 -0
  80. package/dist/workflow/dependency-analyzer.d.ts.map +1 -0
  81. package/dist/workflow/dependency-analyzer.js +264 -0
  82. package/dist/workflow/dependency-analyzer.js.map +1 -0
  83. package/dist/workflow/engines/orchestrator-engine.d.ts +237 -0
  84. package/dist/workflow/engines/orchestrator-engine.d.ts.map +1 -0
  85. package/dist/workflow/engines/orchestrator-engine.js +749 -0
  86. package/dist/workflow/engines/orchestrator-engine.js.map +1 -0
  87. package/dist/workflow/engines/sequential-engine.d.ts +276 -0
  88. package/dist/workflow/engines/sequential-engine.d.ts.map +1 -0
  89. package/dist/workflow/engines/sequential-engine.js +1110 -0
  90. package/dist/workflow/engines/sequential-engine.js.map +1 -0
  91. package/dist/workflow/index.d.ts +15 -0
  92. package/dist/workflow/index.d.ts.map +1 -0
  93. package/dist/workflow/index.js +22 -0
  94. package/dist/workflow/index.js.map +1 -0
  95. package/dist/workflow/mcp/api-client.d.ts +103 -0
  96. package/dist/workflow/mcp/api-client.d.ts.map +1 -0
  97. package/dist/workflow/mcp/api-client.js +193 -0
  98. package/dist/workflow/mcp/api-client.js.map +1 -0
  99. package/dist/workflow/mcp/index.d.ts +16 -0
  100. package/dist/workflow/mcp/index.d.ts.map +1 -0
  101. package/dist/workflow/mcp/index.js +114 -0
  102. package/dist/workflow/mcp/index.js.map +1 -0
  103. package/dist/workflow/mcp/server.d.ts +85 -0
  104. package/dist/workflow/mcp/server.d.ts.map +1 -0
  105. package/dist/workflow/mcp/server.js +520 -0
  106. package/dist/workflow/mcp/server.js.map +1 -0
  107. package/dist/workflow/mcp/tools/escalation.d.ts +36 -0
  108. package/dist/workflow/mcp/tools/escalation.d.ts.map +1 -0
  109. package/dist/workflow/mcp/tools/escalation.js +47 -0
  110. package/dist/workflow/mcp/tools/escalation.js.map +1 -0
  111. package/dist/workflow/mcp/tools/execution.d.ts +59 -0
  112. package/dist/workflow/mcp/tools/execution.d.ts.map +1 -0
  113. package/dist/workflow/mcp/tools/execution.js +67 -0
  114. package/dist/workflow/mcp/tools/execution.js.map +1 -0
  115. package/dist/workflow/mcp/tools/inspection.d.ts +82 -0
  116. package/dist/workflow/mcp/tools/inspection.d.ts.map +1 -0
  117. package/dist/workflow/mcp/tools/inspection.js +57 -0
  118. package/dist/workflow/mcp/tools/inspection.js.map +1 -0
  119. package/dist/workflow/mcp/tools/workflow.d.ts +59 -0
  120. package/dist/workflow/mcp/tools/workflow.d.ts.map +1 -0
  121. package/dist/workflow/mcp/tools/workflow.js +40 -0
  122. package/dist/workflow/mcp/tools/workflow.js.map +1 -0
  123. package/dist/workflow/mcp/types.d.ts +345 -0
  124. package/dist/workflow/mcp/types.d.ts.map +1 -0
  125. package/dist/workflow/mcp/types.js +7 -0
  126. package/dist/workflow/mcp/types.js.map +1 -0
  127. package/dist/workflow/services/prompt-builder.d.ts +36 -0
  128. package/dist/workflow/services/prompt-builder.d.ts.map +1 -0
  129. package/dist/workflow/services/prompt-builder.js +329 -0
  130. package/dist/workflow/services/prompt-builder.js.map +1 -0
  131. package/dist/workflow/services/wakeup-service.d.ts +262 -0
  132. package/dist/workflow/services/wakeup-service.d.ts.map +1 -0
  133. package/dist/workflow/services/wakeup-service.js +809 -0
  134. package/dist/workflow/services/wakeup-service.js.map +1 -0
  135. package/dist/workflow/workflow-engine.d.ts +221 -0
  136. package/dist/workflow/workflow-engine.d.ts.map +1 -0
  137. package/dist/workflow/workflow-engine.js +94 -0
  138. package/dist/workflow/workflow-engine.js.map +1 -0
  139. package/dist/workflow/workflow-event-emitter.d.ts +278 -0
  140. package/dist/workflow/workflow-event-emitter.d.ts.map +1 -0
  141. package/dist/workflow/workflow-event-emitter.js +259 -0
  142. package/dist/workflow/workflow-event-emitter.js.map +1 -0
  143. package/package.json +8 -6
  144. package/dist/public/assets/index-DV9Tbujb.css +0 -1
  145. package/dist/public/assets/index-DcDX9-Ad.js +0 -740
  146. package/dist/public/assets/index-DcDX9-Ad.js.map +0 -1
@@ -42,6 +42,15 @@ export interface UncommittedFileStats {
42
42
  additions: number;
43
43
  deletions: number;
44
44
  }
45
+ /**
46
+ * Info about potential local conflicts when including uncommitted files
47
+ */
48
+ export interface PotentialLocalConflicts {
49
+ /** Number of files that may have merge conflicts */
50
+ count: number;
51
+ /** List of files that may have merge conflicts */
52
+ files: string[];
53
+ }
45
54
  /**
46
55
  * Sync preview result
47
56
  */
@@ -55,6 +64,8 @@ export interface SyncPreviewResult {
55
64
  uncommittedJSONLChanges: string[];
56
65
  /** Stats about uncommitted changes in worktree (not included in sync by default) */
57
66
  uncommittedChanges?: UncommittedFileStats;
67
+ /** Files that may have merge conflicts if "include uncommitted" is selected */
68
+ potentialLocalConflicts?: PotentialLocalConflicts;
58
69
  executionStatus: ExecutionStatus;
59
70
  warnings: string[];
60
71
  }
@@ -67,6 +78,8 @@ export interface SyncResult {
67
78
  filesChanged: number;
68
79
  /** Whether there are unresolved merge conflicts (user must resolve manually) */
69
80
  hasConflicts?: boolean;
81
+ /** List of files that have merge conflicts requiring manual resolution */
82
+ filesWithConflicts?: string[];
70
83
  /** Number of uncommitted files copied from worktree (stage sync only) */
71
84
  uncommittedFilesIncluded?: number;
72
85
  error?: string;
@@ -221,13 +234,70 @@ export declare class WorktreeSyncService {
221
234
  */
222
235
  private _performSquashMergeAllowConflicts;
223
236
  /**
224
- * Copy uncommitted files from worktree to local repo
237
+ * Check if a file has local uncommitted changes compared to HEAD
238
+ *
239
+ * @param filePath - Relative path to the file
240
+ * @returns true if file has uncommitted changes, false otherwise
241
+ */
242
+ private _hasLocalUncommittedChanges;
243
+ /**
244
+ * Detect potential local conflicts for uncommitted worktree files
225
245
  *
226
- * Copies files that are modified or untracked in the worktree
227
- * to the local repository working directory.
246
+ * Checks which uncommitted worktree files also exist locally with changes
247
+ * or are untracked locally. These files may have merge conflicts when synced.
248
+ *
249
+ * @param worktreeFiles - List of uncommitted files in worktree
250
+ * @returns Info about files that may have conflicts
251
+ */
252
+ private _detectPotentialLocalConflicts;
253
+ /**
254
+ * Check if a file is untracked by git (not in the index)
255
+ *
256
+ * @param filePath - Relative path to the file
257
+ * @returns true if file is untracked, false if tracked
258
+ */
259
+ private _isFileUntracked;
260
+ /**
261
+ * Check if a file is a JSONL file in the .sudocode directory
262
+ *
263
+ * @param filePath - Relative path to the file
264
+ * @returns true if file is a .sudocode JSONL file
265
+ */
266
+ private _isJSONLFile;
267
+ /**
268
+ * Perform three-way merge on a file using git merge-file
269
+ *
270
+ * Uses HEAD as base, local working copy as "ours", and worktree version as "theirs".
271
+ * Modifies the local file in place, inserting conflict markers if needed.
272
+ *
273
+ * @param filePath - Relative path to the file in local repo
274
+ * @param worktreeFilePath - Absolute path to the file in worktree
275
+ * @returns true if there are conflicts, false if merge was clean
276
+ */
277
+ private _threeWayMergeFile;
278
+ /**
279
+ * Merge two JSONL files using UUID-based resolution
280
+ *
281
+ * Reads both local and worktree versions, merges entities by UUID,
282
+ * and writes the result back to the local file.
283
+ *
284
+ * @param localFilePath - Absolute path to local JSONL file
285
+ * @param worktreeFilePath - Absolute path to worktree JSONL file
286
+ */
287
+ private _mergeJSONLFiles;
288
+ /**
289
+ * Copy uncommitted files from worktree to local repo with safe merging
290
+ *
291
+ * For files with local uncommitted changes:
292
+ * - JSONL files: Uses UUID-based merge resolution
293
+ * - Other files: Uses git merge-file for three-way merge with conflict markers
294
+ *
295
+ * Files without local changes are copied directly.
228
296
  *
229
297
  * @param worktreePath - Path to the worktree
230
- * @returns Number of files copied
298
+ * @param options - Optional settings
299
+ * @param options.overrideLocalChanges - If true, skip merge and overwrite local changes
300
+ * @returns Object with filesCopied count and list of files with conflicts
231
301
  */
232
302
  private _copyUncommittedFiles;
233
303
  /**
@@ -293,11 +363,13 @@ export declare class WorktreeSyncService {
293
363
  * @param executionId - Execution ID to sync
294
364
  * @param options - Optional settings
295
365
  * @param options.includeUncommitted - If true, also copy uncommitted files from worktree
366
+ * @param options.overrideLocalChanges - If true, overwrite local changes instead of merging
296
367
  * @returns Sync result with details
297
368
  * @throws WorktreeSyncError if sync fails
298
369
  */
299
370
  stageSync(executionId: string, options?: {
300
371
  includeUncommitted?: boolean;
372
+ overrideLocalChanges?: boolean;
301
373
  }): Promise<SyncResult>;
302
374
  /**
303
375
  * Check if worktree branch is an ancestor of target branch
@@ -1 +1 @@
1
- {"version":3,"file":"worktree-sync-service.d.ts","sourceRoot":"","sources":["../../src/services/worktree-sync-service.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"worktree-sync-service.d.ts","sourceRoot":"","sources":["../../src/services/worktree-sync-service.ts"],"names":[],"mappings":"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"}
@@ -12,7 +12,8 @@ import { execSync } from "child_process";
12
12
  import { GitSyncCli, } from "../execution/worktree/git-sync-cli.js";
13
13
  import { ConflictDetector, } from "../execution/worktree/conflict-detector.js";
14
14
  import { mergeThreeWay, hasGitConflictMarkers, parseMergeConflictFile, resolveEntities, } from "@sudocode-ai/cli/dist/merge-resolver.js";
15
- import { writeJSONL } from "@sudocode-ai/cli/dist/jsonl.js";
15
+ import { writeJSONL, readJSONLSync, } from "@sudocode-ai/cli/dist/jsonl.js";
16
+ import * as os from "os";
16
17
  /**
17
18
  * Worktree sync error codes
18
19
  */
@@ -125,6 +126,9 @@ export class WorktreeSyncService {
125
126
  // `${uncommittedChanges.files.length} uncommitted file(s) in worktree will NOT be included (only committed changes are synced).`
126
127
  // );
127
128
  // }
129
+ // 9. Detect potential local conflicts for uncommitted files
130
+ // These are files in worktree that also have local changes or are untracked locally
131
+ const potentialLocalConflicts = this._detectPotentialLocalConflicts(uncommittedChanges?.files || []);
128
132
  return {
129
133
  canSync,
130
134
  conflicts,
@@ -133,6 +137,7 @@ export class WorktreeSyncService {
133
137
  mergeBase,
134
138
  uncommittedJSONLChanges: uncommittedJSONL,
135
139
  uncommittedChanges,
140
+ potentialLocalConflicts,
136
141
  executionStatus: execution.status,
137
142
  warnings,
138
143
  };
@@ -550,15 +555,175 @@ export class WorktreeSyncService {
550
555
  return { filesChanged, hasConflicts };
551
556
  }
552
557
  /**
553
- * Copy uncommitted files from worktree to local repo
558
+ * Check if a file has local uncommitted changes compared to HEAD
554
559
  *
555
- * Copies files that are modified or untracked in the worktree
556
- * to the local repository working directory.
560
+ * @param filePath - Relative path to the file
561
+ * @returns true if file has uncommitted changes, false otherwise
562
+ */
563
+ _hasLocalUncommittedChanges(filePath) {
564
+ try {
565
+ // git diff --quiet exits with 1 if there are changes, 0 if clean
566
+ execSync(`git diff --quiet HEAD -- ${this._escapeShellArg(filePath)}`, {
567
+ cwd: this.repoPath,
568
+ stdio: "pipe",
569
+ });
570
+ return false; // Exit 0 = no changes
571
+ }
572
+ catch {
573
+ return true; // Exit 1 = has changes
574
+ }
575
+ }
576
+ /**
577
+ * Detect potential local conflicts for uncommitted worktree files
578
+ *
579
+ * Checks which uncommitted worktree files also exist locally with changes
580
+ * or are untracked locally. These files may have merge conflicts when synced.
581
+ *
582
+ * @param worktreeFiles - List of uncommitted files in worktree
583
+ * @returns Info about files that may have conflicts
584
+ */
585
+ _detectPotentialLocalConflicts(worktreeFiles) {
586
+ const conflictFiles = [];
587
+ for (const filePath of worktreeFiles) {
588
+ const localPath = path.join(this.repoPath, filePath);
589
+ const localFileExists = fs.existsSync(localPath);
590
+ if (localFileExists) {
591
+ // Check if local file has uncommitted changes vs HEAD or is untracked
592
+ const hasChangesVsHead = this._hasLocalUncommittedChanges(filePath);
593
+ const isUntracked = this._isFileUntracked(filePath);
594
+ if (hasChangesVsHead || isUntracked) {
595
+ conflictFiles.push(filePath);
596
+ }
597
+ }
598
+ }
599
+ return {
600
+ count: conflictFiles.length,
601
+ files: conflictFiles,
602
+ };
603
+ }
604
+ /**
605
+ * Check if a file is untracked by git (not in the index)
606
+ *
607
+ * @param filePath - Relative path to the file
608
+ * @returns true if file is untracked, false if tracked
609
+ */
610
+ _isFileUntracked(filePath) {
611
+ try {
612
+ // git ls-files returns the file path if it's tracked, empty if not
613
+ const result = execSync(`git ls-files -- ${this._escapeShellArg(filePath)}`, {
614
+ cwd: this.repoPath,
615
+ encoding: "utf8",
616
+ stdio: "pipe",
617
+ });
618
+ return result.trim().length === 0; // Empty = untracked
619
+ }
620
+ catch {
621
+ return true; // Assume untracked on error
622
+ }
623
+ }
624
+ /**
625
+ * Check if a file is a JSONL file in the .sudocode directory
626
+ *
627
+ * @param filePath - Relative path to the file
628
+ * @returns true if file is a .sudocode JSONL file
629
+ */
630
+ _isJSONLFile(filePath) {
631
+ return (filePath.endsWith(".jsonl") &&
632
+ (filePath.startsWith(".sudocode/") || filePath.includes("/.sudocode/")));
633
+ }
634
+ /**
635
+ * Perform three-way merge on a file using git merge-file
636
+ *
637
+ * Uses HEAD as base, local working copy as "ours", and worktree version as "theirs".
638
+ * Modifies the local file in place, inserting conflict markers if needed.
639
+ *
640
+ * @param filePath - Relative path to the file in local repo
641
+ * @param worktreeFilePath - Absolute path to the file in worktree
642
+ * @returns true if there are conflicts, false if merge was clean
643
+ */
644
+ _threeWayMergeFile(filePath, worktreeFilePath) {
645
+ const localFilePath = path.join(this.repoPath, filePath);
646
+ // Create temp file for base version from HEAD
647
+ const tempDir = os.tmpdir();
648
+ const baseTempFile = path.join(tempDir, `sudocode-merge-base-${Date.now()}-${path.basename(filePath)}`);
649
+ try {
650
+ // Get base version from HEAD
651
+ let baseContent = "";
652
+ try {
653
+ baseContent = execSync(`git show HEAD:${this._escapeShellArg(filePath)}`, {
654
+ cwd: this.repoPath,
655
+ encoding: "utf8",
656
+ stdio: "pipe",
657
+ });
658
+ }
659
+ catch {
660
+ // File might be new (not in HEAD), use empty base
661
+ baseContent = "";
662
+ }
663
+ fs.writeFileSync(baseTempFile, baseContent, "utf8");
664
+ // git merge-file modifies the first file in place
665
+ // Returns 0 if clean merge, >0 for number of conflicts, -1 for error
666
+ try {
667
+ execSync(`git merge-file -L "LOCAL" -L "BASE" -L "WORKTREE" ${this._escapeShellArg(localFilePath)} ${this._escapeShellArg(baseTempFile)} ${this._escapeShellArg(worktreeFilePath)}`, {
668
+ cwd: this.repoPath,
669
+ stdio: "pipe",
670
+ });
671
+ return false; // Exit 0 = clean merge, no conflicts
672
+ }
673
+ catch (error) {
674
+ // Exit code > 0 means conflicts (number of conflicts)
675
+ // Exit code < 0 means error
676
+ if (error.status > 0) {
677
+ return true; // Has conflicts
678
+ }
679
+ // Real error - rethrow
680
+ throw error;
681
+ }
682
+ }
683
+ finally {
684
+ // Clean up temp file
685
+ if (fs.existsSync(baseTempFile)) {
686
+ fs.unlinkSync(baseTempFile);
687
+ }
688
+ }
689
+ }
690
+ /**
691
+ * Merge two JSONL files using UUID-based resolution
692
+ *
693
+ * Reads both local and worktree versions, merges entities by UUID,
694
+ * and writes the result back to the local file.
695
+ *
696
+ * @param localFilePath - Absolute path to local JSONL file
697
+ * @param worktreeFilePath - Absolute path to worktree JSONL file
698
+ */
699
+ async _mergeJSONLFiles(localFilePath, worktreeFilePath) {
700
+ // Read both versions
701
+ const localEntities = readJSONLSync(localFilePath, { skipErrors: true });
702
+ const worktreeEntities = readJSONLSync(worktreeFilePath, {
703
+ skipErrors: true,
704
+ });
705
+ // Combine and resolve using UUID-based deduplication
706
+ const allEntities = [...localEntities, ...worktreeEntities];
707
+ const { entities: merged } = resolveEntities(allEntities);
708
+ // Write merged result back to local file
709
+ await writeJSONL(localFilePath, merged);
710
+ }
711
+ /**
712
+ * Copy uncommitted files from worktree to local repo with safe merging
713
+ *
714
+ * For files with local uncommitted changes:
715
+ * - JSONL files: Uses UUID-based merge resolution
716
+ * - Other files: Uses git merge-file for three-way merge with conflict markers
717
+ *
718
+ * Files without local changes are copied directly.
557
719
  *
558
720
  * @param worktreePath - Path to the worktree
559
- * @returns Number of files copied
721
+ * @param options - Optional settings
722
+ * @param options.overrideLocalChanges - If true, skip merge and overwrite local changes
723
+ * @returns Object with filesCopied count and list of files with conflicts
560
724
  */
561
- async _copyUncommittedFiles(worktreePath) {
725
+ async _copyUncommittedFiles(worktreePath, options) {
726
+ const { overrideLocalChanges = false } = options || {};
562
727
  // Get list of uncommitted/untracked files in worktree
563
728
  const modifiedOutput = execSync("git diff --name-only", {
564
729
  cwd: worktreePath,
@@ -578,10 +743,11 @@ export class WorktreeSyncService {
578
743
  .filter((line) => line.trim().length > 0);
579
744
  const allFiles = [...new Set([...modifiedFiles, ...untrackedFiles])];
580
745
  if (allFiles.length === 0) {
581
- return 0;
746
+ return { filesCopied: 0, filesWithConflicts: [] };
582
747
  }
583
- // Copy each file from worktree to local repo
748
+ // Process each file from worktree
584
749
  let filesCopied = 0;
750
+ const filesWithConflicts = [];
585
751
  for (const filePath of allFiles) {
586
752
  const srcPath = path.join(worktreePath, filePath);
587
753
  const destPath = path.join(this.repoPath, filePath);
@@ -594,16 +760,39 @@ export class WorktreeSyncService {
594
760
  if (!fs.existsSync(destDir)) {
595
761
  fs.mkdirSync(destDir, { recursive: true });
596
762
  }
597
- // Copy file
598
- fs.copyFileSync(srcPath, destPath);
599
- // Stage the file
600
- execSync(`git add ${this._escapeShellArg(filePath)}`, {
601
- cwd: this.repoPath,
602
- stdio: "pipe",
603
- });
763
+ // Check if local file has uncommitted changes or is untracked
764
+ // We need to merge if: (1) file exists locally AND (2) either has changes vs HEAD or is untracked
765
+ const localFileExists = fs.existsSync(destPath);
766
+ const localHasChangesVsHead = localFileExists && this._hasLocalUncommittedChanges(filePath);
767
+ const localIsUntracked = localFileExists && this._isFileUntracked(filePath);
768
+ const needsMerge = !overrideLocalChanges && (localHasChangesVsHead || localIsUntracked);
769
+ let hasConflicts = false;
770
+ if (!needsMerge) {
771
+ // No local changes OR override mode - copy directly (overwrites local)
772
+ fs.copyFileSync(srcPath, destPath);
773
+ }
774
+ else if (this._isJSONLFile(filePath)) {
775
+ // JSONL file with local changes - use UUID-based merge
776
+ await this._mergeJSONLFiles(destPath, srcPath);
777
+ }
778
+ else {
779
+ // Other file with local changes - use three-way merge
780
+ hasConflicts = this._threeWayMergeFile(filePath, srcPath);
781
+ if (hasConflicts) {
782
+ filesWithConflicts.push(filePath);
783
+ }
784
+ }
785
+ // Stage the file ONLY if it doesn't have conflicts
786
+ // Files with conflict markers should remain unstaged so VS Code can detect them
787
+ if (!hasConflicts) {
788
+ execSync(`git add ${this._escapeShellArg(filePath)}`, {
789
+ cwd: this.repoPath,
790
+ stdio: "pipe",
791
+ });
792
+ }
604
793
  filesCopied++;
605
794
  }
606
- return filesCopied;
795
+ return { filesCopied, filesWithConflicts };
607
796
  }
608
797
  /**
609
798
  * Resolve JSONL merge conflicts in the local repository
@@ -795,11 +984,28 @@ Synced changes from worktree execution.`;
795
984
  const mergeResult = this._performSquashMergeAllowConflicts(execution.branch_name, execution.target_branch);
796
985
  // 8. Check if there are unresolved conflicts
797
986
  if (mergeResult.hasConflicts) {
987
+ // Get list of files with conflicts
988
+ let filesWithConflicts = [];
989
+ try {
990
+ const conflictCheck = execSync("git diff --name-only --diff-filter=U", {
991
+ cwd: this.repoPath,
992
+ encoding: "utf8",
993
+ stdio: "pipe",
994
+ });
995
+ filesWithConflicts = conflictCheck
996
+ .trim()
997
+ .split("\n")
998
+ .filter((f) => f.length > 0);
999
+ }
1000
+ catch {
1001
+ // If command fails, leave empty
1002
+ }
798
1003
  // Return with conflicts info - user must resolve manually
799
1004
  return {
800
1005
  success: false,
801
1006
  filesChanged: mergeResult.filesChanged,
802
1007
  hasConflicts: true,
1008
+ filesWithConflicts,
803
1009
  error: "Merge conflicts detected. Please resolve them manually and commit.",
804
1010
  cleanupOffered: false,
805
1011
  };
@@ -841,11 +1047,12 @@ Synced changes from worktree execution.`;
841
1047
  * @param executionId - Execution ID to sync
842
1048
  * @param options - Optional settings
843
1049
  * @param options.includeUncommitted - If true, also copy uncommitted files from worktree
1050
+ * @param options.overrideLocalChanges - If true, overwrite local changes instead of merging
844
1051
  * @returns Sync result with details
845
1052
  * @throws WorktreeSyncError if sync fails
846
1053
  */
847
1054
  async stageSync(executionId, options) {
848
- const { includeUncommitted = false } = options || {};
1055
+ const { includeUncommitted = false, overrideLocalChanges = false } = options || {};
849
1056
  // 1. Load and validate execution
850
1057
  const execution = await this._loadAndValidateExecution(executionId);
851
1058
  // 2. Validate preconditions (skip dirty working tree check - stage mode doesn't commit)
@@ -875,13 +1082,20 @@ Synced changes from worktree execution.`;
875
1082
  filesChanged = mergeResult.filesChanged;
876
1083
  hasConflicts = mergeResult.hasConflicts;
877
1084
  }
878
- // 7. Copy uncommitted files from worktree if requested
1085
+ // 7. Copy uncommitted files from worktree if requested (with safe merging)
879
1086
  let uncommittedFilesCopied = 0;
1087
+ let filesWithConflicts = [];
880
1088
  if (includeUncommitted && execution.worktree_path) {
881
- uncommittedFilesCopied = await this._copyUncommittedFiles(execution.worktree_path);
1089
+ const copyResult = await this._copyUncommittedFiles(execution.worktree_path, { overrideLocalChanges });
1090
+ uncommittedFilesCopied = copyResult.filesCopied;
1091
+ filesWithConflicts = copyResult.filesWithConflicts;
882
1092
  filesChanged += uncommittedFilesCopied;
1093
+ // If we have conflicts from uncommitted files merge, mark hasConflicts
1094
+ if (filesWithConflicts.length > 0) {
1095
+ hasConflicts = true;
1096
+ }
883
1097
  }
884
- // 8. Auto-resolve JSONL conflicts if any
1098
+ // 8. Auto-resolve JSONL conflicts if any (from git merge --squash)
885
1099
  const jsonlFilesResolved = await this._resolveJSONLConflicts();
886
1100
  if (jsonlFilesResolved > 0) {
887
1101
  // Re-check for remaining conflicts after JSONL resolution
@@ -891,11 +1105,20 @@ Synced changes from worktree execution.`;
891
1105
  encoding: "utf8",
892
1106
  stdio: "pipe",
893
1107
  });
894
- hasConflicts = conflictCheck.trim().length > 0;
1108
+ const remainingConflictFiles = conflictCheck
1109
+ .trim()
1110
+ .split("\n")
1111
+ .filter((f) => f.length > 0);
1112
+ hasConflicts = remainingConflictFiles.length > 0;
1113
+ // Add any remaining conflict files not already tracked
1114
+ for (const file of remainingConflictFiles) {
1115
+ if (!filesWithConflicts.includes(file)) {
1116
+ filesWithConflicts.push(file);
1117
+ }
1118
+ }
895
1119
  }
896
1120
  catch {
897
- // If command fails, assume no conflicts
898
- hasConflicts = false;
1121
+ // If command fails, assume no additional conflicts
899
1122
  }
900
1123
  }
901
1124
  // 9. Check if there are unresolved (non-JSONL) conflicts
@@ -904,6 +1127,7 @@ Synced changes from worktree execution.`;
904
1127
  success: false,
905
1128
  filesChanged,
906
1129
  hasConflicts: true,
1130
+ filesWithConflicts,
907
1131
  uncommittedFilesIncluded: uncommittedFilesCopied,
908
1132
  error: "Merge conflicts detected. Please resolve them manually.",
909
1133
  cleanupOffered: false,
@@ -1052,10 +1276,27 @@ Synced changes from worktree execution.`;
1052
1276
  }
1053
1277
  // 11. Check if there are unresolved conflicts
1054
1278
  if (hasConflicts) {
1279
+ // Get list of files with conflicts
1280
+ let filesWithConflicts = [];
1281
+ try {
1282
+ const conflictCheck = execSync("git diff --name-only --diff-filter=U", {
1283
+ cwd: this.repoPath,
1284
+ encoding: "utf8",
1285
+ stdio: "pipe",
1286
+ });
1287
+ filesWithConflicts = conflictCheck
1288
+ .trim()
1289
+ .split("\n")
1290
+ .filter((f) => f.length > 0);
1291
+ }
1292
+ catch {
1293
+ // If command fails, leave empty
1294
+ }
1055
1295
  return {
1056
1296
  success: false,
1057
1297
  filesChanged,
1058
1298
  hasConflicts: true,
1299
+ filesWithConflicts,
1059
1300
  error: "Merge conflicts detected. Please resolve them manually and commit.",
1060
1301
  cleanupOffered: false,
1061
1302
  };