@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.
Files changed (234) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/README.md +25 -7
  3. package/commands/cursorflow-clean.md +19 -0
  4. package/commands/cursorflow-runs.md +59 -0
  5. package/commands/cursorflow-stop.md +55 -0
  6. package/dist/cli/clean.js +178 -6
  7. package/dist/cli/clean.js.map +1 -1
  8. package/dist/cli/index.js +12 -1
  9. package/dist/cli/index.js.map +1 -1
  10. package/dist/cli/init.js +8 -7
  11. package/dist/cli/init.js.map +1 -1
  12. package/dist/cli/logs.js +126 -77
  13. package/dist/cli/logs.js.map +1 -1
  14. package/dist/cli/monitor.d.ts +7 -0
  15. package/dist/cli/monitor.js +1021 -202
  16. package/dist/cli/monitor.js.map +1 -1
  17. package/dist/cli/prepare.js +39 -21
  18. package/dist/cli/prepare.js.map +1 -1
  19. package/dist/cli/resume.js +268 -163
  20. package/dist/cli/resume.js.map +1 -1
  21. package/dist/cli/run.js +11 -5
  22. package/dist/cli/run.js.map +1 -1
  23. package/dist/cli/runs.d.ts +5 -0
  24. package/dist/cli/runs.js +214 -0
  25. package/dist/cli/runs.js.map +1 -0
  26. package/dist/cli/setup-commands.js +0 -0
  27. package/dist/cli/signal.js +8 -8
  28. package/dist/cli/signal.js.map +1 -1
  29. package/dist/cli/stop.d.ts +5 -0
  30. package/dist/cli/stop.js +215 -0
  31. package/dist/cli/stop.js.map +1 -0
  32. package/dist/cli/tasks.d.ts +10 -0
  33. package/dist/cli/tasks.js +165 -0
  34. package/dist/cli/tasks.js.map +1 -0
  35. package/dist/core/auto-recovery.d.ts +212 -0
  36. package/dist/core/auto-recovery.js +737 -0
  37. package/dist/core/auto-recovery.js.map +1 -0
  38. package/dist/core/failure-policy.d.ts +156 -0
  39. package/dist/core/failure-policy.js +488 -0
  40. package/dist/core/failure-policy.js.map +1 -0
  41. package/dist/core/orchestrator.d.ts +16 -2
  42. package/dist/core/orchestrator.js +439 -105
  43. package/dist/core/orchestrator.js.map +1 -1
  44. package/dist/core/reviewer.d.ts +2 -0
  45. package/dist/core/reviewer.js +2 -0
  46. package/dist/core/reviewer.js.map +1 -1
  47. package/dist/core/runner.d.ts +33 -10
  48. package/dist/core/runner.js +374 -164
  49. package/dist/core/runner.js.map +1 -1
  50. package/dist/services/logging/buffer.d.ts +67 -0
  51. package/dist/services/logging/buffer.js +309 -0
  52. package/dist/services/logging/buffer.js.map +1 -0
  53. package/dist/services/logging/console.d.ts +89 -0
  54. package/dist/services/logging/console.js +169 -0
  55. package/dist/services/logging/console.js.map +1 -0
  56. package/dist/services/logging/file-writer.d.ts +71 -0
  57. package/dist/services/logging/file-writer.js +516 -0
  58. package/dist/services/logging/file-writer.js.map +1 -0
  59. package/dist/services/logging/formatter.d.ts +39 -0
  60. package/dist/services/logging/formatter.js +227 -0
  61. package/dist/services/logging/formatter.js.map +1 -0
  62. package/dist/services/logging/index.d.ts +11 -0
  63. package/dist/services/logging/index.js +30 -0
  64. package/dist/services/logging/index.js.map +1 -0
  65. package/dist/services/logging/parser.d.ts +31 -0
  66. package/dist/services/logging/parser.js +222 -0
  67. package/dist/services/logging/parser.js.map +1 -0
  68. package/dist/services/process/index.d.ts +59 -0
  69. package/dist/services/process/index.js +257 -0
  70. package/dist/services/process/index.js.map +1 -0
  71. package/dist/types/agent.d.ts +20 -0
  72. package/dist/types/agent.js +6 -0
  73. package/dist/types/agent.js.map +1 -0
  74. package/dist/types/config.d.ts +65 -0
  75. package/dist/types/config.js +6 -0
  76. package/dist/types/config.js.map +1 -0
  77. package/dist/types/events.d.ts +125 -0
  78. package/dist/types/events.js +6 -0
  79. package/dist/types/events.js.map +1 -0
  80. package/dist/types/index.d.ts +12 -0
  81. package/dist/types/index.js +37 -0
  82. package/dist/types/index.js.map +1 -0
  83. package/dist/types/lane.d.ts +43 -0
  84. package/dist/types/lane.js +6 -0
  85. package/dist/types/lane.js.map +1 -0
  86. package/dist/types/logging.d.ts +71 -0
  87. package/dist/types/logging.js +16 -0
  88. package/dist/types/logging.js.map +1 -0
  89. package/dist/types/review.d.ts +17 -0
  90. package/dist/types/review.js +6 -0
  91. package/dist/types/review.js.map +1 -0
  92. package/dist/types/run.d.ts +32 -0
  93. package/dist/types/run.js +6 -0
  94. package/dist/types/run.js.map +1 -0
  95. package/dist/types/task.d.ts +71 -0
  96. package/dist/types/task.js +6 -0
  97. package/dist/types/task.js.map +1 -0
  98. package/dist/ui/components.d.ts +134 -0
  99. package/dist/ui/components.js +389 -0
  100. package/dist/ui/components.js.map +1 -0
  101. package/dist/ui/log-viewer.d.ts +49 -0
  102. package/dist/ui/log-viewer.js +449 -0
  103. package/dist/ui/log-viewer.js.map +1 -0
  104. package/dist/utils/checkpoint.d.ts +87 -0
  105. package/dist/utils/checkpoint.js +317 -0
  106. package/dist/utils/checkpoint.js.map +1 -0
  107. package/dist/utils/config.d.ts +4 -0
  108. package/dist/utils/config.js +18 -8
  109. package/dist/utils/config.js.map +1 -1
  110. package/dist/utils/cursor-agent.js.map +1 -1
  111. package/dist/utils/dependency.d.ts +74 -0
  112. package/dist/utils/dependency.js +420 -0
  113. package/dist/utils/dependency.js.map +1 -0
  114. package/dist/utils/doctor.js +17 -11
  115. package/dist/utils/doctor.js.map +1 -1
  116. package/dist/utils/enhanced-logger.d.ts +10 -33
  117. package/dist/utils/enhanced-logger.js +108 -20
  118. package/dist/utils/enhanced-logger.js.map +1 -1
  119. package/dist/utils/git.d.ts +121 -0
  120. package/dist/utils/git.js +484 -11
  121. package/dist/utils/git.js.map +1 -1
  122. package/dist/utils/health.d.ts +91 -0
  123. package/dist/utils/health.js +556 -0
  124. package/dist/utils/health.js.map +1 -0
  125. package/dist/utils/lock.d.ts +95 -0
  126. package/dist/utils/lock.js +332 -0
  127. package/dist/utils/lock.js.map +1 -0
  128. package/dist/utils/log-buffer.d.ts +17 -0
  129. package/dist/utils/log-buffer.js +14 -0
  130. package/dist/utils/log-buffer.js.map +1 -0
  131. package/dist/utils/log-constants.d.ts +23 -0
  132. package/dist/utils/log-constants.js +28 -0
  133. package/dist/utils/log-constants.js.map +1 -0
  134. package/dist/utils/log-formatter.d.ts +25 -0
  135. package/dist/utils/log-formatter.js +237 -0
  136. package/dist/utils/log-formatter.js.map +1 -0
  137. package/dist/utils/log-service.d.ts +19 -0
  138. package/dist/utils/log-service.js +47 -0
  139. package/dist/utils/log-service.js.map +1 -0
  140. package/dist/utils/logger.d.ts +46 -27
  141. package/dist/utils/logger.js +82 -60
  142. package/dist/utils/logger.js.map +1 -1
  143. package/dist/utils/path.d.ts +19 -0
  144. package/dist/utils/path.js +77 -0
  145. package/dist/utils/path.js.map +1 -0
  146. package/dist/utils/process-manager.d.ts +21 -0
  147. package/dist/utils/process-manager.js +138 -0
  148. package/dist/utils/process-manager.js.map +1 -0
  149. package/dist/utils/retry.d.ts +121 -0
  150. package/dist/utils/retry.js +374 -0
  151. package/dist/utils/retry.js.map +1 -0
  152. package/dist/utils/run-service.d.ts +88 -0
  153. package/dist/utils/run-service.js +412 -0
  154. package/dist/utils/run-service.js.map +1 -0
  155. package/dist/utils/state.d.ts +62 -3
  156. package/dist/utils/state.js +317 -11
  157. package/dist/utils/state.js.map +1 -1
  158. package/dist/utils/task-service.d.ts +82 -0
  159. package/dist/utils/task-service.js +348 -0
  160. package/dist/utils/task-service.js.map +1 -0
  161. package/dist/utils/template.d.ts +14 -0
  162. package/dist/utils/template.js +122 -0
  163. package/dist/utils/template.js.map +1 -0
  164. package/dist/utils/types.d.ts +2 -271
  165. package/dist/utils/types.js +16 -0
  166. package/dist/utils/types.js.map +1 -1
  167. package/package.json +38 -23
  168. package/scripts/ai-security-check.js +0 -1
  169. package/scripts/local-security-gate.sh +0 -0
  170. package/scripts/monitor-lanes.sh +94 -0
  171. package/scripts/patches/test-cursor-agent.js +0 -1
  172. package/scripts/release.sh +0 -0
  173. package/scripts/setup-security.sh +0 -0
  174. package/scripts/stream-logs.sh +72 -0
  175. package/scripts/verify-and-fix.sh +0 -0
  176. package/src/cli/clean.ts +187 -6
  177. package/src/cli/index.ts +12 -1
  178. package/src/cli/init.ts +8 -7
  179. package/src/cli/logs.ts +124 -77
  180. package/src/cli/monitor.ts +1815 -898
  181. package/src/cli/prepare.ts +41 -21
  182. package/src/cli/resume.ts +753 -626
  183. package/src/cli/run.ts +12 -5
  184. package/src/cli/runs.ts +212 -0
  185. package/src/cli/setup-commands.ts +0 -0
  186. package/src/cli/signal.ts +8 -7
  187. package/src/cli/stop.ts +209 -0
  188. package/src/cli/tasks.ts +154 -0
  189. package/src/core/auto-recovery.ts +909 -0
  190. package/src/core/failure-policy.ts +592 -0
  191. package/src/core/orchestrator.ts +1131 -704
  192. package/src/core/reviewer.ts +4 -0
  193. package/src/core/runner.ts +444 -180
  194. package/src/services/logging/buffer.ts +326 -0
  195. package/src/services/logging/console.ts +193 -0
  196. package/src/services/logging/file-writer.ts +526 -0
  197. package/src/services/logging/formatter.ts +268 -0
  198. package/src/services/logging/index.ts +16 -0
  199. package/src/services/logging/parser.ts +232 -0
  200. package/src/services/process/index.ts +261 -0
  201. package/src/types/agent.ts +24 -0
  202. package/src/types/config.ts +79 -0
  203. package/src/types/events.ts +156 -0
  204. package/src/types/index.ts +29 -0
  205. package/src/types/lane.ts +56 -0
  206. package/src/types/logging.ts +96 -0
  207. package/src/types/review.ts +20 -0
  208. package/src/types/run.ts +37 -0
  209. package/src/types/task.ts +79 -0
  210. package/src/ui/components.ts +430 -0
  211. package/src/ui/log-viewer.ts +485 -0
  212. package/src/utils/checkpoint.ts +374 -0
  213. package/src/utils/config.ts +18 -8
  214. package/src/utils/cursor-agent.ts +1 -1
  215. package/src/utils/dependency.ts +482 -0
  216. package/src/utils/doctor.ts +18 -11
  217. package/src/utils/enhanced-logger.ts +122 -60
  218. package/src/utils/git.ts +517 -11
  219. package/src/utils/health.ts +596 -0
  220. package/src/utils/lock.ts +346 -0
  221. package/src/utils/log-buffer.ts +28 -0
  222. package/src/utils/log-constants.ts +26 -0
  223. package/src/utils/log-formatter.ts +245 -0
  224. package/src/utils/log-service.ts +49 -0
  225. package/src/utils/logger.ts +100 -51
  226. package/src/utils/path.ts +45 -0
  227. package/src/utils/process-manager.ts +100 -0
  228. package/src/utils/retry.ts +413 -0
  229. package/src/utils/run-service.ts +433 -0
  230. package/src/utils/state.ts +385 -11
  231. package/src/utils/task-service.ts +370 -0
  232. package/src/utils/template.ts +92 -0
  233. package/src/utils/types.ts +2 -314
  234. package/templates/basic.json +21 -0
@@ -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: silent ? 'pipe' : 'inherit',
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
- const { cwd, baseBranch = 'main' } = options;
100
- // Check if branch already exists
101
- const branchExists = runGitResult(['rev-parse', '--verify', branchName], { cwd }).success;
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
- else {
107
- // Create new branch from base
108
- runGit(['worktree', 'add', '-b', branchName, worktreePath, baseBranch], { cwd });
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