@litmers/cursorflow-orchestrator 0.1.5 → 0.1.6
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/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 +59 -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/runner.d.ts +3 -1
- package/dist/core/runner.js +54 -30
- 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 +1 -0
- package/package.json +1 -1
- package/src/cli/doctor.ts +127 -0
- package/src/cli/index.ts +5 -0
- package/src/cli/resume.ts +94 -12
- package/src/cli/run.ts +62 -7
- package/src/cli/setup-commands.ts +19 -0
- package/src/cli/signal.ts +89 -0
- package/src/core/runner.ts +57 -30
- package/src/utils/doctor.ts +312 -0
- package/src/utils/types.ts +1 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/utils/doctor.ts"],"names":[],"mappings":"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"}
|
package/dist/utils/types.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CursorFlow doctor command
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* cursorflow doctor [options]
|
|
6
|
+
*
|
|
7
|
+
* Options:
|
|
8
|
+
* --json Output machine-readable JSON
|
|
9
|
+
* --tasks-dir <path> Also validate lane files (run preflight)
|
|
10
|
+
* --executor <type> cursor-agent | cloud
|
|
11
|
+
* --no-cursor Skip Cursor Agent install/auth checks
|
|
12
|
+
* --help, -h Show help
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import * as logger from '../utils/logger';
|
|
16
|
+
import { runDoctor } from '../utils/doctor';
|
|
17
|
+
|
|
18
|
+
interface DoctorCliOptions {
|
|
19
|
+
json: boolean;
|
|
20
|
+
tasksDir: string | null;
|
|
21
|
+
executor: string | null;
|
|
22
|
+
includeCursorAgentChecks: boolean;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function printHelp(): void {
|
|
26
|
+
console.log(`
|
|
27
|
+
Usage: cursorflow doctor [options]
|
|
28
|
+
|
|
29
|
+
Verify your environment is ready for CursorFlow runs.
|
|
30
|
+
|
|
31
|
+
Options:
|
|
32
|
+
--json Output machine-readable JSON
|
|
33
|
+
--tasks-dir <path> Also validate lane files (run preflight)
|
|
34
|
+
--executor <type> cursor-agent | cloud
|
|
35
|
+
--no-cursor Skip Cursor Agent install/auth checks
|
|
36
|
+
--help, -h Show help
|
|
37
|
+
|
|
38
|
+
Examples:
|
|
39
|
+
cursorflow doctor
|
|
40
|
+
cursorflow doctor --tasks-dir _cursorflow/tasks/demo-test/
|
|
41
|
+
cursorflow doctor --json
|
|
42
|
+
`);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function parseArgs(args: string[]): DoctorCliOptions {
|
|
46
|
+
const tasksDirIdx = args.indexOf('--tasks-dir');
|
|
47
|
+
const executorIdx = args.indexOf('--executor');
|
|
48
|
+
|
|
49
|
+
const options: DoctorCliOptions = {
|
|
50
|
+
json: args.includes('--json'),
|
|
51
|
+
tasksDir: tasksDirIdx >= 0 ? (args[tasksDirIdx + 1] || null) : null,
|
|
52
|
+
executor: executorIdx >= 0 ? (args[executorIdx + 1] || null) : null,
|
|
53
|
+
includeCursorAgentChecks: !args.includes('--no-cursor'),
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
57
|
+
printHelp();
|
|
58
|
+
process.exit(0);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return options;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function printHumanReport(report: ReturnType<typeof runDoctor>): void {
|
|
65
|
+
logger.section('🩺 CursorFlow Doctor');
|
|
66
|
+
logger.info(`cwd: ${report.context.cwd}`);
|
|
67
|
+
if (report.context.repoRoot) logger.info(`repo: ${report.context.repoRoot}`);
|
|
68
|
+
if (report.context.tasksDir) logger.info(`tasks: ${report.context.tasksDir}`);
|
|
69
|
+
|
|
70
|
+
if (report.issues.length === 0) {
|
|
71
|
+
logger.success('All checks passed');
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
for (const issue of report.issues) {
|
|
76
|
+
const header = `${issue.title} (${issue.id})`;
|
|
77
|
+
if (issue.severity === 'error') {
|
|
78
|
+
logger.error(header, '❌');
|
|
79
|
+
} else {
|
|
80
|
+
logger.warn(header, '⚠️');
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
console.log(` ${issue.message}`);
|
|
84
|
+
|
|
85
|
+
if (issue.details) {
|
|
86
|
+
console.log(` Details: ${issue.details}`);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (issue.fixes && issue.fixes.length > 0) {
|
|
90
|
+
console.log(' Fix:');
|
|
91
|
+
for (const fix of issue.fixes) {
|
|
92
|
+
console.log(` - ${fix}`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
console.log('');
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (report.ok) {
|
|
100
|
+
logger.success('Doctor completed with warnings');
|
|
101
|
+
} else {
|
|
102
|
+
logger.error('Doctor found blocking issues');
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async function doctor(args: string[]): Promise<void> {
|
|
107
|
+
const options = parseArgs(args);
|
|
108
|
+
|
|
109
|
+
const report = runDoctor({
|
|
110
|
+
cwd: process.cwd(),
|
|
111
|
+
tasksDir: options.tasksDir || undefined,
|
|
112
|
+
executor: options.executor || undefined,
|
|
113
|
+
includeCursorAgentChecks: options.includeCursorAgentChecks,
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
if (options.json) {
|
|
117
|
+
console.log(JSON.stringify(report, null, 2));
|
|
118
|
+
} else {
|
|
119
|
+
printHumanReport(report);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
process.exit(report.ok ? 0 : 1);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export = doctor;
|
|
126
|
+
|
|
127
|
+
|
package/src/cli/index.ts
CHANGED
|
@@ -14,6 +14,8 @@ const COMMANDS: Record<string, CommandFn> = {
|
|
|
14
14
|
monitor: require('./monitor'),
|
|
15
15
|
clean: require('./clean'),
|
|
16
16
|
resume: require('./resume'),
|
|
17
|
+
doctor: require('./doctor'),
|
|
18
|
+
signal: require('./signal'),
|
|
17
19
|
};
|
|
18
20
|
|
|
19
21
|
function printHelp(): void {
|
|
@@ -28,6 +30,8 @@ Commands:
|
|
|
28
30
|
monitor [run-dir] [options] Monitor lane execution
|
|
29
31
|
clean <type> [options] Clean branches/worktrees/logs
|
|
30
32
|
resume <lane> [options] Resume interrupted lane
|
|
33
|
+
doctor [options] Check environment and preflight
|
|
34
|
+
signal <lane> <msg> Directly intervene in a running lane
|
|
31
35
|
|
|
32
36
|
Global Options:
|
|
33
37
|
--config <path> Config file path
|
|
@@ -39,6 +43,7 @@ Examples:
|
|
|
39
43
|
cursorflow run _cursorflow/tasks/MyFeature/
|
|
40
44
|
cursorflow monitor --watch
|
|
41
45
|
cursorflow clean branches --all
|
|
46
|
+
cursorflow doctor
|
|
42
47
|
|
|
43
48
|
Documentation:
|
|
44
49
|
https://github.com/eungjin-cigro/cursorflow#readme
|
package/src/cli/resume.ts
CHANGED
|
@@ -1,37 +1,119 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* CursorFlow resume command
|
|
2
|
+
* CursorFlow resume command
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
+
import * as path from 'path';
|
|
6
|
+
import * as fs from 'fs';
|
|
7
|
+
import { spawn } from 'child_process';
|
|
5
8
|
import * as logger from '../utils/logger';
|
|
9
|
+
import { loadConfig, getLogsDir } from '../utils/config';
|
|
10
|
+
import { loadState } from '../utils/state';
|
|
11
|
+
import { LaneState } from '../utils/types';
|
|
6
12
|
|
|
7
13
|
interface ResumeOptions {
|
|
8
|
-
lane
|
|
14
|
+
lane: string | null;
|
|
9
15
|
runDir: string | null;
|
|
10
16
|
clean: boolean;
|
|
11
17
|
restart: boolean;
|
|
12
18
|
}
|
|
13
19
|
|
|
14
20
|
function parseArgs(args: string[]): ResumeOptions {
|
|
21
|
+
const runDirIdx = args.indexOf('--run-dir');
|
|
22
|
+
|
|
15
23
|
return {
|
|
16
|
-
lane: args
|
|
17
|
-
runDir: null,
|
|
24
|
+
lane: args.find(a => !a.startsWith('--')) || null,
|
|
25
|
+
runDir: runDirIdx >= 0 ? args[runDirIdx + 1] || null : null,
|
|
18
26
|
clean: args.includes('--clean'),
|
|
19
27
|
restart: args.includes('--restart'),
|
|
20
28
|
};
|
|
21
29
|
}
|
|
22
30
|
|
|
23
|
-
|
|
24
|
-
|
|
31
|
+
/**
|
|
32
|
+
* Find the latest run directory
|
|
33
|
+
*/
|
|
34
|
+
function findLatestRunDir(logsDir: string): string | null {
|
|
35
|
+
const runsDir = path.join(logsDir, 'runs');
|
|
36
|
+
if (!fs.existsSync(runsDir)) return null;
|
|
25
37
|
|
|
38
|
+
const runs = fs.readdirSync(runsDir)
|
|
39
|
+
.filter(d => d.startsWith('run-'))
|
|
40
|
+
.sort()
|
|
41
|
+
.reverse();
|
|
42
|
+
|
|
43
|
+
return runs.length > 0 ? path.join(runsDir, runs[0]!) : null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async function resume(args: string[]): Promise<void> {
|
|
26
47
|
const options = parseArgs(args);
|
|
48
|
+
const config = loadConfig();
|
|
49
|
+
const logsDir = getLogsDir(config);
|
|
50
|
+
|
|
51
|
+
if (!options.lane) {
|
|
52
|
+
throw new Error('Lane name required (e.g., cursorflow resume lane-1)');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
let runDir = options.runDir;
|
|
56
|
+
if (!runDir) {
|
|
57
|
+
runDir = findLatestRunDir(logsDir);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (!runDir || !fs.existsSync(runDir)) {
|
|
61
|
+
throw new Error(`Run directory not found: ${runDir || 'latest'}`);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const laneDir = path.join(runDir, 'lanes', options.lane);
|
|
65
|
+
const statePath = path.join(laneDir, 'state.json');
|
|
66
|
+
|
|
67
|
+
if (!fs.existsSync(statePath)) {
|
|
68
|
+
throw new Error(`Lane state not found at ${statePath}. Is the lane name correct?`);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const state = loadState<LaneState>(statePath);
|
|
72
|
+
if (!state) {
|
|
73
|
+
throw new Error(`Failed to load state from ${statePath}`);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (!state.tasksFile || !fs.existsSync(state.tasksFile)) {
|
|
77
|
+
throw new Error(`Original tasks file not found: ${state.tasksFile}. Resume impossible without task definition.`);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
logger.section(`🔁 Resuming Lane: ${options.lane}`);
|
|
81
|
+
logger.info(`Run: ${path.basename(runDir)}`);
|
|
82
|
+
logger.info(`Tasks: ${state.tasksFile}`);
|
|
83
|
+
logger.info(`Starting from task index: ${options.restart ? 0 : state.currentTaskIndex}`);
|
|
84
|
+
|
|
85
|
+
const runnerPath = require.resolve('../core/runner');
|
|
86
|
+
const runnerArgs = [
|
|
87
|
+
runnerPath,
|
|
88
|
+
state.tasksFile,
|
|
89
|
+
'--run-dir', laneDir,
|
|
90
|
+
'--start-index', options.restart ? '0' : String(state.currentTaskIndex),
|
|
91
|
+
];
|
|
92
|
+
|
|
93
|
+
logger.info(`Spawning runner process...`);
|
|
27
94
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
95
|
+
const child = spawn('node', runnerArgs, {
|
|
96
|
+
stdio: 'inherit',
|
|
97
|
+
env: process.env,
|
|
98
|
+
});
|
|
32
99
|
|
|
33
|
-
|
|
34
|
-
|
|
100
|
+
return new Promise((resolve, reject) => {
|
|
101
|
+
child.on('exit', (code) => {
|
|
102
|
+
if (code === 0) {
|
|
103
|
+
logger.success(`Lane ${options.lane} completed successfully`);
|
|
104
|
+
resolve();
|
|
105
|
+
} else if (code === 2) {
|
|
106
|
+
logger.warn(`Lane ${options.lane} blocked on dependency change`);
|
|
107
|
+
resolve();
|
|
108
|
+
} else {
|
|
109
|
+
reject(new Error(`Lane ${options.lane} failed with exit code ${code}`));
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
child.on('error', (error) => {
|
|
114
|
+
reject(new Error(`Failed to start runner: ${error.message}`));
|
|
115
|
+
});
|
|
116
|
+
});
|
|
35
117
|
}
|
|
36
118
|
|
|
37
119
|
export = resume;
|
package/src/cli/run.ts
CHANGED
|
@@ -6,12 +6,15 @@ import * as path from 'path';
|
|
|
6
6
|
import * as fs from 'fs';
|
|
7
7
|
import * as logger from '../utils/logger';
|
|
8
8
|
import { orchestrate } from '../core/orchestrator';
|
|
9
|
-
import { loadConfig } from '../utils/config';
|
|
9
|
+
import { getLogsDir, loadConfig } from '../utils/config';
|
|
10
|
+
import { runDoctor } from '../utils/doctor';
|
|
11
|
+
import { areCommandsInstalled, setupCommands } from './setup-commands';
|
|
10
12
|
|
|
11
13
|
interface RunOptions {
|
|
12
14
|
tasksDir?: string;
|
|
13
15
|
dryRun: boolean;
|
|
14
16
|
executor: string | null;
|
|
17
|
+
skipDoctor: boolean;
|
|
15
18
|
}
|
|
16
19
|
|
|
17
20
|
function parseArgs(args: string[]): RunOptions {
|
|
@@ -22,28 +25,80 @@ function parseArgs(args: string[]): RunOptions {
|
|
|
22
25
|
tasksDir,
|
|
23
26
|
dryRun: args.includes('--dry-run'),
|
|
24
27
|
executor: executorIdx >= 0 ? args[executorIdx + 1] || null : null,
|
|
28
|
+
skipDoctor: args.includes('--skip-doctor') || args.includes('--no-doctor'),
|
|
25
29
|
};
|
|
26
30
|
}
|
|
27
31
|
|
|
28
32
|
async function run(args: string[]): Promise<void> {
|
|
29
33
|
const options = parseArgs(args);
|
|
30
34
|
|
|
35
|
+
// Auto-setup Cursor commands if missing or outdated
|
|
36
|
+
if (!areCommandsInstalled()) {
|
|
37
|
+
logger.info('Installing missing or outdated Cursor IDE commands...');
|
|
38
|
+
try {
|
|
39
|
+
setupCommands({ silent: true });
|
|
40
|
+
} catch (e) {
|
|
41
|
+
// Non-blocking
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
31
45
|
if (!options.tasksDir) {
|
|
32
46
|
console.log('\nUsage: cursorflow run <tasks-dir> [options]');
|
|
33
47
|
throw new Error('Tasks directory required');
|
|
34
48
|
}
|
|
35
49
|
|
|
36
|
-
if (!fs.existsSync(options.tasksDir)) {
|
|
37
|
-
throw new Error(`Tasks directory not found: ${options.tasksDir}`);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
50
|
const config = loadConfig();
|
|
51
|
+
const logsDir = getLogsDir(config);
|
|
52
|
+
|
|
53
|
+
// Resolve tasks dir:
|
|
54
|
+
// - Prefer the exact path if it exists relative to cwd
|
|
55
|
+
// - Otherwise, fall back to projectRoot-relative path for better ergonomics
|
|
56
|
+
const tasksDir =
|
|
57
|
+
path.isAbsolute(options.tasksDir)
|
|
58
|
+
? options.tasksDir
|
|
59
|
+
: (fs.existsSync(options.tasksDir)
|
|
60
|
+
? path.resolve(process.cwd(), options.tasksDir)
|
|
61
|
+
: path.join(config.projectRoot, options.tasksDir));
|
|
62
|
+
|
|
63
|
+
if (!fs.existsSync(tasksDir)) {
|
|
64
|
+
throw new Error(`Tasks directory not found: ${tasksDir}`);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Preflight checks (doctor)
|
|
68
|
+
if (!options.skipDoctor) {
|
|
69
|
+
const report = runDoctor({
|
|
70
|
+
cwd: process.cwd(),
|
|
71
|
+
tasksDir,
|
|
72
|
+
executor: options.executor || config.executor,
|
|
73
|
+
includeCursorAgentChecks: true,
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
if (!report.ok) {
|
|
77
|
+
logger.section('🛑 Pre-flight check failed');
|
|
78
|
+
for (const issue of report.issues) {
|
|
79
|
+
const header = `${issue.title} (${issue.id})`;
|
|
80
|
+
if (issue.severity === 'error') {
|
|
81
|
+
logger.error(header, '❌');
|
|
82
|
+
} else {
|
|
83
|
+
logger.warn(header, '⚠️');
|
|
84
|
+
}
|
|
85
|
+
console.log(` ${issue.message}`);
|
|
86
|
+
if (issue.details) console.log(` Details: ${issue.details}`);
|
|
87
|
+
if (issue.fixes?.length) {
|
|
88
|
+
console.log(' Fix:');
|
|
89
|
+
for (const fix of issue.fixes) console.log(` - ${fix}`);
|
|
90
|
+
}
|
|
91
|
+
console.log('');
|
|
92
|
+
}
|
|
93
|
+
throw new Error('Pre-flight checks failed. Run `cursorflow doctor` for details.');
|
|
94
|
+
}
|
|
95
|
+
}
|
|
41
96
|
|
|
42
97
|
try {
|
|
43
|
-
await orchestrate(
|
|
98
|
+
await orchestrate(tasksDir, {
|
|
44
99
|
executor: options.executor || config.executor,
|
|
45
100
|
pollInterval: config.pollInterval * 1000,
|
|
46
|
-
runDir: path.join(
|
|
101
|
+
runDir: path.join(logsDir, 'runs', `run-${Date.now()}`),
|
|
47
102
|
});
|
|
48
103
|
} catch (error: any) {
|
|
49
104
|
// Re-throw to be handled by the main entry point
|
|
@@ -183,6 +183,25 @@ export function uninstallCommands(options: SetupOptions = {}): { removed: number
|
|
|
183
183
|
return { removed };
|
|
184
184
|
}
|
|
185
185
|
|
|
186
|
+
/**
|
|
187
|
+
* Check if commands are already installed
|
|
188
|
+
*/
|
|
189
|
+
export function areCommandsInstalled(): boolean {
|
|
190
|
+
const projectRoot = findProjectRoot();
|
|
191
|
+
const targetDir = path.join(projectRoot, '.cursor', 'commands', 'cursorflow');
|
|
192
|
+
const sourceDir = getCommandsSourceDir();
|
|
193
|
+
|
|
194
|
+
if (!fs.existsSync(targetDir) || !fs.existsSync(sourceDir)) {
|
|
195
|
+
return false;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const sourceFiles = fs.readdirSync(sourceDir).filter(f => f.endsWith('.md'));
|
|
199
|
+
const targetFiles = fs.readdirSync(targetDir).filter(f => f.endsWith('.md'));
|
|
200
|
+
|
|
201
|
+
// Basic check: do we have all the files from source in target?
|
|
202
|
+
return sourceFiles.every(f => targetFiles.includes(f));
|
|
203
|
+
}
|
|
204
|
+
|
|
186
205
|
async function main(args: string[]): Promise<any> {
|
|
187
206
|
const options = parseArgs(args);
|
|
188
207
|
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CursorFlow signal command
|
|
3
|
+
*
|
|
4
|
+
* Send a direct message to a running lane
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import * as path from 'path';
|
|
8
|
+
import * as fs from 'fs';
|
|
9
|
+
import * as logger from '../utils/logger';
|
|
10
|
+
import { loadConfig, getLogsDir } from '../utils/config';
|
|
11
|
+
import { appendLog, createConversationEntry } from '../utils/state';
|
|
12
|
+
|
|
13
|
+
interface SignalOptions {
|
|
14
|
+
lane: string | null;
|
|
15
|
+
message: string | null;
|
|
16
|
+
runDir: string | null;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function parseArgs(args: string[]): SignalOptions {
|
|
20
|
+
const runDirIdx = args.indexOf('--run-dir');
|
|
21
|
+
|
|
22
|
+
// First non-option is lane, second (or rest joined) is message
|
|
23
|
+
const nonOptions = args.filter(a => !a.startsWith('--'));
|
|
24
|
+
|
|
25
|
+
return {
|
|
26
|
+
lane: nonOptions[0] || null,
|
|
27
|
+
message: nonOptions.slice(1).join(' ') || null,
|
|
28
|
+
runDir: runDirIdx >= 0 ? args[runDirIdx + 1] || null : null,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function findLatestRunDir(logsDir: string): string | null {
|
|
33
|
+
const runsDir = path.join(logsDir, 'runs');
|
|
34
|
+
if (!fs.existsSync(runsDir)) return null;
|
|
35
|
+
|
|
36
|
+
const runs = fs.readdirSync(runsDir)
|
|
37
|
+
.filter(d => d.startsWith('run-'))
|
|
38
|
+
.sort()
|
|
39
|
+
.reverse();
|
|
40
|
+
|
|
41
|
+
return runs.length > 0 ? path.join(runsDir, runs[0]!) : null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async function signal(args: string[]): Promise<void> {
|
|
45
|
+
const options = parseArgs(args);
|
|
46
|
+
const config = loadConfig();
|
|
47
|
+
const logsDir = getLogsDir(config);
|
|
48
|
+
|
|
49
|
+
if (!options.lane) {
|
|
50
|
+
throw new Error('Lane name required: cursorflow signal <lane> "<message>"');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (!options.message) {
|
|
54
|
+
throw new Error('Message required: cursorflow signal <lane> "<message>"');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
let runDir = options.runDir;
|
|
58
|
+
if (!runDir) {
|
|
59
|
+
runDir = findLatestRunDir(logsDir);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (!runDir || !fs.existsSync(runDir)) {
|
|
63
|
+
throw new Error(`Run directory not found: ${runDir || 'latest'}`);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const convoPath = path.join(runDir, 'lanes', options.lane, 'conversation.jsonl');
|
|
67
|
+
|
|
68
|
+
if (!fs.existsSync(convoPath)) {
|
|
69
|
+
throw new Error(`Conversation log not found at ${convoPath}. Is the lane running?`);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
logger.info(`Sending signal to lane: ${options.lane}`);
|
|
73
|
+
logger.info(`Message: "${options.message}"`);
|
|
74
|
+
|
|
75
|
+
// Append as a "commander" role message
|
|
76
|
+
// Note: We cast to 'system' or similar if 'commander' isn't in the enum,
|
|
77
|
+
// but let's use 'reviewer' or 'system' which agents usually respect,
|
|
78
|
+
// or update the type definition.
|
|
79
|
+
const entry = createConversationEntry('system', `[COMMANDER INTERVENTION]\n${options.message}`, {
|
|
80
|
+
task: 'DIRECT_SIGNAL'
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
appendLog(convoPath, entry);
|
|
84
|
+
|
|
85
|
+
logger.success('Signal sent successfully. The agent will see this message in its next turn or via file monitoring.');
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export = signal;
|
|
89
|
+
|