@litmers/cursorflow-orchestrator 0.1.40 → 0.2.3

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 +8 -3
  3. package/commands/cursorflow-init.md +0 -4
  4. package/dist/cli/index.js +0 -6
  5. package/dist/cli/index.js.map +1 -1
  6. package/dist/cli/logs.js +108 -9
  7. package/dist/cli/logs.js.map +1 -1
  8. package/dist/cli/models.js +20 -3
  9. package/dist/cli/models.js.map +1 -1
  10. package/dist/cli/monitor.d.ts +7 -10
  11. package/dist/cli/monitor.js +1103 -1239
  12. package/dist/cli/monitor.js.map +1 -1
  13. package/dist/cli/resume.js +21 -1
  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 +99 -13
  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 +3 -117
  26. package/dist/core/auto-recovery.js +4 -482
  27. package/dist/core/auto-recovery.js.map +1 -1
  28. package/dist/core/failure-policy.d.ts +0 -53
  29. package/dist/core/failure-policy.js +7 -175
  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 +170 -0
  38. package/dist/core/intervention.js +408 -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 +39 -65
  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 +54 -36
  48. package/dist/core/runner/agent.js.map +1 -1
  49. package/dist/core/runner/pipeline.js +283 -123
  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 -80
  53. package/dist/core/runner/task.js.map +1 -1
  54. package/dist/core/runner.js +8 -2
  55. package/dist/core/runner.js.map +1 -1
  56. package/dist/core/stall-detection.d.ts +11 -4
  57. package/dist/core/stall-detection.js +64 -27
  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 +6 -1
  81. package/dist/services/logging/console.js.map +1 -1
  82. package/dist/services/logging/formatter.d.ts +9 -4
  83. package/dist/services/logging/formatter.js +64 -18
  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 +22 -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 +12 -1
  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 +10 -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 +98 -19
  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/log-constants.d.ts +1 -0
  129. package/dist/utils/log-constants.js +2 -1
  130. package/dist/utils/log-constants.js.map +1 -1
  131. package/dist/utils/log-formatter.d.ts +2 -1
  132. package/dist/utils/log-formatter.js +10 -10
  133. package/dist/utils/log-formatter.js.map +1 -1
  134. package/dist/utils/logger.d.ts +11 -0
  135. package/dist/utils/logger.js +82 -3
  136. package/dist/utils/logger.js.map +1 -1
  137. package/dist/utils/repro-thinking-logs.js +0 -13
  138. package/dist/utils/repro-thinking-logs.js.map +1 -1
  139. package/dist/utils/run-service.js +1 -1
  140. package/dist/utils/run-service.js.map +1 -1
  141. package/examples/README.md +0 -2
  142. package/examples/demo-project/README.md +1 -2
  143. package/package.json +13 -34
  144. package/scripts/setup-security.sh +0 -1
  145. package/scripts/test-log-parser.ts +171 -0
  146. package/scripts/verify-change.sh +272 -0
  147. package/src/cli/index.ts +0 -6
  148. package/src/cli/logs.ts +121 -10
  149. package/src/cli/models.ts +20 -3
  150. package/src/cli/monitor.ts +1273 -1342
  151. package/src/cli/resume.ts +27 -1
  152. package/src/cli/run.ts +29 -11
  153. package/src/cli/signal.ts +120 -18
  154. package/src/cli/tasks.ts +2 -59
  155. package/src/core/agent-supervisor.ts +64 -0
  156. package/src/core/auto-recovery.ts +14 -590
  157. package/src/core/failure-policy.ts +7 -229
  158. package/src/core/git-lifecycle-manager.ts +1011 -0
  159. package/src/core/git-pipeline-coordinator.ts +221 -0
  160. package/src/core/intervention.ts +463 -0
  161. package/src/core/lane-state-machine.ts +1097 -0
  162. package/src/core/orchestrator.ts +48 -64
  163. package/src/core/runner/agent.ts +77 -39
  164. package/src/core/runner/pipeline.ts +318 -138
  165. package/src/core/runner/task.ts +12 -97
  166. package/src/core/runner.ts +8 -2
  167. package/src/core/stall-detection.ts +74 -27
  168. package/src/hooks/contexts/index.ts +256 -0
  169. package/src/hooks/data-accessor.ts +488 -0
  170. package/src/hooks/flow-controller.ts +425 -0
  171. package/src/hooks/index.ts +154 -0
  172. package/src/hooks/manager.ts +434 -0
  173. package/src/hooks/types.ts +544 -0
  174. package/src/services/logging/buffer.ts +104 -43
  175. package/src/services/logging/console.ts +7 -1
  176. package/src/services/logging/formatter.ts +74 -18
  177. package/src/services/logging/index.ts +0 -2
  178. package/src/services/logging/paths.ts +14 -0
  179. package/src/services/logging/raw-log.ts +43 -0
  180. package/src/services/process/index.ts +1 -1
  181. package/src/types/agent.ts +15 -0
  182. package/src/types/config.ts +23 -1
  183. package/src/types/event-categories.ts +663 -0
  184. package/src/types/events.ts +0 -25
  185. package/src/types/flow.ts +10 -6
  186. package/src/types/index.ts +50 -4
  187. package/src/types/lane.ts +1 -2
  188. package/src/types/logging.ts +2 -1
  189. package/src/types/task.ts +12 -1
  190. package/src/ui/log-viewer.ts +3 -0
  191. package/src/utils/config.ts +11 -1
  192. package/src/utils/cursor-agent.ts +68 -16
  193. package/src/utils/enhanced-logger.ts +105 -19
  194. package/src/utils/event-registry.ts +595 -0
  195. package/src/utils/events.ts +0 -16
  196. package/src/utils/flow.ts +83 -0
  197. package/src/utils/log-constants.ts +2 -1
  198. package/src/utils/log-formatter.ts +10 -11
  199. package/src/utils/logger.ts +49 -3
  200. package/src/utils/repro-thinking-logs.ts +0 -15
  201. package/src/utils/run-service.ts +1 -1
  202. package/dist/cli/prepare.d.ts +0 -7
  203. package/dist/cli/prepare.js +0 -690
  204. package/dist/cli/prepare.js.map +0 -1
  205. package/dist/services/logging/file-writer.d.ts +0 -71
  206. package/dist/services/logging/file-writer.js +0 -516
  207. package/dist/services/logging/file-writer.js.map +0 -1
  208. package/dist/types/review.d.ts +0 -17
  209. package/dist/types/review.js +0 -6
  210. package/dist/types/review.js.map +0 -1
  211. package/scripts/ai-security-check.js +0 -233
  212. package/src/cli/prepare.ts +0 -777
  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
 
