@litmers/cursorflow-orchestrator 0.2.3 → 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 (67) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +7 -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/logs.js +51 -61
  7. package/dist/cli/logs.js.map +1 -1
  8. package/dist/cli/monitor.js +56 -44
  9. package/dist/cli/monitor.js.map +1 -1
  10. package/dist/cli/resume.js +2 -2
  11. package/dist/cli/resume.js.map +1 -1
  12. package/dist/core/git-lifecycle-manager.js +2 -2
  13. package/dist/core/git-lifecycle-manager.js.map +1 -1
  14. package/dist/core/git-pipeline-coordinator.js +25 -25
  15. package/dist/core/git-pipeline-coordinator.js.map +1 -1
  16. package/dist/core/orchestrator.js +8 -7
  17. package/dist/core/orchestrator.js.map +1 -1
  18. package/dist/core/runner/pipeline.js +3 -3
  19. package/dist/core/runner/pipeline.js.map +1 -1
  20. package/dist/hooks/data-accessor.js +2 -2
  21. package/dist/hooks/data-accessor.js.map +1 -1
  22. package/dist/services/logging/buffer.d.ts +1 -2
  23. package/dist/services/logging/buffer.js +22 -63
  24. package/dist/services/logging/buffer.js.map +1 -1
  25. package/dist/services/logging/formatter.d.ts +4 -0
  26. package/dist/services/logging/formatter.js +201 -33
  27. package/dist/services/logging/formatter.js.map +1 -1
  28. package/dist/services/logging/paths.d.ts +0 -3
  29. package/dist/services/logging/paths.js +0 -3
  30. package/dist/services/logging/paths.js.map +1 -1
  31. package/dist/types/config.d.ts +1 -9
  32. package/dist/types/logging.d.ts +1 -1
  33. package/dist/utils/config.js +2 -6
  34. package/dist/utils/config.js.map +1 -1
  35. package/dist/utils/enhanced-logger.d.ts +17 -37
  36. package/dist/utils/enhanced-logger.js +237 -267
  37. package/dist/utils/enhanced-logger.js.map +1 -1
  38. package/dist/utils/logger.js +17 -4
  39. package/dist/utils/logger.js.map +1 -1
  40. package/dist/utils/repro-thinking-logs.js +4 -4
  41. package/dist/utils/repro-thinking-logs.js.map +1 -1
  42. package/package.json +2 -2
  43. package/scripts/monitor-lanes.sh +5 -5
  44. package/scripts/stream-logs.sh +1 -1
  45. package/scripts/test-log-parser.ts +8 -42
  46. package/src/cli/complete.ts +305 -0
  47. package/src/cli/logs.ts +46 -60
  48. package/src/cli/monitor.ts +64 -46
  49. package/src/cli/resume.ts +1 -1
  50. package/src/core/git-lifecycle-manager.ts +2 -2
  51. package/src/core/git-pipeline-coordinator.ts +25 -25
  52. package/src/core/orchestrator.ts +7 -7
  53. package/src/core/runner/pipeline.ts +3 -3
  54. package/src/hooks/data-accessor.ts +2 -2
  55. package/src/services/logging/buffer.ts +20 -68
  56. package/src/services/logging/formatter.ts +199 -32
  57. package/src/services/logging/paths.ts +0 -3
  58. package/src/types/config.ts +1 -13
  59. package/src/types/logging.ts +2 -0
  60. package/src/utils/config.ts +2 -6
  61. package/src/utils/enhanced-logger.ts +239 -290
  62. package/src/utils/logger.ts +18 -3
  63. package/src/utils/repro-thinking-logs.ts +4 -4
  64. package/dist/utils/log-formatter.d.ts +0 -26
  65. package/dist/utils/log-formatter.js +0 -274
  66. package/dist/utils/log-formatter.js.map +0 -1
  67. package/src/utils/log-formatter.ts +0 -287
package/CHANGELOG.md CHANGED
@@ -5,6 +5,20 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.2.4] - 2025-12-28
9
+
10
+ ### Added
11
+ - **Logging**: Introduced tool-specific labels (READ, EDIT, SHLL, etc.) and emojis for better visibility.
12
+ - **Logging**: Added path simplification (absolute path to relative `./`) in tool execution logs.
13
+
14
+ ### Improved
15
+ - **Logging**: Enhanced log color consistency by dimming unimportant system and info logs.
16
+ - **Logging**: Improved multi-line thinking message formatting with proper indentation.
17
+
18
+ ### Fixed
19
+ - **Logging**: Fixed box alignment issues for USER and SYS messages by accurately calculating terminal visual width for emojis.
20
+ - **Logging**: Filtered out redundant or empty agent events from readable logs.
21
+
8
22
  ## [0.1.37] - 2025-12-26
