@litmers/cursorflow-orchestrator 0.1.8 ā 0.1.12
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 +55 -0
- package/README.md +113 -319
- package/commands/cursorflow-clean.md +24 -135
- package/commands/cursorflow-doctor.md +74 -18
- package/commands/cursorflow-init.md +33 -50
- package/commands/cursorflow-models.md +51 -0
- package/commands/cursorflow-monitor.md +56 -118
- package/commands/cursorflow-prepare.md +410 -108
- package/commands/cursorflow-resume.md +51 -148
- package/commands/cursorflow-review.md +38 -202
- package/commands/cursorflow-run.md +208 -86
- package/commands/cursorflow-signal.md +38 -12
- package/dist/cli/clean.d.ts +3 -1
- package/dist/cli/clean.js +145 -8
- package/dist/cli/clean.js.map +1 -1
- package/dist/cli/doctor.js +14 -1
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/index.js +32 -21
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/init.js +5 -4
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/models.d.ts +7 -0
- package/dist/cli/models.js +104 -0
- package/dist/cli/models.js.map +1 -0
- package/dist/cli/monitor.js +56 -1
- package/dist/cli/monitor.js.map +1 -1
- package/dist/cli/prepare.d.ts +7 -0
- package/dist/cli/prepare.js +748 -0
- package/dist/cli/prepare.js.map +1 -0
- package/dist/cli/resume.js +56 -0
- package/dist/cli/resume.js.map +1 -1
- package/dist/cli/run.js +30 -1
- package/dist/cli/run.js.map +1 -1
- package/dist/cli/signal.js +18 -0
- package/dist/cli/signal.js.map +1 -1
- package/dist/core/runner.d.ts +9 -1
- package/dist/core/runner.js +139 -23
- package/dist/core/runner.js.map +1 -1
- package/dist/utils/cursor-agent.d.ts +4 -0
- package/dist/utils/cursor-agent.js +58 -10
- package/dist/utils/cursor-agent.js.map +1 -1
- package/dist/utils/doctor.d.ts +10 -0
- package/dist/utils/doctor.js +581 -1
- package/dist/utils/doctor.js.map +1 -1
- package/dist/utils/types.d.ts +11 -0
- package/examples/README.md +114 -59
- package/examples/demo-project/README.md +61 -79
- package/examples/demo-project/_cursorflow/tasks/demo-test/01-create-utils.json +17 -6
- package/examples/demo-project/_cursorflow/tasks/demo-test/02-add-tests.json +17 -6
- package/examples/demo-project/_cursorflow/tasks/demo-test/README.md +66 -25
- package/package.json +1 -1
- package/scripts/patches/test-cursor-agent.js +203 -0
- package/src/cli/clean.ts +156 -9
- package/src/cli/doctor.ts +18 -2
- package/src/cli/index.ts +33 -21
- package/src/cli/init.ts +6 -4
- package/src/cli/models.ts +83 -0
- package/src/cli/monitor.ts +60 -1
- package/src/cli/prepare.ts +844 -0
- package/src/cli/resume.ts +66 -0
- package/src/cli/run.ts +36 -2
- package/src/cli/signal.ts +22 -0
- package/src/core/runner.ts +164 -23
- package/src/utils/cursor-agent.ts +62 -10
- package/src/utils/doctor.ts +633 -5
- package/src/utils/types.ts +11 -0
package/src/cli/index.ts
CHANGED
|
@@ -7,58 +7,70 @@ import * as logger from '../utils/logger';
|
|
|
7
7
|
// Command functions signature
|
|
8
8
|
type CommandFn = (args: string[]) => Promise<void>;
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
/**
|
|
11
|
+
* Command Registry
|
|
12
|
+
*/
|
|
11
13
|
const COMMANDS: Record<string, CommandFn> = {
|
|
12
14
|
init: require('./init'),
|
|
15
|
+
prepare: require('./prepare'),
|
|
13
16
|
run: require('./run'),
|
|
14
17
|
monitor: require('./monitor'),
|
|
15
18
|
clean: require('./clean'),
|
|
16
19
|
resume: require('./resume'),
|
|
17
20
|
doctor: require('./doctor'),
|
|
18
21
|
signal: require('./signal'),
|
|
22
|
+
models: require('./models'),
|
|
23
|
+
setup: require('./setup-commands').main,
|
|
24
|
+
'setup-commands': require('./setup-commands').main,
|
|
19
25
|
};
|
|
20
26
|
|
|
21
27
|
function printHelp(): void {
|
|
22
28
|
console.log(`
|
|
23
|
-
|
|
29
|
+
\x1b[1m\x1b[36mCursorFlow\x1b[0m - Git worktree-based parallel AI agent orchestration
|
|
24
30
|
|
|
25
|
-
|
|
31
|
+
\x1b[1mUSAGE\x1b[0m
|
|
32
|
+
$ \x1b[32mcursorflow\x1b[0m <command> [options]
|
|
26
33
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
34
|
+
\x1b[1mCOMMANDS\x1b[0m
|
|
35
|
+
\x1b[33minit\x1b[0m [options] Initialize CursorFlow in project
|
|
36
|
+
\x1b[33mprepare\x1b[0m <feature> [opts] Prepare task directory and JSON files
|
|
37
|
+
\x1b[33mrun\x1b[0m <tasks-dir> [options] Run orchestration (DAG-based)
|
|
38
|
+
\x1b[33mmonitor\x1b[0m [run-dir] [options] \x1b[36mInteractive\x1b[0m lane dashboard
|
|
39
|
+
\x1b[33mclean\x1b[0m <type> [options] Clean branches/worktrees/logs
|
|
40
|
+
\x1b[33mresume\x1b[0m <lane> [options] Resume interrupted lane
|
|
41
|
+
\x1b[33mdoctor\x1b[0m [options] Check environment and preflight
|
|
42
|
+
\x1b[33msignal\x1b[0m <lane> <msg> Directly intervene in a running lane
|
|
43
|
+
\x1b[33mmodels\x1b[0m [options] List available AI models
|
|
35
44
|
|
|
36
|
-
|
|
45
|
+
\x1b[1mGLOBAL OPTIONS\x1b[0m
|
|
37
46
|
--config <path> Config file path
|
|
38
47
|
--help, -h Show help
|
|
39
48
|
--version, -v Show version
|
|
40
49
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
50
|
+
\x1b[1mEXAMPLES\x1b[0m
|
|
51
|
+
$ \x1b[32mcursorflow init --example\x1b[0m
|
|
52
|
+
$ \x1b[32mcursorflow prepare NewFeature --lanes 3\x1b[0m
|
|
53
|
+
$ \x1b[32mcursorflow run _cursorflow/tasks/MyFeature/\x1b[0m
|
|
54
|
+
$ \x1b[32mcursorflow monitor latest\x1b[0m
|
|
55
|
+
$ \x1b[32mcursorflow models\x1b[0m
|
|
47
56
|
|
|
48
|
-
|
|
57
|
+
\x1b[1mDOCUMENTATION\x1b[0m
|
|
49
58
|
https://github.com/eungjin-cigro/cursorflow#readme
|
|
50
59
|
`);
|
|
51
60
|
}
|
|
52
61
|
|
|
53
62
|
function printVersion(): void {
|
|
54
63
|
const pkg = require('../../package.json');
|
|
55
|
-
console.log(
|
|
64
|
+
console.log(`\x1b[1m\x1b[36mCursorFlow\x1b[0m v${pkg.version}`);
|
|
56
65
|
}
|
|
57
66
|
|
|
58
67
|
async function main(): Promise<void> {
|
|
59
68
|
const args = process.argv.slice(2);
|
|
60
69
|
|
|
61
|
-
if
|
|
70
|
+
// Only show global help if no arguments provided or if first argument is help-related
|
|
71
|
+
const isGlobalHelp = args.length === 0 || args[0] === 'help' || (args.length === 1 && (args[0] === '--help' || args[0] === '-h'));
|
|
72
|
+
|
|
73
|
+
if (isGlobalHelp) {
|
|
62
74
|
printHelp();
|
|
63
75
|
return;
|
|
64
76
|
}
|
package/src/cli/init.ts
CHANGED
|
@@ -291,15 +291,17 @@ async function init(args: string[]): Promise<void> {
|
|
|
291
291
|
logger.section('ā
CursorFlow initialized successfully!');
|
|
292
292
|
|
|
293
293
|
console.log('\nš Next steps:\n');
|
|
294
|
-
console.log(' 1. Review
|
|
295
|
-
console.log(' 2. Type "/" in Cursor IDE to see available commands');
|
|
294
|
+
console.log(' 1. Review \x1b[32mcursorflow.config.js\x1b[0m');
|
|
295
|
+
console.log(' 2. Type \x1b[33m"/"\x1b[0m in Cursor IDE to see available commands');
|
|
296
296
|
|
|
297
297
|
if (options.example) {
|
|
298
|
-
console.log(` 3. Run:
|
|
298
|
+
console.log(` 3. Run: \x1b[32mcursorflow run ${config.tasksDir}/example/\x1b[0m`);
|
|
299
299
|
} else {
|
|
300
|
-
console.log(' 3. Create
|
|
300
|
+
console.log(' 3. Create your first task: \x1b[32mcursorflow prepare MyFeature\x1b[0m');
|
|
301
301
|
}
|
|
302
302
|
|
|
303
|
+
console.log('\nš” Tip: Use \x1b[33mcursorflow models\x1b[0m to see available AI models.');
|
|
304
|
+
|
|
303
305
|
console.log('\nš Documentation:');
|
|
304
306
|
console.log(' https://github.com/eungjin-cigro/cursorflow#readme');
|
|
305
307
|
console.log('');
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CursorFlow models command
|
|
3
|
+
*
|
|
4
|
+
* List available models
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import * as logger from '../utils/logger';
|
|
8
|
+
import { getAvailableModels } from '../utils/cursor-agent';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Model details metadata
|
|
12
|
+
*/
|
|
13
|
+
const MODEL_METADATA: Record<string, { name: string; provider: string; description: string }> = {
|
|
14
|
+
'sonnet-4.5': { name: 'Claude 3.7 Sonnet', provider: 'Anthropic', description: 'General implementation, fast work (Most versatile)' },
|
|
15
|
+
'sonnet-4.5-thinking': { name: 'Claude 3.7 Sonnet (Thinking)', provider: 'Anthropic', description: 'Code review, deeper reasoning (Thinking model)' },
|
|
16
|
+
'opus-4.5': { name: 'Claude 4.0 Opus', provider: 'Anthropic', description: 'Complex tasks, high quality (Advanced)' },
|
|
17
|
+
'opus-4.5-thinking': { name: 'Claude 4.0 Opus (Thinking)', provider: 'Anthropic', description: 'Architecture design (Premium)' },
|
|
18
|
+
'gpt-5.2': { name: 'GPT-5.2', provider: 'OpenAI', description: 'General tasks' },
|
|
19
|
+
'gpt-5.2-high': { name: 'GPT-5.2 High Reasoning', provider: 'OpenAI', description: 'Advanced reasoning (High performance)' },
|
|
20
|
+
'gemini-3-flash': { name: 'Gemini 3 Flash', provider: 'Google', description: 'General tasks' },
|
|
21
|
+
'gemini-3-pro': { name: 'Gemini 3 Pro', provider: 'Google', description: 'Advanced reasoning (High performance)' }
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
function printHelp(): void {
|
|
25
|
+
console.log(`
|
|
26
|
+
Usage: cursorflow models [options]
|
|
27
|
+
|
|
28
|
+
List available AI models for use in tasks.
|
|
29
|
+
|
|
30
|
+
Options:
|
|
31
|
+
--list, -l List models in a table (default)
|
|
32
|
+
--json Output as JSON
|
|
33
|
+
--help, -h Show help
|
|
34
|
+
|
|
35
|
+
Examples:
|
|
36
|
+
cursorflow models
|
|
37
|
+
cursorflow models --json
|
|
38
|
+
`);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async function models(args: string[]): Promise<void> {
|
|
42
|
+
const isJson = args.includes('--json');
|
|
43
|
+
const isHelp = args.includes('--help') || args.includes('-h');
|
|
44
|
+
|
|
45
|
+
if (isHelp) {
|
|
46
|
+
printHelp();
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Fetch available models from cursor-agent
|
|
51
|
+
const availableModelIds = getAvailableModels();
|
|
52
|
+
|
|
53
|
+
const modelsData = availableModelIds.map(id => {
|
|
54
|
+
const meta = MODEL_METADATA[id] || {
|
|
55
|
+
name: id,
|
|
56
|
+
provider: 'Unknown',
|
|
57
|
+
description: 'Discovered via cursor-agent'
|
|
58
|
+
};
|
|
59
|
+
return { id, ...meta };
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
if (isJson) {
|
|
63
|
+
console.log(JSON.stringify(modelsData, null, 2));
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
logger.section('š¤ Available AI Models (from cursor-agent)');
|
|
68
|
+
|
|
69
|
+
const maxIdLen = Math.max(...modelsData.map(m => m.id.length), 10);
|
|
70
|
+
const maxNameLen = Math.max(...modelsData.map(m => m.name.length), 15);
|
|
71
|
+
const maxProviderLen = Math.max(...modelsData.map(m => m.provider.length), 10);
|
|
72
|
+
|
|
73
|
+
console.log(` ${'ID'.padEnd(maxIdLen)} ${'Name'.padEnd(maxNameLen)} ${'Provider'.padEnd(maxProviderLen)} Description`);
|
|
74
|
+
console.log(` ${'ā'.repeat(maxIdLen)} ${'ā'.repeat(maxNameLen)} ${'ā'.repeat(maxProviderLen)} ${'ā'.repeat(30)}`);
|
|
75
|
+
|
|
76
|
+
for (const m of modelsData) {
|
|
77
|
+
console.log(` ${m.id.padEnd(maxIdLen)} ${m.name.padEnd(maxNameLen)} ${m.provider.padEnd(maxProviderLen)} ${m.description}`);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
console.log('\nš” Tip: Use these IDs in your task JSON files under the "model" field.\n');
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export = models;
|
package/src/cli/monitor.ts
CHANGED
|
@@ -19,6 +19,20 @@ interface LaneWithDeps {
|
|
|
19
19
|
interface MonitorOptions {
|
|
20
20
|
runDir?: string;
|
|
21
21
|
interval: number;
|
|
22
|
+
help: boolean;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function printHelp(): void {
|
|
26
|
+
console.log(`
|
|
27
|
+
Usage: cursorflow monitor [run-dir] [options]
|
|
28
|
+
|
|
29
|
+
Interactive lane dashboard to track progress and dependencies.
|
|
30
|
+
|
|
31
|
+
Options:
|
|
32
|
+
[run-dir] Run directory to monitor (default: latest)
|
|
33
|
+
--interval <seconds> Refresh interval (default: 2)
|
|
34
|
+
--help, -h Show help
|
|
35
|
+
`);
|
|
22
36
|
}
|
|
23
37
|
|
|
24
38
|
enum View {
|
|
@@ -44,6 +58,7 @@ class InteractiveMonitor {
|
|
|
44
58
|
private terminalScrollOffset: number = 0;
|
|
45
59
|
private lastTerminalTotalLines: number = 0;
|
|
46
60
|
private interventionInput: string = '';
|
|
61
|
+
private notification: { message: string; type: 'info' | 'error' | 'success'; time: number } | null = null;
|
|
47
62
|
|
|
48
63
|
constructor(runDir: string, interval: number) {
|
|
49
64
|
this.runDir = runDir;
|
|
@@ -155,6 +170,9 @@ class InteractiveMonitor {
|
|
|
155
170
|
this.terminalScrollOffset = 0;
|
|
156
171
|
this.render();
|
|
157
172
|
break;
|
|
173
|
+
case 'k':
|
|
174
|
+
this.killLane();
|
|
175
|
+
break;
|
|
158
176
|
case 'i':
|
|
159
177
|
const lane = this.lanes.find(l => l.name === this.selectedLaneName);
|
|
160
178
|
if (lane) {
|
|
@@ -304,10 +322,43 @@ class InteractiveMonitor {
|
|
|
304
322
|
this.render();
|
|
305
323
|
}
|
|
306
324
|
|
|
325
|
+
private killLane() {
|
|
326
|
+
if (!this.selectedLaneName) return;
|
|
327
|
+
const lane = this.lanes.find(l => l.name === this.selectedLaneName);
|
|
328
|
+
if (!lane) return;
|
|
329
|
+
|
|
330
|
+
const status = this.getLaneStatus(lane.path, lane.name);
|
|
331
|
+
if (status.pid && status.status === 'running') {
|
|
332
|
+
try {
|
|
333
|
+
process.kill(status.pid, 'SIGTERM');
|
|
334
|
+
this.showNotification(`Sent SIGTERM to PID ${status.pid}`, 'success');
|
|
335
|
+
} catch (e) {
|
|
336
|
+
this.showNotification(`Failed to kill PID ${status.pid}`, 'error');
|
|
337
|
+
}
|
|
338
|
+
} else {
|
|
339
|
+
this.showNotification(`No running process found for ${this.selectedLaneName}`, 'info');
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
private showNotification(message: string, type: 'info' | 'error' | 'success') {
|
|
344
|
+
this.notification = { message, type, time: Date.now() };
|
|
345
|
+
this.render();
|
|
346
|
+
}
|
|
347
|
+
|
|
307
348
|
private render() {
|
|
308
349
|
// Clear screen
|
|
309
350
|
process.stdout.write('\x1Bc');
|
|
310
351
|
|
|
352
|
+
// Clear old notifications
|
|
353
|
+
if (this.notification && Date.now() - this.notification.time > 3000) {
|
|
354
|
+
this.notification = null;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
if (this.notification) {
|
|
358
|
+
const color = this.notification.type === 'error' ? '\x1b[31m' : this.notification.type === 'success' ? '\x1b[32m' : '\x1b[36m';
|
|
359
|
+
console.log(`${color}š ${this.notification.message}\x1b[0m\n`);
|
|
360
|
+
}
|
|
361
|
+
|
|
311
362
|
switch (this.view) {
|
|
312
363
|
case View.LIST:
|
|
313
364
|
this.renderList();
|
|
@@ -413,6 +464,7 @@ class InteractiveMonitor {
|
|
|
413
464
|
console.log('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā\n');
|
|
414
465
|
|
|
415
466
|
process.stdout.write(` Status: ${this.getStatusIcon(status.status)} ${status.status}\n`);
|
|
467
|
+
process.stdout.write(` PID: ${status.pid || '-'}\n`);
|
|
416
468
|
process.stdout.write(` Progress: ${status.progress} (${status.currentTask}/${status.totalTasks} tasks)\n`);
|
|
417
469
|
process.stdout.write(` Time: ${this.formatDuration(status.duration)}\n`);
|
|
418
470
|
process.stdout.write(` Branch: ${status.pipelineBranch}\n`);
|
|
@@ -429,7 +481,7 @@ class InteractiveMonitor {
|
|
|
429
481
|
|
|
430
482
|
console.log('\nš¬ Conversation History (Select to see full details):');
|
|
431
483
|
console.log('ā'.repeat(80));
|
|
432
|
-
process.stdout.write(' [ā/ā] Browse | [ā/Enter] Full Msg | [I] Intervene | [T] Live Terminal | [Esc/ā] Back\n\n');
|
|
484
|
+
process.stdout.write(' [ā/ā] Browse | [ā/Enter] Full Msg | [I] Intervene | [K] Kill | [T] Live Terminal | [Esc/ā] Back\n\n');
|
|
433
485
|
|
|
434
486
|
if (this.currentLogs.length === 0) {
|
|
435
487
|
console.log(' (No messages yet)');
|
|
@@ -670,6 +722,7 @@ class InteractiveMonitor {
|
|
|
670
722
|
dependsOn,
|
|
671
723
|
duration,
|
|
672
724
|
error: state.error,
|
|
725
|
+
pid: state.pid
|
|
673
726
|
};
|
|
674
727
|
}
|
|
675
728
|
|
|
@@ -715,6 +768,12 @@ function findLatestRunDir(logsDir: string): string | null {
|
|
|
715
768
|
* Monitor lanes
|
|
716
769
|
*/
|
|
717
770
|
async function monitor(args: string[]): Promise<void> {
|
|
771
|
+
const help = args.includes('--help') || args.includes('-h');
|
|
772
|
+
if (help) {
|
|
773
|
+
printHelp();
|
|
774
|
+
return;
|
|
775
|
+
}
|
|
776
|
+
|
|
718
777
|
const watchIdx = args.indexOf('--watch');
|
|
719
778
|
const intervalIdx = args.indexOf('--interval');
|
|
720
779
|
const interval = intervalIdx >= 0 ? parseInt(args[intervalIdx + 1] || '2') || 2 : 2;
|