@litmers/cursorflow-orchestrator 0.1.18 → 0.1.26
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/CHANGELOG.md +25 -0
- package/README.md +25 -7
- package/commands/cursorflow-clean.md +19 -0
- package/commands/cursorflow-runs.md +59 -0
- package/commands/cursorflow-stop.md +55 -0
- package/dist/cli/clean.js +178 -6
- package/dist/cli/clean.js.map +1 -1
- package/dist/cli/index.js +12 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/init.js +8 -7
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/logs.js +126 -77
- package/dist/cli/logs.js.map +1 -1
- package/dist/cli/monitor.d.ts +7 -0
- package/dist/cli/monitor.js +1021 -202
- package/dist/cli/monitor.js.map +1 -1
- package/dist/cli/prepare.js +39 -21
- package/dist/cli/prepare.js.map +1 -1
- package/dist/cli/resume.js +268 -163
- package/dist/cli/resume.js.map +1 -1
- package/dist/cli/run.js +11 -5
- package/dist/cli/run.js.map +1 -1
- package/dist/cli/runs.d.ts +5 -0
- package/dist/cli/runs.js +214 -0
- package/dist/cli/runs.js.map +1 -0
- package/dist/cli/setup-commands.js +0 -0
- package/dist/cli/signal.js +8 -8
- package/dist/cli/signal.js.map +1 -1
- package/dist/cli/stop.d.ts +5 -0
- package/dist/cli/stop.js +215 -0
- package/dist/cli/stop.js.map +1 -0
- package/dist/cli/tasks.d.ts +10 -0
- package/dist/cli/tasks.js +165 -0
- package/dist/cli/tasks.js.map +1 -0
- package/dist/core/auto-recovery.d.ts +212 -0
- package/dist/core/auto-recovery.js +737 -0
- package/dist/core/auto-recovery.js.map +1 -0
- package/dist/core/failure-policy.d.ts +156 -0
- package/dist/core/failure-policy.js +488 -0
- package/dist/core/failure-policy.js.map +1 -0
- package/dist/core/orchestrator.d.ts +16 -2
- package/dist/core/orchestrator.js +439 -105
- package/dist/core/orchestrator.js.map +1 -1
- package/dist/core/reviewer.d.ts +2 -0
- package/dist/core/reviewer.js +2 -0
- package/dist/core/reviewer.js.map +1 -1
- package/dist/core/runner.d.ts +33 -10
- package/dist/core/runner.js +374 -164
- package/dist/core/runner.js.map +1 -1
- package/dist/services/logging/buffer.d.ts +67 -0
- package/dist/services/logging/buffer.js +309 -0
- package/dist/services/logging/buffer.js.map +1 -0
- package/dist/services/logging/console.d.ts +89 -0
- package/dist/services/logging/console.js +169 -0
- package/dist/services/logging/console.js.map +1 -0
- package/dist/services/logging/file-writer.d.ts +71 -0
- package/dist/services/logging/file-writer.js +516 -0
- package/dist/services/logging/file-writer.js.map +1 -0
- package/dist/services/logging/formatter.d.ts +39 -0
- package/dist/services/logging/formatter.js +227 -0
- package/dist/services/logging/formatter.js.map +1 -0
- package/dist/services/logging/index.d.ts +11 -0
- package/dist/services/logging/index.js +30 -0
- package/dist/services/logging/index.js.map +1 -0
- package/dist/services/logging/parser.d.ts +31 -0
- package/dist/services/logging/parser.js +222 -0
- package/dist/services/logging/parser.js.map +1 -0
- package/dist/services/process/index.d.ts +59 -0
- package/dist/services/process/index.js +257 -0
- package/dist/services/process/index.js.map +1 -0
- package/dist/types/agent.d.ts +20 -0
- package/dist/types/agent.js +6 -0
- package/dist/types/agent.js.map +1 -0
- package/dist/types/config.d.ts +65 -0
- package/dist/types/config.js +6 -0
- package/dist/types/config.js.map +1 -0
- package/dist/types/events.d.ts +125 -0
- package/dist/types/events.js +6 -0
- package/dist/types/events.js.map +1 -0
- package/dist/types/index.d.ts +12 -0
- package/dist/types/index.js +37 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/lane.d.ts +43 -0
- package/dist/types/lane.js +6 -0
- package/dist/types/lane.js.map +1 -0
- package/dist/types/logging.d.ts +71 -0
- package/dist/types/logging.js +16 -0
- package/dist/types/logging.js.map +1 -0
- package/dist/types/review.d.ts +17 -0
- package/dist/types/review.js +6 -0
- package/dist/types/review.js.map +1 -0
- package/dist/types/run.d.ts +32 -0
- package/dist/types/run.js +6 -0
- package/dist/types/run.js.map +1 -0
- package/dist/types/task.d.ts +71 -0
- package/dist/types/task.js +6 -0
- package/dist/types/task.js.map +1 -0
- package/dist/ui/components.d.ts +134 -0
- package/dist/ui/components.js +389 -0
- package/dist/ui/components.js.map +1 -0
- package/dist/ui/log-viewer.d.ts +49 -0
- package/dist/ui/log-viewer.js +449 -0
- package/dist/ui/log-viewer.js.map +1 -0
- package/dist/utils/checkpoint.d.ts +87 -0
- package/dist/utils/checkpoint.js +317 -0
- package/dist/utils/checkpoint.js.map +1 -0
- package/dist/utils/config.d.ts +4 -0
- package/dist/utils/config.js +18 -8
- package/dist/utils/config.js.map +1 -1
- package/dist/utils/cursor-agent.js.map +1 -1
- package/dist/utils/dependency.d.ts +74 -0
- package/dist/utils/dependency.js +420 -0
- package/dist/utils/dependency.js.map +1 -0
- package/dist/utils/doctor.js +17 -11
- package/dist/utils/doctor.js.map +1 -1
- package/dist/utils/enhanced-logger.d.ts +10 -33
- package/dist/utils/enhanced-logger.js +108 -20
- package/dist/utils/enhanced-logger.js.map +1 -1
- package/dist/utils/git.d.ts +121 -0
- package/dist/utils/git.js +484 -11
- package/dist/utils/git.js.map +1 -1
- package/dist/utils/health.d.ts +91 -0
- package/dist/utils/health.js +556 -0
- package/dist/utils/health.js.map +1 -0
- package/dist/utils/lock.d.ts +95 -0
- package/dist/utils/lock.js +332 -0
- package/dist/utils/lock.js.map +1 -0
- package/dist/utils/log-buffer.d.ts +17 -0
- package/dist/utils/log-buffer.js +14 -0
- package/dist/utils/log-buffer.js.map +1 -0
- package/dist/utils/log-constants.d.ts +23 -0
- package/dist/utils/log-constants.js +28 -0
- package/dist/utils/log-constants.js.map +1 -0
- package/dist/utils/log-formatter.d.ts +25 -0
- package/dist/utils/log-formatter.js +237 -0
- package/dist/utils/log-formatter.js.map +1 -0
- package/dist/utils/log-service.d.ts +19 -0
- package/dist/utils/log-service.js +47 -0
- package/dist/utils/log-service.js.map +1 -0
- package/dist/utils/logger.d.ts +46 -27
- package/dist/utils/logger.js +82 -60
- package/dist/utils/logger.js.map +1 -1
- package/dist/utils/path.d.ts +19 -0
- package/dist/utils/path.js +77 -0
- package/dist/utils/path.js.map +1 -0
- package/dist/utils/process-manager.d.ts +21 -0
- package/dist/utils/process-manager.js +138 -0
- package/dist/utils/process-manager.js.map +1 -0
- package/dist/utils/retry.d.ts +121 -0
- package/dist/utils/retry.js +374 -0
- package/dist/utils/retry.js.map +1 -0
- package/dist/utils/run-service.d.ts +88 -0
- package/dist/utils/run-service.js +412 -0
- package/dist/utils/run-service.js.map +1 -0
- package/dist/utils/state.d.ts +62 -3
- package/dist/utils/state.js +317 -11
- package/dist/utils/state.js.map +1 -1
- package/dist/utils/task-service.d.ts +82 -0
- package/dist/utils/task-service.js +348 -0
- package/dist/utils/task-service.js.map +1 -0
- package/dist/utils/template.d.ts +14 -0
- package/dist/utils/template.js +122 -0
- package/dist/utils/template.js.map +1 -0
- package/dist/utils/types.d.ts +2 -271
- package/dist/utils/types.js +16 -0
- package/dist/utils/types.js.map +1 -1
- package/package.json +38 -23
- package/scripts/ai-security-check.js +0 -1
- package/scripts/local-security-gate.sh +0 -0
- package/scripts/monitor-lanes.sh +94 -0
- package/scripts/patches/test-cursor-agent.js +0 -1
- package/scripts/release.sh +0 -0
- package/scripts/setup-security.sh +0 -0
- package/scripts/stream-logs.sh +72 -0
- package/scripts/verify-and-fix.sh +0 -0
- package/src/cli/clean.ts +187 -6
- package/src/cli/index.ts +12 -1
- package/src/cli/init.ts +8 -7
- package/src/cli/logs.ts +124 -77
- package/src/cli/monitor.ts +1815 -898
- package/src/cli/prepare.ts +41 -21
- package/src/cli/resume.ts +753 -626
- package/src/cli/run.ts +12 -5
- package/src/cli/runs.ts +212 -0
- package/src/cli/setup-commands.ts +0 -0
- package/src/cli/signal.ts +8 -7
- package/src/cli/stop.ts +209 -0
- package/src/cli/tasks.ts +154 -0
- package/src/core/auto-recovery.ts +909 -0
- package/src/core/failure-policy.ts +592 -0
- package/src/core/orchestrator.ts +1131 -704
- package/src/core/reviewer.ts +4 -0
- package/src/core/runner.ts +444 -180
- package/src/services/logging/buffer.ts +326 -0
- package/src/services/logging/console.ts +193 -0
- package/src/services/logging/file-writer.ts +526 -0
- package/src/services/logging/formatter.ts +268 -0
- package/src/services/logging/index.ts +16 -0
- package/src/services/logging/parser.ts +232 -0
- package/src/services/process/index.ts +261 -0
- package/src/types/agent.ts +24 -0
- package/src/types/config.ts +79 -0
- package/src/types/events.ts +156 -0
- package/src/types/index.ts +29 -0
- package/src/types/lane.ts +56 -0
- package/src/types/logging.ts +96 -0
- package/src/types/review.ts +20 -0
- package/src/types/run.ts +37 -0
- package/src/types/task.ts +79 -0
- package/src/ui/components.ts +430 -0
- package/src/ui/log-viewer.ts +485 -0
- package/src/utils/checkpoint.ts +374 -0
- package/src/utils/config.ts +18 -8
- package/src/utils/cursor-agent.ts +1 -1
- package/src/utils/dependency.ts +482 -0
- package/src/utils/doctor.ts +18 -11
- package/src/utils/enhanced-logger.ts +122 -60
- package/src/utils/git.ts +517 -11
- package/src/utils/health.ts +596 -0
- package/src/utils/lock.ts +346 -0
- package/src/utils/log-buffer.ts +28 -0
- package/src/utils/log-constants.ts +26 -0
- package/src/utils/log-formatter.ts +245 -0
- package/src/utils/log-service.ts +49 -0
- package/src/utils/logger.ts +100 -51
- package/src/utils/path.ts +45 -0
- package/src/utils/process-manager.ts +100 -0
- package/src/utils/retry.ts +413 -0
- package/src/utils/run-service.ts +433 -0
- package/src/utils/state.ts +385 -11
- package/src/utils/task-service.ts +370 -0
- package/src/utils/template.ts +92 -0
- package/src/utils/types.ts +2 -314
- package/templates/basic.json +21 -0
package/dist/utils/git.d.ts
CHANGED
|
@@ -44,6 +44,10 @@ export declare function getCurrentBranch(cwd?: string): string;
|
|
|
44
44
|
* Get repository root directory
|
|
45
45
|
*/
|
|
46
46
|
export declare function getRepoRoot(cwd?: string): string;
|
|
47
|
+
/**
|
|
48
|
+
* Get main repository root directory (for worktrees)
|
|
49
|
+
*/
|
|
50
|
+
export declare function getMainRepoRoot(cwd?: string): string;
|
|
47
51
|
/**
|
|
48
52
|
* Check if directory is a git repository
|
|
49
53
|
*/
|
|
@@ -140,3 +144,120 @@ export declare function getCommitInfo(commitHash: string, options?: {
|
|
|
140
144
|
* Comparing HEAD with its first parent
|
|
141
145
|
*/
|
|
142
146
|
export declare function getLastOperationStats(cwd?: string): string;
|
|
147
|
+
/**
|
|
148
|
+
* Generate a unique branch name that doesn't conflict with existing branches
|
|
149
|
+
*/
|
|
150
|
+
export declare function generateUniqueBranchName(baseName: string, options?: {
|
|
151
|
+
cwd?: string;
|
|
152
|
+
maxAttempts?: number;
|
|
153
|
+
}): string;
|
|
154
|
+
/**
|
|
155
|
+
* Safe merge result
|
|
156
|
+
*/
|
|
157
|
+
export interface SafeMergeResult {
|
|
158
|
+
success: boolean;
|
|
159
|
+
conflict: boolean;
|
|
160
|
+
conflictingFiles: string[];
|
|
161
|
+
error?: string;
|
|
162
|
+
aborted: boolean;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Safely merge a branch with conflict detection and auto-abort
|
|
166
|
+
*/
|
|
167
|
+
export declare function safeMerge(branchName: string, options?: {
|
|
168
|
+
cwd?: string;
|
|
169
|
+
noFf?: boolean;
|
|
170
|
+
message?: string | null;
|
|
171
|
+
abortOnConflict?: boolean;
|
|
172
|
+
strategy?: 'ours' | 'theirs' | null;
|
|
173
|
+
}): SafeMergeResult;
|
|
174
|
+
/**
|
|
175
|
+
* Get list of conflicting files
|
|
176
|
+
*/
|
|
177
|
+
export declare function getConflictingFiles(cwd?: string): string[];
|
|
178
|
+
/**
|
|
179
|
+
* Check if merge is in progress
|
|
180
|
+
*/
|
|
181
|
+
export declare function isMergeInProgress(cwd?: string): boolean;
|
|
182
|
+
/**
|
|
183
|
+
* Abort ongoing merge
|
|
184
|
+
*/
|
|
185
|
+
export declare function abortMerge(cwd?: string): boolean;
|
|
186
|
+
/**
|
|
187
|
+
* Get HEAD commit hash
|
|
188
|
+
*/
|
|
189
|
+
export declare function getHead(cwd?: string): string;
|
|
190
|
+
/**
|
|
191
|
+
* Get short HEAD commit hash
|
|
192
|
+
*/
|
|
193
|
+
export declare function getHeadShort(cwd?: string): string;
|
|
194
|
+
/**
|
|
195
|
+
* Stash changes with optional message
|
|
196
|
+
*/
|
|
197
|
+
export declare function stash(message?: string, options?: {
|
|
198
|
+
cwd?: string;
|
|
199
|
+
}): boolean;
|
|
200
|
+
/**
|
|
201
|
+
* Pop stashed changes
|
|
202
|
+
*/
|
|
203
|
+
export declare function stashPop(options?: {
|
|
204
|
+
cwd?: string;
|
|
205
|
+
}): boolean;
|
|
206
|
+
/**
|
|
207
|
+
* Clean worktree (remove untracked files)
|
|
208
|
+
*/
|
|
209
|
+
export declare function cleanWorktree(options?: {
|
|
210
|
+
cwd?: string;
|
|
211
|
+
force?: boolean;
|
|
212
|
+
directories?: boolean;
|
|
213
|
+
}): void;
|
|
214
|
+
/**
|
|
215
|
+
* Reset worktree to specific commit/branch
|
|
216
|
+
*/
|
|
217
|
+
export declare function reset(target: string, options?: {
|
|
218
|
+
cwd?: string;
|
|
219
|
+
mode?: 'soft' | 'mixed' | 'hard';
|
|
220
|
+
}): void;
|
|
221
|
+
/**
|
|
222
|
+
* Checkout specific commit or branch
|
|
223
|
+
*/
|
|
224
|
+
export declare function checkout(target: string, options?: {
|
|
225
|
+
cwd?: string;
|
|
226
|
+
force?: boolean;
|
|
227
|
+
createBranch?: boolean;
|
|
228
|
+
}): void;
|
|
229
|
+
/**
|
|
230
|
+
* Get commits between two refs
|
|
231
|
+
*/
|
|
232
|
+
export declare function getCommitsBetween(fromRef: string, toRef: string, options?: {
|
|
233
|
+
cwd?: string;
|
|
234
|
+
}): CommitInfo[];
|
|
235
|
+
/**
|
|
236
|
+
* Enhanced worktree creation with async lock
|
|
237
|
+
*/
|
|
238
|
+
export declare function createWorktreeAsync(worktreePath: string, branchName: string, options?: {
|
|
239
|
+
cwd?: string;
|
|
240
|
+
baseBranch?: string;
|
|
241
|
+
timeout?: number;
|
|
242
|
+
}): Promise<string>;
|
|
243
|
+
/**
|
|
244
|
+
* Prune orphaned worktrees
|
|
245
|
+
*/
|
|
246
|
+
export declare function pruneWorktrees(options?: {
|
|
247
|
+
cwd?: string;
|
|
248
|
+
}): void;
|
|
249
|
+
/**
|
|
250
|
+
* Get worktree for a specific path
|
|
251
|
+
*/
|
|
252
|
+
export declare function getWorktreeForPath(targetPath: string, cwd?: string): WorktreeInfo | null;
|
|
253
|
+
/**
|
|
254
|
+
* Sync branch with remote (fetch + merge or rebase)
|
|
255
|
+
*/
|
|
256
|
+
export declare function syncWithRemote(branch: string, options?: {
|
|
257
|
+
cwd?: string;
|
|
258
|
+
strategy?: 'merge' | 'rebase';
|
|
259
|
+
createIfMissing?: boolean;
|
|
260
|
+
}): {
|
|
261
|
+
success: boolean;
|
|
262
|
+
error?: string;
|
|
263
|
+
};
|
package/dist/utils/git.js
CHANGED
|
@@ -2,11 +2,45 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Git utilities for CursorFlow
|
|
4
4
|
*/
|
|
5
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
6
|
+
if (k2 === undefined) k2 = k;
|
|
7
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
8
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
9
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
10
|
+
}
|
|
11
|
+
Object.defineProperty(o, k2, desc);
|
|
12
|
+
}) : (function(o, m, k, k2) {
|
|
13
|
+
if (k2 === undefined) k2 = k;
|
|
14
|
+
o[k2] = m[k];
|
|
15
|
+
}));
|
|
16
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
17
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
18
|
+
}) : function(o, v) {
|
|
19
|
+
o["default"] = v;
|
|
20
|
+
});
|
|
21
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
22
|
+
var ownKeys = function(o) {
|
|
23
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
24
|
+
var ar = [];
|
|
25
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
26
|
+
return ar;
|
|
27
|
+
};
|
|
28
|
+
return ownKeys(o);
|
|
29
|
+
};
|
|
30
|
+
return function (mod) {
|
|
31
|
+
if (mod && mod.__esModule) return mod;
|
|
32
|
+
var result = {};
|
|
33
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
34
|
+
__setModuleDefault(result, mod);
|
|
35
|
+
return result;
|
|
36
|
+
};
|
|
37
|
+
})();
|
|
5
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
39
|
exports.runGit = runGit;
|
|
7
40
|
exports.runGitResult = runGitResult;
|
|
8
41
|
exports.getCurrentBranch = getCurrentBranch;
|
|
9
42
|
exports.getRepoRoot = getRepoRoot;
|
|
43
|
+
exports.getMainRepoRoot = getMainRepoRoot;
|
|
10
44
|
exports.isGitRepo = isGitRepo;
|
|
11
45
|
exports.worktreeExists = worktreeExists;
|
|
12
46
|
exports.createWorktree = createWorktree;
|
|
@@ -23,18 +57,122 @@ exports.deleteBranch = deleteBranch;
|
|
|
23
57
|
exports.merge = merge;
|
|
24
58
|
exports.getCommitInfo = getCommitInfo;
|
|
25
59
|
exports.getLastOperationStats = getLastOperationStats;
|
|
60
|
+
exports.generateUniqueBranchName = generateUniqueBranchName;
|
|
61
|
+
exports.safeMerge = safeMerge;
|
|
62
|
+
exports.getConflictingFiles = getConflictingFiles;
|
|
63
|
+
exports.isMergeInProgress = isMergeInProgress;
|
|
64
|
+
exports.abortMerge = abortMerge;
|
|
65
|
+
exports.getHead = getHead;
|
|
66
|
+
exports.getHeadShort = getHeadShort;
|
|
67
|
+
exports.stash = stash;
|
|
68
|
+
exports.stashPop = stashPop;
|
|
69
|
+
exports.cleanWorktree = cleanWorktree;
|
|
70
|
+
exports.reset = reset;
|
|
71
|
+
exports.checkout = checkout;
|
|
72
|
+
exports.getCommitsBetween = getCommitsBetween;
|
|
73
|
+
exports.createWorktreeAsync = createWorktreeAsync;
|
|
74
|
+
exports.pruneWorktrees = pruneWorktrees;
|
|
75
|
+
exports.getWorktreeForPath = getWorktreeForPath;
|
|
76
|
+
exports.syncWithRemote = syncWithRemote;
|
|
26
77
|
const child_process_1 = require("child_process");
|
|
78
|
+
const fs = __importStar(require("fs"));
|
|
79
|
+
const path = __importStar(require("path"));
|
|
80
|
+
const path_1 = require("./path");
|
|
81
|
+
/**
|
|
82
|
+
* Acquire a file-based lock for Git operations
|
|
83
|
+
*/
|
|
84
|
+
function acquireLock(lockName, cwd) {
|
|
85
|
+
const repoRoot = cwd || getRepoRoot();
|
|
86
|
+
const lockDir = (0, path_1.safeJoin)(repoRoot, '_cursorflow', 'locks');
|
|
87
|
+
if (!fs.existsSync(lockDir)) {
|
|
88
|
+
fs.mkdirSync(lockDir, { recursive: true });
|
|
89
|
+
}
|
|
90
|
+
const lockFile = (0, path_1.safeJoin)(lockDir, `${lockName}.lock`);
|
|
91
|
+
try {
|
|
92
|
+
// wx flag ensures atomic creation
|
|
93
|
+
fs.writeFileSync(lockFile, String(process.pid), { flag: 'wx' });
|
|
94
|
+
return lockFile;
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Release a file-based lock
|
|
102
|
+
*/
|
|
103
|
+
function releaseLock(lockFile) {
|
|
104
|
+
if (lockFile && fs.existsSync(lockFile)) {
|
|
105
|
+
try {
|
|
106
|
+
fs.unlinkSync(lockFile);
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
// Ignore
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Run Git command with locking
|
|
115
|
+
*/
|
|
116
|
+
async function runGitWithLock(lockName, fn, options = {}) {
|
|
117
|
+
const maxRetries = options.maxRetries ?? 10;
|
|
118
|
+
const retryDelay = options.retryDelay ?? 500;
|
|
119
|
+
let retries = 0;
|
|
120
|
+
let lockFile = null;
|
|
121
|
+
while (retries < maxRetries) {
|
|
122
|
+
lockFile = acquireLock(lockName, options.cwd);
|
|
123
|
+
if (lockFile)
|
|
124
|
+
break;
|
|
125
|
+
retries++;
|
|
126
|
+
const delay = Math.floor(Math.random() * retryDelay) + retryDelay / 2;
|
|
127
|
+
await new Promise(resolve => setTimeout(resolve, delay));
|
|
128
|
+
}
|
|
129
|
+
if (!lockFile) {
|
|
130
|
+
throw new Error(`Failed to acquire lock: ${lockName}`);
|
|
131
|
+
}
|
|
132
|
+
try {
|
|
133
|
+
return fn();
|
|
134
|
+
}
|
|
135
|
+
finally {
|
|
136
|
+
releaseLock(lockFile);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Filter out noisy git stderr messages
|
|
141
|
+
*/
|
|
142
|
+
function filterGitStderr(stderr) {
|
|
143
|
+
if (!stderr)
|
|
144
|
+
return '';
|
|
145
|
+
const lines = stderr.split('\n');
|
|
146
|
+
const filtered = lines.filter(line => {
|
|
147
|
+
// GitHub noise
|
|
148
|
+
if (line.includes('remote: Create a pull request'))
|
|
149
|
+
return false;
|
|
150
|
+
if (line.trim().startsWith('remote:') && line.includes('pull/new'))
|
|
151
|
+
return false;
|
|
152
|
+
if (line.trim() === 'remote:')
|
|
153
|
+
return false; // Empty remote lines
|
|
154
|
+
return true;
|
|
155
|
+
});
|
|
156
|
+
return filtered.join('\n');
|
|
157
|
+
}
|
|
27
158
|
/**
|
|
28
159
|
* Run git command and return output
|
|
29
160
|
*/
|
|
30
161
|
function runGit(args, options = {}) {
|
|
31
162
|
const { cwd, silent = false } = options;
|
|
32
163
|
try {
|
|
164
|
+
const stdioMode = silent ? 'pipe' : ['inherit', 'inherit', 'pipe'];
|
|
33
165
|
const result = (0, child_process_1.spawnSync)('git', args, {
|
|
34
166
|
cwd: cwd || process.cwd(),
|
|
35
167
|
encoding: 'utf8',
|
|
36
|
-
stdio:
|
|
168
|
+
stdio: stdioMode,
|
|
37
169
|
});
|
|
170
|
+
if (!silent && result.stderr) {
|
|
171
|
+
const filteredStderr = filterGitStderr(result.stderr);
|
|
172
|
+
if (filteredStderr) {
|
|
173
|
+
process.stderr.write(filteredStderr);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
38
176
|
if (result.status !== 0 && !silent) {
|
|
39
177
|
throw new Error(`Git command failed: git ${args.join(' ')}\n${result.stderr || ''}`);
|
|
40
178
|
}
|
|
@@ -76,6 +214,24 @@ function getCurrentBranch(cwd) {
|
|
|
76
214
|
function getRepoRoot(cwd) {
|
|
77
215
|
return runGit(['rev-parse', '--show-toplevel'], { cwd, silent: true });
|
|
78
216
|
}
|
|
217
|
+
/**
|
|
218
|
+
* Get main repository root directory (for worktrees)
|
|
219
|
+
*/
|
|
220
|
+
function getMainRepoRoot(cwd) {
|
|
221
|
+
try {
|
|
222
|
+
const result = runGitResult(['worktree', 'list', '--porcelain'], { cwd });
|
|
223
|
+
if (result.success && result.stdout) {
|
|
224
|
+
const firstLine = result.stdout.split('\n')[0];
|
|
225
|
+
if (firstLine && firstLine.startsWith('worktree ')) {
|
|
226
|
+
return firstLine.slice(9).trim();
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
catch {
|
|
231
|
+
// Fallback to normal repo root
|
|
232
|
+
}
|
|
233
|
+
return getRepoRoot(cwd);
|
|
234
|
+
}
|
|
79
235
|
/**
|
|
80
236
|
* Check if directory is a git repository
|
|
81
237
|
*/
|
|
@@ -96,18 +252,59 @@ function worktreeExists(worktreePath, cwd) {
|
|
|
96
252
|
* Create worktree
|
|
97
253
|
*/
|
|
98
254
|
function createWorktree(worktreePath, branchName, options = {}) {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
if (branchExists) {
|
|
103
|
-
// Branch exists, checkout to worktree
|
|
104
|
-
runGit(['worktree', 'add', worktreePath, branchName], { cwd });
|
|
255
|
+
let { cwd, baseBranch } = options;
|
|
256
|
+
if (!baseBranch) {
|
|
257
|
+
baseBranch = getCurrentBranch(cwd) || 'refs/heads/main';
|
|
105
258
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
259
|
+
// Ensure baseBranch is unambiguous (branch name rather than tag)
|
|
260
|
+
const unambiguousBase = (baseBranch.startsWith('refs/') || baseBranch.includes('/'))
|
|
261
|
+
? baseBranch
|
|
262
|
+
: `refs/heads/${baseBranch}`;
|
|
263
|
+
// Use a file-based lock to prevent race conditions during worktree creation
|
|
264
|
+
const lockDir = (0, path_1.safeJoin)(cwd || getRepoRoot(), '_cursorflow', 'locks');
|
|
265
|
+
if (!fs.existsSync(lockDir)) {
|
|
266
|
+
fs.mkdirSync(lockDir, { recursive: true });
|
|
267
|
+
}
|
|
268
|
+
const lockFile = (0, path_1.safeJoin)(lockDir, 'worktree.lock');
|
|
269
|
+
let retries = 20;
|
|
270
|
+
let acquired = false;
|
|
271
|
+
while (retries > 0 && !acquired) {
|
|
272
|
+
try {
|
|
273
|
+
fs.writeFileSync(lockFile, String(process.pid), { flag: 'wx' });
|
|
274
|
+
acquired = true;
|
|
275
|
+
}
|
|
276
|
+
catch {
|
|
277
|
+
retries--;
|
|
278
|
+
const delay = Math.floor(Math.random() * 500) + 200;
|
|
279
|
+
// Use synchronous sleep to keep the function signature synchronous
|
|
280
|
+
const end = Date.now() + delay;
|
|
281
|
+
while (Date.now() < end) { /* wait */ }
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
if (!acquired) {
|
|
285
|
+
throw new Error('Failed to acquire worktree lock after multiple retries');
|
|
286
|
+
}
|
|
287
|
+
try {
|
|
288
|
+
// Check if branch already exists
|
|
289
|
+
const branchExists = runGitResult(['rev-parse', '--verify', branchName], { cwd }).success;
|
|
290
|
+
if (branchExists) {
|
|
291
|
+
// Branch exists, checkout to worktree
|
|
292
|
+
runGit(['worktree', 'add', worktreePath, branchName], { cwd });
|
|
293
|
+
}
|
|
294
|
+
else {
|
|
295
|
+
// Create new branch from base
|
|
296
|
+
runGit(['worktree', 'add', '-b', branchName, worktreePath, unambiguousBase], { cwd });
|
|
297
|
+
}
|
|
298
|
+
return worktreePath;
|
|
299
|
+
}
|
|
300
|
+
finally {
|
|
301
|
+
try {
|
|
302
|
+
fs.unlinkSync(lockFile);
|
|
303
|
+
}
|
|
304
|
+
catch {
|
|
305
|
+
// Ignore
|
|
306
|
+
}
|
|
109
307
|
}
|
|
110
|
-
return worktreePath;
|
|
111
308
|
}
|
|
112
309
|
/**
|
|
113
310
|
* Remove worktree
|
|
@@ -309,4 +506,280 @@ function getLastOperationStats(cwd) {
|
|
|
309
506
|
return '';
|
|
310
507
|
}
|
|
311
508
|
}
|
|
509
|
+
// ============================================================================
|
|
510
|
+
// Enhanced Git Functions for Robustness
|
|
511
|
+
// ============================================================================
|
|
512
|
+
/**
|
|
513
|
+
* Generate a unique branch name that doesn't conflict with existing branches
|
|
514
|
+
*/
|
|
515
|
+
function generateUniqueBranchName(baseName, options = {}) {
|
|
516
|
+
const { cwd, maxAttempts = 10 } = options;
|
|
517
|
+
const timestamp = Date.now().toString(36);
|
|
518
|
+
const random = () => Math.random().toString(36).substring(2, 5);
|
|
519
|
+
// First attempt: base name with timestamp
|
|
520
|
+
let candidate = `${baseName}-${timestamp}-${random()}`;
|
|
521
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
522
|
+
if (!branchExists(candidate, { cwd })) {
|
|
523
|
+
return candidate;
|
|
524
|
+
}
|
|
525
|
+
// Try with new random suffix
|
|
526
|
+
candidate = `${baseName}-${timestamp}-${random()}`;
|
|
527
|
+
}
|
|
528
|
+
// Last resort: use full timestamp with random
|
|
529
|
+
return `${baseName}-${Date.now()}-${random()}`;
|
|
530
|
+
}
|
|
531
|
+
/**
|
|
532
|
+
* Safely merge a branch with conflict detection and auto-abort
|
|
533
|
+
*/
|
|
534
|
+
function safeMerge(branchName, options = {}) {
|
|
535
|
+
const { cwd, noFf = false, message = null, abortOnConflict = true, strategy = null } = options;
|
|
536
|
+
const args = ['merge'];
|
|
537
|
+
if (noFf) {
|
|
538
|
+
args.push('--no-ff');
|
|
539
|
+
}
|
|
540
|
+
if (strategy) {
|
|
541
|
+
args.push('-X', strategy);
|
|
542
|
+
}
|
|
543
|
+
if (message) {
|
|
544
|
+
args.push('-m', message);
|
|
545
|
+
}
|
|
546
|
+
args.push(branchName);
|
|
547
|
+
const result = runGitResult(args, { cwd });
|
|
548
|
+
if (result.success) {
|
|
549
|
+
return {
|
|
550
|
+
success: true,
|
|
551
|
+
conflict: false,
|
|
552
|
+
conflictingFiles: [],
|
|
553
|
+
aborted: false,
|
|
554
|
+
};
|
|
555
|
+
}
|
|
556
|
+
// Check for conflicts
|
|
557
|
+
const output = result.stdout + result.stderr;
|
|
558
|
+
const isConflict = output.includes('CONFLICT') || output.includes('Automatic merge failed');
|
|
559
|
+
if (isConflict) {
|
|
560
|
+
// Get conflicting files
|
|
561
|
+
const conflictingFiles = getConflictingFiles(cwd);
|
|
562
|
+
if (abortOnConflict) {
|
|
563
|
+
// Abort the merge
|
|
564
|
+
runGitResult(['merge', '--abort'], { cwd });
|
|
565
|
+
return {
|
|
566
|
+
success: false,
|
|
567
|
+
conflict: true,
|
|
568
|
+
conflictingFiles,
|
|
569
|
+
error: 'Merge conflict detected and aborted',
|
|
570
|
+
aborted: true,
|
|
571
|
+
};
|
|
572
|
+
}
|
|
573
|
+
return {
|
|
574
|
+
success: false,
|
|
575
|
+
conflict: true,
|
|
576
|
+
conflictingFiles,
|
|
577
|
+
error: 'Merge conflict - manual resolution required',
|
|
578
|
+
aborted: false,
|
|
579
|
+
};
|
|
580
|
+
}
|
|
581
|
+
return {
|
|
582
|
+
success: false,
|
|
583
|
+
conflict: false,
|
|
584
|
+
conflictingFiles: [],
|
|
585
|
+
error: result.stderr || 'Merge failed',
|
|
586
|
+
aborted: false,
|
|
587
|
+
};
|
|
588
|
+
}
|
|
589
|
+
/**
|
|
590
|
+
* Get list of conflicting files
|
|
591
|
+
*/
|
|
592
|
+
function getConflictingFiles(cwd) {
|
|
593
|
+
const result = runGitResult(['diff', '--name-only', '--diff-filter=U'], { cwd });
|
|
594
|
+
if (!result.success)
|
|
595
|
+
return [];
|
|
596
|
+
return result.stdout.split('\n').filter(f => f.trim());
|
|
597
|
+
}
|
|
598
|
+
/**
|
|
599
|
+
* Check if merge is in progress
|
|
600
|
+
*/
|
|
601
|
+
function isMergeInProgress(cwd) {
|
|
602
|
+
const repoRoot = getRepoRoot(cwd);
|
|
603
|
+
return fs.existsSync(path.join(repoRoot, '.git', 'MERGE_HEAD'));
|
|
604
|
+
}
|
|
605
|
+
/**
|
|
606
|
+
* Abort ongoing merge
|
|
607
|
+
*/
|
|
608
|
+
function abortMerge(cwd) {
|
|
609
|
+
const result = runGitResult(['merge', '--abort'], { cwd });
|
|
610
|
+
return result.success;
|
|
611
|
+
}
|
|
612
|
+
/**
|
|
613
|
+
* Get HEAD commit hash
|
|
614
|
+
*/
|
|
615
|
+
function getHead(cwd) {
|
|
616
|
+
return runGit(['rev-parse', 'HEAD'], { cwd, silent: true });
|
|
617
|
+
}
|
|
618
|
+
/**
|
|
619
|
+
* Get short HEAD commit hash
|
|
620
|
+
*/
|
|
621
|
+
function getHeadShort(cwd) {
|
|
622
|
+
return runGit(['rev-parse', '--short', 'HEAD'], { cwd, silent: true });
|
|
623
|
+
}
|
|
624
|
+
/**
|
|
625
|
+
* Stash changes with optional message
|
|
626
|
+
*/
|
|
627
|
+
function stash(message, options = {}) {
|
|
628
|
+
const args = ['stash', 'push'];
|
|
629
|
+
if (message) {
|
|
630
|
+
args.push('-m', message);
|
|
631
|
+
}
|
|
632
|
+
const result = runGitResult(args, { cwd: options.cwd });
|
|
633
|
+
return result.success;
|
|
634
|
+
}
|
|
635
|
+
/**
|
|
636
|
+
* Pop stashed changes
|
|
637
|
+
*/
|
|
638
|
+
function stashPop(options = {}) {
|
|
639
|
+
const result = runGitResult(['stash', 'pop'], { cwd: options.cwd });
|
|
640
|
+
return result.success;
|
|
641
|
+
}
|
|
642
|
+
/**
|
|
643
|
+
* Clean worktree (remove untracked files)
|
|
644
|
+
*/
|
|
645
|
+
function cleanWorktree(options = {}) {
|
|
646
|
+
const args = ['clean'];
|
|
647
|
+
if (options.force)
|
|
648
|
+
args.push('-f');
|
|
649
|
+
if (options.directories)
|
|
650
|
+
args.push('-d');
|
|
651
|
+
runGit(args, { cwd: options.cwd });
|
|
652
|
+
}
|
|
653
|
+
/**
|
|
654
|
+
* Reset worktree to specific commit/branch
|
|
655
|
+
*/
|
|
656
|
+
function reset(target, options = {}) {
|
|
657
|
+
const args = ['reset'];
|
|
658
|
+
if (options.mode)
|
|
659
|
+
args.push(`--${options.mode}`);
|
|
660
|
+
args.push(target);
|
|
661
|
+
runGit(args, { cwd: options.cwd });
|
|
662
|
+
}
|
|
663
|
+
/**
|
|
664
|
+
* Checkout specific commit or branch
|
|
665
|
+
*/
|
|
666
|
+
function checkout(target, options = {}) {
|
|
667
|
+
const args = ['checkout'];
|
|
668
|
+
if (options.force)
|
|
669
|
+
args.push('-f');
|
|
670
|
+
if (options.createBranch)
|
|
671
|
+
args.push('-b');
|
|
672
|
+
args.push(target);
|
|
673
|
+
runGit(args, { cwd: options.cwd });
|
|
674
|
+
}
|
|
675
|
+
/**
|
|
676
|
+
* Get commits between two refs
|
|
677
|
+
*/
|
|
678
|
+
function getCommitsBetween(fromRef, toRef, options = {}) {
|
|
679
|
+
const format = '%H|%h|%an|%ae|%at|%s';
|
|
680
|
+
const result = runGitResult(['log', '--format=' + format, `${fromRef}..${toRef}`], { cwd: options.cwd });
|
|
681
|
+
if (!result.success)
|
|
682
|
+
return [];
|
|
683
|
+
return result.stdout.split('\n')
|
|
684
|
+
.filter(line => line.trim())
|
|
685
|
+
.map(line => {
|
|
686
|
+
const parts = line.split('|');
|
|
687
|
+
return {
|
|
688
|
+
hash: parts[0] || '',
|
|
689
|
+
shortHash: parts[1] || '',
|
|
690
|
+
author: parts[2] || '',
|
|
691
|
+
authorEmail: parts[3] || '',
|
|
692
|
+
timestamp: parseInt(parts[4] || '0'),
|
|
693
|
+
subject: parts[5] || '',
|
|
694
|
+
};
|
|
695
|
+
});
|
|
696
|
+
}
|
|
697
|
+
/**
|
|
698
|
+
* Enhanced worktree creation with async lock
|
|
699
|
+
*/
|
|
700
|
+
async function createWorktreeAsync(worktreePath, branchName, options = {}) {
|
|
701
|
+
let { cwd, baseBranch, timeout = 30000 } = options;
|
|
702
|
+
if (!baseBranch) {
|
|
703
|
+
baseBranch = getCurrentBranch(cwd) || 'refs/heads/main';
|
|
704
|
+
}
|
|
705
|
+
// Ensure baseBranch is unambiguous
|
|
706
|
+
const unambiguousBase = (baseBranch.startsWith('refs/') || baseBranch.includes('/'))
|
|
707
|
+
? baseBranch
|
|
708
|
+
: `refs/heads/${baseBranch}`;
|
|
709
|
+
const { acquireLock, releaseLock } = await Promise.resolve().then(() => __importStar(require('./lock')));
|
|
710
|
+
const lockDir = (0, path_1.safeJoin)(cwd || getRepoRoot(), '_cursorflow', 'locks');
|
|
711
|
+
if (!fs.existsSync(lockDir)) {
|
|
712
|
+
fs.mkdirSync(lockDir, { recursive: true });
|
|
713
|
+
}
|
|
714
|
+
const lockFile = (0, path_1.safeJoin)(lockDir, 'worktree.lock');
|
|
715
|
+
const acquired = await acquireLock(lockFile, {
|
|
716
|
+
timeoutMs: timeout,
|
|
717
|
+
operation: `createWorktree:${branchName}`,
|
|
718
|
+
});
|
|
719
|
+
if (!acquired) {
|
|
720
|
+
throw new Error('Failed to acquire worktree lock after timeout');
|
|
721
|
+
}
|
|
722
|
+
try {
|
|
723
|
+
// Check if branch already exists
|
|
724
|
+
const branchExistsLocal = runGitResult(['rev-parse', '--verify', branchName], { cwd }).success;
|
|
725
|
+
if (branchExistsLocal) {
|
|
726
|
+
runGit(['worktree', 'add', worktreePath, branchName], { cwd });
|
|
727
|
+
}
|
|
728
|
+
else {
|
|
729
|
+
runGit(['worktree', 'add', '-b', branchName, worktreePath, unambiguousBase], { cwd });
|
|
730
|
+
}
|
|
731
|
+
return worktreePath;
|
|
732
|
+
}
|
|
733
|
+
finally {
|
|
734
|
+
await releaseLock(lockFile);
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
/**
|
|
738
|
+
* Prune orphaned worktrees
|
|
739
|
+
*/
|
|
740
|
+
function pruneWorktrees(options = {}) {
|
|
741
|
+
runGit(['worktree', 'prune'], { cwd: options.cwd });
|
|
742
|
+
}
|
|
743
|
+
/**
|
|
744
|
+
* Get worktree for a specific path
|
|
745
|
+
*/
|
|
746
|
+
function getWorktreeForPath(targetPath, cwd) {
|
|
747
|
+
const worktrees = listWorktrees(cwd);
|
|
748
|
+
return worktrees.find(wt => wt.path === targetPath) || null;
|
|
749
|
+
}
|
|
750
|
+
/**
|
|
751
|
+
* Sync branch with remote (fetch + merge or rebase)
|
|
752
|
+
*/
|
|
753
|
+
function syncWithRemote(branch, options = {}) {
|
|
754
|
+
const { cwd, strategy = 'merge', createIfMissing = false } = options;
|
|
755
|
+
// Fetch the branch
|
|
756
|
+
const fetchResult = runGitResult(['fetch', 'origin', branch], { cwd });
|
|
757
|
+
if (!fetchResult.success) {
|
|
758
|
+
if (createIfMissing && fetchResult.stderr.includes('not found')) {
|
|
759
|
+
// Branch doesn't exist on remote, nothing to sync
|
|
760
|
+
return { success: true };
|
|
761
|
+
}
|
|
762
|
+
return { success: false, error: fetchResult.stderr };
|
|
763
|
+
}
|
|
764
|
+
// Merge or rebase
|
|
765
|
+
if (strategy === 'rebase') {
|
|
766
|
+
const result = runGitResult(['rebase', `origin/${branch}`], { cwd });
|
|
767
|
+
if (!result.success) {
|
|
768
|
+
// Abort rebase on failure
|
|
769
|
+
runGitResult(['rebase', '--abort'], { cwd });
|
|
770
|
+
return { success: false, error: result.stderr };
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
else {
|
|
774
|
+
const mergeResult = safeMerge(`origin/${branch}`, {
|
|
775
|
+
cwd,
|
|
776
|
+
message: `chore: sync with origin/${branch}`,
|
|
777
|
+
abortOnConflict: true,
|
|
778
|
+
});
|
|
779
|
+
if (!mergeResult.success) {
|
|
780
|
+
return { success: false, error: mergeResult.error };
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
return { success: true };
|
|
784
|
+
}
|
|
312
785
|
//# sourceMappingURL=git.js.map
|