@litmers/cursorflow-orchestrator 0.1.31 → 0.1.36
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 +27 -0
- package/README.md +182 -59
- package/commands/cursorflow-add.md +159 -0
- package/commands/cursorflow-doctor.md +45 -23
- package/commands/cursorflow-monitor.md +23 -2
- package/commands/cursorflow-new.md +87 -0
- package/commands/cursorflow-run.md +60 -111
- package/dist/cli/add.d.ts +7 -0
- package/dist/cli/add.js +377 -0
- package/dist/cli/add.js.map +1 -0
- package/dist/cli/clean.js +1 -0
- package/dist/cli/clean.js.map +1 -1
- package/dist/cli/config.d.ts +7 -0
- package/dist/cli/config.js +181 -0
- package/dist/cli/config.js.map +1 -0
- package/dist/cli/doctor.js +47 -4
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/index.js +34 -30
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/logs.js +17 -34
- package/dist/cli/logs.js.map +1 -1
- package/dist/cli/monitor.js +62 -65
- package/dist/cli/monitor.js.map +1 -1
- package/dist/cli/new.d.ts +7 -0
- package/dist/cli/new.js +232 -0
- package/dist/cli/new.js.map +1 -0
- package/dist/cli/prepare.js +95 -193
- package/dist/cli/prepare.js.map +1 -1
- package/dist/cli/resume.js +57 -68
- package/dist/cli/resume.js.map +1 -1
- package/dist/cli/run.js +60 -30
- package/dist/cli/run.js.map +1 -1
- package/dist/cli/stop.js +6 -0
- package/dist/cli/stop.js.map +1 -1
- package/dist/cli/tasks.d.ts +5 -3
- package/dist/cli/tasks.js +181 -29
- package/dist/cli/tasks.js.map +1 -1
- package/dist/core/failure-policy.d.ts +9 -0
- package/dist/core/failure-policy.js +9 -0
- package/dist/core/failure-policy.js.map +1 -1
- package/dist/core/orchestrator.d.ts +20 -6
- package/dist/core/orchestrator.js +215 -334
- package/dist/core/orchestrator.js.map +1 -1
- package/dist/core/runner/agent.d.ts +27 -0
- package/dist/core/runner/agent.js +294 -0
- package/dist/core/runner/agent.js.map +1 -0
- package/dist/core/runner/index.d.ts +5 -0
- package/dist/core/runner/index.js +22 -0
- package/dist/core/runner/index.js.map +1 -0
- package/dist/core/runner/pipeline.d.ts +9 -0
- package/dist/core/runner/pipeline.js +539 -0
- package/dist/core/runner/pipeline.js.map +1 -0
- package/dist/core/runner/prompt.d.ts +25 -0
- package/dist/core/runner/prompt.js +175 -0
- package/dist/core/runner/prompt.js.map +1 -0
- package/dist/core/runner/task.d.ts +26 -0
- package/dist/core/runner/task.js +283 -0
- package/dist/core/runner/task.js.map +1 -0
- package/dist/core/runner/utils.d.ts +37 -0
- package/dist/core/runner/utils.js +161 -0
- package/dist/core/runner/utils.js.map +1 -0
- package/dist/core/runner.d.ts +2 -96
- package/dist/core/runner.js +11 -1136
- package/dist/core/runner.js.map +1 -1
- package/dist/core/stall-detection.d.ts +326 -0
- package/dist/core/stall-detection.js +781 -0
- package/dist/core/stall-detection.js.map +1 -0
- package/dist/services/logging/console.js +2 -1
- package/dist/services/logging/console.js.map +1 -1
- package/dist/types/config.d.ts +6 -6
- package/dist/types/flow.d.ts +84 -0
- package/dist/types/flow.js +10 -0
- package/dist/types/flow.js.map +1 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.js +3 -3
- package/dist/types/index.js.map +1 -1
- package/dist/types/lane.d.ts +0 -2
- package/dist/types/logging.d.ts +5 -1
- package/dist/types/task.d.ts +7 -11
- package/dist/utils/config.d.ts +5 -1
- package/dist/utils/config.js +15 -16
- package/dist/utils/config.js.map +1 -1
- package/dist/utils/dependency.d.ts +36 -1
- package/dist/utils/dependency.js +256 -1
- package/dist/utils/dependency.js.map +1 -1
- package/dist/utils/doctor.js +40 -8
- package/dist/utils/doctor.js.map +1 -1
- package/dist/utils/enhanced-logger.d.ts +45 -82
- package/dist/utils/enhanced-logger.js +239 -844
- package/dist/utils/enhanced-logger.js.map +1 -1
- package/dist/utils/flow.d.ts +9 -0
- package/dist/utils/flow.js +73 -0
- package/dist/utils/flow.js.map +1 -0
- package/dist/utils/git.d.ts +29 -0
- package/dist/utils/git.js +115 -5
- package/dist/utils/git.js.map +1 -1
- package/dist/utils/state.js +0 -2
- package/dist/utils/state.js.map +1 -1
- package/dist/utils/task-service.d.ts +2 -2
- package/dist/utils/task-service.js +40 -31
- package/dist/utils/task-service.js.map +1 -1
- package/package.json +4 -3
- package/src/cli/add.ts +397 -0
- package/src/cli/clean.ts +1 -0
- package/src/cli/config.ts +177 -0
- package/src/cli/doctor.ts +48 -4
- package/src/cli/index.ts +36 -32
- package/src/cli/logs.ts +20 -33
- package/src/cli/monitor.ts +70 -75
- package/src/cli/new.ts +235 -0
- package/src/cli/prepare.ts +98 -205
- package/src/cli/resume.ts +61 -76
- package/src/cli/run.ts +333 -306
- package/src/cli/stop.ts +8 -0
- package/src/cli/tasks.ts +200 -21
- package/src/core/failure-policy.ts +9 -0
- package/src/core/orchestrator.ts +279 -379
- package/src/core/runner/agent.ts +314 -0
- package/src/core/runner/index.ts +6 -0
- package/src/core/runner/pipeline.ts +567 -0
- package/src/core/runner/prompt.ts +174 -0
- package/src/core/runner/task.ts +320 -0
- package/src/core/runner/utils.ts +142 -0
- package/src/core/runner.ts +8 -1347
- package/src/core/stall-detection.ts +936 -0
- package/src/services/logging/console.ts +2 -1
- package/src/types/config.ts +6 -6
- package/src/types/flow.ts +91 -0
- package/src/types/index.ts +15 -3
- package/src/types/lane.ts +0 -2
- package/src/types/logging.ts +5 -1
- package/src/types/task.ts +7 -11
- package/src/utils/config.ts +16 -17
- package/src/utils/dependency.ts +311 -2
- package/src/utils/doctor.ts +36 -8
- package/src/utils/enhanced-logger.ts +264 -927
- package/src/utils/flow.ts +42 -0
- package/src/utils/git.ts +145 -5
- package/src/utils/state.ts +0 -2
- package/src/utils/task-service.ts +48 -40
- package/commands/cursorflow-review.md +0 -56
- package/commands/cursorflow-runs.md +0 -59
- package/dist/cli/runs.d.ts +0 -5
- package/dist/cli/runs.js +0 -214
- package/dist/cli/runs.js.map +0 -1
- package/dist/core/reviewer.d.ts +0 -66
- package/dist/core/reviewer.js +0 -265
- package/dist/core/reviewer.js.map +0 -1
- package/src/cli/runs.ts +0 -212
- package/src/core/reviewer.ts +0 -285
package/src/cli/run.ts
CHANGED
|
@@ -1,306 +1,333 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CursorFlow run command
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import * as path from 'path';
|
|
6
|
-
import * as fs from 'fs';
|
|
7
|
-
import * as logger from '../utils/logger';
|
|
8
|
-
import { orchestrate } from '../core/orchestrator';
|
|
9
|
-
import { getLogsDir, loadConfig } from '../utils/config';
|
|
10
|
-
import { runDoctor, getDoctorStatus } from '../utils/doctor';
|
|
11
|
-
import { areCommandsInstalled, setupCommands } from './setup-commands';
|
|
12
|
-
import { safeJoin } from '../utils/path';
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
.
|
|
42
|
-
.
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
if
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
--
|
|
138
|
-
--
|
|
139
|
-
--
|
|
140
|
-
--
|
|
141
|
-
--
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
const
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
//
|
|
207
|
-
|
|
208
|
-
if (
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
const
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
1
|
+
/**
|
|
2
|
+
* CursorFlow run command
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import * as path from 'path';
|
|
6
|
+
import * as fs from 'fs';
|
|
7
|
+
import * as logger from '../utils/logger';
|
|
8
|
+
import { orchestrate } from '../core/orchestrator';
|
|
9
|
+
import { getLogsDir, loadConfig } from '../utils/config';
|
|
10
|
+
import { runDoctor, getDoctorStatus } from '../utils/doctor';
|
|
11
|
+
import { areCommandsInstalled, setupCommands } from './setup-commands';
|
|
12
|
+
import { safeJoin } from '../utils/path';
|
|
13
|
+
import { findFlowDir } from '../utils/flow';
|
|
14
|
+
import { loadState } from '../utils/state';
|
|
15
|
+
import { LaneState } from '../types';
|
|
16
|
+
|
|
17
|
+
interface IncompleteLaneInfo {
|
|
18
|
+
name: string;
|
|
19
|
+
status: string;
|
|
20
|
+
taskIndex: number;
|
|
21
|
+
totalTasks: number;
|
|
22
|
+
error?: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
interface ExistingRunInfo {
|
|
26
|
+
runDir: string;
|
|
27
|
+
runId: string;
|
|
28
|
+
incompleteLanes: IncompleteLaneInfo[];
|
|
29
|
+
completedLanes: string[];
|
|
30
|
+
totalLanes: number;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Find existing run for a tasks directory
|
|
35
|
+
*/
|
|
36
|
+
function findExistingRunForTasks(logsDir: string, tasksDir: string): ExistingRunInfo | null {
|
|
37
|
+
const runsDir = safeJoin(logsDir, 'runs');
|
|
38
|
+
if (!fs.existsSync(runsDir)) return null;
|
|
39
|
+
|
|
40
|
+
const runs = fs.readdirSync(runsDir)
|
|
41
|
+
.filter(d => d.startsWith('run-'))
|
|
42
|
+
.sort()
|
|
43
|
+
.reverse(); // Latest first
|
|
44
|
+
|
|
45
|
+
for (const runId of runs) {
|
|
46
|
+
const runDir = safeJoin(runsDir, runId);
|
|
47
|
+
const lanesDir = safeJoin(runDir, 'lanes');
|
|
48
|
+
|
|
49
|
+
if (!fs.existsSync(lanesDir)) continue;
|
|
50
|
+
|
|
51
|
+
const laneDirs = fs.readdirSync(lanesDir)
|
|
52
|
+
.filter(f => fs.statSync(safeJoin(lanesDir, f)).isDirectory());
|
|
53
|
+
|
|
54
|
+
if (laneDirs.length === 0) continue;
|
|
55
|
+
|
|
56
|
+
// Check if any lane belongs to this tasks directory
|
|
57
|
+
let matchesTasksDir = false;
|
|
58
|
+
const incompleteLanes: IncompleteLaneInfo[] = [];
|
|
59
|
+
const completedLanes: string[] = [];
|
|
60
|
+
|
|
61
|
+
for (const laneName of laneDirs) {
|
|
62
|
+
const statePath = safeJoin(lanesDir, laneName, 'state.json');
|
|
63
|
+
if (!fs.existsSync(statePath)) continue;
|
|
64
|
+
|
|
65
|
+
const state = loadState<LaneState>(statePath);
|
|
66
|
+
if (!state) continue;
|
|
67
|
+
|
|
68
|
+
// Check if this lane's tasks file is in the target tasks directory
|
|
69
|
+
if (state.tasksFile) {
|
|
70
|
+
const taskFileDir = path.dirname(state.tasksFile);
|
|
71
|
+
if (path.resolve(taskFileDir) === path.resolve(tasksDir)) {
|
|
72
|
+
matchesTasksDir = true;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Check completion status
|
|
77
|
+
if (state.status === 'completed') {
|
|
78
|
+
completedLanes.push(laneName);
|
|
79
|
+
} else {
|
|
80
|
+
// Check if process is alive (zombie detection)
|
|
81
|
+
let isZombie = false;
|
|
82
|
+
if (state.status === 'running' && state.pid) {
|
|
83
|
+
try {
|
|
84
|
+
process.kill(state.pid, 0);
|
|
85
|
+
} catch {
|
|
86
|
+
isZombie = true;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
incompleteLanes.push({
|
|
91
|
+
name: laneName,
|
|
92
|
+
status: isZombie ? 'zombie' : state.status,
|
|
93
|
+
taskIndex: state.currentTaskIndex,
|
|
94
|
+
totalTasks: state.totalTasks,
|
|
95
|
+
error: state.error || undefined,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (matchesTasksDir && incompleteLanes.length > 0) {
|
|
101
|
+
return {
|
|
102
|
+
runDir,
|
|
103
|
+
runId,
|
|
104
|
+
incompleteLanes,
|
|
105
|
+
completedLanes,
|
|
106
|
+
totalLanes: laneDirs.length,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
interface RunOptions {
|
|
115
|
+
tasksDir?: string;
|
|
116
|
+
dryRun: boolean;
|
|
117
|
+
executor: string | null;
|
|
118
|
+
maxConcurrent: number | null;
|
|
119
|
+
skipDoctor: boolean;
|
|
120
|
+
skipPreflight: boolean;
|
|
121
|
+
noGit: boolean;
|
|
122
|
+
raw: boolean;
|
|
123
|
+
help: boolean;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function printHelp(): void {
|
|
127
|
+
console.log(`
|
|
128
|
+
Usage: cursorflow run <tasks-dir> [options]
|
|
129
|
+
|
|
130
|
+
Run task orchestration based on dependency graph.
|
|
131
|
+
|
|
132
|
+
If an existing run with incomplete lanes is found for the same tasks directory,
|
|
133
|
+
it will automatically resume instead of starting a new run.
|
|
134
|
+
|
|
135
|
+
Options:
|
|
136
|
+
<tasks-dir> Directory containing task JSON files
|
|
137
|
+
--max-concurrent <num> Limit parallel agents (overrides config)
|
|
138
|
+
--executor <type> cursor-agent | cloud
|
|
139
|
+
--skip-doctor Skip environment checks (not recommended)
|
|
140
|
+
--skip-preflight Skip preflight checks (Git remote, etc.)
|
|
141
|
+
--no-git Disable Git operations (worktree, push, commit)
|
|
142
|
+
--raw Save raw logs (absolute raw, no processing)
|
|
143
|
+
--dry-run Show execution plan without starting agents
|
|
144
|
+
--help, -h Show help
|
|
145
|
+
|
|
146
|
+
Examples:
|
|
147
|
+
cursorflow run _cursorflow/tasks
|
|
148
|
+
cursorflow run _cursorflow/tasks --no-git --skip-doctor
|
|
149
|
+
`);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function parseArgs(args: string[]): RunOptions {
|
|
153
|
+
const tasksDir = args.find(a => !a.startsWith('--'));
|
|
154
|
+
const executorIdx = args.indexOf('--executor');
|
|
155
|
+
const maxConcurrentIdx = args.indexOf('--max-concurrent');
|
|
156
|
+
|
|
157
|
+
return {
|
|
158
|
+
tasksDir,
|
|
159
|
+
dryRun: args.includes('--dry-run'),
|
|
160
|
+
executor: executorIdx >= 0 ? args[executorIdx + 1] || null : null,
|
|
161
|
+
maxConcurrent: maxConcurrentIdx >= 0 ? parseInt(args[maxConcurrentIdx + 1] || '0') || null : null,
|
|
162
|
+
skipDoctor: args.includes('--skip-doctor') || args.includes('--no-doctor'),
|
|
163
|
+
skipPreflight: args.includes('--skip-preflight'),
|
|
164
|
+
noGit: args.includes('--no-git'),
|
|
165
|
+
raw: args.includes('--raw'),
|
|
166
|
+
help: args.includes('--help') || args.includes('-h'),
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
async function run(args: string[]): Promise<void> {
|
|
171
|
+
const options = parseArgs(args);
|
|
172
|
+
|
|
173
|
+
if (options.help) {
|
|
174
|
+
printHelp();
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Auto-setup Cursor commands if missing or outdated
|
|
179
|
+
if (!areCommandsInstalled()) {
|
|
180
|
+
logger.info('Installing missing or outdated Cursor IDE commands...');
|
|
181
|
+
try {
|
|
182
|
+
setupCommands({ silent: true });
|
|
183
|
+
} catch (e) {
|
|
184
|
+
// Non-blocking
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (!options.tasksDir) {
|
|
189
|
+
console.log('\nUsage: cursorflow run <tasks-dir> [options]');
|
|
190
|
+
throw new Error('Tasks directory required');
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const config = loadConfig();
|
|
194
|
+
const logsDir = getLogsDir(config);
|
|
195
|
+
const originalCwd = process.cwd();
|
|
196
|
+
|
|
197
|
+
// Change current directory to project root for consistent path handling
|
|
198
|
+
if (config.projectRoot !== originalCwd) {
|
|
199
|
+
logger.debug(`Changing directory to project root: ${config.projectRoot}`);
|
|
200
|
+
process.chdir(config.projectRoot);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Resolve tasks dir:
|
|
204
|
+
// 1. Prefer the exact path if it exists relative to original cwd
|
|
205
|
+
// 2. Search in flowsDir by name
|
|
206
|
+
// 3. Fall back to projectRoot-relative tasksDir for backward compatibility
|
|
207
|
+
let tasksDir = '';
|
|
208
|
+
if (path.isAbsolute(options.tasksDir)) {
|
|
209
|
+
tasksDir = options.tasksDir;
|
|
210
|
+
} else {
|
|
211
|
+
const relPath = path.resolve(originalCwd, options.tasksDir);
|
|
212
|
+
if (fs.existsSync(relPath)) {
|
|
213
|
+
tasksDir = relPath;
|
|
214
|
+
} else {
|
|
215
|
+
// Try finding in flowsDir
|
|
216
|
+
const flowsDir = safeJoin(config.projectRoot, config.flowsDir);
|
|
217
|
+
const foundFlow = findFlowDir(flowsDir, options.tasksDir);
|
|
218
|
+
if (foundFlow) {
|
|
219
|
+
tasksDir = foundFlow;
|
|
220
|
+
} else {
|
|
221
|
+
// Fallback to legacy tasksDir
|
|
222
|
+
tasksDir = safeJoin(config.projectRoot, options.tasksDir);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (!fs.existsSync(tasksDir)) {
|
|
228
|
+
throw new Error(`Tasks or Flow directory not found: ${options.tasksDir} (resolved to: ${tasksDir})`);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Check for existing incomplete run and auto-resume
|
|
232
|
+
const existingRun = findExistingRunForTasks(logsDir, tasksDir);
|
|
233
|
+
if (existingRun && existingRun.incompleteLanes.length > 0) {
|
|
234
|
+
logger.section('📋 Existing Run Detected');
|
|
235
|
+
logger.info(`Run: ${existingRun.runId}`);
|
|
236
|
+
logger.info(`Completed: ${existingRun.completedLanes.length}/${existingRun.totalLanes} lanes`);
|
|
237
|
+
|
|
238
|
+
console.log('');
|
|
239
|
+
logger.info('Incomplete lanes:');
|
|
240
|
+
for (const lane of existingRun.incompleteLanes) {
|
|
241
|
+
const statusEmoji = lane.status === 'failed' ? '❌' :
|
|
242
|
+
lane.status === 'zombie' ? '🧟' :
|
|
243
|
+
lane.status === 'running' ? '🔄' : '⏸';
|
|
244
|
+
logger.info(` ${statusEmoji} ${lane.name}: ${lane.status} (${lane.taskIndex}/${lane.totalTasks})`);
|
|
245
|
+
if (lane.error) {
|
|
246
|
+
logger.warn(` └─ ${lane.error.substring(0, 60)}${lane.error.length > 60 ? '...' : ''}`);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
console.log('');
|
|
251
|
+
logger.info('🔄 Auto-resuming from existing run...');
|
|
252
|
+
console.log('');
|
|
253
|
+
|
|
254
|
+
// Call the resume command with --all flag
|
|
255
|
+
const resumeCmd = require('./resume');
|
|
256
|
+
const resumeArgs = [
|
|
257
|
+
'--all',
|
|
258
|
+
'--run-dir', existingRun.runDir,
|
|
259
|
+
];
|
|
260
|
+
|
|
261
|
+
if (options.skipDoctor) resumeArgs.push('--skip-doctor');
|
|
262
|
+
if (options.skipPreflight) resumeArgs.push('--skip-preflight');
|
|
263
|
+
if (options.noGit) resumeArgs.push('--no-git');
|
|
264
|
+
if (options.executor) {
|
|
265
|
+
resumeArgs.push('--executor', options.executor);
|
|
266
|
+
}
|
|
267
|
+
if (options.maxConcurrent) {
|
|
268
|
+
resumeArgs.push('--max-concurrent', String(options.maxConcurrent));
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
await resumeCmd(resumeArgs);
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Check if doctor has been run at least once
|
|
276
|
+
const doctorStatus = getDoctorStatus(config.projectRoot);
|
|
277
|
+
if (!doctorStatus) {
|
|
278
|
+
logger.warn('It looks like you haven\'t run `cursorflow doctor` yet.');
|
|
279
|
+
logger.warn('Running doctor is highly recommended to catch environment issues early.');
|
|
280
|
+
console.log(' Run: cursorflow doctor\n');
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Preflight checks (doctor)
|
|
284
|
+
if (!options.skipDoctor && !options.skipPreflight) {
|
|
285
|
+
const report = runDoctor({
|
|
286
|
+
cwd: process.cwd(),
|
|
287
|
+
tasksDir,
|
|
288
|
+
executor: options.executor || config.executor,
|
|
289
|
+
includeCursorAgentChecks: true,
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
if (!report.ok) {
|
|
293
|
+
logger.section('🛑 Pre-flight check failed');
|
|
294
|
+
for (const issue of report.issues) {
|
|
295
|
+
const header = `${issue.title} (${issue.id})`;
|
|
296
|
+
if (issue.severity === 'error') {
|
|
297
|
+
logger.error(header, { emoji: '❌' });
|
|
298
|
+
} else {
|
|
299
|
+
logger.warn(header, { emoji: '⚠️' });
|
|
300
|
+
}
|
|
301
|
+
console.log(` ${issue.message}`);
|
|
302
|
+
if (issue.details) console.log(` Details: ${issue.details}`);
|
|
303
|
+
if (issue.fixes?.length) {
|
|
304
|
+
console.log(' Fix:');
|
|
305
|
+
for (const fix of issue.fixes) console.log(` - ${fix}`);
|
|
306
|
+
}
|
|
307
|
+
console.log('');
|
|
308
|
+
}
|
|
309
|
+
throw new Error('Pre-flight checks failed. Run `cursorflow doctor` for details.');
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
try {
|
|
314
|
+
await orchestrate(tasksDir, {
|
|
315
|
+
executor: options.executor || config.executor,
|
|
316
|
+
pollInterval: config.pollInterval * 1000,
|
|
317
|
+
runDir: path.join(logsDir, 'runs', `run-${Date.now()}`),
|
|
318
|
+
maxConcurrentLanes: options.maxConcurrent || config.maxConcurrentLanes,
|
|
319
|
+
webhooks: config.webhooks || [],
|
|
320
|
+
enhancedLogging: {
|
|
321
|
+
...config.enhancedLogging,
|
|
322
|
+
...(options.raw ? { raw: true } : {}),
|
|
323
|
+
},
|
|
324
|
+
noGit: options.noGit,
|
|
325
|
+
skipPreflight: options.skipPreflight,
|
|
326
|
+
});
|
|
327
|
+
} catch (error: any) {
|
|
328
|
+
// Re-throw to be handled by the main entry point
|
|
329
|
+
throw new Error(`Orchestration failed: ${error.message}`);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
export = run;
|