aiexecode 1.0.54 → 1.0.56

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.

Potentially problematic release.


This version of aiexecode might be problematic. Click here for more details.

@@ -1,12 +1,11 @@
1
1
  // 세션별 실행 기록을 파일로 저장하고 로드하는 모듈
2
- import fs from 'fs/promises';
3
- import { existsSync, mkdirSync } from 'fs';
2
+ import { safeReadFile, safeWriteFile, safeExists, safeMkdir, safeUnlink } from '../util/safe_fs.js';
4
3
  import { join, resolve } from 'path';
5
4
  import chalk from 'chalk';
6
5
  import { formatToolCall, formatToolResult, getToolDisplayName, getToolDisplayConfig } from './tool_registry.js';
7
6
  import { createDebugLogger } from '../util/debug_log.js';
8
7
 
9
- const MAX_HISTORY_SESSIONS = 20; // 최대 보관 세션 수
8
+ const MAX_HISTORY_SESSIONS = 1; // 최대 보관 세션 수
10
9
 
11
10
  const debugLog = createDebugLogger('session_memory.log', 'session_memory');
12
11
 
@@ -21,11 +20,10 @@ function getHistoryFile(sessionID) {
21
20
  }
22
21
 
23
22
  // 디렉토리 생성 확인
24
- function ensureHistoryDir(sessionID) {
23
+ async function ensureHistoryDir(sessionID) {
25
24
  const sessionDir = getSessionDir(sessionID);
26
- if (!existsSync(sessionDir)) {
27
- mkdirSync(sessionDir, { recursive: true });
28
- }
25
+ // recursive: true 이므로 이미 존재해도 에러 없음
26
+ await safeMkdir(sessionDir, { recursive: true });
29
27
  }
30
28
 
31
29
  function consolelog() { }
@@ -38,9 +36,8 @@ function consolelog() { }
38
36
  export async function deleteHistoryFile(sessionID) {
39
37
  try {
40
38
  const historyFile = getHistoryFile(sessionID);
41
- if (existsSync(historyFile)) {
42
- await fs.unlink(historyFile);
43
- }
39
+ // safeUnlink 내부에서 파일 존재 여부를 체크하므로 중복 체크 불필요
40
+ await safeUnlink(historyFile);
44
41
  } catch (err) {
45
42
  }
46
43
  }
@@ -52,10 +49,10 @@ export async function deleteHistoryFile(sessionID) {
52
49
  */
53
50
  export async function loadPreviousSessions(sessionID) {
54
51
  try {
55
- ensureHistoryDir(sessionID);
52
+ await ensureHistoryDir(sessionID);
56
53
  const historyFile = getHistoryFile(sessionID);
57
- if (existsSync(historyFile)) {
58
- const content = await fs.readFile(historyFile, 'utf8');
54
+ if (await safeExists(historyFile)) {
55
+ const content = await safeReadFile(historyFile, 'utf8');
59
56
  const sessions = JSON.parse(content);
60
57
  // consolelog(chalk.blue(`📚 Loaded ${sessions.length} previous sessions from history`));
61
58
  return sessions;
@@ -74,20 +71,45 @@ export async function loadPreviousSessions(sessionID) {
74
71
  export async function saveSessionToHistory(sessionData) {
75
72
  try {
76
73
  const sessionID = sessionData.sessionID || process.app_custom.sessionID;
77
- ensureHistoryDir(sessionID);
74
+ await ensureHistoryDir(sessionID);
78
75
  const historyFile = getHistoryFile(sessionID);
79
76
 
80
- const previousSessions = sessionData.previousSessions || [];
81
- const allSessions = [...previousSessions, sessionData];
77
+ debugLog('========== saveSessionToHistory START ==========');
78
+ debugLog(`sessionID: ${sessionID}`);
79
+ debugLog(`historyFile: ${historyFile}`);
80
+
81
+ // 파일에서 이전 세션들을 로드 (메모리 누적 방지)
82
+ const previousSessions = (await safeExists(historyFile))
83
+ ? JSON.parse(await safeReadFile(historyFile, 'utf8'))
84
+ : [];
85
+
86
+ debugLog(`previousSessions loaded from file: ${previousSessions.length} sessions`);
82
87
 
83
- // 최대 개수 제한
88
+ // sessionData에서 previousSessions 제거 (중복 저장 방지)
89
+ const { previousSessions: _, ...sessionToSave } = sessionData;
90
+
91
+ debugLog(`sessionToSave size: ${JSON.stringify(sessionToSave).length} bytes`);
92
+
93
+ const allSessions = [...previousSessions, sessionToSave];
94
+ debugLog(`allSessions total: ${allSessions.length} sessions`);
95
+
96
+ // 최대 개수 제한 (fileSnapshot은 UI 표시를 위해 모두 유지)
84
97
  const sessionsToKeep = allSessions.length > MAX_HISTORY_SESSIONS
85
98
  ? allSessions.slice(-MAX_HISTORY_SESSIONS)
86
99
  : allSessions;
87
100
 
88
- await fs.writeFile(historyFile, JSON.stringify(sessionsToKeep, null, 2), 'utf8');
101
+ debugLog(`sessionsToKeep: ${sessionsToKeep.length} sessions`);
102
+ const totalSize = JSON.stringify(sessionsToKeep).length;
103
+ debugLog(`Total file size to write: ${totalSize} bytes (${(totalSize / 1024).toFixed(2)} KB)`);
104
+
105
+ await safeWriteFile(historyFile, JSON.stringify(sessionsToKeep, null, 2), 'utf8');
106
+ debugLog('File written successfully');
107
+ debugLog('========== saveSessionToHistory END ==========');
108
+
89
109
  return sessionsToKeep;
90
110
  } catch (err) {
111
+ debugLog(`ERROR in saveSessionToHistory: ${err.message}`);
112
+ debugLog(`Stack: ${err.stack}`);
91
113
  }
92
114
  return null;
93
115
  }
@@ -262,14 +284,6 @@ export function reconstructUIHistory(sessions) {
262
284
  debugLog(`toolUsageHistory items: ${session.toolUsageHistory.length}`);
263
285
  }
264
286
 
265
- // 미션 시작
266
- uiHistory.push({
267
- type: 'user',
268
- text: session.mission,
269
- timestamp: new Date(session.started_at).getTime()
270
- });
271
- debugLog(`Added user mission to uiHistory (index ${uiHistory.length - 1})`);
272
-
273
287
  // orchestratorConversation에서 도구 사용 이벤트 추출
274
288
  // 이전 세션까지의 대화 길이를 계산하여 중복 제거
275
289
  if (session.orchestratorConversation && session.orchestratorConversation.length > 0) {
@@ -462,6 +476,18 @@ export function reconstructUIHistory(sessions) {
462
476
  });
463
477
  }
464
478
  }
479
+
480
+ // user 메시지 타입 처리
481
+ if (item.role === 'user' && item.content) {
482
+ const textContent = item.content.find(c => c.type === 'input_text');
483
+ if (textContent && textContent.text) {
484
+ uiHistory.push({
485
+ type: 'user',
486
+ text: textContent.text,
487
+ timestamp: Date.now()
488
+ });
489
+ }
490
+ }
465
491
  }
466
492
  }
467
493
 
@@ -1,4 +1,4 @@
1
- import { promises as fs } from 'fs';
1
+ import { safeReadFile, safeWriteFile, safeAccess, safeMkdir, safeAppendFile } from '../util/safe_fs.js';
2
2
  import { dirname, resolve, join } from 'path';
3
3
  import * as diff from 'diff';
4
4
  import { assertFileIntegrity, trackFileRead, saveFileSnapshot } from '../system/file_integrity.js';
@@ -62,12 +62,12 @@ export async function write_file({ file_path, content }) {
62
62
 
63
63
  // 디렉토리가 존재하지 않으면 생성
64
64
  try {
65
- await fs.access(dir);
65
+ await safeAccess(dir);
66
66
  internalDebugLog.push(`[${timestamp}] Directory exists`);
67
67
  debugLog(`Directory exists`);
68
68
  } catch (error) {
69
- if (error.code === 'ENOENT') {
70
- await fs.mkdir(dir, { recursive: true });
69
+ if (error.message.includes('ENOENT') || error.message.includes('does not exist')) {
70
+ await safeMkdir(dir, { recursive: true });
71
71
  internalDebugLog.push(`[${timestamp}] Directory created`);
72
72
  debugLog(`Directory created`);
73
73
  } else {
@@ -80,13 +80,13 @@ export async function write_file({ file_path, content }) {
80
80
  let fileExists = false;
81
81
  debugLog(`Checking if file exists...`);
82
82
  try {
83
- originalContent = await fs.readFile(absolutePath, 'utf8');
83
+ originalContent = await safeReadFile(absolutePath, 'utf8');
84
84
  fileExists = true;
85
85
  internalDebugLog.push(`[${timestamp}] File EXISTS - size: ${originalContent.length} bytes`);
86
86
  debugLog(`File EXISTS - size: ${originalContent.length} bytes`);
87
87
  debugLog(`Original content first 100 chars: ${JSON.stringify(originalContent.substring(0, 100))}`);
88
88
  } catch (error) {
89
- if (error.code !== 'ENOENT') {
89
+ if (!error.message.includes('ENOENT') && !error.message.includes('no such file')) {
90
90
  throw error;
91
91
  }
92
92
  internalDebugLog.push(`[${timestamp}] File DOES NOT EXIST`);
@@ -105,8 +105,8 @@ export async function write_file({ file_path, content }) {
105
105
  internalDebugLog.push(`[${timestamp}] assertFileIntegrity FAILED: ${integrityError.message}`);
106
106
  debugLog(`ERROR: assertFileIntegrity FAILED: ${integrityError.message}`);
107
107
  const logFile = getLogFile();
108
- await fs.mkdir(dirname(logFile), { recursive: true }).catch(() => {});
109
- await fs.appendFile(logFile, internalDebugLog.join('\n') + '\n');
108
+ await safeMkdir(dirname(logFile), { recursive: true }).catch(() => {});
109
+ await safeAppendFile(logFile, internalDebugLog.join('\n') + '\n');
110
110
  throw integrityError;
111
111
  }
112
112
  }
@@ -114,7 +114,7 @@ export async function write_file({ file_path, content }) {
114
114
  // 파일 쓰기 (절대경로 사용)
115
115
  internalDebugLog.push(`[${timestamp}] Writing file - content size: ${content.length} bytes`);
116
116
  debugLog(`Writing file - content size: ${content.length} bytes`);
117
- await fs.writeFile(absolutePath, content, 'utf8');
117
+ await safeWriteFile(absolutePath, content, 'utf8');
118
118
  internalDebugLog.push(`[${timestamp}] File written successfully`);
119
119
  debugLog(`File written successfully`);
120
120
 
@@ -130,8 +130,8 @@ export async function write_file({ file_path, content }) {
130
130
  debugLog(`Snapshot saved`);
131
131
 
132
132
  const logFile = getLogFile();
133
- await fs.mkdir(dirname(logFile), { recursive: true }).catch(() => {});
134
- await fs.appendFile(logFile, internalDebugLog.join('\n') + '\n');
133
+ await safeMkdir(dirname(logFile), { recursive: true }).catch(() => {});
134
+ await safeAppendFile(logFile, internalDebugLog.join('\n') + '\n');
135
135
 
136
136
  // diff 생성 (기존 파일이 있었을 경우에만, 절대경로 사용)
137
137
  let diffInfo = null;
@@ -176,8 +176,8 @@ export async function write_file({ file_path, content }) {
176
176
  debugLog(`Stack trace: ${error.stack}`);
177
177
  debugLog('========== write_file EXCEPTION END ==========');
178
178
  const logFile = getLogFile();
179
- await fs.mkdir(dirname(logFile), { recursive: true }).catch(() => {});
180
- await fs.appendFile(logFile, internalDebugLog.join('\n') + '\n').catch(() => {});
179
+ await safeMkdir(dirname(logFile), { recursive: true }).catch(() => {});
180
+ await safeAppendFile(logFile, internalDebugLog.join('\n') + '\n').catch(() => {});
181
181
 
182
182
  // 에러 시에도 절대경로로 반환
183
183
  const absolutePath = resolve(file_path);
@@ -272,10 +272,10 @@ export async function edit_file_replace({ file_path, old_string, new_string }) {
272
272
 
273
273
  // 파일 존재 확인
274
274
  try {
275
- await fs.access(absolutePath);
275
+ await safeAccess(absolutePath);
276
276
  debugLog(`File access OK`);
277
277
  } catch (error) {
278
- if (error.code === 'ENOENT') {
278
+ if (error.message.includes('ENOENT') || error.message.includes('no such file')) {
279
279
  debugLog(`ERROR: File not found`);
280
280
  return {
281
281
  operation_successful: false,
@@ -293,7 +293,7 @@ export async function edit_file_replace({ file_path, old_string, new_string }) {
293
293
 
294
294
  // 원본 파일 읽기 (절대경로 사용)
295
295
  debugLog(`Reading file content...`);
296
- const originalContent = await fs.readFile(absolutePath, 'utf8');
296
+ const originalContent = await safeReadFile(absolutePath, 'utf8');
297
297
  debugLog(`File read successful:`);
298
298
  debugLog(` Content length: ${originalContent.length} bytes`);
299
299
  debugLog(` Line count: ${originalContent.split('\n').length}`);
@@ -415,7 +415,7 @@ export async function edit_file_replace({ file_path, old_string, new_string }) {
415
415
 
416
416
  // 파일 저장 (절대경로 사용)
417
417
  debugLog(`Writing file...`);
418
- await fs.writeFile(absolutePath, newContent, 'utf8');
418
+ await safeWriteFile(absolutePath, newContent, 'utf8');
419
419
  debugLog(`File written successfully`);
420
420
 
421
421
  // 파일 수정 후 해시 업데이트 (절대경로 사용)
@@ -509,10 +509,10 @@ export async function edit_file_range({ file_path, start_line, end_line, new_con
509
509
  // 파일 존재 확인
510
510
  debugLog(`Checking if file exists...`);
511
511
  try {
512
- await fs.access(absolutePath);
512
+ await safeAccess(absolutePath);
513
513
  debugLog(`File access OK`);
514
514
  } catch (error) {
515
- if (error.code === 'ENOENT') {
515
+ if (error.message.includes('ENOENT') || error.message.includes('no such file')) {
516
516
  debugLog(`ERROR: File not found`);
517
517
  debugLog('========== edit_file_range ERROR END ==========');
518
518
  return {
@@ -531,7 +531,7 @@ export async function edit_file_range({ file_path, start_line, end_line, new_con
531
531
 
532
532
  // 원본 파일 읽기 (절대경로 사용)
533
533
  debugLog(`Reading file content...`);
534
- const originalContent = await fs.readFile(absolutePath, 'utf8');
534
+ const originalContent = await safeReadFile(absolutePath, 'utf8');
535
535
  debugLog(`File read successful:`);
536
536
  debugLog(` Content length: ${originalContent.length} bytes`);
537
537
  debugLog(` Line count: ${originalContent.split('\n').length}`);
@@ -632,7 +632,7 @@ export async function edit_file_range({ file_path, start_line, end_line, new_con
632
632
 
633
633
  // 파일 저장 (절대경로 사용)
634
634
  debugLog(`Writing file...`);
635
- await fs.writeFile(absolutePath, newFileContent, 'utf8');
635
+ await safeWriteFile(absolutePath, newFileContent, 'utf8');
636
636
  debugLog(`File written successfully`);
637
637
 
638
638
  // 파일 수정 후 해시 업데이트 (절대경로 사용)
@@ -1,4 +1,4 @@
1
- import { promises as fs } from 'fs';
1
+ import { safeReadFile } from '../util/safe_fs.js';
2
2
  import { resolve } from 'path';
3
3
  import { trackFileRead, saveFileSnapshot } from '../system/file_integrity.js';
4
4
  import { createDebugLogger } from '../util/debug_log.js';
@@ -41,7 +41,7 @@ export async function read_file({ filePath }) {
41
41
  debugLog(` - Absolute path length: ${absolutePath.length}`);
42
42
 
43
43
  debugLog(`Reading file...`);
44
- const content = await fs.readFile(absolutePath, 'utf8');
44
+ const content = await safeReadFile(absolutePath, 'utf8');
45
45
  debugLog(`File read successful: ${content.length} bytes`);
46
46
 
47
47
  const lines = content.split('\n');
@@ -168,7 +168,7 @@ export async function read_file_range({ filePath, startLine, endLine }) {
168
168
 
169
169
  // 파일 전체를 읽고 범위만 추출
170
170
  debugLog(`Reading file...`);
171
- const content = await fs.readFile(absolutePath, 'utf8');
171
+ const content = await safeReadFile(absolutePath, 'utf8');
172
172
  debugLog(`File read successful: ${content.length} bytes`);
173
173
 
174
174
  const lines = content.split('\n');
package/src/tools/glob.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { glob } from 'glob';
2
2
  import { resolve, relative } from 'path';
3
- import { promises as fs } from 'fs';
3
+ import { safeStat } from '../util/safe_fs.js';
4
4
  import { createDebugLogger } from '../util/debug_log.js';
5
5
 
6
6
  const debugLog = createDebugLogger('glob.log', 'glob');
@@ -1,5 +1,5 @@
1
1
  import { execPythonCode } from "../system/code_executer.js";
2
- import fs from 'fs';
2
+ import { safeReadFile } from '../util/safe_fs.js';
3
3
  import { join } from 'path';
4
4
 
5
5
  // 이 파일은 웹 페이지를 가져와서 텍스트 형식으로 변환하는 도구를 제공합니다.
@@ -21,7 +21,7 @@ export async function fetch_web_page({ url }) {
21
21
  const timeout = 30;
22
22
  const user_agent = 'Mozilla/5.0 (compatible; WebFetcher/1.0)';
23
23
  const encoding = 'utf-8';
24
- const pythonCode = await fs.promises.readFile(join(process.app_custom?.__dirname || process.cwd(), 'src', 'tools', 'web_download.py'), 'utf8');
24
+ const pythonCode = await safeReadFile(join(process.app_custom?.__dirname || process.cwd(), 'src', 'tools', 'web_download.py'), 'utf8');
25
25
  const result = await execPythonCode(pythonCode, [url, timeout, user_agent, encoding]);
26
26
 
27
27
  return {
@@ -11,7 +11,7 @@ import { renderMarkdown } from '../utils/markdownRenderer.js';
11
11
  import { FileDiffViewer } from './FileDiffViewer.js';
12
12
  import { getToolDisplayName, formatToolCall } from '../../system/tool_registry.js';
13
13
  import { getFileSnapshot } from '../../system/file_integrity.js';
14
- import fs from 'fs';
14
+ import { safeMkdir, safeAppendFile } from '../../util/safe_fs.js';
15
15
  import { resolve, join, dirname } from 'path';
16
16
  import { DEBUG_LOG_DIR } from '../../util/config.js';
17
17
  import { createHash } from 'crypto';
@@ -25,9 +25,6 @@ const OLDSTRING_LOG_FILE = join(DEBUG_LOG_DIR, 'oldstring.txt');
25
25
  function logOldStringEvidence(evidence) {
26
26
  try {
27
27
  const dir = dirname(OLDSTRING_LOG_FILE);
28
- if (!fs.existsSync(dir)) {
29
- fs.mkdirSync(dir, { recursive: true });
30
- }
31
28
  const timestamp = new Date().toISOString();
32
29
  const separator = '\n' + '='.repeat(80) + '\n';
33
30
 
@@ -62,7 +59,16 @@ function logOldStringEvidence(evidence) {
62
59
  logEntry += `\n--- Result Args ---\n`;
63
60
  logEntry += JSON.stringify(evidence.resultArgs, null, 2) + '\n';
64
61
 
65
- fs.appendFileSync(OLDSTRING_LOG_FILE, logEntry);
62
+ // Convert sync operations to async (non-blocking)
63
+ setImmediate(async () => {
64
+ try {
65
+ // 디렉토리가 없으면 생성 (recursive: true 이므로 이미 존재해도 에러 없음)
66
+ await safeMkdir(dir, { recursive: true });
67
+ await safeAppendFile(OLDSTRING_LOG_FILE, logEntry);
68
+ } catch (err) {
69
+ // Ignore logging errors
70
+ }
71
+ });
66
72
  } catch (err) {
67
73
  // Ignore logging errors
68
74
  }
@@ -1,7 +1,7 @@
1
1
  // 설정 파일 및 환경 관리 유틸리티
2
2
  import { homedir } from 'os';
3
3
  import { join, dirname } from 'path';
4
- import { promises as fs } from 'fs';
4
+ import { safeReadFile, safeWriteFile, safeMkdir, safeReaddir, safeStat, safeCopyFile } from './safe_fs.js';
5
5
  import { fileURLToPath } from 'url';
6
6
  import { DEFAULT_CLAUDE_MODEL } from '../config/claude_models.js';
7
7
  import { DEFAULT_OPENAI_MODEL } from '../config/openai_models.js';
@@ -71,20 +71,20 @@ const DEFAULT_SETTINGS = {
71
71
  */
72
72
  export async function ensureConfigDirectory() {
73
73
  try {
74
- const created = await fs.mkdir(CONFIG_DIR, { recursive: true });
74
+ const created = await safeMkdir(CONFIG_DIR, { recursive: true });
75
75
  if (!created) return;
76
76
 
77
77
  const templateDir = process.app_custom?.__dirname ? join(process.app_custom.__dirname, 'config_template') : null;
78
78
  if (!templateDir) return;
79
79
 
80
- const templateFiles = await fs.readdir(templateDir);
80
+ const templateFiles = await safeReaddir(templateDir);
81
81
  await Promise.all(
82
82
  templateFiles.map(async (fileName) => {
83
83
  const sourcePath = join(templateDir, fileName);
84
- const stat = await fs.stat(sourcePath);
84
+ const stat = await safeStat(sourcePath);
85
85
  if (stat.isFile()) {
86
86
  const destPath = join(CONFIG_DIR, fileName);
87
- await fs.copyFile(sourcePath, destPath);
87
+ await safeCopyFile(sourcePath, destPath);
88
88
  }
89
89
  })
90
90
  );
@@ -101,15 +101,15 @@ export async function ensureConfigDirectory() {
101
101
  export async function loadSettings() {
102
102
  await ensureConfigDirectory();
103
103
  try {
104
- const data = await fs.readFile(SETTINGS_FILE, 'utf8');
104
+ const data = await safeReadFile(SETTINGS_FILE, 'utf8');
105
105
  const parsed = JSON.parse(data);
106
106
  return {
107
107
  ...DEFAULT_SETTINGS,
108
108
  ...(parsed && typeof parsed === 'object' ? parsed : {})
109
109
  };
110
110
  } catch (error) {
111
- if (error.code === 'ENOENT') {
112
- await fs.writeFile(SETTINGS_FILE, JSON.stringify(DEFAULT_SETTINGS, null, 2), 'utf8');
111
+ if (error.message && error.message.includes('Failed to read file')) {
112
+ await safeWriteFile(SETTINGS_FILE, JSON.stringify(DEFAULT_SETTINGS, null, 2), 'utf8');
113
113
  return { ...DEFAULT_SETTINGS };
114
114
  }
115
115
  consolelog(`Failed to load settings from ${SETTINGS_FILE}:`, error);
@@ -125,7 +125,7 @@ export async function loadSettings() {
125
125
  export async function saveSettings(settings) {
126
126
  await ensureConfigDirectory();
127
127
  try {
128
- await fs.writeFile(SETTINGS_FILE, JSON.stringify(settings, null, 2), 'utf8');
128
+ await safeWriteFile(SETTINGS_FILE, JSON.stringify(settings, null, 2), 'utf8');
129
129
  } catch (error) {
130
130
  consolelog(`Failed to save settings to ${SETTINGS_FILE}:`, error);
131
131
  throw error;
@@ -5,7 +5,7 @@
5
5
 
6
6
  import { DEBUG_LOG_DIR } from './config.js';
7
7
  import { join, dirname } from 'path';
8
- import { appendFileSync, existsSync, mkdirSync } from 'fs';
8
+ import { safeMkdir, safeAppendFile } from './safe_fs.js';
9
9
 
10
10
  /**
11
11
  * 디버그 로그를 파일에 기록합니다.
@@ -19,16 +19,21 @@ export function debugLog(logFileName, context, message) {
19
19
  const LOG_FILE = join(DEBUG_LOG_DIR, logFileName);
20
20
  const logDir = dirname(LOG_FILE);
21
21
 
22
- if (!existsSync(logDir)) {
23
- mkdirSync(logDir, { recursive: true });
24
- }
25
-
26
22
  const timestamp = new Date().toISOString();
27
23
  const logMessage = context
28
24
  ? `[${timestamp}] [${context}] ${message}\n`
29
25
  : `[${timestamp}] ${message}\n`;
30
26
 
31
- appendFileSync(LOG_FILE, logMessage);
27
+ // 디렉토리 생성 및 로그 쓰기를 비동기로 처리
28
+ setImmediate(async () => {
29
+ try {
30
+ // 디렉토리가 없으면 생성 (recursive: true 이므로 이미 존재해도 에러 없음)
31
+ await safeMkdir(logDir, { recursive: true });
32
+ await safeAppendFile(LOG_FILE, logMessage);
33
+ } catch (err) {
34
+ // Ignore logging errors
35
+ }
36
+ });
32
37
  } catch (err) {
33
38
  // Ignore logging errors
34
39
  }
@@ -1,5 +1,5 @@
1
1
  // MCP 설정 관리 유틸리티 - Claude Code 스타일
2
- import { promises as fs } from 'fs';
2
+ import { safeReadFile, safeWriteFile, safeMkdir } from './safe_fs.js';
3
3
  import { join } from 'path';
4
4
  import { createHash } from 'crypto';
5
5
  import { CONFIG_DIR } from './config.js';
@@ -22,14 +22,14 @@ export function getMcpConfigPath() {
22
22
  export async function loadMergedMcpConfig() {
23
23
  try {
24
24
  const path = getMcpConfigPath();
25
- const data = await fs.readFile(path, 'utf8');
25
+ const data = await safeReadFile(path, 'utf8');
26
26
  const config = JSON.parse(data);
27
27
 
28
28
  consolelog(`Loaded MCP config from ${path}`);
29
29
  return config.mcpServers || {};
30
30
  } catch (error) {
31
31
  // 파일이 없으면 빈 객체 반환
32
- if (error.code === 'ENOENT') {
32
+ if (error.message && error.message.includes('Failed to read file')) {
33
33
  return {};
34
34
  }
35
35
  consolelog(`Warning: Failed to load MCP config: ${error.message}`);
@@ -76,13 +76,13 @@ export async function saveMcpConfig(servers) {
76
76
  const path = getMcpConfigPath();
77
77
  const dir = join(path, '..');
78
78
 
79
- await fs.mkdir(dir, { recursive: true });
79
+ await safeMkdir(dir, { recursive: true });
80
80
 
81
81
  const config = {
82
82
  mcpServers: servers
83
83
  };
84
84
 
85
- await fs.writeFile(path, JSON.stringify(config, null, 2), 'utf8');
85
+ await safeWriteFile(path, JSON.stringify(config, null, 2), 'utf8');
86
86
  consolelog(`Saved MCP config to ${path}`);
87
87
  return path;
88
88
  }
@@ -1,5 +1,5 @@
1
1
  // 도구 실행 결과를 사람이 읽기 쉬운 형태로 포맷팅하는 유틸리티 함수들
2
- import fs from 'fs';
2
+ // fs module is imported but not used in this file
3
3
 
4
4
  /**
5
5
  * 텍스트를 안전한 문자열로 변환
@@ -1,4 +1,4 @@
1
- import { readFile, access } from "fs/promises";
1
+ import { safeReadFile, safeAccess } from "./safe_fs.js";
2
2
  import { join, dirname } from "path";
3
3
  import { fileURLToPath } from "url";
4
4
 
@@ -21,7 +21,7 @@ export function getProjectRoot() {
21
21
  */
22
22
  async function loadSystemPrompt(promptPath) {
23
23
  try {
24
- const content = await readFile(promptPath, "utf8");
24
+ const content = await safeReadFile(promptPath, "utf8");
25
25
  return content.trim();
26
26
  } catch (error) {
27
27
  consolelog(`Failed to load system prompt from ${promptPath}:`, error);
@@ -36,7 +36,7 @@ async function loadSystemPrompt(promptPath) {
36
36
  */
37
37
  async function fileExists(filePath) {
38
38
  try {
39
- await access(filePath);
39
+ await safeAccess(filePath);
40
40
  return true;
41
41
  } catch {
42
42
  return false;