9
23
 
10
24
  ### Fixed
package/README.md CHANGED
@@ -313,13 +313,11 @@ Logs use the format `[{n}-{t}-{lanename}]`:
313
313
  Example: `[1-2-backend]` = Lane 1, Task 2, lane "backend"
314
314
 
315
315
  ### Features
316
- - **ANSI Stripping**: Clean logs without terminal escape codes
317
- - **Timestamps**: Automatic timestamps on each line (ISO, relative, or short format)
316
+ - **ANSI Stripping**: Logs content without terminal escape codes
317
+ - **Timestamps**: Automatic timestamps on each line (ISO format)
318
318
  - **Log Rotation**: Automatic rotation when files exceed size limits
319
- - **Multiple Formats**:
320
- - `terminal.log` - Clean, readable logs
321
- - `terminal-raw.log` - Raw logs with ANSI codes
322
- - `terminal.jsonl` - Structured JSON for programmatic access
319
+ - **Unified Format**:
320
+ - `terminal.jsonl` - Structured JSON for programmatic access and human-readable display via `cursorflow logs` or `monitor`.
323
321
 
324
322
  ### Usage
325
323
 
@@ -327,7 +325,7 @@ Example: `[1-2-backend]` = Lane 1, Task 2, lane "backend"
327
325
  # View logs summary for latest run
328
326
  cursorflow logs
329
327
 
330
- # View specific lane logs
328
+ # View specific lane logs (automatically formatted from JSONL)
331
329
  cursorflow logs --lane api-setup
332
330
 
333
331
  # View ALL lanes merged (unified timeline)
@@ -377,12 +375,10 @@ module.exports = {
377
375
  // ... other config ...
378
376
  enhancedLogging: {
379
377
  enabled: true, // Enable enhanced logging
380
- stripAnsi: true, // Strip ANSI codes for clean logs
381
- addTimestamps: true, // Add timestamps to each line
378
+ stripAnsi: true, // Strip ANSI codes
382
379
  maxFileSize: 52428800, // 50MB max before rotation
383
380
  maxFiles: 5, // Keep 5 rotated files
384
- keepRawLogs: true, // Keep raw logs separately
385
- writeJsonLog: true, // Generate JSON logs
381
+ writeJsonLog: true, // Generate JSON logs (main format)
386
382
  timestampFormat: 'iso', // 'iso' | 'relative' | 'short'
387
383
  },
388
384
  };
