@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.
- package/dist/execution/executors/agent-executor-wrapper.d.ts.map +1 -1
- package/dist/execution/executors/agent-executor-wrapper.js +57 -2
- package/dist/execution/executors/agent-executor-wrapper.js.map +1 -1
- package/dist/execution/process/builders/claude.d.ts.map +1 -1
- package/dist/execution/process/builders/claude.js +32 -1
- package/dist/execution/process/builders/claude.js.map +1 -1
- package/dist/execution/worktree/config.js +1 -1
- package/dist/execution/worktree/config.js.map +1 -1
- package/dist/execution/worktree/git-cli.d.ts +48 -0
- package/dist/execution/worktree/git-cli.d.ts.map +1 -1
- package/dist/execution/worktree/git-cli.js +81 -0
- package/dist/execution/worktree/git-cli.js.map +1 -1
- package/dist/execution/worktree/types.d.ts.map +1 -1
- package/dist/execution/worktree/types.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +17 -4
- package/dist/index.js.map +1 -1
- package/dist/public/assets/index-Nz4IjDwB.css +1 -0
- package/dist/public/assets/index-Z8yftXvD.js +824 -0
- package/dist/public/assets/index-Z8yftXvD.js.map +1 -0
- package/dist/public/assets/{react-vendor-DiL5hC7l.js → react-vendor-5f1Wq1qs.js} +5 -5
- package/dist/public/assets/{react-vendor-DiL5hC7l.js.map → react-vendor-5f1Wq1qs.js.map} +1 -1
- package/dist/public/assets/{ui-vendor-B4WMPEfa.js → ui-vendor-BDDPoYki.js} +2 -2
- package/dist/public/assets/{ui-vendor-B4WMPEfa.js.map → ui-vendor-BDDPoYki.js.map} +1 -1
- package/dist/public/index.html +4 -4
- package/dist/routes/executions.d.ts.map +1 -1
- package/dist/routes/executions.js +3 -1
- package/dist/routes/executions.js.map +1 -1
- package/dist/routes/issues.d.ts.map +1 -1
- package/dist/routes/issues.js +13 -0
- package/dist/routes/issues.js.map +1 -1
- package/dist/routes/specs.d.ts.map +1 -1
- package/dist/routes/specs.js +14 -0
- package/dist/routes/specs.js.map +1 -1
- package/dist/routes/workflows.d.ts +8 -0
- package/dist/routes/workflows.d.ts.map +1 -0
- package/dist/routes/workflows.js +1729 -0
- package/dist/routes/workflows.js.map +1 -0
- package/dist/services/execution-event-callbacks.d.ts +73 -0
- package/dist/services/execution-event-callbacks.d.ts.map +1 -0
- package/dist/services/execution-event-callbacks.js +82 -0
- package/dist/services/execution-event-callbacks.js.map +1 -0
- package/dist/services/execution-lifecycle.d.ts +38 -2
- package/dist/services/execution-lifecycle.d.ts.map +1 -1
- package/dist/services/execution-lifecycle.js +94 -23
- package/dist/services/execution-lifecycle.js.map +1 -1
- package/dist/services/execution-service.d.ts +31 -3
- package/dist/services/execution-service.d.ts.map +1 -1
- package/dist/services/execution-service.js +161 -34
- package/dist/services/execution-service.js.map +1 -1
- package/dist/services/executions.d.ts +1 -0
- package/dist/services/executions.d.ts.map +1 -1
- package/dist/services/executions.js +4 -0
- package/dist/services/executions.js.map +1 -1
- package/dist/services/project-context.d.ts +25 -0
- package/dist/services/project-context.d.ts.map +1 -1
- package/dist/services/project-context.js +53 -3
- package/dist/services/project-context.js.map +1 -1
- package/dist/services/project-manager.d.ts +7 -0
- package/dist/services/project-manager.d.ts.map +1 -1
- package/dist/services/project-manager.js +108 -13
- package/dist/services/project-manager.js.map +1 -1
- package/dist/services/websocket.d.ts +10 -2
- package/dist/services/websocket.d.ts.map +1 -1
- package/dist/services/websocket.js +18 -0
- package/dist/services/websocket.js.map +1 -1
- package/dist/services/workflow-broadcast-service.d.ts +43 -0
- package/dist/services/workflow-broadcast-service.d.ts.map +1 -0
- package/dist/services/workflow-broadcast-service.js +145 -0
- package/dist/services/workflow-broadcast-service.js.map +1 -0
- package/dist/services/worktree-sync-service.d.ts +76 -4
- package/dist/services/worktree-sync-service.d.ts.map +1 -1
- package/dist/services/worktree-sync-service.js +264 -23
- package/dist/services/worktree-sync-service.js.map +1 -1
- package/dist/workflow/base-workflow-engine.d.ts +186 -0
- package/dist/workflow/base-workflow-engine.d.ts.map +1 -0
- package/dist/workflow/base-workflow-engine.js +549 -0
- package/dist/workflow/base-workflow-engine.js.map +1 -0
- package/dist/workflow/dependency-analyzer.d.ts +78 -0
- package/dist/workflow/dependency-analyzer.d.ts.map +1 -0
- package/dist/workflow/dependency-analyzer.js +264 -0
- package/dist/workflow/dependency-analyzer.js.map +1 -0
- package/dist/workflow/engines/orchestrator-engine.d.ts +237 -0
- package/dist/workflow/engines/orchestrator-engine.d.ts.map +1 -0
- package/dist/workflow/engines/orchestrator-engine.js +749 -0
- package/dist/workflow/engines/orchestrator-engine.js.map +1 -0
- package/dist/workflow/engines/sequential-engine.d.ts +276 -0
- package/dist/workflow/engines/sequential-engine.d.ts.map +1 -0
- package/dist/workflow/engines/sequential-engine.js +1110 -0
- package/dist/workflow/engines/sequential-engine.js.map +1 -0
- package/dist/workflow/index.d.ts +15 -0
- package/dist/workflow/index.d.ts.map +1 -0
- package/dist/workflow/index.js +22 -0
- package/dist/workflow/index.js.map +1 -0
- package/dist/workflow/mcp/api-client.d.ts +103 -0
- package/dist/workflow/mcp/api-client.d.ts.map +1 -0
- package/dist/workflow/mcp/api-client.js +193 -0
- package/dist/workflow/mcp/api-client.js.map +1 -0
- package/dist/workflow/mcp/index.d.ts +16 -0
- package/dist/workflow/mcp/index.d.ts.map +1 -0
- package/dist/workflow/mcp/index.js +114 -0
- package/dist/workflow/mcp/index.js.map +1 -0
- package/dist/workflow/mcp/server.d.ts +85 -0
- package/dist/workflow/mcp/server.d.ts.map +1 -0
- package/dist/workflow/mcp/server.js +520 -0
- package/dist/workflow/mcp/server.js.map +1 -0
- package/dist/workflow/mcp/tools/escalation.d.ts +36 -0
- package/dist/workflow/mcp/tools/escalation.d.ts.map +1 -0
- package/dist/workflow/mcp/tools/escalation.js +47 -0
- package/dist/workflow/mcp/tools/escalation.js.map +1 -0
- package/dist/workflow/mcp/tools/execution.d.ts +59 -0
- package/dist/workflow/mcp/tools/execution.d.ts.map +1 -0
- package/dist/workflow/mcp/tools/execution.js +67 -0
- package/dist/workflow/mcp/tools/execution.js.map +1 -0
- package/dist/workflow/mcp/tools/inspection.d.ts +82 -0
- package/dist/workflow/mcp/tools/inspection.d.ts.map +1 -0
- package/dist/workflow/mcp/tools/inspection.js +57 -0
- package/dist/workflow/mcp/tools/inspection.js.map +1 -0
- package/dist/workflow/mcp/tools/workflow.d.ts +59 -0
- package/dist/workflow/mcp/tools/workflow.d.ts.map +1 -0
- package/dist/workflow/mcp/tools/workflow.js +40 -0
- package/dist/workflow/mcp/tools/workflow.js.map +1 -0
- package/dist/workflow/mcp/types.d.ts +345 -0
- package/dist/workflow/mcp/types.d.ts.map +1 -0
- package/dist/workflow/mcp/types.js +7 -0
- package/dist/workflow/mcp/types.js.map +1 -0
- package/dist/workflow/services/prompt-builder.d.ts +36 -0
- package/dist/workflow/services/prompt-builder.d.ts.map +1 -0
- package/dist/workflow/services/prompt-builder.js +329 -0
- package/dist/workflow/services/prompt-builder.js.map +1 -0
- package/dist/workflow/services/wakeup-service.d.ts +262 -0
- package/dist/workflow/services/wakeup-service.d.ts.map +1 -0
- package/dist/workflow/services/wakeup-service.js +809 -0
- package/dist/workflow/services/wakeup-service.js.map +1 -0
- package/dist/workflow/workflow-engine.d.ts +221 -0
- package/dist/workflow/workflow-engine.d.ts.map +1 -0
- package/dist/workflow/workflow-engine.js +94 -0
- package/dist/workflow/workflow-engine.js.map +1 -0
- package/dist/workflow/workflow-event-emitter.d.ts +278 -0
- package/dist/workflow/workflow-event-emitter.d.ts.map +1 -0
- package/dist/workflow/workflow-event-emitter.js +259 -0
- package/dist/workflow/workflow-event-emitter.js.map +1 -0
- package/package.json +8 -6
- package/dist/public/assets/index-DV9Tbujb.css +0 -1
- package/dist/public/assets/index-DcDX9-Ad.js +0 -740
- 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
|
-
*
|
|
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
|
-
*
|
|
227
|
-
*
|
|
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
|
-
* @
|
|
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":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAC3C,OAAO,KAAK,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAIrE,OAAO,EAEL,KAAK,UAAU,EACf,KAAK,MAAM,EACZ,MAAM,uCAAuC,CAAC;AAC/C,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,aAAa,EACnB,MAAM,4CAA4C,CAAC;
|
|
1
|
+
{"version":3,"file":"worktree-sync-service.d.ts","sourceRoot":"","sources":["../../src/services/worktree-sync-service.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAC3C,OAAO,KAAK,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAIrE,OAAO,EAEL,KAAK,UAAU,EACf,KAAK,MAAM,EACZ,MAAM,uCAAuC,CAAC;AAC/C,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,aAAa,EACnB,MAAM,4CAA4C,CAAC;AAcpD;;GAEG;AACH,oBAAY,qBAAqB;IAC/B,WAAW,gBAAgB;IAC3B,gBAAgB,qBAAqB;IACrC,cAAc,mBAAmB;IACjC,kBAAkB,uBAAuB;IACzC,qBAAqB,0BAA0B;IAC/C,cAAc,mBAAmB;IACjC,cAAc,mBAAmB;IACjC,YAAY,iBAAiB;IAC7B,uBAAuB,4BAA4B;IACnD,oBAAoB,yBAAyB;IAC7C,mBAAmB,wBAAwB;CAC5C;AAED;;GAEG;AACH,qBAAa,iBAAkB,SAAQ,KAAK;IAGjC,IAAI,EAAE,qBAAqB;IAC3B,KAAK,CAAC,EAAE,KAAK;gBAFpB,OAAO,EAAE,MAAM,EACR,IAAI,EAAE,qBAAqB,EAC3B,KAAK,CAAC,EAAE,KAAK,YAAA;CAKvB;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC,oDAAoD;IACpD,KAAK,EAAE,MAAM,CAAC;IACd,kDAAkD;IAClD,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,cAAc,CAAC;IAC1B,IAAI,EAAE,UAAU,CAAC;IACjB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,iDAAiD;IACjD,uBAAuB,EAAE,MAAM,EAAE,CAAC;IAClC,oFAAoF;IACpF,kBAAkB,CAAC,EAAE,oBAAoB,CAAC;IAC1C,+EAA+E;IAC/E,uBAAuB,CAAC,EAAE,uBAAuB,CAAC;IAClD,eAAe,EAAE,eAAe,CAAC;IACjC,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,gFAAgF;IAChF,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,0EAA0E;IAC1E,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC9B,yEAAyE;IACzE,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED;;;;GAIG;AACH,qBAAa,mBAAmB;IAI5B,OAAO,CAAC,EAAE;IACV,OAAO,CAAC,QAAQ;IAJlB,OAAO,CAAC,OAAO,CAAa;gBAGlB,EAAE,EAAE,QAAQ,CAAC,QAAQ,EACrB,QAAQ,EAAE,MAAM;IAK1B;;;;;OAKG;IACG,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAwHlE;;;;;;;;OAQG;YACW,8BAA8B;IAoC5C;;;;;;;;OAQG;YACW,yBAAyB;IAgBvC;;;;;;;;;;;;;;OAcG;YACW,0BAA0B;IA6DxC;;;;;;;;;OASG;YACW,qBAAqB;IAenC;;;;;OAKG;IACH,OAAO,CAAC,oBAAoB;IAK5B;;;;;;;;OAQG;IACH,OAAO,CAAC,wBAAwB;IAqEhC;;;;;;OAMG;IAEH,OAAO,CAAC,iBAAiB;IAIzB;;;;OAIG;IACH,OAAO,CAAC,YAAY;IAsBpB;;;;;OAKG;IACH,OAAO,CAAC,iBAAiB;IAqBzB;;;;;OAKG;IACH,OAAO,CAAC,eAAe;IAKvB;;;;;;;;OAQG;IACG,qBAAqB,CACzB,SAAS,EAAE,SAAS,EACpB,cAAc,EAAE,aAAa,EAAE,GAC9B,OAAO,CAAC,IAAI,CAAC;IAoDhB;;;;;;OAMG;YACW,iBAAiB;IA6B/B;;;;;;;;OAQG;IACG,sBAAsB,CAC1B,YAAY,EAAE,MAAM,EACpB,gBAAgB,EAAE,MAAM,EAAE,GACzB,OAAO,CAAC,IAAI,CAAC;IA+BhB;;;;;;;;;OASG;IACH,OAAO,CAAC,iCAAiC;IAiDzC;;;;;OAKG;IACH,OAAO,CAAC,2BAA2B;IAanC;;;;;;;;OAQG;IACH,OAAO,CAAC,8BAA8B;IA0BtC;;;;;OAKG;IACH,OAAO,CAAC,gBAAgB;IAiBxB;;;;;OAKG;IACH,OAAO,CAAC,YAAY;IAOpB;;;;;;;;;OASG;IACH,OAAO,CAAC,kBAAkB;IA2D1B;;;;;;;;OAQG;YACW,gBAAgB;IAkB9B;;;;;;;;;;;;;OAaG;YACW,qBAAqB;IAiGnC;;;;;;;OAOG;YACW,sBAAsB;IAsBpC;;;;OAIG;YACW,iBAAiB;IAsD/B;;;;;;OAMG;IACH,OAAO,CAAC,sBAAsB;IAe9B;;;;;;OAMG;IACH,OAAO,CAAC,aAAa;IAwBrB;;;;;;OAMG;YACW,mBAAmB;IA2BjC;;;;;;;;;;;OAWG;IACG,UAAU,CACd,WAAW,EAAE,MAAM,EACnB,mBAAmB,CAAC,EAAE,MAAM,GAC3B,OAAO,CAAC,UAAU,CAAC;IAkHtB;;;;;;;;;;;;;;OAcG;IACG,SAAS,CACb,WAAW,EAAE,MAAM,EACnB,OAAO,CAAC,EAAE;QAAE,kBAAkB,CAAC,EAAE,OAAO,CAAC;QAAC,oBAAoB,CAAC,EAAE,OAAO,CAAA;KAAE,GACzE,OAAO,CAAC,UAAU,CAAC;IA2ItB;;;;;;;;;OASG;IACH,OAAO,CAAC,WAAW;IAiBnB;;;;;;;;;;OAUG;IACG,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;CA6K7D"}
|
|
@@ -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
|
-
*
|
|
558
|
+
* Check if a file has local uncommitted changes compared to HEAD
|
|
554
559
|
*
|
|
555
|
-
*
|
|
556
|
-
*
|
|
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
|
-
* @
|
|
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
|
-
//
|
|
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
|
-
//
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
};
|