@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/stop.ts CHANGED
@@ -86,7 +86,15 @@ async function stop(args: string[]): Promise<void> {
86
86
  return;
87
87
  }
88
88
 
89
+ const originalCwd = process.cwd();
89
90
  const config = loadConfig();
91
+
92
+ // Change current directory to project root for consistent path handling
93
+ if (config.projectRoot !== originalCwd) {
94
+ logger.debug(`Changing directory to project root: ${config.projectRoot}`);
95
+ process.chdir(config.projectRoot);
96
+ }
97
+
90
98
  const logsDir = getLogsDir(config);
91
99
  const runsDir = safeJoin(logsDir, 'runs');
92
100
  const runService = new RunService(runsDir);
package/src/cli/tasks.ts CHANGED
@@ -2,36 +2,55 @@
2
2
  * CursorFlow tasks command
3
3
  *
4
4
  * Usage:
5
- * cursorflow tasks # List all tasks
6
- * cursorflow tasks --validate # List all tasks with validation
7
- * cursorflow tasks <name> # Show detailed task info
5
+ * cursorflow tasks # List all flows (new) and tasks (legacy)
6
+ * cursorflow tasks --flows # List only flows
7
+ * cursorflow tasks --legacy # List only legacy tasks
8
+ * cursorflow tasks --validate # List with validation
9
+ * cursorflow tasks <name> # Show detailed info
8
10
  */
9
11
 
12
+ import * as fs from 'fs';
13
+ import * as path from 'path';
10
14
  import * as logger from '../utils/logger';
11
15
  import { TaskService, TaskDirInfo, ValidationStatus } from '../utils/task-service';
12
- import { findProjectRoot, loadConfig, getTasksDir } from '../utils/config';
16
+ import { findProjectRoot, loadConfig, getTasksDir, getFlowsDir } from '../utils/config';
17
+ import { safeJoin } from '../utils/path';
13
18
 
14
19
  const COLORS = logger.COLORS;
15
20
 
21
+ interface FlowInfo {
22
+ id: string;
23
+ name: string;
24
+ path: string;
25
+ timestamp: Date;
26
+ lanes: string[];
27
+ status: string;
28
+ }
29
+
16
30
  interface TasksCliOptions {
17
31
  validate: boolean;
18
32
  taskName: string | null;
33
+ flowsOnly: boolean;
34
+ legacyOnly: boolean;
19
35
  }
20
36
 
21
37
  function printHelp(): void {
22
38
  console.log(`
23
- Usage: cursorflow tasks [options] [task-name]
39
+ Usage: cursorflow tasks [options] [name]
24
40
 
25
- Manage and view prepared tasks in _cursorflow/tasks/.
41
+ List and view flows (new) and prepared tasks (legacy).
26
42
 
27
43
  Options:
28
- --validate Run validation on all tasks before listing
44
+ --flows Show only flows (from _cursorflow/flows/)
45
+ --legacy Show only legacy tasks (from _cursorflow/tasks/)
46
+ --validate Run validation before listing
29
47
  --help, -h Show help
30
48
 
31
49
  Examples:
32
- cursorflow tasks
33
- cursorflow tasks --validate
34
- cursorflow tasks 2412221530_AuthSystem
50
+ cursorflow tasks # List all flows and tasks
51
+ cursorflow tasks --flows # List only flows
52
+ cursorflow tasks TestFeature # Show flow or task details
53
+ cursorflow tasks --validate # Validate all entries
35
54
  `);
36
55
  }
37
56
 
