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