@@ -0,0 +1,7 @@
1
+ /**
2
+ * CursorFlow 'complete' command
3
+ *
4
+ * Consolidates all lanes of a flow into a single branch and cleans up.
5
+ */
6
+ declare function complete(args: string[]): Promise<void>;
7
+ export = complete;
@@ -0,0 +1,304 @@
1
+ "use strict";
2
+ /**
3
+ * CursorFlow 'complete' command
4
+ *
5
+ * Consolidates all lanes of a flow into a single branch and cleans up.
6
+ */
7
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
8
+ if (k2 === undefined) k2 = k;
9
+ var desc = Object.getOwnPropertyDescriptor(m, k);
10
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
11
+ desc = { enumerable: true, get: function() { return m[k]; } };
12
+ }
13
+ Object.defineProperty(o, k2, desc);
14
+ }) : (function(o, m, k, k2) {
15
+ if (k2 === undefined) k2 = k;
16
+ o[k2] = m[k];
17
+ }));
18
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
19
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
20
+ }) : function(o, v) {
21
+ o["default"] = v;
22
+ });
23
+ var __importStar = (this && this.__importStar) || (function () {
24
+ var ownKeys = function(o) {
25
+ ownKeys = Object.getOwnPropertyNames || function (o) {
26
+ var ar = [];
27
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
28
+ return ar;
29
+ };
30
+ return ownKeys(o);
31
+ };
32
+ return function (mod) {
33
+ if (mod && mod.__esModule) return mod;
34
+ var result = {};
35
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
36
+ __setModuleDefault(result, mod);
37
+ return result;
38
+ };
39
+ })();
40
+ const fs = __importStar(require("fs"));
41
+ const path = __importStar(require("path"));
42
+ const logger = __importStar(require("../utils/logger"));
43
+ const git = __importStar(require("../utils/git"));
44
+ const config_1 = require("../utils/config");
45
+ const path_1 = require("../utils/path");
46
+ const run_service_1 = require("../utils/run-service");
47
+ const flow_1 = require("../utils/flow");
48
+ const process_1 = require("../services/process");
49
+ function printHelp() {
50
+ console.log(`
51
+ \x1b[1mcursorflow complete\x1b[0m - Flow 통합 및 마무리
52
+
53
+ \x1b[1m사용법:\x1b[0m
54
+ cursorflow complete <flow-dir> [options]
55
+
56
+ \x1b[1m설명:\x1b[0m
57
+ 모든 레인의 작업이 완료된 후, 각 레인의 브랜치들을 하나의 통합 브랜치로 병합합니다.
58
+ 병합이 성공하면 임시로 사용된 레인 브랜치들과 워크트리들을 삭제합니다.
59
+
60
+ \x1b[33m참고:\x1b[0m Flow가 성공적으로 완료되면 자동으로 통합됩니다.
61
+ 이 명령어는 자동 통합이 실패했을 때 수동으로 복구하거나,
62
+ 특정 옵션으로 통합하고 싶을 때 사용합니다.
63
+
64
+ \x1b[1m옵션:\x1b[0m
65
+ --branch <name> 통합 브랜치 이름 지정 (기본값: feature/<FlowName>-integrated)
66
+ --force 완료되지 않은 레인이 있어도 강제로 진행
67
+ --dry-run 실제로 병합하거나 삭제하지 않고 계획만 출력
68
+ --no-cleanup 병합 후 브랜치 및 워크트리를 삭제하지 않음
69
+ --help, -h 도움말 출력
70
+
71
+ \x1b[1m예시:\x1b[0m
72
+ # 자동 통합 실패 후 수동 복구
73
+ cursorflow complete ShopFeature
74
+
75
+ # 특정 브랜치 이름으로 통합
76
+ cursorflow complete ShopFeature --branch feat/shop-v1
77
+
78
+ # 정리 없이 통합만 수행
79
+ cursorflow complete ShopFeature --no-cleanup
80
+ `);
81
+ }
82
+ function parseArgs(args) {
83
+ const branchIdx = args.indexOf('--branch');
84
+ return {
85
+ flowDir: args.find(a => !a.startsWith('--')),
86
+ targetBranch: branchIdx >= 0 ? args[branchIdx + 1] : undefined,
87
+ force: args.includes('--force'),
88
+ help: args.includes('--help') || args.includes('-h'),
89
+ dryRun: args.includes('--dry-run'),
90
+ noCleanup: args.includes('--no-cleanup'),
91
+ };
92
+ }
93
+ async function complete(args) {
94
+ const options = parseArgs(args);
95
+ if (options.help) {
96
+ printHelp();
97
+ return;
98
+ }
99
+ const config = (0, config_1.loadConfig)();
100
+ git.setVerboseGit(config.verboseGit || false);
101
+ const repoRoot = git.getRepoRoot();
102
+ const logsDir = (0, config_1.getLogsDir)(config);
103
+ const runService = (0, run_service_1.createRunService)(config.projectRoot);
104
+ // 1. Find Flow Directory
105
+ let flowDir = '';
106
+ if (!options.flowDir) {
107
+ flowDir = (0, flow_1.findLatestFlowOrTask)(config) || '';
108
+ }
109
+ else {
110
+ const flowsDir = (0, path_1.safeJoin)(config.projectRoot, config.flowsDir);
111
+ flowDir = (0, flow_1.findFlowDir)(flowsDir, options.flowDir) || path.resolve(options.flowDir);
112
+ }
113
+ if (!flowDir || !fs.existsSync(flowDir)) {
114
+ throw new Error(`Flow 디렉토리를 찾을 수 없습니다: ${options.flowDir || 'latest'}`);
115
+ }
116
+ logger.section(`🏁 Completing Flow: ${path.basename(flowDir)}`);
117
+ // 2. Find Latest Run
118
+ const runs = runService.listRuns().filter(run => {
119
+ // Check if this run belongs to the target flowDir
120
+ // We look at the first lane's state to check tasksFile
121
+ if (run.lanes.length === 0)
122
+ return false;
123
+ const laneDir = (0, path_1.safeJoin)(run.path, 'lanes', run.lanes[0].name);
124
+ const statePath = (0, path_1.safeJoin)(laneDir, 'state.json');
125
+ if (!fs.existsSync(statePath))
126
+ return false;
127
+ try {
128
+ const state = JSON.parse(fs.readFileSync(statePath, 'utf8'));
129
+ return state.tasksFile && path.resolve(path.dirname(state.tasksFile)) === path.resolve(flowDir);
130
+ }
131
+ catch {
132
+ return false;
133
+ }
134
+ });
135
+ if (runs.length === 0) {
136
+ throw new Error(`이 Flow에 대한 실행 기록(Run)을 찾을 수 없습니다.`);
137
+ }
138
+ const latestRun = runs[0];
139
+ logger.info(`Run ID: ${latestRun.id}`);
140
+ // 3. Verify Status
141
+ const summary = (0, process_1.getFlowSummary)(latestRun.path);
142
+ logger.info(`Status: ${summary.completed}/${summary.total} lanes completed`);
143
+ if (summary.completed < summary.total && !options.force) {
144
+ logger.error(`모든 레인이 완료되지 않았습니다 (${summary.completed}/${summary.total}).`);
145
+ logger.info('완료되지 않은 레인:');
146
+ const statuses = (0, process_1.getAllLaneProcessStatuses)(latestRun.path);
147
+ for (const s of statuses) {
148
+ if (s.actualStatus !== 'completed') {
149
+ logger.info(` - ${s.laneName}: ${s.actualStatus}`);
150
+ }
151
+ }
152
+ logger.info('\n강제로 통합하려면 --force 옵션을 사용하세요.');
153
+ process.exit(1);
154
+ }
155
+ // 4. Determine Target Branch Name
156
+ const flowName = path.basename(flowDir).replace(/^\d+_/, '');
157
+ const targetBranch = options.targetBranch || `feature/${flowName}-integrated`;
158
+ // 5. Get Base Branch from flow.meta.json
159
+ let baseBranch = 'main';
160
+ const metaPath = (0, path_1.safeJoin)(flowDir, 'flow.meta.json');
161
+ if (fs.existsSync(metaPath)) {
162
+ try {
163
+ const meta = JSON.parse(fs.readFileSync(metaPath, 'utf8'));
164
+ baseBranch = meta.baseBranch || 'main';
165
+ }
166
+ catch (e) {
167
+ logger.warn(`flow.meta.json 읽기 실패, 기본값 main 사용: ${e}`);
168
+ }
169
+ }
170
+ logger.info(`Target Branch: ${targetBranch}`);
171
+ logger.info(`Base Branch: ${baseBranch}`);
172
+ const laneBranches = latestRun.lanes
173
+ .filter(l => l.pipelineBranch)
174
+ .map(l => l.pipelineBranch);
175
+ if (laneBranches.length === 0) {
176
+ throw new Error(`통합할 브랜치가 없습니다.`);
177
+ }
178
+ logger.info(`Lanes to merge: ${laneBranches.length}`);
179
+ for (const b of laneBranches) {
180
+ logger.info(` - ${b}`);
181
+ }
182
+ if (options.dryRun) {
183
+ logger.section('🧪 Dry Run: No changes will be made');
184
+ logger.info(`Would create branch '${targetBranch}' from '${baseBranch}'`);
185
+ for (const b of laneBranches) {
186
+ logger.info(`Would merge '${b}' into '${targetBranch}'`);
187
+ }
188
+ if (!options.noCleanup) {
189
+ logger.info(`Would delete ${laneBranches.length} lane branches and ${latestRun.worktrees.length} worktrees`);
190
+ }
191
+ return;
192
+ }
193
+ // 6. Perform Merges
194
+ try {
195
+ // Ensure we are on a clean state in the main repo
196
+ const currentBranch = git.getCurrentBranch(repoRoot);
197
+ if (git.hasUncommittedChanges(repoRoot)) {
198
+ throw new Error('메인 저장소에 커밋되지 않은 변경사항이 있습니다. 커밋하거나 스태시한 후 다시 시도하세요.');
199
+ }
200
+ // Checkout base branch and create target branch
201
+ logger.info(`Creating target branch '${targetBranch}' from '${baseBranch}'...`);
202
+ git.runGit(['checkout', baseBranch], { cwd: repoRoot });
203
+ git.runGit(['checkout', '-B', targetBranch], { cwd: repoRoot });
204
+ // Merge each lane branch
205
+ for (const branch of laneBranches) {
206
+ logger.info(`Merging ${branch}...`);
207
+ // Fetch remote branch if needed
208
+ if (!git.branchExists(branch, { cwd: repoRoot })) {
209
+ logger.info(` Fetching ${branch} from remote...`);
210
+ try {
211
+ git.runGit(['fetch', 'origin', branch], { cwd: repoRoot });
212
+ }
213
+ catch (e) {
214
+ logger.warn(` Failed to fetch ${branch}: ${e}`);
215
+ }
216
+ }
217
+ const branchRef = git.branchExists(branch, { cwd: repoRoot }) ? branch : `origin/${branch}`;
218
+ const mergeResult = git.safeMerge(branchRef, {
219
+ cwd: repoRoot,
220
+ noFf: true,
221
+ message: `chore: merge lane ${branch} into flow integration`,
222
+ abortOnConflict: false, // We'll handle it below
223
+ });
224
+ if (!mergeResult.success) {
225
+ if (mergeResult.conflict) {
226
+ logger.error(`❌ '${branch}' 병합 중 충돌이 발생했습니다.`);
227
+ logger.error(`충돌 파일: ${mergeResult.conflictingFiles.join(', ')}`);
228
+ logger.info('\n충돌을 해결한 후 다시 시도하거나, 수동으로 마무리하세요.');
229
+ // Don't abort here, let the user see the state or suggest cleanup
230
+ throw new Error('Merge conflict occurred during integration.');
231
+ }
232
+ else {
233
+ throw new Error(`병합 실패 (${branch}): ${mergeResult.error}`);
234
+ }
235
+ }
236
+ logger.success(`✓ Merged ${branch}`);
237
+ }
238
+ // 7. Push final branch
239
+ logger.info(`Pushing '${targetBranch}' to remote...`);
240
+ git.push(targetBranch, { cwd: repoRoot, setUpstream: true });
241
+ logger.success(`✓ Pushed ${targetBranch}`);
242
+ // 8. Cleanup
243
+ if (!options.noCleanup) {
244
+ logger.section('🧹 Cleaning Up');
245
+ // Delete local and remote lane branches
246
+ for (const branch of laneBranches) {
247
+ logger.info(`Deleting branch: ${branch}`);
248
+ try {
249
+ git.deleteBranch(branch, { cwd: repoRoot, force: true });
250
+ // Try deleting remote branch too
251
+ try {
252
+ git.deleteBranch(branch, { cwd: repoRoot, remote: true });
253
+ }
254
+ catch {
255
+ // Might not exist on remote or no permission
256
+ }
257
+ }
258
+ catch (e) {
259
+ logger.warn(` Failed to delete branch ${branch}: ${e}`);
260
+ }
261
+ }
262
+ // Remove worktrees
263
+ for (const wtPath of latestRun.worktrees) {
264
+ if (fs.existsSync(wtPath)) {
265
+ logger.info(`Removing worktree: ${wtPath}`);
266
+ try {
267
+ git.removeWorktree(wtPath, { cwd: repoRoot, force: true });
268
+ if (fs.existsSync(wtPath)) {
269
+ fs.rmSync(wtPath, { recursive: true, force: true });
270
+ }
271
+ }
272
+ catch (e) {
273
+ logger.warn(` Failed to remove worktree ${wtPath}: ${e}`);
274
+ }
275
+ }
276
+ }
277
+ // Optional: Delete run directory? Maybe keep logs for a while.
278
+ // logger.info(`Deleting run directory: ${latestRun.path}`);
279
+ // fs.rmSync(latestRun.path, { recursive: true, force: true });
280
+ }
281
+ // 9. Update flow status in meta
282
+ if (fs.existsSync(metaPath)) {
283
+ try {
284
+ const meta = JSON.parse(fs.readFileSync(metaPath, 'utf8'));
285
+ meta.status = 'completed';
286
+ meta.integratedBranch = targetBranch;
287
+ meta.integratedAt = new Date().toISOString();
288
+ fs.writeFileSync(metaPath, JSON.stringify(meta, null, 2));
289
+ }
290
+ catch (e) {
291
+ logger.warn(`flow.meta.json 업데이트 실패: ${e}`);
292
+ }
293
+ }
294
+ logger.section(`🎉 Flow Completion Success!`);
295
+ logger.info(`Final integrated branch: ${targetBranch}`);
296
+ logger.info(`You are now on branch: ${targetBranch}`);
297
+ }
298
+ catch (error) {
299
+ logger.error(`\n❌ Flow completion failed: ${error.message}`);
300
+ process.exit(1);
301
+ }
302
+ }
303
+ module.exports = complete;
304
+ //# sourceMappingURL=complete.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"complete.js","sourceRoot":"","sources":["../../src/cli/complete.ts"],"names":[],"mappings":"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"}
package/dist/cli/logs.js CHANGED
@@ -41,7 +41,7 @@ const logger = __importStar(require("../utils/logger"));
41
41
  const config_1 = require("../utils/config");
