@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.
Files changed (150) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/README.md +182 -59
  3. package/commands/cursorflow-add.md +159 -0
  4. package/commands/cursorflow-doctor.md +45 -23
  5. package/commands/cursorflow-monitor.md +23 -2
  6. package/commands/cursorflow-new.md +87 -0
  7. package/commands/cursorflow-run.md +60 -111
  8. package/dist/cli/add.d.ts +7 -0
  9. package/dist/cli/add.js +377 -0
  10. package/dist/cli/add.js.map +1 -0
  11. package/dist/cli/clean.js +1 -0
  12. package/dist/cli/clean.js.map +1 -1
  13. package/dist/cli/config.d.ts +7 -0
  14. package/dist/cli/config.js +181 -0
  15. package/dist/cli/config.js.map +1 -0
  16. package/dist/cli/doctor.js +47 -4
  17. package/dist/cli/doctor.js.map +1 -1
  18. package/dist/cli/index.js +34 -30
  19. package/dist/cli/index.js.map +1 -1
  20. package/dist/cli/logs.js +17 -34
  21. package/dist/cli/logs.js.map +1 -1
  22. package/dist/cli/monitor.js +62 -65
  23. package/dist/cli/monitor.js.map +1 -1
  24. package/dist/cli/new.d.ts +7 -0
  25. package/dist/cli/new.js +232 -0
  26. package/dist/cli/new.js.map +1 -0
  27. package/dist/cli/prepare.js +95 -193
  28. package/dist/cli/prepare.js.map +1 -1
  29. package/dist/cli/resume.js +57 -68
  30. package/dist/cli/resume.js.map +1 -1
  31. package/dist/cli/run.js +60 -30
  32. package/dist/cli/run.js.map +1 -1
  33. package/dist/cli/stop.js +6 -0
  34. package/dist/cli/stop.js.map +1 -1
  35. package/dist/cli/tasks.d.ts +5 -3
  36. package/dist/cli/tasks.js +181 -29
  37. package/dist/cli/tasks.js.map +1 -1
  38. package/dist/core/failure-policy.d.ts +9 -0
  39. package/dist/core/failure-policy.js +9 -0
  40. package/dist/core/failure-policy.js.map +1 -1
  41. package/dist/core/orchestrator.d.ts +20 -6
  42. package/dist/core/orchestrator.js +215 -334
  43. package/dist/core/orchestrator.js.map +1 -1
  44. package/dist/core/runner/agent.d.ts +27 -0
  45. package/dist/core/runner/agent.js +294 -0
  46. package/dist/core/runner/agent.js.map +1 -0
  47. package/dist/core/runner/index.d.ts +5 -0
  48. package/dist/core/runner/index.js +22 -0
  49. package/dist/core/runner/index.js.map +1 -0
  50. package/dist/core/runner/pipeline.d.ts +9 -0
  51. package/dist/core/runner/pipeline.js +539 -0
  52. package/dist/core/runner/pipeline.js.map +1 -0
  53. package/dist/core/runner/prompt.d.ts +25 -0
  54. package/dist/core/runner/prompt.js +175 -0
  55. package/dist/core/runner/prompt.js.map +1 -0
  56. package/dist/core/runner/task.d.ts +26 -0
  57. package/dist/core/runner/task.js +283 -0
  58. package/dist/core/runner/task.js.map +1 -0
  59. package/dist/core/runner/utils.d.ts +37 -0
  60. package/dist/core/runner/utils.js +161 -0
  61. package/dist/core/runner/utils.js.map +1 -0
  62. package/dist/core/runner.d.ts +2 -96
  63. package/dist/core/runner.js +11 -1136
  64. package/dist/core/runner.js.map +1 -1
  65. package/dist/core/stall-detection.d.ts +326 -0
  66. package/dist/core/stall-detection.js +781 -0
  67. package/dist/core/stall-detection.js.map +1 -0
  68. package/dist/services/logging/console.js +2 -1
  69. package/dist/services/logging/console.js.map +1 -1
  70. package/dist/types/config.d.ts +6 -6
  71. package/dist/types/flow.d.ts +84 -0
  72. package/dist/types/flow.js +10 -0
  73. package/dist/types/flow.js.map +1 -0
  74. package/dist/types/index.d.ts +1 -0
  75. package/dist/types/index.js +3 -3
  76. package/dist/types/index.js.map +1 -1
  77. package/dist/types/lane.d.ts +0 -2
  78. package/dist/types/logging.d.ts +5 -1
  79. package/dist/types/task.d.ts +7 -11
  80. package/dist/utils/config.d.ts +5 -1
  81. package/dist/utils/config.js +15 -16
  82. package/dist/utils/config.js.map +1 -1
  83. package/dist/utils/dependency.d.ts +36 -1
  84. package/dist/utils/dependency.js +256 -1
  85. package/dist/utils/dependency.js.map +1 -1
  86. package/dist/utils/doctor.js +40 -8
  87. package/dist/utils/doctor.js.map +1 -1
  88. package/dist/utils/enhanced-logger.d.ts +45 -82
  89. package/dist/utils/enhanced-logger.js +239 -844
  90. package/dist/utils/enhanced-logger.js.map +1 -1
  91. package/dist/utils/flow.d.ts +9 -0
  92. package/dist/utils/flow.js +73 -0
  93. package/dist/utils/flow.js.map +1 -0
  94. package/dist/utils/git.d.ts +29 -0
  95. package/dist/utils/git.js +115 -5
  96. package/dist/utils/git.js.map +1 -1
  97. package/dist/utils/state.js +0 -2
  98. package/dist/utils/state.js.map +1 -1
  99. package/dist/utils/task-service.d.ts +2 -2
  100. package/dist/utils/task-service.js +40 -31
  101. package/dist/utils/task-service.js.map +1 -1
  102. package/package.json +4 -3
  103. package/src/cli/add.ts +397 -0
  104. package/src/cli/clean.ts +1 -0
  105. package/src/cli/config.ts +177 -0
  106. package/src/cli/doctor.ts +48 -4
  107. package/src/cli/index.ts +36 -32
  108. package/src/cli/logs.ts +20 -33
  109. package/src/cli/monitor.ts +70 -75
  110. package/src/cli/new.ts +235 -0
  111. package/src/cli/prepare.ts +98 -205
  112. package/src/cli/resume.ts +61 -76
  113. package/src/cli/run.ts +333 -306
  114. package/src/cli/stop.ts +8 -0
  115. package/src/cli/tasks.ts +200 -21
  116. package/src/core/failure-policy.ts +9 -0
  117. package/src/core/orchestrator.ts +279 -379
  118. package/src/core/runner/agent.ts +314 -0
  119. package/src/core/runner/index.ts +6 -0
  120. package/src/core/runner/pipeline.ts +567 -0
  121. package/src/core/runner/prompt.ts +174 -0
  122. package/src/core/runner/task.ts +320 -0
  123. package/src/core/runner/utils.ts +142 -0
  124. package/src/core/runner.ts +8 -1347
  125. package/src/core/stall-detection.ts +936 -0
  126. package/src/services/logging/console.ts +2 -1
  127. package/src/types/config.ts +6 -6
  128. package/src/types/flow.ts +91 -0
  129. package/src/types/index.ts +15 -3
  130. package/src/types/lane.ts +0 -2
  131. package/src/types/logging.ts +5 -1
  132. package/src/types/task.ts +7 -11
  133. package/src/utils/config.ts +16 -17
  134. package/src/utils/dependency.ts +311 -2
  135. package/src/utils/doctor.ts +36 -8
  136. package/src/utils/enhanced-logger.ts +264 -927
  137. package/src/utils/flow.ts +42 -0
  138. package/src/utils/git.ts +145 -5
  139. package/src/utils/state.ts +0 -2
  140. package/src/utils/task-service.ts +48 -40
  141. package/commands/cursorflow-review.md +0 -56
  142. package/commands/cursorflow-runs.md +0 -59
  143. package/dist/cli/runs.d.ts +0 -5
  144. package/dist/cli/runs.js +0 -214
  145. package/dist/cli/runs.js.map +0 -1
  146. package/dist/core/reviewer.d.ts +0 -66
  147. package/dist/core/reviewer.js +0 -265
  148. package/dist/core/reviewer.js.map +0 -1
  149. package/src/cli/runs.ts +0 -212
  150. package/src/core/reviewer.ts +0 -285
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');
@@ -0,0 +1,177 @@
1
+ /**
2
+ * CursorFlow 'config' command
3
+ *
4
+ * View and set configuration values
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 { safeJoin } from '../utils/path';
12
+
13
+ interface ConfigOptions {
14
+ key: string | null;
15
+ value: string | null;
16
+ list: boolean;
17
+ help: boolean;
18
+ }
19
+
20
+ function printHelp(): void {
21
+ console.log(`
22
+ \x1b[1mcursorflow config\x1b[0m - 설정 조회 및 변경
23
+
24
+ \x1b[1m사용법:\x1b[0m
25
+ cursorflow config # 현재 설정 보기
26
+ cursorflow config <key> # 특정 설정 값 보기
27
+ cursorflow config <key> <value> # 설정 값 변경
28
+
29
+ \x1b[1m주요 설정 키:\x1b[0m
30
+ defaultModel 기본 AI 모델 (예: gemini-3-flash)
31
+ branchPrefix 브랜치 접두사 (예: feature/)
32
+ executor 실행기 (cursor-agent | cloud)
33
+
34
+ \x1b[1m예시:\x1b[0m
35
+ # 현재 설정 보기
36
+ cursorflow config
37
+
38
+ # 기본 모델 확인
39
+ cursorflow config defaultModel
40
+
41
+ # 기본 모델 변경
42
+ cursorflow config defaultModel opus-4.5-thinking
43
+
44
+ \x1b[1m참고:\x1b[0m
45
+ 설정 파일 위치: cursorflow.config.js
46
+ 설정을 영구 저장하려면 cursorflow.config.js 파일을 직접 수정하세요.
47
+ `);
48
+ }
49
+
50
+ function parseArgs(args: string[]): ConfigOptions {
51
+ const result: ConfigOptions = {
52
+ key: null,
53
+ value: null,
54
+ list: false,
55
+ help: false,
56
+ };
57
+
58
+ let positionalCount = 0;
59
+
60
+ for (let i = 0; i < args.length; i++) {
61
+ const arg = args[i];
62
+
63
+ if (arg === '--help' || arg === '-h') {
64
+ result.help = true;
65
+ } else if (arg === '--list' || arg === '-l') {
66
+ result.list = true;
67
+ } else if (!arg.startsWith('--')) {
68
+ if (positionalCount === 0) {
69
+ result.key = arg;
70
+ } else if (positionalCount === 1) {
71
+ result.value = arg;
72
+ }
73
+ positionalCount++;
74
+ }
75
+ }
76
+
77
+ return result;
78
+ }
79
+
80
+ async function config(args: string[]): Promise<void> {
81
+ const options = parseArgs(args);
82
+
83
+ if (options.help) {
84
+ printHelp();
85
+ return;
86
+ }
87
+
88
+ const projectRoot = findProjectRoot();
89
+ const currentConfig = loadConfig(projectRoot);
90
+ const configPath = safeJoin(projectRoot, 'cursorflow.config.js');
91
+
92
+ // If setting a value
93
+ if (options.key && options.value) {
94
+ const key = options.key;
95
+ const value = options.value;
96
+
97
+ // Validate key exists
98
+ if (!(key in currentConfig)) {
99
+ logger.error(`알 수 없는 설정 키: ${key}`);
100
+ console.log('\n사용 가능한 키: defaultModel, branchPrefix, executor, ...');
101
+ process.exit(1);
102
+ }
103
+
104
+ // Update or create config file
105
+ let configContent: string;
106
+
107
+ if (fs.existsSync(configPath)) {
108
+ // Read existing config
109
+ const existingContent = fs.readFileSync(configPath, 'utf-8');
110
+
111
+ // Simple regex replace for the key
112
+ const keyRegex = new RegExp(`(${key}\\s*:\\s*)['"]?[^'",\\n]+['"]?`, 'g');
113
+ if (keyRegex.test(existingContent)) {
114
+ configContent = existingContent.replace(keyRegex, `$1'${value}'`);
115
+ } else {
116
+ // Add the key to the config
117
+ configContent = existingContent.replace(
118
+ /module\.exports\s*=\s*\{/,
119
+ `module.exports = {\n ${key}: '${value}',`
120
+ );
121
+ }
122
+ } else {
123
+ // Create new config file
124
+ configContent = `module.exports = {
125
+ ${key}: '${value}',
126
+ };
127
+ `;
128
+ }
129
+
130
+ fs.writeFileSync(configPath, configContent);
131
+ logger.success(`✅ ${key} = '${value}' 설정됨`);
132
+ console.log(`\n설정 파일: ${configPath}`);
133
+ return;
134
+ }
135
+
136
+ // If viewing a specific key
137
+ if (options.key) {
138
+ const key = options.key;
139
+
140
+ if (!(key in currentConfig)) {
141
+ logger.error(`알 수 없는 설정 키: ${key}`);
142
+ process.exit(1);
143
+ }
144
+
145
+ const value = (currentConfig as any)[key];
146
+ console.log(`${key} = ${JSON.stringify(value)}`);
147
+ return;
148
+ }
149
+
150
+ // List all config
151
+ logger.section('CursorFlow 설정');
152
+ console.log('');
153
+
154
+ const importantKeys = [
155
+ 'defaultModel',
156
+ 'branchPrefix',
157
+ 'executor',
158
+ 'flowsDir',
159
+ 'tasksDir',
160
+ 'logsDir',
161
+ ];
162
+
163
+ console.log('\x1b[1m주요 설정:\x1b[0m');
164
+ for (const key of importantKeys) {
165
+ const value = (currentConfig as any)[key];
166
+ console.log(` ${key.padEnd(20)} ${JSON.stringify(value)}`);
167
+ }
168
+
169
+ console.log('');
170
+ console.log(`설정 파일: ${configPath}`);
171
+ console.log('');
172
+ console.log('설정 변경: cursorflow config <key> <value>');
173
+ console.log('예: cursorflow config defaultModel opus-4.5-thinking');
174
+ }
175
+
176
+ export = config;
177
+
package/src/cli/doctor.ts CHANGED
@@ -13,12 +13,18 @@
13
13
  */
