@litmers/cursorflow-orchestrator 0.1.37 → 0.1.40
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/README.md +13 -13
- package/commands/cursorflow-init.md +113 -32
- package/commands/cursorflow-prepare.md +146 -339
- package/commands/cursorflow-run.md +148 -131
- package/dist/cli/add.js +8 -4
- package/dist/cli/add.js.map +1 -1
- package/dist/cli/index.js +2 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/new.js +3 -5
- package/dist/cli/new.js.map +1 -1
- package/dist/cli/prepare.js +0 -1
- package/dist/cli/prepare.js.map +1 -1
- package/dist/cli/resume.js +24 -15
- package/dist/cli/resume.js.map +1 -1
- package/dist/cli/run.js +1 -6
- package/dist/cli/run.js.map +1 -1
- package/dist/cli/setup-commands.d.ts +1 -0
- package/dist/cli/setup-commands.js +1 -0
- package/dist/cli/setup-commands.js.map +1 -1
- package/dist/core/orchestrator.js +13 -5
- package/dist/core/orchestrator.js.map +1 -1
- package/dist/core/runner/agent.d.ts +5 -1
- package/dist/core/runner/agent.js +31 -1
- package/dist/core/runner/agent.js.map +1 -1
- package/dist/core/runner/pipeline.d.ts +0 -1
- package/dist/core/runner/pipeline.js +136 -173
- package/dist/core/runner/pipeline.js.map +1 -1
- package/dist/core/runner/prompt.d.ts +0 -1
- package/dist/core/runner/prompt.js +11 -16
- package/dist/core/runner/prompt.js.map +1 -1
- package/dist/core/runner/task.d.ts +1 -2
- package/dist/core/runner/task.js +31 -40
- package/dist/core/runner/task.js.map +1 -1
- package/dist/core/runner.js +15 -2
- package/dist/core/runner.js.map +1 -1
- package/dist/core/stall-detection.d.ts +32 -4
- package/dist/core/stall-detection.js +151 -149
- package/dist/core/stall-detection.js.map +1 -1
- package/dist/services/logging/console.d.ts +7 -1
- package/dist/services/logging/console.js +13 -3
- package/dist/services/logging/console.js.map +1 -1
- package/dist/services/logging/formatter.d.ts +1 -0
- package/dist/services/logging/formatter.js +6 -3
- package/dist/services/logging/formatter.js.map +1 -1
- package/dist/types/config.d.ts +3 -1
- package/dist/types/logging.d.ts +1 -1
- package/dist/types/task.d.ts +3 -8
- package/dist/utils/config.js +5 -0
- package/dist/utils/config.js.map +1 -1
- package/dist/utils/doctor.js +4 -4
- package/dist/utils/doctor.js.map +1 -1
- package/dist/utils/enhanced-logger.d.ts +1 -1
- package/dist/utils/enhanced-logger.js +3 -3
- package/dist/utils/enhanced-logger.js.map +1 -1
- package/dist/utils/git.d.ts +12 -1
- package/dist/utils/git.js +56 -1
- package/dist/utils/git.js.map +1 -1
- package/dist/utils/health.js +13 -13
- package/dist/utils/health.js.map +1 -1
- package/dist/utils/log-formatter.d.ts +1 -1
- package/dist/utils/log-formatter.js +45 -8
- package/dist/utils/log-formatter.js.map +1 -1
- package/dist/utils/logger.js +2 -2
- package/dist/utils/logger.js.map +1 -1
- package/package.json +1 -1
- package/src/cli/add.ts +9 -4
- package/src/cli/index.ts +3 -0
- package/src/cli/new.ts +3 -5
- package/src/cli/prepare.ts +0 -1
- package/src/cli/resume.ts +28 -19
- package/src/cli/run.ts +1 -6
- package/src/cli/setup-commands.ts +1 -1
- package/src/core/orchestrator.ts +14 -5
- package/src/core/runner/agent.ts +36 -4
- package/src/core/runner/pipeline.ts +149 -182
- package/src/core/runner/prompt.ts +11 -18
- package/src/core/runner/task.ts +32 -41
- package/src/core/runner.ts +17 -2
- package/src/core/stall-detection.ts +263 -147
- package/src/services/logging/console.ts +13 -3
- package/src/services/logging/formatter.ts +6 -3
- package/src/types/config.ts +3 -1
- package/src/types/logging.ts +4 -2
- package/src/types/task.ts +3 -8
- package/src/utils/config.ts +6 -0
- package/src/utils/doctor.ts +5 -5
- package/src/utils/enhanced-logger.ts +3 -3
- package/src/utils/flow.ts +1 -0
- package/src/utils/git.ts +61 -1
- package/src/utils/health.ts +15 -15
- package/src/utils/log-formatter.ts +51 -8
- package/src/utils/logger.ts +2 -2
- package/commands/cursorflow-add.md +0 -159
- package/commands/cursorflow-clean.md +0 -84
- package/commands/cursorflow-doctor.md +0 -102
- package/commands/cursorflow-models.md +0 -51
- package/commands/cursorflow-monitor.md +0 -90
- package/commands/cursorflow-new.md +0 -87
- package/commands/cursorflow-resume.md +0 -205
- package/commands/cursorflow-signal.md +0 -52
- package/commands/cursorflow-stop.md +0 -55
- package/commands/cursorflow-triggers.md +0 -250
package/src/cli/resume.ts
CHANGED
|
@@ -29,7 +29,6 @@ interface ResumeOptions {
|
|
|
29
29
|
status: boolean;
|
|
30
30
|
maxConcurrent: number;
|
|
31
31
|
help: boolean;
|
|
32
|
-
noGit: boolean;
|
|
33
32
|
executor: string | null;
|
|
34
33
|
}
|
|
35
34
|
|
|
@@ -48,7 +47,6 @@ Options:
|
|
|
48
47
|
--clean Clean up existing worktree before resuming
|
|
49
48
|
--restart Restart from the first task (index 0)
|
|
50
49
|
--skip-doctor Skip environment/branch checks (not recommended)
|
|
51
|
-
--no-git Disable Git operations (must match original run)
|
|
52
50
|
--executor <type> Override executor (default: cursor-agent)
|
|
53
51
|
--help, -h Show help
|
|
54
52
|
|
|
@@ -56,7 +54,7 @@ Examples:
|
|
|
56
54
|
cursorflow resume --status # Check status of all lanes
|
|
57
55
|
cursorflow resume --all # Resume all incomplete lanes
|
|
58
56
|
cursorflow resume lane-1 # Resume single lane
|
|
59
|
-
cursorflow resume _cursorflow/
|
|
57
|
+
cursorflow resume _cursorflow/flows/MyFeature # Resume all lanes in flow
|
|
60
58
|
cursorflow resume --all --restart # Restart all incomplete lanes from task 0
|
|
61
59
|
`);
|
|
62
60
|
}
|
|
@@ -76,7 +74,6 @@ function parseArgs(args: string[]): ResumeOptions {
|
|
|
76
74
|
status: args.includes('--status'),
|
|
77
75
|
maxConcurrent: maxConcurrentIdx >= 0 ? parseInt(args[maxConcurrentIdx + 1] || '3') : 3,
|
|
78
76
|
help: args.includes('--help') || args.includes('-h'),
|
|
79
|
-
noGit: args.includes('--no-git'),
|
|
80
77
|
executor: executorIdx >= 0 ? args[executorIdx + 1] || null : null,
|
|
81
78
|
};
|
|
82
79
|
}
|
|
@@ -397,10 +394,10 @@ function spawnLaneResume(
|
|
|
397
394
|
state: LaneState,
|
|
398
395
|
options: {
|
|
399
396
|
restart: boolean;
|
|
400
|
-
noGit?: boolean;
|
|
401
397
|
pipelineBranch?: string;
|
|
402
398
|
executor?: string | null;
|
|
403
399
|
enhancedLogConfig?: any;
|
|
400
|
+
laneIndex?: number;
|
|
404
401
|
}
|
|
405
402
|
): { child: ChildProcess; logManager: EnhancedLogManager } {
|
|
406
403
|
const runnerPath = require.resolve('../core/runner');
|
|
@@ -417,10 +414,6 @@ function spawnLaneResume(
|
|
|
417
414
|
runnerArgs.push('--worktree-dir', state.worktreeDir);
|
|
418
415
|
}
|
|
419
416
|
|
|
420
|
-
if (options.noGit) {
|
|
421
|
-
runnerArgs.push('--no-git');
|
|
422
|
-
}
|
|
423
|
-
|
|
424
417
|
// Explicitly pass pipeline branch if available (either from state or override)
|
|
425
418
|
const branch = options.pipelineBranch || state.pipelineBranch;
|
|
426
419
|
if (branch) {
|
|
@@ -432,16 +425,26 @@ function spawnLaneResume(
|
|
|
432
425
|
runnerArgs.push('--executor', options.executor);
|
|
433
426
|
}
|
|
434
427
|
|
|
435
|
-
|
|
428
|
+
// Generate lane label: [laneIdx-taskIdx-laneName] format, padded to 18 chars
|
|
429
|
+
// Note: taskName will be updated dynamically via logManager.setTask()
|
|
430
|
+
let currentTaskName = '';
|
|
431
|
+
let currentTaskIdx = (state.currentTaskIndex ?? 0) + 1;
|
|
432
|
+
const getLaneLabel = () => {
|
|
433
|
+
const laneIdx = options.laneIndex ?? 1;
|
|
434
|
+
const combined = `${laneIdx}-${currentTaskIdx}-${laneName}`;
|
|
435
|
+
const label = combined.substring(0, 18).padEnd(18);
|
|
436
|
+
return `[${label}]`;
|
|
437
|
+
};
|
|
438
|
+
|
|
436
439
|
const logManager = createLogManager(laneDir, laneName, options.enhancedLogConfig || {}, (msg) => {
|
|
437
440
|
const formatted = formatMessageForConsole(msg, {
|
|
438
|
-
laneLabel:
|
|
441
|
+
laneLabel: getLaneLabel(),
|
|
439
442
|
includeTimestamp: true
|
|
440
443
|
});
|
|
441
444
|
process.stdout.write(formatted + '\n');
|
|
442
|
-
});
|
|
445
|
+
}, options.laneIndex);
|
|
443
446
|
|
|
444
|
-
const child = spawn(
|
|
447
|
+
const child = spawn(process.execPath, runnerArgs, {
|
|
445
448
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
446
449
|
env: process.env,
|
|
447
450
|
});
|
|
@@ -489,7 +492,6 @@ async function resumeLanes(
|
|
|
489
492
|
restart: boolean;
|
|
490
493
|
maxConcurrent: number;
|
|
491
494
|
skipDoctor: boolean;
|
|
492
|
-
noGit: boolean;
|
|
493
495
|
executor: string | null;
|
|
494
496
|
enhancedLogConfig?: any;
|
|
495
497
|
}
|
|
@@ -547,6 +549,8 @@ async function resumeLanes(
|
|
|
547
549
|
const pending = new Set<string>(resolvableLanes.map(l => l.name));
|
|
548
550
|
const active: Map<string, ChildProcess> = new Map();
|
|
549
551
|
const laneMap = new Map<string, LaneInfo>(resolvableLanes.map(l => [l.name, l]));
|
|
552
|
+
// Track lane indices for consistent logging
|
|
553
|
+
const laneIndexMap = new Map<string, number>(allLanes.map((l, i) => [l.name, i + 1]));
|
|
550
554
|
|
|
551
555
|
const findReadyLane = (): LaneInfo | null => {
|
|
552
556
|
for (const laneName of pending) {
|
|
@@ -558,7 +562,7 @@ async function resumeLanes(
|
|
|
558
562
|
return null;
|
|
559
563
|
};
|
|
560
564
|
|
|
561
|
-
const processNext = (): void => {
|
|
565
|
+
const processNext = async (): Promise<void> => {
|
|
562
566
|
while (active.size < options.maxConcurrent) {
|
|
563
567
|
const lane = findReadyLane();
|
|
564
568
|
if (!lane) {
|
|
@@ -572,13 +576,19 @@ async function resumeLanes(
|
|
|
572
576
|
}
|
|
573
577
|
|
|
574
578
|
pending.delete(lane.name);
|
|
579
|
+
|
|
580
|
+
// Add a small delay between starting lanes to reduce initial resource contention
|
|
581
|
+
if (active.size > 0) {
|
|
582
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
583
|
+
}
|
|
584
|
+
|
|
575
585
|
logger.info(`Starting: ${lane.name} (task ${lane.state!.currentTaskIndex}/${lane.state!.totalTasks})`);
|
|
576
586
|
|
|
577
587
|
const { child } = spawnLaneResume(lane.name, lane.dir, lane.state!, {
|
|
578
588
|
restart: options.restart,
|
|
579
|
-
noGit: options.noGit,
|
|
580
589
|
executor: options.executor,
|
|
581
590
|
enhancedLogConfig: options.enhancedLogConfig,
|
|
591
|
+
laneIndex: laneIndexMap.get(lane.name) ?? 1,
|
|
582
592
|
});
|
|
583
593
|
|
|
584
594
|
active.set(lane.name, child);
|
|
@@ -606,12 +616,12 @@ async function resumeLanes(
|
|
|
606
616
|
}
|
|
607
617
|
};
|
|
608
618
|
|
|
609
|
-
processNext();
|
|
619
|
+
await processNext();
|
|
610
620
|
|
|
611
621
|
while (active.size > 0 || pending.size > 0) {
|
|
612
622
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
613
623
|
if (active.size < options.maxConcurrent && pending.size > 0) {
|
|
614
|
-
processNext();
|
|
624
|
+
await processNext();
|
|
615
625
|
}
|
|
616
626
|
}
|
|
617
627
|
|
|
@@ -725,7 +735,6 @@ async function resume(args: string[]): Promise<void> {
|
|
|
725
735
|
restart: options.restart,
|
|
726
736
|
maxConcurrent: options.maxConcurrent,
|
|
727
737
|
skipDoctor: options.skipDoctor,
|
|
728
|
-
noGit: options.noGit,
|
|
729
738
|
executor: options.executor,
|
|
730
739
|
enhancedLogConfig: config.enhancedLogging,
|
|
731
740
|
});
|
package/src/cli/run.ts
CHANGED
|
@@ -118,7 +118,6 @@ interface RunOptions {
|
|
|
118
118
|
maxConcurrent: number | null;
|
|
119
119
|
skipDoctor: boolean;
|
|
120
120
|
skipPreflight: boolean;
|
|
121
|
-
noGit: boolean;
|
|
122
121
|
raw: boolean;
|
|
123
122
|
help: boolean;
|
|
124
123
|
}
|
|
@@ -138,14 +137,13 @@ Options:
|
|
|
138
137
|
--executor <type> cursor-agent | cloud
|
|
139
138
|
--skip-doctor Skip environment checks (not recommended)
|
|
140
139
|
--skip-preflight Skip preflight checks (Git remote, etc.)
|
|
141
|
-
--no-git Disable Git operations (worktree, push, commit)
|
|
142
140
|
--raw Save raw logs (absolute raw, no processing)
|
|
143
141
|
--dry-run Show execution plan without starting agents
|
|
144
142
|
--help, -h Show help
|
|
145
143
|
|
|
146
144
|
Examples:
|
|
147
145
|
cursorflow run _cursorflow/tasks
|
|
148
|
-
cursorflow run _cursorflow/
|
|
146
|
+
cursorflow run _cursorflow/flows/MyFeature
|
|
149
147
|
`);
|
|
150
148
|
}
|
|
151
149
|
|
|
@@ -161,7 +159,6 @@ function parseArgs(args: string[]): RunOptions {
|
|
|
161
159
|
maxConcurrent: maxConcurrentIdx >= 0 ? parseInt(args[maxConcurrentIdx + 1] || '0') || null : null,
|
|
162
160
|
skipDoctor: args.includes('--skip-doctor') || args.includes('--no-doctor'),
|
|
163
161
|
skipPreflight: args.includes('--skip-preflight'),
|
|
164
|
-
noGit: args.includes('--no-git'),
|
|
165
162
|
raw: args.includes('--raw'),
|
|
166
163
|
help: args.includes('--help') || args.includes('-h'),
|
|
167
164
|
};
|
|
@@ -260,7 +257,6 @@ async function run(args: string[]): Promise<void> {
|
|
|
260
257
|
|
|
261
258
|
if (options.skipDoctor) resumeArgs.push('--skip-doctor');
|
|
262
259
|
if (options.skipPreflight) resumeArgs.push('--skip-preflight');
|
|
263
|
-
if (options.noGit) resumeArgs.push('--no-git');
|
|
264
260
|
if (options.executor) {
|
|
265
261
|
resumeArgs.push('--executor', options.executor);
|
|
266
262
|
}
|
|
@@ -321,7 +317,6 @@ async function run(args: string[]): Promise<void> {
|
|
|
321
317
|
...config.enhancedLogging,
|
|
322
318
|
...(options.raw ? { raw: true } : {}),
|
|
323
319
|
},
|
|
324
|
-
noGit: options.noGit,
|
|
325
320
|
skipPreflight: options.skipPreflight,
|
|
326
321
|
});
|
|
327
322
|
} catch (error: any) {
|
|
@@ -202,7 +202,7 @@ export function areCommandsInstalled(): boolean {
|
|
|
202
202
|
return sourceFiles.every(f => targetFiles.includes(f));
|
|
203
203
|
}
|
|
204
204
|
|
|
205
|
-
async function main(args: string[]): Promise<any> {
|
|
205
|
+
export async function main(args: string[]): Promise<any> {
|
|
206
206
|
const options = parseArgs(args);
|
|
207
207
|
|
|
208
208
|
try {
|
package/src/core/orchestrator.ts
CHANGED
|
@@ -278,12 +278,13 @@ export function spawnLane({
|
|
|
278
278
|
};
|
|
279
279
|
|
|
280
280
|
if (logConfig.enabled) {
|
|
281
|
-
// Helper to get dynamic lane label like [
|
|
281
|
+
// Helper to get dynamic lane label like [1-1-lanename10]
|
|
282
282
|
const getDynamicLabel = () => {
|
|
283
|
-
const laneNum =
|
|
284
|
-
const taskPart = info.currentTaskIndex
|
|
283
|
+
const laneNum = `${laneIndex + 1}`;
|
|
284
|
+
const taskPart = `-${info.currentTaskIndex || 1}`;
|
|
285
285
|
const shortLaneName = laneName.substring(0, 10);
|
|
286
|
-
|
|
286
|
+
const combined = `${laneNum}${taskPart}-${shortLaneName}`.substring(0, 18).padEnd(18);
|
|
287
|
+
return `[${combined}]`;
|
|
287
288
|
};
|
|
288
289
|
|
|
289
290
|
// Create callback for clean console output
|
|
@@ -838,8 +839,10 @@ export async function orchestrate(tasksDir: string, options: {
|
|
|
838
839
|
const now = Date.now();
|
|
839
840
|
|
|
840
841
|
// Register lane with unified stall detection service FIRST
|
|
842
|
+
// Pass intervention capability so stall service knows if continue signals will work
|
|
841
843
|
stallService.registerLane(lane.name, {
|
|
842
844
|
laneRunDir: laneRunDirs[lane.name]!,
|
|
845
|
+
interventionEnabled: config.enableIntervention ?? true,
|
|
843
846
|
});
|
|
844
847
|
|
|
845
848
|
const laneIdx = lanes.findIndex(l => l.name === lane.name);
|
|
@@ -931,13 +934,19 @@ export async function orchestrate(tasksDir: string, options: {
|
|
|
931
934
|
for (const [laneName, info] of running.entries()) {
|
|
932
935
|
const lane = lanes.find(l => l.name === laneName)!;
|
|
933
936
|
|
|
934
|
-
// Check state file for progress updates
|
|
937
|
+
// Check state file for progress updates and sync lane status
|
|
935
938
|
try {
|
|
936
939
|
const stateStat = fs.statSync(info.statePath);
|
|
937
940
|
const stallState = stallService.getState(laneName);
|
|
938
941
|
if (stallState && stateStat.mtimeMs > stallState.lastStateUpdateTime) {
|
|
939
942
|
stallService.recordStateUpdate(laneName);
|
|
940
943
|
}
|
|
944
|
+
|
|
945
|
+
// Sync lane status to stall service (skips stall detection when 'waiting' for dependencies)
|
|
946
|
+
const laneState = loadState<LaneState>(info.statePath);
|
|
947
|
+
if (laneState && stallState && laneState.status !== stallState.laneStatus) {
|
|
948
|
+
stallService.setLaneStatus(laneName, laneState.status || 'running');
|
|
949
|
+
}
|
|
941
950
|
} catch {
|
|
942
951
|
// State file might not exist yet
|
|
943
952
|
}
|
package/src/core/runner/agent.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { spawn, spawnSync } from 'child_process';
|
|
1
|
+
import { spawn, spawnSync, ChildProcess } from 'child_process';
|
|
2
2
|
import * as logger from '../../utils/logger';
|
|
3
3
|
import { AgentSendResult, DependencyRequestPlan } from '../../types';
|
|
4
4
|
import { withRetry } from '../failure-policy';
|
|
@@ -6,6 +6,30 @@ import * as path from 'path';
|
|
|
6
6
|
import * as fs from 'fs';
|
|
7
7
|
import { appendLog, createConversationEntry } from '../../utils/state';
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* Track active child processes for cleanup
|
|
11
|
+
*/
|
|
12
|
+
const activeChildren = new Set<ChildProcess>();
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Cleanup all active children
|
|
16
|
+
*/
|
|
17
|
+
export function cleanupAgentChildren(): void {
|
|
18
|
+
if (activeChildren.size > 0) {
|
|
19
|
+
logger.warn(`Cleaning up ${activeChildren.size} active agent child processes...`);
|
|
20
|
+
for (const child of activeChildren) {
|
|
21
|
+
if (!child.killed) {
|
|
22
|
+
try {
|
|
23
|
+
child.kill('SIGKILL');
|
|
24
|
+
} catch {
|
|
25
|
+
// Ignore
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
activeChildren.clear();
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
9
33
|
/**
|
|
10
34
|
* Execute cursor-agent command with timeout and better error handling
|
|
11
35
|
*/
|
|
@@ -115,7 +139,7 @@ async function cursorAgentSendRaw({ workspaceDir, chatId, prompt, model, signalD
|
|
|
115
139
|
signalDir?: string;
|
|
116
140
|
timeout?: number;
|
|
117
141
|
enableIntervention?: boolean;
|
|
118
|
-
outputFormat?: 'json' | 'plain';
|
|
142
|
+
outputFormat?: 'json' | 'plain' | 'stream-json';
|
|
119
143
|
taskName?: string;
|
|
120
144
|
}): Promise<AgentSendResult> {
|
|
121
145
|
const timeoutMs = timeout || 10 * 60 * 1000; // 10 minutes default
|
|
@@ -125,7 +149,7 @@ async function cursorAgentSendRaw({ workspaceDir, chatId, prompt, model, signalD
|
|
|
125
149
|
args.push('--model', model);
|
|
126
150
|
}
|
|
127
151
|
|
|
128
|
-
if (outputFormat === 'json') {
|
|
152
|
+
if (outputFormat === 'json' || outputFormat === 'stream-json') {
|
|
129
153
|
args.push('--print', '--output-format', 'json');
|
|
130
154
|
}
|
|
131
155
|
|
|
@@ -145,6 +169,12 @@ async function cursorAgentSendRaw({ workspaceDir, chatId, prompt, model, signalD
|
|
|
145
169
|
stdio: enableIntervention ? ['pipe', 'pipe', 'pipe'] : ['ignore', 'pipe', 'pipe'],
|
|
146
170
|
});
|
|
147
171
|
|
|
172
|
+
activeChildren.add(child);
|
|
173
|
+
|
|
174
|
+
if (!enableIntervention) {
|
|
175
|
+
logger.info('ℹ️ Intervention is disabled. Stall recovery using "continue" will not be available for this agent session.');
|
|
176
|
+
}
|
|
177
|
+
|
|
148
178
|
let fullStdout = '';
|
|
149
179
|
let fullStderr = '';
|
|
150
180
|
let timeoutHandle: NodeJS.Timeout;
|
|
@@ -235,6 +265,7 @@ async function cursorAgentSendRaw({ workspaceDir, chatId, prompt, model, signalD
|
|
|
235
265
|
}, timeoutMs);
|
|
236
266
|
|
|
237
267
|
child.on('close', (code) => {
|
|
268
|
+
activeChildren.delete(child);
|
|
238
269
|
clearTimeout(timeoutHandle);
|
|
239
270
|
clearInterval(heartbeatInterval);
|
|
240
271
|
if (signalWatcher) signalWatcher.close();
|
|
@@ -255,6 +286,7 @@ async function cursorAgentSendRaw({ workspaceDir, chatId, prompt, model, signalD
|
|
|
255
286
|
});
|
|
256
287
|
|
|
257
288
|
child.on('error', (err) => {
|
|
289
|
+
activeChildren.delete(child);
|
|
258
290
|
clearTimeout(timeoutHandle);
|
|
259
291
|
if (signalWatcher) signalWatcher.close();
|
|
260
292
|
resolve({ ok: false, exitCode: -1, error: `Failed to start cursor-agent: ${err.message}` });
|
|
@@ -273,7 +305,7 @@ export async function cursorAgentSend(options: {
|
|
|
273
305
|
signalDir?: string;
|
|
274
306
|
timeout?: number;
|
|
275
307
|
enableIntervention?: boolean;
|
|
276
|
-
outputFormat?: 'json' | 'plain';
|
|
308
|
+
outputFormat?: 'json' | 'plain' | 'stream-json';
|
|
277
309
|
taskName?: string;
|
|
278
310
|
}): Promise<AgentSendResult> {
|
|
279
311
|
const laneName = options.signalDir ? path.basename(path.dirname(options.signalDir)) : 'agent';
|