@sylphx/flow 1.8.0 → 1.8.2

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 (126) hide show
  1. package/CHANGELOG.md +72 -0
  2. package/assets/output-styles/silent.md +145 -8
  3. package/assets/rules/core.md +19 -2
  4. package/package.json +2 -12
  5. package/src/commands/flow/execute.ts +470 -0
  6. package/src/commands/flow/index.ts +11 -0
  7. package/src/commands/flow/prompt.ts +35 -0
  8. package/src/commands/flow/setup.ts +312 -0
  9. package/src/commands/flow/targets.ts +18 -0
  10. package/src/commands/flow/types.ts +47 -0
  11. package/src/commands/flow-command.ts +18 -967
  12. package/src/commands/flow-orchestrator.ts +14 -5
  13. package/src/commands/hook-command.ts +1 -1
  14. package/src/commands/init-core.ts +12 -3
  15. package/src/commands/run-command.ts +1 -1
  16. package/src/config/rules.ts +1 -1
  17. package/src/core/error-handling.ts +1 -1
  18. package/src/core/loop-controller.ts +1 -1
  19. package/src/core/state-detector.ts +1 -1
  20. package/src/core/target-manager.ts +1 -1
  21. package/src/index.ts +1 -1
  22. package/src/shared/files/index.ts +1 -1
  23. package/src/shared/processing/index.ts +1 -1
  24. package/src/targets/claude-code.ts +3 -3
  25. package/src/targets/opencode.ts +3 -3
  26. package/src/utils/agent-enhancer.ts +2 -2
  27. package/src/utils/{mcp-config.ts → config/mcp-config.ts} +4 -4
  28. package/src/utils/{paths.ts → config/paths.ts} +1 -1
  29. package/src/utils/{settings.ts → config/settings.ts} +1 -1
  30. package/src/utils/{target-config.ts → config/target-config.ts} +5 -5
  31. package/src/utils/{target-utils.ts → config/target-utils.ts} +3 -3
  32. package/src/utils/display/banner.ts +25 -0
  33. package/src/utils/display/status.ts +55 -0
  34. package/src/utils/{file-operations.ts → files/file-operations.ts} +2 -2
  35. package/src/utils/files/jsonc.ts +36 -0
  36. package/src/utils/{sync-utils.ts → files/sync-utils.ts} +3 -3
  37. package/src/utils/index.ts +42 -61
  38. package/src/utils/version.ts +47 -0
  39. package/src/components/benchmark-monitor.tsx +0 -331
  40. package/src/components/reindex-progress.tsx +0 -261
  41. package/src/composables/functional/index.ts +0 -14
  42. package/src/composables/functional/useEnvironment.ts +0 -171
  43. package/src/composables/functional/useFileSystem.ts +0 -139
  44. package/src/composables/index.ts +0 -4
  45. package/src/composables/useEnv.ts +0 -13
  46. package/src/composables/useRuntimeConfig.ts +0 -27
  47. package/src/core/ai-sdk.ts +0 -603
  48. package/src/core/app-factory.ts +0 -381
  49. package/src/core/builtin-agents.ts +0 -9
  50. package/src/core/command-system.ts +0 -550
  51. package/src/core/config-system.ts +0 -550
  52. package/src/core/connection-pool.ts +0 -390
  53. package/src/core/di-container.ts +0 -155
  54. package/src/core/headless-display.ts +0 -96
  55. package/src/core/interfaces/index.ts +0 -22
  56. package/src/core/interfaces/repository.interface.ts +0 -91
  57. package/src/core/interfaces/service.interface.ts +0 -133
  58. package/src/core/interfaces.ts +0 -96
  59. package/src/core/result.ts +0 -351
  60. package/src/core/service-config.ts +0 -252
  61. package/src/core/session-service.ts +0 -121
  62. package/src/core/storage-factory.ts +0 -115
  63. package/src/core/stream-handler.ts +0 -288
  64. package/src/core/type-utils.ts +0 -427
  65. package/src/core/unified-storage.ts +0 -456
  66. package/src/core/validation/limit.ts +0 -46
  67. package/src/core/validation/query.ts +0 -20
  68. package/src/db/auto-migrate.ts +0 -322
  69. package/src/db/base-database-client.ts +0 -144
  70. package/src/db/cache-db.ts +0 -218
  71. package/src/db/cache-schema.ts +0 -75
  72. package/src/db/database.ts +0 -70
  73. package/src/db/index.ts +0 -252
  74. package/src/db/memory-db.ts +0 -153
  75. package/src/db/memory-schema.ts +0 -29
  76. package/src/db/schema.ts +0 -289
  77. package/src/db/session-repository.ts +0 -733
  78. package/src/domains/index.ts +0 -6
  79. package/src/domains/utilities/index.ts +0 -6
  80. package/src/domains/utilities/time/index.ts +0 -5
  81. package/src/domains/utilities/time/tools.ts +0 -291
  82. package/src/services/agent-service.ts +0 -273
  83. package/src/services/evaluation-service.ts +0 -271
  84. package/src/services/functional/evaluation-logic.ts +0 -296
  85. package/src/services/functional/file-processor.ts +0 -273
  86. package/src/services/functional/index.ts +0 -12
  87. package/src/services/memory.service.ts +0 -476
  88. package/src/types/api/batch.ts +0 -108
  89. package/src/types/api/errors.ts +0 -118
  90. package/src/types/api/index.ts +0 -55
  91. package/src/types/api/requests.ts +0 -76
  92. package/src/types/api/responses.ts +0 -180
  93. package/src/types/api/websockets.ts +0 -85
  94. package/src/types/benchmark.ts +0 -49
  95. package/src/types/database.types.ts +0 -510
  96. package/src/types/memory-types.ts +0 -63
  97. package/src/utils/advanced-tokenizer.ts +0 -191
  98. package/src/utils/ai-model-fetcher.ts +0 -19
  99. package/src/utils/async-file-operations.ts +0 -516
  100. package/src/utils/audio-player.ts +0 -345
  101. package/src/utils/codebase-helpers.ts +0 -211
  102. package/src/utils/console-ui.ts +0 -79
  103. package/src/utils/database-errors.ts +0 -140
  104. package/src/utils/debug-logger.ts +0 -49
  105. package/src/utils/file-scanner.ts +0 -259
  106. package/src/utils/help.ts +0 -20
  107. package/src/utils/immutable-cache.ts +0 -106
  108. package/src/utils/jsonc.ts +0 -158
  109. package/src/utils/memory-tui.ts +0 -414
  110. package/src/utils/models-dev.ts +0 -91
  111. package/src/utils/parallel-operations.ts +0 -487
  112. package/src/utils/process-manager.ts +0 -155
  113. package/src/utils/prompts.ts +0 -120
  114. package/src/utils/search-tool-builder.ts +0 -214
  115. package/src/utils/session-manager.ts +0 -168
  116. package/src/utils/session-title.ts +0 -87
  117. package/src/utils/simplified-errors.ts +0 -410
  118. package/src/utils/template-engine.ts +0 -94
  119. package/src/utils/test-audio.ts +0 -71
  120. package/src/utils/todo-context.ts +0 -46
  121. package/src/utils/token-counter.ts +0 -288
  122. /package/src/utils/{cli-output.ts → display/cli-output.ts} +0 -0
  123. /package/src/utils/{logger.ts → display/logger.ts} +0 -0
  124. /package/src/utils/{notifications.ts → display/notifications.ts} +0 -0
  125. /package/src/utils/{secret-utils.ts → security/secret-utils.ts} +0 -0
  126. /package/src/utils/{security.ts → security/security.ts} +0 -0
