@litmers/cursorflow-orchestrator 0.2.5 → 0.2.7
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.
- package/CHANGELOG.md +29 -20
- package/README.md +13 -8
- package/dist/cli/complete.js +22 -5
- package/dist/cli/complete.js.map +1 -1
- package/dist/cli/index.js +2 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/logs.js +61 -51
- package/dist/cli/logs.js.map +1 -1
- package/dist/cli/monitor.js +45 -56
- package/dist/cli/monitor.js.map +1 -1
- package/dist/cli/resume.js +2 -2
- package/dist/cli/resume.js.map +1 -1
- package/dist/core/git-lifecycle-manager.js +2 -2
- package/dist/core/git-lifecycle-manager.js.map +1 -1
- package/dist/core/git-pipeline-coordinator.js +25 -25
- package/dist/core/git-pipeline-coordinator.js.map +1 -1
- package/dist/core/orchestrator.d.ts +17 -0
- package/dist/core/orchestrator.js +186 -8
- package/dist/core/orchestrator.js.map +1 -1
- package/dist/core/runner/pipeline.js +3 -3
- package/dist/core/runner/pipeline.js.map +1 -1
- package/dist/hooks/data-accessor.js +2 -2
- package/dist/hooks/data-accessor.js.map +1 -1
- package/dist/services/logging/buffer.d.ts +2 -1
- package/dist/services/logging/buffer.js +63 -22
- package/dist/services/logging/buffer.js.map +1 -1
- package/dist/services/logging/formatter.d.ts +0 -4
- package/dist/services/logging/formatter.js +33 -201
- package/dist/services/logging/formatter.js.map +1 -1
- package/dist/services/logging/paths.d.ts +3 -0
- package/dist/services/logging/paths.js +3 -0
- package/dist/services/logging/paths.js.map +1 -1
- package/dist/types/config.d.ts +9 -1
- package/dist/types/flow.d.ts +6 -0
- package/dist/types/logging.d.ts +1 -1
- package/dist/utils/config.js +6 -2
- package/dist/utils/config.js.map +1 -1
- package/dist/utils/enhanced-logger.d.ts +37 -17
- package/dist/utils/enhanced-logger.js +267 -237
- package/dist/utils/enhanced-logger.js.map +1 -1
- package/dist/utils/events.d.ts +18 -15
- package/dist/utils/events.js +8 -5
- package/dist/utils/events.js.map +1 -1
- package/dist/utils/log-formatter.d.ts +26 -0
- package/dist/utils/log-formatter.js +274 -0
- package/dist/utils/log-formatter.js.map +1 -0
- package/dist/utils/logger.js +4 -17
- package/dist/utils/logger.js.map +1 -1
- package/dist/utils/repro-thinking-logs.js +4 -4
- package/dist/utils/repro-thinking-logs.js.map +1 -1
- package/package.json +2 -2
- package/scripts/monitor-lanes.sh +5 -5
- package/scripts/stream-logs.sh +1 -1
- package/scripts/test-log-parser.ts +42 -8
- package/src/cli/complete.ts +21 -6
- package/src/cli/index.ts +2 -0
- package/src/cli/logs.ts +60 -46
- package/src/cli/monitor.ts +47 -64
- package/src/cli/resume.ts +1 -1
- package/src/core/git-lifecycle-manager.ts +2 -2
- package/src/core/git-pipeline-coordinator.ts +25 -25
- package/src/core/orchestrator.ts +214 -7
- package/src/core/runner/pipeline.ts +3 -3
- package/src/hooks/data-accessor.ts +2 -2
- package/src/services/logging/buffer.ts +68 -20
- package/src/services/logging/formatter.ts +32 -199
- package/src/services/logging/paths.ts +3 -0
- package/src/types/config.ts +13 -1
- package/src/types/flow.ts +6 -0
- package/src/types/logging.ts +0 -2
- package/src/utils/config.ts +6 -2
- package/src/utils/enhanced-logger.ts +290 -239
- package/src/utils/events.ts +21 -18
- package/src/utils/log-formatter.ts +287 -0
- package/src/utils/logger.ts +3 -18
- package/src/utils/repro-thinking-logs.ts +4 -4
package/src/utils/events.ts
CHANGED
|
@@ -17,32 +17,35 @@ import {
|
|
|
17
17
|
} from './types';
|
|
18
18
|
|
|
19
19
|
class CursorFlowEvents extends EventEmitter {
|
|
20
|
-
private
|
|
20
|
+
private currentRunId: string = 'unknown';
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
/**
|
|
23
|
+
* Set the current run ID to be used for all events that don't provide one
|
|
24
|
+
*/
|
|
25
|
+
setRunId(runId: string): void {
|
|
26
|
+
this.currentRunId = runId;
|
|
24
27
|
}
|
|
25
28
|
|
|
26
29
|
// Specific event overloads for emit
|
|
27
|
-
emit(type: 'orchestration.started', payload: OrchestrationStartedPayload): boolean;
|
|
28
|
-
emit(type: 'orchestration.completed', payload: OrchestrationCompletedPayload): boolean;
|
|
29
|
-
emit(type: 'orchestration.failed', payload: OrchestrationFailedPayload): boolean;
|
|
30
|
-
emit(type: 'lane.started', payload: LaneStartedPayload): boolean;
|
|
31
|
-
emit(type: 'lane.completed', payload: LaneCompletedPayload): boolean;
|
|
32
|
-
emit(type: 'lane.failed', payload: LaneFailedPayload): boolean;
|
|
33
|
-
emit(type: 'lane.dependency_requested', payload: LaneDependencyRequestedPayload): boolean;
|
|
34
|
-
emit(type: 'task.started', payload: TaskStartedPayload): boolean;
|
|
35
|
-
emit(type: 'task.completed', payload: TaskCompletedPayload): boolean;
|
|
36
|
-
emit(type: 'task.failed', payload: TaskFailedPayload): boolean;
|
|
37
|
-
emit(type: 'agent.prompt_sent', payload: AgentPromptSentPayload): boolean;
|
|
38
|
-
emit(type: 'agent.response_received', payload: AgentResponseReceivedPayload): boolean;
|
|
39
|
-
emit(type: string, payload: any): boolean;
|
|
40
|
-
emit(type: string, payload: any): boolean {
|
|
30
|
+
emit(type: 'orchestration.started', payload: OrchestrationStartedPayload, runId?: string): boolean;
|
|
31
|
+
emit(type: 'orchestration.completed', payload: OrchestrationCompletedPayload, runId?: string): boolean;
|
|
32
|
+
emit(type: 'orchestration.failed', payload: OrchestrationFailedPayload, runId?: string): boolean;
|
|
33
|
+
emit(type: 'lane.started', payload: LaneStartedPayload, runId?: string): boolean;
|
|
34
|
+
emit(type: 'lane.completed', payload: LaneCompletedPayload, runId?: string): boolean;
|
|
35
|
+
emit(type: 'lane.failed', payload: LaneFailedPayload, runId?: string): boolean;
|
|
36
|
+
emit(type: 'lane.dependency_requested', payload: LaneDependencyRequestedPayload, runId?: string): boolean;
|
|
37
|
+
emit(type: 'task.started', payload: TaskStartedPayload, runId?: string): boolean;
|
|
38
|
+
emit(type: 'task.completed', payload: TaskCompletedPayload, runId?: string): boolean;
|
|
39
|
+
emit(type: 'task.failed', payload: TaskFailedPayload, runId?: string): boolean;
|
|
40
|
+
emit(type: 'agent.prompt_sent', payload: AgentPromptSentPayload, runId?: string): boolean;
|
|
41
|
+
emit(type: 'agent.response_received', payload: AgentResponseReceivedPayload, runId?: string): boolean;
|
|
42
|
+
emit(type: string, payload: any, runId?: string): boolean;
|
|
43
|
+
emit(type: string, payload: any, runId?: string): boolean {
|
|
41
44
|
const event: CursorFlowEvent = {
|
|
42
45
|
id: `evt_${Date.now()}_${Math.random().toString(36).slice(2)}`,
|
|
43
46
|
type,
|
|
44
47
|
timestamp: new Date().toISOString(),
|
|
45
|
-
runId: this.
|
|
48
|
+
runId: runId || this.currentRunId,
|
|
46
49
|
payload,
|
|
47
50
|
};
|
|
48
51
|
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility for formatting log messages for console display
|
|
3
|
+
*
|
|
4
|
+
* Format: [HH:MM:SS] [MAIN] or [SUB:<id>] ICON TYPE content
|
|
5
|
+
* (labels are padded to keep alignment consistent)
|
|
6
|
+
*
|
|
7
|
+
* Rules:
|
|
8
|
+
* - Box format only for: user, assistant, system, result
|
|
9
|
+
* - Compact format for: tool, tool_result, thinking (gray/dim)
|
|
10
|
+
* - Tool names simplified: ShellToolCall → Shell
|
|
11
|
+
* - Lane labels fixed 20 chars: [1-1-backend ]
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { COLORS } from './log-constants';
|
|
15
|
+
import { ParsedMessage, stripAnsi } from './enhanced-logger';
|
|
16
|
+
|
|
17
|
+
// Types that should use box format
|
|
18
|
+
const BOX_TYPES = new Set(['user', 'assistant', 'system', 'result']);
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Simplify tool names (ShellToolCall → Shell, etc.)
|
|
22
|
+
*/
|
|
23
|
+
function simplifyToolName(name: string): string {
|
|
24
|
+
// Remove common suffixes
|
|
25
|
+
return name
|
|
26
|
+
.replace(/ToolCall$/i, '')
|
|
27
|
+
.replace(/Tool$/i, '')
|
|
28
|
+
.replace(/^run_terminal_cmd$/i, 'shell')
|
|
29
|
+
.replace(/^search_replace$/i, 'edit');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Format a single parsed message into a human-readable string (compact or multi-line)
|
|
34
|
+
*/
|
|
35
|
+
export function formatMessageForConsole(
|
|
36
|
+
msg: ParsedMessage,
|
|
37
|
+
options: {
|
|
38
|
+
includeTimestamp?: boolean;
|
|
39
|
+
laneLabel?: string;
|
|
40
|
+
compact?: boolean;
|
|
41
|
+
context?: string;
|
|
42
|
+
} = {}
|
|
43
|
+
): string {
|
|
44
|
+
const { includeTimestamp = true, laneLabel = '', compact = false, context = '' } = options;
|
|
45
|
+
const ts = includeTimestamp ? new Date(msg.timestamp).toLocaleTimeString('en-US', { hour12: false }) : '';
|
|
46
|
+
const tsPrefix = ts ? `${COLORS.gray}[${ts}]${COLORS.reset} ` : '';
|
|
47
|
+
|
|
48
|
+
// Handle context (e.g. from logger.info) - keep labels compact
|
|
49
|
+
// Format: [1-1-refactor] without fixed width padding
|
|
50
|
+
let effectiveLaneLabel = laneLabel || (context ? `[${context.substring(0, 12)}]` : '');
|
|
51
|
+
|
|
52
|
+
// Compact label with color
|
|
53
|
+
const labelPrefix = effectiveLaneLabel ? `${COLORS.magenta}${effectiveLaneLabel}${COLORS.reset} ` : '';
|
|
54
|
+
|
|
55
|
+
let typePrefix = '';
|
|
56
|
+
let content = msg.content;
|
|
57
|
+
|
|
58
|
+
// Determine if we should use box format
|
|
59
|
+
// Box format only for: user, assistant, system, result (and only when not compact)
|
|
60
|
+
const useBox = !compact && BOX_TYPES.has(msg.type);
|
|
61
|
+
|
|
62
|
+
// Clean up wrapped prompts for user messages to hide internal instructions
|
|
63
|
+
if (msg.type === 'user') {
|
|
64
|
+
const contextMarker = '### 🛠 Environment & Context';
|
|
65
|
+
const instructionsMarker = '### 📝 Final Instructions';
|
|
66
|
+
|
|
67
|
+
if (content.includes(contextMarker)) {
|
|
68
|
+
const parts = content.split('---\n');
|
|
69
|
+
if (parts.length >= 3) {
|
|
70
|
+
content = parts[1]!.trim();
|
|
71
|
+
} else {
|
|
72
|
+
content = content.split(contextMarker).pop() || content;
|
|
73
|
+
content = content.split(instructionsMarker)[0] || content;
|
|
74
|
+
content = content.replace(/^.*---\n/s, '').trim();
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// For thinking: collapse multiple newlines into single space
|
|
80
|
+
if (msg.type === 'thinking') {
|
|
81
|
+
content = content.replace(/\n\s*\n/g, ' ').replace(/\n/g, ' ').trim();
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
switch (msg.type) {
|
|
85
|
+
case 'user':
|
|
86
|
+
typePrefix = `${COLORS.cyan}🧑 USER${COLORS.reset}`;
|
|
87
|
+
if (!useBox) content = content.replace(/\n/g, ' ').substring(0, 100) + (content.length > 100 ? '...' : '');
|
|
88
|
+
break;
|
|
89
|
+
case 'assistant':
|
|
90
|
+
typePrefix = `${COLORS.green}🤖 ASST${COLORS.reset}`;
|
|
91
|
+
if (!useBox) content = content.replace(/\n/g, ' ').substring(0, 100) + (content.length > 100 ? '...' : '');
|
|
92
|
+
break;
|
|
93
|
+
case 'tool':
|
|
94
|
+
// Tool calls are always compact and gray
|
|
95
|
+
typePrefix = `${COLORS.gray}🔧 TOOL${COLORS.reset}`;
|
|
96
|
+
const toolMatch = content.match(/\[Tool: ([^\]]+)\] (.*)/);
|
|
97
|
+
if (toolMatch) {
|
|
98
|
+
const [, rawName, args] = toolMatch;
|
|
99
|
+
const name = simplifyToolName(rawName!);
|
|
100
|
+
try {
|
|
101
|
+
const parsedArgs = JSON.parse(args!);
|
|
102
|
+
let argStr = '';
|
|
103
|
+
if (rawName === 'read_file' && parsedArgs.target_file) {
|
|
104
|
+
argStr = parsedArgs.target_file;
|
|
105
|
+
} else if (rawName === 'run_terminal_cmd' && parsedArgs.command) {
|
|
106
|
+
argStr = parsedArgs.command;
|
|
107
|
+
} else if (rawName === 'write' && parsedArgs.file_path) {
|
|
108
|
+
argStr = parsedArgs.file_path;
|
|
109
|
+
} else if (rawName === 'search_replace' && parsedArgs.file_path) {
|
|
110
|
+
argStr = parsedArgs.file_path;
|
|
111
|
+
} else {
|
|
112
|
+
const keys = Object.keys(parsedArgs);
|
|
113
|
+
if (keys.length > 0) {
|
|
114
|
+
argStr = String(parsedArgs[keys[0]]).substring(0, 50);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
content = `${COLORS.gray}${name}${COLORS.reset}(${COLORS.gray}${argStr}${COLORS.reset})`;
|
|
118
|
+
} catch {
|
|
119
|
+
content = `${COLORS.gray}${name}${COLORS.reset}: ${args}`;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
break;
|
|
123
|
+
case 'tool_result':
|
|
124
|
+
// Tool results are always compact and gray
|
|
125
|
+
typePrefix = `${COLORS.gray}📄 RESL${COLORS.reset}`;
|
|
126
|
+
const resMatch = content.match(/\[Tool Result: ([^\]]+)\]/);
|
|
127
|
+
if (resMatch) {
|
|
128
|
+
const simpleName = simplifyToolName(resMatch[1]!);
|
|
129
|
+
content = `${COLORS.gray}${simpleName} OK${COLORS.reset}`;
|
|
130
|
+
} else {
|
|
131
|
+
content = `${COLORS.gray}result${COLORS.reset}`;
|
|
132
|
+
}
|
|
133
|
+
break;
|
|
134
|
+
case 'result':
|
|
135
|
+
case 'success':
|
|
136
|
+
typePrefix = `${COLORS.green}✅ DONE${COLORS.reset}`;
|
|
137
|
+
break;
|
|
138
|
+
case 'system':
|
|
139
|
+
typePrefix = `${COLORS.gray}⚙️ SYS${COLORS.reset}`;
|
|
140
|
+
break;
|
|
141
|
+
case 'thinking':
|
|
142
|
+
// Thinking is always compact and gray
|
|
143
|
+
typePrefix = `${COLORS.gray}🤔 THNK${COLORS.reset}`;
|
|
144
|
+
content = `${COLORS.gray}${content.substring(0, 100)}${content.length > 100 ? '...' : ''}${COLORS.reset}`;
|
|
145
|
+
break;
|
|
146
|
+
case 'info':
|
|
147
|
+
typePrefix = `${COLORS.cyan}ℹ️ INFO${COLORS.reset}`;
|
|
148
|
+
break;
|
|
149
|
+
case 'warn':
|
|
150
|
+
typePrefix = `${COLORS.yellow}⚠️ WARN${COLORS.reset}`;
|
|
151
|
+
break;
|
|
152
|
+
case 'error':
|
|
153
|
+
typePrefix = `${COLORS.red}❌ ERR${COLORS.reset}`;
|
|
154
|
+
break;
|
|
155
|
+
case 'raw':
|
|
156
|
+
// Raw type means the content is already fully formatted - return as-is
|
|
157
|
+
return content;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (!typePrefix) return `${tsPrefix}${labelPrefix}${content}`;
|
|
161
|
+
|
|
162
|
+
// Avoid double prefixes (e.g. INFO INFO)
|
|
163
|
+
const plainTypePrefix = stripAnsi(typePrefix).replace(/[^\x00-\x7F]/g, '').trim(); // "INFO", "DONE", etc.
|
|
164
|
+
const plainContent = stripAnsi(content);
|
|
165
|
+
if (plainContent.includes(` ${plainTypePrefix} `) || plainContent.startsWith(`${plainTypePrefix} `)) {
|
|
166
|
+
// If content already has the prefix, try to strip it from content or just use content as is
|
|
167
|
+
// For simplicity, if it's already there, we can just skip adding our typePrefix
|
|
168
|
+
// but we still want the colors. This is tricky.
|
|
169
|
+
// Usually it's better to just return the content if it looks already formatted.
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// A better way: if content starts with an emoji that matches our type, skip typePrefix
|
|
173
|
+
const emojiMap: Record<string, string> = {
|
|
174
|
+
'info': 'ℹ️',
|
|
175
|
+
'success': '✅',
|
|
176
|
+
'result': '✅',
|
|
177
|
+
'warn': '⚠️',
|
|
178
|
+
'error': '❌',
|
|
179
|
+
'tool': '🔧',
|
|
180
|
+
'thinking': '🤔',
|
|
181
|
+
'user': '🧑',
|
|
182
|
+
'assistant': '🤖'
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
const targetEmoji = emojiMap[msg.type];
|
|
186
|
+
if (targetEmoji && plainContent.trim().startsWith(targetEmoji)) {
|
|
187
|
+
return `${tsPrefix}${labelPrefix}${content}`;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Handle separator lines - if content is just a repeat of ━ or ─, extend it
|
|
191
|
+
const separatorMatch = content.match(/^([━─=]+)$/);
|
|
192
|
+
if (separatorMatch) {
|
|
193
|
+
const char = separatorMatch[1]![0]!;
|
|
194
|
+
// Use a fixed width for now (80 is a good standard)
|
|
195
|
+
// In a real terminal we could use process.stdout.columns
|
|
196
|
+
const targetWidth = 80;
|
|
197
|
+
content = char.repeat(targetWidth);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Compact format (single line)
|
|
201
|
+
if (!useBox) {
|
|
202
|
+
return `${tsPrefix}${labelPrefix}${typePrefix.padEnd(12)} ${content}`;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Multi-line box format (only for user, assistant, system, result)
|
|
206
|
+
// Emoji width is 2, so we need to account for that in indent calculation
|
|
207
|
+
const lines = content.split('\n');
|
|
208
|
+
const fullPrefix = `${tsPrefix}${labelPrefix}`;
|
|
209
|
+
const strippedPrefix = stripAnsi(typePrefix);
|
|
210
|
+
// Count emojis (they take 2 terminal columns but 1-2 chars in string)
|
|
211
|
+
const emojiCount = (strippedPrefix.match(/[\u{1F300}-\u{1F9FF}]|[\u{2600}-\u{26FF}]|[\u{2700}-\u{27BF}]|[\u{1F600}-\u{1F64F}]|[\u{1F680}-\u{1F6FF}]|[\u{1F1E0}-\u{1F1FF}]|[\u{2300}-\u{23FF}]|[\u{2B50}-\u{2B55}]|[\u{231A}-\u{231B}]|[\u{23E9}-\u{23F3}]|[\u{23F8}-\u{23FA}]|✅|❌|⚙️|ℹ️|⚠️|🔧|📄|🤔|🧑|🤖/gu) || []).length;
|
|
212
|
+
const visualWidth = strippedPrefix.length + emojiCount; // emoji adds 1 extra width
|
|
213
|
+
|
|
214
|
+
const boxWidth = 80;
|
|
215
|
+
const header = `${typePrefix}┌${'─'.repeat(boxWidth)}`;
|
|
216
|
+
let result = `${fullPrefix}${header}\n`;
|
|
217
|
+
|
|
218
|
+
const indent = ' '.repeat(visualWidth);
|
|
219
|
+
for (const line of lines) {
|
|
220
|
+
result += `${fullPrefix}${indent}│ ${line}\n`;
|
|
221
|
+
}
|
|
222
|
+
result += `${fullPrefix}${indent}└${'─'.repeat(boxWidth)}`;
|
|
223
|
+
|
|
224
|
+
return result;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Detect and format a message that might be a raw JSON string from cursor-agent
|
|
229
|
+
*/
|
|
230
|
+
export function formatPotentialJsonMessage(message: string): string {
|
|
231
|
+
const trimmed = message.trim();
|
|
232
|
+
if (!trimmed.startsWith('{') || !trimmed.endsWith('}')) {
|
|
233
|
+
return message;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
try {
|
|
237
|
+
const json = JSON.parse(trimmed);
|
|
238
|
+
if (!json.type) return message;
|
|
239
|
+
|
|
240
|
+
// Convert JSON to a ParsedMessage-like structure for formatting
|
|
241
|
+
let content: string;
|
|
242
|
+
let type: string;
|
|
243
|
+
|
|
244
|
+
if (json.type === 'thinking' && json.text) {
|
|
245
|
+
content = json.text;
|
|
246
|
+
type = 'thinking';
|
|
247
|
+
} else if (json.type === 'assistant' && json.message?.content) {
|
|
248
|
+
content = json.message.content
|
|
249
|
+
.filter((c: any) => c.type === 'text')
|
|
250
|
+
.map((c: any) => c.text)
|
|
251
|
+
.join('');
|
|
252
|
+
type = 'assistant';
|
|
253
|
+
} else if (json.type === 'user' && json.message?.content) {
|
|
254
|
+
content = json.message.content
|
|
255
|
+
.filter((c: any) => c.type === 'text')
|
|
256
|
+
.map((c: any) => c.text)
|
|
257
|
+
.join('');
|
|
258
|
+
type = 'user';
|
|
259
|
+
} else if (json.type === 'tool_call' && json.subtype === 'started') {
|
|
260
|
+
const rawToolName = Object.keys(json.tool_call)[0] || 'unknown';
|
|
261
|
+
const args = json.tool_call[rawToolName]?.args || {};
|
|
262
|
+
// Tool name will be simplified in formatMessageForConsole
|
|
263
|
+
content = `[Tool: ${rawToolName}] ${JSON.stringify(args)}`;
|
|
264
|
+
type = 'tool';
|
|
265
|
+
} else if (json.type === 'tool_call' && json.subtype === 'completed') {
|
|
266
|
+
const rawToolName = Object.keys(json.tool_call)[0] || 'unknown';
|
|
267
|
+
content = `[Tool Result: ${rawToolName}]`;
|
|
268
|
+
type = 'tool_result';
|
|
269
|
+
} else if (json.type === 'result') {
|
|
270
|
+
content = json.result || 'Task completed';
|
|
271
|
+
type = 'result';
|
|
272
|
+
} else {
|
|
273
|
+
// Unknown type, return as is
|
|
274
|
+
return message;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
return formatMessageForConsole({
|
|
278
|
+
type: type as any,
|
|
279
|
+
role: type,
|
|
280
|
+
content,
|
|
281
|
+
timestamp: json.timestamp_ms || Date.now()
|
|
282
|
+
}, { includeTimestamp: false, compact: true });
|
|
283
|
+
|
|
284
|
+
} catch {
|
|
285
|
+
return message;
|
|
286
|
+
}
|
|
287
|
+
}
|
package/src/utils/logger.ts
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
import * as fs from 'fs';
|
|
12
12
|
import * as path from 'path';
|
|
13
13
|
import { COLORS, LogLevel } from './log-constants';
|
|
14
|
-
import { formatMessageForConsole } from '
|
|
14
|
+
import { formatMessageForConsole } from './log-formatter';
|
|
15
15
|
|
|
16
16
|
export { COLORS, LogLevel };
|
|
17
17
|
|
|
@@ -92,29 +92,14 @@ function logInternal(
|
|
|
92
92
|
return;
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
-
// If context is 'git', use the specialized 'git' message type for better coloring
|
|
96
|
-
const effectiveType = options.context === 'git' ? 'git' : type;
|
|
97
|
-
|
|
98
|
-
// If running in a lane, output JSON for the orchestrator to capture and format
|
|
99
|
-
if (process.env.CURSORFLOW_LANE === 'true') {
|
|
100
|
-
const jsonMsg = {
|
|
101
|
-
type: effectiveType,
|
|
102
|
-
content: message,
|
|
103
|
-
timestamp_ms: Date.now(),
|
|
104
|
-
context: options.context ?? defaultContext ?? undefined,
|
|
105
|
-
};
|
|
106
|
-
console.log(JSON.stringify(jsonMsg));
|
|
107
|
-
return;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
95
|
const formatted = formatMessageForConsole({
|
|
111
|
-
type:
|
|
96
|
+
type: type as any,
|
|
112
97
|
role: 'system',
|
|
113
98
|
content: message,
|
|
114
99
|
timestamp: Date.now(),
|
|
115
100
|
}, {
|
|
116
101
|
includeTimestamp: !options.noTimestamp,
|
|
117
|
-
|
|
102
|
+
context: options.context ?? defaultContext ?? undefined,
|
|
118
103
|
compact: !options.box
|
|
119
104
|
});
|
|
120
105
|
|
|
@@ -11,7 +11,7 @@ async function testThinkingLogs() {
|
|
|
11
11
|
|
|
12
12
|
console.log('--- Initializing Log Manager ---');
|
|
13
13
|
const manager = createLogManager(testDir, 'test-lane-thinking', {
|
|
14
|
-
|
|
14
|
+
keepRawLogs: true
|
|
15
15
|
});
|
|
16
16
|
|
|
17
17
|
manager.setTask('repro-thinking-task', 'sonnet-4.5-thinking');
|
|
@@ -31,9 +31,9 @@ async function testThinkingLogs() {
|
|
|
31
31
|
|
|
32
32
|
manager.close();
|
|
33
33
|
|
|
34
|
-
console.log('\n--- Verifying terminal.
|
|
35
|
-
const
|
|
36
|
-
console.log(
|
|
34
|
+
console.log('\n--- Verifying terminal-readable.log ---');
|
|
35
|
+
const readableLog = fs.readFileSync(path.join(testDir, 'terminal-readable.log'), 'utf8');
|
|
36
|
+
console.log(readableLog);
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
testThinkingLogs().catch(console.error);
|