@litmers/cursorflow-orchestrator 0.1.37 → 0.1.40

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.
Files changed (102) hide show
  1. package/README.md +13 -13
  2. package/commands/cursorflow-init.md +113 -32
  3. package/commands/cursorflow-prepare.md +146 -339
  4. package/commands/cursorflow-run.md +148 -131
  5. package/dist/cli/add.js +8 -4
  6. package/dist/cli/add.js.map +1 -1
  7. package/dist/cli/index.js +2 -0
  8. package/dist/cli/index.js.map +1 -1
  9. package/dist/cli/new.js +3 -5
  10. package/dist/cli/new.js.map +1 -1
  11. package/dist/cli/prepare.js +0 -1
  12. package/dist/cli/prepare.js.map +1 -1
  13. package/dist/cli/resume.js +24 -15
  14. package/dist/cli/resume.js.map +1 -1
  15. package/dist/cli/run.js +1 -6
  16. package/dist/cli/run.js.map +1 -1
  17. package/dist/cli/setup-commands.d.ts +1 -0
  18. package/dist/cli/setup-commands.js +1 -0
  19. package/dist/cli/setup-commands.js.map +1 -1
  20. package/dist/core/orchestrator.js +13 -5
  21. package/dist/core/orchestrator.js.map +1 -1
  22. package/dist/core/runner/agent.d.ts +5 -1
  23. package/dist/core/runner/agent.js +31 -1
  24. package/dist/core/runner/agent.js.map +1 -1
  25. package/dist/core/runner/pipeline.d.ts +0 -1
  26. package/dist/core/runner/pipeline.js +136 -173
  27. package/dist/core/runner/pipeline.js.map +1 -1
  28. package/dist/core/runner/prompt.d.ts +0 -1
  29. package/dist/core/runner/prompt.js +11 -16
  30. package/dist/core/runner/prompt.js.map +1 -1
  31. package/dist/core/runner/task.d.ts +1 -2
  32. package/dist/core/runner/task.js +31 -40
  33. package/dist/core/runner/task.js.map +1 -1
  34. package/dist/core/runner.js +15 -2
  35. package/dist/core/runner.js.map +1 -1
  36. package/dist/core/stall-detection.d.ts +32 -4
  37. package/dist/core/stall-detection.js +151 -149
  38. package/dist/core/stall-detection.js.map +1 -1
  39. package/dist/services/logging/console.d.ts +7 -1
  40. package/dist/services/logging/console.js +13 -3
  41. package/dist/services/logging/console.js.map +1 -1
  42. package/dist/services/logging/formatter.d.ts +1 -0
  43. package/dist/services/logging/formatter.js +6 -3
  44. package/dist/services/logging/formatter.js.map +1 -1
  45. package/dist/types/config.d.ts +3 -1
  46. package/dist/types/logging.d.ts +1 -1
  47. package/dist/types/task.d.ts +3 -8
  48. package/dist/utils/config.js +5 -0
  49. package/dist/utils/config.js.map +1 -1
  50. package/dist/utils/doctor.js +4 -4
  51. package/dist/utils/doctor.js.map +1 -1
  52. package/dist/utils/enhanced-logger.d.ts +1 -1
  53. package/dist/utils/enhanced-logger.js +3 -3
  54. package/dist/utils/enhanced-logger.js.map +1 -1
  55. package/dist/utils/git.d.ts +12 -1
  56. package/dist/utils/git.js +56 -1
  57. package/dist/utils/git.js.map +1 -1
  58. package/dist/utils/health.js +13 -13
  59. package/dist/utils/health.js.map +1 -1
  60. package/dist/utils/log-formatter.d.ts +1 -1
  61. package/dist/utils/log-formatter.js +45 -8
  62. package/dist/utils/log-formatter.js.map +1 -1
  63. package/dist/utils/logger.js +2 -2
  64. package/dist/utils/logger.js.map +1 -1
  65. package/package.json +1 -1
  66. package/src/cli/add.ts +9 -4
  67. package/src/cli/index.ts +3 -0
  68. package/src/cli/new.ts +3 -5
  69. package/src/cli/prepare.ts +0 -1
  70. package/src/cli/resume.ts +28 -19
  71. package/src/cli/run.ts +1 -6
  72. package/src/cli/setup-commands.ts +1 -1
  73. package/src/core/orchestrator.ts +14 -5
  74. package/src/core/runner/agent.ts +36 -4
  75. package/src/core/runner/pipeline.ts +149 -182
  76. package/src/core/runner/prompt.ts +11 -18
  77. package/src/core/runner/task.ts +32 -41
  78. package/src/core/runner.ts +17 -2
  79. package/src/core/stall-detection.ts +263 -147
  80. package/src/services/logging/console.ts +13 -3
  81. package/src/services/logging/formatter.ts +6 -3
  82. package/src/types/config.ts +3 -1
  83. package/src/types/logging.ts +4 -2
  84. package/src/types/task.ts +3 -8
  85. package/src/utils/config.ts +6 -0
  86. package/src/utils/doctor.ts +5 -5
  87. package/src/utils/enhanced-logger.ts +3 -3
  88. package/src/utils/flow.ts +1 -0
  89. package/src/utils/git.ts +61 -1
  90. package/src/utils/health.ts +15 -15
  91. package/src/utils/log-formatter.ts +51 -8
  92. package/src/utils/logger.ts +2 -2
  93. package/commands/cursorflow-add.md +0 -159
  94. package/commands/cursorflow-clean.md +0 -84
  95. package/commands/cursorflow-doctor.md +0 -102
  96. package/commands/cursorflow-models.md +0 -51
  97. package/commands/cursorflow-monitor.md +0 -90
  98. package/commands/cursorflow-new.md +0 -87
  99. package/commands/cursorflow-resume.md +0 -205
  100. package/commands/cursorflow-signal.md +0 -52
  101. package/commands/cursorflow-stop.md +0 -55
  102. package/commands/cursorflow-triggers.md +0 -250