@@ -1,6 +0,0 @@
1
- /**
2
- * Domains Index
3
- * Unified exports for all domain modules
4
- */
5
-
6
- export * from './utilities/index.js';
@@ -1,6 +0,0 @@
1
- /**
2
- * Utilities Domain
3
- * Common utility tools and helpers
4
- */
5
-
6
- export * from './time/index.js';
@@ -1,5 +0,0 @@
1
- /**
2
- * Time utilities domain - Time and date operations
3
- */
4
-
5
- export { registerTimeTools } from './tools.js';
@@ -1,291 +0,0 @@
1
- import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
- import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
3
- import { z } from 'zod';
4
-
5
- // Logger utility
6
- const Logger = {
7
- info: (message: string) => console.error(`[INFO] ${message}`),
8
- success: (message: string) => console.error(`[SUCCESS] ${message}`),
9
- error: (message: string, error?: unknown) => {
10
- console.error(`[ERROR] ${message}`);
11
- if (error) {
12
- console.error(error);
13
- }
14
- },
15
- };
16
-
17
- // Helper function to validate IANA timezone
18
- function isValidTimezone(timezone: string): boolean {
19
- try {
20
- Intl.DateTimeFormat(undefined, { timeZone: timezone });
21
- return true;
22
- } catch {
23
- return false;
24
- }
25
- }
26
-
27
- // Helper function to validate time format (HH:MM)
28
- function isValidTimeFormat(time: string): boolean {
29
- const timeRegex = /^([01]?[0-9]|2[0-3]):[0-5][0-9]$/;
30
- return timeRegex.test(time);
31
- }
32
-
33
- // Get current time in a specific timezone
34
- function getCurrentTime(args: { timezone: string }): CallToolResult {
35
- try {
36
- const { timezone } = args;
37
-
38
- // Validate timezone
39
- if (!isValidTimezone(timezone)) {
40
- return {
41
- content: [
42
- {
43
- type: 'text',
44
- text: `✗ Invalid timezone: ${timezone}. Please use a valid IANA timezone name (e.g., 'America/New_York', 'Europe/London', 'Asia/Tokyo').`,
45
- },
46
- ],
47
- isError: true,
48
- };
49
- }
50
-
51
- // Get current time in specified timezone
52
- const now = new Date();
53
- const timeFormatter = new Intl.DateTimeFormat('en-US', {
54
- timeZone: timezone,
55
- year: 'numeric',
56
- month: 'long',
57
- day: 'numeric',
58
- hour: '2-digit',
59
- minute: '2-digit',
60
- second: '2-digit',
61
- timeZoneName: 'long',
62
- hour12: false,
63
- });
64
-
65
- const parts = timeFormatter.formatToParts(now);
66
- const formatObject: Record<string, string> = {};
67
- for (const part of parts) {
68
- formatObject[part.type] = part.value;
69
- }
70
-
71
- const time24 = new Intl.DateTimeFormat('en-US', {
72
- timeZone: timezone,
73
- hour: '2-digit',
74
- minute: '2-digit',
75
- hour12: false,
76
- }).format(now);
77
-
78
- const isoString = now.toLocaleString('sv-SE', { timeZone: timezone });
79
-
80
- Logger.info(`Retrieved current time for timezone: ${timezone}`);
81
- return {
82
- content: [
83
- {
84
- type: 'text',
85
- text: JSON.stringify(
86
- {
87
- timezone,
88
- current_time: {
89
- date: `${formatObject.month} ${formatObject.day}, ${formatObject.year}`,
90
- time_24h: time24,
91
- time_with_seconds: timeFormatter.format(now),
92
- timezone_name: formatObject.timeZoneName,
93
- iso_format: `${isoString.replace(' ', 'T')}Z`,
94
- unix_timestamp: Math.floor(now.getTime() / 1000),
95
- },
96
- },
97
- null,
98
- 2
99
- ),
100
- },
101
- ],
102
- };
103
- } catch (error: unknown) {
104
- const errorMessage = error instanceof Error ? error.message : String(error);
105
- Logger.error('Error getting current time', error);
106
- return {
107
- content: [
108
- {
109
- type: 'text',
110
- text: `✗ Error getting current time: ${errorMessage}`,
111
- },
112
- ],
113
- isError: true,
114
- };
115
- }
116
- }
117
-
118
- // Convert time between timezones
119
- function convertTime(args: {
120
- source_timezone: string;
121
- time: string;
122
- target_timezone: string;
123
- }): CallToolResult {
124
- try {
125
- const { source_timezone, time, target_timezone } = args;
126
-
127
- // Validate timezones
128
- if (!isValidTimezone(source_timezone)) {
129
- return {
130
- content: [
131
- {
132
- type: 'text',
133
- text: `✗ Invalid source timezone: ${source_timezone}. Please use a valid IANA timezone name.`,
134
- },
135
- ],
136
- isError: true,
137
- };
138
- }
139
-
140
- if (!isValidTimezone(target_timezone)) {
141
- return {
142
- content: [
143
- {
144
- type: 'text',
145
- text: `✗ Invalid target timezone: ${target_timezone}. Please use a valid IANA timezone name.`,
146
- },
147
- ],
148
- isError: true,
149
- };
150
- }
151
-
152
- // Validate time format
153
- if (!isValidTimeFormat(time)) {
154
- return {
155
- content: [
156
- {
157
- type: 'text',
158
- text: `✗ Invalid time format: ${time}. Please use 24-hour format (HH:MM).`,
159
- },
160
- ],
161
- isError: true,
162
- };
163
- }
164
-
165
- // Parse the time and create a date object for today in source timezone
166
- const [hours, minutes] = time.split(':').map(Number);
167
- const now = new Date();
168
-
169
- // Create a date object representing the time in source timezone
170
- const sourceDate = new Date(now.getFullYear(), now.getMonth(), now.getDate(), hours, minutes);
171
-
172
- // Format the source time to get the correct representation
173
- const sourceFormatter = new Intl.DateTimeFormat('en-US', {
174
- timeZone: source_timezone,
175
- year: 'numeric',
176
- month: '2-digit',
177
- day: '2-digit',
178
- hour: '2-digit',
179
- minute: '2-digit',
180
- second: '2-digit',
181
- hour12: false,
182
- });
183
-
184
- const sourceParts = sourceFormatter.formatToParts(sourceDate);
185
- const sourceFormatObject: Record<string, string> = {};
186
- for (const part of sourceParts) {
187
- sourceFormatObject[part.type] = part.value;
188
- }
189
-
190
- // Convert to target timezone
191
- const targetFormatter = new Intl.DateTimeFormat('en-US', {
192
- timeZone: target_timezone,
193
- year: 'numeric',
194
- month: 'long',
195
- day: 'numeric',
196
- hour: '2-digit',
197
- minute: '2-digit',
198
- second: '2-digit',
199
- timeZoneName: 'long',
200
- hour12: false,
201
- });
202
-
203
- const targetTime24 = new Intl.DateTimeFormat('en-US', {
204
- timeZone: target_timezone,
205
- hour: '2-digit',
206
- minute: '2-digit',
207
- hour12: false,
208
- }).format(sourceDate);
209
-
210
- const targetParts = targetFormatter.formatToParts(sourceDate);
211
- const targetFormatObject: Record<string, string> = {};
212
- for (const part of targetParts) {
213
- targetFormatObject[part.type] = part.value;
214
- }
215
-
216
- const targetDate = new Date(sourceDate.toLocaleString('en-US', { timeZone: target_timezone }));
217
- const timeDiffMs = targetDate.getTime() - sourceDate.getTime();
218
- const timeDiffHours = Math.round(timeDiffMs / (1000 * 60 * 60));
219
-
220
- Logger.info(`Converted time from ${source_timezone} to ${target_timezone}`);
221
- return {
222
- content: [
223
- {
224
- type: 'text',
225
- text: JSON.stringify(
226
- {
227
- conversion: {
228
- source: {
229
- timezone: source_timezone,
230
- time: time,
231
- formatted: sourceFormatter.format(sourceDate),
232
- },
233
- target: {
234
- timezone: target_timezone,
235
- time_24h: targetTime24,
236
- formatted: targetFormatter.format(sourceDate),
237
- date: `${targetFormatObject.month} ${targetFormatObject.day}, ${targetFormatObject.year}`,
238
- timezone_name: targetFormatObject.timeZoneName,
239
- },
240
- time_difference_hours: timeDiffHours,
241
- },
242
- },
243
- null,
244
- 2
245
- ),
246
- },
247
- ],
248
- };
249
- } catch (error: unknown) {
250
- const errorMessage = error instanceof Error ? error.message : String(error);
251
- Logger.error('Error converting time', error);
252
- return {
253
- content: [
254
- {
255
- type: 'text',
256
- text: `✗ Error converting time: ${errorMessage}`,
257
- },
258
- ],
259
- isError: true,
260
- };
261
- }
262
- }
263
-
264
- // Register all time tools
265
- export function registerTimeTools(server: McpServer) {
266
- server.registerTool(
267
- 'get_current_time',
268
- {
269
- description: 'Get current time in a specific timezone or system timezone',
270
- inputSchema: {
271
- timezone: z
272
- .string()
273
- .describe("IANA timezone name (e.g., 'America/New_York', 'Europe/London')"),
274
- },
275
- },
276
- getCurrentTime
277
- );
278
-
279
- server.registerTool(
280
- 'convert_time',
281
- {
282
- description: 'Convert time between timezones',
283
- inputSchema: {
284
- source_timezone: z.string().describe('Source IANA timezone name'),
285
- time: z.string().describe('Time in 24-hour format (HH:MM)'),
286
- target_timezone: z.string().describe('Target IANA timezone name'),
287
- },
288
- },
289
- convertTime
290
- );
291
- }
@@ -1,273 +0,0 @@
1
- import { spawn } from 'node:child_process';
2
- import fs from 'node:fs/promises';
3
- import path from 'node:path';
4
- import type { InkMonitor } from '../components/benchmark-monitor.js';
5
- import { DEFAULT_AGENTS } from '../constants/benchmark-constants.js';
6
- import type { TimingData } from '../types/benchmark.js';
7
- import { getAgentsDir } from '../utils/paths.js';
8
- import { ProcessManager } from '../utils/process-manager.js';
9
-
10
- /**
11
- * Get list of agents to run based on user selection
12
- * Pure function - no state, no side effects (except validation errors)
13
- */
14
- export async function getAgentList(agentsOption: string): Promise<string[]> {
15
- if (agentsOption === 'all') {
16
- return DEFAULT_AGENTS;
17
- }
18
-
19
- const selectedAgents = agentsOption.split(',').map((a) => a.trim());
20
-
21
- // Validate selected agents
22
- for (const agent of selectedAgents) {
23
- if (!DEFAULT_AGENTS.includes(agent)) {
24
- throw new Error(`Invalid agent: ${agent}. Available agents: ${DEFAULT_AGENTS.join(', ')}`);
25
- }
26
- }
27
-
28
- return selectedAgents;
29
- }
30
-
31
- /**
32
- * Run a single agent with the given task
33
- */
34
- export async function runAgent(
35
- agentName: string,
36
- outputDir: string,
37
- taskFile: string,
38
- contextFile: string | undefined,
39
- monitor?: InkMonitor,
40
- _maxRetries = 3,
41
- timeout = 3600
42
- ): Promise<void> {
43
- const agentWorkDir = path.join(outputDir, agentName);
44
- await fs.mkdir(agentWorkDir, { recursive: true });
45
-
46
- // Load agent prompt
47
- const agentsDir = getAgentsDir();
48
- const agentFile = path.join(agentsDir, `${agentName}.md`);
49
-
50
- try {
51
- const agentPrompt = await fs.readFile(agentFile, 'utf-8');
52
-
53
- // Prepare task content - instruct agent to work in the temp directory
54
- const taskContent = await fs.readFile(taskFile, 'utf-8');
55
- let fullTask = taskContent;
56
-
57
- if (contextFile) {
58
- try {
59
- const contextContent = await fs.readFile(contextFile, 'utf-8');
60
- fullTask = `CONTEXT:\n${contextContent}\n\nTASK:\n${taskContent}`;
61
- } catch (_error) {
62
- // Silently handle context file errors
63
- }
64
- }
65
-
66
- // Add instruction to work in the temp directory
67
- // FUNCTIONAL: Use template string instead of +=
68
- const finalTask = `${fullTask}\n\nIMPORTANT: Please implement your solution in the current working directory: ${agentWorkDir}\nThis is a temporary directory for testing, so you can create files freely without affecting any production codebase.`;
69
-
70
- // Run Claude Code with the agent prompt
71
- await runSingleAgent(agentName, agentPrompt, finalTask, agentWorkDir, monitor, timeout);
72
- } catch (error) {
73
- throw new Error(`Failed to load agent ${agentName}: ${error}`);
74
- }
75
- }
76
-
77
- /**
78
- * Internal helper: Run a single agent process
79
- * Handles process spawning, monitoring, and cleanup
80
- */
81
- async function runSingleAgent(
82
- agentName: string,
83
- agentPrompt: string,
84
- fullTask: string,
85
- agentWorkDir: string,
86
- monitor?: InkMonitor,
87
- timeout = 3600
88
- ): Promise<void> {
89
- // Write agent prompt to a temp file to avoid command line length limits
90
- const tempPromptFile = path.join(agentWorkDir, '.agent-prompt.md');
91
- await fs.writeFile(tempPromptFile, agentPrompt);
92
-
93
- return new Promise((resolve, reject) => {
94
- let timeoutId: NodeJS.Timeout | undefined;
95
- // Set up timeout
96
- timeoutId = setTimeout(() => {
97
- if (claudeProcess && !claudeProcess.killed) {
98
- claudeProcess.kill('SIGTERM');
99
- // Force kill if it doesn't stop after 5 seconds
100
- setTimeout(() => {
101
- if (!claudeProcess.killed) {
102
- claudeProcess.kill('SIGKILL');
103
- }
104
- }, 5000);
105
- }
106
- // Update agent status to error due to timeout
107
- monitor?.updateAgentStatus(agentName, 'error');
108
- reject(new Error(`Agent ${agentName} timed out after ${timeout} seconds`));
109
- }, timeout * 1000);
110
-
111
- const claudeProcess = spawn(
112
- 'claude',
113
- [
114
- '--system-prompt',
115
- `@${tempPromptFile}`,
116
- '--dangerously-skip-permissions',
117
- '--output-format',
118
- 'stream-json',
119
- '--verbose',
120
- fullTask,
121
- ],
122
- {
123
- cwd: agentWorkDir,
124
- stdio: ['inherit', 'pipe', 'pipe'],
125
- env: {
126
- ...process.env,
127
- // Disable buffering and progress indicators for real-time output
128
- FORCE_NO_PROGRESS: '1',
129
- CI: '1',
130
- PYTHONUNBUFFERED: '1',
131
- },
132
- }
133
- );
134
-
135
- // Set the process PID for debugging
136
- if (claudeProcess.pid) {
137
- monitor?.setAgentPid(agentName, claudeProcess.pid);
138
- }
139
-
140
- // Track this child process for cleanup
141
- ProcessManager.getInstance().trackChildProcess(claudeProcess);
142
-
143
- // FUNCTIONAL: Use arrays for immutable buffer accumulation
144
- const stdoutChunks: string[] = [];
145
- const stderrChunks: string[] = [];
146
- let incompleteStdoutLine = '';
147
-
148
- claudeProcess.stdout?.on('data', (data) => {
149
- const output = data.toString();
150
- stdoutChunks.push(output);
151
-
152
- // Process complete lines only - keep incomplete data in buffer
153
- const combined = incompleteStdoutLine + output;
154
- const lines = combined.split('\n');
155
- incompleteStdoutLine = lines.pop() || ''; // Keep last incomplete line
156
-
157
- for (const line of lines) {
158
- if (!line.trim()) {
159
- continue;
160
- }
161
-
162
- try {
163
- const jsonData = JSON.parse(line);
164
-
165
- if (jsonData.type === 'assistant' && jsonData.message?.content) {
166
- // Extract text content and tool uses from assistant message
167
- for (const content of jsonData.message.content) {
168
- if (content.type === 'text') {
169
- const textContent = content.text.trim();
170
- if (textContent) {
171
- monitor?.addAgentOutput(agentName, textContent);
172
- }
173
- } else if (content.type === 'tool_use') {
174
- const toolName = content.name;
175
- const params = content.input || {};
176
-
177
- // Simple tool display for benchmark output
178
- const paramsStr = JSON.stringify(params);
179
- const toolDisplay = paramsStr.length > 80
180
- ? `${toolName}(${paramsStr.substring(0, 77)}...)`
181
- : `${toolName}(${paramsStr})`;
182
-
183
- monitor?.addAgentOutput(agentName, toolDisplay);
184
- }
185
- }
186
- }
187
- } catch (_e) {
188
- // Skip invalid JSON (shouldn't happen with stream-json)
189
- }
190
- }
191
-
192
- // Don't output directly to console when using React+Ink monitor
193
- // The monitor will handle displaying relevant output
194
- });
195
-
196
- claudeProcess.stderr?.on('data', (data) => {
197
- const output = data.toString();
198
- stderrChunks.push(output);
199
-
200
- // Add error output to monitor (with ANSI cleaning)
201
- monitor?.addAgentOutput(agentName, `ERROR: ${output}`);
202
-
203
- // Don't output directly to console when using React+Ink monitor
204
- // The monitor will handle displaying relevant output
205
- });
206
-
207
- claudeProcess.on('close', async (code) => {
208
- const endTime = Date.now();
209
-
210
- // Update agent end time
211
- const agent = monitor?.getAgents().get(agentName);
212
- if (agent) {
213
- agent.endTime = endTime;
214
- }
215
-
216
- // Clear timeout if process completed normally
217
- if (timeoutId) {
218
- clearTimeout(timeoutId);
219
- }
220
-
221
- // All data should already be processed in stdout event handler
222
-
223
- // Clean up temp prompt file
224
- try {
225
- await fs.unlink(tempPromptFile);
226
- } catch (_error) {
227
- // Ignore cleanup errors
228
- }
229
-
230
- // Write execution log with timing information
231
- // FUNCTIONAL: Join arrays at the end instead of accumulating with +=
232
- const stdoutFinal = stdoutChunks.join('');
233
- const stderrFinal = stderrChunks.join('');
234
- const executionLog = `Execution completed at: ${new Date(endTime).toISOString()}\nExit code: ${code}\n\n=== STDOUT ===\n${stdoutFinal}\n\n=== STDERR ===\n${stderrFinal}\n`;
235
- await fs.writeFile(path.join(agentWorkDir, 'execution-log.txt'), executionLog);
236
-
237
- // Write timing metadata
238
- const timingData: TimingData = {
239
- endTime,
240
- exitCode: code,
241
- stdoutLength: stdoutFinal.length,
242
- stderrLength: stderrFinal.length,
243
- };
244
- await fs.writeFile(
245
- path.join(agentWorkDir, 'timing.json'),
246
- JSON.stringify(timingData, null, 2)
247
- );
248
-
249
- // Update agent status based on exit code
250
- if (code === 0) {
251
- monitor?.updateAgentStatus(agentName, 'completed');
252
- resolve();
253
- } else {
254
- monitor?.updateAgentStatus(agentName, 'error');
255
- await fs.writeFile(path.join(agentWorkDir, 'execution-error.txt'), stderrFinal);
256
-
257
- reject(new Error(`Agent ${agentName} failed with code ${code}`));
258
- }
259
- });
260
-
261
- claudeProcess.on('error', (error) => {
262
- // Clear timeout on error
263
- if (timeoutId) {
264
- clearTimeout(timeoutId);
265
- }
266
-
267
- // Update agent status to error
268
- monitor?.updateAgentStatus(agentName, 'error');
269
-
270
- reject(error);
271
- });
272
- });
273
- }