@litmers/cursorflow-orchestrator 0.2.2 → 0.2.5

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 (95) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +8 -11
  3. package/dist/cli/complete.d.ts +7 -0
  4. package/dist/cli/complete.js +304 -0
  5. package/dist/cli/complete.js.map +1 -0
  6. package/dist/cli/index.js +0 -6
  7. package/dist/cli/index.js.map +1 -1
  8. package/dist/cli/logs.js +51 -61
  9. package/dist/cli/logs.js.map +1 -1
  10. package/dist/cli/monitor.js +74 -46
  11. package/dist/cli/monitor.js.map +1 -1
  12. package/dist/cli/resume.js +2 -2
  13. package/dist/cli/resume.js.map +1 -1
  14. package/dist/cli/signal.js +33 -29
  15. package/dist/cli/signal.js.map +1 -1
  16. package/dist/core/auto-recovery.d.ts +2 -117
  17. package/dist/core/auto-recovery.js +4 -487
  18. package/dist/core/auto-recovery.js.map +1 -1
  19. package/dist/core/failure-policy.d.ts +0 -52
  20. package/dist/core/failure-policy.js +7 -174
  21. package/dist/core/failure-policy.js.map +1 -1
  22. package/dist/core/git-lifecycle-manager.js +2 -2
  23. package/dist/core/git-lifecycle-manager.js.map +1 -1
  24. package/dist/core/git-pipeline-coordinator.js +25 -25
  25. package/dist/core/git-pipeline-coordinator.js.map +1 -1
  26. package/dist/core/intervention.d.ts +0 -6
  27. package/dist/core/intervention.js +1 -17
  28. package/dist/core/intervention.js.map +1 -1
  29. package/dist/core/orchestrator.js +18 -10
  30. package/dist/core/orchestrator.js.map +1 -1
  31. package/dist/core/runner/agent.js +18 -15
  32. package/dist/core/runner/agent.js.map +1 -1
  33. package/dist/core/runner/pipeline.js +3 -3
  34. package/dist/core/runner/pipeline.js.map +1 -1
  35. package/dist/core/stall-detection.js +9 -7
  36. package/dist/core/stall-detection.js.map +1 -1
  37. package/dist/hooks/data-accessor.js +2 -2
  38. package/dist/hooks/data-accessor.js.map +1 -1
  39. package/dist/services/logging/buffer.d.ts +1 -2
  40. package/dist/services/logging/buffer.js +22 -63
  41. package/dist/services/logging/buffer.js.map +1 -1
  42. package/dist/services/logging/formatter.d.ts +4 -0
  43. package/dist/services/logging/formatter.js +201 -33
  44. package/dist/services/logging/formatter.js.map +1 -1
  45. package/dist/services/logging/paths.d.ts +0 -3
  46. package/dist/services/logging/paths.js +0 -3
  47. package/dist/services/logging/paths.js.map +1 -1
  48. package/dist/types/config.d.ts +1 -9
  49. package/dist/types/logging.d.ts +1 -1
  50. package/dist/utils/config.js +2 -6
  51. package/dist/utils/config.js.map +1 -1
  52. package/dist/utils/enhanced-logger.d.ts +17 -37
  53. package/dist/utils/enhanced-logger.js +237 -267
  54. package/dist/utils/enhanced-logger.js.map +1 -1
  55. package/dist/utils/logger.js +17 -4
  56. package/dist/utils/logger.js.map +1 -1
  57. package/dist/utils/repro-thinking-logs.js +4 -4
  58. package/dist/utils/repro-thinking-logs.js.map +1 -1
  59. package/package.json +3 -14
  60. package/scripts/monitor-lanes.sh +5 -5
  61. package/scripts/stream-logs.sh +1 -1
  62. package/scripts/test-log-parser.ts +8 -42
  63. package/src/cli/complete.ts +305 -0
  64. package/src/cli/index.ts +0 -6
  65. package/src/cli/logs.ts +46 -60
  66. package/src/cli/monitor.ts +82 -48
  67. package/src/cli/resume.ts +1 -1
  68. package/src/cli/signal.ts +38 -34
  69. package/src/core/auto-recovery.ts +13 -595
  70. package/src/core/failure-policy.ts +7 -228
  71. package/src/core/git-lifecycle-manager.ts +2 -2
  72. package/src/core/git-pipeline-coordinator.ts +25 -25
  73. package/src/core/intervention.ts +0 -18
  74. package/src/core/orchestrator.ts +20 -10
  75. package/src/core/runner/agent.ts +21 -16
  76. package/src/core/runner/pipeline.ts +3 -3
  77. package/src/core/stall-detection.ts +11 -9
  78. package/src/hooks/data-accessor.ts +2 -2
  79. package/src/services/logging/buffer.ts +20 -68
  80. package/src/services/logging/formatter.ts +199 -32
  81. package/src/services/logging/paths.ts +0 -3
  82. package/src/types/config.ts +1 -13
  83. package/src/types/logging.ts +2 -0
  84. package/src/utils/config.ts +2 -6
  85. package/src/utils/enhanced-logger.ts +239 -290
  86. package/src/utils/logger.ts +18 -3
  87. package/src/utils/repro-thinking-logs.ts +4 -4
  88. package/dist/cli/prepare.d.ts +0 -7
  89. package/dist/cli/prepare.js +0 -690
  90. package/dist/cli/prepare.js.map +0 -1
  91. package/dist/utils/log-formatter.d.ts +0 -26
  92. package/dist/utils/log-formatter.js +0 -274
  93. package/dist/utils/log-formatter.js.map +0 -1
  94. package/src/cli/prepare.ts +0 -777
  95. package/src/utils/log-formatter.ts +0 -287
