@litmers/cursorflow-orchestrator 0.1.3 → 0.1.5
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 +7 -6
- package/dist/cli/clean.d.ts +5 -0
- package/dist/cli/clean.js +57 -0
- package/dist/cli/clean.js.map +1 -0
- package/dist/cli/index.d.ts +6 -0
- package/dist/cli/index.js +120 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/init.d.ts +7 -0
- package/dist/cli/init.js +302 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/cli/monitor.d.ts +8 -0
- package/dist/cli/monitor.js +210 -0
- package/dist/cli/monitor.js.map +1 -0
- package/dist/cli/resume.d.ts +5 -0
- package/dist/cli/resume.js +58 -0
- package/dist/cli/resume.js.map +1 -0
- package/dist/cli/run.d.ts +5 -0
- package/dist/cli/run.js +74 -0
- package/dist/cli/run.js.map +1 -0
- package/dist/cli/setup-commands.d.ts +19 -0
- package/dist/cli/setup-commands.js +218 -0
- package/dist/cli/setup-commands.js.map +1 -0
- package/dist/core/orchestrator.d.ts +47 -0
- package/dist/core/orchestrator.js +192 -0
- package/dist/core/orchestrator.js.map +1 -0
- package/dist/core/reviewer.d.ts +60 -0
- package/dist/core/reviewer.js +239 -0
- package/dist/core/reviewer.js.map +1 -0
- package/dist/core/runner.d.ts +49 -0
- package/dist/core/runner.js +475 -0
- package/dist/core/runner.js.map +1 -0
- package/dist/utils/config.d.ts +31 -0
- package/dist/utils/config.js +198 -0
- package/dist/utils/config.js.map +1 -0
- package/dist/utils/cursor-agent.d.ts +61 -0
- package/dist/utils/cursor-agent.js +263 -0
- package/dist/utils/cursor-agent.js.map +1 -0
- package/dist/utils/git.d.ts +131 -0
- package/dist/utils/git.js +272 -0
- package/dist/utils/git.js.map +1 -0
- package/dist/utils/logger.d.ts +68 -0
- package/dist/utils/logger.js +158 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/state.d.ts +65 -0
- package/dist/utils/state.js +216 -0
- package/dist/utils/state.js.map +1 -0
- package/dist/utils/types.d.ts +117 -0
- package/dist/utils/types.js +6 -0
- package/dist/utils/types.js.map +1 -0
- package/examples/README.md +155 -0
- package/examples/demo-project/README.md +262 -0
- package/examples/demo-project/_cursorflow/tasks/demo-test/01-create-utils.json +18 -0
- package/examples/demo-project/_cursorflow/tasks/demo-test/02-add-tests.json +18 -0
- package/examples/demo-project/_cursorflow/tasks/demo-test/README.md +109 -0
- package/package.json +71 -61
- package/scripts/ai-security-check.js +11 -4
- package/scripts/local-security-gate.sh +76 -0
- package/src/cli/{clean.js → clean.ts} +11 -5
- package/src/cli/{index.js → index.ts} +22 -16
- package/src/cli/{init.js → init.ts} +26 -18
- package/src/cli/{monitor.js → monitor.ts} +57 -44
- package/src/cli/{resume.js → resume.ts} +11 -5
- package/src/cli/run.ts +54 -0
- package/src/cli/{setup-commands.js → setup-commands.ts} +19 -18
- package/src/core/{orchestrator.js → orchestrator.ts} +44 -26
- package/src/core/{reviewer.js → reviewer.ts} +36 -29
- package/src/core/{runner.js → runner.ts} +78 -56
- package/src/utils/{config.js → config.ts} +17 -25
- package/src/utils/{cursor-agent.js → cursor-agent.ts} +38 -47
- package/src/utils/{git.js → git.ts} +70 -56
- package/src/utils/{logger.js → logger.ts} +170 -178
- package/src/utils/{state.js → state.ts} +30 -38
- package/src/utils/types.ts +133 -0
- package/src/cli/run.js +0 -51
|
@@ -1,11 +1,17 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
1
|
/**
|
|
3
2
|
* CursorFlow clean command (stub)
|
|
4
3
|
*/
|
|
5
4
|
|
|
6
|
-
|
|
5
|
+
import * as logger from '../utils/logger';
|
|
7
6
|
|
|
8
|
-
|
|
7
|
+
interface CleanOptions {
|
|
8
|
+
type?: string;
|
|
9
|
+
pattern: string | null;
|
|
10
|
+
dryRun: boolean;
|
|
11
|
+
force: boolean;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function parseArgs(args: string[]): CleanOptions {
|
|
9
15
|
return {
|
|
10
16
|
type: args[0], // branches | worktrees | logs | all
|
|
11
17
|
pattern: null,
|
|
@@ -14,7 +20,7 @@ function parseArgs(args) {
|
|
|
14
20
|
};
|
|
15
21
|
}
|
|
16
22
|
|
|
17
|
-
async function clean(args) {
|
|
23
|
+
async function clean(args: string[]): Promise<void> {
|
|
18
24
|
logger.section('🧹 Cleaning CursorFlow Resources');
|
|
19
25
|
|
|
20
26
|
const options = parseArgs(args);
|
|
@@ -27,4 +33,4 @@ async function clean(args) {
|
|
|
27
33
|
logger.info('This will clean branches, worktrees, and logs');
|
|
28
34
|
}
|
|
29
35
|
|
|
30
|
-
|
|
36
|
+
export = clean;
|
|
@@ -1,11 +1,14 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
1
|
/**
|
|
3
2
|
* CursorFlow CLI - Main entry point
|
|
4
3
|
*/
|
|
5
4
|
|
|
6
|
-
|
|
5
|
+
import * as logger from '../utils/logger';
|
|
7
6
|
|
|
8
|
-
|
|
7
|
+
// Command functions signature
|
|
8
|
+
type CommandFn = (args: string[]) => Promise<void>;
|
|
9
|
+
|
|
10
|
+
// Lazy load commands to speed up help/version output
|
|
11
|
+
const COMMANDS: Record<string, CommandFn> = {
|
|
9
12
|
init: require('./init'),
|
|
10
13
|
run: require('./run'),
|
|
11
14
|
monitor: require('./monitor'),
|
|
@@ -13,7 +16,7 @@ const COMMANDS = {
|
|
|
13
16
|
resume: require('./resume'),
|
|
14
17
|
};
|
|
15
18
|
|
|
16
|
-
function printHelp() {
|
|
19
|
+
function printHelp(): void {
|
|
17
20
|
console.log(`
|
|
18
21
|
CursorFlow - Git worktree-based parallel AI agent orchestration
|
|
19
22
|
|
|
@@ -42,38 +45,40 @@ Documentation:
|
|
|
42
45
|
`);
|
|
43
46
|
}
|
|
44
47
|
|
|
45
|
-
function printVersion() {
|
|
48
|
+
function printVersion(): void {
|
|
46
49
|
const pkg = require('../../package.json');
|
|
47
50
|
console.log(`CursorFlow v${pkg.version}`);
|
|
48
51
|
}
|
|
49
52
|
|
|
50
|
-
async function main() {
|
|
53
|
+
async function main(): Promise<void> {
|
|
51
54
|
const args = process.argv.slice(2);
|
|
52
55
|
|
|
53
56
|
if (args.length === 0 || args.includes('--help') || args.includes('-h')) {
|
|
54
57
|
printHelp();
|
|
55
|
-
|
|
58
|
+
return;
|
|
56
59
|
}
|
|
57
60
|
|
|
58
61
|
if (args.includes('--version') || args.includes('-v')) {
|
|
59
62
|
printVersion();
|
|
60
|
-
|
|
63
|
+
return;
|
|
61
64
|
}
|
|
62
65
|
|
|
63
|
-
const
|
|
66
|
+
const commandName = args[0]!;
|
|
64
67
|
const commandArgs = args.slice(1);
|
|
65
68
|
|
|
66
|
-
|
|
67
|
-
|
|
69
|
+
const command = COMMANDS[commandName];
|
|
70
|
+
|
|
71
|
+
if (!command) {
|
|
72
|
+
logger.error(`Unknown command: ${commandName}`);
|
|
68
73
|
console.log('\nRun "cursorflow --help" for usage information.');
|
|
69
74
|
process.exit(1);
|
|
70
75
|
}
|
|
71
76
|
|
|
72
77
|
try {
|
|
73
|
-
await
|
|
74
|
-
} catch (error) {
|
|
78
|
+
await command(commandArgs);
|
|
79
|
+
} catch (error: any) {
|
|
75
80
|
logger.error(error.message);
|
|
76
|
-
if (process.env
|
|
81
|
+
if (process.env['DEBUG']) {
|
|
77
82
|
console.error(error.stack);
|
|
78
83
|
}
|
|
79
84
|
process.exit(1);
|
|
@@ -83,11 +88,12 @@ async function main() {
|
|
|
83
88
|
if (require.main === module) {
|
|
84
89
|
main().catch(error => {
|
|
85
90
|
logger.error(`Fatal error: ${error.message}`);
|
|
86
|
-
if (process.env
|
|
91
|
+
if (process.env['DEBUG']) {
|
|
87
92
|
console.error(error.stack);
|
|
88
93
|
}
|
|
89
94
|
process.exit(1);
|
|
90
95
|
});
|
|
91
96
|
}
|
|
92
97
|
|
|
93
|
-
|
|
98
|
+
export default main;
|
|
99
|
+
export { main };
|
|
@@ -1,18 +1,25 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
1
|
/**
|
|
3
2
|
* CursorFlow init command
|
|
4
3
|
*
|
|
5
4
|
* Initialize CursorFlow in a project
|
|
6
5
|
*/
|
|
7
6
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
7
|
+
import * as fs from 'fs';
|
|
8
|
+
import * as path from 'path';
|
|
9
|
+
import * as logger from '../utils/logger';
|
|
10
|
+
import { findProjectRoot, createDefaultConfig, CursorFlowConfig } from '../utils/config';
|
|
11
|
+
import { setupCommands } from './setup-commands';
|
|
13
12
|
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
interface InitOptions {
|
|
14
|
+
example: boolean;
|
|
15
|
+
withCommands: boolean;
|
|
16
|
+
configOnly: boolean;
|
|
17
|
+
force: boolean;
|
|
18
|
+
gitignore: boolean;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function parseArgs(args: string[]): InitOptions {
|
|
22
|
+
const options: InitOptions = {
|
|
16
23
|
example: false,
|
|
17
24
|
withCommands: true,
|
|
18
25
|
configOnly: false,
|
|
@@ -53,7 +60,7 @@ function parseArgs(args) {
|
|
|
53
60
|
return options;
|
|
54
61
|
}
|
|
55
62
|
|
|
56
|
-
function printHelp() {
|
|
63
|
+
function printHelp(): void {
|
|
57
64
|
console.log(`
|
|
58
65
|
Usage: cursorflow init [options]
|
|
59
66
|
|
|
@@ -76,7 +83,7 @@ Examples:
|
|
|
76
83
|
`);
|
|
77
84
|
}
|
|
78
85
|
|
|
79
|
-
function createDirectories(projectRoot, config) {
|
|
86
|
+
function createDirectories(projectRoot: string, config: CursorFlowConfig): void {
|
|
80
87
|
const tasksDir = path.join(projectRoot, config.tasksDir);
|
|
81
88
|
const logsDir = path.join(projectRoot, config.logsDir);
|
|
82
89
|
|
|
@@ -95,7 +102,7 @@ function createDirectories(projectRoot, config) {
|
|
|
95
102
|
}
|
|
96
103
|
}
|
|
97
104
|
|
|
98
|
-
function createExampleTasks(projectRoot, config) {
|
|
105
|
+
function createExampleTasks(projectRoot: string, config: CursorFlowConfig): void {
|
|
99
106
|
const exampleDir = path.join(projectRoot, config.tasksDir, 'example');
|
|
100
107
|
|
|
101
108
|
if (!fs.existsSync(exampleDir)) {
|
|
@@ -164,7 +171,7 @@ cursorflow run ${config.tasksDir}/example/
|
|
|
164
171
|
/**
|
|
165
172
|
* Add _cursorflow to .gitignore
|
|
166
173
|
*/
|
|
167
|
-
function updateGitignore(projectRoot) {
|
|
174
|
+
function updateGitignore(projectRoot: string): void {
|
|
168
175
|
const gitignorePath = path.join(projectRoot, '.gitignore');
|
|
169
176
|
const entry = '_cursorflow/';
|
|
170
177
|
|
|
@@ -209,7 +216,7 @@ function updateGitignore(projectRoot) {
|
|
|
209
216
|
logger.success('Added _cursorflow/ to .gitignore');
|
|
210
217
|
}
|
|
211
218
|
|
|
212
|
-
async function init(args) {
|
|
219
|
+
async function init(args: string[]): Promise<void> {
|
|
213
220
|
logger.section('🚀 Initializing CursorFlow');
|
|
214
221
|
|
|
215
222
|
const options = parseArgs(args);
|
|
@@ -228,7 +235,7 @@ async function init(args) {
|
|
|
228
235
|
try {
|
|
229
236
|
createDefaultConfig(projectRoot, options.force);
|
|
230
237
|
logger.success(`Created config file: cursorflow.config.js`);
|
|
231
|
-
} catch (error) {
|
|
238
|
+
} catch (error: any) {
|
|
232
239
|
if (error.message.includes('already exists') && !options.force) {
|
|
233
240
|
logger.warn(error.message);
|
|
234
241
|
} else {
|
|
@@ -237,7 +244,8 @@ async function init(args) {
|
|
|
237
244
|
}
|
|
238
245
|
}
|
|
239
246
|
|
|
240
|
-
|
|
247
|
+
// We need to require the config file after it might have been created
|
|
248
|
+
const config: CursorFlowConfig = require(configPath);
|
|
241
249
|
|
|
242
250
|
if (options.configOnly) {
|
|
243
251
|
logger.section('✅ Configuration initialized');
|
|
@@ -256,7 +264,7 @@ async function init(args) {
|
|
|
256
264
|
logger.info('\n📝 Updating .gitignore...');
|
|
257
265
|
try {
|
|
258
266
|
updateGitignore(projectRoot);
|
|
259
|
-
} catch (error) {
|
|
267
|
+
} catch (error: any) {
|
|
260
268
|
logger.warn(`Failed to update .gitignore: ${error.message}`);
|
|
261
269
|
logger.info('You can manually add "_cursorflow/" to your .gitignore');
|
|
262
270
|
}
|
|
@@ -267,7 +275,7 @@ async function init(args) {
|
|
|
267
275
|
logger.info('\n📋 Installing Cursor commands...');
|
|
268
276
|
try {
|
|
269
277
|
await setupCommands({ force: options.force, silent: false });
|
|
270
|
-
} catch (error) {
|
|
278
|
+
} catch (error: any) {
|
|
271
279
|
logger.warn(`Failed to install Cursor commands: ${error.message}`);
|
|
272
280
|
logger.info('You can install them later with: npx cursorflow-setup');
|
|
273
281
|
}
|
|
@@ -297,4 +305,4 @@ async function init(args) {
|
|
|
297
305
|
console.log('');
|
|
298
306
|
}
|
|
299
307
|
|
|
300
|
-
|
|
308
|
+
export = init;
|
|
@@ -1,18 +1,24 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
1
|
/**
|
|
3
2
|
* CursorFlow monitor command
|
|
4
3
|
*/
|
|
5
4
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
import * as fs from 'fs';
|
|
6
|
+
import * as path from 'path';
|
|
7
|
+
import * as logger from '../utils/logger';
|
|
8
|
+
import { loadState } from '../utils/state';
|
|
9
|
+
import { LaneState } from '../utils/types';
|
|
10
|
+
import { loadConfig } from '../utils/config';
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
interface MonitorOptions {
|
|
13
|
+
runDir?: string;
|
|
14
|
+
watch: boolean;
|
|
15
|
+
interval: number;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function parseArgs(args: string[]): MonitorOptions {
|
|
13
19
|
const watch = args.includes('--watch');
|
|
14
20
|
const intervalIdx = args.indexOf('--interval');
|
|
15
|
-
const interval = intervalIdx >= 0 ? parseInt(args[intervalIdx + 1]) || 2 : 2;
|
|
21
|
+
const interval = intervalIdx >= 0 ? parseInt(args[intervalIdx + 1] || '2') || 2 : 2;
|
|
16
22
|
|
|
17
23
|
// Find run directory (first non-option argument)
|
|
18
24
|
const runDir = args.find(arg => !arg.startsWith('--') && args.indexOf(arg) !== intervalIdx + 1);
|
|
@@ -27,7 +33,7 @@ function parseArgs(args) {
|
|
|
27
33
|
/**
|
|
28
34
|
* Find the latest run directory
|
|
29
35
|
*/
|
|
30
|
-
function findLatestRunDir(logsDir) {
|
|
36
|
+
function findLatestRunDir(logsDir: string): string | null {
|
|
31
37
|
const runsDir = path.join(logsDir, 'runs');
|
|
32
38
|
|
|
33
39
|
if (!fs.existsSync(runsDir)) {
|
|
@@ -43,13 +49,13 @@ function findLatestRunDir(logsDir) {
|
|
|
43
49
|
}))
|
|
44
50
|
.sort((a, b) => b.mtime - a.mtime);
|
|
45
51
|
|
|
46
|
-
return runs.length > 0 ? runs[0]
|
|
52
|
+
return runs.length > 0 ? runs[0]!.path : null;
|
|
47
53
|
}
|
|
48
54
|
|
|
49
55
|
/**
|
|
50
56
|
* List all lanes in a run directory
|
|
51
57
|
*/
|
|
52
|
-
function listLanes(runDir) {
|
|
58
|
+
function listLanes(runDir: string): { name: string; path: string }[] {
|
|
53
59
|
const lanesDir = path.join(runDir, 'lanes');
|
|
54
60
|
|
|
55
61
|
if (!fs.existsSync(lanesDir)) {
|
|
@@ -70,9 +76,16 @@ function listLanes(runDir) {
|
|
|
70
76
|
/**
|
|
71
77
|
* Get lane status
|
|
72
78
|
*/
|
|
73
|
-
function getLaneStatus(lanePath) {
|
|
79
|
+
function getLaneStatus(lanePath: string): {
|
|
80
|
+
status: string;
|
|
81
|
+
currentTask: number | string;
|
|
82
|
+
totalTasks: number | string;
|
|
83
|
+
progress: string;
|
|
84
|
+
pipelineBranch?: string;
|
|
85
|
+
chatId?: string;
|
|
86
|
+
} {
|
|
74
87
|
const statePath = path.join(lanePath, 'state.json');
|
|
75
|
-
const state = loadState(statePath);
|
|
88
|
+
const state = loadState<LaneState & { chatId?: string }>(statePath);
|
|
76
89
|
|
|
77
90
|
if (!state) {
|
|
78
91
|
return {
|
|
@@ -89,7 +102,7 @@ function getLaneStatus(lanePath) {
|
|
|
89
102
|
|
|
90
103
|
return {
|
|
91
104
|
status: state.status || 'unknown',
|
|
92
|
-
currentTask: state.currentTaskIndex + 1,
|
|
105
|
+
currentTask: (state.currentTaskIndex || 0) + 1,
|
|
93
106
|
totalTasks: state.totalTasks || '?',
|
|
94
107
|
progress: `${progress}%`,
|
|
95
108
|
pipelineBranch: state.pipelineBranch || '-',
|
|
@@ -97,10 +110,25 @@ function getLaneStatus(lanePath) {
|
|
|
97
110
|
};
|
|
98
111
|
}
|
|
99
112
|
|
|
113
|
+
/**
|
|
114
|
+
* Get status icon
|
|
115
|
+
*/
|
|
116
|
+
function getStatusIcon(status: string): string {
|
|
117
|
+
const icons: Record<string, string> = {
|
|
118
|
+
'running': '🔄',
|
|
119
|
+
'completed': '✅',
|
|
120
|
+
'failed': '❌',
|
|
121
|
+
'blocked_dependency': '🚫',
|
|
122
|
+
'no state': '⚪',
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
return icons[status] || '❓';
|
|
126
|
+
}
|
|
127
|
+
|
|
100
128
|
/**
|
|
101
129
|
* Display lane status table
|
|
102
130
|
*/
|
|
103
|
-
function displayStatus(runDir, lanes) {
|
|
131
|
+
function displayStatus(runDir: string, lanes: { name: string; path: string }[]): void {
|
|
104
132
|
console.log('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
105
133
|
console.log(`📊 Run: ${path.basename(runDir)}`);
|
|
106
134
|
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
|
|
@@ -131,25 +159,10 @@ function displayStatus(runDir, lanes) {
|
|
|
131
159
|
console.log();
|
|
132
160
|
}
|
|
133
161
|
|
|
134
|
-
/**
|
|
135
|
-
* Get status icon
|
|
136
|
-
*/
|
|
137
|
-
function getStatusIcon(status) {
|
|
138
|
-
const icons = {
|
|
139
|
-
'running': '🔄',
|
|
140
|
-
'completed': '✅',
|
|
141
|
-
'failed': '❌',
|
|
142
|
-
'blocked_dependency': '🚫',
|
|
143
|
-
'no state': '⚪',
|
|
144
|
-
};
|
|
145
|
-
|
|
146
|
-
return icons[status] || '❓';
|
|
147
|
-
}
|
|
148
|
-
|
|
149
162
|
/**
|
|
150
163
|
* Monitor lanes
|
|
151
164
|
*/
|
|
152
|
-
async function monitor(args) {
|
|
165
|
+
async function monitor(args: string[]): Promise<void> {
|
|
153
166
|
logger.section('📡 Monitoring Lane Execution');
|
|
154
167
|
|
|
155
168
|
const options = parseArgs(args);
|
|
@@ -159,20 +172,18 @@ async function monitor(args) {
|
|
|
159
172
|
let runDir = options.runDir;
|
|
160
173
|
|
|
161
174
|
if (!runDir || runDir === 'latest') {
|
|
162
|
-
runDir = findLatestRunDir(config.logsDir);
|
|
175
|
+
runDir = findLatestRunDir(config.logsDir) || undefined;
|
|
163
176
|
|
|
164
177
|
if (!runDir) {
|
|
165
|
-
logger.error(
|
|
166
|
-
|
|
167
|
-
process.exit(1);
|
|
178
|
+
logger.error(`Runs directory: ${path.join(config.logsDir, 'runs')}`);
|
|
179
|
+
throw new Error('No run directories found');
|
|
168
180
|
}
|
|
169
181
|
|
|
170
182
|
logger.info(`Using latest run: ${path.basename(runDir)}`);
|
|
171
183
|
}
|
|
172
184
|
|
|
173
185
|
if (!fs.existsSync(runDir)) {
|
|
174
|
-
|
|
175
|
-
process.exit(1);
|
|
186
|
+
throw new Error(`Run directory not found: ${runDir}`);
|
|
176
187
|
}
|
|
177
188
|
|
|
178
189
|
// Watch mode
|
|
@@ -187,8 +198,8 @@ async function monitor(args) {
|
|
|
187
198
|
process.stdout.write('\x1Bc');
|
|
188
199
|
}
|
|
189
200
|
|
|
190
|
-
const lanes = listLanes(runDir);
|
|
191
|
-
displayStatus(runDir
|
|
201
|
+
const lanes = listLanes(runDir!);
|
|
202
|
+
displayStatus(runDir!, lanes);
|
|
192
203
|
|
|
193
204
|
iteration++;
|
|
194
205
|
};
|
|
@@ -200,10 +211,12 @@ async function monitor(args) {
|
|
|
200
211
|
const intervalId = setInterval(refresh, options.interval * 1000);
|
|
201
212
|
|
|
202
213
|
// Handle Ctrl+C
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
214
|
+
return new Promise((_, reject) => {
|
|
215
|
+
process.on('SIGINT', () => {
|
|
216
|
+
clearInterval(intervalId);
|
|
217
|
+
console.log('\n👋 Monitoring stopped\n');
|
|
218
|
+
process.exit(0);
|
|
219
|
+
});
|
|
207
220
|
});
|
|
208
221
|
|
|
209
222
|
} else {
|
|
@@ -213,4 +226,4 @@ async function monitor(args) {
|
|
|
213
226
|
}
|
|
214
227
|
}
|
|
215
228
|
|
|
216
|
-
|
|
229
|
+
export = monitor;
|
|
@@ -1,11 +1,17 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
1
|
/**
|
|
3
2
|
* CursorFlow resume command (stub)
|
|
4
3
|
*/
|
|
5
4
|
|
|
6
|
-
|
|
5
|
+
import * as logger from '../utils/logger';
|
|
7
6
|
|
|
8
|
-
|
|
7
|
+
interface ResumeOptions {
|
|
8
|
+
lane?: string;
|
|
9
|
+
runDir: string | null;
|
|
10
|
+
clean: boolean;
|
|
11
|
+
restart: boolean;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function parseArgs(args: string[]): ResumeOptions {
|
|
9
15
|
return {
|
|
10
16
|
lane: args[0],
|
|
11
17
|
runDir: null,
|
|
@@ -14,7 +20,7 @@ function parseArgs(args) {
|
|
|
14
20
|
};
|
|
15
21
|
}
|
|
16
22
|
|
|
17
|
-
async function resume(args) {
|
|
23
|
+
async function resume(args: string[]): Promise<void> {
|
|
18
24
|
logger.section('🔁 Resuming Lane');
|
|
19
25
|
|
|
20
26
|
const options = parseArgs(args);
|
|
@@ -28,4 +34,4 @@ async function resume(args) {
|
|
|
28
34
|
logger.info('This will resume interrupted lanes');
|
|
29
35
|
}
|
|
30
36
|
|
|
31
|
-
|
|
37
|
+
export = resume;
|
package/src/cli/run.ts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
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 { loadConfig } from '../utils/config';
|
|
10
|
+
|
|
11
|
+
interface RunOptions {
|
|
12
|
+
tasksDir?: string;
|
|
13
|
+
dryRun: boolean;
|
|
14
|
+
executor: string | null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function parseArgs(args: string[]): RunOptions {
|
|
18
|
+
const tasksDir = args.find(a => !a.startsWith('--'));
|
|
19
|
+
const executorIdx = args.indexOf('--executor');
|
|
20
|
+
|
|
21
|
+
return {
|
|
22
|
+
tasksDir,
|
|
23
|
+
dryRun: args.includes('--dry-run'),
|
|
24
|
+
executor: executorIdx >= 0 ? args[executorIdx + 1] || null : null,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async function run(args: string[]): Promise<void> {
|
|
29
|
+
const options = parseArgs(args);
|
|
30
|
+
|
|
31
|
+
if (!options.tasksDir) {
|
|
32
|
+
console.log('\nUsage: cursorflow run <tasks-dir> [options]');
|
|
33
|
+
throw new Error('Tasks directory required');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (!fs.existsSync(options.tasksDir)) {
|
|
37
|
+
throw new Error(`Tasks directory not found: ${options.tasksDir}`);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const config = loadConfig();
|
|
41
|
+
|
|
42
|
+
try {
|
|
43
|
+
await orchestrate(options.tasksDir, {
|
|
44
|
+
executor: options.executor || config.executor,
|
|
45
|
+
pollInterval: config.pollInterval * 1000,
|
|
46
|
+
runDir: path.join(config.logsDir, 'runs', `run-${Date.now()}`),
|
|
47
|
+
});
|
|
48
|
+
} catch (error: any) {
|
|
49
|
+
// Re-throw to be handled by the main entry point
|
|
50
|
+
throw new Error(`Orchestration failed: ${error.message}`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export = run;
|
|
@@ -1,17 +1,22 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
1
|
/**
|
|
3
2
|
* Setup Cursor commands
|
|
4
3
|
*
|
|
5
4
|
* Installs CursorFlow commands to .cursor/commands/cursorflow/
|
|
6
5
|
*/
|
|
7
6
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
import * as fs from 'fs';
|
|
8
|
+
import * as path from 'path';
|
|
9
|
+
import * as logger from '../utils/logger';
|
|
10
|
+
import { findProjectRoot } from '../utils/config';
|
|
12
11
|
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
interface SetupOptions {
|
|
13
|
+
force?: boolean;
|
|
14
|
+
uninstall?: boolean;
|
|
15
|
+
silent?: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function parseArgs(args: string[]): SetupOptions {
|
|
19
|
+
const options: SetupOptions = {
|
|
15
20
|
force: false,
|
|
16
21
|
uninstall: false,
|
|
17
22
|
silent: false,
|
|
@@ -39,7 +44,7 @@ function parseArgs(args) {
|
|
|
39
44
|
return options;
|
|
40
45
|
}
|
|
41
46
|
|
|
42
|
-
function printHelp() {
|
|
47
|
+
function printHelp(): void {
|
|
43
48
|
console.log(`
|
|
44
49
|
Usage: cursorflow-setup [options]
|
|
45
50
|
|
|
@@ -58,12 +63,12 @@ Examples:
|
|
|
58
63
|
`);
|
|
59
64
|
}
|
|
60
65
|
|
|
61
|
-
function getCommandsSourceDir() {
|
|
66
|
+
function getCommandsSourceDir(): string {
|
|
62
67
|
// Commands are in the package directory
|
|
63
68
|
return path.join(__dirname, '..', '..', 'commands');
|
|
64
69
|
}
|
|
65
70
|
|
|
66
|
-
function setupCommands(options = {}) {
|
|
71
|
+
export function setupCommands(options: SetupOptions = {}): { installed: number; backed: number; skipped: number } {
|
|
67
72
|
const projectRoot = findProjectRoot();
|
|
68
73
|
const targetDir = path.join(projectRoot, '.cursor', 'commands', 'cursorflow');
|
|
69
74
|
const sourceDir = getCommandsSourceDir();
|
|
@@ -139,7 +144,7 @@ function setupCommands(options = {}) {
|
|
|
139
144
|
return { installed, backed, skipped };
|
|
140
145
|
}
|
|
141
146
|
|
|
142
|
-
function uninstallCommands(options = {}) {
|
|
147
|
+
export function uninstallCommands(options: SetupOptions = {}): { removed: number } {
|
|
143
148
|
const projectRoot = findProjectRoot();
|
|
144
149
|
const targetDir = path.join(projectRoot, '.cursor', 'commands', 'cursorflow');
|
|
145
150
|
|
|
@@ -178,7 +183,7 @@ function uninstallCommands(options = {}) {
|
|
|
178
183
|
return { removed };
|
|
179
184
|
}
|
|
180
185
|
|
|
181
|
-
async function main(args) {
|
|
186
|
+
async function main(args: string[]): Promise<any> {
|
|
182
187
|
const options = parseArgs(args);
|
|
183
188
|
|
|
184
189
|
try {
|
|
@@ -187,7 +192,7 @@ async function main(args) {
|
|
|
187
192
|
} else {
|
|
188
193
|
return setupCommands(options);
|
|
189
194
|
}
|
|
190
|
-
} catch (error) {
|
|
195
|
+
} catch (error: any) {
|
|
191
196
|
if (!options.silent) {
|
|
192
197
|
logger.error(error.message);
|
|
193
198
|
}
|
|
@@ -198,13 +203,9 @@ async function main(args) {
|
|
|
198
203
|
if (require.main === module) {
|
|
199
204
|
main(process.argv.slice(2)).catch(error => {
|
|
200
205
|
console.error('❌ Error:', error.message);
|
|
201
|
-
if (process.env
|
|
206
|
+
if (process.env['DEBUG']) {
|
|
202
207
|
console.error(error.stack);
|
|
203
208
|
}
|
|
204
209
|
process.exit(1);
|
|
205
210
|
});
|
|
206
211
|
}
|
|
207
|
-
|
|
208
|
-
module.exports = setupCommands;
|
|
209
|
-
module.exports.setupCommands = setupCommands;
|
|
210
|
-
module.exports.uninstallCommands = uninstallCommands;
|