42
42
  const path_1 = require("../utils/path");
43
43
  const enhanced_logger_1 = require("../utils/enhanced-logger");
44
- const log_formatter_1 = require("../utils/log-formatter");
44
+ const formatter_1 = require("../services/logging/formatter");
45
45
  const log_constants_1 = require("../utils/log-constants");
46
46
  const log_viewer_1 = require("../ui/log-viewer");
47
47
  /**
@@ -154,26 +154,26 @@ function listLanes(runDir) {
154
154
  .filter(d => fs.statSync((0, path_1.safeJoin)(lanesDir, d)).isDirectory());
155
155
  }
156
156
  /**
157
- * Read and display text logs
157
+ * Read and display text logs (converted from JSONL)
158
158
  */
159
159
  function displayTextLogs(laneDir, options) {
160
- let logFile;
161
- const readableLog = (0, path_1.safeJoin)(laneDir, 'terminal-readable.log');
162
- const rawLog = (0, path_1.safeJoin)(laneDir, 'terminal-raw.log');
163
- if (options.raw) {
164
- logFile = rawLog;
165
- }
166
- else {
167
- // Default to readable log (clean option also uses readable now)
168
- logFile = readableLog;
169
- }
160
+ const logFile = (0, path_1.safeJoin)(laneDir, 'terminal.jsonl');
170
161
  if (!fs.existsSync(logFile)) {
171
162
  console.log('No log file found.');
172
163
  return;
173
164
  }
174
- let content = fs.readFileSync(logFile, 'utf8');
175
- let lines = content.split('\n');
176
- // Apply filter (case-insensitive string match to avoid ReDoS)
165
+ const entries = (0, enhanced_logger_1.readJsonLog)(logFile);
166
+ let lines = entries.map(entry => {
167
+ const ts = new Date(entry.timestamp).toLocaleTimeString('en-US', { hour12: false });
168
+ const level = entry.level || 'info';
169
+ const content = entry.content || entry.message || '';
170
+ if (options.raw) {
171
+ // In "raw" mode for JSONL, we show a basic text representation
172
+ return `[${ts}] [${level.toUpperCase()}] ${content}`;
173
+ }
174
+ return `[${ts}] [${level.toUpperCase()}] ${(0, formatter_1.stripAnsi)(content)}`;
175
+ });
176
+ // Apply filter
177
177
  if (options.filter) {
178
178
  const filterLower = options.filter.toLowerCase();
179
179
  lines = lines.filter(line => line.toLowerCase().includes(filterLower));
@@ -182,10 +182,6 @@ function displayTextLogs(laneDir, options) {
182
182
  if (options.tail && lines.length > options.tail) {
183
183
  lines = lines.slice(-options.tail);
184
184
  }
185
- // Clean ANSI if needed (for clean mode or default fallback)
186
- if (!options.raw) {
187
- lines = lines.map(line => (0, enhanced_logger_1.stripAnsi)(line));
188
- }
189
185
  console.log(lines.join('\n'));
190
186
  }
191
187
  /**
@@ -207,7 +203,7 @@ function displayMainLogs(runDir, options) {
207
203
  lines = lines.slice(-options.tail);
208
204
  }
209
205
  if (options.clean) {
210
- lines = lines.map(line => (0, enhanced_logger_1.stripAnsi)(line));
206
+ lines = lines.map(line => (0, formatter_1.stripAnsi)(line));
211
207
  }
212
208
  console.log(lines.join('\n'));
213
209
  }
@@ -245,7 +241,7 @@ function displayJsonLogs(laneDir, options) {
245
241
  const message = entry.message || '';
246
242
  const levelColor = getLevelColor(level);
247
243
  const ts = new Date(entry.timestamp).toLocaleTimeString();
248
- const formattedMsg = (0, log_formatter_1.formatPotentialJsonMessage)(message);
244
+ const formattedMsg = (0, formatter_1.formatPotentialJsonMessage)(message);
249
245
  console.log(`${levelColor}[${ts}] [${level.toUpperCase().padEnd(6)}]${logger.COLORS.reset} ${formattedMsg}`);
250
246
  }
251
247
  }
@@ -375,7 +371,7 @@ function displayMergedLogs(runDir, options) {
375
371
  console.log(`${logger.COLORS.gray}[${ts}]${logger.COLORS.reset} ${laneColor}[${lanePad}]${logger.COLORS.reset} ${logger.COLORS.cyan}── Session Ended ──${logger.COLORS.reset}`);
376
372
  continue;
377
373
  }
378
- const formattedMsg = (0, log_formatter_1.formatPotentialJsonMessage)(message);
374
+ const formattedMsg = (0, formatter_1.formatPotentialJsonMessage)(message);
379
375
  console.log(`${logger.COLORS.gray}[${ts}]${logger.COLORS.reset} ${laneColor}[${lanePad}]${logger.COLORS.reset} ${levelColor}[${levelPad}]${logger.COLORS.reset} ${formattedMsg}`);
380
376
  }
381
377
  console.log(`\n${logger.COLORS.cyan}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${logger.COLORS.reset}`);
@@ -483,7 +479,7 @@ function followAllLogs(runDir, options) {
483
479
  }
484
480
  continue;
485
481
  }
486
- const formattedMsg = (0, log_formatter_1.formatPotentialJsonMessage)(message);
482
+ const formattedMsg = (0, formatter_1.formatPotentialJsonMessage)(message);
487
483
  console.log(`${logger.COLORS.gray}[${ts}]${logger.COLORS.reset} ${entry.laneColor}[${lanePad}]${logger.COLORS.reset} ${levelColor}[${levelPad}]${logger.COLORS.reset} ${formattedMsg}`);
488
484
  }
489
485
  }, 100);
@@ -619,31 +615,19 @@ function escapeHtml(text) {
619
615
  * Follow logs in real-time
620
616
  */
621
617
  function followLogs(laneDir, options) {
622
- let logFile;
623
- const readableLog = (0, path_1.safeJoin)(laneDir, 'terminal-readable.log');
624
- const rawLog = (0, path_1.safeJoin)(laneDir, 'terminal-raw.log');
625
- if (options.raw) {
626
- logFile = rawLog;
627
- }
628
- else {
629
- // Default to readable log
630
- logFile = readableLog;
631
- }
618
+ const logFile = (0, path_1.safeJoin)(laneDir, 'terminal.jsonl');
632
619
  if (!fs.existsSync(logFile)) {
633
620
  console.log('Waiting for log file...');
634
621
  }
635
622
  let lastSize = 0;
636
623
  try {
637
- // Use statSync directly to avoid TOCTOU race condition
638
624
  lastSize = fs.statSync(logFile).size;
639
625
  }
640
626
  catch {
641
- // File doesn't exist yet or other error - start from 0
642
627
  lastSize = 0;
643
628
  }
644
629
  console.log(`${logger.COLORS.cyan}Following ${logFile}... (Ctrl+C to stop)${logger.COLORS.reset}\n`);
645
630
  const checkInterval = setInterval(() => {
646
- // Use fstat on open fd to avoid TOCTOU race condition
647
631
  let fd = null;
648
632
  try {
649
633
  fd = fs.openSync(logFile, 'r');
@@ -651,25 +635,36 @@ function followLogs(laneDir, options) {
651
635
  if (stats.size > lastSize) {
652
636
  const buffer = Buffer.alloc(stats.size - lastSize);
653
637
  fs.readSync(fd, buffer, 0, buffer.length, lastSize);
654
- let content = buffer.toString();
655
- // Apply filter (case-insensitive string match to avoid ReDoS)
656
- if (options.filter) {
657
- const filterLower = options.filter.toLowerCase();
658
- const lines = content.split('\n');
659
- content = lines.filter(line => line.toLowerCase().includes(filterLower)).join('\n');
660
- }
661
- // Clean ANSI if needed (unless raw mode)
662
- if (!options.raw) {
663
- content = (0, enhanced_logger_1.stripAnsi)(content);
664
- }
665
- if (content.trim()) {
666
- process.stdout.write(content);
638
+ const content = buffer.toString();
639
+ const lines = content.split('\n').filter(l => l.trim());
640
+ for (const line of lines) {
641
+ try {
642
+ const entry = JSON.parse(line);
643
+ const ts = new Date(entry.timestamp).toLocaleTimeString('en-US', { hour12: false });
644
+ const level = entry.level || 'info';
645
+ const levelColor = getLevelColor(level);
646
+ const message = entry.content || entry.message || '';
647
+ // Apply level filter
648
+ if (options.level && level !== options.level)
649
+ continue;
650
+ // Apply filter
651
+ if (options.filter) {
652
+ const filterLower = options.filter.toLowerCase();
653
+ if (!message.toLowerCase().includes(filterLower))
654
+ continue;
655
+ }
656
+ const displayMsg = options.raw ? message : (0, formatter_1.stripAnsi)(message);
657
+ console.log(`${logger.COLORS.gray}[${ts}]${logger.COLORS.reset} ${levelColor}[${level.toUpperCase().padEnd(6)}]${logger.COLORS.reset} ${displayMsg}`);
658
+ }
659
+ catch {
660
+ // Skip invalid JSON
661
+ }
667
662
  }
668
663
  lastSize = stats.size;
669
664
  }
670
665
  }
671
666
  catch {
672
- // Ignore errors (file might be rotating)
667
+ // Ignore errors
673
668
  }
674
669
  finally {
675
670
  if (fd !== null) {
@@ -718,7 +713,7 @@ function followMainLogs(runDir, options) {
718
713
  content = lines.filter(line => line.toLowerCase().includes(filterLower)).join('\n');
719
714
  }
720
715
  if (options.clean) {
721
- content = (0, enhanced_logger_1.stripAnsi)(content);
716
+ content = (0, formatter_1.stripAnsi)(content);
722
717
  }
723
718
  if (content.trim()) {
724
719
  process.stdout.write(content);
@@ -758,16 +753,11 @@ function displaySummary(runDir) {
758
753
  }
759
754
  for (const lane of lanes) {
760
755
  const laneDir = (0, path_1.safeJoin)(runDir, 'lanes', lane);
761
- const rawLog = (0, path_1.safeJoin)(laneDir, 'terminal-raw.log');
762
- const readableLog = (0, path_1.safeJoin)(laneDir, 'terminal-readable.log');
756
+ const jsonlLog = (0, path_1.safeJoin)(laneDir, 'terminal.jsonl');
763
757
  console.log(` ${logger.COLORS.green}📁 ${lane}${logger.COLORS.reset}`);
764
- if (fs.existsSync(readableLog)) {
765
- const stats = fs.statSync(readableLog);
766
- console.log(` └─ terminal-readable.log ${formatSize(stats.size)} ${logger.COLORS.yellow}(default)${logger.COLORS.reset}`);
767
- }
768
- if (fs.existsSync(rawLog)) {
769
- const stats = fs.statSync(rawLog);
770
- console.log(` └─ terminal-raw.log ${formatSize(stats.size)}`);
758
+ if (fs.existsSync(jsonlLog)) {
759
+ const stats = fs.statSync(jsonlLog);
760
+ console.log(` └─ terminal.jsonl ${formatSize(stats.size)} ${logger.COLORS.yellow}(unified)${logger.COLORS.reset}`);
771
761
  }
772
762
  console.log('');
773
763
  }
@@ -856,7 +846,7 @@ async function logs(args) {
856
846
  }
857
847
  let content = fs.readFileSync(logFile, 'utf8');
858
848
  if (options.clean) {
859
- content = (0, enhanced_logger_1.stripAnsi)(content);
849
+ content = (0, formatter_1.stripAnsi)(content);
860
850
  }
861
851
  fs.writeFileSync(options.output, content, 'utf8');
862
852
  console.log(`Exported main log to: ${options.output}`);