package/src/types/task.ts CHANGED
@@ -17,6 +17,7 @@ export interface Task {
17
17
 
18
18
  export interface RunnerConfig {
19
19
  tasks: Task[];
20
+ dependsOn?: string[];
20
21
  pipelineBranch?: string;
21
22
  worktreeDir?: string;
22
23
  branchPrefix?: string;
@@ -25,21 +26,15 @@ export interface RunnerConfig {
25
26
  model?: string;
26
27
  dependencyPolicy: DependencyPolicy;
27
28
  /** Output format for cursor-agent (default: 'json') */
28
- agentOutputFormat?: 'json' | 'plain';
29
+ agentOutputFormat?: 'json' | 'plain' | 'stream-json';
29
30
  /** Task execution timeout in milliseconds. Default: 600000 (10 minutes) */
30
31
  timeout?: number;
31
32
  /**
32
33
  * Enable intervention feature (stdin piping for message injection).
33
34
  * Warning: May cause stdout buffering issues on some systems.
34
- * Default: false
35
+ * Default: true
35
36
  */
36
37
  enableIntervention?: boolean;
37
- /**
38
- * Disable Git operations (worktree, branch, push, commit).
39
- * Useful for testing or environments without Git remote.
40
- * Default: false
41
- */
42
- noGit?: boolean;
43
38
  /**
44
39
  * Enable verbose Git logging.
45
40
  * Default: false
@@ -60,6 +60,9 @@ export function loadConfig(projectRoot: string | null = null): CursorFlowConfig
60
60
  allowDependencyChange: false,
61
61
  lockfileReadOnly: true,
62
62
 
63
+ // Intervention
64
+ enableIntervention: true,
65
+
63
66
  // Lane defaults
64
67
  defaultLaneConfig: {
65
68
  devPort: 3001,
@@ -193,6 +196,9 @@ export function createDefaultConfig(projectRoot: string, force = false): string
193
196
  allowDependencyChange: false,
194
197
  lockfileReadOnly: true,
195
198
 
199
+ // Intervention - allows sending messages to running agents
200
+ enableIntervention: true,
201
+
196
202
  // Lane configuration
197
203
  defaultLaneConfig: {
198
204
  devPort: 3001, // 3000 + laneNumber
@@ -618,7 +618,7 @@ function validateBranchNames(
618
618
  /**
619
619
  * Status file to track when doctor was last run successfully.
620
620
  */
621
- const DOCTOR_STATUS_FILE = '.cursorflow/doctor-status.json';
621
+ const DOCTOR_STATUS_FILE = '_cursorflow/doctor-status.json';
622
622
 
623
623
  export function saveDoctorStatus(repoRoot: string, report: DoctorReport): void {
624
624
  const statusPath = safeJoin(repoRoot, DOCTOR_STATUS_FILE);
@@ -720,6 +720,10 @@ export function runDoctor(options: DoctorOptions = {}): DoctorReport {
720
720
  }
721
721
  }
722
722
 
723
+ const repoRoot = resolveRepoRoot(cwd) || undefined;
724
+ context.repoRoot = repoRoot;
725
+ const gitCwd = repoRoot || cwd;
726
+
723
727
  // 1) Git repository checks
724
728
  if (!isInsideGitWorktree(cwd)) {
725
729
  addIssue(issues, {
@@ -736,10 +740,6 @@ export function runDoctor(options: DoctorOptions = {}): DoctorReport {
736
740
  return { ok: false, issues, context };
737
741
  }
738
742
 
739
- const repoRoot = resolveRepoRoot(cwd) || undefined;
740
- context.repoRoot = repoRoot;
741
- const gitCwd = repoRoot || cwd;
742
-
743
743
  if (!hasAtLeastOneCommit(gitCwd)) {
744
744
  addIssue(issues, {
745
745
  id: 'git.no_commits',
@@ -131,13 +131,13 @@ export class EnhancedLogManager {
131
131
  }
132
132
 
133
133
  /**
134
- * Get lane-task label like [L1-T2-lanename10]
134
+ * Get lane-task label like [1-1-lanename10]
135
135
  */
136
136
  private getLaneTaskLabel(): string {
137
137
  const laneNum = (this.session.laneIndex ?? 0) + 1;
138
138
  const taskNum = (this.session.taskIndex ?? 0) + 1;
139
139
  const shortLaneName = this.session.laneName.substring(0, 10);
140
- return `L${laneNum}-T${taskNum}-${shortLaneName}`;
140
+ return `${laneNum}-${taskNum}-${shortLaneName}`.substring(0, 18).padEnd(18);
141
141
  }
142
142
 
143
143
  /**
@@ -267,7 +267,7 @@ export class EnhancedLogManager {
267
267
  */
268
268
  public writeReadableMessage(msg: ParsedMessage): void {
269
269
  // Use formatMessageForConsole for consistent formatting
270
- // Use short lane-task label like [L01-T02]
270
+ // Use short lane-task label like [1-1-lanename10]
271
271
  const formatted = formatMessageForConsole(msg, {
272
272
  laneLabel: `[${this.getLaneTaskLabel()}]`,
273
273
  includeTimestamp: false, // We'll add our own short timestamp
package/src/utils/flow.ts CHANGED
@@ -40,3 +40,4 @@ export function findFlowDir(flowsDir: string, flowName: string): string | null {
40
40
  }
41
41
 
42
42
 
43
+
package/src/utils/git.ts CHANGED
@@ -156,6 +156,7 @@ export function runGit(args: string[], options: GitRunOptions = {}): string {
156
156
  cwd: cwd || process.cwd(),
157
157
  encoding: 'utf8',
158
158
  stdio: stdioMode as any,
159
+ timeout: 30000, // 30 second timeout for all git operations
159
160
  });
160
161
 
161
162
  if (result.error) {
@@ -206,6 +207,7 @@ export function runGitResult(args: string[], options: GitRunOptions = {}): GitRe
206
207
  cwd: cwd || process.cwd(),
207
208
  encoding: 'utf8',
208
209
  stdio: 'pipe',
210
+ timeout: 30000, // 30 second timeout
209
211
  });
210
212
 
211
213
  const gitResult = {
@@ -263,7 +265,7 @@ export function isGitRepo(cwd?: string): boolean {
263
265
  }
264
266
 
265
267
  /**
266
- * Check if worktree exists
268
+ * Check if worktree exists in Git's worktree list
267
269
  */
268
270
  export function worktreeExists(worktreePath: string, cwd?: string): boolean {
269
271
  const result = runGitResult(['worktree', 'list'], { cwd });
@@ -272,6 +274,64 @@ export function worktreeExists(worktreePath: string, cwd?: string): boolean {
272
274
  return result.stdout.includes(worktreePath);
273
275
  }
274
276
 
277
+ /**
278
+ * Check if a directory is a valid Git worktree (not just a regular directory)
279
+ * This prevents accidental checkout in the main repository when the worktree
280
+ * directory exists but is not properly registered as a worktree.
281
+ */
282
+ export function isValidWorktree(dirPath: string): boolean {
283
+ // 1. Directory must exist
284
+ if (!fs.existsSync(dirPath)) {
285
+ return false;
286
+ }
287
+
288
+ // 2. Check if Git recognizes this as a worktree by checking .git file
289
+ // In a worktree, .git is a file (not a directory) pointing to the main repo's .git/worktrees/
290
+ const dotGitPath = path.join(dirPath, '.git');
291
+ if (!fs.existsSync(dotGitPath)) {
292
+ return false;
293
+ }
294
+
295
+ try {
296
+ const stat = fs.statSync(dotGitPath);
297
+
298
+ // In worktrees, .git is a FILE containing "gitdir: /path/to/.git/worktrees/..."
299
+ // In the main repo, .git is a DIRECTORY
300
+ if (stat.isDirectory()) {
301
+ // This is the main repository, not a worktree
302
+ return false;
303
+ }
304
+
305
+ // Read the .git file to verify it points to a valid worktree
306
+ const content = fs.readFileSync(dotGitPath, 'utf8').trim();
307
+ if (!content.startsWith('gitdir:')) {
308
+ return false;
309
+ }
310
+
311
+ // Verify the gitdir path exists
312
+ const gitdirPath = content.replace('gitdir:', '').trim();
313
+ return fs.existsSync(gitdirPath);
314
+ } catch {
315
+ return false;
316
+ }
317
+ }
318
+
319
+ /**
320
+ * Remove a directory that was supposed to be a worktree but isn't valid
321
+ * Used to clean up orphaned/corrupted worktree directories before recreation
322
+ */
323
+ export function cleanupInvalidWorktreeDir(dirPath: string): void {
324
+ if (!fs.existsSync(dirPath)) return;
325
+
326
+ // Only remove if it's NOT a valid worktree (safety check)
327
+ if (isValidWorktree(dirPath)) {
328
+ throw new Error(`Cannot cleanup: ${dirPath} is a valid worktree`);
329
+ }
330
+
331
+ // Remove the directory recursively
332
+ fs.rmSync(dirPath, { recursive: true, force: true });
333
+ }
334
+
275
335
  /**
276
336
  * Create worktree
277
337
  */
@@ -455,14 +455,6 @@ export async function preflightCheck(options: {
455
455
  blockers.push(`Git: ${gitHealth.message}`);
456
456
  }
457
457
 
458
- // Check authentication
459
- if (options.requireAuth !== false) {
460
- const authHealth = await checkAuthHealth();
461
- if (!authHealth.ok) {
462
- blockers.push(`Authentication: ${authHealth.message}`);
463
- }
464
- }
465
-
466
458
  // Check Git remote (warning only unless required)
467
459
  const remoteHealth = await checkGitRemoteHealth(options.cwd);
468
460
  if (!remoteHealth.ok) {
@@ -473,6 +465,21 @@ export async function preflightCheck(options: {
473
465
  }
474
466
  }
475
467
 
468
+ // Check worktrees
469
+ const worktreeHealth = await checkWorktrees(options.cwd);
470
+ if (!worktreeHealth.ok) {
471
+ warnings.push(`Worktrees: ${worktreeHealth.message}`);
472
+ recommendations.push('Run `cursorflow clean worktrees` to clean up orphaned worktrees');
473
+ }
474
+
475
+ // Check authentication
476
+ if (options.requireAuth !== false) {
477
+ const authHealth = await checkAuthHealth();
478
+ if (!authHealth.ok) {
479
+ blockers.push(`Authentication: ${authHealth.message}`);
480
+ }
481
+ }
482
+
476
483
  // Check disk space
477
484
  const diskHealth = await checkDiskSpace();
478
485
  if (!diskHealth.ok) {
@@ -486,13 +493,6 @@ export async function preflightCheck(options: {
486
493
  recommendations.push('Run `cursorflow clean locks` to remove stale locks');
487
494
  }
488
495
 
489
- // Check worktrees
490
- const worktreeHealth = await checkWorktrees(options.cwd);
491
- if (!worktreeHealth.ok) {
492
- warnings.push(`Worktrees: ${worktreeHealth.message}`);
493
- recommendations.push('Run `cursorflow clean worktrees` to clean up orphaned worktrees');
494
- }
495
-
496
496
  // Check system resources
497
497
  const resourceHealth = await checkSystemResources();
498
498
  if (!resourceHealth.ok) {
@@ -7,7 +7,7 @@
7
7
  * - Box format only for: user, assistant, system, result
8
8
  * - Compact format for: tool, tool_result, thinking (gray/dim)
9
9
  * - Tool names simplified: ShellToolCall → Shell
10
- * - Lane labels max 16 chars: [01-types-tests]
10
+ * - Lane labels fixed 20 chars: [1-1-backend ]
11
11
  */
12
12
 
13
13
  import { COLORS } from './log-constants';
@@ -44,12 +44,17 @@ export function formatMessageForConsole(
44
44
  const ts = includeTimestamp ? new Date(msg.timestamp).toLocaleTimeString('en-US', { hour12: false }) : '';
45
45
  const tsPrefix = ts ? `${COLORS.gray}[${ts}]${COLORS.reset} ` : '';
46
46
 
47
- // Handle context (e.g. from logger.info) - max 16 chars
48
- const effectiveLaneLabel = laneLabel || (context ? `[${context}]` : '');
49
- const truncatedLabel = effectiveLaneLabel.length > 16
50
- ? effectiveLaneLabel.substring(0, 16)
51
- : effectiveLaneLabel;
52
- const labelPrefix = truncatedLabel ? `${COLORS.magenta}${truncatedLabel.padEnd(16)}${COLORS.reset} ` : '';
47
+ // Handle context (e.g. from logger.info) - max 18 chars inside brackets
48
+ // Format: [1-1-lanename1234] padded to fixed width 20 (including brackets)
49
+ let effectiveLaneLabel = laneLabel || (context ? `[${context.substring(0, 18).padEnd(18)}]` : '');
50
+
51
+ // Smart truncation: ensure it always ends with ]
52
+ if (effectiveLaneLabel.length > 20) {
53
+ effectiveLaneLabel = effectiveLaneLabel.substring(0, 19) + ']';
54
+ }
55
+
56
+ // Fixed width 20 chars for consistent alignment
57
+ const labelPrefix = effectiveLaneLabel ? `${COLORS.magenta}${effectiveLaneLabel.padEnd(20)}${COLORS.reset} ` : '';
53
58
 
54
59
  let typePrefix = '';
55
60
  let content = msg.content;
@@ -155,6 +160,44 @@ export function formatMessageForConsole(
155
160
 
156
161
  if (!typePrefix) return `${tsPrefix}${labelPrefix}${content}`;
157
162
 
163
+ // Avoid double prefixes (e.g. INFO INFO)
164
+ const plainTypePrefix = stripAnsi(typePrefix).replace(/[^\x00-\x7F]/g, '').trim(); // "INFO", "DONE", etc.
165
+ const plainContent = stripAnsi(content);
166
+ if (plainContent.includes(` ${plainTypePrefix} `) || plainContent.startsWith(`${plainTypePrefix} `)) {
167
+ // If content already has the prefix, try to strip it from content or just use content as is
168
+ // For simplicity, if it's already there, we can just skip adding our typePrefix
169
+ // but we still want the colors. This is tricky.
170
+ // Usually it's better to just return the content if it looks already formatted.
171
+ }
172
+
173
+ // A better way: if content starts with an emoji that matches our type, skip typePrefix
174
+ const emojiMap: Record<string, string> = {
175
+ 'info': 'ℹ️',
176
+ 'success': '✅',
177
+ 'result': '✅',
178
+ 'warn': '⚠️',
179
+ 'error': '❌',
180
+ 'tool': '🔧',
181
+ 'thinking': '🤔',
182
+ 'user': '🧑',
183
+ 'assistant': '🤖'
184
+ };
185
+
186
+ const targetEmoji = emojiMap[msg.type];
187
+ if (targetEmoji && plainContent.trim().startsWith(targetEmoji)) {
188
+ return `${tsPrefix}${labelPrefix}${content}`;
189
+ }
190
+
191
+ // Handle separator lines - if content is just a repeat of ━ or ─, extend it
192
+ const separatorMatch = content.match(/^([━─=]+)$/);
193
+ if (separatorMatch) {
194
+ const char = separatorMatch[1]![0]!;
195
+ // Use a fixed width for now (80 is a good standard)
196
+ // In a real terminal we could use process.stdout.columns
197
+ const targetWidth = 80;
198
+ content = char.repeat(targetWidth);
199
+ }
200
+
158
201
  // Compact format (single line)
159
202
  if (!useBox) {
160
203
  return `${tsPrefix}${labelPrefix}${typePrefix.padEnd(12)} ${content}`;
@@ -169,7 +212,7 @@ export function formatMessageForConsole(
169
212
  const emojiCount = (strippedPrefix.match(/[\u{1F300}-\u{1F9FF}]|[\u{2600}-\u{26FF}]|[\u{2700}-\u{27BF}]|[\u{1F600}-\u{1F64F}]|[\u{1F680}-\u{1F6FF}]|[\u{1F1E0}-\u{1F1FF}]|[\u{2300}-\u{23FF}]|[\u{2B50}-\u{2B55}]|[\u{231A}-\u{231B}]|[\u{23E9}-\u{23F3}]|[\u{23F8}-\u{23FA}]|✅|❌|⚙️|ℹ️|⚠️|🔧|📄|🤔|🧑|🤖/gu) || []).length;
170
213
  const visualWidth = strippedPrefix.length + emojiCount; // emoji adds 1 extra width
171
214
 
172
- const boxWidth = 60;
215
+ const boxWidth = 80;
173
216
  const header = `${typePrefix}┌${'─'.repeat(boxWidth)}`;
174
217
  let result = `${fullPrefix}${header}\n`;
175
218
 
@@ -144,9 +144,9 @@ export function withContext(context: string) {
144
144
  */
145
145
  export function section(message: string): void {
146
146
  console.log('');
147
- console.log(`${COLORS.cyan}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${COLORS.reset}`);
147
+ console.log(`${COLORS.cyan}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${COLORS.reset}`);
148
148
  console.log(`${COLORS.cyan} ${message}${COLORS.reset}`);
149
- console.log(`${COLORS.cyan}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${COLORS.reset}`);
149
+ console.log(`${COLORS.cyan}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${COLORS.reset}`);
150
150
  console.log('');
151
151
  }
152
152
 
@@ -1,159 +0,0 @@
1
- # cursorflow add
2
-
3
- Lane에 Task를 추가합니다.
4
-
5
- ## 사용법
6
-
7
- ```bash
8
- cursorflow add <FlowName> <LaneName> --task "name=...|model=...|prompt=..." [--after ...]
9
- ```
10
-
11
- ## 설명
12
-
13
- 지정된 Flow의 Lane에 Task를 추가합니다.
14
- `--task` 옵션은 여러 번 사용하여 여러 태스크를 순차적으로 추가할 수 있습니다.
15
-
16
- ## --task 형식
17
-
18
- ```
19
- "name=<이름>|model=<모델>|prompt=<프롬프트>"
20
- ```
21
-
22
- ### 필수 필드
23
-
24
- | 필드 | 설명 | 예시 |
25
- |------|------|------|
26
- | `name` | 태스크 이름 (영문, 숫자, -, _) | `name=implement` |
27
- | `prompt` | 태스크 프롬프트/지시사항 | `prompt=API 구현` |
28
-
29
- ### 선택 필드
30
-
31
- | 필드 | 설명 | 예시 |
32
- |------|------|------|
33
- | `model` | AI 모델 (생략 시 기본 모델 사용) | `model=<your-model>` |
34
-
35
- 기본 모델 설정: `cursorflow config defaultModel <model-name>`
36
-
37
- ## --after 형식 (의존성 설정)
38
-
39
- 첫 번째 태스크가 시작되기 전에 완료되어야 할 태스크를 지정합니다.
40
-
41
- | 형식 | 설명 |
42
- |------|------|
43
- | `"lane"` | 해당 레인의 **마지막 태스크** 완료 후 시작 |
44
- | `"lane:task"` | 특정 태스크 완료 후 시작 |
45
- | `"a:t1, b:t2"` | **여러 태스크가 모두 완료**된 후 시작 |
46
-
47
- ## 예시
48
-
49
- ### 기본 사용: 단일 태스크 추가 (기본 모델 사용)
50
-
51
- ```bash
52
- cursorflow add SearchFeature api \
53
- --task "name=implement|prompt=검색 API 구현"
54
- ```
55
-
56
- ### 여러 태스크 추가
57
-
58
- ```bash
59
- cursorflow add SearchFeature api \
60
- --task "name=plan|prompt=API 설계" \
61
- --task "name=implement|prompt=검색 API 구현" \
62
- --task "name=test|prompt=테스트 코드 작성"
63
- ```
64
-
65
- ### 의존성 설정: 특정 태스크 완료 후 시작
66
-
67
- ```bash
68
- # api 레인의 implement 태스크 완료 후 시작
69
- cursorflow add SearchFeature web \
70
- --task "name=ui|prompt=검색 UI 구현" \
71
- --after "api:implement"
72
- ```
73
-
74
- ### 의존성 설정: 레인 전체 완료 후 시작
75
-
76
- ```bash
77
- # api 레인의 마지막 태스크 완료 후 시작
78
- cursorflow add SearchFeature web \
79
- --task "name=ui|prompt=검색 UI 구현" \
80
- --after "api"
81
- ```
82
-
83
- ### 다중 의존성: 여러 태스크 완료 후 시작
84
-
85
- ```bash
86
- # web과 mobile 모두 완료된 후 시작
87
- cursorflow add SearchFeature e2e \
88
- --task "name=verify|prompt=E2E 테스트" \
89
- --after "web:ui, mobile:app"
90
- ```
91
-
92
- ## 출력 예시
93
-
94
- ```
95
- ✅ 3개 태스크 추가 완료
96
-
97
- 📄 01-api.json
98
-
99
- ├── plan (<default-model>)
100
- ├── implement (<default-model>)
101
- └── test (<default-model>)
102
-
103
- 전체 태스크 목록:
104
- 1. plan (new)
105
- 2. implement (new)
106
- 3. test (new)
107
-
108
- 다음 단계:
109
- cursorflow run SearchFeature # Flow 실행
110
- cursorflow doctor SearchFeature # 설정 검증
111
- ```
112
-
113
- ## 생성되는 Lane 파일 구조
114
-
115
- ```json
116
- {
117
- "laneName": "api",
118
- "tasks": [
119
- {
120
- "name": "plan",
121
- "model": "<your-model>",
122
- "prompt": "API 설계"
123
- },
124
- {
125
- "name": "implement",
126
- "model": "<your-model>",
127
- "prompt": "검색 API 구현"
128
- },
129
- {
130
- "name": "test",
131
- "model": "<your-model>",
132
- "prompt": "테스트 코드 작성"
133
- }
134
- ]
135
- }
136
- ```
137
-
138
- ### 의존성이 있는 경우
139
-
140
- ```json
141
- {
142
- "laneName": "web",
143
- "tasks": [
144
- {
145
- "name": "ui",
146
- "model": "<your-model>",
147
- "prompt": "검색 UI 구현",
148
- "dependsOn": ["01-api:implement"]
149
- }
150
- ]
151
- }
152
- ```
153
-
154
- ## 관련 명령어
155
-
156
- - [cursorflow new](cursorflow-new.md) - Flow와 Lane 생성
157
- - [cursorflow run](cursorflow-run.md) - Flow 실행
158
- - [cursorflow doctor](cursorflow-doctor.md) - 설정 검증
159
-
@@ -1,84 +0,0 @@
1
- # CursorFlow Clean
2
-
3
- ## Overview
4
- Clean up temporary resources created by CursorFlow, including Git worktrees, feature branches, log files, and task definitions.
5
-
6
- ## Usage
7
-
8
- ```bash
9
- cursorflow clean <type> [options]
10
- ```
11
-
12
- ## Clean Types
13
-
14
- | Type | Description |
15
- |------|------|
16
- | `branches` | Remove local feature branches created by CursorFlow |
17
- | `worktrees` | Remove temporary Git worktrees |
18
- | `logs` | Clear all run and terminal logs |
19
- | `tasks` | Remove task definition directories (keeps `example/`) |
20
- | `all` | Clean everything (branches, worktrees, logs, and tasks) |
21
-
22
- ## Options
23
-
24
- | Option | Description |
25
- |------|------|
26
- | `--run <id>` | Clean resources linked to a specific run |
27
- | `--older-than <time>` | Clean resources older than a specific time (e.g., `24h`, `7d`) |
28
- | `--orphaned` | Clean orphaned resources (worktrees without runs, etc.) |
29
- | `--dry-run` | Show what would be removed without actually deleting anything |
30
- | `--force` | Force removal (ignore uncommitted changes in worktrees) |
31
- | `--include-latest` | Also remove the most recent item (by default, latest is kept) |
32
- | `--help`, `-h` | Show help |
33
-
34
- ## Examples
35
-
36
- ### Review before deleting (latest is kept by default)
37
- ```bash
38
- cursorflow clean all --dry-run
39
- ```
40
-
41
- ### Clean only worktrees (keeps the latest worktree)
42
- ```bash
43
- cursorflow clean worktrees
44
- ```
45
-
46
- ### Force clean everything including the latest
47
- ```bash
48
- cursorflow clean all --force --include-latest
49
- ```
50
-
51
- ### Remove all worktrees including the latest
52
- ```bash
53
- cursorflow clean worktrees --include-latest
54
- ```
55
-
56
- ### Clean resources for a specific run
57
- ```bash
58
- cursorflow clean all --run run-1734873132
59
- ```
60
-
61
- ### Clean logs and tasks older than 7 days
62
- ```bash
63
- cursorflow clean logs --older-than 7d
64
- cursorflow clean tasks --older-than 7d
65
- ```
66
-
67
- ### Clean orphaned worktrees and branches
68
- ```bash
69
- cursorflow clean all --orphaned
70
- ```
71
-
72
- ## Notes
73
-
74
- 1. **Safety**: It is highly recommended to run with `--dry-run` first to see exactly what will be deleted.
75
- 2. **Worktrees**: The command identifies CursorFlow worktrees by their location (usually in `_cursorflow/worktrees/`) or their prefix.
76
- 3. **Branches**: Only branches starting with the configured `branchPrefix` (default: `cursorflow/`) are targeted.
77
- 4. **Irreversible**: Once logs are deleted, they cannot be recovered.
78
- 5. **Default Behavior**: By default, the most recent item is preserved. The "most recent" is determined by:
79
- - **Worktrees**: Directory modification time
80
- - **Branches**: Latest commit timestamp
81
- - **Logs**: File/directory modification time
82
- - **Tasks**: Directory modification time
83
-
84
- Use `--include-latest` to remove everything including the most recent item.