@litmers/cursorflow-orchestrator 0.1.5 → 0.1.8
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 +15 -6
- package/README.md +33 -2
- package/commands/cursorflow-doctor.md +24 -0
- package/commands/cursorflow-signal.md +19 -0
- package/dist/cli/doctor.d.ts +15 -0
- package/dist/cli/doctor.js +139 -0
- package/dist/cli/doctor.js.map +1 -0
- package/dist/cli/index.js +5 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/monitor.d.ts +1 -1
- package/dist/cli/monitor.js +640 -145
- package/dist/cli/monitor.js.map +1 -1
- package/dist/cli/resume.d.ts +1 -1
- package/dist/cli/resume.js +80 -10
- package/dist/cli/resume.js.map +1 -1
- package/dist/cli/run.js +60 -5
- package/dist/cli/run.js.map +1 -1
- package/dist/cli/setup-commands.d.ts +4 -0
- package/dist/cli/setup-commands.js +16 -0
- package/dist/cli/setup-commands.js.map +1 -1
- package/dist/cli/signal.d.ts +7 -0
- package/dist/cli/signal.js +99 -0
- package/dist/cli/signal.js.map +1 -0
- package/dist/core/orchestrator.d.ts +4 -2
- package/dist/core/orchestrator.js +92 -23
- package/dist/core/orchestrator.js.map +1 -1
- package/dist/core/runner.d.ts +9 -3
- package/dist/core/runner.js +182 -88
- package/dist/core/runner.js.map +1 -1
- package/dist/utils/doctor.d.ts +63 -0
- package/dist/utils/doctor.js +280 -0
- package/dist/utils/doctor.js.map +1 -0
- package/dist/utils/types.d.ts +3 -0
- package/package.json +1 -1
- package/src/cli/doctor.ts +127 -0
- package/src/cli/index.ts +5 -0
- package/src/cli/monitor.ts +693 -185
- package/src/cli/resume.ts +94 -12
- package/src/cli/run.ts +63 -7
- package/src/cli/setup-commands.ts +19 -0
- package/src/cli/signal.ts +89 -0
- package/src/core/orchestrator.ts +102 -27
- package/src/core/runner.ts +203 -99
- package/src/utils/doctor.ts +312 -0
- package/src/utils/types.ts +3 -0
|
@@ -90,7 +90,7 @@ function waitChild(proc) {
|
|
|
90
90
|
});
|
|
91
91
|
}
|
|
92
92
|
/**
|
|
93
|
-
* List lane task files in directory
|
|
93
|
+
* List lane task files in directory and load their configs for dependencies
|
|
94
94
|
*/
|
|
95
95
|
function listLaneFiles(tasksDir) {
|
|
96
96
|
if (!fs.existsSync(tasksDir)) {
|
|
@@ -100,10 +100,23 @@ function listLaneFiles(tasksDir) {
|
|
|
100
100
|
return files
|
|
101
101
|
.filter(f => f.endsWith('.json'))
|
|
102
102
|
.sort()
|
|
103
|
-
.map(f =>
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
103
|
+
.map(f => {
|
|
104
|
+
const filePath = path.join(tasksDir, f);
|
|
105
|
+
const name = path.basename(f, '.json');
|
|
106
|
+
let dependsOn = [];
|
|
107
|
+
try {
|
|
108
|
+
const config = JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
109
|
+
dependsOn = config.dependsOn || [];
|
|
110
|
+
}
|
|
111
|
+
catch (e) {
|
|
112
|
+
logger.warn(`Failed to parse config for lane ${name}: ${e}`);
|
|
113
|
+
}
|
|
114
|
+
return {
|
|
115
|
+
name,
|
|
116
|
+
path: filePath,
|
|
117
|
+
dependsOn,
|
|
118
|
+
};
|
|
119
|
+
});
|
|
107
120
|
}
|
|
108
121
|
/**
|
|
109
122
|
* Monitor lane states
|
|
@@ -116,7 +129,8 @@ function printLaneStatus(lanes, laneRunDirs) {
|
|
|
116
129
|
const statePath = path.join(dir, 'state.json');
|
|
117
130
|
const state = (0, state_1.loadState)(statePath);
|
|
118
131
|
if (!state) {
|
|
119
|
-
|
|
132
|
+
const isWaiting = lane.dependsOn.length > 0;
|
|
133
|
+
return { lane: lane.name, status: isWaiting ? 'waiting' : 'pending', task: '-' };
|
|
120
134
|
}
|
|
121
135
|
const idx = (state.currentTaskIndex || 0) + 1;
|
|
122
136
|
return {
|
|
@@ -131,7 +145,7 @@ function printLaneStatus(lanes, laneRunDirs) {
|
|
|
131
145
|
}
|
|
132
146
|
}
|
|
133
147
|
/**
|
|
134
|
-
* Run orchestration
|
|
148
|
+
* Run orchestration with dependency management
|
|
135
149
|
*/
|
|
136
150
|
async function orchestrate(tasksDir, options = {}) {
|
|
137
151
|
const lanes = listLaneFiles(tasksDir);
|
|
@@ -143,31 +157,86 @@ async function orchestrate(tasksDir, options = {}) {
|
|
|
143
157
|
const laneRunDirs = {};
|
|
144
158
|
for (const lane of lanes) {
|
|
145
159
|
laneRunDirs[lane.name] = path.join(runRoot, 'lanes', lane.name);
|
|
160
|
+
fs.mkdirSync(laneRunDirs[lane.name], { recursive: true });
|
|
146
161
|
}
|
|
147
162
|
logger.section('🧭 Starting Orchestration');
|
|
148
163
|
logger.info(`Tasks directory: ${tasksDir}`);
|
|
149
164
|
logger.info(`Run directory: ${runRoot}`);
|
|
150
165
|
logger.info(`Lanes: ${lanes.length}`);
|
|
151
|
-
|
|
152
|
-
const running =
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
tasksFile: lane.path,
|
|
157
|
-
laneRunDir: laneRunDirs[lane.name],
|
|
158
|
-
executor: options.executor || 'cursor-agent',
|
|
159
|
-
});
|
|
160
|
-
running.push({ lane: lane.name, child, logPath });
|
|
161
|
-
logger.info(`Lane started: ${lane.name}`);
|
|
162
|
-
}
|
|
166
|
+
const maxConcurrent = options.maxConcurrentLanes || 10;
|
|
167
|
+
const running = new Map();
|
|
168
|
+
const exitCodes = {};
|
|
169
|
+
const completedLanes = new Set();
|
|
170
|
+
const failedLanes = new Set();
|
|
163
171
|
// Monitor lanes
|
|
164
172
|
const monitorInterval = setInterval(() => {
|
|
165
173
|
printLaneStatus(lanes, laneRunDirs);
|
|
166
174
|
}, options.pollInterval || 60000);
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
175
|
+
while (completedLanes.size + failedLanes.size < lanes.length) {
|
|
176
|
+
// 1. Identify lanes ready to start
|
|
177
|
+
const readyToStart = lanes.filter(lane => {
|
|
178
|
+
// Not already running or completed
|
|
179
|
+
if (running.has(lane.name) || completedLanes.has(lane.name) || failedLanes.has(lane.name)) {
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
|
+
// Check dependencies
|
|
183
|
+
for (const dep of lane.dependsOn) {
|
|
184
|
+
if (failedLanes.has(dep)) {
|
|
185
|
+
// If a dependency failed, this lane fails too
|
|
186
|
+
logger.error(`Lane ${lane.name} failed because dependency ${dep} failed`);
|
|
187
|
+
failedLanes.add(lane.name);
|
|
188
|
+
exitCodes[lane.name] = 1;
|
|
189
|
+
return false;
|
|
190
|
+
}
|
|
191
|
+
if (!completedLanes.has(dep)) {
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return true;
|
|
196
|
+
});
|
|
197
|
+
// 2. Spawn ready lanes up to maxConcurrent
|
|
198
|
+
for (const lane of readyToStart) {
|
|
199
|
+
if (running.size >= maxConcurrent)
|
|
200
|
+
break;
|
|
201
|
+
logger.info(`Lane started: ${lane.name}`);
|
|
202
|
+
const spawnResult = spawnLane({
|
|
203
|
+
laneName: lane.name,
|
|
204
|
+
tasksFile: lane.path,
|
|
205
|
+
laneRunDir: laneRunDirs[lane.name],
|
|
206
|
+
executor: options.executor || 'cursor-agent',
|
|
207
|
+
});
|
|
208
|
+
running.set(lane.name, spawnResult);
|
|
209
|
+
}
|
|
210
|
+
// 3. Wait for any running lane to finish
|
|
211
|
+
if (running.size > 0) {
|
|
212
|
+
// We need to wait for at least one to finish
|
|
213
|
+
const promises = Array.from(running.entries()).map(async ([name, { child }]) => {
|
|
214
|
+
const code = await waitChild(child);
|
|
215
|
+
return { name, code };
|
|
216
|
+
});
|
|
217
|
+
const finished = await Promise.race(promises);
|
|
218
|
+
running.delete(finished.name);
|
|
219
|
+
exitCodes[finished.name] = finished.code;
|
|
220
|
+
if (finished.code === 0 || finished.code === 2) {
|
|
221
|
+
completedLanes.add(finished.name);
|
|
222
|
+
}
|
|
223
|
+
else {
|
|
224
|
+
failedLanes.add(finished.name);
|
|
225
|
+
}
|
|
226
|
+
printLaneStatus(lanes, laneRunDirs);
|
|
227
|
+
}
|
|
228
|
+
else {
|
|
229
|
+
// Nothing running and nothing ready (but not all finished)
|
|
230
|
+
// This could happen if there's a circular dependency or some logic error
|
|
231
|
+
if (readyToStart.length === 0 && completedLanes.size + failedLanes.size < lanes.length) {
|
|
232
|
+
const remaining = lanes.filter(l => !completedLanes.has(l.name) && !failedLanes.has(l.name));
|
|
233
|
+
logger.error(`Deadlock detected! Remaining lanes cannot start: ${remaining.map(l => l.name).join(', ')}`);
|
|
234
|
+
for (const l of remaining) {
|
|
235
|
+
failedLanes.add(l.name);
|
|
236
|
+
exitCodes[l.name] = 1;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
171
240
|
}
|
|
172
241
|
clearInterval(monitorInterval);
|
|
173
242
|
printLaneStatus(lanes, laneRunDirs);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"orchestrator.js","sourceRoot":"","sources":["../../src/core/orchestrator.ts"],"names":[],"mappings":";AAAA;;;;GAIG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"orchestrator.js","sourceRoot":"","sources":["../../src/core/orchestrator.ts"],"names":[],"mappings":"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"}
|
package/dist/core/runner.d.ts
CHANGED
|
@@ -8,12 +8,16 @@ import { RunnerConfig, Task, TaskExecutionResult, AgentSendResult, DependencyPol
|
|
|
8
8
|
* Execute cursor-agent command with timeout and better error handling
|
|
9
9
|
*/
|
|
10
10
|
export declare function cursorAgentCreateChat(): string;
|
|
11
|
-
|
|
11
|
+
/**
|
|
12
|
+
* Execute cursor-agent command with streaming and better error handling
|
|
13
|
+
*/
|
|
14
|
+
export declare function cursorAgentSend({ workspaceDir, chatId, prompt, model, signalDir }: {
|
|
12
15
|
workspaceDir: string;
|
|
13
16
|
chatId: string;
|
|
14
17
|
prompt: string;
|
|
15
18
|
model?: string;
|
|
16
|
-
|
|
19
|
+
signalDir?: string;
|
|
20
|
+
}): Promise<AgentSendResult>;
|
|
17
21
|
/**
|
|
18
22
|
* Extract dependency change request from agent response
|
|
19
23
|
*/
|
|
@@ -46,4 +50,6 @@ export declare function runTask({ task, config, index, worktreeDir, taskBranch,
|
|
|
46
50
|
/**
|
|
47
51
|
* Run all tasks in sequence
|
|
48
52
|
*/
|
|
49
|
-
export declare function runTasks(config: RunnerConfig, runDir: string
|
|
53
|
+
export declare function runTasks(tasksFile: string, config: RunnerConfig, runDir: string, options?: {
|
|
54
|
+
startIndex?: number;
|
|
55
|
+
}): Promise<TaskExecutionResult[]>;
|
package/dist/core/runner.js
CHANGED
|
@@ -123,7 +123,10 @@ function parseJsonFromStdout(stdout) {
|
|
|
123
123
|
}
|
|
124
124
|
return null;
|
|
125
125
|
}
|
|
126
|
-
|
|
126
|
+
/**
|
|
127
|
+
* Execute cursor-agent command with streaming and better error handling
|
|
128
|
+
*/
|
|
129
|
+
async function cursorAgentSend({ workspaceDir, chatId, prompt, model, signalDir }) {
|
|
127
130
|
const args = [
|
|
128
131
|
'--print',
|
|
129
132
|
'--output-format', 'json',
|
|
@@ -133,65 +136,93 @@ function cursorAgentSend({ workspaceDir, chatId, prompt, model }) {
|
|
|
133
136
|
prompt,
|
|
134
137
|
];
|
|
135
138
|
logger.info('Executing cursor-agent...');
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
139
|
+
return new Promise((resolve) => {
|
|
140
|
+
const child = (0, child_process_1.spawn)('cursor-agent', args, {
|
|
141
|
+
stdio: ['pipe', 'pipe', 'pipe'], // Enable stdin piping
|
|
142
|
+
env: process.env,
|
|
143
|
+
});
|
|
144
|
+
let fullStdout = '';
|
|
145
|
+
let fullStderr = '';
|
|
146
|
+
// Watch for "intervention.txt" signal file if any
|
|
147
|
+
const interventionPath = signalDir ? path.join(signalDir, 'intervention.txt') : null;
|
|
148
|
+
let interventionWatcher = null;
|
|
149
|
+
if (interventionPath && fs.existsSync(path.dirname(interventionPath))) {
|
|
150
|
+
interventionWatcher = fs.watch(path.dirname(interventionPath), (event, filename) => {
|
|
151
|
+
if (filename === 'intervention.txt' && fs.existsSync(interventionPath)) {
|
|
152
|
+
try {
|
|
153
|
+
const message = fs.readFileSync(interventionPath, 'utf8').trim();
|
|
154
|
+
if (message) {
|
|
155
|
+
logger.info(`Injecting intervention: ${message}`);
|
|
156
|
+
child.stdin.write(message + '\n');
|
|
157
|
+
fs.unlinkSync(interventionPath); // Clear it
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
catch (e) {
|
|
161
|
+
logger.warn('Failed to read intervention file');
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
child.stdout.on('data', (data) => {
|
|
167
|
+
const str = data.toString();
|
|
168
|
+
fullStdout += str;
|
|
169
|
+
// Also pipe to our own stdout so it goes to terminal.log
|
|
170
|
+
process.stdout.write(data);
|
|
171
|
+
});
|
|
172
|
+
child.stderr.on('data', (data) => {
|
|
173
|
+
fullStderr += data.toString();
|
|
174
|
+
// Pipe to our own stderr so it goes to terminal.log
|
|
175
|
+
process.stderr.write(data);
|
|
176
|
+
});
|
|
177
|
+
const timeout = setTimeout(() => {
|
|
178
|
+
child.kill();
|
|
179
|
+
resolve({
|
|
145
180
|
ok: false,
|
|
146
181
|
exitCode: -1,
|
|
147
182
|
error: 'cursor-agent timed out after 5 minutes. The LLM request may be taking too long or there may be network issues.',
|
|
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
|
-
exitCode: res.status ?? 0,
|
|
192
|
-
sessionId: json.session_id || chatId,
|
|
193
|
-
resultText: json.result || '',
|
|
194
|
-
};
|
|
183
|
+
});
|
|
184
|
+
}, 300000);
|
|
185
|
+
child.on('close', (code) => {
|
|
186
|
+
clearTimeout(timeout);
|
|
187
|
+
if (interventionWatcher)
|
|
188
|
+
interventionWatcher.close();
|
|
189
|
+
const json = parseJsonFromStdout(fullStdout);
|
|
190
|
+
if (code !== 0 || !json || json.type !== 'result') {
|
|
191
|
+
let errorMsg = fullStderr.trim() || fullStdout.trim() || `exit=${code}`;
|
|
192
|
+
// Check for common errors
|
|
193
|
+
if (errorMsg.includes('not authenticated') || errorMsg.includes('login') || errorMsg.includes('auth')) {
|
|
194
|
+
errorMsg = 'Authentication error. Please sign in to Cursor IDE.';
|
|
195
|
+
}
|
|
196
|
+
else if (errorMsg.includes('rate limit') || errorMsg.includes('quota')) {
|
|
197
|
+
errorMsg = 'API rate limit or quota exceeded.';
|
|
198
|
+
}
|
|
199
|
+
else if (errorMsg.includes('model')) {
|
|
200
|
+
errorMsg = `Model error (requested: ${model || 'default'}). Check your subscription.`;
|
|
201
|
+
}
|
|
202
|
+
resolve({
|
|
203
|
+
ok: false,
|
|
204
|
+
exitCode: code ?? -1,
|
|
205
|
+
error: errorMsg,
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
else {
|
|
209
|
+
resolve({
|
|
210
|
+
ok: !json.is_error,
|
|
211
|
+
exitCode: code ?? 0,
|
|
212
|
+
sessionId: json.session_id || chatId,
|
|
213
|
+
resultText: json.result || '',
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
child.on('error', (err) => {
|
|
218
|
+
clearTimeout(timeout);
|
|
219
|
+
resolve({
|
|
220
|
+
ok: false,
|
|
221
|
+
exitCode: -1,
|
|
222
|
+
error: `Failed to start cursor-agent: ${err.message}`,
|
|
223
|
+
});
|
|
224
|
+
});
|
|
225
|
+
});
|
|
195
226
|
}
|
|
196
227
|
/**
|
|
197
228
|
* Extract dependency change request from agent response
|
|
@@ -293,11 +324,12 @@ async function runTask({ task, config, index, worktreeDir, taskBranch, chatId, r
|
|
|
293
324
|
model,
|
|
294
325
|
}));
|
|
295
326
|
logger.info('Sending prompt to agent...');
|
|
296
|
-
const r1 = cursorAgentSend({
|
|
327
|
+
const r1 = await cursorAgentSend({
|
|
297
328
|
workspaceDir: worktreeDir,
|
|
298
329
|
chatId,
|
|
299
330
|
prompt: prompt1,
|
|
300
331
|
model,
|
|
332
|
+
signalDir: runDir
|
|
301
333
|
});
|
|
302
334
|
(0, state_1.appendLog)(convoPath, (0, state_1.createConversationEntry)('assistant', r1.resultText || r1.error || 'No response', {
|
|
303
335
|
task: task.name,
|
|
@@ -332,7 +364,8 @@ async function runTask({ task, config, index, worktreeDir, taskBranch, chatId, r
|
|
|
332
364
|
/**
|
|
333
365
|
* Run all tasks in sequence
|
|
334
366
|
*/
|
|
335
|
-
async function runTasks(config, runDir) {
|
|
367
|
+
async function runTasks(tasksFile, config, runDir, options = {}) {
|
|
368
|
+
const startIndex = options.startIndex || 0;
|
|
336
369
|
// Ensure cursor-agent is installed
|
|
337
370
|
(0, cursor_agent_1.ensureCursorAgent)();
|
|
338
371
|
// Check authentication before starting
|
|
@@ -353,37 +386,96 @@ async function runTasks(config, runDir) {
|
|
|
353
386
|
}
|
|
354
387
|
logger.success('✓ Cursor authentication OK');
|
|
355
388
|
const repoRoot = git.getRepoRoot();
|
|
356
|
-
|
|
357
|
-
const
|
|
358
|
-
|
|
389
|
+
// Load existing state if resuming
|
|
390
|
+
const statePath = path.join(runDir, 'state.json');
|
|
391
|
+
let state = null;
|
|
392
|
+
if (startIndex > 0 && fs.existsSync(statePath)) {
|
|
393
|
+
state = JSON.parse(fs.readFileSync(statePath, 'utf8'));
|
|
394
|
+
}
|
|
395
|
+
const pipelineBranch = state?.pipelineBranch || config.pipelineBranch || `${config.branchPrefix || 'cursorflow/'}${Date.now().toString(36)}`;
|
|
396
|
+
const worktreeDir = state?.worktreeDir || path.join(repoRoot, config.worktreeRoot || '_cursorflow/worktrees', pipelineBranch);
|
|
397
|
+
if (startIndex === 0) {
|
|
398
|
+
logger.section('🚀 Starting Pipeline');
|
|
399
|
+
}
|
|
400
|
+
else {
|
|
401
|
+
logger.section(`🔁 Resuming Pipeline from task ${startIndex + 1}`);
|
|
402
|
+
}
|
|
359
403
|
logger.info(`Pipeline Branch: ${pipelineBranch}`);
|
|
360
404
|
logger.info(`Worktree: ${worktreeDir}`);
|
|
361
405
|
logger.info(`Tasks: ${config.tasks.length}`);
|
|
362
|
-
// Create worktree
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
406
|
+
// Create worktree only if starting fresh
|
|
407
|
+
if (startIndex === 0 || !fs.existsSync(worktreeDir)) {
|
|
408
|
+
git.createWorktree(worktreeDir, pipelineBranch, {
|
|
409
|
+
baseBranch: config.baseBranch || 'main',
|
|
410
|
+
cwd: repoRoot,
|
|
411
|
+
});
|
|
412
|
+
}
|
|
367
413
|
// Create chat
|
|
368
414
|
logger.info('Creating chat session...');
|
|
369
415
|
const chatId = cursorAgentCreateChat();
|
|
370
|
-
//
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
416
|
+
// Initialize state if not loaded
|
|
417
|
+
if (!state) {
|
|
418
|
+
state = {
|
|
419
|
+
status: 'running',
|
|
420
|
+
pipelineBranch,
|
|
421
|
+
worktreeDir,
|
|
422
|
+
totalTasks: config.tasks.length,
|
|
423
|
+
currentTaskIndex: 0,
|
|
424
|
+
label: pipelineBranch,
|
|
425
|
+
startTime: Date.now(),
|
|
426
|
+
endTime: null,
|
|
427
|
+
error: null,
|
|
428
|
+
dependencyRequest: null,
|
|
429
|
+
tasksFile, // Store tasks file for resume
|
|
430
|
+
dependsOn: config.dependsOn || [],
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
else {
|
|
434
|
+
state.status = 'running';
|
|
435
|
+
state.error = null;
|
|
436
|
+
state.dependencyRequest = null;
|
|
437
|
+
state.dependsOn = config.dependsOn || [];
|
|
438
|
+
}
|
|
439
|
+
(0, state_1.saveState)(statePath, state);
|
|
440
|
+
// Merge dependencies if any
|
|
441
|
+
if (startIndex === 0 && config.dependsOn && config.dependsOn.length > 0) {
|
|
442
|
+
logger.section('🔗 Merging Dependencies');
|
|
443
|
+
// The runDir for the lane is passed in. Dependencies are in ../<depName> relative to this runDir
|
|
444
|
+
const lanesRoot = path.dirname(runDir);
|
|
445
|
+
for (const depName of config.dependsOn) {
|
|
446
|
+
const depRunDir = path.join(lanesRoot, depName);
|
|
447
|
+
const depStatePath = path.join(depRunDir, 'state.json');
|
|
448
|
+
if (!fs.existsSync(depStatePath)) {
|
|
449
|
+
logger.warn(`Dependency state not found for ${depName} at ${depStatePath}`);
|
|
450
|
+
continue;
|
|
451
|
+
}
|
|
452
|
+
try {
|
|
453
|
+
const depState = JSON.parse(fs.readFileSync(depStatePath, 'utf8'));
|
|
454
|
+
if (depState.status !== 'completed') {
|
|
455
|
+
logger.warn(`Dependency ${depName} is in status ${depState.status}, merge might be incomplete`);
|
|
456
|
+
}
|
|
457
|
+
if (depState.pipelineBranch) {
|
|
458
|
+
logger.info(`Merging dependency branch: ${depState.pipelineBranch} (${depName})`);
|
|
459
|
+
// Fetch first to ensure we have the branch
|
|
460
|
+
git.runGit(['fetch', 'origin', depState.pipelineBranch], { cwd: worktreeDir, silent: true });
|
|
461
|
+
// Merge
|
|
462
|
+
git.merge(depState.pipelineBranch, {
|
|
463
|
+
cwd: worktreeDir,
|
|
464
|
+
noFf: true,
|
|
465
|
+
message: `chore: merge dependency ${depName} (${depState.pipelineBranch})`
|
|
466
|
+
});
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
catch (e) {
|
|
470
|
+
logger.error(`Failed to merge dependency ${depName}: ${e}`);
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
// Push the merged state
|
|
474
|
+
git.push(pipelineBranch, { cwd: worktreeDir });
|
|
475
|
+
}
|
|
384
476
|
// Run tasks
|
|
385
477
|
const results = [];
|
|
386
|
-
for (let i =
|
|
478
|
+
for (let i = startIndex; i < config.tasks.length; i++) {
|
|
387
479
|
const task = config.tasks[i];
|
|
388
480
|
const taskBranch = `${pipelineBranch}--${String(i + 1).padStart(2, '0')}-${task.name}`;
|
|
389
481
|
const result = await runTask({
|
|
@@ -399,19 +491,19 @@ async function runTasks(config, runDir) {
|
|
|
399
491
|
results.push(result);
|
|
400
492
|
// Update state
|
|
401
493
|
state.currentTaskIndex = i + 1;
|
|
402
|
-
(0, state_1.saveState)(
|
|
494
|
+
(0, state_1.saveState)(statePath, state);
|
|
403
495
|
// Handle blocked or error
|
|
404
496
|
if (result.status === 'BLOCKED_DEPENDENCY') {
|
|
405
|
-
state.status = 'failed';
|
|
497
|
+
state.status = 'failed';
|
|
406
498
|
state.dependencyRequest = result.dependencyRequest || null;
|
|
407
|
-
(0, state_1.saveState)(
|
|
499
|
+
(0, state_1.saveState)(statePath, state);
|
|
408
500
|
logger.warn('Task blocked on dependency change');
|
|
409
501
|
process.exit(2);
|
|
410
502
|
}
|
|
411
503
|
if (result.status !== 'FINISHED') {
|
|
412
504
|
state.status = 'failed';
|
|
413
505
|
state.error = result.error || 'Unknown error';
|
|
414
|
-
(0, state_1.saveState)(
|
|
506
|
+
(0, state_1.saveState)(statePath, state);
|
|
415
507
|
logger.error(`Task failed: ${result.error}`);
|
|
416
508
|
process.exit(1);
|
|
417
509
|
}
|
|
@@ -423,7 +515,7 @@ async function runTasks(config, runDir) {
|
|
|
423
515
|
// Complete
|
|
424
516
|
state.status = 'completed';
|
|
425
517
|
state.endTime = Date.now();
|
|
426
|
-
(0, state_1.saveState)(
|
|
518
|
+
(0, state_1.saveState)(statePath, state);
|
|
427
519
|
logger.success('All tasks completed!');
|
|
428
520
|
return results;
|
|
429
521
|
}
|
|
@@ -438,8 +530,10 @@ if (require.main === module) {
|
|
|
438
530
|
}
|
|
439
531
|
const tasksFile = args[0];
|
|
440
532
|
const runDirIdx = args.indexOf('--run-dir');
|
|
533
|
+
const startIdxIdx = args.indexOf('--start-index');
|
|
441
534
|
// const executorIdx = args.indexOf('--executor');
|
|
442
535
|
const runDir = runDirIdx >= 0 ? args[runDirIdx + 1] : '.';
|
|
536
|
+
const startIndex = startIdxIdx >= 0 ? parseInt(args[startIdxIdx + 1] || '0') : 0;
|
|
443
537
|
// const executor = executorIdx >= 0 ? args[executorIdx + 1] : 'cursor-agent';
|
|
444
538
|
if (!fs.existsSync(tasksFile)) {
|
|
445
539
|
console.error(`Tasks file not found: ${tasksFile}`);
|
|
@@ -460,7 +554,7 @@ if (require.main === module) {
|
|
|
460
554
|
lockfileReadOnly: true,
|
|
461
555
|
};
|
|
462
556
|
// Run tasks
|
|
463
|
-
runTasks(config, runDir)
|
|
557
|
+
runTasks(tasksFile, config, runDir, { startIndex })
|
|
464
558
|
.then(() => {
|
|
465
559
|
process.exit(0);
|
|
466
560
|
})
|