@litmers/cursorflow-orchestrator 0.1.31 → 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 +213 -333
- 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 +277 -378
- 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
|
@@ -115,7 +115,6 @@ class TaskService {
|
|
|
115
115
|
const content = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
|
116
116
|
const laneName = this.extractLaneName(fileName);
|
|
117
117
|
const tasks = content.tasks || [];
|
|
118
|
-
const dependsOn = content.dependsOn || [];
|
|
119
118
|
const preset = this.detectPreset(tasks);
|
|
120
119
|
const taskFlow = this.generateTaskFlow(tasks);
|
|
121
120
|
lanes.push({
|
|
@@ -124,7 +123,6 @@ class TaskService {
|
|
|
124
123
|
preset,
|
|
125
124
|
taskCount: tasks.length,
|
|
126
125
|
taskFlow,
|
|
127
|
-
dependsOn,
|
|
128
126
|
});
|
|
129
127
|
}
|
|
130
128
|
catch (error) {
|
|
@@ -219,13 +217,6 @@ class TaskService {
|
|
|
219
217
|
if (lane.taskCount === 0) {
|
|
220
218
|
errors.push(`Lane ${lane.fileName} has no tasks defined`);
|
|
221
219
|
}
|
|
222
|
-
// Check dependencies exist
|
|
223
|
-
for (const dep of lane.dependsOn) {
|
|
224
|
-
const depExists = taskInfo.lanes.some(l => l.laneName === dep || l.fileName === dep || l.fileName === `${dep}.json`);
|
|
225
|
-
if (!depExists) {
|
|
226
|
-
warnings.push(`Lane ${lane.fileName} depends on unknown lane: ${dep}`);
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
220
|
// Validate lane file structure
|
|
230
221
|
try {
|
|
231
222
|
const filePath = path.join(taskInfo.path, lane.fileName);
|
|
@@ -251,10 +242,10 @@ class TaskService {
|
|
|
251
242
|
errors.push(`Lane ${lane.fileName}: Invalid JSON - ${error}`);
|
|
252
243
|
}
|
|
253
244
|
}
|
|
254
|
-
// Check for circular dependencies
|
|
255
|
-
const circularDeps = this.
|
|
245
|
+
// Check for circular task-level dependencies
|
|
246
|
+
const circularDeps = this.detectTaskCircularDependencies(taskInfo);
|
|
256
247
|
if (circularDeps.length > 0) {
|
|
257
|
-
errors.push(`Circular dependencies detected: ${circularDeps.join(' -> ')}`);
|
|
248
|
+
errors.push(`Circular task dependencies detected: ${circularDeps.join(' -> ')}`);
|
|
258
249
|
}
|
|
259
250
|
// Determine overall status
|
|
260
251
|
let status = 'valid';
|
|
@@ -277,36 +268,54 @@ class TaskService {
|
|
|
277
268
|
return result;
|
|
278
269
|
}
|
|
279
270
|
/**
|
|
280
|
-
* Detect circular dependencies in
|
|
271
|
+
* Detect circular dependencies in task-level dependency graph
|
|
281
272
|
*/
|
|
282
|
-
|
|
273
|
+
detectTaskCircularDependencies(taskInfo) {
|
|
274
|
+
// Build task graph from all lane files
|
|
275
|
+
const taskGraph = new Map(); // taskId -> dependencies
|
|
276
|
+
for (const lane of taskInfo.lanes) {
|
|
277
|
+
try {
|
|
278
|
+
const filePath = path.join(taskInfo.path, lane.fileName);
|
|
279
|
+
const content = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
|
280
|
+
const laneName = lane.fileName.replace('.json', '');
|
|
281
|
+
if (Array.isArray(content.tasks)) {
|
|
282
|
+
for (const task of content.tasks) {
|
|
283
|
+
const taskId = `${laneName}:${task.name}`;
|
|
284
|
+
const deps = task.dependsOn || [];
|
|
285
|
+
taskGraph.set(taskId, deps);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
catch {
|
|
290
|
+
// Skip invalid files
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
// DFS to detect cycles
|
|
283
294
|
const visited = new Set();
|
|
284
295
|
const stack = new Set();
|
|
285
296
|
const cycle = [];
|
|
286
|
-
const dfs = (
|
|
287
|
-
if (stack.has(
|
|
288
|
-
cycle.push(
|
|
289
|
-
return true;
|
|
297
|
+
const dfs = (taskId) => {
|
|
298
|
+
if (stack.has(taskId)) {
|
|
299
|
+
cycle.push(taskId);
|
|
300
|
+
return true;
|
|
290
301
|
}
|
|
291
|
-
if (visited.has(
|
|
302
|
+
if (visited.has(taskId)) {
|
|
292
303
|
return false;
|
|
293
304
|
}
|
|
294
|
-
visited.add(
|
|
295
|
-
stack.add(
|
|
296
|
-
const
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
return true;
|
|
302
|
-
}
|
|
305
|
+
visited.add(taskId);
|
|
306
|
+
stack.add(taskId);
|
|
307
|
+
const deps = taskGraph.get(taskId) || [];
|
|
308
|
+
for (const dep of deps) {
|
|
309
|
+
if (dfs(dep)) {
|
|
310
|
+
cycle.unshift(taskId);
|
|
311
|
+
return true;
|
|
303
312
|
}
|
|
304
313
|
}
|
|
305
|
-
stack.delete(
|
|
314
|
+
stack.delete(taskId);
|
|
306
315
|
return false;
|
|
307
316
|
};
|
|
308
|
-
for (const
|
|
309
|
-
if (dfs(
|
|
317
|
+
for (const taskId of taskGraph.keys()) {
|
|
318
|
+
if (dfs(taskId)) {
|
|
310
319
|
break;
|
|
311
320
|
}
|
|
312
321
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"task-service.js","sourceRoot":"","sources":["../../src/utils/task-service.ts"],"names":[],"mappings":";AAAA;;;;;;;;GAQG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"task-service.js","sourceRoot":"","sources":["../../src/utils/task-service.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@litmers/cursorflow-orchestrator",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.34",
|
|
4
4
|
"description": "Git worktree-based parallel AI agent orchestration system for Cursor",
|
|
5
5
|
"main": "dist/cli/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"@types/jest": "^30.0.0",
|
|
37
37
|
"@types/node": "^25.0.3",
|
|
38
|
+
"git-http-mock-server": "^2.0.0",
|
|
38
39
|
"husky": "^9.1.7",
|
|
39
40
|
"jest": "^30.2.0",
|
|
40
41
|
"ts-jest": "^29.4.6",
|
|
@@ -81,7 +82,6 @@
|
|
|
81
82
|
"cf:doctor": "node dist/cli/index.js doctor",
|
|
82
83
|
"cf:signal": "node dist/cli/index.js signal",
|
|
83
84
|
"cf:logs": "node dist/cli/index.js logs",
|
|
84
|
-
"cf:runs": "node dist/cli/index.js runs",
|
|
85
85
|
"cf:models": "node dist/cli/index.js models",
|
|
86
86
|
"test:all": "tests/scripts/run-all-tests.sh",
|
|
87
87
|
"test:quick": "tests/scripts/run-all-tests.sh --quick",
|
|
@@ -91,7 +91,8 @@
|
|
|
91
91
|
"test:templates": "tests/scripts/run-all-tests.sh --module templates",
|
|
92
92
|
"test:git": "tests/scripts/run-all-tests.sh --module git",
|
|
93
93
|
"test:integration": "tests/scripts/run-all-tests.sh --module integration-lifecycle --module integration-logging --module integration-parallel",
|
|
94
|
-
"test:lifecycle": "tests/scripts/run-all-tests.sh --module integration-lifecycle",
|
|
94
|
+
"test:lifecycle": "tests/scripts/run-all-tests.sh --module integration-lifecycle --skip-agent",
|
|
95
|
+
"test:lifecycle:local": "tests/scripts/run-all-tests.sh --module integration-lifecycle-local",
|
|
95
96
|
"prerelease": "npm run build && npm run test && npm run test:quick && npm run security:check"
|
|
96
97
|
}
|
|
97
98
|
}
|
package/src/cli/add.ts
ADDED
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CursorFlow 'add' command
|
|
3
|
+
*
|
|
4
|
+
* Adds tasks to an existing Lane in a Flow
|
|
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, FlowTask, ParsedTaskSpec } from '../types/flow';
|
|
12
|
+
import { safeJoin } from '../utils/path';
|
|
13
|
+
|
|
14
|
+
interface AddOptions {
|
|
15
|
+
flowName: string;
|
|
16
|
+
laneName: string;
|
|
17
|
+
taskSpecs: string[];
|
|
18
|
+
after: string[]; // Dependencies: "lane:task" or "lane"
|
|
19
|
+
help: boolean;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function printHelp(): void {
|
|
23
|
+
console.log(`
|
|
24
|
+
\x1b[1mcursorflow add\x1b[0m - Lane에 Task 추가
|
|
25
|
+
|
|
26
|
+
\x1b[1m사용법:\x1b[0m
|
|
27
|
+
cursorflow add <FlowName> <LaneName> --task "name=...|prompt=..." [--after ...]
|
|
28
|
+
|
|
29
|
+
\x1b[1m설명:\x1b[0m
|
|
30
|
+
지정된 Flow의 Lane에 Task를 추가합니다.
|
|
31
|
+
--task 옵션은 여러 번 사용하여 여러 태스크를 순차적으로 추가할 수 있습니다.
|
|
32
|
+
|
|
33
|
+
\x1b[1m--task 형식:\x1b[0m
|
|
34
|
+
"name=<이름>|prompt=<프롬프트>" (기본 모델 사용)
|
|
35
|
+
"name=<이름>|model=<모델>|prompt=<프롬프트>" (모델 지정)
|
|
36
|
+
|
|
37
|
+
필수 필드:
|
|
38
|
+
name 태스크 이름 (영문, 숫자, 대시, 언더스코어)
|
|
39
|
+
prompt 태스크 프롬프트/지시사항
|
|
40
|
+
|
|
41
|
+
선택 필드:
|
|
42
|
+
model AI 모델 (생략 시 기본 모델 사용, cursorflow config로 설정)
|
|
43
|
+
|
|
44
|
+
\x1b[1m--after 형식:\x1b[0m (선택, 의존성 설정)
|
|
45
|
+
첫 번째 태스크가 시작되기 전에 완료되어야 할 태스크를 지정합니다.
|
|
46
|
+
|
|
47
|
+
"lane" 해당 레인의 마지막 태스크 완료 후 시작
|
|
48
|
+
"lane:task" 특정 태스크 완료 후 시작
|
|
49
|
+
"a:t1, b:t2" 여러 태스크가 모두 완료된 후 시작 (콤마 구분)
|
|
50
|
+
|
|
51
|
+
\x1b[1m예시:\x1b[0m
|
|
52
|
+
# 기본 모델 사용 (model 생략)
|
|
53
|
+
cursorflow add SearchFeature api \\
|
|
54
|
+
--task "name=implement|prompt=검색 API 구현"
|
|
55
|
+
|
|
56
|
+
# 모델 지정
|
|
57
|
+
cursorflow add SearchFeature api \\
|
|
58
|
+
--task "name=plan|model=opus-4.5-thinking|prompt=API 설계"
|
|
59
|
+
|
|
60
|
+
# 의존성 설정
|
|
61
|
+
cursorflow add SearchFeature web \\
|
|
62
|
+
--task "name=ui|prompt=검색 UI 구현" \\
|
|
63
|
+
--after "api:implement"
|
|
64
|
+
|
|
65
|
+
\x1b[1m기본 모델 설정:\x1b[0m
|
|
66
|
+
cursorflow config defaultModel <모델명>
|
|
67
|
+
예: cursorflow config defaultModel gemini-3-flash
|
|
68
|
+
`);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function parseArgs(args: string[]): AddOptions {
|
|
72
|
+
const result: AddOptions = {
|
|
73
|
+
flowName: '',
|
|
74
|
+
laneName: '',
|
|
75
|
+
taskSpecs: [],
|
|
76
|
+
after: [],
|
|
77
|
+
help: false,
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
let i = 0;
|
|
81
|
+
let positionalCount = 0;
|
|
82
|
+
|
|
83
|
+
while (i < args.length) {
|
|
84
|
+
const arg = args[i];
|
|
85
|
+
|
|
86
|
+
if (arg === '--help' || arg === '-h') {
|
|
87
|
+
result.help = true;
|
|
88
|
+
} else if (arg === '--task' && args[i + 1]) {
|
|
89
|
+
result.taskSpecs.push(args[++i]);
|
|
90
|
+
} else if (arg === '--after' && args[i + 1]) {
|
|
91
|
+
// Parse comma-separated dependencies
|
|
92
|
+
const deps = args[++i].split(',').map(d => d.trim()).filter(d => d);
|
|
93
|
+
result.after.push(...deps);
|
|
94
|
+
} else if (!arg.startsWith('--')) {
|
|
95
|
+
if (positionalCount === 0) {
|
|
96
|
+
result.flowName = arg;
|
|
97
|
+
} else if (positionalCount === 1) {
|
|
98
|
+
result.laneName = arg;
|
|
99
|
+
}
|
|
100
|
+
positionalCount++;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
i++;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return result;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Parse task spec string into ParsedTaskSpec
|
|
111
|
+
* Format: "name=value|model=value|prompt=value"
|
|
112
|
+
* Note: model is optional, uses defaultModel from config if not specified
|
|
113
|
+
*/
|
|
114
|
+
function parseTaskSpec(spec: string, defaultModel: string): ParsedTaskSpec {
|
|
115
|
+
const parts = spec.split('|');
|
|
116
|
+
const result: Partial<ParsedTaskSpec> = {};
|
|
117
|
+
|
|
118
|
+
for (const part of parts) {
|
|
119
|
+
const eqIndex = part.indexOf('=');
|
|
120
|
+
if (eqIndex === -1) continue;
|
|
121
|
+
|
|
122
|
+
const key = part.substring(0, eqIndex).trim().toLowerCase();
|
|
123
|
+
const value = part.substring(eqIndex + 1).trim();
|
|
124
|
+
|
|
125
|
+
if (key === 'name') result.name = value;
|
|
126
|
+
else if (key === 'model') result.model = value;
|
|
127
|
+
else if (key === 'prompt') result.prompt = value;
|
|
128
|
+
else if (key === 'timeout') result.timeout = parseInt(value, 10);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Validate required fields
|
|
132
|
+
if (!result.name) {
|
|
133
|
+
throw new Error(`태스크 스펙에 'name' 필드가 필요합니다: ${spec}`);
|
|
134
|
+
}
|
|
135
|
+
if (!result.prompt) {
|
|
136
|
+
throw new Error(`태스크 스펙에 'prompt' 필드가 필요합니다: ${spec}`);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Use default model if not specified
|
|
140
|
+
if (!result.model) {
|
|
141
|
+
result.model = defaultModel;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Validate name format
|
|
145
|
+
if (!/^[a-zA-Z0-9_-]+$/.test(result.name)) {
|
|
146
|
+
throw new Error(`태스크 이름은 영문, 숫자, 대시, 언더스코어만 사용 가능합니다: ${result.name}`);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return result as ParsedTaskSpec;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Find flow directory by name
|
|
154
|
+
*/
|
|
155
|
+
function findFlowDir(flowsDir: string, flowName: string): string | null {
|
|
156
|
+
if (!fs.existsSync(flowsDir)) {
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const dirs = fs.readdirSync(flowsDir)
|
|
161
|
+
.filter(name => {
|
|
162
|
+
const dirPath = safeJoin(flowsDir, name);
|
|
163
|
+
return fs.statSync(dirPath).isDirectory();
|
|
164
|
+
})
|
|
165
|
+
.filter(name => {
|
|
166
|
+
// Match by exact name or by suffix (ignoring ID prefix)
|
|
167
|
+
const match = name.match(/^\d+_(.+)$/);
|
|
168
|
+
return match ? match[1] === flowName : name === flowName;
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
if (dirs.length === 0) {
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Return the most recent one (highest ID)
|
|
176
|
+
dirs.sort((a, b) => b.localeCompare(a));
|
|
177
|
+
return safeJoin(flowsDir, dirs[0]);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Find lane file in flow directory
|
|
182
|
+
*/
|
|
183
|
+
function findLaneFile(flowDir: string, laneName: string): string | null {
|
|
184
|
+
const files = fs.readdirSync(flowDir)
|
|
185
|
+
.filter(name => name.endsWith('.json') && name !== 'flow.meta.json');
|
|
186
|
+
|
|
187
|
+
for (const file of files) {
|
|
188
|
+
// Match by lane name in filename (e.g., "01-backend.json" -> "backend")
|
|
189
|
+
const match = file.match(/^\d+-([^.]+)\.json$/);
|
|
190
|
+
if (match && match[1] === laneName) {
|
|
191
|
+
return safeJoin(flowDir, file);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return null;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Resolve --after dependencies to dependsOn format
|
|
200
|
+
*/
|
|
201
|
+
function resolveAfterDependencies(
|
|
202
|
+
after: string[],
|
|
203
|
+
flowDir: string
|
|
204
|
+
): string[] {
|
|
205
|
+
const dependsOn: string[] = [];
|
|
206
|
+
|
|
207
|
+
for (const dep of after) {
|
|
208
|
+
if (dep.includes(':')) {
|
|
209
|
+
// Already in "lane:task" format
|
|
210
|
+
dependsOn.push(dep);
|
|
211
|
+
} else {
|
|
212
|
+
// Just lane name - find the lane file and get the last task
|
|
213
|
+
const laneFile = findLaneFile(flowDir, dep);
|
|
214
|
+
if (!laneFile) {
|
|
215
|
+
throw new Error(`레인을 찾을 수 없습니다: ${dep}`);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const laneConfig: LaneConfig = JSON.parse(fs.readFileSync(laneFile, 'utf-8'));
|
|
219
|
+
if (laneConfig.tasks.length === 0) {
|
|
220
|
+
throw new Error(`레인 '${dep}'에 태스크가 없습니다. --after를 사용하려면 먼저 태스크를 추가하세요.`);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const lastTask = laneConfig.tasks[laneConfig.tasks.length - 1];
|
|
224
|
+
|
|
225
|
+
// Get lane name from file
|
|
226
|
+
const fileName = path.basename(laneFile);
|
|
227
|
+
const match = fileName.match(/^(\d+-[^.]+)\.json$/);
|
|
228
|
+
const laneId = match ? match[1] : dep;
|
|
229
|
+
|
|
230
|
+
dependsOn.push(`${laneId}:${lastTask.name}`);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
return dependsOn;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
async function addTasks(args: string[]): Promise<void> {
|
|
238
|
+
const options = parseArgs(args);
|
|
239
|
+
|
|
240
|
+
if (options.help) {
|
|
241
|
+
printHelp();
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Validate inputs
|
|
246
|
+
if (!options.flowName) {
|
|
247
|
+
logger.error('Flow 이름이 필요합니다.');
|
|
248
|
+
console.log('\n사용법: cursorflow add <FlowName> <LaneName> --task "..."');
|
|
249
|
+
console.log('도움말: cursorflow add --help');
|
|
250
|
+
process.exit(1);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if (!options.laneName) {
|
|
254
|
+
logger.error('Lane 이름이 필요합니다.');
|
|
255
|
+
console.log('\n사용법: cursorflow add ' + options.flowName + ' <LaneName> --task "..."');
|
|
256
|
+
process.exit(1);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (options.taskSpecs.length === 0) {
|
|
260
|
+
logger.error('최소 하나의 태스크가 필요합니다.');
|
|
261
|
+
console.log('\n예: cursorflow add ' + options.flowName + ' ' + options.laneName + ' \\');
|
|
262
|
+
console.log(' --task "name=implement|model=sonnet-4.5|prompt=기능 구현"');
|
|
263
|
+
process.exit(1);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Load config and find paths
|
|
267
|
+
const projectRoot = findProjectRoot();
|
|
268
|
+
const config = loadConfig(projectRoot);
|
|
269
|
+
const flowsDir = safeJoin(projectRoot, config.flowsDir);
|
|
270
|
+
|
|
271
|
+
// Find flow directory
|
|
272
|
+
const flowDir = findFlowDir(flowsDir, options.flowName);
|
|
273
|
+
if (!flowDir) {
|
|
274
|
+
logger.error(`Flow를 찾을 수 없습니다: ${options.flowName}`);
|
|
275
|
+
console.log('\n사용 가능한 Flow 목록을 확인하세요:');
|
|
276
|
+
console.log(' ls ' + flowsDir);
|
|
277
|
+
process.exit(1);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// Find lane file
|
|
281
|
+
const laneFile = findLaneFile(flowDir, options.laneName);
|
|
282
|
+
if (!laneFile) {
|
|
283
|
+
logger.error(`Lane을 찾을 수 없습니다: ${options.laneName}`);
|
|
284
|
+
|
|
285
|
+
// List available lanes
|
|
286
|
+
const files = fs.readdirSync(flowDir)
|
|
287
|
+
.filter(name => name.endsWith('.json') && name !== 'flow.meta.json');
|
|
288
|
+
if (files.length > 0) {
|
|
289
|
+
console.log('\n사용 가능한 Lane:');
|
|
290
|
+
files.forEach(f => {
|
|
291
|
+
const match = f.match(/^\d+-([^.]+)\.json$/);
|
|
292
|
+
if (match) console.log(' - ' + match[1]);
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
process.exit(1);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// Parse task specs (use defaultModel from config if model not specified)
|
|
299
|
+
const parsedTasks: ParsedTaskSpec[] = [];
|
|
300
|
+
for (const spec of options.taskSpecs) {
|
|
301
|
+
try {
|
|
302
|
+
parsedTasks.push(parseTaskSpec(spec, config.defaultModel));
|
|
303
|
+
} catch (error: any) {
|
|
304
|
+
logger.error(error.message);
|
|
305
|
+
process.exit(1);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// Check for duplicate task names within the specs
|
|
310
|
+
const taskNames = parsedTasks.map(t => t.name);
|
|
311
|
+
const duplicates = taskNames.filter((name, idx) => taskNames.indexOf(name) !== idx);
|
|
312
|
+
if (duplicates.length > 0) {
|
|
313
|
+
logger.error(`중복된 태스크 이름: ${[...new Set(duplicates)].join(', ')}`);
|
|
314
|
+
process.exit(1);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// Load existing lane config
|
|
318
|
+
const laneConfig: LaneConfig = JSON.parse(fs.readFileSync(laneFile, 'utf-8'));
|
|
319
|
+
|
|
320
|
+
// Check for duplicate task names with existing tasks
|
|
321
|
+
const existingNames = laneConfig.tasks.map(t => t.name);
|
|
322
|
+
const conflicts = taskNames.filter(name => existingNames.includes(name));
|
|
323
|
+
if (conflicts.length > 0) {
|
|
324
|
+
logger.error(`이미 존재하는 태스크 이름: ${conflicts.join(', ')}`);
|
|
325
|
+
process.exit(1);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// Resolve --after dependencies
|
|
329
|
+
let dependsOn: string[] = [];
|
|
330
|
+
if (options.after.length > 0) {
|
|
331
|
+
try {
|
|
332
|
+
dependsOn = resolveAfterDependencies(options.after, flowDir);
|
|
333
|
+
} catch (error: any) {
|
|
334
|
+
logger.error(error.message);
|
|
335
|
+
process.exit(1);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// Create FlowTask objects
|
|
340
|
+
const newTasks: FlowTask[] = parsedTasks.map((spec, index) => {
|
|
341
|
+
const task: FlowTask = {
|
|
342
|
+
name: spec.name,
|
|
343
|
+
model: spec.model,
|
|
344
|
+
prompt: spec.prompt,
|
|
345
|
+
};
|
|
346
|
+
|
|
347
|
+
// Add dependsOn only to the first task
|
|
348
|
+
if (index === 0 && dependsOn.length > 0) {
|
|
349
|
+
task.dependsOn = dependsOn;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
if (spec.timeout) {
|
|
353
|
+
task.timeout = spec.timeout;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
return task;
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
// Add tasks to lane
|
|
360
|
+
laneConfig.tasks.push(...newTasks);
|
|
361
|
+
|
|
362
|
+
// Save updated lane config
|
|
363
|
+
fs.writeFileSync(laneFile, JSON.stringify(laneConfig, null, 2));
|
|
364
|
+
|
|
365
|
+
// Print success message
|
|
366
|
+
const laneFileName = path.basename(laneFile);
|
|
367
|
+
logger.section(`✅ ${newTasks.length}개 태스크 추가 완료`);
|
|
368
|
+
console.log('');
|
|
369
|
+
console.log(` 📄 ${laneFileName}`);
|
|
370
|
+
console.log('');
|
|
371
|
+
|
|
372
|
+
newTasks.forEach((task, index) => {
|
|
373
|
+
const isLast = index === newTasks.length - 1;
|
|
374
|
+
const prefix = isLast ? ' └──' : ' ├──';
|
|
375
|
+
let depInfo = '';
|
|
376
|
+
if (task.dependsOn && task.dependsOn.length > 0) {
|
|
377
|
+
depInfo = ` \x1b[33m← ${task.dependsOn.join(', ')}\x1b[0m`;
|
|
378
|
+
}
|
|
379
|
+
console.log(`${prefix} ${task.name} (${task.model})${depInfo}`);
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
console.log('');
|
|
383
|
+
logger.info('전체 태스크 목록:');
|
|
384
|
+
laneConfig.tasks.forEach((task, index) => {
|
|
385
|
+
const isNew = newTasks.some(t => t.name === task.name);
|
|
386
|
+
const marker = isNew ? '\x1b[32m(new)\x1b[0m' : '\x1b[90m(기존)\x1b[0m';
|
|
387
|
+
console.log(` ${index + 1}. ${task.name} ${marker}`);
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
console.log('');
|
|
391
|
+
console.log('다음 단계:');
|
|
392
|
+
console.log(` cursorflow run ${options.flowName} # Flow 실행`);
|
|
393
|
+
console.log(` cursorflow doctor ${options.flowName} # 설정 검증`);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
export = addTasks;
|
|
397
|
+
|
package/src/cli/clean.ts
CHANGED
|
@@ -165,6 +165,7 @@ async function clean(args: string[]): Promise<void> {
|
|
|
165
165
|
}
|
|
166
166
|
|
|
167
167
|
const config = loadConfig();
|
|
168
|
+
git.setVerboseGit(config.verboseGit || false);
|
|
168
169
|
const repoRoot = git.getRepoRoot();
|
|
169
170
|
const logsDir = getLogsDir(config);
|
|
170
171
|
const runsDir = safeJoin(logsDir, 'runs');
|