@litmers/cursorflow-orchestrator 0.1.39 → 0.2.2

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 (214) hide show
  1. package/CHANGELOG.md +0 -2
  2. package/README.md +20 -16
  3. package/commands/cursorflow-init.md +0 -4
  4. package/dist/cli/logs.js +108 -9
  5. package/dist/cli/logs.js.map +1 -1
  6. package/dist/cli/models.js +20 -3
  7. package/dist/cli/models.js.map +1 -1
  8. package/dist/cli/monitor.d.ts +7 -10
  9. package/dist/cli/monitor.js +1088 -1240
  10. package/dist/cli/monitor.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 +23 -5
  14. package/dist/cli/resume.js.map +1 -1
  15. package/dist/cli/run.js +28 -9
  16. package/dist/cli/run.js.map +1 -1
  17. package/dist/cli/signal.d.ts +6 -1
  18. package/dist/cli/signal.js +94 -12
  19. package/dist/cli/signal.js.map +1 -1
  20. package/dist/cli/tasks.js +3 -46
  21. package/dist/cli/tasks.js.map +1 -1
  22. package/dist/core/agent-supervisor.d.ts +23 -0
  23. package/dist/core/agent-supervisor.js +42 -0
  24. package/dist/core/agent-supervisor.js.map +1 -0
  25. package/dist/core/auto-recovery.d.ts +2 -1
  26. package/dist/core/auto-recovery.js +6 -1
  27. package/dist/core/auto-recovery.js.map +1 -1
  28. package/dist/core/failure-policy.d.ts +0 -1
  29. package/dist/core/failure-policy.js +0 -1
  30. package/dist/core/failure-policy.js.map +1 -1
  31. package/dist/core/git-lifecycle-manager.d.ts +284 -0
  32. package/dist/core/git-lifecycle-manager.js +778 -0
  33. package/dist/core/git-lifecycle-manager.js.map +1 -0
  34. package/dist/core/git-pipeline-coordinator.d.ts +21 -0
  35. package/dist/core/git-pipeline-coordinator.js +205 -0
  36. package/dist/core/git-pipeline-coordinator.js.map +1 -0
  37. package/dist/core/intervention.d.ts +176 -0
  38. package/dist/core/intervention.js +424 -0
  39. package/dist/core/intervention.js.map +1 -0
  40. package/dist/core/lane-state-machine.d.ts +423 -0
  41. package/dist/core/lane-state-machine.js +890 -0
  42. package/dist/core/lane-state-machine.js.map +1 -0
  43. package/dist/core/orchestrator.d.ts +4 -1
  44. package/dist/core/orchestrator.js +38 -63
  45. package/dist/core/orchestrator.js.map +1 -1
  46. package/dist/core/runner/agent.d.ts +7 -1
  47. package/dist/core/runner/agent.js +45 -30
  48. package/dist/core/runner/agent.js.map +1 -1
  49. package/dist/core/runner/pipeline.js +283 -109
  50. package/dist/core/runner/pipeline.js.map +1 -1
  51. package/dist/core/runner/task.d.ts +4 -5
  52. package/dist/core/runner/task.js +6 -77
  53. package/dist/core/runner/task.js.map +1 -1
  54. package/dist/core/runner.js +11 -2
  55. package/dist/core/runner.js.map +1 -1
  56. package/dist/core/stall-detection.d.ts +27 -4
  57. package/dist/core/stall-detection.js +116 -28
  58. package/dist/core/stall-detection.js.map +1 -1
  59. package/dist/hooks/contexts/index.d.ts +104 -0
  60. package/dist/hooks/contexts/index.js +134 -0
  61. package/dist/hooks/contexts/index.js.map +1 -0
  62. package/dist/hooks/data-accessor.d.ts +86 -0
  63. package/dist/hooks/data-accessor.js +410 -0
  64. package/dist/hooks/data-accessor.js.map +1 -0
  65. package/dist/hooks/flow-controller.d.ts +136 -0
  66. package/dist/hooks/flow-controller.js +351 -0
  67. package/dist/hooks/flow-controller.js.map +1 -0
  68. package/dist/hooks/index.d.ts +68 -0
  69. package/dist/hooks/index.js +105 -0
  70. package/dist/hooks/index.js.map +1 -0
  71. package/dist/hooks/manager.d.ts +129 -0
  72. package/dist/hooks/manager.js +389 -0
  73. package/dist/hooks/manager.js.map +1 -0
  74. package/dist/hooks/types.d.ts +463 -0
  75. package/dist/hooks/types.js +45 -0
  76. package/dist/hooks/types.js.map +1 -0
  77. package/dist/services/logging/buffer.d.ts +2 -2
  78. package/dist/services/logging/buffer.js +95 -42
  79. package/dist/services/logging/buffer.js.map +1 -1
  80. package/dist/services/logging/console.js +8 -5
  81. package/dist/services/logging/console.js.map +1 -1
  82. package/dist/services/logging/formatter.d.ts +9 -3
  83. package/dist/services/logging/formatter.js +64 -17
  84. package/dist/services/logging/formatter.js.map +1 -1
  85. package/dist/services/logging/index.d.ts +0 -1
  86. package/dist/services/logging/index.js +0 -1
  87. package/dist/services/logging/index.js.map +1 -1
  88. package/dist/services/logging/paths.d.ts +8 -0
  89. package/dist/services/logging/paths.js +48 -0
  90. package/dist/services/logging/paths.js.map +1 -0
  91. package/dist/services/logging/raw-log.d.ts +6 -0
  92. package/dist/services/logging/raw-log.js +37 -0
  93. package/dist/services/logging/raw-log.js.map +1 -0
  94. package/dist/services/process/index.js +1 -1
  95. package/dist/services/process/index.js.map +1 -1
  96. package/dist/types/agent.d.ts +15 -0
  97. package/dist/types/config.d.ts +24 -1
  98. package/dist/types/event-categories.d.ts +601 -0
  99. package/dist/types/event-categories.js +233 -0
  100. package/dist/types/event-categories.js.map +1 -0
  101. package/dist/types/events.d.ts +0 -20
  102. package/dist/types/flow.d.ts +10 -6
  103. package/dist/types/index.d.ts +1 -1
  104. package/dist/types/index.js +17 -3
  105. package/dist/types/index.js.map +1 -1
  106. package/dist/types/lane.d.ts +1 -1
  107. package/dist/types/logging.d.ts +1 -1
  108. package/dist/types/task.d.ts +13 -2
  109. package/dist/ui/log-viewer.d.ts +3 -0
  110. package/dist/ui/log-viewer.js +3 -0
  111. package/dist/ui/log-viewer.js.map +1 -1
  112. package/dist/utils/config.js +15 -1
  113. package/dist/utils/config.js.map +1 -1
  114. package/dist/utils/cursor-agent.d.ts +11 -1
  115. package/dist/utils/cursor-agent.js +63 -16
  116. package/dist/utils/cursor-agent.js.map +1 -1
  117. package/dist/utils/enhanced-logger.d.ts +5 -1
  118. package/dist/utils/enhanced-logger.js +99 -20
  119. package/dist/utils/enhanced-logger.js.map +1 -1
  120. package/dist/utils/event-registry.d.ts +222 -0
  121. package/dist/utils/event-registry.js +463 -0
  122. package/dist/utils/event-registry.js.map +1 -0
  123. package/dist/utils/events.d.ts +1 -13
  124. package/dist/utils/events.js.map +1 -1
  125. package/dist/utils/flow.d.ts +10 -0
  126. package/dist/utils/flow.js +75 -0
  127. package/dist/utils/flow.js.map +1 -1
  128. package/dist/utils/git.d.ts +12 -1
  129. package/dist/utils/git.js +54 -1
  130. package/dist/utils/git.js.map +1 -1
  131. package/dist/utils/log-constants.d.ts +1 -0
  132. package/dist/utils/log-constants.js +2 -1
  133. package/dist/utils/log-constants.js.map +1 -1
  134. package/dist/utils/log-formatter.d.ts +3 -2
  135. package/dist/utils/log-formatter.js +11 -11
  136. package/dist/utils/log-formatter.js.map +1 -1
  137. package/dist/utils/logger.d.ts +11 -0
  138. package/dist/utils/logger.js +82 -3
  139. package/dist/utils/logger.js.map +1 -1
  140. package/dist/utils/repro-thinking-logs.js +0 -13
  141. package/dist/utils/repro-thinking-logs.js.map +1 -1
  142. package/dist/utils/run-service.js +1 -1
  143. package/dist/utils/run-service.js.map +1 -1
  144. package/examples/README.md +0 -2
  145. package/examples/demo-project/README.md +1 -2
  146. package/package.json +18 -28
  147. package/scripts/setup-security.sh +0 -1
  148. package/scripts/test-log-parser.ts +171 -0
  149. package/scripts/verify-change.sh +272 -0
  150. package/src/cli/logs.ts +121 -10
  151. package/src/cli/models.ts +20 -3
  152. package/src/cli/monitor.ts +1257 -1342
  153. package/src/cli/prepare.ts +0 -1
  154. package/src/cli/resume.ts +29 -5
  155. package/src/cli/run.ts +29 -11
  156. package/src/cli/signal.ts +115 -17
  157. package/src/cli/tasks.ts +2 -59
  158. package/src/core/agent-supervisor.ts +64 -0
  159. package/src/core/auto-recovery.ts +7 -1
  160. package/src/core/failure-policy.ts +0 -1
  161. package/src/core/git-lifecycle-manager.ts +1011 -0
  162. package/src/core/git-pipeline-coordinator.ts +221 -0
  163. package/src/core/intervention.ts +481 -0
  164. package/src/core/lane-state-machine.ts +1097 -0
  165. package/src/core/orchestrator.ts +45 -62
  166. package/src/core/runner/agent.ts +66 -33
  167. package/src/core/runner/pipeline.ts +318 -122
  168. package/src/core/runner/task.ts +12 -93
  169. package/src/core/runner.ts +12 -2
  170. package/src/core/stall-detection.ts +145 -28
  171. package/src/hooks/contexts/index.ts +256 -0
  172. package/src/hooks/data-accessor.ts +488 -0
  173. package/src/hooks/flow-controller.ts +425 -0
  174. package/src/hooks/index.ts +154 -0
  175. package/src/hooks/manager.ts +434 -0
  176. package/src/hooks/types.ts +544 -0
  177. package/src/services/logging/buffer.ts +104 -43
  178. package/src/services/logging/console.ts +9 -5
  179. package/src/services/logging/formatter.ts +74 -17
  180. package/src/services/logging/index.ts +0 -2
  181. package/src/services/logging/paths.ts +14 -0
  182. package/src/services/logging/raw-log.ts +43 -0
  183. package/src/services/process/index.ts +1 -1
  184. package/src/types/agent.ts +15 -0
  185. package/src/types/config.ts +25 -1
  186. package/src/types/event-categories.ts +663 -0
  187. package/src/types/events.ts +0 -25
  188. package/src/types/flow.ts +10 -6
  189. package/src/types/index.ts +50 -4
  190. package/src/types/lane.ts +1 -2
  191. package/src/types/logging.ts +2 -1
  192. package/src/types/task.ts +13 -2
  193. package/src/ui/log-viewer.ts +3 -0
  194. package/src/utils/config.ts +17 -1
  195. package/src/utils/cursor-agent.ts +68 -16
  196. package/src/utils/enhanced-logger.ts +106 -20
  197. package/src/utils/event-registry.ts +595 -0
  198. package/src/utils/events.ts +0 -16
  199. package/src/utils/flow.ts +84 -0
  200. package/src/utils/git.ts +59 -1
  201. package/src/utils/log-constants.ts +2 -1
  202. package/src/utils/log-formatter.ts +11 -12
  203. package/src/utils/logger.ts +49 -3
  204. package/src/utils/repro-thinking-logs.ts +0 -15
  205. package/src/utils/run-service.ts +1 -1
  206. package/dist/services/logging/file-writer.d.ts +0 -71
  207. package/dist/services/logging/file-writer.js +0 -516
  208. package/dist/services/logging/file-writer.js.map +0 -1
  209. package/dist/types/review.d.ts +0 -17
  210. package/dist/types/review.js +0 -6
  211. package/dist/types/review.js.map +0 -1
  212. package/scripts/ai-security-check.js +0 -233
  213. package/src/services/logging/file-writer.ts +0 -526
  214. package/src/types/review.ts +0 -20