@@ -1,10 +1,6 @@
1
1
  "use strict";
2
2
  /**
3
- * Enhanced Logger - Simplified terminal output capture
4
- *
5
- * Features:
6
- * - Raw log: Original output as-is
7
- * - Readable log: Formatted with formatMessageForConsole style
3
+ * Enhanced Logger - Simplified JSONL terminal output capture
8
4
  */
9
5
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
10
6
  if (k2 === undefined) k2 = k;
@@ -41,86 +37,31 @@ var __importStar = (this && this.__importStar) || (function () {
41
37
  })();
42
38
  Object.defineProperty(exports, "__esModule", { value: true });
43
39
  exports.CleanLogTransform = exports.StreamingMessageParser = exports.EnhancedLogManager = exports.DEFAULT_LOG_CONFIG = void 0;
44
- exports.stripAnsi = stripAnsi;
45
- exports.formatTimestamp = formatTimestamp;
46
40
  exports.createLogManager = createLogManager;
47
41
  exports.readJsonLog = readJsonLog;
48
42
  exports.exportLogs = exportLogs;
49
43
  const fs = __importStar(require("fs"));
50
44
  const path = __importStar(require("path"));
51
- const log_formatter_1 = require("./log-formatter");
45
+ const formatter_1 = require("../services/logging/formatter");
52
46
  const path_1 = require("./path");
53
47
  const paths_1 = require("../services/logging/paths");
54
48
  exports.DEFAULT_LOG_CONFIG = {
55
49
  enabled: true,
56
50
  stripAnsi: true,
57
- addTimestamps: true,
58
51
  maxFileSize: 50 * 1024 * 1024, // 50MB
59
52
  maxFiles: 5,
60
- keepRawLogs: true,
61
- keepAbsoluteRawLogs: false,
62
- raw: false,
63
- writeJsonLog: false, // Disabled by default now
53
+ writeJsonLog: true,
64
54
  timestampFormat: 'iso',
65
55
  };
