@litmers/cursorflow-orchestrator 0.1.18 → 0.1.26

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 (234) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/README.md +25 -7
  3. package/commands/cursorflow-clean.md +19 -0
  4. package/commands/cursorflow-runs.md +59 -0
  5. package/commands/cursorflow-stop.md +55 -0
  6. package/dist/cli/clean.js +178 -6
  7. package/dist/cli/clean.js.map +1 -1
  8. package/dist/cli/index.js +12 -1
  9. package/dist/cli/index.js.map +1 -1
  10. package/dist/cli/init.js +8 -7
  11. package/dist/cli/init.js.map +1 -1
  12. package/dist/cli/logs.js +126 -77
  13. package/dist/cli/logs.js.map +1 -1
  14. package/dist/cli/monitor.d.ts +7 -0
  15. package/dist/cli/monitor.js +1021 -202
  16. package/dist/cli/monitor.js.map +1 -1
  17. package/dist/cli/prepare.js +39 -21
  18. package/dist/cli/prepare.js.map +1 -1
  19. package/dist/cli/resume.js +268 -163
  20. package/dist/cli/resume.js.map +1 -1
  21. package/dist/cli/run.js +11 -5
  22. package/dist/cli/run.js.map +1 -1
  23. package/dist/cli/runs.d.ts +5 -0
  24. package/dist/cli/runs.js +214 -0
  25. package/dist/cli/runs.js.map +1 -0
  26. package/dist/cli/setup-commands.js +0 -0
  27. package/dist/cli/signal.js +8 -8
  28. package/dist/cli/signal.js.map +1 -1
  29. package/dist/cli/stop.d.ts +5 -0
  30. package/dist/cli/stop.js +215 -0
  31. package/dist/cli/stop.js.map +1 -0
  32. package/dist/cli/tasks.d.ts +10 -0
  33. package/dist/cli/tasks.js +165 -0
  34. package/dist/cli/tasks.js.map +1 -0
  35. package/dist/core/auto-recovery.d.ts +212 -0
  36. package/dist/core/auto-recovery.js +737 -0
  37. package/dist/core/auto-recovery.js.map +1 -0
  38. package/dist/core/failure-policy.d.ts +156 -0
  39. package/dist/core/failure-policy.js +488 -0
  40. package/dist/core/failure-policy.js.map +1 -0
  41. package/dist/core/orchestrator.d.ts +16 -2
  42. package/dist/core/orchestrator.js +439 -105
  43. package/dist/core/orchestrator.js.map +1 -1
  44. package/dist/core/reviewer.d.ts +2 -0
  45. package/dist/core/reviewer.js +2 -0
  46. package/dist/core/reviewer.js.map +1 -1
  47. package/dist/core/runner.d.ts +33 -10
  48. package/dist/core/runner.js +374 -164
  49. package/dist/core/runner.js.map +1 -1
  50. package/dist/services/logging/buffer.d.ts +67 -0
  51. package/dist/services/logging/buffer.js +309 -0
  52. package/dist/services/logging/buffer.js.map +1 -0
  53. package/dist/services/logging/console.d.ts +89 -0
  54. package/dist/services/logging/console.js +169 -0
  55. package/dist/services/logging/console.js.map +1 -0
  56. package/dist/services/logging/file-writer.d.ts +71 -0
  57. package/dist/services/logging/file-writer.js +516 -0
  58. package/dist/services/logging/file-writer.js.map +1 -0
  59. package/dist/services/logging/formatter.d.ts +39 -0
  60. package/dist/services/logging/formatter.js +227 -0
  61. package/dist/services/logging/formatter.js.map +1 -0
  62. package/dist/services/logging/index.d.ts +11 -0
  63. package/dist/services/logging/index.js +30 -0
  64. package/dist/services/logging/index.js.map +1 -0
  65. package/dist/services/logging/parser.d.ts +31 -0
  66. package/dist/services/logging/parser.js +222 -0
  67. package/dist/services/logging/parser.js.map +1 -0
  68. package/dist/services/process/index.d.ts +59 -0
  69. package/dist/services/process/index.js +257 -0
  70. package/dist/services/process/index.js.map +1 -0
  71. package/dist/types/agent.d.ts +20 -0
  72. package/dist/types/agent.js +6 -0
  73. package/dist/types/agent.js.map +1 -0
  74. package/dist/types/config.d.ts +65 -0
  75. package/dist/types/config.js +6 -0
  76. package/dist/types/config.js.map +1 -0
  77. package/dist/types/events.d.ts +125 -0
  78. package/dist/types/events.js +6 -0
  79. package/dist/types/events.js.map +1 -0
  80. package/dist/types/index.d.ts +12 -0
  81. package/dist/types/index.js +37 -0
  82. package/dist/types/index.js.map +1 -0
  83. package/dist/types/lane.d.ts +43 -0
  84. package/dist/types/lane.js +6 -0
  85. package/dist/types/lane.js.map +1 -0
  86. package/dist/types/logging.d.ts +71 -0
  87. package/dist/types/logging.js +16 -0
  88. package/dist/types/logging.js.map +1 -0
  89. package/dist/types/review.d.ts +17 -0
  90. package/dist/types/review.js +6 -0
  91. package/dist/types/review.js.map +1 -0
  92. package/dist/types/run.d.ts +32 -0
  93. package/dist/types/run.js +6 -0
  94. package/dist/types/run.js.map +1 -0
  95. package/dist/types/task.d.ts +71 -0
  96. package/dist/types/task.js +6 -0
  97. package/dist/types/task.js.map +1 -0
  98. package/dist/ui/components.d.ts +134 -0
  99. package/dist/ui/components.js +389 -0
  100. package/dist/ui/components.js.map +1 -0
  101. package/dist/ui/log-viewer.d.ts +49 -0
  102. package/dist/ui/log-viewer.js +449 -0
  103. package/dist/ui/log-viewer.js.map +1 -0
  104. package/dist/utils/checkpoint.d.ts +87 -0
  105. package/dist/utils/checkpoint.js +317 -0
  106. package/dist/utils/checkpoint.js.map +1 -0
  107. package/dist/utils/config.d.ts +4 -0
  108. package/dist/utils/config.js +18 -8
  109. package/dist/utils/config.js.map +1 -1
  110. package/dist/utils/cursor-agent.js.map +1 -1
  111. package/dist/utils/dependency.d.ts +74 -0
  112. package/dist/utils/dependency.js +420 -0
  113. package/dist/utils/dependency.js.map +1 -0
  114. package/dist/utils/doctor.js +17 -11
  115. package/dist/utils/doctor.js.map +1 -1
  116. package/dist/utils/enhanced-logger.d.ts +10 -33
  117. package/dist/utils/enhanced-logger.js +108 -20
  118. package/dist/utils/enhanced-logger.js.map +1 -1
  119. package/dist/utils/git.d.ts +121 -0
  120. package/dist/utils/git.js +484 -11
  121. package/dist/utils/git.js.map +1 -1
  122. package/dist/utils/health.d.ts +91 -0
  123. package/dist/utils/health.js +556 -0
  124. package/dist/utils/health.js.map +1 -0
  125. package/dist/utils/lock.d.ts +95 -0
  126. package/dist/utils/lock.js +332 -0
  127. package/dist/utils/lock.js.map +1 -0
  128. package/dist/utils/log-buffer.d.ts +17 -0
  129. package/dist/utils/log-buffer.js +14 -0
  130. package/dist/utils/log-buffer.js.map +1 -0
  131. package/dist/utils/log-constants.d.ts +23 -0
  132. package/dist/utils/log-constants.js +28 -0
  133. package/dist/utils/log-constants.js.map +1 -0
  134. package/dist/utils/log-formatter.d.ts +25 -0
  135. package/dist/utils/log-formatter.js +237 -0
  136. package/dist/utils/log-formatter.js.map +1 -0
  137. package/dist/utils/log-service.d.ts +19 -0
  138. package/dist/utils/log-service.js +47 -0
  139. package/dist/utils/log-service.js.map +1 -0
  140. package/dist/utils/logger.d.ts +46 -27
  141. package/dist/utils/logger.js +82 -60
  142. package/dist/utils/logger.js.map +1 -1
  143. package/dist/utils/path.d.ts +19 -0
  144. package/dist/utils/path.js +77 -0
  145. package/dist/utils/path.js.map +1 -0
  146. package/dist/utils/process-manager.d.ts +21 -0
  147. package/dist/utils/process-manager.js +138 -0
  148. package/dist/utils/process-manager.js.map +1 -0
  149. package/dist/utils/retry.d.ts +121 -0
  150. package/dist/utils/retry.js +374 -0
  151. package/dist/utils/retry.js.map +1 -0
  152. package/dist/utils/run-service.d.ts +88 -0
  153. package/dist/utils/run-service.js +412 -0
  154. package/dist/utils/run-service.js.map +1 -0
  155. package/dist/utils/state.d.ts +62 -3
  156. package/dist/utils/state.js +317 -11
  157. package/dist/utils/state.js.map +1 -1
  158. package/dist/utils/task-service.d.ts +82 -0
  159. package/dist/utils/task-service.js +348 -0
  160. package/dist/utils/task-service.js.map +1 -0
  161. package/dist/utils/template.d.ts +14 -0
  162. package/dist/utils/template.js +122 -0
  163. package/dist/utils/template.js.map +1 -0
  164. package/dist/utils/types.d.ts +2 -271
  165. package/dist/utils/types.js +16 -0
  166. package/dist/utils/types.js.map +1 -1
  167. package/package.json +38 -23
  168. package/scripts/ai-security-check.js +0 -1
  169. package/scripts/local-security-gate.sh +0 -0
  170. package/scripts/monitor-lanes.sh +94 -0
  171. package/scripts/patches/test-cursor-agent.js +0 -1
  172. package/scripts/release.sh +0 -0
  173. package/scripts/setup-security.sh +0 -0
  174. package/scripts/stream-logs.sh +72 -0
  175. package/scripts/verify-and-fix.sh +0 -0
  176. package/src/cli/clean.ts +187 -6
  177. package/src/cli/index.ts +12 -1
  178. package/src/cli/init.ts +8 -7
  179. package/src/cli/logs.ts +124 -77
  180. package/src/cli/monitor.ts +1815 -898
  181. package/src/cli/prepare.ts +41 -21
  182. package/src/cli/resume.ts +753 -626
  183. package/src/cli/run.ts +12 -5
  184. package/src/cli/runs.ts +212 -0
  185. package/src/cli/setup-commands.ts +0 -0
  186. package/src/cli/signal.ts +8 -7
  187. package/src/cli/stop.ts +209 -0
  188. package/src/cli/tasks.ts +154 -0
  189. package/src/core/auto-recovery.ts +909 -0
  190. package/src/core/failure-policy.ts +592 -0
  191. package/src/core/orchestrator.ts +1131 -704
  192. package/src/core/reviewer.ts +4 -0
  193. package/src/core/runner.ts +444 -180
  194. package/src/services/logging/buffer.ts +326 -0
  195. package/src/services/logging/console.ts +193 -0
  196. package/src/services/logging/file-writer.ts +526 -0
  197. package/src/services/logging/formatter.ts +268 -0
  198. package/src/services/logging/index.ts +16 -0
  199. package/src/services/logging/parser.ts +232 -0
  200. package/src/services/process/index.ts +261 -0
  201. package/src/types/agent.ts +24 -0
  202. package/src/types/config.ts +79 -0
  203. package/src/types/events.ts +156 -0
  204. package/src/types/index.ts +29 -0
  205. package/src/types/lane.ts +56 -0
  206. package/src/types/logging.ts +96 -0
  207. package/src/types/review.ts +20 -0
  208. package/src/types/run.ts +37 -0
  209. package/src/types/task.ts +79 -0
  210. package/src/ui/components.ts +430 -0
  211. package/src/ui/log-viewer.ts +485 -0
  212. package/src/utils/checkpoint.ts +374 -0
  213. package/src/utils/config.ts +18 -8
  214. package/src/utils/cursor-agent.ts +1 -1
  215. package/src/utils/dependency.ts +482 -0
  216. package/src/utils/doctor.ts +18 -11
  217. package/src/utils/enhanced-logger.ts +122 -60
  218. package/src/utils/git.ts +517 -11
  219. package/src/utils/health.ts +596 -0
  220. package/src/utils/lock.ts +346 -0
  221. package/src/utils/log-buffer.ts +28 -0
  222. package/src/utils/log-constants.ts +26 -0
  223. package/src/utils/log-formatter.ts +245 -0
  224. package/src/utils/log-service.ts +49 -0
  225. package/src/utils/logger.ts +100 -51
  226. package/src/utils/path.ts +45 -0
  227. package/src/utils/process-manager.ts +100 -0
  228. package/src/utils/retry.ts +413 -0
  229. package/src/utils/run-service.ts +433 -0
  230. package/src/utils/state.ts +385 -11
  231. package/src/utils/task-service.ts +370 -0
  232. package/src/utils/template.ts +92 -0
  233. package/src/utils/types.ts +2 -314
  234. package/templates/basic.json +21 -0
