@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
package/src/cli/clean.ts CHANGED
@@ -9,6 +9,8 @@ import * as path from 'path';
9
9
  import * as logger from '../utils/logger';
10
10
  import * as git from '../utils/git';
11
11
  import { loadConfig, getLogsDir, getTasksDir } from '../utils/config';
12
+ import { safeJoin } from '../utils/path';
13
+ import { RunService } from '../utils/run-service';
12
14
 
13
15
  interface CleanOptions {
14
16
  type?: string;
@@ -19,6 +21,9 @@ interface CleanOptions {
19
21
  help: boolean;
20
22
  keepLatest: boolean;
21
23
  includeLatest: boolean;
24
+ run?: string;
25
+ olderThan?: number;
26
+ orphaned: boolean;
22
27
  }
23
28
 
24
29
  function printHelp(): void {
@@ -35,6 +40,9 @@ Types:
35
40
  all Remove all of the above (default)
36
41
 
37
42
  Options:
43
+ --run <id> Clean resources linked to a specific run
44
+ --older-than <days> Clean resources older than N days
45
+ --orphaned Clean orphaned resources (not linked to any run)
38
46
  --dry-run Show what would be removed without deleting
39
47
  --force Force removal (ignore uncommitted changes)
40
48
  --include-latest Also remove the most recent item (by default, latest is kept)
@@ -44,6 +52,9 @@ Options:
44
52
 
45
53
  function parseArgs(args: string[]): CleanOptions {
46
54
  const includeLatest = args.includes('--include-latest');
55
+ const runIdx = args.indexOf('--run');
56
+ const olderThanIdx = args.indexOf('--older-than');
57
+
47
58
  return {
48
59
  type: args.find(a => ['branches', 'worktrees', 'logs', 'tasks', 'all'].includes(a)),
49
60
  pattern: null,
@@ -53,6 +64,9 @@ function parseArgs(args: string[]): CleanOptions {
53
64
  help: args.includes('--help') || args.includes('-h'),
54
65
  keepLatest: !includeLatest, // Default: keep latest, unless --include-latest is specified
55
66
  includeLatest,
67
+ run: runIdx >= 0 ? args[runIdx + 1] : undefined,
68
+ olderThan: olderThanIdx >= 0 ? parseInt(args[olderThanIdx + 1] || '0') : undefined,
69
+ orphaned: args.includes('--orphaned'),
56
70
  };
57
71
  }
58
72
 
@@ -78,9 +92,33 @@ async function clean(args: string[]): Promise<void> {
78
92
 
79
93
  const config = loadConfig();
80
94
  const repoRoot = git.getRepoRoot();
95
+ const logsDir = getLogsDir(config);
96
+ const runsDir = safeJoin(logsDir, 'runs');
97
+ const runService = new RunService(runsDir);
81
98
 
82
99
  logger.section('🧹 Cleaning CursorFlow Resources');
83
100
 
101
+ // Handle specific run cleanup
102
+ if (options.run) {
103
+ await cleanRunResources(runService, options.run, repoRoot, options);
104
+ logger.success('\n✨ Run cleaning complete!');
105
+ return;
106
+ }
107
+
108
+ // Handle older-than cleanup
109
+ if (options.olderThan !== undefined) {
110
+ await cleanOlderRuns(runService, options.olderThan, repoRoot, options);
111
+ logger.success(`\n✨ Older than ${options.olderThan} days cleaning complete!`);
112
+ return;
113
+ }
114
+
115
+ // Handle orphaned cleanup
116
+ if (options.orphaned) {
117
+ await cleanOrphanedResources(runService, config, repoRoot, options);
118
+ logger.success('\n✨ Orphaned resources cleaning complete!');
119
+ return;
120
+ }
121
+
84
122
  const type = options.type || 'all';
85
123
 
86
124
  if (type === 'all') {
@@ -105,7 +143,7 @@ async function cleanWorktrees(config: any, repoRoot: string, options: CleanOptio
105
143
  logger.info('\nChecking worktrees...');
106
144
  const worktrees = git.listWorktrees(repoRoot);
107
145
 
108
- const worktreeRoot = path.join(repoRoot, config.worktreeRoot || '_cursorflow/worktrees');
146
+ const worktreeRoot = safeJoin(repoRoot, config.worktreeRoot || '_cursorflow/worktrees');
109
147
  let toRemove = worktrees.filter(wt => {
110
148
  // Skip main worktree
111
149
  if (wt.path === repoRoot) return false;
@@ -226,9 +264,9 @@ async function cleanLogs(config: any, options: CleanOptions) {
226
264
  const entries = fs.readdirSync(logsDir, { withFileTypes: true });
227
265
  let items = entries.map(entry => ({
228
266
  name: entry.name,
229
- path: path.join(logsDir, entry.name),
267
+ path: safeJoin(logsDir, entry.name),
230
268
  isDir: entry.isDirectory(),
231
- mtime: getModTime(path.join(logsDir, entry.name))
269
+ mtime: getModTime(safeJoin(logsDir, entry.name))
232
270
  }));
233
271
 
234
272
  if (items.length <= 1) {
@@ -288,9 +326,9 @@ async function cleanTasks(config: any, options: CleanOptions) {
288
326
  .filter(entry => entry.name !== 'example')
289
327
  .map(entry => ({
290
328
  name: entry.name,
291
- path: path.join(tasksDir, entry.name),
329
+ path: safeJoin(tasksDir, entry.name),
292
330
  isDir: entry.isDirectory(),
293
- mtime: getModTime(path.join(tasksDir, entry.name))
331
+ mtime: getModTime(safeJoin(tasksDir, entry.name))
294
332
  }));
295
333
 
296
334
  if (items.length <= 1) {
@@ -325,7 +363,7 @@ async function cleanTasks(config: any, options: CleanOptions) {
325
363
  const entries = fs.readdirSync(tasksDir, { withFileTypes: true });
326
364
  for (const entry of entries) {
327
365
  if (entry.name === 'example') continue;
328
- const itemPath = path.join(tasksDir, entry.name);
366
+ const itemPath = safeJoin(tasksDir, entry.name);
329
367
  logger.info(` Removing task: ${entry.name}...`);
330
368
  fs.rmSync(itemPath, { recursive: true, force: true });
331
369
  }
@@ -337,4 +375,147 @@ async function cleanTasks(config: any, options: CleanOptions) {
337
375
  }
338
376
  }
339
377
 
378
+ async function cleanRunResources(runService: RunService, runId: string, repoRoot: string, options: CleanOptions) {
379
+ const run = runService.getRunInfo(runId);
380
+ if (!run) {
381
+ logger.warn(`Run not found: ${runId}`);
382
+ return;
383
+ }
384
+
385
+ logger.info(`\nCleaning resources for run: ${runId} (${run.taskName})`);
386
+
387
+ // Clean branches
388
+ if (run.branches.length > 0) {
389
+ logger.info(' Cleaning branches...');
390
+ for (const branch of run.branches) {
391
+ if (options.dryRun) {
392
+ logger.info(` [DRY RUN] Would delete branch: ${branch}`);
393
+ } else {
394
+ try {
395
+ git.deleteBranch(branch, { cwd: repoRoot, force: true });
396
+ logger.info(` Deleted branch: ${branch}`);
397
+ } catch (e: any) {
398
+ logger.warn(` Could not delete branch ${branch}: ${e.message}`);
399
+ }
400
+ }
401
+ }
402
+ }
403
+
404
+ // Clean worktrees
405
+ if (run.worktrees.length > 0) {
406
+ logger.info(' Cleaning worktrees...');
407
+ for (const wtPath of run.worktrees) {
408
+ if (options.dryRun) {
409
+ logger.info(` [DRY RUN] Would remove worktree: ${wtPath}`);
410
+ } else {
411
+ try {
412
+ git.removeWorktree(wtPath, { cwd: repoRoot, force: true });
413
+ if (fs.existsSync(wtPath)) {
414
+ fs.rmSync(wtPath, { recursive: true, force: true });
415
+ }
416
+ logger.info(` Removed worktree: ${wtPath}`);
417
+ } catch (e: any) {
418
+ logger.warn(` Could not remove worktree ${wtPath}: ${e.message}`);
419
+ }
420
+ }
421
+ }
422
+ }
423
+
424
+ // Delete run directory
425
+ if (options.dryRun) {
426
+ logger.info(` [DRY RUN] Would delete run directory: ${run.path}`);
427
+ } else {
428
+ try {
429
+ runService.deleteRun(runId, { force: options.force });
430
+ logger.info(` Deleted run directory: ${run.path}`);
431
+ } catch (e: any) {
432
+ logger.error(` Failed to delete run ${runId}: ${e.message}`);
433
+ }
434
+ }
435
+ }
436
+
437
+ async function cleanOlderRuns(runService: RunService, days: number, repoRoot: string, options: CleanOptions) {
438
+ const cutoff = Date.now() - (days * 24 * 60 * 60 * 1000);
439
+ const runs = runService.listRuns();
440
+ const olderRuns = runs.filter(run => run.startTime < cutoff);
441
+
442
+ if (olderRuns.length === 0) {
443
+ logger.info(`No runs found older than ${days} days.`);
444
+ return;
445
+ }
446
+
447
+ logger.info(`Found ${olderRuns.length} runs older than ${days} days.`);
448
+ for (const run of olderRuns) {
449
+ await cleanRunResources(runService, run.id, repoRoot, options);
450
+ }
451
+ }
452
+
453
+ async function cleanOrphanedResources(runService: RunService, config: any, repoRoot: string, options: CleanOptions) {
454
+ const runs = runService.listRuns();
455
+ const linkedWorktrees = new Set(runs.flatMap(r => r.worktrees));
456
+ const linkedBranches = new Set(runs.flatMap(r => r.branches));
457
+
458
+ // Clean orphaned worktrees
459
+ logger.info('\nChecking for orphaned worktrees...');
460
+ const worktrees = git.listWorktrees(repoRoot);
461
+ const worktreeRoot = safeJoin(repoRoot, config.worktreeRoot || '_cursorflow/worktrees');
462
+
463
+ const orphanedWorktrees = worktrees.filter(wt => {
464
+ if (wt.path === repoRoot) return false;
465
+ const isInsideRoot = wt.path.startsWith(worktreeRoot);
466
+ const hasPrefix = path.basename(wt.path).startsWith(config.worktreePrefix || 'cursorflow-');
467
+ return (isInsideRoot || hasPrefix) && !linkedWorktrees.has(wt.path);
468
+ });
469
+
470
+ if (orphanedWorktrees.length === 0) {
471
+ logger.info(' No orphaned worktrees found.');
472
+ } else {
473
+ for (const wt of orphanedWorktrees) {
474
+ if (options.dryRun) {
475
+ logger.info(` [DRY RUN] Would remove orphaned worktree: ${wt.path}`);
476
+ } else {
477
+ try {
478
+ git.removeWorktree(wt.path, { cwd: repoRoot, force: true });
479
+ if (fs.existsSync(wt.path)) {
480
+ fs.rmSync(wt.path, { recursive: true, force: true });
481
+ }
482
+ logger.info(` Removed orphaned worktree: ${wt.path}`);
483
+ } catch (e: any) {
484
+ logger.warn(` Could not remove orphaned worktree ${wt.path}: ${e.message}`);
485
+ }
486
+ }
487
+ }
488
+ }
489
+
490
+ // Clean orphaned branches
491
+ logger.info('\nChecking for orphaned branches...');
492
+ const result = git.runGitResult(['branch', '--list'], { cwd: repoRoot });
493
+ if (result.success) {
494
+ const branches = result.stdout
495
+ .split('\n')
496
+ .map(b => b.replace(/\*/g, '').trim())
497
+ .filter(b => b && b !== 'main' && b !== 'master');
498
+
499
+ const prefix = config.branchPrefix || 'feature/';
500
+ const orphanedBranches = branches.filter(b => b.startsWith(prefix) && !linkedBranches.has(b));
501
+
502
+ if (orphanedBranches.length === 0) {
503
+ logger.info(' No orphaned branches found.');
504
+ } else {
505
+ for (const branch of orphanedBranches) {
506
+ if (options.dryRun) {
507
+ logger.info(` [DRY RUN] Would delete orphaned branch: ${branch}`);
508
+ } else {
509
+ try {
510
+ git.deleteBranch(branch, { cwd: repoRoot, force: true });
511
+ logger.info(` Deleted orphaned branch: ${branch}`);
512
+ } catch (e: any) {
513
+ logger.warn(` Could not delete orphaned branch ${branch}: ${e.message}`);
514
+ }
515
+ }
516
+ }
517
+ }
518
+ }
519
+ }
520
+
340
521
  export = clean;
package/src/cli/index.ts CHANGED
@@ -21,6 +21,9 @@ const COMMANDS: Record<string, CommandFn> = {
21
21
  signal: require('./signal'),
22
22
  models: require('./models'),
23
23
  logs: require('./logs'),
24
+ runs: require('./runs'),
25
+ tasks: require('./tasks'),
26
+ stop: require('./stop'),
24
27
  setup: require('./setup-commands').main,
25
28
  'setup-commands': require('./setup-commands').main,
26
29
  };
@@ -34,10 +37,14 @@ function printHelp(): void {
34
37
 
35
38
  \x1b[1mCOMMANDS\x1b[0m
36
39
  \x1b[33minit\x1b[0m [options] Initialize CursorFlow in project
40
+ \x1b[33msetup\x1b[0m [options] Install Cursor IDE commands
37
41
  \x1b[33mprepare\x1b[0m <feature> [opts] Prepare task directory and JSON files
38
42
  \x1b[33mrun\x1b[0m <tasks-dir> [options] Run orchestration (DAG-based)
39
43
  \x1b[33mmonitor\x1b[0m [run-dir] [options] \x1b[36mInteractive\x1b[0m lane dashboard
40
- \x1b[33mclean\x1b[0m <type> [options] Clean branches/worktrees/logs
44
+ \x1b[33mtasks\x1b[0m [name] [options] Browse and validate prepared tasks
45
+ \x1b[33mruns\x1b[0m [run-id] [options] List and view run details
46
+ \x1b[33mstop\x1b[0m [run-id] [options] Stop running workflows
47
+ \x1b[33mclean\x1b[0m <type> [options] Clean branches/worktrees/logs/tasks
41
48
  \x1b[33mresume\x1b[0m [lane] [options] Resume lane(s) - use --all for batch resume
42
49
  \x1b[33mdoctor\x1b[0m [options] Check environment and preflight
43
50
  \x1b[33msignal\x1b[0m <lane> <msg> Directly intervene in a running lane
@@ -54,6 +61,10 @@ function printHelp(): void {
54
61
  $ \x1b[32mcursorflow prepare NewFeature --lanes 3\x1b[0m
55
62
  $ \x1b[32mcursorflow run _cursorflow/tasks/MyFeature/\x1b[0m
56
63
  $ \x1b[32mcursorflow monitor latest\x1b[0m
64
+ $ \x1b[32mcursorflow logs --all --follow\x1b[0m
65
+ $ \x1b[32mcursorflow runs --running\x1b[0m
66
+ $ \x1b[32mcursorflow resume --all\x1b[0m
67
+ $ \x1b[32mcursorflow doctor\x1b[0m
57
68
  $ \x1b[32mcursorflow models\x1b[0m
58
69
 
59
70
  \x1b[1mDOCUMENTATION\x1b[0m
package/src/cli/init.ts CHANGED
@@ -9,6 +9,7 @@ import * as path from 'path';
9
9
  import * as logger from '../utils/logger';
10
10
  import { findProjectRoot, createDefaultConfig, CursorFlowConfig } from '../utils/config';
11
11
  import { setupCommands } from './setup-commands';
12
+ import { safeJoin } from '../utils/path';
12
13
 
13
14
  interface InitOptions {
14
15
  example: boolean;
@@ -84,8 +85,8 @@ Examples:
84
85
  }
85
86
 
86
87
  function createDirectories(projectRoot: string, config: CursorFlowConfig): void {
87
- const tasksDir = path.join(projectRoot, config.tasksDir);
88
- const logsDir = path.join(projectRoot, config.logsDir);
88
+ const tasksDir = safeJoin(projectRoot, config.tasksDir);
89
+ const logsDir = safeJoin(projectRoot, config.logsDir);
89
90
 
90
91
  if (!fs.existsSync(tasksDir)) {
91
92
  fs.mkdirSync(tasksDir, { recursive: true });
@@ -103,7 +104,7 @@ function createDirectories(projectRoot: string, config: CursorFlowConfig): void
103
104
  }
104
105
 
105
106
  function createExampleTasks(projectRoot: string, config: CursorFlowConfig): void {
106
- const exampleDir = path.join(projectRoot, config.tasksDir, 'example');
107
+ const exampleDir = safeJoin(projectRoot, config.tasksDir, 'example');
107
108
 
108
109
  if (!fs.existsSync(exampleDir)) {
109
110
  fs.mkdirSync(exampleDir, { recursive: true });
@@ -111,7 +112,7 @@ function createExampleTasks(projectRoot: string, config: CursorFlowConfig): void
111
112
 
112
113
  const exampleTask = {
113
114
  repository: "https://github.com/your-org/your-repo",
114
- baseBranch: "main",
115
+ // baseBranch is auto-detected from current branch at runtime
115
116
  branchPrefix: "cursorflow/example-",
116
117
  executor: "cursor-agent",
117
118
  autoCreatePr: false,
@@ -134,13 +135,13 @@ Create a simple hello.txt file with a greeting message.
134
135
  ]
135
136
  };
136
137
 
137
- const taskPath = path.join(exampleDir, '01-hello.json');
138
+ const taskPath = safeJoin(exampleDir, '01-hello.json');
138
139
  fs.writeFileSync(taskPath, JSON.stringify(exampleTask, null, 2) + '\n', 'utf8');
139
140
 
140
141
  logger.success(`Created example task: ${path.relative(projectRoot, taskPath)}`);
141
142
 
142
143
  // Create README
143
- const readmePath = path.join(exampleDir, 'README.md');
144
+ const readmePath = safeJoin(exampleDir, 'README.md');
144
145
  const readme = `# Example Task
145
146
 
146
147
  This is an example CursorFlow task to help you get started.
@@ -172,7 +173,7 @@ cursorflow run ${config.tasksDir}/example/
172
173
  * Add _cursorflow to .gitignore
173
174
  */
174
175
  function updateGitignore(projectRoot: string): void {
175
- const gitignorePath = path.join(projectRoot, '.gitignore');
176
+ const gitignorePath = safeJoin(projectRoot, '.gitignore');
176
177
  const entry = '_cursorflow/';
177
178
 
178
179
  // Try to read existing .gitignore (avoid TOCTOU by reading directly)