@litmers/cursorflow-orchestrator 0.1.3 → 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 +17 -7
- package/README.md +33 -2
- package/commands/cursorflow-doctor.md +24 -0
- package/commands/cursorflow-signal.md +19 -0
- 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/doctor.d.ts +15 -0
- package/dist/cli/doctor.js +139 -0
- package/dist/cli/doctor.js.map +1 -0
- package/dist/cli/index.d.ts +6 -0
- package/dist/cli/index.js +125 -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 +128 -0
- package/dist/cli/resume.js.map +1 -0
- package/dist/cli/run.d.ts +5 -0
- package/dist/cli/run.js +128 -0
- package/dist/cli/run.js.map +1 -0
- package/dist/cli/setup-commands.d.ts +23 -0
- package/dist/cli/setup-commands.js +234 -0
- package/dist/cli/setup-commands.js.map +1 -0
- 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 +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 +51 -0
- package/dist/core/runner.js +499 -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/doctor.d.ts +63 -0
- package/dist/utils/doctor.js +280 -0
- package/dist/utils/doctor.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 +118 -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/doctor.ts +127 -0
- package/src/cli/{index.js → index.ts} +27 -16
- package/src/cli/{init.js → init.ts} +26 -18
- package/src/cli/{monitor.js → monitor.ts} +57 -44
- package/src/cli/resume.ts +119 -0
- package/src/cli/run.ts +109 -0
- package/src/cli/{setup-commands.js → setup-commands.ts} +38 -18
- package/src/cli/signal.ts +89 -0
- 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} +125 -76
- package/src/utils/{config.js → config.ts} +17 -25
- package/src/utils/{cursor-agent.js → cursor-agent.ts} +38 -47
- package/src/utils/doctor.ts +312 -0
- 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 +134 -0
- package/src/cli/resume.js +0 -31
- package/src/cli/run.js +0 -51
|
@@ -1,15 +1,22 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
1
|
/**
|
|
3
2
|
* State management utilities for CursorFlow
|
|
4
3
|
*/
|
|
5
4
|
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
import * as fs from 'fs';
|
|
6
|
+
import * as path from 'path';
|
|
7
|
+
import {
|
|
8
|
+
LaneState,
|
|
9
|
+
ConversationEntry,
|
|
10
|
+
GitLogEntry,
|
|
11
|
+
EventEntry,
|
|
12
|
+
RunnerConfig
|
|
13
|
+
} from './types';
|
|
14
|
+
export { LaneState, ConversationEntry, GitLogEntry, EventEntry };
|
|
8
15
|
|
|
9
16
|
/**
|
|
10
17
|
* Save state to JSON file
|
|
11
18
|
*/
|
|
12
|
-
function saveState(statePath, state) {
|
|
19
|
+
export function saveState(statePath: string, state: any): void {
|
|
13
20
|
const stateDir = path.dirname(statePath);
|
|
14
21
|
|
|
15
22
|
if (!fs.existsSync(stateDir)) {
|
|
@@ -22,15 +29,15 @@ function saveState(statePath, state) {
|
|
|
22
29
|
/**
|
|
23
30
|
* Load state from JSON file
|
|
24
31
|
*/
|
|
25
|
-
function loadState(statePath) {
|
|
32
|
+
export function loadState<T = any>(statePath: string): T | null {
|
|
26
33
|
if (!fs.existsSync(statePath)) {
|
|
27
34
|
return null;
|
|
28
35
|
}
|
|
29
36
|
|
|
30
37
|
try {
|
|
31
38
|
const content = fs.readFileSync(statePath, 'utf8');
|
|
32
|
-
return JSON.parse(content);
|
|
33
|
-
} catch (error) {
|
|
39
|
+
return JSON.parse(content) as T;
|
|
40
|
+
} catch (error: any) {
|
|
34
41
|
console.warn(`Warning: Failed to parse state file ${statePath}: ${error.message}`);
|
|
35
42
|
return null;
|
|
36
43
|
}
|
|
@@ -39,7 +46,7 @@ function loadState(statePath) {
|
|
|
39
46
|
/**
|
|
40
47
|
* Append to JSONL log file
|
|
41
48
|
*/
|
|
42
|
-
function appendLog(logPath, entry) {
|
|
49
|
+
export function appendLog(logPath: string, entry: any): void {
|
|
43
50
|
const logDir = path.dirname(logPath);
|
|
44
51
|
|
|
45
52
|
if (!fs.existsSync(logDir)) {
|
|
@@ -53,7 +60,7 @@ function appendLog(logPath, entry) {
|
|
|
53
60
|
/**
|
|
54
61
|
* Read JSONL log file
|
|
55
62
|
*/
|
|
56
|
-
function readLog(logPath) {
|
|
63
|
+
export function readLog<T = any>(logPath: string): T[] {
|
|
57
64
|
if (!fs.existsSync(logPath)) {
|
|
58
65
|
return [];
|
|
59
66
|
}
|
|
@@ -63,8 +70,8 @@ function readLog(logPath) {
|
|
|
63
70
|
return content
|
|
64
71
|
.split('\n')
|
|
65
72
|
.filter(line => line.trim())
|
|
66
|
-
.map(line => JSON.parse(line));
|
|
67
|
-
} catch (error) {
|
|
73
|
+
.map(line => JSON.parse(line) as T);
|
|
74
|
+
} catch (error: any) {
|
|
68
75
|
console.warn(`Warning: Failed to parse log file ${logPath}: ${error.message}`);
|
|
69
76
|
return [];
|
|
70
77
|
}
|
|
@@ -73,7 +80,7 @@ function readLog(logPath) {
|
|
|
73
80
|
/**
|
|
74
81
|
* Create initial lane state
|
|
75
82
|
*/
|
|
76
|
-
function createLaneState(laneName, config) {
|
|
83
|
+
export function createLaneState(laneName: string, config: RunnerConfig): LaneState {
|
|
77
84
|
return {
|
|
78
85
|
label: laneName,
|
|
79
86
|
status: 'pending',
|
|
@@ -91,7 +98,7 @@ function createLaneState(laneName, config) {
|
|
|
91
98
|
/**
|
|
92
99
|
* Update lane state
|
|
93
100
|
*/
|
|
94
|
-
function updateLaneState(state, updates) {
|
|
101
|
+
export function updateLaneState(state: LaneState, updates: Partial<LaneState>): LaneState {
|
|
95
102
|
return {
|
|
96
103
|
...state,
|
|
97
104
|
...updates,
|
|
@@ -102,10 +109,10 @@ function updateLaneState(state, updates) {
|
|
|
102
109
|
/**
|
|
103
110
|
* Create conversation log entry
|
|
104
111
|
*/
|
|
105
|
-
function createConversationEntry(role, text, options = {}) {
|
|
112
|
+
export function createConversationEntry(role: ConversationEntry['role'], text: string, options: { task?: string; model?: string } = {}): ConversationEntry {
|
|
106
113
|
return {
|
|
107
114
|
timestamp: new Date().toISOString(),
|
|
108
|
-
role,
|
|
115
|
+
role,
|
|
109
116
|
task: options.task || null,
|
|
110
117
|
fullText: text,
|
|
111
118
|
textLength: text.length,
|
|
@@ -116,10 +123,10 @@ function createConversationEntry(role, text, options = {}) {
|
|
|
116
123
|
/**
|
|
117
124
|
* Create git operation log entry
|
|
118
125
|
*/
|
|
119
|
-
function createGitLogEntry(operation, details = {}) {
|
|
126
|
+
export function createGitLogEntry(operation: string, details: any = {}): GitLogEntry {
|
|
120
127
|
return {
|
|
121
128
|
timestamp: new Date().toISOString(),
|
|
122
|
-
operation,
|
|
129
|
+
operation,
|
|
123
130
|
...details,
|
|
124
131
|
};
|
|
125
132
|
}
|
|
@@ -127,7 +134,7 @@ function createGitLogEntry(operation, details = {}) {
|
|
|
127
134
|
/**
|
|
128
135
|
* Create event log entry
|
|
129
136
|
*/
|
|
130
|
-
function createEventEntry(event, data = {}) {
|
|
137
|
+
export function createEventEntry(event: string, data: any = {}): EventEntry {
|
|
131
138
|
return {
|
|
132
139
|
timestamp: new Date().toISOString(),
|
|
133
140
|
event,
|
|
@@ -138,7 +145,7 @@ function createEventEntry(event, data = {}) {
|
|
|
138
145
|
/**
|
|
139
146
|
* Get latest run directory
|
|
140
147
|
*/
|
|
141
|
-
function getLatestRunDir(logsDir) {
|
|
148
|
+
export function getLatestRunDir(logsDir: string): string | null {
|
|
142
149
|
if (!fs.existsSync(logsDir)) {
|
|
143
150
|
return null;
|
|
144
151
|
}
|
|
@@ -152,13 +159,13 @@ function getLatestRunDir(logsDir) {
|
|
|
152
159
|
return null;
|
|
153
160
|
}
|
|
154
161
|
|
|
155
|
-
return path.join(logsDir, runs[0]);
|
|
162
|
+
return path.join(logsDir, runs[0]!);
|
|
156
163
|
}
|
|
157
164
|
|
|
158
165
|
/**
|
|
159
166
|
* List all lanes in a run directory
|
|
160
167
|
*/
|
|
161
|
-
function listLanesInRun(runDir) {
|
|
168
|
+
export function listLanesInRun(runDir: string): { name: string; dir: string; statePath: string }[] {
|
|
162
169
|
if (!fs.existsSync(runDir)) {
|
|
163
170
|
return [];
|
|
164
171
|
}
|
|
@@ -175,8 +182,8 @@ function listLanesInRun(runDir) {
|
|
|
175
182
|
/**
|
|
176
183
|
* Get lane state summary
|
|
177
184
|
*/
|
|
178
|
-
function getLaneStateSummary(statePath) {
|
|
179
|
-
const state = loadState(statePath);
|
|
185
|
+
export function getLaneStateSummary(statePath: string): { status: string; progress: string; label?: string; error?: string | null } {
|
|
186
|
+
const state = loadState<LaneState>(statePath);
|
|
180
187
|
if (!state) {
|
|
181
188
|
return { status: 'unknown', progress: '-' };
|
|
182
189
|
}
|
|
@@ -190,18 +197,3 @@ function getLaneStateSummary(statePath) {
|
|
|
190
197
|
error: state.error,
|
|
191
198
|
};
|
|
192
199
|
}
|
|
193
|
-
|
|
194
|
-
module.exports = {
|
|
195
|
-
saveState,
|
|
196
|
-
loadState,
|
|
197
|
-
appendLog,
|
|
198
|
-
readLog,
|
|
199
|
-
createLaneState,
|
|
200
|
-
updateLaneState,
|
|
201
|
-
createConversationEntry,
|
|
202
|
-
createGitLogEntry,
|
|
203
|
-
createEventEntry,
|
|
204
|
-
getLatestRunDir,
|
|
205
|
-
listLanesInRun,
|
|
206
|
-
getLaneStateSummary,
|
|
207
|
-
};
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared type definitions for CursorFlow
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export interface LaneConfig {
|
|
6
|
+
devPort: number;
|
|
7
|
+
autoCreatePr: boolean;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface CursorFlowConfig {
|
|
11
|
+
tasksDir: string;
|
|
12
|
+
logsDir: string;
|
|
13
|
+
baseBranch: string;
|
|
14
|
+
branchPrefix: string;
|
|
15
|
+
executor: 'cursor-agent' | 'cloud';
|
|
16
|
+
pollInterval: number;
|
|
17
|
+
allowDependencyChange: boolean;
|
|
18
|
+
lockfileReadOnly: boolean;
|
|
19
|
+
enableReview: boolean;
|
|
20
|
+
reviewModel: string;
|
|
21
|
+
maxReviewIterations: number;
|
|
22
|
+
defaultLaneConfig: LaneConfig;
|
|
23
|
+
logLevel: string;
|
|
24
|
+
verboseGit: boolean;
|
|
25
|
+
worktreePrefix: string;
|
|
26
|
+
maxConcurrentLanes: number;
|
|
27
|
+
projectRoot: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface DependencyPolicy {
|
|
31
|
+
allowDependencyChange: boolean;
|
|
32
|
+
lockfileReadOnly: boolean;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface Task {
|
|
36
|
+
name: string;
|
|
37
|
+
prompt: string;
|
|
38
|
+
model?: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface RunnerConfig {
|
|
42
|
+
tasks: Task[];
|
|
43
|
+
pipelineBranch?: string;
|
|
44
|
+
branchPrefix?: string;
|
|
45
|
+
worktreeRoot?: string;
|
|
46
|
+
baseBranch?: string;
|
|
47
|
+
model?: string;
|
|
48
|
+
dependencyPolicy: DependencyPolicy;
|
|
49
|
+
reviewModel?: string;
|
|
50
|
+
maxReviewIterations?: number;
|
|
51
|
+
acceptanceCriteria?: string[];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface DependencyRequestPlan {
|
|
55
|
+
reason: string;
|
|
56
|
+
changes: string[];
|
|
57
|
+
commands: string[];
|
|
58
|
+
notes?: string;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface TaskExecutionResult {
|
|
62
|
+
taskName: string;
|
|
63
|
+
taskBranch: string;
|
|
64
|
+
status: 'FINISHED' | 'ERROR' | 'BLOCKED_DEPENDENCY';
|
|
65
|
+
error?: string;
|
|
66
|
+
dependencyRequest?: DependencyRequestPlan | null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface AgentSendResult {
|
|
70
|
+
ok: boolean;
|
|
71
|
+
exitCode: number;
|
|
72
|
+
error?: string;
|
|
73
|
+
sessionId?: string;
|
|
74
|
+
resultText?: string;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export interface ReviewIssue {
|
|
78
|
+
severity: 'critical' | 'major' | 'minor';
|
|
79
|
+
description: string;
|
|
80
|
+
file?: string;
|
|
81
|
+
suggestion?: string;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export interface ReviewResult {
|
|
85
|
+
status: 'approved' | 'needs_changes';
|
|
86
|
+
buildSuccess: boolean;
|
|
87
|
+
issues: ReviewIssue[];
|
|
88
|
+
suggestions: string[];
|
|
89
|
+
summary: string;
|
|
90
|
+
raw: string;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export interface TaskResult {
|
|
94
|
+
taskName: string;
|
|
95
|
+
taskBranch: string;
|
|
96
|
+
[key: string]: any;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export interface LaneState {
|
|
100
|
+
label: string;
|
|
101
|
+
status: 'pending' | 'running' | 'completed' | 'failed' | 'paused' | 'waiting' | 'reviewing';
|
|
102
|
+
currentTaskIndex: number;
|
|
103
|
+
totalTasks: number;
|
|
104
|
+
worktreeDir: string | null;
|
|
105
|
+
pipelineBranch: string | null;
|
|
106
|
+
startTime: number;
|
|
107
|
+
endTime: number | null;
|
|
108
|
+
error: string | null;
|
|
109
|
+
dependencyRequest: DependencyRequestPlan | null;
|
|
110
|
+
updatedAt?: number;
|
|
111
|
+
tasksFile?: string; // Original tasks file path
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export interface ConversationEntry {
|
|
115
|
+
timestamp: string;
|
|
116
|
+
role: 'user' | 'assistant' | 'reviewer' | 'system';
|
|
117
|
+
task: string | null;
|
|
118
|
+
fullText: string;
|
|
119
|
+
textLength: number;
|
|
120
|
+
model: string | null;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export interface GitLogEntry {
|
|
124
|
+
timestamp: string;
|
|
125
|
+
operation: string;
|
|
126
|
+
[key: string]: any;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export interface EventEntry {
|
|
130
|
+
timestamp: string;
|
|
131
|
+
event: string;
|
|
132
|
+
[key: string]: any;
|
|
133
|
+
}
|
|
134
|
+
|
package/src/cli/resume.js
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* CursorFlow resume command (stub)
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
const logger = require('../utils/logger');
|
|
7
|
-
|
|
8
|
-
function parseArgs(args) {
|
|
9
|
-
return {
|
|
10
|
-
lane: args[0],
|
|
11
|
-
runDir: null,
|
|
12
|
-
clean: args.includes('--clean'),
|
|
13
|
-
restart: args.includes('--restart'),
|
|
14
|
-
};
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
async function resume(args) {
|
|
18
|
-
logger.section('🔁 Resuming Lane');
|
|
19
|
-
|
|
20
|
-
const options = parseArgs(args);
|
|
21
|
-
|
|
22
|
-
logger.info('This command will be fully implemented in the next phase');
|
|
23
|
-
logger.info(`Lane: ${options.lane}`);
|
|
24
|
-
logger.info(`Clean: ${options.clean}`);
|
|
25
|
-
logger.info(`Restart: ${options.restart}`);
|
|
26
|
-
|
|
27
|
-
logger.warn('\n⚠️ Implementation pending');
|
|
28
|
-
logger.info('This will resume interrupted lanes');
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
module.exports = resume;
|
package/src/cli/run.js
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* CursorFlow run command
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
const path = require('path');
|
|
7
|
-
const fs = require('fs');
|
|
8
|
-
const logger = require('../utils/logger');
|
|
9
|
-
const { orchestrate } = require('../core/orchestrator');
|
|
10
|
-
const { loadConfig } = require('../utils/config');
|
|
11
|
-
|
|
12
|
-
function parseArgs(args) {
|
|
13
|
-
return {
|
|
14
|
-
tasksDir: args.find(a => !a.startsWith('--')),
|
|
15
|
-
dryRun: args.includes('--dry-run'),
|
|
16
|
-
executor: args[args.indexOf('--executor') + 1] || null,
|
|
17
|
-
};
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
async function run(args) {
|
|
21
|
-
const options = parseArgs(args);
|
|
22
|
-
|
|
23
|
-
if (!options.tasksDir) {
|
|
24
|
-
logger.error('Tasks directory required');
|
|
25
|
-
console.log('\nUsage: cursorflow run <tasks-dir> [options]');
|
|
26
|
-
process.exit(1);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
if (!fs.existsSync(options.tasksDir)) {
|
|
30
|
-
logger.error(`Tasks directory not found: ${options.tasksDir}`);
|
|
31
|
-
process.exit(1);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const config = loadConfig();
|
|
35
|
-
|
|
36
|
-
try {
|
|
37
|
-
await orchestrate(options.tasksDir, {
|
|
38
|
-
executor: options.executor || config.executor,
|
|
39
|
-
pollInterval: config.pollInterval * 1000,
|
|
40
|
-
runDir: path.join(config.logsDir, 'runs', `run-${Date.now()}`),
|
|
41
|
-
});
|
|
42
|
-
} catch (error) {
|
|
43
|
-
logger.error(`Orchestration failed: ${error.message}`);
|
|
44
|
-
if (process.env.DEBUG) {
|
|
45
|
-
console.error(error.stack);
|
|
46
|
-
}
|
|
47
|
-
process.exit(1);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
module.exports = run;
|