@@ -0,0 +1,326 @@
1
+ /**
2
+ * Log Buffer Service - In-memory log buffer for TUI
3
+ *
4
+ * Provides real-time log streaming and filtering for the interactive monitor.
5
+ */
6
+
7
+ import * as fs from 'fs';
8
+ import * as path from 'path';
9
+ import { EventEmitter } from 'events';
10
+ import { LogImportance, JsonLogEntry, BufferedLogEntry as BufferedLogEntryType, MessageType } from '../../types/logging';
11
+ import { COLORS } from './console';
12
+
13
+ // Re-export types for convenience
14
+ export type { BufferedLogEntry } from '../../types/logging';
15
+
16
+ export interface LogBufferOptions {
17
+ maxEntries?: number;
18
+ pollInterval?: number;
19
+ }
20
+
21
+ export interface LogFilter {
22
+ lane?: string;
23
+ importance?: LogImportance;
24
+ search?: string;
25
+ type?: string;
26
+ }
27
+
28
+ export interface LogBufferState {
29
+ totalEntries: number;
30
+ filteredCount: number;
31
+ newCount: number;
32
+ isStreaming: boolean;
33
+ lanes: string[];
34
+ }
35
+
36
+ const LANE_COLORS = [
37
+ COLORS.cyan,
38
+ COLORS.magenta,
39
+ COLORS.yellow,
40
+ COLORS.green,
41
+ COLORS.blue,
42
+ COLORS.red,
43
+ ];
44
+
45
+ export class LogBufferService extends EventEmitter {
46
+ private runDir: string;
47
+ private options: Required<LogBufferOptions>;
48
+ private entries: BufferedLogEntryType[] = [];
49
+ private entryIdCounter = 0;
50
+ private isStreaming = false;
51
+ private pollTimer: NodeJS.Timeout | null = null;
52
+ private filePositions = new Map<string, number>();
53
+ private lanes: string[] = [];
54
+ private laneColorMap = new Map<string, string>();
55
+ private newEntriesCount = 0;
56
+ private lastAcknowledgedId = 0;
57
+
58
+ constructor(runDir: string, options: LogBufferOptions = {}) {
59
+ super();
60
+ this.runDir = runDir;
61
+ this.options = {
62
+ maxEntries: options.maxEntries ?? 10000,
63
+ pollInterval: options.pollInterval ?? 100,
64
+ };
65
+ }
66
+
67
+ startStreaming(): void {
68
+ if (this.isStreaming) return;
69
+ this.isStreaming = true;
70
+ this.discoverLanes();
71
+ this.pollLogs();
72
+ this.pollTimer = setInterval(() => this.pollLogs(), this.options.pollInterval);
73
+ this.emit('started');
74
+ }
75
+
76
+ stopStreaming(): void {
77
+ if (!this.isStreaming) return;
78
+ this.isStreaming = false;
79
+ if (this.pollTimer) {
80
+ clearInterval(this.pollTimer);
81
+ this.pollTimer = null;
82
+ }
83
+ this.emit('stopped');
84
+ }
85
+
86
+ private discoverLanes(): void {
87
+ const lanesDir = path.join(this.runDir, 'lanes');
88
+ if (!fs.existsSync(lanesDir)) return;
89
+
90
+ const dirs = fs.readdirSync(lanesDir).filter(name => {
91
+ const dirPath = path.join(lanesDir, name);
92
+ return fs.statSync(dirPath).isDirectory();
93
+ });
94
+
95
+ this.lanes = dirs.sort();
96
+ this.lanes.forEach((lane, index) => {
97
+ this.laneColorMap.set(lane, LANE_COLORS[index % LANE_COLORS.length]);
98
+ });
99
+ }
100
+
101
+ private pollLogs(): void {
102
+ const lanesDir = path.join(this.runDir, 'lanes');
103
+ if (!fs.existsSync(lanesDir)) return;
104
+
105
+ // Re-discover lanes to pick up new ones
106
+ this.discoverLanes();
107
+
108
+ const newEntries: BufferedLogEntryType[] = [];
109
+
110
+ for (const laneName of this.lanes) {
111
+ const jsonlPath = path.join(lanesDir, laneName, 'terminal.jsonl');
112
+
113
+ let fd: number | null = null;
114
+ try {
115
+ // Read file content atomically to avoid TOCTOU race condition
116
+ const lastPos = this.filePositions.get(jsonlPath) || 0;
117
+ fd = fs.openSync(jsonlPath, 'r');
118
+ const stat = fs.fstatSync(fd); // Use fstat on open fd to avoid race
119
+
120
+ if (stat.size > lastPos) {
121
+ const buffer = Buffer.alloc(stat.size - lastPos);
122
+ fs.readSync(fd, buffer, 0, buffer.length, lastPos);
123
+
124
+ const newContent = buffer.toString('utf-8');
125
+ const lines = newContent.split('\n').filter(line => line.trim());
126
+
127
+ 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 */ }
133
+ }
134
+
135
+ this.filePositions.set(jsonlPath, stat.size);
136
+ }
137
+ } catch { /* File in use, skip */ }
138
+ finally {
139
+ if (fd !== null) {
140
+ try { fs.closeSync(fd); } catch { /* ignore */ }
141
+ }
142
+ }
143
+ }
144
+
145
+ if (newEntries.length > 0) {
146
+ newEntries.sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime());
147
+ this.entries.push(...newEntries);
148
+ this.newEntriesCount += newEntries.length;
149
+
150
+ if (this.entries.length > this.options.maxEntries) {
151
+ const excess = this.entries.length - this.options.maxEntries;
152
+ this.entries.splice(0, excess);
153
+ }
154
+
155
+ this.emit('update', newEntries);
156
+ }
157
+ }
158
+
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);
164
+ const importance = this.inferImportance(type, level);
165
+
166
+ return {
167
+ id: ++this.entryIdCounter,
168
+ timestamp,
169
+ laneName,
170
+ level,
171
+ type,
172
+ message: this.truncateMessage(message),
173
+ importance,
174
+ laneColor: this.laneColorMap.get(laneName) || COLORS.white,
175
+ raw: entry,
176
+ };
177
+ }
178
+
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';
191
+ }
192
+ }
193
+
194
+ private inferImportance(type: string, level: string): LogImportance {
195
+ if (level === 'error' || type === 'error' || type === 'result') return LogImportance.HIGH;
196
+ if (type === 'tool' || type === 'tool_result') return LogImportance.MEDIUM;
197
+ if (type === 'thinking' || level === 'debug') return LogImportance.DEBUG;
198
+ if (type === 'assistant' || type === 'user') return LogImportance.MEDIUM;
199
+ return LogImportance.INFO;
200
+ }
201
+
202
+ private truncateMessage(message: string, maxLength = 500): string {
203
+ if (message.length <= maxLength) return message;
204
+ return message.substring(0, maxLength) + '...';
205
+ }
206
+
207
+ getEntries(options: {
208
+ offset?: number;
209
+ limit?: number;
210
+ filter?: LogFilter;
211
+ fromEnd?: boolean;
212
+ } = {}): BufferedLogEntryType[] {
213
+ const { offset = 0, limit = 100, filter, fromEnd = true } = options;
214
+ let filtered = this.applyFilter(this.entries, filter);
215
+
216
+ if (fromEnd) {
217
+ const start = Math.max(0, filtered.length - offset - limit);
218
+ const end = Math.max(0, filtered.length - offset);
219
+ return filtered.slice(start, end);
220
+ }
221
+ return filtered.slice(offset, offset + limit);
222
+ }
223
+
224
+ private applyFilter(entries: BufferedLogEntryType[], filter?: LogFilter): BufferedLogEntryType[] {
225
+ if (!filter) return entries;
226
+
227
+ return entries.filter(entry => {
228
+ if (filter.lane && entry.laneName !== filter.lane) return false;
229
+
230
+ if (filter.importance) {
231
+ const order = [LogImportance.DEBUG, LogImportance.INFO, LogImportance.LOW,
232
+ LogImportance.MEDIUM, LogImportance.HIGH, LogImportance.CRITICAL];
233
+ if (order.indexOf(entry.importance) < order.indexOf(filter.importance)) return false;
234
+ }
235
+
236
+ if (filter.type && entry.type !== filter.type) return false;
237
+
238
+ if (filter.search) {
239
+ const searchLower = filter.search.toLowerCase();
240
+ if (!entry.message.toLowerCase().includes(searchLower) &&
241
+ !entry.type.toLowerCase().includes(searchLower)) return false;
242
+ }
243
+
244
+ return true;
245
+ });
246
+ }
247
+
248
+ getTotalCount(filter?: LogFilter): number {
249
+ if (!filter) return this.entries.length;
250
+ return this.applyFilter(this.entries, filter).length;
251
+ }
252
+
253
+ getNewEntriesCount(): number {
254
+ return this.newEntriesCount;
255
+ }
256
+
257
+ acknowledgeNewEntries(): void {
258
+ this.newEntriesCount = 0;
259
+ this.lastAcknowledgedId = this.entryIdCounter;
260
+ }
261
+
262
+ getLanes(): string[] {
263
+ return [...this.lanes];
264
+ }
265
+
266
+ getState(): LogBufferState {
267
+ return {
268
+ totalEntries: this.entries.length,
269
+ filteredCount: this.entries.length,
270
+ newCount: this.newEntriesCount,
271
+ isStreaming: this.isStreaming,
272
+ lanes: this.getLanes(),
273
+ };
274
+ }
275
+
276
+ getLaneColor(laneName: string): string {
277
+ return this.laneColorMap.get(laneName) || COLORS.white;
278
+ }
279
+
280
+ clear(): void {
281
+ this.entries = [];
282
+ this.entryIdCounter = 0;
283
+ this.newEntriesCount = 0;
284
+ this.lastAcknowledgedId = 0;
285
+ this.filePositions.clear();
286
+ }
287
+
288
+ formatEntry(entry: BufferedLogEntryType, options: { showLane?: boolean; showTimestamp?: boolean } = {}): string {
289
+ const { showLane = true, showTimestamp = true } = options;
290
+ const parts: string[] = [];
291
+
292
+ if (showTimestamp) {
293
+ const ts = entry.timestamp.toLocaleTimeString('en-US', { hour12: false });
294
+ parts.push(`${COLORS.gray}[${ts}]${COLORS.reset}`);
295
+ }
296
+
297
+ if (showLane) {
298
+ parts.push(`${entry.laneColor}[${entry.laneName.padEnd(12)}]${COLORS.reset}`);
299
+ }
300
+
301
+ parts.push(this.getTypeIndicator(entry.type));
302
+ parts.push(entry.message);
303
+
304
+ return parts.join(' ');
305
+ }
306
+
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}`,
318
+ };
319
+ return indicators[type.toLowerCase()] || `${COLORS.gray}[${type.toUpperCase().padEnd(6)}]${COLORS.reset}`;
320
+ }
321
+ }
322
+
323
+ export function createLogBuffer(runDir: string, options?: LogBufferOptions): LogBufferService {
324
+ return new LogBufferService(runDir, options);
325
+ }
326
+
@@ -0,0 +1,193 @@
1
+ /**
2
+ * Console Logging Utilities
3
+ *
4
+ * Provides formatted console output with colors, timestamps, and context support.
5
+ */
6
+
7
+ export enum LogLevel {
8
+ error = 0,
9
+ warn = 1,
10
+ info = 2,
11
+ debug = 3,
12
+ }
13
+
14
+ export const COLORS = {
15
+ reset: '\x1b[0m',
16
+ red: '\x1b[31m',
17
+ yellow: '\x1b[33m',
18
+ green: '\x1b[32m',
19
+ blue: '\x1b[34m',
20
+ cyan: '\x1b[36m',
21
+ magenta: '\x1b[35m',
22
+ gray: '\x1b[90m',
23
+ bold: '\x1b[1m',
24
+ white: '\x1b[37m',
25
+ } as const;
26
+
27
+ let currentLogLevel: number = LogLevel.info;
28
+
29
+ /**
30
+ * Set log level
31
+ */
32
+ export function setLogLevel(level: string | number): void {
33
+ if (typeof level === 'string') {
34
+ currentLogLevel = (LogLevel as any)[level] ?? LogLevel.info;
35
+ } else {
36
+ currentLogLevel = level;
37
+ }
38
+ }
39
+
40
+ /**
41
+ * Get current log level
42
+ */
43
+ export function getLogLevel(): number {
44
+ return currentLogLevel;
45
+ }
46
+
47
+ /**
48
+ * Format timestamp as [HH:MM:SS] in local time
49
+ */
50
+ export function formatTimestamp(date: Date = new Date()): string {
51
+ return date.toLocaleTimeString('en-US', { hour12: false });
52
+ }
53
+
54
+ /**
55
+ * Log options interface for contextual logging
56
+ */
57
+ export interface LogOptions {
58
+ context?: string;
59
+ emoji?: string;
60
+ noTimestamp?: boolean;
61
+ color?: string;
62
+ }
63
+
64
+ /**
65
+ * Internal log function with color support
66
+ */
67
+ function logWithColor(
68
+ color: string,
69
+ level: keyof typeof LogLevel,
70
+ message: string,
71
+ options: LogOptions = {}
72
+ ): void {
73
+ if (LogLevel[level] > currentLogLevel) return;
74
+
75
+ const { context, emoji = '', noTimestamp = false } = options;
76
+ const timestamp = noTimestamp ? '' : `${COLORS.gray}[${formatTimestamp()}]${COLORS.reset}`;
77
+ const contextPart = context ? ` ${COLORS.magenta}[${context}]${COLORS.reset}` : '';
78
+ const emojiPart = emoji ? `${emoji} ` : '';
79
+ const levelPart = `${color}${level.toUpperCase().padEnd(5)}${COLORS.reset}`;
80
+
81
+ const lines = String(message).split('\n');
82
+ const prefix = `${timestamp}${contextPart} ${emojiPart}${levelPart}`;
83
+
84
+ for (const line of lines) {
85
+ console.log(`${prefix} ${line}`);
86
+ }
87
+ }
88
+
89
+ function normalizeOptions(options: LogOptions | string | undefined, defaultEmoji: string): LogOptions {
90
+ if (typeof options === 'string') {
91
+ return { emoji: options };
92
+ }
93
+ return { emoji: defaultEmoji, ...options };
94
+ }
95
+
96
+ // Primary logging functions
97
+ export function error(message: string, options?: LogOptions | string): void {
98
+ logWithColor(COLORS.red, 'error', message, normalizeOptions(options, '❌'));
99
+ }
100
+
101
+ export function warn(message: string, options?: LogOptions | string): void {
102
+ logWithColor(COLORS.yellow, 'warn', message, normalizeOptions(options, '⚠️'));
103
+ }
104
+
105
+ export function info(message: string, options?: LogOptions | string): void {
106
+ logWithColor(COLORS.cyan, 'info', message, normalizeOptions(options, 'ℹ️'));
107
+ }
108
+
109
+ export function success(message: string, options?: LogOptions | string): void {
110
+ logWithColor(COLORS.green, 'info', message, normalizeOptions(options, '✅'));
111
+ }
112
+
113
+ export function debug(message: string, options?: LogOptions | string): void {
114
+ logWithColor(COLORS.gray, 'debug', message, normalizeOptions(options, '🔍'));
115
+ }
116
+
117
+ export function progress(message: string, options?: LogOptions | string): void {
118
+ logWithColor(COLORS.blue, 'info', message, normalizeOptions(options, '🔄'));
119
+ }
120
+
121
+ /**
122
+ * Create a context-bound logger
123
+ */
124
+ export function withContext(context: string) {
125
+ return {
126
+ error: (message: string, options?: Omit<LogOptions, 'context'>) =>
127
+ error(message, { ...options, context }),
128
+ warn: (message: string, options?: Omit<LogOptions, 'context'>) =>
129
+ warn(message, { ...options, context }),
130
+ info: (message: string, options?: Omit<LogOptions, 'context'>) =>
131
+ info(message, { ...options, context }),
132
+ success: (message: string, options?: Omit<LogOptions, 'context'>) =>
133
+ success(message, { ...options, context }),
134
+ debug: (message: string, options?: Omit<LogOptions, 'context'>) =>
135
+ debug(message, { ...options, context }),
136
+ progress: (message: string, options?: Omit<LogOptions, 'context'>) =>
137
+ progress(message, { ...options, context }),
138
+ };
139
+ }
140
+
141
+ /**
142
+ * Lane-specific output
143
+ */
144
+ export function laneOutput(laneName: string, message: string, isError = false): void {
145
+ const timestamp = `${COLORS.gray}[${formatTimestamp()}]${COLORS.reset}`;
146
+ const laneLabel = `${COLORS.magenta}${laneName.padEnd(10)}${COLORS.reset}`;
147
+ const output = isError ? `${COLORS.red}${message}${COLORS.reset}` : message;
148
+
149
+ if (isError) {
150
+ process.stderr.write(`${timestamp} ${laneLabel} ${output}\n`);
151
+ } else {
152
+ process.stdout.write(`${timestamp} ${laneLabel} ${output}\n`);
153
+ }
154
+ }
155
+
156
+ /**
157
+ * Section header
158
+ */
159
+ export function section(message: string): void {
160
+ console.log('');
161
+ console.log(`${COLORS.cyan}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${COLORS.reset}`);
162
+ console.log(`${COLORS.cyan} ${message}${COLORS.reset}`);
163
+ console.log(`${COLORS.cyan}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${COLORS.reset}`);
164
+ console.log('');
165
+ }
166
+
167
+ /**
168
+ * Simple log without formatting
169
+ */
170
+ export function log(message: string | any): void {
171
+ console.log(message);
172
+ }
173
+
174
+ /**
175
+ * Raw output (no formatting)
176
+ */
177
+ export function raw(message: string): void {
178
+ process.stdout.write(message);
179
+ }
180
+
181
+ /**
182
+ * Status indicators
183
+ */
184
+ export const STATUS = {
185
+ running: `${COLORS.blue}🔄${COLORS.reset}`,
186
+ done: `${COLORS.green}✅${COLORS.reset}`,
187
+ failed: `${COLORS.red}❌${COLORS.reset}`,
188
+ warning: `${COLORS.yellow}⚠️${COLORS.reset}`,
189
+ pending: `${COLORS.gray}⏳${COLORS.reset}`,
190
+ paused: `${COLORS.yellow}⏸️${COLORS.reset}`,
191
+ waiting: `${COLORS.gray}⏳${COLORS.reset}`,
192
+ } as const;
193
+