66
56
  /**
67
- * ANSI escape sequence regex pattern
68
- */
69
- const ANSI_REGEX = /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g;
70
- const EXTENDED_ANSI_REGEX = /(?:\x1B[@-Z\\-_]|\x1B\[[0-?]*[ -/]*[@-~]|\x1B\][^\x07]*(?:\x07|\x1B\\)|\x1B[PX^_][^\x1B]*\x1B\\|\x1B.)/g;
71
- /**
72
- * Strip ANSI escape sequences from text
73
- */
74
- function stripAnsi(text) {
75
- return text
76
- .replace(EXTENDED_ANSI_REGEX, '')
77
- .replace(ANSI_REGEX, '')
78
- .replace(/\r[^\n]/g, '\n')
79
- .replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, '');
80
- }
81
- /**
82
- * Format timestamp
83
- */
84
- function formatTimestamp(format, startTime) {
85
- const now = Date.now();
86
- switch (format) {
87
- case 'iso':
88
- return new Date(now).toISOString();
89
- case 'relative':
90
- if (startTime) {
91
- const elapsed = now - startTime;
92
- const seconds = Math.floor(elapsed / 1000);
93
- const minutes = Math.floor(seconds / 60);
94
- const hours = Math.floor(minutes / 60);
95
- if (hours > 0) {
96
- return `+${hours}h${minutes % 60}m${seconds % 60}s`;
97
- }
98
- else if (minutes > 0) {
99
- return `+${minutes}m${seconds % 60}s`;
100
- }
101
- else {
102
- return `+${seconds}s`;
103
- }
104
- }
105
- return new Date(now).toISOString();
106
- case 'short':
107
- return new Date(now).toLocaleTimeString('en-US', { hour12: false });
108
- default:
109
- return new Date(now).toISOString();
110
- }
111
- }
112
- /**
113
- * Simplified Log Manager - Only raw and readable logs
57
+ * Enhanced Log Manager - JSONL format only
114
58
  */
