@litmers/cursorflow-orchestrator 0.1.31 → 0.1.36
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 +27 -0
- package/README.md +182 -59
- package/commands/cursorflow-add.md +159 -0
- package/commands/cursorflow-doctor.md +45 -23
- package/commands/cursorflow-monitor.md +23 -2
- package/commands/cursorflow-new.md +87 -0
- package/commands/cursorflow-run.md +60 -111
- 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/doctor.js +47 -4
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/index.js +34 -30
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/logs.js +17 -34
- package/dist/cli/logs.js.map +1 -1
- package/dist/cli/monitor.js +62 -65
- 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 +57 -68
- package/dist/cli/resume.js.map +1 -1
- package/dist/cli/run.js +60 -30
- package/dist/cli/run.js.map +1 -1
- package/dist/cli/stop.js +6 -0
- package/dist/cli/stop.js.map +1 -1
- package/dist/cli/tasks.d.ts +5 -3
- package/dist/cli/tasks.js +181 -29
- 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 +215 -334
- 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/services/logging/console.js +2 -1
- package/dist/services/logging/console.js.map +1 -1
- 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.d.ts +5 -1
- package/dist/utils/config.js +15 -16
- 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/doctor.js +40 -8
- package/dist/utils/doctor.js.map +1 -1
- package/dist/utils/enhanced-logger.d.ts +45 -82
- package/dist/utils/enhanced-logger.js +239 -844
- package/dist/utils/enhanced-logger.js.map +1 -1
- package/dist/utils/flow.d.ts +9 -0
- package/dist/utils/flow.js +73 -0
- package/dist/utils/flow.js.map +1 -0
- 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/doctor.ts +48 -4
- package/src/cli/index.ts +36 -32
- package/src/cli/logs.ts +20 -33
- package/src/cli/monitor.ts +70 -75
- package/src/cli/new.ts +235 -0
- package/src/cli/prepare.ts +98 -205
- package/src/cli/resume.ts +61 -76
- package/src/cli/run.ts +333 -306
- package/src/cli/stop.ts +8 -0
- package/src/cli/tasks.ts +200 -21
- package/src/core/failure-policy.ts +9 -0
- package/src/core/orchestrator.ts +279 -379
- 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/services/logging/console.ts +2 -1
- 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 +16 -17
- package/src/utils/dependency.ts +311 -2
- package/src/utils/doctor.ts +36 -8
- package/src/utils/enhanced-logger.ts +264 -927
- package/src/utils/flow.ts +42 -0
- 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/prepare.ts
CHANGED
|
@@ -21,124 +21,105 @@ interface PrepareOptions {
|
|
|
21
21
|
lanes: number;
|
|
22
22
|
template: string | null;
|
|
23
23
|
preset: PresetType | null; // --preset complex|simple|merge
|
|
24
|
-
sequential: boolean;
|
|
25
|
-
deps: string | null;
|
|
26
24
|
// Terminal-first options
|
|
27
25
|
prompt: string | null;
|
|
28
26
|
criteria: string[];
|
|
29
27
|
model: string | null;
|
|
30
|
-
taskSpecs: string[]; // Multiple --task "name|model|prompt|criteria"
|
|
28
|
+
taskSpecs: string[]; // Multiple --task "name|model|prompt|criteria|dependsOn|timeout"
|
|
31
29
|
// Incremental options
|
|
32
30
|
addLane: string | null; // Add lane to existing task dir
|
|
33
31
|
addTask: string | null; // Add task to existing lane file
|
|
34
|
-
dependsOnLanes: string[]; // --depends-on for new lane
|
|
35
32
|
force: boolean;
|
|
36
33
|
help: boolean;
|
|
37
34
|
}
|
|
38
35
|
|
|
39
36
|
function printHelp(): void {
|
|
40
37
|
console.log(`
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
Prepare task files for a new feature - Terminal-first workflow.
|
|
38
|
+
cursorflow prepare - 태스크 파일 생성
|
|
44
39
|
|
|
45
40
|
═══════════════════════════════════════════════════════════════════════════════
|
|
46
|
-
|
|
41
|
+
시나리오: "쇼핑몰" 프로젝트에서 백엔드 API와 프론트엔드 동시 개발
|
|
47
42
|
═══════════════════════════════════════════════════════════════════════════════
|
|
48
43
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
cursorflow prepare FeatureName --preset complex --prompt "Implement user auth"
|
|
53
|
-
|
|
54
|
-
# Simple implementation: implement → test
|
|
55
|
-
cursorflow prepare BugFix --preset simple --prompt "Fix login bug"
|
|
44
|
+
[Case 1] 가장 간단하게 - 버그 하나 고치기
|
|
45
|
+
─────────────────────────────────────────
|
|
46
|
+
cursorflow prepare FixCartBug --prompt "장바구니 수량 버그 수정"
|
|
56
47
|
|
|
57
|
-
|
|
58
|
-
|
|
48
|
+
결과: _cursorflow/tasks/2412251030_FixCartBug/
|
|
49
|
+
└── 01-FixCartBug.json (implement 태스크 1개)
|
|
59
50
|
|
|
60
|
-
# Multiple sequential lanes (auto-detects merge preset for dependent lanes)
|
|
61
|
-
cursorflow prepare FullStack --lanes 3 --sequential --prompt "Build your layer"
|
|
62
51
|
|
|
63
|
-
|
|
52
|
+
[Case 2] 프리셋 사용 - 계획부터 테스트까지
|
|
53
|
+
─────────────────────────────────────────
|
|
54
|
+
cursorflow prepare PaymentAPI --preset complex --prompt "Stripe 결제 연동"
|
|
64
55
|
|
|
65
|
-
|
|
66
|
-
cursorflow prepare --add-lane _cursorflow/tasks/2412211530_FullStack \\
|
|
67
|
-
--preset merge --depends-on "01-lane-1,02-lane-2"
|
|
56
|
+
결과: 01-PaymentAPI.json에 plan → implement → test 태스크 생성
|
|
68
57
|
|
|
69
|
-
|
|
58
|
+
프리셋:
|
|
59
|
+
--preset complex plan → implement → test
|
|
60
|
+
--preset simple implement → test
|
|
61
|
+
(없으면) implement만
|
|
70
62
|
|
|
71
|
-
# Append a task to an existing lane
|
|
72
|
-
cursorflow prepare --add-task _cursorflow/tasks/2412211530_FullStack/01-lane-1.json \\
|
|
73
|
-
--task "verify|sonnet-4.5|Double-check all requirements|All criteria met"
|
|
74
63
|
|
|
75
|
-
|
|
64
|
+
[Case 3] 병렬 레인 - 백엔드/프론트 동시 개발
|
|
65
|
+
─────────────────────────────────────────
|
|
66
|
+
cursorflow prepare ShopFeature --lanes 2 --preset complex \\
|
|
67
|
+
--prompt "상품 검색 기능"
|
|
76
68
|
|
|
77
|
-
|
|
69
|
+
결과: 01-lane-1.json (백엔드) ─┬─ 동시 실행
|
|
70
|
+
02-lane-2.json (프론트) ─┘
|
|
78
71
|
|
|
79
|
-
## Step 5: Run
|
|
80
72
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
73
|
+
[Case 4] 의존성 - 프론트가 백엔드 완료 후 시작
|
|
74
|
+
─────────────────────────────────────────
|
|
75
|
+
cursorflow prepare --add-task ./02-lane-2.json \\
|
|
76
|
+
--task "integrate|sonnet-4.5|API 연동|완료|01-lane-1:implement"
|
|
77
|
+
└─ 이 태스크 완료 후 시작
|
|
84
78
|
|
|
85
|
-
|
|
79
|
+
실행 흐름:
|
|
80
|
+
01-lane-1: [plan] → [implement] → [test]
|
|
81
|
+
↓ 완료되면
|
|
82
|
+
02-lane-2: [plan] ───────┴─────→ [integrate]
|
|
86
83
|
|
|
87
|
-
--preset complex plan → implement → test (for complex features)
|
|
88
|
-
--preset simple implement → test (for simple changes)
|
|
89
|
-
--preset merge merge → test (auto-applied when --depends-on is set)
|
|
90
84
|
|
|
91
|
-
|
|
85
|
+
[Case 5] 커스텀 태스크 - 원하는 대로 구성
|
|
86
|
+
─────────────────────────────────────────
|
|
87
|
+
cursorflow prepare CustomFlow \\
|
|
88
|
+
--task "setup|sonnet-4.5|DB 스키마 생성|완료" \\
|
|
89
|
+
--task "api|sonnet-4.5|REST API 구현|동작" \\
|
|
90
|
+
--task "test|sonnet-4.5|테스트 작성|통과"
|
|
92
91
|
|
|
93
|
-
Core:
|
|
94
|
-
<feature-name> Name of the feature (for new task directories)
|
|
95
|
-
--lanes <num> Number of lanes to create (default: 1)
|
|
96
|
-
--preset <type> Use preset template: complex | simple | merge
|
|
97
92
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
--task <spec> Full task spec: "name|model|prompt|criteria|dependsOn|timeout" (repeatable)
|
|
93
|
+
[Case 6] 나중에 추가 - 레인이나 태스크 덧붙이기
|
|
94
|
+
─────────────────────────────────────────
|
|
95
|
+
# 새 레인 추가
|
|
96
|
+
cursorflow prepare --add-lane ./tasks/ShopFeature --preset simple
|
|
103
97
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
--
|
|
107
|
-
--depends-on <lanes> Dependencies for --add-lane: "01-lane-1,02-lane-2"
|
|
108
|
-
Task-level deps: In --task, add "lane:task" at the end.
|
|
109
|
-
Example: "test|sonnet-4.5|Run tests|All pass|01-lane-1:setup"
|
|
110
|
-
Task-level timeout: In --task, add milliseconds at the end.
|
|
111
|
-
Example: "heavy|sonnet-4.5|Big task|Done||1200000"
|
|
112
|
-
|
|
113
|
-
Incremental (add to existing):
|
|
114
|
-
--add-lane <dir> Add a new lane to existing task directory
|
|
115
|
-
--add-task <file> Append task(s) to existing lane JSON file
|
|
116
|
-
|
|
117
|
-
Advanced:
|
|
118
|
-
--template <path|url|name> External template JSON file, URL, or built-in name
|
|
119
|
-
--force Overwrite existing files
|
|
98
|
+
# 기존 레인에 태스크 추가
|
|
99
|
+
cursorflow prepare --add-task ./01-lane-1.json \\
|
|
100
|
+
--task "docs|sonnet-4.5|API 문서화|완성"
|
|
120
101
|
|
|
121
102
|
═══════════════════════════════════════════════════════════════════════════════
|
|
122
103
|
|
|
123
|
-
|
|
104
|
+
--task 형식: "이름|모델|프롬프트|완료조건|의존성|타임아웃"
|
|
124
105
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
106
|
+
예시:
|
|
107
|
+
"build|sonnet-4.5|빌드하기|완료" 기본
|
|
108
|
+
"deploy|sonnet-4.5|배포|성공|01-lane:build" 의존성
|
|
109
|
+
"heavy|sonnet-4.5|대용량|완료||1200000" 타임아웃 20분
|
|
128
110
|
|
|
129
|
-
|
|
130
|
-
cursorflow prepare FixLoginBug --preset simple \\
|
|
131
|
-
--prompt "Fix the login validation bug in auth.ts"
|
|
132
|
-
|
|
133
|
-
# 3. Add a merge/integration lane
|
|
134
|
-
cursorflow prepare --add-lane _cursorflow/tasks/2412211530_AuthSystem \\
|
|
135
|
-
--preset merge --depends-on "01-lane-1,02-lane-2"
|
|
111
|
+
═══════════════════════════════════════════════════════════════════════════════
|
|
136
112
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
113
|
+
옵션 요약:
|
|
114
|
+
--prompt <text> 작업 설명
|
|
115
|
+
--preset <type> complex | simple | merge
|
|
116
|
+
--lanes <num> 병렬 레인 수 (기본: 1)
|
|
117
|
+
--task <spec> 커스텀 태스크 (반복 가능)
|
|
118
|
+
--add-lane <dir> 기존 디렉토리에 레인 추가
|
|
119
|
+
--add-task <file> 기존 레인에 태스크 추가
|
|
120
|
+
--model <model> AI 모델 (기본: sonnet-4.5)
|
|
121
|
+
--template <path> 외부 템플릿 파일
|
|
122
|
+
--force 덮어쓰기
|
|
142
123
|
`);
|
|
143
124
|
}
|
|
144
125
|
|
|
@@ -148,15 +129,12 @@ function parseArgs(args: string[]): PrepareOptions {
|
|
|
148
129
|
lanes: 1,
|
|
149
130
|
template: null,
|
|
150
131
|
preset: null,
|
|
151
|
-
sequential: false,
|
|
152
|
-
deps: null,
|
|
153
132
|
prompt: null,
|
|
154
133
|
criteria: [],
|
|
155
134
|
model: null,
|
|
156
135
|
taskSpecs: [],
|
|
157
136
|
addLane: null,
|
|
158
137
|
addTask: null,
|
|
159
|
-
dependsOnLanes: [],
|
|
160
138
|
force: false,
|
|
161
139
|
help: false,
|
|
162
140
|
};
|
|
@@ -169,8 +147,6 @@ function parseArgs(args: string[]): PrepareOptions {
|
|
|
169
147
|
result.help = true;
|
|
170
148
|
} else if (arg === '--force') {
|
|
171
149
|
result.force = true;
|
|
172
|
-
} else if (arg === '--sequential') {
|
|
173
|
-
result.sequential = true;
|
|
174
150
|
} else if (arg === '--lanes' && args[i + 1]) {
|
|
175
151
|
result.lanes = parseInt(args[++i]) || 1;
|
|
176
152
|
} else if (arg === '--template' && args[i + 1]) {
|
|
@@ -182,8 +158,6 @@ function parseArgs(args: string[]): PrepareOptions {
|
|
|
182
158
|
} else {
|
|
183
159
|
throw new Error(`Invalid preset: "${presetValue}". Must be one of: complex, simple, merge`);
|
|
184
160
|
}
|
|
185
|
-
} else if (arg === '--deps' && args[i + 1]) {
|
|
186
|
-
result.deps = args[++i];
|
|
187
161
|
} else if (arg === '--prompt' && args[i + 1]) {
|
|
188
162
|
result.prompt = args[++i];
|
|
189
163
|
} else if (arg === '--criteria' && args[i + 1]) {
|
|
@@ -196,8 +170,6 @@ function parseArgs(args: string[]): PrepareOptions {
|
|
|
196
170
|
result.addLane = args[++i];
|
|
197
171
|
} else if (arg === '--add-task' && args[i + 1]) {
|
|
198
172
|
result.addTask = args[++i];
|
|
199
|
-
} else if (arg === '--depends-on' && args[i + 1]) {
|
|
200
|
-
result.dependsOnLanes = args[++i].split(',').map(d => d.trim()).filter(d => d);
|
|
201
173
|
} else if (!arg.startsWith('--') && !result.featureName) {
|
|
202
174
|
result.featureName = arg;
|
|
203
175
|
}
|
|
@@ -216,10 +188,7 @@ function parseTaskSpec(spec: string): Task {
|
|
|
216
188
|
throw new Error(`Invalid task spec: "${spec}". Expected format: "name|model|prompt[|criteria[|dependsOn[|timeout]]]"`);
|
|
217
189
|
}
|
|
218
190
|
|
|
219
|
-
const [name, model, prompt,
|
|
220
|
-
const acceptanceCriteria = criteriaStr
|
|
221
|
-
? criteriaStr.split(',').map(c => c.trim()).filter(c => c)
|
|
222
|
-
: undefined;
|
|
191
|
+
const [name, model, prompt, _criteriaStr, depsStr, timeoutStr] = parts;
|
|
223
192
|
|
|
224
193
|
const dependsOn = depsStr
|
|
225
194
|
? depsStr.split(',').map(d => d.trim()).filter(d => d)
|
|
@@ -231,7 +200,6 @@ function parseTaskSpec(spec: string): Task {
|
|
|
231
200
|
name: name.trim(),
|
|
232
201
|
model: model.trim() || 'sonnet-4.5',
|
|
233
202
|
prompt: prompt.trim(),
|
|
234
|
-
...(acceptanceCriteria && acceptanceCriteria.length > 0 ? { acceptanceCriteria } : {}),
|
|
235
203
|
...(dependsOn && dependsOn.length > 0 ? { dependsOn } : {}),
|
|
236
204
|
...(timeout ? { timeout } : {}),
|
|
237
205
|
};
|
|
@@ -286,11 +254,6 @@ The plan document should include:
|
|
|
286
254
|
- Data structures and interfaces
|
|
287
255
|
- Step-by-step implementation tasks
|
|
288
256
|
- Potential risks and edge cases`,
|
|
289
|
-
acceptanceCriteria: [
|
|
290
|
-
`Plan document saved to ${planDocPath}`,
|
|
291
|
-
'All required files are identified',
|
|
292
|
-
'Approach is clearly defined',
|
|
293
|
-
],
|
|
294
257
|
},
|
|
295
258
|
{
|
|
296
259
|
name: 'implement',
|
|
@@ -318,11 +281,6 @@ ${basePrompt}
|
|
|
318
281
|
- Refer back to the plan document if unsure about any step.
|
|
319
282
|
- Verify all edge cases from the plan are handled.
|
|
320
283
|
- Ensure code follows project conventions.`,
|
|
321
|
-
acceptanceCriteria: criteria.length > 0 ? criteria : [
|
|
322
|
-
'Code implemented according to plan',
|
|
323
|
-
'No build errors',
|
|
324
|
-
'All edge cases handled',
|
|
325
|
-
],
|
|
326
284
|
},
|
|
327
285
|
{
|
|
328
286
|
name: 'test',
|
|
@@ -346,11 +304,6 @@ Write comprehensive tests for the implementation.
|
|
|
346
304
|
## Important
|
|
347
305
|
- All tests must pass before completing.
|
|
348
306
|
- Cover happy path and error cases from the plan.`,
|
|
349
|
-
acceptanceCriteria: [
|
|
350
|
-
'Unit tests written',
|
|
351
|
-
'All tests pass',
|
|
352
|
-
'Edge cases covered',
|
|
353
|
-
],
|
|
354
307
|
}
|
|
355
308
|
);
|
|
356
309
|
break;
|
|
@@ -375,11 +328,6 @@ ${basePrompt}
|
|
|
375
328
|
## Important
|
|
376
329
|
- Keep changes focused and minimal.
|
|
377
330
|
- Follow existing code conventions.`,
|
|
378
|
-
acceptanceCriteria: criteria.length > 0 ? criteria : [
|
|
379
|
-
'Implementation complete',
|
|
380
|
-
'No build errors',
|
|
381
|
-
'Code follows conventions',
|
|
382
|
-
],
|
|
383
331
|
},
|
|
384
332
|
{
|
|
385
333
|
name: 'test',
|
|
@@ -397,10 +345,6 @@ Test the implementation thoroughly.
|
|
|
397
345
|
|
|
398
346
|
## Important
|
|
399
347
|
- All tests must pass before completing.`,
|
|
400
|
-
acceptanceCriteria: [
|
|
401
|
-
'Tests written/updated',
|
|
402
|
-
'All tests pass',
|
|
403
|
-
],
|
|
404
348
|
}
|
|
405
349
|
);
|
|
406
350
|
break;
|
|
@@ -427,11 +371,6 @@ Merge dependent branches and resolve any conflicts.
|
|
|
427
371
|
- Resolve all conflicts cleanly.
|
|
428
372
|
- Ensure code from all merged branches works together.
|
|
429
373
|
- Check that no functionality was broken by the merge.`,
|
|
430
|
-
acceptanceCriteria: [
|
|
431
|
-
'All conflicts resolved',
|
|
432
|
-
'No build errors',
|
|
433
|
-
'Integration verified',
|
|
434
|
-
],
|
|
435
374
|
},
|
|
436
375
|
{
|
|
437
376
|
name: 'test',
|
|
@@ -451,11 +390,6 @@ Run comprehensive tests after the merge.
|
|
|
451
390
|
## Important
|
|
452
391
|
- All tests must pass.
|
|
453
392
|
- Test the interaction between merged features.`,
|
|
454
|
-
acceptanceCriteria: criteria.length > 0 ? criteria : [
|
|
455
|
-
'All unit tests pass',
|
|
456
|
-
'Integration tests pass',
|
|
457
|
-
'No regressions',
|
|
458
|
-
],
|
|
459
393
|
}
|
|
460
394
|
);
|
|
461
395
|
break;
|
|
@@ -468,9 +402,9 @@ function buildTasksFromOptions(
|
|
|
468
402
|
options: PrepareOptions,
|
|
469
403
|
laneNumber: number,
|
|
470
404
|
featureName: string,
|
|
471
|
-
|
|
405
|
+
_hasDependencies: boolean = false
|
|
472
406
|
): Task[] {
|
|
473
|
-
// Priority: --task > --preset
|
|
407
|
+
// Priority: --task > --preset > --prompt alone > default
|
|
474
408
|
|
|
475
409
|
// 1. Explicit --task specifications (highest priority)
|
|
476
410
|
if (options.taskSpecs.length > 0) {
|
|
@@ -481,18 +415,16 @@ function buildTasksFromOptions(
|
|
|
481
415
|
return tasks;
|
|
482
416
|
}
|
|
483
417
|
|
|
484
|
-
// 2. Preset template (use when --preset specified
|
|
418
|
+
// 2. Preset template (use when --preset specified)
|
|
485
419
|
// --prompt serves as context when used with preset
|
|
486
|
-
if (options.preset
|
|
487
|
-
// Auto-apply merge preset if lane has dependencies and no explicit preset
|
|
488
|
-
const preset = options.preset || (hasDependencies ? 'merge' : 'complex');
|
|
420
|
+
if (options.preset) {
|
|
489
421
|
return buildTasksFromPreset(
|
|
490
|
-
preset,
|
|
422
|
+
options.preset,
|
|
491
423
|
featureName,
|
|
492
424
|
laneNumber,
|
|
493
425
|
options.prompt || `Implement ${featureName}`,
|
|
494
426
|
options.criteria,
|
|
495
|
-
|
|
427
|
+
false
|
|
496
428
|
);
|
|
497
429
|
}
|
|
498
430
|
|
|
@@ -504,10 +436,6 @@ function buildTasksFromOptions(
|
|
|
504
436
|
prompt: options.prompt,
|
|
505
437
|
};
|
|
506
438
|
|
|
507
|
-
if (options.criteria.length > 0) {
|
|
508
|
-
task.acceptanceCriteria = options.criteria;
|
|
509
|
-
}
|
|
510
|
-
|
|
511
439
|
return [task];
|
|
512
440
|
}
|
|
513
441
|
|
|
@@ -518,7 +446,7 @@ function buildTasksFromOptions(
|
|
|
518
446
|
laneNumber,
|
|
519
447
|
`Implement ${featureName}`,
|
|
520
448
|
options.criteria,
|
|
521
|
-
|
|
449
|
+
false
|
|
522
450
|
);
|
|
523
451
|
}
|
|
524
452
|
|
|
@@ -538,11 +466,6 @@ function getDefaultConfig(laneNumber: number, featureName: string, tasks: Task[]
|
|
|
538
466
|
lockfileReadOnly: true,
|
|
539
467
|
},
|
|
540
468
|
|
|
541
|
-
// Review Settings
|
|
542
|
-
enableReview: true,
|
|
543
|
-
reviewModel: 'sonnet-4.5-thinking',
|
|
544
|
-
maxReviewIterations: 3,
|
|
545
|
-
|
|
546
469
|
// Lane Metadata
|
|
547
470
|
laneNumber: laneNumber,
|
|
548
471
|
devPort: 3000 + laneNumber,
|
|
@@ -552,24 +475,6 @@ function getDefaultConfig(laneNumber: number, featureName: string, tasks: Task[]
|
|
|
552
475
|
};
|
|
553
476
|
}
|
|
554
477
|
|
|
555
|
-
function parseDeps(depsStr: string): Map<number, number[]> {
|
|
556
|
-
const map = new Map<number, number[]>();
|
|
557
|
-
// Format: "2:1;3:1,2"
|
|
558
|
-
const lanes = depsStr.split(';');
|
|
559
|
-
for (const lane of lanes) {
|
|
560
|
-
const [targetStr, depsPart] = lane.split(':');
|
|
561
|
-
if (!targetStr || !depsPart) continue;
|
|
562
|
-
|
|
563
|
-
const target = parseInt(targetStr);
|
|
564
|
-
const deps = depsPart.split(',').map(d => parseInt(d)).filter(d => !isNaN(d));
|
|
565
|
-
|
|
566
|
-
if (!isNaN(target) && deps.length > 0) {
|
|
567
|
-
map.set(target, deps);
|
|
568
|
-
}
|
|
569
|
-
}
|
|
570
|
-
return map;
|
|
571
|
-
}
|
|
572
|
-
|
|
573
478
|
function replacePlaceholders(obj: any, context: { featureName: string; laneNumber: number; devPort: number }): any {
|
|
574
479
|
if (typeof obj === 'string') {
|
|
575
480
|
return obj
|
|
@@ -626,8 +531,6 @@ async function addLaneToDir(options: PrepareOptions): Promise<void> {
|
|
|
626
531
|
const fileName = `${laneNumber.toString().padStart(2, '0')}-${laneName}.json`;
|
|
627
532
|
const filePath = safeJoin(taskDir, fileName);
|
|
628
533
|
|
|
629
|
-
const hasDependencies = options.dependsOnLanes.length > 0;
|
|
630
|
-
|
|
631
534
|
// Load template if provided
|
|
632
535
|
let template = null;
|
|
633
536
|
if (options.template) {
|
|
@@ -639,24 +542,18 @@ async function addLaneToDir(options: PrepareOptions): Promise<void> {
|
|
|
639
542
|
if (template) {
|
|
640
543
|
taskConfig = { ...template, laneNumber, devPort: 3000 + laneNumber };
|
|
641
544
|
} else {
|
|
642
|
-
// Build tasks from options
|
|
643
|
-
const tasks = buildTasksFromOptions(options, laneNumber, featureName,
|
|
545
|
+
// Build tasks from options
|
|
546
|
+
const tasks = buildTasksFromOptions(options, laneNumber, featureName, false);
|
|
644
547
|
taskConfig = getDefaultConfig(laneNumber, featureName, tasks);
|
|
645
548
|
}
|
|
646
549
|
|
|
647
550
|
// Replace placeholders
|
|
648
|
-
const
|
|
551
|
+
const finalConfig = replacePlaceholders(taskConfig, {
|
|
649
552
|
featureName,
|
|
650
553
|
laneNumber,
|
|
651
554
|
devPort: 3000 + laneNumber,
|
|
652
555
|
});
|
|
653
556
|
|
|
654
|
-
// Add dependencies if specified
|
|
655
|
-
const finalConfig = {
|
|
656
|
-
...processedConfig,
|
|
657
|
-
...(hasDependencies ? { dependsOn: options.dependsOnLanes } : {}),
|
|
658
|
-
};
|
|
659
|
-
|
|
660
557
|
// Use atomic write with wx flag to avoid TOCTOU race condition (unless force is set)
|
|
661
558
|
// SECURITY NOTE: Writing user-defined task configuration to the file system.
|
|
662
559
|
// The input is from CLI arguments and templates, used to generate CursorFlow lane files.
|
|
@@ -672,10 +569,9 @@ async function addLaneToDir(options: PrepareOptions): Promise<void> {
|
|
|
672
569
|
|
|
673
570
|
const tasksList = finalConfig.tasks || [];
|
|
674
571
|
const taskSummary = tasksList.map((t: any) => t.name).join(' → ');
|
|
675
|
-
const
|
|
676
|
-
const presetInfo = options.preset ? ` [${options.preset}]` : (hasDependencies ? ' [merge]' : (template ? ' [template]' : ''));
|
|
572
|
+
const presetInfo = options.preset ? ` [${options.preset}]` : (template ? ' [template]' : '');
|
|
677
573
|
|
|
678
|
-
logger.success(`Added lane: ${fileName} [${taskSummary}]${presetInfo}
|
|
574
|
+
logger.success(`Added lane: ${fileName} [${taskSummary}]${presetInfo}`);
|
|
679
575
|
logger.info(`Directory: ${taskDir}`);
|
|
680
576
|
|
|
681
577
|
console.log(`\nNext steps:`);
|
|
@@ -750,29 +646,17 @@ async function createNewFeature(options: PrepareOptions): Promise<void> {
|
|
|
750
646
|
template = await resolveTemplate(options.template);
|
|
751
647
|
}
|
|
752
648
|
|
|
753
|
-
|
|
754
|
-
const dependencyMap = options.sequential
|
|
755
|
-
? new Map(Array.from({ length: options.lanes - 1 }, (_, i) => [i + 2, [i + 1]]))
|
|
756
|
-
: (options.deps ? parseDeps(options.deps) : new Map<number, number[]>());
|
|
757
|
-
|
|
758
|
-
const laneInfoList: { name: string; fileName: string; dependsOn: string[]; preset: string }[] = [];
|
|
649
|
+
const laneInfoList: { name: string; fileName: string; preset: string }[] = [];
|
|
759
650
|
|
|
760
651
|
for (let i = 1; i <= options.lanes; i++) {
|
|
761
652
|
const laneName = `lane-${i}`;
|
|
762
653
|
const fileName = `${i.toString().padStart(2, '0')}-${laneName}.json`;
|
|
763
654
|
const filePath = safeJoin(taskDir, fileName);
|
|
764
655
|
|
|
765
|
-
const depNums = dependencyMap.get(i) || [];
|
|
766
|
-
const dependsOn = depNums.map(n => {
|
|
767
|
-
const depLaneName = `lane-${n}`;
|
|
768
|
-
return `${n.toString().padStart(2, '0')}-${depLaneName}`;
|
|
769
|
-
});
|
|
770
|
-
|
|
771
|
-
const hasDependencies = dependsOn.length > 0;
|
|
772
656
|
const devPort = 3000 + i;
|
|
773
657
|
|
|
774
658
|
let taskConfig;
|
|
775
|
-
let effectivePreset: EffectivePresetType = options.preset ||
|
|
659
|
+
let effectivePreset: EffectivePresetType = options.preset || 'complex';
|
|
776
660
|
|
|
777
661
|
if (template) {
|
|
778
662
|
// Use template
|
|
@@ -780,31 +664,25 @@ async function createNewFeature(options: PrepareOptions): Promise<void> {
|
|
|
780
664
|
effectivePreset = 'custom';
|
|
781
665
|
} else {
|
|
782
666
|
// Build from CLI options
|
|
783
|
-
const tasks = buildTasksFromOptions(options, i, options.featureName,
|
|
667
|
+
const tasks = buildTasksFromOptions(options, i, options.featureName, false);
|
|
784
668
|
taskConfig = getDefaultConfig(i, options.featureName, tasks);
|
|
785
669
|
}
|
|
786
670
|
|
|
787
671
|
// Replace placeholders
|
|
788
|
-
const
|
|
672
|
+
const finalConfig = replacePlaceholders(taskConfig, {
|
|
789
673
|
featureName: options.featureName,
|
|
790
674
|
laneNumber: i,
|
|
791
675
|
devPort: devPort,
|
|
792
676
|
});
|
|
793
677
|
|
|
794
|
-
// Add dependencies if any
|
|
795
|
-
const finalConfig = {
|
|
796
|
-
...processedConfig,
|
|
797
|
-
...(dependsOn.length > 0 ? { dependsOn } : {}),
|
|
798
|
-
};
|
|
799
|
-
|
|
800
678
|
// SECURITY NOTE: Writing generated lane configuration (containing user prompts) to file system.
|
|
801
679
|
fs.writeFileSync(filePath, JSON.stringify(finalConfig, null, 2) + '\n', 'utf8');
|
|
802
680
|
|
|
803
681
|
const taskSummary = finalConfig.tasks?.map((t: any) => t.name).join(' → ') || 'default';
|
|
804
682
|
const presetLabel = effectivePreset !== 'custom' ? ` [${effectivePreset}]` : '';
|
|
805
|
-
logger.success(`Created: ${fileName} [${taskSummary}]${presetLabel}
|
|
683
|
+
logger.success(`Created: ${fileName} [${taskSummary}]${presetLabel}`);
|
|
806
684
|
|
|
807
|
-
laneInfoList.push({ name: laneName, fileName,
|
|
685
|
+
laneInfoList.push({ name: laneName, fileName, preset: effectivePreset });
|
|
808
686
|
}
|
|
809
687
|
|
|
810
688
|
// Create README
|
|
@@ -826,14 +704,29 @@ cursorflow run ${path.relative(config.projectRoot, taskDir)}
|
|
|
826
704
|
|
|
827
705
|
## Lanes
|
|
828
706
|
|
|
829
|
-
${laneInfoList.map(l => `- **${l.fileName.replace('.json', '')}** [${l.preset}]
|
|
707
|
+
${laneInfoList.map(l => `- **${l.fileName.replace('.json', '')}** [${l.preset}]`).join('\n')}
|
|
708
|
+
|
|
709
|
+
## Task-Level Dependencies
|
|
710
|
+
|
|
711
|
+
To make a task wait for another task to complete before starting, use the \`dependsOn\` field:
|
|
712
|
+
|
|
713
|
+
\`\`\`json
|
|
714
|
+
{
|
|
715
|
+
"tasks": [
|
|
716
|
+
{
|
|
717
|
+
"name": "my-task",
|
|
718
|
+
"prompt": "...",
|
|
719
|
+
"dependsOn": ["other-lane:other-task"]
|
|
720
|
+
}
|
|
721
|
+
]
|
|
722
|
+
}
|
|
723
|
+
\`\`\`
|
|
830
724
|
|
|
831
725
|
## Modifying Tasks
|
|
832
726
|
|
|
833
727
|
\`\`\`bash
|
|
834
|
-
# Add a new lane
|
|
835
|
-
cursorflow prepare --add-lane ${path.relative(config.projectRoot, taskDir)}
|
|
836
|
-
--preset merge --depends-on "01-lane-1"
|
|
728
|
+
# Add a new lane
|
|
729
|
+
cursorflow prepare --add-lane ${path.relative(config.projectRoot, taskDir)} --preset complex
|
|
837
730
|
|
|
838
731
|
# Add task to existing lane
|
|
839
732
|
cursorflow prepare --add-task ${path.relative(config.projectRoot, taskDir)}/01-lane-1.json \\
|