@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
@@ -1,526 +0,0 @@
1
- /**
2
- * Log File Writer - Write logs to files with rotation
3
- *
4
- * Manages log file writing with support for multiple formats.
5
- */
6
-
7
- import * as fs from 'fs';
8
- import * as path from 'path';
9
- import { Transform, TransformCallback } from 'stream';
10
- import { LogSession, JsonLogEntry, ParsedMessage } from '../../types/logging';
11
- import { EnhancedLogConfig } from '../../types/config';
12
- import { StreamingMessageParser } from './parser';
13
- import { stripAnsi, formatTimestampISO } from './formatter';
14
-
15
- export const DEFAULT_LOG_CONFIG: EnhancedLogConfig = {
16
- enabled: true,
17
- stripAnsi: true,
18
- addTimestamps: true,
19
- maxFileSize: 50 * 1024 * 1024,
20
- maxFiles: 5,
21
- keepRawLogs: true,
22
- writeJsonLog: true,
23
- timestampFormat: 'iso',
24
- };
25
-
26
- /**
27
- * Transform stream for clean log output
28
- */
29
- export class CleanLogTransform extends Transform {
30
- private config: EnhancedLogConfig;
31
- private session: LogSession;
32
- private buffer = '';
33
-
34
- constructor(config: EnhancedLogConfig, session: LogSession) {
35
- super({ encoding: 'utf8' });
36
- this.config = config;
37
- this.session = session;
38
- }
39
-
40
- _transform(chunk: Buffer, _encoding: BufferEncoding, callback: TransformCallback): void {
41
- let text = chunk.toString();
42
- this.buffer += text;
43
- const lines = this.buffer.split('\n');
44
- this.buffer = lines.pop() || '';
45
-
46
- for (const line of lines) {
47
- let processed = line;
48
- if (this.config.stripAnsi) processed = stripAnsi(processed);
49
- if (this.config.addTimestamps && processed.trim() && !this.hasTimestamp(processed)) {
50
- const ts = formatTimestampISO(this.config.timestampFormat, this.session.startTime);
51
- processed = `[${ts}] ${processed}`;
52
- }
53
- this.push(processed + '\n');
54
- }
55
- callback();
56
- }
57
-
58
- _flush(callback: TransformCallback): void {
59
- if (this.buffer.trim()) {
60
- let processed = this.buffer;
61
- if (this.config.stripAnsi) processed = stripAnsi(processed);
62
- if (this.config.addTimestamps && processed.trim() && !this.hasTimestamp(processed)) {
63
- const ts = formatTimestampISO(this.config.timestampFormat, this.session.startTime);
64
- processed = `[${ts}] ${processed}`;
65
- }
66
- this.push(processed + '\n');
67
- }
68
- callback();
69
- }
70
-
71
- private hasTimestamp(line: string): boolean {
72
- return /^\[\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/.test(line.trim());
73
- }
74
- }
75
-
76
- /**
77
- * Enhanced Log Manager - Handles all log file operations
78
- */
79
- export class EnhancedLogManager {
80
- private config: EnhancedLogConfig;
81
- private session: LogSession;
82
- private logDir: string;
83
-
84
- private cleanLogPath: string;
85
- private rawLogPath: string;
86
- private jsonLogPath: string;
87
- private readableLogPath: string;
88
-
89
- private cleanLogFd: number | null = null;
90
- private rawLogFd: number | null = null;
91
- private jsonLogFd: number | null = null;
92
- private readableLogFd: number | null = null;
93
-
94
- private cleanLogSize = 0;
95
- private rawLogSize = 0;
96
-
97
- private cleanTransform: CleanLogTransform | null = null;
98
- private streamingParser: StreamingMessageParser | null = null;
99
- private lineBuffer = '';
100
- private onParsedMessage?: (msg: ParsedMessage) => void;
101
-
102
- constructor(
103
- logDir: string,
104
- session: LogSession,
105
- config: Partial<EnhancedLogConfig> = {},
106
- onParsedMessage?: (msg: ParsedMessage) => void
107
- ) {
108
- this.config = { ...DEFAULT_LOG_CONFIG, ...config };
109
- this.session = session;
110
- this.logDir = logDir;
111
- this.onParsedMessage = onParsedMessage;
112
-
113
- fs.mkdirSync(logDir, { recursive: true });
114
-
115
- this.cleanLogPath = path.join(logDir, 'terminal.log');
116
- this.rawLogPath = path.join(logDir, 'terminal-raw.log');
117
- this.jsonLogPath = path.join(logDir, 'terminal.jsonl');
118
- this.readableLogPath = path.join(logDir, 'terminal-readable.log');
119
-
120
- this.initLogFiles();
121
- }
122
-
123
- private initLogFiles(): void {
124
- this.rotateIfNeeded(this.cleanLogPath);
125
- if (this.config.keepRawLogs) this.rotateIfNeeded(this.rawLogPath);
126
-
127
- this.cleanLogFd = fs.openSync(this.cleanLogPath, 'a');
128
- if (this.config.keepRawLogs) this.rawLogFd = fs.openSync(this.rawLogPath, 'a');
129
- if (this.config.writeJsonLog) this.jsonLogFd = fs.openSync(this.jsonLogPath, 'a');
130
- this.readableLogFd = fs.openSync(this.readableLogPath, 'a');
131
-
132
- try {
133
- this.cleanLogSize = fs.statSync(this.cleanLogPath).size;
134
- if (this.config.keepRawLogs) this.rawLogSize = fs.statSync(this.rawLogPath).size;
135
- } catch {
136
- this.cleanLogSize = 0;
137
- this.rawLogSize = 0;
138
- }
139
-
140
- this.writeSessionHeader();
141
-
142
- this.cleanTransform = new CleanLogTransform(this.config, this.session);
143
- this.cleanTransform.on('data', (data: string) => this.writeToCleanLog(data));
144
-
145
- this.streamingParser = new StreamingMessageParser(msg => {
146
- this.writeReadableMessage(msg);
147
- if (this.onParsedMessage) this.onParsedMessage(msg);
148
- });
149
- }
150
-
151
- private writeSessionHeader(): void {
152
- const header = `
153
- ╔══════════════════════════════════════════════════════════════════════════════╗
154
- ║ CursorFlow Session Log ║
155
- ╠══════════════════════════════════════════════════════════════════════════════╣
156
- ║ Session ID: ${this.session.id.padEnd(62)}║
157
- ║ Lane: ${this.session.laneName.padEnd(62)}║
158
- ║ Task: ${(this.session.taskName || '-').padEnd(62)}║
159
- ║ Model: ${(this.session.model || 'default').padEnd(62)}║
160
- ║ Started: ${new Date(this.session.startTime).toISOString().padEnd(62)}║
161
- ╚══════════════════════════════════════════════════════════════════════════════╝
162
-
163
- `;
164
- this.writeToCleanLog(header);
165
- if (this.config.keepRawLogs && this.rawLogFd !== null) {
166
- fs.writeSync(this.rawLogFd, header);
167
- }
168
-
169
- this.writeJsonEntry({
170
- timestamp: new Date(this.session.startTime).toISOString(),
171
- level: 'session',
172
- source: 'system',
173
- lane: this.session.laneName,
174
- task: this.session.taskName,
175
- message: 'Session started',
176
- metadata: { sessionId: this.session.id, model: this.session.model, ...this.session.metadata },
177
- });
178
- }
179
-
180
- private writeReadableMessage(msg: ParsedMessage): void {
181
- if (this.readableLogFd === null) return;
182
-
183
- const ts = new Date(msg.timestamp).toISOString();
184
- let formatted: string;
185
-
186
- switch (msg.type) {
187
- case 'system':
188
- formatted = `[${ts}] ⚙️ SYSTEM: ${msg.content}\n`;
189
- break;
190
-
191
- case 'user':
192
- case 'assistant':
193
- case 'result': {
194
- const isUser = msg.type === 'user';
195
- const isResult = msg.type === 'result';
196
- const headerText = isUser ? '🧑 USER' : isResult ? '🤖 ASSISTANT (Final)' : '🤖 ASSISTANT';
197
- const duration = msg.metadata?.duration_ms ? ` (${Math.round(msg.metadata.duration_ms / 1000)}s)` : '';
198
- const label = `[ ${headerText}${duration} ] `;
199
- const totalWidth = 80;
200
- const topBorder = `┌─${label}${'─'.repeat(Math.max(0, totalWidth - label.length - 2))}`;
201
- const bottomBorder = `└─${'─'.repeat(totalWidth - 2)}`;
202
-
203
- formatted = `[${ts}] ${topBorder}\n`;
204
- for (const line of msg.content.split('\n')) {
205
- formatted += `[${ts}] │ ${line}\n`;
206
- }
207
- formatted += `[${ts}] ${bottomBorder}\n`;
208
- break;
209
- }
210
-
211
- case 'tool':
212
- formatted = `[${ts}] 🔧 TOOL: ${msg.content}\n`;
213
- break;
214
-
215
- case 'tool_result': {
216
- const lines = msg.metadata?.lines ? ` (${msg.metadata.lines} lines)` : '';
217
- formatted = `[${ts}] 📄 RESL: ${msg.metadata?.toolName || 'Tool'}${lines}\n`;
218
- break;
219
- }
220
-
221
- case 'thinking': {
222
- const thinkLabel = `[ 🤔 THINKING ] `;
223
- const thinkWidth = 80;
224
- const thinkTop = `┌─${thinkLabel}${'─'.repeat(Math.max(0, thinkWidth - thinkLabel.length - 2))}`;
225
- const thinkBottom = `└─${'─'.repeat(thinkWidth - 2)}`;
226
-
227
- formatted = `[${ts}] ${thinkTop}\n`;
228
- for (const line of msg.content.trim().split('\n')) {
229
- formatted += `[${ts}] │ ${line}\n`;
230
- }
231
- formatted += `[${ts}] ${thinkBottom}\n`;
232
- break;
233
- }
234
-
235
- default:
236
- formatted = `[${ts}] ${msg.content}\n`;
237
- }
238
-
239
- try {
240
- fs.writeSync(this.readableLogFd, formatted);
241
- } catch { /* Ignore write errors */ }
242
- }
243
-
244
- private rotateIfNeeded(logPath: string): void {
245
- if (!fs.existsSync(logPath)) return;
246
- try {
247
- if (fs.statSync(logPath).size >= this.config.maxFileSize) {
248
- this.rotateLog(logPath);
249
- }
250
- } catch { /* Ignore */ }
251
- }
252
-
253
- private rotateLog(logPath: string): void {
254
- const dir = path.dirname(logPath);
255
- const ext = path.extname(logPath);
256
- const base = path.basename(logPath, ext);
257
-
258
- for (let i = this.config.maxFiles - 1; i >= 1; i--) {
259
- const oldPath = path.join(dir, `${base}.${i}${ext}`);
260
- const newPath = path.join(dir, `${base}.${i + 1}${ext}`);
261
- if (fs.existsSync(oldPath)) {
262
- if (i === this.config.maxFiles - 1) {
263
- fs.unlinkSync(oldPath);
264
- } else {
265
- fs.renameSync(oldPath, newPath);
266
- }
267
- }
268
- }
269
- fs.renameSync(logPath, path.join(dir, `${base}.1${ext}`));
270
- }
271
-
272
- private writeToCleanLog(data: string): void {
273
- if (this.cleanLogFd === null) return;
274
- const buffer = Buffer.from(data);
275
- fs.writeSync(this.cleanLogFd, buffer);
276
- this.cleanLogSize += buffer.length;
277
-
278
- if (this.cleanLogSize >= this.config.maxFileSize) {
279
- fs.closeSync(this.cleanLogFd);
280
- this.rotateLog(this.cleanLogPath);
281
- this.cleanLogFd = fs.openSync(this.cleanLogPath, 'a');
282
- this.cleanLogSize = 0;
283
- }
284
- }
285
-
286
- private writeToRawLog(data: string): void {
287
- if (this.rawLogFd === null) return;
288
- const buffer = Buffer.from(data);
289
- fs.writeSync(this.rawLogFd, buffer);
290
- this.rawLogSize += buffer.length;
291
-
292
- if (this.rawLogSize >= this.config.maxFileSize) {
293
- fs.closeSync(this.rawLogFd);
294
- this.rotateLog(this.rawLogPath);
295
- this.rawLogFd = fs.openSync(this.rawLogPath, 'a');
296
- this.rawLogSize = 0;
297
- }
298
- }
299
-
300
- private writeJsonEntry(entry: JsonLogEntry): void {
301
- if (this.jsonLogFd === null) return;
302
- fs.writeSync(this.jsonLogFd, JSON.stringify(entry) + '\n');
303
- }
304
-
305
- writeStdout(data: Buffer | string): void {
306
- const text = data.toString();
307
- if (this.config.keepRawLogs) this.writeToRawLog(text);
308
- if (this.cleanTransform) this.cleanTransform.write(data);
309
-
310
- this.lineBuffer += text;
311
- const lines = this.lineBuffer.split('\n');
312
- this.lineBuffer = lines.pop() || '';
313
-
314
- for (const line of lines) {
315
- const cleanLine = stripAnsi(line).trim();
316
- if (!cleanLine) continue;
317
-
318
- if (cleanLine.startsWith('{')) {
319
- if (this.streamingParser) this.streamingParser.parseLine(cleanLine);
320
- if (this.config.writeJsonLog) {
321
- try {
322
- const json = JSON.parse(cleanLine);
323
- this.writeJsonEntry({
324
- timestamp: new Date().toISOString(),
325
- level: 'stdout',
326
- lane: this.session.laneName,
327
- task: this.session.taskName,
328
- message: cleanLine.substring(0, 2000),
329
- metadata: json,
330
- });
331
- continue;
332
- } catch { /* Not JSON */ }
333
- }
334
- }
335
-
336
- if (this.config.writeJsonLog && !this.isNoise(cleanLine)) {
337
- this.writeJsonEntry({
338
- timestamp: new Date().toISOString(),
339
- level: 'stdout',
340
- lane: this.session.laneName,
341
- task: this.session.taskName,
342
- message: cleanLine.substring(0, 1000),
343
- });
344
- }
345
- }
346
- }
347
-
348
- writeStderr(data: Buffer | string): void {
349
- const text = data.toString();
350
- if (this.config.keepRawLogs) this.writeToRawLog(text);
351
- if (this.cleanTransform) this.cleanTransform.write(data);
352
-
353
- if (this.readableLogFd !== null) {
354
- for (const line of text.split('\n')) {
355
- const cleanLine = stripAnsi(line).trim();
356
- if (cleanLine && !this.isNoise(cleanLine)) {
357
- try {
358
- fs.writeSync(this.readableLogFd, `[${new Date().toISOString()}] ❌ STDERR: ${cleanLine}\n`);
359
- } catch { /* Ignore */ }
360
- }
361
- }
362
- }
363
-
364
- if (this.config.writeJsonLog) {
365
- const cleanText = stripAnsi(text).trim();
366
- if (cleanText) {
367
- this.writeJsonEntry({
368
- timestamp: new Date().toISOString(),
369
- level: 'stderr',
370
- lane: this.session.laneName,
371
- task: this.session.taskName,
372
- message: cleanText.substring(0, 1000),
373
- });
374
- }
375
- }
376
- }
377
-
378
- log(level: 'info' | 'error' | 'debug', message: string, metadata?: Record<string, any>): void {
379
- const ts = formatTimestampISO(this.config.timestampFormat, this.session.startTime);
380
- const line = `[${ts}] [${level.toUpperCase().padEnd(5)}] ${message}\n`;
381
- this.writeToCleanLog(line);
382
- if (this.config.keepRawLogs) this.writeToRawLog(line);
383
-
384
- if (this.readableLogFd !== null) {
385
- const icon = level === 'error' ? '❌ ERROR' : level === 'info' ? 'ℹ️ INFO' : '🔍 DEBUG';
386
- try {
387
- fs.writeSync(this.readableLogFd, `${new Date().toISOString()} ${icon}: ${message}\n`);
388
- } catch { /* Ignore */ }
389
- }
390
-
391
- if (this.config.writeJsonLog) {
392
- this.writeJsonEntry({
393
- timestamp: new Date().toISOString(),
394
- level,
395
- lane: this.session.laneName,
396
- task: this.session.taskName,
397
- message,
398
- metadata,
399
- });
400
- }
401
- }
402
-
403
- section(title: string): void {
404
- const divider = '═'.repeat(78);
405
- const line = `\n${divider}\n ${title}\n${divider}\n`;
406
- this.writeToCleanLog(line);
407
- if (this.config.keepRawLogs) this.writeToRawLog(line);
408
-
409
- if (this.readableLogFd !== null) {
410
- const ts = new Date().toISOString();
411
- try {
412
- fs.writeSync(this.readableLogFd, `[${ts}] ━━━ ${title} ${'━'.repeat(60)}\n`);
413
- } catch { /* Ignore */ }
414
- }
415
- }
416
-
417
- setTask(taskName: string, model?: string): void {
418
- this.session.taskName = taskName;
419
- if (model) this.session.model = model;
420
- this.section(`Task: ${taskName}${model ? ` (Model: ${model})` : ''}`);
421
-
422
- if (this.config.writeJsonLog) {
423
- this.writeJsonEntry({
424
- timestamp: new Date().toISOString(),
425
- level: 'info',
426
- source: 'system',
427
- lane: this.session.laneName,
428
- task: taskName,
429
- message: `Task started: ${taskName}`,
430
- metadata: { model },
431
- });
432
- }
433
- }
434
-
435
- private isNoise(text: string): boolean {
436
- if (!text.trim()) return true;
437
- const patterns = [/^[\s│├└─┌┐┘┴┬┤]+$/, /^[.\s]+$/, /^[=>\s-]+$/, /^\d+%$/, /^⠋|⠙|⠹|⠸|⠼|⠴|⠦|⠧|⠇|⠏/];
438
- return patterns.some(p => p.test(text));
439
- }
440
-
441
- getLogPaths(): { clean: string; raw?: string; json?: string; readable: string } {
442
- return {
443
- clean: this.cleanLogPath,
444
- raw: this.config.keepRawLogs ? this.rawLogPath : undefined,
445
- json: this.config.writeJsonLog ? this.jsonLogPath : undefined,
446
- readable: this.readableLogPath,
447
- };
448
- }
449
-
450
- getFileDescriptors(): { stdout: number; stderr: number } {
451
- const fd = this.rawLogFd !== null ? this.rawLogFd : this.cleanLogFd!;
452
- return { stdout: fd, stderr: fd };
453
- }
454
-
455
- close(): void {
456
- if (this.cleanTransform) this.cleanTransform.end();
457
-
458
- if (this.streamingParser) {
459
- if (this.lineBuffer.trim()) this.streamingParser.parseLine(this.lineBuffer);
460
- this.streamingParser.flush();
461
- }
462
-
463
- const endMarker = `
464
- ╔══════════════════════════════════════════════════════════════════════════════╗
465
- ║ Session Ended: ${new Date().toISOString().padEnd(60)}║
466
- ║ Duration: ${this.formatDuration(Date.now() - this.session.startTime).padEnd(65)}║
467
- ╚══════════════════════════════════════════════════════════════════════════════╝
468
-
469
- `;
470
-
471
- if (this.cleanLogFd !== null) {
472
- fs.writeSync(this.cleanLogFd, endMarker);
473
- fs.closeSync(this.cleanLogFd);
474
- this.cleanLogFd = null;
475
- }
476
-
477
- if (this.rawLogFd !== null) {
478
- fs.writeSync(this.rawLogFd, endMarker);
479
- fs.closeSync(this.rawLogFd);
480
- this.rawLogFd = null;
481
- }
482
-
483
- if (this.jsonLogFd !== null) {
484
- this.writeJsonEntry({
485
- timestamp: new Date().toISOString(),
486
- level: 'session',
487
- source: 'system',
488
- lane: this.session.laneName,
489
- message: 'Session ended',
490
- metadata: { sessionId: this.session.id, duration: Date.now() - this.session.startTime },
491
- });
492
- fs.closeSync(this.jsonLogFd);
493
- this.jsonLogFd = null;
494
- }
495
-
496
- if (this.readableLogFd !== null) {
497
- fs.writeSync(this.readableLogFd, endMarker);
498
- fs.closeSync(this.readableLogFd);
499
- this.readableLogFd = null;
500
- }
501
- }
502
-
503
- private formatDuration(ms: number): string {
504
- const seconds = Math.floor((ms / 1000) % 60);
505
- const minutes = Math.floor((ms / (1000 * 60)) % 60);
506
- const hours = Math.floor(ms / (1000 * 60 * 60));
507
- if (hours > 0) return `${hours}h ${minutes}m ${seconds}s`;
508
- if (minutes > 0) return `${minutes}m ${seconds}s`;
509
- return `${seconds}s`;
510
- }
511
- }
512
-
513
- export function createLogManager(
514
- laneRunDir: string,
515
- laneName: string,
516
- config?: Partial<EnhancedLogConfig>,
517
- onParsedMessage?: (msg: ParsedMessage) => void
518
- ): EnhancedLogManager {
519
- const session: LogSession = {
520
- id: `${laneName}-${Date.now().toString(36)}`,
521
- laneName,
522
- startTime: Date.now(),
523
- };
524
- return new EnhancedLogManager(laneRunDir, session, config, onParsedMessage);
525
- }
526
-
@@ -1,20 +0,0 @@
1
- /**
2
- * Review-related type definitions
3
- */
4
-
5
- export interface ReviewIssue {
6
- severity: 'critical' | 'major' | 'minor';
7
- description: string;
8
- file?: string;
9
- suggestion?: string;
10
- }
11
-
12
- export interface ReviewResult {
13
- status: 'approved' | 'needs_changes';
14
- buildSuccess: boolean;
15
- issues: ReviewIssue[];
16
- suggestions: string[];
17
- summary: string;
18
- raw: string;
19
- }
20
-