14
14
 
15
15
  import * as logger from '../utils/logger';
16
+ import * as path from 'path';
17
+ import * as fs from 'fs';
16
18
  import { runDoctor, saveDoctorStatus } from '../utils/doctor';
17
19
  import { runInteractiveAgentTest } from '../utils/cursor-agent';
20
+ import { loadConfig, findProjectRoot } from '../utils/config';
21
+ import { safeJoin } from '../utils/path';
22
+ import { findFlowDir } from '../utils/flow';
18
23
 
19
24
  interface DoctorCliOptions {
20
25
  json: boolean;
21
26
  tasksDir: string | null;
27
+ flowOrTaskName: string | null;
22
28
  executor: string | null;
23
29
  includeCursorAgentChecks: boolean;
24
30
  testAgent: boolean;
@@ -26,13 +32,16 @@ interface DoctorCliOptions {
26
32
 
27
33
  function printHelp(): void {
28
34
  console.log(`
29
- Usage: cursorflow doctor [options]
35
+ Usage: cursorflow doctor [flow-name] [options]
30
36
 
31
37
  Verify your environment is ready for CursorFlow runs.
32
38
 
39
+ Arguments:
40
+ [flow-name] Flow or task name to validate (optional)
41
+
33
42
  Options:
34
43
  --json Output machine-readable JSON
35
- --tasks-dir <path> Also validate lane files (run preflight)
44
+ --tasks-dir <path> Validate specific directory (legacy)
36
45
  --executor <type> cursor-agent | cloud
37
46
  --no-cursor Skip Cursor Agent install/auth checks
38
47
  --test-agent Run interactive agent test (to approve MCP/permissions)
@@ -40,18 +49,30 @@ Options:
40
49
 
41
50
  Examples:
42
51
  cursorflow doctor
52
+ cursorflow doctor TestFeature
43
53
  cursorflow doctor --test-agent
44
- cursorflow doctor --tasks-dir _cursorflow/tasks/demo-test/
54
+ cursorflow doctor --tasks-dir _cursorflow/flows/001_TestFeature
45
55
  `);
46
56
  }
47
57
 
48
58
  function parseArgs(args: string[]): DoctorCliOptions {
49
59
  const tasksDirIdx = args.indexOf('--tasks-dir');
50
60
  const executorIdx = args.indexOf('--executor');
61
+
62
+ // Find positional argument (flow/task name)
63
+ // Exclude args that are values for options
64
+ const optionValueIndices = new Set<number>();
65
+ if (tasksDirIdx >= 0 && tasksDirIdx + 1 < args.length) optionValueIndices.add(tasksDirIdx + 1);
66
+ if (executorIdx >= 0 && executorIdx + 1 < args.length) optionValueIndices.add(executorIdx + 1);
67
+
68
+ const flowOrTaskName = args.find((arg, idx) =>
69
+ !arg.startsWith('--') && !optionValueIndices.has(idx)
70
+ ) || null;
51
71
 
52
72
  const options: DoctorCliOptions = {
53
73
  json: args.includes('--json'),
54
74
  tasksDir: tasksDirIdx >= 0 ? (args[tasksDirIdx + 1] || null) : null,
75
+ flowOrTaskName,
55
76
  executor: executorIdx >= 0 ? (args[executorIdx + 1] || null) : null,
56
77
  includeCursorAgentChecks: !args.includes('--no-cursor'),
57
78
  testAgent: args.includes('--test-agent'),
@@ -117,9 +138,32 @@ async function doctor(args: string[]): Promise<void> {
117
138
  process.exit(success ? 0 : 1);
118
139
  }
119
140
 
141
+ // Resolve tasksDir from flow name if provided
142
+ let tasksDir = options.tasksDir;
143
+ if (!tasksDir && options.flowOrTaskName) {
144
+ try {
145
+ const config = loadConfig();
146
+ const flowsDir = safeJoin(config.projectRoot, config.flowsDir);
147
+ const foundFlow = findFlowDir(flowsDir, options.flowOrTaskName);
148
+ if (foundFlow) {
149
+ tasksDir = foundFlow;
150
+ } else {
151
+ // Try as a direct path
152
+ const directPath = path.resolve(process.cwd(), options.flowOrTaskName);
153
+ if (fs.existsSync(directPath)) {
154
+ tasksDir = directPath;
155
+ } else {
156
+ tasksDir = options.flowOrTaskName;
157
+ }
158
+ }
159
+ } catch {
160
+ tasksDir = options.flowOrTaskName;
161
+ }
162
+ }
163
+
120
164
  const report = runDoctor({
121
165
  cwd: process.cwd(),
122
- tasksDir: options.tasksDir || undefined,
166
+ tasksDir: tasksDir || undefined,
123
167
  executor: options.executor || undefined,
124
168
  includeCursorAgentChecks: options.includeCursorAgentChecks,
125
169
  });
package/src/cli/index.ts CHANGED
@@ -12,6 +12,11 @@ type CommandFn = (args: string[]) => Promise<void>;
12
12
  */
13
13
  const COMMANDS: Record<string, CommandFn> = {
14
14
  init: require('./init'),
15
+ // New Flow architecture commands
16
+ new: require('./new'),
17
+ add: require('./add'),
18
+ config: require('./config'),
19
+ // Legacy prepare command (deprecated)
15
20
  prepare: require('./prepare'),
16
21
  run: require('./run'),
17
22
  monitor: require('./monitor'),
@@ -21,7 +26,6 @@ const COMMANDS: Record<string, CommandFn> = {
21
26
  signal: require('./signal'),
22
27
  models: require('./models'),
23
28
  logs: require('./logs'),
24
- runs: require('./runs'),
25
29
  tasks: require('./tasks'),
26
30
  stop: require('./stop'),
27
31
  setup: require('./setup-commands').main,
@@ -35,37 +39,37 @@ function printHelp(): void {
35
39
  \x1b[1mUSAGE\x1b[0m
36
40
  $ \x1b[32mcursorflow\x1b[0m <command> [options]
37
41
 
38
- \x1b[1mCOMMANDS\x1b[0m
39
- \x1b[33minit\x1b[0m [options] Initialize CursorFlow in project
40
- \x1b[33msetup\x1b[0m [options] Install Cursor IDE commands
41
- \x1b[33mprepare\x1b[0m <feature> [opts] Prepare task directory and JSON files
42
- \x1b[33mrun\x1b[0m <tasks-dir> [options] Run orchestration (DAG-based)
43
- \x1b[33mmonitor\x1b[0m [run-dir] [options] \x1b[36mInteractive\x1b[0m lane dashboard
44
- \x1b[33mtasks\x1b[0m [name] [options] Browse and validate prepared tasks
45
- \x1b[33mruns\x1b[0m [run-id] [options] List and view run details
46
- \x1b[33mstop\x1b[0m [run-id] [options] Stop running workflows
47
- \x1b[33mclean\x1b[0m <type> [options] Clean branches/worktrees/logs/tasks
48
- \x1b[33mresume\x1b[0m [lane] [options] Resume lane(s) - use --all for batch resume
49
- \x1b[33mdoctor\x1b[0m [options] Check environment and preflight
50
- \x1b[33msignal\x1b[0m <lane> <msg> Directly intervene in a running lane
51
- \x1b[33mmodels\x1b[0m [options] List available AI models
52
- \x1b[33mlogs\x1b[0m [run-dir] [options] View, export, and follow logs
53
-
54
- \x1b[1mGLOBAL OPTIONS\x1b[0m
55
- --config <path> Config file path
56
- --help, -h Show help
57
- --version, -v Show version
58
-
59
- \x1b[1mEXAMPLES\x1b[0m
60
- $ \x1b[32mcursorflow init --example\x1b[0m
61
- $ \x1b[32mcursorflow prepare NewFeature --lanes 3\x1b[0m
62
- $ \x1b[32mcursorflow run _cursorflow/tasks/MyFeature/\x1b[0m
63
- $ \x1b[32mcursorflow monitor latest\x1b[0m
64
- $ \x1b[32mcursorflow logs --all --follow\x1b[0m
65
- $ \x1b[32mcursorflow runs --running\x1b[0m
66
- $ \x1b[32mcursorflow resume --all\x1b[0m
67
- $ \x1b[32mcursorflow doctor\x1b[0m
68
- $ \x1b[32mcursorflow models\x1b[0m
42
+ \x1b[1mFLOW COMMANDS (New)\x1b[0m
43
+ \x1b[33mnew\x1b[0m <flow> --lanes "..." Create a new Flow with Lanes
44
+ \x1b[33madd\x1b[0m <flow> <lane> --task Add Tasks to a Lane
45
+ \x1b[33mconfig\x1b[0m [key] [value] View or set config (e.g., defaultModel)
46
+
47
+ \x1b[1mEXECUTION\x1b[0m
48
+ \x1b[33mrun\x1b[0m <flow> [options] Run orchestration (DAG-based)
49
+ \x1b[33mmonitor\x1b[0m [run-dir] [options] \x1b[36mInteractive\x1b[0m lane dashboard
50
+ \x1b[33mstop\x1b[0m [run-id] [options] Stop running workflows
51
+ \x1b[33mresume\x1b[0m [lane] [options] Resume lane(s)
52
+
53
+ \x1b[1mINSPECTION\x1b[0m
54
+ \x1b[33mtasks\x1b[0m [name] [options] Browse and validate prepared tasks
55
+ \x1b[33mlogs\x1b[0m [run-dir] [options] View, export, and follow logs
56
+ \x1b[33mdoctor\x1b[0m [options] Check environment and preflight
57
+
58
+ \x1b[1mUTILITY\x1b[0m
59
+ \x1b[33minit\x1b[0m [options] Initialize CursorFlow in project
60
+ \x1b[33msetup\x1b[0m [options] Install Cursor IDE commands
61
+ \x1b[33mclean\x1b[0m <type> [options] Clean branches/worktrees/logs/tasks
62
+ \x1b[33msignal\x1b[0m <lane> <msg> Directly intervene in a running lane
63
+ \x1b[33mmodels\x1b[0m [options] List available AI models
64
+
65
+ \x1b[1mLEGACY\x1b[0m
66
+ \x1b[33mprepare\x1b[0m <feature> [opts] (deprecated) Use 'new' + 'add' instead
67
+
68
+ \x1b[1mQUICK START\x1b[0m
69
+ $ \x1b[32mcursorflow new MyFeature --lanes "backend,frontend"\x1b[0m
70
+ $ \x1b[32mcursorflow add MyFeature backend --task "name=impl|model=sonnet-4.5|prompt=API 구현"\x1b[0m
71
+ $ \x1b[32mcursorflow add MyFeature frontend --task "name=ui|model=sonnet-4.5|prompt=UI 구현" --after "backend"\x1b[0m
72
+ $ \x1b[32mcursorflow run MyFeature\x1b[0m
69
73
 
70
74
  \x1b[1mDOCUMENTATION\x1b[0m
71
75
  https://github.com/eungjin-cigro/cursorflow#readme
package/src/cli/logs.ts CHANGED
@@ -5,7 +5,7 @@
5
5
  import * as fs from 'fs';
6
6
  import * as path from 'path';
7
7
  import * as logger from '../utils/logger';
8
- import { loadConfig } from '../utils/config';
8
+ import { loadConfig, getLogsDir } from '../utils/config';
9
9
  import { safeJoin } from '../utils/path';
10
10
  import {
11
11
  readJsonLog,
@@ -160,17 +160,12 @@ function displayTextLogs(
160
160
  let logFile: string;
161
161
  const readableLog = safeJoin(laneDir, 'terminal-readable.log');
162
162
  const rawLog = safeJoin(laneDir, 'terminal-raw.log');
163
- const cleanLog = safeJoin(laneDir, 'terminal.log');
164
163
 
165
164
  if (options.raw) {
166
165
  logFile = rawLog;
167
- } else if (options.clean) {
168
- logFile = cleanLog;
169
- } else if (options.readable && fs.existsSync(readableLog)) {
170
- logFile = readableLog;
171
166
  } else {
172
- // Default or fallback to clean log
173
- logFile = cleanLog;
167
+ // Default to readable log (clean option also uses readable now)
168
+ logFile = readableLog;
174
169
  }
175
170
 
176
171
  if (!fs.existsSync(logFile)) {
@@ -686,17 +681,12 @@ function followLogs(laneDir: string, options: LogsOptions): void {
686
681
  let logFile: string;
687
682
  const readableLog = safeJoin(laneDir, 'terminal-readable.log');
688
683
  const rawLog = safeJoin(laneDir, 'terminal-raw.log');
689
- const cleanLog = safeJoin(laneDir, 'terminal.log');
690
684
 
691
685
  if (options.raw) {
692
686
  logFile = rawLog;
693
- } else if (options.clean) {
694
- logFile = cleanLog;
695
- } else if (options.readable && fs.existsSync(readableLog)) {
696
- logFile = readableLog;
697
687
  } else {
698
- // Default or fallback to clean log
699
- logFile = cleanLog;
688
+ // Default to readable log
689
+ logFile = readableLog;
700
690
  }
701
691
 
702
692
  if (!fs.existsSync(logFile)) {
@@ -778,16 +768,14 @@ function displaySummary(runDir: string): void {
778
768
 
779
769
  for (const lane of lanes) {
780
770
  const laneDir = safeJoin(runDir, 'lanes', lane);
781
- const cleanLog = safeJoin(laneDir, 'terminal.log');
782
771
  const rawLog = safeJoin(laneDir, 'terminal-raw.log');
783
- const jsonLog = safeJoin(laneDir, 'terminal.jsonl');
784
772
  const readableLog = safeJoin(laneDir, 'terminal-readable.log');
785
773
 
786
774
  console.log(` ${logger.COLORS.green}📁 ${lane}${logger.COLORS.reset}`);
787
775
 
788
- if (fs.existsSync(cleanLog)) {
789
- const stats = fs.statSync(cleanLog);
790
- console.log(` └─ terminal.log ${formatSize(stats.size)}`);
776
+ if (fs.existsSync(readableLog)) {
777
+ const stats = fs.statSync(readableLog);
778
+ console.log(` └─ terminal-readable.log ${formatSize(stats.size)} ${logger.COLORS.yellow}(default)${logger.COLORS.reset}`);
791
779
  }
792
780
 
793
781
  if (fs.existsSync(rawLog)) {
@@ -795,18 +783,6 @@ function displaySummary(runDir: string): void {
795
783
  console.log(` └─ terminal-raw.log ${formatSize(stats.size)}`);
796
784
  }
797
785
 
798
- if (fs.existsSync(readableLog)) {
799
- const stats = fs.statSync(readableLog);
800
- console.log(` └─ terminal-readable.log ${formatSize(stats.size)} ${logger.COLORS.yellow}(parsed AI output)${logger.COLORS.reset}`);
801
- }
802
-
803
- if (fs.existsSync(jsonLog)) {
804
- const stats = fs.statSync(jsonLog);
805
- const entries = readJsonLog(jsonLog);
806
- const errors = entries.filter(e => e.level === 'error' || e.level === 'stderr').length;
807
- console.log(` └─ terminal.jsonl ${formatSize(stats.size)} (${entries.length} entries, ${errors} errors)`);
808
- }
809
-
810
786
  console.log('');
811
787
  }
812
788
  }
@@ -832,11 +808,22 @@ async function logs(args: string[]): Promise<void> {
832
808
  }
833
809
 
834
810
  const config = loadConfig();
811
+ const originalCwd = process.cwd();
812
+
813
+ // Change current directory to project root for consistent path handling
814
+ if (config.projectRoot !== originalCwd) {
815
+ logger.debug(`Changing directory to project root: ${config.projectRoot}`);
816
+ process.chdir(config.projectRoot);
817
+ }
835
818
 
836
819
  // Find run directory
837
820
  let runDir = options.runDir;
821
+ if (runDir && runDir !== 'latest' && !path.isAbsolute(runDir)) {
822
+ runDir = path.resolve(originalCwd, runDir);
823
+ }
824
+
838
825
  if (!runDir || runDir === 'latest') {
839
- runDir = findLatestRunDir(config.logsDir) || undefined;
826
+ runDir = findLatestRunDir(getLogsDir(config)) || undefined;
840
827
  }
841
828
 
842
829
  if (!runDir || !fs.existsSync(runDir)) {