@@ -39,6 +58,8 @@ function parseArgs(args: string[]): TasksCliOptions {
39
58
  const options: TasksCliOptions = {
40
59
  validate: args.includes('--validate'),
41
60
  taskName: null,
61
+ flowsOnly: args.includes('--flows'),
62
+ legacyOnly: args.includes('--legacy'),
42
63
  };
43
64
 
44
65
  const nameArg = args.find(arg => !arg.startsWith('-'));
@@ -54,6 +75,136 @@ function parseArgs(args: string[]): TasksCliOptions {
54
75
  return options;
55
76
  }
56
77
 
78
+ /**
79
+ * List flows from _cursorflow/flows/
80
+ */
81
+ function listFlows(flowsDir: string): FlowInfo[] {
82
+ if (!fs.existsSync(flowsDir)) {
83
+ return [];
84
+ }
85
+
86
+ const dirs = fs.readdirSync(flowsDir)
87
+ .filter(name => {
88
+ const dirPath = safeJoin(flowsDir, name);
89
+ return fs.statSync(dirPath).isDirectory() && !name.startsWith('.');
90
+ })
91
+ .sort((a, b) => b.localeCompare(a)); // Most recent first
92
+
93
+ return dirs.map(name => {
94
+ const flowPath = safeJoin(flowsDir, name);
95
+ const metaPath = safeJoin(flowPath, 'flow.meta.json');
96
+
97
+ let meta: any = {};
98
+ try {
99
+ if (fs.existsSync(metaPath)) {
100
+ meta = JSON.parse(fs.readFileSync(metaPath, 'utf-8'));
101
+ }
102
+ } catch {}
103
+
104
+ // Parse flow name from directory (e.g., "001_TestFeature" -> "TestFeature")
105
+ const match = name.match(/^(\d+)_(.+)$/);
106
+ const id = match ? match[1] : name;
107
+ const flowName = match ? match[2] : name;
108
+
109
+ // Get lane files
110
+ const laneFiles = fs.readdirSync(flowPath)
111
+ .filter(f => f.endsWith('.json') && f !== 'flow.meta.json')
112
+ .map(f => {
113
+ const laneMatch = f.match(/^\d+-([^.]+)\.json$/);
114
+ return laneMatch ? laneMatch[1] : f.replace('.json', '');
115
+ });
116
+
117
+ return {
118
+ id,
119
+ name: flowName,
120
+ path: flowPath,
121
+ timestamp: meta.createdAt ? new Date(meta.createdAt) : new Date(),
122
+ lanes: laneFiles,
123
+ status: meta.status || 'pending',
124
+ };
125
+ });
126
+ }
127
+
128
+ /**
129
+ * Get flow info by name
130
+ */
131
+ function getFlowInfo(flowsDir: string, flowName: string): FlowInfo | null {
132
+ const flows = listFlows(flowsDir);
133
+ return flows.find(f => f.name === flowName || `${f.id}_${f.name}` === flowName) || null;
134
+ }
135
+
136
+ /**
137
+ * Print flows list
138
+ */
139
+ function printFlowsList(flows: FlowInfo[]): void {
140
+ if (flows.length === 0) {
141
+ logger.info('No flows found in _cursorflow/flows/');
142
+ return;
143
+ }
144
+
145
+ console.log(`${COLORS.bold}Flows:${COLORS.reset}`);
146
+
147
+ for (let i = 0; i < flows.length; i++) {
148
+ const flow = flows[i]!;
149
+ const prefix = i === 0 ? ' ▶' : ' ';
150
+ const name = `${flow.id}_${flow.name}`.padEnd(30);
151
+ const lanes = `${flow.lanes.length} lane${flow.lanes.length !== 1 ? 's' : ''}`.padEnd(10);
152
+ const status = flow.status.padEnd(10);
153
+ const date = formatDate(flow.timestamp);
154
+
155
+ let color = COLORS.reset;
156
+ if (flow.status === 'completed') color = COLORS.green;
157
+ else if (flow.status === 'running') color = COLORS.cyan;
158
+ else if (flow.status === 'failed') color = COLORS.red;
159
+
160
+ console.log(`${color}${prefix} ${name} ${lanes} ${status} ${date}${COLORS.reset}`);
161
+ }
162
+ }
163
+
164
+ /**
165
+ * Print flow details
166
+ */
167
+ function printFlowDetail(flow: FlowInfo, flowsDir: string): void {
168
+ console.log(`${COLORS.bold}Flow: ${flow.name}${COLORS.reset}`);
169
+ console.log(`${COLORS.cyan}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${COLORS.reset}`);
170
+ console.log(` ID: ${flow.id}`);
171
+ console.log(` Status: ${flow.status}`);
172
+ console.log(` Path: ${flow.path}`);
173
+ console.log('');
174
+
175
+ if (flow.lanes.length === 0) {
176
+ console.log('No lanes defined.');
177
+ } else {
178
+ console.log(`${COLORS.bold}Lanes:${COLORS.reset}`);
179
+ for (const laneName of flow.lanes) {
180
+ // Read lane file for task info
181
+ const laneFiles = fs.readdirSync(flow.path)
182
+ .filter(f => f.endsWith('.json') && f !== 'flow.meta.json');
183
+
184
+ const laneFile = laneFiles.find(f => {
185
+ const match = f.match(/^\d+-([^.]+)\.json$/);
186
+ return match && match[1] === laneName;
187
+ });
188
+
189
+ if (laneFile) {
190
+ try {
191
+ const laneData = JSON.parse(fs.readFileSync(safeJoin(flow.path, laneFile), 'utf-8'));
192
+ const tasks = laneData.tasks || [];
193
+ const taskFlow = tasks.map((t: any) => t.name).join(' → ');
194
+ console.log(` ${laneName.padEnd(18)} ${COLORS.blue}[${tasks.length} tasks]${COLORS.reset} ${taskFlow}`);
195
+ } catch {
196
+ console.log(` ${laneName.padEnd(18)} ${COLORS.yellow}[error reading]${COLORS.reset}`);
197
+ }
198
+ } else {
199
+ console.log(` ${laneName}`);
200
+ }
201
+ }
202
+ }
203
+
204
+ console.log('');
205
+ console.log(`${COLORS.gray}Run: cursorflow run ${flow.name}${COLORS.reset}`);
206
+ }
207
+
57
208
  function formatDate(date: Date): string {
58
209
  const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
59
210
  return `${months[date.getMonth()]} ${date.getDate()}`;
@@ -107,9 +258,8 @@ function printTaskDetail(info: TaskDirInfo): void {
107
258
  const fileName = lane.fileName.padEnd(18);
108
259
  const preset = `[${lane.preset}]`.padEnd(10);
109
260
  const flow = lane.taskFlow;
110
- const depends = lane.dependsOn.length > 0 ? ` ${COLORS.gray}(depends: ${lane.dependsOn.join(', ')})${COLORS.reset}` : '';
111
261
 
112
- console.log(` ${fileName} ${COLORS.blue}${preset}${COLORS.reset} ${flow}${depends}`);
262
+ console.log(` ${fileName} ${COLORS.blue}${preset}${COLORS.reset} ${flow}`);
113
263
  }
114
264
  }
115
265
 
@@ -119,25 +269,37 @@ async function tasks(args: string[]): Promise<void> {
119
269
  const options = parseArgs(args);
120
270
  const projectRoot = findProjectRoot();
121
271
  const config = loadConfig(projectRoot);
272
+ const flowsDir = getFlowsDir(config);
122
273
  const tasksDir = getTasksDir(config);
123
274
  const taskService = new TaskService(tasksDir);
124
275
 
276
+ // Check for specific flow/task by name
125
277
  if (options.taskName) {
126
- const info = taskService.getTaskDirInfo(options.taskName);
127
- if (!info) {
128
- logger.error(`Task not found: ${options.taskName}`);
129
- process.exit(1);
278
+ // First try to find as a flow
279
+ const flowInfo = getFlowInfo(flowsDir, options.taskName);
280
+ if (flowInfo) {
281
+ printFlowDetail(flowInfo, flowsDir);
282
+ return;
130
283
  }
131
284
 
132
- // Always validate for detail view to have report
285
+ // Then try as a legacy task
286
+ const taskInfo = taskService.getTaskDirInfo(options.taskName);
287
+ if (taskInfo) {
133
288
  taskService.validateTaskDir(options.taskName);
134
289
  const updatedInfo = taskService.getTaskDirInfo(options.taskName)!;
135
-
136
290
  printTaskDetail(updatedInfo);
137
- } else {
138
- let taskList = taskService.listTaskDirs();
291
+ return;
292
+ }
293
+
294
+ logger.error(`Flow or task not found: ${options.taskName}`);
295
+ process.exit(1);
296
+ }
297
+
298
+ // List flows and/or tasks
299
+ const flows = options.legacyOnly ? [] : listFlows(flowsDir);
300
+ let taskList = options.flowsOnly ? [] : taskService.listTaskDirs();
139
301
 
140
- if (options.validate) {
302
+ if (options.validate && taskList.length > 0) {
141
303
  const spinner = logger.createSpinner('Validating tasks...');
142
304
  spinner.start();
143
305
  for (const task of taskList) {
@@ -147,8 +309,25 @@ async function tasks(args: string[]): Promise<void> {
147
309
  taskList = taskService.listTaskDirs();
148
310
  }
149
311
 
312
+ // Print results
313
+ if (flows.length > 0) {
314
+ printFlowsList(flows);
315
+ if (taskList.length > 0) {
316
+ console.log('');
317
+ }
318
+ }
319
+
320
+ if (taskList.length > 0) {
321
+ console.log(`${COLORS.gray}Legacy Tasks:${COLORS.reset}`);
150
322
  printTasksList(taskList);
151
323
  }
324
+
325
+ if (flows.length === 0 && taskList.length === 0) {
326
+ logger.info('No flows or tasks found.');
327
+ console.log('');
328
+ console.log('Create a new flow:');
329
+ console.log(' cursorflow new <FlowName> --lanes "lane1,lane2"');
330
+ }
152
331
  }
153
332
 
154
333
  export = tasks;
@@ -129,6 +129,15 @@ export interface FailureContext {
129
129
  /**
130
130
  * Analyze stall condition with multi-layer detection and escalating recovery
131
131
  *
132
+ * @deprecated Use StallDetectionService from './stall-detection' instead.
133
+ * This function is kept for backward compatibility but will be removed in a future version.
134
+ *
135
+ * The new unified StallDetectionService provides:
136
+ * - Single source of truth for stall state
137
+ * - Automatic recovery action execution
138
+ * - Better heartbeat filtering
139
+ * - Consistent state management
140
+ *
132
141
  * Recovery escalation stages:
133
142
  * 1. Phase 0 → Phase 1: Send continue signal (after 2 min idle)
134
143
  * 2. Phase 1 → Phase 2: Send stronger prompt (after 2 min grace)