@@ -3,18 +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
10
- * - Lane labels fixed 20 chars: [1-1-backend ]
14
+ * - Lane labels fixed 14 chars: [1-1-lanename ]
15
+ * - Type labels: emoji + 4char (USER, ASST, TOOL, RESL, SYS, DONE, THNK)
11
16
  */
12
17
 
13
18
  import { COLORS } from './console';
14
19
  import { ParsedMessage, MessageType } from '../../types/logging';
15
20
 
16
- // Types that should use box format
17
- 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']);
18
26
 
19
27
  /**
20
28
  * Strip ANSI escape sequences from text
@@ -65,10 +73,11 @@ export function formatMessageForConsole(
65
73
  : '';
66
74
  const tsPrefix = ts ? `${COLORS.gray}[${ts}]${COLORS.reset} ` : '';
67
75
 
68
- // Lane label max 20 chars
69
- const truncatedLabel = laneLabel.length > 20 ? laneLabel.substring(0, 20) : 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;
70
79
  const labelPrefix = truncatedLabel
71
- ? `${COLORS.magenta}${truncatedLabel.padEnd(20)}${COLORS.reset} `
80
+ ? `${COLORS.magenta}[${truncatedLabel.padEnd(14)}]${COLORS.reset} `
72
81
  : '';
73
82
 
74
83
  // Determine if should use box format
@@ -78,17 +87,17 @@ export function formatMessageForConsole(
78
87
  if (!typePrefix) return `${tsPrefix}${labelPrefix}${formattedContent}`;
79
88
 
80
89
  if (!useBox) {
81
- return `${tsPrefix}${labelPrefix}${typePrefix.padEnd(12)} ${formattedContent}`;
90
+ // Compact format: type prefix is already formatted with proper spacing
91
+ return `${tsPrefix}${labelPrefix}${typePrefix} ${formattedContent}`;
82
92
  }
83
93
 
84
- // Multi-line box format (only for user, assistant, system, result)
85
- // Emoji width is 2, so we need to account for that in indent calculation
94
+ // Multi-line box format (only for user, assistant, result)
86
95
  const lines = formattedContent.split('\n');
87
96
  const fullPrefix = `${tsPrefix}${labelPrefix}`;
88
97
  const strippedPrefix = stripAnsi(typePrefix);
89
98
  // Count emojis (they take 2 terminal columns but 1-2 chars in string)
90
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;
91
- const visualWidth = strippedPrefix.length + emojiCount; // emoji adds 1 extra width
100
+ const visualWidth = strippedPrefix.length + emojiCount;
92
101
 
93
102
  const boxWidth = 60;
94
103
  const header = `${typePrefix}┌${'─'.repeat(boxWidth)}`;
@@ -112,6 +121,9 @@ function formatMessageContent(msg: ParsedMessage, forceCompact: boolean): { type
112
121
  formattedContent = formattedContent.replace(/\n\s*\n/g, ' ').replace(/\n/g, ' ').trim();
113
122
  }
114
123
 
124
+ // Determine if this type should be gray (less important)
125
+ const isGray = GRAY_TYPES.has(msg.type);
126
+
115
127
  switch (msg.type) {
116
128
  case 'user':
117
129
  typePrefix = `${COLORS.cyan}🧑 USER${COLORS.reset}`;
@@ -124,20 +136,20 @@ function formatMessageContent(msg: ParsedMessage, forceCompact: boolean): { type
124
136
  break;
125
137
 
126
138
  case 'tool':
127
- // Tool calls are always gray
139
+ // Tool calls are always gray (less important)
128
140
  typePrefix = `${COLORS.gray}🔧 TOOL${COLORS.reset}`;
129
141
  formattedContent = formatToolCall(formattedContent);
130
142
  break;
131
143
 
132
144
  case 'tool_result':
133
- // Tool results are always gray
145
+ // Tool results are always gray (less important)
134
146
  typePrefix = `${COLORS.gray}📄 RESL${COLORS.reset}`;
135
147
  const resMatch = formattedContent.match(/\[Tool Result: ([^\]]+)\]/);
136
148
  if (resMatch) {
137
149
  const simpleName = simplifyToolName(resMatch[1]!);
138
150
  formattedContent = `${COLORS.gray}${simpleName} OK${COLORS.reset}`;
139
151
  } else {
140
- formattedContent = `${COLORS.gray}result${COLORS.reset}`;
152
+ formattedContent = `${COLORS.gray}OK${COLORS.reset}`;
141
153
  }
142
154
  break;
143
155
 
@@ -146,14 +158,57 @@ function formatMessageContent(msg: ParsedMessage, forceCompact: boolean): { type
146
158
  break;
147
159
 
148
160
  case 'system':
149
- 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}`;
150
164
  break;
151
165
 
152
166
  case 'thinking':
153
- // Thinking is always gray and compact
167
+ // Thinking is always gray and compact (less important)
154
168
  typePrefix = `${COLORS.gray}🤔 THNK${COLORS.reset}`;
155
169
  formattedContent = `${COLORS.gray}${truncate(formattedContent, 100)}${COLORS.reset}`;
156
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;
157
212
  }
158
213
 
159
214
  return { typePrefix, formattedContent };
@@ -236,6 +291,7 @@ function getTypeInfo(type: MessageType): { label: string; color: string } {
236
291
  progress: { label: 'PROG ', color: COLORS.blue || COLORS.cyan },
237
292
  stdout: { label: 'STDOUT', color: COLORS.white },
238
293
  stderr: { label: 'STDERR', color: COLORS.red },
294
+ raw: { label: 'RAW ', color: COLORS.white },
239
295
  };
240
296
 
241
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,7 +83,7 @@ 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[];
74
88
  /** Enable intervention feature (stdin piping for message injection) */
75
89
  enableIntervention?: boolean;
@@ -77,5 +91,13 @@ export interface CursorFlowConfig {
77
91
  enhancedLogging?: Partial<EnhancedLogConfig>;
78
92
  /** Default AI model for tasks (default: 'gemini-3-flash') */
79
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;
80
102
  }
81
103