@litmers/cursorflow-orchestrator 0.1.30 → 0.1.34
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 +144 -52
- package/commands/cursorflow-add.md +159 -0
- package/commands/cursorflow-monitor.md +23 -2
- package/commands/cursorflow-new.md +87 -0
- package/dist/cli/add.d.ts +7 -0
- package/dist/cli/add.js +377 -0
- package/dist/cli/add.js.map +1 -0
- package/dist/cli/clean.js +1 -0
- package/dist/cli/clean.js.map +1 -1
- package/dist/cli/config.d.ts +7 -0
- package/dist/cli/config.js +181 -0
- package/dist/cli/config.js.map +1 -0
- package/dist/cli/index.js +34 -30
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/logs.js +7 -33
- package/dist/cli/logs.js.map +1 -1
- package/dist/cli/monitor.js +51 -62
- package/dist/cli/monitor.js.map +1 -1
- package/dist/cli/new.d.ts +7 -0
- package/dist/cli/new.js +232 -0
- package/dist/cli/new.js.map +1 -0
- package/dist/cli/prepare.js +95 -193
- package/dist/cli/prepare.js.map +1 -1
- package/dist/cli/resume.js +11 -47
- package/dist/cli/resume.js.map +1 -1
- package/dist/cli/run.js +27 -22
- package/dist/cli/run.js.map +1 -1
- package/dist/cli/tasks.js +1 -2
- package/dist/cli/tasks.js.map +1 -1
- package/dist/core/failure-policy.d.ts +9 -0
- package/dist/core/failure-policy.js +9 -0
- package/dist/core/failure-policy.js.map +1 -1
- package/dist/core/orchestrator.d.ts +20 -6
- package/dist/core/orchestrator.js +217 -331
- package/dist/core/orchestrator.js.map +1 -1
- package/dist/core/runner/agent.d.ts +27 -0
- package/dist/core/runner/agent.js +294 -0
- package/dist/core/runner/agent.js.map +1 -0
- package/dist/core/runner/index.d.ts +5 -0
- package/dist/core/runner/index.js +22 -0
- package/dist/core/runner/index.js.map +1 -0
- package/dist/core/runner/pipeline.d.ts +9 -0
- package/dist/core/runner/pipeline.js +539 -0
- package/dist/core/runner/pipeline.js.map +1 -0
- package/dist/core/runner/prompt.d.ts +25 -0
- package/dist/core/runner/prompt.js +175 -0
- package/dist/core/runner/prompt.js.map +1 -0
- package/dist/core/runner/task.d.ts +26 -0
- package/dist/core/runner/task.js +283 -0
- package/dist/core/runner/task.js.map +1 -0
- package/dist/core/runner/utils.d.ts +37 -0
- package/dist/core/runner/utils.js +161 -0
- package/dist/core/runner/utils.js.map +1 -0
- package/dist/core/runner.d.ts +2 -96
- package/dist/core/runner.js +11 -1136
- package/dist/core/runner.js.map +1 -1
- package/dist/core/stall-detection.d.ts +326 -0
- package/dist/core/stall-detection.js +781 -0
- package/dist/core/stall-detection.js.map +1 -0
- package/dist/types/config.d.ts +6 -6
- package/dist/types/flow.d.ts +84 -0
- package/dist/types/flow.js +10 -0
- package/dist/types/flow.js.map +1 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.js +3 -3
- package/dist/types/index.js.map +1 -1
- package/dist/types/lane.d.ts +0 -2
- package/dist/types/logging.d.ts +5 -1
- package/dist/types/task.d.ts +7 -11
- package/dist/utils/config.js +7 -15
- package/dist/utils/config.js.map +1 -1
- package/dist/utils/dependency.d.ts +36 -1
- package/dist/utils/dependency.js +256 -1
- package/dist/utils/dependency.js.map +1 -1
- package/dist/utils/enhanced-logger.d.ts +45 -82
- package/dist/utils/enhanced-logger.js +238 -844
- package/dist/utils/enhanced-logger.js.map +1 -1
- package/dist/utils/git.d.ts +29 -0
- package/dist/utils/git.js +115 -5
- package/dist/utils/git.js.map +1 -1
- package/dist/utils/state.js +0 -2
- package/dist/utils/state.js.map +1 -1
- package/dist/utils/task-service.d.ts +2 -2
- package/dist/utils/task-service.js +40 -31
- package/dist/utils/task-service.js.map +1 -1
- package/package.json +4 -3
- package/src/cli/add.ts +397 -0
- package/src/cli/clean.ts +1 -0
- package/src/cli/config.ts +177 -0
- package/src/cli/index.ts +36 -32
- package/src/cli/logs.ts +7 -31
- package/src/cli/monitor.ts +55 -71
- package/src/cli/new.ts +235 -0
- package/src/cli/prepare.ts +98 -205
- package/src/cli/resume.ts +13 -56
- package/src/cli/run.ts +311 -306
- package/src/cli/tasks.ts +1 -2
- package/src/core/failure-policy.ts +9 -0
- package/src/core/orchestrator.ts +281 -375
- package/src/core/runner/agent.ts +314 -0
- package/src/core/runner/index.ts +6 -0
- package/src/core/runner/pipeline.ts +567 -0
- package/src/core/runner/prompt.ts +174 -0
- package/src/core/runner/task.ts +320 -0
- package/src/core/runner/utils.ts +142 -0
- package/src/core/runner.ts +8 -1347
- package/src/core/stall-detection.ts +936 -0
- package/src/types/config.ts +6 -6
- package/src/types/flow.ts +91 -0
- package/src/types/index.ts +15 -3
- package/src/types/lane.ts +0 -2
- package/src/types/logging.ts +5 -1
- package/src/types/task.ts +7 -11
- package/src/utils/config.ts +8 -16
- package/src/utils/dependency.ts +311 -2
- package/src/utils/enhanced-logger.ts +263 -927
- package/src/utils/git.ts +145 -5
- package/src/utils/state.ts +0 -2
- package/src/utils/task-service.ts +48 -40
- package/commands/cursorflow-review.md +0 -56
- package/commands/cursorflow-runs.md +0 -59
- package/dist/cli/runs.d.ts +0 -5
- package/dist/cli/runs.js +0 -214
- package/dist/cli/runs.js.map +0 -1
- package/dist/core/reviewer.d.ts +0 -66
- package/dist/core/reviewer.js +0 -265
- package/dist/core/reviewer.js.map +0 -1
- package/src/cli/runs.ts +0 -212
- package/src/core/reviewer.ts +0 -285
package/src/cli/new.ts
ADDED
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CursorFlow 'new' command
|
|
3
|
+
*
|
|
4
|
+
* Creates a new Flow with empty Lane files
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import * as fs from 'fs';
|
|
8
|
+
import * as path from 'path';
|
|
9
|
+
import * as logger from '../utils/logger';
|
|
10
|
+
import { loadConfig, findProjectRoot } from '../utils/config';
|
|
11
|
+
import { FlowMeta, LaneConfig } from '../types/flow';
|
|
12
|
+
import { safeJoin } from '../utils/path';
|
|
13
|
+
import * as git from '../utils/git';
|
|
14
|
+
|
|
15
|
+
interface NewOptions {
|
|
16
|
+
flowName: string;
|
|
17
|
+
lanes: string[];
|
|
18
|
+
help: boolean;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function printHelp(): void {
|
|
22
|
+
console.log(`
|
|
23
|
+
\x1b[1mcursorflow new\x1b[0m - Flow와 Lane 생성
|
|
24
|
+
|
|
25
|
+
\x1b[1m사용법:\x1b[0m
|
|
26
|
+
cursorflow new <FlowName> --lanes "lane1,lane2,..."
|
|
27
|
+
|
|
28
|
+
\x1b[1m설명:\x1b[0m
|
|
29
|
+
새로운 Flow 디렉토리를 생성하고, 지정된 Lane 파일들의 뼈대를 만듭니다.
|
|
30
|
+
각 Lane에 실제 Task를 추가하려면 'cursorflow add' 명령을 사용하세요.
|
|
31
|
+
|
|
32
|
+
\x1b[1m옵션:\x1b[0m
|
|
33
|
+
--lanes <names> 콤마로 구분된 레인 이름 목록 (필수)
|
|
34
|
+
예: --lanes "backend,frontend,mobile"
|
|
35
|
+
|
|
36
|
+
\x1b[1m예시:\x1b[0m
|
|
37
|
+
# 백엔드와 프론트엔드 2개 레인 생성
|
|
38
|
+
cursorflow new ShopFeature --lanes "backend,frontend"
|
|
39
|
+
|
|
40
|
+
# API, Web, Mobile 3개 레인 생성
|
|
41
|
+
cursorflow new SearchFeature --lanes "api,web,mobile"
|
|
42
|
+
|
|
43
|
+
\x1b[1m생성 결과:\x1b[0m
|
|
44
|
+
_cursorflow/flows/001_ShopFeature/
|
|
45
|
+
├── flow.meta.json # Flow 메타데이터
|
|
46
|
+
├── 01-backend.json # Lane 1 (빈 상태)
|
|
47
|
+
└── 02-frontend.json # Lane 2 (빈 상태)
|
|
48
|
+
`);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function parseArgs(args: string[]): NewOptions {
|
|
52
|
+
const result: NewOptions = {
|
|
53
|
+
flowName: '',
|
|
54
|
+
lanes: [],
|
|
55
|
+
help: false,
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
let i = 0;
|
|
59
|
+
while (i < args.length) {
|
|
60
|
+
const arg = args[i];
|
|
61
|
+
|
|
62
|
+
if (arg === '--help' || arg === '-h') {
|
|
63
|
+
result.help = true;
|
|
64
|
+
} else if (arg === '--lanes' && args[i + 1]) {
|
|
65
|
+
result.lanes = args[++i].split(',').map(l => l.trim()).filter(l => l);
|
|
66
|
+
} else if (!arg.startsWith('--') && !result.flowName) {
|
|
67
|
+
result.flowName = arg;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
i++;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return result;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Get next flow ID by scanning existing flows
|
|
78
|
+
*/
|
|
79
|
+
function getNextFlowId(flowsDir: string): string {
|
|
80
|
+
if (!fs.existsSync(flowsDir)) {
|
|
81
|
+
return '001';
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const dirs = fs.readdirSync(flowsDir)
|
|
85
|
+
.filter(name => {
|
|
86
|
+
const dirPath = safeJoin(flowsDir, name);
|
|
87
|
+
return fs.statSync(dirPath).isDirectory();
|
|
88
|
+
})
|
|
89
|
+
.filter(name => /^\d+_/.test(name));
|
|
90
|
+
|
|
91
|
+
if (dirs.length === 0) {
|
|
92
|
+
return '001';
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const maxId = Math.max(...dirs.map(name => {
|
|
96
|
+
const match = name.match(/^(\d+)_/);
|
|
97
|
+
return match ? parseInt(match[1], 10) : 0;
|
|
98
|
+
}));
|
|
99
|
+
|
|
100
|
+
return String(maxId + 1).padStart(3, '0');
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Create flow.meta.json
|
|
105
|
+
*/
|
|
106
|
+
function createFlowMeta(flowId: string, flowName: string, lanes: string[], baseBranch: string): FlowMeta {
|
|
107
|
+
return {
|
|
108
|
+
id: flowId,
|
|
109
|
+
name: flowName,
|
|
110
|
+
createdAt: new Date().toISOString(),
|
|
111
|
+
createdBy: 'user',
|
|
112
|
+
baseBranch,
|
|
113
|
+
status: 'pending',
|
|
114
|
+
lanes,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Create empty lane config
|
|
120
|
+
*/
|
|
121
|
+
function createEmptyLaneConfig(laneName: string): LaneConfig {
|
|
122
|
+
return {
|
|
123
|
+
laneName,
|
|
124
|
+
tasks: [],
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async function newFlow(args: string[]): Promise<void> {
|
|
129
|
+
const options = parseArgs(args);
|
|
130
|
+
|
|
131
|
+
if (options.help) {
|
|
132
|
+
printHelp();
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Validate inputs
|
|
137
|
+
if (!options.flowName) {
|
|
138
|
+
logger.error('Flow 이름이 필요합니다.');
|
|
139
|
+
console.log('\n사용법: cursorflow new <FlowName> --lanes "lane1,lane2"');
|
|
140
|
+
console.log('도움말: cursorflow new --help');
|
|
141
|
+
process.exit(1);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (options.lanes.length === 0) {
|
|
145
|
+
logger.error('최소 하나의 레인이 필요합니다.');
|
|
146
|
+
console.log('\n예: cursorflow new ' + options.flowName + ' --lanes "backend,frontend"');
|
|
147
|
+
process.exit(1);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Validate lane names (alphanumeric, dash, underscore only)
|
|
151
|
+
const invalidLanes = options.lanes.filter(l => !/^[a-zA-Z0-9_-]+$/.test(l));
|
|
152
|
+
if (invalidLanes.length > 0) {
|
|
153
|
+
logger.error(`잘못된 레인 이름: ${invalidLanes.join(', ')}`);
|
|
154
|
+
console.log('레인 이름은 영문, 숫자, 대시(-), 언더스코어(_)만 사용 가능합니다.');
|
|
155
|
+
process.exit(1);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Check for duplicate lane names
|
|
159
|
+
const uniqueLanes = new Set(options.lanes);
|
|
160
|
+
if (uniqueLanes.size !== options.lanes.length) {
|
|
161
|
+
logger.error('중복된 레인 이름이 있습니다.');
|
|
162
|
+
process.exit(1);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Load config and determine paths
|
|
166
|
+
const projectRoot = findProjectRoot();
|
|
167
|
+
const config = loadConfig(projectRoot);
|
|
168
|
+
const flowsDir = safeJoin(projectRoot, config.flowsDir);
|
|
169
|
+
|
|
170
|
+
// Ensure flows directory exists
|
|
171
|
+
if (!fs.existsSync(flowsDir)) {
|
|
172
|
+
fs.mkdirSync(flowsDir, { recursive: true });
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Get next flow ID
|
|
176
|
+
const flowId = getNextFlowId(flowsDir);
|
|
177
|
+
const flowDirName = `${flowId}_${options.flowName}`;
|
|
178
|
+
const flowDir = safeJoin(flowsDir, flowDirName);
|
|
179
|
+
|
|
180
|
+
// Check if flow already exists
|
|
181
|
+
if (fs.existsSync(flowDir)) {
|
|
182
|
+
logger.error(`Flow 디렉토리가 이미 존재합니다: ${flowDirName}`);
|
|
183
|
+
process.exit(1);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Get current branch as base branch
|
|
187
|
+
let baseBranch = 'main';
|
|
188
|
+
try {
|
|
189
|
+
baseBranch = git.getCurrentBranch(projectRoot);
|
|
190
|
+
} catch {
|
|
191
|
+
logger.warn('현재 브랜치를 가져올 수 없어 main을 기본값으로 사용합니다.');
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Create flow directory
|
|
195
|
+
fs.mkdirSync(flowDir, { recursive: true });
|
|
196
|
+
|
|
197
|
+
// Create flow.meta.json
|
|
198
|
+
const flowMeta = createFlowMeta(flowId, options.flowName, options.lanes, baseBranch);
|
|
199
|
+
const metaPath = safeJoin(flowDir, 'flow.meta.json');
|
|
200
|
+
fs.writeFileSync(metaPath, JSON.stringify(flowMeta, null, 2));
|
|
201
|
+
|
|
202
|
+
// Create lane files
|
|
203
|
+
options.lanes.forEach((laneName, index) => {
|
|
204
|
+
const laneNumber = String(index + 1).padStart(2, '0');
|
|
205
|
+
const laneFileName = `${laneNumber}-${laneName}.json`;
|
|
206
|
+
const lanePath = safeJoin(flowDir, laneFileName);
|
|
207
|
+
|
|
208
|
+
const laneConfig = createEmptyLaneConfig(laneName);
|
|
209
|
+
fs.writeFileSync(lanePath, JSON.stringify(laneConfig, null, 2));
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
// Print success message
|
|
213
|
+
logger.section(`✅ Flow 생성 완료: ${flowDirName}`);
|
|
214
|
+
console.log('');
|
|
215
|
+
console.log(` 📁 ${flowDir}`);
|
|
216
|
+
console.log(` ├── flow.meta.json`);
|
|
217
|
+
options.lanes.forEach((laneName, index) => {
|
|
218
|
+
const laneNumber = String(index + 1).padStart(2, '0');
|
|
219
|
+
const isLast = index === options.lanes.length - 1;
|
|
220
|
+
const prefix = isLast ? '└──' : '├──';
|
|
221
|
+
console.log(` ${prefix} ${laneNumber}-${laneName}.json (빈 상태)`);
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
console.log('');
|
|
225
|
+
logger.info('다음 단계: 각 레인에 태스크를 추가하세요.');
|
|
226
|
+
console.log('');
|
|
227
|
+
options.lanes.forEach((laneName) => {
|
|
228
|
+
console.log(` cursorflow add ${options.flowName} ${laneName} \\`);
|
|
229
|
+
console.log(` --task "name=implement|model=sonnet-4.5|prompt=..."`);
|
|
230
|
+
console.log('');
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
export = newFlow;
|
|
235
|
+
|