@@ -9,6 +9,7 @@ import * as path from 'path';
9
9
  import { EventEmitter } from 'events';
10
10
  import { LogImportance, JsonLogEntry, BufferedLogEntry as BufferedLogEntryType, MessageType } from '../../types/logging';
11
11
  import { COLORS } from './console';
12
+ import { stripAnsi } from './formatter';
12
13
 
13
14
  // Re-export types for convenience
14
15
  export type { BufferedLogEntry } from '../../types/logging';
@@ -108,13 +109,13 @@ export class LogBufferService extends EventEmitter {
108
109
  const newEntries: BufferedLogEntryType[] = [];
109
110
 
110
111
  for (const laneName of this.lanes) {
111
- const jsonlPath = path.join(lanesDir, laneName, 'terminal.jsonl');
112
+ const readablePath = path.join(lanesDir, laneName, 'terminal-readable.log');
112
113
 
113
114
  let fd: number | null = null;
114
115
  try {
115
116
  // Read file content atomically to avoid TOCTOU race condition
116
- const lastPos = this.filePositions.get(jsonlPath) || 0;
117
- fd = fs.openSync(jsonlPath, 'r');
117
+ const lastPos = this.filePositions.get(readablePath) || 0;
118
+ fd = fs.openSync(readablePath, 'r');
118
119
  const stat = fs.fstatSync(fd); // Use fstat on open fd to avoid race
119
120
 
120
121
  if (stat.size > lastPos) {
@@ -125,14 +126,11 @@ export class LogBufferService extends EventEmitter {
125
126
  const lines = newContent.split('\n').filter(line => line.trim());
126
127
 
127
128
  for (const line of lines) {
128
- try {
129
- const entry = JSON.parse(line) as JsonLogEntry;
130
- const processed = this.processEntry(entry, laneName);
131
- if (processed) newEntries.push(processed);
132
- } catch { /* Skip invalid JSON */ }
129
+ const processed = this.processReadableLine(line, laneName);
130
+ if (processed) newEntries.push(processed);
133
131
  }
134
132
 
135
- this.filePositions.set(jsonlPath, stat.size);
133
+ this.filePositions.set(readablePath, stat.size);
136
134
  }
137
135
  } catch { /* File in use, skip */ }
138
136
  finally {
@@ -156,11 +154,11 @@ export class LogBufferService extends EventEmitter {
156
154
  }
157
155
  }
158
156
 
159
- private processEntry(entry: JsonLogEntry, laneName: string): BufferedLogEntryType | null {
160
- const timestamp = new Date(entry.timestamp || Date.now());
161
- const type = (entry.type || 'unknown') as MessageType;
162
- const level = entry.level || this.inferLevel(type);
163
- const message = entry.content || entry.message || JSON.stringify(entry);
157
+ private processReadableLine(line: string, laneName: string): BufferedLogEntryType | null {
158
+ const cleaned = stripAnsi(line).trim();
159
+ if (!cleaned) return null;
160
+
161
+ const { timestamp, message, level, type } = this.parseReadableMessage(cleaned);
164
162
  const importance = this.inferImportance(type, level);
165
163
 
166
164
  return {
@@ -172,23 +170,63 @@ export class LogBufferService extends EventEmitter {
172
170
  message: this.truncateMessage(message),
173
171
  importance,
174
172
  laneColor: this.laneColorMap.get(laneName) || COLORS.white,
175
- raw: entry,
173
+ raw: {
174
+ timestamp: timestamp.toISOString(),
175
+ level: level as JsonLogEntry['level'],
176
+ lane: laneName,
177
+ message,
178
+ },
176
179
  };
177
180
  }
178
181
 
179
- private inferLevel(type: string): string {
180
- switch (type.toLowerCase()) {
181
- case 'error':
182
- case 'stderr':
183
- return 'error';
184
- case 'warning':
185
- return 'warn';
186
- case 'debug':
187
- case 'thinking':
188
- return 'debug';
189
- default:
190
- return 'info';
182
+ private parseReadableMessage(line: string): {
183
+ timestamp: Date;
184
+ message: string;
185
+ level: string;
186
+ type: MessageType | string;
187
+ } {
188
+ let remaining = line;
189
+ let timestamp = new Date();
190
+
191
+ const isoMatch = remaining.match(/^\[(\d{4}-\d{2}-\d{2}T[^\]]+)\]\s*/);
192
+ if (isoMatch) {
193
+ timestamp = new Date(isoMatch[1]!);
194
+ remaining = remaining.slice(isoMatch[0].length);
195
+ } else {
196
+ const timeMatch = remaining.match(/^\[(\d{2}:\d{2}:\d{2})\]\s*/);
197
+ if (timeMatch) {
198
+ const [hours, minutes, seconds] = timeMatch[1]!.split(':').map(Number);
199
+ const now = new Date();
200
+ now.setHours(hours || 0, minutes || 0, seconds || 0, 0);
201
+ timestamp = now;
202
+ remaining = remaining.slice(timeMatch[0].length);
203
+ }
191
204
  }
205
+
206
+ const labelMatch = remaining.match(/^\[[^\]]+\]\s*/);
207
+ if (labelMatch) {
208
+ remaining = remaining.slice(labelMatch[0].length);
209
+ }
210
+
211
+ const upper = remaining.toUpperCase();
212
+ let level = 'info';
213
+ let type: MessageType | string = 'stdout';
214
+
215
+ if (remaining.includes('❌') || upper.includes('ERR') || upper.includes('ERROR')) {
216
+ level = 'error';
217
+ type = 'error';
218
+ } else if (remaining.includes('⚠️') || upper.includes('WARN')) {
219
+ level = 'warn';
220
+ type = 'warn';
221
+ } else if (remaining.includes('🔍') || upper.includes('DEBUG')) {
222
+ level = 'debug';
223
+ type = 'debug';
224
+ } else if (remaining.includes('ℹ️') || upper.includes('INFO')) {
225
+ level = 'info';
226
+ type = 'info';
227
+ }
228
+
229
+ return { timestamp, message: remaining, level, type };
192
230
  }
193
231
 
194
232
  private inferImportance(type: string, level: string): LogImportance {
@@ -295,32 +333,55 @@ export class LogBufferService extends EventEmitter {
295
333
  }
296
334
 
297
335
  if (showLane) {
298
- parts.push(`${entry.laneColor}[${entry.laneName.padEnd(12)}]${COLORS.reset}`);
336
+ // Lane label: fixed 14 chars inside brackets
337
+ const truncatedLane = entry.laneName.length > 14 ? entry.laneName.substring(0, 14) : entry.laneName;
338
+ parts.push(`${COLORS.magenta}[${truncatedLane.padEnd(14)}]${COLORS.reset}`);
299
339
  }
300
340
 
301
- parts.push(this.getTypeIndicator(entry.type));
302
- parts.push(entry.message);
341
+ const { indicator, messageColor } = this.getTypeIndicator(entry.type);
342
+ parts.push(indicator);
343
+
344
+ // Apply message color if needed
345
+ const coloredMessage = messageColor ? `${messageColor}${entry.message}${COLORS.reset}` : entry.message;
346
+ parts.push(coloredMessage);
303
347
 
304
348
  return parts.join(' ');
305
349
  }
306
350
 
307
- private getTypeIndicator(type: string): string {
308
- const indicators: Record<string, string> = {
309
- user: `${COLORS.cyan}[USER ]${COLORS.reset}`,
310
- assistant: `${COLORS.green}[ASST ]${COLORS.reset}`,
311
- tool: `${COLORS.yellow}[TOOL ]${COLORS.reset}`,
312
- tool_result: `${COLORS.gray}[RESULT]${COLORS.reset}`,
313
- error: `${COLORS.red}[ERROR ]${COLORS.reset}`,
314
- stderr: `${COLORS.red}[ERROR ]${COLORS.reset}`,
315
- thinking: `${COLORS.gray}[THINK ]${COLORS.reset}`,
316
- result: `${COLORS.green}[DONE ]${COLORS.reset}`,
317
- stdout: `${COLORS.white}[STDOUT]${COLORS.reset}`,
351
+ private getTypeIndicator(type: string): { indicator: string; messageColor: string } {
352
+ // Color rules:
353
+ // - Important (colored): user(cyan), assistant(green), result(green), error(red), warn(yellow)
354
+ // - Less important (gray): tool, tool_result, thinking, system, debug, stdout, info, raw
355
+ const indicators: Record<string, { indicator: string; messageColor: string }> = {
356
+ user: { indicator: `${COLORS.cyan}🧑 USER${COLORS.reset}`, messageColor: '' },
357
+ assistant: { indicator: `${COLORS.green}🤖 ASST${COLORS.reset}`, messageColor: '' },
358
+ tool: { indicator: `${COLORS.gray}🔧 TOOL${COLORS.reset}`, messageColor: COLORS.gray },
359
+ tool_result: { indicator: `${COLORS.gray}📄 RESL${COLORS.reset}`, messageColor: COLORS.gray },
360
+ error: { indicator: `${COLORS.red}❌ ERR${COLORS.reset}`, messageColor: COLORS.red },
361
+ stderr: { indicator: `${COLORS.red} >>${COLORS.reset}`, messageColor: COLORS.red },
362
+ thinking: { indicator: `${COLORS.gray}🤔 THNK${COLORS.reset}`, messageColor: COLORS.gray },
363
+ result: { indicator: `${COLORS.green}✅ DONE${COLORS.reset}`, messageColor: '' },
364
+ success: { indicator: `${COLORS.green}✅ DONE${COLORS.reset}`, messageColor: '' },
365
+ stdout: { indicator: `${COLORS.gray} >>${COLORS.reset}`, messageColor: COLORS.gray },
366
+ raw: { indicator: `${COLORS.gray} >>${COLORS.reset}`, messageColor: COLORS.gray },
367
+ info: { indicator: `${COLORS.gray}ℹ️ INFO${COLORS.reset}`, messageColor: COLORS.gray },
368
+ warn: { indicator: `${COLORS.yellow}⚠️ WARN${COLORS.reset}`, messageColor: '' },
369
+ debug: { indicator: `${COLORS.gray}🔍 DBUG${COLORS.reset}`, messageColor: COLORS.gray },
370
+ system: { indicator: `${COLORS.gray}⚙️ SYS${COLORS.reset}`, messageColor: COLORS.gray },
371
+ progress: { indicator: `${COLORS.blue}🔄 PROG${COLORS.reset}`, messageColor: '' },
372
+ };
373
+
374
+ const match = indicators[type.toLowerCase()];
375
+ if (match) return match;
376
+
377
+ // Default: gray for unknown types
378
+ return {
379
+ indicator: `${COLORS.gray} ${type.toUpperCase().substring(0, 4).padEnd(4)}${COLORS.reset}`,
380
+ messageColor: COLORS.gray
318
381
  };
319
- return indicators[type.toLowerCase()] || `${COLORS.gray}[${type.toUpperCase().padEnd(6)}]${COLORS.reset}`;
320
382
  }
321
383
  }
322
384
 
323
385
  export function createLogBuffer(runDir: string, options?: LogBufferOptions): LogBufferService {
324
386
  return new LogBufferService(runDir, options);
325
387
  }
326
-
@@ -94,6 +94,10 @@ function normalizeOptions(options: LogOptions | string | undefined, defaultEmoji
94
94
  }
95
95
 
96
96
  // Primary logging functions
97
+ // Color rules:
98
+ // - Important (colored): error(red), warn(yellow), success(green)
99
+ // - Less important (gray): info, debug
100
+
97
101
  export function error(message: string, options?: LogOptions | string): void {
98
102
  logWithColor(COLORS.red, 'error', message, normalizeOptions(options, '❌'));
99
103
  }
@@ -103,7 +107,8 @@ export function warn(message: string, options?: LogOptions | string): void {
103
107
  }
104
108
 
105
109
  export function info(message: string, options?: LogOptions | string): void {
106
- logWithColor(COLORS.cyan, 'info', message, normalizeOptions(options, 'ℹ️'));
110
+ // Info is gray (less important) - focus on important messages
111
+ logWithColor(COLORS.gray, 'info', message, normalizeOptions(options, 'ℹ️'));
107
112
  }
108
113
 
109
114
  export function success(message: string, options?: LogOptions | string): void {
@@ -115,6 +120,7 @@ export function debug(message: string, options?: LogOptions | string): void {
115
120
  }
116
121
 
117
122
  export function progress(message: string, options?: LogOptions | string): void {
123
+ // Progress is blue (visible but not critical)
118
124
  logWithColor(COLORS.blue, 'info', message, normalizeOptions(options, '🔄'));
119
125
  }
120
126
 
@@ -149,12 +155,10 @@ export function withContext(context: string) {
149
155
  */
150
156
  export function laneOutput(laneName: string, message: string, isError = false, laneIndex?: number, taskIndex?: number, taskName?: string): void {
151
157
  const timestamp = `${COLORS.gray}[${formatTimestamp()}]${COLORS.reset}`;
152
- // Format: [laneIdx-taskIdx-laneName-taskName] padded to 18 chars inside brackets
158
+ // Format: [laneIdx-taskIdx-laneName] padded to 18 chars inside brackets
153
159
  const lIdx = laneIndex ?? 1;
154
160
  const tIdx = taskIndex ?? 1;
155
- const combined = taskName
156
- ? `${lIdx}-${tIdx}-${laneName}-${taskName}`
157
- : `${lIdx}-${tIdx}-${laneName}`;
161
+ const combined = `${lIdx}-${tIdx}-${laneName}`;
158
162
  const label = combined.substring(0, 18).padEnd(18);
159
163
  const laneLabel = `${COLORS.magenta}[${label}]${COLORS.reset}`;
160
164
  const output = isError ? `${COLORS.red}${message}${COLORS.reset}` : message;
@@ -3,17 +3,26 @@
3
3
  *
4
4
  * Formats log messages for console display with various styles.
5
5
  *
6
- * Rules:
7
- * - Box format only for: user, assistant, system, result
8
- * - Compact format for: tool, tool_result, thinking (gray/dim)
6
+ * ## Color Rules (by importance):
7
+ * - HIGH (colored): user(cyan), assistant(green), result(green), error(red), warn(yellow)
8
+ * - LOW (gray/dim): tool, tool_result, thinking, system, debug, stdout, raw, info
9
+ *
10
+ * ## Format Rules:
11
+ * - Box format only for: user, assistant, result
12
+ * - Compact format for: tool, tool_result, thinking, system (gray/dim)
9
13
  * - Tool names simplified: ShellToolCall → Shell
14
+ * - Lane labels fixed 14 chars: [1-1-lanename ]
15
+ * - Type labels: emoji + 4char (USER, ASST, TOOL, RESL, SYS, DONE, THNK)
10
16
  */
11
17
 
12
18
  import { COLORS } from './console';
13
19
  import { ParsedMessage, MessageType } from '../../types/logging';
14
20
 
15
- // Types that should use box format
16
- const BOX_TYPES = new Set(['user', 'assistant', 'system', 'result']);
21
+ // Types that should use box format (important messages)
22
+ const BOX_TYPES = new Set(['user', 'assistant', 'result']);
23
+
24
+ // Types that should be gray/dim (less important)
25
+ const GRAY_TYPES = new Set(['tool', 'tool_result', 'thinking', 'system', 'debug', 'stdout', 'raw', 'info']);
17
26
 
18
27
  /**
19
28
  * Strip ANSI escape sequences from text
@@ -64,10 +73,11 @@ export function formatMessageForConsole(
64
73
  : '';
65
74
  const tsPrefix = ts ? `${COLORS.gray}[${ts}]${COLORS.reset} ` : '';
66
75
 
67
- // Lane label max 16 chars
68
- const truncatedLabel = laneLabel.length > 16 ? laneLabel.substring(0, 16) : laneLabel;
76
+ // Lane label: fixed 14 chars inside brackets [1-1-lanename ]
77
+ const labelContent = laneLabel.replace(/^\[|\]$/g, ''); // Remove existing brackets if any
78
+ const truncatedLabel = labelContent.length > 14 ? labelContent.substring(0, 14) : labelContent;
69
79
  const labelPrefix = truncatedLabel
70
- ? `${COLORS.magenta}${truncatedLabel.padEnd(16)}${COLORS.reset} `
80
+ ? `${COLORS.magenta}[${truncatedLabel.padEnd(14)}]${COLORS.reset} `
71
81
  : '';
72
82
 
73
83
  // Determine if should use box format
@@ -77,17 +87,17 @@ export function formatMessageForConsole(
77
87
  if (!typePrefix) return `${tsPrefix}${labelPrefix}${formattedContent}`;
78
88
 
79
89
  if (!useBox) {
80
- return `${tsPrefix}${labelPrefix}${typePrefix.padEnd(12)} ${formattedContent}`;
90
+ // Compact format: type prefix is already formatted with proper spacing
91
+ return `${tsPrefix}${labelPrefix}${typePrefix} ${formattedContent}`;
81
92
  }
82
93
 
83
- // Multi-line box format (only for user, assistant, system, result)
84
- // Emoji width is 2, so we need to account for that in indent calculation
94
+ // Multi-line box format (only for user, assistant, result)
85
95
  const lines = formattedContent.split('\n');
86
96
  const fullPrefix = `${tsPrefix}${labelPrefix}`;
87
97
  const strippedPrefix = stripAnsi(typePrefix);
88
98
  // Count emojis (they take 2 terminal columns but 1-2 chars in string)
89
99
  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;
90
- const visualWidth = strippedPrefix.length + emojiCount; // emoji adds 1 extra width
100
+ const visualWidth = strippedPrefix.length + emojiCount;
91
101
 
92
102
  const boxWidth = 60;
93
103
  const header = `${typePrefix}┌${'─'.repeat(boxWidth)}`;
@@ -111,6 +121,9 @@ function formatMessageContent(msg: ParsedMessage, forceCompact: boolean): { type
111
121
  formattedContent = formattedContent.replace(/\n\s*\n/g, ' ').replace(/\n/g, ' ').trim();
112
122
  }
113
123
 
124
+ // Determine if this type should be gray (less important)
125
+ const isGray = GRAY_TYPES.has(msg.type);
126
+
114
127
  switch (msg.type) {
115
128
  case 'user':
116
129
  typePrefix = `${COLORS.cyan}🧑 USER${COLORS.reset}`;
@@ -123,20 +136,20 @@ function formatMessageContent(msg: ParsedMessage, forceCompact: boolean): { type
123
136
  break;
124
137
 
125
138
  case 'tool':
126
- // Tool calls are always gray
139
+ // Tool calls are always gray (less important)
127
140
  typePrefix = `${COLORS.gray}🔧 TOOL${COLORS.reset}`;
128
141
  formattedContent = formatToolCall(formattedContent);
129
142
  break;
130
143
 
131
144
  case 'tool_result':
132
- // Tool results are always gray
145
+ // Tool results are always gray (less important)
133
146
  typePrefix = `${COLORS.gray}📄 RESL${COLORS.reset}`;
134
147
  const resMatch = formattedContent.match(/\[Tool Result: ([^\]]+)\]/);
135
148
  if (resMatch) {
136
149
  const simpleName = simplifyToolName(resMatch[1]!);
137
150
  formattedContent = `${COLORS.gray}${simpleName} OK${COLORS.reset}`;
138
151
  } else {
139
- formattedContent = `${COLORS.gray}result${COLORS.reset}`;
152
+ formattedContent = `${COLORS.gray}OK${COLORS.reset}`;
140
153
  }
141
154
  break;
142
155
 
@@ -145,14 +158,57 @@ function formatMessageContent(msg: ParsedMessage, forceCompact: boolean): { type
145
158
  break;
146
159
 
147
160
  case 'system':
148
- typePrefix = `${COLORS.gray}⚙️ SYS${COLORS.reset}`;
161
+ // System messages are gray (less important)
162
+ typePrefix = `${COLORS.gray}⚙️ SYS${COLORS.reset}`;
163
+ formattedContent = `${COLORS.gray}${formattedContent}${COLORS.reset}`;
149
164
  break;
150
165
 
151
166
  case 'thinking':
152
- // Thinking is always gray and compact
167
+ // Thinking is always gray and compact (less important)
153
168
  typePrefix = `${COLORS.gray}🤔 THNK${COLORS.reset}`;
154
169
  formattedContent = `${COLORS.gray}${truncate(formattedContent, 100)}${COLORS.reset}`;
155
170
  break;
171
+
172
+ case 'info':
173
+ // Info messages are gray (less important)
174
+ typePrefix = `${COLORS.gray}ℹ️ INFO${COLORS.reset}`;
175
+ formattedContent = `${COLORS.gray}${formattedContent}${COLORS.reset}`;
176
+ break;
177
+
178
+ case 'warn':
179
+ // Warnings are yellow (important)
180
+ typePrefix = `${COLORS.yellow}⚠️ WARN${COLORS.reset}`;
181
+ break;
182
+
183
+ case 'error':
184
+ // Errors are red (important)
185
+ typePrefix = `${COLORS.red}❌ ERR${COLORS.reset}`;
186
+ break;
187
+
188
+ case 'success':
189
+ typePrefix = `${COLORS.green}✅ DONE${COLORS.reset}`;
190
+ break;
191
+
192
+ case 'debug':
193
+ // Debug is gray (less important)
194
+ typePrefix = `${COLORS.gray}🔍 DBUG${COLORS.reset}`;
195
+ formattedContent = `${COLORS.gray}${formattedContent}${COLORS.reset}`;
196
+ break;
197
+
198
+ case 'progress':
199
+ typePrefix = `${COLORS.blue}🔄 PROG${COLORS.reset}`;
200
+ break;
201
+
202
+ case 'stdout':
203
+ case 'raw':
204
+ // Raw output is gray (less important)
205
+ typePrefix = `${COLORS.gray} >>${COLORS.reset}`;
206
+ formattedContent = `${COLORS.gray}${formattedContent}${COLORS.reset}`;
207
+ break;
208
+
209
+ case 'stderr':
210
+ typePrefix = `${COLORS.red} >>${COLORS.reset}`;
211
+ break;
156
212
  }
157
213
 
158
214
  return { typePrefix, formattedContent };
@@ -235,6 +291,7 @@ function getTypeInfo(type: MessageType): { label: string; color: string } {
235
291
  progress: { label: 'PROG ', color: COLORS.blue || COLORS.cyan },
236
292
  stdout: { label: 'STDOUT', color: COLORS.white },
237
293
  stderr: { label: 'STDERR', color: COLORS.red },
294
+ raw: { label: 'RAW ', color: COLORS.white },
238
295
  };
239
296
 
240
297
  return typeMap[type] || { label: type.toUpperCase().padEnd(6), color: COLORS.white };
@@ -9,8 +9,6 @@ export * from './console';
9
9
  export * from './formatter';
10
10
  export * from './parser';
11
11
  export * from './buffer';
12
- export * from './file-writer';
13
12
 
14
13
  // Re-export types
15
14
  export * from '../../types/logging';
16
-
@@ -0,0 +1,14 @@
1
+ import * as path from 'path';
2
+
3
+ export const LOG_FILE_NAMES = {
4
+ clean: 'terminal.log',
5
+ raw: 'terminal-raw.log',
6
+ jsonl: 'terminal.jsonl',
7
+ readable: 'terminal-readable.log',
8
+ } as const;
9
+
10
+ export type LogFileType = keyof typeof LOG_FILE_NAMES;
11
+
12
+ export function getLaneLogPath(laneDir: string, type: LogFileType): string {
13
+ return path.join(laneDir, LOG_FILE_NAMES[type]);
14
+ }
@@ -0,0 +1,43 @@
1
+ import { stripAnsi } from './formatter';
2
+
3
+ export interface ParsedRawLogLine {
4
+ timestamp: Date;
5
+ message: string;
6
+ level: string;
7
+ }
8
+
9
+ const ISO_TIMESTAMP = /^\[(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z?)\]\s*/;
10
+ const SHORT_TIMESTAMP = /^\[(\d{2}:\d{2}:\d{2})\]\s*/;
11
+
12
+ export function parseRawLogLine(line: string, fallbackTime: Date): ParsedRawLogLine {
13
+ const cleanLine = stripAnsi(line).trimEnd();
14
+ let timestamp = fallbackTime;
15
+ let message = cleanLine;
16
+
17
+ const isoMatch = cleanLine.match(ISO_TIMESTAMP);
18
+ if (isoMatch) {
19
+ const parsed = new Date(isoMatch[1]);
20
+ if (!Number.isNaN(parsed.getTime())) {
21
+ timestamp = parsed;
22
+ }
23
+ message = cleanLine.slice(isoMatch[0].length);
24
+ } else {
25
+ const shortMatch = cleanLine.match(SHORT_TIMESTAMP);
26
+ if (shortMatch) {
27
+ const [hours, minutes, seconds] = shortMatch[1].split(':').map(Number);
28
+ const candidate = new Date(fallbackTime);
29
+ candidate.setHours(hours, minutes, seconds, 0);
30
+ timestamp = candidate;
31
+ message = cleanLine.slice(shortMatch[0].length);
32
+ }
33
+ }
34
+
35
+ const lower = message.toLowerCase();
36
+ const level = lower.includes('error') || lower.includes('failed') || lower.includes('❌')
37
+ ? 'error'
38
+ : lower.includes('warn')
39
+ ? 'warn'
40
+ : 'info';
41
+
42
+ return { timestamp, message, level };
43
+ }
@@ -154,7 +154,7 @@ export function getLaneProcessStatus(lanePath: string, laneName: string): LanePr
154
154
  if (result.startTime) {
155
155
  if (result.endTime) {
156
156
  result.duration = result.endTime - result.startTime;
157
- } else if (result.stateStatus === 'running' || result.stateStatus === 'reviewing') {
157
+ } else if (result.stateStatus === 'running') {
158
158
  result.duration = Date.now() - result.startTime;
159
159
  }
160
160
  }
@@ -15,10 +15,25 @@ export interface DependencyRequestPlan {
15
15
  }
16
16
 
17
17
  export interface AgentSendResult {
18
+ /** Whether the operation succeeded */
18
19
  ok: boolean;
20
+ /** Process exit code */
19
21
  exitCode: number;
22
+ /** Error message if failed */
20
23
  error?: string;
24
+ /** Session ID from cursor-agent */
21
25
  sessionId?: string;
26
+ /** Result text from the agent response */
22
27
  resultText?: string;
28
+ /** Total execution time in milliseconds */
29
+ durationMs?: number;
30
+ /** API call time in milliseconds */
31
+ durationApiMs?: number;
32
+ /** Unique request ID for debugging */
33
+ requestId?: string;
34
+ /** Result subtype: 'success' or 'error' */
35
+ subtype?: 'success' | 'error';
36
+ /** Model used for this request */
37
+ model?: string;
23
38
  }
24
39
 
@@ -49,6 +49,20 @@ export interface EnhancedLogConfig {
49
49
  raw?: boolean;
50
50
  }
51
51
 
52
+ /**
53
+ * Hook 시스템 설정
54
+ */
55
+ export interface HooksConfig {
56
+ /** Hook 정의 파일 경로 (e.g., './cursorflow.hooks.ts') */
57
+ file?: string;
58
+ /** Hook 실행 타임아웃 (ms, 기본: 30000) */
59
+ timeout?: number;
60
+ /** 에러 시 계속 진행 여부 (기본: false) */
61
+ continueOnError?: boolean;
62
+ /** 디버그 모드 */
63
+ debug?: boolean;
64
+ }
65
+
52
66
  export interface CursorFlowConfig {
53
67
  tasksDir: string;
54
68
  /** New flows directory (replaces tasksDir in new architecture) */
@@ -69,11 +83,21 @@ export interface CursorFlowConfig {
69
83
  maxConcurrentLanes: number;
70
84
  projectRoot: string;
71
85
  /** Output format for cursor-agent (default: 'json') */
72
- agentOutputFormat: 'json' | 'plain' | 'stream-json';
86
+ agentOutputFormat: 'json' | 'stream-json';
73
87
  webhooks?: WebhookConfig[];
88
+ /** Enable intervention feature (stdin piping for message injection) */
89
+ enableIntervention?: boolean;
74
90
  /** Enhanced logging configuration */
75
91
  enhancedLogging?: Partial<EnhancedLogConfig>;
76
92
  /** Default AI model for tasks (default: 'gemini-3-flash') */
77
93
  defaultModel: string;
94
+ /** Auto-approve agent commands (--force flag). Default: true for automation. */
95
+ autoApproveCommands?: boolean;
96
+ /** Auto-approve MCP servers (--approve-mcps flag). Default: true for automation. */
97
+ autoApproveMcps?: boolean;
98
+ /** Enable browser automation (--browser flag). Required for web testing/scraping. */
99
+ browser?: boolean;
100
+ /** Hook 시스템 설정 */
101
+ hooks?: HooksConfig;
78
102
  }
79
103