115
59
  class EnhancedLogManager {
116
60
  config;
117
61
  session;
118
62
  logDir;
119
- rawLogPath;
120
- readableLogPath;
121
- rawLogFd = null;
122
- readableLogFd = null;
123
- rawLogSize = 0;
63
+ jsonlLogPath;
64
+ jsonlLogFd = null;
124
65
  onParsedMessage;
125
66
  constructor(logDir, session, config = {}, onParsedMessage) {
126
67
  this.config = { ...exports.DEFAULT_LOG_CONFIG, ...config };
@@ -130,10 +71,7 @@ class EnhancedLogManager {
130
71
  // Ensure log directory exists
131
72
  fs.mkdirSync(logDir, { recursive: true });
132
73
  // Subprocess (lane) logs live in the lane run directory to avoid nesting with main logs.
133
- // Main process logs are written separately to a run-level file (see utils/logger.ts).
134
- // Set up log file paths (simplified)
135
- this.rawLogPath = (0, paths_1.getLaneLogPath)(logDir, 'raw');
136
- this.readableLogPath = (0, paths_1.getLaneLogPath)(logDir, 'readable');
74
+ this.jsonlLogPath = (0, paths_1.getLaneLogPath)(logDir, 'jsonl');
137
75
  // Initialize log files
138
76
  this.initLogFiles();
139
77
  }
@@ -156,43 +94,34 @@ class EnhancedLogManager {
156
94
  * Initialize log files and write session headers
157
95
  */
158
96
  initLogFiles() {
159
- // Check and rotate if necessary
160
- if (this.config.keepRawLogs) {
161
- this.rotateIfNeeded(this.rawLogPath);
162
- this.rawLogFd = fs.openSync(this.rawLogPath, 'a');
97
+ if (this.config.writeJsonLog) {
98
+ // Ensure parent directory exists before opening file
99
+ const logDir = path.dirname(this.jsonlLogPath);
100
+ fs.mkdirSync(logDir, { recursive: true });
101
+ this.rotateIfNeeded(this.jsonlLogPath);
102
+ this.jsonlLogFd = fs.openSync(this.jsonlLogPath, 'a');
163
103
  }
164
- this.rotateIfNeeded(this.readableLogPath);
165
- this.readableLogFd = fs.openSync(this.readableLogPath, 'a');
166
- // Get initial file size for raw log if enabled
167
- if (this.rawLogFd !== null) {
168
- try {
169
- this.rawLogSize = fs.statSync(this.rawLogPath).size;
170
- }
171
- catch {
172
- this.rawLogSize = 0;
173
- }
174
- }
175
- // Write session header
176
- this.writeSessionHeader();
104
+ // Write session start to JSON log
105
+ this.writeSessionStart();
177
106
  }
178
107
  /**
179
- * Write session header to logs
108
+ * Write session start to JSON log
180
109
  */
181
- writeSessionHeader() {
182
- const header = `
183
- ╔══════════════════════════════════════════════════════════════════════════════╗
184
- ║ CursorFlow Session Log ║
185
- ╠══════════════════════════════════════════════════════════════════════════════╣
186
- Session ID: ${this.session.id.padEnd(62)}║
187
- ║ Lane: ${this.session.laneName.padEnd(62)}║
188
- ║ Task: ${(this.session.taskName || '-').padEnd(62)}║
189
- ║ Model: ${(this.session.model || 'default').padEnd(62)}║
190
- ║ Started: ${new Date(this.session.startTime).toISOString().padEnd(62)}║
191
- ╚══════════════════════════════════════════════════════════════════════════════╝
192
-
193
- `;
194
- this.writeToRawLog(header);
195
- this.writeToReadableLog(header);
110
+ writeSessionStart() {
111
+ if (this.config.writeJsonLog) {
112
+ this.writeToJsonLog({
113
+ type: 'session',
114
+ role: 'system',
115
+ content: 'Session started',
116
+ timestamp: this.session.startTime,
117
+ metadata: {
118
+ sessionId: this.session.id,
119
+ laneName: this.session.laneName,
120
+ taskName: this.session.taskName,
121
+ model: this.session.model,
122
+ }
123
+ });
124
+ }
196
125
  }
197
126
  /**
198
127
  * Rotate log file if it exceeds max size
@@ -235,49 +164,35 @@ class EnhancedLogManager {
235
164
  fs.renameSync(logPath, rotatedPath);
236
165
  }
237
166
  /**
238
- * Write to raw log with size tracking
167
+ * Write to JSONL log
239
168
  */
240
- writeToRawLog(data) {
241
- if (this.rawLogFd === null)
242
- return;
243
- const buffer = typeof data === 'string' ? Buffer.from(data) : data;
244
- fs.writeSync(this.rawLogFd, buffer);
245
- this.rawLogSize += buffer.length;
246
- // Check if rotation needed
247
- if (this.rawLogSize >= this.config.maxFileSize) {
248
- fs.closeSync(this.rawLogFd);
249
- this.rotateLog(this.rawLogPath);
250
- this.rawLogFd = fs.openSync(this.rawLogPath, 'a');
251
- this.rawLogSize = 0;
252
- }
253
- }
254
- /**
255
- * Write to readable log
256
- */
257
- writeToReadableLog(data) {
258
- if (this.readableLogFd === null)
169
+ writeToJsonLog(msg) {
170
+ if (this.jsonlLogFd === null)
259
171
  return;
260
172
  try {
261
- fs.writeSync(this.readableLogFd, data);
173
+ // Add lane and task context to JSON log
174
+ const entry = {
175
+ ...msg,
176
+ lane: this.session.laneName,
177
+ task: this.session.taskName,
178
+ laneIndex: this.session.laneIndex,
179
+ taskIndex: this.session.taskIndex,
180
+ timestamp_iso: new Date(msg.timestamp).toISOString(),
181
+ };
182
+ fs.writeSync(this.jsonlLogFd, JSON.stringify(entry) + '\n');
262
183
  }
263
184
  catch {
264
185
  // Ignore write errors
265
186
  }
266
187
  }
267
188
  /**
268
- * Write a parsed message to the readable log using formatMessageForConsole style
189
+ * Write a parsed message to the JSON log and console
269
190
  */
270
191
  writeReadableMessage(msg) {
271
- // Use formatMessageForConsole for consistent formatting
272
- // Use short lane-task label like [1-1-lanename10]
273
- const formatted = (0, log_formatter_1.formatMessageForConsole)(msg, {
274
- laneLabel: `[${this.getLaneTaskLabel()}]`,
275
- includeTimestamp: false, // We'll add our own short timestamp
276
- });
277
- // Strip ANSI codes and add short timestamp for file output
278
- const clean = stripAnsi(formatted);
279
- const ts = this.getShortTime();
280
- this.writeToReadableLog(`[${ts}] ${clean}\n`);
192
+ // Write to JSON log
193
+ if (this.config.writeJsonLog) {
194
+ this.writeToJsonLog(msg);
195
+ }
281
196
  // Callback for console output
282
197
  if (this.onParsedMessage) {
283
198
  this.onParsedMessage(msg);
@@ -288,9 +203,6 @@ class EnhancedLogManager {
288
203
  */
289
204
  writeStdout(data) {
290
205
  const text = data.toString();
291
- // Write raw log (original data)
292
- this.writeToRawLog(data);
293
- // Parse JSON output and write to readable log
294
206
  const lines = text.split('\n');
295
207
  for (const line of lines) {
296
208
  const trimmed = line.trim();
@@ -307,6 +219,11 @@ class EnhancedLogManager {
307
219
  }
308
220
  // parseJsonToMessage returned null - create fallback message for known JSON
309
221
  if (json.type) {
222
+ // Skip noisy events that we don't want to show
223
+ if (json.type === 'thinking' || json.type === 'call' ||
224
+ (json.type === 'tool_call' && !json.tool_call)) {
225
+ continue;
226
+ }
310
227
  const fallbackMsg = {
311
228
  type: 'info',
312
229
  role: 'system',
@@ -321,41 +238,29 @@ class EnhancedLogManager {
321
238
  // Not valid JSON, fall through
322
239
  }
323
240
  }
324
- // Non-JSON line - write as-is with short timestamp and lane-task label
325
- const cleanLine = stripAnsi(trimmed);
241
+ // Non-JSON line
242
+ const cleanLine = (0, formatter_1.stripAnsi)(trimmed);
326
243
  if (cleanLine && !this.isNoiseLog(cleanLine)) {
327
- const hasTimestamp = /^\[(\d{4}-\d{2}-\d{2}T|\d{2}:\d{2}:\d{2})\]/.test(cleanLine);
328
- const label = this.getLaneTaskLabel();
329
- const ts = this.getShortTime();
330
- // For file output, use clean line (no ANSI codes)
331
- let fileFormattedLine;
332
- if (hasTimestamp) {
333
- fileFormattedLine = cleanLine.includes(`[${label}]`)
334
- ? cleanLine
335
- : cleanLine.replace(/^(\[[^\]]+\])/, `$1 [${label}]`);
336
- }
337
- else {
338
- fileFormattedLine = `[${ts}] [${label}] ${cleanLine}`;
339
- }
340
- this.writeToReadableLog(`${fileFormattedLine}\n`);
341
- // For console output, preserve ANSI colors from original line
244
+ // Detect if this looks like Git output
245
+ const isGit = this.isGitOutput(cleanLine);
246
+ const type = isGit ? 'git' : 'stdout';
247
+ // Output to console
342
248
  if (this.onParsedMessage) {
343
- let consoleFormattedLine;
344
- if (hasTimestamp) {
345
- consoleFormattedLine = trimmed.includes(`[${label}]`)
346
- ? trimmed
347
- : trimmed.replace(/^(\[[^\]]+\])/, `$1 [${label}]`);
348
- }
349
- else {
350
- consoleFormattedLine = `[${ts}] [${label}] ${trimmed}`;
351
- }
352
- const rawMsg = {
353
- type: 'raw',
249
+ this.onParsedMessage({
250
+ type,
354
251
  role: 'system',
355
- content: consoleFormattedLine,
252
+ content: trimmed,
356
253
  timestamp: Date.now(),
357
- };
358
- this.onParsedMessage(rawMsg);
254
+ });
255
+ }
256
+ // Write to JSON log
257
+ if (this.config.writeJsonLog) {
258
+ this.writeToJsonLog({
259
+ type,
260
+ role: 'system',
261
+ content: cleanLine,
262
+ timestamp: Date.now(),
263
+ });
359
264
  }
360
265
  }
361
266
  }
@@ -367,13 +272,32 @@ class EnhancedLogManager {
367
272
  const type = json.type;
368
273
  const timestamp = json.timestamp_ms || Date.now();
369
274
  switch (type) {
275
+ case 'info':
276
+ case 'warn':
277
+ case 'error':
278
+ case 'success':
279
+ case 'debug':
280
+ case 'progress':
370
281
  case 'system':
371
- return {
372
- type: 'system',
373
- role: 'system',
374
- content: `Model: ${json.model || 'unknown'}, Mode: ${json.permissionMode || 'default'}`,
375
- timestamp,
376
- };
282
+ case 'git':
283
+ // Handle internal logger JSON or cursor-agent system events
284
+ if (json.content && typeof json.content === 'string') {
285
+ return {
286
+ type: type,
287
+ role: 'system',
288
+ content: json.content,
289
+ timestamp,
290
+ };
291
+ }
292
+ if (type === 'system') {
293
+ return {
294
+ type: 'system',
295
+ role: 'system',
296
+ content: `Model: ${json.model || 'unknown'}, Mode: ${json.permissionMode || 'default'}`,
297
+ timestamp,
298
+ };
299
+ }
300
+ return null;
377
301
  case 'user':
378
302
  if (json.message?.content) {
379
303
  const textContent = json.message.content
@@ -464,45 +388,68 @@ class EnhancedLogManager {
464
388
  */
465
389
  writeStderr(data) {
466
390
  const text = data.toString();
467
- // Write raw log
468
- this.writeToRawLog(data);
469
- // Write to readable log - treat stderr same as stdout
470
- // Git and many tools use stderr for non-error output
471
391
  const lines = text.split('\n');
472
392
  for (const line of lines) {
473
- const cleanLine = stripAnsi(line).trim();
393
+ const cleanLine = (0, formatter_1.stripAnsi)(line).trim();
474
394
  if (cleanLine && !this.isNoiseLog(cleanLine)) {
475
- const hasTimestamp = /^\[(\d{4}-\d{2}-\d{2}T|\d{2}:\d{2}:\d{2})\]/.test(cleanLine);
476
- const label = this.getLaneTaskLabel();
477
- const ts = this.getShortTime();
478
- // Determine if this is actually an error message
479
395
  const isError = this.isErrorLine(cleanLine);
480
- const prefix = isError ? '❌ ERR' : '';
481
- let formattedLine;
482
- if (hasTimestamp) {
483
- formattedLine = cleanLine.includes(`[${label}]`)
484
- ? cleanLine
485
- : cleanLine.replace(/^(\[[^\]]+\])/, `$1 [${label}]${prefix ? ' ' + prefix : ''}`);
486
- }
487
- else {
488
- formattedLine = prefix
489
- ? `[${ts}] [${label}] ${prefix} ${cleanLine}`
490
- : `[${ts}] [${label}] ${cleanLine}`;
396
+ const isGit = this.isGitOutput(cleanLine);
397
+ const type = isError ? 'stderr' : (isGit ? 'git' : 'stderr');
398
+ // Write to JSON log
399
+ if (this.config.writeJsonLog) {
400
+ this.writeToJsonLog({
401
+ type,
402
+ role: 'system',
403
+ content: cleanLine,
404
+ timestamp: Date.now(),
405
+ metadata: { isError, isGit }
406
+ });
491
407
  }
492
- this.writeToReadableLog(`${formattedLine}\n`);
493
408
  // Output to console
494
409
  if (this.onParsedMessage) {
495
- const rawMsg = {
496
- type: 'raw',
410
+ this.onParsedMessage({
411
+ type,
497
412
  role: 'system',
498
- content: formattedLine,
413
+ content: line.trim(),
499
414
  timestamp: Date.now(),
500
- };
501
- this.onParsedMessage(rawMsg);
415
+ });
502
416
  }
503
417
  }
504
418
  }
505
419
  }
420
+ /**
421
+ * Check if a line is likely Git output
422
+ */
423
+ isGitOutput(text) {
424
+ const gitPatterns = [
425
+ /^Running: git /i,
426
+ /^git version /i,
427
+ /^On branch /i,
428
+ /^Your branch is /i,
429
+ /^Changes to be committed:/i,
430
+ /^Changes not staged for commit:/i,
431
+ /^Untracked files:/i,
432
+ /^\s*\((use "git |use --|use -)/i,
433
+ /^\s*modified:\s+/i,
434
+ /^\s*new file:\s+/i,
435
+ /^\s*deleted:\s+/i,
436
+ /^\s*renamed:\s+/i,
437
+ /^Switched to branch /i,
438
+ /^Switched to a new branch /i,
439
+ /^Merge made by the /i,
440
+ /^Everything up-to-date/i,
441
+ /^Already up to date/i,
442
+ /^branch '.*' set up to track remote branch '.*' from 'origin'/i,
443
+ /^Counting objects: /i,
444
+ /^Compressing objects: /i,
445
+ /^Writing objects: /i,
446
+ /^Total \d+ \(delta \d+\)/i,
447
+ /^remote: Resolving deltas:/i,
448
+ /^To .*\.git/i,
449
+ /^[a-f0-9]{7,40}\.\.[a-f0-9]{7,40}\s+.*->\s+.*/i,
450
+ ];
451
+ return gitPatterns.some(p => p.test(text));
452
+ }
506
453
  /**
507
454
  * Check if a line is actually an error message
508
455
  */
@@ -526,21 +473,40 @@ class EnhancedLogManager {
526
473
  * Write a custom log entry
527
474
  */
528
475
  log(level, message, metadata) {
529
- const ts = this.getShortTime();
530
- const label = this.getLaneTaskLabel();
531
- const emoji = level === 'error' ? '❌' : level === 'info' ? 'ℹ️' : '🔍';
532
- const line = `[${ts}] [${label}] ${emoji} ${level.toUpperCase()} ${message}\n`;
533
- this.writeToRawLog(line);
534
- this.writeToReadableLog(line);
476
+ const isGit = metadata?.context === 'git';
477
+ const type = isGit ? 'git' : level;
478
+ if (this.config.writeJsonLog) {
479
+ this.writeToJsonLog({
480
+ type,
481
+ role: 'system',
482
+ content: message,
483
+ timestamp: Date.now(),
484
+ metadata
485
+ });
486
+ }
487
+ if (this.onParsedMessage) {
488
+ this.onParsedMessage({
489
+ type,
490
+ role: 'system',
491
+ content: message,
492
+ timestamp: Date.now(),
493
+ metadata
494
+ });
495
+ }
535
496
  }
536
497
  /**
537
498
  * Add a section marker
538
499
  */
539
500
  section(title) {
540
- const divider = '═'.repeat(78);
541
- const line = `\n${divider}\n ${title}\n${divider}\n`;
542
- this.writeToRawLog(line);
543
- this.writeToReadableLog(line);
501
+ if (this.config.writeJsonLog) {
502
+ this.writeToJsonLog({
503
+ type: 'info',
504
+ role: 'system',
505
+ content: `--- ${title} ---`,
506
+ timestamp: Date.now(),
507
+ metadata: { isSection: true }
508
+ });
509
+ }
544
510
  }
545
511
  /**
546
512
  * Update task context
@@ -575,47 +541,39 @@ class EnhancedLogManager {
575
541
  */
576
542
  getLogPaths() {
577
543
  return {
578
- clean: this.readableLogPath, // For backward compatibility
579
- raw: this.rawLogPath,
580
- readable: this.readableLogPath,
544
+ jsonl: this.jsonlLogPath,
545
+ clean: this.jsonlLogPath, // Backward compatibility
581
546
  };
582
547
  }
583
548
  /**
584
549
  * Create file descriptors for process stdio redirection
585
550
  */
586
551
  getFileDescriptors() {
587
- const fd = this.rawLogFd;
552
+ const fd = this.jsonlLogFd;
588
553
  return { stdout: fd, stderr: fd };
589
554
  }
590
555
  /**
591
556
  * Close all log files
592
557
  */
593
558
  close() {
594
- // Write session end marker
595
- const endMarker = `
596
- ╔══════════════════════════════════════════════════════════════════════════════╗
597
- ║ Session Ended: ${new Date().toISOString().padEnd(60)}║
598
- ║ Duration: ${this.formatDuration(Date.now() - this.session.startTime).padEnd(65)}║
599
- ╚══════════════════════════════════════════════════════════════════════════════╝
600
-
601
- `;
602
- if (this.rawLogFd !== null) {
603
- try {
604
- fs.writeSync(this.rawLogFd, endMarker);
605
- fs.fsyncSync(this.rawLogFd);
606
- fs.closeSync(this.rawLogFd);
607
- }
608
- catch { }
609
- this.rawLogFd = null;
610
- }
611
- if (this.readableLogFd !== null) {
559
+ if (this.jsonlLogFd !== null) {
612
560
  try {
613
- fs.writeSync(this.readableLogFd, endMarker);
614
- fs.fsyncSync(this.readableLogFd);
615
- fs.closeSync(this.readableLogFd);
561
+ if (this.config.writeJsonLog) {
562
+ this.writeToJsonLog({
563
+ type: 'session',
564
+ role: 'system',
565
+ content: 'Session ended',
566
+ timestamp: Date.now(),
567
+ metadata: {
568
+ duration: Date.now() - this.session.startTime,
569
+ }
570
+ });
571
+ }
572
+ fs.fsyncSync(this.jsonlLogFd);
573
+ fs.closeSync(this.jsonlLogFd);
616
574
  }
617
575
  catch { }
618
- this.readableLogFd = null;
576
+ this.jsonlLogFd = null;
619
577
  }
620
578
  }
621
579
  /**
@@ -623,39 +581,25 @@ class EnhancedLogManager {
623
581
  */
624
582
  getLastError() {
625
583
  try {
626
- if (!fs.existsSync(this.readableLogPath))
584
+ if (!fs.existsSync(this.jsonlLogPath))
627
585
  return null;
628
- const content = fs.readFileSync(this.readableLogPath, 'utf8');
629
- const lines = content.split('\n').filter(l => l.includes('❌') ||
630
- l.includes('[ERROR]') ||
631
- l.includes('error:') ||
632
- l.includes('Fatal') ||
633
- l.includes('fail'));
634
- if (lines.length === 0) {
635
- const allLines = content.split('\n').filter(l => l.trim());
636
- return allLines.slice(-5).join('\n');
586
+ const content = fs.readFileSync(this.jsonlLogPath, 'utf8');
587
+ const lines = content.split('\n').filter(l => l.trim());
588
+ for (let i = lines.length - 1; i >= 0; i--) {
589
+ try {
590
+ const entry = JSON.parse(lines[i]);
591
+ if (entry.type === 'error' || entry.type === 'stderr' && entry.metadata?.isError) {
592
+ return entry.content;
593
+ }
594
+ }
595
+ catch { }
637
596
  }
638
- return lines[lines.length - 1].trim();
597
+ return null;
639
598
  }
640
599
  catch {
641
600
  return null;
642
601
  }
643
602
  }
644
- /**
645
- * Format duration for display
646
- */
647
- formatDuration(ms) {
648
- const seconds = Math.floor((ms / 1000) % 60);
649
- const minutes = Math.floor((ms / (1000 * 60)) % 60);
650
- const hours = Math.floor(ms / (1000 * 60 * 60));
651
- if (hours > 0) {
652
- return `${hours}h ${minutes}m ${seconds}s`;
653
- }
654
- else if (minutes > 0) {
655
- return `${minutes}m ${seconds}s`;
656
- }
657
- return `${seconds}s`;
658
- }
659
603
  }
660
604
  exports.EnhancedLogManager = EnhancedLogManager;
661
605
  /**
@@ -672,19 +616,45 @@ function createLogManager(laneRunDir, laneName, config, onParsedMessage, laneInd
672
616
  return new EnhancedLogManager(laneRunDir, session, config, onParsedMessage);
673
617
  }
674
618
  /**
675
- * Read and parse JSON log file (legacy compatibility - returns empty array)
619
+ * Read and parse JSON log file
676
620
  */
677
621
  function readJsonLog(logPath) {
678
- return [];
622
+ try {
623
+ if (!fs.existsSync(logPath))
624
+ return [];
625
+ const content = fs.readFileSync(logPath, 'utf8');
626
+ return content.split('\n')
627
+ .filter(l => l.trim())
628
+ .map(l => {
629
+ try {
630
+ return JSON.parse(l);
631
+ }
632
+ catch {
633
+ return null;
634
+ }
635
+ })
636
+ .filter(Boolean);
637
+ }
638
+ catch {
639
+ return [];
640
+ }
679
641
  }
680
642
  /**
681
- * Export logs (legacy compatibility)
643
+ * Export logs
682
644
  */
683
645
  function exportLogs(laneRunDir, format, outputPath) {
684
- const logPath = (0, paths_1.getLaneLogPath)(laneRunDir, 'raw');
646
+ const logPath = (0, paths_1.getLaneLogPath)(laneRunDir, 'jsonl');
685
647
  let output = '';
686
648
  if (fs.existsSync(logPath)) {
687
- output = fs.readFileSync(logPath, 'utf8');
649
+ if (format === 'json') {
650
+ const logs = readJsonLog(logPath);
651
+ output = JSON.stringify(logs, null, 2);
652
+ }
653
+ else {
654
+ // Basic text conversion for other formats
655
+ const logs = readJsonLog(logPath);
656
+ output = logs.map(l => `[${l.timestamp_iso}] [${l.type}] ${l.content}`).join('\n');
657
+ }
688
658
  }
689
659
  if (outputPath) {
690
660
  fs.writeFileSync(outputPath, output, 'utf8');